Skip to content

Commit 2f375da

Browse files
author
Jon Gjengset
committed
clean: add --include-cache option
This patch adds the `--include-cache` flag to `cargo clean`, which allows the command to also remove related artifacts from `CARGO_HOME` such as downloaded `.crate` files in `~/.cargo/cache/` and extracted source directories in `~/.cargo/src/`. Note that this feature is not intended to replace the `cargo-cache` command which does smart cache management. Instead, this command simply blows away whatever it's asked to delete with no smarts whatsoever. Fixes #3289.
1 parent e11cd81 commit 2f375da

File tree

11 files changed

+210
-5
lines changed

11 files changed

+210
-5
lines changed

src/bin/cargo/commands/clean.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ pub fn cli() -> App {
77
subcommand("clean")
88
.about("Remove artifacts that cargo has generated in the past")
99
.arg(opt("quiet", "No output printed to stdout").short("q"))
10+
.arg(opt(
11+
"include-cache",
12+
"Whether to clean Cargo's cache directories",
13+
))
1014
.arg_package_spec_simple("Package to clean artifacts for")
1115
.arg_manifest_path()
1216
.arg_target_triple("Target triple to clean output for")
@@ -31,6 +35,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
3135
requested_profile: args.get_profile_name(config, "dev", ProfileChecking::Custom)?,
3236
profile_specified: args.is_present("profile") || args.is_present("release"),
3337
doc: args.is_present("doc"),
38+
include_cache: args.is_present("include-cache"),
3439
};
3540
ops::clean(&ws, &opts)?;
3641
Ok(())

src/cargo/core/source/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33

44
use crate::core::package::PackageSet;
55
use crate::core::{Dependency, Package, PackageId, Summary};
6-
use crate::util::{CargoResult, Config};
6+
use crate::util::{CargoResult, Config, Filesystem};
77

88
mod source_id;
99

@@ -101,6 +101,10 @@ pub trait Source {
101101
/// Query if a package is yanked. Only registry sources can mark packages
102102
/// as yanked. This ignores the yanked whitelist.
103103
fn is_yanked(&mut self, _pkg: PackageId) -> CargoResult<bool>;
104+
105+
fn source_cache(&mut self) -> Option<&mut Filesystem>;
106+
107+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem>;
104108
}
105109

106110
pub enum MaybePackage {
@@ -178,6 +182,14 @@ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
178182
fn is_yanked(&mut self, pkg: PackageId) -> CargoResult<bool> {
179183
(**self).is_yanked(pkg)
180184
}
185+
186+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
187+
(**self).source_cache()
188+
}
189+
190+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
191+
(**self).dot_crate_cache()
192+
}
181193
}
182194

183195
impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
@@ -240,6 +252,14 @@ impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
240252
fn is_yanked(&mut self, pkg: PackageId) -> CargoResult<bool> {
241253
(**self).is_yanked(pkg)
242254
}
255+
256+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
257+
(**self).source_cache()
258+
}
259+
260+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
261+
(**self).dot_crate_cache()
262+
}
243263
}
244264

245265
/// A `HashMap` of `SourceId` -> `Box<Source>`.

src/cargo/ops/cargo_clean.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
22
use crate::core::profiles::Profiles;
33
use crate::core::{PackageIdSpec, TargetKind, Workspace};
44
use crate::ops;
5+
use crate::sources::SourceConfigMap;
56
use crate::util::errors::CargoResult;
67
use crate::util::interning::InternedString;
78
use crate::util::lev_distance;
89
use crate::util::Config;
910

1011
use anyhow::Context as _;
1112
use cargo_util::paths;
13+
use std::collections::{hash_map, HashMap};
1214
use std::fs;
1315
use std::path::Path;
1416

@@ -24,6 +26,8 @@ pub struct CleanOptions<'a> {
2426
pub requested_profile: InternedString,
2527
/// Whether to just clean the doc directory
2628
pub doc: bool,
29+
/// Whether to also clean the package cache
30+
pub include_cache: bool,
2731
}
2832

2933
/// Cleans the package's build artifacts.
@@ -53,6 +57,12 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
5357
// Note that we don't bother grabbing a lock here as we're just going to
5458
// blow it all away anyway.
5559
if opts.spec.is_empty() {
60+
if opts.include_cache {
61+
// We also need to remove src/ and cache/ from CARGO_HOME.
62+
rm_rf(&config.registry_cache_path().into_path_unlocked(), config)?;
63+
rm_rf(&config.registry_source_path().into_path_unlocked(), config)?;
64+
}
65+
5666
return rm_rf(&target_dir.into_path_unlocked(), config);
5767
}
5868

@@ -133,6 +143,16 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
133143
}
134144
let packages = pkg_set.get_many(pkg_ids)?;
135145

