135 lines
3.1 KiB
Bash
Executable file
135 lines
3.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
MODE="fix"
|
|
GIT_ONLY=false
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/format-code.sh [--check|--fix] [--git]
|
|
|
|
Modes:
|
|
--check Check formatting only
|
|
--fix Apply formatting changes
|
|
--git Limit scope to staged C++ files
|
|
|
|
Defaults to formatting all C++ files in src/, include/, and tests/.
|
|
Files under tests/fixtures/ are always excluded.
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--check)
|
|
MODE="check"
|
|
shift
|
|
;;
|
|
--fix)
|
|
MODE="fix"
|
|
shift
|
|
;;
|
|
--git)
|
|
GIT_ONLY=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'Unknown option: %s\n' "$1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -n "${LLVM_BIN:-}" && -x "${LLVM_BIN}/clang-format" ]]; then
|
|
CLANG_FORMAT="${LLVM_BIN}/clang-format"
|
|
elif command -v clang-format >/dev/null 2>&1; then
|
|
CLANG_FORMAT="$(command -v clang-format)"
|
|
else
|
|
printf 'clang-format not found in PATH or LLVM_BIN\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$REPO_ROOT/.clang-format" ]]; then
|
|
printf '.clang-format not found at repo root\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
declare -a files=()
|
|
|
|
append_file_if_cpp() {
|
|
local file="$1"
|
|
[[ -f "$file" ]] || return 0
|
|
[[ "$file" == tests/fixtures/* ]] && return 0
|
|
case "$file" in
|
|
src/*|include/*|tests/*)
|
|
;;
|
|
*)
|
|
return 0
|
|
;;
|
|
esac
|
|
case "$file" in
|
|
*.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp)
|
|
files+=("$file")
|
|
;;
|
|
esac
|
|
}
|
|
|
|
if [[ "$GIT_ONLY" == true ]]; then
|
|
while IFS= read -r file; do
|
|
[[ -n "$file" ]] || continue
|
|
append_file_if_cpp "$file"
|
|
done < <(git diff --cached --name-only --diff-filter=ACMR)
|
|
else
|
|
while IFS= read -r file; do
|
|
[[ -n "$file" ]] || continue
|
|
files+=("$file")
|
|
done < <(
|
|
find src include tests \( -type f \) \
|
|
\( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.h' -o -name '*.hh' -o -name '*.hpp' \) \
|
|
-not -path 'tests/fixtures/*' \
|
|
| sort
|
|
)
|
|
fi
|
|
|
|
if [[ ${#files[@]} -eq 0 ]]; then
|
|
printf 'No C++ files to process\n'
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$GIT_ONLY" == true ]]; then
|
|
deduped=()
|
|
while IFS= read -r line; do
|
|
[[ -n "$line" ]] && deduped+=("$line")
|
|
done < <(printf '%s\n' "${files[@]}" | sort -u)
|
|
files=("${deduped[@]}")
|
|
fi
|
|
|
|
failures=0
|
|
for file in "${files[@]}"; do
|
|
if [[ "$MODE" == "check" ]]; then
|
|
if ! "$CLANG_FORMAT" --style=file --dry-run --Werror "$file"; then
|
|
failures=$((failures + 1))
|
|
fi
|
|
else
|
|
"$CLANG_FORMAT" --style=file -i "$file"
|
|
fi
|
|
done
|
|
|
|
if [[ "$MODE" == "check" ]]; then
|
|
if [[ "$failures" -gt 0 ]]; then
|
|
printf '%d file(s) need formatting\n' "$failures" >&2
|
|
exit 1
|
|
fi
|
|
printf 'Formatting check passed\n'
|
|
exit 0
|
|
fi
|
|
|
|
printf 'Formatted %d file(s)\n' "${#files[@]}"
|