.ai/skills/build-windows-exe/SKILL.md
Builds Windows executables (x64 and ARM64) for XerahS using the packaging script. Handles Windows packaging, Inno Setup, artifact validation, and Windows-specific file lock recovery. Use build-common for shared build timeout, lock, and dependency guardrails.
npx skillsauth add sharex/xerahs build-windows-exeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert Windows build automation specialist for .NET projects.
Follow these instructions exactly and in order to build Windows executables for XerahS while avoiding common file locking issues.
<task> <goal>Build Windows installers for both x64 and ARM64 architectures.</goal> <goal>Handle file locking issues that commonly occur during compilation.</goal> <goal>Ensure Inno Setup successfully creates installer executables.</goal> <goal>Validate that the build artifacts exist and are recent.</goal> </task> <context> <build_script_path>build\windows\package-windows.ps1</build_script_path> <dist_output_path>dist</dist_output_path> <common_locked_file>ShareX.ImageEditor\src\ShareX.ImageEditor\obj\Release\net10.0-windows10.0.26100.0\ShareX.ImageEditor.dll</common_locked_file> <expected_outputs> - XerahS-{version}-win-x64.exe - XerahS-{version}-win-arm64.exe </expected_outputs> </context>Before Windows packaging work, follow build-common for shared timeout, lock recovery, no-concurrent-build, -m:1, TFM, and SkiaSharp rules. This Windows skill owns Windows packaging commands, Inno Setup behavior, and Windows artifact validation.
Always pull the latest ShareX.ImageEditor submodule before building to ensure the embedded image editor is up-to-date.
# Run from the repository root.
git submodule update --remote --merge ShareX.ImageEditor
CRITICAL: File locking is the #1 cause of Windows build failures. Always clean before building.
Kill all potential file-locking processes:
Get-Process | Where-Object {
$_.Name -like '*XerahS*' -or
$_.Name -like '*dotnet*' -or
$_.Name -like '*MSBuild*' -or
$_.Name -like '*VBCSCompiler*'
} | Stop-Process -Force -ErrorAction SilentlyContinue
Clean the solution:
# Run from the repository root.
dotnet clean src/desktop/XerahS.sln --nologo -c Release
If file locks persist, delete the problematic obj folder:
Remove-Item 'ShareX.ImageEditor\src\ShareX.ImageEditor\obj' -Recurse -Force -ErrorAction SilentlyContinue
Run the packaging script:
.\build\windows\package-windows.ps1
Check for successful completion:
dist folderIf you see errors like:
CS2012: Cannot open '...ShareX.ImageEditor.dll' for writingThe process cannot access the file ... because it is being used by another processfile may be locked by 'VBCSCompiler' or '.NET Host' or 'csc'Then apply these fixes:
Kill compiler processes again:
Get-Process | Where-Object {
$_.Name -like '*VBCSCompiler*' -or
$_.Name -like '*dotnet*' -or
$_.Name -like '*csc*'
} | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Remove the locked obj folder:
Remove-Item 'ShareX.ImageEditor\src\ShareX.ImageEditor\obj\Release' -Recurse -Force -ErrorAction SilentlyContinue
Pre-build the ShareX.ImageEditor project separately with single-threaded compilation:
dotnet build 'ShareX.ImageEditor\src\ShareX.ImageEditor\ShareX.ImageEditor.csproj' -c Release -p:UseSharedCompilation=false /m:1
/m:1 flag forces single-threaded buildUseSharedCompilation=false disables the VBCSCompiler serverRe-run the packaging script:
.\build\windows\package-windows.ps1
If ARM64 continues to fail but x64 succeeds, build ARM64 manually:
Clean and kill processes:
Get-Process | Where-Object { $_.Name -like '*VBCSCompiler*' -or $_.Name -like '*dotnet*' } | Stop-Process -Force -ErrorAction SilentlyContinue
Remove-Item 'ShareX.ImageEditor\src\ShareX.ImageEditor\obj\Release' -Recurse -Force -ErrorAction SilentlyContinue
Pre-build ShareX.ImageEditor:
dotnet build 'ShareX.ImageEditor\src\ShareX.ImageEditor\ShareX.ImageEditor.csproj' -c Release -p:UseSharedCompilation=false /m:1
Publish ARM64 manually:
$root = (Get-Location).Path
$project = "$root\src\desktop\app\XerahS.App\XerahS.App.csproj"
$publishOutput = "$root\build\publish-temp-win-arm64"
dotnet publish $project -c Release -p:OS=Windows_NT -r win-arm64 -p:PublishSingleFile=false -p:SkipBundlePlugins=true -p:UseSharedCompilation=false --self-contained true -o $publishOutput
Publish plugins:
$pluginsDir = "$publishOutput\Plugins"
New-Item -ItemType Directory -Force -Path $pluginsDir | Out-Null
Get-ChildItem "$root\src\desktop\plugins" -Filter "*.csproj" -Recurse | ForEach-Object {
$pluginId = $_.BaseName
$pluginJsonPath = Join-Path $_.Directory.FullName "plugin.json"
if (Test-Path $pluginJsonPath) {
$json = Get-Content $pluginJsonPath -Raw | ConvertFrom-Json
if ($json.pluginId) { $pluginId = $json.pluginId }
}
Write-Host "Publishing plugin: $pluginId"
dotnet publish $_.FullName -c Release -r win-arm64 -p:UseSharedCompilation=false --self-contained false -o "$pluginsDir\$pluginId"
}
Run Inno Setup manually:
$isccPath = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
$issScript = "$root\build\windows\XerahS-setup.iss"
$version = ([xml](Get-Content "$root\Directory.Build.props")).SelectSingleNode("//Version").InnerText.Trim()
$outputDir = "$root\dist"
& $isccPath "/dMyAppReleaseDirectory=$publishOutput" "/dOutputBaseFilename=XerahS-$version-win-arm64" "/dOutputDir=$outputDir" $issScript
Check that both installers exist:
Get-ChildItem 'dist' -Filter '*.exe' | Select-Object Name, Length, LastWriteTime | Format-Table -AutoSize
Verify they were created recently (within the last few minutes)
Expected output:
Name Length LastWriteTime
---- ------ -------------
XerahS-{version}-win-arm64.exe ~55-60MB [Today's date]
XerahS-{version}-win-x64.exe ~55-60MB [Today's date]
NEVER run two builds at the same time. ShareX.ImageEditor targets multiple TFMs (net9.0, net10.0, net9.0-windows10.0.26100.0, net10.0-windows10.0.26100.0) and MSBuild parallelism causes all of them to race on the same ShareX.ImageEditor.dll output path, producing CS2012 file lock errors.
package-windows.ps1 already iterates win-x64 then win-arm64 sequentially via foreach — never invoke it twice concurrently./m:1 on the dotnet publish call, which forces single-threaded MSBuild and eliminates the intra-build race.dotnet build-server shutdown and kill VBCSCompiler between consecutive build sessions.-p:UseSharedCompilation=false: Disables VBCSCompiler server-p:nodeReuse=false: Prevents MSBuild from reusing build nodes/m:1: Forces single-threaded build (slower but no race conditions)-p:SkipBundlePlugins=true: Avoids custom MSBuild target path resolution bugsShareX.ImageEditor is the usual culprit: It has parallel TFM builds (net9.0, net10.0, with/without Windows SDK)| Symptom | Solution |
|---------|----------|
| "XerahS.exe" does not exist (Inno Setup) | The main app didn't publish; check for earlier build errors |
| CS2012 file lock error | Kill VBCSCompiler, delete obj folder, rebuild with /m:1 |
| Installer created but old timestamp | Build failed silently; check logs in iscc_log_win-{arch}.txt |
| Only x64 succeeds, ARM64 fails | Use Phase 4 manual ARM64 build process |
| All builds fail | Clean solution, restart terminal, ensure no XerahS instances running |
build/windows/iscc_log_win-{arch}.txttesting
Reference for writing effective XerahS Improvement Proposals (XIPs), including structure, templates, review checks, and implementation patterns. Use sync-xips for creating, editing, and syncing XIP GitHub issues and local backups.
documentation
Rules and workflows for updating docs/CHANGELOG.md, including version grouping, consolidation, and commit-entry attribution.
testing
Create and maintain XerahS Improvement Proposals (XIPs) with GitHub as source of truth and docs/proposals/xip folder as backup. Use when creating or editing XIPs, syncing XIPs between GitHub issues and the docs/proposals/xip folder, or when the user mentions XIP, GitHub issues for XIP, or local XIP files.
documentation
Repository maintenance preparation for XerahS. Use before release or changelog work to sync repositories, inspect submodule state, identify version/changelog needs, and hand off commit/push/version rules to git-workflow.