@@ -18,16 +18,16 @@ extern crate getopts;
18
18
extern crate serde_json as json;
19
19
20
20
use std:: env;
21
+ use std:: fs;
21
22
use std:: hash:: { Hash , Hasher } ;
22
23
use std:: io:: { self , Write } ;
23
- use std:: path:: PathBuf ;
24
+ use std:: path:: { Path , PathBuf } ;
24
25
use std:: process:: { Command , ExitStatus } ;
25
26
use std:: str;
26
27
use std:: collections:: HashSet ;
27
28
use std:: iter:: FromIterator ;
28
29
29
30
use getopts:: { Matches , Options } ;
30
- use json:: Value ;
31
31
32
32
fn main ( ) {
33
33
let exit_status = execute ( ) ;
@@ -251,145 +251,104 @@ impl WorkspaceHitlist {
251
251
}
252
252
}
253
253
254
- fn get_cargo_metadata_from_utf8 ( v : & [ u8 ] ) -> Option < Value > {
255
- json:: from_str ( str:: from_utf8 ( v) . ok ( ) ?) . ok ( )
256
- }
257
-
258
- fn get_json_array_with < ' a > ( v : & ' a Value , key : & str ) -> Option < & ' a Vec < Value > > {
259
- v. as_object ( ) ?. get ( key) ?. as_array ( )
260
- }
261
-
262
- // `cargo metadata --no-deps | jq '.["packages"]'`
263
- fn get_packages ( v : & [ u8 ] ) -> Result < Vec < Value > , io:: Error > {
264
- let e = io:: Error :: new (
265
- io:: ErrorKind :: NotFound ,
266
- String :: from ( "`cargo metadata` returned json without a 'packages' key" ) ,
267
- ) ;
268
- match get_cargo_metadata_from_utf8 ( v) {
269
- Some ( ref json_obj) => get_json_array_with ( json_obj, "packages" ) . cloned ( ) . ok_or ( e) ,
270
- None => Err ( e) ,
271
- }
272
- }
254
+ fn get_targets ( workspace_hitlist : & WorkspaceHitlist ) -> Result < HashSet < Target > , io:: Error > {
255
+ let mut targets = HashSet :: new ( ) ;
273
256
274
- fn extract_target_from_package ( package : & Value ) -> Option < Vec < Target > > {
275
- let jtargets = get_json_array_with ( package, "targets" ) ?;
276
- let mut targets: Vec < Target > = vec ! [ ] ;
277
- for jtarget in jtargets {
278
- targets. push ( Target :: from_json ( jtarget) ?) ;
257
+ match * workspace_hitlist {
258
+ WorkspaceHitlist :: None => get_targets_root_only ( & mut targets) ?,
259
+ WorkspaceHitlist :: All => get_targets_recursive ( None , & mut targets, & mut HashSet :: new ( ) ) ?,
260
+ WorkspaceHitlist :: Some ( ref hitlist) => get_targets_with_hitlist ( hitlist, & mut targets) ?,
279
261
}
280
- Some ( targets)
281
- }
282
262
283
- fn filter_packages_with_hitlist (
284
- packages : Vec < Value > ,
285
- workspace_hitlist : & WorkspaceHitlist ,
286
- ) -> Result < Vec < Value > , & String > {
287
- let some_hitlist: Option < HashSet < & String > > =
288
- workspace_hitlist. get_some ( ) . map ( HashSet :: from_iter) ;
289
- if some_hitlist. is_none ( ) {
290
- return Ok ( packages) ;
291
- }
292
- let mut hitlist = some_hitlist. unwrap ( ) ;
293
- let members: Vec < Value > = packages
294
- . into_iter ( )
295
- . filter ( |member| {
296
- member
297
- . as_object ( )
298
- . and_then ( |member_obj| {
299
- member_obj
300
- . get ( "name" )
301
- . and_then ( Value :: as_str)
302
- . map ( |member_name| {
303
- hitlist. take ( & member_name. to_string ( ) ) . is_some ( )
304
- } )
305
- } )
306
- . unwrap_or ( false )
307
- } )
308
- . collect ( ) ;
309
- if hitlist. is_empty ( ) {
310
- Ok ( members)
263
+ if targets. is_empty ( ) {
264
+ Err ( io:: Error :: new (
265
+ io:: ErrorKind :: Other ,
266
+ format ! ( "Failed to find targets" ) ,
267
+ ) )
311
268
} else {
312
- Err ( hitlist . into_iter ( ) . next ( ) . unwrap ( ) )
269
+ Ok ( targets )
313
270
}
314
271
}
315
272
316
- fn get_dependencies_from_package ( package : & Value ) -> Option < Vec < PathBuf > > {
317
- let jdependencies = get_json_array_with ( package , "dependencies" ) ?;
318
- let root_path = env :: current_dir ( ) . ok ( ) ? ;
319
- let mut dependencies : Vec < PathBuf > = vec ! [ ] ;
320
- for jdep in jdependencies {
321
- let jdependency = jdep . as_object ( ) ? ;
322
- if !jdependency . get ( "source" ) ? . is_null ( ) {
323
- continue ;
273
+ fn get_targets_root_only ( targets : & mut HashSet < Target > ) -> Result < ( ) , io :: Error > {
274
+ let metadata = get_cargo_metadata ( None ) ?;
275
+
276
+ for package in metadata . packages {
277
+ for target in package . targets {
278
+ if target . name == package . name {
279
+ targets . insert ( Target :: from_target ( & target ) ) ;
280
+ }
324
281
}
325
- let name = jdependency. get ( "name" ) ?. as_str ( ) ?;
326
- let mut path = root_path. clone ( ) ;
327
- path. push ( & name) ;
328
- dependencies. push ( path) ;
329
282
}
330
- Some ( dependencies)
283
+
284
+ Ok ( ( ) )
331
285
}
332
286
333
- // Returns a vector of local dependencies under this crate
334
- fn get_path_to_local_dependencies ( packages : & [ Value ] ) -> Vec < PathBuf > {
335
- let mut local_dependencies: Vec < PathBuf > = vec ! [ ] ;
336
- for package in packages {
337
- if let Some ( mut d) = get_dependencies_from_package ( package) {
338
- local_dependencies. append ( & mut d) ;
287
+ fn get_targets_recursive (
288
+ manifest_path : Option < & Path > ,
289
+ mut targets : & mut HashSet < Target > ,
290
+ visited : & mut HashSet < String > ,
291
+ ) -> Result < ( ) , io:: Error > {
292
+ let metadata = get_cargo_metadata ( manifest_path) ?;
293
+
294
+ for package in metadata. packages {
295
+ add_targets ( & package. targets , & mut targets) ;
296
+
297
+ // Look for local dependencies.
298
+ for dependency in package. dependencies {
299
+ if dependency. source . is_some ( ) || visited. contains ( & dependency. name ) {
300
+ continue ;
301
+ }
302
+
303
+ let mut manifest_path = PathBuf :: from ( & package. manifest_path ) ;
304
+
305
+ manifest_path. pop ( ) ;
306
+ manifest_path. push ( & dependency. name ) ;
307
+ manifest_path. push ( "Cargo.toml" ) ;
308
+
309
+ if manifest_path. exists ( ) {
310
+ visited. insert ( dependency. name ) ;
311
+ get_targets_recursive ( Some ( & manifest_path) , & mut targets, visited) ?;
312
+ }
339
313
}
340
314
}
341
- local_dependencies
315
+
316
+ Ok ( ( ) )
342
317
}
343
318
344
- // Returns a vector of all compile targets of a crate
345
- fn get_targets ( workspace_hitlist : & WorkspaceHitlist ) -> Result < Vec < Target > , io:: Error > {
346
- let output = Command :: new ( "cargo" )
347
- . args ( & [ "metadata" , "--no-deps" , "--format-version=1" ] )
348
- . output ( ) ?;
349
- if output. status . success ( ) {
350
- let cur_dir = env:: current_dir ( ) ?;
351
- let mut targets: Vec < Target > = vec ! [ ] ;
352
- let packages = get_packages ( & output. stdout ) ?;
353
-
354
- // If we can find any local dependencies, we will try to get targets from those as well.
355
- if * workspace_hitlist == WorkspaceHitlist :: All {
356
- for path in get_path_to_local_dependencies ( & packages) {
357
- match env:: set_current_dir ( path) {
358
- Ok ( ..) => match get_targets ( workspace_hitlist) {
359
- Ok ( ref mut t) => targets. append ( t) ,
360
- Err ( ..) => continue ,
361
- } ,
362
- Err ( ..) => continue ,
363
- }
364
- }
365
- }
319
+ fn get_targets_with_hitlist (
320
+ target_names : & [ String ] ,
321
+ targets : & mut HashSet < Target > ,
322
+ ) -> Result < ( ) , io:: Error > {
323
+ let metadata = get_cargo_metadata ( None ) ?;
366
324
367
- env:: set_current_dir ( cur_dir) ?;
368
- match filter_packages_with_hitlist ( packages, workspace_hitlist) {
369
- Ok ( packages) => {
370
- for package in packages {
371
- if let Some ( mut target) = extract_target_from_package ( & package) {
372
- targets. append ( & mut target) ;
373
- }
374
- }
375
- Ok ( targets)
376
- }
377
- Err ( package) => {
378
- // Mimick cargo of only outputting one <package> spec.
379
- Err ( io:: Error :: new (
380
- io:: ErrorKind :: InvalidInput ,
381
- format ! ( "package `{}` is not a member of the workspace" , package) ,
382
- ) )
325
+ let mut hitlist: HashSet < & String > = HashSet :: from_iter ( target_names) ;
326
+
327
+ for package in metadata. packages {
328
+ for target in package. targets {
329
+ if hitlist. remove ( & target. name ) {
330
+ targets. insert ( Target :: from_target ( & target) ) ;
383
331
}
384
332
}
333
+ }
334
+
335
+ if hitlist. is_empty ( ) {
336
+ Ok ( ( ) )
385
337
} else {
338
+ let package = hitlist. iter ( ) . next ( ) . unwrap ( ) ;
386
339
Err ( io:: Error :: new (
387
- io:: ErrorKind :: NotFound ,
388
- str :: from_utf8 ( & output . stderr ) . unwrap ( ) ,
340
+ io:: ErrorKind :: InvalidInput ,
341
+ format ! ( "package `{}` is not a member of the workspace" , package ) ,
389
342
) )
390
343
}
391
344
}
392
345
346
+ fn add_targets ( target_paths : & [ cargo_metadata:: Target ] , targets : & mut HashSet < Target > ) {
347
+ for target in target_paths {
348
+ targets. insert ( Target :: from_target ( & target) ) ;
349
+ }
350
+ }
351
+
393
352
fn format_files (
394
353
files : & [ PathBuf ] ,
395
354
fmt_args : & [ String ] ,
@@ -424,3 +383,13 @@ fn format_files(
424
383
} ) ?;
425
384
command. wait ( )
426
385
}
386
+
387
+ fn get_cargo_metadata ( manifest_path : Option < & Path > ) -> Result < cargo_metadata:: Metadata , io:: Error > {
388
+ match cargo_metadata:: metadata ( manifest_path) {
389
+ Ok ( metadata) => Ok ( metadata) ,
390
+ Err ( ..) => Err ( io:: Error :: new (
391
+ io:: ErrorKind :: Other ,
392
+ "`cargo manifest` failed." ,
393
+ ) ) ,
394
+ }
395
+ }
0 commit comments