Skip to content

Commit 60d20b1

Browse files
authored
Rollup merge of rust-lang#104046 - RalfJung:run-miri-run, r=oli-obk
bootstrap: add support for running Miri on a file This enables: ``` ./x.py run src/tools/miri --stage 0 --args src/tools/miri/tests/pass/hello.rs ``` That can be super helpful for debugging. Also avoid sharing the Miri sysroot dir with a system-wide (rustup-managed) installation of Miri. Fixes rust-lang#76666
2 parents c729acb + a9edee7 commit 60d20b1

File tree

6 files changed

+155
-64
lines changed

6 files changed

+155
-64
lines changed

src/bootstrap/builder.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ impl<'a> Builder<'a> {
755755
run::BuildManifest,
756756
run::BumpStage0,
757757
run::ReplaceVersionPlaceholder,
758+
run::Miri,
758759
),
759760
// These commands either don't use paths, or they're special-cased in Build::build()
760761
Kind::Clean | Kind::Format | Kind::Setup => vec![],
@@ -818,7 +819,7 @@ impl<'a> Builder<'a> {
818819
Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
819820
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
820821
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
821-
Subcommand::Run { ref paths } => (Kind::Run, &paths[..]),
822+
Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]),
822823
Subcommand::Format { .. } => (Kind::Format, &[][..]),
823824
Subcommand::Clean { .. } | Subcommand::Setup { .. } => {
824825
panic!()

src/bootstrap/flags.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub enum Subcommand {
140140
},
141141
Run {
142142
paths: Vec<PathBuf>,
143+
args: Vec<String>,
143144
},
144145
Setup {
145146
profile: Profile,
@@ -342,6 +343,9 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
342343
Kind::Format => {
343344
opts.optflag("", "check", "check formatting instead of applying.");
344345
}
346+
Kind::Run => {
347+
opts.optmulti("", "args", "arguments for the tool", "ARGS");
348+
}
345349
_ => {}
346350
};
347351

@@ -613,7 +617,7 @@ Arguments:
613617
println!("\nrun requires at least a path!\n");
614618
usage(1, &opts, verbose, &subcommand_help);
615619
}
616-
Subcommand::Run { paths }
620+
Subcommand::Run { paths, args: matches.opt_strs("args") }
617621
}
618622
Kind::Setup => {
619623
let profile = if paths.len() > 1 {
@@ -721,24 +725,29 @@ impl Subcommand {
721725
}
722726

723727
pub fn test_args(&self) -> Vec<&str> {
724-
let mut args = vec![];
725-
726728
match *self {
727729
Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
728-
args.extend(test_args.iter().flat_map(|s| s.split_whitespace()))
730+
test_args.iter().flat_map(|s| s.split_whitespace()).collect()
729731
}
730-
_ => (),
732+
_ => vec![],
731733
}
732-
733-
args
734734
}
735735

736736
pub fn rustc_args(&self) -> Vec<&str> {
737737
match *self {
738738
Subcommand::Test { ref rustc_args, .. } => {
739739
rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
740740
}
741-
_ => Vec::new(),
741+
_ => vec![],
742+
}
743+
}
744+
745+
pub fn args(&self) -> Vec<&str> {
746+
match *self {
747+
Subcommand::Run { ref args, .. } => {
748+
args.iter().flat_map(|s| s.split_whitespace()).collect()
749+
}
750+
_ => vec![],
742751
}
743752
}
744753

src/bootstrap/run.rs

+66-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use std::process::Command;
2+
13
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
4+
use crate::config::TargetSelection;
25
use crate::dist::distdir;
3-
use crate::tool::Tool;
6+
use crate::test;
7+
use crate::tool::{self, SourceType, Tool};
48
use crate::util::output;
5-
use std::process::Command;
9+
use crate::Mode;
610

711
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
812
pub struct ExpandYamlAnchors;
@@ -125,3 +129,63 @@ impl Step for ReplaceVersionPlaceholder {
125129
builder.run(&mut cmd);
126130
}
127131
}
132+
133+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
134+
pub struct Miri {
135+
stage: u32,
136+
host: TargetSelection,
137+
target: TargetSelection,
138+
}
139+
140+
impl Step for Miri {
141+
type Output = ();
142+
const ONLY_HOSTS: bool = false;
143+
144+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
145+
run.path("src/tools/miri")
146+
}
147+
148+
fn make_run(run: RunConfig<'_>) {
149+
run.builder.ensure(Miri {
150+
stage: run.builder.top_stage,
151+
host: run.build_triple(),
152+
target: run.target,
153+
});
154+
}
155+
156+
fn run(self, builder: &Builder<'_>) {
157+
let stage = self.stage;
158+
let host = self.host;
159+
let target = self.target;
160+
let compiler = builder.compiler(stage, host);
161+
162+
let miri = builder
163+
.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() })
164+
.expect("in-tree tool");
165+
let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target);
166+
167+
// # Run miri.
168+
// Running it via `cargo run` as that figures out the right dylib path.
169+
// add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so.
170+
let mut miri = tool::prepare_tool_cargo(
171+
builder,
172+
compiler,
173+
Mode::ToolRustc,
174+
host,
175+
"run",
176+
"src/tools/miri",
177+
SourceType::InTree,
178+
&[],
179+
);
180+
miri.add_rustc_lib_path(builder, compiler);
181+
// Forward arguments.
182+
miri.arg("--").arg("--target").arg(target.rustc_target_arg());
183+
miri.args(builder.config.cmd.args());
184+
185+
// miri tests need to know about the stage sysroot
186+
miri.env("MIRI_SYSROOT", &miri_sysroot);
187+
188+
let mut miri = Command::from(miri);
189+
builder.run(&mut miri);
190+
}
191+
}

