import argparse from typing_extensions import Protocol import yapf from . import paths, proc class FormatArguments(Protocol): check: bool cpp: bool py: bool def start() -> None: parser = argparse.ArgumentParser() parser.add_argument('--check', help='Check whether files need to be formatted, but do not modify them.', action='store_true') parser.add_argument('--no-cpp', help='Skip formatting/checking C++ files', action='store_false', dest='cpp') parser.add_argument('--no-py', help='Skip formatting/checking Python files', action='store_false', dest='py') args: FormatArguments = parser.parse_args() if args.cpp: format_cpp(args) if args.py: format_py(args) def format_cpp(args: FormatArguments) -> None: src_dir = paths.PROJECT_ROOT / 'src' cpp_files = src_dir.glob('**/*.[hc]pp') cf_args: proc.CommandLine = [ ('--dry-run', '--Werror') if args.check else (), '-i', # Modify files in-place '--verbose', ] for cf_cand in ('clang-format-10', 'clang-format-9', 'clang-format-8', 'clang-format'): cf = paths.find_exe(cf_cand) if not cf: continue break else: raise RuntimeError('No clang-format executable found') print(f'Using clang-format: {cf_cand}') res = proc.run([cf, cf_args, cpp_files]) if res.returncode and args.check: raise RuntimeError('Format checks failed for one or more C++ files. (See above.)') if res.returncode: raise RuntimeError('Format execution failed. Check output above.') def format_py(args: FormatArguments) -> None: py_files = paths.TOOLS_DIR.rglob('*.py') rc = yapf.main( list(proc.flatten_cmd([ '--parallel', '--verbose', ('--diff') if args.check else ('--in-place'), py_files, ]))) if rc and args.check: raise RuntimeError('Format checks for one or more Python files. (See above.)') if rc: raise RuntimeError('Format execution failed for Python code. See above.') if __name__ == "__main__": start()