@@ -11,9 +11,11 @@ use crate::clippy_project_root;
11
11
12
12
use std:: collections:: HashMap ;
13
13
use std:: process:: Command ;
14
+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
14
15
use std:: { env, fmt, fs:: write, path:: PathBuf } ;
15
16
16
17
use clap:: ArgMatches ;
18
+ use rayon:: prelude:: * ;
17
19
use serde:: { Deserialize , Serialize } ;
18
20
use serde_json:: Value ;
19
21
@@ -37,7 +39,7 @@ struct TomlCrate {
37
39
38
40
/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
39
41
/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
40
- #[ derive( Debug , Serialize , Deserialize , Eq , Hash , PartialEq ) ]
42
+ #[ derive( Debug , Serialize , Deserialize , Eq , Hash , PartialEq , Ord , PartialOrd ) ]
41
43
enum CrateSource {
42
44
CratesIo {
43
45
name : String ,
@@ -215,11 +217,34 @@ impl CrateSource {
215
217
impl Crate {
216
218
/// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
217
219
/// issued
218
- fn run_clippy_lints ( & self , cargo_clippy_path : & PathBuf ) -> Vec < ClippyWarning > {
219
- println ! ( "Linting {} {}..." , & self . name, & self . version) ;
220
+ fn run_clippy_lints (
221
+ & self ,
222
+ cargo_clippy_path : & PathBuf ,
223
+ target_dir_index : & AtomicUsize ,
224
+ thread_limit : usize ,
225
+ total_crates_to_lint : usize ,
226
+ ) -> Vec < ClippyWarning > {
227
+ // advance the atomic index by one
228
+ let index = target_dir_index. fetch_add ( 1 , Ordering :: SeqCst ) ;
229
+ // "loop" the index within 0..thread_limit
230
+ let target_dir_index = index % thread_limit;
231
+ let perc = ( ( index * 100 ) as f32 / total_crates_to_lint as f32 ) as u8 ;
232
+
233
+ if thread_limit == 1 {
234
+ println ! (
235
+ "{}/{} {}% Linting {} {}" ,
236
+ index, total_crates_to_lint, perc, & self . name, & self . version
237
+ ) ;
238
+ } else {
239
+ println ! (
240
+ "{}/{} {}% Linting {} {} in target dir {:?}" ,
241
+ index, total_crates_to_lint, perc, & self . name, & self . version, target_dir_index
242
+ ) ;
243
+ }
244
+
220
245
let cargo_clippy_path = std:: fs:: canonicalize ( cargo_clippy_path) . unwrap ( ) ;
221
246
222
- let shared_target_dir = clippy_project_root ( ) . join ( "target/lintcheck/shared_target_dir/ " ) ;
247
+ let shared_target_dir = clippy_project_root ( ) . join ( "target/lintcheck/shared_target_dir" ) ;
223
248
224
249
let mut args = vec ! [ "--" , "--message-format=json" , "--" , "--cap-lints=warn" ] ;
225
250
@@ -232,7 +257,11 @@ impl Crate {
232
257
}
233
258
234
259
let all_output = std:: process:: Command :: new ( & cargo_clippy_path)
235
- . env ( "CARGO_TARGET_DIR" , shared_target_dir)
260
+ // use the looping index to create individual target dirs
261
+ . env (
262
+ "CARGO_TARGET_DIR" ,
263
+ shared_target_dir. join ( format ! ( "_{:?}" , target_dir_index) ) ,
264
+ )
236
265
// lint warnings will look like this:
237
266
// src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
238
267
. args ( & args)
@@ -283,13 +312,13 @@ fn filter_clippy_warnings(line: &str) -> bool {
283
312
284
313
/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
285
314
fn build_clippy ( ) {
286
- let output = Command :: new ( "cargo" )
315
+ let status = Command :: new ( "cargo" )
287
316
. arg ( "build" )
288
- . output ( )
317
+ . status ( )
289
318
. expect ( "Failed to build clippy!" ) ;
290
- if !output . status . success ( ) {
291
- eprintln ! ( "Failed to compile Clippy" ) ;
292
- eprintln ! ( "stderr: {}" , String :: from_utf8_lossy ( & output . stderr ) )
319
+ if !status. success ( ) {
320
+ eprintln ! ( "Error: Failed to compile Clippy! " ) ;
321
+ std :: process :: exit ( 1 ) ;
293
322
}
294
323
}
295
324
@@ -356,6 +385,9 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
356
385
unreachable ! ( "Failed to translate TomlCrate into CrateSource!" ) ;
357
386
}
358
387
} ) ;
388
+ // sort the crates
389
+ crate_sources. sort ( ) ;
390
+
359
391
( toml_filename, crate_sources)
360
392
}
361
393
@@ -454,15 +486,46 @@ pub fn run(clap_config: &ArgMatches) {
454
486
. into_iter ( )
455
487
. map ( |krate| krate. download_and_extract ( ) )
456
488
. filter ( |krate| krate. name == only_one_crate)
457
- . map ( |krate| krate. run_clippy_lints ( & cargo_clippy_path) )
489
+ . map ( |krate| krate. run_clippy_lints ( & cargo_clippy_path, & AtomicUsize :: new ( 0 ) , 1 , 1 ) )
458
490
. flatten ( )
459
491
. collect ( )
460
492
} else {
493
+ let counter = std:: sync:: atomic:: AtomicUsize :: new ( 0 ) ;
494
+
495
+ // Ask rayon for thread count. Assume that half of that is the number of physical cores
496
+ // Use one target dir for each core so that we can run N clippys in parallel.
497
+ // We need to use different target dirs because cargo would lock them for a single build otherwise,
498
+ // killing the parallelism. However this also means that deps will only be reused half/a
499
+ // quarter of the time which might result in a longer wall clock runtime
500
+
501
+ // This helps when we check many small crates with dep-trees that don't have a lot of branches in
502
+ // order to achive some kind of parallelism
503
+
504
+ // by default, use a single thread
505
+ let num_cpus = match clap_config. value_of ( "threads" ) {
506
+ Some ( threads) => {
507
+ let threads: usize = threads
508
+ . parse ( )
509
+ . expect ( & format ! ( "Failed to parse '{}' to a digit" , threads) ) ;
510
+ if threads == 0 {
511
+ // automatic choice
512
+ // Rayon seems to return thread count so half that for core count
513
+ ( rayon:: current_num_threads ( ) / 2 ) as usize
514
+ } else {
515
+ threads
516
+ }
517
+ } ,
518
+ // no -j passed, use a single thread
519
+ None => 1 ,
520
+ } ;
521
+
522
+ let num_crates = crates. len ( ) ;
523
+
461
524
// check all crates (default)
462
525
crates
463
- . into_iter ( )
526
+ . into_par_iter ( )
464
527
. map ( |krate| krate. download_and_extract ( ) )
465
- . map ( |krate| krate. run_clippy_lints ( & cargo_clippy_path) )
528
+ . map ( |krate| krate. run_clippy_lints ( & cargo_clippy_path, & counter , num_cpus , num_crates ) )
466
529
. flatten ( )
467
530
. collect ( )
468
531
} ;
0 commit comments