146+
let mut registries = HashMap::new();
147+
let (_pc_lock, sources) = if opts.include_cache {
148+
(
149+
Some(config.acquire_package_cache_lock()?),
150+
Some(SourceConfigMap::new(config)?),
151+
)
152+
} else {
153+
(None, None)
154+
};
155+
136156
for pkg in packages {
137157
let pkg_dir = format!("{}-*", pkg.name());
138158

@@ -141,6 +161,30 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
141161
rm_rf_glob(&layout.fingerprint().join(&pkg_dir), config)?;
142162
}
143163

164+
if let Some(sources) = &sources {
165+
let source_id = pkg.package_id().source_id();
166+
let registry = match registries.entry(source_id) {
167+
hash_map::Entry::Occupied(o) => o.into_mut(),
168+
hash_map::Entry::Vacant(v) => {
169+
let reg = sources.load(source_id, &Default::default())?;
170+
v.insert(reg)
171+
}
172+
};
173+
174+
// The as_path_unlocked are okay since we've acquired the package cache lock.
175+
if let Some(src_path) = registry.source_cache() {
176+
rm_rf_glob(&src_path.as_path_unlocked().join(&pkg_dir), config)?;
177+
}
178+
if let Some(cache_path) = registry.dot_crate_cache() {
179+
rm_rf_glob(
180+
&cache_path
181+
.as_path_unlocked()
182+
.join(&format!("{}.crate", pkg_dir)),
183+
config,
184+
)?;
185+
}
186+
}
187+
144188
for target in pkg.targets() {
145189
if target.is_custom_build() {
146190
// Get both the build_script_build and the output directory.

src/cargo/sources/directory.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::core::source::MaybePackage;
66
use crate::core::{Dependency, Package, PackageId, Source, SourceId, Summary};
77
use crate::sources::PathSource;
88
use crate::util::errors::CargoResult;
9-
use crate::util::Config;
9+
use crate::util::{Config, Filesystem};
1010

1111
use anyhow::Context as _;
1212
use cargo_util::{paths, Sha256};
@@ -205,4 +205,12 @@ impl<'cfg> Source for DirectorySource<'cfg> {
205205
fn is_yanked(&mut self, _pkg: PackageId) -> CargoResult<bool> {
206206
Ok(false)
207207
}
208+
209+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
210+
None
211+
}
212+
213+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
214+
None
215+
}
208216
}

src/cargo/sources/git/source.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::sources::git::utils::GitRemote;
55
use crate::sources::PathSource;
66
use crate::util::errors::CargoResult;
77
use crate::util::hex::short_hash;
8-
use crate::util::Config;
8+
use crate::util::{Config, Filesystem};
99
use anyhow::Context;
1010
use log::trace;
1111
use std::fmt::{self, Debug, Formatter};
@@ -212,6 +212,14 @@ impl<'cfg> Source for GitSource<'cfg> {
212212
fn is_yanked(&mut self, _pkg: PackageId) -> CargoResult<bool> {
213213
Ok(false)
214214
}
215+
216+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
217+
None
218+
}
219+
220+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
221+
None
222+
}
215223
}
216224

217225
#[cfg(test)]

src/cargo/sources/path.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
66
use crate::core::source::MaybePackage;
77
use crate::core::{Dependency, Package, PackageId, Source, SourceId, Summary};
88
use crate::ops;
9-
use crate::util::{internal, CargoResult, Config};
9+
use crate::util::{internal, CargoResult, Config, Filesystem};
1010
use anyhow::Context as _;
1111
use cargo_util::paths;
1212
use filetime::FileTime;
@@ -541,4 +541,12 @@ impl<'cfg> Source for PathSource<'cfg> {
541541
fn is_yanked(&mut self, _pkg: PackageId) -> CargoResult<bool> {
542542
Ok(false)
543543
}
544+
545+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
546+
None
547+
}
548+
549+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
550+
None
551+
}
544552
}

src/cargo/sources/registry/local.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,8 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> {
120120
) -> CargoResult<File> {
121121
panic!("this source doesn't download")
122122
}
123+
124+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
125+
None
126+
}
123127
}

src/cargo/sources/registry/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ pub trait RegistryData {
435435
/// (remote=git, local=files).
436436
fn index_path(&self) -> &Filesystem;
437437

438+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem>;
439+
438440
/// Loads the JSON for a specific named package from the index.
439441
///
440442
/// * `root` is the root path to the index.
@@ -790,4 +792,12 @@ impl<'cfg> Source for RegistrySource<'cfg> {
790792
}
791793
self.index.is_yanked(pkg, &mut *self.ops)
792794
}
795+
796+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
797+
Some(&mut self.src_path)
798+
}
799+
800+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
801+
self.ops.dot_crate_cache()
802+
}
793803
}

