prepare_tool_cargo: add support for a miri-test mode, and use it in the cargo-miri smoke test and Miri sysroot build

This commit is contained in:
Ralf Jung 2024-03-28 23:03:31 +01:00
parent fd7909aa59
commit 2a939422ca
4 changed files with 85 additions and 77 deletions

View File

@ -121,7 +121,6 @@ fn run(self, builder: &Builder<'_>) -> Self::Output {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Miri { pub struct Miri {
stage: u32,
host: TargetSelection, host: TargetSelection,
target: TargetSelection, target: TargetSelection,
} }
@ -135,22 +134,17 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Miri { run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
stage: run.builder.top_stage,
host: run.build_triple(),
target: run.target,
});
} }
fn run(self, builder: &Builder<'_>) { fn run(self, builder: &Builder<'_>) {
let stage = self.stage; let stage = builder.top_stage;
let host = self.host; let host = self.host;
let target = self.target; let target = self.target;
let compiler = builder.compiler(stage, host); let compiler = builder.compiler(stage, host);
let miri = let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler_std, target);
let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target);
// # Run miri. // # Run miri.
// Running it via `cargo run` as that figures out the right dylib path. // Running it via `cargo run` as that figures out the right dylib path.

View File

@ -493,7 +493,6 @@ fn run(self, builder: &Builder<'_>) {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Miri { pub struct Miri {
stage: u32,
host: TargetSelection, host: TargetSelection,
target: TargetSelection, target: TargetSelection,
} }
@ -502,41 +501,31 @@ impl Miri {
/// Run `cargo miri setup` for the given target, return where the Miri sysroot was put. /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.
pub fn build_miri_sysroot( pub fn build_miri_sysroot(
builder: &Builder<'_>, builder: &Builder<'_>,
compiler: Compiler, compiler_std: Compiler,
miri: &Path,
target: TargetSelection, target: TargetSelection,
) -> String { ) -> String {
let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); let miri_sysroot = builder.out.join(compiler_std.host.triple).join("miri-sysroot");
let mut cargo = tool::prepare_tool_cargo( let mut cargo = builder::Cargo::new(
builder, builder,
compiler, compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again
Mode::ToolRustc, Mode::Std,
compiler.host, SourceType::Submodule,
"run", target,
"src/tools/miri/cargo-miri", "miri-setup",
SourceType::InTree,
&[],
); );
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. // Tell `cargo miri setup` where to find the sources.
cargo.env("MIRI_LIB_SRC", builder.src.join("library")); 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. // Tell it where to put the sysroot.
cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_SYSROOT", &miri_sysroot);
// Debug things.
cargo.env("RUST_BACKTRACE", "1");
let mut cargo = Command::from(cargo); let mut cargo = Command::from(cargo);
let _guard = builder.msg( let _guard = builder.msg(
Kind::Build, Kind::Build,
compiler.stage + 1, compiler_std.stage,
"miri sysroot", "miri sysroot",
compiler.host, compiler_std.host,
compiler.host, compiler_std.host,
); );
builder.run(&mut cargo); builder.run(&mut cargo);
@ -574,16 +563,12 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Miri { run.builder.ensure(Miri { host: run.build_triple(), target: run.target });
stage: run.builder.top_stage,
host: run.build_triple(),
target: run.target,
});
} }
/// Runs `cargo test` for miri. /// Runs `cargo test` for miri.
fn run(self, builder: &Builder<'_>) { fn run(self, builder: &Builder<'_>) {
let stage = self.stage; let stage = builder.top_stage;
let host = self.host; let host = self.host;
let target = self.target; let target = self.target;
let compiler = builder.compiler(stage, host); let compiler = builder.compiler(stage, host);
@ -592,18 +577,15 @@ fn run(self, builder: &Builder<'_>) {
let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
let miri = let miri =
builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); builder.ensure(tool::Miri { compiler, target: host, extra_features: Vec::new() });
let _cargo_miri = builder.ensure(tool::CargoMiri { // the ui tests also assume cargo-miri has been built
compiler, builder.ensure(tool::CargoMiri { compiler, target: host, extra_features: Vec::new() });
target: self.host,
extra_features: Vec::new(),
});
// The stdlib we need might be at a different stage. And just asking for the // 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. // sysroot does not seem to populate it, so we do that first.
builder.ensure(compile::Std::new(compiler_std, host)); builder.ensure(compile::Std::new(compiler_std, host));
let sysroot = builder.sysroot(compiler_std); let sysroot = builder.sysroot(compiler_std);
// We also need a Miri sysroot. // 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`. // # Run `cargo test`.
let mut cargo = tool::prepare_tool_cargo( let mut cargo = tool::prepare_tool_cargo(
@ -616,10 +598,13 @@ fn run(self, builder: &Builder<'_>) {
SourceType::InTree, SourceType::InTree,
&[], &[],
); );
let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
cargo.add_rustc_lib_path(builder); 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 // miri tests need to know about the stage sysroot
cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_SYSROOT", &miri_sysroot);
cargo.env("MIRI_HOST_SYSROOT", &sysroot); cargo.env("MIRI_HOST_SYSROOT", &sysroot);
@ -632,10 +617,8 @@ fn run(self, builder: &Builder<'_>) {
// Set the target. // Set the target.
cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); 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); let _time = helpers::timeit(builder);
builder.run(&mut cargo); builder.run(&mut cargo);
} }
@ -650,8 +633,14 @@ fn run(self, builder: &Builder<'_>) {
// Optimizations can change error locations and remove UB so don't run `fail` tests. // Optimizations can change error locations and remove UB so don't run `fail` tests.
cargo.args(["tests/pass", "tests/panic"]); 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); let _time = helpers::timeit(builder);
builder.run(&mut cargo); builder.run(&mut cargo);
} }
@ -660,28 +649,20 @@ fn run(self, builder: &Builder<'_>) {
// # Run `cargo miri test`. // # Run `cargo miri test`.
// This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // 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 // 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( let mut cargo = tool::prepare_tool_cargo(
builder, builder,
compiler, compiler_std, // this is compiler+1; cargo_miri_cmd will do -1 again
Mode::ToolRustc, Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!
host, target,
"run", "miri-test",
"src/tools/miri/cargo-miri", "src/tools/miri/test-cargo-miri",
SourceType::Submodule, 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. // 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). // 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 { if builder.doc_tests != DocTests::No {
@ -697,17 +678,16 @@ fn run(self, builder: &Builder<'_>) {
} }
} }
// Tell `cargo miri` where to find things. // Tell `cargo miri` where to find the sysroots.
cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_SYSROOT", &miri_sysroot);
cargo.env("MIRI_HOST_SYSROOT", 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. // Finally, pass test-args and run everything.
cargo.arg("--").args(builder.config.test_args()); cargo.arg("--").args(builder.config.test_args());
let mut cargo = Command::from(cargo); 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); let _time = helpers::timeit(builder);
builder.run(&mut cargo); builder.run(&mut cargo);
} }

