24
24
"""
25
25
26
26
import argparse
27
- import glob
28
27
import json
29
- import multiprocessing
30
- import os
31
28
import re
32
- import shutil
33
29
import subprocess
34
30
import sys
35
- import tempfile
36
- import threading
37
- import traceback
38
- import yaml
39
-
40
- is_py2 = sys .version [0 ] == '2'
41
-
42
- if is_py2 :
43
- import Queue as queue
44
- else :
45
- import queue as queue
46
-
47
-
48
- def run_tidy (task_queue , lock , timeout ):
49
- watchdog = None
50
- while True :
51
- command = task_queue .get ()
52
- try :
53
- proc = subprocess .Popen (command ,
54
- stdout = subprocess .PIPE ,
55
- stderr = subprocess .PIPE )
56
-
57
- if timeout is not None :
58
- watchdog = threading .Timer (timeout , proc .kill )
59
- watchdog .start ()
60
-
61
- stdout , stderr = proc .communicate ()
62
-
63
- with lock :
64
- sys .stdout .write (stdout .decode ('utf-8' ) + '\n ' )
65
- if stderr :
66
- sys .stderr .write (stderr .decode ('utf-8' ) + '\n ' )
67
- except Exception as e :
68
- with lock :
69
- sys .stderr .write ('Failed: ' + str (e ) + ': ' .join (command ) + '\n ' )
70
- finally :
71
- with lock :
72
- if (not timeout is None ) and (not watchdog is None ):
73
- if not watchdog .is_alive ():
74
- sys .stderr .write ('Terminated by timeout: ' +
75
- ' ' .join (command ) + '\n ' )
76
- watchdog .cancel ()
77
- task_queue .task_done ()
78
-
79
-
80
- def start_workers (max_tasks , tidy_caller , task_queue , lock , timeout ):
81
- for _ in range (max_tasks ):
82
- t = threading .Thread (target = tidy_caller , args = (task_queue , lock , timeout ))
83
- t .daemon = True
84
- t .start ()
85
-
86
- def merge_replacement_files (tmpdir , mergefile ):
87
- """Merge all replacement files in a directory into a single file"""
88
- # The fixes suggested by clang-tidy >= 4.0.0 are given under
89
- # the top level key 'Diagnostics' in the output yaml files
90
- mergekey = "Diagnostics"
91
- merged = []
92
- for replacefile in glob .iglob (os .path .join (tmpdir , '*.yaml' )):
93
- content = yaml .safe_load (open (replacefile , 'r' ))
94
- if not content :
95
- continue # Skip empty files.
96
- merged .extend (content .get (mergekey , []))
97
-
98
- if merged :
99
- # MainSourceFile: The key is required by the definition inside
100
- # include/clang/Tooling/ReplacementsYaml.h, but the value
101
- # is actually never used inside clang-apply-replacements,
102
- # so we set it to '' here.
103
- output = { 'MainSourceFile' : '' , mergekey : merged }
104
- with open (mergefile , 'w' ) as out :
105
- yaml .safe_dump (output , out )
106
- else :
107
- # Empty the file:
108
- open (mergefile , 'w' ).close ()
109
31
110
32
111
33
def main ():
@@ -126,10 +48,6 @@ def main():
126
48
help = 'custom pattern selecting file paths to check '
127
49
'(case insensitive, overridden by -regex)' )
128
50
129
- parser .add_argument ('-j' , type = int , default = 1 ,
130
- help = 'number of tidy instances to be run in parallel.' )
131
- parser .add_argument ('-timeout' , type = int , default = None ,
132
- help = 'timeout per each file in seconds.' )
133
51
parser .add_argument ('-fix' , action = 'store_true' , default = False ,
134
52
help = 'apply suggested fixes' )
135
53
parser .add_argument ('-checks' ,
@@ -166,7 +84,7 @@ def main():
166
84
match = re .search ('^\+\+\+\ \" ?(.*?/){%s}([^ \t \n \" ]*)' % args .p , line )
167
85
if match :
168
86
filename = match .group (2 )
169
- if filename is None :
87
+ if filename == None :
170
88
continue
171
89
172
90
if args .regex is not None :
@@ -184,83 +102,44 @@ def main():
184
102
line_count = int (match .group (3 ))
185
103
if line_count == 0 :
186
104
continue
187
- end_line = start_line + line_count - 1
105
+ end_line = start_line + line_count - 1 ;
188
106
lines_by_file .setdefault (filename , []).append ([start_line , end_line ])
189
107
190
- if not any (lines_by_file ):
108
+ if len (lines_by_file ) == 0 :
191
109
print ("No relevant changes found." )
192
110
sys .exit (0 )
193
111
194
- max_task_count = args .j
195
- if max_task_count == 0 :
196
- max_task_count = multiprocessing .cpu_count ()
197
- max_task_count = min (len (lines_by_file ), max_task_count )
112
+ line_filter_json = json .dumps (
113
+ [{"name" : name , "lines" : lines_by_file [name ]} for name in lines_by_file ],
114
+ separators = (',' , ':' ))
198
115
199
- tmpdir = None
200
- if args .export_fixes :
201
- tmpdir = tempfile .mkdtemp ()
202
-
203
- # Tasks for clang-tidy.
204
- task_queue = queue .Queue (max_task_count )
205
- # A lock for console output.
206
- lock = threading .Lock ()
207
-
208
- # Run a pool of clang-tidy workers.
209
- start_workers (max_task_count , run_tidy , task_queue , lock , args .timeout )
210
-
211
- quote = ""
212
- if sys .platform != 'win32' :
213
- quote = "'"
116
+ quote = "" ;
117
+ if sys .platform == 'win32' :
118
+ line_filter_json = re .sub (r'"' , r'"""' , line_filter_json )
119
+ else :
120
+ quote = "'" ;
214
121
215
- # Form the common args list.
216
- common_clang_tidy_args = []
122
+ # Run clang-tidy on files containing changes.
123
+ command = [args .clang_tidy_binary ]
124
+ command .append ('-line-filter=' + quote + line_filter_json + quote )
217
125
if args .fix :
218
- common_clang_tidy_args .append ('-fix' )
126
+ command .append ('-fix' )
127
+ if args .export_fixes :
128
+ command .append ('-export-fixes=' + args .export_fixes )
219
129
if args .checks != '' :
220
- common_clang_tidy_args .append ('-checks=' + quote + args .checks + quote )
130
+ command .append ('-checks=' + quote + args .checks + quote )
221
131
if args .quiet :
222
- common_clang_tidy_args .append ('-quiet' )
132
+ command .append ('-quiet' )
223
133
if args .build_path is not None :
224
- common_clang_tidy_args .append ('-p=%s' % args .build_path )
134
+ command .append ('-p=%s' % args .build_path )
135
+ command .extend (lines_by_file .keys ())
225
136
for arg in args .extra_arg :
226
- common_clang_tidy_args .append ('-extra-arg=%s' % arg )
137
+ command .append ('-extra-arg=%s' % arg )
227
138
for arg in args .extra_arg_before :
228
- common_clang_tidy_args .append ('-extra-arg-before=%s' % arg )
229
-
230
- for name in lines_by_file :
231
- line_filter_json = json .dumps (
232
- [{"name" : name , "lines" : lines_by_file [name ]}],
233
- separators = (',' , ':' ))
234
-
235
- # Run clang-tidy on files containing changes.
236
- command = [args .clang_tidy_binary ]
237
- command .append ('-line-filter=' + quote + line_filter_json + quote )
238
- if args .export_fixes :
239
- # Get a temporary file. We immediately close the handle so clang-tidy can
240
- # overwrite it.
241
- (handle , tmp_name ) = tempfile .mkstemp (suffix = '.yaml' , dir = tmpdir )
242
- os .close (handle )
243
- command .append ('-export-fixes=' + tmp_name )
244
- command .extend (common_clang_tidy_args )
245
- command .append (name )
246
- command .extend (clang_tidy_args )
247
-
248
- task_queue .put (command )
249
-
250
- # Wait for all threads to be done.
251
- task_queue .join ()
252
-
253
- if args .export_fixes :
254
- print ('Writing fixes to ' + args .export_fixes + ' ...' )
255
- try :
256
- merge_replacement_files (tmpdir , args .export_fixes )
257
- except :
258
- sys .stderr .write ('Error exporting fixes.\n ' )
259
- traceback .print_exc ()
260
-
261
- if tmpdir :
262
- shutil .rmtree (tmpdir )
139
+ command .append ('-extra-arg-before=%s' % arg )
140
+ command .extend (clang_tidy_args )
263
141
142
+ sys .exit (subprocess .call (' ' .join (command ), shell = True ))
264
143
265
144
if __name__ == '__main__' :
266
145
main ()
0 commit comments