diff --git a/clang/tools/clang-format/clang-format-diff.py b/clang/tools/clang-format/clang-format-diff.py --- a/clang/tools/clang-format/clang-format-diff.py +++ b/clang/tools/clang-format/clang-format-diff.py @@ -25,6 +25,7 @@ import argparse import difflib +import os import re import subprocess import sys @@ -34,7 +35,6 @@ else: from io import BytesIO as StringIO - def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class= @@ -65,6 +65,9 @@ 'file to use.') parser.add_argument('-binary', default='clang-format', help='location of binary to use for clang-format') + parser.add_argument('-j', default=1, type=int, metavar='N', + help='number of concurrent clang-format processes to spawn in ' + 'parallel') args = parser.parse_args() # Extract changed lines for each file. @@ -99,46 +102,59 @@ ['-lines', str(start_line) + ':' + str(end_line)]) # Reformat files containing changes in place. - for filename, lines in lines_by_file.items(): - if args.i and args.verbose: - print('Formatting {}'.format(filename)) - command = [args.binary, filename] - if args.i: - command.append('-i') - if args.sort_includes: - command.append('-sort-includes') - command.extend(lines) - if args.style: - command.extend(['-style', args.style]) - if args.fallback_style: - command.extend(['-fallback-style', args.fallback_style]) - - try: - p = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=None, - stdin=subprocess.PIPE, - universal_newlines=True) - except OSError as e: - # Give the user more context when clang-format isn't - # found/isn't executable, etc. - raise RuntimeError( - 'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)) - - stdout, stderr = p.communicate() - if p.returncode != 0: - sys.exit(p.returncode) - - if not args.i: - with open(filename) as f: - code = f.readlines() - formatted_code = StringIO(stdout).readlines() - diff = difflib.unified_diff(code, formatted_code, - filename, filename, - '(before formatting)', '(after formatting)') - diff_string = ''.join(diff) - if len(diff_string) > 0: - sys.stdout.write(diff_string) + lbf = list(lines_by_file.items()) + procs = [None for i in range(args.j)] + while lbf: + spawned_one = False + for i, proc in enumerate(procs): + if not lbf: + break + if proc is None or proc.poll() is not None: + if proc is not None: + stdout, stderr = proc.communicate() + if proc.returncode != 0: + sys.exit(proc.returncode) + if not args.i: + with open(filename) as f: + code = f.readlines() + formatted_code = StringIO(stdout).readlines() + diff = difflib.unified_diff(code, formatted_code, + filename, filename, + '(before formatting)', + '(after formatting)') + diff_string = ''.join(diff) + if len(diff_string) > 0: + sys.stdout.write(diff_string) + filename, lines = lbf.pop() + spawned_one = True + if args.i and args.verbose: + print('Formatting {}'.format(filename)) + command = [args.binary, filename] + if args.i: + command.append('-i') + if args.sort_includes: + command.append('-sort-includes') + command.extend(lines) + if args.style: + command.extend(['-style', args.style]) + if args.fallback_style: + command.extend(['-fallback-style', args.fallback_style]) + try: + procs[i] = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=None, + stdin=subprocess.PIPE, + universal_newlines=True) + except OSError as e: + # Give the user more context when clang-format isn't + # found/isn't executable, etc. + raise RuntimeError( + 'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)) + # If we didn't spawn a single process after iterating through the whole + # list, wait on one of them to finish until we iterate through again, to + # prevent spinning in the case where we have a small number of jobs. + if not spawned_one: + procs[0].wait() if __name__ == '__main__': main()