47def run_tidy(task_queue, lock, timeout, failed_files):
50 command = task_queue.get()
52 proc = subprocess.Popen(
53 command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
56 if timeout
is not None:
57 watchdog = threading.Timer(timeout, proc.kill)
60 stdout, stderr = proc.communicate()
61 if proc.returncode != 0:
62 if proc.returncode < 0:
63 msg =
"Terminated by signal %d : %s\n" % (
67 stderr += msg.encode(
"utf-8")
68 failed_files.append(command)
71 sys.stdout.write(stdout.decode(
"utf-8") +
"\n")
74 sys.stderr.write(stderr.decode(
"utf-8") +
"\n")
76 except Exception
as e:
78 sys.stderr.write(
"Failed: " + str(e) +
": ".join(command) +
"\n")
81 if not (timeout
is None or watchdog
is None):
82 if not watchdog.is_alive():
84 "Terminated by timeout: " +
" ".join(command) +
"\n"
87 task_queue.task_done()
140 parser = argparse.ArgumentParser(
141 description=
"Run clang-tidy against changed files, and "
142 "output diagnostics only for modified "
146 "-clang-tidy-binary",
148 default=
"clang-tidy",
149 help=
"path to clang-tidy binary",
155 help=
"strip the smallest prefix containing P slashes",
161 help=
"custom pattern selecting file paths to check "
162 "(case sensitive, overrides -iregex)",
167 default=
r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)",
168 help=
"custom pattern selecting file paths to check "
169 "(case insensitive, overridden by -regex)",
175 help=
"number of tidy instances to be run in parallel.",
178 "-timeout", type=int, default=
None, help=
"timeout per each file in seconds."
181 "-fix", action=
"store_true", default=
False, help=
"apply suggested fixes"
185 help=
"checks filter, when not specified, use clang-tidy " "default",
191 help=
"Specify the path of .clang-tidy or custom config file",
194 parser.add_argument(
"-use-color", action=
"store_true", help=
"Use colors in output")
196 "-path", dest=
"build_path", help=
"Path used to read a compile command database."
201 metavar=
"FILE_OR_DIRECTORY",
203 help=
"A directory or a yaml file to store suggested fixes in, "
204 "which can be applied with clang-apply-replacements. If the "
205 "parameter is a directory, the fixes of each compilation unit are "
206 "stored in individual yaml files in the directory.",
213 help=
"A directory to store suggested fixes in, which can be applied "
214 "with clang-apply-replacements. The fixes of each compilation unit are "
215 "stored in individual yaml files in the directory.",
222 help=
"Additional argument to append to the compiler " "command line.",
226 dest=
"extra_arg_before",
229 help=
"Additional argument to prepend to the compiler " "command line.",
236 help=
"Arguments to remove from the compiler command line.",
242 help=
"Run clang-tidy in quiet mode",
249 help=
"Load the specified plugin in clang-tidy.",
254 help=
"Allow empty enabled checks.",
258 dest=
"skip_non_compiling",
261 help=
"Only check files in the compilation database",
264 "-warnings-as-errors",
265 help=
"Upgrades clang-tidy warnings to errors. Same format as '-checks'.",
271 help=
"Hide progress",
277 clang_tidy_args.extend(argv[argv.index(
"--") :])
278 argv = argv[: argv.index(
"--")]
280 args = parser.parse_args(argv)
287 for line
in sys.stdin:
288 match = re.search(
r'^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line)
290 filename = match.group(2)
294 if args.regex
is not None:
295 if not re.match(
"^%s$" % args.regex, filename):
298 if not re.match(
"^%s$" % args.iregex, filename, re.IGNORECASE):
303 compiling_files
is not None
304 and (Path.cwd() / filename)
not in compiling_files
308 match = re.search(
r"^@@.*\+(\d+)(,(\d+))?", line)
310 start_line = int(match.group(1))
313 line_count = int(match.group(3))
316 end_line = start_line + line_count - 1
317 lines_by_file.setdefault(filename, []).append([start_line, end_line])
319 if not any(lines_by_file):
320 print(
"No relevant changes found.")
323 max_task_count = args.j
324 if max_task_count == 0:
325 max_task_count = multiprocessing.cpu_count()
326 max_task_count = min(len(lines_by_file), max_task_count)
327 if not args.hide_progress:
328 print(f
"Running clang-tidy in {max_task_count} threads...")
330 combine_fixes =
False
331 export_fixes_dir =
None
332 delete_fixes_dir =
False
333 if args.export_fixes
is not None:
335 if args.export_fixes.endswith(os.path.sep)
and not os.path.isdir(
338 os.makedirs(args.export_fixes)
340 if not os.path.isdir(args.export_fixes):
343 "Cannot combine fixes in one yaml file. Either install PyYAML or specify an output directory."
348 if os.path.isdir(args.export_fixes):
349 export_fixes_dir = args.export_fixes
352 export_fixes_dir = tempfile.mkdtemp()
353 delete_fixes_dir =
True
356 task_queue = queue.Queue(max_task_count)
358 lock = threading.Lock()
365 max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files)
369 common_clang_tidy_args = []
371 common_clang_tidy_args.append(
"-fix")
372 if args.checks !=
"":
373 common_clang_tidy_args.append(
"-checks=" + args.checks)
374 if args.config_file !=
"":
375 common_clang_tidy_args.append(
"-config-file=" + args.config_file)
377 common_clang_tidy_args.append(
"-quiet")
378 if args.build_path
is not None:
379 common_clang_tidy_args.append(
"-p=%s" % args.build_path)
381 common_clang_tidy_args.append(
"--use-color")
382 if args.allow_no_checks:
383 common_clang_tidy_args.append(
"--allow-no-checks")
384 for arg
in args.extra_arg:
385 common_clang_tidy_args.append(
"-extra-arg=%s" % arg)
386 for arg
in args.extra_arg_before:
387 common_clang_tidy_args.append(
"-extra-arg-before=%s" % arg)
388 for arg
in args.removed_arg:
389 common_clang_tidy_args.append(
"-removed-arg=%s" % arg)
390 for plugin
in args.plugins:
391 common_clang_tidy_args.append(
"-load=%s" % plugin)
392 if args.warnings_as_errors:
393 common_clang_tidy_args.append(
"-warnings-as-errors=" + args.warnings_as_errors)
395 for name
in lines_by_file:
396 line_filter_json = json.dumps(
397 [{
"name": name,
"lines": lines_by_file[name]}], separators=(
",",
":")
401 command = [args.clang_tidy_binary]
402 command.append(
"-line-filter=" + line_filter_json)
403 if args.export_fixes
is not None:
406 (handle, tmp_name) = tempfile.mkstemp(suffix=
".yaml", dir=export_fixes_dir)
408 command.append(
"-export-fixes=" + tmp_name)
409 command.extend(common_clang_tidy_args)
411 command.extend(clang_tidy_args)
413 task_queue.put(command)
426 if not args.hide_progress:
427 print(f
"Writing fixes to {args.export_fixes} ...")
431 sys.stderr.write(
"Error exporting fixes.\n")
432 traceback.print_exc()
436 shutil.rmtree(export_fixes_dir)
437 sys.exit(return_code)