Skip to content

Commit 26a83d1

Browse files
authored
Merge pull request #26 from rust-lang/purge-caches
Add the Workspace::purge_all_caches method
2 parents dfd4dfc + 6a5f5b0 commit 26a83d1

File tree

8 files changed

+185
-3
lines changed

8 files changed

+185
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55

66
## Unreleased
77

8+
### Added
9+
10+
- New method `Workspace::purge_all_caches`
11+
812
### Changed
913

1014
- Subcommands executed in sandbox respect configs of parent command

src/workspace.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,31 @@ impl Workspace {
220220
Ok(())
221221
}
222222

223+
/// Remove all the contents of the caches in the workspace, freeing disk space.
224+
pub fn purge_all_caches(&self) -> Result<(), Error> {
225+
let mut paths = vec![
226+
self.cache_dir(),
227+
self.cargo_home().join("git"),
228+
self.cargo_home().join("registry").join("src"),
229+
self.cargo_home().join("registry").join("cache"),
230+
];
231+
232+
for index in std::fs::read_dir(self.cargo_home().join("registry").join("index"))? {
233+
let index = index?;
234+
if index.file_type()?.is_dir() {
235+
paths.push(index.path().join(".cache"));
236+
}
237+
}
238+
239+
for path in &paths {
240+
if path.exists() {
241+
remove_dir_all(&path)?;
242+
}
243+
}
244+
245+
Ok(())
246+
}
247+
223248
/// Return a list of all the toolchains present in the workspace.
224249
///
225250
/// # Example
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "process-lines"
3+
version = "0.1.0"
4+
authors = ["Pietro Albini <pietro@pietroalbini.org>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
println!("Hello, world!");
3+
println!("Hello, world again!");
4+
}

