24
24
25
25
use std:: collections:: { BTreeSet , HashMap , HashSet } ;
26
26
use std:: hash:: { Hash , Hasher } ;
27
- use std:: iter:: FromIterator ;
28
27
use std:: sync:: Arc ;
29
28
30
29
use crate :: core:: compiler:: standard_lib;
@@ -41,8 +40,11 @@ use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
41
40
use crate :: ops;
42
41
use crate :: ops:: resolve:: WorkspaceResolve ;
43
42
use crate :: util:: config:: Config ;
43
+ use crate :: util:: restricted_names:: is_glob_pattern;
44
44
use crate :: util:: { closest_msg, profile, CargoResult , StableHasher } ;
45
45
46
+ use anyhow:: Context as _;
47
+
46
48
/// Contains information about how a package should be compiled.
47
49
///
48
50
/// Note on distinction between `CompileOptions` and `BuildConfig`:
@@ -116,6 +118,7 @@ impl Packages {
116
118
} )
117
119
}
118
120
121
+ /// Converts selected packages from a workspace to `PackageIdSpec`s.
119
122
pub fn to_package_id_specs ( & self , ws : & Workspace < ' _ > ) -> CargoResult < Vec < PackageIdSpec > > {
120
123
let specs = match self {
121
124
Packages :: All => ws
@@ -124,33 +127,40 @@ impl Packages {
124
127
. map ( PackageIdSpec :: from_package_id)
125
128
. collect ( ) ,
126
129
Packages :: OptOut ( opt_out) => {
127
- let mut opt_out = BTreeSet :: from_iter ( opt_out. iter ( ) . cloned ( ) ) ;
128
- let packages = ws
130
+ let ( mut patterns , mut names ) = opt_patterns_and_names ( opt_out) ? ;
131
+ let specs = ws
129
132
. members ( )
130
- . filter ( |pkg| !opt_out. remove ( pkg. name ( ) . as_str ( ) ) )
133
+ . filter ( |pkg| {
134
+ !names. remove ( pkg. name ( ) . as_str ( ) ) && !match_patterns ( pkg, & mut patterns)
135
+ } )
131
136
. map ( Package :: package_id)
132
137
. map ( PackageIdSpec :: from_package_id)
133
138
. collect ( ) ;
134
- if !opt_out. is_empty ( ) {
135
- ws. config ( ) . shell ( ) . warn ( format ! (
136
- "excluded package(s) {} not found in workspace `{}`" ,
137
- opt_out
138
- . iter( )
139
- . map( |x| x. as_ref( ) )
140
- . collect:: <Vec <_>>( )
141
- . join( ", " ) ,
142
- ws. root( ) . display( ) ,
143
- ) ) ?;
144
- }
145
- packages
139
+ let warn = |e| ws. config ( ) . shell ( ) . warn ( e) ;
140
+ emit_package_not_found ( ws, names, true ) . or_else ( warn) ?;
141
+ emit_pattern_not_found ( ws, patterns, true ) . or_else ( warn) ?;
142
+ specs
146
143
}
147
144
Packages :: Packages ( packages) if packages. is_empty ( ) => {
148
145
vec ! [ PackageIdSpec :: from_package_id( ws. current( ) ?. package_id( ) ) ]
149
146
}
150
- Packages :: Packages ( packages) => packages
151
- . iter ( )
152
- . map ( |p| PackageIdSpec :: parse ( p) )
153
- . collect :: < CargoResult < Vec < _ > > > ( ) ?,
147
+ Packages :: Packages ( opt_in) => {
148
+ let ( mut patterns, packages) = opt_patterns_and_names ( opt_in) ?;
149
+ let mut specs = packages
150
+ . iter ( )
151
+ . map ( |p| PackageIdSpec :: parse ( p) )
152
+ . collect :: < CargoResult < Vec < _ > > > ( ) ?;
153
+ if !patterns. is_empty ( ) {
154
+ let matched_pkgs = ws
155
+ . members ( )
156
+ . filter ( |pkg| match_patterns ( pkg, & mut patterns) )
157
+ . map ( Package :: package_id)
158
+ . map ( PackageIdSpec :: from_package_id) ;
159
+ specs. extend ( matched_pkgs) ;
160
+ }
161
+ emit_pattern_not_found ( ws, patterns, false ) ?;
162
+ specs
163
+ }
154
164
Packages :: Default => ws
155
165
. default_members ( )
156
166
. map ( Package :: package_id)
@@ -170,27 +180,35 @@ impl Packages {
170
180
Ok ( specs)
171
181
}
172
182
183
+ /// Gets a list of selected packages from a workspace.
173
184
pub fn get_packages < ' ws > ( & self , ws : & ' ws Workspace < ' _ > ) -> CargoResult < Vec < & ' ws Package > > {
174
185
let packages: Vec < _ > = match self {
175
186
Packages :: Default => ws. default_members ( ) . collect ( ) ,
176
187
Packages :: All => ws. members ( ) . collect ( ) ,
177
- Packages :: OptOut ( opt_out) => ws
178
- . members ( )
179
- . filter ( |pkg| !opt_out. iter ( ) . any ( |name| pkg. name ( ) . as_str ( ) == name) )
180
- . collect ( ) ,
181
- Packages :: Packages ( packages) => packages
182
- . iter ( )
183
- . map ( |name| {
184
- ws. members ( )
185
- . find ( |pkg| pkg. name ( ) . as_str ( ) == name)
186
- . ok_or_else ( || {
187
- anyhow:: format_err!(
188
- "package `{}` is not a member of the workspace" ,
189
- name
190
- )
191
- } )
192
- } )
193
- . collect :: < CargoResult < Vec < _ > > > ( ) ?,
188
+ Packages :: OptOut ( opt_out) => {
189
+ let ( mut patterns, mut names) = opt_patterns_and_names ( opt_out) ?;
190
+ let packages = ws
191
+ . members ( )
192
+ . filter ( |pkg| {
193
+ !names. remove ( pkg. name ( ) . as_str ( ) ) && !match_patterns ( pkg, & mut patterns)
194
+ } )
195
+ . collect ( ) ;
196
+ emit_package_not_found ( ws, names, true ) ?;
197
+ emit_pattern_not_found ( ws, patterns, true ) ?;
198
+ packages
199
+ }
200
+ Packages :: Packages ( opt_in) => {
201
+ let ( mut patterns, mut names) = opt_patterns_and_names ( opt_in) ?;
202
+ let packages = ws
203
+ . members ( )
204
+ . filter ( |pkg| {
205
+ names. remove ( pkg. name ( ) . as_str ( ) ) || match_patterns ( pkg, & mut patterns)
206
+ } )
207
+ . collect ( ) ;
208
+ emit_package_not_found ( ws, names, false ) ?;
209
+ emit_pattern_not_found ( ws, patterns, false ) ?;
210
+ packages
211
+ }
194
212
} ;
195
213
Ok ( packages)
196
214
}
@@ -577,6 +595,13 @@ impl FilterRule {
577
595
FilterRule :: Just ( ref targets) => Some ( targets. clone ( ) ) ,
578
596
}
579
597
}
598
+
599
+ pub ( crate ) fn contains_glob_patterns ( & self ) -> bool {
600
+ match self {
601
+ FilterRule :: All => false ,
602
+ FilterRule :: Just ( targets) => targets. iter ( ) . any ( is_glob_pattern) ,
603
+ }
604
+ }
580
605
}
581
606
582
607
impl CompileFilter {
@@ -706,6 +731,24 @@ impl CompileFilter {
706
731
CompileFilter :: Only { .. } => true ,
707
732
}
708
733
}
734
+
735
+ pub ( crate ) fn contains_glob_patterns ( & self ) -> bool {
736
+ match self {
737
+ CompileFilter :: Default { .. } => false ,
738
+ CompileFilter :: Only {
739
+ bins,
740
+ examples,
741
+ tests,
742
+ benches,
743
+ ..
744
+ } => {
745
+ bins. contains_glob_patterns ( )
746
+ || examples. contains_glob_patterns ( )
747
+ || tests. contains_glob_patterns ( )
748
+ || benches. contains_glob_patterns ( )
749
+ }
750
+ }
751
+ }
709
752
}
710
753
711
754
/// A proposed target.
@@ -1163,8 +1206,16 @@ fn find_named_targets<'a>(
1163
1206
is_expected_kind : fn ( & Target ) -> bool ,
1164
1207
mode : CompileMode ,
1165
1208
) -> CargoResult < Vec < Proposal < ' a > > > {
1166
- let filter = |t : & Target | t. name ( ) == target_name && is_expected_kind ( t) ;
1167
- let proposals = filter_targets ( packages, filter, true , mode) ;
1209
+ let is_glob = is_glob_pattern ( target_name) ;
1210
+ let proposals = if is_glob {
1211
+ let pattern = build_glob ( target_name) ?;
1212
+ let filter = |t : & Target | is_expected_kind ( t) && pattern. matches ( t. name ( ) ) ;
1213
+ filter_targets ( packages, filter, true , mode)
1214
+ } else {
1215
+ let filter = |t : & Target | t. name ( ) == target_name && is_expected_kind ( t) ;
1216
+ filter_targets ( packages, filter, true , mode)
1217
+ } ;
1218
+
1168
1219
if proposals. is_empty ( ) {
1169
1220
let targets = packages. iter ( ) . flat_map ( |pkg| {
1170
1221
pkg. targets ( )
@@ -1173,8 +1224,9 @@ fn find_named_targets<'a>(
1173
1224
} ) ;
1174
1225
let suggestion = closest_msg ( target_name, targets, |t| t. name ( ) ) ;
1175
1226
anyhow:: bail!(
1176
- "no {} target named `{}`{}" ,
1227
+ "no {} target {} `{}`{}" ,
1177
1228
target_desc,
1229
+ if is_glob { "matches pattern" } else { "named" } ,
1178
1230
target_name,
1179
1231
suggestion
1180
1232
) ;
@@ -1291,3 +1343,86 @@ fn traverse_and_share(
1291
1343
new_graph. entry ( new_unit. clone ( ) ) . or_insert ( new_deps) ;
1292
1344
new_unit
1293
1345
}
1346
+
1347
+ /// Build `glob::Pattern` with informative context.
1348
+ fn build_glob ( pat : & str ) -> CargoResult < glob:: Pattern > {
1349
+ glob:: Pattern :: new ( pat) . with_context ( || format ! ( "cannot build glob pattern from `{}`" , pat) )
1350
+ }
1351
+
1352
+ /// Emits "package not found" error.
1353
+ ///
1354
+ /// > This function should be used only in package selection processes such like
1355
+ /// `Packages::to_package_id_specs` and `Packages::get_packages`.
1356
+ fn emit_package_not_found (
1357
+ ws : & Workspace < ' _ > ,
1358
+ opt_names : BTreeSet < & str > ,
1359
+ opt_out : bool ,
1360
+ ) -> CargoResult < ( ) > {
1361
+ if !opt_names. is_empty ( ) {
1362
+ anyhow:: bail!(
1363
+ "{}package(s) `{}` not found in workspace `{}`" ,
1364
+ if opt_out { "excluded " } else { "" } ,
1365
+ opt_names. into_iter( ) . collect:: <Vec <_>>( ) . join( ", " ) ,
1366
+ ws. root( ) . display( ) ,
1367
+ )
1368
+ }
1369
+ Ok ( ( ) )
1370
+ }
1371
+
1372
+ /// Emits "glob pattern not found" error.
1373
+ ///
1374
+ /// > This function should be used only in package selection processes such like
1375
+ /// `Packages::to_package_id_specs` and `Packages::get_packages`.
1376
+ fn emit_pattern_not_found (
1377
+ ws : & Workspace < ' _ > ,
1378
+ opt_patterns : Vec < ( glob:: Pattern , bool ) > ,
1379
+ opt_out : bool ,
1380
+ ) -> CargoResult < ( ) > {
1381
+ let not_matched = opt_patterns
1382
+ . iter ( )
1383
+ . filter ( |( _, matched) | !* matched)
1384
+ . map ( |( pat, _) | pat. as_str ( ) )
1385
+ . collect :: < Vec < _ > > ( ) ;
1386
+ if !not_matched. is_empty ( ) {
1387
+ anyhow:: bail!(
1388
+ "{}package pattern(s) `{}` not found in workspace `{}`" ,
1389
+ if opt_out { "excluded " } else { "" } ,
1390
+ not_matched. join( ", " ) ,
1391
+ ws. root( ) . display( ) ,
1392
+ )
1393
+ }
1394
+ Ok ( ( ) )
1395
+ }
1396
+
1397
+ /// Checks whether a package matches any of a list of glob patterns generated
1398
+ /// from `opt_patterns_and_names`.
1399
+ ///
1400
+ /// > This function should be used only in package selection processes such like
1401
+ /// `Packages::to_package_id_specs` and `Packages::get_packages`.
1402
+ fn match_patterns ( pkg : & Package , patterns : & mut Vec < ( glob:: Pattern , bool ) > ) -> bool {
1403
+ patterns. iter_mut ( ) . any ( |( m, matched) | {
1404
+ let is_matched = m. matches ( pkg. name ( ) . as_str ( ) ) ;
1405
+ * matched |= is_matched;
1406
+ is_matched
1407
+ } )
1408
+ }
1409
+
1410
+ /// Given a list opt-in or opt-out package selection strings, generates two
1411
+ /// collections that represent glob patterns and package names respectively.
1412
+ ///
1413
+ /// > This function should be used only in package selection processes such like
1414
+ /// `Packages::to_package_id_specs` and `Packages::get_packages`.
1415
+ fn opt_patterns_and_names (
1416
+ opt : & [ String ] ,
1417
+ ) -> CargoResult < ( Vec < ( glob:: Pattern , bool ) > , BTreeSet < & str > ) > {
1418
+ let mut opt_patterns = Vec :: new ( ) ;
1419
+ let mut opt_names = BTreeSet :: new ( ) ;
1420
+ for x in opt. iter ( ) {
1421
+ if is_glob_pattern ( x) {
1422
+ opt_patterns. push ( ( build_glob ( x) ?, false ) ) ;
1423
+ } else {
1424
+ opt_names. insert ( String :: as_str ( x) ) ;
1425
+ }
1426
+ }
1427
+ Ok ( ( opt_patterns, opt_names) )
1428
+ }
0 commit comments