From c44336573bf88c00230962204c3d8739cc03c54d Mon Sep 17 00:00:00 2001
From: patrick96
Date: Tue, 27 Sep 2022 23:42:29 +0200
Subject: [PATCH] build: Create clangformat(-dryrun) build targets
The clangformat target updates all files in-place while the
clangformat-dryrun target prints an error message for each format-change
clang-format would apply.
The latter exits with a non-zero error code if there are any changes.
---
.editorconfig | 3 +++
cmake/04-targets.cmake | 20 ++++++++++++-----
common/clang-format.sh | 20 -----------------
common/file-runner.py | 50 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 68 insertions(+), 25 deletions(-)
delete mode 100755 common/clang-format.sh
create mode 100755 common/file-runner.py
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()