]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/cargo-miri/src/setup.rs
Rollup merge of #106470 - ehuss:tidy-no-wasm, r=Mark-Simulacrum
[rust.git] / src / tools / miri / cargo-miri / src / setup.rs
1 //! Implements `cargo miri setup`.
2
3 use std::env;
4 use std::ffi::OsStr;
5 use std::path::PathBuf;
6 use std::process::{self, Command};
7
8 use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig};
9 use rustc_version::VersionMeta;
10
11 use crate::util::*;
12
13 /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
14 /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
15 /// done all this already.
16 pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta, verbose: usize) {
17     let only_setup = matches!(subcommand, MiriCommand::Setup);
18     let ask_user = !only_setup;
19     let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
20     if !only_setup && std::env::var_os("MIRI_SYSROOT").is_some() {
21         // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
22         return;
23     }
24
25     // Determine where the rust sources are located.  The env var trumps auto-detection.
26     let rust_src_env_var = std::env::var_os("MIRI_LIB_SRC");
27     let rust_src = match rust_src_env_var {
28         Some(path) => {
29             let path = PathBuf::from(path);
30             // Make path absolute if possible.
31             path.canonicalize().unwrap_or(path)
32         }
33         None => {
34             // Check for `rust-src` rustup component.
35             let rustup_src = rustc_build_sysroot::rustc_sysroot_src(miri_for_host())
36                 .expect("could not determine sysroot source directory");
37             if !rustup_src.exists() {
38                 // Ask the user to install the `rust-src` component, and use that.
39                 let mut cmd = Command::new("rustup");
40                 cmd.args(["component", "add", "rust-src"]);
41                 ask_to_run(
42                     cmd,
43                     ask_user,
44                     "install the `rust-src` component for the selected toolchain",
45                 );
46             }
47             rustup_src
48         }
49     };
50     if !rust_src.exists() {
51         show_error!("given Rust source directory `{}` does not exist.", rust_src.display());
52     }
53     if rust_src.file_name().and_then(OsStr::to_str) != Some("library") {
54         show_error!(
55             "given Rust source directory `{}` does not seem to be the `library` subdirectory of \
56              a Rust source checkout.",
57             rust_src.display()
58         );
59     }
60
61     // Determine where to put the sysroot.
62     let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") {
63         Some(dir) => PathBuf::from(dir),
64         None => {
65             let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
66             user_dirs.cache_dir().to_owned()
67         }
68     };
69     // Sysroot configuration and build details.
70     let sysroot_config = if std::env::var_os("MIRI_NO_STD").is_some() {
71         SysrootConfig::NoStd
72     } else {
73         SysrootConfig::WithStd {
74             std_features: ["panic_unwind", "backtrace"].into_iter().map(Into::into).collect(),
75         }
76     };
77     let cargo_cmd = {
78         let mut command = cargo();
79         // Use Miri as rustc to build a libstd compatible with us (and use the right flags).
80         // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
81         // because we still need bootstrap to distinguish between host and target crates.
82         // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
83         // for target crates.
84         // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
85         // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
86         // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`.
87         let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
88         if env::var_os("RUSTC_STAGE").is_some() {
89             assert!(env::var_os("RUSTC").is_some());
90             command.env("RUSTC_REAL", &cargo_miri_path);
91         } else {
92             command.env("RUSTC", &cargo_miri_path);
93         }
94         command.env("MIRI_CALLED_FROM_SETUP", "1");
95         // Make sure there are no other wrappers getting in our way (Cc
96         // https://github.com/rust-lang/miri/issues/1421,
97         // https://github.com/rust-lang/miri/issues/2429). Looks like setting
98         // `RUSTC_WRAPPER` to the empty string overwrites `build.rustc-wrapper` set via
99         // `config.toml`.
100         command.env("RUSTC_WRAPPER", "");
101
102         if only_setup && !print_sysroot {
103             // Forward output. Even make it verbose, if requested.
104             for _ in 0..verbose {
105                 command.arg("-v");
106             }
107         } else {
108             // Supress output.
109             command.stdout(process::Stdio::null());
110             command.stderr(process::Stdio::null());
111         }
112
113         command
114     };
115     // Disable debug assertions in the standard library -- Miri is already slow enough.
116     // But keep the overflow checks, they are cheap. This completely overwrites flags
117     // the user might have set, which is consistent with normal `cargo build` that does
118     // not apply `RUSTFLAGS` to the sysroot either.
119     let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"];
120     // Make sure all target-level Miri invocations know their sysroot.
121     std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
122
123     // Do the build.
124     if print_sysroot {
125         // Be silent.
126     } else if only_setup {
127         // We want to be explicit.
128         eprintln!("Preparing a sysroot for Miri (target: {target})...");
129     } else {
130         // We want to be quiet, but still let the user know that something is happening.
131         eprint!("Preparing a sysroot for Miri (target: {target})... ");
132     }
133     SysrootBuilder::new(&sysroot_dir, target)
134         .build_mode(BuildMode::Check)
135         .rustc_version(rustc_version.clone())
136         .sysroot_config(sysroot_config)
137         .rustflags(rustflags)
138         .cargo(cargo_cmd)
139         .build_from_source(&rust_src)
140         .unwrap_or_else(|err| {
141             if print_sysroot {
142                 show_error!("failed to build sysroot")
143             } else if only_setup {
144                 show_error!("failed to build sysroot: {err:?}")
145             } else {
146                 show_error!(
147                     "failed to build sysroot; run `cargo miri setup` to see the error details"
148                 )
149             }
150         });
151     if print_sysroot {
152         // Be silent.
153     } else if only_setup {
154         eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
155     } else {
156         eprintln!("done");
157     }
158     if print_sysroot {
159         // Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
160         println!("{}", sysroot_dir.display());
161     }
162 }