14 Parallel clang-tidy runner 15 ========================== 17 Runs clang-tidy over all files in a compilation database. Requires clang-tidy 18 and clang-apply-replacements in $PATH. 21 - Run clang-tidy on all files in the current working directory with a default 22 set of checks and show warnings in the cpp files and all project headers. 23 run-clang-tidy.py $PWD 25 - Fix all header guards. 26 run-clang-tidy.py -fix -checks=-*,llvm-header-guard 28 - Fix all header guards included from clang-tidy and header guards 29 for clang-tidy headers. 30 run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ 31 -header-filter=extra/clang-tidy 33 Compilation database setup: 34 http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html 37 from __future__
import print_function
42 import multiprocessing
53 is_py2 = sys.version[0] ==
'2' 61 """Adjusts the directory until a compilation database is found.""" 63 while not os.path.isfile(os.path.join(result, path)):
64 if os.path.realpath(result) ==
'/':
65 print(
'Error: could not find compilation database.')
68 return os.path.realpath(result)
74 return os.path.normpath(os.path.join(directory, f))
78 header_filter, extra_arg, extra_arg_before, quiet,
80 """Gets a command line for clang-tidy.""" 81 start = [clang_tidy_binary]
82 if header_filter
is not None:
83 start.append(
'-header-filter=' + header_filter)
86 start.append(
'-header-filter=^' + build_path +
'/.*')
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, 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)
164 sys.stdout.write(
' '.
join(invocation) +
'\n')
165 return_code = subprocess.call(invocation)
167 failed_files.append(name)
172 parser = argparse.ArgumentParser(description=
'Runs clang-tidy over all files ' 173 'in a compilation database. Requires ' 174 'clang-tidy and clang-apply-replacements in ' 176 parser.add_argument(
'-clang-tidy-binary', metavar=
'PATH',
177 default=
'clang-tidy',
178 help=
'path to clang-tidy binary')
179 parser.add_argument(
'-clang-apply-replacements-binary', metavar=
'PATH',
180 default=
'clang-apply-replacements',
181 help=
'path to clang-apply-replacements binary')
182 parser.add_argument(
'-checks', default=
None,
183 help=
'checks filter, when not specified, use clang-tidy ' 185 parser.add_argument(
'-config', default=
None,
186 help=
'Specifies a configuration in YAML/JSON format: ' 187 ' -config="{Checks: \'*\', ' 188 ' CheckOptions: [{key: x, ' 190 'When the value is empty, clang-tidy will ' 191 'attempt to find a file named .clang-tidy for ' 192 'each source file in its parent directories.')
193 parser.add_argument(
'-header-filter', default=
None,
194 help=
'regular expression matching the names of the ' 195 'headers to output diagnostics from. Diagnostics from ' 196 'the main file of each translation unit are always ' 198 parser.add_argument(
'-export-fixes', metavar=
'filename', dest=
'export_fixes',
199 help=
'Create a yaml file to store suggested fixes in, ' 200 'which can be applied with clang-apply-replacements.')
201 parser.add_argument(
'-j', type=int, default=0,
202 help=
'number of tidy instances to be run in parallel.')
203 parser.add_argument(
'files', nargs=
'*', default=[
'.*'],
204 help=
'files to be processed (regex on path)')
205 parser.add_argument(
'-fix', action=
'store_true', help=
'apply fix-its')
206 parser.add_argument(
'-format', action=
'store_true', help=
'Reformat code ' 207 'after applying fixes')
208 parser.add_argument(
'-style', default=
'file', help=
'The style of reformat ' 209 'code after applying fixes')
210 parser.add_argument(
'-p', dest=
'build_path',
211 help=
'Path used to read a compile command database.')
212 parser.add_argument(
'-extra-arg', dest=
'extra_arg',
213 action=
'append', default=[],
214 help=
'Additional argument to append to the compiler ' 216 parser.add_argument(
'-extra-arg-before', dest=
'extra_arg_before',
217 action=
'append', default=[],
218 help=
'Additional argument to prepend to the compiler ' 220 parser.add_argument(
'-quiet', action=
'store_true',
221 help=
'Run clang-tidy in quiet mode')
222 args = parser.parse_args()
224 db_path =
'compile_commands.json' 226 if args.build_path
is not None:
227 build_path = args.build_path
233 invocation = [args.clang_tidy_binary,
'-list-checks']
234 invocation.append(
'-p=' + build_path)
236 invocation.append(
'-checks=' + args.checks)
237 invocation.append(
'-')
238 subprocess.check_call(invocation)
240 print(
"Unable to run clang-tidy.", file=sys.stderr)
244 database = json.load(open(os.path.join(build_path, db_path)))
246 for entry
in database]
250 max_task = multiprocessing.cpu_count()
253 if args.fix
or args.export_fixes:
255 tmpdir = tempfile.mkdtemp()
258 file_name_re = re.compile(
'|'.
join(args.files))
263 task_queue = queue.Queue(max_task)
266 for _
in range(max_task):
267 t = threading.Thread(target=run_tidy,
268 args=(args, tmpdir, build_path, task_queue, failed_files))
274 if file_name_re.search(name):
279 if len(failed_files):
282 except KeyboardInterrupt:
285 print(
'\nCtrl-C detected, goodbye.')
287 shutil.rmtree(tmpdir)
290 if args.export_fixes:
291 print(
'Writing fixes to ' + args.export_fixes +
' ...')
295 print(
'Error exporting fixes.\n', file=sys.stderr)
296 traceback.print_exc()
300 print(
'Applying fixes ...')
304 print(
'Error applying fixes.\n', file=sys.stderr)
305 traceback.print_exc()
309 shutil.rmtree(tmpdir)
310 sys.exit(return_code)
312 if __name__ ==
'__main__':
def run_tidy(args, tmpdir, build_path, queue, 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)