Files
2020-11-27 13:33:28 +01:00

164 lines
3.8 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import difflib
import subprocess
import sys
from multiprocessing.pool import ThreadPool
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
_SRC_DIR = _SCRIPT_DIR.parent / "src"
_EXCLUDE_DIRS = ["third_party"]
def collect_sources(file_ext):
# Find all files in the src folder.
files = []
files.extend(_SRC_DIR.glob(f"*{file_ext}"))
# Find all files in non-excluded sub-folders.
dirs = _SRC_DIR.glob("*")
dirs = [d for d in dirs if d.is_dir() and d.name not in _EXCLUDE_DIRS]
for dir in dirs:
files.extend(dir.glob(f"**/*{file_ext}"))
return files
def clang_tidy(src, build_path, fix):
file_name = src.relative_to(_SRC_DIR)
print(f"clang-tidy: {file_name}")
args = ["clang-tidy", "-p", build_path]
if fix:
args.append("--fix")
args.append(src)
proc = subprocess.run(
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8"
)
return proc.stdout
def read_file(file_name):
with open(file_name, encoding="utf8") as f:
return f.readlines()
def files_equal(a, b):
if len(a) != len(b):
return False
for i in range(0, len(a)):
if a[i] != b[i]:
return False
return True
def clang_format(src, fix):
file_name = src.relative_to(_SRC_DIR)
print(f"clang-format: {file_name}")
args = ["clang-format", "-Werror"]
if fix:
args.append("-i")
else:
args.append("--dry-run")
args.append(src)
before = read_file(src)
proc = subprocess.run(
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8"
)
after = read_file(src)
result = proc.stderr
if not files_equal(after, before):
if result:
result += "\n"
result += f"{src}: warning: code was clang-formatted:\n"
result += "".join(
difflib.context_diff(
before, after, fromfile=str(file_name), tofile=str(file_name), n=0
)
)
return result
def run_clang_tidy(build_path, fix):
sources = collect_sources(".cpp")
# Run jobs in a thread pool.
pool = ThreadPool()
results = []
for src in sources:
results.append(
pool.apply_async(
clang_tidy,
[
src,
build_path,
fix,
],
)
)
# Wait for all workers to finish.
pool.close()
pool.join()
return [r.get() for r in results if r.get()]
def run_clang_format(fix):
sources = collect_sources(".cpp")
sources.extend(collect_sources(".hpp"))
# Run jobs in a thread pool.
pool = ThreadPool()
results = []
for src in sources:
results.append(
pool.apply_async(
clang_format,
[
src,
fix,
],
)
)
# Wait for all workers to finish.
pool.close()
pool.join()
return [r.get() for r in results if r.get()]
def run_linters(build_path, fix):
errors = []
errors.extend(run_clang_tidy(build_path, fix))
errors.extend(run_clang_format(fix))
all_ok = True
if errors:
all_ok = False
print(f"\n***ERROR: Some checks failed 😣")
for err in errors:
print(err)
else:
print("\nAll checks passed! 👍")
return all_ok
def main():
parser = argparse.ArgumentParser(description="Run linter checks.")
parser.add_argument("-p", "--build-path", required=True, help="build path")
parser.add_argument("--fix", action="store_true", help="try to fix problems")
args = parser.parse_args()
if not run_linters(args.build_path, args.fix):
return 1
return 0
if __name__ == "__main__":
sys.exit(main())