#!/usr/bin/env bash # scripts/quality/clang-tidy.sh # # Run clang-tidy over every public header under code/include/. We use # `--use-color` and tee output to build-tidy/clang-tidy.log for later # diffing. # # Local-only. The check is exploratory until we agree on which warning # classes are reasonable to enforce — CGAL header-only code triggers a # lot of `modernize-*` / `readability-*` warnings that are upstream's # choice, not ours. See `.clang-tidy` for the curated subset. # # Usage: # bash scripts/quality/clang-tidy.sh # all headers # bash scripts/quality/clang-tidy.sh code/include/CGAL # subset # # Prerequisite: a compile_commands.json with the right include paths # (cmake generates this automatically with CMAKE_EXPORT_COMPILE_COMMANDS=ON). # # Exit codes: # 0 clang-tidy ran (output captured) # 1 clang-tidy reported at least one error (post-policy filter) # 2 prerequisite missing set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$ROOT" BUILD_DIR="build-tidy" LOG="$BUILD_DIR/clang-tidy.log" command -v clang-tidy >/dev/null 2>&1 || { echo "FAIL: clang-tidy not in PATH." >&2 echo " macOS: brew install llvm && export PATH=\"\$(brew --prefix llvm)/bin:\$PATH\"" >&2 echo " Linux: sudo apt install clang-tidy" >&2 exit 2 } TARGET_DIR="${1:-code/include}" [ -d "$TARGET_DIR" ] || { echo "FAIL: $TARGET_DIR is not a directory" >&2; exit 2; } # Generate compile_commands.json (clang-tidy needs it for include paths). # Enable WITH_CGAL_TESTS so the CGAL include directories are part of at # least one compile entry — clang-tidy walks those when linting headers # that don't appear in compile_commands.json directly. cmake -S code -B "$BUILD_DIR" \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DWITH_CGAL_TESTS=ON \ -DCMAKE_BUILD_TYPE=Release \ -Wno-dev >/dev/null # ── macOS workaround: brew-installed clang-tidy doesn't know where the # Apple Command-Line-Tools SDK lives, so it can't find , , # , etc. Pass `--extra-arg=-isysroot ...` to teach it. EXTRA_ARGS=() if [ "$(uname -s)" = "Darwin" ]; then SDK="$(xcrun --show-sdk-path 2>/dev/null || true)" if [ -n "$SDK" ]; then EXTRA_ARGS+=(--extra-arg=-isysroot --extra-arg="$SDK") fi fi mkdir -p "$BUILD_DIR" : > "$LOG" # Find every .h / .hpp under TARGET_DIR (skip deps + macOS dup files + # viewer-only headers — those need `-DWITH_VIEWER=ON` plus a system # GLFW/libigl that we don't drag into the lint build). HEADERS=$(find "$TARGET_DIR" \ \( -name "*.h" -o -name "*.hpp" \) \ -type f \ | grep -v "code/deps/" \ | grep -v " 2\." \ | grep -v "viewer_utils\.h$" \ | grep -v "mesh_utils\.hpp$" \ | sort) echo "========================================" echo " clang-tidy run" echo " target: $TARGET_DIR" echo " config: .clang-tidy" echo " log: $LOG" echo "========================================" echo n=0 for h in $HEADERS; do n=$((n + 1)) echo "── [$n] $h ─────────────────────────────────" # Use --quiet so we only see actual diagnostics, not "n warnings # generated" boilerplate. Pipe through tee for the log file. clang-tidy --quiet \ -p "$BUILD_DIR" \ "${EXTRA_ARGS[@]}" \ "$h" 2>&1 | tee -a "$LOG" || true done echo echo "── Summary ──────────────────────────────────" warn=$(grep -c "warning:" "$LOG" || true) err=$(grep -c "error:" "$LOG" || true) echo " files inspected: $n" echo " warnings: $warn" echo " errors: $err" echo " full log: $LOG" if [ "${err:-0}" -gt 0 ]; then exit 1 fi exit 0