@samitouri / QOSAMI-WSL / commits / 198d7f83

Run clang-format in parallel (#40419)

* Run clang-format in parallel * Apply PR feedback

Blue committed May 5, 2026 at 11:24 UTC 198d7f8364fd0cf0b02cbe273fa0e91ac8820cf4
1 file changed +114 -40
tools/FormatSource.ps1.in
+114 -40
@@ -150,29 +150,16 @@ if (-not (Test-Path -Type Leaf -Path $ClangFormat)) {
150 exit 1
151 }
152
153 -function Format-Directory {
153 +function Collect-FilesToFormat {
154 [CmdletBinding()]
155 param (
156 [Parameter(Mandatory = $True)]
157 [string]$Path,
158 - [Parameter(Mandatory = $True)]
159 - [string]$ClangFormat,
160 - [string]$RepoRoot = $null,
161 - [AllowEmptyString()][string]$FilePatterns = $null,
162 - [string[]]$ModifiedFiles = $null,
163 - [boolean]$Verify = $false
158 + [string[]]$ModifiedFiles = $null
159 )
160 process {
166 - if (-not (Test-Path -Path $Path)) {
167 - Write-Host -ForegroundColor Red "Item not found: $Path"
168 - return $False
169 - }
170 - if ($null -eq $FilePatterns) {
171 - $FilePatterns = ""
172 - }
161 $Path = Resolve-Path $Path
174 - $Success = $True
175 - $FilesToFormat = @()
162 + $files = [System.Collections.Generic.List[string]]::new()
163 if ((Get-Item -Path $Path) -is [System.IO.DirectoryInfo]) {
164 Get-ChildItem -Path $Path -File `
165 | Where-Object { $_.Name -match $FilePatterns } `
@@ -180,46 +167,133 @@ function Format-Directory {
167 $FilePath = "$Path\$($_.Name)"
168 if (($null -eq $ModifiedFiles) -or ($ModifiedFiles -contains $FilePath)) {
169 if (!($FilePath -match "Intermediate")) {
183 - $FilesToFormat += $FilePath
170 + [void]$files.Add($FilePath)
171 }
172 }
173 }
174 Get-ChildItem -Path $Path -Directory `
175 | Where-Object { $_.Name -notmatch $IgnoreFolders } `
176 | ForEach-Object {
190 - $SubResult = (Format-Directory -Path "$Path\$($_.Name)" `
191 - -ClangFormat $ClangFormat `
192 - -RepoRoot $RepoRoot `
193 - -FilePatterns $FilePatterns `
194 - -ModifiedFiles $ModifiedFiles `
195 - -Verify $Verify)
196 - $Success = $SubResult -and $Success
177 + $subFiles = @(Collect-FilesToFormat -Path "$Path\$($_.Name)" `
178 + -ModifiedFiles $ModifiedFiles)
179 + foreach ($f in $subFiles) {
180 + [void]$files.Add($f)
181 + }
182 }
183 }
184 else {
200 - $FilesToFormat += $Path
185 + [void]$files.Add("$Path")
186 }
202 - $FilesToFormat | ForEach-Object {
203 - if ($Verify) {
204 - Write-Host "[clang-format] Checking formatting: $_"
205 - & $ClangFormat --style=file -Werror --dry-run $_
187 + return @($files)
188 + }
189 +}
190 +
191 +$allFiles = @(Collect-FilesToFormat -Path $Path -ModifiedFiles $ModifiedFiles)
192 +
193 +if ($allFiles.Count -eq 0) {
194 + Write-Host -ForegroundColor Green "No files to format."
195 + exit 0
196 +}
197 +
198 +$threadCount = [Environment]::ProcessorCount
199 +$totalFiles = $allFiles.Count
200 +Write-Host "[clang-format] Checking $totalFiles files using $threadCount threads..."
201 +
202 +# Create runspace pool for parallel execution (one thread per CPU core)
203 +$pool = [RunspaceFactory]::CreateRunspacePool(1, $threadCount)
204 +$pool.Open()
205 +
206 +$formatScript = {
207 + param($clangFormatExe, $filePath, $verifyMode)
208 + $pinfo = [System.Diagnostics.ProcessStartInfo]::new()
209 + $pinfo.FileName = $clangFormatExe
210 + if ($verifyMode) {
211 + $pinfo.Arguments = "--style=file -Werror --dry-run `"$filePath`""
212 + }
213 + else {
214 + $pinfo.Arguments = "--style=file -Werror -i `"$filePath`""
215 + }
216 + $pinfo.RedirectStandardError = $true # Not redirecting stdout to avoid a pipe deadlock.
217 + $pinfo.UseShellExecute = $false
218 +
219 + $proc = [System.Diagnostics.Process]::new()
220 + $proc.StartInfo = $pinfo
221 + [void]$proc.Start()
222 + $stderr = $proc.StandardError.ReadToEnd()
223 + $proc.WaitForExit()
224 +
225 + [PSCustomObject]@{
226 + File = $filePath
227 + ExitCode = $proc.ExitCode
228 + Output = $stderr.TrimEnd()
229 + }
230 +}
231 +
232 +# Submit all jobs to the runspace pool
233 +$jobs = [System.Collections.ArrayList]::new()
234 +foreach ($file in $allFiles) {
235 + $ps = [PowerShell]::Create()
236 + $ps.RunspacePool = $pool
237 + [void]$ps.AddScript($formatScript).AddArgument($ClangFormat).AddArgument($file).AddArgument($Verify)
238 + $handle = $ps.BeginInvoke()
239 + [void]$jobs.Add([PSCustomObject]@{
240 + PowerShell = $ps
241 + Handle = $handle
242 + })
243 +}
244 +
245 +# Determine console width for progress display
246 +try { $lineWidth = [Math]::Max(([Console]::BufferWidth - 1), 40) } catch { $lineWidth = 120 }
247 +
248 +$Success = $true
249 +
250 +try {
251 + while ($jobs.Count -gt 0) {
252 + $finishedJobs = @($jobs | Where-Object { $_.Handle.IsCompleted })
253 + foreach ($job in $finishedJobs) {
254 + try {
255 + $result = $job.PowerShell.EndInvoke($job.Handle)[0]
256 }
207 - else {
208 - Write-Host "[clang-format] Formatting $_"
209 - & $ClangFormat --style=file -Werror -i $_
257 + catch {
258 + $result = [PSCustomObject]@{ File = "unknown"; ExitCode = 1; Output = $_.Exception.Message }
259 }
211 - $Success = (0 -eq $LASTEXITCODE) -and $Success
260 + $job.PowerShell.Dispose()
261 + [void]$jobs.Remove($job)
262 +
263 + if ($result.ExitCode -ne 0) {
264 + $Success = $false
265 + # Clear progress line and print error permanently
266 + [Console]::Write("`r" + (" " * $lineWidth) + "`r")
267 + Write-Host -ForegroundColor Red "[clang-format] $($result.File)"
268 + if ($result.Output) {
269 + Write-Host $result.Output
270 + }
271 + }
272 +
273 + # Update progress line in-place with latest file
274 + $completed = $totalFiles - $jobs.Count
275 + $status = "[clang-format] ($completed/$totalFiles) $($result.File)"
276 + if ($status.Length -gt $lineWidth) {
277 + $status = $status.Substring(0, $lineWidth)
278 + }
279 + [Console]::Write("`r" + $status.PadRight($lineWidth))
280 }
213 - return $Success
281 + Start-Sleep -Milliseconds 50
282 }
283 }
284 +finally {
285 + # Clean up any remaining jobs and the pool
286 + foreach ($job in $jobs) {
287 + $job.PowerShell.Stop()
288 + $job.PowerShell.Dispose()
289 + }
290
217 -$Success = (Format-Directory -Path $Path `
218 - -ClangFormat $ClangFormat `
219 - -FilePatterns $FilePatterns `
220 - -ModifiedFiles $ModifiedFiles `
221 - -RepoRoot $RepoRoot `
222 - -Verify $Verify)
291 + # Clear final progress line
292 + [Console]::Write("`r" + (" " * $lineWidth) + "`r")
293 +
294 + $pool.Close()
295 + $pool.Dispose()
296 +}
297
298 if ($Success) {
299 Write-Host "Done."