[clang-tools-extra] r217368 - [clang-tidy] Add a little python script that can run clang-tidy and apply fixes over an entire codebase.

Benjamin Kramer benny.kra at googlemail.com
Mon Sep 8 07:01:31 PDT 2014


Author: d0k
Date: Mon Sep  8 09:01:31 2014
New Revision: 217368

URL: http://llvm.org/viewvc/llvm-project?rev=217368&view=rev
Log:
[clang-tidy] Add a little python script that can run clang-tidy and apply fixes over an entire codebase.

Ever wanted to fix all the header guards in clang? Now it's easy.
  Make sure clang-tidy is in $PATH and a compilation database is available.
  $ ./run-clang-tidy.py -checks=-*,llvm-header-guard -fix
  ... get coffee (or more CPU cores) ...
  $ svn diff

Some may argue that this is just a glorified xargs -P, but it does a bit more ;)

Differential Revision: http://reviews.llvm.org/D5188

Added:
    clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py   (with props)

Added: clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py?rev=217368&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py (added)
+++ clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py Mon Sep  8 09:01:31 2014
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+#
+#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+# FIXME: Integrate with clang-tidy-diff.py
+
+"""
+Parallel clang-tidy runner
+==========================
+
+Runs clang-tidy over all files in a compilation database. Requires clang-tidy
+and clang-apply-replacements in $PATH.
+
+Example invocations.
+- Run clang-tidy on all files in the current working directory with a default
+  set of checks and show warnings in the cpp files and all project headers.
+    run-clang-tidy.py $PWD
+
+- Fix all header guards.
+    run-clang-tidy.py -fix -checks=-*,llvm-header-guard
+
+- Fix all header guards included from clang-tidy and header guards
+  for clang-tidy headers.
+    run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \
+                      -header-filter=extra/clang-tidy
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+
+
+def find_compilation_database(path):
+  """Adjusts the directory until a compilation database is found."""
+  result = './'
+  while not os.path.isfile(os.path.join(result, path)):
+    if os.path.realpath(result) == '/':
+      print 'Error: could not find compilation database.'
+      sys.exit(1)
+    result += '../'
+  return os.path.realpath(result)
+
+
+def get_tidy_invocation(f, checks, tmpdir, build_path, header_filter):
+  """Gets a command line for clang-tidy."""
+  start = ['clang-tidy']
+  if header_filter is not None:
+    start.append('-header-filter=' + header_filter)
+  else:
+    # Show warnings in all in-project headers by default.
+    start.append('-header-filter=^' + build_path + '/.*')
+  if checks:
+    start.append('-checks=-*,' + checks)
+  if tmpdir is not None:
+    start.append('-export-fixes')
+    # Get a temporary file. We immediately close the handle so clang-tidy can
+    # overwrite it.
+    (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
+    handle.close()
+    start.append(name)
+  start.append('-p=' + build_path)
+  start.append(f)
+  return start
+
+
+def apply_fixes(tmpdir, format_enabled):
+  """Calls clang-apply-fixes on a given directory. Deletes the dir when done."""
+  invocation = ['clang-apply-replacements']
+  if format_enabled:
+    invocation.append('-format')
+  invocation.append(tmpdir)
+  subprocess.call(invocation)
+  shutil.rmtree(tmpdir)
+
+
+def run_tidy(args, tmpdir, build_path, queue):
+  """Takes filenames out of queue and runs clang-tidy on them."""
+  while True:
+    name = queue.get()
+    invocation = get_tidy_invocation(name, args.checks, tmpdir,
+                                     build_path, args.header_filter)
+    sys.stdout.write(' '.join(invocation) + '\n')
+    subprocess.call(invocation)
+    queue.task_done()
+
+
+def main():
+  parser = argparse.ArgumentParser(description='Runs clang-tidy over all files '
+                                   'in a compilation database. Requires '
+                                   'clang-tidy and clang-apply-replacements in '
+                                   '$PATH.')
+  parser.add_argument('-checks', default=None,
+                      help='checks filter, when not specified, use clang-tidy '
+                      'default')
+  parser.add_argument('-header-filter', default=None,
+                      help='regular expression matching the names of the '
+                      'headers to output diagnostics from. Diagnostics from '
+                      'the main file of each translation unit are always '
+                      'displayed.')
+  parser.add_argument('-j', type=int, default=0,
+                      help='number of tidy instances to be run in parallel.')
+  parser.add_argument('files', nargs='*', default=['.*'],
+                      help='files to be processed (regex on path)')
+  parser.add_argument('-fix', action='store_true', help='apply fix-its')
+  parser.add_argument('-format', action='store_true', help='Reformat code '
+                      'after applying fixes')
+  args = parser.parse_args()
+
+  # Find our database.
+  db_path = 'compile_commands.json'
+  build_path = find_compilation_database(db_path)
+
+  # Load the database and extract all files.
+  database = json.load(open(os.path.join(build_path, db_path)))
+  files = [entry['file'] for entry in database]
+
+  max_task = args.j
+  if max_task == 0:
+    max_task = multiprocessing.cpu_count()
+
+  tmpdir = None
+  if args.fix:
+    tmpdir = tempfile.mkdtemp()
+
+  # Build up a big regexy filter from all command line arguments.
+  file_name_re = re.compile('(' + ')|('.join(args.files) + ')')
+
+  try:
+    # Spin up a bunch of tidy-launching threads.
+    queue = Queue.Queue(max_task)
+    for _ in range(max_task):
+      t = threading.Thread(target=run_tidy,
+                           args=(args, tmpdir, build_path, queue))
+      t.daemon = True
+      t.start()
+
+    # Fill the queue with files.
+    for name in files:
+      if file_name_re.search(name):
+        queue.put(name)
+
+    # Wait for all threads to be done.
+    queue.join()
+
+  except KeyboardInterrupt:
+    # This is a sad hack. Unfortunately subprocess goes
+    # bonkers with ctrl-c and we start forking merrily.
+    print '\nCtrl-C detected, goodbye.'
+    if args.fix:
+      shutil.rmtree(tmpdir)
+    os.kill(0, 9)
+
+  if args.fix:
+    print 'Applying fixes ...'
+    apply_fixes(tmpdir, args.format)
+
+if __name__ == '__main__':
+  main()

Propchange: clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py
------------------------------------------------------------------------------
    svn:executable = *





More information about the cfe-commits mailing list