View File

@ -1253,6 +1253,30 @@ pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command {
cmd 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 { pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
let mut cmd = Command::new(self.bootstrap_out.join("rustdoc")); let mut cmd = Command::new(self.bootstrap_out.join("rustdoc"));
cmd.env("RUSTC_STAGE", compiler.stage.to_string()) cmd.env("RUSTC_STAGE", compiler.stage.to_string())
@ -1296,18 +1320,24 @@ pub fn bare_cargo(
target: TargetSelection, target: TargetSelection,
cmd: &str, cmd: &str,
) -> Command { ) -> Command {
let mut cargo = if cmd == "clippy" { let mut cargo;
self.cargo_clippy_cmd(compiler) 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 { } 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. // 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. // This matters when using vendoring and the working directory is outside the repository.
cargo.current_dir(&self.src); cargo.current_dir(&self.src);
let out_dir = self.stage_out(compiler, mode); 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` // 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 // from out of tree it shouldn't matter, since x.py is only used for
@ -1337,7 +1367,8 @@ pub fn bare_cargo(
if self.config.rust_optimize.is_release() { if self.config.rust_optimize.is_release() {
// FIXME: cargo bench/install do not accept `--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"); cargo.arg("--release");
} }
} }
@ -1353,7 +1384,8 @@ pub fn bare_cargo(
/// Cargo. This cargo will be configured to use `compiler` as the actual /// Cargo. This cargo will be configured to use `compiler` as the actual
/// rustc compiler, its output will be scoped by `mode`'s output directory, /// 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 /// 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( fn cargo(
&self, &self,
compiler: Compiler, compiler: Compiler,

View File

@ -250,6 +250,8 @@ pub enum Mode {
/// directory. This is for miscellaneous sets of tools that are built /// directory. This is for miscellaneous sets of tools that are built
/// using the bootstrap stage0 compiler in its entirety (target libraries /// using the bootstrap stage0 compiler in its entirety (target libraries
/// and all). Typically these tools compile with stable Rust. /// and all). Typically these tools compile with stable Rust.
///
/// Only works for stage 0.
ToolBootstrap, ToolBootstrap,
/// Build a tool which uses the locally built std, placing output in the /// Build a tool which uses the locally built std, placing output in the