src/bootstrap/test.rs

+55-43
Original file line numberDiff line numberDiff line change
@@ -465,49 +465,20 @@ pub struct Miri {
465465
target: TargetSelection,
466466
}
467467

468-
impl Step for Miri {
469-
type Output = ();
470-
const ONLY_HOSTS: bool = false;
471-
472-
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
473-
run.path("src/tools/miri")
474-
}
475-
476-
fn make_run(run: RunConfig<'_>) {
477-
run.builder.ensure(Miri {
478-
stage: run.builder.top_stage,
479-
host: run.build_triple(),
480-
target: run.target,
481-
});
482-
}
483-
484-
/// Runs `cargo test` for miri.
485-
fn run(self, builder: &Builder<'_>) {
486-
let stage = self.stage;
487-
let host = self.host;
488-
let target = self.target;
489-
let compiler = builder.compiler(stage, host);
490-
// We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri.
491-
// Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage.
492-
let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
493-
494-
let miri = builder
495-
.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() })
496-
.expect("in-tree tool");
497-
let _cargo_miri = builder
498-
.ensure(tool::CargoMiri { compiler, target: self.host, extra_features: Vec::new() })
499-
.expect("in-tree tool");
500-
// The stdlib we need might be at a different stage. And just asking for the
501-
// sysroot does not seem to populate it, so we do that first.
502-
builder.ensure(compile::Std::new(compiler_std, host));
503-
let sysroot = builder.sysroot(compiler_std);
504-
505-
// # Run `cargo miri setup` for the given target.
468+
impl Miri {
469+
/// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.
470+
pub fn build_miri_sysroot(
471+
builder: &Builder<'_>,
472+
compiler: Compiler,
473+
miri: &Path,
474+
target: TargetSelection,
475+
) -> String {
476+
let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysrot");
506477
let mut cargo = tool::prepare_tool_cargo(
507478
builder,
508479
compiler,
509480
Mode::ToolRustc,
510-
host,
481+
compiler.host,
511482
"run",
512483
"src/tools/miri/cargo-miri",
513484
SourceType::InTree,
@@ -521,6 +492,8 @@ impl Step for Miri {
521492
cargo.env("MIRI_LIB_SRC", builder.src.join("library"));
522493
// Tell it where to find Miri.
523494
cargo.env("MIRI", &miri);
495+
// Tell it where to put the sysroot.
496+
cargo.env("MIRI_SYSROOT", &miri_sysroot);
524497
// Debug things.
525498
cargo.env("RUST_BACKTRACE", "1");
526499

@@ -535,7 +508,7 @@ impl Step for Miri {
535508
cargo.arg("--print-sysroot");
536509

537510
// FIXME: Is there a way in which we can re-use the usual `run` helpers?
538-
let miri_sysroot = if builder.config.dry_run {
511+
if builder.config.dry_run {
539512
String::new()
540513
} else {
541514
builder.verbose(&format!("running: {:?}", cargo));
@@ -548,7 +521,48 @@ impl Step for Miri {
548521
let sysroot = stdout.trim_end();
549522
builder.verbose(&format!("`cargo miri setup --print-sysroot` said: {:?}", sysroot));
550523
sysroot.to_owned()
551-
};
524+
}
525+
}
526+
}
527+
528+
impl Step for Miri {
529+
type Output = ();
530+
const ONLY_HOSTS: bool = false;
531+
532+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
533+
run.path("src/tools/miri")
534+
}
535+
536+
fn make_run(run: RunConfig<'_>) {
537+
run.builder.ensure(Miri {
538+
stage: run.builder.top_stage,
539+
host: run.build_triple(),
540+
target: run.target,
541+
});
542+
}
543+
544+
/// Runs `cargo test` for miri.
545+
fn run(self, builder: &Builder<'_>) {
546+
let stage = self.stage;
547+
let host = self.host;
548+
let target = self.target;
549+
let compiler = builder.compiler(stage, host);
550+
// We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri.
551+
// Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage.
552+
let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host);
553+
554+
let miri = builder
555+
.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() })
556+
.expect("in-tree tool");
557+
let _cargo_miri = builder
558+
.ensure(tool::CargoMiri { compiler, target: self.host, extra_features: Vec::new() })
559+
.expect("in-tree tool");
560+
// The stdlib we need might be at a different stage. And just asking for the
561+
// sysroot does not seem to populate it, so we do that first.
562+
builder.ensure(compile::Std::new(compiler_std, host));
563+
let sysroot = builder.sysroot(compiler_std);
564+
// We also need a Miri sysroot.
565+
let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target);
552566

553567
// # Run `cargo test`.
554568
let mut cargo = tool::prepare_tool_cargo(
@@ -566,7 +580,6 @@ impl Step for Miri {
566580
// miri tests need to know about the stage sysroot
567581
cargo.env("MIRI_SYSROOT", &miri_sysroot);
568582
cargo.env("MIRI_HOST_SYSROOT", sysroot);
569-
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
570583
cargo.env("MIRI", &miri);
571584
// propagate --bless
572585
if builder.config.cmd.bless() {
@@ -607,7 +620,6 @@ impl Step for Miri {
607620
// Tell `cargo miri` where to find things.
608621
cargo.env("MIRI_SYSROOT", &miri_sysroot);
609622
cargo.env("MIRI_HOST_SYSROOT", sysroot);
610-
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
611623
cargo.env("MIRI", &miri);
612624
// Debug things.
613625
cargo.env("RUST_BACKTRACE", "1");

src/tools/miri/README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,10 @@ Moreover, Miri recognizes some environment variables:
433433
trigger a re-build of the standard library; you have to clear the Miri build
434434
cache manually (on Linux, `rm -rf ~/.cache/miri`).
435435
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
436-
using `cargo miri`, only set this if you do not want to use the automatically created sysroot. For
437-
directly invoking the Miri driver, this variable (or a `--sysroot` flag) is mandatory.
436+
using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
437+
automatically created sysroot. For directly invoking the Miri driver, this variable (or a
438+
`--sysroot` flag) is mandatory. When invoking `cargo miri setup`, this indicates where the sysroot
439+
will be put.
438440
* `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target
439441
architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same
440442
purpose.

src/tools/miri/cargo-miri/src/setup.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
1717
let only_setup = matches!(subcommand, MiriCommand::Setup);
1818
let ask_user = !only_setup;
1919
let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
20-
if std::env::var_os("MIRI_SYSROOT").is_some() {
21-
if only_setup {
22-
println!("WARNING: MIRI_SYSROOT already set, not doing anything.")
23-
}
20+
if !only_setup && std::env::var_os("MIRI_SYSROOT").is_some() {
21+
// Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
2422
return;
2523
}
2624

@@ -61,8 +59,13 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
6159
}
6260

6361
// Determine where to put the sysroot.
64-
let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
65-
let sysroot_dir = user_dirs.cache_dir();
62+
let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") {
63+
Some(dir) => PathBuf::from(dir),
64+
None => {
65+
let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
66+
user_dirs.cache_dir().to_owned()
67+
}
68+
};
6669
// Sysroot configuration and build details.
6770
let sysroot_config = if std::env::var_os("MIRI_NO_STD").is_some() {
6871
SysrootConfig::NoStd
@@ -111,7 +114,7 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
111114
(command, rustflags)
112115
};
113116
// Make sure all target-level Miri invocations know their sysroot.
114-
std::env::set_var("MIRI_SYSROOT", sysroot_dir);
117+
std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
115118

116119
// Do the build.
117120
if only_setup {
@@ -121,7 +124,7 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
121124
// We want to be quiet, but still let the user know that something is happening.
122125
eprint!("Preparing a sysroot for Miri (target: {target})... ");
123126
}
124-
Sysroot::new(sysroot_dir, target)
127+
Sysroot::new(&sysroot_dir, target)
125128
.build_from_source(&rust_src, BuildMode::Check, sysroot_config, rustc_version, cargo_cmd)
126129
.unwrap_or_else(|_| {
127130
if only_setup {

0 commit comments

Comments
 (0)