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"
[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"
pub repository: Option<String>,
pub badges: BTreeMap<String, BTreeMap<String, String>>,
pub links: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub v: Option<u32>,
}
#[derive(Serialize)]
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(())
}
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"),
terminal_width: Option<Option<usize>> = ("Provide a terminal width to rustc for error truncation"),
timings: Option<Vec<String>> = ("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<String> = ("Allow rustdoc to scrape examples from reverse-dependencies for documentation"),
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<Option<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
"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 {
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();
.filter(|t| t.src_path().is_path())
.cloned()
.collect();
- let features = if config.cli_unstable().namespaced_features {
- // Convert Vec<FeatureValue> to Vec<InternedString>
- 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<FeatureValue> to Vec<InternedString>
+ 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(),
//! 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`.
///
/// 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<HashMap<PackageId, Vec<InternedString>>>,
- /// 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<HashMap<PackageId, HashSet<InternedString>>>,
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.
let mut opts = FeatureOpts::default();
let unstable_flags = ws.config().cli_unstable();
let mut enable = |feat_opts: &Vec<String>| {
- opts.new_resolver = true;
for opt in feat_opts {
match opt.as_ref() {
"build_dep" | "host_dep" => opts.decouple_host_deps = true,
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;
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)
}
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,
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
pkg_id: PackageId,
features_for: FeaturesFor,
) -> CargoResult<Vec<InternedString>> {
- 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)
}
}
///
/// 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<_> =
) -> CargoResult<ResolvedFeatures> {
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,
Ok(ResolvedFeatures {
activated_features: r.activated_features,
activated_dependencies: r.activated_dependencies,
- legacy_features: None,
- legacy_dependencies: None,
opts: r.opts,
})
}
.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<PackageId, HashSet<InternedString>> {
- let mut result: HashMap<PackageId, HashSet<InternedString>> = 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
-}
package_id: PackageId,
dependencies: Vec<Dependency>,
features: Rc<FeatureMap>,
- has_namespaced_features: bool,
- has_overlapping_features: Option<InternedString>,
checksum: Option<String>,
links: Option<InternedString>,
}
features: &BTreeMap<InternedString, Vec<InternedString>>,
links: Option<impl Into<InternedString>>,
) -> CargoResult<Summary> {
- // ****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: `{}`",
)
}
}
- 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,
features: Rc::new(feature_map),
checksum: None,
links: links.map(|l| l.into()),
- has_namespaced_features,
- has_overlapping_features,
}),
})
}
&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()
}
/// 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<InternedString, Vec<InternedString>>,
dependencies: &[Dependency],
-) -> CargoResult<(FeatureMap, bool)> {
+) -> CargoResult<FeatureMap> {
use self::FeatureValue::*;
let mut dep_map = HashMap::new();
for dep in dependencies.iter() {
(*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.
);
}
- Ok((map, has_namespaced_features))
+ Ok(map)
}
/// FeatureValue represents the types of dependencies a feature can have.
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)?;
);
}
// 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 {
license_file: license_file.clone(),
badges: badges.clone(),
links: links.clone(),
- v: None,
},
tarball,
)
{
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
// 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()
},
)
.filter(move |is| {
- if is.v > max_version {
+ if is.v > INDEX_V_MAX {
debug!(
"unsupported schema version {} ({} {})",
is.v,
} else {
true
}
- })
- .filter(move |is| {
- is.summary
- .unstable_gate(namespaced_features, weak_dep_features)
- .is_ok()
}))
}
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(),
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).
-
-> **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).
-
-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:
+By default, this optional dependency implicitly defines a feature that looks
+like this:
+
+```toml
+[features]
+gif = ["dep:gif"]
+```
+
+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]
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
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
"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"]
+ }
}
```
* [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.
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)
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)
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.
}
#[cargo_test]
-fn invalid2() {
+fn same_name() {
+ // Feature with the same name as a dependency.
let p = project()
.file(
"Cargo.toml",
.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();
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.
.file("src/lib.rs", "")
.build();
- p.cargo("check -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("check")
.with_stderr(
"\
[UPDATING] [..]
.file("src/main.rs", "")
.build();
- p.cargo("build -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("build")
.with_status(101)
.with_stderr(
"\
.file("src/main.rs", "")
.build();
- p.cargo("build -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("build")
.with_status(101)
.with_stderr(
"\
.file("src/main.rs", "")
.build();
- p.cargo("build -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("build")
+
.with_status(101)
.with_stderr(
"\
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("check")
.with_stderr(
"\
[UPDATING] [..]
",
)
.run();
- p.cargo("check -Z namespaced-features --features baz")
- .masquerade_as_nightly_cargo()
+ p.cargo("check --features baz")
.with_stderr(
"\
[DOWNLOADING] crates ...
.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(
"\
.file("src/lib.rs", "")
.build();
- p.cargo("check -Z namespaced-features")
- .masquerade_as_nightly_cargo()
- .run();
+ p.cargo("check").run();
}
#[cargo_test]
.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 `[..]`
)
.build();
- p.cargo("run -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("run")
.with_stderr(
"\
[UPDATING] [..]
.with_stdout("")
.run();
- p.cargo("run -Z namespaced-features --features baz")
- .masquerade_as_nightly_cargo()
+ p.cargo("run --features baz")
.with_stderr(
"\
[DOWNLOADING] crates ...
)
.build();
- p.cargo("run -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("run")
.with_stderr(
"\
[UPDATING] [..]
.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 ...
.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`. \
.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(
"\
.file("src/lib.rs", "")
.build();
- p.cargo("check -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("check")
.with_status(101)
.with_stderr(
"\
.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(
"\
.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(
"\
.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(
"\
.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#"
{
)
.build();
- p.cargo("check -Z namespaced-features --features f1")
- .masquerade_as_nightly_cargo()
+ p.cargo("check --features f1")
.with_stderr(
"\
[UPDATING] [..]
.file("src/lib.rs", "")
.build();
- p.cargo("build -Z namespaced-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("build")
.with_status(101)
.with_stderr(
"\
.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)
)
.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
)
.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)
)
.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
.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)
)
.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
.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] [..]
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] [..]
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")
// 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] [..]
)
.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
.file("src/lib.rs", "")
.build();
- p.cargo("check -Z weak-dep-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("check")
.with_stderr(
"\
[UPDATING] [..]
.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`
.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] [..]
.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
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 [..]
.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
.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(
"\
#[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
)
.build();
- p.cargo("run -Z weak-dep-features")
- .masquerade_as_nightly_cargo()
+ p.cargo("run")
.with_stderr(
"\
[UPDATING] [..]
.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] [..]
)
.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
// "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
.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)
)
.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)
)
.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
)
.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
.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] [..]