src/cargo/sources/registry/remote.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
329329
}
330330
false
331331
}
332+
333+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
334+
Some(&mut self.cache_path)
335+
}
332336
}
333337

334338
impl<'cfg> Drop for RemoteRegistry<'cfg> {

src/cargo/sources/replaced.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::core::source::MaybePackage;
22
use crate::core::{Dependency, Package, PackageId, Source, SourceId, Summary};
33
use crate::util::errors::CargoResult;
4+
use crate::util::Filesystem;
45

56
use anyhow::Context as _;
67

@@ -127,4 +128,12 @@ impl<'cfg> Source for ReplacedSource<'cfg> {
127128
fn is_yanked(&mut self, pkg: PackageId) -> CargoResult<bool> {
128129
self.inner.is_yanked(pkg)
129130
}
131+
132+
fn source_cache(&mut self) -> Option<&mut Filesystem> {
133+
self.inner.source_cache()
134+
}
135+
136+
fn dot_crate_cache(&mut self) -> Option<&mut Filesystem> {
137+
self.inner.dot_crate_cache()
138+
}
130139
}

tests/testsuite/clean.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Tests for the `cargo clean` command.
22
3+
use cargo::core::SourceId;
4+
use cargo_test_support::install::cargo_home;
35
use cargo_test_support::paths::is_symlink;
4-
use cargo_test_support::registry::Package;
6+
use cargo_test_support::registry::{registry_url, Package};
57
use cargo_test_support::{basic_bin_manifest, basic_manifest, git, main_file, project, rustc_host};
68
use std::env;
79
use std::path::Path;
@@ -299,6 +301,89 @@ fn clean_verbose() {
299301
p.cargo("build").run();
300302
}
301303

304+
#[cargo_test]
305+
fn clean_include_cache() {
306+
let p = project()
307+
.file(
308+
"Cargo.toml",
309+
r#"
310+
[package]
311+
name = "foo"
312+
version = "0.0.1"
313+
authors = []
314+
315+
[dependencies]
316+
bar = "0.1"
317+
baz = "0.1"
318+
"#,
319+
)
320+
.file("src/main.rs", "fn main() {}")
321+
.build();
322+
323+
Package::new("bar", "0.1.0").publish();
324+
Package::new("baz", "0.1.0").publish();
325+
326+
p.cargo("build").run();
327+
328+
let src_cache = cargo_home().join("registry").join("src");
329+
let dot_crate_cache = cargo_home().join("registry").join("cache");
330+
assert!(src_cache.exists());
331+
assert!(dot_crate_cache.exists());
332+
p.cargo("clean --include-cache").with_stdout("").run();
333+
assert!(!src_cache.exists());
334+
assert!(!dot_crate_cache.exists());
335+
}
336+
#[cargo_test]
337+
fn clean_package_include_cache() {
338+
let p = project()
339+
.file(
340+
"Cargo.toml",
341+
r#"
342+
[package]
343+
name = "foo"
344+
version = "0.0.1"
345+
authors = []
346+
347+
[dependencies]
348+
bar = "0.1"
349+
baz = "0.1"
350+
"#,
351+
)
352+
.file("src/main.rs", "fn main() {}")
353+
.build();
354+
355+
Package::new("bar", "0.1.0").publish();
356+
Package::new("baz", "0.1.0").publish();
357+
358+
p.cargo("build").run();
359+
360+
let id = SourceId::for_registry(&registry_url()).unwrap();
361+
let hash = cargo::util::hex::short_hash(&id);
362+
let src_cache = cargo_home()
363+
.join("registry")
364+
.join("src")
365+
.join(format!("-{}", hash));
366+
let bar_src_cache = src_cache.join(format!("bar-0.1.0"));
367+
let baz_src_cache = src_cache.join(format!("baz-0.1.0"));
368+
let dot_crate_cache = cargo_home()
369+
.join("registry")
370+
.join("cache")
371+
.join(format!("-{}", hash));
372+
let bar_dot_crate_cache = dot_crate_cache.join(format!("bar-0.1.0.crate"));
373+
let baz_dot_crate_cache = dot_crate_cache.join(format!("baz-0.1.0.crate"));
374+
assert!(bar_src_cache.exists());
375+
assert!(baz_src_cache.exists());
376+
assert!(bar_dot_crate_cache.exists());
377+
assert!(baz_dot_crate_cache.exists());
378+
p.cargo("clean --include-cache -p bar")
379+
.with_stdout("")
380+
.run();
381+
assert!(!bar_src_cache.exists());
382+
assert!(baz_src_cache.exists());
383+
assert!(!bar_dot_crate_cache.exists());
384+
assert!(baz_dot_crate_cache.exists());
385+
}
386+
302387
#[cargo_test]
303388
fn clean_remove_rlib_rmeta() {
304389
let p = project()

0 commit comments

Comments
 (0)