mandelbrot/scripts/source-baseline.ps1
2026-08-23 17:12:29 +09:00

99 lines
5.9 KiB
PowerShell

$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'build-hosted.ps1') | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Hosted build failed with exit code $LASTEXITCODE." }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'build-standalone.ps1') | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Standalone build failed with exit code $LASTEXITCODE." }
$contract = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\source-contract.ps1') | ConvertFrom-Json
if ($LASTEXITCODE -ne 0 -or $contract.status -ne 'pass') { throw 'Source contract failed; baseline was not written.' }
$wasmManifest = Get-Content -LiteralPath (Join-Path $workspace 'dist\wasm\manifest.json') -Raw -Encoding UTF8 | ConvertFrom-Json
$shallowPayload = $wasmManifest.payloads | Where-Object symbol -eq 'WASM_SIMD_B64' | Select-Object -First 1
$shallowHashed = ([System.IO.Path]::GetFileNameWithoutExtension($shallowPayload.file) + '.' + $shallowPayload.sha256.Substring(0,16) + [System.IO.Path]::GetExtension($shallowPayload.file))
function File-Info([string]$Path) {
$item = Get-Item -LiteralPath $Path
[pscustomobject][ordered]@{ file=$item.FullName.Substring($workspace.Length).TrimStart('\').Replace('\','/'); bytes=$item.Length; sha256=(Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() }
}
$node = Get-Command node -ErrorAction SilentlyContinue
$clang = Get-Command clang -ErrorAction SilentlyContinue
$nodeVersion = if ($node) { (& $node.Source --version | Select-Object -First 1) } else { $null }
$sourceBuildVerified = $false
$sourceBuildReason = 'Pinned clang 17.0.6 and Node CLI were not available.'
if ($node -and $clang -and ((& $clang.Source --version | Select-Object -First 1) -match '17\.0\.6')) {
$generatedManifest = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'scripts\build-wasm.ps1') -Compiler $clang.Source
if ($LASTEXITCODE -ne 0) { throw 'Source-WASM build failed while recording the baseline.' }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\kernel-golden.ps1') -GeneratedDirectory (Split-Path $generatedManifest)
if ($LASTEXITCODE -ne 0) { throw 'Generated-WASM golden failed while recording the baseline.' }
$sourceBuildVerified = $true
$sourceBuildReason = $null
} else {
$previousPath = Join-Path $workspace 'audit\v23-source-baseline.json'
$candidateManifest = Join-Path $workspace 'build\wasm-v23\manifest.json'
if ((Test-Path -LiteralPath $previousPath) -and (Test-Path -LiteralPath $candidateManifest)) {
$previous = Get-Content -LiteralPath $previousPath -Raw -Encoding UTF8 | ConvertFrom-Json
$candidateHash = (Get-FileHash -LiteralPath $candidateManifest -Algorithm SHA256).Hash.ToLowerInvariant()
$sourceInputsMatch = $true
foreach ($record in $previous.sourceBuild.files) {
$recordPath = Join-Path $workspace ($record.file.Replace('/', [System.IO.Path]::DirectorySeparatorChar))
if (-not (Test-Path -LiteralPath $recordPath) -or (Get-FileHash -LiteralPath $recordPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $record.sha256) { $sourceInputsMatch = $false; break }
}
if ($sourceInputsMatch -and $previous.sourceBuild.compiledInThisEnvironment -and $previous.sourceBuild.goldenStatus -eq 'pass' -and $previous.sourceBuild.generatedManifest.sha256 -eq $candidateHash) {
$generatedManifest = $candidateManifest
$sourceBuildVerified = $true
$nodeVersion = $previous.sourceBuild.node
$sourceBuildReason = 'Verified generated manifest reused; fixed toolchain was removed after the successful gate.'
}
}
}
$hostedFiles = @(
File-Info (Join-Path $workspace 'dist\hosted\index.html')
File-Info (Join-Path $workspace 'dist\hosted\hosted-loader.js')
File-Info (Join-Path $workspace 'dist\hosted\script.js')
File-Info (Join-Path $workspace "dist\wasm\$shallowHashed")
)
$standaloneFiles = @(
File-Info (Join-Path $workspace 'dist\standalone\index.html')
File-Info (Join-Path $workspace 'dist\standalone\script.js')
File-Info (Join-Path $workspace 'dist\standalone\kernels.js')
)
$kernelSources = @(
File-Info (Join-Path $workspace 'src\shallow_kernel.c')
File-Info (Join-Path $workspace 'src\deep_kernel.c')
File-Info (Join-Path $workspace 'src\bla_kernel_v18.c')
File-Info (Join-Path $workspace 'src\color_kernel.c')
File-Info (Join-Path $workspace 'src\abi.json')
File-Info (Join-Path $workspace 'toolchain.lock.json')
)
$baseline = [ordered]@{
format = 'mandelbrot-source-baseline-v23'
generatedUtc = [DateTime]::UtcNow.ToString('o')
rendererVersion = 23
contract = $contract
hosted = [ordered]@{
firstViewFiles = $hostedFiles
firstViewUncompressedBytes = ($hostedFiles | Measure-Object bytes -Sum).Sum
deepRequestContractBeforeDeepView = 0
deepRequestMeasurementStatus = 'not-run'
note = 'The zero is a static loader contract, not a measured request count. Transfer compression, request count, parse, compile, and runtime timings require the fixed Hosted browser benchmark.'
}
standalone = [ordered]@{
files = $standaloneFiles
uncompressedBytes = ($standaloneFiles | Measure-Object bytes -Sum).Sum
}
sourceBuild = [ordered]@{
files=$kernelSources
compiler='clang 17.0.6'
node=$nodeVersion
compiledInThisEnvironment=$sourceBuildVerified
goldenStatus=if ($sourceBuildVerified) { 'pass' } else { 'not-run' }
generatedManifest=if ($sourceBuildVerified) { File-Info $generatedManifest } else { $null }
reason=$sourceBuildReason
}
browserBenchmark = [ordered]@{ status='not-run'; report='audit/v23-browser-baseline.json'; reason='No in-app browser binding was available in this environment.' }
}
$path = Join-Path $workspace 'audit\v23-source-baseline.json'
$baseline | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding UTF8
Write-Output $path