Skip to content

Commit 4c78479

Browse files
committed
Auto merge of #1406 - RalfJung:miri-as-rustc, r=RalfJung
cargo-miri: never invoke rustc Always go through 'MIRI_BE_RUSTC=1 miri' instead. This is based on @oli-obk's great idea to add a way to make Miri behave like rustc, which already helped us in #1405. Now it means in `cargo-miri` we run *all* crates through the same binary, and use the env var to determine if we compile or interpret them. This makes sure the compiler is consistent. The `rustc` binary of the current toolchain is now not used at all, only the `miri` binary is. In particular this means we can kill the sysroot consistency check. :)
2 parents 4cbe1f4 + 845b89c commit 4c78479

File tree

3 files changed

+39
-95
lines changed

3 files changed

+39
-95
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ Try deleting `~/.cache/miri`.
156156
This means the sysroot you are using was not compiled with Miri in mind. This
157157
should never happen when you use `cargo miri` because that takes care of setting
158158
up the sysroot. If you are using `miri` (the Miri driver) directly, see
159-
[below][testing-miri] for how to set up the sysroot.
159+
[CONTRIBUTING.md](CONTRIBUTING.md) for how to use `./miri`.
160160

161161

162162
## Miri `-Z` flags and environment variables

src/bin/cargo-miri.rs

+34-90
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::path::{Path, PathBuf};
88
use std::process::Command;
99
use std::ffi::OsString;
1010

11+
use rustc_version::VersionMeta;
12+
1113
const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 20);
1214

1315
const CARGO_MIRI_HELP: &str = r#"Interprets bin crates and tests in Miri
@@ -97,6 +99,10 @@ fn miri() -> Command {
9799
Command::new(find_miri())
98100
}
99101

