about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/README.md4
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs23
-rw-r--r--src/tools/miri/cargo-miri/src/setup.rs9
-rw-r--r--src/tools/miri/cargo-miri/src/util.rs64
4 files changed, 84 insertions, 16 deletions
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 60bf07b1736..944d2bbe879 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/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/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 79ce1f4ca32..315f7a23a91 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/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<Item = String>) {
     // 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<Item = String>) {
     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<Item = String>) {
     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<Item = String>) {
         .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/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index 8ae5b8c3e82..a98e1fcd485 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/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/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs
index 3c591268455..6c1a074cd8c 100644
--- a/src/tools/miri/cargo-miri/src/util.rs
+++ b/src/tools/miri/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))
+}