@@ -637,8 +637,15 @@ impl<'cfg> Workspace<'cfg> {
637
637
self . resolve_behavior . unwrap_or ( ResolveBehavior :: V1 )
638
638
}
639
639
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 ( )
642
649
|| match self . resolve_behavior ( ) {
643
650
ResolveBehavior :: V1 => false ,
644
651
ResolveBehavior :: V2 => true ,
@@ -947,15 +954,16 @@ impl<'cfg> Workspace<'cfg> {
947
954
. map ( |m| ( m, RequestedFeatures :: new_all ( true ) ) )
948
955
. collect ( ) ) ;
949
956
}
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)
952
959
} else {
953
- self . members_with_features_stable ( specs, requested_features)
960
+ self . members_with_features_old ( specs, requested_features)
954
961
}
955
962
}
956
963
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 (
959
967
& self ,
960
968
specs : & [ PackageIdSpec ] ,
961
969
requested_features : & RequestedFeatures ,
@@ -1053,30 +1061,69 @@ impl<'cfg> Workspace<'cfg> {
1053
1061
Ok ( members)
1054
1062
}
1055
1063
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 (
1058
1067
& self ,
1059
1068
specs : & [ PackageIdSpec ] ,
1060
1069
requested_features : & RequestedFeatures ,
1061
1070
) -> 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
+
1062
1092
let ms = self . members ( ) . filter_map ( |member| {
1063
1093
let member_id = member. package_id ( ) ;
1064
1094
match self . current_opt ( ) {
1065
1095
// The features passed on the command-line only apply to
1066
1096
// the "current" package (determined by the cwd).
1067
1097
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) )
1069
1104
}
1070
1105
_ => {
1071
1106
// Ignore members that are not enabled on the command-line.
1072
1107
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) )
1080
1127
} else {
1081
1128
// This member was not requested on the command-line, skip.
1082
1129
None
0 commit comments