JUCE_Template/run-clang-format.py

151 lines
4.6 KiB
Python

#!/usr/bin/env python
# pylint: disable=C0111
"""
Runs clang format over all cpp files
"""
from __future__ import print_function
import argparse
import fnmatch
import os
import multiprocessing
import queue
import subprocess
import sys
import threading
def glob_files(args):
files = []
extensions = args.extensions.split(',')
for directory in args.directories:
for root, _, filenames in os.walk(directory):
for ext in extensions:
for filename in fnmatch.filter(filenames, '*.' + ext):
files.append(os.path.join(root, filename))
return files
def parse_args(argv=None):
if argv is None:
argv = sys.argv
parser = argparse.ArgumentParser(
description='Runs clang-format over all files in given directories.'
' Requires clang-format in PATH.')
parser.add_argument('--clang-format-binary', metavar='PATH',
default='clang-format',
help='path to clang-format binary')
parser.add_argument('-e', '--extensions', dest='extensions',
help='comma-delimited list of extensions used to glob source files',
default="c,cc,cpp,cxx,c++,h,hh,hpp,hxx,h++")
parser.add_argument('-style',
help='formatting style',
default="file")
parser.add_argument('--no-inplace', dest='inplace', action='store_false',
help='do not format files inplace, but write output to the console'
' (useful for debugging)',
default=True)
parser.add_argument('-j', metavar='THREAD_COUNT', type=int, default=0,
help='number of clang-format instances to be run in parallel')
parser.add_argument('-v', '--verbose', action='store_true',
help='output verbose comments')
parser.add_argument(metavar='DIRPATH', dest='directories', nargs='*',
help='path(s) used to glob source files')
args = parser.parse_args(argv[1:])
if not args.directories:
args.directories = [os.getcwd()]
check_clang_format_binary(args)
return args
def _get_format_invocation(args, filename):
invocation = [args.clang_format_binary]
invocation.append('-style=' + args.style)
if args.inplace:
invocation.append('-i')
invocation.append(filename)
return invocation
def check_clang_format_binary(args):
"""Checks if invoking supplied clang-format binary works."""
try:
subprocess.check_output([args.clang_format_binary, '--version'])
except OSError:
print('Unable to run clang-format. Is clang-format '
'binary correctly specified?', file=sys.stderr)
raise
def run_format(args, task_queue, formatted_files):
"""Takes filenames out of queue and runs clang-format on them."""
while True:
filename = task_queue.get()
invocation = _get_format_invocation(args, filename)
if args.verbose:
print('Processing {}'.format(filename))
formatted = subprocess.check_output(invocation)
formatted_files[filename] = formatted
task_queue.task_done()
def format_all(args, files):
max_task = args.j
if max_task == 0:
max_task = multiprocessing.cpu_count()
formatted_files = {}
try:
# Spin up a bunch of format-launching threads.
task_queue = queue.Queue(max_task)
for _ in range(max_task):
task_thread = threading.Thread(target=run_format,
args=(args, task_queue, formatted_files))
task_thread.daemon = True
task_thread.start()
# Fill the queue with files.
for name in files:
task_queue.put(name)
# Wait for all threads to be done.
task_queue.join()
except OSError:
print("Cannot find clang-format at '{}'.".format(args.clang_format_binary),
file=sys.stderr)
raise
except subprocess.CalledProcessError as ex:
print("Running clang-format failed with non-zero status.", file=sys.stderr)
print("Command : {}".format(' '.join(ex.cmd)), file=sys.stderr)
print("Return code: {}".format(str(ex.returncode)), file=sys.stderr)
raise
return formatted_files
def main():
args = parse_args()
files = glob_files(args)
format_all(args, files)
if __name__ == '__main__':
main() # pragma: no cover