Skip to content

Commit 53adc7e

Browse files
committed
Add a summary to cargo clean.
This adds a summary at the end when `cargo clean` finishes that displays how many files and bytes were removed.
1 parent e7cc1ca commit 53adc7e

File tree

5 files changed

+58
-10
lines changed

5 files changed

+58
-10
lines changed

crates/cargo-test-support/src/compare.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ fn substitute_macros(input: &str) -> String {
206206
("[UPDATING]", " Updating"),
207207
("[ADDING]", " Adding"),
208208
("[REMOVING]", " Removing"),
209+
("[REMOVED]", " Removed"),
209210
("[DOCTEST]", " Doc-tests"),
210211
("[PACKAGING]", " Packaging"),
211212
("[PACKAGED]", " Packaged"),

src/cargo/ops/cargo_clean.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::ops;
55
use crate::util::edit_distance;
66
use crate::util::errors::CargoResult;
77
use crate::util::interning::InternedString;
8-
use crate::util::{Config, Progress, ProgressStyle};
8+
use crate::util::{human_readable_bytes, Config, Progress, ProgressStyle};
99
use anyhow::bail;
1010
use cargo_util::paths;
1111
use std::fs;
@@ -31,6 +31,8 @@ pub struct CleanContext<'cfg> {
3131
pub config: &'cfg Config,
3232
progress: Box<dyn CleaningProgressBar + 'cfg>,
3333
pub dry_run: bool,
34+
num_files_folders_cleaned: u64,
35+
total_bytes_removed: u64,
3436
}
3537

3638
/// Cleans various caches.
@@ -76,6 +78,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
7678
}
7779
}
7880

81+
ctx.display_summary()?;
7982
Ok(())
8083
}
8184

@@ -267,6 +270,8 @@ impl<'cfg> CleanContext<'cfg> {
267270
config,
268271
progress: Box::new(progress),
269272
dry_run: false,
273+
num_files_folders_cleaned: 0,
274+
total_bytes_removed: 0,
270275
}
271276
}
272277

@@ -340,20 +345,28 @@ impl<'cfg> CleanContext<'cfg> {
340345
}
341346
self.progress.display_now()?;
342347

343-
let rm_file = |path: &Path| {
348+
let mut rm_file = |path: &Path, meta: Result<std::fs::Metadata, _>| {
349+
if let Ok(meta) = meta {
350+
// Note: This can over-count bytes removed for hard-linked
351+
// files. It also under-counts since it only counts the exact
352+
// byte sizes and not the block sizes.
353+
self.total_bytes_removed += meta.len();
354+
}
344355
if !self.dry_run {
345356
paths::remove_file(path)?;
346357
}
347358
Ok(())
348359
};
349360

350361
if !meta.is_dir() {
351-
return rm_file(path);
362+
self.num_files_folders_cleaned += 1;
363+
return rm_file(path, Ok(meta));
352364
}
353365

354366
for entry in walkdir::WalkDir::new(path).contents_first(true) {
355367
let entry = entry?;
356368
self.progress.on_clean()?;
369+
self.num_files_folders_cleaned += 1;
357370
if self.dry_run {
358371
self.config
359372
.shell()
@@ -368,13 +381,35 @@ impl<'cfg> CleanContext<'cfg> {
368381
paths::remove_dir_all(entry.path())?;
369382
}
370383
} else {
371-
rm_file(entry.path())?;
384+
rm_file(entry.path(), entry.metadata())?;
372385
}
373386
}
374387

375388
Ok(())
376389
}
377390

391+
fn display_summary(&self) -> CargoResult<()> {
392+
let status = if self.dry_run { "Summary" } else { "Removed" };
393+
let byte_count = if self.total_bytes_removed == 0 {
394+
String::new()
395+
} else {
396+
// Don't show a fractional number of bytes.
397+
if self.total_bytes_removed < 1024 {
398+
format!(", {}B total", self.total_bytes_removed)
399+
} else {
400+
let (bytes, unit) = human_readable_bytes(self.total_bytes_removed);
401+
format!(", {bytes:.1}{unit} total")
402+
}
403+
};
404+
self.config.shell().status(
405+
status,
406+
format!(
407+
"{} files/directories{byte_count}",
408+
self.num_files_folders_cleaned
409+
),
410+
)
411+
}
412+
378413
/// Deletes all of the given paths, showing a progress bar as it proceeds.
379414
///
380415
/// If any path does not exist, or is not accessible, this will not

