Allow selectively cleaning packages

This adds a new argument to `cargo clean` which will enable selectively cleaning
particular packages. The command only cleans the package specified, no other
(not the dependencies of the package).

cc #537
This commit is contained in:
Alex Crichton 2014-09-23 09:03:34 -07:00
parent aa42fe5d43
commit 325c5f2def
7 changed files with 130 additions and 41 deletions

View file

@ -10,20 +10,34 @@ docopt!(Options, "
Remove artifacts that cargo has generated in the past
Usage:
cargo clean [options]
cargo clean [options] [<spec>]
Options:
-h, --help Print this message
--manifest-path PATH Path to the manifest to the package to clean
--target TRIPLE Target triple to clean output for (default all)
-v, --verbose Use verbose output
", flag_manifest_path: Option<String>)
pub fn execute(options: Options, _shell: &mut MultiShell) -> CliResult<Option<()>> {
If <spec> is provided, then it is interpreted as a package id specification and
only the output for the package specified will be removed. If <spec> is not
provided, then all output from cargo will be cleaned out. Note that a lockfile
must exist for <spec> to be given.
For more information about <spec>, see `cargo help pkgid`.
", flag_manifest_path: Option<String>, arg_spec: Option<String>,
flag_target: Option<String>)
pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
shell.set_verbose(options.flag_verbose);
debug!("executing; cmd=cargo-clean; args={}", os::args());
let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path));
ops::clean(&root).map(|_| None).map_err(|err| {
let mut opts = ops::CleanOptions {
shell: shell,
spec: options.arg_spec.as_ref().map(|s| s.as_slice()),
target: options.flag_target.as_ref().map(|s| s.as_slice()),
};
ops::clean(&root, &mut opts).map(|_| None).map_err(|err| {
CliError::from_boxed(err, 101)
})
}

View file

@ -1,30 +1,81 @@
use std::io::fs::{rmdir_recursive, PathExtensions};
use std::io::fs::{mod, PathExtensions};
use core::source::Source;
use core::{MultiShell, PackageSet};
use core::source::{Source, SourceMap};
use sources::PathSource;
use util::{CargoResult, human, ChainError};
use util::{CargoResult, human, ChainError, Config};
use ops::{mod, Layout, Context};
pub struct CleanOptions<'a> {
pub spec: Option<&'a str>,
pub target: Option<&'a str>,
pub shell: &'a mut MultiShell<'a>
}
/// Cleans the project from build artifacts.
pub fn clean(manifest_path: &Path) -> CargoResult<()> {
pub fn clean(manifest_path: &Path, opts: &mut CleanOptions) -> CargoResult<()> {
let mut src = try!(PathSource::for_path(&manifest_path.dir_path()));
try!(src.update());
let root = try!(src.get_root_package());
let manifest = root.get_manifest();
let build_dir = manifest.get_target_dir();
if build_dir.exists() {
try!(rmdir_recursive(build_dir).chain_error(|| {
human("Could not remove build directory")
}))
}
// If we have a spec, then we need to delete some package,s otherwise, just
// remove the whole target directory and be done with it!
let spec = match opts.spec {
Some(spec) => spec,
None => return rm_rf(manifest.get_target_dir()),
};
let doc_dir = manifest.get_doc_dir();
if doc_dir.exists() {
try!(rmdir_recursive(doc_dir).chain_error(|| {
human("Could not remove documentation directory")
}))
// Load the lockfile (if one's available), and resolve spec to a pkgid
let lockfile = root.get_root().join("Cargo.lock");
let source_id = root.get_package_id().get_source_id();
let resolve = match try!(ops::load_lockfile(&lockfile, source_id)) {
Some(resolve) => resolve,
None => return Err(human("A Cargo.lock must exist before cleaning"))
};
let pkgid = try!(resolve.query(spec));
// Translate the PackageId to a Package
let mut cfg = try!(Config::new(opts.shell, None, None));
let pkg = {
let mut source = pkgid.get_source_id().load(&mut cfg);
try!(source.update());
(try!(source.get([pkgid.clone()]))).into_iter().next().unwrap()
};
// Create a compilation context to have access to information like target
// filenames and such
let srcs = SourceMap::new();
let pkgs = PackageSet::new([]);
let cx = try!(Context::new("compile", &resolve, &srcs, &pkgs, &mut cfg,
Layout::at(root.get_absolute_target_dir()),
None));
// And finally, clean everything out!
for target in pkg.get_targets().iter() {
let layout = Layout::new(&root, opts.target,
target.get_profile().get_dest());
try!(rm_rf(&layout.native(&pkg)));
try!(rm_rf(&layout.fingerprint(&pkg)));
for filename in try!(cx.target_filenames(target)).iter() {
let filename = filename.as_slice();
try!(rm_rf(&layout.dest().join(filename)));
try!(rm_rf(&layout.deps().join(filename)));
}
}
Ok(())
}
fn rm_rf(path: &Path) -> CargoResult<()> {
if path.is_dir() {
try!(fs::rmdir_recursive(path).chain_error(|| {
human("could not remove build directory")
}));
} else if path.exists() {
try!(fs::unlink(path).chain_error(|| {
human("failed to remove build artifact")
}));
}
Ok(())
}

View file

@ -5,7 +5,6 @@ use std::io::{fs, File, UserRWX, BufferedReader};
use core::{Package, Target, PathKind};
use util;
use util::hex::short_hash;
use util::{CargoResult, Fresh, Dirty, Freshness, internal, Require, profile};
use super::{Kind, KindTarget};
@ -194,12 +193,9 @@ fn prepare(is_fresh: bool, loc: Path, fingerprint: String,
/// Return the (old, new) location for fingerprints for a package
pub fn dirs(cx: &Context, pkg: &Package, kind: Kind) -> (Path, Path) {
let dirname = format!("{}-{}", pkg.get_name(),
short_hash(pkg.get_package_id()));
let dirname = dirname.as_slice();
let layout = cx.layout(kind);
let layout = layout.proxy();
(layout.old_fingerprint().join(dirname), layout.fingerprint().join(dirname))
(layout.old_fingerprint(pkg), layout.fingerprint(pkg))
}
/// Returns the (old, new) location for the dep info file of a target.

View file

@ -70,7 +70,20 @@ pub struct LayoutProxy<'a> {
}
impl Layout {
pub fn new(root: Path) -> Layout {
pub fn new(pkg: &Package, triple: Option<&str>, dest: Option<&str>) -> Layout {
let mut path = pkg.get_absolute_target_dir();
match triple {
Some(s) => path.push(s),
None => {}
}
match dest {
Some(s) => path.push(s),
None => {}
}
Layout::at(path)
}
pub fn at(root: Path) -> Layout {
Layout {
deps: root.join("deps"),
native: root.join("native"),
@ -127,18 +140,22 @@ impl Layout {
pub fn dest<'a>(&'a self) -> &'a Path { &self.root }
pub fn deps<'a>(&'a self) -> &'a Path { &self.deps }
pub fn native(&self, package: &Package) -> Path {
self.native.join(self.native_name(package))
self.native.join(self.pkg_dir(package))
}
pub fn fingerprint(&self, package: &Package) -> Path {
self.fingerprint.join(self.pkg_dir(package))
}
pub fn fingerprint(&self) -> &Path { &self.fingerprint }
pub fn old_dest<'a>(&'a self) -> &'a Path { &self.old_root }
pub fn old_deps<'a>(&'a self) -> &'a Path { &self.old_deps }
pub fn old_native(&self, package: &Package) -> Path {
self.old_native.join(self.native_name(package))
self.old_native.join(self.pkg_dir(package))
}
pub fn old_fingerprint(&self, package: &Package) -> Path {
self.old_fingerprint.join(self.pkg_dir(package))
}
pub fn old_fingerprint(&self) -> &Path { &self.old_fingerprint }
fn native_name(&self, pkg: &Package) -> String {
fn pkg_dir(&self, pkg: &Package) -> String {
format!("{}-{}", pkg.get_name(), short_hash(pkg.get_package_id()))
}
}

View file

@ -11,10 +11,12 @@ use util::{Config, internal, ChainError, Fresh, profile};
use self::job::{Job, Work};
use self::job_queue::{JobQueue, StageStart, StageCustomBuild, StageLibraries};
use self::job_queue::{StageBinaries, StageEnd};
use self::context::{Context, PlatformRequirement, PlatformTarget};
use self::context::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::compilation::Compilation;
pub use self::context::Context;
pub use self::context::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::context::{PlatformRequirement, PlatformTarget};
pub use self::layout::{Layout, LayoutProxy};
mod context;
mod compilation;
@ -24,7 +26,7 @@ mod job_queue;
mod layout;
#[deriving(PartialEq, Eq)]
enum Kind { KindPlugin, KindTarget }
pub enum Kind { KindPlugin, KindTarget }
// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
@ -57,11 +59,10 @@ pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);
let root = pkg.get_absolute_target_dir();
let dest = uniq_target_dest(targets).unwrap_or("");
let host_layout = layout::Layout::new(root.join(dest));
let dest = uniq_target_dest(targets);
let host_layout = Layout::new(pkg, None, dest);
let target_layout = config.target().map(|target| {
layout::Layout::new(root.join(target).join(dest))
layout::Layout::new(pkg, Some(target), dest)
});
let mut cx = try!(Context::new(env, resolve, sources, deps, config,

View file

@ -1,7 +1,10 @@
pub use self::cargo_clean::clean;
pub use self::cargo_clean::{clean, CleanOptions};
pub use self::cargo_compile::{compile, CompileOptions};
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
pub use self::cargo_rustc::{compile_targets, Compilation};
pub use self::cargo_rustc::{compile_targets, Compilation, Layout, Kind};
pub use self::cargo_rustc::{KindTarget, KindPlugin, Context, LayoutProxy};
pub use self::cargo_rustc::{PlatformRequirement, PlatformTarget};
pub use self::cargo_rustc::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::cargo_run::run;
pub use self::cargo_new::{new, NewOptions};
pub use self::cargo_doc::{doc, DocOptions};

View file

@ -605,6 +605,13 @@ test!(recompilation {
{} foo v0.5.0 ({})\n",
COMPILING, git_project.url(),
COMPILING, p.url())));
// Make sure clean only cleans one dep
assert_that(p.process(cargo_dir().join("cargo")).arg("clean").arg("foo"),
execs().with_stdout(""));
assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
execs().with_stdout(format!("{} foo v0.5.0 ({})\n",
COMPILING, p.url())));
})
test!(update_with_shared_deps {