Skip to content

Commit

Permalink
Auto merge of #4561 - nossralf:uninstall-many, r=matklad
Browse files Browse the repository at this point in the history
Support uninstallation of multiple packages

This is a WIP pull request with support for uninstalling multiple packages. It mirrors the logic used for `cargo install`.

Fixes #4560
  • Loading branch information
bors committed Oct 30, 2017
2 parents 16d4f84 + 05224d6 commit 42e031d
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 9 deletions.
8 changes: 5 additions & 3 deletions src/bin/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ pub struct Options {
#[serde(rename = "flag_Z")]
flag_z: Vec<String>,

arg_spec: String,
arg_spec: Vec<String>,
}

pub const USAGE: &'static str = "
Remove a Rust binary
Usage:
cargo uninstall [options] <spec>
cargo uninstall [options] <spec>...
cargo uninstall (-h | --help)
Options:
Expand Down Expand Up @@ -49,7 +49,9 @@ pub fn execute(options: Options, config: &mut Config) -> CliResult {
&options.flag_z)?;

let root = options.flag_root.as_ref().map(|s| &s[..]);
ops::uninstall(root, &options.arg_spec, &options.flag_bin, config)?;
let specs = options.arg_spec.iter().map(|s| &s[..]).collect::<Vec<_>>();

ops::uninstall(root, specs, &options.flag_bin, config)?;
Ok(())
}

49 changes: 48 additions & 1 deletion src/cargo/ops/cargo_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,10 +557,57 @@ pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
}

pub fn uninstall(root: Option<&str>,
spec: &str,
specs: Vec<&str>,
bins: &[String],
config: &Config) -> CargoResult<()> {
if specs.len() > 1 && bins.len() > 0 {
bail!("A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.");
}

let root = resolve_root(root, config)?;
let scheduled_error = if specs.len() == 1 {
uninstall_one(root, specs[0], bins, config)?;
false
} else {
let mut succeeded = vec![];
let mut failed = vec![];
for spec in specs {
let root = root.clone();
match uninstall_one(root, spec, bins, config) {
Ok(()) => succeeded.push(spec),
Err(e) => {
::handle_error(e, &mut config.shell());
failed.push(spec)
}
}
}

let mut summary = vec![];
if !succeeded.is_empty() {
summary.push(format!("Successfully uninstalled {}!", succeeded.join(", ")));
}
if !failed.is_empty() {
summary.push(format!("Failed to uninstall {} (see error(s) above).", failed.join(", ")));
}

if !succeeded.is_empty() || !failed.is_empty() {
config.shell().status("\nSummary:", summary.join(" "))?;
}

!failed.is_empty()
};

if scheduled_error {
bail!("some packages failed to uninstall");
}

Ok(())
}

pub fn uninstall_one(root: Filesystem,
spec: &str,
bins: &[String],
config: &Config) -> CargoResult<()> {
let crate_metadata = metadata(config, &root)?;
let mut metadata = read_crate_list(&crate_metadata)?;
let mut to_remove = Vec::new();
Expand Down
38 changes: 33 additions & 5 deletions tests/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,15 @@ error: some crates failed to install
assert_that(cargo_home(), has_installed_exe("foo"));
assert_that(cargo_home(), has_installed_exe("bar"));

assert_that(cargo_process("uninstall").arg("foo"),
assert_that(cargo_process("uninstall").args(&["foo", "bar"]),
execs().with_status(0).with_stderr(&format!("\
[REMOVING] {home}[..]bin[..]foo[..]
",
home = cargo_home().display())));
assert_that(cargo_process("uninstall").arg("bar"),
execs().with_status(0).with_stderr(&format!("\
[REMOVING] {home}[..]bin[..]bar[..]
Summary: Successfully uninstalled foo, bar!
",
home = cargo_home().display())));

assert_that(cargo_home(), is_not(has_installed_exe("foo")));
assert_that(cargo_home(), is_not(has_installed_exe("bar")));
}
Expand Down Expand Up @@ -963,3 +962,32 @@ fn test_install_git_cannot_be_a_base_url() {
error: invalid url `github.com:rust-lang-nursery/rustfmt.git`: cannot-be-a-base-URLs are not supported
"));
}

#[test]
fn uninstall_multiple_and_specifying_bin() {
assert_that(cargo_process("uninstall").args(&["foo", "bar"]).arg("--bin").arg("baz"),
execs().with_status(101).with_stderr("\
error: A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.
"));
}

#[test]
fn uninstall_multiple_and_some_pkg_does_not_exist() {
pkg("foo", "0.0.1");

assert_that(cargo_process("install").arg("foo"),
execs().with_status(0));

assert_that(cargo_process("uninstall").args(&["foo", "bar"]),
execs().with_status(101).with_stderr(&format!("\
[REMOVING] {home}[..]bin[..]foo[..]
error: package id specification `bar` matched no packages
Summary: Successfully uninstalled foo! Failed to uninstall bar (see error(s) above).
error: some packages failed to uninstall
",
home = cargo_home().display())));

assert_that(cargo_home(), is_not(has_installed_exe("foo")));
assert_that(cargo_home(), is_not(has_installed_exe("bar")));
}

0 comments on commit 42e031d

Please sign in to comment.