mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Implement weak dependency features.
This commit is contained in:
parent
28af36face
commit
9ffcf69093
@ -43,6 +43,7 @@ Available unstable (nightly-only) flags:
|
|||||||
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
|
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
|
||||||
-Z terminal-width -- Provide a terminal width to rustc for error truncation
|
-Z terminal-width -- Provide a terminal width to rustc for error truncation
|
||||||
-Z namespaced-features -- Allow features with `dep:` prefix
|
-Z namespaced-features -- Allow features with `dep:` prefix
|
||||||
|
-Z weak-dep-features -- Allow `dep_name?/feature` feature syntax
|
||||||
|
|
||||||
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
|
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
|
||||||
);
|
);
|
||||||
|
@ -358,6 +358,7 @@ pub struct CliUnstable {
|
|||||||
pub rustdoc_map: bool,
|
pub rustdoc_map: bool,
|
||||||
pub terminal_width: Option<Option<usize>>,
|
pub terminal_width: Option<Option<usize>>,
|
||||||
pub namespaced_features: bool,
|
pub namespaced_features: bool,
|
||||||
|
pub weak_dep_features: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
|
fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
|
||||||
@ -464,6 +465,7 @@ impl CliUnstable {
|
|||||||
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
|
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
|
||||||
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
|
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
|
||||||
"namespaced-features" => self.namespaced_features = parse_empty(k, v)?,
|
"namespaced-features" => self.namespaced_features = parse_empty(k, v)?,
|
||||||
|
"weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?,
|
||||||
_ => bail!("unknown `-Z` flag specified: {}", k),
|
_ => bail!("unknown `-Z` flag specified: {}", k),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +494,10 @@ impl Requirements<'_> {
|
|||||||
dep_name,
|
dep_name,
|
||||||
dep_feature,
|
dep_feature,
|
||||||
dep_prefix,
|
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)?,
|
} => self.require_dep_feature(*dep_name, *dep_feature, *dep_prefix)?,
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -177,6 +177,10 @@ impl FeatureOpts {
|
|||||||
if let ForceAllTargets::Yes = force_all_targets {
|
if let ForceAllTargets::Yes = force_all_targets {
|
||||||
opts.ignore_inactive_targets = false;
|
opts.ignore_inactive_targets = false;
|
||||||
}
|
}
|
||||||
|
if unstable_flags.weak_dep_features {
|
||||||
|
// Force this ON because it only works with the new resolver.
|
||||||
|
opts.new_resolver = true;
|
||||||
|
}
|
||||||
Ok(opts)
|
Ok(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,6 +315,15 @@ pub struct FeatureResolver<'a, 'cfg> {
|
|||||||
/// This has to be separate from `FeatureOpts.decouple_host_deps` because
|
/// This has to be separate from `FeatureOpts.decouple_host_deps` because
|
||||||
/// `for_host` tracking is also needed for `itarget` to work properly.
|
/// `for_host` tracking is also needed for `itarget` to work properly.
|
||||||
track_for_host: bool,
|
track_for_host: bool,
|
||||||
|
/// `dep_name?/feat_name` features that will be activated if `dep_name` is
|
||||||
|
/// ever activated.
|
||||||
|
///
|
||||||
|
/// 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)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
||||||
@ -353,6 +366,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
|||||||
activated_dependencies: HashMap::new(),
|
activated_dependencies: HashMap::new(),
|
||||||
processed_deps: HashSet::new(),
|
processed_deps: HashSet::new(),
|
||||||
track_for_host,
|
track_for_host,
|
||||||
|
deferred_weak_dependencies: HashMap::new(),
|
||||||
};
|
};
|
||||||
r.do_resolve(specs, requested_features)?;
|
r.do_resolve(specs, requested_features)?;
|
||||||
log::debug!("features={:#?}", r.activated_features);
|
log::debug!("features={:#?}", r.activated_features);
|
||||||
@ -399,6 +413,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
|||||||
for_host: bool,
|
for_host: bool,
|
||||||
fvs: &[FeatureValue],
|
fvs: &[FeatureValue],
|
||||||
) -> CargoResult<()> {
|
) -> CargoResult<()> {
|
||||||
|
log::trace!("activate_pkg {} {}", pkg_id.name(), for_host);
|
||||||
// Add an empty entry to ensure everything is covered. This is intended for
|
// Add an empty entry to ensure everything is covered. This is intended for
|
||||||
// finding bugs where the resolver missed something it should have visited.
|
// finding bugs where the resolver missed something it should have visited.
|
||||||
// Remove this in the future if `activated_features` uses an empty default.
|
// Remove this in the future if `activated_features` uses an empty default.
|
||||||
@ -446,56 +461,28 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
|||||||
for_host: bool,
|
for_host: bool,
|
||||||
fv: &FeatureValue,
|
fv: &FeatureValue,
|
||||||
) -> CargoResult<()> {
|
) -> CargoResult<()> {
|
||||||
|
log::trace!("activate_fv {} {} {}", pkg_id.name(), for_host, fv);
|
||||||
match fv {
|
match fv {
|
||||||
FeatureValue::Feature(f) => {
|
FeatureValue::Feature(f) => {
|
||||||
self.activate_rec(pkg_id, for_host, *f)?;
|
self.activate_rec(pkg_id, for_host, *f)?;
|
||||||
}
|
}
|
||||||
FeatureValue::Dep { dep_name } => {
|
FeatureValue::Dep { dep_name } => {
|
||||||
// Mark this dependency as activated.
|
self.activate_dependency(pkg_id, for_host, *dep_name)?;
|
||||||
self.activated_dependencies
|
|
||||||
.entry((pkg_id, self.opts.decouple_host_deps && for_host))
|
|
||||||
.or_default()
|
|
||||||
.insert(*dep_name);
|
|
||||||
// Activate the optional dep.
|
|
||||||
for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
|
|
||||||
for (dep, dep_for_host) in deps {
|
|
||||||
if dep.name_in_toml() != *dep_name {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
|
|
||||||
self.activate_pkg(dep_pkg_id, dep_for_host, &fvs)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FeatureValue::DepFeature {
|
FeatureValue::DepFeature {
|
||||||
dep_name,
|
dep_name,
|
||||||
dep_feature,
|
dep_feature,
|
||||||
dep_prefix,
|
dep_prefix,
|
||||||
|
weak,
|
||||||
} => {
|
} => {
|
||||||
// Activate a feature within a dependency.
|
self.activate_dep_feature(
|
||||||
for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
|
pkg_id,
|
||||||
for (dep, dep_for_host) in deps {
|
for_host,
|
||||||
if dep.name_in_toml() != *dep_name {
|
*dep_name,
|
||||||
continue;
|
*dep_feature,
|
||||||
}
|
*dep_prefix,
|
||||||
if dep.is_optional() {
|
*weak,
|
||||||
// Activate the dependency on self.
|
)?;
|
||||||
let fv = FeatureValue::Dep {
|
|
||||||
dep_name: *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
|
|
||||||
// name.
|
|
||||||
self.activate_rec(pkg_id, for_host, *dep_name)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Activate the feature on the dependency.
|
|
||||||
let fv = FeatureValue::new(*dep_feature);
|
|
||||||
self.activate_fv(dep_pkg_id, dep_for_host, &fv)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -509,6 +496,12 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
|||||||
for_host: bool,
|
for_host: bool,
|
||||||
feature_to_enable: InternedString,
|
feature_to_enable: InternedString,
|
||||||
) -> CargoResult<()> {
|
) -> CargoResult<()> {
|
||||||
|
log::trace!(
|
||||||
|
"activate_rec {} {} feat={}",
|
||||||
|
pkg_id.name(),
|
||||||
|
for_host,
|
||||||
|
feature_to_enable
|
||||||
|
);
|
||||||
let enabled = self
|
let enabled = self
|
||||||
.activated_features
|
.activated_features
|
||||||
.entry((pkg_id, self.opts.decouple_host_deps && for_host))
|
.entry((pkg_id, self.opts.decouple_host_deps && for_host))
|
||||||
@ -539,6 +532,110 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Activate a dependency (`dep:dep_name` syntax).
|
||||||
|
fn activate_dependency(
|
||||||
|
&mut self,
|
||||||
|
pkg_id: PackageId,
|
||||||
|
for_host: bool,
|
||||||
|
dep_name: InternedString,
|
||||||
|
) -> CargoResult<()> {
|
||||||
|
// Mark this dependency as activated.
|
||||||
|
let save_for_host = self.opts.decouple_host_deps && for_host;
|
||||||
|
self.activated_dependencies
|
||||||
|
.entry((pkg_id, save_for_host))
|
||||||
|
.or_default()
|
||||||
|
.insert(dep_name);
|
||||||
|
// Check for any deferred features.
|
||||||
|
let to_enable = self
|
||||||
|
.deferred_weak_dependencies
|
||||||
|
.remove(&(pkg_id, for_host, dep_name));
|
||||||
|
// Activate the optional dep.
|
||||||
|
for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
|
||||||
|
for (dep, dep_for_host) in deps {
|
||||||
|
if dep.name_in_toml() != dep_name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(to_enable) = &to_enable {
|
||||||
|
for (dep_feature, dep_prefix) in to_enable {
|
||||||
|
log::trace!(
|
||||||
|
"activate deferred {} {} -> {}/{}",
|
||||||
|
pkg_id.name(),
|
||||||
|
for_host,
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
|
||||||
|
self.activate_pkg(dep_pkg_id, dep_for_host, &fvs)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Activate a feature within a dependency (`dep_name/feat_name` syntax).
|
||||||
|
fn activate_dep_feature(
|
||||||
|
&mut self,
|
||||||
|
pkg_id: PackageId,
|
||||||
|
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) {
|
||||||
|
for (dep, dep_for_host) in deps {
|
||||||
|
if dep.name_in_toml() != dep_name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if dep.is_optional() {
|
||||||
|
let save_for_host = self.opts.decouple_host_deps && for_host;
|
||||||
|
if weak
|
||||||
|
&& !self
|
||||||
|
.activated_dependencies
|
||||||
|
.get(&(pkg_id, save_for_host))
|
||||||
|
.map(|deps| deps.contains(&dep_name))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
// This is weak, but not yet activated. Defer in case
|
||||||
|
// something comes along later and enables it.
|
||||||
|
log::trace!(
|
||||||
|
"deferring feature {} {} -> {}/{}",
|
||||||
|
pkg_id.name(),
|
||||||
|
for_host,
|
||||||
|
dep_name,
|
||||||
|
dep_feature
|
||||||
|
);
|
||||||
|
self.deferred_weak_dependencies
|
||||||
|
.entry((pkg_id, for_host, dep_name))
|
||||||
|
.or_default()
|
||||||
|
.insert((dep_feature, dep_prefix));
|
||||||
|
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
|
||||||
|
// name.
|
||||||
|
self.activate_rec(pkg_id, for_host, dep_name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Activate the feature on the dependency.
|
||||||
|
let fv = FeatureValue::new(dep_feature);
|
||||||
|
self.activate_fv(dep_pkg_id, dep_for_host, &fv)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns Vec of FeatureValues from a Dependency definition.
|
/// Returns Vec of FeatureValues from a Dependency definition.
|
||||||
fn fvs_from_dependency(&self, dep_id: PackageId, dep: &Dependency) -> Vec<FeatureValue> {
|
fn fvs_from_dependency(&self, dep_id: PackageId, dep: &Dependency) -> Vec<FeatureValue> {
|
||||||
let summary = self.resolve.summary(dep_id);
|
let summary = self.resolve.summary(dep_id);
|
||||||
|
@ -86,7 +86,11 @@ impl Summary {
|
|||||||
|
|
||||||
/// Returns an error if this Summary is using an unstable feature that is
|
/// Returns an error if this Summary is using an unstable feature that is
|
||||||
/// not enabled.
|
/// not enabled.
|
||||||
pub fn unstable_gate(&self, namespaced_features: bool) -> CargoResult<()> {
|
pub fn unstable_gate(
|
||||||
|
&self,
|
||||||
|
namespaced_features: bool,
|
||||||
|
weak_dep_features: bool,
|
||||||
|
) -> CargoResult<()> {
|
||||||
if !namespaced_features {
|
if !namespaced_features {
|
||||||
if self.inner.has_namespaced_features {
|
if self.inner.has_namespaced_features {
|
||||||
bail!(
|
bail!(
|
||||||
@ -101,6 +105,22 @@ impl Summary {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !weak_dep_features {
|
||||||
|
for (feat_name, features) in self.features() {
|
||||||
|
for fv in features {
|
||||||
|
if matches!(fv, FeatureValue::DepFeature{weak: true, ..}) {
|
||||||
|
bail!(
|
||||||
|
"optional dependency features with `?` syntax are only \
|
||||||
|
allowed on the nightly channel and requires the \
|
||||||
|
`-Z weak-dep-features` flag on the command line\n\
|
||||||
|
Feature `{}` had feature value `{}`.",
|
||||||
|
feat_name,
|
||||||
|
fv
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +313,7 @@ fn build_feature_map(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DepFeature { dep_name, .. } => {
|
DepFeature { dep_name, weak, .. } => {
|
||||||
// Validation of the feature name will be performed in the resolver.
|
// Validation of the feature name will be performed in the resolver.
|
||||||
if !is_any_dep {
|
if !is_any_dep {
|
||||||
bail!(
|
bail!(
|
||||||
@ -303,6 +323,12 @@ fn build_feature_map(
|
|||||||
dep_name
|
dep_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if *weak && !is_optional_dep {
|
||||||
|
bail!("feature `{}` includes `{}` with a `?`, but `{}` is not an optional dependency\n\
|
||||||
|
A non-optional dependency of the same name is defined; \
|
||||||
|
consider removing the `?` or changing the dependency to be optional",
|
||||||
|
feature, fv, dep_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,6 +372,10 @@ pub enum FeatureValue {
|
|||||||
/// If this is true, then the feature used the `dep:` prefix, which
|
/// If this is true, then the feature used the `dep:` prefix, which
|
||||||
/// prevents enabling the feature named `dep_name`.
|
/// prevents enabling the feature named `dep_name`.
|
||||||
dep_prefix: bool,
|
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.
|
||||||
|
weak: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,10 +390,16 @@ impl FeatureValue {
|
|||||||
} else {
|
} else {
|
||||||
(dep, false)
|
(dep, false)
|
||||||
};
|
};
|
||||||
|
let (dep, weak) = if let Some(dep) = dep.strip_suffix('?') {
|
||||||
|
(dep, true)
|
||||||
|
} else {
|
||||||
|
(dep, false)
|
||||||
|
};
|
||||||
FeatureValue::DepFeature {
|
FeatureValue::DepFeature {
|
||||||
dep_name: InternedString::new(dep),
|
dep_name: InternedString::new(dep),
|
||||||
dep_feature: InternedString::new(dep_feat),
|
dep_feature: InternedString::new(dep_feat),
|
||||||
dep_prefix,
|
dep_prefix,
|
||||||
|
weak,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -393,13 +429,13 @@ impl fmt::Display for FeatureValue {
|
|||||||
DepFeature {
|
DepFeature {
|
||||||
dep_name,
|
dep_name,
|
||||||
dep_feature,
|
dep_feature,
|
||||||
dep_prefix: true,
|
dep_prefix,
|
||||||
} => write!(f, "dep:{}/{}", dep_name, dep_feature),
|
weak,
|
||||||
DepFeature {
|
} => {
|
||||||
dep_name,
|
let dep_prefix = if *dep_prefix { "dep:" } else { "" };
|
||||||
dep_feature,
|
let weak = if *weak { "?" } else { "" };
|
||||||
dep_prefix: false,
|
write!(f, "{}{}{}/{}", dep_prefix, dep_name, weak, dep_feature)
|
||||||
} => write!(f, "{}/{}", dep_name, dep_feature),
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1082,11 +1082,20 @@ fn validate_required_features(
|
|||||||
target_name
|
target_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
FeatureValue::DepFeature { weak: true, .. } => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"invalid feature `{}` in required-features of target `{}`: \
|
||||||
|
optional dependency with `?` is not allowed in required-features",
|
||||||
|
fv,
|
||||||
|
target_name
|
||||||
|
);
|
||||||
|
}
|
||||||
// Handling of dependent_crate/dependent_crate_feature syntax
|
// Handling of dependent_crate/dependent_crate_feature syntax
|
||||||
FeatureValue::DepFeature {
|
FeatureValue::DepFeature {
|
||||||
dep_name,
|
dep_name,
|
||||||
dep_feature,
|
dep_feature,
|
||||||
dep_prefix: false,
|
dep_prefix: false,
|
||||||
|
weak: false,
|
||||||
} => {
|
} => {
|
||||||
match resolve
|
match resolve
|
||||||
.deps(summary.package_id())
|
.deps(summary.package_id())
|
||||||
|
@ -568,6 +568,11 @@ fn add_feature_rec(
|
|||||||
dep_name,
|
dep_name,
|
||||||
dep_feature,
|
dep_feature,
|
||||||
dep_prefix,
|
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: _,
|
||||||
} => {
|
} => {
|
||||||
let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
|
let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
|
||||||
Some(indexes) => indexes.clone(),
|
Some(indexes) => indexes.clone(),
|
||||||
|
@ -272,6 +272,7 @@ impl<'cfg> RegistryIndex<'cfg> {
|
|||||||
let source_id = self.source_id;
|
let source_id = self.source_id;
|
||||||
let config = self.config;
|
let config = self.config;
|
||||||
let namespaced_features = self.config.cli_unstable().namespaced_features;
|
let namespaced_features = self.config.cli_unstable().namespaced_features;
|
||||||
|
let weak_dep_features = self.config.cli_unstable().weak_dep_features;
|
||||||
|
|
||||||
// First up actually parse what summaries we have available. If Cargo
|
// First up actually parse what summaries we have available. If Cargo
|
||||||
// has run previously this will parse a Cargo-specific cache file rather
|
// has run previously this will parse a Cargo-specific cache file rather
|
||||||
@ -299,7 +300,11 @@ impl<'cfg> RegistryIndex<'cfg> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.filter(move |is| is.summary.unstable_gate(namespaced_features).is_ok()))
|
.filter(move |is| {
|
||||||
|
is.summary
|
||||||
|
.unstable_gate(namespaced_features, weak_dep_features)
|
||||||
|
.is_ok()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_summaries(
|
fn load_summaries(
|
||||||
|
@ -1197,7 +1197,8 @@ impl TomlManifest {
|
|||||||
me.features.as_ref().unwrap_or(&empty_features),
|
me.features.as_ref().unwrap_or(&empty_features),
|
||||||
project.links.as_deref(),
|
project.links.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
summary.unstable_gate(config.cli_unstable().namespaced_features)?;
|
let unstable = config.cli_unstable();
|
||||||
|
summary.unstable_gate(unstable.namespaced_features, unstable.weak_dep_features)?;
|
||||||
|
|
||||||
let metadata = ManifestMetadata {
|
let metadata = ManifestMetadata {
|
||||||
description: project.description.clone(),
|
description: project.description.clone(),
|
||||||
|
@ -906,3 +906,26 @@ error[E0308]: mismatched types
|
|||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Weak dependency features
|
||||||
|
* Tracking Issue: [#8832](https://github.com/rust-lang/cargo/issues/8832)
|
||||||
|
|
||||||
|
The `-Z weak-dep-features` command-line options enables the ability to use
|
||||||
|
`dep_name?/feat_name` syntax in the `[features]` table. The `?` indicates that
|
||||||
|
the optional dependency `dep_name` will not be automatically enabled. The
|
||||||
|
feature `feat_name` will only be added if something else enables the
|
||||||
|
`dep_name` dependency.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.117", optional = true, default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = ["serde?/std"]
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the `std` feature enables the `std` feature on the `serde`
|
||||||
|
dependency. However, unlike the normal `serde/std` syntax, it will not enable
|
||||||
|
the optional dependency `serde` unless something else has included it.
|
||||||
|
@ -120,6 +120,7 @@ mod vendor;
|
|||||||
mod verify_project;
|
mod verify_project;
|
||||||
mod version;
|
mod version;
|
||||||
mod warn_on_failure;
|
mod warn_on_failure;
|
||||||
|
mod weak_dep_features;
|
||||||
mod workspaces;
|
mod workspaces;
|
||||||
mod yank;
|
mod yank;
|
||||||
|
|
||||||
|
566
tests/testsuite/weak_dep_features.rs
Normal file
566
tests/testsuite/weak_dep_features.rs
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
//! Tests for weak-dep-features.
|
||||||
|
|
||||||
|
use cargo_test_support::project;
|
||||||
|
use cargo_test_support::registry::{Dependency, Package};
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
// Helper to create lib.rs files that check features.
|
||||||
|
fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
for feature in enabled_features {
|
||||||
|
write!(s, "#[cfg(not(feature=\"{feature}\"))] compile_error!(\"expected feature {feature} to be enabled\");\n",
|
||||||
|
feature=feature).unwrap();
|
||||||
|
}
|
||||||
|
for feature in disabled_features {
|
||||||
|
write!(s, "#[cfg(feature=\"{feature}\")] compile_error!(\"did not expect feature {feature} to be enabled\");\n",
|
||||||
|
feature=feature).unwrap();
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn gated() {
|
||||||
|
// Need -Z weak-dep-features to enable.
|
||||||
|
Package::new("bar", "1.0.0").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 = ["bar?/feat"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
p.cargo("check")
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
optional dependency features with `?` syntax are only allowed on the nightly \
|
||||||
|
channel and requires the `-Z weak-dep-features` flag on the command line
|
||||||
|
Feature `f1` had feature value `bar?/feat`.
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn dependency_gate_ignored() {
|
||||||
|
// Dependencies with ? features in the registry are ignored in the
|
||||||
|
// registry if not on nightly.
|
||||||
|
Package::new("baz", "1.0.0").feature("feat", &[]).publish();
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.add_dep(Dependency::new("baz", "1.0").optional(true))
|
||||||
|
.feature("feat", &["baz?/feat"])
|
||||||
|
.publish();
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = "1.0"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[ERROR] no matching package named `bar` found
|
||||||
|
location searched: registry `https://github.com/rust-lang/crates.io-index`
|
||||||
|
required by package `foo v0.1.0 ([..]/foo)`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// Publish a version without the ? feature, it should ignore 1.0.0
|
||||||
|
// an use this instead.
|
||||||
|
Package::new("bar", "1.0.1")
|
||||||
|
.add_dep(Dependency::new("baz", "1.0").optional(true))
|
||||||
|
.feature("feat", &["baz"])
|
||||||
|
.publish();
|
||||||
|
p.cargo("check")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] bar [..]
|
||||||
|
[CHECKING] bar v1.0.1
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn simple() {
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.feature("feat", &[])
|
||||||
|
.file("src/lib.rs", &require(&["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 = ["bar?/feat"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", &require(&["f1"], &[]))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// It's a bit unfortunate that this has to download `bar`, but avoiding
|
||||||
|
// that is extremely difficult.
|
||||||
|
p.cargo("check -Z weak-dep-features --features f1")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] bar v1.0.0 [..]
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("check -Z weak-dep-features --features f1,bar")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[CHECKING] bar v1.0.0
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn deferred() {
|
||||||
|
// A complex chain that requires deferring enabling the feature due to
|
||||||
|
// another dependency getting enabled.
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.feature("feat", &[])
|
||||||
|
.file("src/lib.rs", &require(&["feat"], &[]))
|
||||||
|
.publish();
|
||||||
|
Package::new("dep", "1.0.0")
|
||||||
|
.add_dep(Dependency::new("bar", "1.0").optional(true))
|
||||||
|
.feature("feat", &["bar?/feat"])
|
||||||
|
.publish();
|
||||||
|
Package::new("bar_activator", "1.0.0")
|
||||||
|
.feature_dep("dep", "1.0", &["bar"])
|
||||||
|
.publish();
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dep = { version = "1.0", features = ["feat"] }
|
||||||
|
bar_activator = "1.0"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check -Z weak-dep-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] dep v1.0.0 [..]
|
||||||
|
[DOWNLOADED] bar_activator v1.0.0 [..]
|
||||||
|
[DOWNLOADED] bar v1.0.0 [..]
|
||||||
|
[CHECKING] bar v1.0.0
|
||||||
|
[CHECKING] dep v1.0.0
|
||||||
|
[CHECKING] bar_activator v1.0.0
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn not_optional_dep() {
|
||||||
|
// Attempt to use dep_name?/feat where dep_name is not optional.
|
||||||
|
Package::new("dep", "1.0.0").feature("feat", &[]).publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dep = "1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
feat = ["dep?/feat"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check -Z weak-dep-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr("\
|
||||||
|
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
feature `feat` includes `dep?/feat` with a `?`, but `dep` is not an optional dependency
|
||||||
|
A non-optional dependency of the same name is defined; consider removing the `?` or changing the dependency to be optional
|
||||||
|
")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn optional_cli_syntax() {
|
||||||
|
// --features bar?/feat
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.feature("feat", &[])
|
||||||
|
.file("src/lib.rs", &require(&["feat"], &[]))
|
||||||
|
.publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = { version = "1.0", optional = true }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check --features bar?/feat -Z weak-dep-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] bar v1.0.0 [..]
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("check --features bar?/feat,bar -Z weak-dep-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[CHECKING] bar v1.0.0
|
||||||
|
[CHECKING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn required_features() {
|
||||||
|
// required-features doesn't allow ?
|
||||||
|
Package::new("bar", "1.0.0").feature("feat", &[]).publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = { version = "1.0", optional = true }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "foo"
|
||||||
|
required-features = ["bar?/feat"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/main.rs", "fn main() {}")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check -Z weak-dep-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[ERROR] invalid feature `bar?/feat` in required-features of target `foo`: \
|
||||||
|
optional dependency with `?` is not allowed in required-features
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn weak_with_host_decouple() {
|
||||||
|
// -Z weak-opt-features with -Z features=host
|
||||||
|
//
|
||||||
|
// foo v0.1.0
|
||||||
|
// └── common v1.0.0
|
||||||
|
// └── bar v1.0.0 <-- does not have `feat` enabled
|
||||||
|
// [build-dependencies]
|
||||||
|
// └── bar_activator v1.0.0
|
||||||
|
// └── common v1.0.0
|
||||||
|
// └── bar v1.0.0 <-- does have `feat` enabled
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.feature("feat", &[])
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
pub fn feat() -> bool {
|
||||||
|
cfg!(feature = "feat")
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.publish();
|
||||||
|
|
||||||
|
Package::new("common", "1.0.0")
|
||||||
|
.add_dep(Dependency::new("bar", "1.0").optional(true))
|
||||||
|
.feature("feat", &["bar?/feat"])
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
#[cfg(feature = "bar")]
|
||||||
|
pub fn feat() -> bool { bar::feat() }
|
||||||
|
#[cfg(not(feature = "bar"))]
|
||||||
|
pub fn feat() -> bool { false }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.publish();
|
||||||
|
|
||||||
|
Package::new("bar_activator", "1.0.0")
|
||||||
|
.feature_dep("common", "1.0", &["bar", "feat"])
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
pub fn feat() -> bool {
|
||||||
|
common::feat()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.publish();
|
||||||
|
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
common = { version = "1.0", features = ["feat"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bar_activator = "1.0"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"src/main.rs",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
assert!(!common::feat());
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"build.rs",
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
assert!(bar_activator::feat());
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("run -Z weak-dep-features -Z features=host_dep")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[COMPILING] bar v1.0.0
|
||||||
|
[COMPILING] common v1.0.0
|
||||||
|
[COMPILING] bar_activator v1.0.0
|
||||||
|
[COMPILING] foo v0.1.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
[RUNNING] `target/debug/foo[EXE]`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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
|
||||||
|
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",
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar_includer = { version = "1.0", features = ["feat"] }
|
||||||
|
bar_activator = "1.0"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("check -Z weak-dep-features -Z namespaced-features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr_unordered(
|
||||||
|
"\
|
||||||
|
[UPDATING] [..]
|
||||||
|
[DOWNLOADING] crates ...
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[DOWNLOADED] [..]
|
||||||
|
[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] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn tree() {
|
||||||
|
Package::new("bar", "1.0.0")
|
||||||
|
.feature("feat", &[])
|
||||||
|
.file("src/lib.rs", &require(&["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 = ["bar?/feat"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", &require(&["f1"], &[]))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("tree -Z weak-dep-features --features f1")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stdout("foo v0.1.0 ([ROOT]/foo)")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree -Z weak-dep-features --features f1,bar")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([ROOT]/foo)
|
||||||
|
└── bar v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree -Z weak-dep-features --features f1,bar -e features")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
foo v0.1.0 ([ROOT]/foo)
|
||||||
|
└── bar feature \"default\"
|
||||||
|
└── bar v1.0.0
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("tree -Z weak-dep-features --features f1,bar -e features -i bar")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stdout(
|
||||||
|
"\
|
||||||
|
bar v1.0.0
|
||||||
|
├── 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\"
|
||||||
|
└── foo feature \"f1\" (command-line)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user