From a35e24fd8a721f8630353de21e59b6d3445ecd54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Thu, 6 Aug 2026 23:24:50 +0200 Subject: [PATCH 1/7] AAA --- build.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index d4d1f002fe3..ef367e5eda5 100644 --- a/build.psm1 +++ b/build.psm1 @@ -495,7 +495,7 @@ function Start-PSBuild { # Excluded sqlite3 folder is due to this Roslyn issue: https://github.com/dotnet/roslyn/issues/23060 # Excluded src/Modules/nuget.config as this is required for release build. # Excluded nuget.config as this is required for release build. - git clean -fdX --exclude .vs/PowerShell/v16/Server/sqlite3 --exclude src/Modules/nuget.config --exclude nuget.config + measure-command { Start-Process -Wait -NoNewWindow git "clean -fdX --exclude .vs/PowerShell/v16/Server/sqlite3 --exclude src/Modules/nuget.config --exclude nuget.config" } } finally { Write-LogGroupEnd -Title "Cleaning your working directory" Pop-Location From 0df4e10f721165d0e4e1376f24bd806f5ebf36ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Thu, 6 Aug 2026 22:32:37 +0200 Subject: [PATCH 2/7] Don't prompt if git clean cannot unlink files Before --exclude .vs/PowerShell/v16/Server/sqlite3 doesn't exist. --exclude src/Modules/nuget.config is not cleaned up. --exclude nuget.config is not cleaned up. You can verify that the nuget.config files are not cleaned up like so: git clean -fdX git ls-file -ico --standard-exclude | sls nuget.config Therefore, the --exclude options have been removed because they are no-ops. Caveat: The sqlite3 file/folder may depend on the Visual Studio version. If Visual Studio is open, depending on its state git clean may fail to unlink files under the .vs\ directory and prompt interactively during the build. After gi clean ... may return different sets of paths in subsequent calls: git clean --dry-run -dX # some paths git clean -fdX git clean --dry-run -dX # more paths git clean -fdX git clean --dry-run -dX # empty ...therefore, we use a while loop. If a file cannot be deleted, we skip it (and its directory). Caveat: Using the -Verbose switch results in very slow cleaning. After this change, files locked by VS don't cause the build script to prompt the user. --- build.psm1 | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/build.psm1 b/build.psm1 index ef367e5eda5..a1caeee7746 100644 --- a/build.psm1 +++ b/build.psm1 @@ -488,14 +488,62 @@ function Start-PSBuild { Stop-Process -Verbose } + function CleanLenient { + [CmdletBinding()] + param() + $failed = $false + # `git clean` may keep returning different sets of paths. + while (-not $failed -and (git clean --dry-run -dX | Select-Object -First 1)) { + # `git clean` will prompt if it can't unlink files, so we delete in PS to skip errors. + foreach ($line in git clean --dry-run -dX) { + if (-not $line.StartsWith("Would remove ")) { + Write-Warning "Expected git clean --dry-run prefix 'Would remove' not found. The clean operation may be unreliable. The build script may need updating." + continue + } + $path = Join-Path $PSScriptRoot $line.Substring("Would remove ".Length) + Write-Verbose "Cleaning path '$path'..." + # Visual Studio is Windows only. + if ($path.Contains('\.vs\')) { + # VS may take locks on files while it's open. Skip them. + if ([System.IO.Directory]::Exists($path)) { + $failedDir = $false + foreach ($filePath in [System.IO.Directory]::EnumerateFiles($path, "*.*", [System.IO.SearchOption]::AllDirectories)) { + Write-Verbose "Removing file '$filePath'." + try { + [System.IO.File]::Delete($filePath) + } catch { + $failed = $true + $failedDir = $true + Write-Warning "Clean operation could not remove file '$filePath'." + } + } + if (-not $failedDir) { + Write-Information "Removing directory '$path'." + [System.IO.Directory]::Delete($path, $true) + } + } else { + Write-Verbose "Removing file '$path'." + try { + [System.IO.File]::Delete($path) + } catch { + $failed = $true + Write-Warning "Clean operation could not remove file '$path'." + } + } + } elseif ([System.IO.File]::Exists($path)) { + [System.IO.File]::Delete($path) + } else { + [System.IO.Directory]::Delete($path, $true) + } + } + } + } + if ($Clean) { Write-LogGroupStart -Title "Cleaning your working directory" Push-Location $PSScriptRoot try { - # Excluded sqlite3 folder is due to this Roslyn issue: https://github.com/dotnet/roslyn/issues/23060 - # Excluded src/Modules/nuget.config as this is required for release build. - # Excluded nuget.config as this is required for release build. - measure-command { Start-Process -Wait -NoNewWindow git "clean -fdX --exclude .vs/PowerShell/v16/Server/sqlite3 --exclude src/Modules/nuget.config --exclude nuget.config" } + CleanLenient } finally { Write-LogGroupEnd -Title "Cleaning your working directory" Pop-Location From 1c22e8614b5e2280fae854f587ca254d48ef6a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Fri, 7 Aug 2026 06:42:10 +0200 Subject: [PATCH 3/7] Don't use `while` to keep calling `git clean --dry-run ...` The problem doesn't seem to be that `git clean --dry-run` keeps returning paths, but rather that the following keeps returning paths: - git clean -fdX - git ls-files --ignore --standard-exclude --cached --others But the second command isn't used in the script so far and this was only noticed during debugging. --- build.psm1 | 70 +++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/build.psm1 b/build.psm1 index a1caeee7746..17eb7af8494 100644 --- a/build.psm1 +++ b/build.psm1 @@ -491,50 +491,44 @@ function Start-PSBuild { function CleanLenient { [CmdletBinding()] param() - $failed = $false - # `git clean` may keep returning different sets of paths. - while (-not $failed -and (git clean --dry-run -dX | Select-Object -First 1)) { - # `git clean` will prompt if it can't unlink files, so we delete in PS to skip errors. - foreach ($line in git clean --dry-run -dX) { - if (-not $line.StartsWith("Would remove ")) { - Write-Warning "Expected git clean --dry-run prefix 'Would remove' not found. The clean operation may be unreliable. The build script may need updating." - continue - } - $path = Join-Path $PSScriptRoot $line.Substring("Would remove ".Length) - Write-Verbose "Cleaning path '$path'..." - # Visual Studio is Windows only. - if ($path.Contains('\.vs\')) { - # VS may take locks on files while it's open. Skip them. - if ([System.IO.Directory]::Exists($path)) { - $failedDir = $false - foreach ($filePath in [System.IO.Directory]::EnumerateFiles($path, "*.*", [System.IO.SearchOption]::AllDirectories)) { - Write-Verbose "Removing file '$filePath'." - try { - [System.IO.File]::Delete($filePath) - } catch { - $failed = $true - $failedDir = $true - Write-Warning "Clean operation could not remove file '$filePath'." - } - } - if (-not $failedDir) { - Write-Information "Removing directory '$path'." - [System.IO.Directory]::Delete($path, $true) - } - } else { - Write-Verbose "Removing file '$path'." + # `git clean` will prompt if it can't unlink files, so we delete in PS to skip errors. + foreach ($line in git clean --dry-run -dX) { + if (-not $line.StartsWith("Would remove ")) { + Write-Warning "Expected git clean --dry-run prefix 'Would remove' not found. The clean operation may be unreliable. The build script may need updating." + continue + } + $path = Join-Path $PSScriptRoot $line.Substring("Would remove ".Length) + Write-Verbose "Cleaning path '$path'..." + # Visual Studio is Windows only. + if ($path.Contains('\.vs\')) { + # VS may take locks on files while it's open. Skip them. + if ([System.IO.Directory]::Exists($path)) { + $failedDir = $false + foreach ($filePath in [System.IO.Directory]::EnumerateFiles($path, "*.*", [System.IO.SearchOption]::AllDirectories)) { + Write-Verbose "Removing file '$filePath'." try { - [System.IO.File]::Delete($path) + [System.IO.File]::Delete($filePath) } catch { - $failed = $true - Write-Warning "Clean operation could not remove file '$path'." + $failedDir = $true + Write-Warning "Clean operation could not remove file '$filePath'." } } - } elseif ([System.IO.File]::Exists($path)) { - [System.IO.File]::Delete($path) + if (-not $failedDir) { + Write-Information "Removing directory '$path'." + [System.IO.Directory]::Delete($path, $true) + } } else { - [System.IO.Directory]::Delete($path, $true) + Write-Verbose "Removing file '$path'." + try { + [System.IO.File]::Delete($path) + } catch { + Write-Warning "Clean operation could not remove file '$path'." + } } + } elseif ([System.IO.File]::Exists($path)) { + [System.IO.File]::Delete($path) + } else { + [System.IO.Directory]::Delete($path, $true) } } } From 639e9eda60a44ba2cc46eaeb3a807addc5547c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Fri, 7 Aug 2026 06:45:10 +0200 Subject: [PATCH 4/7] Quotes --- build.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index 17eb7af8494..63ad26263de 100644 --- a/build.psm1 +++ b/build.psm1 @@ -495,9 +495,10 @@ function Start-PSBuild { foreach ($line in git clean --dry-run -dX) { if (-not $line.StartsWith("Would remove ")) { Write-Warning "Expected git clean --dry-run prefix 'Would remove' not found. The clean operation may be unreliable. The build script may need updating." + if (-not $line.StartsWith('Would remove ')) { continue } - $path = Join-Path $PSScriptRoot $line.Substring("Would remove ".Length) + $path = Join-Path $PSScriptRoot $line.Substring('Would remove '.Length) Write-Verbose "Cleaning path '$path'..." # Visual Studio is Windows only. if ($path.Contains('\.vs\')) { From 9e4746765f710fbf2b99b932059f0611da9c68bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Fri, 7 Aug 2026 06:45:20 +0200 Subject: [PATCH 5/7] Space --- build.psm1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.psm1 b/build.psm1 index 63ad26263de..84ee21b728f 100644 --- a/build.psm1 +++ b/build.psm1 @@ -493,9 +493,8 @@ function Start-PSBuild { param() # `git clean` will prompt if it can't unlink files, so we delete in PS to skip errors. foreach ($line in git clean --dry-run -dX) { - if (-not $line.StartsWith("Would remove ")) { - Write-Warning "Expected git clean --dry-run prefix 'Would remove' not found. The clean operation may be unreliable. The build script may need updating." if (-not $line.StartsWith('Would remove ')) { + Write-Warning "Expected git clean --dry-run prefix 'Would remove ' not found. The clean operation may be unreliable. The build script may need updating." continue } $path = Join-Path $PSScriptRoot $line.Substring('Would remove '.Length) From e677469223453a5a42888c956a74f873fe6251cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Fri, 7 Aug 2026 06:53:57 +0200 Subject: [PATCH 6/7] Preserve existing behavior more closely --- build.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build.psm1 b/build.psm1 index 84ee21b728f..444ba0305ef 100644 --- a/build.psm1 +++ b/build.psm1 @@ -528,6 +528,7 @@ function Start-PSBuild { } elseif ([System.IO.File]::Exists($path)) { [System.IO.File]::Delete($path) } else { + Write-Information "Removing directory '$path'." [System.IO.Directory]::Delete($path, $true) } } From 651a2f734ea0e67428da1beac47158527edfdc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20L=C3=B3pez?= Date: Thu, 6 Aug 2026 23:39:28 +0200 Subject: [PATCH 7/7] AAA --- build.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index 444ba0305ef..48e8379da74 100644 --- a/build.psm1 +++ b/build.psm1 @@ -538,7 +538,7 @@ function Start-PSBuild { Write-LogGroupStart -Title "Cleaning your working directory" Push-Location $PSScriptRoot try { - CleanLenient + Measure-Command { CleanLenient } } finally { Write-LogGroupEnd -Title "Cleaning your working directory" Pop-Location