180 lines
6.9 KiB
Bash
Executable file
180 lines
6.9 KiB
Bash
Executable file
#!/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 <Windows.h> 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 <Windows.h>" >&2
|
|
echo " #include <Psapi.h>" >&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
|