102+
fn version_info() -> VersionMeta {
103+
VersionMeta::for_command(miri()).expect("failed to determine underlying rustc version of Miri")
104+
}
105+
100106
fn cargo() -> Command {
101107
Command::new(env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")))
102108
}
@@ -105,10 +111,6 @@ fn xargo_check() -> Command {
105111
Command::new(env::var_os("XARGO_CHECK").unwrap_or_else(|| OsString::from("xargo-check")))
106112
}
107113

108-
fn rustc() -> Command {
109-
Command::new(env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")))
110-
}
111-
112114
fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
113115
// We need to get the manifest, and then the metadata, to enumerate targets.
114116
let manifest_path =
@@ -153,55 +155,6 @@ fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
153155
package.targets.into_iter()
154156
}
155157

156-
/// Make sure that the `miri` and `rustc` binary are from the same sysroot.
157-
/// This can be violated e.g. when miri is locally built and installed with a different
158-
/// toolchain than what is used when `cargo miri` is run.
159-
fn test_sysroot_consistency() {
160-
fn get_sysroot(mut cmd: Command) -> PathBuf {
161-
let out = cmd
162-
.arg("--print")
163-
.arg("sysroot")
164-
.output()
165-
.expect("Failed to run rustc to get sysroot info");
166-
let stdout = String::from_utf8(out.stdout).expect("stdout is not valid UTF-8");
167-
let stderr = String::from_utf8(out.stderr).expect("stderr is not valid UTF-8");
168-
assert!(
169-
out.status.success(),
170-
"Bad status code {} when getting sysroot info via {:?}.\nstdout:\n{}\nstderr:\n{}",
171-
out.status,
172-
cmd,
173-
stdout,
174-
stderr,
175-
);
176-
let stdout = stdout.trim();
177-
PathBuf::from(stdout)
178-
.canonicalize()
179-
.unwrap_or_else(|_| panic!("Failed to canonicalize sysroot: {}", stdout))
180-
}
181-
182-
// Do not check sysroots if we got built as part of a Rust distribution.
183-
// During `bootstrap`, the sysroot does not match anyway, and then some distros
184-
// play symlink tricks so the sysroots may be different even for the final stage
185-
// (see <https://github.com/mozilla/nixpkgs-mozilla/issues/198>).
186-
if option_env!("RUSTC_STAGE").is_some() {
187-
return;
188-
}
189-
190-
let rustc_sysroot = get_sysroot(rustc());
191-
let miri_sysroot = get_sysroot(miri());
192-
193-
if rustc_sysroot != miri_sysroot {
194-
show_error(format!(
195-
"miri was built for a different sysroot than the rustc in your current toolchain.\n\
196-
Make sure you use the same toolchain to run miri that you used to build it!\n\
197-
rustc sysroot: `{}`\n\
198-
miri sysroot: `{}`",
199-
rustc_sysroot.display(),
200-
miri_sysroot.display()
201-
));
202-
}
203-
}
204-
205158
fn xargo_version() -> Option<(u32, u32, u32)> {
206159
let out = xargo_check().arg("--version").output().ok()?;
207160
if !out.status.success() {
@@ -300,10 +253,10 @@ fn setup(subcommand: MiriCommand) {
300253
Some(val) => PathBuf::from(val),
301254
None => {
302255
// Check for `rust-src` rustup component.
303-
let sysroot = rustc()
256+
let sysroot = miri()
304257
.args(&["--print", "sysroot"])
305258
.output()
306-
.expect("failed to get rustc sysroot")
259+
.expect("failed to determine sysroot")
307260
.stdout;
308261
let sysroot = std::str::from_utf8(&sysroot).unwrap();
309262
let sysroot = Path::new(sysroot.trim_end_matches('\n'));
@@ -316,7 +269,7 @@ fn setup(subcommand: MiriCommand) {
316269
ask_to_run(
317270
cmd,
318271
ask_user,
319-
"install the rustc-src component for the selected toolchain",
272+
"install the `rust-src` component for the selected toolchain",
320273
);
321274
}
322275
rustup_src
@@ -368,7 +321,7 @@ path = "lib.rs"
368321

369322
// Determine architectures.
370323
// We always need to set a target so rustc bootstrap can tell apart host from target crates.
371-
let host = rustc_version::version_meta().unwrap().host;
324+
let host = version_info().host;
372325
let target = get_arg_flag_value("--target");
373326
let target = target.as_ref().unwrap_or(&host);
374327
// Now invoke xargo.
@@ -389,7 +342,6 @@ path = "lib.rs"
389342
command.env("RUSTC", find_miri());
390343
}
391344
command.env("MIRI_BE_RUSTC", "1");
392-
command.env("RUSTFLAGS", miri::miri_default_args().join(" "));
393345
// Finally run it!
394346
if command.status().expect("failed to run xargo").success().not() {
395347
show_error(format!("Failed to run xargo"));
@@ -424,9 +376,6 @@ fn in_cargo_miri() {
424376
};
425377
let verbose = has_arg_flag("-v");
426378

427-
// Some basic sanity checks
428-
test_sysroot_consistency();
429-
430379
// We always setup.
431380
setup(subcommand);
432381
if subcommand == MiriCommand::Setup {
@@ -478,7 +427,7 @@ fn in_cargo_miri() {
478427
if get_arg_flag_value("--target").is_none() {
479428
// When no `--target` is given, default to the host.
480429
cmd.arg("--target");
481-
cmd.arg(rustc_version::version_meta().unwrap().host);
430+
cmd.arg(version_info().host);
482431
}
483432

484433
// Serialize the remaining args into a special environemt variable.
@@ -540,51 +489,46 @@ fn inside_cargo_rustc() {
540489
let verbose = std::env::var_os("MIRI_VERBOSE").is_some();
541490
let target_crate = is_target_crate();
542491

543-
// Figure out which arguments we need to pass.
544-
let mut args: Vec<String> = std::env::args().skip(2).collect(); // skip `cargo-miri rustc`
545-
// We make sure to only specify our custom Xargo sysroot and
546-
// other args for target crates - that is, crates which are ultimately
547-
// going to get interpreted by Miri.
492+
let mut cmd = miri();
493+
// Forward arguments.
494+
cmd.args(std::env::args().skip(2)); // skip `cargo-miri rustc`
495+
496+
// We make sure to only specify our custom Xargo sysroot for target crates - that is,
497+
// crates which are ultimately going to get interpreted by Miri.
548498
if target_crate {
549-
// FIXME: breaks for non-UTF-8 sysroots (use `var_os` instead).
550499
let sysroot =
551-
std::env::var("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
552-
args.push("--sysroot".to_owned());
553-
args.push(sysroot);
554-
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
500+
env::var_os("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
501+
cmd.arg("--sysroot");
502+
cmd.arg(sysroot);
555503
}
556504

557-
// Figure out the binary we need to call. If this is a runnable target crate, we want to call
558-
// Miri to start interpretation; otherwise we want to call rustc to build the crate as usual.
559-
let mut command = if target_crate && is_runnable_crate() {
560-
// This is the 'target crate' - the binary or test crate that
561-
// we want to interpret under Miri. We deserialize the user-provided arguments
562-
// from the special environment variable "MIRI_ARGS", and feed them
563-
// to the 'miri' binary.
505+
// If this is a runnable target crate, we want Miri to start interpretation;
506+
// otherwise we want Miri to behave like rustc and build the crate as usual.
507+
if target_crate && is_runnable_crate() {
508+
// This is the binary or test crate that we want to interpret under Miri.
509+
// We deserialize the arguments that are meant for Miri from the special environment
510+
// variable "MIRI_ARGS", and feed them to the 'miri' binary.
564511
//
565512
// `env::var` is okay here, well-formed JSON is always UTF-8.
566513
let magic = std::env::var("MIRI_ARGS").expect("missing MIRI_ARGS");
567-
let mut user_args: Vec<String> =
514+
let miri_args: Vec<String> =
568515
serde_json::from_str(&magic).expect("failed to deserialize MIRI_ARGS");
569-
args.append(&mut user_args);
570-
// Run this in Miri.
571-
miri()
516+
cmd.args(miri_args);
572517
} else {
573-
rustc()
518+
// We want to compile, not interpret.
519+
cmd.env("MIRI_BE_RUSTC", "1");
574520
};
575521

576522
// Run it.
577-
command.args(&args);
578523
if verbose {
579-
eprintln!("+ {:?}", command);
524+
eprintln!("+ {:?}", cmd);
580525
}
581-
582-
match command.status() {
526+
match cmd.status() {
583527
Ok(exit) =>
584528
if !exit.success() {
585529
std::process::exit(exit.code().unwrap_or(42));
586530
},
587-
Err(e) => panic!("error running {:?}:\n{:?}", command, e),
531+
Err(e) => panic!("error running {:?}:\n{:?}", cmd, e),
588532
}
589533
}
590534

@@ -609,6 +553,6 @@ fn main() {
609553
// dependencies get dispatched to `rustc`, the final test/binary to `miri`.
610554
inside_cargo_rustc();
611555
} else {
612-
show_error(format!("must be called with either `miri` or `rustc` as first argument."))
556+
show_error(format!("`cargo-miri` must be called with either `miri` or `rustc` as first argument."))
613557
}
614558
}

src/bin/miri.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ fn run_compiler(mut args: Vec<String>, callbacks: &mut (dyn rustc_driver::Callba
139139
}
140140
}
141141

142+
// Some options have different defaults in Miri than in plain rustc; apply those by making
143+
// them the first arguments after the binary name (but later arguments can overwrite them).
144+
args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
145+
142146
// Invoke compiler, and handle return code.
143147
let result = rustc_driver::catch_fatal_errors(move || {
144148
rustc_driver::run_compiler(&args, callbacks, None, None)
@@ -182,10 +186,6 @@ fn main() {
182186
if rustc_args.is_empty() {
183187
// Very first arg: binary name.
184188
rustc_args.push(arg);
185-
// After this, push Miri default args (before everything else so they can be overwritten).
186-
for arg in miri::miri_default_args().iter() {
187-
rustc_args.push(arg.to_string());
188-
}
189189
} else if after_dashdash {
190190
// Everything that comes after `--` is forwarded to the interpreted crate.
191191
crate_args.push(arg);

0 commit comments

Comments
 (0)