&mut self,
package: InternedString,
feat: InternedString,
- dep_prefix: bool,
+ weak: bool,
) -> Result<(), RequirementError> {
// If `package` is indeed an optional dependency then we activate the
// feature named `package`, but otherwise if `package` is a required
// dependency then there's no feature associated with it.
- if !dep_prefix
+ if !weak
&& self
.summary
.dependencies()
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix,
// Weak features are always activated in the dependency
// resolver. They will be narrowed inside the new feature
// resolver.
- weak: _,
- } => self.require_dep_feature(*dep_name, *dep_feature, *dep_prefix)?,
+ weak,
+ } => self.require_dep_feature(*dep_name, *dep_feature, *weak)?,
};
Ok(())
}
match feature {
// Maybe call validate_feature_name here once it is an error?
FeatureValue::Feature(_) => {}
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true, ..
- } => {
+ FeatureValue::Dep { .. } => {
bail!(
"feature `{}` is not allowed to use explicit `dep:` syntax",
feature
///
/// The key is the `(package, for_host, dep_name)` of the package whose
/// dependency will trigger the addition of new features. The value is the
- /// set of `(feature, dep_prefix)` features to activate (`dep_prefix` is a
- /// bool that indicates if `dep:` prefix was used).
- deferred_weak_dependencies:
- HashMap<(PackageId, bool, InternedString), HashSet<(InternedString, bool)>>,
+ /// set of features to activate.
+ deferred_weak_dependencies: HashMap<(PackageId, bool, InternedString), HashSet<InternedString>>,
}
impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix,
weak,
} => {
- self.activate_dep_feature(
- pkg_id,
- for_host,
- *dep_name,
- *dep_feature,
- *dep_prefix,
- *weak,
- )?;
+ self.activate_dep_feature(pkg_id, for_host, *dep_name, *dep_feature, *weak)?;
}
}
Ok(())
continue;
}
if let Some(to_enable) = &to_enable {
- for (dep_feature, dep_prefix) in to_enable {
+ for dep_feature in to_enable {
log::trace!(
"activate deferred {} {} -> {}/{}",
pkg_id.name(),
dep_name,
dep_feature
);
- if !dep_prefix {
- self.activate_rec(pkg_id, for_host, dep_name)?;
- }
let fv = FeatureValue::new(*dep_feature);
self.activate_fv(dep_pkg_id, dep_for_host, &fv)?;
}
for_host: bool,
dep_name: InternedString,
dep_feature: InternedString,
- dep_prefix: bool,
weak: bool,
) -> CargoResult<()> {
for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
self.deferred_weak_dependencies
.entry((pkg_id, for_host, dep_name))
.or_default()
- .insert((dep_feature, dep_prefix));
+ .insert(dep_feature);
continue;
}
// Activate the dependency on self.
let fv = FeatureValue::Dep { dep_name };
self.activate_fv(pkg_id, for_host, &fv)?;
- if !dep_prefix {
- // To retain compatibility with old behavior,
- // this also enables a feature of the same
+ if !weak {
+ // The old behavior before weak dependencies were
+ // added is to also enables a feature of the same
// name.
self.activate_rec(pkg_id, for_host, dep_name)?;
}
.values()
.flatten()
.filter_map(|fv| match fv {
- Dep { dep_name }
- | DepFeature {
- dep_name,
- dep_prefix: true,
- ..
- } => Some(*dep_name),
+ Dep { dep_name } => Some(*dep_name),
_ => None,
})
.collect();
DepFeature {
dep_name: InternedString,
dep_feature: InternedString,
- /// If this is true, then the feature used the `dep:` prefix, which
- /// prevents enabling the feature named `dep_name`.
- dep_prefix: bool,
/// If `true`, indicates the `?` syntax is used, which means this will
/// not automatically enable the dependency unless the dependency is
/// activated through some other means.
Some(pos) => {
let (dep, dep_feat) = feature.split_at(pos);
let dep_feat = &dep_feat[1..];
- let (dep, dep_prefix) = if let Some(dep) = dep.strip_prefix("dep:") {
- (dep, true)
- } else {
- (dep, false)
- };
let (dep, weak) = if let Some(dep) = dep.strip_suffix('?') {
(dep, true)
} else {
FeatureValue::DepFeature {
dep_name: InternedString::new(dep),
dep_feature: InternedString::new(dep_feat),
- dep_prefix,
weak,
}
}
/// Returns `true` if this feature explicitly used `dep:` syntax.
pub fn has_dep_prefix(&self) -> bool {
- matches!(
- self,
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true,
- ..
- }
- )
+ matches!(self, FeatureValue::Dep { .. })
}
}
DepFeature {
dep_name,
dep_feature,
- dep_prefix,
weak,
} => {
- let dep_prefix = if *dep_prefix { "dep:" } else { "" };
let weak = if *weak { "?" } else { "" };
- write!(f, "{}{}{}/{}", dep_prefix, dep_name, weak, dep_feature)
+ write!(f, "{}{}/{}", dep_name, weak, dep_feature)
}
}
}
}
}
// This should be enforced by CliFeatures.
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true, ..
- } => panic!("unexpected dep: syntax {}", feature),
+ FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix: _,
weak: _,
} => {
if dependencies.contains_key(dep_name) {
.map(|s| s.to_string())
.collect::<Vec<_>>()
}
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true, ..
- } => panic!("unexpected dep: syntax {}", feature),
+ FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix: _,
weak: _,
} => {
// Finds set of `pkg/feat` that are very similar to current `pkg/feat`.
cwd_features.insert(feature.clone());
}
// This should be enforced by CliFeatures.
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true, ..
- } => panic!("unexpected dep: syntax {}", feature),
+ FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix: _,
weak: _,
} => {
// I think weak can be ignored here.
))?;
}
}
- FeatureValue::Dep { .. }
- | FeatureValue::DepFeature {
- dep_prefix: true, ..
- } => {
+ FeatureValue::Dep { .. } => {
anyhow::bail!(
"invalid feature `{}` in required-features of target `{}`: \
`dep:` prefixed feature values are not allowed in required-features",
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix: false,
weak: false,
} => {
match resolve
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix: _,
weak,
} => {
let dep_connections = match graph.dep_name_map[&package_index].get(&dep_name) {
FeatureValue::DepFeature {
dep_name,
dep_feature,
- dep_prefix,
- // `weak` is ignored, because it will be skipped if the
- // corresponding dependency is not found in the map, which
- // means it wasn't activated. Skipping is handled by
- // `is_dep_activated` when the graph was built.
- weak: _,
+ // Note: `weak` is mostly handled when the graph is built in
+ // `is_dep_activated` which is responsible for skipping
+ // unactivated weak dependencies. Here it is only used to
+ // determine if the feature of the dependency name is
+ // activated on self.
+ weak,
} => {
let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
Some(indexes) => indexes.clone(),
};
for (dep_index, is_optional) in dep_indexes {
let dep_pkg_id = graph.package_id_for_index(dep_index);
- if is_optional && !dep_prefix {
+ if is_optional && !weak {
// Activate the optional dep on self.
add_feature(
graph,
.run();
}
-#[cargo_test]
-fn crate_feature_explicit() {
- // dep:name/feature syntax shouldn't set implicit feature.
- Package::new("bar", "1.0.0")
- .file(
- "src/lib.rs",
- r#"
- #[cfg(not(feature="feat"))]
- compile_error!{"feat missing"}
- "#,
- )
- .feature("feat", &[])
- .publish();
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
-
- [dependencies]
- bar = {version = "1.0", optional=true}
-
- [features]
- f1 = ["dep:bar/feat"]
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #[cfg(not(feature="f1"))]
- compile_error!{"f1 missing"}
-
- #[cfg(feature="bar")]
- compile_error!{"bar should not be set"}
- "#,
- )
- .build();
-
- p.cargo("check -Z namespaced-features --features f1")
- .masquerade_as_nightly_cargo()
- .with_stderr(
- "\
-[UPDATING] [..]
-[DOWNLOADING] crates ...
-[DOWNLOADED] bar v1.0.0 [..]
-[CHECKING] bar v1.0.0
-[CHECKING] foo v0.1.0 [..]
-[FINISHED] [..]
-",
- )
- .run();
-}
-
#[cargo_test]
fn crate_syntax_bad_name() {
// "dep:bar" = []
[features]
a = ["bar/feat2"]
- b = ["dep:bar/feat2"]
bar = ["dep:bar"]
"#,
)
)
.run();
- p.cargo("tree -e features --features b -Z namespaced-features")
- .masquerade_as_nightly_cargo()
- .with_stdout(
- "\
-foo v0.1.0 ([ROOT]/foo)
-├── bar feature \"default\"
-│ └── bar v1.0.0
-│ └── baz feature \"default\"
-│ └── baz v1.0.0
-└── bar feature \"feat1\"
- └── bar v1.0.0 (*)
-",
- )
- .run();
-
- p.cargo("tree -e features --features b -i bar -Z namespaced-features")
- .masquerade_as_nightly_cargo()
- .with_stdout(
- "\
-bar v1.0.0
-├── bar feature \"default\"
-│ └── foo v0.1.0 ([ROOT]/foo)
-│ ├── foo feature \"b\" (command-line)
-│ └── foo feature \"default\" (command-line)
-├── bar feature \"feat1\"
-│ └── foo v0.1.0 ([ROOT]/foo) (*)
-└── bar feature \"feat2\"
- └── foo feature \"b\" (command-line)
-",
- )
- .run();
-
p.cargo("tree -e features --features bar -Z namespaced-features")
.masquerade_as_nightly_cargo()
.with_stdout(
}
#[cargo_test]
-fn deferred_with_namespaced() {
- // Interaction with -Z namespaced-features using dep: syntax.
- //
- // `bar` is deferred with bar?/feat
- // `bar2` is deferred with dep:bar2?/feat
+fn weak_namespaced() {
+ // Behavior with a dep: dependency.
Package::new("bar", "1.0.0")
.feature("feat", &[])
.file("src/lib.rs", &require(&["feat"], &[]))
.publish();
- Package::new("bar2", "1.0.0")
- .feature("feat", &[])
- .file("src/lib.rs", &require(&["feat"], &[]))
- .publish();
- Package::new("bar_includer", "1.0.0")
- .add_dep(Dependency::new("bar", "1.0").optional(true))
- .add_dep(Dependency::new("bar2", "1.0").optional(true))
- .feature("feat", &["bar?/feat", "dep:bar2?/feat"])
- .feature("feat2", &["dep:bar2"])
- .file("src/lib.rs", &require(&["bar"], &["bar2"]))
- .publish();
- Package::new("bar_activator", "1.0.0")
- .feature_dep("bar_includer", "1.0", &["bar", "feat2"])
- .publish();
let p = project()
.file(
"Cargo.toml",
version = "0.1.0"
[dependencies]
- bar_includer = { version = "1.0", features = ["feat"] }
- bar_activator = "1.0"
+ bar = { version = "1.0", optional = true }
+
+ [features]
+ f1 = ["bar?/feat"]
+ f2 = ["dep:bar"]
"#,
)
- .file("src/lib.rs", "")
+ .file("src/lib.rs", &require(&["f1"], &["f2", "bar"]))
.build();
- p.cargo("check -Z weak-dep-features -Z namespaced-features")
+ p.cargo("check -Z weak-dep-features -Z namespaced-features --features f1")
.masquerade_as_nightly_cargo()
- .with_stderr_unordered(
+ .with_stderr(
"\
[UPDATING] [..]
[DOWNLOADING] crates ...
-[DOWNLOADED] [..]
-[DOWNLOADED] [..]
-[DOWNLOADED] [..]
-[DOWNLOADED] [..]
+[DOWNLOADED] bar v1.0.0 [..]
+[CHECKING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+
+ p.cargo("tree -Z weak-dep-features -Z namespaced-features -f")
+ .arg("{p} feats:{f}")
+ .masquerade_as_nightly_cargo()
+ .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:")
+ .run();
+
+ p.cargo("tree -Z weak-dep-features -Z namespaced-features --features f1 -f")
+ .arg("{p} feats:{f}")
+ .masquerade_as_nightly_cargo()
+ .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:f1")
+ .run();
+
+ p.cargo("tree -Z weak-dep-features -Z namespaced-features --features f1,f2 -f")
+ .arg("{p} feats:{f}")
+ .masquerade_as_nightly_cargo()
+ .with_stdout(
+ "\
+foo v0.1.0 ([ROOT]/foo) feats:f1,f2
+└── bar v1.0.0 feats:feat
+",
+ )
+ .run();
+
+ // "bar" remains not-a-feature
+ p.change_file("src/lib.rs", &require(&["f1", "f2"], &["bar"]));
+
+ p.cargo("check -Z weak-dep-features -Z namespaced-features --features f1,f2")
+ .masquerade_as_nightly_cargo()
+ .with_stderr(
+ "\
[CHECKING] bar v1.0.0
-[CHECKING] bar2 v1.0.0
-[CHECKING] bar_includer v1.0.0
-[CHECKING] bar_activator v1.0.0
[CHECKING] foo v0.1.0 [..]
[FINISHED] [..]
",
├── bar feature \"default\"
│ └── foo v0.1.0 ([ROOT]/foo)
│ ├── foo feature \"bar\" (command-line)
-│ │ └── foo feature \"f1\" (command-line)
│ ├── foo feature \"default\" (command-line)
│ └── foo feature \"f1\" (command-line)
└── bar feature \"feat\"