From b6f12d7b493b7ed38cd16fbe1432dc47e2aafc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 18 Jun 2025 17:06:38 +0200 Subject: [PATCH 01/22] Rename extended rustc tool macros --- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/build_steps/tool.rs | 28 ++++++++++++---------- src/bootstrap/src/core/builder/tests.rs | 6 ++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 119fa4237bcfe..dde4a86251fe0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -386,7 +386,7 @@ impl Step for RustAnalyzer { let stage = self.stage; let host = self.host; let compiler = builder.compiler(stage, host); - let compiler = tool::get_tool_rustc_compiler(builder, compiler); + let compiler = tool::get_tool_rustc_build_compiler(builder, compiler); // We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite, // but we do need the standard library to be present. diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index f5fa33b98f3bf..c3c14165aa1f2 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -115,7 +115,7 @@ impl Step for ToolBuild { let target_compiler = self.build_compiler; self.build_compiler = if self.mode == Mode::ToolRustc { - get_tool_rustc_compiler(builder, self.build_compiler) + get_tool_rustc_build_compiler(builder, self.build_compiler) } else { self.build_compiler }; @@ -347,7 +347,7 @@ pub fn prepare_tool_cargo( } /// Handle stage-off logic for `ToolRustc` tools when necessary. -pub(crate) fn get_tool_rustc_compiler( +pub(crate) fn get_tool_rustc_build_compiler( builder: &Builder<'_>, target_compiler: Compiler, ) -> Compiler { @@ -1302,7 +1302,9 @@ impl Step for LibcxxVersionTool { } } -macro_rules! tool_extended { +/// Creates a step that builds an extended `Mode::ToolRustc` tool +/// and installs it into the sysroot of a corresponding compiler. +macro_rules! tool_rustc_extended { ( $name:ident { path: $path:expr, @@ -1326,7 +1328,7 @@ macro_rules! tool_extended { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - should_run_tool_build_step( + should_run_extended_rustc_tool( run, $tool_name, $path, @@ -1343,7 +1345,7 @@ macro_rules! tool_extended { fn run(self, builder: &Builder<'_>) -> ToolBuildResult { let Self { compiler, target } = self; - run_tool_build_step( + build_extended_rustc_tool( builder, compiler, target, @@ -1367,7 +1369,7 @@ macro_rules! tool_extended { } } -fn should_run_tool_build_step<'a>( +fn should_run_extended_rustc_tool<'a>( run: ShouldRun<'a>, tool_name: &'static str, path: &'static str, @@ -1392,7 +1394,7 @@ fn should_run_tool_build_step<'a>( } #[expect(clippy::too_many_arguments)] // silence overeager clippy lint -fn run_tool_build_step( +fn build_extended_rustc_tool( builder: &Builder<'_>, compiler: Compiler, target: TargetSelection, @@ -1441,19 +1443,19 @@ fn run_tool_build_step( } } -tool_extended!(Cargofmt { +tool_rustc_extended!(Cargofmt { path: "src/tools/rustfmt", tool_name: "cargo-fmt", stable: true, add_bins_to_sysroot: ["cargo-fmt"] }); -tool_extended!(CargoClippy { +tool_rustc_extended!(CargoClippy { path: "src/tools/clippy", tool_name: "cargo-clippy", stable: true, add_bins_to_sysroot: ["cargo-clippy"] }); -tool_extended!(Clippy { +tool_rustc_extended!(Clippy { path: "src/tools/clippy", tool_name: "clippy-driver", stable: true, @@ -1464,7 +1466,7 @@ tool_extended!(Clippy { } } }); -tool_extended!(Miri { +tool_rustc_extended!(Miri { path: "src/tools/miri", tool_name: "miri", stable: false, @@ -1472,13 +1474,13 @@ tool_extended!(Miri { // Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri. cargo_args: &["--all-targets"], }); -tool_extended!(CargoMiri { +tool_rustc_extended!(CargoMiri { path: "src/tools/miri/cargo-miri", tool_name: "cargo-miri", stable: false, add_bins_to_sysroot: ["cargo-miri"] }); -tool_extended!(Rustfmt { +tool_rustc_extended!(Rustfmt { path: "src/tools/rustfmt", tool_name: "rustfmt", stable: true, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f012645b7ef5c..1abfba71b9c2f 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -580,12 +580,12 @@ fn test_get_tool_rustc_compiler() { let compiler = Compiler::new(2, target_triple_1); let expected = Compiler::new(1, target_triple_1); - let actual = tool::get_tool_rustc_compiler(&builder, compiler); + let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); assert_eq!(expected, actual); let compiler = Compiler::new(1, target_triple_1); let expected = Compiler::new(0, target_triple_1); - let actual = tool::get_tool_rustc_compiler(&builder, compiler); + let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); assert_eq!(expected, actual); let mut config = configure("build", &[], &[]); @@ -595,7 +595,7 @@ fn test_get_tool_rustc_compiler() { let compiler = Compiler::new(1, target_triple_1); let expected = Compiler::new(1, target_triple_1); - let actual = tool::get_tool_rustc_compiler(&builder, compiler); + let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); assert_eq!(expected, actual); } From 65c3597323878816f54d87e2a4ba4e3032349495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 09:50:40 +0200 Subject: [PATCH 02/22] Add metadata to `Cargo` and `RustAnalyzer` tools --- src/bootstrap/src/core/build_steps/tool.rs | 16 ++++++++++++++++ src/bootstrap/src/core/builder/tests.rs | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index c3c14165aa1f2..363ce57b0d368 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -886,6 +886,14 @@ impl Step for Cargo { artifact_kind: ToolArtifactKind::Binary, }) } + + fn metadata(&self) -> Option { + // FIXME: fix staging logic + Some( + StepMetadata::build("cargo", self.target) + .built_by(self.compiler.with_stage(self.compiler.stage - 1)), + ) + } } /// Represents a built LldWrapper, the `lld-wrapper` tool itself, and a directory @@ -1105,6 +1113,14 @@ impl Step for RustAnalyzer { artifact_kind: ToolArtifactKind::Binary, }) } + + fn metadata(&self) -> Option { + // FIXME: fix staging logic + Some( + StepMetadata::build("rust-analyzer", self.target) + .built_by(self.compiler.with_stage(self.compiler.stage - 1)), + ) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1abfba71b9c2f..73d9d5a6e99b4 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1101,6 +1101,8 @@ mod snapshot { [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> + [build] rustc 0 -> cargo 1 + [build] rustc 0 -> rust-analyzer 1 [build] rustc 0 -> rustfmt 1 [build] rustc 0 -> cargo-fmt 1 [build] rustc 0 -> clippy-driver 1 @@ -1291,6 +1293,8 @@ mod snapshot { [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> + [build] rustc 0 -> cargo 1 + [build] rustc 0 -> rust-analyzer 1 [build] rustc 0 -> rustfmt 1 [build] rustc 0 -> cargo-fmt 1 [build] rustc 0 -> clippy-driver 1 From 57a6c930e8fbabf58355fff9586e6d39dbf700c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 10:52:16 +0200 Subject: [PATCH 03/22] Cleanup `ensure_if_default` to not require `Option` output --- src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/tool.rs | 7 ++++--- src/bootstrap/src/core/builder/mod.rs | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 4699813abf42a..a4c9896499079 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1548,7 +1548,7 @@ impl Step for Extended { let mut built_tools = HashSet::new(); macro_rules! add_component { ($name:expr => $step:expr) => { - if let Some(tarball) = builder.ensure_if_default($step, Kind::Dist) { + if let Some(Some(tarball)) = builder.ensure_if_default($step, Kind::Dist) { tarballs.push(tarball); built_tools.insert($name); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 363ce57b0d368..9237d7c3f03a1 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1130,7 +1130,8 @@ pub struct RustAnalyzerProcMacroSrv { } impl Step for RustAnalyzerProcMacroSrv { - type Output = Option; + type Output = ToolBuildResult; + const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -1152,7 +1153,7 @@ impl Step for RustAnalyzerProcMacroSrv { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Self::Output { let tool_result = builder.ensure(ToolBuild { build_compiler: self.compiler, target: self.target, @@ -1176,7 +1177,7 @@ impl Step for RustAnalyzerProcMacroSrv { FileType::Executable, ); - Some(tool_result) + tool_result } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 96289a63785e2..9c45ad8575e9c 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1705,11 +1705,11 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. - pub(crate) fn ensure_if_default>>( + pub(crate) fn ensure_if_default>( &'a self, step: S, kind: Kind, - ) -> S::Output { + ) -> Option { let desc = StepDescription::from::(kind); let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); @@ -1721,7 +1721,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } // Only execute if it's supposed to run as default - if desc.default && should_run.is_really_default() { self.ensure(step) } else { None } + if desc.default && should_run.is_really_default() { Some(self.ensure(step)) } else { None } } /// Checks if any of the "should_run" paths is in the `Builder` paths. From 8fed3fbd4fdd08620eb1de77a6ff80ba34071d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 10:58:02 +0200 Subject: [PATCH 04/22] Add step metadata to `RustAnalyzerProcMacroSrv` --- src/bootstrap/src/core/build_steps/tool.rs | 8 ++++++++ src/bootstrap/src/core/builder/tests.rs | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 9237d7c3f03a1..bc79d3432ca7e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1179,6 +1179,14 @@ impl Step for RustAnalyzerProcMacroSrv { tool_result } + + fn metadata(&self) -> Option { + // FIXME: fix ToolRust staging logic + Some( + StepMetadata::build("rust-analyzer-proc-macro-srv", self.target) + .built_by(self.compiler.with_stage(self.compiler.stage - 1)), + ) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 73d9d5a6e99b4..319de1c5cf3d1 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1097,6 +1097,7 @@ mod snapshot { [dist] docs [doc] std 2 crates=[] [dist] mingw + [build] rustc 0 -> rust-analyzer-proc-macro-srv 1 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [dist] rustc 1 -> std 1 @@ -1289,6 +1290,7 @@ mod snapshot { [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 1 + [build] rustc 0 -> rust-analyzer-proc-macro-srv 1 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [dist] rustc 1 -> std 1 From 2f3bfff0511088bfb84e84b15273be5a3d00f2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:01:30 +0200 Subject: [PATCH 05/22] Rename `Builder::rustdoc` to `Builder::rustdoc_for_compiler` To clarify what it does. --- src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/doc.rs | 6 +++--- src/bootstrap/src/core/build_steps/perf.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 16 ++++++++++------ src/bootstrap/src/core/build_steps/tool.rs | 13 +++++++------ src/bootstrap/src/core/builder/cargo.rs | 4 ++-- src/bootstrap/src/core/builder/mod.rs | 9 ++++++--- src/bootstrap/src/core/builder/tests.rs | 4 ++-- 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index a4c9896499079..f1f112896a97a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -425,7 +425,7 @@ impl Step for Rustc { .as_ref() .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { - let rustdoc = builder.rustdoc(compiler); + let rustdoc = builder.rustdoc_for_compiler(compiler); builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 37418f640aca6..4d8505fb2b95f 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -149,7 +149,7 @@ impl Step for RustbookSrc

{ let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); if let Some(compiler) = self.rustdoc_compiler { - let mut rustdoc = builder.rustdoc(compiler); + let mut rustdoc = builder.rustdoc_for_compiler(compiler); rustdoc.pop(); let old_path = env::var_os("PATH").unwrap_or_default(); let new_path = @@ -365,7 +365,7 @@ impl Step for Standalone { } let html = out.join(filename).with_extension("html"); - let rustdoc = builder.rustdoc(compiler); + let rustdoc = builder.rustdoc_for_compiler(compiler); if up_to_date(&path, &html) && up_to_date(&footer, &html) && up_to_date(&favicon, &html) @@ -463,7 +463,7 @@ impl Step for Releases { let html = out.join("releases.html"); let tmppath = out.join("releases.md"); let inpath = builder.src.join("RELEASES.md"); - let rustdoc = builder.rustdoc(compiler); + let rustdoc = builder.rustdoc_for_compiler(compiler); if !up_to_date(&inpath, &html) || !up_to_date(&footer, &html) || !up_to_date(&favicon, &html) diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 4d61b38c876d0..108b7f90c149e 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -157,7 +157,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); if let Some(opts) = args.cmd.shared_opts() && opts.profiles.contains(&Profile::Doc) { - builder.ensure(Rustdoc { compiler }); + builder.ensure(Rustdoc { target_compiler: compiler }); } let sysroot = builder.ensure(Sysroot::new(compiler)); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index dde4a86251fe0..1884dec89f932 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -263,7 +263,7 @@ impl Step for Cargotest { .arg(&out_dir) .args(builder.config.test_args()) .env("RUSTC", builder.rustc(compiler)) - .env("RUSTDOC", builder.rustdoc(compiler)); + .env("RUSTDOC", builder.rustdoc_for_compiler(compiler)); add_rustdoc_cargo_linker_args( &mut cmd, builder, @@ -878,7 +878,7 @@ impl Step for RustdocTheme { .env("RUSTC_SYSROOT", builder.sysroot(self.compiler)) .env("RUSTDOC_LIBDIR", builder.sysroot_target_libdir(self.compiler, self.compiler.host)) .env("CFG_RELEASE_CHANNEL", &builder.config.channel) - .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) + .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.compiler)) .env("RUSTC_BOOTSTRAP", "1"); cmd.args(linker_args(builder, self.compiler.host, LldThreads::No, self.compiler.stage)); @@ -1037,7 +1037,11 @@ impl Step for RustdocGUI { let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); let out_dir = builder.test_out(self.target).join("rustdoc-gui"); - build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler)); + build_stamp::clear_if_dirty( + builder, + &out_dir, + &builder.rustdoc_for_compiler(self.compiler), + ); if let Some(src) = builder.config.src.to_str() { cmd.arg("--rust-src").arg(src); @@ -1053,7 +1057,7 @@ impl Step for RustdocGUI { cmd.arg("--jobs").arg(builder.jobs().to_string()); - cmd.env("RUSTDOC", builder.rustdoc(self.compiler)) + cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.compiler)) .env("RUSTC", builder.rustc(self.compiler)); add_rustdoc_cargo_linker_args( @@ -1742,7 +1746,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the || mode == "rustdoc-json" || suite == "coverage-run-rustdoc" { - cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler)); + cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(compiler)); } if mode == "rustdoc-json" { @@ -2258,7 +2262,7 @@ impl BookTest { // mdbook just executes a binary named "rustdoc", so we need to update // PATH so that it points to our rustdoc. - let mut rustdoc_path = builder.rustdoc(compiler); + let mut rustdoc_path = builder.rustdoc_for_compiler(compiler); rustdoc_path.pop(); let old_path = env::var_os("PATH").unwrap_or_default(); let new_path = env::join_paths(iter::once(rustdoc_path).chain(env::split_paths(&old_path))) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index bc79d3432ca7e..77d15b1316535 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -723,7 +723,7 @@ impl Step for RemoteTestServer { pub struct Rustdoc { /// This should only ever be 0 or 2. /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here. - pub compiler: Compiler, + pub target_compiler: Compiler, } impl Step for Rustdoc { @@ -736,12 +736,13 @@ impl Step for Rustdoc { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.target) }); + run.builder.ensure(Rustdoc { + target_compiler: run.builder.compiler(run.builder.top_stage, run.target), + }); } fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - let target_compiler = self.compiler; + let target_compiler = self.target_compiler; let target = target_compiler.host; if target_compiler.stage == 0 { @@ -838,11 +839,11 @@ impl Step for Rustdoc { fn metadata(&self) -> Option { Some( - StepMetadata::build("rustdoc", self.compiler.host) + StepMetadata::build("rustdoc", self.target_compiler.host) // rustdoc is ToolRustc, so stage N rustdoc is built by stage N-1 rustc // FIXME: make this stage deduction automatic somehow // FIXME: log the compiler that actually built ToolRustc steps - .stage(self.compiler.stage.saturating_sub(1)), + .stage(self.target_compiler.stage.saturating_sub(1)), ) } } diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 6b3236ef47ef6..1d0c9da694f01 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -508,7 +508,7 @@ impl Builder<'_> { } _ => panic!("doc mode {mode:?} not expected"), }; - let rustdoc = self.rustdoc(compiler); + let rustdoc = self.rustdoc_for_compiler(compiler); build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } @@ -822,7 +822,7 @@ impl Builder<'_> { } let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler), + Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler), _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), }; diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 9c45ad8575e9c..1a28c170048e1 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1535,8 +1535,11 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s .map(|entry| entry.path()) } - pub fn rustdoc(&self, compiler: Compiler) -> PathBuf { - self.ensure(tool::Rustdoc { compiler }).tool_path + /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`. + /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to + /// `target_compiler`. + pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { + self.ensure(tool::Rustdoc { target_compiler }).tool_path } pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { @@ -1596,7 +1599,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s // equivalently to rustc. .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)) .env("CFG_RELEASE_CHANNEL", &self.config.channel) - .env("RUSTDOC_REAL", self.rustdoc(compiler)) + .env("RUSTDOC_REAL", self.rustdoc_for_compiler(compiler)) .env("RUSTC_BOOTSTRAP", "1"); cmd.arg("-Wrustdoc::invalid_codeblock_attributes"); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 319de1c5cf3d1..d6f61f87863f6 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -284,7 +284,7 @@ mod defaults { // not the one it was built by. assert_eq!( first(cache.all::()), - &[tool::Rustdoc { compiler: Compiler::new(1, a) },] + &[tool::Rustdoc { target_compiler: Compiler::new(1, a) },] ); } } @@ -341,7 +341,7 @@ mod dist { // stage minus 1 if --stage is not 0. Very confusing! assert_eq!( first(builder.cache.all::()), - &[tool::Rustdoc { compiler: Compiler::new(2, a) },] + &[tool::Rustdoc { target_compiler: Compiler::new(2, a) },] ); } } From 6d562b2c747c61a15de50c056e809c5202dff5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 12:03:55 +0200 Subject: [PATCH 06/22] Implement `RustcPrivateCompilers` to unify building of `rustc_private` tools --- src/bootstrap/src/core/build_steps/compile.rs | 1 + src/bootstrap/src/core/build_steps/dist.rs | 83 +++--- src/bootstrap/src/core/build_steps/install.rs | 8 +- src/bootstrap/src/core/build_steps/run.rs | 14 +- src/bootstrap/src/core/build_steps/test.rs | 94 ++++--- src/bootstrap/src/core/build_steps/tool.rs | 264 ++++++++++-------- src/bootstrap/src/core/builder/mod.rs | 23 +- src/bootstrap/src/core/builder/tests.rs | 62 ++-- src/bootstrap/src/lib.rs | 7 +- 9 files changed, 287 insertions(+), 269 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 59541bf12def0..488217549d9b2 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1635,6 +1635,7 @@ impl Step for CodegenBackend { let target = self.target; let backend = self.backend; + // FIXME: migrate to RustcPrivateCompilers builder.ensure(Rustc::new(compiler, target)); if builder.config.keep_stage.contains(&compiler.stage) { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f1f112896a97a..2d504cbd55af6 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -20,7 +20,7 @@ use object::read::archive::ArchiveFile; use tracing::instrument; use crate::core::build_steps::doc::DocumentationFormat; -use crate::core::build_steps::tool::{self, Tool}; +use crate::core::build_steps::tool::{self, RustcPrivateCompilers, Tool}; use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor}; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata}; @@ -431,13 +431,14 @@ impl Step for Rustc { let ra_proc_macro_srv_compiler = builder.compiler_for(compiler.stage, builder.config.host_target, compiler.host); - builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host)); + let compilers = RustcPrivateCompilers::from_build_compiler( + builder, + ra_proc_macro_srv_compiler, + compiler.host, + ); if let Some(ra_proc_macro_srv) = builder.ensure_if_default( - tool::RustAnalyzerProcMacroSrv { - compiler: ra_proc_macro_srv_compiler, - target: compiler.host, - }, + tool::RustAnalyzerProcMacroSrv::from_compilers(compilers), builder.kind, ) { let dst = image.join("libexec"); @@ -1228,7 +1229,7 @@ impl Step for Cargo { #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct RustAnalyzer { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1244,7 +1245,7 @@ impl Step for RustAnalyzer { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustAnalyzer { - compiler: run.builder.compiler_for( + build_compiler: run.builder.compiler_for( run.builder.top_stage, run.builder.config.host_target, run.target, @@ -1254,12 +1255,11 @@ impl Step for RustAnalyzer { } fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; let target = self.target; + let compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); - builder.ensure(compile::Rustc::new(compiler, target)); - - let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target }); + let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(compilers)); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); tarball.set_overlay(OverlayKind::RustAnalyzer); @@ -1270,9 +1270,9 @@ impl Step for RustAnalyzer { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Clippy { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1288,7 +1288,7 @@ impl Step for Clippy { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Clippy { - compiler: run.builder.compiler_for( + build_compiler: run.builder.compiler_for( run.builder.top_stage, run.builder.config.host_target, run.target, @@ -1298,16 +1298,15 @@ impl Step for Clippy { } fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; let target = self.target; - - builder.ensure(compile::Rustc::new(compiler, target)); + let compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target); // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. - let clippy = builder.ensure(tool::Clippy { compiler, target }); - let cargoclippy = builder.ensure(tool::CargoClippy { compiler, target }); + let clippy = builder.ensure(tool::Clippy::from_compilers(compilers)); + let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(compilers)); let mut tarball = Tarball::new(builder, "clippy", &target.triple); tarball.set_overlay(OverlayKind::Clippy); @@ -1319,9 +1318,9 @@ impl Step for Clippy { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Miri { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1337,7 +1336,7 @@ impl Step for Miri { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Miri { - compiler: run.builder.compiler_for( + build_compiler: run.builder.compiler_for( run.builder.top_stage, run.builder.config.host_target, run.target, @@ -1354,15 +1353,12 @@ impl Step for Miri { return None; } - let compiler = self.compiler; - let target = self.target; - - builder.ensure(compile::Rustc::new(compiler, target)); - - let miri = builder.ensure(tool::Miri { compiler, target }); - let cargomiri = builder.ensure(tool::CargoMiri { compiler, target }); + let compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); + let miri = builder.ensure(tool::Miri::from_compilers(compilers)); + let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(compilers)); - let mut tarball = Tarball::new(builder, "miri", &target.triple); + let mut tarball = Tarball::new(builder, "miri", &self.target.triple); tarball.set_overlay(OverlayKind::Miri); tarball.is_preview(true); tarball.add_file(&miri.tool_path, "bin", FileType::Executable); @@ -1466,9 +1462,9 @@ impl Step for CodegenBackend { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustfmt { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1484,7 +1480,7 @@ impl Step for Rustfmt { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustfmt { - compiler: run.builder.compiler_for( + build_compiler: run.builder.compiler_for( run.builder.top_stage, run.builder.config.host_target, run.target, @@ -1494,14 +1490,13 @@ impl Step for Rustfmt { } fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; - let target = self.target; + let compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target); - builder.ensure(compile::Rustc::new(compiler, target)); + let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(compilers)); + let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(compilers)); - let rustfmt = builder.ensure(tool::Rustfmt { compiler, target }); - let cargofmt = builder.ensure(tool::Cargofmt { compiler, target }); - let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); + let mut tarball = Tarball::new(builder, "rustfmt", &self.target.triple); tarball.set_overlay(OverlayKind::Rustfmt); tarball.is_preview(true); tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable); @@ -1569,11 +1564,11 @@ impl Step for Extended { add_component!("rust-docs" => Docs { host: target }); add_component!("rust-json-docs" => JsonDocs { host: target }); add_component!("cargo" => Cargo { compiler, target }); - add_component!("rustfmt" => Rustfmt { compiler, target }); - add_component!("rust-analyzer" => RustAnalyzer { compiler, target }); + add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target }); + add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target }); add_component!("llvm-components" => LlvmTools { target }); - add_component!("clippy" => Clippy { compiler, target }); - add_component!("miri" => Miri { compiler, target }); + add_component!("clippy" => Clippy { build_compiler: compiler, target }); + add_component!("miri" => Miri { build_compiler: compiler, target }); add_component!("analysis" => Analysis { compiler, target }); add_component!("rustc-codegen-cranelift" => CodegenBackend { compiler: builder.compiler(stage, target), diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 4513a138e192a..2427b0191294e 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -221,7 +221,7 @@ install!((self, builder, _config), }; RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), only_hosts: true, { if let Some(tarball) = - builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target }) + builder.ensure(dist::RustAnalyzer { build_compiler: self.compiler, target: self.target }) { install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball); } else { @@ -232,12 +232,12 @@ install!((self, builder, _config), }; Clippy, alias = "clippy", Self::should_build(_config), only_hosts: true, { let tarball = builder - .ensure(dist::Clippy { compiler: self.compiler, target: self.target }) + .ensure(dist::Clippy { build_compiler: self.compiler, target: self.target }) .expect("missing clippy"); install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball); }; Miri, alias = "miri", Self::should_build(_config), only_hosts: true, { - if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) { + if let Some(tarball) = builder.ensure(dist::Miri { build_compiler: self.compiler, target: self.target }) { install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball); } else { // Miri is only available on nightly @@ -257,7 +257,7 @@ install!((self, builder, _config), }; Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, { if let Some(tarball) = builder.ensure(dist::Rustfmt { - compiler: self.compiler, + build_compiler: self.compiler, target: self.target }) { install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball); diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index b2293fdd9b523..984744b5678f5 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -9,7 +9,7 @@ use clap_complete::{Generator, shells}; use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; -use crate::core::build_steps::tool::{self, SourceType, Tool}; +use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool}; use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; @@ -135,13 +135,13 @@ impl Step for Miri { } // This compiler runs on the host, we'll just use it for the target. - let target_compiler = builder.compiler(stage, target); - let miri_build = builder.ensure(tool::Miri { compiler: target_compiler, target }); - // Rustc tools are off by one stage, so use the build compiler to run miri. + let compilers = RustcPrivateCompilers::new(builder, stage, target); + let miri_build = builder.ensure(tool::Miri::from_compilers(compilers)); let host_compiler = miri_build.build_compiler; // Get a target sysroot for Miri. - let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); + let miri_sysroot = + test::Miri::build_miri_sysroot(builder, compilers.link_compiler(), target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. @@ -465,8 +465,8 @@ impl Step for Rustfmt { std::process::exit(1); } - let compiler = builder.compiler(stage, host); - let rustfmt_build = builder.ensure(tool::Rustfmt { compiler, target: host }); + let compilers = RustcPrivateCompilers::new(builder, stage, host); + let rustfmt_build = builder.ensure(tool::Rustfmt::from_compilers(compilers)); let mut rustfmt = tool::prepare_tool_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 1884dec89f932..f725e458c4a56 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -17,7 +17,9 @@ use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::get_completion_paths; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; -use crate::core::build_steps::tool::{self, COMPILETEST_ALLOW_FEATURES, SourceType, Tool}; +use crate::core::build_steps::tool::{ + self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, Tool, +}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; use crate::core::builder::{ @@ -364,8 +366,7 @@ impl Step for Cargo { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RustAnalyzer { - stage: u32, - host: TargetSelection, + compilers: RustcPrivateCompilers, } impl Step for RustAnalyzer { @@ -378,19 +379,18 @@ impl Step for RustAnalyzer { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Self { stage: run.builder.top_stage, host: run.target }); + run.builder.ensure(Self { + compilers: RustcPrivateCompilers::new( + run.builder, + run.builder.top_stage, + run.builder.host_target, + ), + }); } /// Runs `cargo test` for rust-analyzer fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; - let compiler = builder.compiler(stage, host); - let compiler = tool::get_tool_rustc_build_compiler(builder, compiler); - - // We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite, - // but we do need the standard library to be present. - builder.ensure(compile::Rustc::new(compiler, host)); + let host = self.compilers.target(); let workspace_path = "src/tools/rust-analyzer"; // until the whole RA test suite runs on `i686`, we only run @@ -398,7 +398,7 @@ impl Step for RustAnalyzer { let crate_path = "src/tools/rust-analyzer/crates/proc-macro-srv"; let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + self.compilers.build_compiler(), Mode::ToolRustc, host, Kind::Test, @@ -425,8 +425,7 @@ impl Step for RustAnalyzer { /// Runs `cargo test` for rustfmt. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { - stage: u32, - host: TargetSelection, + compilers: RustcPrivateCompilers, } impl Step for Rustfmt { @@ -438,36 +437,39 @@ impl Step for Rustfmt { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Rustfmt { stage: run.builder.top_stage, host: run.target }); + run.builder.ensure(Rustfmt { + compilers: RustcPrivateCompilers::new( + run.builder, + run.builder.top_stage, + run.builder.host_target, + ), + }); } /// Runs `cargo test` for rustfmt. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; - let compiler = builder.compiler(stage, host); - - let tool_result = builder.ensure(tool::Rustfmt { compiler, target: self.host }); - let compiler = tool_result.build_compiler; + let tool_result = builder.ensure(tool::Rustfmt::from_compilers(self.compilers)); + let build_compiler = tool_result.build_compiler; + let target = self.compilers.target(); let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolRustc, - host, + target, Kind::Test, "src/tools/rustfmt", SourceType::InTree, &[], ); - let dir = testdir(builder, compiler.host); + let dir = testdir(builder, target); t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", host, builder); + run_cargo_test(cargo, &[], &[], "rustfmt", target, builder); } } @@ -542,12 +544,14 @@ impl Step for Miri { } // This compiler runs on the host, we'll just use it for the target. - let target_compiler = builder.compiler(stage, host); + let compilers = RustcPrivateCompilers::new(builder, stage, host); // Build our tools. - let miri = builder.ensure(tool::Miri { compiler: target_compiler, target: host }); + let miri = builder.ensure(tool::Miri::from_compilers(compilers)); // the ui tests also assume cargo-miri has been built - builder.ensure(tool::CargoMiri { compiler: target_compiler, target: host }); + builder.ensure(tool::CargoMiri::from_compilers(compilers)); + + let target_compiler = compilers.link_compiler(); // We also need sysroots, for Miri and for the host (the latter for build scripts). // This is for the tests so everything is done with the target compiler. @@ -756,7 +760,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Clippy { - host: TargetSelection, + compilers: RustcPrivateCompilers, } impl Step for Clippy { @@ -769,23 +773,30 @@ impl Step for Clippy { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Clippy { host: run.target }); + run.builder.ensure(Clippy { + compilers: RustcPrivateCompilers::new( + run.builder, + run.builder.top_stage, + run.builder.host_target, + ), + }); } /// Runs `cargo test` for clippy. fn run(self, builder: &Builder<'_>) { - let stage = builder.top_stage; - let host = self.host; + let host = self.compilers.target(); + // We need to carefully distinguish the compiler that builds clippy, and the compiler // that is linked into the clippy being tested. `target_compiler` is the latter, // and it must also be used by clippy's test runner to build tests and their dependencies. - let target_compiler = builder.compiler(stage, host); + let compilers = self.compilers; + let target_compiler = compilers.link_compiler(); - let tool_result = builder.ensure(tool::Clippy { compiler: target_compiler, target: host }); - let tool_compiler = tool_result.build_compiler; + let tool_result = builder.ensure(tool::Clippy::from_compilers(compilers)); + let build_compiler = tool_result.build_compiler; let mut cargo = tool::prepare_tool_cargo( builder, - tool_compiler, + build_compiler, Mode::ToolRustc, host, Kind::Test, @@ -794,9 +805,10 @@ impl Step for Clippy { &[], ); - cargo.env("RUSTC_TEST_SUITE", builder.rustc(tool_compiler)); - cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(tool_compiler)); - let host_libs = builder.stage_out(tool_compiler, Mode::ToolRustc).join(builder.cargo_dir()); + cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler)); + cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler)); + let host_libs = + builder.stage_out(build_compiler, Mode::ToolRustc).join(builder.cargo_dir()); cargo.env("HOST_LIBS", host_libs); // Build the standard library that the tests can use. @@ -826,7 +838,7 @@ impl Step for Clippy { let cargo = prepare_cargo_test(cargo, &[], &[], host, builder); let _guard = - builder.msg_sysroot_tool(Kind::Test, tool_compiler.stage, "clippy", host, host); + builder.msg_sysroot_tool(Kind::Test, build_compiler.stage, "clippy", host, host); // Clippy reports errors if it blessed the outputs if cargo.allow_failure().run(builder) { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 77d15b1316535..b3e054a9d8bab 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -90,11 +90,8 @@ impl Builder<'_> { pub struct ToolBuildResult { /// Artifact path of the corresponding tool that was built. pub tool_path: PathBuf, - /// Compiler used to build the tool. For non-`ToolRustc` tools this is equal to `target_compiler`. - /// For `ToolRustc` this is one stage before of the `target_compiler`. + /// Compiler used to build the tool. pub build_compiler: Compiler, - /// Target compiler passed to `Step`. - pub target_compiler: Compiler, } impl Step for ToolBuild { @@ -108,26 +105,13 @@ impl Step for ToolBuild { /// /// This will build the specified tool with the specified `host` compiler in /// `stage` into the normal cargo output directory. - fn run(mut self, builder: &Builder<'_>) -> ToolBuildResult { + fn run(self, builder: &Builder<'_>) -> ToolBuildResult { let target = self.target; let mut tool = self.tool; let path = self.path; - let target_compiler = self.build_compiler; - self.build_compiler = if self.mode == Mode::ToolRustc { - get_tool_rustc_build_compiler(builder, self.build_compiler) - } else { - self.build_compiler - }; - match self.mode { - Mode::ToolRustc => { - // If compiler was forced, its artifacts should have been prepared earlier. - if !self.build_compiler.is_forced_compiler() { - builder.std(self.build_compiler, self.build_compiler.host); - builder.ensure(compile::Rustc::new(self.build_compiler, target)); - } - } + Mode::ToolRustc => {} Mode::ToolStd => { // If compiler was forced, its artifacts should have been prepared earlier. if !self.build_compiler.is_forced_compiler() { @@ -184,8 +168,7 @@ impl Step for ToolBuild { Kind::Build, self.mode, self.tool, - // A stage N tool is built with the stage N-1 compiler. - self.build_compiler.stage + 1, + self.build_compiler.stage, &self.build_compiler.host, &self.target, ); @@ -216,7 +199,7 @@ impl Step for ToolBuild { .join(format!("lib{tool}.rlib")), }; - ToolBuildResult { tool_path, build_compiler: self.build_compiler, target_compiler } + ToolBuildResult { tool_path, build_compiler: self.build_compiler } } } } @@ -346,27 +329,6 @@ pub fn prepare_tool_cargo( cargo } -/// Handle stage-off logic for `ToolRustc` tools when necessary. -pub(crate) fn get_tool_rustc_build_compiler( - builder: &Builder<'_>, - target_compiler: Compiler, -) -> Compiler { - if target_compiler.is_forced_compiler() { - return target_compiler; - } - - if builder.download_rustc() && target_compiler.stage == 1 { - // We shouldn't drop to stage0 compiler when using CI rustc. - return builder.compiler(1, builder.config.host_target); - } - - // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise - // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage - // compilers, which isn't what we want. Rustc tools should be linked in the same way as the - // compiler it's paired with, so it must be built with the previous stage compiler. - builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.host_target) -} - /// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it. /// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool. pub enum ToolTargetBuildMode { @@ -753,7 +715,6 @@ impl Step for Rustdoc { return ToolBuildResult { tool_path: builder.initial_rustdoc.clone(), build_compiler: target_compiler, - target_compiler, }; } @@ -785,11 +746,7 @@ impl Step for Rustdoc { let bin_rustdoc = bin_rustdoc(); builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable); - return ToolBuildResult { - tool_path: bin_rustdoc, - build_compiler: target_compiler, - target_compiler, - }; + return ToolBuildResult { tool_path: bin_rustdoc, build_compiler: target_compiler }; } } @@ -805,23 +762,23 @@ impl Step for Rustdoc { extra_features.push("jemalloc".to_string()); } - let ToolBuildResult { tool_path, build_compiler, target_compiler } = - builder.ensure(ToolBuild { - build_compiler: target_compiler, - target, - // Cargo adds a number of paths to the dylib search path on windows, which results in - // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" - // rustdoc a different name. - tool: "rustdoc_tool_binary", - mode: Mode::ToolRustc, - path: "src/tools/rustdoc", - source_type: SourceType::InTree, - extra_features, - allow_features: "", - cargo_args: Vec::new(), - artifact_kind: ToolArtifactKind::Binary, - }); + let ToolBuildResult { tool_path, build_compiler } = builder.ensure(ToolBuild { + build_compiler: target_compiler, + target, + // Cargo adds a number of paths to the dylib search path on windows, which results in + // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" + // rustdoc a different name. + tool: "rustdoc_tool_binary", + mode: Mode::ToolRustc, + path: "src/tools/rustdoc", + source_type: SourceType::InTree, + extra_features, + allow_features: "", + cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, + }); + // FIXME: handle the build/target compiler split here somehow // don't create a stage0-sysroot/bin directory. if target_compiler.stage > 0 { if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None { @@ -831,9 +788,9 @@ impl Step for Rustdoc { } let bin_rustdoc = bin_rustdoc(); builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable); - ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler } + ToolBuildResult { tool_path: bin_rustdoc, build_compiler } } else { - ToolBuildResult { tool_path, build_compiler, target_compiler } + ToolBuildResult { tool_path, build_compiler } } } @@ -1075,8 +1032,13 @@ impl Step for WasmComponentLd { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustAnalyzer { - pub compiler: Compiler, - pub target: TargetSelection, + compilers: RustcPrivateCompilers, +} + +impl RustAnalyzer { + pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self { + Self { compilers } + } } impl RustAnalyzer { @@ -1095,15 +1057,16 @@ impl Step for RustAnalyzer { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustAnalyzer { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), - target: run.target, + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), }); } fn run(self, builder: &Builder<'_>) -> ToolBuildResult { + let build_compiler = self.compilers.build_compiler; + let target = self.compilers.target(); builder.ensure(ToolBuild { - build_compiler: self.compiler, - target: self.target, + build_compiler, + target, tool: "rust-analyzer", mode: Mode::ToolRustc, path: "src/tools/rust-analyzer", @@ -1116,18 +1079,22 @@ impl Step for RustAnalyzer { } fn metadata(&self) -> Option { - // FIXME: fix staging logic Some( - StepMetadata::build("rust-analyzer", self.target) - .built_by(self.compiler.with_stage(self.compiler.stage - 1)), + StepMetadata::build("rust-analyzer", self.compilers.target()) + .built_by(self.compilers.build_compiler), ) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustAnalyzerProcMacroSrv { - pub compiler: Compiler, - pub target: TargetSelection, + compilers: RustcPrivateCompilers, +} + +impl RustAnalyzerProcMacroSrv { + pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self { + Self { compilers } + } } impl Step for RustAnalyzerProcMacroSrv { @@ -1149,15 +1116,14 @@ impl Step for RustAnalyzerProcMacroSrv { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustAnalyzerProcMacroSrv { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), - target: run.target, + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), }); } fn run(self, builder: &Builder<'_>) -> Self::Output { let tool_result = builder.ensure(ToolBuild { - build_compiler: self.compiler, - target: self.target, + build_compiler: self.compilers.build_compiler, + target: self.compilers.target(), tool: "rust-analyzer-proc-macro-srv", mode: Mode::ToolRustc, path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli", @@ -1170,7 +1136,7 @@ impl Step for RustAnalyzerProcMacroSrv { // Copy `rust-analyzer-proc-macro-srv` to `/libexec/` // so that r-a can use it. - let libexec_path = builder.sysroot(self.compiler).join("libexec"); + let libexec_path = builder.sysroot(self.compilers.link_compiler).join("libexec"); t!(fs::create_dir_all(&libexec_path)); builder.copy_link( &tool_result.tool_path, @@ -1182,10 +1148,9 @@ impl Step for RustAnalyzerProcMacroSrv { } fn metadata(&self) -> Option { - // FIXME: fix ToolRust staging logic Some( - StepMetadata::build("rust-analyzer-proc-macro-srv", self.target) - .built_by(self.compiler.with_stage(self.compiler.stage - 1)), + StepMetadata::build("rust-analyzer-proc-macro-srv", self.compilers.target()) + .built_by(self.compilers.build_compiler), ) } } @@ -1328,6 +1293,70 @@ impl Step for LibcxxVersionTool { } } +/// Represents which compilers are involved in the compilation of a tool +/// that depends on compiler internals (`rustc_private`). +/// Their compilation looks like this: +/// +/// - `build_compiler` (stage N-1) builds `link_compiler` (stage N) to produce .rlibs +/// - These .rlibs are copied into the sysroot of `build_compiler` +/// - `build_compiler` (stage N-1) builds `` (stage N) +/// - `` links to .rlibs from `link_compiler` +/// +/// Eventually, this could also be used for .rmetas and check builds, but so far we only deal with +/// normal builds here. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct RustcPrivateCompilers { + /// Compiler that builds the tool and that builds `link_compiler`. + build_compiler: Compiler, + /// Compiler to which .rlib artifacts the tool links to. + /// The host target of this compiler corresponds to the target of the tool. + link_compiler: Compiler, +} + +impl RustcPrivateCompilers { + /// Create compilers for a `rustc_private` tool with the given `stage` and for the given + /// `target`. + pub fn new(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { + assert!(stage > 0); + + let build_compiler = if builder.download_rustc() && stage == 1 { + // We shouldn't drop to stage0 compiler when using CI rustc. + builder.compiler(1, builder.config.host_target) + } else { + builder.compiler(stage - 1, builder.config.host_target) + }; + + // This is the compiler we'll link to + // FIXME: make 100% sure that `link_compiler` was indeed built with `build_compiler`... + let link_compiler = builder.compiler(stage, target); + + Self { build_compiler, link_compiler } + } + + /// Create rustc tool compilers from the build compiler. + pub fn from_build_compiler( + builder: &Builder<'_>, + build_compiler: Compiler, + target: TargetSelection, + ) -> Self { + let link_compiler = builder.compiler(build_compiler.stage + 1, target); + Self { build_compiler, link_compiler } + } + + pub fn build_compiler(&self) -> Compiler { + self.build_compiler + } + + pub fn link_compiler(&self) -> Compiler { + self.link_compiler + } + + /// Target of the tool being compiled + pub fn target(&self) -> TargetSelection { + self.link_compiler.host + } +} + /// Creates a step that builds an extended `Mode::ToolRustc` tool /// and installs it into the sysroot of a corresponding compiler. macro_rules! tool_rustc_extended { @@ -1344,8 +1373,15 @@ macro_rules! tool_rustc_extended { ) => { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, + compilers: RustcPrivateCompilers, + } + + impl $name { + pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self { + Self { + compilers, + } + } } impl Step for $name { @@ -1364,17 +1400,15 @@ macro_rules! tool_rustc_extended { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), - target: run.target, + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), }); } fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - let Self { compiler, target } = self; + let Self { compilers } = self; build_extended_rustc_tool( builder, - compiler, - target, + compilers, $tool_name, $path, None $( .or(Some(&$add_bins_to_sysroot)) )?, @@ -1384,11 +1418,9 @@ macro_rules! tool_rustc_extended { } fn metadata(&self) -> Option { - // FIXME: refactor extended tool steps to make the build_compiler explicit, - // it is offset by one now for rustc tools Some( - StepMetadata::build($tool_name, self.target) - .built_by(self.compiler.with_stage(self.compiler.stage.saturating_sub(1))) + StepMetadata::build($tool_name, self.compilers.target()) + .built_by(self.compilers.build_compiler) ) } } @@ -1422,36 +1454,36 @@ fn should_run_extended_rustc_tool<'a>( #[expect(clippy::too_many_arguments)] // silence overeager clippy lint fn build_extended_rustc_tool( builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, + compilers: RustcPrivateCompilers, tool_name: &'static str, path: &'static str, add_bins_to_sysroot: Option<&[&str]>, add_features: Option, TargetSelection, &mut Vec)>, cargo_args: Option<&[&'static str]>, ) -> ToolBuildResult { + let target = compilers.target(); let mut extra_features = Vec::new(); if let Some(func) = add_features { func(builder, target, &mut extra_features); } - let ToolBuildResult { tool_path, build_compiler, target_compiler } = - builder.ensure(ToolBuild { - build_compiler: compiler, - target, - tool: tool_name, - mode: Mode::ToolRustc, - path, - extra_features, - source_type: SourceType::InTree, - allow_features: "", - cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(), - artifact_kind: ToolArtifactKind::Binary, - }); - + let build_compiler = compilers.build_compiler; + let ToolBuildResult { tool_path, .. } = builder.ensure(ToolBuild { + build_compiler, + target, + tool: tool_name, + mode: Mode::ToolRustc, + path, + extra_features, + source_type: SourceType::InTree, + allow_features: "", + cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(), + artifact_kind: ToolArtifactKind::Binary, + }); + + let target_compiler = compilers.link_compiler; if let Some(add_bins_to_sysroot) = add_bins_to_sysroot && !add_bins_to_sysroot.is_empty() - && target_compiler.stage > 0 { let bindir = builder.sysroot(target_compiler).join("bin"); t!(fs::create_dir_all(&bindir)); @@ -1463,9 +1495,9 @@ fn build_extended_rustc_tool( // Return a path into the bin dir. let path = bindir.join(exe(tool_name, target_compiler.host)); - ToolBuildResult { tool_path: path, build_compiler, target_compiler } + ToolBuildResult { tool_path: path, build_compiler } } else { - ToolBuildResult { tool_path, build_compiler, target_compiler } + ToolBuildResult { tool_path, build_compiler } } } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 1a28c170048e1..b30c460d5f1b7 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -16,6 +16,7 @@ use tracing::instrument; pub use self::cargo::{Cargo, cargo_profile_var}; pub use crate::Compiler; use crate::core::build_steps::compile::{Std, StdLink}; +use crate::core::build_steps::tool::RustcPrivateCompilers; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, }; @@ -1555,10 +1556,13 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s return cmd; } - let _ = - self.ensure(tool::Clippy { compiler: run_compiler, target: self.build.host_target }); - let cargo_clippy = self - .ensure(tool::CargoClippy { compiler: run_compiler, target: self.build.host_target }); + // FIXME: double check that `run_compiler`'s stage is what we want to use + let compilers = + RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); + assert_eq!(run_compiler, compilers.link_compiler()); + + let _ = self.ensure(tool::Clippy::from_compilers(compilers)); + let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers)); let mut dylib_path = helpers::dylib_path(); dylib_path.insert(0, self.sysroot(run_compiler).join("lib")); @@ -1570,11 +1574,14 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + + let compilers = + RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); + assert_eq!(run_compiler, compilers.link_compiler()); + // Prepare the tools - let miri = - self.ensure(tool::Miri { compiler: run_compiler, target: self.build.host_target }); - let cargo_miri = - self.ensure(tool::CargoMiri { compiler: run_compiler, target: self.build.host_target }); + let miri = self.ensure(tool::Miri::from_compilers(compilers)); + let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers)); // Invoke cargo-miri, make sure it can find miri and cargo. let mut cmd = command(cargo_miri.tool_path); cmd.env("MIRI", &miri.tool_path); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d6f61f87863f6..77832c5dc3124 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -569,36 +569,6 @@ fn test_is_builder_target() { } } -#[test] -fn test_get_tool_rustc_compiler() { - let mut config = configure("build", &[], &[]); - config.download_rustc_commit = None; - let build = Build::new(config); - let builder = Builder::new(&build); - - let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); - - let compiler = Compiler::new(2, target_triple_1); - let expected = Compiler::new(1, target_triple_1); - let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); - assert_eq!(expected, actual); - - let compiler = Compiler::new(1, target_triple_1); - let expected = Compiler::new(0, target_triple_1); - let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); - assert_eq!(expected, actual); - - let mut config = configure("build", &[], &[]); - config.download_rustc_commit = Some("".to_owned()); - let build = Build::new(config); - let builder = Builder::new(&build); - - let compiler = Compiler::new(1, target_triple_1); - let expected = Compiler::new(1, target_triple_1); - let actual = tool::get_tool_rustc_build_compiler(&builder, compiler); - assert_eq!(expected, actual); -} - /// When bootstrap detects a step dependency cycle (which is a bug), its panic /// message should show the actual steps on the stack, not just several copies /// of `Any { .. }`. @@ -1097,19 +1067,19 @@ mod snapshot { [dist] docs [doc] std 2 crates=[] [dist] mingw - [build] rustc 0 -> rust-analyzer-proc-macro-srv 1 + [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> [build] rustc 0 -> cargo 1 - [build] rustc 0 -> rust-analyzer 1 - [build] rustc 0 -> rustfmt 1 - [build] rustc 0 -> cargo-fmt 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [build] rustc 0 -> miri 1 - [build] rustc 0 -> cargo-miri 1 + [build] rustc 1 -> rust-analyzer 2 + [build] rustc 1 -> rustfmt 2 + [build] rustc 1 -> cargo-fmt 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [build] rustc 1 -> miri 2 + [build] rustc 1 -> cargo-miri 2 "); } @@ -1290,19 +1260,19 @@ mod snapshot { [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 1 - [build] rustc 0 -> rust-analyzer-proc-macro-srv 1 + [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> [build] rustc 0 -> cargo 1 - [build] rustc 0 -> rust-analyzer 1 - [build] rustc 0 -> rustfmt 1 - [build] rustc 0 -> cargo-fmt 1 - [build] rustc 0 -> clippy-driver 1 - [build] rustc 0 -> cargo-clippy 1 - [build] rustc 0 -> miri 1 - [build] rustc 0 -> cargo-miri 1 + [build] rustc 1 -> rust-analyzer 2 + [build] rustc 1 -> rustfmt 2 + [build] rustc 1 -> cargo-fmt 2 + [build] rustc 1 -> clippy-driver 2 + [build] rustc 1 -> cargo-clippy 2 + [build] rustc 1 -> miri 2 + [build] rustc 1 -> cargo-miri 2 [build] rustc 1 -> LlvmBitcodeLinker 2 "); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 011b52df97bbd..7c7638c4b7693 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1160,17 +1160,18 @@ impl Build { fn msg_sysroot_tool( &self, action: impl Into, - stage: u32, + build_stage: u32, what: impl Display, host: TargetSelection, target: TargetSelection, ) -> Option { let action = action.into().description(); let msg = |fmt| format!("{action} {what} {fmt}"); + let msg = if host == target { - msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1)) + msg(format_args!("(stage{build_stage} -> stage{}, {target})", build_stage + 1)) } else { - msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1)) + msg(format_args!("(stage{build_stage}:{host} -> stage{}:{target})", build_stage + 1)) }; self.group(&msg) } From b944144087df2bd56a035d7571c0e50b4f8d9d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:09:55 +0200 Subject: [PATCH 07/22] Add step metadata and a few tests for `Doc` steps --- src/bootstrap/src/core/build_steps/doc.rs | 8 ++ src/bootstrap/src/core/builder/tests.rs | 95 +++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 4d8505fb2b95f..6d780b5474c40 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -901,6 +901,10 @@ impl Step for Rustc { builder.open_in_browser(index); } } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc("rustc", self.target).stage(self.stage)) + } } macro_rules! tool_doc { @@ -1018,6 +1022,10 @@ macro_rules! tool_doc { })? } } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc(stringify!($tool), self.target)) + } } } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 77832c5dc3124..06ae73a6ad3da 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1674,6 +1674,101 @@ mod snapshot { [doc] std 1 crates=[alloc,core] "); } + + #[test] + fn doc_compiler_stage_0() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("compiler") + .stage(0) + .render_steps(), @r" + [build] rustdoc 0 + [build] llvm + [doc] rustc 0 + "); + } + + #[test] + fn doc_compiler_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("compiler") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustdoc 0 + [doc] rustc 1 + "); + } + + #[test] + fn doc_compiler_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("compiler") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [build] rustdoc 1 + [doc] rustc 2 + "); + } + + #[test] + fn doc_compiletest_stage_0() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("src/tools/compiletest") + .stage(0) + .render_steps(), @r" + [build] rustdoc 0 + [doc] Compiletest + "); + } + + #[test] + fn doc_compiletest_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("src/tools/compiletest") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustdoc 0 + [doc] Compiletest + "); + } + + #[test] + fn doc_compiletest_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("src/tools/compiletest") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [build] rustdoc 1 + [doc] Compiletest + "); + } } struct ExecutedSteps { From 2c7581fd256b50d33494595e36e8319a18318ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:28:05 +0200 Subject: [PATCH 08/22] Refactor `Rustdoc` --- src/bootstrap/src/core/build_steps/tool.rs | 121 +++++++++++---------- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 63 +++++------ 3 files changed, 90 insertions(+), 96 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index b3e054a9d8bab..9ee83075ea0f7 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -681,15 +681,21 @@ impl Step for RemoteTestServer { } } -#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] +/// Represents `Rustdoc` that either comes from the external stage0 sysroot or that is built +/// locally. +/// Rustdoc is special, because it both essentially corresponds to a `Compiler` (that can be +/// externally provided), but also to a `ToolRustc` tool. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustdoc { - /// This should only ever be 0 or 2. - /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here. + /// If the stage of `target_compiler` is `0`, then rustdoc is externally provided. + /// Otherwise it is built locally. pub target_compiler: Compiler, } impl Step for Rustdoc { - type Output = ToolBuildResult; + /// Path to the built rustdoc binary. + type Output = PathBuf; + const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -703,21 +709,20 @@ impl Step for Rustdoc { }); } - fn run(self, builder: &Builder<'_>) -> ToolBuildResult { + fn run(self, builder: &Builder<'_>) -> Self::Output { let target_compiler = self.target_compiler; let target = target_compiler.host; + // If stage is 0, we use a prebuilt rustdoc from stage0 if target_compiler.stage == 0 { if !target_compiler.is_snapshot(builder) { panic!("rustdoc in stage 0 must be snapshot rustdoc"); } - return ToolBuildResult { - tool_path: builder.initial_rustdoc.clone(), - build_compiler: target_compiler, - }; + return builder.initial_rustdoc.clone(); } + // If stage is higher, we build rustdoc instead let bin_rustdoc = || { let sysroot = builder.sysroot(target_compiler); let bindir = sysroot.join("bin"); @@ -729,10 +734,7 @@ impl Step for Rustdoc { // If CI rustc is enabled and we haven't modified the rustdoc sources, // use the precompiled rustdoc from CI rustc's sysroot to speed up bootstrapping. - if builder.download_rustc() - && target_compiler.stage > 0 - && builder.rust_info().is_managed_git_subrepository() - { + if builder.download_rustc() && builder.rust_info().is_managed_git_subrepository() { let files_to_track = &["src/librustdoc", "src/tools/rustdoc", "src/rustdoc-json-types"]; // Check if unchanged @@ -745,8 +747,7 @@ impl Step for Rustdoc { let bin_rustdoc = bin_rustdoc(); builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable); - - return ToolBuildResult { tool_path: bin_rustdoc, build_compiler: target_compiler }; + return bin_rustdoc; } } @@ -762,45 +763,39 @@ impl Step for Rustdoc { extra_features.push("jemalloc".to_string()); } - let ToolBuildResult { tool_path, build_compiler } = builder.ensure(ToolBuild { - build_compiler: target_compiler, - target, - // Cargo adds a number of paths to the dylib search path on windows, which results in - // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" - // rustdoc a different name. - tool: "rustdoc_tool_binary", - mode: Mode::ToolRustc, - path: "src/tools/rustdoc", - source_type: SourceType::InTree, - extra_features, - allow_features: "", - cargo_args: Vec::new(), - artifact_kind: ToolArtifactKind::Binary, - }); - - // FIXME: handle the build/target compiler split here somehow - // don't create a stage0-sysroot/bin directory. - if target_compiler.stage > 0 { - if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None { - // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into - // our final binaries - compile::strip_debug(builder, target, &tool_path); - } - let bin_rustdoc = bin_rustdoc(); - builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable); - ToolBuildResult { tool_path: bin_rustdoc, build_compiler } - } else { - ToolBuildResult { tool_path, build_compiler } + let compilers = RustcPrivateCompilers::from_link_compiler(builder, target_compiler); + let tool_path = builder + .ensure(ToolBuild { + build_compiler: compilers.build_compiler, + target, + // Cargo adds a number of paths to the dylib search path on windows, which results in + // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" + // rustdoc a different name. + tool: "rustdoc_tool_binary", + mode: Mode::ToolRustc, + path: "src/tools/rustdoc", + source_type: SourceType::InTree, + extra_features, + allow_features: "", + cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, + }) + .tool_path; + + if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None { + // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into + // our final binaries + compile::strip_debug(builder, target, &tool_path); } + let bin_rustdoc = bin_rustdoc(); + builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable); + bin_rustdoc } fn metadata(&self) -> Option { Some( StepMetadata::build("rustdoc", self.target_compiler.host) - // rustdoc is ToolRustc, so stage N rustdoc is built by stage N-1 rustc - // FIXME: make this stage deduction automatic somehow - // FIXME: log the compiler that actually built ToolRustc steps - .stage(self.target_compiler.stage.saturating_sub(1)), + .stage(self.target_compiler.stage), ) } } @@ -1317,18 +1312,11 @@ impl RustcPrivateCompilers { /// Create compilers for a `rustc_private` tool with the given `stage` and for the given /// `target`. pub fn new(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { - assert!(stage > 0); - - let build_compiler = if builder.download_rustc() && stage == 1 { - // We shouldn't drop to stage0 compiler when using CI rustc. - builder.compiler(1, builder.config.host_target) - } else { - builder.compiler(stage - 1, builder.config.host_target) - }; + let build_compiler = Self::build_compiler_from_stage(builder, stage); // This is the compiler we'll link to // FIXME: make 100% sure that `link_compiler` was indeed built with `build_compiler`... - let link_compiler = builder.compiler(stage, target); + let link_compiler = builder.compiler(build_compiler.stage + 1, target); Self { build_compiler, link_compiler } } @@ -1343,6 +1331,25 @@ impl RustcPrivateCompilers { Self { build_compiler, link_compiler } } + /// Create rustc tool compilers from the link compiler. + pub fn from_link_compiler(builder: &Builder<'_>, link_compiler: Compiler) -> Self { + Self { + build_compiler: Self::build_compiler_from_stage(builder, link_compiler.stage), + link_compiler, + } + } + + fn build_compiler_from_stage(builder: &Builder<'_>, stage: u32) -> Compiler { + assert!(stage > 0); + + if builder.download_rustc() && stage == 1 { + // We shouldn't drop to stage0 compiler when using CI rustc. + builder.compiler(1, builder.config.host_target) + } else { + builder.compiler(stage - 1, builder.config.host_target) + } + } + pub fn build_compiler(&self) -> Compiler { self.build_compiler } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index b30c460d5f1b7..660c869b67e91 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1540,7 +1540,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to /// `target_compiler`. pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { - self.ensure(tool::Rustdoc { target_compiler }).tool_path + self.ensure(tool::Rustdoc { target_compiler }) } pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 06ae73a6ad3da..26158abf4bd4c 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -279,13 +279,6 @@ mod defaults { first(cache.all::()), &[tool::ErrorIndex { compiler: Compiler::new(1, a) }] ); - // docs should be built with the stage0 compiler, not with the stage0 artifacts. - // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to, - // not the one it was built by. - assert_eq!( - first(cache.all::()), - &[tool::Rustdoc { target_compiler: Compiler::new(1, a) },] - ); } } @@ -337,12 +330,6 @@ mod dist { first(builder.cache.all::()), &[tool::ErrorIndex { compiler: Compiler::new(1, a) }] ); - // This is actually stage 1, but Rustdoc::run swaps out the compiler with - // stage minus 1 if --stage is not 0. Very confusing! - assert_eq!( - first(builder.cache.all::()), - &[tool::Rustdoc { target_compiler: Compiler::new(2, a) },] - ); } } @@ -627,7 +614,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustdoc 0 + [build] rustdoc 1 "); } @@ -650,10 +637,10 @@ mod snapshot { [build] rustc 2 -> std 2 [build] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [build] rustdoc 1 + [build] rustdoc 2 [build] llvm [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 "); } @@ -750,7 +737,7 @@ mod snapshot { [build] rustc 1 -> LldWrapper 2 [build] rustc 1 -> LlvmBitcodeLinker 2 [build] rustc 2 -> std 2 - [build] rustdoc 1 + [build] rustdoc 2 " ); } @@ -779,7 +766,7 @@ mod snapshot { [build] rustc 1 -> rustc 2 [build] rustc 1 -> LldWrapper 2 [build] rustc 1 -> LlvmBitcodeLinker 2 - [build] rustdoc 1 + [build] rustdoc 2 " ); } @@ -968,7 +955,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] "); } @@ -1017,7 +1004,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 [build] rustc 0 -> LintDocs 1 @@ -1059,7 +1046,7 @@ mod snapshot { [build] rustc 1 -> LldWrapper 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustc 1 -> LlvmBitcodeLinker 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 [build] rustc 0 -> LintDocs 1 @@ -1098,7 +1085,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 @@ -1135,7 +1122,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 [build] rustc 0 -> LintDocs 1 @@ -1149,7 +1136,7 @@ mod snapshot { [dist] rustc [build] llvm [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> @@ -1172,7 +1159,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 @@ -1190,7 +1177,7 @@ mod snapshot { [dist] rustc [build] llvm [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [dist] rustc [dist] rustc 1 -> std 1 [dist] rustc 1 -> std 1 @@ -1214,7 +1201,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 [build] rustc 0 -> RustInstaller 1 @@ -1246,7 +1233,7 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 2 -> std 2 [build] rustc 1 -> std 1 @@ -1259,7 +1246,7 @@ mod snapshot { [build] llvm [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 - [build] rustdoc 1 + [build] rustdoc 2 [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 [build] rustc 0 -> GenerateCopyright 1 [dist] rustc @@ -1595,7 +1582,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] "); } @@ -1609,7 +1596,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[core] "); } @@ -1624,7 +1611,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[core] "); } @@ -1654,7 +1641,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[alloc,core] "); } @@ -1670,7 +1657,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] std 1 crates=[alloc,core] "); } @@ -1700,7 +1687,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] rustc 1 "); } @@ -1718,7 +1705,7 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [build] rustc 2 -> std 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] rustc 2 "); } @@ -1747,7 +1734,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustdoc 0 + [build] rustdoc 1 [doc] Compiletest "); } @@ -1765,7 +1752,7 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [build] rustc 2 -> std 2 - [build] rustdoc 1 + [build] rustdoc 2 [doc] Compiletest "); } From 91d40d270d66baa374169be662c403993f5a6591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:28:13 +0200 Subject: [PATCH 09/22] Fix `ToolRustc` build with `download-rustc` --- src/bootstrap/src/core/build_steps/tool.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 9ee83075ea0f7..469def86f8f15 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -111,7 +111,13 @@ impl Step for ToolBuild { let path = self.path; match self.mode { - Mode::ToolRustc => {} + Mode::ToolRustc => { + // FIXME: remove this, it's only needed for download-rustc... + if !self.build_compiler.is_forced_compiler() && builder.download_rustc() { + builder.std(self.build_compiler, self.build_compiler.host); + builder.ensure(compile::Rustc::new(self.build_compiler, target)); + } + } Mode::ToolStd => { // If compiler was forced, its artifacts should have been prepared earlier. if !self.build_compiler.is_forced_compiler() { From 7a2c4d312276bec98e4182099d0f8b8b09d55fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:45:33 +0200 Subject: [PATCH 10/22] Add step metadata and a simple test for codegen backends --- src/bootstrap/src/core/build_steps/compile.rs | 7 +++++++ src/bootstrap/src/core/builder/tests.rs | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 488217549d9b2..2c54a31ecbe44 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1705,6 +1705,13 @@ impl Step for CodegenBackend { let codegen_backend = codegen_backend.to_str().unwrap(); t!(stamp.add_stamp(codegen_backend).write()); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::build(&format!("rustc_codegen_{}", self.backend), self.target) + .built_by(self.compiler), + ) + } } /// Creates the `codegen-backends` folder for a compiler that's about to be diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 26158abf4bd4c..8d458c74cf5b8 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -719,6 +719,26 @@ mod snapshot { "); } + #[test] + fn build_compiler_codegen_backend() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx + .config("build") + .args(&["--set", "rust.codegen-backends=['llvm', 'cranelift']"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> rustc_codegen_cranelift 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> rustc_codegen_cranelift 2 + [build] rustdoc 1 + " + ); + } + #[test] fn build_compiler_tools() { let ctx = TestCtx::new(); From 06855be09d67e48980f986cad13ce9059165f0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 13:51:01 +0200 Subject: [PATCH 11/22] Port codegen backends to `RustcPrivateCompilers` --- src/bootstrap/src/core/build_steps/compile.rs | 61 +++++++++---------- src/bootstrap/src/core/build_steps/doc.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 13 ++-- src/bootstrap/src/core/build_steps/tool.rs | 14 ++--- src/bootstrap/src/core/builder/tests.rs | 3 - src/bootstrap/src/lib.rs | 13 +--- 6 files changed, 45 insertions(+), 61 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 2c54a31ecbe44..c76a96028fa83 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -19,7 +19,7 @@ use serde_derive::Deserialize; use tracing::{instrument, span}; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; -use crate::core::build_steps::tool::{SourceType, copy_lld_artifacts}; +use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; use crate::core::builder; use crate::core::builder::{ @@ -1131,7 +1131,7 @@ impl Step for Rustc { cargo.env("RUSTC_BOLT_LINK_FLAGS", "1"); } - let _guard = builder.msg_sysroot_tool( + let _guard = builder.msg_rustc_tool( Kind::Build, build_compiler.stage, format_args!("compiler artifacts{}", crate_description(&self.crates)), @@ -1544,9 +1544,8 @@ impl Step for RustcLink { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CodegenBackend { - pub target: TargetSelection, - pub compiler: Compiler, - pub backend: CodegenBackendKind, + compilers: RustcPrivateCompilers, + backend: CodegenBackendKind, } fn needs_codegen_config(run: &RunConfig<'_>) -> bool { @@ -1610,8 +1609,11 @@ impl Step for CodegenBackend { } run.builder.ensure(CodegenBackend { - target: run.target, - compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + compilers: RustcPrivateCompilers::new( + run.builder, + run.builder.top_stage, + run.target, + ), backend: backend.clone(), }); } @@ -1624,21 +1626,17 @@ impl Step for CodegenBackend { name = "CodegenBackend::run", skip_all, fields( - compiler = ?self.compiler, - target = ?self.target, - backend = ?self.target, + compilers = ?self.compilers, + backend = ?self.backend, ), ), )] fn run(self, builder: &Builder<'_>) { - let compiler = self.compiler; - let target = self.target; let backend = self.backend; + let target = self.compilers.target(); + let build_compiler = self.compilers.build_compiler(); - // FIXME: migrate to RustcPrivateCompilers - builder.ensure(Rustc::new(compiler, target)); - - if builder.config.keep_stage.contains(&compiler.stage) { + if builder.config.keep_stage.contains(&build_compiler.stage) { trace!("`keep-stage` requested"); builder.info( "WARNING: Using a potentially old codegen backend. \ @@ -1649,17 +1647,11 @@ impl Step for CodegenBackend { return; } - let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - if compiler_to_use != compiler { - builder.ensure(CodegenBackend { compiler: compiler_to_use, target, backend }); - return; - } - - let out_dir = builder.cargo_out(compiler, Mode::Codegen, target); + let out_dir = builder.cargo_out(build_compiler, Mode::Codegen, target); let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Codegen, SourceType::InTree, target, @@ -1680,8 +1672,13 @@ impl Step for CodegenBackend { let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); - let _guard = - builder.msg_build(compiler, format_args!("codegen backend {}", backend.name()), target); + let _guard = builder.msg_rustc_tool( + Kind::Build, + build_compiler.stage, + format_args!("codegen backend {}", backend.name()), + build_compiler.host, + target, + ); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); if builder.config.dry_run() { return; @@ -1701,15 +1698,15 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend); + let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend); let codegen_backend = codegen_backend.to_str().unwrap(); t!(stamp.add_stamp(codegen_backend).write()); } fn metadata(&self) -> Option { Some( - StepMetadata::build(&format!("rustc_codegen_{}", self.backend), self.target) - .built_by(self.compiler), + StepMetadata::build(&self.backend.crate_name(), self.compilers.target()) + .built_by(self.compilers.build_compiler()), ) } } @@ -2198,8 +2195,10 @@ impl Step for Assemble { continue; } builder.ensure(CodegenBackend { - compiler: build_compiler, - target: target_compiler.host, + compilers: RustcPrivateCompilers::from_build_and_link_compiler( + build_compiler, + target_compiler, + ), backend: backend.clone(), }); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 6d780b5474c40..d4539a0eb34d1 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -811,7 +811,7 @@ impl Step for Rustc { let compiler = builder.compiler(stage, builder.config.host_target); builder.std(compiler, builder.config.host_target); - let _guard = builder.msg_sysroot_tool( + let _guard = builder.msg_rustc_tool( Kind::Doc, stage, format!("compiler{}", crate_description(&self.crates)), diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f725e458c4a56..6c6b2e585495e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -603,7 +603,7 @@ impl Step for Miri { cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); { - let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); + let _guard = builder.msg_rustc_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -619,7 +619,7 @@ impl Step for Miri { cargo.args(["tests/pass", "tests/panic"]); { - let _guard = builder.msg_sysroot_tool( + let _guard = builder.msg_rustc_tool( Kind::Test, stage, "miri (mir-opt-level 4)", @@ -696,7 +696,7 @@ impl Step for CargoMiri { // Finally, run everything. let mut cargo = BootstrapCommand::from(cargo); { - let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); + let _guard = builder.msg_rustc_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -837,8 +837,7 @@ impl Step for Clippy { cargo.add_rustc_lib_path(builder); let cargo = prepare_cargo_test(cargo, &[], &[], host, builder); - let _guard = - builder.msg_sysroot_tool(Kind::Test, build_compiler.stage, "clippy", host, host); + let _guard = builder.msg_rustc_tool(Kind::Test, build_compiler.stage, "clippy", host, host); // Clippy reports errors if it blessed the outputs if cargo.allow_failure().run(builder) { @@ -1105,7 +1104,7 @@ impl Step for RustdocGUI { } let _time = helpers::timeit(builder); - let _guard = builder.msg_sysroot_tool( + let _guard = builder.msg_rustc_tool( Kind::Test, self.compiler.stage, "rustdoc-gui", @@ -2597,7 +2596,7 @@ fn run_cargo_test<'a>( let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder); let _time = helpers::timeit(builder); let _group = description.into().and_then(|what| { - builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target) + builder.msg_rustc_tool(Kind::Test, compiler.stage, what, compiler.host, target) }); #[cfg(feature = "build-metrics")] diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 469def86f8f15..c1b8b03e6a1af 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -71,13 +71,9 @@ impl Builder<'_> { ) -> Option { match mode { // depends on compiler stage, different to host compiler - Mode::ToolRustc => self.msg_sysroot_tool( - kind, - build_stage, - format_args!("tool {tool}"), - *host, - *target, - ), + Mode::ToolRustc => { + self.msg_rustc_tool(kind, build_stage, format_args!("tool {tool}"), *host, *target) + } // doesn't depend on compiler, same as host compiler _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target), } @@ -1327,6 +1323,10 @@ impl RustcPrivateCompilers { Self { build_compiler, link_compiler } } + pub fn from_build_and_link_compiler(build_compiler: Compiler, link_compiler: Compiler) -> Self { + Self { build_compiler, link_compiler } + } + /// Create rustc tool compilers from the build compiler. pub fn from_build_compiler( builder: &Builder<'_>, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8d458c74cf5b8..ac7d8c7cb48b3 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -731,9 +731,6 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 0 -> rustc_codegen_cranelift 1 [build] rustc 1 -> std 1 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> rustc_codegen_cranelift 2 [build] rustdoc 1 " ); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 7c7638c4b7693..69546e923d0d4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1100,17 +1100,6 @@ impl Build { self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into()) } - #[must_use = "Groups should not be dropped until the Step finishes running"] - #[track_caller] - fn msg_build( - &self, - compiler: Compiler, - what: impl Display, - target: impl Into>, - ) -> Option { - self.msg(Kind::Build, compiler.stage, what, compiler.host, target) - } - /// Return a `Group` guard for a [`Step`] that is built for each `--stage`. /// /// [`Step`]: crate::core::builder::Step @@ -1157,7 +1146,7 @@ impl Build { #[must_use = "Groups should not be dropped until the Step finishes running"] #[track_caller] - fn msg_sysroot_tool( + fn msg_rustc_tool( &self, action: impl Into, build_stage: u32, From c3da07beb42b7cd32cfff30d357368c6299c36dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 14:01:59 +0200 Subject: [PATCH 12/22] Rename `link_compiler` to `target_compiler` --- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/build_steps/run.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 4 +- src/bootstrap/src/core/build_steps/tool.rs | 45 ++++++++++--------- src/bootstrap/src/core/builder/mod.rs | 4 +- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c76a96028fa83..95707e37018e9 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2195,7 +2195,7 @@ impl Step for Assemble { continue; } builder.ensure(CodegenBackend { - compilers: RustcPrivateCompilers::from_build_and_link_compiler( + compilers: RustcPrivateCompilers::from_build_and_target_compiler( build_compiler, target_compiler, ), diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 984744b5678f5..962dd372849d2 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -141,7 +141,7 @@ impl Step for Miri { // Get a target sysroot for Miri. let miri_sysroot = - test::Miri::build_miri_sysroot(builder, compilers.link_compiler(), target); + test::Miri::build_miri_sysroot(builder, compilers.target_compiler(), target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 6c6b2e585495e..073a1d62d7f1a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -551,7 +551,7 @@ impl Step for Miri { // the ui tests also assume cargo-miri has been built builder.ensure(tool::CargoMiri::from_compilers(compilers)); - let target_compiler = compilers.link_compiler(); + let target_compiler = compilers.target_compiler(); // We also need sysroots, for Miri and for the host (the latter for build scripts). // This is for the tests so everything is done with the target compiler. @@ -790,7 +790,7 @@ impl Step for Clippy { // that is linked into the clippy being tested. `target_compiler` is the latter, // and it must also be used by clippy's test runner to build tests and their dependencies. let compilers = self.compilers; - let target_compiler = compilers.link_compiler(); + let target_compiler = compilers.target_compiler(); let tool_result = builder.ensure(tool::Clippy::from_compilers(compilers)); let build_compiler = tool_result.build_compiler; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index c1b8b03e6a1af..a56090ab1a4a6 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -765,7 +765,7 @@ impl Step for Rustdoc { extra_features.push("jemalloc".to_string()); } - let compilers = RustcPrivateCompilers::from_link_compiler(builder, target_compiler); + let compilers = RustcPrivateCompilers::from_target_compiler(builder, target_compiler); let tool_path = builder .ensure(ToolBuild { build_compiler: compilers.build_compiler, @@ -1133,7 +1133,7 @@ impl Step for RustAnalyzerProcMacroSrv { // Copy `rust-analyzer-proc-macro-srv` to `/libexec/` // so that r-a can use it. - let libexec_path = builder.sysroot(self.compilers.link_compiler).join("libexec"); + let libexec_path = builder.sysroot(self.compilers.target_compiler).join("libexec"); t!(fs::create_dir_all(&libexec_path)); builder.copy_link( &tool_result.tool_path, @@ -1294,20 +1294,20 @@ impl Step for LibcxxVersionTool { /// that depends on compiler internals (`rustc_private`). /// Their compilation looks like this: /// -/// - `build_compiler` (stage N-1) builds `link_compiler` (stage N) to produce .rlibs +/// - `build_compiler` (stage N-1) builds `target_compiler` (stage N) to produce .rlibs /// - These .rlibs are copied into the sysroot of `build_compiler` /// - `build_compiler` (stage N-1) builds `` (stage N) -/// - `` links to .rlibs from `link_compiler` +/// - `` links to .rlibs from `target_compiler` /// /// Eventually, this could also be used for .rmetas and check builds, but so far we only deal with /// normal builds here. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct RustcPrivateCompilers { - /// Compiler that builds the tool and that builds `link_compiler`. + /// Compiler that builds the tool and that builds `target_compiler`. build_compiler: Compiler, /// Compiler to which .rlib artifacts the tool links to. /// The host target of this compiler corresponds to the target of the tool. - link_compiler: Compiler, + target_compiler: Compiler, } impl RustcPrivateCompilers { @@ -1317,14 +1317,17 @@ impl RustcPrivateCompilers { let build_compiler = Self::build_compiler_from_stage(builder, stage); // This is the compiler we'll link to - // FIXME: make 100% sure that `link_compiler` was indeed built with `build_compiler`... - let link_compiler = builder.compiler(build_compiler.stage + 1, target); + // FIXME: make 100% sure that `target_compiler` was indeed built with `build_compiler`... + let target_compiler = builder.compiler(build_compiler.stage + 1, target); - Self { build_compiler, link_compiler } + Self { build_compiler, target_compiler } } - pub fn from_build_and_link_compiler(build_compiler: Compiler, link_compiler: Compiler) -> Self { - Self { build_compiler, link_compiler } + pub fn from_build_and_target_compiler( + build_compiler: Compiler, + target_compiler: Compiler, + ) -> Self { + Self { build_compiler, target_compiler } } /// Create rustc tool compilers from the build compiler. @@ -1333,15 +1336,15 @@ impl RustcPrivateCompilers { build_compiler: Compiler, target: TargetSelection, ) -> Self { - let link_compiler = builder.compiler(build_compiler.stage + 1, target); - Self { build_compiler, link_compiler } + let target_compiler = builder.compiler(build_compiler.stage + 1, target); + Self { build_compiler, target_compiler } } - /// Create rustc tool compilers from the link compiler. - pub fn from_link_compiler(builder: &Builder<'_>, link_compiler: Compiler) -> Self { + /// Create rustc tool compilers from the target compiler. + pub fn from_target_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self { Self { - build_compiler: Self::build_compiler_from_stage(builder, link_compiler.stage), - link_compiler, + build_compiler: Self::build_compiler_from_stage(builder, target_compiler.stage), + target_compiler, } } @@ -1360,13 +1363,13 @@ impl RustcPrivateCompilers { self.build_compiler } - pub fn link_compiler(&self) -> Compiler { - self.link_compiler + pub fn target_compiler(&self) -> Compiler { + self.target_compiler } /// Target of the tool being compiled pub fn target(&self) -> TargetSelection { - self.link_compiler.host + self.target_compiler.host } } @@ -1494,7 +1497,7 @@ fn build_extended_rustc_tool( artifact_kind: ToolArtifactKind::Binary, }); - let target_compiler = compilers.link_compiler; + let target_compiler = compilers.target_compiler; if let Some(add_bins_to_sysroot) = add_bins_to_sysroot && !add_bins_to_sysroot.is_empty() { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 660c869b67e91..98deb66030267 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1559,7 +1559,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s // FIXME: double check that `run_compiler`'s stage is what we want to use let compilers = RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); - assert_eq!(run_compiler, compilers.link_compiler()); + assert_eq!(run_compiler, compilers.target_compiler()); let _ = self.ensure(tool::Clippy::from_compilers(compilers)); let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers)); @@ -1577,7 +1577,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s let compilers = RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target); - assert_eq!(run_compiler, compilers.link_compiler()); + assert_eq!(run_compiler, compilers.target_compiler()); // Prepare the tools let miri = self.ensure(tool::Miri::from_compilers(compilers)); From 0ff8ff38ee75af184419fa396d6fc4db53eb22ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 18:22:36 +0200 Subject: [PATCH 13/22] Appease Clippy --- src/bootstrap/src/core/build_steps/tool.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index a56090ab1a4a6..6204476456ceb 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1467,7 +1467,6 @@ fn should_run_extended_rustc_tool<'a>( ) } -#[expect(clippy::too_many_arguments)] // silence overeager clippy lint fn build_extended_rustc_tool( builder: &Builder<'_>, compilers: RustcPrivateCompilers, From 316b5d390203c0592dab9810cf80b5cb6f7ce536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 21 Jul 2025 17:38:20 +0200 Subject: [PATCH 14/22] Add basic Cargo snapshot test --- src/bootstrap/src/core/build_steps/tool.rs | 2 ++ src/bootstrap/src/core/builder/tests.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 6204476456ceb..00be04e26dc3b 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -802,6 +802,8 @@ impl Step for Rustdoc { } } +/// Builds the cargo tool. +/// Note that it can be built using a stable compiler. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Cargo { pub compiler: Compiler, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index ac7d8c7cb48b3..3b3f321750f22 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1000,6 +1000,19 @@ mod snapshot { "); } + #[test] + fn build_cargo() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("build") + .paths(&["cargo"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> cargo 2 + "); + } + #[test] fn dist_default_stage() { let ctx = TestCtx::new(); From 21d7a540135a1f7a9c3a35fa139df8faefde637c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 21 Jul 2025 17:41:02 +0200 Subject: [PATCH 15/22] Make `Cargo` a `ToolTarget` tool --- src/bootstrap/src/core/build_steps/tool.rs | 10 +++------- src/bootstrap/src/core/builder/tests.rs | 6 +----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 00be04e26dc3b..a2c7c8c65b06e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -822,7 +822,7 @@ impl Step for Cargo { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Cargo { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + compiler: get_tool_target_compiler(run.builder, ToolTargetBuildMode::Build(run.target)), target: run.target, }); } @@ -834,7 +834,7 @@ impl Step for Cargo { build_compiler: self.compiler, target: self.target, tool: "cargo", - mode: Mode::ToolRustc, + mode: Mode::ToolTarget, path: "src/tools/cargo", source_type: SourceType::Submodule, extra_features: Vec::new(), @@ -845,11 +845,7 @@ impl Step for Cargo { } fn metadata(&self) -> Option { - // FIXME: fix staging logic - Some( - StepMetadata::build("cargo", self.target) - .built_by(self.compiler.with_stage(self.compiler.stage - 1)), - ) + Some(StepMetadata::build("cargo", self.target).built_by(self.compiler)) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 3b3f321750f22..6bd5b16eabb3d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1006,11 +1006,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("build") .paths(&["cargo"]) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> cargo 2 - "); + .render_steps(), @"[build] rustc 0 -> cargo 1 "); } #[test] From 021f2ff54d2569b6074a019510d105050849b99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 21 Jul 2025 17:59:33 +0200 Subject: [PATCH 16/22] Update `CargoTest` --- src/bootstrap/src/core/build_steps/test.rs | 31 +++++++++++++++------- src/bootstrap/src/core/builder/tests.rs | 17 ++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 073a1d62d7f1a..e542ab7c1276a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -228,7 +228,7 @@ impl Step for HtmlCheck { /// order to test cargo. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargotest { - stage: u32, + build_compiler: Compiler, host: TargetSelection, } @@ -241,7 +241,12 @@ impl Step for Cargotest { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Cargotest { stage: run.builder.top_stage, host: run.target }); + // FIXME: support testing stage 0 cargo? + assert!(run.builder.top_stage > 0); + run.builder.ensure(Cargotest { + build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target), + host: run.target, + }); } /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler. @@ -249,9 +254,13 @@ impl Step for Cargotest { /// This tool in `src/tools` will check out a few Rust projects and run `cargo /// test` to ensure that we don't regress the test suites there. fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(self.stage, self.host); - builder.ensure(compile::Rustc::new(compiler, compiler.host)); - let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); + // Here we need to ensure that we run cargo with an in-tree compiler, because we test + // both their behavior together. + // We can build cargo with the earlier stage compiler though. + let cargo = + builder.ensure(tool::Cargo { compiler: self.build_compiler, target: self.host }); + let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host); + builder.std(tested_compiler, self.host); // Note that this is a short, cryptic, and not scoped directory name. This // is currently to minimize the length of path on Windows where we otherwise @@ -264,17 +273,21 @@ impl Step for Cargotest { cmd.arg(&cargo.tool_path) .arg(&out_dir) .args(builder.config.test_args()) - .env("RUSTC", builder.rustc(compiler)) - .env("RUSTDOC", builder.rustdoc_for_compiler(compiler)); + .env("RUSTC", builder.rustc(tested_compiler)) + .env("RUSTDOC", builder.rustdoc_for_compiler(tested_compiler)); add_rustdoc_cargo_linker_args( &mut cmd, builder, - compiler.host, + tested_compiler.host, LldThreads::No, - compiler.stage, + tested_compiler.stage, ); cmd.delay_failure().run(builder); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("cargotest", self.host).stage(self.build_compiler.stage + 1)) + } } /// Runs `cargo test` for cargo itself. diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 6bd5b16eabb3d..1c82f804fd2be 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1599,6 +1599,23 @@ mod snapshot { steps.assert_contains_fuzzy(StepMetadata::build("rustc", host)); } + #[test] + fn test_cargotest() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("cargotest") + .render_steps(), @r" + [build] rustc 0 -> cargo 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> CargoTest 1 + [build] rustdoc 1 + [test] cargotest 1 + "); + } + #[test] fn doc_library() { let ctx = TestCtx::new(); From 3e6aa8169115ed1f9a3474ab45284561a30d780a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 21 Jul 2025 18:06:49 +0200 Subject: [PATCH 17/22] Add snapshot tests for `test cargo` and update Cargo snapshot tests --- src/bootstrap/src/core/builder/tests.rs | 37 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1c82f804fd2be..057f378479752 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1085,7 +1085,7 @@ mod snapshot { [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> - [build] rustc 0 -> cargo 1 + [build] rustc 1 -> cargo 2 [build] rustc 1 -> rust-analyzer 2 [build] rustc 1 -> rustfmt 2 [build] rustc 1 -> cargo-fmt 2 @@ -1278,7 +1278,7 @@ mod snapshot { [dist] rustc [dist] rustc 1 -> std 1 [dist] src <> - [build] rustc 0 -> cargo 1 + [build] rustc 1 -> cargo 2 [build] rustc 1 -> rust-analyzer 2 [build] rustc 1 -> rustfmt 2 [build] rustc 1 -> cargo-fmt 2 @@ -1599,6 +1599,39 @@ mod snapshot { steps.assert_contains_fuzzy(StepMetadata::build("rustc", host)); } + #[test] + fn test_cargo_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("cargo") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> cargo 3 + [build] rustdoc 2 + "); + } + + #[test] + fn test_cargo_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("cargo") + .stage(2) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> cargo 3 + [build] rustdoc 2 + "); + } + #[test] fn test_cargotest() { let ctx = TestCtx::new(); From 528e7dc9aec150250097e5918879023051f06249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 28 Jul 2025 15:01:14 +0200 Subject: [PATCH 18/22] Make build compiler explicit in `dist::Cargo` --- src/bootstrap/src/core/build_steps/dist.rs | 12 +++++------ src/bootstrap/src/core/build_steps/install.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 6 +++--- src/bootstrap/src/core/build_steps/tool.rs | 21 ++++++++++++++----- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 2d504cbd55af6..0465a071159ba 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1173,7 +1173,7 @@ impl Step for PlainSourceTarball { #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct Cargo { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1189,7 +1189,7 @@ impl Step for Cargo { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Cargo { - compiler: run.builder.compiler_for( + build_compiler: run.builder.compiler_for( run.builder.top_stage, run.builder.config.host_target, run.target, @@ -1199,12 +1199,10 @@ impl Step for Cargo { } fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; + let build_compiler = self.build_compiler; let target = self.target; - builder.ensure(compile::Rustc::new(compiler, target)); - - let cargo = builder.ensure(tool::Cargo { compiler, target }); + let cargo = builder.ensure(tool::Cargo::from_build_compiler(build_compiler, target)); let src = builder.src.join("src/tools/cargo"); let etc = src.join("src/etc"); @@ -1563,7 +1561,7 @@ impl Step for Extended { add_component!("rust-docs" => Docs { host: target }); add_component!("rust-json-docs" => JsonDocs { host: target }); - add_component!("cargo" => Cargo { compiler, target }); + add_component!("cargo" => Cargo { build_compiler: compiler, target }); add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target }); add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target }); add_component!("llvm-components" => LlvmTools { target }); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 2427b0191294e..f628330e9ed4d 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -215,7 +215,7 @@ install!((self, builder, _config), }; Cargo, alias = "cargo", Self::should_build(_config), only_hosts: true, { let tarball = builder - .ensure(dist::Cargo { compiler: self.compiler, target: self.target }) + .ensure(dist::Cargo { build_compiler: self.compiler, target: self.target }) .expect("missing cargo"); install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball); }; diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e542ab7c1276a..c5e18f610476b 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -258,7 +258,7 @@ impl Step for Cargotest { // both their behavior together. // We can build cargo with the earlier stage compiler though. let cargo = - builder.ensure(tool::Cargo { compiler: self.build_compiler, target: self.host }); + builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host); builder.std(tested_compiler, self.host); @@ -332,7 +332,7 @@ impl Step for Cargo { let compiler = builder.compiler(stage, self.host); - let cargo = builder.ensure(tool::Cargo { compiler, target: self.host }); + let cargo = builder.ensure(tool::Cargo::from_build_compiler(compiler, self.host)); let compiler = cargo.build_compiler; let cargo = tool::prepare_tool_cargo( @@ -1751,7 +1751,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // If we're using `--stage 0`, we should provide the bootstrap cargo. builder.initial_cargo.clone() } else { - builder.ensure(tool::Cargo { compiler, target: compiler.host }).tool_path + builder.ensure(tool::Cargo::from_build_compiler(compiler, compiler.host)).tool_path }; cmd.arg("--cargo-path").arg(cargo_path); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index a2c7c8c65b06e..0dc9002d4e419 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -806,8 +806,16 @@ impl Step for Rustdoc { /// Note that it can be built using a stable compiler. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Cargo { - pub compiler: Compiler, - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, +} + +impl Cargo { + /// Returns `Cargo` that will be **compiled** by the passed compiler, for the given + /// `target`. + pub fn from_build_compiler(build_compiler: Compiler, target: TargetSelection) -> Self { + Self { build_compiler, target } + } } impl Step for Cargo { @@ -822,7 +830,10 @@ impl Step for Cargo { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Cargo { - compiler: get_tool_target_compiler(run.builder, ToolTargetBuildMode::Build(run.target)), + build_compiler: get_tool_target_compiler( + run.builder, + ToolTargetBuildMode::Build(run.target), + ), target: run.target, }); } @@ -831,7 +842,7 @@ impl Step for Cargo { builder.build.require_submodule("src/tools/cargo", None); builder.ensure(ToolBuild { - build_compiler: self.compiler, + build_compiler: self.build_compiler, target: self.target, tool: "cargo", mode: Mode::ToolTarget, @@ -845,7 +856,7 @@ impl Step for Cargo { } fn metadata(&self) -> Option { - Some(StepMetadata::build("cargo", self.target).built_by(self.compiler)) + Some(StepMetadata::build("cargo", self.target).built_by(self.build_compiler)) } } From 1d1efed4d46531fc185887b7f3849501eb28ed24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 13:31:40 +0200 Subject: [PATCH 19/22] Fix `x test cargo` --- src/bootstrap/src/core/build_steps/test.rs | 47 ++++++++++------------ src/bootstrap/src/core/build_steps/tool.rs | 7 +++- src/bootstrap/src/core/builder/tests.rs | 13 ++---- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c5e18f610476b..5a3859792c733 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -18,7 +18,8 @@ use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::get_completion_paths; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{ - self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, Tool, + self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, Tool, ToolTargetBuildMode, + get_tool_target_compiler, }; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; @@ -293,7 +294,7 @@ impl Step for Cargotest { /// Runs `cargo test` for cargo itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { - stage: u32, + build_compiler: Compiler, host: TargetSelection, } @@ -310,35 +311,29 @@ impl Step for Cargo { } fn make_run(run: RunConfig<'_>) { - // If stage is explicitly set or not lower than 2, keep it. Otherwise, make sure it's at least 2 - // as tests for this step don't work with a lower stage. - let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 { - run.builder.top_stage - } else { - 2 - }; - - run.builder.ensure(Cargo { stage, host: run.target }); + run.builder.ensure(Cargo { + build_compiler: get_tool_target_compiler( + run.builder, + ToolTargetBuildMode::Build(run.target), + ), + host: run.target, + }); } /// Runs `cargo test` for `cargo` packaged with Rust. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - - if stage < 2 { - eprintln!("WARNING: cargo tests on stage {stage} may not behave well."); - eprintln!("HELP: consider using stage 2"); - } - - let compiler = builder.compiler(stage, self.host); - - let cargo = builder.ensure(tool::Cargo::from_build_compiler(compiler, self.host)); - let compiler = cargo.build_compiler; + // FIXME: we now use the same compiler to build cargo and then we also test that compiler + // using cargo. + // We could build cargo using a different compiler, but that complicates some things, + // because when we run cargo tests, the crates that are being compiled are accessing the + // sysroot of the build compiler, rather than the compiler being tested. + // Since these two compilers are currently the same, it works. + builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); let cargo = tool::prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, + self.build_compiler, + Mode::ToolTarget, self.host, Kind::Test, Self::CRATE_PATH, @@ -355,7 +350,7 @@ impl Step for Cargo { // Forcibly disable tests using nightly features since any changes to // those features won't be able to land. cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1"); - cargo.env("PATH", path_for_cargo(builder, compiler)); + cargo.env("PATH", path_for_cargo(builder, self.build_compiler)); // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the // same value as `-Zroot-dir`. @@ -367,7 +362,7 @@ impl Step for Cargo { crates: vec!["cargo".into()], target: self.host.triple.to_string(), host: self.host.triple.to_string(), - stage, + stage: self.build_compiler.stage + 1, }, builder, ); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 0dc9002d4e419..e3f49fa126ed8 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -841,6 +841,7 @@ impl Step for Cargo { fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.build.require_submodule("src/tools/cargo", None); + builder.std(self.build_compiler, self.target); builder.ensure(ToolBuild { build_compiler: self.build_compiler, target: self.target, @@ -849,7 +850,11 @@ impl Step for Cargo { path: "src/tools/cargo", source_type: SourceType::Submodule, extra_features: Vec::new(), - allow_features: "", + // Cargo is compilable with a stable compiler, but since we run in bootstrap, + // with RUSTC_BOOTSTRAP being set, some "clever" build scripts enable specialization + // based on this, which breaks stuff. We thus have to explicitly allow these features + // here. + allow_features: "min_specialization,specialization", cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, }) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 057f378479752..67ad60e308dd4 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1606,12 +1606,8 @@ mod snapshot { ctx.config("test") .path("cargo") .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 2 -> cargo 3 - [build] rustdoc 2 + [build] rustc 0 -> cargo 1 + [build] rustdoc 0 "); } @@ -1626,9 +1622,8 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 2 -> cargo 3 - [build] rustdoc 2 + [build] rustc 1 -> cargo 2 + [build] rustdoc 1 "); } From 47da0147716e01e6f70cd6a275b6e7251b27929f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 31 Jul 2025 08:38:54 +0200 Subject: [PATCH 20/22] Make `x test cargo` stage N test rustc stage N --- src/bootstrap/src/core/build_steps/test.rs | 53 ++++++++++++++++------ src/bootstrap/src/core/builder/tests.rs | 7 +++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 5a3859792c733..e8b9ac440f500 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -4,10 +4,12 @@ //! However, this contains ~all test parts we expect people to be able to build and run locally. use std::collections::HashSet; +use std::env::join_paths; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::{env, fs, iter}; +use build_helper::exit; #[cfg(feature = "tracing")] use tracing::instrument; @@ -242,8 +244,12 @@ impl Step for Cargotest { } fn make_run(run: RunConfig<'_>) { - // FIXME: support testing stage 0 cargo? - assert!(run.builder.top_stage > 0); + if run.builder.top_stage == 0 { + eprintln!( + "ERROR: running cargotest with stage 0 is currently unsupported. Use at least stage 1." + ); + exit!(1); + } run.builder.ensure(Cargotest { build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target), host: run.target, @@ -322,14 +328,18 @@ impl Step for Cargo { /// Runs `cargo test` for `cargo` packaged with Rust. fn run(self, builder: &Builder<'_>) { - // FIXME: we now use the same compiler to build cargo and then we also test that compiler - // using cargo. - // We could build cargo using a different compiler, but that complicates some things, - // because when we run cargo tests, the crates that are being compiled are accessing the - // sysroot of the build compiler, rather than the compiler being tested. - // Since these two compilers are currently the same, it works. + // When we do a "stage 1 cargo test", it means that we test the stage 1 rustc + // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then + // run its tests against the stage 1 compiler (called `tested_compiler` below). builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); + let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host); + builder.std(tested_compiler, self.host); + // We also need to build rustdoc for cargo tests + // It will be located in the bindir of `tested_compiler`, so we don't need to explicitly + // pass its path to Cargo. + builder.rustdoc_for_compiler(tested_compiler); + let cargo = tool::prepare_tool_cargo( builder, self.build_compiler, @@ -350,7 +360,27 @@ impl Step for Cargo { // Forcibly disable tests using nightly features since any changes to // those features won't be able to land. cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1"); - cargo.env("PATH", path_for_cargo(builder, self.build_compiler)); + + // Configure PATH to find the right rustc. NB. we have to use PATH + // and not RUSTC because the Cargo test suite has tests that will + // fail if rustc is not spelled `rustc`. + cargo.env("PATH", bin_path_for_cargo(builder, tested_compiler)); + + // The `cargo` command configured above has dylib dir path set to the `build_compiler`'s + // libdir. That causes issues in cargo test, because the programs that cargo compiles are + // incorrectly picking that libdir, even though they should be picking the + // `tested_compiler`'s libdir. We thus have to override the precedence here. + let existing_dylib_path = cargo + .get_envs() + .find(|(k, _)| *k == OsStr::new(dylib_path_var())) + .and_then(|(_, v)| v) + .unwrap_or(OsStr::new("")); + cargo.env( + dylib_path_var(), + join_paths([builder.rustc_libdir(tested_compiler).as_ref(), existing_dylib_path]) + .unwrap_or_default(), + ); + // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the // same value as `-Zroot-dir`. @@ -859,10 +889,7 @@ impl Step for Clippy { } } -fn path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString { - // Configure PATH to find the right rustc. NB. we have to use PATH - // and not RUSTC because the Cargo test suite has tests that will - // fail if rustc is not spelled `rustc`. +fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString { let path = builder.sysroot(compiler).join("bin"); let old_path = env::var_os("PATH").unwrap_or_default(); env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("") diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 67ad60e308dd4..139ddc9ed2496 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1607,6 +1607,10 @@ mod snapshot { .path("cargo") .render_steps(), @r" [build] rustc 0 -> cargo 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustdoc 1 [build] rustdoc 0 "); } @@ -1623,6 +1627,9 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> cargo 2 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [build] rustdoc 2 [build] rustdoc 1 "); } From c46f42d239a67ec1e45fe70e8f791fb47423bbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 1 Aug 2025 13:24:34 +0200 Subject: [PATCH 21/22] Clarify comments on Cargo (self-)test steps --- src/bootstrap/src/core/build_steps/test.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e8b9ac440f500..218a6f563e4fe 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -250,6 +250,9 @@ impl Step for Cargotest { ); exit!(1); } + // We want to build cargo stage N (where N == top_stage), and rustc stage N, + // and test both of these together. + // So we need to get a build compiler stage N-1 to build the stage N components. run.builder.ensure(Cargotest { build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target), host: run.target, @@ -261,9 +264,15 @@ impl Step for Cargotest { /// This tool in `src/tools` will check out a few Rust projects and run `cargo /// test` to ensure that we don't regress the test suites there. fn run(self, builder: &Builder<'_>) { - // Here we need to ensure that we run cargo with an in-tree compiler, because we test - // both their behavior together. - // We can build cargo with the earlier stage compiler though. + // cargotest's staging has several pieces: + // consider ./x test cargotest --stage=2. + // + // The test goal is to exercise a (stage 2 cargo, stage 2 rustc) pair through a stage 2 + // cargotest tool. + // To produce the stage 2 cargo and cargotest, we need to do so with the stage 1 rustc and std. + // Importantly, the stage 2 rustc being tested (`tested_compiler`) via stage 2 cargotest is + // the rustc built by an earlier stage 1 rustc (the build_compiler). These are two different + // compilers! let cargo = builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host); @@ -298,6 +307,7 @@ impl Step for Cargotest { } /// Runs `cargo test` for cargo itself. +/// We label these tests as "cargo self-tests". #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { build_compiler: Compiler, @@ -328,7 +338,7 @@ impl Step for Cargo { /// Runs `cargo test` for `cargo` packaged with Rust. fn run(self, builder: &Builder<'_>) { - // When we do a "stage 1 cargo test", it means that we test the stage 1 rustc + // When we do a "stage 1 cargo self-test", it means that we test the stage 1 rustc // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then // run its tests against the stage 1 compiler (called `tested_compiler` below). builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host)); From ac28b5b93a834662f6a51139c262b71c04e16bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 4 Aug 2025 12:21:27 +0200 Subject: [PATCH 22/22] Fix splitting dylib paths --- src/bootstrap/src/core/build_steps/test.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 218a6f563e4fe..1d5b1cea604e4 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -4,7 +4,7 @@ //! However, this contains ~all test parts we expect people to be able to build and run locally. use std::collections::HashSet; -use std::env::join_paths; +use std::env::split_paths; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::{env, fs, iter}; @@ -34,8 +34,8 @@ use crate::core::config::flags::{Subcommand, get_completion}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ - self, LldThreads, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args, - linker_flags, t, target_supports_cranelift_backend, up_to_date, + self, LldThreads, add_dylib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, + linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, debug, envify}; @@ -380,16 +380,14 @@ impl Step for Cargo { // libdir. That causes issues in cargo test, because the programs that cargo compiles are // incorrectly picking that libdir, even though they should be picking the // `tested_compiler`'s libdir. We thus have to override the precedence here. - let existing_dylib_path = cargo + let mut existing_dylib_paths = cargo .get_envs() .find(|(k, _)| *k == OsStr::new(dylib_path_var())) .and_then(|(_, v)| v) - .unwrap_or(OsStr::new("")); - cargo.env( - dylib_path_var(), - join_paths([builder.rustc_libdir(tested_compiler).as_ref(), existing_dylib_path]) - .unwrap_or_default(), - ); + .map(|value| split_paths(value).collect::>()) + .unwrap_or_default(); + existing_dylib_paths.insert(0, builder.rustc_libdir(tested_compiler)); + add_dylib_path(existing_dylib_paths, &mut cargo); // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the