diff --git a/src/cargo/ops/common_for_install_and_uninstall.rs b/src/cargo/ops/common_for_install_and_uninstall.rs index 81083c7fbf8..f33847d5767 100644 --- a/src/cargo/ops/common_for_install_and_uninstall.rs +++ b/src/cargo/ops/common_for_install_and_uninstall.rs @@ -616,9 +616,10 @@ where let examples = candidates .iter() .filter(|cand| cand.targets().iter().filter(|t| t.is_example()).count() > 0); - let pkg = match one(binaries, |v| multi_err("binaries", v))? { + let git_url = source.source_id().url().to_string(); + let pkg = match one(binaries, |v| multi_err("binaries", &git_url, v))? { Some(p) => p, - None => match one(examples, |v| multi_err("examples", v))? { + None => match one(examples, |v| multi_err("examples", &git_url, v))? { Some(p) => p, None => bail!( "no packages found with binaries or \ @@ -629,17 +630,20 @@ where Ok(pkg.clone()) }; - fn multi_err(kind: &str, mut pkgs: Vec<&Package>) -> String { + fn multi_err(kind: &str, git_url: &str, mut pkgs: Vec<&Package>) -> String { pkgs.sort_unstable_by_key(|a| a.name()); + let first_pkg = pkgs[0]; format!( "multiple packages with {} found: {}. When installing a git repository, \ - cargo will always search the entire repo for any Cargo.toml. \ - Please specify which to install.", + cargo will always search the entire repo for any Cargo.toml.\n\ + Please specify a package, e.g. `cargo install --git {} {}`.", kind, pkgs.iter() .map(|p| p.name().as_str()) .collect::>() - .join(", ") + .join(", "), + git_url, + first_pkg.name() ) } } diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs index 48deb05ead6..dd9844f170b 100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@ -560,7 +560,7 @@ Available binaries: } #[cargo_test] -fn multiple_crates_error() { +fn multiple_packages_containing_binaries() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") @@ -568,20 +568,101 @@ fn multiple_crates_error() { .file("a/src/main.rs", "fn main() {}") .build(); + let git_url = p.url().to_string(); cargo_process("install --git") .arg(p.url().to_string()) .with_status(101) - .with_stderr( + .with_stderr(format!( "\ [UPDATING] git repository [..] [ERROR] multiple packages with binaries found: bar, foo. \ -When installing a git repository, cargo will always search the entire repo for any Cargo.toml. \ -Please specify which to install. -", - ) +When installing a git repository, cargo will always search the entire repo for any Cargo.toml. +Please specify a package, e.g. `cargo install --git {git_url} bar`. +" + )) + .run(); +} + +#[cargo_test] +fn multiple_packages_matching_example() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .file("examples/ex1.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .file("bar/examples/ex1.rs", "fn main() {}") + .build(); + + let git_url = p.url().to_string(); + cargo_process("install --example ex1 --git") + .arg(p.url().to_string()) + .with_status(101) + .with_stderr(format!( + "\ +[UPDATING] git repository [..] +[ERROR] multiple packages with examples found: bar, foo. \ +When installing a git repository, cargo will always search the entire repo for any Cargo.toml. +Please specify a package, e.g. `cargo install --git {git_url} bar`." + )) .run(); } +#[cargo_test] +fn multiple_binaries_deep_select_uses_package_name() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .arg("baz") + .run(); +} + +#[cargo_test] +fn multiple_binaries_in_selected_package_installs_all() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/bin/bin1.rs", "fn main() {}") + .file("bar/src/bin/bin2.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .arg("bar") + .run(); + + let cargo_home = cargo_home(); + assert_has_installed_exe(&cargo_home, "bin1"); + assert_has_installed_exe(&cargo_home, "bin2"); +} + +#[cargo_test] +fn multiple_binaries_in_selected_package_with_bin_option_installs_only_one() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/bin/bin1.rs", "fn main() {}") + .file("bar/src/bin/bin2.rs", "fn main() {}") + .build(); + + cargo_process("install --bin bin1 --git") + .arg(p.url().to_string()) + .arg("bar") + .run(); + + let cargo_home = cargo_home(); + assert_has_installed_exe(&cargo_home, "bin1"); + assert_has_not_installed_exe(&cargo_home, "bin2"); +} + #[cargo_test] fn multiple_crates_select() { let p = git::repo(&paths::root().join("foo"))