Skip to content

Commit fc08cce

Browse files
committed
Auto merge of #1136 - Aaron1011:feature/check-libstd, r=RalfJung
Use 'cargo check' to build the sysroot and target crate Fixes #1057 I'm using my original approach from PR #1048. Ideally, we would distinguish between build-deps/dependencies/'final crate' via a different approach (e.g. the target directory). However, I haven't been able to get that to work just yet. However, everything should be working with the approach I'm using. At a minimum, we can use this PR to verify that everything works as expected when we don't actually produce native build outputs.
2 parents 3c444bf + 47f2b12 commit fc08cce

File tree

2 files changed

+109
-65
lines changed

2 files changed

+109
-65
lines changed

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ required-features = ["rustc_tests"]
3333
cargo_metadata = { version = "0.9.0", optional = true }
3434
directories = { version = "2.0", optional = true }
3535
rustc_version = { version = "0.2.3", optional = true }
36+
serde_json = { version = "1.0.44", optional = true }
37+
3638
getrandom = { version = "0.1.8", features = ["std"] }
3739
byteorder = "1.3"
3840
env_logger = "0.7.1"
@@ -56,7 +58,7 @@ vergen = "3"
5658

5759
[features]
5860
default = ["cargo_miri"]
59-
cargo_miri = ["cargo_metadata", "directories", "rustc_version"]
61+
cargo_miri = ["cargo_metadata", "directories", "rustc_version", "serde_json"]
6062
rustc_tests = []
6163

6264
[dev-dependencies]

src/bin/cargo-miri.rs

+106-64
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ops::Not;
66
use std::path::{Path, PathBuf};
77
use std::process::Command;
88

9-
const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 17);
9+
const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 19);
1010

1111
const CARGO_MIRI_HELP: &str = r#"Interprets bin crates and tests in Miri
1212
@@ -23,7 +23,7 @@ Common options:
2323
--features Features to compile for the package
2424
-V, --version Print version info and exit
2525
26-
Other [options] are the same as `cargo rustc`. Everything after the first "--" is
26+
Other [options] are the same as `cargo check`. Everything after the first "--" is
2727
passed verbatim to Miri, which will pass everything after the second "--" verbatim
2828
to the interpreted program.
2929
"#;
@@ -84,6 +84,31 @@ fn get_arg_flag_value(name: &str) -> Option<String> {
8484
}
8585
}
8686

87+
/// Returns the path to the `miri` binary
88+
fn find_miri() -> PathBuf {
89+
let mut path = std::env::current_exe().expect("current executable path invalid");
90+
path.set_file_name("miri");
91+
path
92+
}
93+
94+
fn cargo() -> Command {
95+
if let Ok(val) = std::env::var("CARGO") {
96+
// Bootstrap tells us where to find cargo
97+
Command::new(val)
98+
} else {
99+
Command::new("cargo")
100+
}
101+
}
102+
103+
fn xargo() -> Command {
104+
if let Ok(val) = std::env::var("XARGO") {
105+
// Bootstrap tells us where to find xargo
106+
Command::new(val)
107+
} else {
108+
Command::new("xargo-check")
109+
}
110+
}
111+
87112
fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
88113
// We need to get the manifest, and then the metadata, to enumerate targets.
89114
let manifest_path =
@@ -127,13 +152,6 @@ fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
127152
package.targets.into_iter()
128153
}
129154

130-
/// Returns the path to the `miri` binary
131-
fn find_miri() -> PathBuf {
132-
let mut path = std::env::current_exe().expect("current executable path invalid");
133-
path.set_file_name("miri");
134-
path
135-
}
136-
137155
/// Make sure that the `miri` and `rustc` binary are from the same sysroot.
138156
/// This can be violated e.g. when miri is locally built and installed with a different
139157
/// toolchain than what is used when `cargo miri` is run.
@@ -183,24 +201,6 @@ fn test_sysroot_consistency() {
183201
}
184202
}
185203