tests/testsuite/clean.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ fn clean_doc() {
272272

273273
assert!(doc_path.is_dir());
274274

275-
p.cargo("clean --doc").run();
275+
p.cargo("clean --doc").with_stderr("[REMOVED] [..]").run();
276276

277277
assert!(!doc_path.is_dir());
278278
assert!(p.build_dir().is_dir());
@@ -414,9 +414,10 @@ fn clean_verbose() {
414414
if cfg!(target_os = "macos") {
415415
// Rust 1.69 has changed so that split-debuginfo=unpacked includes unpacked for rlibs.
416416
for obj in p.glob("target/debug/deps/bar-*.o") {
417-
expected.push_str(&format!("[REMOVING] [..]{}", obj.unwrap().display()));
417+
expected.push_str(&format!("[REMOVING] [..]{}\n", obj.unwrap().display()));
418418
}
419419
}
420+
expected.push_str("[REMOVED] [..] files/directories, [..] total\n");
420421
p.cargo("clean -p bar --verbose")
421422
.with_stderr_unordered(&expected)
422423
.run();
@@ -607,7 +608,8 @@ error: package ID specification `baz` did not match any packages
607608
p.cargo("clean -p bar:1.0.0")
608609
.with_stderr(
609610
"warning: version qualifier in `-p bar:1.0.0` is ignored, \
610-
cleaning all versions of `bar` found",
611+
cleaning all versions of `bar` found\n\
612+
[REMOVED] [..] files/directories, [..] total",
611613
)
612614
.run();
613615
let mut walker = walkdir::WalkDir::new(p.build_dir())
@@ -703,10 +705,16 @@ fn clean_dry_run() {
703705
};
704706

705707
// Start with no files.
706-
p.cargo("clean --dry-run").with_stdout("").run();
708+
p.cargo("clean --dry-run")
709+
.with_stdout("")
710+
.with_stderr("[SUMMARY] 0 files/directories")
711+
.run();
707712
p.cargo("check").run();
708713
let before = ls_r();
709-
p.cargo("clean --dry-run").with_stdout("[CWD]/target").run();
714+
p.cargo("clean --dry-run")
715+
.with_stdout("[CWD]/target")
716+
.with_stderr("[SUMMARY] [..] files/directories, [..] total")
717+
.run();
710718
// Verify it didn't delete anything.
711719
let after = ls_r();
712720
assert_eq!(before, after);
@@ -715,6 +723,7 @@ fn clean_dry_run() {
715723
// Verify the verbose output.
716724
p.cargo("clean --dry-run -v")
717725
.with_stdout_unordered(expected)
726+
.with_stderr("[SUMMARY] [..] files/directories, [..] total")
718727
.run();
719728
}
720729

tests/testsuite/profile_custom.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,9 @@ fn clean_custom_dirname() {
543543
assert!(!p.build_dir().join("release").is_dir());
544544

545545
// This should clean 'other'
546-
p.cargo("clean --profile=other").with_stderr("").run();
546+
p.cargo("clean --profile=other")
547+
.with_stderr("[REMOVED] [..] files/directories, [..] total")
548+
.run();
547549
assert!(p.build_dir().join("debug").is_dir());
548550
assert!(!p.build_dir().join("other").is_dir());
549551
}

tests/testsuite/script.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,7 @@ fn cmd_clean_with_embedded() {
980980
.with_stderr(
981981
"\
982982
[WARNING] `package.edition` is unspecified, defaulting to `2021`
983+
[REMOVED] [..] files/directories, [..] total
983984
",
984985
)
985986
.run();

0 commit comments

Comments
 (0)