mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-12 01:38:50 -04:00
init
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
# Canonical native (CMake) configure flags, plus the shared helpers (file assertions, checked
|
||||
# process spawn, file hashing) both LocalBuild.ps1 and Prepare-NativePrebuilt.ps1 dot-source.
|
||||
# They MUST stay byte-identical: the prebuilt package ships static archives linked into objects
|
||||
# the user's machine compiles, so any flag divergence is an ABI/ODR hazard, not just a rebuild.
|
||||
# The flag fingerprint below is recorded in the package provenance and re-checked before use.
|
||||
|
||||
Set-StrictMode -Version 3.0
|
||||
|
||||
function Assert-File([string]$Path, [string]$Description) {
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "$Description is missing: $Path" }
|
||||
}
|
||||
|
||||
function Assert-Directory([string]$Path, [string]$Description) {
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Container)) { throw "$Description is missing: $Path" }
|
||||
}
|
||||
|
||||
function Get-MkwFileSha256([string]$Path) {
|
||||
<#
|
||||
Lower-case SHA-256 of one file. -LiteralPath is required: Get-FileHash treats a positional
|
||||
path as a wildcard, so an install directory containing [, ] or * would hash the wrong file.
|
||||
#>
|
||||
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
|
||||
function Get-MkwToolchainPath([string]$ToolchainRoot) {
|
||||
<#
|
||||
The scrubbed PATH a bundled-toolchain build runs with. The bundled toolchain must be the only
|
||||
one visible, or an ambient Visual Studio/MSYS install on PATH could pull in its own headers,
|
||||
libraries, or linker. Shared by LocalBuild.ps1 and Prepare-NativePrebuilt.ps1 since the
|
||||
installed Toolkit and the maintainer portable-tools tree share the same subdirectory layout.
|
||||
#>
|
||||
if ([string]::IsNullOrWhiteSpace($ToolchainRoot)) { throw 'A toolchain root is required.' }
|
||||
$root = [IO.Path]::GetFullPath($ToolchainRoot)
|
||||
return (@(
|
||||
(Join-Path $root 'llvm-mingw\bin'),
|
||||
(Join-Path $root 'CMake\bin'),
|
||||
(Join-Path $root 'Ninja'),
|
||||
(Join-Path $env:SystemRoot 'System32'),
|
||||
$env:SystemRoot
|
||||
) -join ';')
|
||||
}
|
||||
|
||||
function Get-MkwProjectPins([string]$ProjectFile) {
|
||||
<#
|
||||
The Mario Kart Wii facts pinned by projects/mkwii/recomp.yml (game identity, clean input
|
||||
digests, translation entry point, Retro-WFC endpoint), read from one place instead of being
|
||||
restated; Test-PinnedFacts.ps1 checks the copies that can't read YAML (C++ header, C#
|
||||
constants) against these values. Parsing is literal, not a YAML dependency, since the file is
|
||||
machine-written with a fixed shape.
|
||||
#>
|
||||
Assert-File $ProjectFile 'Translation project file'
|
||||
$pins = [ordered]@{
|
||||
GameId = ''; DolSha256 = ''; RelSha256 = ''; EntryPoint = ''; RetroWfcPayloadUri = ''
|
||||
}
|
||||
$section = ''
|
||||
$inputKey = ''
|
||||
foreach ($raw in [IO.File]::ReadAllLines($ProjectFile)) {
|
||||
$line = ($raw -replace '#.*$', '')
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
if ($line -match '^([A-Za-z0-9_]+):') { $section = $Matches[1]; $inputKey = ''; continue }
|
||||
if ($section -eq 'inputs' -and $line -match '^\s{2}([A-Za-z0-9_]+):\s*$') { $inputKey = $Matches[1]; continue }
|
||||
if ($section -eq 'project' -and $line -match '^\s*game_id:\s*(\S+)\s*$') {
|
||||
$pins.GameId = $Matches[1]
|
||||
} elseif ($section -eq 'inputs' -and $line -match '^\s*sha256:\s*([0-9a-fA-F]{64})\s*$') {
|
||||
if ($inputKey -eq 'dol') { $pins.DolSha256 = $Matches[1].ToLowerInvariant() }
|
||||
elseif ($inputKey -eq 'rel') { $pins.RelSha256 = $Matches[1].ToLowerInvariant() }
|
||||
} elseif ($section -eq 'translation' -and $pins.EntryPoint -eq '' -and
|
||||
$line -match '^\s*-\s*(0[xX][0-9a-fA-F]+)\s*$') {
|
||||
$pins.EntryPoint = $Matches[1].ToLowerInvariant()
|
||||
} elseif ($line -match '^\s*retro_wfc_payload:\s*(\S+)\s*$') {
|
||||
$pins.RetroWfcPayloadUri = $Matches[1]
|
||||
}
|
||||
}
|
||||
foreach ($name in @($pins.Keys)) {
|
||||
if ([string]::IsNullOrWhiteSpace($pins[$name])) {
|
||||
throw "$ProjectFile does not pin $name; the project file is not the shape this build expects."
|
||||
}
|
||||
}
|
||||
return $pins
|
||||
}
|
||||
|
||||
function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Description,
|
||||
[string]$LogPrefix = 'MKWCBUILD', [string]$StepId = '') {
|
||||
<#
|
||||
Runs a build tool and turns a non-zero exit code into a described failure. Start-Process -Wait
|
||||
is deliberate: it waits for the whole process tree, since a .NET single-file bundle host may
|
||||
hand off to an extracted child that PowerShell's call operator would not wait for. Start-Process
|
||||
doesn't publish $LASTEXITCODE, so this sets it manually for callers that check it.
|
||||
-StepId emits the machine-readable form the installer's progress bar consumes (BuildStepIds in
|
||||
WiiCompiled.Setup/InstallProgress.cs); the human sentence stays on the same log line.
|
||||
#>
|
||||
if ($StepId) { Write-Host "${LogPrefix}:STEP:${StepId} $Description" }
|
||||
else { Write-Host "${LogPrefix}: $Description" }
|
||||
$quotedArguments = @($Arguments | ForEach-Object {
|
||||
if ($_.Contains('"')) { throw "A native build argument contains an unsupported quote: $_" }
|
||||
'"' + $_ + '"'
|
||||
})
|
||||
$process = Start-Process -FilePath $FilePath -ArgumentList $quotedArguments `
|
||||
-NoNewWindow -Wait -PassThru
|
||||
$exitCode = $process.ExitCode
|
||||
$global:LASTEXITCODE = $exitCode
|
||||
if ($exitCode -ne 0) { throw "$Description failed with exit code $exitCode." }
|
||||
}
|
||||
|
||||
function Get-MkwNativeFixedConfigureFlags {
|
||||
<#
|
||||
The path-independent half of the configure command line, identical on every machine, which is
|
||||
what makes one precompiled aurora/third-party package valid for all users. Path-bearing
|
||||
arguments are added separately by Get-MkwNativeConfigureArguments and excluded from the fingerprint.
|
||||
#>
|
||||
return @(
|
||||
'-DCMAKE_BUILD_TYPE=Release',
|
||||
'-DCMAKE_SYSTEM_PROCESSOR=x86_64',
|
||||
'-DAURORA_DAWN_PROVIDER=package', '-DAURORA_SDL3_PROVIDER=vendor',
|
||||
'-DCMAKE_POLICY_DEFAULT_CMP0168=NEW', '-DFETCHCONTENT_FULLY_DISCONNECTED=ON',
|
||||
'-DAWK:FILEPATH='
|
||||
)
|
||||
}
|
||||
|
||||
function Get-MkwNativeFlagFingerprint {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
SHA-256 over the canonical flag list, used to reject a stale package.
|
||||
#>
|
||||
$canonical = (Get-MkwNativeFixedConfigureFlags) -join "`n"
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($canonical))
|
||||
} finally { $sha.Dispose() }
|
||||
return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '')
|
||||
}
|
||||
|
||||
function Get-MkwDawnRuntimeSha256([string]$DependenciesDirectory) {
|
||||
<#
|
||||
SHA-256 of the pinned Dawn runtime the prebuilt aurora must match. The precompiled aurora
|
||||
archives are compiled and linked against the pinned dawn_prebuilt tree's headers/import
|
||||
library, but the upstream release asset is mutable (the same version tag has already served
|
||||
two different windows-amd64 archives), so the version string alone can't prove agreement.
|
||||
A mismatch surfaces as an access violation at surface creation, not a link error, so the
|
||||
digest is recorded at harvest and re-checked before use. Returns '' when the tree is absent.
|
||||
#>
|
||||
if ([string]::IsNullOrWhiteSpace($DependenciesDirectory)) { return '' }
|
||||
$runtime = Join-Path $DependenciesDirectory 'dawn_prebuilt\bin\webgpu_dawn.dll'
|
||||
if (-not (Test-Path -LiteralPath $runtime -PathType Leaf)) { return '' }
|
||||
return Get-MkwFileSha256 $runtime
|
||||
}
|
||||
|
||||
function Get-MkwThirdPartySourceFingerprint([string]$ThirdPartyDirectory) {
|
||||
<#
|
||||
SHA-256 over runtime/third_party, the vendored sources behind the prebuilt package. The
|
||||
precompiled Crypto++ archive is linked while first-party TUs compile against the same headers,
|
||||
so a workspace whose vendored tree moved on must not consume archives harvested from an older
|
||||
one (same reasoning as the aurora fingerprint below). Unlike that scan this hashes hidden files
|
||||
too, since Build-Installer stages this tree with Copy-Item -Force. Returns '' when absent.
|
||||
#>
|
||||
if ([string]::IsNullOrWhiteSpace($ThirdPartyDirectory)) { return '' }
|
||||
if (-not (Test-Path -LiteralPath $ThirdPartyDirectory -PathType Container)) { return '' }
|
||||
$root = [IO.Path]::GetFullPath($ThirdPartyDirectory).TrimEnd('\')
|
||||
$files = Get-ChildItem -LiteralPath $root -Recurse -File |
|
||||
Sort-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') }
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$builder = [Text.StringBuilder]::new()
|
||||
foreach ($file in $files) {
|
||||
$relative = $file.FullName.Substring($root.Length + 1).Replace('\', '/')
|
||||
$content = Get-MkwFileSha256 $file.FullName
|
||||
[void]$builder.Append($relative).Append(' ').Append($content).Append("`n")
|
||||
}
|
||||
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($builder.ToString()))
|
||||
} finally { $sha.Dispose() }
|
||||
return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '')
|
||||
}
|
||||
|
||||
function Get-MkwAuroraSourceFingerprint([string]$AuroraDirectory) {
|
||||
<#
|
||||
SHA-256 over the aurora sources the precompiled archives were harvested from. The compiler,
|
||||
flag, and Dawn digests only prove the package agrees with its target environment; none notice
|
||||
aurora itself moving on, and since the runtime calls aurora headers directly, a package
|
||||
harvested before an aurora change links old code against new call sites (undefined symbol at
|
||||
best, silently stale renderer at worst). extern/ is excluded because the payload ships those
|
||||
trees separately. Returns '' when the tree is absent.
|
||||
#>
|
||||
if ([string]::IsNullOrWhiteSpace($AuroraDirectory)) { return '' }
|
||||
if (-not (Test-Path -LiteralPath $AuroraDirectory -PathType Container)) { return '' }
|
||||
$root = [IO.Path]::GetFullPath($AuroraDirectory).TrimEnd('\')
|
||||
$excludedPrefixes = @(
|
||||
(Join-Path $root 'extern') + '\',
|
||||
(Join-Path $root 'build') + '\'
|
||||
)
|
||||
# Hidden files are skipped deliberately: Build-Installer.ps1 stages this tree
|
||||
# with Copy-Item -Recurse (no -Force), so they never reach the payload and
|
||||
# hashing them would make the two sides disagree by construction.
|
||||
$files = Get-ChildItem -LiteralPath $root -Recurse -File |
|
||||
Where-Object {
|
||||
$full = $_.FullName
|
||||
-not ($excludedPrefixes | Where-Object { $full.StartsWith($_, [StringComparison]::OrdinalIgnoreCase) })
|
||||
} |
|
||||
Sort-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') }
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$builder = [Text.StringBuilder]::new()
|
||||
foreach ($file in $files) {
|
||||
$relative = $file.FullName.Substring($root.Length + 1).Replace('\', '/')
|
||||
$content = Get-MkwFileSha256 $file.FullName
|
||||
[void]$builder.Append($relative).Append(' ').Append($content).Append("`n")
|
||||
}
|
||||
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($builder.ToString()))
|
||||
} finally { $sha.Dispose() }
|
||||
return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '')
|
||||
}
|
||||
|
||||
function ConvertTo-MkwCMakePath([string]$Path) {
|
||||
return [IO.Path]::GetFullPath($Path).Replace('\', '/')
|
||||
}
|
||||
|
||||
function Get-MkwNativeConfigureArguments {
|
||||
<# The complete `cmake` configure argument list for a local runtime build. #>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)] [string]$SourceDirectory,
|
||||
[Parameter(Mandatory)] [string]$BuildDirectory,
|
||||
[Parameter(Mandatory)] [string]$Ninja,
|
||||
[Parameter(Mandatory)] [string]$CCompiler,
|
||||
[Parameter(Mandatory)] [string]$CxxCompiler,
|
||||
[Parameter(Mandatory)] [string]$ResourceCompiler,
|
||||
[string]$DependenciesDirectory,
|
||||
[string]$NativePrebuiltDirectory,
|
||||
[string[]]$AdditionalArguments = @()
|
||||
)
|
||||
|
||||
$arguments = @(
|
||||
'-S', (ConvertTo-MkwCMakePath $SourceDirectory),
|
||||
'-B', (ConvertTo-MkwCMakePath $BuildDirectory),
|
||||
'-G', 'Ninja',
|
||||
"-DCMAKE_MAKE_PROGRAM=$(ConvertTo-MkwCMakePath $Ninja)",
|
||||
"-DCMAKE_C_COMPILER=$(ConvertTo-MkwCMakePath $CCompiler)",
|
||||
"-DCMAKE_CXX_COMPILER=$(ConvertTo-MkwCMakePath $CxxCompiler)",
|
||||
"-DCMAKE_RC_COMPILER=$(ConvertTo-MkwCMakePath $ResourceCompiler)"
|
||||
)
|
||||
$arguments += Get-MkwNativeFixedConfigureFlags
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($DependenciesDirectory) -and
|
||||
(Test-Path -LiteralPath $DependenciesDirectory -PathType Container)) {
|
||||
$cppWinRt = Join-Path $DependenciesDirectory 'cppwinrt'
|
||||
if (Test-Path -LiteralPath (Join-Path $cppWinRt 'winrt\base.h') -PathType Leaf) {
|
||||
$arguments += "-DMKW_CPPWINRT_INCLUDE_DIR=$(ConvertTo-MkwCMakePath $cppWinRt)"
|
||||
}
|
||||
foreach ($directory in Get-ChildItem -LiteralPath $DependenciesDirectory -Directory) {
|
||||
# native_prebuilt holds compiled output, not a FetchContent source tree.
|
||||
if ($directory.Name -eq 'native_prebuilt') { continue }
|
||||
# FetchContent uppercases the declared name but preserves punctuation
|
||||
# (for example abseil-cpp -> FETCHCONTENT_SOURCE_DIR_ABSEIL-CPP).
|
||||
$name = $directory.Name.ToUpperInvariant()
|
||||
$arguments += "-DFETCHCONTENT_SOURCE_DIR_$name=$(ConvertTo-MkwCMakePath $directory.FullName)"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($NativePrebuiltDirectory)) {
|
||||
$arguments += "-DMKW_NATIVE_PREBUILT_DIR=$(ConvertTo-MkwCMakePath $NativePrebuiltDirectory)"
|
||||
}
|
||||
|
||||
$arguments += $AdditionalArguments
|
||||
return $arguments
|
||||
}
|
||||
Reference in New Issue
Block a user