-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34 from StephanKa/feature/add-scripts-clang-tidy-…
…clang-format-cmake-targets added scripts for clang-tidy and clang-format, added different cmake targets
- Loading branch information
Showing
4 changed files
with
994 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,23 @@ | ||
FIND_PACKAGE(Python COMPONENTS Interpreter REQUIRED) | ||
|
||
# To exclude directories from the format check, add corresponding clang-format config files into those directories. | ||
ADD_CUSTOM_TARGET(clang-format-check | ||
USES_TERMINAL | ||
COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/run-clang-format.py -warnings-as-errors | ||
) | ||
|
||
ADD_CUSTOM_TARGET(clang-format-check-fix | ||
USES_TERMINAL | ||
COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/run-clang-format.py -fix | ||
) | ||
|
||
ADD_CUSTOM_TARGET(format-check | ||
ADD_CUSTOM_TARGET(clang-tidy-check | ||
USES_TERMINAL | ||
COMMAND ${CMAKE_SOURCE_DIR}/scripts/run-clang-format.py -warnings-as-errors | ||
) | ||
COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/run-clang-tidy.py | ||
) | ||
|
||
ADD_CUSTOM_TARGET(format-check-fix | ||
ADD_CUSTOM_TARGET(clang-tidy-diff-check | ||
USES_TERMINAL | ||
COMMAND ${CMAKE_SOURCE_DIR}/scripts/run-clang-format.py -fix | ||
) | ||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | ||
COMMAND git diff -U0 HEAD --no-prefix | ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/run-clang-tidy-diff.py -path ${CMAKE_BINARY_DIR} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
""" | ||
This script will call clang-format. | ||
Call example: run-clang-format.py - Parallel clang-format runner | ||
Based on run-clang-tidy.py, which is part of the LLVM Project, under the | ||
Apache License v2.0 with LLVM Exceptions. | ||
See https://llvm.org/LICENSE.txt for license information. | ||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
""" | ||
|
||
import argparse | ||
import json | ||
import multiprocessing | ||
import os | ||
import subprocess | ||
import sys | ||
import threading | ||
import queue as queue | ||
|
||
|
||
def find_compilation_database(path): | ||
"""Adjust 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 make_absolute(f, directory): | ||
"""Create a absolute path from given parameters.""" | ||
if os.path.isabs(f): | ||
return f | ||
return os.path.normpath(os.path.join(directory, f)) | ||
|
||
|
||
def get_format_invocation(f, clang_format_binary, fix, warnings_as_errors, quiet): | ||
"""Get a command line for clang-format.""" | ||
start = [clang_format_binary] | ||
if fix: | ||
start.append('-i') | ||
else: | ||
start.append('--dry-run') | ||
if warnings_as_errors: | ||
start.append('--Werror') | ||
start.append(f) | ||
return start | ||
|
||
|
||
def run_format(args, _, queue, lock, failed_files): | ||
"""Take filenames out of queue and runs clang-format on them.""" | ||
while True: | ||
name = queue.get() | ||
invocation = get_format_invocation(name, args.clang_format_binary, args.fix, args.warnings_as_errors, | ||
args.quiet) | ||
|
||
proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
output, err = proc.communicate() | ||
if proc.returncode != 0: | ||
failed_files.append(name) | ||
with lock: | ||
sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8')) | ||
if len(err) > 0: | ||
sys.stdout.flush() | ||
sys.stderr.write(err.decode('utf-8')) | ||
queue.task_done() | ||
|
||
|
||
if __name__ == '__main__': | ||
parser = argparse.ArgumentParser(description='Runs clang-format over all files ' | ||
'in a compilation database. 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('-p', dest='build_path', help='Path used to read a compile command database.') | ||
parser.add_argument('-j', type=int, default=0, help='number of tidy instances to be run in parallel.') | ||
parser.add_argument('-fix', action='store_true', help='reformat files') | ||
parser.add_argument('-warnings-as-errors', action='store_true', | ||
help='Let the clang-tidy process return != 0 if a check failed.') | ||
parser.add_argument('-quiet', action='store_true', help='Run clang-format in quiet mode') | ||
args = parser.parse_args() | ||
|
||
db_path = 'compile_commands.json' | ||
|
||
if args.build_path is not None: | ||
build_path = args.build_path | ||
else: | ||
# Find our database | ||
build_path = find_compilation_database(db_path) | ||
|
||
try: | ||
with open(os.devnull, 'w') as dev_null: | ||
subprocess.check_call([args.clang_format_binary, '--dump-config'], stdout=dev_null) | ||
except Exception as ex: | ||
print(f'Unable to run clang-format. {ex} - {sys.stderr}') | ||
sys.exit(1) | ||
|
||
# Load the database and extract all files. | ||
database = json.load(open(os.path.join(build_path, db_path))) | ||
files = [make_absolute(entry['file'], entry['directory']) for entry in database] | ||
|
||
max_task = args.j | ||
if max_task == 0: | ||
max_task = multiprocessing.cpu_count() | ||
|
||
return_code = 0 | ||
try: | ||
# Spin up a bunch of format-launching threads. | ||
task_queue = queue.Queue(max_task) | ||
# List of files with a non-zero return code. | ||
failed_files = [] | ||
lock = threading.Lock() | ||
for _ in range(max_task): | ||
t = threading.Thread(target=run_format, args=(args, build_path, task_queue, lock, failed_files)) | ||
t.daemon = True | ||
t.start() | ||
|
||
# Fill the queue with files. | ||
for name in files: | ||
task_queue.put(name) | ||
|
||
# Wait for all threads to be done. | ||
task_queue.join() | ||
if len(failed_files): | ||
return_code = 1 | ||
|
||
except KeyboardInterrupt: | ||
# This is a sad hack. Unfortunately subprocess goes | ||
# bonkers with ctrl-c and we start forking merrily. | ||
print('\nCtrl-C detected, goodbye.') | ||
os.kill(0, 9) | ||
|
||
sys.exit(return_code) |
Oops, something went wrong.