From db03930b5e241196c454203bfdfd6369d44f51de Mon Sep 17 00:00:00 2001 From: Martin Rakovec Date: Tue, 9 Aug 2022 13:49:57 +0200 Subject: [PATCH 1/3] Code style fixes Signed-off-by: Martin Rakovec --- oclint-json-compilation-database | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oclint-json-compilation-database b/oclint-json-compilation-database index 5827543..ff1cf5e 100755 --- a/oclint-json-compilation-database +++ b/oclint-json-compilation-database @@ -63,12 +63,12 @@ for file_item in compilation_database: file_path = file_item["file"] if not platform.system() == "Windows": file_path = get_source_path(file_item["file"], file_item["directory"]) - if source_exist_at(file_path) and not file_path in source_list: + if source_exist_at(file_path) and file_path not in source_list: source_list.append(file_path) if args.includes: matched_list = [] for inclusion_filter in args.includes: - matched_list.extend( source_list_inclusion_filter(source_list, inclusion_filter) ) + matched_list.extend(source_list_inclusion_filter(source_list, inclusion_filter)) source_list = matched_list if args.excludes: for exclusion_filter in args.excludes: From acf742c8181925141f51478dd30ca1f0e417d708 Mon Sep 17 00:00:00 2001 From: Martin Rakovec Date: Tue, 9 Aug 2022 13:57:08 +0200 Subject: [PATCH 2/3] Improve OCLint binary check Signed-off-by: Martin Rakovec --- oclint-json-compilation-database | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/oclint-json-compilation-database b/oclint-json-compilation-database index ff1cf5e..5eb0063 100755 --- a/oclint-json-compilation-database +++ b/oclint-json-compilation-database @@ -48,8 +48,18 @@ def source_list_exclusion_filter(source_list, exclusion_filter): filtered_list.append(path) return filtered_list -if not source_exist_at(OCLINT_BIN): - print("Error: OCLint executable file not found.") + +def check_working_oclint(oclint_bin) -> bool: + try: + with open(os.devnull, 'w') as dev_null: + subprocess.check_call([oclint_bin, "--version"], stdout=dev_null) + except subprocess.CalledProcessError: + print("Error: OCLint executable was not found or it does not work.") + return False + return True + + +if not check_working_oclint(OCLINT_BIN): sys.exit(99) json_compilation_database = os.path.join(args.build_path, 'compile_commands.json') From a5453a4a2feb545a084a91ffdf2cb4e0d1b48d42 Mon Sep 17 00:00:00 2001 From: Martin Rakovec Date: Tue, 9 Aug 2022 13:59:58 +0200 Subject: [PATCH 3/3] Add support for parallel processing Inspired by run-clang-tidy Signed-off-by: Martin Rakovec --- oclint-json-compilation-database | 56 ++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/oclint-json-compilation-database b/oclint-json-compilation-database index 5eb0063..440ee2d 100755 --- a/oclint-json-compilation-database +++ b/oclint-json-compilation-database @@ -7,12 +7,31 @@ import argparse import re import subprocess import sys +import queue +import threading +import multiprocessing +import shutil OCLINT_BIN_FOLDER = os.path.dirname(os.path.abspath(__file__)) OCLINT_BIN = OCLINT_BIN_FOLDER + os.sep + "oclint" if platform.system() == "Windows": OCLINT_BIN += ".exe" + +def run_lint(oclint_arguments, queue, lock): + """Takes filenames out of queue and runs oclint on them.""" + while True: + name = queue.get() + invocation = oclint_arguments + [name] + proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=sys.stdout.fileno()) + out, err = proc.communicate() + lock.acquire() + print("OC lint parsing file: " + name) + sys.stdout.write(out.decode("utf-8", errors="backslashreplace")) + lock.release() + queue.task_done() + + arg_parser = argparse.ArgumentParser(description='OCLint for JSON Compilation Database (compile_commands.json)') arg_parser.add_argument("-v", action="store_true", dest="invocation", help="show invocation command with arguments") arg_parser.add_argument('-debug', '--debug', action="store_true", dest="debug", help="invoke OCLint in debug mode") @@ -21,6 +40,8 @@ arg_parser.add_argument('-e', '-exclude', '--exclude', action='append', dest='ex arg_parser.add_argument('-p', action='store', metavar='build-path', dest='build_path', default=os.getcwd(), help="specify the directory containing compile_commands.json") arg_parser.add_argument('oclint_args', nargs='*', help="arguments that are passed to OCLint invocation") +arg_parser.add_argument('-j', type=int, default=0, + help='number of oclint instances to be run in parallel.') args = arg_parser.parse_args() def get_source_path(file_attr, dir_attr): @@ -88,10 +109,39 @@ if args.oclint_args: oclint_arguments += args.oclint_args if args.debug: oclint_arguments.append('-debug') -oclint_arguments += source_list + +max_task = args.j +if max_task == 0: + max_task = multiprocessing.cpu_count() + if args.invocation: print('------------------------------ OCLint ------------------------------') print(subprocess.list2cmdline(oclint_arguments)) print('--------------------------------------------------------------------') -exit_code = subprocess.call(oclint_arguments) -sys.exit(exit_code) + +try: + # Spin up a bunch of oclint-launching threads. + queue = queue.Queue(max_task) + lock = threading.RLock() + for _ in range(max_task): + t = threading.Thread(target=run_lint, + args=(oclint_arguments, queue, lock)) + t.daemon = True + t.start() + + # Fill the queue with files. + for name in source_list: + 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) + +sys.exit(0)