Skip to content

Commit 9855f92

Browse files
committed
Auto merge of #11835 - domsleee:git-install-bin-flag-hint, r=weihanglo
`cargo install --git` multiple packages with binaries found hint ### What does this PR try to resolve? The issues discussed in: #4830 namely this one: > 4. a multiple packages with binaries found error should give users a hint about how to specify a single crate I think improving the error message to provide a suggestion is a simple change that would help a lot of people when this happens, sorry if I'm out of line for just opening a PR here :) ### Before cargo 1.68.0 (115f345 2023-02-26) ![image](https://user-images.githubusercontent.com/14891742/224546157-d9b48bfd-f896-4fd1-9f0a-e04a3ad60ae2.png) ### After ![image](https://user-images.githubusercontent.com/14891742/224546182-d4b451ae-1b28-41b6-9d74-db860532512b.png) ### Additional information I added in a related test documenting existing behaviours * multiple_examples_error: "multiple packages with examples found" (i.e. not "binaries") I added these tests which aren't necessarily related to this PR, but could be considered if the behaviour were to change * `multiple_binaries_deep_select_uses_package_name`: it uses the name, not the path. Is this a problem for crates with the same name? * `multiple_binaries_in_selected_package_installs_all`: so `--bins` is implied when a package is specified? * `multiple_binaries_in_selected_package_with_bin_option_installs_only_one`: demonstrates a use case where `--bin` and `[crate]` are both used
2 parents d4249c9 + 3363931 commit 9855f92

File tree

2 files changed

+97
-12
lines changed

2 files changed

+97
-12
lines changed

src/cargo/ops/common_for_install_and_uninstall.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,10 @@ where
616616
let examples = candidates
617617
.iter()
618618
.filter(|cand| cand.targets().iter().filter(|t| t.is_example()).count() > 0);
619-
let pkg = match one(binaries, |v| multi_err("binaries", v))? {
619+
let git_url = source.source_id().url().to_string();
620+
let pkg = match one(binaries, |v| multi_err("binaries", &git_url, v))? {
620621
Some(p) => p,
621-
None => match one(examples, |v| multi_err("examples", v))? {
622+
None => match one(examples, |v| multi_err("examples", &git_url, v))? {
622623
Some(p) => p,
623624
None => bail!(
624625
"no packages found with binaries or \
@@ -629,17 +630,20 @@ where
629630
Ok(pkg.clone())
630631
};
631632

632-
fn multi_err(kind: &str, mut pkgs: Vec<&Package>) -> String {
633+
fn multi_err(kind: &str, git_url: &str, mut pkgs: Vec<&Package>) -> String {
633634
pkgs.sort_unstable_by_key(|a| a.name());
635+
let first_pkg = pkgs[0];
634636
format!(
635637
"multiple packages with {} found: {}. When installing a git repository, \
636-
cargo will always search the entire repo for any Cargo.toml. \
637-
Please specify which to install.",
638+
cargo will always search the entire repo for any Cargo.toml.\n\
639+
Please specify a package, e.g. `cargo install --git {} {}`.",
638640
kind,
639641
pkgs.iter()
640642
.map(|p| p.name().as_str())
641643
.collect::<Vec<_>>()
642-
.join(", ")
644+
.join(", "),
645+
git_url,
646+
first_pkg.name()
643647
)
644648
}
645649
}

tests/testsuite/install.rs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,28 +560,109 @@ Available binaries:
560560
}
561561

562562
#[cargo_test]
563-
fn multiple_crates_error() {
563+
fn multiple_packages_containing_binaries() {
564564
let p = git::repo(&paths::root().join("foo"))
565565
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
566566
.file("src/main.rs", "fn main() {}")
567567
.file("a/Cargo.toml", &basic_manifest("bar", "0.1.0"))
568568
.file("a/src/main.rs", "fn main() {}")
569569
.build();
570570

571+
let git_url = p.url().to_string();
571572
cargo_process("install --git")
572573
.arg(p.url().to_string())
573574
.with_status(101)
574-
.with_stderr(
575+
.with_stderr(format!(
575576
"\
576577
[UPDATING] git repository [..]
577578
[ERROR] multiple packages with binaries found: bar, foo. \
578-
When installing a git repository, cargo will always search the entire repo for any Cargo.toml. \
579-
Please specify which to install.
580-
",
581-
)
579+
When installing a git repository, cargo will always search the entire repo for any Cargo.toml.
580+
Please specify a package, e.g. `cargo install --git {git_url} bar`.
581+
"
582+
))
583+
.run();
584+
}
585+
586+
#[cargo_test]
587+
fn multiple_packages_matching_example() {
588+
let p = git::repo(&paths::root().join("foo"))
589+
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
590+
.file("src/lib.rs", "")
591+
.file("examples/ex1.rs", "fn main() {}")
592+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
593+
.file("bar/src/lib.rs", "")
594+
.file("bar/examples/ex1.rs", "fn main() {}")
595+
.build();
596+
597+
let git_url = p.url().to_string();
598+
cargo_process("install --example ex1 --git")
599+
.arg(p.url().to_string())
600+
.with_status(101)
601+
.with_stderr(format!(
602+
"\
603+
[UPDATING] git repository [..]
604+
[ERROR] multiple packages with examples found: bar, foo. \
605+
When installing a git repository, cargo will always search the entire repo for any Cargo.toml.
606+
Please specify a package, e.g. `cargo install --git {git_url} bar`."
607+
))
582608
.run();
583609
}
584610

611+
#[cargo_test]
612+
fn multiple_binaries_deep_select_uses_package_name() {
613+
let p = git::repo(&paths::root().join("foo"))
614+
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
615+
.file("src/main.rs", "fn main() {}")
616+
.file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
617+
.file("bar/baz/src/main.rs", "fn main() {}")
618+
.build();
619+
620+
cargo_process("install --git")
621+
.arg(p.url().to_string())
622+
.arg("baz")
623+
.run();
624+
}
625+
626+
#[cargo_test]
627+
fn multiple_binaries_in_selected_package_installs_all() {
628+
let p = git::repo(&paths::root().join("foo"))
629+
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
630+
.file("src/lib.rs", "")
631+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
632+
.file("bar/src/bin/bin1.rs", "fn main() {}")
633+
.file("bar/src/bin/bin2.rs", "fn main() {}")
634+
.build();
635+
636+
cargo_process("install --git")
637+
.arg(p.url().to_string())
638+
.arg("bar")
639+
.run();
640+
641+
let cargo_home = cargo_home();
642+
assert_has_installed_exe(&cargo_home, "bin1");
643+
assert_has_installed_exe(&cargo_home, "bin2");
644+
}
645+
646+
#[cargo_test]
647+
fn multiple_binaries_in_selected_package_with_bin_option_installs_only_one() {
648+
let p = git::repo(&paths::root().join("foo"))
649+
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
650+
.file("src/lib.rs", "")
651+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
652+
.file("bar/src/bin/bin1.rs", "fn main() {}")
653+
.file("bar/src/bin/bin2.rs", "fn main() {}")
654+
.build();
655+
656+
cargo_process("install --bin bin1 --git")
657+
.arg(p.url().to_string())
658+
.arg("bar")
659+
.run();
660+
661+
let cargo_home = cargo_home();
662+
assert_has_installed_exe(&cargo_home, "bin1");
663+
assert_has_not_installed_exe(&cargo_home, "bin2");
664+
}
665+
585666
#[cargo_test]
586667
fn multiple_crates_select() {
587668
let p = git::repo(&paths::root().join("foo"))

0 commit comments

Comments
 (0)