[CmdletBinding()] param() $ErrorActionPreference = 'Stop' $WorkDir = [IO.Path]::GetFullPath($PSScriptRoot) $Standalone = [IO.Path]::GetFullPath((Join-Path $WorkDir '..\..')) $PluginRoot = [IO.Path]::GetFullPath((Join-Path $Standalone '..')) $BuildDir = Join-Path $WorkDir 'build' $OutDir = Join-Path $WorkDir 'bin' [IO.Directory]::CreateDirectory($BuildDir) | Out-Null [IO.Directory]::CreateDirectory($OutDir) | Out-Null $Runner = Get-Process -Id $PID $Runner.PriorityClass = 'BelowNormal' $Runner.ProcessorAffinity = [IntPtr]0xFF00 $Affinity = [IntPtr]0xFF00 $VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat' if (-not (Test-Path -LiteralPath $VcVars -PathType Leaf)) { throw "MSVC environment script missing: $VcVars" } # Import the exact MSVC environment without running the compiler through an unrestricted shell. $EnvironmentLines = & $env:ComSpec /d /s /c "call `"$VcVars`" >nul 2>nul && set" if ($LASTEXITCODE -ne 0) { throw "vcvars64 environment setup failed: $LASTEXITCODE" } foreach ($Line in $EnvironmentLines) { $Equals = $Line.IndexOf('=') if ($Equals -le 0) { continue } $Name = $Line.Substring(0, $Equals) if ($Name.StartsWith('=')) { continue } [Environment]::SetEnvironmentVariable($Name, $Line.Substring($Equals + 1), 'Process') } $Compiler = Join-Path $env:VCToolsInstallDir 'bin\Hostx64\x64\cl.exe' if (-not (Test-Path -LiteralPath $Compiler -PathType Leaf)) { throw "MSVC compiler missing: $Compiler" } $Source = Join-Path $WorkDir 'SharpMesherSpike.cpp' $CoreInclude = Join-Path $Standalone 'Core\include' $CoreLibrary = Join-Path $Standalone 'Build\VoxelForgeCore\VoxelForgeCore.lib' if (-not (Test-Path -LiteralPath $CoreLibrary -PathType Leaf)) { throw "Core library missing: $CoreLibrary" } $Exe = Join-Path $OutDir 'SharpMesherSpike.exe' $Obj = Join-Path $BuildDir 'SharpMesherSpike.obj' $Stdout = Join-Path $WorkDir 'build.stdout.log' $Stderr = Join-Path $WorkDir 'build.stderr.log' $CompilerArguments = '/nologo /std:c++20 /O2 /fp:precise /EHsc /MP8 /D_CRT_SECURE_NO_WARNINGS ' + ('/I"{0}" /I"{1}" /I"{2}" /Fo"{3}" "{4}" /Fe:"{5}" /link "{6}"' -f ` (Join-Path $Standalone 'Compat'), ` (Join-Path $Standalone 'Source\VoxelForge\Public'), $CoreInclude, $Obj, $Source, $Exe, $CoreLibrary) $Attempt = 0 do { $Attempt++ Remove-Item -LiteralPath $Stdout, $Stderr -Force -ErrorAction SilentlyContinue $Info = [Diagnostics.ProcessStartInfo]::new() $Info.FileName = $Compiler $Info.Arguments = $CompilerArguments $Info.WorkingDirectory = $PluginRoot $Info.UseShellExecute = $false $Info.CreateNoWindow = $true $Info.RedirectStandardOutput = $true $Info.RedirectStandardError = $true $Process = [Diagnostics.Process]::new() $Process.StartInfo = $Info if (-not $Process.Start()) { throw 'Could not start cl.exe.' } $ProcessId = $Process.Id $Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::BelowNormal $Process.ProcessorAffinity = $Affinity $OwnedPids = [Collections.Generic.HashSet[int]]::new() [void]$OwnedPids.Add($ProcessId) $StartAt = [DateTime]::UtcNow $OutputTask = $Process.StandardOutput.ReadToEndAsync() $ErrorTask = $Process.StandardError.ReadToEndAsync() $TimedOut = $false $MemoryStop = $false while (-not $Process.HasExited) { $Process.Refresh() $Processes = @(Get-CimInstance -ClassName Win32_Process -ErrorAction SilentlyContinue) $FoundChild = $true while ($FoundChild) { $FoundChild = $false foreach ($Candidate in $Processes) { if ($OwnedPids.Contains([int]$Candidate.ParentProcessId) -and $OwnedPids.Add([int]$Candidate.ProcessId)) { $FoundChild = $true } } } foreach ($OwnedPid in $OwnedPids) { try { $Child = Get-Process -Id $OwnedPid -ErrorAction Stop $Child.PriorityClass = 'BelowNormal' $Child.ProcessorAffinity = $Affinity if ($Child.PrivateMemorySize64 -ge 8GB) { $MemoryStop = $true; Stop-Process -Id $OwnedPid -Force } } catch {} } if ($MemoryStop) { break } if (([DateTime]::UtcNow - $StartAt).TotalSeconds -ge 900) { $TimedOut = $true; break } Start-Sleep -Milliseconds 200 } if ($TimedOut -or $MemoryStop) { foreach ($OwnedPid in $OwnedPids) { try { Stop-Process -Id $OwnedPid -Force -ErrorAction Stop } catch {} } try { $Process.Kill() } catch {} } $Process.WaitForExit() $CompilerStdout = $OutputTask.GetAwaiter().GetResult() $CompilerStderr = $ErrorTask.GetAwaiter().GetResult() [IO.File]::WriteAllText($Stdout, $CompilerStdout) [IO.File]::WriteAllText($Stderr, $CompilerStderr) if ($TimedOut) { throw 'Build exceeded 15 minutes and was stopped.' } if ($MemoryStop) { throw 'Build process reached the 8 GB private-memory cap and was stopped.' } if ($Process.ExitCode -eq -1073741502 -and $Attempt -eq 1) { Write-Output 'Build exited 0xC0000142; waiting 60 seconds before the one allowed retry.' Start-Sleep -Seconds 60 continue } if ($Process.ExitCode -ne 0) { Write-Output "Build failed exit_code=$($Process.ExitCode) stdout=$Stdout stderr=$Stderr" if ($CompilerStdout) { Write-Output $CompilerStdout } if ($CompilerStderr) { Write-Output $CompilerStderr } throw 'Sharp mesher spike build failed.' } } while ($Process.ExitCode -eq -1073741502 -and $Attempt -lt 2) Write-Output "Build passed exe=$Exe elapsed_seconds=$([Math]::Round(([DateTime]::UtcNow - $StartAt).TotalSeconds,2)) priority=BelowNormal affinity=0xFF00"