Skip to content

Commit a9645e1

Browse files
author
Jon Gjengset
committed
Don't error on no binaries installed
If we interpret `cargo install` and `cargo install --bins` as "install the binaries that are available", it should not be considered an error if no binaries ended up being installed due to required features. Instead, this should provide the user with a warning that this may not have been what they intended. Fixes #10289.
1 parent b1636fc commit a9645e1

File tree

2 files changed

+50
-15
lines changed

2 files changed

+50
-15
lines changed

src/cargo/ops/cargo_install.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::{env, fs};
55

66
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, Freshness, UnitOutput};
77
use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Workspace};
8-
use crate::ops::common_for_install_and_uninstall::*;
8+
use crate::ops::CompileFilter;
9+
use crate::ops::{common_for_install_and_uninstall::*, FilterRule};
910
use crate::sources::{GitSource, PathSource, SourceConfigMap};
1011
use crate::util::errors::CargoResult;
1112
use crate::util::{Config, Filesystem, Rustc, ToSemver, VersionReqExt};
@@ -272,7 +273,7 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
272273
Ok(duplicates)
273274
}
274275

275-
fn install_one(mut self) -> CargoResult<()> {
276+
fn install_one(mut self) -> CargoResult<bool> {
276277
self.config.shell().status("Installing", &self.pkg)?;
277278

278279
let dst = self.root.join("bin").into_path_unlocked();
@@ -322,7 +323,41 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
322323
})
323324
.collect::<CargoResult<_>>()?;
324325
if binaries.is_empty() {
325-
bail!("no binaries are available for install using the selected features");
326+
// Cargo already warns the user if they use a target specifier that matches nothing,
327+
// but we want to error if the user asked for a _particular_ binary to be installed,
328+
// and we didn't end up installing it.
329+
//
330+
// NOTE: This _should_ be impossible to hit since --bin=does_not_exist will fail on
331+
// target selection, and --bin=requires_a without --features=a will fail with "target
332+
// .. requires the features ..". But rather than assume that's the case, we define the
333+
// behavior for this fallback case as well.
334+
if matches!(
335+
self.opts.filter,
336+
CompileFilter::Only {
337+
bins: FilterRule::Just(..),
338+
..
339+
}
340+
) {
341+
bail!("no binaries are available for install using the selected features");
342+
}
343+
344+
// If the user did not specify a filter and there _are_ binaries available, but none
345+
// were selected given the current set of features, let the user know.
346+
if matches!(
347+
self.opts.filter,
348+
CompileFilter::Default { .. }
349+
| CompileFilter::Only {
350+
bins: FilterRule::All,
351+
..
352+
}
353+
) && self.pkg.targets().iter().any(|t| t.is_bin())
354+
{
355+
self.config
356+
.shell()
357+
.warn("none of the package's binaries are available for install using the selected features")?;
358+
}
359+
360+
return Ok(false);
326361
}
327362
// This is primarily to make testing easier.
328363
binaries.sort_unstable();
@@ -455,7 +490,7 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
455490
executables(successful_bins.iter())
456491
),
457492
)?;
458-
Ok(())
493+
Ok(true)
459494
} else {
460495
if !to_install.is_empty() {
461496
self.config.shell().status(
@@ -481,7 +516,7 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
481516
),
482517
)?;
483518
}
484-
Ok(())
519+
Ok(true)
485520
}
486521
}
487522

@@ -545,10 +580,11 @@ pub fn install(
545580
no_track,
546581
true,
547582
)?;
583+
let mut installed_anything = true;
548584
if let Some(installable_pkg) = installable_pkg {
549-
installable_pkg.install_one()?;
585+
installed_anything = installable_pkg.install_one()?;
550586
}
551-
(true, false)
587+
(installed_anything, false)
552588
} else {
553589
let mut succeeded = vec![];
554590
let mut failed = vec![];
@@ -601,8 +637,10 @@ pub fn install(
601637

602638
for (krate, result) in install_results {
603639
match result {
604-
Ok(()) => {
605-
succeeded.push(krate);
640+
Ok(installed) => {
641+
if installed {
642+
succeeded.push(krate);
643+
}
606644
}
607645
Err(e) => {
608646
crate::display_error(&e, &mut config.shell());

tests/testsuite/required_features.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -650,12 +650,11 @@ fn install_default_features() {
650650
p.cargo("uninstall foo").run();
651651

652652
p.cargo("install --path . --no-default-features")
653-
.with_status(101)
654653
.with_stderr(
655654
"\
656655
[INSTALLING] foo v0.0.1 ([..])
657656
[FINISHED] release [optimized] target(s) in [..]
658-
[ERROR] no binaries are available for install using the selected features
657+
[WARNING] none of the package's binaries are available for install using the selected features
659658
",
660659
)
661660
.run();
@@ -772,12 +771,11 @@ fn install_multiple_required_features() {
772771
p.cargo("uninstall foo").run();
773772

774773
p.cargo("install --path . --no-default-features")
775-
.with_status(101)
776774
.with_stderr(
777775
"\
778776
[INSTALLING] foo v0.0.1 ([..])
779777
[FINISHED] release [optimized] target(s) in [..]
780-
[ERROR] no binaries are available for install using the selected features
778+
[WARNING] none of the package's binaries are available for install using the selected features
781779
",
782780
)
783781
.run();
@@ -1029,12 +1027,11 @@ Consider enabling them by passing, e.g., `--features=\"bar/a\"`
10291027

10301028
// install
10311029
p.cargo("install --path .")
1032-
.with_status(101)
10331030
.with_stderr(
10341031
"\
10351032
[INSTALLING] foo v0.0.1 ([..])
10361033
[FINISHED] release [optimized] target(s) in [..]
1037-
[ERROR] no binaries are available for install using the selected features
1034+
[WARNING] none of the package's binaries are available for install using the selected features
10381035
",
10391036
)
10401037
.run();

0 commit comments

Comments
 (0)