Skip to content

Commit c2fcd46

Browse files
Aaron1011RalfJung
authored andcommitted
Use 'cargo check' to build the sysroot and target crate
Fixes rust-lang#1057 Since we are no longer using "cargo rustc", we now use the rustc arguments passed by Cargo to determine whether we are building a build dependency, normal dependency, or "target" (final binary or test) crate.
1 parent 29b0bbf commit c2fcd46

File tree

3 files changed

+71
-29
lines changed

3 files changed

+71
-29
lines changed

Cargo.lock

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ rustc-workspace-hack = "1.0.0"
5050
# between "cargo build" and "cargo intall".
5151
num-traits = "*"
5252
serde = { version = "*", features = ["derive"] }
53+
serde_json = "1.0.44"
5354

5455
[build-dependencies]
5556
vergen = "3"

src/bin/cargo-miri.rs

Lines changed: 64 additions & 24 deletions
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
@@ -84,6 +84,34 @@ fn get_arg_flag_value(name: &str) -> Option<String> {
8484
}
8585
}
8686

87+
88+
/// Determines if we are being invoked (as rustc) to build a runnable
89+
/// executable. We run "cargo check", so this should only happen when
90+
/// we are trying to compile a build script or build script dependency,
91+
/// which actually needs to be executed on the host platform.
92+
///
93+
/// Currently, we detect this by checking for "--emit=link",
94+
/// which indicates that Cargo instruced rustc to output
95+
/// a native object.
96+
fn is_build_dep() -> bool {
97+
std::env::args().any(|arg| arg.starts_with("--emit=") && arg.contains("link"))
98+
}
99+
100+
/// Returns whether or not Cargo invoked the wrapper (this binary) to compile
101+
/// the final, target crate (either a test for 'cargo test', or a binary for 'cargo run')
102+
/// Cargo does not give us this information directly, so we need to check
103+
/// various command-line flags.
104+
fn is_target_crate(is_build_script: bool) -> bool {
105+
let is_bin = get_arg_flag_value("--crate-type").as_deref() == Some("bin");
106+
let is_test = std::env::args().find(|arg| arg == "--test").is_some();
107+
108+
// The final runnable (under Miri) crate will either be a binary crate
109+
// or a test crate. We make sure to exclude build scripts here, since
110+
// they are also build with "--crate-type bin"
111+
(is_bin || is_test) && !is_build_script
112+
}
113+
114+
87115
fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
88116
// We need to get the manifest, and then the metadata, to enumerate targets.
89117
let manifest_path =
@@ -197,7 +225,7 @@ fn xargo() -> Command {
197225
// Bootstrap tells us where to find xargo
198226
Command::new(val)
199227
} else {
200-
Command::new("xargo")
228+
Command::new("xargo-check")
201229
}
202230
}
203231

@@ -467,7 +495,7 @@ fn in_cargo_miri() {
467495
// change to add additional arguments. `FLAGS` is set to identify
468496
// this target. The user gets to control what gets actually passed to Miri.
469497
let mut cmd = cargo();
470-
cmd.arg("rustc");
498+
cmd.arg("check");
471499
match (subcommand, kind.as_str()) {
472500
(MiriCommand::Run, "bin") => {
473501
// FIXME: we just run all the binaries here.
@@ -494,10 +522,15 @@ fn in_cargo_miri() {
494522
}
495523
cmd.arg(arg);
496524
}
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");
525+
526+
// Serialize our actual args into a special environemt variable.
527+
// This will be read by `inside_cargo_rustc` when we go to invoke
528+
// our actual target crate (the binary or the test we are running).
529+
// Since we're using "cargo check", we have no other way of passing
530+
// these arguments.
531+
let args_vec: Vec<String> = args.collect();
532+
cmd.env("MIRI_MAGIC_ARGS", serde_json::to_string(&args_vec).expect("failed to serialize args"));
533+
501534
let path = std::env::current_exe().expect("current executable path invalid");
502535
cmd.env("RUSTC_WRAPPER", path);
503536
if verbose {
@@ -517,25 +550,32 @@ fn inside_cargo_rustc() {
517550
let sysroot = std::env::var("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
518551

519552
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));
523553

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).
554+
let in_build_script = is_build_dep();
555+
556+
// Build scripts need to be compiled to actual runnable executables,
557+
// and therefore completely bypass Miri. We make sure to only specify
558+
// our custom Xargo sysroot for non-build-script crate - that is,
559+
// crates which are ultimately going to get interpreted by Miri.
560+
let mut args = if in_build_script {
561+
rustc_args.collect()
562+
} else {
563+
let mut args: Vec<String> = rustc_args
564+
.chain(Some("--sysroot".to_owned()))
565+
.chain(Some(sysroot))
566+
.collect();
567+
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
568+
args
569+
};
570+
527571
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>>();
572+
if is_target_crate(in_build_script) {
573+
// This is the 'target crate '- the binary or test crate that
574+
// we want to interpret under Miri. We deserialize the user-provided arguments
575+
// from the special environment variable "MIRI_MAGIC_ARGS", and feed them
576+
// to the 'miri' binary.
577+
let magic = std::env::var("MIRI_MAGIC_ARGS").expect("missing MIRI_MAGIC_ARGS");
578+
let mut user_args: Vec<String> = serde_json::from_str(&magic).expect("failed to deserialize args");
539579
args.append(&mut user_args);
540580
// Run this in Miri.
541581
true

0 commit comments

Comments
 (0)