mirror of
https://github.com/rust-lang/cargo
synced 2024-10-14 19:52:45 +00:00
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:
parent
aa42fe5d43
commit
325c5f2def
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue