diff --git a/src/bin/install.rs b/src/bin/install.rs index a0c000202fc..7ee781fc5a5 100644 --- a/src/bin/install.rs +++ b/src/bin/install.rs @@ -19,6 +19,7 @@ pub struct Options { flag_root: Option, flag_list: bool, flag_force: bool, + flag_reinstall: bool, flag_frozen: bool, flag_locked: bool, @@ -55,6 +56,7 @@ Specifying what crate to install: Build and install options: -h, --help Print this message -j N, --jobs N Number of parallel jobs, defaults to # of CPUs + -r, --reinstall Reinstall the package -f, --force Force overwriting existing crates or binaries --features FEATURES Space-separated list of features to activate --all-features Build all available features @@ -93,8 +95,9 @@ one of them, and if you'd rather install examples the `--example` argument can be used as well. By default cargo will refuse to overwrite existing binaries. The `--force` flag -enables overwriting existing binaries. Thus you can reinstall a crate with -`cargo install --force `. +enables overwriting existing binaries. Thus you can install new crate with +overwriting old one with `cargo install --force `. However, if you want +just to reinstall the crate, you `cargo install --reinstall `. As a special convenience, omitting the specification entirely will install the crate in the current directory. That is, `install` is equivalent to @@ -169,7 +172,7 @@ pub fn execute(options: Options, config: &mut Config) -> CliResult { if options.flag_list { ops::install_list(root, config)?; } else { - ops::install(root, krates, &source, vers, &compile_opts, options.flag_force)?; + ops::install(root, krates, &source, vers, &compile_opts, options.flag_force, options.flag_reinstall)?; } Ok(()) } diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index c85923e453b..34948824099 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -18,6 +18,8 @@ use util::{Config, internal}; use util::{Filesystem, FileLock}; use util::errors::{CargoResult, CargoResultExt}; +type Duplicates = BTreeMap>; + #[derive(Deserialize, Serialize)] #[serde(untagged)] enum CrateListing { @@ -57,13 +59,14 @@ pub fn install(root: Option<&str>, source_id: &SourceId, vers: Option<&str>, opts: &ops::CompileOptions, - force: bool) -> CargoResult<()> { + force: bool, + reinstall: bool) -> CargoResult<()> { let root = resolve_root(root, opts.config)?; let map = SourceConfigMap::new(opts.config)?; let (installed_anything, scheduled_error) = if krates.len() <= 1 { install_one(&root, &map, krates.into_iter().next(), source_id, vers, opts, - force, true)?; + force, reinstall, true)?; (true, false) } else { let mut succeeded = vec![]; @@ -73,7 +76,7 @@ pub fn install(root: Option<&str>, let root = root.clone(); let map = map.clone(); match install_one(&root, &map, Some(krate), source_id, vers, - opts, force, first) { + opts, force, reinstall, first) { Ok(()) => succeeded.push(krate), Err(e) => { ::handle_error(e, &mut opts.config.shell()); @@ -127,6 +130,7 @@ fn install_one(root: &Filesystem, vers: Option<&str>, opts: &ops::CompileOptions, force: bool, + reinstall: bool, is_first_install: bool) -> CargoResult<()> { let config = opts.config; @@ -179,7 +183,11 @@ fn install_one(root: &Filesystem, }; let pkg = ws.current()?; - config.shell().status("Installing", pkg)?; + if reinstall { + uninstall_one(root.to_owned(), pkg.name(), &Vec::default(), config)?; + } + + config.shell().status("Checking", pkg)?; // Preflight checks to check up front whether we'll overwrite something. // We have to check this again afterwards, but may as well avoid building @@ -191,6 +199,8 @@ fn install_one(root: &Filesystem, check_overwrites(&dst, pkg, &opts.filter, &list, force)?; } + config.shell().status("Installing", pkg)?; + let compile = ops::compile_ws(&ws, Some(source), opts, @@ -452,7 +462,7 @@ fn check_overwrites(dst: &Path, pkg: &Package, filter: &ops::CompileFilter, prev: &CrateListingV1, - force: bool) -> CargoResult>> { + force: bool) -> CargoResult { // If explicit --bin or --example flags were passed then those'll // get checked during cargo_compile, we only care about the "build // everything" case here @@ -480,7 +490,7 @@ fn check_overwrites(dst: &Path, fn find_duplicates(dst: &Path, pkg: &Package, filter: &ops::CompileFilter, - prev: &CrateListingV1) -> BTreeMap> { + prev: &CrateListingV1) -> Duplicates { let check = |name: String| { // Need to provide type, works around Rust Issue #93349 let name = format!("{}{}", name, env::consts::EXE_SUFFIX); @@ -513,7 +523,7 @@ fn find_duplicates(dst: &Path, all_bins.iter().chain(all_examples.iter()) .filter_map(|t| check(t.clone())) - .collect::>>() + .collect::() } } } diff --git a/tests/cargotest/support/mod.rs b/tests/cargotest/support/mod.rs index c513d57b16e..8c20ef1f7f1 100644 --- a/tests/cargotest/support/mod.rs +++ b/tests/cargotest/support/mod.rs @@ -890,6 +890,7 @@ fn substitute_macros(input: &str) -> String { ("[UPLOADING]", " Uploading"), ("[VERIFYING]", " Verifying"), ("[ARCHIVING]", " Archiving"), + ("[CHECKING]", " Checking"), ("[INSTALLING]", " Installing"), ("[REPLACING]", " Replacing"), ("[UNPACKING]", " Unpacking"), diff --git a/tests/directory.rs b/tests/directory.rs index 6dde10e97e8..ca85352f645 100644 --- a/tests/directory.rs +++ b/tests/directory.rs @@ -146,7 +146,8 @@ fn simple_install() { assert_that(cargo_process().arg("install").arg("bar"), execs().with_status(0).with_stderr( -" Installing bar v0.1.0 +" Checking bar v0.1.0 + Installing bar v0.1.0 Compiling foo v0.1.0 Compiling bar v0.1.0 Finished release [optimized] target(s) in [..] secs @@ -191,7 +192,8 @@ fn simple_install_fail() { assert_that(cargo_process().arg("install").arg("bar"), execs().with_status(101).with_stderr( -" Installing bar v0.1.0 +" Checking bar v0.1.0 + Installing bar v0.1.0 error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]` Caused by: @@ -240,7 +242,8 @@ fn install_without_feature_dep() { assert_that(cargo_process().arg("install").arg("bar"), execs().with_status(0).with_stderr( -" Installing bar v0.1.0 +" Checking bar v0.1.0 + Installing bar v0.1.0 Compiling foo v0.1.0 Compiling bar v0.1.0 Finished release [optimized] target(s) in [..] secs diff --git a/tests/install.rs b/tests/install.rs index 1ae6806c447..f20ea61303f 100644 --- a/tests/install.rs +++ b/tests/install.rs @@ -37,6 +37,7 @@ fn simple() { execs().with_status(0).with_stderr(&format!("\ [UPDATING] registry `[..]` [DOWNLOADING] foo v0.0.1 (registry [..]) +[CHECKING] foo v0.0.1 [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] release [optimized] target(s) in [..] @@ -63,11 +64,13 @@ fn multiple_pkgs() { execs().with_status(101).with_stderr(&format!("\ [UPDATING] registry `[..]` [DOWNLOADING] foo v0.0.1 (registry `file://[..]`) +[CHECKING] foo v0.0.1 [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] release [optimized] target(s) in [..] [INSTALLING] {home}[..]bin[..]foo[..] [DOWNLOADING] bar v0.0.2 (registry `file://[..]`) +[CHECKING] bar v0.0.2 [INSTALLING] bar v0.0.2 [COMPILING] bar v0.0.2 [FINISHED] release [optimized] target(s) in [..] @@ -102,6 +105,7 @@ fn pick_max_version() { execs().with_status(0).with_stderr(&format!("\ [UPDATING] registry `[..]` [DOWNLOADING] foo v0.0.2 (registry [..]) +[CHECKING] foo v0.0.2 [INSTALLING] foo v0.0.2 [COMPILING] foo v0.0.2 [FINISHED] release [optimized] target(s) in [..] @@ -212,7 +216,7 @@ fn install_path() { assert_that(cargo_home(), has_installed_exe("foo")); assert_that(cargo_process("install").arg("--path").arg(".").cwd(p.root()), execs().with_status(101).with_stderr("\ -[INSTALLING] foo v0.1.0 [..] +[CHECKING] foo v0.1.0 [..] [ERROR] binary `foo[..]` already exists in destination as part of `foo v0.1.0 [..]` Add --force to overwrite ")); @@ -378,7 +382,7 @@ fn no_binaries() { assert_that(cargo_process("install").arg("--path").arg(p.root()).arg("foo"), execs().with_status(101).with_stderr("\ -[INSTALLING] foo [..] +[CHECKING] foo [..] [ERROR] specified package has no binaries ")); } @@ -419,13 +423,60 @@ fn install_twice() { execs().with_status(0)); assert_that(cargo_process("install").arg("--path").arg(p.root()), execs().with_status(101).with_stderr("\ -[INSTALLING] foo v0.1.0 [..] +[CHECKING] foo v0.1.0 ([..]) [ERROR] binary `foo-bin1[..]` already exists in destination as part of `foo v0.1.0 ([..])` binary `foo-bin2[..]` already exists in destination as part of `foo v0.1.0 ([..])` Add --force to overwrite ")); } +#[test] +fn install_reinstall() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg("--reinstall").arg(p.root()), + execs().with_status(101)); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + + let p = project("foo2") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.2.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--reinstall").arg("--path").arg(p.root()), + execs().with_status(0).with_stderr(&format!("\ +[REMOVING] [..] +[CHECKING] foo v0.2.0 ([..]) +[INSTALLING] foo v0.2.0 ([..]) +[COMPILING] foo v0.2.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] {home}[..]bin[..]foo[..] +warning: be sure to add `[..]` to your PATH to be able to run the installed binaries +", + home = cargo_home().display()))); + + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ +foo v0.2.0 ([..]): + foo[..] +")); +} + #[test] fn install_force() { let p = project("foo") @@ -453,6 +504,7 @@ fn install_force() { assert_that(cargo_process("install").arg("--force").arg("--path").arg(p.root()), execs().with_status(0).with_stderr(&format!("\ +[CHECKING] foo v0.2.0 ([..]) [INSTALLING] foo v0.2.0 ([..]) [COMPILING] foo v0.2.0 ([..]) [FINISHED] release [optimized] target(s) in [..] @@ -497,6 +549,7 @@ fn install_force_partial_overlap() { assert_that(cargo_process("install").arg("--force").arg("--path").arg(p.root()), execs().with_status(0).with_stderr(&format!("\ +[CHECKING] foo v0.2.0 ([..]) [INSTALLING] foo v0.2.0 ([..]) [COMPILING] foo v0.2.0 ([..]) [FINISHED] release [optimized] target(s) in [..] @@ -549,6 +602,7 @@ fn install_force_bin() { .arg("--path") .arg(p.root()), execs().with_status(0).with_stderr(&format!("\ +[CHECKING] foo v0.2.0 ([..]) [INSTALLING] foo v0.2.0 ([..]) [COMPILING] foo v0.2.0 ([..]) [FINISHED] release [optimized] target(s) in [..] @@ -606,6 +660,7 @@ fn git_repo() { assert_that(cargo_process("install").arg("--locked").arg("--git").arg(p.url().to_string()), execs().with_status(0).with_stderr(&format!("\ [UPDATING] git repository `[..]` +[CHECKING] foo v0.1.0 ([..]) [INSTALLING] foo v0.1.0 ([..]) [COMPILING] foo v0.1.0 ([..]) [FINISHED] release [optimized] target(s) in [..] @@ -776,7 +831,9 @@ fn do_not_rebuilds_on_local_install() { assert_that(p.cargo("build").arg("--release"), execs().with_status(0)); assert_that(cargo_process("install").arg("--path").arg(p.root()), - execs().with_status(0).with_stderr("[INSTALLING] [..] + execs().with_status(0).with_stderr("\ +[CHECKING] [..] +[INSTALLING] [..] [FINISHED] release [optimized] target(s) in [..] [INSTALLING] [..] warning: be sure to add `[..]` to your PATH to be able to run the installed binaries diff --git a/tests/required-features.rs b/tests/required-features.rs index 233f7f6f501..6d2247c049c 100644 --- a/tests/required-features.rs +++ b/tests/required-features.rs @@ -552,6 +552,7 @@ fn install_default_features() { assert_that(p.cargo("install").arg("--no-default-features"), execs().with_status(101).with_stderr(format!("\ +[CHECKING] foo v0.0.1 ([..]) [INSTALLING] foo v0.0.1 ([..]) [FINISHED] release [optimized] target(s) in [..] [ERROR] no binaries are available for install using the selected features @@ -566,6 +567,7 @@ fn install_default_features() { assert_that(p.cargo("install").arg("--bin=foo").arg("--no-default-features"), execs().with_status(101).with_stderr(format!("\ +[CHECKING] foo v0.0.1 ([..]) [INSTALLING] foo v0.0.1 ([..]) [ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ `[..]target` @@ -584,6 +586,7 @@ Consider enabling them by passing e.g. `--features=\"a\"` assert_that(p.cargo("install").arg("--example=foo").arg("--no-default-features"), execs().with_status(101).with_stderr(format!("\ +[CHECKING] foo v0.0.1 ([..]) [INSTALLING] foo v0.0.1 ([..]) [ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ `[..]target` @@ -666,6 +669,7 @@ fn install_multiple_required_features() { assert_that(p.cargo("install").arg("--no-default-features"), execs().with_status(101).with_stderr("\ +[CHECKING] foo v0.0.1 ([..]) [INSTALLING] foo v0.0.1 ([..]) [FINISHED] release [optimized] target(s) in [..] [ERROR] no binaries are available for install using the selected features @@ -871,6 +875,7 @@ Consider enabling them by passing e.g. `--features=\"bar/a\"` // install assert_that(p.cargo("install"), execs().with_status(101).with_stderr(format!("\ +[CHECKING] foo v0.0.1 ([..]) [INSTALLING] foo v0.0.1 ([..]) [FINISHED] release [optimized] target(s) in [..] [ERROR] no binaries are available for install using the selected features