Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize namespaced and weak dependency features. #10269

Merged
merged 1 commit into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion crates/crates-io/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 0 additions & 2 deletions crates/crates-io/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ pub struct NewCrate {
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)]
Expand Down
4 changes: 1 addition & 3 deletions src/bin/cargo/commands/read_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
10 changes: 6 additions & 4 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -652,7 +651,6 @@ unstable_cli_options!(
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"),
Expand Down Expand Up @@ -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<Option<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
Expand Down Expand Up @@ -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 {
Expand Down
36 changes: 14 additions & 22 deletions src/cargo/core/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<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(),
Expand Down
106 changes: 19 additions & 87 deletions src/cargo/core/resolver/features.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down Expand Up @@ -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<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.
Expand Down Expand Up @@ -132,7 +117,6 @@ impl FeatureOpts {
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,
Expand All @@ -159,26 +143,13 @@ 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;
}
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)
}

Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -336,30 +299,28 @@ impl ResolvedFeatures {
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)
}
}

/// Compares the result against the original resolver behavior.
///
/// 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<_> =
Expand Down Expand Up @@ -427,17 +388,6 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
) -> 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,
Expand All @@ -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,
})
}
Expand Down Expand Up @@ -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<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
}
Loading