about summary refs log tree commit diff
path: root/src/doc/rustc-dev-guide
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2025-03-03 08:38:46 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2025-03-03 08:38:46 +0200
commitdd3a5f9a64f97fd02e1d254236cb1612ed119e82 (patch)
tree3f99fe82095b8a0ec0b3d1cfa541983a0eaa728e /src/doc/rustc-dev-guide
parent969868ba30b41af0cece305cd68d133369b492a4 (diff)
parentdaf59857d6d2b87af4b846316bf1561a6083ed51 (diff)
downloadrust-dd3a5f9a64f97fd02e1d254236cb1612ed119e82.tar.gz
rust-dd3a5f9a64f97fd02e1d254236cb1612ed119e82.zip
Merge from rust-lang/rust
Diffstat (limited to 'src/doc/rustc-dev-guide')
-rw-r--r--src/doc/rustc-dev-guide/.github/workflows/ci.yml6
-rw-r--r--src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml2
-rw-r--r--src/doc/rustc-dev-guide/examples/README5
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-example.rs14
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs18
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-interface-example.rs8
-rw-r--r--src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs6
-rw-r--r--src/doc/rustc-dev-guide/rust-version2
-rw-r--r--src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md43
-rw-r--r--src/doc/rustc-dev-guide/src/building/optimized-build.md2
-rw-r--r--src/doc/rustc-dev-guide/src/building/suggested.md33
-rw-r--r--src/doc/rustc-dev-guide/src/compiler-debugging.md2
-rw-r--r--src/doc/rustc-dev-guide/src/profiling/with_perf.md7
-rw-r--r--src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md1
-rw-r--r--src/doc/rustc-dev-guide/src/rustc-driver/interacting-with-the-ast.md1
-rw-r--r--src/doc/rustc-dev-guide/src/tests/ci.md6
-rw-r--r--src/doc/rustc-dev-guide/src/tests/docker.md9
17 files changed, 134 insertions, 31 deletions
diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml
index 3f810e2fbcc..22a4fb1901a 100644
--- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml
+++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml
@@ -6,8 +6,8 @@ on:
       - master
   pull_request:
   schedule:
-    # Run at 18:00 UTC every day
-    - cron: '0 18 * * *'
+    # Run multiple times a day as the successfull cached links are not checked every time.
+    - cron: '0 */8 * * *'
 
 jobs:
   ci:
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
     env:
       MDBOOK_VERSION: 0.4.21
-      MDBOOK_LINKCHECK2_VERSION: 0.8.1
+      MDBOOK_LINKCHECK2_VERSION: 0.9.1
       MDBOOK_MERMAID_VERSION: 0.12.6
       MDBOOK_TOC_VERSION: 0.11.2
       DEPLOY_DIR: book/html
diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
index dc5395a19dd..b19eccf9eb8 100644
--- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
+++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
@@ -111,4 +111,4 @@ jobs:
           to: 196385
           type: "stream"
           topic: "Subtree sync automation"
-          content: ${{ steps.message.outputs.message }}
+          content: ${{ steps.create-message.outputs.message }}
diff --git a/src/doc/rustc-dev-guide/examples/README b/src/doc/rustc-dev-guide/examples/README
index ca49dd74db2..05e44673700 100644
--- a/src/doc/rustc-dev-guide/examples/README
+++ b/src/doc/rustc-dev-guide/examples/README
@@ -4,7 +4,10 @@ For each example to compile, you will need to first run the following:
 
 To create an executable:
 
-    rustc rustc-driver-example.rs
+    rustup run nightly rustc rustc-driver-example.rs
+
+You might need to be more specific about the exact nightly version. See the comments at the top of
+the examples for the version they were written for.
 
 To run an executable:
 
diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs
index 14998965ac8..984bd3e37ae 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs
@@ -1,3 +1,5 @@
+// Tested with nightly-2025-02-13
+
 #![feature(rustc_private)]
 
 extern crate rustc_ast;
