| # Suggested workflows | |
| The full bootstrapping process takes quite a while. | |
| Here are some suggestions to make your life easier. | |
| ## Installing a pre-push hook | |
| CI will automatically fail your build if it doesn't pass `tidy`, our internal | |
| tool for ensuring code quality. | |
| If you'd like, you can install a [Git | |
| hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) that will | |
| automatically run `./x test tidy` on each push, to ensure your code is up to par. | |
| If the hook fails then run `./x test tidy --bless` and commit the changes. | |
| If you decide later that the pre-push behavior is undesirable, you can delete | |
| the `pre-push` file in `.git/hooks`. | |
| A prebuilt git hook lives at [`src/etc/pre-push.sh`]. | |
| It can be copied into your `.git/hooks` folder as `pre-push` (without the `.sh` extension!). | |
| You can also install the hook as a step of running `./x setup`! | |
| ## Config extensions | |
| When working on different tasks, you might need to switch between different bootstrap configurations. | |
| Sometimes you may want to keep an old configuration for future use. | |
| But saving raw config values in | |
| random files and manually copying and pasting them can quickly become messy, especially if you have a | |
| long history of different configurations. | |
| To simplify managing multiple configurations, you can create config extensions. | |
| For example, you can create a simple config file named `cross.toml`: | |
| ```toml | |
| [build] | |
| build = "x86_64-unknown-linux-gnu" | |
| host = ["i686-unknown-linux-gnu"] | |
| target = ["i686-unknown-linux-gnu"] | |
| [llvm] | |
| download-ci-llvm = false | |
| [target.x86_64-unknown-linux-gnu] | |
| llvm-config = "/path/to/llvm-19/bin/llvm-config" | |
| ``` | |
| Then, include this in your `bootstrap.toml`: | |
| ```toml | |
| include = ["cross.toml"] | |
| ``` | |
| You can also include extensions within extensions recursively. | |
| **Note:** In the `include` field, the overriding logic follows a right-to-left order. | |
| For example, | |
| in `include = ["a.toml", "b.toml"]`, extension `b.toml` overrides `a.toml`. | |
| Also, parent extensions always override the inner ones. | |
| ## Configuring `rust-analyzer` for `rustc` | |
| ### Checking the "library" tree | |
| Checking the "library" tree requires a stage1 compiler, which can be a heavy process on some computers. | |
| For this reason, bootstrap has a flag called `--skip-std-check-if-no-download-rustc` that skips checking the | |
| "library" tree if `rust.download-rustc` isn't available. | |
| If you want to avoid putting a heavy load on your computer | |
| with `rust-analyzer`, you can add the `--skip-std-check-if-no-download-rustc` flag to your `./x check` command in | |
| the `rust-analyzer` configuration. | |
| ### Project-local rust-analyzer setup | |
| `rust-analyzer` can help you check and format your code whenever you save a file. | |
| By default, `rust-analyzer` runs the `cargo check` and `rustfmt` commands, | |
| but you can override these commands to use more adapted versions of these tools | |
| when hacking on `rustc`. | |
| With custom setup, `rust-analyzer` can use `./x check` | |
| to check the sources, and the stage 0 rustfmt to format them. | |
| The default `rust-analyzer.check.overrideCommand` command line will check all | |
| the crates and tools in the repository. | |
| If you are working on a specific part, | |
| you can override the command to only check the part you are working on to save checking time. | |
| For example, if you are working on the compiler, you can override | |
| the command to `x check compiler --json-output` to only check the compiler part. | |
| You can run `x check --help --verbose` to see the available parts. | |
| Running `./x setup editor` will prompt you to create a project-local LSP config | |
| file for one of the supported editors. | |
| You can also create the config file as a step of running `./x setup`. | |
| ### Using a shared build directory for rust-analyzer | |
| By default, when rust-analyzer runs a bootstrap command, | |
| it will use a separate build directory from manual command-line builds. | |
| You can override this your generated LSP config file if you want to save disk space. | |
| However, this is not recommended: | |
| - Each build will lock the build directory and force the other to wait, so it | |
| becomes impossible to run command-line builds while rust-analyzer is running | |
| commands in the background. | |
| - There is an increased risk of one of the builds deleting previously-built | |
| artifacts due to conflicting compiler flags or other settings, forcing | |
| additional rebuilds in some cases. | |
| ### Visual Studio Code | |
| Selecting `vscode` in `./x setup editor` will prompt you to create a | |
| `.vscode/settings.json` file which will configure Visual Studio code. | |
| The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_settings.json`]. | |
| If running `./x check` on save is inconvenient, in VS Code you can use a [Build Task] instead: | |
| ```JSON | |
| // .vscode/tasks.json | |
| { | |
| "version": "2.0.0", | |
| "tasks": [ | |
| { | |
| "label": "./x check", | |
| "command": "./x check", | |
| "type": "shell", | |
| "problemMatcher": "$rustc", | |
| "presentation": { "clear": true }, | |
| "group": { "kind": "build", "isDefault": true } | |
| } | |
| ] | |
| } | |
| ``` | |
| [Build Task]: https://code.visualstudio.com/docs/editor/tasks | |
| ### Neovim | |
| For Neovim users, there are a few options: | |
| 1. The easiest way is using [neoconf.nvim][neoconf.nvim] but it uses the | |
| deprecated `require('lspconfig')` API which displays a warning on neovim 0.11+. | |
| 2. Using `coc.nvim` is another option but it requires node.js to be installed. | |
| 3. Using a custom script to load rust-analyzer settings. | |
| #### neoconf.nvim | |
| [neoconf.nvim][neoconf.nvim] allows for project-local configuration files with the native LSP. | |
| The steps for how to use it are below. | |
| Note that they require rust-analyzer to already be configured with Neovim. | |
| Steps for this can be [found here][r-a nvim lsp]. | |
| 1. First install the plugin. | |
| This can be done by following the steps in the README. | |
| 2. Run `./x setup editor`, and select `vscode` to create a `.vscode/settings.json` file. | |
| `neoconf` is able to read and update | |
| rust-analyzer settings automatically when the project is opened when this file is detected. | |
| Neovim does not expand VS Code's `${workspaceFolder}` variable, so replace each occurrence | |
| in the generated file with the absolute path to your rust repository. | |
| #### coc.nvim | |
| If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to | |
| create a `.vim/coc-settings.json`. | |
| The settings can be edited with `:CocLocalConfig`. | |
| The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. | |
| #### Custom LSP settings | |
| If you're running neovim 0.11+, you can configure rust-analyzer with just | |
| [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and a custom script. | |
| 1. Make sure [rust-analyzer LSP][r-a nvim lsp] is set up | |
| 2. Create `$HOME/.config/nvim/after/plugged/rust_analyzer.lua` with the following content: | |
| ```lua | |
| -- Capture the default functions from nvim-lspconfig/lsp/rust_analyzer.lua before overriding it. | |
| -- This file is in after/plugin to guarantee nvim-lspconfig has been initialised already. | |
| local default_root_dir = vim.lsp.config["rust_analyzer"].root_dir | |
| local default_before_init = vim.lsp.config["rust_analyzer"].before_init | |
| vim.lsp.config("rust_analyzer", { | |
| cmd = { "rust-analyzer" }, | |
| filetypes = { "rust" }, | |
| -- To support rust-lang/rust, we need to detect when we're in the rust repo and use the git root | |
| -- instead of cargo project root. | |
| root_dir = function(bufnr, on_dir) | |
| local git_root = vim.fs.root(bufnr, { ".git" }) | |
| if git_root then | |
| if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then | |
| on_dir(git_root) | |
| return | |
| end | |
| end | |
| -- For anything that doesn't match rust-lang/rust, fallback to default root_dir | |
| default_root_dir(bufnr, on_dir) | |
| end, | |
| before_init = function(init_params, config) | |
| -- When inside rust-lang/rust, we need to use the special rust-analyzer settings. | |
| local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") | |
| if vim.uv.fs_stat(settings) then | |
| local file = io.open(settings) | |
| -- nvim 0.12+ supports comments otherwise you'll need content:gsub("//[^\n]*", ""). | |
| local json = vim.json.decode(file:read("*a"), { skip_comments = true }) | |
| file:close() | |
| config.settings["rust-analyzer"] = vim.tbl_deep_extend( | |
| "force", -- Overwrite with the special settings when there is a conflict. | |
| config.settings["rust-analyzer"] or {}, | |
| json.lsp["rust-analyzer"].initialization_options | |
| ) | |
| end | |
| default_before_init(init_params, config) | |
| end, | |
| }) | |
| vim.lsp.enable("rust_analyzer") | |
| ``` | |
| If you would like to use the build task that is described above, you may either | |
| make your own command in your config, or you can install a plugin such as | |
| [overseer.nvim](https://github.com/stevearc/overseer.nvim) that can [read VSCode's `task.json` | |
| files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks), | |
| and follow the same instructions as above. | |
| [neoconf.nvim]: https://github.com/folke/neoconf.nvim/ | |
| [r-a nvim lsp]: https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp | |
| ### Emacs | |
| Emacs provides support for rust-analyzer with project-local configuration | |
| through [Eglot](https://www.gnu.org/software/emacs/manual/html_node/eglot/). | |
| Steps for setting up Eglot with rust-analyzer can be [found | |
| here](https://rust-analyzer.github.io/manual.html#eglot). | |
| Having set up Emacs & Eglot for Rust development in general, you can run | |
| `./x setup editor` and select `emacs`, which will prompt you to create | |
| `.dir-locals.el` with the recommended configuration for Eglot. | |
| The recommended settings live at [`src/etc/rust_analyzer_eglot.el`]. | |
| For more information on project-specific Eglot configuration, consult [the | |
| manual](https://www.gnu.org/software/emacs/manual/html_node/eglot/Project_002dspecific-configuration.html). | |
| ### Helix | |
| Helix comes with built-in LSP and rust-analyzer support. | |
| It can be configured through `languages.toml`, as described | |
| [here](https://docs.helix-editor.com/languages.html). | |
| You can run `./x setup editor` and select `helix`, which will prompt you to | |
| create `languages.toml` with the recommended configuration for Helix. | |
| The recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. | |
| ### Zed | |
| Zed comes with built-in LSP and rust-analyzer support. | |
| It can be configured through `.zed/settings.json`, as described | |
| [here](https://zed.dev/docs/configuring-languages). | |
| Selecting `zed` in `./x setup editor` will prompt you to create a `.zed/settings.json` | |
| file which will configure Zed with the recommended configuration. | |
| The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.json`]. | |
| ## Check, check, and check again | |
| When doing simple refactoring, it can be useful to run `./x check` continuously. | |
| If you set up `rust-analyzer` as described above, this will be | |
| done for you every time you save a file. | |
| Here you are just checking that the | |
| compiler can **build**, but often that is all you need (e.g., when renaming a | |
| method). | |
| You can then run `./x build` when you actually need to run tests. | |
| In fact, it is sometimes useful to put off tests even when you are not 100% sure the code will work. | |
| You can then keep building up refactoring commits and only run the tests at some later time. | |
| You can then use `git bisect` to track down **precisely** which commit caused the problem. | |
| A nice side-effect of this style | |
| is that you are left with a fairly fine-grained set of commits at the end, all | |
| of which build and pass tests. | |
| This often helps reviewing. | |
| ## Configuring `rustup` to use nightly | |
| Some parts of the bootstrap process uses pinned, nightly versions of tools like rustfmt. | |
| To make things like `cargo fmt` work correctly in your repo, | |
| [install a nightly toolchain] with rustup, then run this command: | |
| ```console | |
| cd <path to rustc repo> | |
| rustup override set nightly | |
| ``` | |
| Don't forget to do this for all directories you have [setup a worktree for]. | |
| You may need to use the | |
| pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. | |
| **Note** see [the section on vscode] for how to configure it with this real | |
| rustfmt `x` uses, and [the section on rustup] for how to setup `rustup` | |
| toolchain for your bootstrapped compiler | |
| **Note** This does _not_ allow you to build `rustc` with cargo directly. | |
| You still have to use `x` to work on the compiler or standard library, this just | |
| lets you use `cargo fmt`. | |
| [install a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust | |
| [setup a worktree for]: ./suggested.md#working-on-multiple-branches-at-the-same-time | |
| [the section on vscode]: suggested.md#configuring-rust-analyzer-for-rustc | |
| [the section on rustup]: how-to-build-and-run.md?highlight=rustup#creating-a-rustup-toolchain | |
| ## Faster Builds with CI-rustc | |
| If you are not working on the compiler, you often don't need to build the compiler tree. | |
| For example, you can skip building the compiler and only build the `library` tree or the | |
| tools under `src/tools`. | |
| To achieve that, you have to enable this by setting the `download-rustc` | |
| option in your configuration. | |
| This tells bootstrap to use the latest nightly compiler for `stage > 0` | |
| steps, meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler | |
| for `stage > 0` steps. | |
| This way, it will never need to build the in-tree compiler. | |
| As a result, your build time will be significantly reduced by not building the in-tree compiler. | |
| ## Faster rebuilds with `--keep-stage-std` | |
| Sometimes just checking whether the compiler builds is not enough. | |
| A common example is that you need to add a `debug!` statement to inspect the value of | |
| some state or better understand the problem. | |
| In that case, you don't really need a full build. | |
| By bypassing bootstrap's cache invalidation, you can often get these builds to complete faster. | |
| The only catch is this requires a bit of fudging and may produce compilers that don't work (but | |
| that is easily detected and fixed). | |
| The sequence of commands you want is as follows: | |
| - Initial build: `./x build library` | |
| - Subsequent builds: `./x build library --keep-stage-std=1` | |
| As mentioned, the effect of `--keep-stage-std=1` is that we just _assume_ that the | |
| old standard library can be re-used. | |
| If you are editing the compiler, this is | |
| often true: you haven't changed the standard library, after all. | |
| But sometimes, it's not true: for example, if you are editing the "metadata" part of | |
| the compiler, which controls how the compiler encodes types and other states | |
| into the `rlib` files, or if you are editing things that wind up in the metadata | |
| (such as the definition of the MIR). | |
| That is, you might get weird behavior from a compile when using | |
| `--keep-stage-std=1`, for example, strange [ICEs](../appendix/glossary.md#ice) or other panics. | |
| In that case, you should simply remove the `--keep-stage-std=1` from the command and rebuild. | |
| You can also use `--keep-stage-std=1` when running tests. | |
| Something like this: | |
| - Initial test run: `./x test tests/ui` | |
| - Subsequent test run: `./x test tests/ui --keep-stage-std=1` | |
| ## Using incremental compilation | |
| You can further enable the `--incremental` flag to save additional time in subsequent rebuilds: | |
| ```bash | |
| ./x test tests/ui --incremental --test-args issue-1234 | |
| ``` | |
| If you don't want to include the flag with every command, you can enable it in the `bootstrap.toml`: | |
| ```toml | |
| [rust] | |
| incremental = true | |
| ``` | |
| Note that incremental compilation will use more disk space than usual. | |
| If disk space is a concern for you, you might want to check the size of the `build` | |
| directory from time to time. | |
| ## Fine-tuning optimizations | |
| Setting `optimize = false` makes the compiler too slow for tests. | |
| However, to improve the test cycle, you can disable optimizations selectively only for the | |
| crates you'll have to rebuild | |
| ([source](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/incremental.20compilation.20question/near/202712165)). | |
| For example, when working on `rustc_mir_build`, the `rustc_mir_build` and | |
| `rustc_driver` crates take the most time to incrementally rebuild. | |
| You could therefore set the following in the root `Cargo.toml`: | |
| ```toml | |
| [profile.release.package.rustc_mir_build] | |
| opt-level = 0 | |
| [profile.release.package.rustc_driver] | |
| opt-level = 0 | |
| ``` | |
| ## Working on multiple branches at the same time | |
| Working on multiple branches in parallel can be a little annoying, since | |
| building the compiler on one branch will cause the old build and the incremental | |
| compilation cache to be overwritten. | |
| One solution would be to have multiple | |
| clones of the repository, but that would mean storing the Git metadata multiple | |
| times, and having to update each clone individually. | |
| Fortunately, Git has a better solution called [worktrees]. | |
| This lets you create multiple "working trees", which all share the same Git database. | |
| Moreover, | |
| because all of the worktrees share the same object database, if you update a | |
| branch (e.g. `main`) in any of them, you can use the new commits from any of the | |
| worktrees. | |
| One caveat, though, is that submodules do not get shared. | |
| They will still be cloned multiple times. | |
| [worktrees]: https://git-scm.com/docs/git-worktree | |
| Given you are inside the root directory for your Rust repository, you can create | |
| a "linked working tree" in a new "rust2" directory by running the following command: | |
| ```bash | |
| git worktree add ../rust2 | |
| ``` | |
| Creating a new worktree for a new branch based on `main` looks like: | |
| ```bash | |
| git worktree add -b my-feature ../rust2 main | |
| ``` | |
| You can then use that rust2 folder as a separate workspace for modifying and building `rustc`! | |
| ## Working with nix | |
| Several nix configurations are defined in `src/tools/nix-dev-shell`. | |
| If you're using direnv, you can create a symbolic link to `src/tools/nix-dev-shell/envrc-flake` or `src/tools/nix-dev-shell/envrc-shell` | |
| ```bash | |
| ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc # Use flake | |
| ``` | |
| or | |
| ```bash | |
| ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # Use nix-shell | |
| ``` | |
| If you're using the flake, make sure to also update it with the following command: | |
| ``` | |
| nix flake update --flake ./src/tools/nix-dev-shell | |
| ``` | |
| The shell creates a command named `x` that runs the `./x.py` script with all dependencies | |
| set up correctly. | |
| ### Note | |
| Note that when using nix on a not-NixOS distribution, it may be necessary to set | |
| **`build.patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect | |
| whether it's running in nix and enable patching automatically, but this | |
| detection can have false negatives. | |
| You can also use your nix shell to manage `bootstrap.toml`: | |
| ```nix | |
| let | |
| config = pkgs.writeText "rustc-config" '' | |
| # Your bootstrap.toml content goes here | |
| '' | |
| pkgs.mkShell { | |
| /* ... */ | |
| # This environment variable tells bootstrap where our bootstrap.toml is. | |
| RUST_BOOTSTRAP_CONFIG = config; | |
| } | |
| ``` | |
| ## Shell Completions | |
| If you use Bash, Zsh, Fish or PowerShell, you can find automatically-generated shell | |
| completion scripts for `./x` in | |
| [`src/etc/completions`](https://github.com/rust-lang/rust/tree/HEAD/src/etc/completions). | |
| You can use `source ./src/etc/completions/x.<extension>` to load completions | |
| for your shell of choice, or `& .\src\etc\completions\x.ps1` for PowerShell. | |
| Adding this to your shell's startup script (e.g. `.bashrc`) will automatically | |
| load this completion. | |
| [`src/etc/rust_analyzer_settings.json`]: https://github.com/rust-lang/rust/blob/HEAD/src/etc/rust_analyzer_settings.json | |
| [`src/etc/rust_analyzer_eglot.el`]: https://github.com/rust-lang/rust/blob/HEAD/src/etc/rust_analyzer_eglot.el | |
| [`src/etc/rust_analyzer_helix.toml`]: https://github.com/rust-lang/rust/blob/HEAD/src/etc/rust_analyzer_helix.toml | |
| [`src/etc/rust_analyzer_zed.json`]: https://github.com/rust-lang/rust/blob/HEAD/src/etc/rust_analyzer_zed.json | |
| [`src/etc/pre-push.sh`]: https://github.com/rust-lang/rust/blob/HEAD/src/etc/pre-push.sh |