Add a --dry-run option to cargo clean.

This adds a `--dry-run` option to have `cargo clean` display what it
would delete without actually deleting it.
This commit is contained in:
Eric Huss 2023-09-06 20:37:47 -07:00
parent 61e8ef30d9
commit 45c5394703
4 changed files with 86 additions and 6 deletions

View File

@ -14,6 +14,13 @@ pub fn cli() -> Command {
.arg_target_triple("Target triple to clean output for")
.arg_target_dir()
.arg_manifest_path()
.arg(
flag(
"dry-run",
"Display what would be deleted without deleting anything",
)
.short('n'),
)
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help clean</>` for more detailed information.\n"
))
@ -33,6 +40,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
requested_profile: args.get_profile_name(config, "dev", ProfileChecking::Custom)?,
profile_specified: args.contains_id("profile") || args.flag("release"),
doc: args.flag("doc"),
dry_run: args.flag("dry-run"),
};
ops::clean(&ws, &opts)?;
Ok(())

View File

@ -23,11 +23,14 @@ pub struct CleanOptions<'cfg> {
pub requested_profile: InternedString,
/// Whether to just clean the doc directory
pub doc: bool,
/// If set, doesn't delete anything.
pub dry_run: bool,
}
pub struct CleanContext<'cfg> {
pub config: &'cfg Config,
progress: Box<dyn CleaningProgressBar + 'cfg>,
pub dry_run: bool,
}
/// Cleans various caches.
@ -35,6 +38,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
let mut target_dir = ws.target_dir();
let config = opts.config;
let mut ctx = CleanContext::new(config);
ctx.dry_run = opts.dry_run;
if opts.doc {
if !opts.spec.is_empty() {
@ -262,6 +266,7 @@ impl<'cfg> CleanContext<'cfg> {
CleanContext {
config,
progress: Box::new(progress),
dry_run: false,
}
}
@ -322,26 +327,48 @@ impl<'cfg> CleanContext<'cfg> {
}
};
self.config
.shell()
.verbose(|shell| shell.status("Removing", path.display()))?;
if self.dry_run {
// Concise because if in verbose mode, the path will be written in
// the loop below.
self.config
.shell()
.concise(|shell| Ok(writeln!(shell.out(), "{}", path.display())?))?;
} else {
self.config
.shell()
.verbose(|shell| shell.status("Removing", path.display()))?;
}
self.progress.display_now()?;
let rm_file = |path: &Path| {
if !self.dry_run {
paths::remove_file(path)?;
}
Ok(())
};
if !meta.is_dir() {
return paths::remove_file(path);
return rm_file(path);
}
for entry in walkdir::WalkDir::new(path).contents_first(true) {
let entry = entry?;
self.progress.on_clean()?;
if self.dry_run {
self.config
.shell()
.verbose(|shell| Ok(writeln!(shell.out(), "{}", entry.path().display())?))?;
}
if entry.file_type().is_dir() {
// The contents should have been removed by now, but sometimes a race condition is hit
// where other files have been added by the OS. `paths::remove_dir_all` also falls back
// to `std::fs::remove_dir_all`, which may be more reliable than a simple walk in
// platform-specific edge cases.
paths::remove_dir_all(entry.path())?;
if !self.dry_run {
paths::remove_dir_all(entry.path())?;
}
} else {
paths::remove_file(entry.path())?;
rm_file(entry.path())?;
}
}

View File

@ -5,6 +5,7 @@ Usage: cargo[EXE] clean [OPTIONS]
Options:
--doc Whether or not to clean just the documentation directory
-q, --quiet Do not print cargo log messages
-n, --dry-run Display what would be deleted without deleting anything
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value

View File

@ -782,6 +782,50 @@ fn clean_spec_reserved() {
.run();
}
#[cargo_test]
fn clean_dry_run() {
// Basic `clean --dry-run` test.
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
let ls_r = || -> Vec<_> {
let mut file_list: Vec<_> = walkdir::WalkDir::new(p.build_dir())
.into_iter()
.filter_map(|e| e.map(|e| e.path().to_owned()).ok())
.collect();
file_list.sort();
file_list
};
// Start with no files.
p.cargo("clean --dry-run").with_stdout("").run();
p.cargo("check").run();
let before = ls_r();
p.cargo("clean --dry-run").with_stdout("[CWD]/target").run();
// Verify it didn't delete anything.
let after = ls_r();
assert_eq!(before, after);
let expected = cargo::util::iter_join(before.iter().map(|p| p.to_str().unwrap()), "\n");
eprintln!("{expected}");
// Verify the verbose output.
p.cargo("clean --dry-run -v")
.with_stdout_unordered(expected)
.run();
}
#[cargo_test]
fn doc_with_package_selection() {
// --doc with -p