From fd7909aa59a761f8ba5bf34df3d84a6afa4bff1c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 28 Mar 2024 22:46:07 +0100 Subject: [PATCH 1/8] x.py test miri: respect --no-doc / --doc --- src/bootstrap/src/core/build_steps/test.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 48596af7ca029..11baf0cda80ba 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -680,12 +680,22 @@ impl Step for Miri { .arg("--manifest-path") .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); cargo.arg("--target").arg(target.rustc_target_arg()); - cargo.arg("--").args(builder.config.test_args()); // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test". // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage. // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER). - cargo.env("RUSTDOC", builder.rustdoc(compiler_std)); + if builder.doc_tests != DocTests::No { + cargo.env("RUSTDOC", builder.rustdoc(compiler_std)); + } + match builder.doc_tests { + DocTests::Yes => {} + DocTests::No => { + cargo.arg("--tests"); + } + DocTests::Only => { + cargo.arg("--doc"); + } + } // Tell `cargo miri` where to find things. cargo.env("MIRI_SYSROOT", &miri_sysroot); @@ -694,6 +704,8 @@ impl Step for Miri { // Debug things. cargo.env("RUST_BACKTRACE", "1"); + // Finally, pass test-args and run everything. + cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { let _time = helpers::timeit(builder); From 2a939422ca7471317b7b0d617272e6c18ca5a291 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 28 Mar 2024 23:03:31 +0100 Subject: [PATCH 2/8] prepare_tool_cargo: add support for a miri-test mode, and use it in the cargo-miri smoke test and Miri sysroot build --- src/bootstrap/src/core/build_steps/run.rs | 14 +-- src/bootstrap/src/core/build_steps/test.rs | 100 +++++++++------------ src/bootstrap/src/core/builder.rs | 46 ++++++++-- src/bootstrap/src/lib.rs | 2 + 4 files changed, 85 insertions(+), 77 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 61ee2fc1f6f3d..bad51825235e5 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -121,7 +121,6 @@ impl Step for ReplaceVersionPlaceholder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, host: TargetSelection, target: TargetSelection, } @@ -135,22 +134,17 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { host: run.build_triple(), target: run.target }); } fn run(self, builder: &Builder<'_>) { - let stage = self.stage; + let stage = builder.top_stage; let host = self.host; let target = self.target; let compiler = builder.compiler(stage, host); - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target); + let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); + let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler_std, target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 11baf0cda80ba..2561f58e35624 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -493,7 +493,6 @@ impl Step for RustDemangler { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, host: TargetSelection, target: TargetSelection, } @@ -502,41 +501,31 @@ impl Miri { /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put. pub fn build_miri_sysroot( builder: &Builder<'_>, - compiler: Compiler, - miri: &Path, + compiler_std: Compiler, target: TargetSelection, ) -> String { - let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); - let mut cargo = tool::prepare_tool_cargo( + let miri_sysroot = builder.out.join(compiler_std.host.triple).join("miri-sysroot"); + let mut cargo = builder::Cargo::new( builder, - compiler, - Mode::ToolRustc, - compiler.host, - "run", - "src/tools/miri/cargo-miri", - SourceType::InTree, - &[], + compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again + Mode::Std, + SourceType::Submodule, + target, + "miri-setup", ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("setup"); - cargo.arg("--target").arg(target.rustc_target_arg()); // Tell `cargo miri setup` where to find the sources. cargo.env("MIRI_LIB_SRC", builder.src.join("library")); - // Tell it where to find Miri. - cargo.env("MIRI", miri); // Tell it where to put the sysroot. cargo.env("MIRI_SYSROOT", &miri_sysroot); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); let mut cargo = Command::from(cargo); let _guard = builder.msg( Kind::Build, - compiler.stage + 1, + compiler_std.stage, "miri sysroot", - compiler.host, - compiler.host, + compiler_std.host, + compiler_std.host, ); builder.run(&mut cargo); @@ -574,16 +563,12 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { host: run.build_triple(), target: run.target }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; + let stage = builder.top_stage; let host = self.host; let target = self.target; let compiler = builder.compiler(stage, host); @@ -592,18 +577,15 @@ impl Step for Miri { let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let _cargo_miri = builder.ensure(tool::CargoMiri { - compiler, - target: self.host, - extra_features: Vec::new(), - }); + builder.ensure(tool::Miri { compiler, target: host, extra_features: Vec::new() }); + // the ui tests also assume cargo-miri has been built + builder.ensure(tool::CargoMiri { compiler, target: host, extra_features: Vec::new() }); // The stdlib we need might be at a different stage. And just asking for the // sysroot does not seem to populate it, so we do that first. builder.ensure(compile::Std::new(compiler_std, host)); let sysroot = builder.sysroot(compiler_std); // We also need a Miri sysroot. - let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target); + let miri_sysroot = Miri::build_miri_sysroot(builder, compiler_std, target); // # Run `cargo test`. let mut cargo = tool::prepare_tool_cargo( @@ -616,10 +598,13 @@ impl Step for Miri { SourceType::InTree, &[], ); - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); cargo.add_rustc_lib_path(builder); + // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test + // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", &sysroot); @@ -632,10 +617,8 @@ impl Step for Miri { // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); - // This can NOT be `run_cargo_test` since the Miri test runner - // does not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -650,8 +633,14 @@ impl Step for Miri { // Optimizations can change error locations and remove UB so don't run `fail` tests. cargo.args(["tests/pass", "tests/panic"]); - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool( + Kind::Test, + compiler.stage, + "miri (mir-opt-level 4)", + host, + target, + ); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -660,28 +649,20 @@ impl Step for Miri { // # Run `cargo miri test`. // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // that we get the desired output), but that is sufficient to make sure that the libtest harness - // itself executes properly under Miri. + // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. + // Everything here needs `compiler_std` to be actually testing the Miri in the current stage. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, - host, - "run", - "src/tools/miri/cargo-miri", + compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again + Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! + target, + "miri-test", + "src/tools/miri/test-cargo-miri", SourceType::Submodule, &[], ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("test"); - if builder.config.locked_deps { - cargo.arg("--locked"); - } - cargo - .arg("--manifest-path") - .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); - cargo.arg("--target").arg(target.rustc_target_arg()); - // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test". + // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "miri", not a "test". // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage. // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER). if builder.doc_tests != DocTests::No { @@ -697,17 +678,16 @@ impl Step for Miri { } } - // Tell `cargo miri` where to find things. + // Tell `cargo miri` where to find the sysroots. cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("MIRI", &miri); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); // Finally, pass test-args and run everything. cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { + let _guard = + builder.msg_sysroot_tool(Kind::Test, compiler.stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 7f93fdc72ef08..8294abcb4eb77 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1253,6 +1253,30 @@ impl<'a> Builder<'a> { cmd } + pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command { + assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build); + + let miri = self.ensure(tool::Miri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + let cargo_miri = self.ensure(tool::CargoMiri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + // Invoke cargo-miri, make sure we can find miri and cargo. + let mut cmd = Command::new(cargo_miri); + cmd.env("MIRI", &miri); + cmd.env("CARGO", &self.initial_cargo); + // Need to add the run_compiler libs. Not entirely sure why that has to be one stage up from + // what Miri was built for. + self.add_rustc_lib_path(run_compiler, &mut cmd); + cmd + } + pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command { let mut cmd = Command::new(self.bootstrap_out.join("rustdoc")); cmd.env("RUSTC_STAGE", compiler.stage.to_string()) @@ -1296,18 +1320,24 @@ impl<'a> Builder<'a> { target: TargetSelection, cmd: &str, ) -> Command { - let mut cargo = if cmd == "clippy" { - self.cargo_clippy_cmd(compiler) + let mut cargo; + if cmd == "clippy" { + cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd); + } else if let Some(subcmd) = cmd.strip_prefix("miri-") { + cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg(subcmd); } else { - Command::new(&self.initial_cargo) - }; + cargo = Command::new(&self.initial_cargo); + cargo.arg(cmd); + } // Run cargo from the source root so it can find .cargo/config. // This matters when using vendoring and the working directory is outside the repository. cargo.current_dir(&self.src); let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); + cargo.env("CARGO_TARGET_DIR", &out_dir); // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` // from out of tree it shouldn't matter, since x.py is only used for @@ -1337,7 +1367,8 @@ impl<'a> Builder<'a> { if self.config.rust_optimize.is_release() { // FIXME: cargo bench/install do not accept `--release` - if cmd != "bench" && cmd != "install" { + // and miri doesn't want it + if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") { cargo.arg("--release"); } } @@ -1353,7 +1384,8 @@ impl<'a> Builder<'a> { /// Cargo. This cargo will be configured to use `compiler` as the actual /// rustc compiler, its output will be scoped by `mode`'s output directory, /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. + /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands + /// to be run with Miri. fn cargo( &self, compiler: Compiler, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d8397ab51de3c..44452446eb87f 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -250,6 +250,8 @@ pub enum Mode { /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries /// and all). Typically these tools compile with stable Rust. + /// + /// Only works for stage 0. ToolBootstrap, /// Build a tool which uses the locally built std, placing output in the From 10d68caf61c62f54ba854392e407bdc3f2547895 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 29 Mar 2024 12:27:07 +0100 Subject: [PATCH 3/8] cargo-miri: reorder a comment to make it less confusing --- src/tools/miri/cargo-miri/src/phases.rs | 2 +- src/tools/miri/cargo-miri/src/setup.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 8c0f605fd6ec2..6b801600bc0aa 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -213,7 +213,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { } // Run cargo. - debug_cmd("[cargo-miri miri]", verbose, &cmd); + debug_cmd("[cargo-miri cargo]", verbose, &cmd); exec(cmd) } diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index a98e1fcd485ea..401e9158faec5 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -90,13 +90,13 @@ pub fn setup( let cargo_cmd = { let mut command = cargo(); // Use Miri as rustc to build a libstd compatible with us (and use the right flags). + // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags + // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). + // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`, // because we still need bootstrap to distinguish between host and target crates. // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used // for target crates. - // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags - // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). - // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); if env::var_os("RUSTC_STAGE").is_some() { assert!(env::var_os("RUSTC").is_some()); From a632781bb582ca81d68510a22361da22c0d98052 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 29 Mar 2024 12:27:20 +0100 Subject: [PATCH 4/8] bootstrap/rustc: remove a miri hack --- src/bootstrap/src/bin/rustc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 74a924d86c796..12251924717d1 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -150,7 +150,7 @@ fn main() { { cmd.arg("-Ztls-model=initial-exec"); } - } else if std::env::var("MIRI").is_err() { + } else { // Find any host flags that were passed by bootstrap. // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces. if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") { From 160a8c8e8cedd9982f2288fecd990c6a6008704d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Mar 2024 13:27:17 +0100 Subject: [PATCH 5/8] shift Miri's stage so that it matches other rustc-based tools --- src/bootstrap/src/core/build_steps/run.rs | 28 ++++--- src/bootstrap/src/core/build_steps/test.rs | 92 ++++++++++++---------- src/bootstrap/src/core/builder.rs | 11 +-- 3 files changed, 74 insertions(+), 57 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index bad51825235e5..7028bffea5442 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -121,7 +121,6 @@ impl Step for ReplaceVersionPlaceholder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - host: TargetSelection, target: TargetSelection, } @@ -134,24 +133,35 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { host: run.build_triple(), target: run.target }); + run.builder.ensure(Miri { target: run.target }); } fn run(self, builder: &Builder<'_>) { - let stage = builder.top_stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); - let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler_std, target); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be run at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Get a target sysroot for Miri. + let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. let mut miri = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "run", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 2561f58e35624..c3fab96ef26e0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -493,7 +493,6 @@ impl Step for RustDemangler { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - host: TargetSelection, target: TargetSelection, } @@ -501,13 +500,13 @@ impl Miri { /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put. pub fn build_miri_sysroot( builder: &Builder<'_>, - compiler_std: Compiler, + compiler: Compiler, target: TargetSelection, ) -> String { - let miri_sysroot = builder.out.join(compiler_std.host.triple).join("miri-sysroot"); + let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); let mut cargo = builder::Cargo::new( builder, - compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again + compiler, Mode::Std, SourceType::Submodule, target, @@ -520,13 +519,8 @@ impl Miri { cargo.env("MIRI_SYSROOT", &miri_sysroot); let mut cargo = Command::from(cargo); - let _guard = builder.msg( - Kind::Build, - compiler_std.stage, - "miri sysroot", - compiler_std.host, - compiler_std.host, - ); + let _guard = + builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); builder.run(&mut cargo); // # Determine where Miri put its sysroot. @@ -563,34 +557,51 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { host: run.build_triple(), target: run.target }); + run.builder.ensure(Miri { target: run.target }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let stage = builder.top_stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. - // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); - - let miri = - builder.ensure(tool::Miri { compiler, target: host, extra_features: Vec::new() }); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be tested at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Build our tools. + let miri = builder.ensure(tool::Miri { + compiler: host_compiler, + target: host, + extra_features: Vec::new(), + }); // the ui tests also assume cargo-miri has been built - builder.ensure(tool::CargoMiri { compiler, target: host, extra_features: Vec::new() }); - // The stdlib we need might be at a different stage. And just asking for the - // sysroot does not seem to populate it, so we do that first. - builder.ensure(compile::Std::new(compiler_std, host)); - let sysroot = builder.sysroot(compiler_std); - // We also need a Miri sysroot. - let miri_sysroot = Miri::build_miri_sysroot(builder, compiler_std, target); + builder.ensure(tool::CargoMiri { + compiler: host_compiler, + target: host, + extra_features: Vec::new(), + }); + + // We also need sysroots, for Miri and for the host (the latter for build scripts). + // This is for the tests so everything is done with the target compiler. + let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target); + builder.ensure(compile::Std::new(target_compiler, host)); + let sysroot = builder.sysroot(target_compiler); // # Run `cargo test`. + // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "test", @@ -603,7 +614,7 @@ impl Step for Miri { // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder); // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); @@ -618,7 +629,7 @@ impl Step for Miri { cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); { - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -636,7 +647,7 @@ impl Step for Miri { { let _guard = builder.msg_sysroot_tool( Kind::Test, - compiler.stage, + stage, "miri (mir-opt-level 4)", host, target, @@ -650,10 +661,10 @@ impl Step for Miri { // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // that we get the desired output), but that is sufficient to make sure that the libtest harness // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. - // Everything here needs `compiler_std` to be actually testing the Miri in the current stage. + // This is running the build `cargo-miri` for the given target, so we need the target compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again + target_compiler, Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! target, "miri-test", @@ -662,16 +673,12 @@ impl Step for Miri { &[], ); - // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "miri", not a "test". - // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage. - // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER). - if builder.doc_tests != DocTests::No { - cargo.env("RUSTDOC", builder.rustdoc(compiler_std)); - } + // We're not using `prepare_cargo_test` so we have to do this ourselves. + // (We're not using that as the test-cargo-miri crate is not known to bootstrap.) match builder.doc_tests { DocTests::Yes => {} DocTests::No => { - cargo.arg("--tests"); + cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]); } DocTests::Only => { cargo.arg("--doc"); @@ -686,8 +693,7 @@ impl Step for Miri { cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { - let _guard = - builder.msg_sysroot_tool(Kind::Test, compiler.stage, "cargo-miri", host, target); + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 8294abcb4eb77..c97d8121e0ca6 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1031,10 +1031,10 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } - /// Obtain a compiler at a given stage and for a given host. Explicitly does - /// not take `Compiler` since all `Compiler` instances are meant to be - /// obtained through this function, since it ensures that they are valid - /// (i.e., built and assembled). + /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the + /// compiler will run on, *not* the target it will build code for). Explicitly does not take + /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, + /// since it ensures that they are valid (i.e., built and assembled). pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler { self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } }) } @@ -1703,7 +1703,8 @@ impl<'a> Builder<'a> { .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env( "RUSTDOC_REAL", - if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) { + // Make sure to handle both `test` and `miri-test` commands. + if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) { self.rustdoc(compiler) } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") From 882c115ca74d336fd6d3f98c6e4c7ffad9cf8e8f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Mar 2024 13:37:32 +0100 Subject: [PATCH 6/8] remove a pointless env var CARGO_EXTRA_FLAGS is respected by the ./miri script which we are not invoking here --- src/bootstrap/src/core/build_steps/test.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c3fab96ef26e0..8a79c28fc6c62 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -620,10 +620,6 @@ impl Step for Miri { cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", &sysroot); cargo.env("MIRI", &miri); - if builder.config.locked_deps { - // enforce lockfiles - cargo.env("CARGO_EXTRA_FLAGS", "--locked"); - } // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); From 4ecfb33d84f3e6c6fe34bda21c4506337b53b8e9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Mar 2024 13:52:32 +0100 Subject: [PATCH 7/8] move parallel_compiler handling into prepare_tool_cargo so that it is done everywhere --- src/bootstrap/src/core/build_steps/compile.rs | 6 ------ src/bootstrap/src/core/build_steps/tool.rs | 12 ++---------- src/bootstrap/src/core/builder.rs | 9 +++++++++ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d40a3ea4c884e..5bb7dc5b482db 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1130,12 +1130,6 @@ pub fn rustc_cargo_env( cargo.env("CFG_DEFAULT_LINKER", s); } - if builder.config.rustc_parallel { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - cargo.rustflag("--cfg=parallel_compiler"); - cargo.rustdocflag("--cfg=parallel_compiler"); - } if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index deab3fce54ca7..1edf65d28b76e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -469,7 +469,7 @@ impl Step for Rustdoc { features.push("jemalloc".to_string()); } - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -480,10 +480,6 @@ impl Step for Rustdoc { features.as_slice(), ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - let _guard = builder.msg_tool( Mode::ToolRustc, "rustdoc", @@ -732,7 +728,7 @@ impl Step for LlvmBitcodeLinker { builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); builder.ensure(compile::Rustc::new(self.compiler, self.target)); - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, self.compiler, Mode::ToolRustc, @@ -743,10 +739,6 @@ impl Step for LlvmBitcodeLinker { &self.extra_features, ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - builder.run(&mut cargo.into()); let tool_out = builder diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index c97d8121e0ca6..4b2ee1d050ad2 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2100,6 +2100,15 @@ impl<'a> Builder<'a> { rustflags.arg("-Zinline-mir"); } + if builder.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); + } + // set rustc args passed from command line let rustc_args = self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::>(); From 4a456deb601706b6fe458c3bae8511387989f017 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Mar 2024 15:09:38 +0100 Subject: [PATCH 8/8] add FIXME for making the cargo cmd properly typed --- src/bootstrap/src/core/builder.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 4b2ee1d050ad2..4941f5a8ca592 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1318,7 +1318,7 @@ impl<'a> Builder<'a> { compiler: Compiler, mode: Mode, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Command { let mut cargo; if cmd == "clippy" { @@ -1392,7 +1392,7 @@ impl<'a> Builder<'a> { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = self.bare_cargo(compiler, mode, target, cmd); let out_dir = self.stage_out(compiler, mode); @@ -2100,7 +2100,7 @@ impl<'a> Builder<'a> { rustflags.arg("-Zinline-mir"); } - if builder.config.rustc_parallel + if self.config.rustc_parallel && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) { // keep in sync with `bootstrap/lib.rs:Build::rustc_features` @@ -2342,7 +2342,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd); cargo.configure_linker(builder); @@ -2356,7 +2356,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { builder.cargo(compiler, mode, source_type, target, cmd) }