#!/usr/bin/env bash set -euo pipefail # Determine repository root REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" FORMAT_SCRIPT="$REPO_ROOT/scripts/format-code.sh" FORMAT_SCRIPT_PS1="$REPO_ROOT/scripts/format-code.ps1" # Collect staged C/C++ files (only these will be formatted) files=() while IFS= read -r line; do [[ -n "$line" ]] && files+=("$line") done < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|cc|cpp|cxx|h|hh|hpp)$' || true) if [[ ${#files[@]} -eq 0 ]]; then exit 0 fi # Format staged files and restage them if [[ "$OSTYPE" =~ ^(msys|cygwin|win32|mingw) ]]; then if command -v pwsh >/dev/null 2>&1; then printf '%s\n' "${files[@]}" | pwsh -NoProfile -ExecutionPolicy Bypass -File "$FORMAT_SCRIPT_PS1" -Serial -StdinFiles elif command -v powershell >/dev/null 2>&1; then printf '%s\n' "${files[@]}" | powershell -NoProfile -ExecutionPolicy Bypass -File "$FORMAT_SCRIPT_PS1" -Serial -StdinFiles else printf '%s\n' "${files[@]}" | "$FORMAT_SCRIPT" --serial --stdin-files fi else printf '%s\n' "${files[@]}" | "$FORMAT_SCRIPT" --serial --stdin-files fi git add -- "${files[@]}" # Verify formatting matches expectations if [[ "$OSTYPE" =~ ^(msys|cygwin|win32|mingw) ]]; then if command -v pwsh >/dev/null 2>&1; then if ! printf '%s\n' "${files[@]}" | pwsh -NoProfile -ExecutionPolicy Bypass -File "$FORMAT_SCRIPT_PS1" -Check -Serial -StdinFiles; then echo "clang-format check failed. Run 'scripts/format-code.ps1 -Git' to fix formatting." >&2 exit 1 fi elif command -v powershell >/dev/null 2>&1; then if ! printf '%s\n' "${files[@]}" | powershell -NoProfile -ExecutionPolicy Bypass -File "$FORMAT_SCRIPT_PS1" -Check -Serial -StdinFiles; then echo "clang-format check failed. Run 'scripts/format-code.ps1 -Git' to fix formatting." >&2 exit 1 fi else if ! printf '%s\n' "${files[@]}" | "$FORMAT_SCRIPT" --check --serial --stdin-files; then echo "clang-format check failed. Run 'scripts/format-code.sh --git' to fix formatting." >&2 exit 1 fi fi else if ! printf '%s\n' "${files[@]}" | "$FORMAT_SCRIPT" --check --serial --stdin-files; then echo "clang-format check failed. Run 'scripts/format-code.sh --git' to fix formatting." >&2 exit 1 fi fi # Optional clang-tidy (set YAMS_LINT_TIDY=1) if [ "${YAMS_LINT_TIDY:-}" = "1" ]; then for f in "${files[@]}"; do if [ -f "$f" ]; then clang-tidy "$f" --quiet || true fi done fi # Check Windows header include order and required defines # Psapi.h depends on Windows.h types (BOOL, DWORD, WINAPI) # WIN32_LEAN_AND_MEAN and NOMINMAX should be defined before Windows.h windows_header_errors=() for f in "${files[@]}"; do if [ -f "$f" ]; then # Check for Psapi.h includes if grep -qE '#include\s*<[^>]*[Pp]sapi\.h[^>]*>' "$f"; then # Get line numbers for Psapi.h and Windows.h psapi_line=$(grep -nE '#include\s*<[^>]*[Pp]sapi\.h[^>]*>' "$f" | head -1 | cut -d: -f1) win_line=$(grep -nE '#include\s*<[^>]*[Ww]indows\.h[^>]*>' "$f" | head -1 | cut -d: -f1 || echo "") if [ -z "$win_line" ]; then windows_header_errors+=("$f: Missing #include before Psapi.h (Psapi.h depends on Windows types)") elif [ "$psapi_line" -lt "$win_line" ]; then windows_header_errors+=("$f: Psapi.h included at line $psapi_line, but Windows.h must come first (line $win_line)") fi # Check for WIN32_LEAN_AND_MEAN lean_mean_line=$(grep -nE '#define\s+WIN32_LEAN_AND_MEAN' "$f" | head -1 | cut -d: -f1 || echo "") if [ -z "$lean_mean_line" ]; then windows_header_errors+=("$f: Missing #define WIN32_LEAN_AND_MEAN before Windows headers") elif [ -n "$win_line" ] && [ "$lean_mean_line" -gt "$win_line" ]; then windows_header_errors+=("$f: WIN32_LEAN_AND_MEAN must be defined before Windows.h (currently at line $lean_mean_line, Windows.h at $win_line)") fi # Check for NOMINMAX nominmax_line=$(grep -nE '#define\s+NOMINMAX' "$f" | head -1 | cut -d: -f1 || echo "") if [ -z "$nominmax_line" ]; then windows_header_errors+=("$f: Missing #define NOMINMAX before Windows headers") elif [ -n "$win_line" ] && [ "$nominmax_line" -gt "$win_line" ]; then windows_header_errors+=("$f: NOMINMAX must be defined before Windows.h (currently at line $nominmax_line, Windows.h at $win_line)") fi fi fi done if [ ${#windows_header_errors[@]} -ne 0 ]; then echo "" echo "ERROR: Windows header include order violations detected:" >&2 for err in "${windows_header_errors[@]}"; do echo " - $err" >&2 done echo "" >&2 echo "Fix: Include Windows.h BEFORE Psapi.h, and define WIN32_LEAN_AND_MEAN and NOMINMAX first." >&2 echo "Example:" >&2 echo " #ifdef _WIN32" >&2 echo " #ifndef WIN32_LEAN_AND_MEAN" >&2 echo " #define WIN32_LEAN_AND_MEAN 1" >&2 echo " #endif" >&2 echo " #ifndef NOMINMAX" >&2 echo " #define NOMINMAX 1" >&2 echo " #endif" >&2 echo " #include " >&2 echo " #include " >&2 echo " #endif" >&2 exit 1 fi # Check for non-GPL license headers # Only check new/changed files, exclude third_party and subprojects license_errors=() for f in "${files[@]}"; do if [ -f "$f" ]; then # Skip third-party directories if [[ "$f" == *"third_party/"* ]] || [[ "$f" == *"subprojects/"* ]] || [[ "$f" == *"external/"* ]]; then continue fi # Check for Apache-2.0 license if grep -qE "SPDX-License-Identifier:\s*Apache-2\.0" "$f"; then license_errors+=("$f: Found SPDX-License-Identifier: Apache-2.0 (must be GPL-3.0-or-later)") fi # Check for MIT license if grep -qE "SPDX-License-Identifier:\s*MIT" "$f"; then license_errors+=("$f: Found SPDX-License-Identifier: MIT (must be GPL-3.0-or-later)") fi # Check for BSD license if grep -qE "SPDX-License-Identifier:\s*BSD" "$f"; then license_errors+=("$f: Found SPDX-License-Identifier: BSD-XX (must be GPL-3.0-or-later)") fi # Check for full Apache license text if grep -qE "Licensed under the Apache License, Version 2\.0" "$f"; then license_errors+=("$f: Found Apache License text (must be GPL-3.0-or-later)") fi # Check for full MIT license text if grep -qE "Permission is hereby granted, free of charge" "$f"; then license_errors+=("$f: Found MIT License text (must be GPL-3.0-or-later)") fi fi done if [ ${#license_errors[@]} -ne 0 ]; then echo "" echo "ERROR: Non-GPL license headers detected:" >&2 for err in "${license_errors[@]}"; do echo " - $err" >&2 done echo "" >&2 echo "Fix: Update license header to GPL-3.0-or-later:" >&2 echo " // SPDX-License-Identifier: GPL-3.0-or-later" >&2 echo "" >&2 echo "Or run the license update script:" >&2 echo " python3 scripts/update-licenses.py" >&2 echo "" >&2 echo "Note: third_party/, subprojects/, and external/ directories are excluded from this check." >&2 exit 1 fi exit 0