diff --git a/README.md b/README.md index 60bf07b173..944d2bbe87 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ Miri builds and vice-versa. You may be running `cargo miri` with a different compiler version than the one used to build the custom libstd that Miri uses, and Miri failed to detect that. -Try deleting `~/.cache/miri`. +Try running `cargo miri clean`. #### "no mir for `std::rt::lang_start_internal`" @@ -465,7 +465,7 @@ Moreover, Miri recognizes some environment variables: must point to the `library` subdirectory of a `rust-lang/rust` repository checkout. Note that changing files in that directory does not automatically trigger a re-build of the standard library; you have to clear the Miri build - cache manually (on Linux, `rm -rf ~/.cache/miri`; + cache with `cargo miri clean` or deleting it manually (on Linux, `rm -rf ~/.cache/miri`; on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`; and on macOS, `rm -rf ~/Library/Caches/org.rust-lang.miri`). * `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 79ce1f4ca3..315f7a23a9 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -20,6 +20,7 @@ Subcommands: test, t Run tests nextest Run tests with nextest (requires cargo-nextest installed) setup Only perform automatic setup, but without asking questions (for getting a proper libstd) + clean Clean the Miri cache & target directory The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively. @@ -74,14 +75,15 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // We cannot know which of those flags take arguments and which do not, // so we cannot detect subcommands later. let Some(subcommand) = args.next() else { - show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`)"); + show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`, `clean`)"); }; let subcommand = match &*subcommand { "setup" => MiriCommand::Setup, "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand), + "clean" => MiriCommand::Clean, _ => show_error!( - "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, and `setup`." + "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, `clean`, and `setup`." ), }; let verbose = num_arg_flag("-v"); @@ -93,6 +95,16 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let target = get_arg_flag_value("--target"); let target = target.as_ref().unwrap_or(host); + // If cleaning the the target directory & sysroot cache, + // delete them then exit. There is no reason to setup a new + // sysroot in this execution. + if let MiriCommand::Clean = subcommand { + let metadata = get_cargo_metadata(); + clean_target_dir(&metadata); + clean_sysroot(); + return; + } + // We always setup. let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose); @@ -110,6 +122,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let cargo_cmd = match subcommand { MiriCommand::Forward(s) => s, MiriCommand::Setup => return, // `cargo miri setup` stops here. + MiriCommand::Clean => unreachable!(), }; let metadata = get_cargo_metadata(); let mut cmd = cargo(); @@ -142,11 +155,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { .arg(format!("target.'cfg(all())'.runner=[{cargo_miri_path_for_toml}, 'runner']")); // Set `--target-dir` to `miri` inside the original target directory. - let mut target_dir = match get_arg_flag_value("--target-dir") { - Some(dir) => PathBuf::from(dir), - None => metadata.target_directory.clone().into_std_path_buf(), - }; - target_dir.push("miri"); + let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); // *After* we set all the flags that need setting, forward everything else. Make sure to skip diff --git a/cargo-miri/src/setup.rs b/cargo-miri/src/setup.rs index 8ae5b8c3e8..a98e1fcd48 100644 --- a/cargo-miri/src/setup.rs +++ b/cargo-miri/src/setup.rs @@ -67,13 +67,8 @@ pub fn setup( } // Determine where to put the sysroot. - let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") { - Some(dir) => PathBuf::from(dir), - None => { - let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap(); - user_dirs.cache_dir().to_owned() - } - }; + let sysroot_dir = get_sysroot_dir(); + // Sysroot configuration and build details. let no_std = match std::env::var_os("MIRI_NO_STD") { None => diff --git a/cargo-miri/src/util.rs b/cargo-miri/src/util.rs index 3c59126845..6c1a074cd8 100644 --- a/cargo-miri/src/util.rs +++ b/cargo-miri/src/util.rs @@ -74,6 +74,8 @@ pub enum MiriCommand { Setup, /// A command to be forwarded to cargo. Forward(String), + /// Clean the miri cache + Clean, } /// Escapes `s` in a way that is suitable for using it as a string literal in TOML syntax. @@ -249,3 +251,65 @@ pub fn debug_cmd(prefix: &str, verbose: usize, cmd: &Command) { } eprintln!("{prefix} running command: {cmd:?}"); } + +/// Get the target directory for miri output. +/// +/// Either in an argument passed-in, or from cargo metadata. +pub fn get_target_dir(meta: &Metadata) -> PathBuf { + let mut output = match get_arg_flag_value("--target-dir") { + Some(dir) => PathBuf::from(dir), + None => meta.target_directory.clone().into_std_path_buf(), + }; + output.push("miri"); + output +} + +/// Determines where the sysroot of this exeuction is +/// +/// Either in a user-specified spot by an envar, or in a default cache location. +pub fn get_sysroot_dir() -> PathBuf { + match std::env::var_os("MIRI_SYSROOT") { + Some(dir) => PathBuf::from(dir), + None => { + let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap(); + user_dirs.cache_dir().to_owned() + } + } +} + +/// An idempotent version of the stdlib's remove_dir_all +/// it is considered a success if the directory was not there. +fn remove_dir_all_idem(dir: &Path) -> std::io::Result<()> { + match std::fs::remove_dir_all(dir) { + Ok(_) => Ok(()), + // If the directory doesn't exist, it is still a success. + Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), + Err(err) => Err(err), + } +} + +/// Deletes the Miri sysroot cache +/// Returns an error if the MIRI_SYSROOT env var is set. +pub fn clean_sysroot() { + if std::env::var_os("MIRI_SYSROOT").is_some() { + show_error!( + "MIRI_SYSROOT is set. Please clean your custom sysroot cache directory manually." + ) + } + + let sysroot_dir = get_sysroot_dir(); + + eprintln!("Cleaning sysroot cache at {}", sysroot_dir.display()); + + // Keep it simple, just remove the directory. + remove_dir_all_idem(&sysroot_dir).unwrap_or_else(|err| show_error!("{}", err)); +} + +/// Deletes the Miri target directory +pub fn clean_target_dir(meta: &Metadata) { + let target_dir = get_target_dir(meta); + + eprintln!("Cleaning target directory at {}", target_dir.display()); + + remove_dir_all_idem(&target_dir).unwrap_or_else(|err| show_error!("{}", err)) +}