@@ -73,7 +75,7 @@ impl rustc_driver::Callbacks for MyCallbacks {
             let hir = tcx.hir();
             let item = hir.item(id);
             match item.kind {
-                rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => {
+                rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => {
                     let name = item.ident;
                     let ty = tcx.type_of(item.hir_id().owner.def_id);
                     println!("{name:?}:\t{ty:?}")
@@ -87,5 +89,13 @@ impl rustc_driver::Callbacks for MyCallbacks {
 }
 
 fn main() {
-    run_compiler(&["main.rs".to_string()], &mut MyCallbacks);
+    run_compiler(
+        &[
+            // The first argument, which in practice contains the name of the binary being executed
+            // (i.e. "rustc") is ignored by rustc.
+            "ignored".to_string(),
+            "main.rs".to_string(),
+        ],
+        &mut MyCallbacks,
+    );
 }
diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
index dc63e1aa511..3270c722e07 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs
@@ -1,3 +1,5 @@
+// Tested with nightly-2025-02-13
+
 #![feature(rustc_private)]
 
 extern crate rustc_ast;
@@ -18,7 +20,7 @@ use std::path::Path;
 use std::sync::Arc;
 
 use rustc_ast_pretty::pprust::item_to_string;
-use rustc_driver::{Compilation, run_compiler};
+use rustc_driver::{run_compiler, Compilation};
 use rustc_interface::interface::{Compiler, Config};
 use rustc_middle::ty::TyCtxt;
 
@@ -74,8 +76,8 @@ impl rustc_driver::Callbacks for MyCallbacks {
         for id in hir_krate.items() {
             let item = hir_krate.item(id);
             // Use pattern-matching to find a specific node inside the main function.
-            if let rustc_hir::ItemKind::Fn(_, _, body_id) = item.kind {
-                let expr = &tcx.hir_body(body_id).value;
+            if let rustc_hir::ItemKind::Fn { body, .. } = item.kind {
+                let expr = &tcx.hir_body(body).value;
                 if let rustc_hir::ExprKind::Block(block, _) = expr.kind {
                     if let rustc_hir::StmtKind::Let(let_stmt) = block.stmts[0].kind {
                         if let Some(expr) = let_stmt.init {
@@ -94,5 +96,13 @@ impl rustc_driver::Callbacks for MyCallbacks {
 }
 
 fn main() {
-    run_compiler(&["main.rs".to_string()], &mut MyCallbacks);
+    run_compiler(
+        &[
+            // The first argument, which in practice contains the name of the binary being executed
+            // (i.e. "rustc") is ignored by rustc.
+            "ignored".to_string(),
+            "main.rs".to_string(),
+        ],
+        &mut MyCallbacks,
+    );
 }
diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs
index 30f48ea5297..70f27c2a82a 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs
@@ -1,3 +1,5 @@
+// Tested with nightly-2025-02-13
+
 #![feature(rustc_private)]
 
 extern crate rustc_driver;
@@ -9,8 +11,6 @@ extern crate rustc_interface;
 extern crate rustc_session;
 extern crate rustc_span;
 
-use std::sync::Arc;
-
 use rustc_errors::registry;
 use rustc_hash::FxHashMap;
 use rustc_session::config;
@@ -56,7 +56,7 @@ fn main() {
         expanded_args: Vec::new(),
         ice_file: None,
         hash_untracked_state: None,
-        using_internal_features: Arc::default(),
+        using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES,
     };
     rustc_interface::run_compiler(config, |compiler| {
         // Parse the program and print the syntax tree.
@@ -68,7 +68,7 @@ fn main() {
                 let hir = tcx.hir();
                 let item = hir.item(id);
                 match item.kind {
-                    rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => {
+                    rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => {
                         let name = item.ident;
                         let ty = tcx.type_of(item.hir_id().owner.def_id);
                         println!("{name:?}:\t{ty:?}")
diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs
index 2355cb85ab3..39b236e1783 100644
--- a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs
+++ b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs
@@ -1,3 +1,5 @@
+// Tested with nightly-2025-02-13
+
 #![feature(rustc_private)]
 
 extern crate rustc_data_structures;
@@ -15,7 +17,7 @@ use std::sync::{Arc, Mutex};
 use rustc_errors::emitter::Emitter;
 use rustc_errors::registry::{self, Registry};
 use rustc_errors::translation::Translate;
-use rustc_errors::{DiagCtxt, DiagInner, FluentBundle};
+use rustc_errors::{DiagInner, FluentBundle};
 use rustc_session::config;
 use rustc_span::source_map::SourceMap;
 
@@ -79,7 +81,7 @@ fn main() {
         expanded_args: Vec::new(),
         ice_file: None,
         hash_untracked_state: None,
-        using_internal_features: Arc::default(),
+        using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES,
     };
     rustc_interface::run_compiler(config, |compiler| {
         let krate = rustc_interface::passes::parse(&compiler.sess);
diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index 78e9ecdf174..ce21bb8ef39 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-124cc92199ffa924f6b4c7cc819a85b65e0c3984
+4ecd70ddd1039a3954056c1071e40278048476fa
diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
index 04d8e91dcb4..24b9783ddf0 100644
--- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
+++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
@@ -1,7 +1,46 @@
 # Debugging bootstrap
 
+There are two main ways to debug bootstrap itself. The first is through println logging, and the second is through the `tracing` feature.
+
 > FIXME: this section should be expanded
 
+## `println` logging
+
+Bootstrap has extensive unstructured logging. Most of it is gated behind the `--verbose` flag (pass `-vv` for even more detail).
+
+If you want to know which `Step` ran a command, you could invoke bootstrap like so:
+
+```
+$ ./x dist rustc --dry-run -vv
+learning about cargo
+running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50)
+running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/library/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50)
+> Assemble { target_compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu } }
+  > Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu }
+    > Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false }
+Removing sysroot /home/jyn/src/rust2/build/tmp-dry-run/x86_64-unknown-linux-gnu/stage1 to avoid caching bugs
+    < Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false }
+  < Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu }
+...
+```
+
+This will go through all the recursive dependency calculations, where `Step`s internally call `builder.ensure()`, without actually running cargo or the compiler.
+
+In some cases, even this may not be enough logging (if so, please add more!). In that case, you can omit `--dry-run`, which will show the normal output inline with the debug logging:
+
+```
+      c Sysroot { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu }, force_recompile: false }
+using sysroot /home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0-sysroot
+Building stage0 library artifacts (x86_64-unknown-linux-gnu)
+running: cd "/home/jyn/src/rust2" && env ... RUSTC_VERBOSE="2" RUSTC_WRAPPER="/home/jyn/src/rust2/build/bootstrap/debug/rustc" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-Zroot-dir=/home/jyn/src/rust2" "-v" "-v" "--manifest-path" "/home/jyn/src/rust2/library/sysroot/Cargo.toml" "--message-format" "json-render-diagnostics"
+   0.293440230s  INFO prepare_target{force=false package_id=sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot) target="sysroot"}: cargo::core::compiler::fingerprint: fingerprint error for sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot)/Build/TargetInner { name_inferred: true, ..: lib_target("sysroot", ["lib"], "/home/jyn/src/rust2/library/sysroot/src/lib.rs", Edition2021) }
+...
+```
+
+In most cases this should not be necessary.
+
+TODO: we should convert all this to structured logging so it's easier to control precisely.
+
 ## `tracing` in bootstrap
 
 Bootstrap has conditional [`tracing`][tracing] setup to provide structured logging.
@@ -53,11 +92,11 @@ Checking stage0 bootstrap artifacts (x86_64-unknown-linux-gnu)
 Build completed successfully in 0:00:08
 ```
 
-#### Controlling log output
+#### Controlling tracing output
 
 The env var `BOOTSTRAP_TRACING` accepts a [`tracing` env-filter][tracing-env-filter].
 
-There are two orthogonal ways to control which kind of logs you want:
+There are two orthogonal ways to control which kind of tracing logs you want:
 
 1. You can specify the log **level**, e.g. `DEBUG` or `TRACE`.
 2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` vs custom targets like `CONFIG_HANDLING`.
diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md
index 8feda59829b..f8ca1d0dc39 100644
--- a/src/doc/rustc-dev-guide/src/building/optimized-build.md
+++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md
@@ -126,4 +126,4 @@ Here is an example of how can `opt-dist` be used locally (outside of CI):
 [`Environment`]: https://github.com/rust-lang/rust/blob/ee451f8faccf3050c76cdcd82543c917b40c7962/src/tools/opt-dist/src/environment.rs#L5
 
 > Note: if you want to run the actual CI pipeline, instead of running `opt-dist` locally,
-> you can execute `python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux`.
+> you can execute `cargo run --manifest-path src/ci/citool/Cargo.toml run-local dist-x86_64-linux`.
diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md
index 5c041d6cb70..2498838144e 100644
--- a/src/doc/rustc-dev-guide/src/building/suggested.md
+++ b/src/doc/rustc-dev-guide/src/building/suggested.md
@@ -120,10 +120,35 @@ create a `.vim/coc-settings.json`. The settings can be edited with
 [`src/etc/rust_analyzer_settings.json`].
 
 Another way is without a plugin, and creating your own logic in your
-configuration. To do this you must translate the JSON to Lua yourself. The
-translation is 1:1 and fairly straight-forward. It must be put in the
-`["rust-analyzer"]` key of the setup table, which is [shown
-here](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#rust_analyzer).
+configuration. The following code will work for any checkout of rust-lang/rust (newer than Febuary 2025):
+
+```lua
+lspconfig.rust_analyzer.setup {
+    root_dir = function()
+        local default = lspconfig.rust_analyzer.config_def.default_config.root_dir()
+        -- the default root detection uses the cargo workspace root.
+        -- but for rust-lang/rust, the standard library is in its own workspace.
+        -- use the git root instead.
+        local compiler_config = vim.fs.joinpath(default, "../src/bootstrap/defaults/config.compiler.toml")
+        if vim.fs.basename(default) == "library" and vim.uv.fs_stat(compiler_config) then
+            return vim.fs.dirname(default)
+        end
+        return default
+    end,
+    on_init = function(client)
+        local path = client.workspace_folders[1].name
+        local config = vim.fs.joinpath(path, "src/etc/rust_analyzer_zed.json")
+        if vim.uv.fs_stat(config) then
+            -- load rust-lang/rust settings
+            local file = io.open(config)
+            local json = vim.json.decode(file:read("*a"))
+            client.config.settings["rust-analyzer"] = json.lsp["rust-analyzer"].initialization_options
+            client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
+        end
+        return true
+    end
+}
+```
 
 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
diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md
index e2097b26e5c..c16b3ee7abd 100644
--- a/src/doc/rustc-dev-guide/src/compiler-debugging.md
+++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md
@@ -368,7 +368,7 @@ error: layout_of(&'a u32) = Layout {
 error: aborting due to previous error
 ```
 
-[`Layout`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html
+[`Layout`]: https://doc.rust-lang.org/nightly/nightly-rustc/stable_mir/abi/struct.Layout.html
 
 
 ## Configuring CodeLLDB for debugging `rustc`
diff --git a/src/doc/rustc-dev-guide/src/profiling/with_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_perf.md
index 6cd98f886dd..51a22d18577 100644
--- a/src/doc/rustc-dev-guide/src/profiling/with_perf.md
+++ b/src/doc/rustc-dev-guide/src/profiling/with_perf.md
@@ -52,6 +52,13 @@ you made in the beginning. But there are some things to be aware of:
 - You probably don't want incremental messing about with your
   profile. So something like `CARGO_INCREMENTAL=0` can be helpful.
 
+In case to avoid the issue of `addr2line xxx/elf: could not read first record` when reading
+collected data from `cargo`, you may need use the latest version of `addr2line`:
+
+```bash
+cargo install addr2line --features="bin"
+```
+
 ### Gathering a perf profile from a `perf.rust-lang.org` test
 
 Often we want to analyze a specific test from `perf.rust-lang.org`.
diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md b/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md
index e3ca323058c..1043df6ecb6 100644
--- a/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md
+++ b/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md
@@ -8,7 +8,6 @@ otherwise be printed to stderr.
 To get diagnostics from the compiler,
 configure [`rustc_interface::Config`] to output diagnostic to a buffer,
 and run [`TyCtxt.analysis`].
-The following was tested with <!-- date-check: september 2024 --> `nightly-2024-09-16`:
 
 ```rust
 {{#include ../../examples/rustc-interface-getting-diagnostics.rs}}
diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/interacting-with-the-ast.md b/src/doc/rustc-dev-guide/src/rustc-driver/interacting-with-the-ast.md
index 5eaa0c82c9e..f46418701a7 100644
--- a/src/doc/rustc-dev-guide/src/rustc-driver/interacting-with-the-ast.md
+++ b/src/doc/rustc-dev-guide/src/rustc-driver/interacting-with-the-ast.md
@@ -5,7 +5,6 @@
 ## Getting the type of an expression
 
 To get the type of an expression, use the [`after_analysis`] callback to get a [`TyCtxt`].
-The following was tested with <!-- date-check: december 2024 --> `nightly-2024-12-15`:
 
 ```rust
 {{#include ../../examples/rustc-driver-interacting-with-the-ast.rs}}
diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md
index a4b22392f19..ae6adb678af 100644
--- a/src/doc/rustc-dev-guide/src/tests/ci.md
+++ b/src/doc/rustc-dev-guide/src/tests/ci.md
@@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined
 in [`.github/workflows/ci.yml`], which contains a bunch of steps that are
 unified for all CI jobs that we execute. When a commit is pushed to a
 corresponding branch or a PR, the workflow executes the
-[`src/ci/github-actions/ci.py`] script, which dynamically generates the specific CI
+[`src/ci/citool`] crate, which dynamically generates the specific CI
 jobs that should be executed. This script uses the [`jobs.yml`] file as an
 input, which contains a declarative configuration of all our CI jobs.
 
@@ -299,7 +299,7 @@ platform’s custom [Docker container]. This has a lot of advantages for us:
 - We can avoid reinstalling tools (like QEMU or the Android emulator) every time
   thanks to Docker image caching.
 - Users can run the same tests in the same environment locally by just running
-  `python3 src/ci/github-actions/ci.py run-local <job-name>`, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and
+  `cargo run --manifest-path src/ci/citool/Cargo.toml run-local <job-name>`, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and
   other restrictions.
 
 The docker images prefixed with `dist-` are used for building artifacts while
@@ -443,7 +443,7 @@ this:
 [GitHub Actions]: https://github.com/rust-lang/rust/actions
 [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml
 [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml
-[`src/ci/github-actions/ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py
+[`src/ci/citool`]: https://github.com/rust-lang/rust/blob/master/src/ci/citool
 [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions
 [bors]: https://github.com/bors
 [homu]: https://github.com/rust-lang/homu
diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md
index 2ca08d42130..a8a388ef90c 100644
--- a/src/doc/rustc-dev-guide/src/tests/docker.md
+++ b/src/doc/rustc-dev-guide/src/tests/docker.md
@@ -53,6 +53,15 @@ Some additional notes about using the interactive mode:
   containers. With the container name, run `docker exec -it <CONTAINER>
   /bin/bash` where `<CONTAINER>` is the container name like `4ba195e95cef`.
 
+The approach described above is a relatively low-level interface for running the Docker images
+directly. If you want to run a full CI Linux job locally with Docker, in a way that is as close to CI as possible, you can use the following command:
+
+```bash
+cargo run --manifest-path src/ci/citool/Cargo.toml run-local <job-name>
+# For example:
+cargo run --manifest-path src/ci/citool/Cargo.toml run-local dist-x86_64-linux-alt
+```
+
 [Docker]: https://www.docker.com/
 [`src/ci/docker`]: https://github.com/rust-lang/rust/tree/master/src/ci/docker
 [`src/ci/docker/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/run.sh