From 3a3bd98783b7c4cd109184938b13177c5d881291 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:59:47 +0100 Subject: [PATCH 1/3] test: Add test for 'clean --workspace' Co-authored-by: Dino --- tests/testsuite/clean.rs | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/testsuite/clean.rs b/tests/testsuite/clean.rs index db2e91ba602..c976a14ce98 100644 --- a/tests/testsuite/clean.rs +++ b/tests/testsuite/clean.rs @@ -199,6 +199,74 @@ fn clean_p_only_cleans_specified_package() { ); } +#[cargo_test] +fn clean_workspace_does_not_touch_non_workspace_packages() { + Package::new("external_dependency", "0.1.0").publish(); + let foo_manifest = r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [dependencies] + external_dependency = "0.1.0" + "#; + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "foo", + "foo_core", + "foo-base", + ] + "#, + ) + .file("foo/Cargo.toml", foo_manifest) + .file("foo/src/lib.rs", "//! foo") + .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) + .file("foo_core/src/lib.rs", "//! foo_core") + .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) + .file("foo-base/src/lib.rs", "//! foo-base") + .build(); + + let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); + + p.cargo("check -p foo -p foo_core -p foo-base").run(); + + let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Artifacts present for all after building + assert!(fingerprint_names.iter().any(|e| e == "foo")); + assert!(fingerprint_names.iter().any(|e| e == "foo_core")); + assert!(fingerprint_names.iter().any(|e| e == "foo-base")); + + let num_external_dependency_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "external_dependency") + .count(); + assert_ne!(num_external_dependency_artifacts, 0); + + p.cargo("clean -p foo -p foo_core -p foo-base").run(); + + fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Cleaning workspace members leaves artifacts for the external dependency + assert!( + !fingerprint_names + .iter() + .any(|e| e == "foo" || e == "foo_core" || e == "foo-base") + ); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "external_dependency") + .count(), + num_external_dependency_artifacts, + ); +} + fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec { std::fs::read_dir(fingerprint_path) .expect("Build dir should be readable") From 9c063db7b251d8007b78ea57fb59e7d0b39e374c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:37:46 +0100 Subject: [PATCH 2/3] test: Add test for 'clean --workspace -p FOO' --- tests/testsuite/clean.rs | 72 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/testsuite/clean.rs b/tests/testsuite/clean.rs index c976a14ce98..99a6dba5136 100644 --- a/tests/testsuite/clean.rs +++ b/tests/testsuite/clean.rs @@ -267,6 +267,78 @@ fn clean_workspace_does_not_touch_non_workspace_packages() { ); } +#[cargo_test] +fn clean_workspace_with_extra_package_specifiers() { + Package::new("external_dependency_1", "0.1.0").publish(); + Package::new("external_dependency_2", "0.1.0").publish(); + let foo_manifest = r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [dependencies] + external_dependency_1 = "0.1.0" + external_dependency_2 = "0.1.0" + "#; + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "foo", + "foo_core", + "foo-base", + ] + "#, + ) + .file("foo/Cargo.toml", foo_manifest) + .file("foo/src/lib.rs", "//! foo") + .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) + .file("foo_core/src/lib.rs", "//! foo_core") + .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) + .file("foo-base/src/lib.rs", "//! foo-base") + .build(); + + let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); + + p.cargo("check -p foo -p foo_core -p foo-base").run(); + + let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Artifacts present for all after building + assert!(fingerprint_names.iter().any(|e| e == "foo")); + assert!(fingerprint_names.iter().any(|e| e == "foo_core")); + assert!(fingerprint_names.iter().any(|e| e == "foo-base")); + + let num_external_dependency_2_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "external_dependency_2") + .count(); + assert_ne!(num_external_dependency_2_artifacts, 0); + + p.cargo("clean -p foo -p foo_core -p foo-base -p external_dependency_1") + .run(); + + fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Cleaning workspace members and external_dependency_1 leaves artifacts for the external_dependency_2 + assert!( + !fingerprint_names.iter().any(|e| e == "foo" + || e == "foo_core" + || e == "foo-base" + || e == "external_dependency_1") + ); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "external_dependency_2") + .count(), + num_external_dependency_2_artifacts, + ); +} + fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec { std::fs::read_dir(fingerprint_path) .expect("Build dir should be readable") From ecc89a8856bdd8c6831baf7aec02d896689ec94c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:00:10 +0100 Subject: [PATCH 3/3] clean: Add --workspace support Co-authored-by: Dino --- src/bin/cargo/commands/clean.rs | 11 +++++- src/cargo/ops/cargo_clean.rs | 5 ++- src/doc/man/cargo-clean.md | 6 +++ src/doc/man/generated_txt/cargo-clean.txt | 3 ++ src/doc/src/commands/cargo-clean.md | 7 ++++ src/etc/man/cargo-clean.1 | 5 +++ .../cargo_clean/help/stdout.term.svg | 38 ++++++++++--------- tests/testsuite/clean.rs | 5 +-- 8 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/bin/cargo/commands/clean.rs b/src/bin/cargo/commands/clean.rs index ec2879749ef..a9dfab8d5ef 100644 --- a/src/bin/cargo/commands/clean.rs +++ b/src/bin/cargo/commands/clean.rs @@ -7,6 +7,7 @@ use cargo::ops::CleanContext; use cargo::ops::{self, CleanOptions}; use cargo::util::print_available_packages; use clap_complete::ArgValueCandidates; +use indexmap::IndexSet; use std::time::Duration; pub fn cli() -> Command { @@ -18,6 +19,10 @@ pub fn cli() -> Command { "Package to clean artifacts for", ArgValueCandidates::new(get_pkg_name_candidates), ) + .arg( + flag("workspace", "Clean artifacts of the workspace members") + .help_heading(heading::PACKAGE_SELECTION), + ) .arg_release("Whether or not to clean release artifacts") .arg_profile("Clean artifacts of the specified profile") .arg_target_triple("Target triple to clean output for") @@ -146,10 +151,14 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { if args.is_present_with_zero_values("package") { print_available_packages(&ws)?; } + let mut spec = IndexSet::from_iter(values(args, "package")); + if args.flag("workspace") { + spec.extend(ws.members().map(|package| package.name().to_string())) + }; let opts = CleanOptions { gctx, - spec: values(args, "package"), + spec, targets: args.targets()?, requested_profile: args.get_profile_name("dev", ProfileChecking::Custom)?, profile_specified: args.contains_id("profile") || args.flag("release"), diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index a70972fae1b..767726562f2 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -9,6 +9,7 @@ use crate::util::interning::InternedString; use crate::util::{GlobalContext, Progress, ProgressStyle}; use anyhow::bail; use cargo_util::paths; +use indexmap::IndexSet; use std::collections::{HashMap, HashSet}; use std::fs; use std::path::{Path, PathBuf}; @@ -17,7 +18,7 @@ use std::rc::Rc; pub struct CleanOptions<'gctx> { pub gctx: &'gctx GlobalContext, /// A list of packages to clean. If empty, everything is cleaned. - pub spec: Vec, + pub spec: IndexSet, /// The target arch triple to clean, or None for the host arch pub targets: Vec, /// Whether to clean the release directory @@ -108,7 +109,7 @@ fn clean_specs( ws: &Workspace<'_>, profiles: &Profiles, targets: &[String], - spec: &[String], + spec: &IndexSet, dry_run: bool, ) -> CargoResult<()> { // Clean specific packages. diff --git a/src/doc/man/cargo-clean.md b/src/doc/man/cargo-clean.md index bc8fdc2ba5d..d5c5b6c7cfd 100644 --- a/src/doc/man/cargo-clean.md +++ b/src/doc/man/cargo-clean.md @@ -26,10 +26,16 @@ When no packages are selected, all packages and all dependencies in the workspace are cleaned. {{#options}} + {{#option "`-p` _spec_..." "`--package` _spec_..." }} Clean only the specified packages. This flag may be specified multiple times. See {{man "cargo-pkgid" 1}} for the SPEC format. {{/option}} + +{{#option "`--workspace`" }} +Clean artifacts of the workspace members. +{{/option}} + {{/options}} ### Clean Options diff --git a/src/doc/man/generated_txt/cargo-clean.txt b/src/doc/man/generated_txt/cargo-clean.txt index a81fb9095fc..b90b983129e 100644 --- a/src/doc/man/generated_txt/cargo-clean.txt +++ b/src/doc/man/generated_txt/cargo-clean.txt @@ -21,6 +21,9 @@ OPTIONS Clean only the specified packages. This flag may be specified multiple times. See cargo-pkgid(1) for the SPEC format. + --workspace + Clean artifacts of the workspace members. + Clean Options --dry-run Displays a summary of what would be deleted without deleting diff --git a/src/doc/src/commands/cargo-clean.md b/src/doc/src/commands/cargo-clean.md index e1e91e8c389..274b82a7347 100644 --- a/src/doc/src/commands/cargo-clean.md +++ b/src/doc/src/commands/cargo-clean.md @@ -22,12 +22,19 @@ When no packages are selected, all packages and all dependencies in the workspace are cleaned.
+
-p spec
--package spec

Clean only the specified packages. This flag may be specified multiple times. See cargo-pkgid(1) for the SPEC format.

+ +
--workspace
+

Clean artifacts of the workspace members.

+
+ +
### Clean Options diff --git a/src/etc/man/cargo-clean.1 b/src/etc/man/cargo-clean.1 index 60edaebe37b..f380f1582d3 100644 --- a/src/etc/man/cargo-clean.1 +++ b/src/etc/man/cargo-clean.1 @@ -23,6 +23,11 @@ workspace are cleaned. Clean only the specified packages. This flag may be specified multiple times. See \fBcargo\-pkgid\fR(1) for the SPEC format. .RE +.sp +\fB\-\-workspace\fR +.RS 4 +Clean artifacts of the workspace members. +.RE .SS "Clean Options" .sp \fB\-\-dry\-run\fR diff --git a/tests/testsuite/cargo_clean/help/stdout.term.svg b/tests/testsuite/cargo_clean/help/stdout.term.svg index e51b7707b43..65a13758063 100644 --- a/tests/testsuite/cargo_clean/help/stdout.term.svg +++ b/tests/testsuite/cargo_clean/help/stdout.term.svg @@ -1,7 +1,7 @@ - +