155 lines
4.2 KiB
PowerShell
155 lines
4.2 KiB
PowerShell
|
|
<#
|
||
|
|
.SYNOPSIS
|
||
|
|
Runs clang-format -i on project *.cpp and *.h files.
|
||
|
|
|
||
|
|
.DESCRIPTION
|
||
|
|
Formats all C/C++ source and header files in the repository, excluding
|
||
|
|
generated, vendored, and build directories (open-x4-sdk, builtinFonts,
|
||
|
|
hyphenation tries, uzlib, .pio, *.generated.h).
|
||
|
|
|
||
|
|
The clang-format binary path is resolved once and cached in
|
||
|
|
bin/clang-format-fix.local. On first run it checks a default path,
|
||
|
|
then PATH, then common install locations. Edit the .local file to
|
||
|
|
override manually.
|
||
|
|
|
||
|
|
.PARAMETER g
|
||
|
|
Format only git-modified files (git diff --name-only HEAD) instead of
|
||
|
|
the full tree.
|
||
|
|
|
||
|
|
.PARAMETER h
|
||
|
|
Show this help text.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
.\clang-format-fix.ps1
|
||
|
|
Format all files.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
.\clang-format-fix.ps1 -g
|
||
|
|
Format only git-modified files.
|
||
|
|
#>
|
||
|
|
|
||
|
|
param(
|
||
|
|
[switch]$g,
|
||
|
|
[switch]$h
|
||
|
|
)
|
||
|
|
|
||
|
|
if ($h) {
|
||
|
|
Get-Help $PSCommandPath -Detailed
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
$repoRoot = (Resolve-Path "$PSScriptRoot\..").Path
|
||
|
|
$configFile = Join-Path $PSScriptRoot 'clang-format-fix.local'
|
||
|
|
$defaultPath = 'C:\Program Files\LLVM\bin\clang-format.exe'
|
||
|
|
|
||
|
|
$candidatePaths = @(
|
||
|
|
'C:\Program Files\LLVM\bin\clang-format.exe'
|
||
|
|
'C:\Program Files (x86)\LLVM\bin\clang-format.exe'
|
||
|
|
'C:\msys64\ucrt64\bin\clang-format.exe'
|
||
|
|
'C:\msys64\mingw64\bin\clang-format.exe'
|
||
|
|
"$env:LOCALAPPDATA\LLVM\bin\clang-format.exe"
|
||
|
|
)
|
||
|
|
|
||
|
|
function Find-ClangFormat {
|
||
|
|
# Try PATH first
|
||
|
|
$inPath = Get-Command clang-format -ErrorAction SilentlyContinue
|
||
|
|
if ($inPath) { return $inPath.Source }
|
||
|
|
|
||
|
|
# Try candidate paths
|
||
|
|
foreach ($p in $candidatePaths) {
|
||
|
|
if (Test-Path $p) { return $p }
|
||
|
|
}
|
||
|
|
return $null
|
||
|
|
}
|
||
|
|
|
||
|
|
function Resolve-ClangFormat {
|
||
|
|
# 1. Read from config if present
|
||
|
|
if (Test-Path $configFile) {
|
||
|
|
$saved = (Get-Content $configFile -Raw).Trim()
|
||
|
|
if ($saved -and (Test-Path $saved)) { return $saved }
|
||
|
|
Write-Host "Configured path no longer valid: $saved"
|
||
|
|
}
|
||
|
|
|
||
|
|
# 2. Check default
|
||
|
|
if (Test-Path $defaultPath) {
|
||
|
|
$defaultPath | Set-Content $configFile
|
||
|
|
Write-Host "Saved clang-format path to $configFile"
|
||
|
|
return $defaultPath
|
||
|
|
}
|
||
|
|
|
||
|
|
# 3. Search PATH and candidate locations
|
||
|
|
$found = Find-ClangFormat
|
||
|
|
if ($found) {
|
||
|
|
$found | Set-Content $configFile
|
||
|
|
Write-Host "Found clang-format at $found - saved to $configFile"
|
||
|
|
return $found
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Error "clang-format not found. Install LLVM or add clang-format to PATH."
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
$clangFormat = Resolve-ClangFormat
|
||
|
|
|
||
|
|
$exclude = @(
|
||
|
|
'open-x4-sdk'
|
||
|
|
'lib\EpdFont\builtinFonts'
|
||
|
|
'lib\Epub\Epub\hyphenation\generated'
|
||
|
|
'lib\uzlib'
|
||
|
|
'.pio'
|
||
|
|
)
|
||
|
|
|
||
|
|
function Test-Excluded($fullPath) {
|
||
|
|
foreach ($ex in $exclude) {
|
||
|
|
if ($fullPath -like "*\$ex\*") { return $true }
|
||
|
|
}
|
||
|
|
if ($fullPath -like '*.generated.h') { return $true }
|
||
|
|
return $false
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($g) {
|
||
|
|
# Only git-modified *.cpp / *.h files
|
||
|
|
# Covers both staged and unstaged changes
|
||
|
|
$files = @(git -C $repoRoot diff --name-only HEAD) +
|
||
|
|
@(git -C $repoRoot diff --name-only --cached) |
|
||
|
|
Sort-Object -Unique |
|
||
|
|
Where-Object { $_ -match '\.(cpp|h)$' } |
|
||
|
|
ForEach-Object { Get-Item (Join-Path $repoRoot $_) -ErrorAction SilentlyContinue } |
|
||
|
|
Where-Object { $_ -and -not (Test-Excluded $_.FullName) }
|
||
|
|
} else {
|
||
|
|
$files = Get-ChildItem -Path $repoRoot -Recurse -Include *.cpp, *.h -File |
|
||
|
|
Where-Object { -not (Test-Excluded $_.FullName) }
|
||
|
|
}
|
||
|
|
|
||
|
|
$files = @($files)
|
||
|
|
|
||
|
|
if ($files.Count -eq 0) {
|
||
|
|
Write-Host 'No files to format.'
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Host "Formatting $($files.Count) files..."
|
||
|
|
$i = 0
|
||
|
|
$changed = 0
|
||
|
|
$failures = 0
|
||
|
|
foreach ($f in $files) {
|
||
|
|
$i++
|
||
|
|
$rel = $f.FullName.Substring($repoRoot.Length + 1)
|
||
|
|
$hashBefore = (Get-FileHash $f.FullName -Algorithm MD5).Hash
|
||
|
|
& $clangFormat -i $f.FullName
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
$failures++
|
||
|
|
Write-Host " [$i/$($files.Count)] $rel (FAILED, exit code $LASTEXITCODE)"
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
$hashAfter = (Get-FileHash $f.FullName -Algorithm MD5).Hash
|
||
|
|
if ($hashBefore -ne $hashAfter) {
|
||
|
|
$changed++
|
||
|
|
Write-Host " [$i/$($files.Count)] $rel (changed)"
|
||
|
|
} else {
|
||
|
|
Write-Host " [$i/$($files.Count)] $rel"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Write-Host "Done. $changed/$($files.Count) files changed, $failures failed."
|
||
|
|
if ($failures -gt 0) { exit 1 }
|