[CmdletBinding()] param( [string]$SourceScript, [string]$OutputDir, [string]$BuildDir, [string]$ExeName = 'PortableCaffeine.exe', [switch]$ConsoleTarget, [switch]$KeepExtractedSource ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # In Windows PowerShell 5.1, $PSScriptRoot may be empty during param default evaluation. # Resolve script-relative defaults after the param block for compatibility. $scriptRoot = if (-not [string]::IsNullOrWhiteSpace($PSScriptRoot)) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { (Get-Location).Path } if ([string]::IsNullOrWhiteSpace($SourceScript)) { $SourceScript = Join-Path $scriptRoot 'caffeine.ps1' } if ([string]::IsNullOrWhiteSpace($OutputDir)) { $OutputDir = Join-Path $scriptRoot 'dist' } if ([string]::IsNullOrWhiteSpace($BuildDir)) { $BuildDir = Join-Path $scriptRoot 'build' } function Get-CSharpFromCaffeineScript { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not (Test-Path -LiteralPath $Path)) { throw "Source script not found: $Path" } $raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 $pattern = '(?s)\$csharp\s*=\s*@''\r?\n(?.*?)\r?\n''@' $match = [regex]::Match($raw, $pattern) if (-not $match.Success) { throw "Could not locate embedded C# here-string (`$csharp = @' ... '@) in $Path" } return $match.Groups['code'].Value } function Get-CscPath { $candidates = @( (Join-Path $env:WINDIR 'Microsoft.NET\Framework64\v4.0.30319\csc.exe'), (Join-Path $env:WINDIR 'Microsoft.NET\Framework\v4.0.30319\csc.exe') ) foreach ($candidate in $candidates) { if ($candidate -and (Test-Path -LiteralPath $candidate)) { return $candidate } } $cmd = Get-Command csc.exe -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } throw "csc.exe not found. Install .NET Framework build tools (or run on Windows with .NET Framework 4.x)." } if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { throw "This build script is intended for Windows because it compiles WinForms code against .NET Framework (csc.exe)." } New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null $csharpSource = Get-CSharpFromCaffeineScript -Path $SourceScript $generatedCsPath = Join-Path $BuildDir 'PortableCaffeine.generated.cs' $outExePath = Join-Path $OutputDir $ExeName $targetKind = if ($ConsoleTarget) { 'exe' } else { 'winexe' } $cscPath = Get-CscPath [System.IO.File]::WriteAllText($generatedCsPath, $csharpSource, (New-Object System.Text.UTF8Encoding($false))) $arguments = @( '/nologo' "/target:$targetKind" '/optimize+' '/platform:anycpu' "/out:$outExePath" '/r:System.dll' '/r:System.Windows.Forms.dll' '/r:System.Drawing.dll' $generatedCsPath ) Write-Host "Compiling with: $cscPath" Write-Host "Target: $targetKind" Write-Host "Output: $outExePath" & $cscPath @arguments $exitCode = $LASTEXITCODE if ($exitCode -ne 0) { throw "csc.exe failed with exit code $exitCode" } if (-not $KeepExtractedSource) { Remove-Item -LiteralPath $generatedCsPath -Force -ErrorAction SilentlyContinue } Write-Host "" Write-Host "Build succeeded." Write-Host "EXE: $outExePath" if ($KeepExtractedSource) { Write-Host "Extracted source kept at: $generatedCsPath" }