Auto merge of #3312 - RossSmyth:miri-clean, r=RalfJung

Add "cargo miri clean" command

My first reaction when my miri cache was messed up was to attempt run this, which obviously failed. This helps paper over platform differences and such.
This commit is contained in:
bors 2024-02-24 18:00:29 +00:00
commit 4db9a3652a
4 changed files with 84 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -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 =>

View File

@ -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))
}