Skip to content

Commit 2c30ebe

Browse files
committed
Auto merge of #11647 - peterallin:peterallin/11617, r=weihanglo
Make cargo install report needed features ### What does this PR try to resolve? The problem described in issue #11617 where cargo tells the user, that no binaries are available because of the chosen features, but does not tell which features could be enabled to fix the situation. Fixes #11617. ### How should we test and review this PR? Try to cargo install a crate, where all binaries need some features enabled, without enabling the features and check the error message. The test suite already contains tests for this.
2 parents bf096ca + f92f42f commit 2c30ebe

File tree

2 files changed

+166
-3
lines changed

2 files changed

+166
-3
lines changed

Diff for: src/cargo/ops/cargo_install.rs

+49-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::Arc;
44
use std::{env, fs};
55

66
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, UnitOutput};
7-
use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Workspace};
7+
use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Target, Workspace};
88
use crate::ops::{common_for_install_and_uninstall::*, FilterRule};
99
use crate::ops::{CompileFilter, Packages};
1010
use crate::sources::{GitSource, PathSource, SourceConfigMap};
@@ -14,6 +14,7 @@ use crate::{drop_println, ops};
1414

1515
use anyhow::{bail, format_err, Context as _};
1616
use cargo_util::paths;
17+
use itertools::Itertools;
1718
use semver::VersionReq;
1819
use tempfile::Builder as TempFileBuilder;
1920

@@ -360,10 +361,16 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
360361
//
361362
// Note that we know at this point that _if_ bins or examples is set to `::Just`,
362363
// they're `::Just([])`, which is `FilterRule::none()`.
363-
if self.pkg.targets().iter().any(|t| t.is_executable()) {
364+
let binaries: Vec<_> = self
365+
.pkg
366+
.targets()
367+
.iter()
368+
.filter(|t| t.is_executable())
369+
.collect();
370+
if !binaries.is_empty() {
364371
self.config
365372
.shell()
366-
.warn("none of the package's binaries are available for install using the selected features")?;
373+
.warn(make_warning_about_missing_features(&binaries))?;
367374
}
368375

369376
return Ok(false);
@@ -546,6 +553,45 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
546553
}
547554
}
548555

