29
import multiprocessing
40
is_py2 = sys.version[0] == '2'
48
def run_tidy(task_queue, lock, timeout):
51
command = task_queue.get()
53
proc = subprocess.Popen(command,
54
stdout=subprocess.PIPE,
55
stderr=subprocess.PIPE)
57
if timeout is not None:
58
watchdog = threading.Timer(timeout, proc.kill)
61
stdout, stderr = proc.communicate()
64
sys.stdout.write(stdout.decode('utf-8') + '\n')
67
sys.stderr.write(stderr.decode('utf-8') + '\n')
69
except Exception as e:
71
sys.stderr.write('Failed: ' + str(e) + ': '.join(command) + '\n')
74
if (not timeout is None) and (not watchdog is None):
75
if not watchdog.is_alive():
76
sys.stderr.write('Terminated by timeout: ' +
77
' '.join(command) + '\n')
79
task_queue.task_done()
82
def start_workers(max_tasks, tidy_caller, task_queue, lock, timeout):
83
for _ in range(max_tasks):
84
t = threading.Thread(target=tidy_caller, args=(task_queue, lock, timeout))
88
def merge_replacement_files(tmpdir, mergefile):
89
"""Merge all replacement files in a directory into a single file"""
90
# The fixes suggested by clang-tidy >= 4.0.0 are given under
91
# the top level key 'Diagnostics' in the output yaml files
92
mergekey = "Diagnostics"
94
for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')):
95
content = yaml.safe_load(open(replacefile, 'r'))
97
continue # Skip empty files.
98
merged.extend(content.get(mergekey, []))
101
# MainSourceFile: The key is required by the definition inside
102
# include/clang/Tooling/ReplacementsYaml.h, but the value
103
# is actually never used inside clang-apply-replacements,
104
# so we set it to '' here.
105
output = { 'MainSourceFile': '', mergekey: merged }
106
with open(mergefile, 'w') as out:
107
yaml.safe_dump(output, out)
110
open(mergefile, 'w').close()
127
47
r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)',
128
48
help='custom pattern selecting file paths to check '
129
49
'(case insensitive, overridden by -regex)')
130
parser.add_argument('-j', type=int, default=1,
131
help='number of tidy instances to be run in parallel.')
132
parser.add_argument('-timeout', type=int, default=None,
133
help='timeout per each file in seconds.')
134
51
parser.add_argument('-fix', action='store_true', default=False,
135
52
help='apply suggested fixes')
136
53
parser.add_argument('-checks',
185
102
line_count = int(match.group(3))
186
103
if line_count == 0:
188
end_line = start_line + line_count - 1
105
end_line = start_line + line_count - 1;
189
106
lines_by_file.setdefault(filename, []).append([start_line, end_line])
191
if not any(lines_by_file):
108
if len(lines_by_file) == 0:
192
109
print("No relevant changes found.")
195
max_task_count = args.j
196
if max_task_count == 0:
197
max_task_count = multiprocessing.cpu_count()
198
max_task_count = min(len(lines_by_file), max_task_count)
201
if args.export_fixes:
202
tmpdir = tempfile.mkdtemp()
204
# Tasks for clang-tidy.
205
task_queue = queue.Queue(max_task_count)
206
# A lock for console output.
207
lock = threading.Lock()
209
# Run a pool of clang-tidy workers.
210
start_workers(max_task_count, run_tidy, task_queue, lock, args.timeout)
212
# Form the common args list.
213
common_clang_tidy_args = []
112
line_filter_json = json.dumps(
113
[{"name" : name, "lines" : lines_by_file[name]} for name in lines_by_file],
114
separators = (',', ':'))
117
if sys.platform == 'win32':
118
line_filter_json=re.sub(r'"', r'"""', line_filter_json)
122
# Run clang-tidy on files containing changes.
123
command = [args.clang_tidy_binary]
124
command.append('-line-filter=' + quote + line_filter_json + quote)
215
common_clang_tidy_args.append('-fix')
126
command.append('-fix')
127
if args.export_fixes:
128
command.append('-export-fixes=' + args.export_fixes)
216
129
if args.checks != '':
217
common_clang_tidy_args.append('-checks=' + args.checks)
130
command.append('-checks=' + quote + args.checks + quote)
219
common_clang_tidy_args.append('-quiet')
132
command.append('-quiet')
220
133
if args.build_path is not None:
221
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())
222
136
for arg in args.extra_arg:
223
common_clang_tidy_args.append('-extra-arg=%s' % arg)
137
command.append('-extra-arg=%s' % arg)
224
138
for arg in args.extra_arg_before:
225
common_clang_tidy_args.append('-extra-arg-before=%s' % arg)
227
for name in lines_by_file:
228
line_filter_json = json.dumps(
229
[{"name": name, "lines": lines_by_file[name]}],
230
separators=(',', ':'))
232
# Run clang-tidy on files containing changes.
233
command = [args.clang_tidy_binary]
234
command.append('-line-filter=' + line_filter_json)
235
if args.export_fixes:
236
# Get a temporary file. We immediately close the handle so clang-tidy can
238
(handle, tmp_name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
240
command.append('-export-fixes=' + tmp_name)
241
command.extend(common_clang_tidy_args)
243
command.extend(clang_tidy_args)
245
task_queue.put(command)
247
# Wait for all threads to be done.
250
if args.export_fixes:
251
print('Writing fixes to ' + args.export_fixes + ' ...')
253
merge_replacement_files(tmpdir, args.export_fixes)
255
sys.stderr.write('Error exporting fixes.\n')
256
traceback.print_exc()
259
shutil.rmtree(tmpdir)
139
command.append('-extra-arg-before=%s' % arg)
140
command.extend(clang_tidy_args)
142
sys.exit(subprocess.call(' '.join(command), shell=True))
262
144
if __name__ == '__main__':