summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/clean.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/clean.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/clean.rs675
1 files changed, 675 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/clean.rs b/src/tools/cargo/tests/testsuite/clean.rs
new file mode 100644
index 000000000..e0885fd26
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/clean.rs
@@ -0,0 +1,675 @@
+//! Tests for the `cargo clean` command.
+
+use cargo_test_support::registry::Package;
+use cargo_test_support::{
+ basic_bin_manifest, basic_manifest, git, main_file, project, project_in, rustc_host,
+};
+use glob::GlobError;
+use std::env;
+use std::path::{Path, PathBuf};
+
+#[cargo_test]
+fn cargo_clean_simple() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ p.cargo("build").run();
+ assert!(p.build_dir().is_dir());
+
+ p.cargo("clean").run();
+ assert!(!p.build_dir().is_dir());
+}
+
+#[cargo_test]
+fn different_dir() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file("src/bar/a.rs", "")
+ .build();
+
+ p.cargo("build").run();
+ assert!(p.build_dir().is_dir());
+
+ p.cargo("clean").cwd("src").with_stdout("").run();
+ assert!(!p.build_dir().is_dir());
+}
+
+#[cargo_test]
+fn clean_multiple_packages() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.d1]
+ path = "d1"
+ [dependencies.d2]
+ path = "d2"
+
+ [[bin]]
+ name = "foo"
+ "#,
+ )
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file("d1/Cargo.toml", &basic_bin_manifest("d1"))
+ .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }")
+ .file("d2/Cargo.toml", &basic_bin_manifest("d2"))
+ .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }")
+ .build();
+
+ p.cargo("build -p d1 -p d2 -p foo").run();
+
+ let d1_path = &p
+ .build_dir()
+ .join("debug")
+ .join(format!("d1{}", env::consts::EXE_SUFFIX));
+ let d2_path = &p
+ .build_dir()
+ .join("debug")
+ .join(format!("d2{}", env::consts::EXE_SUFFIX));
+
+ assert!(p.bin("foo").is_file());
+ assert!(d1_path.is_file());
+ assert!(d2_path.is_file());
+
+ p.cargo("clean -p d1 -p d2")
+ .cwd("src")
+ .with_stdout("")
+ .run();
+ assert!(p.bin("foo").is_file());
+ assert!(!d1_path.is_file());
+ assert!(!d2_path.is_file());
+}
+
+#[cargo_test]
+fn clean_multiple_packages_in_glob_char_path() {
+ let p = project_in("[d1]")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+ let foo_path = &p.build_dir().join("debug").join("deps");
+
+ #[cfg(not(target_env = "msvc"))]
+ let file_glob = "foo-*";
+
+ #[cfg(target_env = "msvc")]
+ let file_glob = "foo.pdb";
+
+ // Assert that build artifacts are produced
+ p.cargo("build").run();
+ assert_ne!(get_build_artifacts(foo_path, file_glob).len(), 0);
+
+ // Assert that build artifacts are destroyed
+ p.cargo("clean -p foo").run();
+ assert_eq!(get_build_artifacts(foo_path, file_glob).len(), 0);
+}
+
+fn get_build_artifacts(path: &PathBuf, file_glob: &str) -> Vec<Result<PathBuf, GlobError>> {
+ let pattern = path.to_str().expect("expected utf-8 path");
+ let pattern = glob::Pattern::escape(pattern);
+
+ let path = PathBuf::from(pattern).join(file_glob);
+ let path = path.to_str().expect("expected utf-8 path");
+ glob::glob(path)
+ .expect("expected glob to run")
+ .into_iter()
+ .collect::<Vec<Result<PathBuf, GlobError>>>()
+}
+
+#[cargo_test]
+fn clean_p_only_cleans_specified_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = [
+ "foo",
+ "foo_core",
+ "foo-base",
+ ]
+ "#,
+ )
+ .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0"))
+ .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("build -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"));
+ let num_foo_core_artifacts = fingerprint_names
+ .iter()
+ .filter(|&e| e == "foo_core")
+ .count();
+ assert_ne!(num_foo_core_artifacts, 0);
+ let num_foo_base_artifacts = fingerprint_names
+ .iter()
+ .filter(|&e| e == "foo-base")
+ .count();
+ assert_ne!(num_foo_base_artifacts, 0);
+
+ p.cargo("clean -p foo").run();
+
+ fingerprint_names = get_fingerprints_without_hashes(fingerprint_path);
+
+ // Cleaning `foo` leaves artifacts for the others
+ assert!(!fingerprint_names.iter().any(|e| e == "foo"));
+ assert_eq!(
+ fingerprint_names
+ .iter()
+ .filter(|&e| e == "foo_core")
+ .count(),
+ num_foo_core_artifacts,
+ );
+ assert_eq!(
+ fingerprint_names
+ .iter()
+ .filter(|&e| e == "foo-base")
+ .count(),
+ num_foo_core_artifacts,
+ );
+}
+
+fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec<String> {
+ std::fs::read_dir(fingerprint_path)
+ .expect("Build dir should be readable")
+ .filter_map(|entry| entry.ok())
+ .map(|entry| {
+ let name = entry.file_name();
+ let name = name
+ .into_string()
+ .expect("fingerprint name should be UTF-8");
+ name.rsplit_once('-')
+ .expect("Name should contain at least one hyphen")
+ .0
+ .to_owned()
+ })
+ .collect()
+}
+
+#[cargo_test]
+fn clean_release() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ a = { path = "a" }
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("a/Cargo.toml", &basic_manifest("a", "0.0.1"))
+ .file("a/src/lib.rs", "")
+ .build();
+
+ p.cargo("build --release").run();
+
+ p.cargo("clean -p foo").run();
+ p.cargo("build --release").with_stdout("").run();
+
+ p.cargo("clean -p foo --release").run();
+ p.cargo("build --release")
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([..])
+[FINISHED] release [optimized] target(s) in [..]
+",
+ )
+ .run();
+
+ p.cargo("build").run();
+
+ p.cargo("clean").arg("--release").run();
+ assert!(p.build_dir().is_dir());
+ assert!(p.build_dir().join("debug").is_dir());
+ assert!(!p.build_dir().join("release").is_dir());
+}
+
+#[cargo_test]
+fn clean_doc() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ a = { path = "a" }
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("a/Cargo.toml", &basic_manifest("a", "0.0.1"))
+ .file("a/src/lib.rs", "")
+ .build();
+
+ p.cargo("doc").run();
+
+ let doc_path = &p.build_dir().join("doc");
+
+ assert!(doc_path.is_dir());
+
+ p.cargo("clean --doc").run();
+
+ assert!(!doc_path.is_dir());
+ assert!(p.build_dir().is_dir());
+}
+
+#[cargo_test]
+fn build_script() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ build = "build.rs"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file(
+ "build.rs",
+ r#"
+ use std::path::PathBuf;
+ use std::env;
+
+ fn main() {
+ let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ if env::var("FIRST").is_ok() {
+ std::fs::File::create(out.join("out")).unwrap();
+ } else {
+ assert!(!out.join("out").exists());
+ }
+ }
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .build();
+
+ p.cargo("build").env("FIRST", "1").run();
+ p.cargo("clean -p foo").run();
+ p.cargo("build -v")
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([..])
+[RUNNING] `rustc [..] build.rs [..]`
+[RUNNING] `[..]build-script-build`
+[RUNNING] `rustc [..] src/main.rs [..]`
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_git() {
+ let git = git::new("dep", |project| {
+ project
+ .file("Cargo.toml", &basic_manifest("dep", "0.5.0"))
+ .file("src/lib.rs", "")
+ });
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ dep = {{ git = '{}' }}
+ "#,
+ git.url()
+ ),
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("build").run();
+ p.cargo("clean -p dep").with_stdout("").run();
+ p.cargo("build").run();
+}
+
+#[cargo_test]
+fn registry() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ Package::new("bar", "0.1.0").publish();
+
+ p.cargo("build").run();
+ p.cargo("clean -p bar").with_stdout("").run();
+ p.cargo("build").run();
+}
+
+#[cargo_test]
+fn clean_verbose() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ Package::new("bar", "0.1.0").publish();
+
+ p.cargo("build").run();
+ let mut expected = String::from(
+ "\
+[REMOVING] [..]target/debug/.fingerprint/bar[..]
+[REMOVING] [..]target/debug/deps/libbar[..].rlib
+[REMOVING] [..]target/debug/deps/bar-[..].d
+[REMOVING] [..]target/debug/deps/libbar[..].rmeta
+",
+ );
+ if cfg!(target_os = "macos") {
+ // Rust 1.69 has changed so that split-debuginfo=unpacked includes unpacked for rlibs.
+ for obj in p.glob("target/debug/deps/bar-*.o") {
+ expected.push_str(&format!("[REMOVING] [..]{}", obj.unwrap().display()));
+ }
+ }
+ p.cargo("clean -p bar --verbose")
+ .with_stderr_unordered(&expected)
+ .run();
+ p.cargo("build").run();
+}
+
+#[cargo_test]
+fn clean_remove_rlib_rmeta() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build").run();
+ assert!(p.target_debug_dir().join("libfoo.rlib").exists());
+ let rmeta = p.glob("target/debug/deps/*.rmeta").next().unwrap().unwrap();
+ assert!(rmeta.exists());
+ p.cargo("clean -p foo").run();
+ assert!(!p.target_debug_dir().join("libfoo.rlib").exists());
+ assert!(!rmeta.exists());
+}
+
+#[cargo_test]
+fn package_cleans_all_the_things() {
+ // -p cleans everything
+ // Use dashes everywhere to make sure dash/underscore stuff is handled.
+ for crate_type in &["rlib", "dylib", "cdylib", "staticlib", "proc-macro"] {
+ // Try each crate type individually since the behavior changes when
+ // they are combined.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo-bar"
+ version = "0.1.0"
+
+ [lib]
+ crate-type = ["{}"]
+ "#,
+ crate_type
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("build").run();
+ p.cargo("clean -p foo-bar").run();
+ assert_all_clean(&p.build_dir());
+ }
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo-bar"
+ version = "0.1.0"
+ edition = "2018"
+
+ [lib]
+ crate-type = ["rlib", "dylib", "staticlib"]
+
+ [[example]]
+ name = "foo-ex-rlib"
+ crate-type = ["rlib"]
+ test = true
+
+ [[example]]
+ name = "foo-ex-cdylib"
+ crate-type = ["cdylib"]
+ test = true
+
+ [[example]]
+ name = "foo-ex-bin"
+ test = true
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("src/lib/some-main.rs", "fn main() {}")
+ .file("src/bin/other-main.rs", "fn main() {}")
+ .file("examples/foo-ex-rlib.rs", "")
+ .file("examples/foo-ex-cdylib.rs", "")
+ .file("examples/foo-ex-bin.rs", "fn main() {}")
+ .file("tests/foo-test.rs", "")
+ .file("benches/foo-bench.rs", "")
+ .file("build.rs", "fn main() {}")
+ .build();
+
+ p.cargo("build --all-targets")
+ .env("CARGO_INCREMENTAL", "1")
+ .run();
+ p.cargo("test --all-targets")
+ .env("CARGO_INCREMENTAL", "1")
+ .run();
+ p.cargo("check --all-targets")
+ .env("CARGO_INCREMENTAL", "1")
+ .run();
+ p.cargo("clean -p foo-bar").run();
+ assert_all_clean(&p.build_dir());
+
+ // Try some targets.
+ p.cargo("build --all-targets --target")
+ .arg(rustc_host())
+ .run();
+ p.cargo("clean -p foo-bar --target").arg(rustc_host()).run();
+ assert_all_clean(&p.build_dir());
+}
+
+// Ensures that all files for the package have been deleted.
+#[track_caller]
+fn assert_all_clean(build_dir: &Path) {
+ let walker = walkdir::WalkDir::new(build_dir).into_iter();
+ for entry in walker.filter_entry(|e| {
+ let path = e.path();
+ // This is a known limitation, clean can't differentiate between
+ // the different build scripts from different packages.
+ !(path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .starts_with("build_script_build")
+ && path
+ .parent()
+ .unwrap()
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ == "incremental")
+ }) {
+ let entry = entry.unwrap();
+ let path = entry.path();
+ if let ".rustc_info.json" | ".cargo-lock" | "CACHEDIR.TAG" =
+ path.file_name().unwrap().to_str().unwrap()
+ {
+ continue;
+ }
+ if path.is_symlink() || path.is_file() {
+ panic!("{:?} was not cleaned", path);
+ }
+ }
+}
+
+#[cargo_test]
+fn clean_spec_multiple() {
+ // clean -p foo where foo matches multiple versions
+ Package::new("bar", "1.0.0").publish();
+ Package::new("bar", "2.0.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar1 = {version="1.0", package="bar"}
+ bar2 = {version="2.0", package="bar"}
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build").run();
+
+ // Check suggestion for bad pkgid.
+ p.cargo("clean -p baz")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: package ID specification `baz` did not match any packages
+
+<tab>Did you mean `bar`?
+",
+ )
+ .run();
+
+ p.cargo("clean -p bar:1.0.0")
+ .with_stderr(
+ "warning: version qualifier in `-p bar:1.0.0` is ignored, \
+ cleaning all versions of `bar` found",
+ )
+ .run();
+ let mut walker = walkdir::WalkDir::new(p.build_dir())
+ .into_iter()
+ .filter_map(|e| e.ok())
+ .filter(|e| {
+ let n = e.file_name().to_str().unwrap();
+ n.starts_with("bar") || n.starts_with("libbar")
+ });
+ if let Some(e) = walker.next() {
+ panic!("{:?} was not cleaned", e.path());
+ }
+}
+
+#[cargo_test]
+fn clean_spec_reserved() {
+ // Clean when a target (like a test) has a reserved name. In this case,
+ // make sure `clean -p` doesn't delete the reserved directory `build` when
+ // there is a test named `build`.
+ Package::new("bar", "1.0.0")
+ .file("src/lib.rs", "")
+ .file("build.rs", "fn main() {}")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("tests/build.rs", "")
+ .build();
+
+ p.cargo("build --all-targets").run();
+ assert!(p.target_debug_dir().join("build").is_dir());
+ let build_test = p.glob("target/debug/deps/build-*").next().unwrap().unwrap();
+ assert!(build_test.exists());
+ // Tests are never "uplifted".
+ assert!(p.glob("target/debug/build-*").next().is_none());
+
+ p.cargo("clean -p foo").run();
+ // Should not delete this.
+ assert!(p.target_debug_dir().join("build").is_dir());
+
+ // This should not rebuild bar.
+ p.cargo("build -v --all-targets")
+ .with_stderr(
+ "\
+[FRESH] bar v1.0.0
+[COMPILING] foo v0.1.0 [..]
+[RUNNING] `rustc [..]
+[RUNNING] `rustc [..]
+[RUNNING] `rustc [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}