Skip to content

Commit 3e7c2e1

Browse files
rsmyth-ecmiRossSmyth
authored andcommitted
Add "cargo miri clean" command
1 parent 3815fc0 commit 3e7c2e1

File tree

4 files changed

+84
-16
lines changed

4 files changed

+84
-16
lines changed

src/tools/miri/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ Miri builds and vice-versa.
279279

280280
You may be running `cargo miri` with a different compiler version than the one
281281
used to build the custom libstd that Miri uses, and Miri failed to detect that.
282-
Try deleting `~/.cache/miri`.
282+
Try running `cargo miri clean`.
283283

284284
#### "no mir for `std::rt::lang_start_internal`"
285285

@@ -465,7 +465,7 @@ Moreover, Miri recognizes some environment variables:
465465
must point to the `library` subdirectory of a `rust-lang/rust` repository
466466
checkout. Note that changing files in that directory does not automatically
467467
trigger a re-build of the standard library; you have to clear the Miri build
468-
cache manually (on Linux, `rm -rf ~/.cache/miri`;
468+
cache with `cargo miri clean` or deleting it manually (on Linux, `rm -rf ~/.cache/miri`;
469469
on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`;
470470
and on macOS, `rm -rf ~/Library/Caches/org.rust-lang.miri`).
471471
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When

src/tools/miri/cargo-miri/src/phases.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Subcommands:
2020
test, t Run tests
2121
nextest Run tests with nextest (requires cargo-nextest installed)
2222
setup Only perform automatic setup, but without asking questions (for getting a proper libstd)
23+
clean Clean the Miri cache & target directory
2324
2425
The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively.
2526
@@ -74,14 +75,15 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
7475
// We cannot know which of those flags take arguments and which do not,
7576
// so we cannot detect subcommands later.
7677
let Some(subcommand) = args.next() else {
77-
show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`)");
78+
show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`, `clean`)");
7879
};
7980
let subcommand = match &*subcommand {
8081
"setup" => MiriCommand::Setup,
8182
"test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand),
83+
"clean" => MiriCommand::Clean,
8284
_ =>
8385
show_error!(
84-
"`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, and `setup`."
86+
"`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, `clean`, and `setup`."
8587
),
8688
};
8789
let verbose = num_arg_flag("-v");
@@ -93,6 +95,16 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
9395
let target = get_arg_flag_value("--target");
9496
let target = target.as_ref().unwrap_or(host);
9597

98+
// If cleaning the the target directory & sysroot cache,
99+
// delete them then exit. There is no reason to setup a new
100+
// sysroot in this execution.
101+
if let MiriCommand::Clean = subcommand {
102+
let metadata = get_cargo_metadata();
103+
clean_target_dir(&metadata);
104+
clean_sysroot();
105+
return;
106+
}
107+
96108
// We always setup.
97109
let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose);
98110

@@ -110,6 +122,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
110122
let cargo_cmd = match subcommand {
111123
MiriCommand::Forward(s) => s,
112124
MiriCommand::Setup => return, // `cargo miri setup` stops here.
125+
MiriCommand::Clean => unreachable!(),
113126
};
114127
let metadata = get_cargo_metadata();
115128
let mut cmd = cargo();
@@ -142,11 +155,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
142155
.arg(format!("target.'cfg(all())'.runner=[{cargo_miri_path_for_toml}, 'runner']"));
143156

144157
// Set `--target-dir` to `miri` inside the original target directory.
145-
let mut target_dir = match get_arg_flag_value("--target-dir") {
146-
Some(dir) => PathBuf::from(dir),
147-
None => metadata.target_directory.clone().into_std_path_buf(),
148-
};
149-
target_dir.push("miri");
158+
let target_dir = get_target_dir(&metadata);
150159
cmd.arg("--target-dir").arg(target_dir);
151160

152161
// *After* we set all the flags that need setting, forward everything else. Make sure to skip

src/tools/miri/cargo-miri/src/setup.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,8 @@ pub fn setup(
6767
}
6868

6969
// Determine where to put the sysroot.
70-
let sysroot_dir = match std::env::var_os("MIRI_SYSROOT") {
71-
Some(dir) => PathBuf::from(dir),
72-
None => {
73-
let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
74-
user_dirs.cache_dir().to_owned()
75-
}
76-
};
70+
let sysroot_dir = get_sysroot_dir();
71+
7772
// Sysroot configuration and build details.
7873
let no_std = match std::env::var_os("MIRI_NO_STD") {
7974
None =>

src/tools/miri/cargo-miri/src/util.rs

+64
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ pub enum MiriCommand {
7474
Setup,
7575
/// A command to be forwarded to cargo.
7676
Forward(String),
77+
/// Clean the miri cache
78+
Clean,
7779
}
7880

7981
/// 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) {
249251
}
250252
eprintln!("{prefix} running command: {cmd:?}");
251253
}
254+
255+
/// Get the target directory for miri output.
256+
///
257+
/// Either in an argument passed-in, or from cargo metadata.
258+
pub fn get_target_dir(meta: &Metadata) -> PathBuf {
259+
let mut output = match get_arg_flag_value("--target-dir") {
260+
Some(dir) => PathBuf::from(dir),
261+
None => meta.target_directory.clone().into_std_path_buf(),
262+
};
263+
output.push("miri");
264+
output
265+
}
266+
267+
/// Determines where the sysroot of this exeuction is
268+
///
269+
/// Either in a user-specified spot by an envar, or in a default cache location.
270+
pub fn get_sysroot_dir() -> PathBuf {
271+
match std::env::var_os("MIRI_SYSROOT") {
272+
Some(dir) => PathBuf::from(dir),
273+
None => {
274+
let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
275+
user_dirs.cache_dir().to_owned()
276+
}
277+
}
278+
}
279+
280+
/// An idempotent version of the stdlib's remove_dir_all
281+
/// it is considered a success if the directory was not there.
282+
fn remove_dir_all_idem(dir: &Path) -> std::io::Result<()> {
283+
match std::fs::remove_dir_all(dir) {
284+
Ok(_) => Ok(()),
285+
// If the directory doesn't exist, it is still a success.
286+
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()),
287+
Err(err) => Err(err),
288+
}
289+
}
290+
291+
/// Deletes the Miri sysroot cache
292+
/// Returns an error if the MIRI_SYSROOT env var is set.
293+
pub fn clean_sysroot() {
294+
if std::env::var_os("MIRI_SYSROOT").is_some() {
295+
show_error!(
296+
"MIRI_SYSROOT is set. Please clean your custom sysroot cache directory manually."
297+
)
298+
}
299+
300+
let sysroot_dir = get_sysroot_dir();
301+
302+
eprintln!("Cleaning sysroot cache at {}", sysroot_dir.display());
303+
304+
// Keep it simple, just remove the directory.
305+
remove_dir_all_idem(&sysroot_dir).unwrap_or_else(|err| show_error!("{}", err));
306+
}
307+
308+
/// Deletes the Miri target directory
309+
pub fn clean_target_dir(meta: &Metadata) {
310+
let target_dir = get_target_dir(meta);
311+
312+
eprintln!("Cleaning target directory at {}", target_dir.display());
313+
314+
remove_dir_all_idem(&target_dir).unwrap_or_else(|err| show_error!("{}", err))
315+
}

0 commit comments

Comments
 (0)