mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
Warn on missing required-features
Co-authored-by: Eric Huss <eric@huss.org>
This commit is contained in:
parent
a93b317273
commit
19a9579031
@ -34,7 +34,7 @@ use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
|
||||
use crate::core::profiles::{Profiles, UnitFor};
|
||||
use crate::core::resolver::features::{self, FeaturesFor};
|
||||
use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts};
|
||||
use crate::core::{Package, PackageSet, Target};
|
||||
use crate::core::{FeatureValue, Package, PackageSet, Shell, Summary, Target};
|
||||
use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
|
||||
use crate::ops;
|
||||
use crate::ops::resolve::WorkspaceResolve;
|
||||
@ -417,6 +417,7 @@ pub fn create_bcx<'a, 'cfg>(
|
||||
&build_config.requested_kinds,
|
||||
build_config.mode,
|
||||
&resolve,
|
||||
&workspace_resolve,
|
||||
&resolved_features,
|
||||
&pkg_set,
|
||||
&profiles,
|
||||
@ -701,6 +702,7 @@ fn generate_targets(
|
||||
requested_kinds: &[CompileKind],
|
||||
mode: CompileMode,
|
||||
resolve: &Resolve,
|
||||
workspace_resolve: &Option<Resolve>,
|
||||
resolved_features: &features::ResolvedFeatures,
|
||||
package_set: &PackageSet<'_>,
|
||||
profiles: &Profiles,
|
||||
@ -929,6 +931,13 @@ fn generate_targets(
|
||||
{
|
||||
let unavailable_features = match target.required_features() {
|
||||
Some(rf) => {
|
||||
warn_on_missing_features(
|
||||
workspace_resolve,
|
||||
rf,
|
||||
pkg.summary(),
|
||||
&mut config.shell(),
|
||||
)?;
|
||||
|
||||
let features = features_map.entry(pkg).or_insert_with(|| {
|
||||
resolve_all_features(resolve, resolved_features, package_set, pkg.package_id())
|
||||
});
|
||||
@ -958,6 +967,68 @@ fn generate_targets(
|
||||
Ok(units.into_iter().collect())
|
||||
}
|
||||
|
||||
fn warn_on_missing_features(
|
||||
resolve: &Option<Resolve>,
|
||||
required_features: &[String],
|
||||
summary: &Summary,
|
||||
shell: &mut Shell,
|
||||
) -> CargoResult<()> {
|
||||
let resolve = match resolve {
|
||||
None => return Ok(()),
|
||||
Some(resolve) => resolve,
|
||||
};
|
||||
|
||||
for feature in required_features {
|
||||
match FeatureValue::new(feature.into(), summary) {
|
||||
// No need to do anything here, since the feature must exist to be parsed as such
|
||||
FeatureValue::Feature(_) => {}
|
||||
// Possibly mislabeled feature that was not found
|
||||
FeatureValue::Crate(krate) => {
|
||||
if !summary
|
||||
.dependencies()
|
||||
.iter()
|
||||
.any(|dep| dep.name_in_toml() == krate && dep.is_optional())
|
||||
{
|
||||
shell.warn(format!(
|
||||
"feature `{}` is not present in [features] section.",
|
||||
krate
|
||||
))?;
|
||||
}
|
||||
}
|
||||
// Handling of dependent_crate/dependent_crate_feature syntax
|
||||
FeatureValue::CrateFeature(krate, feature) => {
|
||||
match resolve
|
||||
.deps(summary.package_id())
|
||||
.find(|(_dep_id, deps)| deps.iter().any(|dep| dep.name_in_toml() == krate))
|
||||
{
|
||||
Some((dep_id, _deps)) => {
|
||||
let dep_summary = resolve.summary(dep_id);
|
||||
if !dep_summary.features().contains_key(&feature)
|
||||
&& !dep_summary
|
||||
.dependencies()
|
||||
.iter()
|
||||
.any(|dep| dep.name_in_toml() == feature && dep.is_optional())
|
||||
{
|
||||
shell.warn(format!(
|
||||
"feature `{}` does not exist in package `{}`.",
|
||||
feature, dep_id
|
||||
))?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
shell.warn(format!(
|
||||
"dependency `{}` specified in required-features as `{}/{}` \
|
||||
does not exist.",
|
||||
krate, krate, feature
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets all of the features enabled for a package, plus its dependencies'
|
||||
/// features.
|
||||
///
|
||||
|
@ -595,6 +595,9 @@ fn bench_autodiscover_2015() {
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
edition = "2015"
|
||||
|
||||
[features]
|
||||
magic = []
|
||||
|
||||
[[bench]]
|
||||
name = "bench_magic"
|
||||
|
@ -291,7 +291,7 @@ fn invalid9() {
|
||||
.build();
|
||||
|
||||
p.cargo("build --features bar")
|
||||
.with_stderr(
|
||||
.with_stderr(
|
||||
"\
|
||||
error: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with that name, but only optional dependencies can be used as features.
|
||||
",
|
||||
@ -1514,14 +1514,14 @@ fn namespaced_shadowed_dep() {
|
||||
.build();
|
||||
|
||||
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
|
||||
"\
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]`
|
||||
|
||||
Caused by:
|
||||
Feature `baz` includes the optional dependency of the same name, but this is left implicit in the features included by this feature.
|
||||
Consider adding `crate:baz` to this feature's requirements.
|
||||
",
|
||||
)
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -1550,7 +1550,7 @@ fn namespaced_shadowed_non_optional() {
|
||||
.build();
|
||||
|
||||
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
|
||||
"\
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]`
|
||||
|
||||
Caused by:
|
||||
@ -1558,7 +1558,7 @@ Caused by:
|
||||
Additionally, the dependency must be marked as optional to be included in the feature definition.
|
||||
Consider adding `crate:baz` to this feature's requirements and marking the dependency as `optional = true`
|
||||
",
|
||||
)
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -1587,15 +1587,14 @@ fn namespaced_implicit_non_optional() {
|
||||
.build();
|
||||
|
||||
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
|
||||
"\
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]`
|
||||
|
||||
Caused by:
|
||||
Feature `bar` includes `baz` which is not defined as a feature.
|
||||
A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition
|
||||
",
|
||||
).run(
|
||||
);
|
||||
).run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
@ -2190,3 +2189,47 @@ fn registry_summary_order_doesnt_matter() {
|
||||
.with_stdout("it works")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn nonexistent_required_features() {
|
||||
Package::new("required_dependency", "0.1.0")
|
||||
.feature("simple", &[])
|
||||
.publish();
|
||||
Package::new("optional_dependency", "0.2.0")
|
||||
.feature("optional", &[])
|
||||
.publish();
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
[features]
|
||||
existing = []
|
||||
fancy = ["optional_dependency"]
|
||||
[dependencies]
|
||||
required_dependency = { version = "0.1", optional = false}
|
||||
optional_dependency = { version = "0.2", optional = true}
|
||||
[[example]]
|
||||
name = "ololo"
|
||||
required-features = ["not_present",
|
||||
"existing",
|
||||
"fancy",
|
||||
"required_dependency/not_existing",
|
||||
"required_dependency/simple",
|
||||
"optional_dependency/optional",
|
||||
"not_specified_dependency/some_feature"]"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file("examples/ololo.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build --examples")
|
||||
.with_stderr_contains(
|
||||
r#"warning: feature `not_present` is not present in [features] section.
|
||||
warning: feature `not_existing` does not exist in package `required_dependency v0.1.0`.
|
||||
warning: dependency `not_specified_dependency` specified in required-features as `not_specified_dependency/some_feature` does not exist."#,
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user