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, 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') +
'\n')
172 sys.stderr.write(err.decode(
'utf-8') +
'\n')
177 parser = argparse.ArgumentParser(description=
'Runs clang-tidy over all files ' 178 'in a compilation database. Requires ' 179 'clang-tidy and clang-apply-replacements in ' 181 parser.add_argument(
'-clang-tidy-binary', metavar=
'PATH',
182 default=
'clang-tidy',
183 help=
'path to clang-tidy binary')
184 parser.add_argument(
'-clang-apply-replacements-binary', metavar=
'PATH',
185 default=
'clang-apply-replacements',
186 help=
'path to clang-apply-replacements binary')
187 parser.add_argument(
'-checks', default=
None,
188 help=
'checks filter, when not specified, use clang-tidy ' 190 parser.add_argument(
'-config', default=
None,
191 help=
'Specifies a configuration in YAML/JSON format: ' 192 ' -config="{Checks: \'*\', ' 193 ' CheckOptions: [{key: x, ' 195 'When the value is empty, clang-tidy will ' 196 'attempt to find a file named .clang-tidy for ' 197 'each source file in its parent directories.')
198 parser.add_argument(
'-header-filter', default=
None,
199 help=
'regular expression matching the names of the ' 200 'headers to output diagnostics from. Diagnostics from ' 201 'the main file of each translation unit are always ' 203 parser.add_argument(
'-export-fixes', metavar=
'filename', dest=
'export_fixes',
204 help=
'Create a yaml file to store suggested fixes in, ' 205 'which can be applied with clang-apply-replacements.')
206 parser.add_argument(
'-j', type=int, default=0,
207 help=
'number of tidy instances to be run in parallel.')
208 parser.add_argument(
'files', nargs=
'*', default=[
'.*'],
209 help=
'files to be processed (regex on path)')
210 parser.add_argument(
'-fix', action=
'store_true', help=
'apply fix-its')
211 parser.add_argument(
'-format', action=
'store_true', help=
'Reformat code ' 212 'after applying fixes')
213 parser.add_argument(
'-style', default=
'file', help=
'The style of reformat ' 214 'code after applying fixes')
215 parser.add_argument(
'-p', dest=
'build_path',
216 help=
'Path used to read a compile command database.')
217 parser.add_argument(
'-extra-arg', dest=
'extra_arg',
218 action=
'append', default=[],
219 help=
'Additional argument to append to the compiler ' 221 parser.add_argument(
'-extra-arg-before', dest=
'extra_arg_before',
222 action=
'append', default=[],
223 help=
'Additional argument to prepend to the compiler ' 225 parser.add_argument(
'-quiet', action=
'store_true',
226 help=
'Run clang-tidy in quiet mode')
227 args = parser.parse_args()
229 db_path =
'compile_commands.json' 231 if args.build_path
is not None:
232 build_path = args.build_path
238 invocation = [args.clang_tidy_binary,
'-list-checks']
239 invocation.append(
'-p=' + build_path)
241 invocation.append(
'-checks=' + args.checks)
242 invocation.append(
'-')
243 subprocess.check_call(invocation)
245 print(
"Unable to run clang-tidy.", file=sys.stderr)
249 database = json.load(open(os.path.join(build_path, db_path)))
251 for entry
in database]
255 max_task = multiprocessing.cpu_count()
258 if args.fix
or args.export_fixes:
260 tmpdir = tempfile.mkdtemp()
263 file_name_re = re.compile(
'|'.
join(args.files))
268 task_queue = queue.Queue(max_task)
271 lock = threading.Lock()
272 for _
in range(max_task):
273 t = threading.Thread(target=run_tidy,
274 args=(args, tmpdir, build_path, task_queue, lock, failed_files))
280 if file_name_re.search(name):
285 if len(failed_files):
288 except KeyboardInterrupt:
291 print(
'\nCtrl-C detected, goodbye.')
293 shutil.rmtree(tmpdir)
296 if args.export_fixes:
297 print(
'Writing fixes to ' + args.export_fixes +
' ...')
301 print(
'Error exporting fixes.\n', file=sys.stderr)
302 traceback.print_exc()
306 print(
'Applying fixes ...')
310 print(
'Error applying fixes.\n', file=sys.stderr)
311 traceback.print_exc()
315 shutil.rmtree(tmpdir)
316 sys.exit(return_code)
318 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)