1 //! Implements `cargo miri setup`.
5 use std::path::PathBuf;
6 use std::process::{self, Command};
8 use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig};
9 use rustc_version::VersionMeta;
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`.
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 {
29 let path = PathBuf::from(path);
30 // Make path absolute if possible.
31 path.canonicalize().unwrap_or(path)
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"]);
44 "install the `rust-src` component for the selected toolchain",
50 if !rust_src.exists() {
51 show_error!("given Rust source directory `{}` does not exist.", rust_src.display());
53 if rust_src.file_name().and_then(OsStr::to_str) != Some("library") {
55 "given Rust source directory `{}` does not seem to be the `library` subdirectory of \
56 a Rust source checkout.",
61 // Determine where to put the sysroot.
62 let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") {
63 Some(dir) => PathBuf::from(dir),
65 let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
66 user_dirs.cache_dir().to_owned()
69 // Sysroot configuration and build details.
70 let sysroot_config = if std::env::var_os("MIRI_NO_STD").is_some() {
73 SysrootConfig::WithStd {
74 std_features: ["panic_unwind", "backtrace"].into_iter().map(Into::into).collect(),
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
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);
92 command.env("RUSTC", &cargo_miri_path);
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
100 command.env("RUSTC_WRAPPER", "");
102 if only_setup && !print_sysroot {
103 // Forward output. Even make it verbose, if requested.
104 for _ in 0..verbose {
109 command.stdout(process::Stdio::null());
110 command.stderr(process::Stdio::null());
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);
126 } else if only_setup {
127 // We want to be explicit.
128 eprintln!("Preparing a sysroot for Miri (target: {target})...");
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})... ");
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)
139 .build_from_source(&rust_src)
140 .unwrap_or_else(|err| {
142 show_error!("failed to build sysroot")
143 } else if only_setup {
144 show_error!("failed to build sysroot: {err:?}")
147 "failed to build sysroot; run `cargo miri setup` to see the error details"
153 } else if only_setup {
154 eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
159 // Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
160 println!("{}", sysroot_dir.display());