diff --git a/.editorconfig b/.editorconfig index 57c08e6e..ff6aa42b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,9 @@ indent_style = space indent_size = 2 charset = utf-8 +[*.py] +indent_size = 4 + [Makefile] indent_style = tab indent_size = 2 diff --git a/cmake/04-targets.cmake b/cmake/04-targets.cmake index 7424341b..0c1e8f51 100644 --- a/cmake/04-targets.cmake +++ b/cmake/04-targets.cmake @@ -17,13 +17,23 @@ add_custom_target(uninstall # folders where the clang tools should operate set(CLANG_SEARCH_PATHS ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/tests) -# Target: codeformat (clang-format) {{{ +# Runs clang-format on all source files +add_custom_target( + clangformat + COMMAND ${PROJECT_SOURCE_DIR}/common/file-runner.py + --dirs ${CLANG_SEARCH_PATHS} + -- clang-format -style=file -i --verbose + ) -add_custom_target(codeformat) -add_custom_command(TARGET codeformat - COMMAND ${PROJECT_SOURCE_DIR}/common/clang-format.sh ${CLANG_SEARCH_PATHS}) +# Dry-runs clang-format on all source files +# Useful for CI since it will exit with an error code +add_custom_target( + clangformat-dryrun + COMMAND ${PROJECT_SOURCE_DIR}/common/file-runner.py + --dirs ${CLANG_SEARCH_PATHS} + -- clang-format -style=file --dry-run -Werror --verbose + ) -# }}} # Target: codecheck (clang-tidy) {{{ add_custom_target(codecheck) diff --git a/common/clang-format.sh b/common/clang-format.sh deleted file mode 100755 index 0a912c8e..00000000 --- a/common/clang-format.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -main() { - if [ $# -lt 1 ]; then - echo "$0 DIR..." 1>&2 - exit 1 - fi - - # Search paths - search="${*:-.}" - - echo "$0 in $search" - - # shellcheck disable=2086 - find $search -regex ".*.[c|h]pp" \ - -exec printf "\\033[32;1m** \\033[0mFormatting %s\\n" {} \; \ - -exec clang-format -style=file -i {} \; -} - -main "$@" diff --git a/common/file-runner.py b/common/file-runner.py new file mode 100755 index 00000000..a4d97c9f --- /dev/null +++ b/common/file-runner.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import sys +import os +import argparse +import subprocess + +EXTENSIONS = set('.' + ext for ext in ['c', 'h', 'cpp', 'hpp', 'inl']) + + +def get_files(dirs): + """ + Generator which yields all files in the given directories with any of the + EXTENSIONS. + """ + for dir in dirs: + for root, _, files in os.walk(dir): + for file in files: + path = Path(os.path.join(root, file)) + if path.suffix in EXTENSIONS: + yield path + + +def main(): + parser = argparse.ArgumentParser( + description=""" + Run command on all C/C++ source files in the given directories + """) + parser.add_argument('--dirs', type=Path, nargs='+', + help='Directories to search in') + parser.add_argument('command', nargs='+', + help='Command to which to pass found files') + args = parser.parse_args() + + all_files = list(str(file) for file in get_files(args.dirs)) + + if not all_files: + print("No files found") + sys.exit(1) + + result = subprocess.run(args.command + all_files) + print(f'Formatted {len(all_files)} files') + + if result.returncode != 0: + sys.exit(result.returncode) + + +if __name__ == '__main__': + main()