From 43a063c80a534d279452ee94c3f6615c603a36d5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 6 Jan 2022 15:46:15 -0800 Subject: [PATCH] Stabilize namespaced and weak dependency features. --- Cargo.toml | 2 +- crates/crates-io/Cargo.toml | 2 +- crates/crates-io/lib.rs | 2 - src/bin/cargo/commands/read_manifest.rs | 4 +- src/cargo/core/features.rs | 10 +- src/cargo/core/package.rs | 36 ++--- src/cargo/core/resolver/features.rs | 106 +++----------- src/cargo/core/summary.rs | 62 +------- src/cargo/ops/cargo_output_metadata.rs | 6 +- src/cargo/ops/registry.rs | 1 - src/cargo/sources/registry/index.rs | 14 +- src/cargo/util/toml/mod.rs | 2 - src/doc/src/reference/features.md | 72 ++++++--- src/doc/src/reference/registries.md | 40 ++++- src/doc/src/reference/unstable.md | 97 ++---------- tests/testsuite/features.rs | 25 +++- tests/testsuite/features_namespaced.rs | 187 ++++-------------------- tests/testsuite/old_cargos.rs | 3 +- tests/testsuite/weak_dep_features.rs | 165 +++------------------ 19 files changed, 227 insertions(+), 609 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 20dfd540f..0f9dcc1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ atty = "0.2" bytesize = "1.0" cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" } cargo-util = { path = "crates/cargo-util", version = "0.1.2" } -crates-io = { path = "crates/crates-io", version = "0.33.1" } +crates-io = { path = "crates/crates-io", version = "0.34.0" } crossbeam-utils = "0.8" curl = { version = "0.4.41", features = ["http2"] } curl-sys = "0.4.50" diff --git a/crates/crates-io/Cargo.toml b/crates/crates-io/Cargo.toml index 025f8e8eb..777bdfac9 100644 --- a/crates/crates-io/Cargo.toml +++ b/crates/crates-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crates-io" -version = "0.33.1" +version = "0.34.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cargo" diff --git a/crates/crates-io/lib.rs b/crates/crates-io/lib.rs index f7ed67382..bbc12893e 100644 --- a/crates/crates-io/lib.rs +++ b/crates/crates-io/lib.rs @@ -55,8 +55,6 @@ pub struct NewCrate { pub repository: Option, pub badges: BTreeMap>, pub links: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub v: Option, } #[derive(Serialize)] diff --git a/src/bin/cargo/commands/read_manifest.rs b/src/bin/cargo/commands/read_manifest.rs index 86867152c..d6a7bccbb 100644 --- a/src/bin/cargo/commands/read_manifest.rs +++ b/src/bin/cargo/commands/read_manifest.rs @@ -15,8 +15,6 @@ Deprecated, use `cargo metadata --no-deps` instead.\ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let ws = args.workspace(config)?; - config - .shell() - .print_json(&ws.current()?.serialized(config))?; + config.shell().print_json(&ws.current()?.serialized())?; Ok(()) } diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 968290a38..8be29b9b7 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -642,7 +642,6 @@ unstable_cli_options!( minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"), mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"), multitarget: bool = ("Allow passing multiple `--target` flags to the cargo subcommand selected"), - namespaced_features: bool = ("Allow features with `dep:` prefix"), no_index_update: bool = ("Do not update the registry index even if the cache is outdated"), panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"), host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"), @@ -652,7 +651,6 @@ unstable_cli_options!( terminal_width: Option> = ("Provide a terminal width to rustc for error truncation"), timings: Option> = ("Display concurrency information"), unstable_options: bool = ("Allow the usage of unstable options"), - weak_dep_features: bool = ("Allow `dep_name?/feature` feature syntax"), // TODO(wcrichto): move scrape example configuration into Cargo.toml before stabilization // See: https://github.com/rust-lang/cargo/pull/9525#discussion_r728470927 rustdoc_scrape_examples: Option = ("Allow rustdoc to scrape examples from reverse-dependencies for documentation"), @@ -707,6 +705,10 @@ const STABILIZED_NAMED_PROFILES: &str = "The named-profiles feature is now alway const STABILIZED_FUTURE_INCOMPAT_REPORT: &str = "The future-incompat-report feature is now always enabled."; +const STABILIZED_WEAK_DEP_FEATURES: &str = "Weak dependency features are now always available."; + +const STABILISED_NAMESPACED_FEATURES: &str = "Namespaced features are now always available."; + fn deserialize_build_std<'de, D>(deserializer: D) -> Result>, D::Error> where D: serde::Deserializer<'de>, @@ -873,8 +875,8 @@ impl CliUnstable { "multitarget" => self.multitarget = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, "terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?), - "namespaced-features" => self.namespaced_features = parse_empty(k, v)?, - "weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?, + "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES), + "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES), "credential-process" => self.credential_process = parse_empty(k, v)?, "rustdoc-scrape-examples" => { if let Some(s) = v { diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index dd73ea25c..c447610f2 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -208,7 +208,7 @@ impl Package { self.targets().iter().any(|t| t.is_example() || t.is_bin()) } - pub fn serialized(&self, config: &Config) -> SerializedPackage { + pub fn serialized(&self) -> SerializedPackage { let summary = self.manifest().summary(); let package_id = summary.package_id(); let manmeta = self.manifest().metadata(); @@ -222,27 +222,19 @@ impl Package { .filter(|t| t.src_path().is_path()) .cloned() .collect(); - let features = if config.cli_unstable().namespaced_features { - // Convert Vec to Vec - summary - .features() - .iter() - .map(|(k, v)| { - ( - *k, - v.iter() - .map(|fv| InternedString::new(&fv.to_string())) - .collect(), - ) - }) - .collect() - } else { - self.manifest() - .original() - .features() - .cloned() - .unwrap_or_default() - }; + // Convert Vec to Vec + let features = summary + .features() + .iter() + .map(|(k, v)| { + ( + *k, + v.iter() + .map(|fv| InternedString::new(&fv.to_string())) + .collect(), + ) + }) + .collect(); SerializedPackage { name: package_id.name(), diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index 31b6c7833..1c1ef451e 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -1,19 +1,14 @@ //! Feature resolver. //! //! This is a new feature resolver that runs independently of the main -//! dependency resolver. It is enabled when the user specifies `resolver = -//! "2"` in `Cargo.toml`. +//! dependency resolver. It has several options which can enable new feature +//! resolution behavior. //! //! One of its key characteristics is that it can avoid unifying features for //! shared dependencies in some situations. See `FeatureOpts` for the //! different behaviors that can be enabled. If no extra options are enabled, //! then it should behave exactly the same as the dependency resolver's -//! feature resolution. This can be verified by setting the -//! `__CARGO_FORCE_NEW_FEATURES=compare` environment variable and running -//! Cargo's test suite (or building other projects), and checking if it -//! panics. Note: the `features2` tests will fail because they intentionally -//! compare the old vs new behavior, so forcing the old behavior will -//! naturally fail the tests. +//! feature resolution. //! //! The preferred way to engage this new resolver is via //! `resolve_ws_with_opts`. @@ -59,22 +54,12 @@ pub struct ResolvedFeatures { /// /// The value is the `name_in_toml` of the dependencies. activated_dependencies: ActivateMap, - /// This is only here for legacy support when the new resolver is not enabled. - /// - /// This is the set of features enabled for each package. - legacy_features: Option>>, - /// This is only here for legacy support when the new resolver is not enabled. - /// - /// This is the set of optional dependencies enabled for each package. - legacy_dependencies: Option>>, opts: FeatureOpts, } /// Options for how the feature resolver works. #[derive(Default)] pub struct FeatureOpts { - /// Use the new resolver instead of the old one. - new_resolver: bool, /// Build deps and proc-macros will not share share features with other dep kinds. decouple_host_deps: bool, /// Dev dep features will not be activated unless needed. @@ -132,7 +117,6 @@ impl FeatureOpts { let mut opts = FeatureOpts::default(); let unstable_flags = ws.config().cli_unstable(); let mut enable = |feat_opts: &Vec| { - opts.new_resolver = true; for opt in feat_opts { match opt.as_ref() { "build_dep" | "host_dep" => opts.decouple_host_deps = true, @@ -159,15 +143,6 @@ impl FeatureOpts { enable(&vec!["all".to_string()]).unwrap(); } } - // This env var is intended for testing only. - if let Ok(env_opts) = std::env::var("__CARGO_FORCE_NEW_FEATURES") { - if env_opts == "1" { - opts.new_resolver = true; - } else { - let env_opts = env_opts.split(',').map(|s| s.to_string()).collect(); - enable(&env_opts)?; - } - } if let HasDevUnits::Yes = has_dev_units { // Dev deps cannot be decoupled when they are in use. opts.decouple_dev_deps = false; @@ -175,10 +150,6 @@ impl FeatureOpts { if let ForceAllTargets::Yes = force_all_targets { 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) } @@ -187,7 +158,6 @@ impl FeatureOpts { match behavior { ResolveBehavior::V1 => FeatureOpts::default(), ResolveBehavior::V2 => FeatureOpts { - new_resolver: true, decouple_host_deps: true, decouple_dev_deps: has_dev_units == HasDevUnits::No, ignore_inactive_targets: true, @@ -306,18 +276,11 @@ impl ResolvedFeatures { features_for: FeaturesFor, dep_name: InternedString, ) -> bool { - if let Some(legacy) = &self.legacy_dependencies { - legacy - .get(&pkg_id) - .map(|deps| deps.contains(&dep_name)) - .unwrap_or(false) - } else { - let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep; - self.activated_dependencies - .get(&(pkg_id, is_build)) - .map(|deps| deps.contains(&dep_name)) - .unwrap_or(false) - } + let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep; + self.activated_dependencies + .get(&(pkg_id, is_build)) + .map(|deps| deps.contains(&dep_name)) + .unwrap_or(false) } /// Variant of `activated_features` that returns `None` if this is @@ -336,15 +299,11 @@ impl ResolvedFeatures { pkg_id: PackageId, features_for: FeaturesFor, ) -> CargoResult> { - if let Some(legacy) = &self.legacy_features { - Ok(legacy.get(&pkg_id).map_or_else(Vec::new, |v| v.clone())) + let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep; + if let Some(fs) = self.activated_features.get(&(pkg_id, is_build)) { + Ok(fs.iter().cloned().collect()) } else { - let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep; - if let Some(fs) = self.activated_features.get(&(pkg_id, is_build)) { - Ok(fs.iter().cloned().collect()) - } else { - bail!("features did not find {:?} {:?}", pkg_id, is_build) - } + bail!("features did not find {:?} {:?}", pkg_id, is_build) } } @@ -352,14 +311,16 @@ impl ResolvedFeatures { /// /// Used by `cargo fix --edition` to display any differences. pub fn compare_legacy(&self, legacy: &ResolvedFeatures) -> DiffMap { - let legacy_features = legacy.legacy_features.as_ref().unwrap(); self.activated_features .iter() .filter_map(|((pkg_id, for_host), new_features)| { - let old_features = match legacy_features.get(pkg_id) { - Some(feats) => feats.iter().cloned().collect(), - None => BTreeSet::new(), - }; + let old_features = legacy + .activated_features + .get(&(*pkg_id, *for_host)) + // The new features may have for_host entries where the old one does not. + .or_else(|| legacy.activated_features.get(&(*pkg_id, false))) + .map(|feats| feats.iter().cloned().collect()) + .unwrap_or_else(|| BTreeSet::new()); // The new resolver should never add features. assert_eq!(new_features.difference(&old_features).next(), None); let removed_features: BTreeSet<_> = @@ -427,17 +388,6 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { ) -> CargoResult { use crate::util::profile; let _p = profile::start("resolve features"); - - if !opts.new_resolver { - // Legacy mode. - return Ok(ResolvedFeatures { - activated_features: HashMap::new(), - activated_dependencies: HashMap::new(), - legacy_features: Some(resolve.features_clone()), - legacy_dependencies: Some(compute_legacy_deps(resolve)), - opts, - }); - } let track_for_host = opts.decouple_host_deps || opts.ignore_inactive_targets; let mut r = FeatureResolver { ws, @@ -460,8 +410,6 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { Ok(ResolvedFeatures { activated_features: r.activated_features, activated_dependencies: r.activated_dependencies, - legacy_features: None, - legacy_dependencies: None, opts: r.opts, }) } @@ -826,19 +774,3 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { .proc_macro() } } - -/// Computes a map of PackageId to the set of optional dependencies that are -/// enabled for that dep (when the new resolver is not enabled). -fn compute_legacy_deps(resolve: &Resolve) -> HashMap> { - let mut result: HashMap> = HashMap::new(); - for pkg_id in resolve.iter() { - for (_dep_id, deps) in resolve.deps(pkg_id) { - for dep in deps { - if dep.is_optional() { - result.entry(pkg_id).or_default().insert(dep.name_in_toml()); - } - } - } - } - result -} diff --git a/src/cargo/core/summary.rs b/src/cargo/core/summary.rs index 4f48fafa6..9c5396eec 100644 --- a/src/cargo/core/summary.rs +++ b/src/cargo/core/summary.rs @@ -23,8 +23,6 @@ struct Inner { package_id: PackageId, dependencies: Vec, features: Rc, - has_namespaced_features: bool, - has_overlapping_features: Option, checksum: Option, links: Option, } @@ -37,15 +35,11 @@ impl Summary { features: &BTreeMap>, links: Option>, ) -> CargoResult { - // ****CAUTION**** If you change anything here than may raise a new + // ****CAUTION**** If you change anything here that may raise a new // error, be sure to coordinate that change with either the index // schema field or the SummariesCache version. - let mut has_overlapping_features = None; for dep in dependencies.iter() { let dep_name = dep.name_in_toml(); - if features.contains_key(&dep_name) { - has_overlapping_features = Some(dep_name); - } if dep.is_optional() && !dep.is_transitive() { bail!( "dev-dependencies are not allowed to be optional: `{}`", @@ -53,8 +47,7 @@ impl Summary { ) } } - let (feature_map, has_namespaced_features) = - build_feature_map(config, pkg_id, features, &dependencies)?; + let feature_map = build_feature_map(config, pkg_id, features, &dependencies)?; Ok(Summary { inner: Rc::new(Inner { package_id: pkg_id, @@ -62,8 +55,6 @@ impl Summary { features: Rc::new(feature_map), checksum: None, links: links.map(|l| l.into()), - has_namespaced_features, - has_overlapping_features, }), }) } @@ -87,46 +78,6 @@ impl Summary { &self.inner.features } - /// Returns an error if this Summary is using an unstable feature that is - /// not enabled. - pub fn unstable_gate( - &self, - namespaced_features: bool, - weak_dep_features: bool, - ) -> CargoResult<()> { - if !namespaced_features { - if self.inner.has_namespaced_features { - bail!( - "namespaced features with the `dep:` prefix are only allowed on \ - the nightly channel and requires the `-Z namespaced-features` flag on the command-line" - ); - } - if let Some(dep_name) = self.inner.has_overlapping_features { - bail!( - "features and dependencies cannot have the same name: `{}`", - dep_name - ) - } - } - 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(()) - } - pub fn checksum(&self) -> Option<&str> { self.inner.checksum.as_deref() } @@ -181,16 +132,12 @@ impl Hash for Summary { /// Checks features for errors, bailing out a CargoResult:Err if invalid, /// and creates FeatureValues for each feature. -/// -/// The returned `bool` indicates whether or not the `[features]` table -/// included a `dep:` prefixed namespaced feature (used for gating on -/// nightly). fn build_feature_map( config: &Config, pkg_id: PackageId, features: &BTreeMap>, dependencies: &[Dependency], -) -> CargoResult<(FeatureMap, bool)> { +) -> CargoResult { use self::FeatureValue::*; let mut dep_map = HashMap::new(); for dep in dependencies.iter() { @@ -210,7 +157,6 @@ fn build_feature_map( (*feature, fvs) }) .collect(); - let has_namespaced_features = map.values().flatten().any(|fv| fv.has_dep_prefix()); // Add implicit features for optional dependencies if they weren't // explicitly listed anywhere. @@ -372,7 +318,7 @@ fn build_feature_map( ); } - Ok((map, has_namespaced_features)) + Ok(map) } /// FeatureValue represents the types of dependencies a feature can have. diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index b2e100f6c..8cb3951dd 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -31,9 +31,8 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo VERSION ); } - let config = ws.config(); let (packages, resolve) = if opt.no_deps { - let packages = ws.members().map(|pkg| pkg.serialized(config)).collect(); + let packages = ws.members().map(|pkg| pkg.serialized()).collect(); (packages, None) } else { let (packages, resolve) = build_resolve_graph(ws, opt)?; @@ -152,11 +151,10 @@ fn build_resolve_graph( ); } // Get a Vec of Packages. - let config = ws.config(); let actual_packages = package_map .into_iter() .filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg)) - .map(|pkg| pkg.serialized(config)) + .map(|pkg| pkg.serialized()) .collect(); let mr = MetadataResolve { diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index fe0a94692..b8e47d921 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -287,7 +287,6 @@ fn transmit( license_file: license_file.clone(), badges: badges.clone(), links: links.clone(), - v: None, }, tarball, ) diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index 3d873acd3..1394d9c1a 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -293,8 +293,6 @@ impl<'cfg> RegistryIndex<'cfg> { { let source_id = self.source_id; let config = self.config; - 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 // has run previously this will parse a Cargo-specific cache file rather @@ -309,11 +307,6 @@ impl<'cfg> RegistryIndex<'cfg> { // minimize the amount of work being done here and parse as little as // necessary. let raw_data = &summaries.raw_data; - let max_version = if namespaced_features || weak_dep_features { - INDEX_V_MAX - } else { - 1 - }; Ok(summaries .versions .iter_mut() @@ -328,7 +321,7 @@ impl<'cfg> RegistryIndex<'cfg> { }, ) .filter(move |is| { - if is.v > max_version { + if is.v > INDEX_V_MAX { debug!( "unsupported schema version {} ({} {})", is.v, @@ -339,11 +332,6 @@ impl<'cfg> RegistryIndex<'cfg> { } else { true } - }) - .filter(move |is| { - is.summary - .unstable_gate(namespaced_features, weak_dep_features) - .is_ok() })) } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 39835c752..0b09c1691 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1299,8 +1299,6 @@ impl TomlManifest { me.features.as_ref().unwrap_or(&empty_features), project.links.as_deref(), )?; - let unstable = config.cli_unstable(); - summary.unstable_gate(unstable.namespaced_features, unstable.weak_dep_features)?; let metadata = ManifestMetadata { description: project.description.clone(), diff --git a/src/doc/src/reference/features.md b/src/doc/src/reference/features.md index 0defb30b7..612f341a5 100644 --- a/src/doc/src/reference/features.md +++ b/src/doc/src/reference/features.md @@ -116,21 +116,33 @@ an external package to handle GIF images. This can be expressed like this: gif = { version = "0.11.1", optional = true } ``` -Optional dependencies implicitly define a feature of the same name as the -dependency. This means that the same `cfg(feature = "gif")` syntax can be used -in the code, and the dependency can be enabled just like a feature such as -`--features gif` (see [Command-line feature -options](#command-line-feature-options) below). +By default, this optional dependency implicitly defines a feature that looks +like this: -> **Note**: A feature in the `[feature]` table cannot use the same name as a -> dependency. Experimental support for enabling this and other extensions is -> available on the nightly channel via [namespaced -> features](unstable.md#namespaced-features). +```toml +[features] +gif = ["dep:gif"] +``` -Explicitly defined features can enable optional dependencies, too. Just -include the name of the optional dependency in the feature list. For example, -let's say in order to support the AVIF image format, our library needs two -other dependencies to be enabled: +This means that this dependency will only be included if the `gif` +feature is enabled. +The same `cfg(feature = "gif")` syntax can be used in the code, and the +dependency can be enabled just like any feature such as `--features gif` (see +[Command-line feature options](#command-line-feature-options) below). + +In some cases, you may not want to expose a feature that has the same name +as the optional dependency. +For example, perhaps the optional dependency is an internal detail, or you +want to group multiple optional dependencies together, or you just want to use +a better name. +If you specify the optional dependency with the `dep:` prefix anywhere +in the `[features]` table, that disables the implicit feature. + +> **Note**: The `dep:` syntax is only available starting with Rust 1.60. +> Previous versions can only use the implicit feature name. + +For example, let's say in order to support the AVIF image format, our library +needs two other dependencies to be enabled: ```toml [dependencies] @@ -138,10 +150,13 @@ ravif = { version = "0.6.3", optional = true } rgb = { version = "0.8.25", optional = true } [features] -avif = ["ravif", "rgb"] +avif = ["dep:ravif", "dep:rgb"] ``` In this example, the `avif` feature will enable the two listed dependencies. +This also avoids creating the implicit `ravif` and `rgb` features, since we +don't want users to enable those individually as they are internal details to +our crate. > **Note**: Another way to optionally include a dependency is to use > [platform-specific dependencies]. Instead of using features, these are @@ -185,10 +200,31 @@ jpeg-decoder = { version = "0.1.20", default-features = false } parallel = ["jpeg-decoder/rayon"] ``` -> **Note**: The `"package-name/feature-name"` syntax will also enable -> `package-name` if it is an optional dependency. Experimental support for -> disabling that behavior is available on the nightly channel via [weak -> dependency features](unstable.md#weak-dependency-features). +The `"package-name/feature-name"` syntax will also enable `package-name` +if it is an optional dependency. Often this is not what you want. +You can add a `?` as in `"package-name?/feature-name"` which will only enable +the given feature if something else enables the optional dependency. + +> **Note**: The `?` syntax is only available starting with Rust 1.60. + +For example, let's say we have added some serialization support to our +library, and it requires enabling a corresponding feature in some optional +dependencies. +That can be done like this: + +```toml +[dependencies] +serde = { version = "1.0.133", optional = true } +rgb = { version = "0.8.25", optional = true } + +[features] +serde = ["dep:serde", "rgb?/serde"] +``` + +In this example, enabling the `serde` feature will enable the serde +dependency. +It will also enable the `serde` feature for the `rgb` dependency, but only if +something else has enabled the `rgb` dependency. ### Command-line feature options diff --git a/src/doc/src/reference/registries.md b/src/doc/src/reference/registries.md index ef23caeee..45af03236 100644 --- a/src/doc/src/reference/registries.md +++ b/src/doc/src/reference/registries.md @@ -263,7 +263,45 @@ explaining the format of the entry. "yanked": false, // The `links` string value from the package's manifest, or null if not // specified. This field is optional and defaults to null. - "links": null + "links": null, + // An unsigned 32-bit integer value indicating the schema version of this + // entry. + // + // If this not specified, it should be interpreted as the default of 1. + // + // Cargo (starting with version 1.51) will ignore versions it does not + // recognize. This provides a method to safely introduce changes to index + // entries and allow older versions of cargo to ignore newer entries it + // doesn't understand. Versions older than 1.51 ignore this field, and + // thus may misinterpret the meaning of the index entry. + // + // The current values are: + // + // * 1: The schema as documented here, not including newer additions. + // This is honored in Rust version 1.51 and newer. + // * 2: The addition of the `features2` field. + // This is honored in Rust version 1.60 and newer. + "v": 2, + // This optional field contains features with new, extended syntax. + // Specifically, namespaced features (`dep:`) and weak dependencies + // (`pkg?/feat`). + // + // This is separated from `features` because versions older than 1.19 + // will fail to load due to not being able to parse the new syntax, even + // with a `Cargo.lock` file. + // + // Cargo will merge any values listed here with the "features" field. + // + // If this field is included, the "v" field should be set to at least 2. + // + // Registries are not required to use this field for extended feature + // syntax, they are allowed to include those in the "features" field. + // Using this is only necessary if the registry wants to support cargo + // versions older than 1.19, which in practice is only crates.io since + // those older versions do not support other registries. + "features2": { + "serde": ["dep:serde", "chrono?/serde"] + } } ``` diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index ff29e8f2b..48917ad4a 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -69,8 +69,6 @@ Each new feature described below should explain how to use it. * [avoid-dev-deps](#avoid-dev-deps) — Prevents the resolver from including dev-dependencies during resolution. * [minimal-versions](#minimal-versions) — Forces the resolver to use the lowest compatible version instead of the highest. * [public-dependency](#public-dependency) — Allows dependencies to be classified as either public or private. - * [Namespaced features](#namespaced-features) — Separates optional dependencies into a separate namespace from regular features, and allows feature names to be the same as some dependency name. - * [Weak dependency features](#weak-dependency-features) — Allows setting features for dependencies without enabling optional dependencies. * Output behavior * [out-dir](#out-dir) — Adds a directory where artifacts are copied to. * [terminal-width](#terminal-width) — Tells rustc the width of the terminal so that long diagnostic messages can be truncated to be more readable. @@ -252,68 +250,6 @@ dir-name = "lto" # Emits to target/lto instead of target/release-lto lto = true ``` - -### Namespaced features -* Original issue: [#1286](https://github.com/rust-lang/cargo/issues/1286) -* Tracking Issue: [#5565](https://github.com/rust-lang/cargo/issues/5565) - -The `namespaced-features` option makes two changes to how features can be -specified: - -* Features may now be defined with the same name as a dependency. -* Optional dependencies can be explicitly enabled in the `[features]` table - with the `dep:` prefix, which enables the dependency without enabling a - feature of the same name. - -By default, an optional dependency `foo` will define a feature `foo = -["dep:foo"]` *unless* `dep:foo` is mentioned in any other feature, or the -`foo` feature is already defined. This helps prevent unnecessary boilerplate -of listing every optional dependency, but still allows you to override the -implicit feature. - -This allows two use cases that were previously not possible: - -* You can "hide" an optional dependency, so that external users cannot - explicitly enable that optional dependency. -* There is no longer a need to create "funky" feature names to work around the - restriction that features cannot shadow dependency names. - -To enable namespaced-features, use the `-Z namespaced-features` command-line -flag. - -An example of hiding an optional dependency: - -```toml -[dependencies] -regex = { version = "1.4.1", optional = true } -lazy_static = { version = "1.4.0", optional = true } - -[features] -regex = ["dep:regex", "dep:lazy_static"] -``` - -In this example, the "regex" feature enables both `regex` and `lazy_static`. -The `lazy_static` feature does not exist, and a user cannot explicitly enable -it. This helps hide internal details of how your package is implemented. - -An example of avoiding "funky" names: - -```toml -[dependencies] -bigdecimal = "0.1" -chrono = "0.4" -num-bigint = "0.2" -serde = {version = "1.0", optional = true } - -[features] -serde = ["dep:serde", "bigdecimal/serde", "chrono/serde", "num-bigint/serde"] -``` - -In this case, `serde` is a natural name to use for a feature, because it is -relevant to your exported API. However, previously you would need to use a -name like `serde1` to work around the naming limitation if you wanted to also -enable other features. - ### Build-plan * Tracking Issue: [#5579](https://github.com/rust-lang/cargo/issues/5579) @@ -898,29 +834,6 @@ error[E0308]: mismatched types 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. - ### per-package-target * Tracking Issue: [#9406](https://github.com/rust-lang/cargo/pull/9406) * Original Pull Request: [#9030](https://github.com/rust-lang/cargo/pull/9030) @@ -1375,3 +1288,13 @@ The profile `strip` option has been stabilized in the 1.59 release. See the Support for generating a future-incompat report has been stabilized in the 1.59 release. See the [future incompat report chapter](future-incompat-report.md) for more information. + +### Namespaced features + +Namespaced features has been stabilized in the 1.60 release. +See the [Features chapter](features.md#optional-dependencies) for more information. + +### Weak dependency features + +Weak dependency features has been stabilized in the 1.60 release. +See the [Features chapter](features.md#dependency-features) for more information. diff --git a/tests/testsuite/features.rs b/tests/testsuite/features.rs index 49a61301b..8a8ed78e5 100644 --- a/tests/testsuite/features.rs +++ b/tests/testsuite/features.rs @@ -36,7 +36,8 @@ Caused by: } #[cargo_test] -fn invalid2() { +fn same_name() { + // Feature with the same name as a dependency. let p = project() .file( "Cargo.toml", @@ -59,14 +60,24 @@ fn invalid2() { .file("bar/src/lib.rs", "") .build(); - p.cargo("build") - .with_status(101) - .with_stderr( + p.cargo("tree -f") + .arg("{p} [{f}]") + .with_stderr("") + .with_stdout( "\ -[ERROR] failed to parse manifest at `[..]` +foo v0.0.1 ([..]) [] +└── bar v1.0.0 ([..]) [] +", + ) + .run(); -Caused by: - features and dependencies cannot have the same name: `bar` + p.cargo("tree --features bar -f") + .arg("{p} [{f}]") + .with_stderr("") + .with_stdout( + "\ +foo v0.0.1 ([..]) [bar,baz] +└── bar v1.0.0 ([..]) [] ", ) .run(); diff --git a/tests/testsuite/features_namespaced.rs b/tests/testsuite/features_namespaced.rs index 5b5ea47e7..78cdc2b15 100644 --- a/tests/testsuite/features_namespaced.rs +++ b/tests/testsuite/features_namespaced.rs @@ -4,99 +4,6 @@ use super::features2::switch_to_resolver_2; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::{project, publish}; -#[cargo_test] -fn gated() { - // Need namespaced-features to use `dep:` syntax. - Package::new("bar", "1.0.0").publish(); - let p = project() - .file( - "Cargo.toml", - r#" - [package] - name = "foo" - version = "0.1.0" - - [dependencies] - bar = { version = "1.0", optional = true } - - [features] - foo = ["dep:bar"] - "#, - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("check") - .with_status(101) - .with_stderr( - "\ -[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` - -Caused by: - namespaced features with the `dep:` prefix are only allowed on the nightly channel \ - and requires the `-Z namespaced-features` flag on the command-line -", - ) - .run(); -} - -#[cargo_test] -fn dependency_gate_ignored() { - // Dependencies with `dep:` features are ignored in the registry if not on nightly. - Package::new("baz", "1.0.0").publish(); - Package::new("bar", "1.0.0") - .add_dep(Dependency::new("baz", "1.0").optional(true)) - .feature("feat", &["dep:baz"]) - .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 `crates-io` -required by package `foo v0.1.0 ([..]/foo)` -", - ) - .run(); - - // Publish a version without namespaced features, it should ignore 1.0.0 - // and 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 dependency_with_crate_syntax() { // Registry dependency uses dep: syntax. @@ -120,8 +27,7 @@ fn dependency_with_crate_syntax() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_stderr( "\ [UPDATING] [..] @@ -156,8 +62,7 @@ fn namespaced_invalid_feature() { .file("src/main.rs", "") .build(); - p.cargo("build -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("build") .with_status(101) .with_stderr( "\ @@ -188,8 +93,7 @@ fn namespaced_invalid_dependency() { .file("src/main.rs", "") .build(); - p.cargo("build -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("build") .with_status(101) .with_stderr( "\ @@ -223,8 +127,8 @@ fn namespaced_non_optional_dependency() { .file("src/main.rs", "") .build(); - p.cargo("build -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("build") + .with_status(101) .with_stderr( "\ @@ -261,8 +165,7 @@ fn namespaced_implicit_feature() { .file("src/main.rs", "fn main() {}") .build(); - p.cargo("check -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_stderr( "\ [UPDATING] [..] @@ -271,8 +174,7 @@ fn namespaced_implicit_feature() { ", ) .run(); - p.cargo("check -Z namespaced-features --features baz") - .masquerade_as_nightly_cargo() + p.cargo("check --features baz") .with_stderr( "\ [DOWNLOADING] crates ... @@ -307,8 +209,7 @@ fn namespaced_shadowed_dep() { .file("src/main.rs", "fn main() {}") .build(); - p.cargo("build -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("build") .with_status(101) .with_stderr( "\ @@ -344,9 +245,7 @@ fn namespaced_shadowed_non_optional() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z namespaced-features") - .masquerade_as_nightly_cargo() - .run(); + p.cargo("check").run(); } #[cargo_test] @@ -370,7 +269,7 @@ fn namespaced_implicit_non_optional() { .file("src/main.rs", "fn main() {}") .build(); - p.cargo("build -Z namespaced-features").masquerade_as_nightly_cargo().with_status(101).with_stderr( + p.cargo("build").with_status(101).with_stderr( "\ [ERROR] failed to parse manifest at `[..]` @@ -411,8 +310,7 @@ fn namespaced_same_name() { ) .build(); - p.cargo("run -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("run") .with_stderr( "\ [UPDATING] [..] @@ -424,8 +322,7 @@ fn namespaced_same_name() { .with_stdout("") .run(); - p.cargo("run -Z namespaced-features --features baz") - .masquerade_as_nightly_cargo() + p.cargo("run --features baz") .with_stderr( "\ [DOWNLOADING] crates ... @@ -473,8 +370,7 @@ fn no_implicit_feature() { ) .build(); - p.cargo("run -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("run") .with_stderr( "\ [UPDATING] [..] @@ -486,8 +382,7 @@ fn no_implicit_feature() { .with_stdout("") .run(); - p.cargo("run -Z namespaced-features --features regex") - .masquerade_as_nightly_cargo() + p.cargo("run --features regex") .with_stderr_unordered( "\ [DOWNLOADING] crates ... @@ -503,8 +398,7 @@ fn no_implicit_feature() { .with_stdout("regex") .run(); - p.cargo("run -Z namespaced-features --features lazy_static") - .masquerade_as_nightly_cargo() + p.cargo("run --features lazy_static") .with_stderr( "\ [ERROR] Package `foo v0.1.0 [..]` does not have feature `lazy_static`. \ @@ -538,8 +432,7 @@ fn crate_syntax_bad_name() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z namespaced-features --features dep:bar") - .masquerade_as_nightly_cargo() + p.cargo("check --features dep:bar") .with_status(101) .with_stderr( "\ @@ -574,8 +467,7 @@ fn crate_syntax_in_dep() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_status(101) .with_stderr( "\ @@ -608,8 +500,7 @@ fn crate_syntax_cli() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z namespaced-features --features dep:bar") - .masquerade_as_nightly_cargo() + p.cargo("check --features dep:bar") .with_status(101) .with_stderr( "\ @@ -619,8 +510,7 @@ fn crate_syntax_cli() { .run(); switch_to_resolver_2(&p); - p.cargo("check -Z namespaced-features --features dep:bar") - .masquerade_as_nightly_cargo() + p.cargo("check --features dep:bar") .with_status(101) .with_stderr( "\ @@ -653,8 +543,7 @@ fn crate_required_features() { .file("src/main.rs", "fn main() {}") .build(); - p.cargo("check -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_status(101) .with_stderr( "\ @@ -685,8 +574,7 @@ fn json_exposed() { .file("src/lib.rs", "") .build(); - p.cargo("metadata -Z namespaced-features --no-deps") - .masquerade_as_nightly_cargo() + p.cargo("metadata --no-deps") .with_json( r#" { @@ -775,8 +663,7 @@ fn crate_feature_with_explicit() { ) .build(); - p.cargo("check -Z namespaced-features --features f1") - .masquerade_as_nightly_cargo() + p.cargo("check --features f1") .with_stderr( "\ [UPDATING] [..] @@ -814,8 +701,7 @@ fn optional_explicit_without_crate() { .file("src/lib.rs", "") .build(); - p.cargo("build -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("build") .with_status(101) .with_stderr( "\ @@ -856,13 +742,11 @@ fn tree() { .file("src/lib.rs", "") .build(); - p.cargo("tree -e features -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features") .with_stdout("foo v0.1.0 ([ROOT]/foo)") .run(); - p.cargo("tree -e features --features a -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --features a") .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) @@ -876,8 +760,7 @@ foo v0.1.0 ([ROOT]/foo) ) .run(); - p.cargo("tree -e features --features a -i bar -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --features a -i bar") .with_stdout( "\ bar v1.0.0 @@ -895,8 +778,7 @@ bar v1.0.0 ) .run(); - p.cargo("tree -e features --features bar -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --features bar") .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) @@ -910,8 +792,7 @@ foo v0.1.0 ([ROOT]/foo) ) .run(); - p.cargo("tree -e features --features bar -i bar -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --features bar -i bar") .with_stdout( "\ bar v1.0.0 @@ -948,13 +829,11 @@ fn tree_no_implicit() { .file("src/lib.rs", "") .build(); - p.cargo("tree -e features -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features") .with_stdout("foo v0.1.0 ([ROOT]/foo)") .run(); - p.cargo("tree -e features --all-features -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --all-features") .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) @@ -964,8 +843,7 @@ foo v0.1.0 ([ROOT]/foo) ) .run(); - p.cargo("tree -e features -i bar --all-features -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features -i bar --all-features") .with_stdout( "\ bar v1.0.0 @@ -1116,8 +994,7 @@ fn publish() { .file("src/lib.rs", "") .build(); - p.cargo("publish --token sekrit -Z namespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("publish --token sekrit") .with_stderr( "\ [UPDATING] [..] diff --git a/tests/testsuite/old_cargos.rs b/tests/testsuite/old_cargos.rs index 10179bc2b..b3d1e3ca5 100644 --- a/tests/testsuite/old_cargos.rs +++ b/tests/testsuite/old_cargos.rs @@ -558,8 +558,7 @@ fn index_cache_rebuild() { fs::remove_file(p.root().join("Cargo.lock")).unwrap(); // This should rebuild the cache and use 1.0.1. - p.cargo("check -Znamespaced-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_stderr( "\ [UPDATING] [..] diff --git a/tests/testsuite/weak_dep_features.rs b/tests/testsuite/weak_dep_features.rs index 42c4fcdc5..6d7d8857d 100644 --- a/tests/testsuite/weak_dep_features.rs +++ b/tests/testsuite/weak_dep_features.rs @@ -20,100 +20,6 @@ fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String { 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 `crates-io` -required by package `foo v0.1.0 ([..]/foo)` -", - ) - .run(); - - // Publish a version without the ? feature, it should ignore 1.0.0 - // and 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") @@ -140,8 +46,7 @@ fn simple() { // 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() + p.cargo("check --features f1") .with_stderr( "\ [UPDATING] [..] @@ -153,8 +58,7 @@ fn simple() { ) .run(); - p.cargo("check -Z weak-dep-features --features f1,bar") - .masquerade_as_nightly_cargo() + p.cargo("check --features f1,bar") .with_stderr( "\ [CHECKING] bar v1.0.0 @@ -196,8 +100,7 @@ fn deferred() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_stderr( "\ [UPDATING] [..] @@ -238,8 +141,7 @@ fn not_optional_dep() { .file("src/lib.rs", "") .build(); - p.cargo("check -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_status(101) .with_stderr("\ error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -275,8 +177,7 @@ fn optional_cli_syntax() { .build(); // Does not build bar. - p.cargo("check --features bar?/feat -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check --features bar?/feat") .with_stderr( "\ [UPDATING] [..] @@ -289,8 +190,7 @@ fn optional_cli_syntax() { .run(); // Builds bar. - p.cargo("check --features bar?/feat,bar -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check --features bar?/feat,bar") .with_stderr( "\ [CHECKING] bar v1.0.0 @@ -304,8 +204,7 @@ fn optional_cli_syntax() { switch_to_resolver_2(&p); p.build_dir().rm_rf(); // Does not build bar. - p.cargo("check --features bar?/feat -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check --features bar?/feat") .with_stderr( "\ [CHECKING] foo v0.1.0 [..] @@ -315,8 +214,7 @@ fn optional_cli_syntax() { .run(); // Builds bar. - p.cargo("check --features bar?/feat,bar -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check --features bar?/feat,bar") .with_stderr( "\ [CHECKING] bar v1.0.0 @@ -351,8 +249,7 @@ fn required_features() { .file("src/main.rs", "fn main() {}") .build(); - p.cargo("check -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("check") .with_status(101) .with_stderr( "\ @@ -366,7 +263,7 @@ optional dependency with `?` is not allowed in required-features #[cargo_test] fn weak_with_host_decouple() { - // -Z weak-opt-features with new resolver + // weak-dep-features with new resolver // // foo v0.1.0 // └── common v1.0.0 @@ -447,8 +344,7 @@ fn weak_with_host_decouple() { ) .build(); - p.cargo("run -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("run") .with_stderr( "\ [UPDATING] [..] @@ -493,8 +389,7 @@ fn weak_namespaced() { .file("src/lib.rs", &require(&["f1"], &["f2", "bar"])) .build(); - p.cargo("check -Z weak-dep-features -Z namespaced-features --features f1") - .masquerade_as_nightly_cargo() + p.cargo("check --features f1") .with_stderr( "\ [UPDATING] [..] @@ -506,21 +401,18 @@ fn weak_namespaced() { ) .run(); - p.cargo("tree -Z weak-dep-features -Z namespaced-features -f") + p.cargo("tree -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") + p.cargo("tree --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") + p.cargo("tree --features f1,f2 -f") .arg("{p} feats:{f}") - .masquerade_as_nightly_cargo() .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) feats:f1,f2 @@ -532,8 +424,7 @@ foo v0.1.0 ([ROOT]/foo) feats:f1,f2 // "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() + p.cargo("check --features f1,f2") .with_stderr( "\ [CHECKING] bar v1.0.0 @@ -568,13 +459,11 @@ fn tree() { .file("src/lib.rs", &require(&["f1"], &[])) .build(); - p.cargo("tree -Z weak-dep-features --features f1") - .masquerade_as_nightly_cargo() + p.cargo("tree --features f1") .with_stdout("foo v0.1.0 ([ROOT]/foo)") .run(); - p.cargo("tree -Z weak-dep-features --features f1,bar") - .masquerade_as_nightly_cargo() + p.cargo("tree --features f1,bar") .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) @@ -583,8 +472,7 @@ foo v0.1.0 ([ROOT]/foo) ) .run(); - p.cargo("tree -Z weak-dep-features --features f1,bar -e features") - .masquerade_as_nightly_cargo() + p.cargo("tree --features f1,bar -e features") .with_stdout( "\ foo v0.1.0 ([ROOT]/foo) @@ -594,8 +482,7 @@ foo v0.1.0 ([ROOT]/foo) ) .run(); - p.cargo("tree -Z weak-dep-features --features f1,bar -e features -i bar") - .masquerade_as_nightly_cargo() + p.cargo("tree --features f1,bar -e features -i bar") .with_stdout( "\ bar v1.0.0 @@ -610,20 +497,17 @@ bar v1.0.0 ) .run(); - p.cargo("tree -Z weak-dep-features -e features --features bar?/feat") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features --features bar?/feat") .with_stdout("foo v0.1.0 ([ROOT]/foo)") .run(); // This is a little strange in that it produces no output. // Maybe `cargo tree` should print a note about why? - p.cargo("tree -Z weak-dep-features -e features -i bar --features bar?/feat") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features -i bar --features bar?/feat") .with_stdout("") .run(); - p.cargo("tree -Z weak-dep-features -e features -i bar --features bar?/feat,bar") - .masquerade_as_nightly_cargo() + p.cargo("tree -e features -i bar --features bar?/feat,bar") .with_stdout( "\ bar v1.0.0 @@ -663,8 +547,7 @@ fn publish() { .file("src/lib.rs", "") .build(); - p.cargo("publish --token sekrit -Z weak-dep-features") - .masquerade_as_nightly_cargo() + p.cargo("publish --token sekrit") .with_stderr( "\ [UPDATING] [..]