tests/buildtest/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn test_hello_world() {
2828

2929
#[test]
3030
fn test_process_lines() {
31-
runner::run("hello-world", |run| {
31+
runner::run("process-lines", |run| {
3232
run.build(SandboxBuilder::new().enable_networking(false), |build| {
3333
let storage = rustwide::logging::LogStorage::new(LevelFilter::Info);
3434
let mut ex = false;

tests/integration/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
mod crates_git;
2+
mod purge_caches;

tests/integration/purge_caches.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use failure::Error;
2+
use rustwide::cmd::SandboxBuilder;
3+
use rustwide::{Crate, Toolchain};
4+
use std::collections::HashMap;
5+
use std::path::{Path, PathBuf};
6+
7+
const WORKSPACE_NAME: &str = "purge-caches";
8+
9+
#[test]
10+
fn test_purge_caches() -> Result<(), Error> {
11+
let workspace_path = crate::utils::workspace_path(WORKSPACE_NAME);
12+
let workspace = crate::utils::init_named_workspace(WORKSPACE_NAME)?;
13+
14+
// Do an initial purge to prevent stale files from being present.
15+
workspace.purge_all_build_dirs()?;
16+
workspace.purge_all_caches()?;
17+
18+
let toolchain = Toolchain::dist("stable");
19+
toolchain.install(&workspace)?;
20+
21+
let start_contents = WorkspaceContents::collect(&workspace_path)?;
22+
23+
let crates = vec![
24+
Crate::crates_io("lazy_static", "1.0.0"),
25+
Crate::git("https://github.com/pietroalbini/git-credential-null"),
26+
];
27+
28+
// Simulate a build, which is going to fill up the caches.
29+
for krate in &crates {
30+
krate.fetch(&workspace)?;
31+
32+
let sandbox = SandboxBuilder::new().enable_networking(false);
33+
let mut build_dir = workspace.build_dir("shared");
34+
build_dir.build(&toolchain, krate, sandbox).run(|build| {
35+
build.cargo().args(&["check"]).run()?;
36+
Ok(())
37+
})?;
38+
}
39+
40+
// After all the builds are done purge everything again, and ensure the contents are the same
41+
// as when we started.
42+
workspace.purge_all_build_dirs()?;
43+
workspace.purge_all_caches()?;
44+
let end_contents = WorkspaceContents::collect(&workspace_path)?;
45+
start_contents.assert_same(end_contents);
46+
47+
Ok(())
48+
}
49+
50+
/// Define which files should be ignored when comparing the two workspaces. If there are expected
51+
/// changes, update the function to match them.
52+
fn should_compare(base: &Path, path: &Path) -> bool {
53+
let components = match path.strip_prefix(base) {
54+
Ok(stripped) => stripped
55+
.components()
56+
.map(|component| component.as_os_str().to_string_lossy().to_string())
57+
.collect::<Vec<_>>(),
58+
Err(_) => return true,
59+
};
60+
61+
let components = components.iter().map(|c| c.as_str()).collect::<Vec<_>>();
62+
match components.as_slice() {
63+
// The indexes could be updated during the build. The index is not considered a cache
64+
// though, so it's fine to ignore it during the comparison.
65+
["cargo-home", "registry", "index", _, ".git", ..] => false,
66+
["cargo-home", "registry", "index", _, ".cargo-index-lock"] => false,
67+
["cargo-home", "registry", "index", _, ".last-updated"] => false,
68+
69+
_ => true,
70+
}
71+
}
72+
73+
#[derive(Debug, PartialEq, Eq)]
74+
struct WorkspaceContents {
75+
base: PathBuf,
76+
files: HashMap<PathBuf, u64>,
77+
}
78+
79+
impl WorkspaceContents {
80+
fn collect(path: &Path) -> Result<Self, Error> {
81+
let mut files = HashMap::new();
82+
83+
for entry in walkdir::WalkDir::new(path)
84+
.into_iter()
85+
.filter_entry(|entry| should_compare(path, entry.path()))
86+
{
87+
let entry = entry?;
88+
let metadata = entry.metadata()?;
89+
90+
if !metadata.is_file() {
91+
continue;
92+
}
93+
94+
files.insert(entry.path().into(), metadata.len());
95+
}
96+
97+
Ok(Self {
98+
base: path.into(),
99+
files,
100+
})
101+
}
102+
103+
fn assert_same(self, mut other: Self) {
104+
let mut same = true;
105+
106+
println!("=== start directory differences ===");
107+
108+
for (path, start_digest) in self.files.into_iter() {
109+
if let Some(end_digest) = other.files.remove(&path) {
110+
if start_digest != end_digest {
111+
println!("file {} changed its size", path.display());
112+
same = false;
113+
}
114+
} else {
115+
println!("file {} was removed", path.display());
116+
same = false;
117+
}
118+
}
119+
120+
for (path, _) in other.files.into_iter() {
121+
println!("file {} was added", path.display());
122+
same = false;
123+
}
124+
125+
println!("=== end directory differences ===");
126+
127+
if !same {
128+
panic!("the contents of the directory changed");
129+
}
130+
}
131+
}

tests/utils/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
use failure::Error;
22
use log::LevelFilter;
33
use rustwide::{Workspace, WorkspaceBuilder};
4-
use std::path::Path;
4+
use std::path::{Path, PathBuf};
55

66
static USER_AGENT: &str = "rustwide-tests (https://github.com/rust-lang/rustwide)";
77

8+
pub(crate) fn workspace_path(name: &str) -> PathBuf {
9+
Path::new(".workspaces").join(name)
10+
}
11+
812
pub(crate) fn init_workspace() -> Result<Workspace, Error> {
13+
init_named_workspace("integration")
14+
}
15+
16+
pub(crate) fn init_named_workspace(name: &str) -> Result<Workspace, Error> {
917
init_logs();
10-
let workspace_path = Path::new(".workspaces").join("integration");
18+
let workspace_path = workspace_path(name);
1119
let mut builder = WorkspaceBuilder::new(&workspace_path, USER_AGENT).fast_init(true);
1220

1321
if std::env::var("RUSTWIDE_TEST_INSIDE_DOCKER").is_ok() {

0 commit comments

Comments
 (0)