Skip to content

Commit

Permalink
Fix a number of bugs with cargo clean
Browse files Browse the repository at this point in the history
* Clean out both host/target platforms if any are available.
* Clean out build script output.
* Add a --release flag to also be able to clean out release folder
* Be sure to clean out benchmarks, tests, etc.
* Cleaning out registry packages no longer panics

Closes rust-lang#2121
  • Loading branch information
alexcrichton authored and JanLikar committed Jan 26, 2016
1 parent 150e057 commit aab5119
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 24 deletions.
3 changes: 3 additions & 0 deletions src/bin/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct Options {
flag_verbose: bool,
flag_quiet: bool,
flag_color: Option<String>,
flag_release: bool,
}

pub const USAGE: &'static str = "
Expand All @@ -25,6 +26,7 @@ Options:
-p SPEC, --package SPEC ... Package to clean artifacts for
--manifest-path PATH Path to the manifest to the package to clean
--target TRIPLE Target triple to clean output for (default all)
--release Whether or not to clean release artifacts
-v, --verbose Use verbose output
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
Expand All @@ -45,6 +47,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
config: config,
spec: &options.flag_package,
target: options.flag_target.as_ref().map(|s| &s[..]),
release: options.flag_release,
};
ops::clean(&root, &opts).map(|_| None).map_err(|err| {
CliError::from_boxed(err, 101)
Expand Down
60 changes: 37 additions & 23 deletions src/cargo/ops/cargo_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ use std::fs;
use std::io::prelude::*;
use std::path::Path;

use core::{Package, PackageSet, Profiles, Profile};
use core::{Package, PackageSet, Profiles};
use core::source::{Source, SourceMap};
use core::registry::PackageRegistry;
use util::{CargoResult, human, ChainError, Config};
use ops::{self, Layout, Context, BuildConfig, Kind, Unit};

pub struct CleanOptions<'a> {
pub spec: &'a [String],
pub target: Option<&'a str>,
pub config: &'a Config,
pub release: bool,
}

/// Cleans the project from build artifacts.
Expand All @@ -37,39 +39,51 @@ pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> {
// filenames and such
let srcs = SourceMap::new();
let pkgs = PackageSet::new(&[]);
let profiles = Profiles::default();

let dest = if opts.release {"release"} else {"debug"};
let host_layout = Layout::new(opts.config, &root, None, dest);
let target_layout = opts.target.map(|target| {
Layout::new(opts.config, &root, Some(target), dest)
});

let cx = try!(Context::new(&resolve, &srcs, &pkgs, opts.config,
Layout::at(target_dir),
None, BuildConfig::default(),
&profiles));
host_layout, target_layout,
BuildConfig::default(),
root.manifest().profiles()));

let mut registry = PackageRegistry::new(opts.config);

// resolve package specs and remove the corresponding packages
for spec in opts.spec {
let pkgid = try!(resolve.query(spec));

// Translate the PackageId to a Package
let pkg = {
let mut source = pkgid.source_id().load(opts.config);
try!(source.update());
(try!(source.get(&[pkgid.clone()]))).into_iter().next().unwrap()
try!(registry.add_sources(&[pkgid.source_id().clone()]));
(try!(registry.get(&[pkgid.clone()]))).into_iter().next().unwrap()
};

// And finally, clean everything out!
for target in pkg.targets().iter() {
// TODO: `cargo clean --release`
let layout = Layout::new(opts.config, &root, opts.target, "debug");
try!(rm_rf(&layout.fingerprint(&pkg)));
let profiles = [Profile::default_dev(), Profile::default_test()];
for profile in profiles.iter() {
let unit = Unit {
pkg: &pkg,
target: target,
profile: profile,
kind: Kind::Target,
};
for filename in try!(cx.target_filenames(&unit)).iter() {
try!(rm_rf(&layout.dest().join(&filename)));
try!(rm_rf(&layout.deps().join(&filename)));
for target in pkg.targets() {
for kind in [Kind::Host, Kind::Target].iter() {
let layout = cx.layout(&pkg, *kind);
try!(rm_rf(&layout.proxy().fingerprint(&pkg)));
try!(rm_rf(&layout.build(&pkg)));
let Profiles {
ref release, ref dev, ref test, ref bench, ref doc,
ref custom_build,
} = *root.manifest().profiles();
for profile in [release, dev, test, bench, doc, custom_build].iter() {
let unit = Unit {
pkg: &pkg,
target: target,
profile: profile,
kind: *kind,
};
let root = cx.out_dir(&unit);
for filename in try!(cx.target_filenames(&unit)).iter() {
try!(rm_rf(&root.join(&filename)));
}
}
}
}
Expand Down
136 changes: 135 additions & 1 deletion tests/test_cargo_clean.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::env;

use support::{project, execs, main_file, basic_bin_manifest};
use support::{git, project, execs, main_file, basic_bin_manifest};
use support::{COMPILING, RUNNING};
use support::registry::Package;
use hamcrest::{assert_that, existing_dir, existing_file, is_not};

fn setup() {
Expand Down Expand Up @@ -93,3 +95,135 @@ test!(clean_multiple_packages {
assert_that(d1_path, is_not(existing_file()));
assert_that(d2_path, is_not(existing_file()));
});

test!(clean_release {
let p = project("foo")
.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", r#"
[package]
name = "a"
version = "0.0.1"
authors = []
"#)
.file("a/src/lib.rs", "");
p.build();

assert_that(p.cargo_process("build").arg("--release"),
execs().with_status(0));

assert_that(p.cargo("clean").arg("-p").arg("foo"),
execs().with_status(0));
assert_that(p.cargo("build").arg("--release"),
execs().with_status(0).with_stdout(""));

assert_that(p.cargo("clean").arg("-p").arg("foo").arg("--release"),
execs().with_status(0));
assert_that(p.cargo("build").arg("--release"),
execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ([..])
", compiling = COMPILING)));
});

test!(build_script {
let p = project("foo")
.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!(!std::fs::metadata(out.join("out")).is_ok());
}
}
"#)
.file("a/src/lib.rs", "");
p.build();

assert_that(p.cargo_process("build").env("FIRST", "1"),
execs().with_status(0));
assert_that(p.cargo("clean").arg("-p").arg("foo"),
execs().with_status(0));
assert_that(p.cargo("build").arg("-v"),
execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ([..])
{running} `rustc build.rs [..]`
{running} `[..]build-script-build[..]`
{running} `rustc src[..]main.rs [..]`
", compiling = COMPILING, running = RUNNING)));
});

test!(clean_git {
let git = git::new("dep", |project| {
project.file("Cargo.toml", r#"
[project]
name = "dep"
version = "0.5.0"
authors = []
"#)
.file("src/lib.rs", "")
}).unwrap();

let p = project("foo")
.file("Cargo.toml", &format!(r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
dep = {{ git = '{}' }}
"#, git.url()))
.file("src/main.rs", "fn main() {}");
p.build();

assert_that(p.cargo_process("build"),
execs().with_status(0));
assert_that(p.cargo("clean").arg("-p").arg("dep"),
execs().with_status(0).with_stdout(""));
assert_that(p.cargo("build"),
execs().with_status(0));
});

test!(registry {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
bar = "0.1"
"#)
.file("src/main.rs", "fn main() {}");
p.build();

Package::new("bar", "0.1.0").publish();

assert_that(p.cargo_process("build"),
execs().with_status(0));
assert_that(p.cargo("clean").arg("-p").arg("bar"),
execs().with_status(0).with_stdout(""));
assert_that(p.cargo("build"),
execs().with_status(0));
});

0 comments on commit aab5119

Please sign in to comment.