From a1d1c59566aad7cbe28e1185e98e8a7cd59b5a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 11:53:25 +0200 Subject: [PATCH 01/23] Add snapshot test for `tier-check` --- src/bootstrap/src/core/builder/tests.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 3c2cb7828501d..5e20610841135 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2069,6 +2069,19 @@ mod snapshot { "); } + #[test] + fn test_tier_check() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("tier-check") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + "); + } + #[test] fn doc_all() { let ctx = TestCtx::new(); From 749a9745452d943bf658024f2d14e02437d66c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 11:55:07 +0200 Subject: [PATCH 02/23] Fixup `x test tier-check` --- src/bootstrap/src/core/build_steps/test.rs | 40 +++++++++++----------- src/bootstrap/src/core/builder/tests.rs | 2 +- src/bootstrap/src/lib.rs | 19 ++++++++++ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index abe79405984b1..c182493761e18 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3250,9 +3250,15 @@ impl Step for Bootstrap { } } +fn get_compiler_to_test(builder: &Builder<'_>, target: TargetSelection) -> Compiler { + builder.compiler(builder.top_stage, target) +} + +/// Tests the Platform Support page in the rustc book. +/// `test_compiler` is used to query the actual targets that are checked. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TierCheck { - pub compiler: Compiler, + test_compiler: Compiler, } impl Step for TierCheck { @@ -3265,42 +3271,36 @@ impl Step for TierCheck { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler_for( - run.builder.top_stage, - run.builder.build.host_target, - run.target, - ); - run.builder.ensure(TierCheck { compiler }); + run.builder + .ensure(TierCheck { test_compiler: get_compiler_to_test(run.builder, run.target) }); } - /// Tests the Platform Support page in the rustc book. fn run(self, builder: &Builder<'_>) { - builder.std(self.compiler, self.compiler.host); + let tool_build_compiler = builder.compiler(0, builder.host_target); + let mut cargo = tool::prepare_tool_cargo( builder, - self.compiler, - Mode::ToolStd, - self.compiler.host, + tool_build_compiler, + Mode::ToolBootstrap, + tool_build_compiler.host, Kind::Run, "src/tools/tier-check", SourceType::InTree, &[], ); cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md")); - cargo.arg(builder.rustc(self.compiler)); + cargo.arg(builder.rustc(self.test_compiler)); if builder.is_verbose() { cargo.arg("--verbose"); } - let _guard = builder.msg( - Kind::Test, - "platform support check", - None, - self.compiler, - self.compiler.host, - ); + let _guard = builder.msg_test("platform support check", self.test_compiler); BootstrapCommand::from(cargo).delay_failure().run(builder); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("tier-check", self.test_compiler.host)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5e20610841135..d255ed0fbbd84 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2078,7 +2078,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 + [test] rustc 0 -> tier-check 1 "); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index b8ee83b20e46b..1beeb16b44f37 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1159,6 +1159,25 @@ impl Build { self.group(&msg) } + /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target` + /// (determined by `host_and_stage`). + /// Use this instead of [`Builder::msg`] when there is no clear `build_compiler` to be + /// determined. + /// + /// [`Step`]: crate::core::builder::Step + #[must_use = "Groups should not be dropped until the Step finishes running"] + #[track_caller] + fn msg_test( + &self, + what: impl Display, + host_and_stage: impl Into, + ) -> Option { + let HostAndStage { host, stage } = host_and_stage.into(); + let action = Kind::Test.description(); + let msg = format!("{action} stage{stage} {what} ({host})"); + self.group(&msg) + } + /// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`. /// /// [`Step`]: crate::core::builder::Step From fbaed592ba227181c22b8d53b7acb6b8d8a34765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 13:46:21 +0200 Subject: [PATCH 03/23] Forbid running tests on stage 0 unless `build.compiletest-allow-stage0` is enabled --- src/bootstrap/src/core/config/config.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index a8eb563015f51..450f325f1a970 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1002,12 +1002,18 @@ impl Config { (0, Subcommand::Install) => { check_stage0("install"); } + (0, Subcommand::Test { .. }) if build_compiletest_allow_stage0 != Some(true) => { + eprintln!( + "ERROR: cannot test anything on stage 0. Use at least stage 1. If you want to run compiletest with an external stage0 toolchain, enable `build.compiletest-allow-stage0`." + ); + exit!(1); + } _ => {} } if flags_compile_time_deps && !matches!(flags_cmd, Subcommand::Check { .. }) { eprintln!( - "WARNING: Can't use --compile-time-deps with any subcommand other than check." + "ERROR: Can't use --compile-time-deps with any subcommand other than check." ); exit!(1); } From 43f1f63c586a7f1644c810c7341979adbe92a73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 13:58:12 +0200 Subject: [PATCH 04/23] Add snapshot test for `x test` --- src/bootstrap/src/core/builder/tests.rs | 72 ++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d255ed0fbbd84..d17c3b982d2a5 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1990,6 +1990,76 @@ mod snapshot { .render_steps(), @"[check] rustc 0 -> RunMakeSupport 1 "); } + #[test] + fn test_all() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .render_steps(), @r" + [build] rustc 0 -> Tidy 1 + [test] tidy <> + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Compiletest 1 + [test] Ui + [test] Crashes + [build] rustc 0 -> CoverageDump 1 + [build] rustc 1 -> std 1 + [test] CodegenLlvm + [test] CodegenUnits + [test] AssemblyLlvm + [test] Incremental + [test] Debuginfo + [test] UiFullDeps + [build] rustdoc 1 + [test] Rustdoc + [test] CoverageRunRustdoc + [test] Pretty + [build] rustc 1 -> std 1 + [test] CrateLibrustc + [build] rustc 1 -> rustc 2 + [build] rustdoc 0 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [doc] rustc 0 -> standalone 1 + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [build] rustc 0 -> error-index 1 + [doc] rustc 0 -> error-index 1 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 0 -> releases 1 + [build] rustc 0 -> Linkchecker 1 + [test] tier-check + [doc] rustc (book) + [doc] rustc 1 -> std 1 crates=[] + [build] rustc 0 -> RustdocTheme 1 + [test] RustdocUi + [build] rustc 0 -> JsonDocCk 1 + [build] rustc 0 -> JsonDocLint 1 + [test] RustdocJson + [doc] rustc 0 -> rustc 1 + [build] rustc 0 -> HtmlChecker 1 + [build] rustc 0 -> RunMakeSupport 1 + [build] rustc 1 -> cargo 2 + [test] RunMake + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); @@ -2078,7 +2148,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 - [test] rustc 0 -> tier-check 1 + [test] tier-check "); } From 09a7ed632f8f50e2be54d852a9bec06fe33f95ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 14:12:16 +0200 Subject: [PATCH 05/23] Fix staging of `TestFloatParse` The tool wasn't useful for anything, it was only built as a part of the test, but we can just use `cargo test` and `cargo run` in the test, no need to (pre-)build the tool itself. --- src/bootstrap/src/core/build_steps/check.rs | 6 +- src/bootstrap/src/core/build_steps/test.rs | 92 +++++++++++++-------- src/bootstrap/src/core/build_steps/tool.rs | 37 +-------- 3 files changed, 61 insertions(+), 74 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index bebae893ee7fa..a604e7c058593 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -8,8 +8,8 @@ use crate::core::build_steps::compile::{ }; use crate::core::build_steps::tool; use crate::core::build_steps::tool::{ - COMPILETEST_ALLOW_FEATURES, SourceType, ToolTargetBuildMode, get_tool_target_compiler, - prepare_tool_cargo, + COMPILETEST_ALLOW_FEATURES, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode, + get_tool_target_compiler, prepare_tool_cargo, }; use crate::core::builder::{ self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, @@ -791,7 +791,7 @@ tool_check_step!(MiroptTestTools { tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse", mode: |_builder| Mode::ToolStd, - allow_features: tool::TestFloatParse::ALLOW_FEATURES + allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES }); tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c182493761e18..65360c42a6a21 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -18,8 +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, ToolTargetBuildMode, - get_tool_target_compiler, + self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, + TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler, }; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; @@ -2888,6 +2888,7 @@ impl Step for Crate { } } +/// Run cargo tests for the rustdoc crate. /// Rustdoc is special in various ways, which is why this step is different from `Crate`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRustdoc { @@ -2983,7 +2984,8 @@ impl Step for CrateRustdoc { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRustdocJsonTypes { - host: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl Step for CrateRustdocJsonTypes { @@ -2998,23 +3000,22 @@ impl Step for CrateRustdocJsonTypes { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - builder.ensure(CrateRustdocJsonTypes { host: run.target }); + builder.ensure(CrateRustdocJsonTypes { + build_compiler: get_tool_target_compiler( + builder, + ToolTargetBuildMode::Build(run.target), + ), + target: run.target, + }); } fn run(self, builder: &Builder<'_>) { - let target = self.host; - - // Use the previous stage compiler to reuse the artifacts that are - // created when running compiletest for tests/rustdoc. If this used - // `compiler`, then it would cause rustdoc to be built *again*, which - // isn't really necessary. - let compiler = builder.compiler_for(builder.top_stage, target, target); - builder.ensure(compile::Rustc::new(compiler, target)); + let target = self.target; let cargo = tool::prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, + self.build_compiler, + Mode::ToolTarget, target, builder.kind, "src/rustdoc-json-types", @@ -3023,7 +3024,7 @@ impl Step for CrateRustdocJsonTypes { ); // FIXME: this looks very wrong, libtest doesn't accept `-C` arguments and the quotes are fishy. - let libtest_args = if self.host.contains("musl") { + let libtest_args = if target.contains("musl") { ["'-Ctarget-feature=-crt-static'"].as_slice() } else { &[] @@ -3707,14 +3708,35 @@ impl Step for CodegenGCC { } } +/// Get a build compiler that can be used to test the standard library (i.e. its stage will +/// correspond to the stage that we want to test). +fn get_test_build_compiler_for_std(builder: &Builder<'_>) -> Compiler { + if builder.top_stage == 0 { + eprintln!( + "ERROR: cannot run tests on stage 0. `build.compiletest-allow-stage0` only works for compiletest test suites." + ); + exit!(1); + } + builder.compiler(builder.top_stage, builder.host_target) +} + /// Test step that does two things: /// - Runs `cargo test` for the `src/tools/test-float-parse` tool. /// - Invokes the `test-float-parse` tool to test the standard library's /// float parsing routines. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestFloatParse { - path: PathBuf, - host: TargetSelection, + /// The build compiler which will build and run unit tests of `test-float-parse`, and which will + /// build the `test-float-parse` tool itself. + /// + /// Note that the staging is a bit funny here, because this step essentially tests std, but it + /// also needs to build the tool. So if we test stage1 std, we build: + /// 1) stage1 rustc + /// 2) Use that to build stage1 libstd + /// 3) Use that to build and run *stage2* test-float-parse + build_compiler: Compiler, + /// Target for which we build std and test that std. + target: TargetSelection, } impl Step for TestFloatParse { @@ -3727,47 +3749,47 @@ impl Step for TestFloatParse { } fn make_run(run: RunConfig<'_>) { - for path in run.paths { - let path = path.assert_single_path().path.clone(); - run.builder.ensure(Self { path, host: run.target }); - } + run.builder.ensure(Self { + build_compiler: get_test_build_compiler_for_std(run.builder), + target: run.target, + }); } fn run(self, builder: &Builder<'_>) { - let bootstrap_host = builder.config.host_target; - let compiler = builder.compiler(builder.top_stage, bootstrap_host); - let path = self.path.to_str().unwrap(); - let crate_name = self.path.iter().next_back().unwrap().to_str().unwrap(); + let build_compiler = self.build_compiler; + let target = self.target; - builder.ensure(tool::TestFloatParse { host: self.host }); + // Build the standard library that will be tested, and a stdlib for host code + builder.std(build_compiler, target); + builder.std(build_compiler, builder.host_target); // Run any unit tests in the crate let mut cargo_test = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolStd, - bootstrap_host, + target, Kind::Test, - path, + "src/tools/test-float-parse", SourceType::InTree, &[], ); - cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES); + cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES); - run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder); + run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolStd, - bootstrap_host, + target, Kind::Run, - path, + "src/tools/test-float-parse", SourceType::InTree, &[], ); - cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES); + cargo_run.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES); if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) { cargo_run.args(["--", "--skip-huge"]); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index b62c9a906b79c..e15b570a5a7a4 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1539,42 +1539,7 @@ tool_rustc_extended!(Rustfmt { add_bins_to_sysroot: ["rustfmt"] }); -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TestFloatParse { - pub host: TargetSelection, -} - -impl TestFloatParse { - pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; -} - -impl Step for TestFloatParse { - type Output = ToolBuildResult; - const IS_HOST: bool = true; - const DEFAULT: bool = false; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/test-float-parse") - } - - fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - let bootstrap_host = builder.config.host_target; - let compiler = builder.compiler(builder.top_stage, bootstrap_host); - - builder.ensure(ToolBuild { - build_compiler: compiler, - target: bootstrap_host, - tool: "test-float-parse", - mode: Mode::ToolStd, - path: "src/tools/test-float-parse", - source_type: SourceType::InTree, - extra_features: Vec::new(), - allow_features: Self::ALLOW_FEATURES, - cargo_args: Vec::new(), - artifact_kind: ToolArtifactKind::Binary, - }) - } -} +pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; impl Builder<'_> { /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for From 4181ae83d50953ea03a53b0cba6620fb940561cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 14:34:56 +0200 Subject: [PATCH 06/23] Remove `compiler_for` from `test::CodegenGCC` --- src/bootstrap/src/core/build_steps/test.rs | 82 ++++++++-------------- 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 65360c42a6a21..063f112de6116 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3580,7 +3580,7 @@ impl Step for CodegenCranelift { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CodegenGCC { - compiler: Compiler, + compilers: RustcPrivateCompilers, target: TargetSelection, } @@ -3596,7 +3596,7 @@ impl Step for CodegenGCC { fn make_run(run: RunConfig<'_>) { let builder = run.builder; let host = run.build_triple(); - let compiler = run.builder.compiler_for(run.builder.top_stage, host, host); + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host); if builder.doc_tests == DocTests::Only { return; @@ -3625,68 +3625,41 @@ impl Step for CodegenGCC { return; } - builder.ensure(CodegenGCC { compiler, target: run.target }); + builder.ensure(CodegenGCC { compilers, target: run.target }); } fn run(self, builder: &Builder<'_>) { - let compiler = self.compiler; + let compilers = self.compilers; let target = self.target; let gcc = builder.ensure(Gcc { target }); builder.ensure( - compile::Std::new(compiler, target) + compile::Std::new(compilers.build_compiler(), target) .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]), ); - // If we're not doing a full bootstrap but we're testing a stage2 - // version of libstd, then what we're actually testing is the libstd - // produced in stage1. Reflect that here by updating the compiler that - // we're working with automatically. - let compiler = builder.compiler_for(compiler.stage, compiler.host, target); - - let build_cargo = || { - let mut cargo = builder::Cargo::new( - builder, - compiler, - Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works - SourceType::InTree, - target, - Kind::Run, - ); - - cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc")); - cargo - .arg("--manifest-path") - .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml")); - compile::rustc_cargo_env(builder, &mut cargo, target); - add_cg_gcc_cargo_flags(&mut cargo, &gcc); + let _msg = builder.msg_test("rustc_codegen_gcc", compilers.build_compiler()); - // Avoid incremental cache issues when changing rustc - cargo.env("CARGO_BUILD_INCREMENTAL", "false"); - cargo.rustflag("-Cpanic=abort"); - - cargo - }; - - builder.info(&format!( - "{} GCC stage{} ({} -> {})", - Kind::Test.description(), - compiler.stage, - &compiler.host, - target - )); - let _time = helpers::timeit(builder); + let mut cargo = builder::Cargo::new( + builder, + compilers.build_compiler(), + Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works + SourceType::InTree, + target, + Kind::Run, + ); - // FIXME: Uncomment the `prepare` command below once vendoring is implemented. - /* - let mut prepare_cargo = build_cargo(); - prepare_cargo.arg("--").arg("prepare"); - #[expect(deprecated)] - builder.config.try_run(&mut prepare_cargo.into()).unwrap(); - */ + cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc")); + cargo + .arg("--manifest-path") + .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml")); + compile::rustc_cargo_env(builder, &mut cargo, target); + add_cg_gcc_cargo_flags(&mut cargo, &gcc); - let mut cargo = build_cargo(); + // Avoid incremental cache issues when changing rustc + cargo.env("CARGO_BUILD_INCREMENTAL", "false"); + cargo.rustflag("-Cpanic=abort"); cargo // cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead. @@ -3698,7 +3671,7 @@ impl Step for CodegenGCC { .arg("--gcc-path") .arg(gcc.libgccjit.parent().unwrap()) .arg("--out-dir") - .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_gcc")) + .arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc")) .arg("--release") .arg("--mini-tests") .arg("--std-tests"); @@ -3706,6 +3679,13 @@ impl Step for CodegenGCC { cargo.into_cmd().run(builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("rustc_codegen_gcc", self.target) + .built_by(self.compilers.build_compiler()), + ) + } } /// Get a build compiler that can be used to test the standard library (i.e. its stage will From 1f8149183eaa91486ef9797454298703de9aaa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 14:59:34 +0200 Subject: [PATCH 07/23] Remove `compiler_for` from `test::CodegenCranelift` --- src/bootstrap/src/core/build_steps/test.rs | 84 +++++++++------------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 063f112de6116..377fe7c829d20 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3452,7 +3452,7 @@ impl Step for TestHelpers { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CodegenCranelift { - compiler: Compiler, + compilers: RustcPrivateCompilers, target: TargetSelection, } @@ -3468,7 +3468,7 @@ impl Step for CodegenCranelift { fn make_run(run: RunConfig<'_>) { let builder = run.builder; let host = run.build_triple(); - let compiler = run.builder.compiler_for(run.builder.top_stage, host, host); + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host); if builder.doc_tests == DocTests::Only { return; @@ -3498,71 +3498,50 @@ impl Step for CodegenCranelift { return; } - builder.ensure(CodegenCranelift { compiler, target: run.target }); + builder.ensure(CodegenCranelift { compilers, target: run.target }); } fn run(self, builder: &Builder<'_>) { - let compiler = self.compiler; - let target = self.target; - - builder.std(compiler, target); + let compilers = self.compilers; + let build_compiler = compilers.build_compiler(); - // If we're not doing a full bootstrap but we're testing a stage2 - // version of libstd, then what we're actually testing is the libstd - // produced in stage1. Reflect that here by updating the compiler that - // we're working with automatically. - let compiler = builder.compiler_for(compiler.stage, compiler.host, target); + // We need to run the cranelift tests with the compiler against cranelift links to, not with + // the build compiler. + let target_compiler = compilers.target_compiler(); + let target = self.target; - let build_cargo = || { - let mut cargo = builder::Cargo::new( - builder, - compiler, - Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works - SourceType::InTree, - target, - Kind::Run, - ); + builder.std(target_compiler, target); - cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift")); - cargo - .arg("--manifest-path") - .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml")); - compile::rustc_cargo_env(builder, &mut cargo, target); + let mut cargo = builder::Cargo::new( + builder, + target_compiler, + Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works + SourceType::InTree, + target, + Kind::Run, + ); - // Avoid incremental cache issues when changing rustc - cargo.env("CARGO_BUILD_INCREMENTAL", "false"); + cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift")); + cargo + .arg("--manifest-path") + .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml")); + compile::rustc_cargo_env(builder, &mut cargo, target); - cargo - }; + // Avoid incremental cache issues when changing rustc + cargo.env("CARGO_BUILD_INCREMENTAL", "false"); - builder.info(&format!( - "{} cranelift stage{} ({} -> {})", - Kind::Test.description(), - compiler.stage, - &compiler.host, - target - )); - let _time = helpers::timeit(builder); + let _guard = builder.msg_test("rustc_codegen_cranelift", target_compiler); // FIXME handle vendoring for source tarballs before removing the --skip-test below let download_dir = builder.out.join("cg_clif_download"); - // FIXME: Uncomment the `prepare` command below once vendoring is implemented. - /* - let mut prepare_cargo = build_cargo(); - prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir); - #[expect(deprecated)] - builder.config.try_run(&mut prepare_cargo.into()).unwrap(); - */ - - let mut cargo = build_cargo(); cargo .arg("--") .arg("test") .arg("--download-dir") .arg(&download_dir) .arg("--out-dir") - .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_clif")) + .arg(builder.stage_out(build_compiler, Mode::Codegen).join("cg_clif")) .arg("--no-unstable-features") .arg("--use-backend") .arg("cranelift") @@ -3576,6 +3555,13 @@ impl Step for CodegenCranelift { cargo.into_cmd().run(builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("rustc_codegen_cranelift", self.target) + .built_by(self.compilers.build_compiler()), + ) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -3639,7 +3625,7 @@ impl Step for CodegenGCC { .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]), ); - let _msg = builder.msg_test("rustc_codegen_gcc", compilers.build_compiler()); + let _guard = builder.msg_test("rustc_codegen_gcc", compilers.build_compiler()); let mut cargo = builder::Cargo::new( builder, From 2f1bbc679ec46169700d16758e3d23c14d308324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 15:04:47 +0200 Subject: [PATCH 08/23] Refactor `test::LintDocs` --- src/bootstrap/src/core/build_steps/doc.rs | 6 ++++- src/bootstrap/src/core/build_steps/test.rs | 31 +++++++++++++++++----- src/bootstrap/src/core/builder/tests.rs | 1 + 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 8c20c8c479af0..3d13fa70d8425 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -791,7 +791,11 @@ fn doc_std( } /// Prepare a compiler that will be able to document something for `target` at `stage`. -fn prepare_doc_compiler(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Compiler { +pub fn prepare_doc_compiler( + builder: &Builder<'_>, + target: TargetSelection, + stage: u32, +) -> Compiler { assert!(stage > 0, "Cannot document anything in stage 0"); let build_compiler = builder.compiler(stage - 1, builder.host_target); builder.std(build_compiler, target); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 377fe7c829d20..f513e0ccc25b9 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -12,7 +12,7 @@ use std::{env, fs, iter}; use build_helper::exit; use crate::core::build_steps::compile::{Std, run_cargo}; -use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; 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; @@ -3306,8 +3306,8 @@ impl Step for TierCheck { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct LintDocs { - pub compiler: Compiler, - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl Step for LintDocs { @@ -3320,8 +3320,21 @@ impl Step for LintDocs { } fn make_run(run: RunConfig<'_>) { + // Bump the stage to 2, because the rustc book requires an in-tree compiler. + // At the same time, since this step is enabled by default, we don't want `x test` to fail + // in stage 1. + let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 { + run.builder.top_stage + } else { + 2 + }; + run.builder.ensure(LintDocs { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler( + run.builder, + run.builder.config.host_target, + stage, + ), target: run.target, }); } @@ -3329,8 +3342,14 @@ impl Step for LintDocs { /// Tests that the lint examples in the rustc book generate the correct /// lints and have the expected format. fn run(self, builder: &Builder<'_>) { - builder - .ensure(crate::core::build_steps::doc::RustcBook::validate(self.compiler, self.target)); + builder.ensure(crate::core::build_steps::doc::RustcBook::validate( + self.build_compiler, + self.target, + )); + } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("lint-docs", self.target).built_by(self.build_compiler)) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d17c3b982d2a5..8a7fb1e5e683a 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2046,6 +2046,7 @@ mod snapshot { [build] rustc 0 -> Linkchecker 1 [test] tier-check [doc] rustc (book) + [test] rustc 1 -> lint-docs 2 [doc] rustc 1 -> std 1 crates=[] [build] rustc 0 -> RustdocTheme 1 [test] RustdocUi From df99001ac2fa68ef1e7afceff811a13601dae383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 15:21:59 +0200 Subject: [PATCH 09/23] Remove stage0 checking --- src/bootstrap/src/core/build_steps/test.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f513e0ccc25b9..61848d966c267 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3693,18 +3693,6 @@ impl Step for CodegenGCC { } } -/// Get a build compiler that can be used to test the standard library (i.e. its stage will -/// correspond to the stage that we want to test). -fn get_test_build_compiler_for_std(builder: &Builder<'_>) -> Compiler { - if builder.top_stage == 0 { - eprintln!( - "ERROR: cannot run tests on stage 0. `build.compiletest-allow-stage0` only works for compiletest test suites." - ); - exit!(1); - } - builder.compiler(builder.top_stage, builder.host_target) -} - /// Test step that does two things: /// - Runs `cargo test` for the `src/tools/test-float-parse` tool. /// - Invokes the `test-float-parse` tool to test the standard library's @@ -3734,10 +3722,8 @@ impl Step for TestFloatParse { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Self { - build_compiler: get_test_build_compiler_for_std(run.builder), - target: run.target, - }); + run.builder + .ensure(Self { build_compiler: get_compiler_to_test(run.builder), target: run.target }); } fn run(self, builder: &Builder<'_>) { From 92e9d25bde88fed8b3fa6338c2a0393bde5c3885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 16:30:02 +0200 Subject: [PATCH 10/23] Small refactoring around `RemoteCopyLibs` --- src/bootstrap/src/core/build_steps/test.rs | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 61848d966c267..fcb09548d2f25 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1741,7 +1741,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the builder.std(compiler, target); } - builder.ensure(RemoteCopyLibs { compiler, target }); + builder.ensure(RemoteCopyLibs { build_compiler: compiler, target }); // compiletest currently has... a lot of arguments, so let's just pass all // of them! @@ -2844,7 +2844,7 @@ impl Step for Crate { // Also prepare a sysroot for the target. if !builder.config.is_host_target(target) { builder.ensure(compile::Std::new(compiler, target).force_recompile(true)); - builder.ensure(RemoteCopyLibs { compiler, target }); + builder.ensure(RemoteCopyLibs { build_compiler: compiler, target }); } // Build `cargo test` command @@ -3052,7 +3052,7 @@ impl Step for CrateRustdocJsonTypes { /// the build target (us) and the server is built for the target. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RemoteCopyLibs { - compiler: Compiler, + build_compiler: Compiler, target: TargetSelection, } @@ -3064,18 +3064,17 @@ impl Step for RemoteCopyLibs { } fn run(self, builder: &Builder<'_>) { - let compiler = self.compiler; + let build_compiler = self.build_compiler; let target = self.target; if !builder.remote_tested(target) { return; } - builder.std(compiler, target); + builder.std(build_compiler, target); builder.info(&format!("REMOTE copy libs to emulator ({target})")); - let remote_test_server = - builder.ensure(tool::RemoteTestServer { build_compiler: compiler, target }); + let remote_test_server = builder.ensure(tool::RemoteTestServer { build_compiler, target }); // Spawn the emulator and wait for it to come online let tool = builder.tool_exe(Tool::RemoteTestClient); @@ -3090,7 +3089,7 @@ impl Step for RemoteCopyLibs { cmd.run(builder); // Push all our dylibs to the emulator - for f in t!(builder.sysroot_target_libdir(compiler, target).read_dir()) { + for f in t!(builder.sysroot_target_libdir(build_compiler, target).read_dir()) { let f = t!(f); if helpers::is_dylib(&f.path()) { command(&tool).arg("push").arg(f.path()).run(builder); @@ -3136,6 +3135,9 @@ impl Step for Distcheck { .map(|args| args.split(" ").map(|s| s.to_string()).collect::>()) .unwrap_or_default(); + // FIXME: unpack the source tarballs into a directory outside the source checkout, to + // ensure that it cannot access any local state + // Also ensure that it doesn't use download-ci-llvm command("tar") .arg("-xf") .arg(plain_src_tarball.tarball()) @@ -3722,8 +3724,10 @@ impl Step for TestFloatParse { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(Self { build_compiler: get_compiler_to_test(run.builder), target: run.target }); + run.builder.ensure(Self { + build_compiler: get_compiler_to_test(run.builder, run.target), + target: run.target, + }); } fn run(self, builder: &Builder<'_>) { From b97758680307776f52e7e6dd0004043da8db38da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 16:48:02 +0200 Subject: [PATCH 11/23] Add metadata to a bunch of steps, rename variables and add comments --- src/bootstrap/src/core/build_steps/test.rs | 119 ++++++++++++++++----- src/bootstrap/src/core/builder/tests.rs | 11 ++ 2 files changed, 102 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index fcb09548d2f25..0a931ce0a8598 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -98,6 +98,13 @@ impl Step for CrateBootstrap { let crate_name = path.rsplit_once('/').unwrap().1; run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("crate-bootstrap", self.host) + .with_metadata(self.path.as_path().to_string_lossy().to_string()), + ) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -173,6 +180,10 @@ You can skip linkcheck with --skip src/tools/linkchecker" fn make_run(run: RunConfig<'_>) { run.builder.ensure(Linkcheck { host: run.target }); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("link-check", self.host)) + } } fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool { @@ -221,6 +232,10 @@ impl Step for HtmlCheck { .arg(builder.doc_out(self.target)) .run(builder); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("html-check", self.target)) + } } /// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out @@ -399,6 +414,10 @@ impl Step for Cargo { let _time = helpers::timeit(builder); add_flags_and_try_run_tests(builder, &mut cargo); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("cargo", self.host).built_by(self.build_compiler)) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -457,6 +476,13 @@ impl Step for RustAnalyzer { cargo.add_rustc_lib_path(builder); run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("rust-analyzer", self.compilers.target()) + .built_by(self.compilers.build_compiler()), + ) + } } /// Runs `cargo test` for rustfmt. @@ -508,6 +534,13 @@ impl Step for Rustfmt { run_cargo_test(cargo, &[], &[], "rustfmt", target, builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("rustfmt", self.compilers.target()) + .built_by(self.compilers.build_compiler()), + ) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -887,6 +920,13 @@ impl Step for Clippy { crate::exit!(1); } } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("clippy", self.compilers.target()) + .built_by(self.compilers.build_compiler()), + ) + } } fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString { @@ -895,9 +935,11 @@ fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString { env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("") } +/// Run the rustdoc-themes tool to test a given compiler. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustdocTheme { - pub compiler: Compiler, + /// The compiler (more accurately, its rustdoc) that we test. + test_compiler: Compiler, } impl Step for RustdocTheme { @@ -910,9 +952,9 @@ impl Step for RustdocTheme { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.target); + let test_compiler = run.builder.compiler(run.builder.top_stage, run.target); - run.builder.ensure(RustdocTheme { compiler }); + run.builder.ensure(RustdocTheme { test_compiler }); } fn run(self, builder: &Builder<'_>) { @@ -920,21 +962,34 @@ impl Step for RustdocTheme { let mut cmd = builder.tool_cmd(Tool::RustdocTheme); cmd.arg(rustdoc.to_str().unwrap()) .arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap()) - .env("RUSTC_STAGE", self.compiler.stage.to_string()) - .env("RUSTC_SYSROOT", builder.sysroot(self.compiler)) - .env("RUSTDOC_LIBDIR", builder.sysroot_target_libdir(self.compiler, self.compiler.host)) + .env("RUSTC_STAGE", self.test_compiler.stage.to_string()) + .env("RUSTC_SYSROOT", builder.sysroot(self.test_compiler)) + .env( + "RUSTDOC_LIBDIR", + builder.sysroot_target_libdir(self.test_compiler, self.test_compiler.host), + ) .env("CFG_RELEASE_CHANNEL", &builder.config.channel) - .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.compiler)) + .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.test_compiler)) .env("RUSTC_BOOTSTRAP", "1"); - cmd.args(linker_args(builder, self.compiler.host, LldThreads::No)); + cmd.args(linker_args(builder, self.test_compiler.host, LldThreads::No)); cmd.delay_failure().run(builder); } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test("rustdoc-theme", self.test_compiler.host) + .stage(self.test_compiler.stage), + ) + } } +/// Test rustdoc JS for the standard library. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustdocJSStd { - pub target: TargetSelection, + /// Compiler that will build the standary library. + build_compiler: Compiler, + target: TargetSelection, } impl Step for RustdocJSStd { @@ -948,7 +1003,10 @@ impl Step for RustdocJSStd { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustdocJSStd { target: run.target }); + run.builder.ensure(RustdocJSStd { + build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target), + target: run.target, + }); } fn run(self, builder: &Builder<'_>) { @@ -976,19 +1034,18 @@ impl Step for RustdocJSStd { } } builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( - builder.compiler(builder.top_stage, builder.host_target), + self.build_compiler, self.target, DocumentationFormat::Html, )); - let _guard = builder.msg( - Kind::Test, - "rustdoc-js-std", - None, - (builder.config.host_target, builder.top_stage), - self.target, - ); + let _guard = + builder.msg(Kind::Test, "rustdoc-js-std", None, self.build_compiler, self.target); command.run(builder); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("rustdoc-js-std", self.target).stage(self.build_compiler.stage)) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -1046,10 +1103,12 @@ fn get_browser_ui_test_version(builder: &Builder<'_>, npm: &Path) -> Option) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(RustdocGUI { target: run.target, compiler }); + let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RustdocGUI { test_compiler, target: run.target }); } fn run(self, builder: &Builder<'_>) { - builder.std(self.compiler, self.target); + builder.std(self.test_compiler, self.target); let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); @@ -1086,7 +1145,7 @@ impl Step for RustdocGUI { build_stamp::clear_if_dirty( builder, &out_dir, - &builder.rustdoc_for_compiler(self.compiler), + &builder.rustdoc_for_compiler(self.test_compiler), ); if let Some(src) = builder.config.src.to_str() { @@ -1103,10 +1162,10 @@ impl Step for RustdocGUI { cmd.arg("--jobs").arg(builder.jobs().to_string()); - cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.compiler)) - .env("RUSTC", builder.rustc(self.compiler)); + cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.test_compiler)) + .env("RUSTC", builder.rustc(self.test_compiler)); - add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No); + add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No); for path in &builder.paths { if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) { @@ -1133,9 +1192,13 @@ impl Step for RustdocGUI { } let _time = helpers::timeit(builder); - let _guard = builder.msg(Kind::Test, "rustdoc-gui", None, self.compiler, self.target); + let _guard = builder.msg_test("rustdoc-gui", (self.target, self.test_compiler.stage)); try_run_tests(builder, &mut cmd, true); } + + fn metadata(&self) -> Option { + Some(StepMetadata::test("rustdoc-gui", self.target).stage(self.test_compiler.stage)) + } } /// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8a7fb1e5e683a..f382b3a651166 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2020,6 +2020,10 @@ mod snapshot { [test] CrateLibrustc [build] rustc 1 -> rustc 2 [build] rustdoc 0 + [test] crate-bootstrap src/tools/coverage-dump + [test] crate-bootstrap src/tools/jsondoclint + [test] crate-bootstrap src/tools/replace-version-placeholder + [test] crate-bootstrap tidyselftest [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 [doc] unstable-book (book) @@ -2044,17 +2048,22 @@ mod snapshot { [doc] style-guide (book) [doc] rustc 0 -> releases 1 [build] rustc 0 -> Linkchecker 1 + [test] link-check [test] tier-check + [test] rustc 0 -> rust-analyzer 1 [doc] rustc (book) [test] rustc 1 -> lint-docs 2 [doc] rustc 1 -> std 1 crates=[] + [test] rustc 1 -> rustdoc-js-std 2 [build] rustc 0 -> RustdocTheme 1 + [test] rustdoc-theme 1 [test] RustdocUi [build] rustc 0 -> JsonDocCk 1 [build] rustc 0 -> JsonDocLint 1 [test] RustdocJson [doc] rustc 0 -> rustc 1 [build] rustc 0 -> HtmlChecker 1 + [test] html-check [build] rustc 0 -> RunMakeSupport 1 [build] rustc 1 -> cargo 2 [test] RunMake @@ -2101,6 +2110,7 @@ mod snapshot { [build] rustc 1 -> std 1 [build] rustdoc 1 [build] rustdoc 0 + [test] rustc 0 -> cargo 1 "); } @@ -2120,6 +2130,7 @@ mod snapshot { [build] rustc 2 -> std 2 [build] rustdoc 2 [build] rustdoc 1 + [test] rustc 1 -> cargo 2 "); } From 0a27573dbce5872616204231a589eaf33b120442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 16:48:49 +0200 Subject: [PATCH 12/23] Move the `test!` macro closer to its usages --- src/bootstrap/src/core/build_steps/test.rs | 140 ++++++++++----------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0a931ce0a8598..c9483585b1f56 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1316,76 +1316,6 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to } } -fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf { - builder.out.join(host).join("test") -} - -/// Declares a test step that invokes compiletest on a particular test suite. -macro_rules! test { - ( - $( #[$attr:meta] )* // allow docstrings and attributes - $name:ident { - path: $path:expr, - mode: $mode:expr, - suite: $suite:expr, - default: $default:expr - $( , IS_HOST: $IS_HOST:expr )? // default: false - $( , compare_mode: $compare_mode:expr )? // default: None - $( , )? // optional trailing comma - } - ) => { - $( #[$attr] )* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, - } - - impl Step for $name { - type Output = (); - const DEFAULT: bool = $default; - const IS_HOST: bool = (const { - #[allow(unused_assignments, unused_mut)] - let mut value = false; - $( value = $IS_HOST; )? - value - }); - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.suite_path($path) - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - - run.builder.ensure($name { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - builder.ensure(Compiletest { - compiler: self.compiler, - target: self.target, - mode: $mode, - suite: $suite, - path: $path, - compare_mode: (const { - #[allow(unused_assignments, unused_mut)] - let mut value = None; - $( value = $compare_mode; )? - value - }), - }) - } - - fn metadata(&self) -> Option { - Some( - StepMetadata::test(stringify!($name), self.target) - ) - } - } - }; -} - /// Runs `cargo test` on the `src/tools/run-make-support` crate. /// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1462,6 +1392,76 @@ impl Step for CrateBuildHelper { } } +fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf { + builder.out.join(host).join("test") +} + +/// Declares a test step that invokes compiletest on a particular test suite. +macro_rules! test { + ( + $( #[$attr:meta] )* // allow docstrings and attributes + $name:ident { + path: $path:expr, + mode: $mode:expr, + suite: $suite:expr, + default: $default:expr + $( , IS_HOST: $IS_HOST:expr )? // default: false + $( , compare_mode: $compare_mode:expr )? // default: None + $( , )? // optional trailing comma + } + ) => { + $( #[$attr] )* + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $name { + pub compiler: Compiler, + pub target: TargetSelection, + } + + impl Step for $name { + type Output = (); + const DEFAULT: bool = $default; + const IS_HOST: bool = (const { + #[allow(unused_assignments, unused_mut)] + let mut value = false; + $( value = $IS_HOST; )? + value + }); + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.suite_path($path) + } + + fn make_run(run: RunConfig<'_>) { + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + + run.builder.ensure($name { compiler, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + builder.ensure(Compiletest { + compiler: self.compiler, + target: self.target, + mode: $mode, + suite: $suite, + path: $path, + compare_mode: (const { + #[allow(unused_assignments, unused_mut)] + let mut value = None; + $( value = $compare_mode; )? + value + }), + }) + } + + fn metadata(&self) -> Option { + Some( + StepMetadata::test(stringify!($name), self.target) + ) + } + } + }; +} + test!(Ui { path: "tests/ui", mode: "ui", suite: "ui", default: true }); test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes", default: true }); From d59820986a994dd235d2defaf25c279dd3e16847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 17:04:58 +0200 Subject: [PATCH 13/23] Fix staging for compiler/std crate tests --- src/bootstrap/src/core/build_steps/test.rs | 162 ++++++++++++++------- 1 file changed, 106 insertions(+), 56 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c9483585b1f56..d2f7a9a1f43f8 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -96,7 +96,7 @@ impl Step for CrateBootstrap { ); let crate_name = path.rsplit_once('/').unwrap().1; - run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder, Mode::ToolBootstrap); } fn metadata(&self) -> Option { @@ -153,7 +153,15 @@ You can skip linkcheck with --skip src/tools/linkchecker" SourceType::InTree, &[], ); - run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder); + run_cargo_test( + cargo, + &[], + &[], + "linkchecker self tests", + bootstrap_host, + builder, + Mode::ToolBootstrap, + ); if builder.doc_tests == DocTests::No { return; @@ -474,7 +482,7 @@ impl Step for RustAnalyzer { cargo.env("SKIP_SLOW_TESTS", "1"); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder); + run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder, Mode::ToolRustc); } fn metadata(&self) -> Option { @@ -532,7 +540,7 @@ impl Step for Rustfmt { cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", target, builder); + run_cargo_test(cargo, &[], &[], "rustfmt", target, builder, Mode::ToolRustc); } fn metadata(&self) -> Option { @@ -825,7 +833,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cargo.env("TEST_RUSTC", builder.rustc(compiler)); cargo.allow_features(COMPILETEST_ALLOW_FEATURES); - run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder); + run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder, Mode::ToolStd); } } @@ -1351,7 +1359,15 @@ impl Step for CrateRunMakeSupport { &[], ); cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder); + run_cargo_test( + cargo, + &[], + &[], + "run-make-support self test", + host, + builder, + Mode::ToolBootstrap, + ); } } @@ -1388,7 +1404,15 @@ impl Step for CrateBuildHelper { &[], ); cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder); + run_cargo_test( + cargo, + &[], + &[], + "build_helper self test", + host, + builder, + Mode::ToolBootstrap, + ); } } @@ -2342,9 +2366,10 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} } } +/// Runs the documentation tests for a book in `src/doc` using the `rustdoc` of `test_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct BookTest { - compiler: Compiler, + test_compiler: Compiler, path: PathBuf, name: &'static str, is_ext_doc: bool, @@ -2359,9 +2384,6 @@ impl Step for BookTest { run.never() } - /// Runs the documentation tests for a book in `src/doc`. - /// - /// This uses the `rustdoc` that sits next to `compiler`. fn run(self, builder: &Builder<'_>) { // External docs are different from local because: // - Some books need pre-processing by mdbook before being tested. @@ -2384,13 +2406,13 @@ impl BookTest { /// This runs the equivalent of `mdbook test` (via the rustbook wrapper) /// which in turn runs `rustdoc --test` on each file in the book. fn run_ext_doc(self, builder: &Builder<'_>) { - let compiler = self.compiler; + let test_compiler = self.test_compiler; - builder.std(compiler, compiler.host); + builder.std(test_compiler, test_compiler.host); // 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_for_compiler(compiler); + let mut rustdoc_path = builder.rustdoc_for_compiler(test_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))) @@ -2413,7 +2435,7 @@ impl BookTest { let target = builder.config.host_target; let cargo = tool::prepare_tool_cargo( builder, - compiler, + test_compiler, mode, target, Kind::Build, @@ -2422,7 +2444,7 @@ impl BookTest { &[], ); - let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + let stamp = BuildStamp::new(&builder.cargo_out(test_compiler, mode, target)) .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); @@ -2455,8 +2477,8 @@ impl BookTest { Kind::Test, format_args!("mdbook {}", self.path.display()), None, - compiler, - compiler.host, + test_compiler, + test_compiler.host, ); let _time = helpers::timeit(builder); let toolstate = if rustbook_cmd.delay_failure().run(builder) { @@ -2469,12 +2491,18 @@ impl BookTest { /// This runs `rustdoc --test` on all `.md` files in the path. fn run_local_doc(self, builder: &Builder<'_>) { - let compiler = self.compiler; - let host = self.compiler.host; + let test_compiler = self.test_compiler; + let host = self.test_compiler.host; - builder.std(compiler, host); + builder.std(test_compiler, host); - let _guard = builder.msg(Kind::Test, format!("book {}", self.name), None, compiler, host); + let _guard = builder.msg( + Kind::Test, + format!("book {}", self.name), + None, + (test_compiler.host, test_compiler.stage - 1), + host, + ); // Do a breadth-first traversal of the `src/doc` directory and just run // tests for all files that end in `*.md` @@ -2497,7 +2525,7 @@ impl BookTest { files.sort(); for file in files { - markdown_test(builder, compiler, &file); + markdown_test(builder, test_compiler, &file); } } } @@ -2513,7 +2541,7 @@ macro_rules! test_book { $( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { - compiler: Compiler, + test_compiler: Compiler, } impl Step for $name { @@ -2527,7 +2555,7 @@ macro_rules! test_book { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { - compiler: run.builder.compiler(run.builder.top_stage, run.target), + test_compiler: run.builder.compiler(run.builder.top_stage, run.target), }); } @@ -2547,7 +2575,7 @@ macro_rules! test_book { )? builder.ensure(BookTest { - compiler: self.compiler, + test_compiler: self.test_compiler, path: PathBuf::from($path), name: $book_name, is_ext_doc: !$default, @@ -2667,7 +2695,8 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> /// which have their own separate test steps.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateLibrustc { - compiler: Compiler, + /// The compiler that will *build* rustc in test mode. + build_compiler: Compiler, target: TargetSelection, crates: Vec, } @@ -2684,18 +2713,18 @@ impl Step for CrateLibrustc { fn make_run(run: RunConfig<'_>) { let builder = run.builder; let host = run.build_triple(); - let compiler = builder.compiler_for(builder.top_stage, host, host); + let build_compiler = builder.compiler(builder.top_stage - 1, host); let crates = run.make_run_crates(Alias::Compiler); - builder.ensure(CrateLibrustc { compiler, target: run.target, crates }); + builder.ensure(CrateLibrustc { build_compiler, target: run.target, crates }); } fn run(self, builder: &Builder<'_>) { - builder.std(self.compiler, self.target); + builder.std(self.build_compiler, self.target); // To actually run the tests, delegate to a copy of the `Crate` step. builder.ensure(Crate { - compiler: self.compiler, + build_compiler: self.build_compiler, target: self.target, mode: Mode::Rustc, crates: self.crates, @@ -2717,12 +2746,14 @@ fn run_cargo_test<'a>( description: impl Into>, target: TargetSelection, builder: &Builder<'_>, + mode: impl Into>, ) -> bool { + let mode = mode.into(); let compiler = cargo.compiler(); 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(Kind::Test, what, None, compiler, target)); + description.into().and_then(|what| builder.msg(Kind::Test, what, mode, compiler, target)); #[cfg(feature = "build-metrics")] builder.metrics.begin_test_suite( @@ -2820,10 +2851,11 @@ fn prepare_cargo_test( /// library crates and compiler crates. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Crate { - pub compiler: Compiler, - pub target: TargetSelection, - pub mode: Mode, - pub crates: Vec, + /// The compiler that will *build* libstd or rustc in test mode. + build_compiler: Compiler, + target: TargetSelection, + mode: Mode, + crates: Vec, } impl Step for Crate { @@ -2837,14 +2869,14 @@ impl Step for Crate { fn make_run(run: RunConfig<'_>) { let builder = run.builder; let host = run.build_triple(); - let compiler = builder.compiler_for(builder.top_stage, host, host); + let build_compiler = builder.compiler(builder.top_stage, host); let crates = run .paths .iter() .map(|p| builder.crate_paths[&p.assert_single_path().path].clone()) .collect(); - builder.ensure(Crate { compiler, target: run.target, mode: Mode::Std, crates }); + builder.ensure(Crate { build_compiler, target: run.target, mode: Mode::Std, crates }); } /// Runs all unit tests plus documentation tests for a given crate defined @@ -2856,19 +2888,13 @@ impl Step for Crate { /// Currently this runs all tests for a DAG by passing a bunch of `-p foo` /// arguments, and those arguments are discovered from `cargo metadata`. fn run(self, builder: &Builder<'_>) { - let compiler = self.compiler; + let build_compiler = self.build_compiler; let target = self.target; let mode = self.mode; // Prepare sysroot // See [field@compile::Std::force_recompile]. - builder.ensure(Std::new(compiler, compiler.host).force_recompile(true)); - - // If we're not doing a full bootstrap but we're testing a stage2 - // version of libstd, then what we're actually testing is the libstd - // produced in stage1. Reflect that here by updating the compiler that - // we're working with automatically. - let compiler = builder.compiler_for(compiler.stage, compiler.host, target); + builder.ensure(Std::new(build_compiler, build_compiler.host).force_recompile(true)); let mut cargo = if builder.kind == Kind::Miri { if builder.top_stage == 0 { @@ -2880,7 +2906,7 @@ impl Step for Crate { // (Implicitly prepares target sysroot) let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, mode, SourceType::InTree, target, @@ -2906,12 +2932,19 @@ impl Step for Crate { } else { // Also prepare a sysroot for the target. if !builder.config.is_host_target(target) { - builder.ensure(compile::Std::new(compiler, target).force_recompile(true)); - builder.ensure(RemoteCopyLibs { build_compiler: compiler, target }); + builder.ensure(compile::Std::new(build_compiler, target).force_recompile(true)); + builder.ensure(RemoteCopyLibs { build_compiler, target }); } // Build `cargo test` command - builder::Cargo::new(builder, compiler, mode, SourceType::InTree, target, builder.kind) + builder::Cargo::new( + builder, + build_compiler, + mode, + SourceType::InTree, + target, + builder.kind, + ) }; match mode { @@ -2930,7 +2963,7 @@ impl Step for Crate { } } Mode::Rustc => { - compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates); + compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); } _ => panic!("can only test libraries"), }; @@ -2947,7 +2980,15 @@ impl Step for Crate { crates.push("alloctests".to_owned()); } - run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder); + run_cargo_test( + cargo, + &[], + &crates, + &*crate_description(&self.crates), + target, + builder, + mode, + ); } } @@ -3041,7 +3082,15 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder); + run_cargo_test( + cargo, + &[], + &["rustdoc:0.0.0".to_string()], + "rustdoc", + target, + builder, + Mode::ToolRustc, + ); } } @@ -3100,6 +3149,7 @@ impl Step for CrateRustdocJsonTypes { "rustdoc-json-types", target, builder, + Mode::ToolTarget, ); } } @@ -3300,7 +3350,7 @@ impl Step for Bootstrap { // bootstrap tests are racy on directory creation so just run them one at a time. // Since there's not many this shouldn't be a problem. - run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder); + run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder, Mode::ToolBootstrap); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3443,7 +3493,7 @@ impl Step for RustInstaller { let _guard = builder.msg(Kind::Test, "rust-installer", None, build_compiler, bootstrap_host); - run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder, Mode::ToolBootstrap); // We currently don't support running the test.sh script outside linux(?) environments. // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a @@ -3814,7 +3864,7 @@ impl Step for TestFloatParse { ); cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES); - run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder); + run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder, Mode::ToolStd); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( From 85274ebe27250edc8fc80c3f9a52143f3253c15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 17:09:02 +0200 Subject: [PATCH 14/23] Fix spacing when testing individual crates --- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 5 +++-- src/bootstrap/src/lib.rs | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index f794f4e079aeb..8d7d19490fb13 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -295,7 +295,7 @@ pub fn crate_description(crates: &[impl AsRef]) -> String { return "".into(); } - let mut descr = String::from(" {"); + let mut descr = String::from("{"); descr.push_str(crates[0].as_ref()); for krate in &crates[1..] { descr.push_str(", "); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f382b3a651166..c715a2b4dbd0c 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2017,9 +2017,10 @@ mod snapshot { [test] CoverageRunRustdoc [test] Pretty [build] rustc 1 -> std 1 + [build] rustc 0 -> std 0 + [build] rustdoc 0 [test] CrateLibrustc [build] rustc 1 -> rustc 2 - [build] rustdoc 0 [test] crate-bootstrap src/tools/coverage-dump [test] crate-bootstrap src/tools/jsondoclint [test] crate-bootstrap src/tools/replace-version-placeholder @@ -2054,7 +2055,7 @@ mod snapshot { [doc] rustc (book) [test] rustc 1 -> lint-docs 2 [doc] rustc 1 -> std 1 crates=[] - [test] rustc 1 -> rustdoc-js-std 2 + [test] rustdoc-js-std 1 [build] rustc 0 -> RustdocTheme 1 [test] rustdoc-theme 1 [test] RustdocUi diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1beeb16b44f37..29d0eac670aa0 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1144,14 +1144,18 @@ impl Build { }; let action = action.into().description(); - let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}"); + let what = what.to_string(); + let msg = |fmt| { + let space = if !what.is_empty() { " " } else { "" }; + format!("{action} stage{actual_stage} {what}{space}{fmt}") + }; let msg = if let Some(target) = target.into() { let build_stage = host_and_stage.stage; let host = host_and_stage.host; if host == target { - msg(format_args!(" (stage{build_stage} -> stage{actual_stage}, {target})")) + msg(format_args!("(stage{build_stage} -> stage{actual_stage}, {target})")) } else { - msg(format_args!(" (stage{build_stage}:{host} -> stage{actual_stage}:{target})")) + msg(format_args!("(stage{build_stage}:{host} -> stage{actual_stage}:{target})")) } } else { msg(format_args!("")) From 4728ef4fd08ca2a1404d126edf47aa7c3f0cb63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 17:30:30 +0200 Subject: [PATCH 15/23] Do not run tests on CI in stage 0 --- src/ci/docker/host-x86_64/tidy/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/tidy/Dockerfile b/src/ci/docker/host-x86_64/tidy/Dockerfile index c8558689d3ba9..2dda51b155e90 100644 --- a/src/ci/docker/host-x86_64/tidy/Dockerfile +++ b/src/ci/docker/host-x86_64/tidy/Dockerfile @@ -44,5 +44,5 @@ RUN bash -c 'npm install -g eslint@$(cat /tmp/eslint.version)' # NOTE: intentionally uses python2 for x.py so we can test it still works. # validate-toolstate only runs in our CI, so it's ok for it to only support python3. -ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 \ +ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \ src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck From d1e94e115831c594a4f00922530a8ff4ea3c1cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 17:18:19 +0200 Subject: [PATCH 16/23] 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 606d88d3db44c..2cc2fb486fad5 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -531,4 +531,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "It is now possible to `check/build/dist` the standard stage 0 library if you use a stage0 rustc built from in-tree sources. This is useful for quickly cross-compiling the standard library. You have to enable build.local-rebuild for this to work.", }, + ChangeInfo { + change_id: 145663, + severity: ChangeSeverity::Warning, + summary: "It is no longer possible to `x test` with stage 0, except for running compiletest and opting into `build.compiletest-allow-stage0`.", + }, ]; From 6c93dfb52b5616d160f22e1d5a1d0dd609475bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 18:05:22 +0200 Subject: [PATCH 17/23] Skip bootstrap tests on CI --- src/bootstrap/src/core/build_steps/tool.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index e15b570a5a7a4..65c4c49908653 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1539,7 +1539,7 @@ tool_rustc_extended!(Rustfmt { add_bins_to_sysroot: ["rustfmt"] }); -pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; +pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f16_f128"; impl Builder<'_> { /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index c715a2b4dbd0c..53948a22189a2 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1995,6 +1995,10 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("test") + // Skip bootstrap tests, as for some reason the recursive nature of running + // bootstrap tests under bootstrap tests causes non-deterministic snapshot diffs + // on CI. + .args(&["--skip", "bootstrap"]) .render_steps(), @r" [build] rustc 0 -> Tidy 1 [test] tidy <> From a9b85e9297f6c89531eecd68254b0c358957742e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 19:37:45 +0200 Subject: [PATCH 18/23] Fix doclink --- src/bootstrap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 29d0eac670aa0..0584c53f549ee 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1165,7 +1165,7 @@ impl Build { /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target` /// (determined by `host_and_stage`). - /// Use this instead of [`Builder::msg`] when there is no clear `build_compiler` to be + /// Use this instead of [`builder::Builder::msg`] when there is no clear `build_compiler` to be /// determined. /// /// [`Step`]: crate::core::builder::Step From 4da586f9c597b12e2a99fe9a7e4021e82452d091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 19:53:24 +0200 Subject: [PATCH 19/23] Fake nodejs in snapshot test --- src/bootstrap/src/core/builder/tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 53948a22189a2..e4f39968e12e8 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1999,6 +1999,8 @@ mod snapshot { // bootstrap tests under bootstrap tests causes non-deterministic snapshot diffs // on CI. .args(&["--skip", "bootstrap"]) + // rustdoc-js-std requires nodejs to be present + .args(&["--set", "build.nodejs=/bin/nodejs"]) .render_steps(), @r" [build] rustc 0 -> Tidy 1 [test] tidy <> From c8ae70a04ca648d5700b2061d1171273fd894e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 20 Aug 2025 21:01:31 +0200 Subject: [PATCH 20/23] Fix tests and doclink --- src/bootstrap/src/core/builder/tests.rs | 18 +++++++++--------- src/bootstrap/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index e4f39968e12e8..ee8d8351af7a8 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1995,15 +1995,18 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("test") - // Skip bootstrap tests, as for some reason the recursive nature of running - // bootstrap tests under bootstrap tests causes non-deterministic snapshot diffs - // on CI. - .args(&["--skip", "bootstrap"]) - // rustdoc-js-std requires nodejs to be present - .args(&["--set", "build.nodejs=/bin/nodejs"]) + // Bootstrap only run by default on CI, so we have to emulate that also locally. + .args(&["--ci", "true"]) + // These rustdoc tests requires nodejs to be present. + // We can't easily opt out of it, so if it is present on the local PC, the test + // would have different result on CI, where nodejs might be missing. + .args(&["--skip", "rustdoc-js-std"]) + .args(&["--skip", "rustdoc-js"]) + .args(&["--skip", "rustdoc-gui"]) .render_steps(), @r" [build] rustc 0 -> Tidy 1 [test] tidy <> + [build] rustdoc 0 [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 @@ -2024,7 +2027,6 @@ mod snapshot { [test] Pretty [build] rustc 1 -> std 1 [build] rustc 0 -> std 0 - [build] rustdoc 0 [test] CrateLibrustc [build] rustc 1 -> rustc 2 [test] crate-bootstrap src/tools/coverage-dump @@ -2060,8 +2062,6 @@ mod snapshot { [test] rustc 0 -> rust-analyzer 1 [doc] rustc (book) [test] rustc 1 -> lint-docs 2 - [doc] rustc 1 -> std 1 crates=[] - [test] rustdoc-js-std 1 [build] rustc 0 -> RustdocTheme 1 [test] rustdoc-theme 1 [test] RustdocUi diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0584c53f549ee..9a882eae08edf 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1165,7 +1165,7 @@ impl Build { /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target` /// (determined by `host_and_stage`). - /// Use this instead of [`builder::Builder::msg`] when there is no clear `build_compiler` to be + /// Use this instead of [`Build::msg`] when there is no clear `build_compiler` to be /// determined. /// /// [`Step`]: crate::core::builder::Step From ee109d377b7a7c311d506bd800cbfcaed01575e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 27 Aug 2025 11:35:10 +0200 Subject: [PATCH 21/23] Add test stage 2 snapshot tests --- src/bootstrap/src/core/builder/tests.rs | 107 +++++++++++++++++++++--- 1 file changed, 97 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index ee8d8351af7a8..41a90fdae7a74 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1990,19 +1990,23 @@ mod snapshot { .render_steps(), @"[check] rustc 0 -> RunMakeSupport 1 "); } + fn prepare_test_config(ctx: &TestCtx) -> ConfigBuilder { + ctx.config("test") + // Bootstrap only runs by default on CI, so we have to emulate that also locally. + .args(&["--ci", "true"]) + // These rustdoc tests requires nodejs to be present. + // We can't easily opt out of it, so if it is present on the local PC, the test + // would have different result on CI, where nodejs might be missing. + .args(&["--skip", "rustdoc-js-std"]) + .args(&["--skip", "rustdoc-js"]) + .args(&["--skip", "rustdoc-gui"]) + } + #[test] - fn test_all() { + fn test_all_stage_1() { let ctx = TestCtx::new(); insta::assert_snapshot!( - ctx.config("test") - // Bootstrap only run by default on CI, so we have to emulate that also locally. - .args(&["--ci", "true"]) - // These rustdoc tests requires nodejs to be present. - // We can't easily opt out of it, so if it is present on the local PC, the test - // would have different result on CI, where nodejs might be missing. - .args(&["--skip", "rustdoc-js-std"]) - .args(&["--skip", "rustdoc-js"]) - .args(&["--skip", "rustdoc-gui"]) + prepare_test_config(&ctx) .render_steps(), @r" [build] rustc 0 -> Tidy 1 [test] tidy <> @@ -2077,6 +2081,89 @@ mod snapshot { "); } + #[test] + fn test_all_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + prepare_test_config(&ctx) + .stage(2) + .render_steps(), @r" + [build] rustc 0 -> Tidy 1 + [test] tidy <> + [build] rustdoc 0 + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> std 2 + [build] rustc 0 -> Compiletest 1 + [test] Ui + [test] Crashes + [build] rustc 0 -> CoverageDump 1 + [build] rustc 2 -> std 2 + [test] CodegenLlvm + [test] CodegenUnits + [test] AssemblyLlvm + [test] Incremental + [test] Debuginfo + [build] rustc 2 -> rustc 3 + [test] UiFullDeps + [build] rustdoc 2 + [test] Rustdoc + [test] CoverageRunRustdoc + [test] Pretty + [build] rustc 2 -> std 2 + [build] rustc 1 -> std 1 + [build] rustdoc 1 + [test] rustc 1 -> CrateLibrustc 2 + [test] crate-bootstrap src/tools/coverage-dump + [test] crate-bootstrap src/tools/jsondoclint + [test] crate-bootstrap src/tools/replace-version-placeholder + [test] crate-bootstrap tidyselftest + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [doc] rustc 1 -> standalone 2 + [doc] rustc 2 -> 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 1 -> error-index 2 + [doc] rustc 1 -> error-index 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 + [build] rustc 0 -> Linkchecker 1 + [test] link-check + [test] tier-check + [test] rustc 1 -> rust-analyzer 2 + [doc] rustc (book) + [test] rustc 1 -> lint-docs 2 + [build] rustc 0 -> RustdocTheme 1 + [test] rustdoc-theme 2 + [test] RustdocUi + [build] rustc 0 -> JsonDocCk 1 + [build] rustc 0 -> JsonDocLint 1 + [test] RustdocJson + [doc] rustc 1 -> rustc 2 + [build] rustc 0 -> HtmlChecker 1 + [test] html-check + [build] rustc 0 -> RunMakeSupport 1 + [build] rustc 2 -> cargo 3 + [test] RunMake + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); From a0002971af6622f10cdaabdceb3247b3a3ad5f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 25 Aug 2025 17:40:01 +0200 Subject: [PATCH 22/23] Fix stage 1 compiler tests --- compiler/rustc_lint_defs/src/builtin.rs | 11 +++--- src/bootstrap/src/core/build_steps/doc.rs | 2 ++ src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 42 ++++++++++++++++++++-- src/bootstrap/src/core/config/config.rs | 4 +-- src/tools/lint-docs/src/lib.rs | 42 +++++++++++++++++----- src/tools/lint-docs/src/main.rs | 13 +++++++ 7 files changed, 97 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 97aa106596799..ce92263babd8e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2309,10 +2309,10 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(sanitize)] + /// #![cfg_attr(not(bootstrap), feature(sanitize))] /// /// #[inline(always)] - /// #[sanitize(address = "off")] + /// #[cfg_attr(not(bootstrap), sanitize(address = "off"))] /// fn x() {} /// /// fn main() { @@ -4832,13 +4832,16 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + #[cfg_attr(bootstrap, doc = "```rust")] /// #![doc = in_root!()] /// /// macro_rules! in_root { () => { "" } } /// /// fn main() {} - /// ``` + #[cfg_attr(not(bootstrap), doc = "```")] + #[cfg_attr(bootstrap, doc = "```")] + // ^ Needed to avoid tidy warning about odd number of backticks /// /// {{produces}} /// diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 3d13fa70d8425..82ac04b77beb6 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1293,6 +1293,8 @@ impl Step for RustcBook { // functional sysroot. builder.std(self.build_compiler, self.target); let mut cmd = builder.tool_cmd(Tool::LintDocs); + cmd.arg("--build-rustc-stage"); + cmd.arg(self.build_compiler.stage.to_string()); cmd.arg("--src"); cmd.arg(builder.src.join("compiler")); cmd.arg("--out"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index d2f7a9a1f43f8..31bf2949ce205 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2732,7 +2732,7 @@ impl Step for CrateLibrustc { } fn metadata(&self) -> Option { - Some(StepMetadata::test("CrateLibrustc", self.target)) + Some(StepMetadata::test("CrateLibrustc", self.target).built_by(self.build_compiler)) } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 41a90fdae7a74..2f5e8528fa840 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2031,7 +2031,7 @@ mod snapshot { [test] Pretty [build] rustc 1 -> std 1 [build] rustc 0 -> std 0 - [test] CrateLibrustc + [test] rustc 0 -> CrateLibrustc 1 [build] rustc 1 -> rustc 2 [test] crate-bootstrap src/tools/coverage-dump [test] crate-bootstrap src/tools/jsondoclint @@ -2164,6 +2164,40 @@ mod snapshot { "); } + #[test] + fn test_compiler_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("compiler") + .stage(1) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> std 0 + [build] rustdoc 0 + [test] rustc 0 -> CrateLibrustc 1 + "); + } + + #[test] + fn test_compiler_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .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 1 -> std 1 + [build] rustdoc 1 + [test] rustc 1 -> CrateLibrustc 2 + "); + } + #[test] fn test_exclude() { let ctx = TestCtx::new(); @@ -2181,13 +2215,15 @@ mod snapshot { let get_steps = |args: &[&str]| ctx.config("test").args(args).get_steps(); + let rustc_metadata = + || StepMetadata::test("CrateLibrustc", host).built_by(Compiler::new(0, host)); // Ensure our test is valid, and `test::Rustc` would be run without the exclude. - get_steps(&[]).assert_contains(StepMetadata::test("CrateLibrustc", host)); + get_steps(&[]).assert_contains(rustc_metadata()); let steps = get_steps(&["--skip", "compiler/rustc_data_structures"]); // Ensure tests for rustc are not skipped. - steps.assert_contains(StepMetadata::test("CrateLibrustc", host)); + steps.assert_contains(rustc_metadata()); steps.assert_contains_fuzzy(StepMetadata::build("rustc", host)); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 450f325f1a970..d12cc962187f9 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1012,9 +1012,7 @@ impl Config { } if flags_compile_time_deps && !matches!(flags_cmd, Subcommand::Check { .. }) { - eprintln!( - "ERROR: Can't use --compile-time-deps with any subcommand other than check." - ); + eprintln!("ERROR: Can't use --compile-time-deps with any subcommand other than check."); exit!(1); } diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index b33344ca5dda4..bc38e931fe515 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -59,6 +59,8 @@ pub struct LintExtractor<'a> { pub rustc_target: &'a str, /// The target linker overriding `rustc`'s default pub rustc_linker: Option<&'a str>, + /// Stage of the compiler that builds the docs (the stage of `rustc_path`). + pub build_rustc_stage: u32, /// Verbose output. pub verbose: bool, /// Validate the style and the code example. @@ -216,14 +218,7 @@ impl<'a> LintExtractor<'a> { if let Some(text) = line.strip_prefix("/// ") { doc_lines.push(text.to_string()); } else if let Some(text) = line.strip_prefix("#[doc = \"") { - let escaped = text.strip_suffix("\"]").unwrap(); - let mut buf = String::new(); - unescape_str(escaped, |_, res| match res { - Ok(c) => buf.push(c), - Err(err) => { - assert!(!err.is_fatal(), "failed to unescape string literal") - } - }); + let buf = parse_doc_string(text); doc_lines.push(buf); } else if line == "///" { doc_lines.push("".to_string()); @@ -234,6 +229,20 @@ impl<'a> LintExtractor<'a> { // Ignore allow of lints (useful for // invalid_rust_codeblocks). continue; + } else if let Some(text) = + line.strip_prefix("#[cfg_attr(not(bootstrap), doc = \"") + { + if self.build_rustc_stage >= 1 { + let buf = parse_doc_string(text); + doc_lines.push(buf); + } + } else if let Some(text) = + line.strip_prefix("#[cfg_attr(bootstrap, doc = \"") + { + if self.build_rustc_stage == 0 { + let buf = parse_doc_string(text); + doc_lines.push(buf); + } } else { let name = lint_name(line).map_err(|e| { format!( @@ -580,6 +589,23 @@ impl<'a> LintExtractor<'a> { } } +/// Parses a doc string that follows `#[doc = "`. +fn parse_doc_string(text: &str) -> String { + let escaped = text.strip_suffix("]").unwrap_or(text); + let escaped = escaped.strip_suffix(")").unwrap_or(escaped).strip_suffix("\""); + let Some(escaped) = escaped else { + panic!("Cannot extract docstring content from {text}"); + }; + let mut buf = String::new(); + unescape_str(escaped, |_, res| match res { + Ok(c) => buf.push(c), + Err(err) => { + assert!(!err.is_fatal(), "failed to unescape string literal") + } + }); + buf +} + /// Adds `Lint`s that have been renamed. fn add_renamed_lints(lints: &mut Vec) { for (level, names) in RENAMES { diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs index e377283b1a453..1933ce4d2f1e3 100644 --- a/src/tools/lint-docs/src/main.rs +++ b/src/tools/lint-docs/src/main.rs @@ -25,6 +25,7 @@ fn doit() -> Result<(), Box> { let mut args = std::env::args().skip(1); let mut src_path = None; let mut out_path = None; + let mut build_rustc_stage = None; let mut rustc_path = None; let mut rustc_target = None; let mut rustc_linker = None; @@ -32,6 +33,14 @@ fn doit() -> Result<(), Box> { let mut validate = false; while let Some(arg) = args.next() { match arg.as_str() { + "--build-rustc-stage" => { + build_rustc_stage = match args.next() { + Some(s) => { + Some(s.parse::().expect("build rustc stage has to be an integer")) + } + None => return Err("--build-rustc-stage requires a value".into()), + }; + } "--src" => { src_path = match args.next() { Some(s) => Some(PathBuf::from(s)), @@ -67,6 +76,9 @@ fn doit() -> Result<(), Box> { s => return Err(format!("unexpected argument `{}`", s).into()), } } + if build_rustc_stage.is_none() { + return Err("--build-rustc-stage must be specified to the stage of the compiler that generates the docs".into()); + } if src_path.is_none() { return Err("--src must be specified to the directory with the compiler source".into()); } @@ -85,6 +97,7 @@ fn doit() -> Result<(), Box> { rustc_path: &rustc_path.unwrap(), rustc_target: &rustc_target.unwrap(), rustc_linker: rustc_linker.as_deref(), + build_rustc_stage: build_rustc_stage.unwrap(), verbose, validate, }; From 9a756361f4749532609d3d12cb359c43cee7b664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 27 Aug 2025 11:38:39 +0200 Subject: [PATCH 23/23] Do not run `lint-docs` tests in stage 1 `x test` by default --- src/bootstrap/src/core/build_steps/test.rs | 18 ++++++++---------- src/bootstrap/src/core/builder/tests.rs | 2 -- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 31bf2949ce205..7c9236983a952 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3431,24 +3431,22 @@ impl Step for LintDocs { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/lint-docs") + let stage = run.builder.top_stage; + // Lint docs tests might not work with stage 1, so do not run this test by default in + // `x test` below stage 2. + run.path("src/tools/lint-docs").default_condition(stage > 1) } fn make_run(run: RunConfig<'_>) { - // Bump the stage to 2, because the rustc book requires an in-tree compiler. - // At the same time, since this step is enabled by default, we don't want `x test` to fail - // in stage 1. - let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 { - run.builder.top_stage - } else { - 2 - }; + if run.builder.top_stage < 2 { + eprintln!("WARNING: lint-docs tests might not work below stage 2"); + } run.builder.ensure(LintDocs { build_compiler: prepare_doc_compiler( run.builder, run.builder.config.host_target, - stage, + run.builder.top_stage, ), target: run.target, }); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 2f5e8528fa840..1cd10cd43d833 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2064,8 +2064,6 @@ mod snapshot { [test] link-check [test] tier-check [test] rustc 0 -> rust-analyzer 1 - [doc] rustc (book) - [test] rustc 1 -> lint-docs 2 [build] rustc 0 -> RustdocTheme 1 [test] rustdoc-theme 1 [test] RustdocUi