2 use std::ffi::OsString;
3 use std::fs::{self, File};
4 use std::io::{self, BufRead, BufReader, BufWriter, Write};
6 use std::path::{Path, PathBuf};
7 use std::process::Command;
9 use serde::{Deserialize, Serialize};
11 use rustc_version::VersionMeta;
13 const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 22);
15 const CARGO_MIRI_HELP: &str = r#"Runs binary crates and tests in Miri
18 cargo miri [subcommand] [<cargo options>...] [--] [<program/test suite options>...]
23 setup Only perform automatic setup, but without asking questions (for getting a proper libstd)
25 The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively.
29 cargo miri test -- test-suite-filter
32 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
39 /// The inforamtion Miri needs to run a crate. Stored as JSON when the crate is "compiled".
40 #[derive(Serialize, Deserialize)]
42 /// The command-line arguments.
45 env: Vec<(OsString, OsString)>,
46 /// The current working directory.
47 current_dir: OsString,
51 /// Gather all the information we need.
52 fn collect(args: env::Args) -> Self {
53 let args = args.collect();
54 let env = env::vars_os().collect();
55 let current_dir = env::current_dir().unwrap().into_os_string();
56 CrateRunInfo { args, env, current_dir }
59 fn store(&self, filename: &Path) {
60 let file = File::create(filename)
61 .unwrap_or_else(|_| show_error(format!("cannot create `{}`", filename.display())));
62 let file = BufWriter::new(file);
63 serde_json::ser::to_writer(file, self)
64 .unwrap_or_else(|_| show_error(format!("cannot write to `{}`", filename.display())));
69 println!("{}", CARGO_MIRI_HELP);
75 env!("CARGO_PKG_VERSION"),
76 env!("VERGEN_SHA_SHORT"),
77 env!("VERGEN_COMMIT_DATE")
81 fn show_error(msg: String) -> ! {
82 eprintln!("fatal error: {}", msg);
86 // Determines whether a `--flag` is present.
87 fn has_arg_flag(name: &str) -> bool {
88 let mut args = std::env::args().take_while(|val| val != "--");
89 args.any(|val| val == name)
92 /// Gets the value of a `--flag`.
93 fn get_arg_flag_value(name: &str) -> Option<String> {
94 // Stop searching at `--`.
95 let mut args = std::env::args().take_while(|val| val != "--");
97 let arg = match args.next() {
101 if !arg.starts_with(name) {
104 // Strip leading `name`.
105 let suffix = &arg[name.len()..];
106 if suffix.is_empty() {
107 // This argument is exactly `name`; the next one is the value.
109 } else if suffix.starts_with('=') {
110 // This argument is `name=value`; get the value.
111 // Strip leading `=`.
112 return Some(suffix[1..].to_owned());
117 /// Returns the path to the `miri` binary
118 fn find_miri() -> PathBuf {
119 if let Some(path) = env::var_os("MIRI") {
122 let mut path = std::env::current_exe().expect("current executable path invalid");
123 path.set_file_name("miri");
127 fn miri() -> Command {
128 Command::new(find_miri())
131 fn version_info() -> VersionMeta {
132 VersionMeta::for_command(miri()).expect("failed to determine underlying rustc version of Miri")
135 fn cargo() -> Command {
136 Command::new(env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")))
139 fn xargo_check() -> Command {
140 Command::new(env::var_os("XARGO_CHECK").unwrap_or_else(|| OsString::from("xargo-check")))
143 /// Execute the command. If it fails, fail this process with the same exit code.
144 /// Otherwise, continue.
145 fn exec(mut cmd: Command) {
146 let exit_status = cmd.status().expect("failed to run command");
147 if exit_status.success().not() {
148 std::process::exit(exit_status.code().unwrap_or(-1))
152 fn xargo_version() -> Option<(u32, u32, u32)> {
153 let out = xargo_check().arg("--version").output().ok()?;
154 if !out.status.success() {
157 // Parse output. The first line looks like "xargo 0.3.12 (b004f1c 2018-12-13)".
162 .expect("malformed `xargo --version` output: not at least one line")
163 .expect("malformed `xargo --version` output: error reading first line");
164 let (name, version) = {
165 let mut split = line.split(' ');
167 split.next().expect("malformed `xargo --version` output: empty"),
168 split.next().expect("malformed `xargo --version` output: not at least two words"),
172 // This is some fork of xargo
175 let mut version_pieces = version.split('.');
176 let major = version_pieces
178 .expect("malformed `xargo --version` output: not a major version piece")
180 .expect("malformed `xargo --version` output: major version is not an integer");
181 let minor = version_pieces
183 .expect("malformed `xargo --version` output: not a minor version piece")
185 .expect("malformed `xargo --version` output: minor version is not an integer");
186 let patch = version_pieces
188 .expect("malformed `xargo --version` output: not a patch version piece")
190 .expect("malformed `xargo --version` output: patch version is not an integer");
191 if !version_pieces.next().is_none() {
192 panic!("malformed `xargo --version` output: more than three pieces in version");
194 Some((major, minor, patch))
197 fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
198 // Disable interactive prompts in CI (GitHub Actions, Travis, AppVeyor, etc).
199 // Azure doesn't set `CI` though (nothing to see here, just Microsoft being Microsoft),
200 // so we also check their `TF_BUILD`.
201 let is_ci = env::var_os("CI").is_some() || env::var_os("TF_BUILD").is_some();
203 let mut buf = String::new();
204 print!("I will run `{:?}` to {}. Proceed? [Y/n] ", cmd, text);
205 io::stdout().flush().unwrap();
206 io::stdin().read_line(&mut buf).unwrap();
207 match buf.trim().to_lowercase().as_ref() {
209 "" | "y" | "yes" => {}
210 "n" | "no" => show_error(format!("aborting as per your request")),
211 a => show_error(format!("invalid answer `{}`", a)),
214 println!("Running `{:?}` to {}.", cmd, text);
217 if cmd.status().expect(&format!("failed to execute {:?}", cmd)).success().not() {
218 show_error(format!("failed to {}", text));
222 /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
223 /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
224 /// done all this already.
225 fn setup(subcommand: MiriCommand) {
226 if std::env::var_os("MIRI_SYSROOT").is_some() {
227 if subcommand == MiriCommand::Setup {
228 println!("WARNING: MIRI_SYSROOT already set, not doing anything.")
233 // Subcommands other than `setup` will do a setup if necessary, but
234 // interactively confirm first.
235 let ask_user = subcommand != MiriCommand::Setup;
237 // First, we need xargo.
238 if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) {
239 if std::env::var_os("XARGO_CHECK").is_some() {
240 // The user manually gave us a xargo binary; don't do anything automatically.
241 show_error(format!("xargo is too old; please upgrade to the latest version"))
243 let mut cmd = cargo();
244 cmd.args(&["install", "xargo"]);
245 ask_to_run(cmd, ask_user, "install a recent enough xargo");
248 // Determine where the rust sources are located. `XARGO_RUST_SRC` env var trumps everything.
249 let rust_src = match std::env::var_os("XARGO_RUST_SRC") {
251 let path = PathBuf::from(path);
252 // Make path absolute if possible.
253 path.canonicalize().unwrap_or(path)
256 // Check for `rust-src` rustup component.
258 .args(&["--print", "sysroot"])
260 .expect("failed to determine sysroot")
262 let sysroot = std::str::from_utf8(&sysroot).unwrap();
263 let sysroot = Path::new(sysroot.trim_end_matches('\n'));
264 // Check for `$SYSROOT/lib/rustlib/src/rust/library`; test if that contains `std/Cargo.toml`.
266 sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
267 if !rustup_src.join("std").join("Cargo.toml").exists() {
268 // Ask the user to install the `rust-src` component, and use that.
269 let mut cmd = Command::new("rustup");
270 cmd.args(&["component", "add", "rust-src"]);
274 "install the `rust-src` component for the selected toolchain",
280 if !rust_src.exists() {
281 show_error(format!("given Rust source directory `{}` does not exist.", rust_src.display()));
284 // Next, we need our own libstd. Prepare a xargo project for that purpose.
285 // We will do this work in whatever is a good cache dir for this platform.
286 let dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
287 let dir = dirs.cache_dir();
289 fs::create_dir_all(&dir).unwrap();
291 // The interesting bit: Xargo.toml
292 File::create(dir.join("Xargo.toml"))
297 default_features = false
298 # We support unwinding, so enable that panic runtime.
299 features = ["panic_unwind", "backtrace"]
305 // The boring bits: a dummy project for xargo.
306 // FIXME: With xargo-check, can we avoid doing this?
307 File::create(dir.join("Cargo.toml"))
313 description = "A dummy project for building libstd with xargo."
321 File::create(dir.join("lib.rs")).unwrap();
323 // Determine architectures.
324 // We always need to set a target so rustc bootstrap can tell apart host from target crates.
325 let host = version_info().host;
326 let target = get_arg_flag_value("--target");
327 let target = target.as_ref().unwrap_or(&host);
329 let mut command = xargo_check();
330 command.arg("check").arg("-q");
331 command.arg("--target").arg(target);
332 command.current_dir(&dir);
333 command.env("XARGO_HOME", &dir);
334 command.env("XARGO_RUST_SRC", &rust_src);
335 // Use Miri as rustc to build a libstd compatible with us (and use the right flags).
336 // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
337 // because we still need bootstrap to distinguish between host and target crates.
338 // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
339 // for target crates.
340 // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
341 // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
342 // The `MIRI_BE_RUSTC` will mean we dispatch to `phase_setup_rustc`.
343 let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
344 if env::var_os("RUSTC_STAGE").is_some() {
345 command.env("RUSTC_REAL", &cargo_miri_path);
347 command.env("RUSTC", &cargo_miri_path);
349 command.env("MIRI_BE_RUSTC", "1");
350 // Make sure there are no other wrappers or flags getting in our way
351 // (Cc https://github.com/rust-lang/miri/issues/1421).
352 // This is consistent with normal `cargo build` that does not apply `RUSTFLAGS`
353 // to the sysroot either.
354 command.env_remove("RUSTC_WRAPPER");
355 command.env_remove("RUSTFLAGS");
356 // Disable debug assertions in the standard library -- Miri is already slow enough.
357 // But keep the overflow checks, they are cheap.
358 command.env("RUSTFLAGS", "-Cdebug-assertions=off -Coverflow-checks=on");
360 if command.status().expect("failed to run xargo").success().not() {
361 show_error(format!("failed to run xargo"));
364 // That should be it! But we need to figure out where xargo built stuff.
365 // Unfortunately, it puts things into a different directory when the
366 // architecture matches the host.
367 let sysroot = if target == &host { dir.join("HOST") } else { PathBuf::from(dir) };
368 std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
369 // Figure out what to print.
370 let print_sysroot = subcommand == MiriCommand::Setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
372 // Print just the sysroot and nothing else; this way we do not need any escaping.
373 println!("{}", sysroot.display());
374 } else if subcommand == MiriCommand::Setup {
375 println!("A libstd for Miri is now available in `{}`.", sysroot.display());
379 fn phase_setup_rustc(args: env::Args) {
380 // Mostly we just forward everything.
381 // `MIRI_BE_RUST` is already set.
382 let mut cmd = miri();
385 // Patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does).
386 if get_arg_flag_value("--crate-name").as_deref() == Some("panic_abort") {
387 cmd.arg("-C").arg("panic=abort");
394 fn phase_cargo_miri(mut args: env::Args) {
395 // Check for version and help flags even when invoked as `cargo-miri`.
396 if has_arg_flag("--help") || has_arg_flag("-h") {
400 if has_arg_flag("--version") || has_arg_flag("-V") {
405 // Require a subcommand before any flags.
406 // We cannot know which of those flags take arguments and which do not,
407 // so we cannot detect subcommands later.
408 let subcommand = match args.next().as_deref() {
409 Some("test") => MiriCommand::Test,
410 Some("run") => MiriCommand::Run,
411 Some("setup") => MiriCommand::Setup,
413 _ => show_error(format!("`cargo miri` supports the following subcommands: `run`, `test`, and `setup`.")),
415 let verbose = has_arg_flag("-v");
420 // Invoke actual cargo for the job, but with different flags.
421 // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
422 // requires some extra work to make the build check-only (see all the `--emit` hacks below).
423 // <https://github.com/rust-lang/miri/pull/1540#issuecomment-693553191> describes an alternative
424 // approach that uses `cargo check`, making that part easier but target and binary handling
426 let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
427 let cargo_cmd = match subcommand {
428 MiriCommand::Test => "test",
429 MiriCommand::Run => "run",
430 MiriCommand::Setup => return, // `cargo miri setup` stops here.
432 let mut cmd = cargo();
435 // Make sure we know the build target, and cargo does, too.
436 // This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
437 // and it later helps us detect which crates are proc-macro/build-script
438 // (host crates) and which crates are needed for the program itself.
439 let target = if let Some(target) = get_arg_flag_value("--target") {
442 // No target given. Pick default and tell cargo about it.
443 let host = version_info().host;
449 // Forward all further arguments. We do some processing here because we want to
450 // detect people still using the old way of passing flags to Miri
451 // (`cargo miri -- -Zmiri-foo`).
452 while let Some(arg) = args.next() {
455 // Check if the next argument starts with `-Zmiri`. If yes, we assume
456 // this is an old-style invocation.
457 if let Some(next_arg) = args.next() {
458 if next_arg.starts_with("-Zmiri") || next_arg == "--" {
460 "WARNING: it seems like you are setting Miri's flags in `cargo miri` the old way,\n\
461 i.e., by passing them after the first `--`. This style is deprecated; please set\n\
462 the MIRIFLAGS environment variable instead. `cargo miri run/test` now interprets\n\
463 arguments the exact same way as `cargo run/test`."
465 // Old-style invocation. Turn these into MIRIFLAGS, if there are any.
466 if next_arg != "--" {
467 let mut miriflags = env::var("MIRIFLAGS").unwrap_or_default();
469 miriflags.push_str(&next_arg);
470 while let Some(further_arg) = args.next() {
471 if further_arg == "--" {
472 // End of the Miri flags!
476 miriflags.push_str(&further_arg);
478 env::set_var("MIRIFLAGS", miriflags);
480 // Pass the remaining flags to cargo.
484 // Not a Miri argument after all, make sure we pass it to cargo.
490 // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
491 // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
492 // the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
493 if env::var_os("RUSTC_WRAPPER").is_some() {
494 println!("WARNING: Ignoring `RUSTC_WRAPPER` environment variable, Miri does not support wrapping.");
496 cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
498 // Set the runner for the current target to us as well, so we can interpret the binaries.
499 let runner_env_name = format!("CARGO_TARGET_{}_RUNNER", target.to_uppercase().replace('-', "_"));
500 cmd.env(&runner_env_name, &cargo_miri_path);
502 // Set rustdoc to us as well, so we can make it do nothing (see issue #584).
503 cmd.env("RUSTDOC", &cargo_miri_path);
507 eprintln!("[cargo-miri miri] RUSTC_WRAPPER={:?}", cargo_miri_path);
508 eprintln!("[cargo-miri miri] {}={:?}", runner_env_name, cargo_miri_path);
509 eprintln!("[cargo-miri miri] RUSTDOC={:?}", cargo_miri_path);
510 eprintln!("[cargo-miri miri] {:?}", cmd);
511 cmd.env("MIRI_VERBOSE", ""); // This makes the other phases verbose.
516 fn phase_cargo_rustc(args: env::Args) {
517 /// Determines if we are being invoked (as rustc) to build a crate for
518 /// the "target" architecture, in contrast to the "host" architecture.
519 /// Host crates are for build scripts and proc macros and still need to
520 /// be built like normal; target crates need to be built for or interpreted
523 /// Currently, we detect this by checking for "--target=", which is
524 /// never set for host crates. This matches what rustc bootstrap does,
525 /// which hopefully makes it "reliable enough". This relies on us always
526 /// invoking cargo itself with `--target`, which `in_cargo_miri` ensures.
527 fn is_target_crate() -> bool {
528 get_arg_flag_value("--target").is_some()
531 /// Returns whether or not Cargo invoked the wrapper (this binary) to compile
532 /// the final, binary crate (either a test for 'cargo test', or a binary for 'cargo run')
533 /// Cargo does not give us this information directly, so we need to check
534 /// various command-line flags.
535 fn is_runnable_crate() -> bool {
536 let is_bin = get_arg_flag_value("--crate-type").as_deref().unwrap_or("bin") == "bin";
537 let is_test = has_arg_flag("--test");
541 fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
542 let mut path = PathBuf::from(get_arg_flag_value("--out-dir").unwrap());
546 get_arg_flag_value("--crate-name").unwrap(),
547 // This is technically a `-C` flag but the prefix seems unique enough...
548 // (and cargo passes this before the filename so it should be unique)
549 get_arg_flag_value("extra-filename").unwrap_or(String::new()),
555 let verbose = std::env::var_os("MIRI_VERBOSE").is_some();
556 let target_crate = is_target_crate();
557 let print = get_arg_flag_value("--print").is_some(); // whether this is cargo passing `--print` to get some infos
559 // rlib and cdylib are just skipped, we cannot interpret them and do not need them
560 // for the rest of the build either.
561 match get_arg_flag_value("--crate-type").as_deref() {
562 Some("rlib") | Some("cdylib") => {
564 eprint!("[cargo-miri rustc] (rlib/cdylib skipped)");
571 if !print && target_crate && is_runnable_crate() {
572 // This is the binary or test crate that we want to interpret under Miri.
573 // But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
574 // like we want them.
575 // Instead of compiling, we write JSON into the output file with all the relevant command-line flags
576 // and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
577 let info = CrateRunInfo::collect(args);
578 let filename = out_filename("", "");
580 eprintln!("[cargo-miri rustc] writing run info to `{}`", filename.display());
583 info.store(&filename);
584 // For Windows, do the same thing again with `.exe` appended to the filename.
585 // (Need to do this here as cargo moves that "binary" to a different place before running it.)
586 info.store(&out_filename("", ".exe"));
591 let mut cmd = miri();
592 let mut emit_link_hack = false;
593 // Arguments are treated very differently depending on whether this crate is
594 // for interpretation by Miri, or for use by a build script / proc macro.
595 if !print && target_crate {
596 // Forward arguments, but remove "link" from "--emit" to make this a check-only build.
597 let emit_flag = "--emit";
599 if arg.starts_with(emit_flag) {
600 // Patch this argument. First, extract its value.
601 let val = &arg[emit_flag.len()..];
602 assert!(val.starts_with("="), "`cargo` should pass `--emit=X` as one argument");
604 let mut val: Vec<_> = val.split(',').collect();
605 // Now make sure "link" is not in there, but "metadata" is.
606 if let Some(i) = val.iter().position(|&s| s == "link") {
607 emit_link_hack = true;
609 if !val.iter().any(|&s| s == "metadata") {
610 val.push("metadata");
613 cmd.arg(format!("{}={}", emit_flag, val.join(",")));
619 // Use our custom sysroot.
621 env::var_os("MIRI_SYSROOT").expect("the wrapper should have set MIRI_SYSROOT");
622 cmd.arg("--sysroot");
625 // For host crates or when we are printing, just forward everything.
629 // We want to compile, not interpret. We still use Miri to make sure the compiler version etc
630 // are the exact same as what is used for interpretation.
631 cmd.env("MIRI_BE_RUSTC", "1");
635 eprintln!("[cargo-miri rustc] {:?}", cmd);
639 // Create a stub .rlib file if "link" was requested by cargo.
641 // Some platforms prepend "lib", some do not... let's just create both files.
642 let filename = out_filename("lib", ".rlib");
643 File::create(filename).expect("failed to create rlib file");
644 let filename = out_filename("", ".rlib");
645 File::create(filename).expect("failed to create rlib file");
649 fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
650 let verbose = std::env::var_os("MIRI_VERBOSE").is_some();
652 let file = File::open(&binary)
653 .unwrap_or_else(|_| show_error(format!("file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary)));
654 let file = BufReader::new(file);
655 let info: CrateRunInfo = serde_json::from_reader(file)
656 .unwrap_or_else(|_| show_error(format!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary)));
658 // Set missing env vars. Looks like `build.rs` vars are still set at run-time, but
659 // `CARGO_BIN_EXE_*` are not. This means we can give the run-time environment precedence,
660 // to rather do too little than too much.
661 for (name, val) in info.env {
662 if env::var_os(&name).is_none() {
663 env::set_var(name, val);
667 let mut cmd = miri();
668 // Forward rustc arguments.
669 // We need to patch "--extern" filenames because we forced a check-only
670 // build without cargo knowing about that: replace `.rlib` suffix by
672 // We also need to remove `--error-format` as cargo specifies that to be JSON,
673 // but when we run here, cargo does not interpret the JSON any more. `--json`
674 // then also nees to be dropped.
675 let mut args = info.args.into_iter();
676 let extern_flag = "--extern";
677 let error_format_flag = "--error-format";
678 let json_flag = "--json";
679 while let Some(arg) = args.next() {
680 if arg == extern_flag {
681 cmd.arg(extern_flag); // always forward flag, but adjust filename
682 // `--extern` is always passed as a separate argument by cargo.
683 let next_arg = args.next().expect("`--extern` should be followed by a filename");
684 if let Some(next_lib) = next_arg.strip_suffix(".rlib") {
685 // If this is an rlib, make it an rmeta.
686 cmd.arg(format!("{}.rmeta", next_lib));
688 // Some other extern file (e.g., a `.so`). Forward unchanged.
691 } else if arg.starts_with(error_format_flag) {
692 let suffix = &arg[error_format_flag.len()..];
693 assert!(suffix.starts_with('='));
694 // Drop this argument.
695 } else if arg.starts_with(json_flag) {
696 let suffix = &arg[json_flag.len()..];
697 assert!(suffix.starts_with('='));
698 // Drop this argument.
705 env::var_os("MIRI_SYSROOT").expect("the wrapper should have set MIRI_SYSROOT");
706 cmd.arg("--sysroot");
708 // Respect `MIRIFLAGS`.
709 if let Ok(a) = env::var("MIRIFLAGS") {
710 // This code is taken from `RUSTFLAGS` handling in cargo.
714 .filter(|s| !s.is_empty())
715 .map(str::to_string);
719 // Then pass binary arguments.
721 cmd.args(binary_args);
723 // Make sure we use the build-time working directory for interpreting Miri/rustc arguments.
724 // But then we need to switch to the run-time one, which we instruct Miri do do by setting `MIRI_CWD`.
725 cmd.current_dir(info.current_dir);
726 cmd.env("MIRI_CWD", env::current_dir().unwrap());
730 eprintln!("[cargo-miri runner] {:?}", cmd);
736 // Rustc does not support non-UTF-8 arguments so we make no attempt either.
737 // (We do support non-UTF-8 environment variables though.)
738 let mut args = std::env::args();
740 args.next().unwrap();
742 // Dispatch running as part of sysroot compilation.
743 if env::var_os("MIRI_BE_RUSTC").is_some() {
744 phase_setup_rustc(args);
748 // Dispatch to `cargo-miri` phase. There are three phases:
749 // - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
750 // cargo. We set RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.
751 // - When we are executed due to RUSTC_WRAPPER, we build crates or store the flags of
752 // binary crates for later interpretation.
753 // - When we are executed due to CARGO_TARGET_RUNNER, we start interpretation based on the
754 // flags that were stored earlier.
755 // On top of that, we are also called as RUSTDOC, but that is just a stub currently.
756 match args.next().as_deref() {
757 Some("miri") => phase_cargo_miri(args),
758 Some("rustc") => phase_cargo_rustc(args),
760 // We have to distinguish the "runner" and "rustfmt" cases.
761 // As runner, the first argument is the binary (a file that should exist, with an absolute path);
762 // as rustfmt, the first argument is a flag (`--something`).
763 let binary = Path::new(arg);
765 assert!(!arg.starts_with("--")); // not a flag
766 phase_cargo_runner(binary, args);
767 } else if arg.starts_with("--") {
769 eprintln!("Running doctests is not currently supported by Miri.")
771 show_error(format!("`cargo-miri` called with unexpected first argument `{}`; please only invoke this binary through `cargo miri`", arg));
774 _ => show_error(format!("`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`")),