From 938859fbb3e3c0292ae23e3c412d58edc67262ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 17 Jun 2025 12:59:13 +0200 Subject: [PATCH 01/18] Make default check stage be 1, and error out on checking with stage 0 --- src/bootstrap/defaults/bootstrap.library.toml | 1 - src/bootstrap/src/core/config/config.rs | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/defaults/bootstrap.library.toml b/src/bootstrap/defaults/bootstrap.library.toml index 6a867093b781d..24ff87191d68b 100644 --- a/src/bootstrap/defaults/bootstrap.library.toml +++ b/src/bootstrap/defaults/bootstrap.library.toml @@ -1,7 +1,6 @@ # These defaults are meant for contributors to the standard library and documentation. [build] bench-stage = 1 -check-stage = 1 test-stage = 1 [rust] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 49b3fec4c358d..59693dc3e4cf6 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1025,7 +1025,7 @@ impl Config { || bench_stage.is_some(); config.stage = match config.cmd { - Subcommand::Check { .. } => flags_stage.or(check_stage).unwrap_or(0), + Subcommand::Check { .. } => flags_stage.or(check_stage).unwrap_or(1), Subcommand::Clippy { .. } | Subcommand::Fix => flags_stage.or(check_stage).unwrap_or(1), // `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden. Subcommand::Doc { .. } => { @@ -1052,9 +1052,16 @@ impl Config { }; // Now check that the selected stage makes sense, and if not, print a warning and end - if let (0, Subcommand::Build) = (config.stage, &config.cmd) { - eprintln!("WARNING: cannot build anything on stage 0. Use at least stage 1."); - exit!(1); + match (config.stage, &config.cmd) { + (0, Subcommand::Build) => { + eprintln!("WARNING: cannot build anything on stage 0. Use at least stage 1."); + exit!(1); + } + (0, Subcommand::Check { .. }) => { + eprintln!("WARNING: cannot check anything on stage 0. Use at least stage 1."); + exit!(1); + } + _ => {} } // CI should always run stage 2 builds, unless it specifically states otherwise From 01a7ab6ff06226b02c4962076bf6e9a470348378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 18 Jun 2025 22:27:37 +0200 Subject: [PATCH 02/18] Remove `custom_stage` override from `check::Std` and make 1 be the default check stage for it --- src/bootstrap/src/core/build_steps/check.rs | 65 ++++----------------- 1 file changed, 11 insertions(+), 54 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 6c5f70b2f4381..2d68931dadee5 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -20,16 +20,13 @@ pub struct Std { /// /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc crates: Vec, - /// Never use this from outside calls. It is intended for internal use only within `check::Std::make_run` - /// and `check::Std::run`. - custom_stage: Option, } impl Std { const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"]; pub fn new(target: TargetSelection) -> Self { - Self { target, crates: vec![], custom_stage: None } + Self { target, crates: vec![] } } } @@ -48,14 +45,7 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); - - let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 1 { - run.builder.top_stage - } else { - 1 - }; - - run.builder.ensure(Std { target: run.target, crates, custom_stage: Some(stage) }); + run.builder.ensure(Std { target: run.target, crates }); } fn run(self, builder: &Builder<'_>) { @@ -66,40 +56,20 @@ impl Step for Std { return; } - let stage = self.custom_stage.unwrap_or(builder.top_stage); - + let stage = builder.top_stage; let target = self.target; - let compiler = builder.compiler(stage, builder.config.host_target); - - if stage == 0 { - let mut is_explicitly_called = - builder.paths.iter().any(|p| p.starts_with("library") || p.starts_with("std")); - - if !is_explicitly_called { - for c in Std::CRATE_OR_DEPS { - is_explicitly_called = builder.paths.iter().any(|p| p.starts_with(c)); - } - } - - if is_explicitly_called { - eprintln!("WARNING: stage 0 std is precompiled and does nothing during `x check`."); - } - - // Reuse the stage0 libstd - builder.std(compiler, target); - return; - } + let build_compiler = builder.compiler(stage, builder.config.host_target); let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Std, SourceType::InTree, target, Kind::Check, ); - std_cargo(builder, target, compiler.stage, &mut cargo); + std_cargo(builder, target, build_compiler.stage, &mut cargo); if matches!(builder.config.cmd, Subcommand::Fix) { // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot. cargo.arg("--lib"); @@ -115,16 +85,9 @@ impl Step for Std { Some(stage), ); - let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check"); + let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check"); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); - // We skip populating the sysroot in non-zero stage because that'll lead - // to rlib/rmeta conflicts if std gets built during this session. - if compiler.stage == 0 { - let libdir = builder.sysroot_target_libdir(compiler, target); - let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &stamp); - } drop(_guard); // don't check test dependencies if we haven't built libtest @@ -140,21 +103,14 @@ impl Step for Std { // Currently only the "libtest" tree of crates does this. let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Std, SourceType::InTree, target, Kind::Check, ); - // If we're not in stage 0, tests and examples will fail to compile - // from `core` definitions being loaded from two different `libcore` - // .rmeta and .rlib files. - if compiler.stage == 0 { - cargo.arg("--all-targets"); - } - - std_cargo(builder, target, compiler.stage, &mut cargo); + std_cargo(builder, target, build_compiler.stage, &mut cargo); // Explicitly pass -p for all dependencies krates -- this will force cargo // to also check the tests/benches/examples for these crates, rather @@ -163,7 +119,8 @@ impl Step for Std { cargo.arg("-p").arg(krate); } - let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test"); + let stamp = + build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test"); let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage)); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } From ea72aa58fdd47fe0a23a56d6b2589cd980533b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Jul 2025 17:50:50 +0200 Subject: [PATCH 03/18] Make build compiler explicit in `check::Rustc` and `check::Std` --- src/bootstrap/src/core/build_steps/check.rs | 88 +++++++++++++------- src/bootstrap/src/core/build_steps/clippy.rs | 4 +- src/bootstrap/src/core/builder/tests.rs | 68 +++++++-------- 3 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 2d68931dadee5..591ad3315e1f3 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,5 +1,7 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. +use build_helper::exit; + use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -9,10 +11,12 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{self, BuildStamp}; -use crate::{Mode, Subcommand}; +use crate::{Compiler, Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { + /// Compiler that will check this std. + pub build_compiler: Compiler, pub target: TargetSelection, /// Whether to build only a subset of crates. /// @@ -25,8 +29,8 @@ pub struct Std { impl Std { const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"]; - pub fn new(target: TargetSelection) -> Self { - Self { target, crates: vec![] } + pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self { + Self { build_compiler, target, crates: vec![] } } } @@ -45,7 +49,11 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); - run.builder.ensure(Std { target: run.target, crates }); + run.builder.ensure(Std { + build_compiler: run.builder.compiler(run.builder.top_stage, run.target), + target: run.target, + crates, + }); } fn run(self, builder: &Builder<'_>) { @@ -56,9 +64,9 @@ impl Step for Std { return; } - let stage = builder.top_stage; + let build_compiler = self.build_compiler; + let stage = build_compiler.stage; let target = self.target; - let build_compiler = builder.compiler(stage, builder.config.host_target); let mut cargo = builder::Cargo::new( builder, @@ -69,7 +77,7 @@ impl Step for Std { Kind::Check, ); - std_cargo(builder, target, build_compiler.stage, &mut cargo); + std_cargo(builder, target, stage, &mut cargo); if matches!(builder.config.cmd, Subcommand::Fix) { // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot. cargo.arg("--lib"); @@ -126,12 +134,21 @@ impl Step for Std { } fn metadata(&self) -> Option { - Some(StepMetadata::check("std", self.target)) + Some(StepMetadata::check("std", self.target).built_by(self.build_compiler)) } } +fn default_compiler_for_checking_rustc(builder: &Builder<'_>) -> Compiler { + // When checking the stage N compiler, we want to do it with the stage N-1 compiler, + builder.compiler(builder.top_stage - 1, builder.config.host_target) +} + +/// Checks rustc using `build_compiler` and copies the built +/// .rmeta files into the sysroot of `build_copoiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustc { + /// Compiler that will check this rustc. + pub build_compiler: Compiler, pub target: TargetSelection, /// Whether to build only a subset of crates. /// @@ -142,13 +159,13 @@ pub struct Rustc { } impl Rustc { - pub fn new(target: TargetSelection, builder: &Builder<'_>) -> Self { + pub fn new(builder: &Builder<'_>, build_compiler: Compiler, target: TargetSelection) -> Self { let crates = builder .in_tree_crates("rustc-main", Some(target)) .into_iter() .map(|krate| krate.name.to_string()) .collect(); - Self { target, crates } + Self { build_compiler, target, crates } } } @@ -163,40 +180,46 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { let crates = run.make_run_crates(Alias::Compiler); - run.builder.ensure(Rustc { target: run.target, crates }); + run.builder.ensure(Rustc { + target: run.target, + build_compiler: default_compiler_for_checking_rustc(run.builder), + crates, + }); } - /// Builds the compiler. + /// Check the compiler. /// - /// This will build the compiler for a particular stage of the build using + /// This will check the compiler for a particular stage of the build using /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + if builder.top_stage < 2 && builder.config.host_target != self.target { + eprintln!("Cannot do a cross-compilation check on stage 1, use stage 2"); + exit!(1); + } + + let build_compiler = self.build_compiler; let target = self.target; - if compiler.stage != 0 { - // If we're not in stage 0, then we won't have a std from the beta - // compiler around. That means we need to make sure there's one in - // the sysroot for the compiler to find. Otherwise, we're going to - // fail when building crates that need to generate code (e.g., build - // scripts and their dependencies). - builder.std(compiler, compiler.host); - builder.std(compiler, target); - } else { - builder.ensure(Std::new(target)); - } + // Build host std for compiling build scripts + builder.std(build_compiler, build_compiler.host); + + // Build target std so that the checked rustc can link to it during the check + // FIXME: maybe we can a way to only do a check of std here? + // But for that we would have to copy the stdlib rmetas to the sysroot of the build + // compiler, which conflicts with std rlibs, if we also build std. + builder.std(build_compiler, target); let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Rustc, SourceType::InTree, target, Kind::Check, ); - rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates); + rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); // Explicitly pass -p for all compiler crates -- this will force cargo // to also check the tests/benches/examples for these crates, rather @@ -211,17 +234,18 @@ impl Step for Rustc { None, ); - let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check"); + let stamp = + build_stamp::librustc_stamp(builder, build_compiler, target).with_prefix("check"); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); - let libdir = builder.sysroot_target_libdir(compiler, target); - let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); + let libdir = builder.sysroot_target_libdir(build_compiler, target); + let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host); add_to_sysroot(builder, &libdir, &hostdir, &stamp); } fn metadata(&self) -> Option { - Some(StepMetadata::check("rustc", self.target)) + Some(StepMetadata::check("rustc", self.target).built_by(self.build_compiler)) } } @@ -462,7 +486,7 @@ fn run_tool_check_step( let display_name = path.rsplit('/').next().unwrap(); let compiler = builder.compiler(builder.top_stage, builder.config.host_target); - builder.ensure(Rustc::new(target, builder)); + builder.ensure(Rustc::new(builder, compiler, target)); let mut cargo = prepare_tool_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 1e44b5b67a44c..a0371eb71556b 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -215,7 +215,7 @@ impl Step for Rustc { builder.std(compiler, compiler.host); builder.std(compiler, target); } else { - builder.ensure(check::Std::new(target)); + builder.ensure(check::Std::new(compiler, target)); } } @@ -287,7 +287,7 @@ macro_rules! lint_any { let target = self.target; if !builder.download_rustc() { - builder.ensure(check::Rustc::new(target, builder)); + builder.ensure(check::Rustc::new(builder, compiler, target)); }; let cargo = prepare_tool_cargo( diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1c5267cb75e9b..4d2d25cd6c784 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1240,24 +1240,21 @@ mod snapshot { ctx.config("check") .path("compiler") .render_steps(), @r" - [check] std [build] llvm - [check] rustc - [check] cranelift - [check] gcc + [check] rustc 0 -> rustc 1 "); insta::assert_snapshot!( ctx.config("check") .path("rustc") .render_steps(), @r" - [check] std [build] llvm - [check] rustc + [check] rustc 0 -> rustc 1 "); } #[test] + #[should_panic] fn check_compiler_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("compiler").stage(0).run(); @@ -1272,11 +1269,7 @@ mod snapshot { .stage(1) .render_steps(), @r" [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [check] rustc - [check] cranelift - [check] gcc + [check] rustc 0 -> rustc 1 "); } @@ -1291,11 +1284,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 2 -> std 2 - [check] rustc - [check] cranelift - [check] gcc + [check] rustc 1 -> rustc 2 "); } @@ -1311,23 +1300,24 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [build] rustc 1 -> std 1 + [build] llvm + [check] rustc 1 -> rustc 2 + [check] rustc 1 -> Rustdoc 2 + [check] rustc 1 -> cranelift 2 + [check] rustc 1 -> gcc 2 + [check] rustc 1 -> Clippy 2 + [check] rustc 1 -> Miri 2 + [check] rustc 1 -> CargoMiri 2 + [check] rustc 0 -> MiroptTestTools 1 + [check] rustc 1 -> Rustfmt 2 + [check] rustc 1 -> rust-analyzer 2 [build] rustc 1 -> rustc 2 [build] rustc 2 -> std 2 - [build] rustc 1 -> std 1 [build] rustc 2 -> std 2 - [check] rustc - [check] Rustdoc - [check] cranelift - [check] gcc - [check] Clippy - [check] Miri - [check] CargoMiri - [check] MiroptTestTools - [check] Rustfmt - [check] rust-analyzer - [check] TestFloatParse - [check] FeaturesStatusDump - [check] std + [check] rustc 2 -> TestFloatParse 3 + [check] rustc 0 -> FeaturesStatusDump 1 + [check] rustc 2 -> std 2 "); } @@ -1340,11 +1330,12 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [check] std + [check] rustc 1 -> std 1 "); } #[test] + #[should_panic] fn check_library_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("library").stage(0).run(); @@ -1360,7 +1351,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [check] std + [check] rustc 1 -> std 1 "); } @@ -1376,7 +1367,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [check] std + [check] rustc 2 -> std 2 "); } @@ -1402,14 +1393,15 @@ mod snapshot { ctx.config("check") .path("miri") .render_steps(), @r" - [check] std [build] llvm - [check] rustc + [build] rustc 0 -> rustc 1 + [check] rustc 0 -> rustc 1 [check] Miri "); } #[test] + #[should_panic] fn check_miri_stage_0() { let ctx = TestCtx::new(); ctx.config("check").path("miri").stage(0).run(); @@ -1425,8 +1417,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [check] rustc + [check] rustc 0 -> rustc 1 [check] Miri "); } @@ -1443,8 +1434,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 - [build] rustc 2 -> std 2 - [check] rustc + [check] rustc 1 -> rustc 2 [check] Miri "); } From ae93d1f8ebb6e421acc5e81f8d1d96e651d697c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 25 Jun 2025 17:24:41 +0200 Subject: [PATCH 04/18] Fixup check of rust-analyzer, codegen backends, compiletest and other tools --- src/bootstrap/src/core/build_steps/check.rs | 136 ++++++++++++++------ src/bootstrap/src/core/build_steps/tool.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 39 +++--- 3 files changed, 113 insertions(+), 64 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 591ad3315e1f3..315c12ee5b441 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -144,7 +144,7 @@ fn default_compiler_for_checking_rustc(builder: &Builder<'_>) -> Compiler { } /// Checks rustc using `build_compiler` and copies the built -/// .rmeta files into the sysroot of `build_copoiler`. +/// .rmeta files into the sysroot of `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustc { /// Compiler that will check this rustc. @@ -249,8 +249,19 @@ impl Step for Rustc { } } +/// Prepares a build compiler sysroot that will check a `Mode::ToolRustc` tool. +/// Also checks rustc using this compiler, to prepare .rmetas that the tool will link to. +fn prepare_compiler_for_tool_rustc(builder: &Builder<'_>, target: TargetSelection) -> Compiler { + // When we check tool stage N, we check it with compiler stage N-1 + let build_compiler = builder.compiler(builder.top_stage - 1, builder.config.host_target); + builder.ensure(Rustc::new(builder, build_compiler, target)); + build_compiler +} + +/// Checks a single codegen backend. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CodegenBackend { + pub build_compiler: Compiler, pub target: TargetSelection, pub backend: &'static str, } @@ -265,8 +276,10 @@ impl Step for CodegenBackend { } fn make_run(run: RunConfig<'_>) { + // FIXME: only check the backend(s) that were actually selected in run.paths + let build_compiler = prepare_compiler_for_tool_rustc(run.builder, run.target); for &backend in &["cranelift", "gcc"] { - run.builder.ensure(CodegenBackend { target: run.target, backend }); + run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend }); } } @@ -277,15 +290,13 @@ impl Step for CodegenBackend { return; } - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + let build_compiler = self.build_compiler; let target = self.target; let backend = self.backend; - builder.ensure(Rustc::new(target, builder)); - let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Codegen, SourceType::InTree, target, @@ -295,23 +306,25 @@ impl Step for CodegenBackend { cargo .arg("--manifest-path") .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); - rustc_cargo_env(builder, &mut cargo, target, compiler.stage); + rustc_cargo_env(builder, &mut cargo, target, build_compiler.stage); - let _guard = builder.msg_check(backend, target, None); + let _guard = builder.msg_check(&format!("rustc_codegen_{backend}"), target, None); - let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend) + let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, backend) .with_prefix("check"); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } fn metadata(&self) -> Option { - Some(StepMetadata::check(self.backend, self.target)) + Some(StepMetadata::check(self.backend, self.target).built_by(self.build_compiler)) } } +/// Checks Rust analyzer that links to .rmetas from a checked rustc. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RustAnalyzer { + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -332,18 +345,17 @@ impl Step for RustAnalyzer { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustAnalyzer { target: run.target }); + let build_compiler = prepare_compiler_for_tool_rustc(run.builder, run.target); + run.builder.ensure(RustAnalyzer { build_compiler, target: run.target }); } fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + let build_compiler = self.build_compiler; let target = self.target; - builder.ensure(Rustc::new(target, builder)); - let mut cargo = prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolRustc, target, builder.kind, @@ -360,7 +372,7 @@ impl Step for RustAnalyzer { // Cargo's output path in a given stage, compiled by a particular // compiler for the specified target. - let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::ToolRustc, target)) .with_prefix("rust-analyzer-check"); let _guard = builder.msg_check("rust-analyzer artifacts", target, None); @@ -368,7 +380,7 @@ impl Step for RustAnalyzer { } fn metadata(&self) -> Option { - Some(StepMetadata::check("rust-analyzer", self.target)) + Some(StepMetadata::check("rust-analyzer", self.target).built_by(self.build_compiler)) } } @@ -405,7 +417,7 @@ impl Step for Compiletest { ); if mode != Mode::ToolBootstrap { - builder.ensure(Rustc::new(self.target, builder)); + builder.std(compiler, self.target); } let mut cargo = prepare_tool_cargo( @@ -441,12 +453,14 @@ macro_rules! tool_check_step { // The part of this path after the final '/' is also used as a display name. path: $path:literal $(, alt_path: $alt_path:literal )* + , mode: $mode:path $(, default: $default:literal )? $( , )? } ) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -461,16 +475,33 @@ macro_rules! tool_check_step { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure($name { target: run.target }); + let host = run.builder.config.host_target; + let target = run.target; + let build_compiler = match $mode { + Mode::ToolBootstrap => run.builder.compiler(0, host), + Mode::ToolStd => { + // A small number of tools rely on in-tree standard + // library crates (e.g. compiletest needs libtest). + let build_compiler = run.builder.compiler(run.builder.top_stage, host); + run.builder.std(build_compiler, host); + run.builder.std(build_compiler, target); + build_compiler + } + Mode::ToolRustc => { + prepare_compiler_for_tool_rustc(run.builder, target) + } + _ => panic!("unexpected mode for tool check step: {:?}", $mode), + }; + run.builder.ensure($name { target, build_compiler }); } fn run(self, builder: &Builder<'_>) { - let Self { target } = self; - run_tool_check_step(builder, target, stringify!($name), $path); + let Self { target, build_compiler } = self; + run_tool_check_step(builder, build_compiler, target, $path, $mode); } fn metadata(&self) -> Option { - Some(StepMetadata::check(stringify!($name), self.target)) + Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler)) } } } @@ -479,19 +510,17 @@ macro_rules! tool_check_step { /// Used by the implementation of `Step::run` in `tool_check_step!`. fn run_tool_check_step( builder: &Builder<'_>, + build_compiler: Compiler, target: TargetSelection, - step_type_name: &str, path: &str, + mode: Mode, ) { let display_name = path.rsplit('/').next().unwrap(); - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); - - builder.ensure(Rustc::new(builder, compiler, target)); let mut cargo = prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, + build_compiler, + mode, target, builder.kind, path, @@ -503,33 +532,56 @@ fn run_tool_check_step( &[], ); + // FIXME: check bootstrap doesn't currently work with --all-targets cargo.arg("--all-targets"); - let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) - .with_prefix(&format!("{}-check", step_type_name.to_lowercase())); + let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target)) + .with_prefix(&format!("{display_name}-check")); + + let stage = match mode { + // Mode::ToolRustc is included here because of how msg_sysroot_tool prints stages + Mode::Std | Mode::ToolRustc => build_compiler.stage, + _ => build_compiler.stage + 1, + }; - let _guard = builder.msg_check(format!("{display_name} artifacts"), target, None); + let _guard = + builder.msg_tool(builder.kind, mode, display_name, stage, &build_compiler.host, &target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } -tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc" }); +tool_check_step!(Rustdoc { + path: "src/tools/rustdoc", + alt_path: "src/librustdoc", + mode: Mode::ToolRustc +}); // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead // of a submodule. Since the SourceType only drives the deny-warnings // behavior, treat it as in-tree so that any new warnings in clippy will be // rejected. -tool_check_step!(Clippy { path: "src/tools/clippy" }); -tool_check_step!(Miri { path: "src/tools/miri" }); -tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri" }); -tool_check_step!(Rustfmt { path: "src/tools/rustfmt" }); -tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" }); -tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse" }); -tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump" }); - -tool_check_step!(Bootstrap { path: "src/bootstrap", default: false }); +tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustc }); +tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustc }); +tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustc }); +tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustc }); +tool_check_step!(MiroptTestTools { + path: "src/tools/miropt-test-tools", + mode: Mode::ToolBootstrap +}); +// We want to test the local std +tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse", mode: Mode::ToolStd }); +tool_check_step!(FeaturesStatusDump { + path: "src/tools/features-status-dump", + mode: Mode::ToolBootstrap +}); + +tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false }); // `run-make-support` will be built as part of suitable run-make compiletest test steps, but support // check to make it easier to work on. -tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false }); +tool_check_step!(RunMakeSupport { + path: "src/tools/run-make-support", + mode: Mode::ToolBootstrap, + default: false +}); /// Check step for the `coverage-dump` bootstrap tool. The coverage-dump tool /// is used internally by coverage tests. diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index ad3f8d8976701..b05b34b9b2280 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -77,7 +77,7 @@ impl Builder<'_> { *target, ), // doesn't depend on compiler, same as host compiler - _ => self.msg(Kind::Build, build_stage, format_args!("tool {tool}"), *host, *target), + _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target), } } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 4d2d25cd6c784..38215d71f41ec 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1242,6 +1242,8 @@ mod snapshot { .render_steps(), @r" [build] llvm [check] rustc 0 -> rustc 1 + [check] rustc 0 -> cranelift 1 + [check] rustc 0 -> gcc 1 "); insta::assert_snapshot!( @@ -1270,6 +1272,8 @@ mod snapshot { .render_steps(), @r" [build] llvm [check] rustc 0 -> rustc 1 + [check] rustc 0 -> cranelift 1 + [check] rustc 0 -> gcc 1 "); } @@ -1285,6 +1289,8 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> rustc 2 + [check] rustc 1 -> cranelift 2 + [check] rustc 1 -> gcc 2 "); } @@ -1372,6 +1378,7 @@ mod snapshot { } #[test] + #[should_panic] fn check_library_cross_compile() { let ctx = TestCtx::new(); insta::assert_snapshot!( @@ -1394,9 +1401,8 @@ mod snapshot { .path("miri") .render_steps(), @r" [build] llvm - [build] rustc 0 -> rustc 1 [check] rustc 0 -> rustc 1 - [check] Miri + [check] rustc 0 -> Miri 1 "); } @@ -1416,9 +1422,8 @@ mod snapshot { .stage(1) .render_steps(), @r" [build] llvm - [build] rustc 0 -> rustc 1 [check] rustc 0 -> rustc 1 - [check] Miri + [check] rustc 0 -> Miri 1 "); } @@ -1433,9 +1438,8 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 [check] rustc 1 -> rustc 2 - [check] Miri + [check] rustc 1 -> Miri 2 "); } @@ -1456,9 +1460,9 @@ mod snapshot { .path("compiletest") .args(&["--set", "build.compiletest-use-stage0-libtest=false"]) .render_steps(), @r" - [check] std [build] llvm - [check] rustc + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 [check] compiletest "); } @@ -1470,11 +1474,10 @@ mod snapshot { ctx.config("check") .path("rustc_codegen_cranelift") .render_steps(), @r" - [check] std [build] llvm - [check] rustc - [check] cranelift - [check] gcc + [check] rustc 0 -> rustc 1 + [check] rustc 0 -> cranelift 1 + [check] rustc 0 -> gcc 1 "); } @@ -1485,10 +1488,9 @@ mod snapshot { ctx.config("check") .path("rust-analyzer") .render_steps(), @r" - [check] std [build] llvm - [check] rustc - [check] rust-analyzer + [check] rustc 0 -> rustc 1 + [check] rustc 0 -> rust-analyzer 1 "); } @@ -1498,12 +1500,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("run-make-support") - .render_steps(), @r" - [check] std - [build] llvm - [check] rustc - [check] RunMakeSupport - "); + .render_steps(), @"[check] rustc 0 -> RunMakeSupport 1 "); } #[test] From f37f066b875d6deaa22081192a86c08c9a48e5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 26 Jun 2025 09:24:52 +0200 Subject: [PATCH 05/18] Implement `CoverageDump` checking through the `tool_check_step` macro --- src/bootstrap/src/core/build_steps/check.rs | 71 ++------------------- 1 file changed, 5 insertions(+), 66 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 315c12ee5b441..e0456e21c3ad0 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -583,69 +583,8 @@ tool_check_step!(RunMakeSupport { default: false }); -/// Check step for the `coverage-dump` bootstrap tool. The coverage-dump tool -/// is used internally by coverage tests. -/// -/// FIXME(Zalathar): This is temporarily separate from the other tool check -/// steps so that it can use the stage 0 compiler instead of `top_stage`, -/// without introducing conflicts with the stage 0 redesign (#119899). -/// -/// After the stage 0 redesign lands, we can look into using the stage 0 -/// compiler to check all bootstrap tools (#139170). -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct CoverageDump; - -impl CoverageDump { - const PATH: &str = "src/tools/coverage-dump"; -} - -impl Step for CoverageDump { - type Output = (); - - /// Most contributors won't care about coverage-dump, so don't make their - /// check builds slower unless they opt in and check it explicitly. - const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path(Self::PATH) - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Self {}); - } - - fn run(self, builder: &Builder<'_>) -> Self::Output { - // Make sure we haven't forgotten any fields, if there are any. - let Self {} = self; - let display_name = "coverage-dump"; - let host = builder.config.host_target; - let target = host; - let mode = Mode::ToolBootstrap; - - let compiler = builder.compiler(0, host); - let cargo = prepare_tool_cargo( - builder, - compiler, - mode, - target, - builder.kind, - Self::PATH, - SourceType::InTree, - &[], - ); - - let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) - .with_prefix(&format!("{display_name}-check")); - - let _guard = builder.msg_tool( - builder.kind, - mode, - display_name, - compiler.stage, - &compiler.host, - &target, - ); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); - } -} +tool_check_step!(CoverageDump { + path: "src/tools/coverage-dump", + mode: Mode::ToolBootstrap, + default: false +}); From 4c5d073bc82b575967969ce7103ada5f6f03dc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 26 Jun 2025 09:40:22 +0200 Subject: [PATCH 06/18] Unify selection of build compiler for checking in a single function --- src/bootstrap/src/core/build_steps/check.rs | 95 ++++++++++----------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index e0456e21c3ad0..9f12059d4eec0 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -50,7 +50,7 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); run.builder.ensure(Std { - build_compiler: run.builder.compiler(run.builder.top_stage, run.target), + build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std), target: run.target, crates, }); @@ -138,11 +138,6 @@ impl Step for Std { } } -fn default_compiler_for_checking_rustc(builder: &Builder<'_>) -> Compiler { - // When checking the stage N compiler, we want to do it with the stage N-1 compiler, - builder.compiler(builder.top_stage - 1, builder.config.host_target) -} - /// Checks rustc using `build_compiler` and copies the built /// .rmeta files into the sysroot of `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -182,7 +177,7 @@ impl Step for Rustc { let crates = run.make_run_crates(Alias::Compiler); run.builder.ensure(Rustc { target: run.target, - build_compiler: default_compiler_for_checking_rustc(run.builder), + build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc), crates, }); } @@ -193,11 +188,6 @@ impl Step for Rustc { /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. fn run(self, builder: &Builder<'_>) { - if builder.top_stage < 2 && builder.config.host_target != self.target { - eprintln!("Cannot do a cross-compilation check on stage 1, use stage 2"); - exit!(1); - } - let build_compiler = self.build_compiler; let target = self.target; @@ -249,13 +239,45 @@ impl Step for Rustc { } } -/// Prepares a build compiler sysroot that will check a `Mode::ToolRustc` tool. -/// Also checks rustc using this compiler, to prepare .rmetas that the tool will link to. -fn prepare_compiler_for_tool_rustc(builder: &Builder<'_>, target: TargetSelection) -> Compiler { - // When we check tool stage N, we check it with compiler stage N-1 - let build_compiler = builder.compiler(builder.top_stage - 1, builder.config.host_target); - builder.ensure(Rustc::new(builder, build_compiler, target)); - build_compiler +/// Prepares a compiler that will check something with the given `mode`. +fn prepare_compiler_for_check( + builder: &Builder<'_>, + target: TargetSelection, + mode: Mode, +) -> Compiler { + let host = builder.host_target; + match mode { + Mode::ToolBootstrap => builder.compiler(0, host), + Mode::ToolStd => { + // A small number of tools rely on in-tree standard + // library crates (e.g. compiletest needs libtest). + let build_compiler = builder.compiler(builder.top_stage, host); + builder.std(build_compiler, host); + builder.std(build_compiler, target); + build_compiler + } + Mode::ToolRustc | Mode::Codegen => { + // When checking tool stage N, we check it with compiler stage N-1 + let build_compiler = builder.compiler(builder.top_stage - 1, host); + builder.ensure(Rustc::new(builder, build_compiler, target)); + build_compiler + } + Mode::Rustc => { + if builder.top_stage < 2 && host != target { + eprintln!("Cannot do a cross-compilation check of rustc on stage 1, use stage 2"); + exit!(1); + } + + // When checking the stage N compiler, we want to do it with the stage N-1 compiler + builder.compiler(builder.top_stage - 1, host) + } + Mode::Std => { + // When checking std stage N, we want to do it with the stage N compiler + // Note: we don't need to build the host stdlib here, because when compiling std, the + // stage 0 stdlib is used to compile build scripts and proc macros. + builder.compiler(builder.top_stage, host) + } + } } /// Checks a single codegen backend. @@ -277,7 +299,7 @@ impl Step for CodegenBackend { fn make_run(run: RunConfig<'_>) { // FIXME: only check the backend(s) that were actually selected in run.paths - let build_compiler = prepare_compiler_for_tool_rustc(run.builder, run.target); + let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); for &backend in &["cranelift", "gcc"] { run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend }); } @@ -345,7 +367,7 @@ impl Step for RustAnalyzer { } fn make_run(run: RunConfig<'_>) { - let build_compiler = prepare_compiler_for_tool_rustc(run.builder, run.target); + let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::ToolRustc); run.builder.ensure(RustAnalyzer { build_compiler, target: run.target }); } @@ -410,19 +432,11 @@ impl Step for Compiletest { } else { Mode::ToolStd }; - - let compiler = builder.compiler( - if mode == Mode::ToolBootstrap { 0 } else { builder.top_stage }, - builder.config.host_target, - ); - - if mode != Mode::ToolBootstrap { - builder.std(compiler, self.target); - } + let build_compiler = prepare_compiler_for_check(builder, self.target, mode); let mut cargo = prepare_tool_cargo( builder, - compiler, + build_compiler, mode, self.target, builder.kind, @@ -435,7 +449,7 @@ impl Step for Compiletest { cargo.arg("--all-targets"); - let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, self.target)) + let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, self.target)) .with_prefix("compiletest-check"); let _guard = builder.msg_check("compiletest artifacts", self.target, None); @@ -475,23 +489,8 @@ macro_rules! tool_check_step { } fn make_run(run: RunConfig<'_>) { - let host = run.builder.config.host_target; let target = run.target; - let build_compiler = match $mode { - Mode::ToolBootstrap => run.builder.compiler(0, host), - Mode::ToolStd => { - // A small number of tools rely on in-tree standard - // library crates (e.g. compiletest needs libtest). - let build_compiler = run.builder.compiler(run.builder.top_stage, host); - run.builder.std(build_compiler, host); - run.builder.std(build_compiler, target); - build_compiler - } - Mode::ToolRustc => { - prepare_compiler_for_tool_rustc(run.builder, target) - } - _ => panic!("unexpected mode for tool check step: {:?}", $mode), - }; + let build_compiler = prepare_compiler_for_check(run.builder, target, $mode); run.builder.ensure($name { target, build_compiler }); } From 2f88bff8724de0d8b27be2976e2468e0af87783a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 25 Jun 2025 16:32:09 +0200 Subject: [PATCH 07/18] Add change tracker entry --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 68312a503eec6..2965278c981b5 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -446,4 +446,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Added new option `build.tidy-extra-checks` to specify a default value for the --extra-checks cli flag.", }, + ChangeInfo { + change_id: 143048, + severity: ChangeSeverity::Warning, + summary: "The default check stage has been changed to 1. It is no longer possible to `x check` with stage 0. All check commands have to be on stage 1+.", + }, ]; From b82ed7d9076739dad82196ad235be58b3448ef51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Jul 2025 18:04:10 +0200 Subject: [PATCH 08/18] Add staging comment to `check::Rustc` --- src/bootstrap/src/core/build_steps/check.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 9f12059d4eec0..28644ecc340a2 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -187,6 +187,8 @@ impl Step for Rustc { /// This will check the compiler for a particular stage of the build using /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. + /// + /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it. fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; let target = self.target; From e112b5fe56a1ed913ef09587a7157899861f23b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Jul 2025 18:28:00 +0200 Subject: [PATCH 09/18] Update `Mode::ToolStd` comment --- src/bootstrap/src/core/build_steps/check.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 28644ecc340a2..555a02ac5872d 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -251,10 +251,15 @@ fn prepare_compiler_for_check( match mode { Mode::ToolBootstrap => builder.compiler(0, host), Mode::ToolStd => { - // A small number of tools rely on in-tree standard - // library crates (e.g. compiletest needs libtest). + // These tools require the local standard library to be checked let build_compiler = builder.compiler(builder.top_stage, host); + + // We need to build the host stdlib to check the tool itself. + // We need to build the target stdlib so that the tool can link to it. builder.std(build_compiler, host); + // We could only check this library in theory, but `check::Std` doesn't copy rmetas + // into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it + // instead. builder.std(build_compiler, target); build_compiler } From d3a8fbc69ed1fe364c39891d85ab1d246c30fcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Jul 2025 17:56:24 +0200 Subject: [PATCH 10/18] Fix CI --- src/ci/docker/host-x86_64/pr-check-1/Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile index 7a8056b70dc22..05b71df92cc52 100644 --- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile @@ -45,11 +45,12 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/ # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. ENV SCRIPT \ /scripts/check-default-config-profiles.sh && \ - python3 ../x.py build --stage 1 src/tools/build-manifest && \ + python3 ../x.py build src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ - python3 ../x.py check --stage 1 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ - python3 ../x.py check --stage 1 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ + # We use --stage 2 because we're cross-compiling + python3 ../x.py check --stage 2 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/validate-toolstate.sh && \ reuse --include-submodules lint && \ python3 ../x.py test collect-license-metadata && \ From 996cd081727d7090fb245673da25e217a276de85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 3 Jul 2025 18:29:46 +0200 Subject: [PATCH 11/18] Update library cross-compilation test --- src/bootstrap/src/core/builder/tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 38215d71f41ec..f0dba1a85f348 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1378,7 +1378,6 @@ mod snapshot { } #[test] - #[should_panic] fn check_library_cross_compile() { let ctx = TestCtx::new(); insta::assert_snapshot!( @@ -1388,8 +1387,8 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [check] std - [check] std + [check] rustc 1 -> std 1 + [check] rustc 1 -> std 1 "); } From 05e94e855375b5934a1fc42ddd2d8474f540eb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 10:17:08 +0200 Subject: [PATCH 12/18] Add `#[tracing::instrument]` to the LLVM build step --- src/bootstrap/src/core/build_steps/llvm.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index de67a5f77e643..b2056f5cf3786 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -263,6 +263,15 @@ impl Step for Llvm { } /// Compile LLVM for `target`. + #[cfg_attr( + feature = "tracing", + instrument( + level = "debug", + name = "Llvm::run", + skip_all, + fields(target = ?self.target), + ), + )] fn run(self, builder: &Builder<'_>) -> LlvmResult { let target = self.target; let target_native = if self.target.starts_with("riscv") { From e38a28d4bac9251042eadf15b50b14cfb16f2dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 10:55:12 +0200 Subject: [PATCH 13/18] Use stage auto-bump when cross-checking on stage 1 --- src/bootstrap/src/core/build_steps/check.rs | 22 +++++++++++---------- src/bootstrap/src/core/builder/tests.rs | 9 ++------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 555a02ac5872d..40632f35bd77e 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,5 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use build_helper::exit; - use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -264,19 +262,23 @@ fn prepare_compiler_for_check( build_compiler } Mode::ToolRustc | Mode::Codegen => { + // FIXME: this is a hack, see description of Mode::Rustc below + let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage }; // When checking tool stage N, we check it with compiler stage N-1 - let build_compiler = builder.compiler(builder.top_stage - 1, host); + let build_compiler = builder.compiler(stage, host); builder.ensure(Rustc::new(builder, build_compiler, target)); build_compiler } Mode::Rustc => { - if builder.top_stage < 2 && host != target { - eprintln!("Cannot do a cross-compilation check of rustc on stage 1, use stage 2"); - exit!(1); - } - - // When checking the stage N compiler, we want to do it with the stage N-1 compiler - builder.compiler(builder.top_stage - 1, host) + // This is a horrible hack, because we actually change the compiler stage numbering + // here. If you do `x check --stage 1 --host FOO`, we build stage 1 host rustc, + // and use that to check stage 1 FOO rustc (which actually makes that stage 2 FOO + // rustc). + // + // FIXME: remove this and either fix cross-compilation check on stage 2 (which has a + // myriad of other problems) or disable cross-checking on stage 1. + let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage }; + builder.compiler(stage, host) } Mode::Std => { // When checking std stage N, we want to do it with the stage N compiler diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f0dba1a85f348..a6bd531fd14b7 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1299,7 +1299,6 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("check") - .stage(2) .targets(&[TEST_TRIPLE_1]) .hosts(&[TEST_TRIPLE_1]) .render_steps(), @r" @@ -1307,7 +1306,6 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [build] rustc 1 -> std 1 - [build] llvm [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> cranelift 2 @@ -1318,12 +1316,9 @@ mod snapshot { [check] rustc 0 -> MiroptTestTools 1 [check] rustc 1 -> Rustfmt 2 [check] rustc 1 -> rust-analyzer 2 - [build] rustc 1 -> rustc 2 - [build] rustc 2 -> std 2 - [build] rustc 2 -> std 2 - [check] rustc 2 -> TestFloatParse 3 + [check] rustc 1 -> TestFloatParse 2 [check] rustc 0 -> FeaturesStatusDump 1 - [check] rustc 2 -> std 2 + [check] rustc 1 -> std 1 "); } From f0aa2a830f275014e996fe162d343065221468fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 11:46:23 +0200 Subject: [PATCH 14/18] Add support for allowing features when checking tools --- src/bootstrap/src/core/build_steps/check.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 40632f35bd77e..dad8e69e9333c 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -3,6 +3,7 @@ use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; +use crate::core::build_steps::tool; use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo}; use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, @@ -477,6 +478,7 @@ macro_rules! tool_check_step { path: $path:literal $(, alt_path: $alt_path:literal )* , mode: $mode:path + $(, allow_features: $allow_features:expr )? $(, default: $default:literal )? $( , )? } @@ -505,7 +507,12 @@ macro_rules! tool_check_step { fn run(self, builder: &Builder<'_>) { let Self { target, build_compiler } = self; - run_tool_check_step(builder, build_compiler, target, $path, $mode); + let allow_features = { + let mut _value = ""; + $( _value = $allow_features; )? + _value + }; + run_tool_check_step(builder, build_compiler, target, $path, $mode, allow_features); } fn metadata(&self) -> Option { @@ -522,6 +529,7 @@ fn run_tool_check_step( target: TargetSelection, path: &str, mode: Mode, + allow_features: &str, ) { let display_name = path.rsplit('/').next().unwrap(); @@ -539,6 +547,7 @@ fn run_tool_check_step( SourceType::InTree, &[], ); + cargo.allow_features(allow_features); // FIXME: check bootstrap doesn't currently work with --all-targets cargo.arg("--all-targets"); @@ -575,7 +584,11 @@ tool_check_step!(MiroptTestTools { mode: Mode::ToolBootstrap }); // We want to test the local std -tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse", mode: Mode::ToolStd }); +tool_check_step!(TestFloatParse { + path: "src/tools/test-float-parse", + mode: Mode::ToolStd, + allow_features: tool::TestFloatParse::ALLOW_FEATURES +}); tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump", mode: Mode::ToolBootstrap From dddb831ebb3efce799750cf6e84135ce51dd96ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 12:42:47 +0200 Subject: [PATCH 15/18] Horrible hack to make codegen backends "work" during check --- src/bootstrap/src/core/build_steps/compile.rs | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c3a3eddd16128..82de2bcb4b67b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1742,17 +1742,19 @@ fn copy_codegen_backends_to_sysroot( } let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); - let dylib = t!(fs::read_to_string(stamp.path())); - let file = Path::new(&dylib); - let filename = file.file_name().unwrap().to_str().unwrap(); - // change `librustc_codegen_cranelift-xxxxxx.so` to - // `librustc_codegen_cranelift-release.so` - let target_filename = { - let dash = filename.find('-').unwrap(); - let dot = filename.find('.').unwrap(); - format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) - }; - builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); + if stamp.path().exists() { + let dylib = t!(fs::read_to_string(stamp.path())); + let file = Path::new(&dylib); + let filename = file.file_name().unwrap().to_str().unwrap(); + // change `librustc_codegen_cranelift-xxxxxx.so` to + // `librustc_codegen_cranelift-release.so` + let target_filename = { + let dash = filename.find('-').unwrap(); + let dot = filename.find('.').unwrap(); + format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) + }; + builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); + } } } @@ -2163,6 +2165,25 @@ impl Step for Assemble { continue; // Already built as part of rustc } + // FIXME: this is a horrible hack used to make `x check` work when other codegen + // backends are enabled. + // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot. + // Then it checks codegen backends, which correctly use these rmetas. + // Then it needs to check std, but for that it needs to build stage 1 rustc. + // This copies the build rmetas into the stage0 sysroot, effectively poisoning it, + // because we then have both check and build rmetas in the same sysroot. + // That would be fine on its own. However, when another codegen backend is enabled, + // then building stage 1 rustc implies also building stage 1 codegen backend (even if + // it isn't used for anything). And since that tries to use the poisoned + // rmetas, it fails to build. + // We don't actually need to build rustc-private codegen backends for checking std, + // so instead we skip that. + // Note: this would be also an issue for other rustc-private tools, but that is "solved" + // by check::Std being last in the list of checked things (see + // `Builder::get_step_descriptions`). + if builder.kind == Kind::Check && builder.top_stage == 1 { + continue; + } builder.ensure(CodegenBackend { compiler: build_compiler, target: target_compiler.host, From 263f2024532550a15894161dc5c8b9857777ee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 22:11:22 +0200 Subject: [PATCH 16/18] Fix Clippy --- src/bootstrap/src/core/build_steps/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index dad8e69e9333c..3a88469735be6 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -340,7 +340,7 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); rustc_cargo_env(builder, &mut cargo, target, build_compiler.stage); - let _guard = builder.msg_check(&format!("rustc_codegen_{backend}"), target, None); + let _guard = builder.msg_check(format!("rustc_codegen_{backend}"), target, None); let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, backend) .with_prefix("check"); From 49cc81d352f6c365f13ce65a920152f4b9f83d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 23:16:51 +0200 Subject: [PATCH 17/18] Use stage 1 check in `pr-check-1` again --- src/ci/docker/host-x86_64/pr-check-1/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile index 05b71df92cc52..8bbcc18e2bef8 100644 --- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile @@ -48,8 +48,7 @@ ENV SCRIPT \ python3 ../x.py build src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ - # We use --stage 2 because we're cross-compiling - python3 ../x.py check --stage 2 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/validate-toolstate.sh && \ reuse --include-submodules lint && \ From cf0eddb2f1def7ee6e1c8ee2611f53cec39bba29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 5 Jul 2025 23:56:17 +0200 Subject: [PATCH 18/18] Do not ever cross-check bootstrap tools --- src/bootstrap/src/core/build_steps/check.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 3a88469735be6..0497bae86a1c8 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -502,6 +502,13 @@ macro_rules! tool_check_step { fn make_run(run: RunConfig<'_>) { let target = run.target; let build_compiler = prepare_compiler_for_check(run.builder, target, $mode); + + // It doesn't make sense to cross-check bootstrap tools + if $mode == Mode::ToolBootstrap && target != run.builder.host_target { + println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path)); + return; + }; + run.builder.ensure($name { target, build_compiler }); }