11mod extractor;
22
3+ extern crate num_cpus;
4+
35use clap;
46use flate2:: write:: GzEncoder ;
7+ use rayon:: prelude:: * ;
58use std:: fs;
69use std:: io:: { BufRead , BufWriter , Write } ;
710use std:: path:: { Path , PathBuf } ;
@@ -42,6 +45,39 @@ impl TrapCompression {
4245 }
4346}
4447
48+ /**
49+ * Gets the number of threads the extractor should use, by reading the
50+ * CODEQL_THREADS environment variable and using it as described in the
51+ * extractor spec:
52+ *
53+ * "If the number is positive, it indicates the number of threads that should
54+ * be used. If the number is negative or zero, it should be added to the number
55+ * of cores available on the machine to determine how many threads to use
56+ * (minimum of 1). If unspecified, should be considered as set to 1."
57+ */
58+ fn num_codeql_threads ( ) -> usize {
59+ match std:: env:: var ( "CODEQL_THREADS" ) {
60+ // Use 1 thread if the environment variable isn't set.
61+ Err ( _) => 1 ,
62+
63+ Ok ( num) => match num. parse :: < i32 > ( ) {
64+ Ok ( num) if num <= 0 => {
65+ let reduction = -num as usize ;
66+ num_cpus:: get ( ) - reduction
67+ }
68+ Ok ( num) => num as usize ,
69+
70+ Err ( _) => {
71+ tracing:: error!(
72+ "Unable to parse CODEQL_THREADS value '{}'; defaulting to 1 thread." ,
73+ & num
74+ ) ;
75+ 1
76+ }
77+ } ,
78+ }
79+ }
80+
4581fn main ( ) -> std:: io:: Result < ( ) > {
4682 tracing_subscriber:: fmt ( )
4783 . with_target ( false )
@@ -50,6 +86,21 @@ fn main() -> std::io::Result<()> {
5086 . with_env_filter ( tracing_subscriber:: EnvFilter :: from_default_env ( ) )
5187 . init ( ) ;
5288
89+ let num_threads = num_codeql_threads ( ) ;
90+ tracing:: info!(
91+ "Using {} {}" ,
92+ num_threads,
93+ if num_threads == 1 {
94+ "thread"
95+ } else {
96+ "threads"
97+ }
98+ ) ;
99+ rayon:: ThreadPoolBuilder :: new ( )
100+ . num_threads ( num_threads)
101+ . build_global ( )
102+ . unwrap ( ) ;
103+
53104 let matches = clap:: App :: new ( "Ruby extractor" )
54105 . version ( "1.0" )
55106 . author ( "GitHub" )
@@ -76,28 +127,28 @@ fn main() -> std::io::Result<()> {
76127
77128 let language = tree_sitter_ruby:: language ( ) ;
78129 let schema = node_types:: read_node_types_str ( tree_sitter_ruby:: NODE_TYPES ) ?;
79- let mut extractor = extractor:: create ( language, schema) ;
80- for line in std:: io:: BufReader :: new ( file_list) . lines ( ) {
81- let path = PathBuf :: from ( line?) . canonicalize ( ) ?;
130+ let lines: std:: io:: Result < Vec < String > > = std:: io:: BufReader :: new ( file_list) . lines ( ) . collect ( ) ;
131+ let lines = lines?;
132+ lines. par_iter ( ) . try_for_each ( |line| {
133+ let path = PathBuf :: from ( line) . canonicalize ( ) ?;
82134 let trap_file = path_for ( & trap_dir, & path, trap_compression. extension ( ) ) ;
83135 let src_archive_file = path_for ( & src_archive_dir, & path, "" ) ;
84- let trap = extractor. extract ( & path) ?;
136+ let trap = extractor:: extract ( language , & schema , & path) ?;
85137 std:: fs:: create_dir_all ( & src_archive_file. parent ( ) . unwrap ( ) ) ?;
86138 std:: fs:: copy ( & path, & src_archive_file) ?;
87139 std:: fs:: create_dir_all ( & trap_file. parent ( ) . unwrap ( ) ) ?;
88140 let trap_file = std:: fs:: File :: create ( & trap_file) ?;
89141 let mut trap_file = BufWriter :: new ( trap_file) ;
90142 match trap_compression {
91143 TrapCompression :: None => {
92- write ! ( trap_file, "{}" , trap) ? ;
144+ write ! ( trap_file, "{}" , trap)
93145 }
94146 TrapCompression :: Gzip => {
95147 let mut compressed_writer = GzEncoder :: new ( trap_file, flate2:: Compression :: fast ( ) ) ;
96- write ! ( compressed_writer, "{}" , trap) ? ;
148+ write ! ( compressed_writer, "{}" , trap)
97149 }
98150 }
99- }
100- return Ok ( ( ) ) ;
151+ } )
101152}
102153
103154fn path_for ( dir : & Path , path : & Path , ext : & str ) -> PathBuf {
0 commit comments