Skip to content

Commit 70f16f4

Browse files
committed
feat: add the package feature unification option
1 parent 73b5b33 commit 70f16f4

File tree

3 files changed

+65
-121
lines changed

3 files changed

+65
-121
lines changed

src/cargo/ops/resolve.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ pub fn resolve_ws_with_opts<'gctx>(
162162
FeatureUnification::Workspace => {
163163
vec![ops::Packages::All(Vec::new()).to_package_id_specs(ws)?]
164164
}
165+
FeatureUnification::Package => specs.iter().map(|spec| vec![spec.clone()]).collect(),
165166
};
166167
let specs: Vec<_> = individual_specs
167168
.iter()
@@ -269,7 +270,28 @@ pub fn resolve_ws_with_opts<'gctx>(
269270
for specs in individual_specs {
270271
let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
271272

273+
// We want to narrow the features to the current specs so that stuff like `cargo check -p a
274+
// -p b -F a/a,b/b` works and the resolver does not contain that `a` does not have feature
275+
// `b` and vice-versa. However, resolver v1 needs to see even features of unselected
276+
// packages turned on if it was because of working directory being inside the unselected
277+
// package, because they might turn on a feature of a selected package.
272278
let narrowed_features = match feature_unification {
279+
FeatureUnification::Package => {
280+
let mut narrowed_features = cli_features.clone();
281+
let enabled_features = members_with_features
282+
.iter()
283+
.filter_map(|(package, cli_features)| {
284+
specs
285+
.iter()
286+
.any(|spec| spec.matches(package.package_id()))
287+
.then_some(cli_features.features.iter())
288+
})
289+
.flatten()
290+
.cloned()
291+
.collect();
292+
narrowed_features.features = Rc::new(enabled_features);
293+
Cow::Owned(narrowed_features)
294+
}
273295
FeatureUnification::Selected | FeatureUnification::Workspace => {
274296
Cow::Borrowed(cli_features)
275297
}

src/cargo/util/context/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,6 +2864,7 @@ pub enum IncompatibleRustVersions {
28642864
#[derive(Copy, Clone, Debug, Deserialize)]
28652865
#[serde(rename_all = "kebab-case")]
28662866
pub enum FeatureUnification {
2867+
Package,
28672868
Selected,
28682869
Workspace,
28692870
}

tests/testsuite/feature_unification.rs

Lines changed: 42 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn package_feature_unification() {
136136
".cargo/config.toml",
137137
r#"
138138
[resolver]
139-
feature-unification = "selected"
139+
feature-unification = "package"
140140
"#,
141141
)
142142
.file(
@@ -245,14 +245,21 @@ fn package_feature_unification() {
245245
p.cargo("check -p a -p b")
246246
.arg("-Zfeature-unification")
247247
.masquerade_as_nightly_cargo(&["feature-unification"])
248-
.with_status(101)
249-
.with_stderr_contains("[ERROR] features were unified")
248+
.with_stderr_data(
249+
str![[r#"
250+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
251+
252+
"#]]
253+
.unordered(),
254+
)
250255
.run();
251256
p.cargo("check")
252257
.arg("-Zfeature-unification")
253258
.masquerade_as_nightly_cargo(&["feature-unification"])
254-
.with_status(101)
255-
.with_stderr_contains("[ERROR] features were unified")
259+
.with_stderr_data(str![[r#"
260+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
261+
262+
"#]])
256263
.run();
257264
// Sanity check that compilation without package feature unification does not work
258265
p.cargo("check -p a -p b")
@@ -271,7 +278,7 @@ fn package_feature_unification_default_features() {
271278
".cargo/config.toml",
272279
r#"
273280
[resolver]
274-
feature-unification = "selected"
281+
feature-unification = "package"
275282
"#,
276283
)
277284
.file(
@@ -366,17 +373,9 @@ fn package_feature_unification_default_features() {
366373
p.cargo("check")
367374
.arg("-Zfeature-unification")
368375
.masquerade_as_nightly_cargo(&["feature-unification"])
369-
.with_status(101)
370376
.with_stderr_data(
371377
str![[r#"
372-
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
373-
[ERROR] features were unified
374-
--> common/src/lib.rs:3:17
375-
|
376-
3 | compile_error!("features were unified");
377-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
378-
379-
[ERROR] could not compile `common` (lib) due to 1 previous error
378+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
380379
381380
"#]]
382381
.unordered(),
@@ -407,7 +406,7 @@ fn package_feature_unification_cli_features() {
407406
".cargo/config.toml",
408407
r#"
409408
[resolver]
410-
feature-unification = "selected"
409+
feature-unification = "package"
411410
"#,
412411
)
413412
.file(
@@ -481,30 +480,17 @@ fn package_feature_unification_cli_features() {
481480
p.cargo("check -p a -p b -F a,b")
482481
.arg("-Zfeature-unification")
483482
.masquerade_as_nightly_cargo(&["feature-unification"])
484-
.with_status(101)
485483
.with_stderr_data(
486484
str![[r#"
485+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
487486
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
488-
[ERROR] features were unified
489-
--> common/src/lib.rs:3:17
490-
|
491-
3 | compile_error!("features were unified");
492-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
493-
494-
[ERROR] could not compile `common` (lib) due to 1 previous error
495487
[UPDATING] `dummy-registry` index
496488
[LOCKING] 1 package to latest compatible version
497489
[DOWNLOADING] crates ...
498490
[DOWNLOADED] outside v0.1.0 (registry `dummy-registry`)
499491
[CHECKING] outside v0.1.0
500-
[ERROR] features were unified
501-
--> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17
502-
|
503-
3 | compile_error!("features were unified");
504-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
505-
506-
[ERROR] could not compile `outside` (lib) due to 1 previous error
507-
[WARNING] build failed, waiting for other jobs to finish...
492+
[CHECKING] b v0.1.0 ([ROOT]/foo/b)
493+
[CHECKING] a v0.1.0 ([ROOT]/foo/a)
508494
509495
"#]]
510496
.unordered(),
@@ -513,26 +499,9 @@ fn package_feature_unification_cli_features() {
513499
p.cargo("check --workspace --exclude common -F a,b")
514500
.arg("-Zfeature-unification")
515501
.masquerade_as_nightly_cargo(&["feature-unification"])
516-
.with_status(101)
517502
.with_stderr_data(
518503
str![[r#"
519-
[CHECKING] outside v0.1.0
520-
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
521-
[ERROR] features were unified
522-
--> common/src/lib.rs:3:17
523-
|
524-
3 | compile_error!("features were unified");
525-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
526-
527-
[ERROR] features were unified
528-
--> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17
529-
|
530-
3 | compile_error!("features were unified");
531-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
532-
533-
[ERROR] could not compile `outside` (lib) due to 1 previous error
534-
[WARNING] build failed, waiting for other jobs to finish...
535-
[ERROR] could not compile `common` (lib) due to 1 previous error
504+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
536505
537506
"#]]
538507
.unordered(),
@@ -542,26 +511,9 @@ fn package_feature_unification_cli_features() {
542511
p.cargo("check -p a -p b -F a/a,b/b")
543512
.arg("-Zfeature-unification")
544513
.masquerade_as_nightly_cargo(&["feature-unification"])
545-
.with_status(101)
546514
.with_stderr_data(
547515
str![[r#"
548-
[CHECKING] outside v0.1.0
549-
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
550-
[ERROR] features were unified
551-
--> common/src/lib.rs:3:17
552-
|
553-
3 | compile_error!("features were unified");
554-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
555-
556-
[ERROR] features were unified
557-
--> [ROOT]/home/.cargo/registry/src/-[HASH]/outside-0.1.0/src/lib.rs:3:17
558-
|
559-
3 | compile_error!("features were unified");
560-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
561-
562-
[ERROR] could not compile `common` (lib) due to 1 previous error
563-
[WARNING] build failed, waiting for other jobs to finish...
564-
[ERROR] could not compile `outside` (lib) due to 1 previous error
516+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
565517
566518
"#]]
567519
.unordered(),
@@ -618,7 +570,7 @@ fn package_feature_unification_weak_dependencies() {
618570
".cargo/config.toml",
619571
r#"
620572
[resolver]
621-
feature-unification = "selected"
573+
feature-unification = "package"
622574
"#,
623575
)
624576
.file(
@@ -690,17 +642,12 @@ fn package_feature_unification_weak_dependencies() {
690642
p.cargo("check -p a -p b")
691643
.arg("-Zfeature-unification")
692644
.masquerade_as_nightly_cargo(&["feature-unification"])
693-
.with_status(101)
694645
.with_stderr_data(
695646
str![[r#"
696647
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
697-
[ERROR] features were unified
698-
--> common/src/lib.rs:3:17
699-
|
700-
3 | compile_error!("features were unified");
701-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
702-
703-
[ERROR] could not compile `common` (lib) due to 1 previous error
648+
[CHECKING] a v0.1.0 ([ROOT]/foo/a)
649+
[CHECKING] b v0.1.0 ([ROOT]/foo/b)
650+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
704651
705652
"#]]
706653
.unordered(),
@@ -709,16 +656,9 @@ fn package_feature_unification_weak_dependencies() {
709656
p.cargo("check")
710657
.arg("-Zfeature-unification")
711658
.masquerade_as_nightly_cargo(&["feature-unification"])
712-
.with_status(101)
713659
.with_stderr_data(str![[r#"
714660
[CHECKING] common v0.1.0 ([ROOT]/foo/common)
715-
[ERROR] features were unified
716-
--> common/src/lib.rs:3:17
717-
|
718-
3 | compile_error!("features were unified");
719-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
720-
721-
[ERROR] could not compile `common` (lib) due to 1 previous error
661+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
722662
723663
"#]])
724664
.run();
@@ -873,27 +813,27 @@ common v0.1.0 ([ROOT]/foo/common)
873813
p.cargo("tree -e features")
874814
.arg("-Zfeature-unification")
875815
.masquerade_as_nightly_cargo(&["feature-unification"])
876-
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") // To be changed to package later
816+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
877817
.with_stdout_data(str![[r#"
818+
common v0.1.0 ([ROOT]/foo/common)
878819
a v0.1.0 ([ROOT]/foo/a)
879820
├── common feature "a"
880821
│ └── common v0.1.0 ([ROOT]/foo/common)
881-
├── common feature "default" (command-line)
822+
├── common feature "default"
882823
│ └── common v0.1.0 ([ROOT]/foo/common)
883824
├── outside feature "a"
884825
│ └── outside v0.1.0
885826
└── outside feature "default"
886827
└── outside v0.1.0
887-
888828
b v0.1.0 ([ROOT]/foo/b)
889829
├── common feature "b"
890830
│ └── common v0.1.0 ([ROOT]/foo/common)
891-
├── common feature "default" (command-line) (*)
831+
├── common feature "default"
832+
│ └── common v0.1.0 ([ROOT]/foo/common)
892833
├── outside feature "b"
893834
│ └── outside v0.1.0
894-
└── outside feature "default" (*)
895-
896-
common v0.1.0 ([ROOT]/foo/common)
835+
└── outside feature "default"
836+
└── outside v0.1.0
897837
898838
"#]])
899839
.run();
@@ -973,7 +913,7 @@ fn cargo_install_ignores_config() {
973913
.arg(p.root())
974914
.arg("-Zfeature-unification")
975915
.masquerade_as_nightly_cargo(&["feature-unification"])
976-
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected")
916+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
977917
.with_stderr_data(str![[r#"
978918
[INSTALLING] a v0.1.0 ([ROOT]/foo)
979919
[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s
@@ -1029,7 +969,7 @@ edition = "2021"
1029969
.build();
1030970

1031971
p.cargo("fix --edition --allow-no-vcs")
1032-
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected")
972+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1033973
.arg("-Zfeature-unification")
1034974
.masquerade_as_nightly_cargo(&["feature-unification"])
1035975
.with_stderr_data(str![[r#"
@@ -1111,10 +1051,10 @@ fn edition_v2_resolver_report() {
11111051
.build();
11121052

11131053
p.cargo("fix --edition --allow-no-vcs --workspace")
1114-
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected")
1054+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
11151055
.arg("-Zfeature-unification")
11161056
.masquerade_as_nightly_cargo(&["feature-unification"])
1117-
.with_status(0)
1057+
.with_status(101)
11181058
.with_stderr_data(
11191059
str![[r#"
11201060
[MIGRATING] Cargo.toml from 2018 edition to 2021
@@ -1125,26 +1065,7 @@ fn edition_v2_resolver_report() {
11251065
[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`)
11261066
[DOWNLOADED] opt_dep v1.0.0 (registry `dummy-registry`)
11271067
[MIGRATING] bar/Cargo.toml from 2018 edition to 2021
1128-
[NOTE] Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo.
1129-
This may cause some dependencies to be built with fewer features enabled than previously.
1130-
More information about the resolver changes may be found at https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html
1131-
When building the following dependencies, the given features will no longer be used:
1132-
1133-
common v1.0.0 removed features: dev-feat, f1, opt_dep
1134-
common v1.0.0 (as host dependency) removed features: dev-feat, f1
1135-
1136-
The following differences only apply when building with dev-dependencies:
1137-
1138-
common v1.0.0 removed features: f1, opt_dep
1139-
1140-
[CHECKING] opt_dep v1.0.0
1141-
[CHECKING] bar v1.0.0
1142-
[CHECKING] bar v0.1.0 ([ROOT]/foo/bar)
1143-
[MIGRATING] bar/src/lib.rs from 2018 edition to 2021
1144-
[CHECKING] common v1.0.0
1145-
[CHECKING] foo v0.1.0 ([ROOT]/foo)
1146-
[MIGRATING] src/lib.rs from 2018 edition to 2021
1147-
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
1068+
[ERROR] cannot fix edition when using `feature-unification = "package"`.
11481069
11491070
"#]]
11501071
.unordered(),
@@ -1213,7 +1134,7 @@ fn feature_unification_of_cli_features_within_workspace() {
12131134
p.cargo("check -p parent -F grandchild/a")
12141135
.arg("-Zfeature-unification")
12151136
.masquerade_as_nightly_cargo(&["feature-unification"])
1216-
// .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1137+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
12171138
.with_status(101)
12181139
.with_stderr_data(str![[r#"
12191140
[ERROR] the package 'parent' does not contain this feature: grandchild/a
@@ -1246,7 +1167,7 @@ fn feature_unification_of_cli_features_within_workspace() {
12461167
p.cargo("check -p child -F grandchild/a")
12471168
.arg("-Zfeature-unification")
12481169
.masquerade_as_nightly_cargo(&["feature-unification"])
1249-
// .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1170+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
12501171
.with_stderr_data(str![[r#"
12511172
[CHECKING] grandchild v0.1.0 ([ROOT]/foo/grandchild)
12521173
[CHECKING] child v0.1.0 ([ROOT]/foo/child)
@@ -1278,7 +1199,7 @@ fn feature_unification_of_cli_features_within_workspace() {
12781199
p.cargo("check -F grandchild/a")
12791200
.arg("-Zfeature-unification")
12801201
.masquerade_as_nightly_cargo(&["feature-unification"])
1281-
// .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1202+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
12821203
.with_stderr_data(str![[r#"
12831204
[CHECKING] parent v0.1.0 ([ROOT]/foo/parent)
12841205
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -1309,7 +1230,7 @@ fn feature_unification_of_cli_features_within_workspace() {
13091230
p.cargo("check -F grandchild/a --workspace --exclude grandchild")
13101231
.arg("-Zfeature-unification")
13111232
.masquerade_as_nightly_cargo(&["feature-unification"])
1312-
// .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1233+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
13131234
.with_stderr_data(str![[r#"
13141235
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
13151236
@@ -1339,7 +1260,7 @@ fn feature_unification_of_cli_features_within_workspace() {
13391260
p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child")
13401261
.arg("-Zfeature-unification")
13411262
.masquerade_as_nightly_cargo(&["feature-unification"])
1342-
// .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
1263+
.env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package")
13431264
.with_status(101)
13441265
.with_stderr_data(str![[r#"
13451266
[ERROR] the package 'parent' does not contain this feature: grandchild/a

0 commit comments

Comments
 (0)