186-
fn cargo() -> Command {
187-
if let Ok(val) = std::env::var("CARGO") {
188-
// Bootstrap tells us where to find cargo
189-
Command::new(val)
190-
} else {
191-
Command::new("cargo")
192-
}
193-
}
194-
195-
fn xargo() -> Command {
196-
if let Ok(val) = std::env::var("XARGO") {
197-
// Bootstrap tells us where to find xargo
198-
Command::new(val)
199-
} else {
200-
Command::new("xargo")
201-
}
202-
}
203-
204204
fn xargo_version() -> Option<(u32, u32, u32)> {
205205
let out = xargo().arg("--version").output().ok()?;
206206
if !out.status.success() {
@@ -357,6 +357,7 @@ features = ["panic_unwind"]
357357
)
358358
.unwrap();
359359
// The boring bits: a dummy project for xargo.
360+
// FIXME: With xargo-check, can we avoid doing this?
360361
File::create(dir.join("Cargo.toml"))
361362
.unwrap()
362363
.write_all(
@@ -419,12 +420,12 @@ fn main() {
419420
}
420421

421422
if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
422-
// This arm is for when `cargo miri` is called. We call `cargo rustc` for each applicable target,
423+
// This arm is for when `cargo miri` is called. We call `cargo check` for each applicable target,
423424
// but with the `RUSTC` env var set to the `cargo-miri` binary so that we come back in the other branch,
424425
// and dispatch the invocations to `rustc` and `miri`, respectively.
425426
in_cargo_miri();
426427
} else if let Some("rustc") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
427-
// This arm is executed when `cargo-miri` runs `cargo rustc` with the `RUSTC_WRAPPER` env var set to itself:
428+
// This arm is executed when `cargo-miri` runs `cargo check` with the `RUSTC_WRAPPER` env var set to itself:
428429
// dependencies get dispatched to `rustc`, the final test/binary to `miri`.
429430
inside_cargo_rustc();
430431
} else {
@@ -463,11 +464,11 @@ fn in_cargo_miri() {
463464
.kind
464465
.get(0)
465466
.expect("badly formatted cargo metadata: target::kind is an empty array");
466-
// Now we run `cargo rustc $FLAGS $ARGS`, giving the user the
467+
// Now we run `cargo check $FLAGS $ARGS`, giving the user the
467468
// change to add additional arguments. `FLAGS` is set to identify
468469
// this target. The user gets to control what gets actually passed to Miri.
469470
let mut cmd = cargo();
470-
cmd.arg("rustc");
471+
cmd.arg("check");
471472
match (subcommand, kind.as_str()) {
472473
(MiriCommand::Run, "bin") => {
473474
// FIXME: we just run all the binaries here.
@@ -487,20 +488,29 @@ fn in_cargo_miri() {
487488
// The remaining targets we do not even want to build.
488489
_ => continue,
489490
}
490-
// Add user-defined args until first `--`.
491+
// Forward user-defined `cargo` args until first `--`.
491492
while let Some(arg) = args.next() {
492493
if arg == "--" {
493494
break;
494495
}
495496
cmd.arg(arg);
496497
}
497-
// Add `--` (to end the `cargo` flags), and then the user flags. We add markers around the
498-
// user flags to be able to identify them later. "cargo rustc" adds more stuff after this,
499-
// so we have to mark both the beginning and the end.
500-
cmd.arg("--").arg("cargo-miri-marker-begin").args(args).arg("cargo-miri-marker-end");
498+
499+
// Serialize the remaining args into a special environemt variable.
500+
// This will be read by `inside_cargo_rustc` when we go to invoke
501+
// our actual target crate (the binary or the test we are running).
502+
// Since we're using "cargo check", we have no other way of passing
503+
// these arguments.
504+
let args_vec: Vec<String> = args.collect();
505+
cmd.env("MIRI_ARGS", serde_json::to_string(&args_vec).expect("failed to serialize args"));
506+
507+
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
508+
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
509+
// the two codepaths.
501510
let path = std::env::current_exe().expect("current executable path invalid");
502511
cmd.env("RUSTC_WRAPPER", path);
503512
if verbose {
513+
cmd.env("MIRI_VERBOSE", ""); // this makes `inside_cargo_rustc` verbose.
504514
eprintln!("+ {:?}", cmd);
505515
}
506516

@@ -514,38 +524,71 @@ fn in_cargo_miri() {
514524
}
515525

516526
fn inside_cargo_rustc() {
517-
let sysroot = std::env::var("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
518-
519-
let rustc_args = std::env::args().skip(2); // skip `cargo rustc`
520-
let mut args: Vec<String> =
521-
rustc_args.chain(Some("--sysroot".to_owned())).chain(Some(sysroot)).collect();
522-
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
523-
524-
// See if we can find the `cargo-miri` markers. Those only get added to the binary we want to
525-
// run. They also serve to mark the user-defined arguments, which we have to move all the way
526-
// to the end (they get added somewhere in the middle).
527-
let needs_miri =
528-
if let Some(begin) = args.iter().position(|arg| arg == "cargo-miri-marker-begin") {
529-
let end = args
530-
.iter()
531-
.position(|arg| arg == "cargo-miri-marker-end")
532-
.expect("cannot find end marker");
533-
// These mark the user arguments. We remove the first and last as they are the markers.
534-
let mut user_args = args.drain(begin..=end);
535-
assert_eq!(user_args.next().unwrap(), "cargo-miri-marker-begin");
536-
assert_eq!(user_args.next_back().unwrap(), "cargo-miri-marker-end");
537-
// Collect the rest and add it back at the end.
538-
let mut user_args = user_args.collect::<Vec<String>>();
527+
/// Determines if we are being invoked (as rustc) to build a runnable
528+
/// executable. We run "cargo check", so this should only happen when
529+
/// we are trying to compile a build script or build script dependency,
530+
/// which actually needs to be executed on the host platform.
531+
///
532+
/// Currently, we detect this by checking for "--emit=link",
533+
/// which indicates that Cargo instruced rustc to output
534+
/// a native object.
535+
fn is_target_crate() -> bool {
536+
// `--emit` is sometimes missing, e.g. cargo calls rustc for "--print".
537+
// That is definitely not a target crate.
538+
// If `--emit` is present, then host crates are built ("--emit=link,...),
539+
// while the rest is only checked.
540+
get_arg_flag_value("--emit").map_or(false, |emit| !emit.contains("link"))
541+
}
542+
543+
/// Returns whether or not Cargo invoked the wrapper (this binary) to compile
544+
/// the final, target crate (either a test for 'cargo test', or a binary for 'cargo run')
545+
/// Cargo does not give us this information directly, so we need to check
546+
/// various command-line flags.
547+
fn is_runnable_crate() -> bool {
548+
let is_bin = get_arg_flag_value("--crate-type").as_deref() == Some("bin");
549+
let is_test = has_arg_flag("--test");
550+
551+
// The final runnable (under Miri) crate will either be a binary crate
552+
// or a test crate. We make sure to exclude build scripts here, since
553+
// they are also build with "--crate-type bin"
554+
is_bin || is_test
555+
}
556+
557+
let verbose = std::env::var("MIRI_VERBOSE").is_ok();
558+
let target_crate = is_target_crate();
559+
560+
// Figure out which arguments we need to pass.
561+
let mut args: Vec<String> = std::env::args().skip(2).collect(); // skip `cargo-miri rustc`
562+
// We make sure to only specify our custom Xargo sysroot and
563+
// other args for target crates - that is, crates which are ultimately
564+
// going to get interpreted by Miri.
565+
if target_crate {
566+
let sysroot = std::env::var("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
567+
args.push("--sysroot".to_owned());
568+
args.push(sysroot);
569+
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
570+
}
571+
572+
// Figure out the binary we need to call. If this is a runnable target crate, we want to call
573+
// Miri to start interpretation; otherwise we want to call rustc to build the crate as usual.
574+
let mut command =
575+
if target_crate && is_runnable_crate() {
576+
// This is the 'target crate' - the binary or test crate that
577+
// we want to interpret under Miri. We deserialize the user-provided arguments
578+
// from the special environment variable "MIRI_ARGS", and feed them
579+
// to the 'miri' binary.
580+
let magic = std::env::var("MIRI_ARGS").expect("missing MIRI_ARGS");
581+
let mut user_args: Vec<String> = serde_json::from_str(&magic).expect("failed to deserialize MIRI_ARGS");
539582
args.append(&mut user_args);
540583
// Run this in Miri.
541-
true
584+
Command::new(find_miri())
542585
} else {
543-
false
586+
Command::new("rustc")
544587
};
545588

546-
let mut command = if needs_miri { Command::new(find_miri()) } else { Command::new("rustc") };
589+
// Run it.
547590
command.args(&args);
548-
if has_arg_flag("-v") {
591+
if verbose {
549592
eprintln!("+ {:?}", command);
550593
}
551594

@@ -554,7 +597,6 @@ fn inside_cargo_rustc() {
554597
if !exit.success() {
555598
std::process::exit(exit.code().unwrap_or(42));
556599
},
557-
Err(ref e) if needs_miri => panic!("error during miri run: {:?}", e),
558-
Err(ref e) => panic!("error during rustc call: {:?}", e),
600+
Err(ref e) => panic!("error running {:?}:\n{:?}", command, e),
559601
}
560602
}

0 commit comments

Comments
 (0)