]> git.lizzy.rs Git - rust.git/blob - cargo-miri/bin.rs
Update src/data_race.rs
[rust.git] / cargo-miri / bin.rs
1 use std::env;
2 use std::ffi::OsString;
3 use std::fs::{self, File};
4 use std::io::{self, BufRead, BufReader, BufWriter, Write};
5 use std::ops::Not;
6 use std::path::{Path, PathBuf};
7 use std::process::Command;
8
9 use serde::{Deserialize, Serialize};
10
11 use rustc_version::VersionMeta;
12
13 const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 22);
14
15 const CARGO_MIRI_HELP: &str = r#"Runs binary crates and tests in Miri
16
17 Usage:
18     cargo miri [subcommand] [<cargo options>...] [--] [<program/test suite options>...]
19
20 Subcommands:
21     run                      Run binaries
22     test                     Run tests
23     setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)
24
25 The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively.
26
27 Examples:
28     cargo miri run
29     cargo miri test -- test-suite-filter
30 "#;
31
32 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
33 enum MiriCommand {
34     Run,
35     Test,
36     Setup,
37 }
38
39 /// The inforamtion Miri needs to run a crate. Stored as JSON when the crate is "compiled".
40 #[derive(Serialize, Deserialize)]
41 struct CrateRunInfo {
42     /// The command-line arguments.
43     args: Vec<String>,
44     /// The environment.
45     env: Vec<(OsString, OsString)>,
46     /// The current working directory.
47     current_dir: OsString,
48 }
49
50 impl CrateRunInfo {
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 }
57     }
58
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())));
65     }
66 }
67
68 fn show_help() {
69     println!("{}", CARGO_MIRI_HELP);
70 }
71
72 fn show_version() {
73     println!(
74         "miri {} ({} {})",
75         env!("CARGO_PKG_VERSION"),
76         env!("VERGEN_SHA_SHORT"),
77         env!("VERGEN_COMMIT_DATE")
78     );
79 }
80
81 fn show_error(msg: String) -> ! {
82     eprintln!("fatal error: {}", msg);
83     std::process::exit(1)
84 }
85
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)
90 }
91
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 != "--");
96     loop {
97         let arg = match args.next() {
98             Some(arg) => arg,
99             None => return None,
100         };
101         if !arg.starts_with(name) {
102             continue;
103         }
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.
108             return args.next();
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());
113         }
114     }
115 }
116
117 /// Returns the path to the `miri` binary
118 fn find_miri() -> PathBuf {
119     if let Some(path) = env::var_os("MIRI") {
120         return path.into();
121     }
122     let mut path = std::env::current_exe().expect("current executable path invalid");
123     path.set_file_name("miri");
124     path
125 }
126
127 fn miri() -> Command {
128     Command::new(find_miri())
129 }
130
131 fn version_info() -> VersionMeta {
132     VersionMeta::for_command(miri()).expect("failed to determine underlying rustc version of Miri")
133 }
134
135 fn cargo() -> Command {
136     Command::new(env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")))
137 }
138
139 fn xargo_check() -> Command {
140     Command::new(env::var_os("XARGO_CHECK").unwrap_or_else(|| OsString::from("xargo-check")))
141 }
142
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))
149     }
150 }
151
152 fn xargo_version() -> Option<(u32, u32, u32)> {
153     let out = xargo_check().arg("--version").output().ok()?;
154     if !out.status.success() {
155         return None;
156     }
157     // Parse output. The first line looks like "xargo 0.3.12 (b004f1c 2018-12-13)".
158     let line = out
159         .stderr
160         .lines()
161         .nth(0)
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(' ');
166         (
167             split.next().expect("malformed `xargo --version` output: empty"),
168             split.next().expect("malformed `xargo --version` output: not at least two words"),
169         )
170     };
171     if name != "xargo" {
172         // This is some fork of xargo
173         return None;
174     }
175     let mut version_pieces = version.split('.');
176     let major = version_pieces
177         .next()
178         .expect("malformed `xargo --version` output: not a major version piece")
179         .parse()
180         .expect("malformed `xargo --version` output: major version is not an integer");
181     let minor = version_pieces
182         .next()
183         .expect("malformed `xargo --version` output: not a minor version piece")
184         .parse()
185         .expect("malformed `xargo --version` output: minor version is not an integer");
186     let patch = version_pieces
187         .next()
188         .expect("malformed `xargo --version` output: not a patch version piece")
189         .parse()
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");
193     }
194     Some((major, minor, patch))
195 }
196
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();
202     if ask && !is_ci {
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() {
208             // Proceed.
209             "" | "y" | "yes" => {}
210             "n" | "no" => show_error(format!("aborting as per your request")),
211             a => show_error(format!("invalid answer `{}`", a)),
212         };
213     } else {
214         println!("Running `{:?}` to {}.", cmd, text);
215     }
216
217     if cmd.status().expect(&format!("failed to execute {:?}", cmd)).success().not() {
218         show_error(format!("failed to {}", text));
219     }
220 }
221
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.")
229         }
230         return;
231     }
232
233     // Subcommands other than `setup` will do a setup if necessary, but
234     // interactively confirm first.
235     let ask_user = subcommand != MiriCommand::Setup;
236
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"))
242         }
243         let mut cmd = cargo();
244         cmd.args(&["install", "xargo"]);
245         ask_to_run(cmd, ask_user, "install a recent enough xargo");
246     }
247
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") {
250         Some(path) => {
251             let path = PathBuf::from(path);
252             // Make path absolute if possible.
253             path.canonicalize().unwrap_or(path)
254         }
255         None => {
256             // Check for `rust-src` rustup component.
257             let sysroot = miri()
258                 .args(&["--print", "sysroot"])
259                 .output()
260                 .expect("failed to determine sysroot")
261                 .stdout;
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`.
265             let rustup_src =
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"]);
271                 ask_to_run(
272                     cmd,
273                     ask_user,
274                     "install the `rust-src` component for the selected toolchain",
275                 );
276             }
277             rustup_src
278         }
279     };
280     if !rust_src.exists() {
281         show_error(format!("given Rust source directory `{}` does not exist.", rust_src.display()));
282     }
283
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();
288     if !dir.exists() {
289         fs::create_dir_all(&dir).unwrap();
290     }
291     // The interesting bit: Xargo.toml
292     File::create(dir.join("Xargo.toml"))
293         .unwrap()
294         .write_all(
295             br#"
296 [dependencies.std]
297 default_features = false
298 # We support unwinding, so enable that panic runtime.
299 features = ["panic_unwind", "backtrace"]
300
301 [dependencies.test]
302 "#,
303         )
304         .unwrap();
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"))
308         .unwrap()
309         .write_all(
310             br#"
311 [package]
312 name = "miri-xargo"
313 description = "A dummy project for building libstd with xargo."
314 version = "0.0.0"
315
316 [lib]
317 path = "lib.rs"
318 "#,
319         )
320         .unwrap();
321     File::create(dir.join("lib.rs")).unwrap();
322
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);
328     // Now invoke xargo.
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);
346     } else {
347         command.env("RUSTC", &cargo_miri_path);
348     }
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");
359     // Finally run it!
360     if command.status().expect("failed to run xargo").success().not() {
361         show_error(format!("failed to run xargo"));
362     }
363
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
371     if print_sysroot {
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());
376     }
377 }
378
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();
383     cmd.args(args);
384
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");
388     }
389
390     // Run it!
391     exec(cmd);
392 }
393
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") {
397         show_help();
398         return;
399     }
400     if has_arg_flag("--version") || has_arg_flag("-V") {
401         show_version();
402         return;
403     }
404
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,
412         // Invalid command.
413         _ => show_error(format!("`cargo miri` supports the following subcommands: `run`, `test`, and `setup`.")),
414     };
415     let verbose = has_arg_flag("-v");
416
417     // We always setup.
418     setup(subcommand);
419
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
425     // harder.
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.
431     };
432     let mut cmd = cargo();
433     cmd.arg(cargo_cmd);
434
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") {
440         target
441     } else {
442         // No target given. Pick default and tell cargo about it.
443         let host = version_info().host;
444         cmd.arg("--target");
445         cmd.arg(&host);
446         host
447     };
448
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() {
453         cmd.arg(&arg);
454         if arg == "--" {
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 == "--" {
459                     eprintln!(
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`."
464                     );
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();
468                         miriflags.push(' ');
469                         miriflags.push_str(&next_arg);
470                         while let Some(further_arg) = args.next() {
471                             if further_arg == "--" {
472                                 // End of the Miri flags!
473                                 break;
474                             }
475                             miriflags.push(' ');
476                             miriflags.push_str(&further_arg);
477                         }
478                         env::set_var("MIRIFLAGS", miriflags);
479                     }
480                     // Pass the remaining flags to cargo.
481                     cmd.args(args);
482                     break;
483                 }
484                 // Not a Miri argument after all, make sure we pass it to cargo.
485                 cmd.arg(next_arg);
486             }
487         }
488     }
489
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.");
495     }
496     cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
497
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);
501
502     // Set rustdoc to us as well, so we can make it do nothing (see issue #584).
503     cmd.env("RUSTDOC", &cargo_miri_path);
504
505     // Run cargo.
506     if verbose {
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.
512     }
513     exec(cmd)
514 }
515
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
521     /// by Miri.
522     ///
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()
529     }
530
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");
538         is_bin || is_test
539     }
540
541     fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
542         let mut path = PathBuf::from(get_arg_flag_value("--out-dir").unwrap());
543         path.push(format!(
544             "{}{}{}{}",
545             prefix,
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()),
550             suffix,
551         ));
552         path
553     }
554
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
558
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") => {
563             if verbose {
564                 eprint!("[cargo-miri rustc] (rlib/cdylib skipped)");
565             }
566             return;
567         }
568         _ => {},
569     }
570
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("", "");
579         if verbose {
580             eprintln!("[cargo-miri rustc] writing run info to `{}`", filename.display());
581         }
582
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"));
587
588         return;
589     }
590
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";
598         for arg in args {
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");
603                 let val = &val[1..];
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;
608                     val.remove(i);
609                     if !val.iter().any(|&s| s == "metadata") {
610                         val.push("metadata");
611                     }
612                 }
613                 cmd.arg(format!("{}={}", emit_flag, val.join(",")));
614             } else {
615                 cmd.arg(arg);
616             }
617         }
618
619         // Use our custom sysroot.
620         let sysroot =
621             env::var_os("MIRI_SYSROOT").expect("the wrapper should have set MIRI_SYSROOT");
622         cmd.arg("--sysroot");
623         cmd.arg(sysroot);
624     } else {
625         // For host crates or when we are printing, just forward everything.
626         cmd.args(args);
627     }
628
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");
632
633     // Run it.
634     if verbose {
635         eprintln!("[cargo-miri rustc] {:?}", cmd);
636     }
637     exec(cmd);
638
639     // Create a stub .rlib file if "link" was requested by cargo.
640     if emit_link_hack {
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");
646     }
647 }
648
649 fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
650     let verbose = std::env::var_os("MIRI_VERBOSE").is_some();
651
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)));
657
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);
664         }
665     }
666
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
671     // `.rmeta`.
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));
687             } else {
688                 // Some other extern file (e.g., a `.so`). Forward unchanged.
689                 cmd.arg(next_arg);
690             }
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.
699         } else {
700             cmd.arg(arg);
701         }
702     }
703     // Set sysroot.
704     let sysroot =
705         env::var_os("MIRI_SYSROOT").expect("the wrapper should have set MIRI_SYSROOT");
706     cmd.arg("--sysroot");
707     cmd.arg(sysroot);
708     // Respect `MIRIFLAGS`.
709     if let Ok(a) = env::var("MIRIFLAGS") {
710         // This code is taken from `RUSTFLAGS` handling in cargo.
711         let args = a
712             .split(' ')
713             .map(str::trim)
714             .filter(|s| !s.is_empty())
715             .map(str::to_string);
716         cmd.args(args);
717     }
718
719     // Then pass binary arguments.
720     cmd.arg("--");
721     cmd.args(binary_args);
722
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());
727
728     // Run it.
729     if verbose {
730         eprintln!("[cargo-miri runner] {:?}", cmd);
731     }
732     exec(cmd)
733 }
734
735 fn main() {
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();
739     // Skip binary name.
740     args.next().unwrap();
741
742     // Dispatch running as part of sysroot compilation.
743     if env::var_os("MIRI_BE_RUSTC").is_some() {
744         phase_setup_rustc(args);
745         return;
746     }
747
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),
759         Some(arg) => {
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);
764             if binary.exists() {
765                 assert!(!arg.starts_with("--")); // not a flag
766                 phase_cargo_runner(binary, args);
767             } else if arg.starts_with("--") {
768                 // We are rustdoc.
769                 eprintln!("Running doctests is not currently supported by Miri.")
770             } else {
771                 show_error(format!("`cargo-miri` called with unexpected first argument `{}`; please only invoke this binary through `cargo miri`", arg));
772             }
773         }
774         _ => show_error(format!("`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`")),
775     }
776 }