From 325c5f2defe58701986ed41ec1c6eb4b04594888 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 23 Sep 2014 09:03:34 -0700 Subject: [PATCH] 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 --- src/bin/clean.rs | 24 +++++-- src/cargo/ops/cargo_clean.rs | 83 +++++++++++++++++++----- src/cargo/ops/cargo_rustc/fingerprint.rs | 6 +- src/cargo/ops/cargo_rustc/layout.rs | 29 +++++++-- src/cargo/ops/cargo_rustc/mod.rs | 15 +++-- src/cargo/ops/mod.rs | 7 +- tests/test_cargo_compile_git_deps.rs | 7 ++ 7 files changed, 130 insertions(+), 41 deletions(-) diff --git a/src/bin/clean.rs b/src/bin/clean.rs index a671d0508..fac0c9c3d 100644 --- a/src/bin/clean.rs +++ b/src/bin/clean.rs @@ -10,20 +10,34 @@ docopt!(Options, " Remove artifacts that cargo has generated in the past Usage: - cargo clean [options] + cargo clean [options] [] 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) -pub fn execute(options: Options, _shell: &mut MultiShell) -> CliResult> { +If is provided, then it is interpreted as a package id specification and +only the output for the package specified will be removed. If is not +provided, then all output from cargo will be cleaned out. Note that a lockfile +must exist for to be given. + +For more information about , see `cargo help pkgid`. +", flag_manifest_path: Option, arg_spec: Option, + flag_target: Option) + +pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { + 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) }) } diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index d8362f23b..cb8786a47 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -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(()) +} diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index c0ca783c3..f1840da87 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -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. diff --git a/src/cargo/ops/cargo_rustc/layout.rs b/src/cargo/ops/cargo_rustc/layout.rs index 59025ed66..82397d3f5 100644 --- a/src/cargo/ops/cargo_rustc/layout.rs +++ b/src/cargo/ops/cargo_rustc/layout.rs @@ -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())) } } diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 5da63efe4..307743177 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -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, diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index c3c584ac3..ddf5c2b9f 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -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}; diff --git a/tests/test_cargo_compile_git_deps.rs b/tests/test_cargo_compile_git_deps.rs index 283113bac..879488900 100644 --- a/tests/test_cargo_compile_git_deps.rs +++ b/tests/test_cargo_compile_git_deps.rs @@ -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 {