@@ -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
0 commit comments