556+
fn make_warning_about_missing_features(binaries: &[&Target]) -> String {
557+
let max_targets_listed = 7;
558+
let target_features_message = binaries
559+
.iter()
560+
.take(max_targets_listed)
561+
.map(|b| {
562+
let name = b.description_named();
563+
let features = b
564+
.required_features()
565+
.unwrap_or(&Vec::new())
566+
.iter()
567+
.map(|f| format!("`{f}`"))
568+
.join(", ");
569+
format!(" {name} requires the features: {features}")
570+
})
571+
.join("\n");
572+
573+
let additional_bins_message = if binaries.len() > max_targets_listed {
574+
format!(
575+
"\n{} more targets also requires features not enabled. See them in the Cargo.toml file.",
576+
binaries.len() - max_targets_listed
577+
)
578+
} else {
579+
"".into()
580+
};
581+
582+
let example_features = binaries[0]
583+
.required_features()
584+
.map(|f| f.join(" "))
585+
.unwrap_or_default();
586+
587+
format!(
588+
"\
589+
none of the package's binaries are available for install using the selected features
590+
{target_features_message}{additional_bins_message}
591+
Consider enabling some of the needed features by passing, e.g., `--features=\"{example_features}\"`"
592+
)
593+
}
594+
549595
pub fn install(
550596
config: &Config,
551597
root: Option<&str>,

Diff for: tests/testsuite/required_features.rs

+117
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ fn install_default_features() {
640640
[INSTALLING] foo v0.0.1 ([..])
641641
[FINISHED] release [optimized] target(s) in [..]
642642
[WARNING] none of the package's binaries are available for install using the selected features
643+
bin \"foo\" requires the features: `a`
644+
example \"foo\" requires the features: `a`
645+
Consider enabling some of the needed features by passing, e.g., `--features=\"a\"`
643646
",
644647
)
645648
.run();
@@ -792,6 +795,11 @@ fn install_multiple_required_features() {
792795
[INSTALLING] foo v0.0.1 ([..])
793796
[FINISHED] release [optimized] target(s) in [..]
794797
[WARNING] none of the package's binaries are available for install using the selected features
798+
bin \"foo_1\" requires the features: `b`, `c`
799+
bin \"foo_2\" requires the features: `a`
800+
example \"foo_3\" requires the features: `b`, `c`
801+
example \"foo_4\" requires the features: `a`
802+
Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"`
795803
",
796804
)
797805
.run();
@@ -802,6 +810,11 @@ fn install_multiple_required_features() {
802810
[WARNING] Target filter `bins` specified, but no targets matched. This is a no-op
803811
[FINISHED] release [optimized] target(s) in [..]
804812
[WARNING] none of the package's binaries are available for install using the selected features
813+
bin \"foo_1\" requires the features: `b`, `c`
814+
bin \"foo_2\" requires the features: `a`
815+
example \"foo_3\" requires the features: `b`, `c`
816+
example \"foo_4\" requires the features: `a`
817+
Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"`
805818
",
806819
)
807820
.run();
@@ -812,6 +825,11 @@ fn install_multiple_required_features() {
812825
[WARNING] Target filter `examples` specified, but no targets matched. This is a no-op
813826
[FINISHED] release [optimized] target(s) in [..]
814827
[WARNING] none of the package's binaries are available for install using the selected features
828+
bin \"foo_1\" requires the features: `b`, `c`
829+
bin \"foo_2\" requires the features: `a`
830+
example \"foo_3\" requires the features: `b`, `c`
831+
example \"foo_4\" requires the features: `a`
832+
Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"`
815833
",
816834
)
817835
.run();
@@ -822,6 +840,11 @@ fn install_multiple_required_features() {
822840
[WARNING] Target filters `bins`, `examples` specified, but no targets matched. This is a no-op
823841
[FINISHED] release [optimized] target(s) in [..]
824842
[WARNING] none of the package's binaries are available for install using the selected features
843+
bin \"foo_1\" requires the features: `b`, `c`
844+
bin \"foo_2\" requires the features: `a`
845+
example \"foo_3\" requires the features: `b`, `c`
846+
example \"foo_4\" requires the features: `a`
847+
Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"`
825848
",
826849
)
827850
.run();
@@ -1080,6 +1103,9 @@ Consider enabling them by passing, e.g., `--features=\"bar/a\"`
10801103
[INSTALLING] foo v0.0.1 ([..])
10811104
[FINISHED] release [optimized] target(s) in [..]
10821105
[WARNING] none of the package's binaries are available for install using the selected features
1106+
bin \"foo\" requires the features: `bar/a`
1107+
example \"foo\" requires the features: `bar/a`
1108+
Consider enabling some of the needed features by passing, e.g., `--features=\"bar/a\"`
10831109
",
10841110
)
10851111
.run();
@@ -1333,3 +1359,94 @@ Consider enabling them by passing, e.g., `--features=\"a1/f1\"`
13331359
.with_stdout("a1 f1\na2 f2")
13341360
.run();
13351361
}
1362+
1363+
#[cargo_test]
1364+
fn truncated_install_warning_message() {
1365+
let p = project()
1366+
.file(
1367+
"Cargo.toml",
1368+
r#"
1369+
[package]
1370+
name = "foo"
1371+
version = "0.1.0"
1372+
edition = "2021"
1373+
1374+
[features]
1375+
feature1 = []
1376+
feature2 = []
1377+
feature3 = []
1378+
feature4 = []
1379+
feature5 = []
1380+
1381+
[[bin]]
1382+
name = "foo1"
1383+
required-features = ["feature1", "feature2", "feature3"]
1384+
1385+
[[bin]]
1386+
name = "foo2"
1387+
required-features = ["feature2"]
1388+
1389+
[[bin]]
1390+
name = "foo3"
1391+
required-features = ["feature3"]
1392+
1393+
[[bin]]
1394+
name = "foo4"
1395+
required-features = ["feature4", "feature1"]
1396+
1397+
[[bin]]
1398+
name = "foo5"
1399+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1400+
1401+
[[bin]]
1402+
name = "foo6"
1403+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1404+
1405+
[[bin]]
1406+
name = "foo7"
1407+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1408+
1409+
[[bin]]
1410+
name = "foo8"
1411+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1412+
1413+
[[bin]]
1414+
name = "foo9"
1415+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1416+
1417+
[[bin]]
1418+
name = "foo10"
1419+
required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"]
1420+
1421+
[[example]]
1422+
name = "example1"
1423+
required-features = ["feature1", "feature2"]
1424+
"#,
1425+
)
1426+
.file("src/bin/foo1.rs", "fn main() {}")
1427+
.file("src/bin/foo2.rs", "fn main() {}")
1428+
.file("src/bin/foo3.rs", "fn main() {}")
1429+
.file("src/bin/foo4.rs", "fn main() {}")
1430+
.file("src/bin/foo5.rs", "fn main() {}")
1431+
.file("src/bin/foo6.rs", "fn main() {}")
1432+
.file("src/bin/foo7.rs", "fn main() {}")
1433+
.file("src/bin/foo8.rs", "fn main() {}")
1434+
.file("src/bin/foo9.rs", "fn main() {}")
1435+
.file("src/bin/foo10.rs", "fn main() {}")
1436+
.file("examples/example1.rs", "fn main() {}")
1437+
.build();
1438+
1439+
p.cargo("install --path .").with_stderr("\
1440+
[INSTALLING] foo v0.1.0 ([..])
1441+
[FINISHED] release [optimized] target(s) in [..]
1442+
[WARNING] none of the package's binaries are available for install using the selected features
1443+
bin \"foo1\" requires the features: `feature1`, `feature2`, `feature3`
1444+
bin \"foo2\" requires the features: `feature2`
1445+
bin \"foo3\" requires the features: `feature3`
1446+
bin \"foo4\" requires the features: `feature4`, `feature1`
1447+
bin \"foo5\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5`
1448+
bin \"foo6\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5`
1449+
bin \"foo7\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5`
1450+
4 more targets also requires features not enabled. See them in the Cargo.toml file.
1451+
Consider enabling some of the needed features by passing, e.g., `--features=\"feature1 feature2 feature3\"`").run();
1452+
}

0 commit comments

Comments
 (0)