chore: Add powershell script for clang-formatting (#1472)

## Summary

* **What is the goal of this PR?** Add a windows equivalent for the
linux clang-format-fix script
* **What changes are included?**

## Additional Context
```
.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
    .local/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.
```
---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< YES >**_
This commit is contained in:
jpirnay
2026-03-24 00:04:59 +01:00
committed by GitHub
parent 0cbfaa007d
commit 0245972132

154
bin/clang-format-fix.ps1 Normal file
View File

@@ -0,0 +1,154 @@
<#
.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 }