]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/cargo-miri/src/main.rs
Add 'src/tools/miri/' from commit '75dd959a3a40eb5b4574f8d2e23aa6efbeb33573'
[rust.git] / src / tools / miri / cargo-miri / src / main.rs
1 #![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq, rustc::internal)]
2
3 #[macro_use]
4 mod util;
5
6 mod arg;
7 mod phases;
8 mod setup;
9 mod version;
10
11 use std::{env, iter};
12
13 use crate::phases::*;
14
15 fn main() {
16     // Rustc does not support non-UTF-8 arguments so we make no attempt either.
17     // (We do support non-UTF-8 environment variables though.)
18     let mut args = std::env::args();
19     // Skip binary name.
20     args.next().unwrap();
21
22     // Dispatch to `cargo-miri` phase. Here is a rough idea of "who calls who".
23     //
24     // Initially, we are invoked as `cargo-miri miri run/test`. We first run the setup phase:
25     // - We call `xargo`, and set `RUSTC` back to us, together with `MIRI_CALLED_FROM_XARGO`,
26     //   so that xargo's rustc invocations end up in `phase_rustc` with `RustcPhase::Setup`.
27     //   There we then call the Miri driver with `MIRI_BE_RUSTC` to perform the actual build.
28     //
29     // Then we call `cargo run/test`, exactly forwarding all user flags, plus some configuration so
30     // that we control every binary invoked by cargo:
31     // - We set RUSTC_WRAPPER to ourselves, so for (almost) all rustc invocations, we end up in
32     //   `phase_rustc` with `RustcPhase::Build`. This will in turn either determine that a
33     //   dependency needs to be built (for which it invokes the Miri driver with `MIRI_BE_RUSTC`),
34     //   or determine that this is a binary Miri should run, in which case we generate a JSON file
35     //   with all the information needed to build and run this crate.
36     //   (We don't run it yet since cargo thinks this is a build step, not a run step -- running the
37     //   binary here would lead to a bad user experience.)
38     // - We set RUSTC to the Miri driver and also set `MIRI_BE_RUSTC`, so that gets called by build
39     //   scripts (and cargo uses it for the version query).
40     // - We set `target.*.runner` to `cargo-miri runner`, which ends up calling `phase_runner` for
41     //   `RunnerPhase::Cargo`. This parses the JSON file written in `phase_rustc` and then invokes
42     //   the actual Miri driver for interpretation.
43     // - We set RUSTDOC to ourselves, which ends up in `phase_rustdoc`. There we call regular
44     //   rustdoc with some extra flags, and we set `MIRI_CALLED_FROM_RUSTDOC` to recognize this
45     //   phase in our recursive invocations:
46     //   - We set the `--test-builder` flag of rustdoc to ourselves, which ends up in `phase_rustc`
47     //     with `RustcPhase::Rustdoc`. There we perform a check-build (needed to get the expected
48     //     build failures for `compile_fail` doctests) and then store a JSON file with the
49     //     information needed to run this test.
50     //   - We also set `--runtool` to ourselves, which ends up in `phase_runner` with
51     //     `RunnerPhase::Rustdoc`. There we parse the JSON file written in `phase_rustc` and invoke
52     //     the Miri driver for interpretation.
53
54     // Dispatch running as part of sysroot compilation.
55     if env::var_os("MIRI_CALLED_FROM_XARGO").is_some() {
56         phase_rustc(args, RustcPhase::Setup);
57         return;
58     }
59
60     // The way rustdoc invokes rustc is indistuingishable from the way cargo invokes rustdoc by the
61     // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate.
62     if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() {
63         // ...however, we then also see this variable when rustdoc invokes us as the testrunner!
64         // The runner is invoked as `$runtool ($runtool-arg)* output_file`;
65         // since we don't specify any runtool-args, and rustdoc supplies multiple arguments to
66         // the test-builder unconditionally, we can just check the number of remaining arguments:
67         if args.len() == 1 {
68             phase_runner(args, RunnerPhase::Rustdoc);
69         } else {
70             phase_rustc(args, RustcPhase::Rustdoc);
71         }
72
73         return;
74     }
75
76     let Some(first) = args.next() else {
77         show_error!(
78             "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
79         )
80     };
81     match first.as_str() {
82         "miri" => phase_cargo_miri(args),
83         "runner" => phase_runner(args, RunnerPhase::Cargo),
84         arg if arg == env::var("RUSTC").unwrap() => {
85             // If the first arg is equal to the RUSTC env ariable (which should be set at this
86             // point), then we need to behave as rustc. This is the somewhat counter-intuitive
87             // behavior of having both RUSTC and RUSTC_WRAPPER set
88             // (see https://github.com/rust-lang/cargo/issues/10886).
89             phase_rustc(args, RustcPhase::Build)
90         }
91         _ => {
92             // Everything else must be rustdoc. But we need to get `first` "back onto the iterator",
93             // it is some part of the rustdoc invocation.
94             phase_rustdoc(iter::once(first).chain(args));
95         }
96     }
97 }