13 Parallel clang-tidy runner 14 ========================== 16 Runs clang-tidy over all files in a compilation database. Requires clang-tidy 17 and clang-apply-replacements in $PATH. 20 - Run clang-tidy on all files in the current working directory with a default 21 set of checks and show warnings in the cpp files and all project headers. 22 run-clang-tidy.py $PWD 24 - Fix all header guards. 25 run-clang-tidy.py -fix -checks=-*,llvm-header-guard 27 - Fix all header guards included from clang-tidy and header guards 28 for clang-tidy headers. 29 run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ 30 -header-filter=extra/clang-tidy 32 Compilation database setup: 33 http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html 36 from __future__
import print_function
41 import multiprocessing
56 is_py2 = sys.version[0] ==
'2' 64 """Adjusts the directory until a compilation database is found.""" 66 while not os.path.isfile(os.path.join(result, path)):
67 if os.path.realpath(result) ==
'/':
68 print(
'Error: could not find compilation database.')
71 return os.path.realpath(result)
77 return os.path.normpath(os.path.join(directory, f))
81 header_filter, extra_arg, extra_arg_before, quiet,
83 """Gets a command line for clang-tidy.""" 84 start = [clang_tidy_binary]
85 if header_filter
is not None:
86 start.append(
'-header-filter=' + header_filter)
88 start.append(
'-checks=' + checks)
89 if tmpdir
is not None:
90 start.append(
'-export-fixes')
93 (handle, name) = tempfile.mkstemp(suffix=
'.yaml', dir=tmpdir)
97 start.append(
'-extra-arg=%s' % arg)
98 for arg
in extra_arg_before:
99 start.append(
'-extra-arg-before=%s' % arg)
100 start.append(
'-p=' + build_path)
102 start.append(
'-quiet')
104 start.append(
'-config=' + config)
110 """Merge all replacement files in a directory into a single file""" 113 mergekey=
"Diagnostics" 115 for replacefile
in glob.iglob(os.path.join(tmpdir,
'*.yaml')):
116 content = yaml.safe_load(open(replacefile,
'r')) 119 merged.extend(content.get(mergekey, []))
126 output = {
'MainSourceFile':
'', mergekey: merged }
127 with open(mergefile,
'w')
as out:
128 yaml.safe_dump(output, out)
131 open(mergefile,
'w').close()
135 """Checks if invoking supplied clang-apply-replacements binary works.""" 137 subprocess.check_call([args.clang_apply_replacements_binary,
'--version'])
139 print(
'Unable to run clang-apply-replacements. Is clang-apply-replacements ' 140 'binary correctly specified?', file=sys.stderr)
141 traceback.print_exc()
146 """Calls clang-apply-fixes on a given directory.""" 147 invocation = [args.clang_apply_replacements_binary]
149 invocation.append(
'-format')
151 invocation.append(
'-style=' + args.style)
152 invocation.append(tmpdir)
153 subprocess.call(invocation)
156 def run_tidy(args, tmpdir, build_path, queue, lock, failed_files):
157 """Takes filenames out of queue and runs clang-tidy on them.""" 161 tmpdir, build_path, args.header_filter,
162 args.extra_arg, args.extra_arg_before,
163 args.quiet, args.config)
165 proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
166 output, err = proc.communicate()
167 if proc.returncode != 0:
168 failed_files.append(name)
170 sys.stdout.write(
' '.
join(invocation) +
'\n' + output.decode(
'utf-8'))
173 sys.stderr.write(err.decode(
'utf-8'))
178 parser = argparse.ArgumentParser(description=
'Runs clang-tidy over all files ' 179 'in a compilation database. Requires ' 180 'clang-tidy and clang-apply-replacements in ' 182 parser.add_argument(
'-clang-tidy-binary', metavar=
'PATH',
183 default=
'clang-tidy',
184 help=
'path to clang-tidy binary')
185 parser.add_argument(
'-clang-apply-replacements-binary', metavar=
'PATH',
186 default=
'clang-apply-replacements',
187 help=
'path to clang-apply-replacements binary')
188 parser.add_argument(
'-checks', default=
None,
189 help=
'checks filter, when not specified, use clang-tidy ' 191 parser.add_argument(
'-config', default=
None,
192 help=
'Specifies a configuration in YAML/JSON format: ' 193 ' -config="{Checks: \'*\', ' 194 ' CheckOptions: [{key: x, ' 196 'When the value is empty, clang-tidy will ' 197 'attempt to find a file named .clang-tidy for ' 198 'each source file in its parent directories.')
199 parser.add_argument(
'-header-filter', default=
None,
200 help=
'regular expression matching the names of the ' 201 'headers to output diagnostics from. Diagnostics from ' 202 'the main file of each translation unit are always ' 205 parser.add_argument(
'-export-fixes', metavar=
'filename', dest=
'export_fixes',
206 help=
'Create a yaml file to store suggested fixes in, ' 207 'which can be applied with clang-apply-replacements.')
208 parser.add_argument(
'-j', type=int, default=0,
209 help=
'number of tidy instances to be run in parallel.')
210 parser.add_argument(
'files', nargs=
'*', default=[
'.*'],
211 help=
'files to be processed (regex on path)')
212 parser.add_argument(
'-fix', action=
'store_true', help=
'apply fix-its')
213 parser.add_argument(
'-format', action=
'store_true', help=
'Reformat code ' 214 'after applying fixes')
215 parser.add_argument(
'-style', default=
'file', help=
'The style of reformat ' 216 'code after applying fixes')
217 parser.add_argument(
'-p', dest=
'build_path',
218 help=
'Path used to read a compile command database.')
219 parser.add_argument(
'-extra-arg', dest=
'extra_arg',
220 action=
'append', default=[],
221 help=
'Additional argument to append to the compiler ' 223 parser.add_argument(
'-extra-arg-before', dest=
'extra_arg_before',
224 action=
'append', default=[],
225 help=
'Additional argument to prepend to the compiler ' 227 parser.add_argument(
'-quiet', action=
'store_true',
228 help=
'Run clang-tidy in quiet mode')
229 args = parser.parse_args()
231 db_path =
'compile_commands.json' 233 if args.build_path
is not None:
234 build_path = args.build_path
240 invocation = [args.clang_tidy_binary,
'-list-checks']
241 invocation.append(
'-p=' + build_path)
243 invocation.append(
'-checks=' + args.checks)
244 invocation.append(
'-')
247 with open(os.devnull,
'w')
as dev_null:
248 subprocess.check_call(invocation, stdout=dev_null)
250 subprocess.check_call(invocation)
252 print(
"Unable to run clang-tidy.", file=sys.stderr)
256 database = json.load(open(os.path.join(build_path, db_path)))
258 for entry
in database]
262 max_task = multiprocessing.cpu_count()
265 if args.fix
or (yaml
and args.export_fixes):
267 tmpdir = tempfile.mkdtemp()
270 file_name_re = re.compile(
'|'.
join(args.files))
275 task_queue = queue.Queue(max_task)
278 lock = threading.Lock()
279 for _
in range(max_task):
280 t = threading.Thread(target=run_tidy,
281 args=(args, tmpdir, build_path, task_queue, lock, failed_files))
287 if file_name_re.search(name):
292 if len(failed_files):
295 except KeyboardInterrupt:
298 print(
'\nCtrl-C detected, goodbye.')
300 shutil.rmtree(tmpdir)
303 if yaml
and args.export_fixes:
304 print(
'Writing fixes to ' + args.export_fixes +
' ...')
308 print(
'Error exporting fixes.\n', file=sys.stderr)
309 traceback.print_exc()
313 print(
'Applying fixes ...')
317 print(
'Error applying fixes.\n', file=sys.stderr)
318 traceback.print_exc()
322 shutil.rmtree(tmpdir)
323 sys.exit(return_code)
325 if __name__ ==
'__main__':
def run_tidy(args, tmpdir, build_path, queue, lock, failed_files)
def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, header_filter, extra_arg, extra_arg_before, quiet, config)
def make_absolute(f, directory)
def find_compilation_database(path)
def check_clang_apply_replacements_binary(args)
def merge_replacement_files(tmpdir, mergefile)
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
def apply_fixes(args, tmpdir)