Skip to content

Commit

Permalink
Add "cargo miri clean" command
Browse files Browse the repository at this point in the history
  • Loading branch information
RossSmyth committed Feb 24, 2024
1 parent 3815fc0 commit 3e7c2e1
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`"

Expand Down Expand Up @@ -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
Expand Down
23 changes: 16 additions & 7 deletions src/tools/miri/cargo-miri/src/phases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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");
Expand All @@ -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);

Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down
9 changes: 2 additions & 7 deletions src/tools/miri/cargo-miri/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
64 changes: 64 additions & 0 deletions src/tools/miri/cargo-miri/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
}

0 comments on commit 3e7c2e1

Please sign in to comment.