Skip to content

Commit 4aa5223

Browse files
committed
Auto merge of #8997 - ehuss:stabilize-features, r=alexcrichton
Stabilize -Zfeatures and -Zpackage-features. This follows through with [RFC 2957](rust-lang/rfcs#2957) to stabilize the new feature resolver, and `-Zpackage-features` command-line changes. This also rewrites the "Features" chapter to try to expand it a little. I decided to leave the `-Zfeatures` flag in for now for testing, but it can be removed at a later date. There is a code change related to the `package-name/feature-name` syntax for the `--features` flag. I wanted to stabilize that for `resolver = "1"`, but I previously neglected to separate that behavior out, so it required change to `Workspace::members_with_features` to make that work (see the `resolver1_member_features` test). Closes #4328 Closes #5364 Closes #7914 Closes #7915 Closes #7916 Closes #8088 Closes #8431
2 parents 0583aa4 + a500276 commit 4aa5223

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1481
-1277
lines changed

src/cargo/core/compiler/unit_dependencies.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -518,12 +518,12 @@ fn dep_build_script(
518518
// build.rs unit use the same features. This is because some
519519
// people use `cfg!` and `#[cfg]` expressions to check for enabled
520520
// features instead of just checking `CARGO_FEATURE_*` at runtime.
521-
// In the case with `-Zfeatures=host_dep`, and a shared
522-
// dependency has different features enabled for normal vs. build,
523-
// then the build.rs script will get compiled twice. I believe it
524-
// is not feasible to only build it once because it would break a
525-
// large number of scripts (they would think they have the wrong
526-
// set of features enabled).
521+
// In the case with the new feature resolver (decoupled host
522+
// deps), and a shared dependency has different features enabled
523+
// for normal vs. build, then the build.rs script will get
524+
// compiled twice. I believe it is not feasible to only build it
525+
// once because it would break a large number of scripts (they
526+
// would think they have the wrong set of features enabled).
527527
let script_unit_for = UnitFor::new_host(unit_for.is_for_host_features());
528528
new_unit_dep_with_profile(
529529
state,

src/cargo/core/features.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ features! {
210210
[unstable] named_profiles: bool,
211211

212212
// Opt-in new-resolver behavior.
213-
[unstable] resolver: bool,
213+
[stable] resolver: bool,
214214

215215
// Allow to specify whether binaries should be stripped.
216216
[unstable] strip: bool,
@@ -338,7 +338,6 @@ pub struct CliUnstable {
338338
pub no_index_update: bool,
339339
pub avoid_dev_deps: bool,
340340
pub minimal_versions: bool,
341-
pub package_features: bool,
342341
pub advanced_env: bool,
343342
pub config_include: bool,
344343
pub dual_proc_macros: bool,
@@ -445,7 +444,6 @@ impl CliUnstable {
445444
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
446445
"avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
447446
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
448-
"package-features" => self.package_features = parse_empty(k, v)?,
449447
"advanced-env" => self.advanced_env = parse_empty(k, v)?,
450448
"config-include" => self.config_include = parse_empty(k, v)?,
451449
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,

src/cargo/core/resolver/features.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
//! Feature resolver.
22
//!
33
//! This is a new feature resolver that runs independently of the main
4-
//! dependency resolver. It is intended to make it easier to experiment with
5-
//! new behaviors. When `-Zfeatures` is not used, it will fall back to using
6-
//! the original `Resolve` feature computation. With `-Zfeatures` enabled,
7-
//! this will walk the dependency graph and compute the features using a
8-
//! different algorithm.
4+
//! dependency resolver. It is enabled when the user specifies `resolver =
5+
//! "2"` in `Cargo.toml`.
96
//!
107
//! One of its key characteristics is that it can avoid unifying features for
118
//! shared dependencies in some situations. See `FeatureOpts` for the
@@ -61,11 +58,11 @@ pub struct ResolvedFeatures {
6158
///
6259
/// The value is the `name_in_toml` of the dependencies.
6360
activated_dependencies: ActivateMap,
64-
/// This is only here for legacy support when `-Zfeatures` is not enabled.
61+
/// This is only here for legacy support when the new resolver is not enabled.
6562
///
6663
/// This is the set of features enabled for each package.
6764
legacy_features: Option<HashMap<PackageId, Vec<InternedString>>>,
68-
/// This is only here for legacy support when `-Zfeatures` is not enabled.
65+
/// This is only here for legacy support when the new resolver is not enabled.
6966
///
7067
/// This is the set of optional dependencies enabled for each package.
7168
legacy_dependencies: Option<HashMap<PackageId, HashSet<InternedString>>>,
@@ -75,7 +72,7 @@ pub struct ResolvedFeatures {
7572
/// Options for how the feature resolver works.
7673
#[derive(Default)]
7774
struct FeatureOpts {
78-
/// -Zfeatures is enabled, use new resolver.
75+
/// Use the new resolver instead of the old one.
7976
new_resolver: bool,
8077
/// Build deps and proc-macros will not share share features with other dep kinds.
8178
decouple_host_deps: bool,

src/cargo/core/workspace.rs

+64-17
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,15 @@ impl<'cfg> Workspace<'cfg> {
637637
self.resolve_behavior.unwrap_or(ResolveBehavior::V1)
638638
}
639639

640-
pub fn allows_unstable_package_features(&self) -> bool {
641-
self.config().cli_unstable().package_features
640+
/// Returns `true` if this workspace uses the new CLI features behavior.
641+
///
642+
/// The old behavior only allowed choosing the features from the package
643+
/// in the current directory, regardless of which packages were chosen
644+
/// with the -p flags. The new behavior allows selecting features from the
645+
/// packages chosen on the command line (with -p or --workspace flags),
646+
/// ignoring whatever is in the current directory.
647+
pub fn allows_new_cli_feature_behavior(&self) -> bool {
648+
self.is_virtual()
642649
|| match self.resolve_behavior() {
643650
ResolveBehavior::V1 => false,
644651
ResolveBehavior::V2 => true,
@@ -947,15 +954,16 @@ impl<'cfg> Workspace<'cfg> {
947954
.map(|m| (m, RequestedFeatures::new_all(true)))
948955
.collect());
949956
}
950-
if self.allows_unstable_package_features() {
951-
self.members_with_features_pf(specs, requested_features)
957+
if self.allows_new_cli_feature_behavior() {
958+
self.members_with_features_new(specs, requested_features)
952959
} else {
953-
self.members_with_features_stable(specs, requested_features)
960+
self.members_with_features_old(specs, requested_features)
954961
}
955962
}
956963

957-
/// New command-line feature selection with -Zpackage-features.
958-
fn members_with_features_pf(
964+
/// New command-line feature selection behavior with resolver = "2" or the
965+
/// root of a virtual workspace. See `allows_new_cli_feature_behavior`.
966+
fn members_with_features_new(
959967
&self,
960968
specs: &[PackageIdSpec],
961969
requested_features: &RequestedFeatures,
@@ -1053,30 +1061,69 @@ impl<'cfg> Workspace<'cfg> {
10531061
Ok(members)
10541062
}
10551063

1056-
/// This is the current "stable" behavior for command-line feature selection.
1057-
fn members_with_features_stable(
1064+
/// This is the "old" behavior for command-line feature selection.
1065+
/// See `allows_new_cli_feature_behavior`.
1066+
fn members_with_features_old(
10581067
&self,
10591068
specs: &[PackageIdSpec],
10601069
requested_features: &RequestedFeatures,
10611070
) -> CargoResult<Vec<(&Package, RequestedFeatures)>> {
1071+
// Split off any features with the syntax `member-name/feature-name` into a map
1072+
// so that those features can be applied directly to those workspace-members.
1073+
let mut member_specific_features: HashMap<&str, BTreeSet<InternedString>> = HashMap::new();
1074+
// Features for the member in the current directory.
1075+
let mut cwd_features = BTreeSet::new();
1076+
for feature in requested_features.features.iter() {
1077+
if let Some(index) = feature.find('/') {
1078+
let name = &feature[..index];
1079+
if specs.iter().any(|spec| spec.name() == name) {
1080+
member_specific_features
1081+
.entry(name)
1082+
.or_default()
1083+
.insert(InternedString::new(&feature[index + 1..]));
1084+
} else {
1085+
cwd_features.insert(*feature);
1086+
}
1087+
} else {
1088+
cwd_features.insert(*feature);
1089+
};
1090+
}
1091+
10621092
let ms = self.members().filter_map(|member| {
10631093
let member_id = member.package_id();
10641094
match self.current_opt() {
10651095
// The features passed on the command-line only apply to
10661096
// the "current" package (determined by the cwd).
10671097
Some(current) if member_id == current.package_id() => {
1068-
Some((member, requested_features.clone()))
1098+
let feats = RequestedFeatures {
1099+
features: Rc::new(cwd_features.clone()),
1100+
all_features: requested_features.all_features,
1101+
uses_default_features: requested_features.uses_default_features,
1102+
};
1103+
Some((member, feats))
10691104
}
10701105
_ => {
10711106
// Ignore members that are not enabled on the command-line.
10721107
if specs.iter().any(|spec| spec.matches(member_id)) {
1073-
// -p for a workspace member that is not the
1074-
// "current" one, don't use the local
1075-
// `--features`, only allow `--all-features`.
1076-
Some((
1077-
member,
1078-
RequestedFeatures::new_all(requested_features.all_features),
1079-
))
1108+
// -p for a workspace member that is not the "current"
1109+
// one.
1110+
//
1111+
// The odd behavior here is due to backwards
1112+
// compatibility. `--features` and
1113+
// `--no-default-features` used to only apply to the
1114+
// "current" package. As an extension, this allows
1115+
// member-name/feature-name to set member-specific
1116+
// features, which should be backwards-compatible.
1117+
let feats = RequestedFeatures {
1118+
features: Rc::new(
1119+
member_specific_features
1120+
.remove(member.name().as_str())
1121+
.unwrap_or_default(),
1122+
),
1123+
uses_default_features: true,
1124+
all_features: requested_features.all_features,
1125+
};
1126+
Some((member, feats))
10801127
} else {
10811128
// This member was not requested on the command-line, skip.
10821129
None

src/cargo/util/command_prelude.rs

-14
Original file line numberDiff line numberDiff line change
@@ -310,20 +310,6 @@ pub trait ArgMatchesExt {
310310
if config.cli_unstable().avoid_dev_deps {
311311
ws.set_require_optional_deps(false);
312312
}
313-
if ws.is_virtual() && !ws.allows_unstable_package_features() {
314-
// --all-features is actually honored. In general, workspaces and
315-
// feature flags are a bit of a mess right now.
316-
for flag in &["features", "no-default-features"] {
317-
if self._is_present(flag) {
318-
bail!(
319-
"--{} is not allowed in the root of a virtual workspace\n\
320-
note: while this was previously accepted, it didn't actually do anything\n\
321-
help: change the current directory to the package directory, or use the --manifest-path flag to the path of the package",
322-
flag
323-
);
324-
}
325-
}
326-
}
327313
Ok(ws)
328314
}
329315

src/cargo/util/toml/mod.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -883,19 +883,7 @@ impl TomlManifest {
883883
.unwrap()
884884
.clone();
885885
package.workspace = None;
886-
let mut cargo_features = self.cargo_features.clone();
887886
package.resolver = ws.resolve_behavior().to_manifest();
888-
if package.resolver.is_some() {
889-
// This should be removed when stabilizing.
890-
match &mut cargo_features {
891-
None => cargo_features = Some(vec!["resolver".to_string()]),
892-
Some(feats) => {
893-
if !feats.iter().any(|feat| feat == "resolver") {
894-
feats.push("resolver".to_string());
895-
}
896-
}
897-
}
898-
}
899887
if let Some(license_file) = &package.license_file {
900888
let license_path = Path::new(&license_file);
901889
let abs_license_path = paths::normalize_path(&package_root.join(license_path));
@@ -977,7 +965,7 @@ impl TomlManifest {
977965
patch: None,
978966
workspace: None,
979967
badges: self.badges.clone(),
980-
cargo_features,
968+
cargo_features: self.cargo_features.clone(),
981969
});
982970

983971
fn map_deps(

src/doc/man/generated_txt/cargo-bench.txt

+9-12
Original file line numberDiff line numberDiff line change
@@ -163,28 +163,25 @@ OPTIONS
163163
--tests --benches --examples.
164164

165165
Feature Selection
166-
The feature flags allow you to control the enabled features for the
167-
"current" package. The "current" package is the package in the current
168-
directory, or the one specified in --manifest-path. If running in the
169-
root of a virtual workspace, then the default features are selected for
170-
all workspace members, or all features if --all-features is specified.
166+
The feature flags allow you to control which features are enabled. When
167+
no feature options are given, the default feature is activated for every
168+
selected package.
171169

172-
When no feature options are given, the default feature is activated for
173-
every selected package.
170+
See the features documentation
171+
<https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options>
172+
for more details.
174173

175174
--features features
176-
Space or comma separated list of features to activate. These
177-
features only apply to the current directory's package. Features of
178-
direct dependencies may be enabled with <dep-name>/<feature-name>
175+
Space or comma separated list of features to activate. Features of
176+
workspace members may be enabled with package-name/feature-name
179177
syntax. This flag may be specified multiple times, which enables all
180178
specified features.
181179

182180
--all-features
183181
Activate all available features of all selected packages.
184182

185183
--no-default-features
186-
Do not activate the default feature of the current directory's
187-
package.
184+
Do not activate the default feature of the selected packages.
188185

189186
Compilation Options
190187
--target triple

src/doc/man/generated_txt/cargo-build.txt

+9-12
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,25 @@ OPTIONS
106106
--tests --benches --examples.
107107

108108
Feature Selection
109-
The feature flags allow you to control the enabled features for the
110-
"current" package. The "current" package is the package in the current
111-
directory, or the one specified in --manifest-path. If running in the
112-
root of a virtual workspace, then the default features are selected for
113-
all workspace members, or all features if --all-features is specified.
109+
The feature flags allow you to control which features are enabled. When
110+
no feature options are given, the default feature is activated for every
111+
selected package.
114112

115-
When no feature options are given, the default feature is activated for
116-
every selected package.
113+
See the features documentation
114+
<https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options>
115+
for more details.
117116

118117
--features features
119-
Space or comma separated list of features to activate. These
120-
features only apply to the current directory's package. Features of
121-
direct dependencies may be enabled with <dep-name>/<feature-name>
118+
Space or comma separated list of features to activate. Features of
119+
workspace members may be enabled with package-name/feature-name
122120
syntax. This flag may be specified multiple times, which enables all
123121
specified features.
124122

125123
--all-features
126124
Activate all available features of all selected packages.
127125

128126
--no-default-features
129-
Do not activate the default feature of the current directory's
130-
package.
127+
Do not activate the default feature of the selected packages.
131128

132129
Compilation Options
133130
--target triple

src/doc/man/generated_txt/cargo-check.txt

+9-12
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,25 @@ OPTIONS
112112
--tests --benches --examples.
113113

114114
Feature Selection
115-
The feature flags allow you to control the enabled features for the
116-
"current" package. The "current" package is the package in the current
117-
directory, or the one specified in --manifest-path. If running in the
118-
root of a virtual workspace, then the default features are selected for
119-
all workspace members, or all features if --all-features is specified.
115+
The feature flags allow you to control which features are enabled. When
116+
no feature options are given, the default feature is activated for every
117+
selected package.
120118

121-
When no feature options are given, the default feature is activated for
122-
every selected package.
119+
See the features documentation
120+
<https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options>
121+
for more details.
123122

124123
--features features
125-
Space or comma separated list of features to activate. These
126-
features only apply to the current directory's package. Features of
127-
direct dependencies may be enabled with <dep-name>/<feature-name>
124+
Space or comma separated list of features to activate. Features of
125+
workspace members may be enabled with package-name/feature-name
128126
syntax. This flag may be specified multiple times, which enables all
129127
specified features.
130128

131129
--all-features
132130
Activate all available features of all selected packages.
133131

134132
--no-default-features
135-
Do not activate the default feature of the current directory's
136-
package.
133+
Do not activate the default feature of the selected packages.
137134

138135
Compilation Options
139136
--target triple

0 commit comments

Comments
 (0)