From bb5e2b11661bf6846a6912337098df5243231547 Mon Sep 17 00:00:00 2001 From: patchzyy <64382339+patchzyy@users.noreply.github.com> Date: Sun, 25 Oct 2026 09:47:03 +0100 Subject: [PATCH] Fix prebuilt aurora package being rejected (#357) * fix prebuilt package rejected on user machines * Time out a hung translator step The translator can hang after a crash instead of exiting, and the installer's clean retry runs only on a failed exit. Each translator step now gets 1800 s; past that its process tree is killed and the step fails, so the retry runs. * time out mod awareness check too --------- Co-authored-by: therenow9 <60898774+therenow9@users.noreply.github.com> --- Launcher/Build-Installer.ps1 | 18 ++++ Launcher/LocalBuild.ps1 | 97 +++++-------------- Launcher/NativeBuildFlags.ps1 | 172 +++++++++++++++++++++++----------- 3 files changed, 162 insertions(+), 125 deletions(-) diff --git a/Launcher/Build-Installer.ps1 b/Launcher/Build-Installer.ps1 index faeb12d..7be602f 100644 --- a/Launcher/Build-Installer.ps1 +++ b/Launcher/Build-Installer.ps1 @@ -278,6 +278,24 @@ $packagedRuntimeFingerprint = (Get-Content -LiteralPath (Join-Path $workspace 'D if ($stagedRuntimeFingerprint -ne $packagedRuntimeFingerprint) { throw "The staged runtime ($stagedRuntimeFingerprint) does not match the precompiled runtime archive ($packagedRuntimeFingerprint)." } +# Users' builds run LocalBuild.ps1 under Windows PowerShell 5.1, whatever shell built this +# installer, so the staged package is accepted (or not) exactly the way theirs will be. +$prebuiltGate = Join-Path $workRoot 'native-prebuilt-gate.ps1' +@' +param([string]$Workspace, [string]$Toolkit) +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 3.0 +. (Join-Path $Workspace 'NativeBuildFlags.ps1') +$package = Resolve-MkwNativePrebuiltDirectory (Join-Path $Workspace 'Dependencies') ` + (Join-Path $Toolkit 'llvm-mingw\bin') (Join-Path $Workspace 'aurora-main') (Join-Path $Workspace 'runtime\third_party') +if (-not $package) { exit 3 } +'@ | Set-Content -LiteralPath $prebuiltGate -Encoding UTF8 +& (Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe') -NoLogo -NoProfile -NonInteractive ` + -ExecutionPolicy Bypass -File $prebuiltGate -Workspace $workspace -Toolkit $toolkit +if ($LASTEXITCODE -ne 0) { + throw "Windows PowerShell rejects the staged precompiled aurora package (exit $LASTEXITCODE); every installation would build aurora from source." +} +Remove-Item -LiteralPath $prebuiltGate -Force [IO.Directory]::CreateDirectory((Join-Path $payloadRoot 'host')) | Out-Null Copy-Item $setupHost (Join-Path $payloadRoot 'host\WiiCompiled-Setup.exe') diff --git a/Launcher/LocalBuild.ps1 b/Launcher/LocalBuild.ps1 index e319eb6..5969cbe 100644 --- a/Launcher/LocalBuild.ps1 +++ b/Launcher/LocalBuild.ps1 @@ -34,65 +34,6 @@ Set-StrictMode -Version 3.0 # See NativeBuildFlags.ps1; Build-Installer.ps1 stages it beside this script. . (Join-Path $PSScriptRoot 'NativeBuildFlags.ps1') -function Resolve-NativePrebuiltDirectory([string]$DependenciesDirectory, [string]$ToolchainBin, [string]$AuroraDirectory, [string]$ThirdPartyDirectory) { - <# - The shipped precompiled aurora/third-party package, verified against its recorded provenance - (exact compiler, exact flags) before use. Anything unexpected falls back to building from source. - #> - $package = Join-Path $DependenciesDirectory 'native_prebuilt' - $provenancePath = Join-Path $package 'provenance.json' - if (-not (Test-Path -LiteralPath $provenancePath -PathType Leaf)) { - Write-Host 'MKWCBUILD: No precompiled aurora package was shipped; building it from source' - return '' - } - try { - $provenance = Get-Content -LiteralPath $provenancePath -Raw | ConvertFrom-Json - $compiler = Join-Path $ToolchainBin 'clang-22.exe' - $compilerHash = Get-MkwFileSha256 $compiler - if ($provenance.CompilerSha256 -ne $compilerHash) { - Write-Host 'MKWCBUILD: The precompiled aurora package was built by a different compiler; building it from source' - return '' - } - if ($provenance.FlagFingerprint -ne (Get-MkwNativeFlagFingerprint)) { - Write-Host 'MKWCBUILD: The precompiled aurora package was built with different flags; building it from source' - return '' - } - # The archives embed Dawn's headers and link its import library, so a - # dawn_prebuilt tree that is not the one they were harvested against is - # an ABI mismatch that only shows up as a crash inside the GPU backend. - $recordedDawn = $provenance.PSObject.Properties['DawnRuntimeSha256'] - if ($null -eq $recordedDawn -or - $recordedDawn.Value -ne (Get-MkwDawnRuntimeSha256 $DependenciesDirectory)) { - Write-Host 'MKWCBUILD: The precompiled aurora package was built against a different Dawn; building it from source' - return '' - } - # The archives are aurora; the runtime compiled here calls its headers. - # If this workspace's aurora sources are not the ones that were harvested - # (a newer toolkit, or a locally modified tree), linking the package would - # mix two versions of aurora, so build it from source instead. - $recordedAurora = $provenance.PSObject.Properties['AuroraSourceFingerprint'] - if ($null -eq $recordedAurora -or - $recordedAurora.Value -ne (Get-MkwAuroraSourceFingerprint $AuroraDirectory)) { - Write-Host 'MKWCBUILD: The precompiled aurora package was built from different aurora sources; building it from source' - return '' - } - # Same reasoning for the vendored Crypto++ the package also carries: - # first-party TUs compile against this workspace's third_party headers, - # so a package harvested from an older tree must not be linked here. - $recordedThirdParty = $provenance.PSObject.Properties['ThirdPartySourceFingerprint'] - if ($null -eq $recordedThirdParty -or - $recordedThirdParty.Value -ne (Get-MkwThirdPartySourceFingerprint $ThirdPartyDirectory)) { - Write-Host 'MKWCBUILD: The precompiled aurora package was built from different vendored third-party sources; building it from source' - return '' - } - } catch { - Write-Host "MKWCBUILD: The precompiled aurora package could not be validated ($($_.Exception.Message)); building it from source" - return '' - } - Write-Host 'MKWCBUILD: Using the precompiled aurora and third-party libraries' - return $package -} - function Write-MkwBuildStep([string]$StepId, [string]$Message) { <# Announces a build step. The identifier is the contract (BuildStepIds in @@ -160,6 +101,8 @@ if (-not [string]::IsNullOrWhiteSpace($RetroRewindPackageDirectory) -and $RetroRewindPackageDirectory = $Workspace + $RetroRewindPackageDirectory.Substring($realWorkspace.Length) } $translator = Join-Path $Toolkit 'Translator\Translator.Cli.exe' +# A translator step takes 3-19 s on 16 threads; past this it has hung, and failing lets the clean retry run. +$translatorTimeoutSeconds = 1800 $toolchain = Get-MkwBuildSafePath $Toolkit 'toolchain' 'CMake\bin\cmake.exe' $cmake = Join-Path $toolchain 'CMake\bin\cmake.exe' $ninja = Join-Path $toolchain 'Ninja\ninja.exe' @@ -208,8 +151,9 @@ if ($Parallel -gt 0) { $globalJobs = $Parallel } else { $translatorThreads = [math]::Max(1, [math]::Min([Environment]::ProcessorCount, 16)) + # A translated TU peaks around 0.26 GB in clang; 0.5 GiB each after a 2 GiB reserve. $translatedJobs = [math]::Max(1, [math]::Min([Environment]::ProcessorCount, - [math]::Floor($memoryGiB / 2))) + [math]::Floor(($memoryGiB - 2) / 0.5))) $globalJobs = [math]::Max($translatedJobs, [Environment]::ProcessorCount) } @@ -286,11 +230,18 @@ try { # same translated functions produces the same base tree. Only the translator can # decide that - it takes parsing the Kamek patch set against the recorded translated # function ranges - and it fails closed, so anything but exit 0 retranslates. - & $translator @( - 'check-base-mod-awareness', '--project', $project, '--profile', 'retro-rewind', - '--translation-output-metadata', $baseMetadata, '--code-pul', $retroCodePul - ) | Write-Host - if ($LASTEXITCODE -ne 0) { + $modAware = $true + try { + Invoke-Checked $translator @( + 'check-base-mod-awareness', '--project', $project, '--profile', 'retro-rewind', + '--translation-output-metadata', $baseMetadata, '--code-pul', $retroCodePul + ) 'Checking whether the base translation covers the new Code.pul' ` + -TimeoutSeconds $translatorTimeoutSeconds + } catch { + Write-Host "MKWCBUILD: $($_.Exception.Message)" + $modAware = $false + } + if (-not $modAware) { Write-MkwBuildStep 'retranslate-base' 'The base translation is stale; retranslating the base game for the new Code.pul' $reuseBase = $false } @@ -313,12 +264,14 @@ try { '--outdir', $functions, '--output-metadata', $baseMetadata, '--production-source-bundle', (Join-Path $generated 'base_translation_sources.bin'), '--no-function-files', '--prune-stale', '--threads', $translatorThreads - ) 'Translating the user-owned base game' -StepId 'translate-base' + ) 'Translating the user-owned base game' -StepId 'translate-base' ` + -TimeoutSeconds $translatorTimeoutSeconds Invoke-Checked $translator @( 'emit-base-manifest', '--project', $project, '--out', $baseManifestDir, '--functions-dir', $functions, '--translation-output-metadata', $baseMetadata, '--region', $pins.Region - ) 'Creating the local base translation manifest' -StepId 'emit-base-manifest' + ) 'Creating the local base translation manifest' -StepId 'emit-base-manifest' ` + -TimeoutSeconds $translatorTimeoutSeconds if (-not [string]::IsNullOrWhiteSpace($TranslationFingerprint)) { [ordered]@{ SchemaVersion = 1; TranslationFingerprint = $TranslationFingerprint; DolSha256 = $pins.DolSha256 } | @@ -348,11 +301,12 @@ try { throw 'Retro Rewind requires either -RetroWfcOfflineDirectory or -SkipRetroWfcPayload.' } Invoke-Checked $translator $translateModArguments 'Translating the selected Retro Rewind Code.pul' ` - -StepId 'translate-mod' + -StepId 'translate-mod' -TimeoutSeconds $translatorTimeoutSeconds } Invoke-Checked $translator @('generate-data-init', '--project', $project) ` - 'Generating local game data initialization' -StepId 'generate-data-init' + 'Generating local game data initialization' -StepId 'generate-data-init' ` + -TimeoutSeconds $translatorTimeoutSeconds $shardArgs = @( 'emit-build-shards', '--project', $project, '--base-metadata', $baseMetadata, @@ -364,7 +318,8 @@ try { $shardArgs += @('--resolved-profile', (Join-Path $retroOut 'resolved_dispatch_profile.json'), '--retro-cpp-dir', (Join-Path $retroOut 'cpp')) } - Invoke-Checked $translator $shardArgs 'Preparing local native build shards' -StepId 'emit-build-shards' + Invoke-Checked $translator $shardArgs 'Preparing local native build shards' -StepId 'emit-build-shards' ` + -TimeoutSeconds $translatorTimeoutSeconds # The build directory is kept whenever the exact toolchain that filled it is the one # installed now and its CMake cache still belongs to this workspace path. Object files are @@ -396,7 +351,7 @@ try { } } $dependencies = Join-Path $Workspace 'Dependencies' - $nativePrebuilt = Resolve-NativePrebuiltDirectory $dependencies $toolchainBin ` + $nativePrebuilt = Resolve-MkwNativePrebuiltDirectory $dependencies $toolchainBin ` (Join-Path $Workspace 'aurora-main') (Join-Path $Workspace 'runtime\third_party') if ($X86CpuProfile -eq 'v2') { Write-Host 'MKWCBUILD: This CPU does not support x86-64-v3; building for x86-64-v2' diff --git a/Launcher/NativeBuildFlags.ps1 b/Launcher/NativeBuildFlags.ps1 index 9e8ad39..9cf4318 100644 --- a/Launcher/NativeBuildFlags.ps1 +++ b/Launcher/NativeBuildFlags.ps1 @@ -157,7 +157,8 @@ function Resolve-MkwProjectForAssets([string]$Workspace, [string]$AssetsDirector } function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Description, - [string]$LogPrefix = 'MKWCBUILD', [string]$StepId = '', [bool]$WaitForProcessTree = $true) { + [string]$LogPrefix = 'MKWCBUILD', [string]$StepId = '', [bool]$WaitForProcessTree = $true, + [int]$TimeoutSeconds = 0) { <# Runs a build tool and turns a non-zero exit code into a described failure. By default, Start-Process -Wait waits for the whole process tree, since a .NET single-file bundle host may @@ -166,6 +167,7 @@ function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Descri 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. + -TimeoutSeconds waits on the process itself and kills its tree when time runs out. #> if ($StepId) { Write-Host "${LogPrefix}:STEP:${StepId} $Description" } else { Write-Host "${LogPrefix}: $Description" } @@ -174,7 +176,17 @@ function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Descri # Backslashes directly before the closing quote would otherwise escape it. '"' + ($_ -replace '(\\+)$', '$1$1') + '"' }) - if ($WaitForProcessTree) { + if ($TimeoutSeconds -gt 0) { + $process = Start-Process -FilePath $FilePath -ArgumentList $quotedArguments -NoNewWindow -PassThru + # Holding the handle keeps ExitCode readable once the process has exited. + $null = $process.Handle + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + # taskkill's stderr would end the script under 'Stop' before the message below. + try { & taskkill.exe /T /F /PID $process.Id 2>&1 | Out-Null } catch {} + throw "$Description did not finish within $TimeoutSeconds seconds and was stopped." + } + $exitCode = $process.ExitCode + } elseif ($WaitForProcessTree) { $process = Start-Process -FilePath $FilePath -ArgumentList $quotedArguments ` -NoNewWindow -Wait -PassThru $exitCode = $process.ExitCode @@ -236,69 +248,121 @@ function Get-MkwDawnRuntimeSha256([string]$DependenciesDirectory) { return Get-MkwFileSha256 $runtime } -function Get-MkwThirdPartySourceFingerprint([string]$ThirdPartyDirectory) { +function ConvertTo-MkwHex([byte[]]$Bytes) { + return [BitConverter]::ToString($Bytes).Replace('-', '').ToLowerInvariant() +} + +function Get-MkwSourceTreeFingerprint([string]$Directory, [string[]]$ExcludedTopDirectories = @()) { <# - 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), skipping dot-paths by name the same way. - Returns '' when absent. + SHA-256 over "relative-path content-sha256" lines of a source tree. It must come out the same + under powershell.exe 5.1 and pwsh 7 and for any checkout's line endings, because the package is + harvested on a release machine and re-checked on every user's: paths are sorted ordinally (not + with Sort-Object, whose culture collation differs between NLS and ICU) and text content is hashed + with CRLF folded to LF. Dot-paths are skipped by name, not by the hidden attribute, which the + payload zip drops. Returns '' when the tree is 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 -Force | - Where-Object { -not ($_.FullName.Substring($root.Length + 1) -split '\\' | Where-Object { $_.StartsWith('.') }) } | - Sort-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') } + if ([string]::IsNullOrWhiteSpace($Directory)) { return '' } + if (-not (Test-Path -LiteralPath $Directory -PathType Container)) { return '' } + $root = [IO.Path]::GetFullPath($Directory).TrimEnd('\') + $relatives = [Collections.Generic.List[string]]::new() + foreach ($file in [IO.Directory]::EnumerateFiles($root, '*', [IO.SearchOption]::AllDirectories)) { + $relative = $file.Substring($root.Length + 1).Replace('\', '/') + $parts = $relative.Split('/') + if ($parts.Length -gt 1 -and $ExcludedTopDirectories -contains $parts[0]) { continue } + $hidden = $false + foreach ($part in $parts) { if ($part.StartsWith('.', [StringComparison]::Ordinal)) { $hidden = $true; break } } + if (-not $hidden) { $relatives.Add($relative) } + } + $sorted = $relatives.ToArray() + [Array]::Sort($sorted, [StringComparer]::Ordinal) + + $latin1 = [Text.Encoding]::GetEncoding(28591) $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") + foreach ($relative in $sorted) { + $bytes = [IO.File]::ReadAllBytes($root + '\' + $relative.Replace('/', '\')) + # Latin-1 maps bytes 1:1, so the round trip only changes the line endings. + $text = $latin1.GetString($bytes) + if ($text.IndexOf([char]0) -lt 0 -and $text.Contains("`r`n")) { + $bytes = $latin1.GetBytes($text.Replace("`r`n", "`n")) + } + [void]$builder.Append($relative).Append(' ').Append((ConvertTo-MkwHex $sha.ComputeHash($bytes))).Append("`n") } - $bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($builder.ToString())) + return ConvertTo-MkwHex $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($builder.ToString())) } finally { $sha.Dispose() } - return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '') +} + +function Get-MkwThirdPartySourceFingerprint([string]$ThirdPartyDirectory) { + <# + 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. + #> + return Get-MkwSourceTreeFingerprint $ThirdPartyDirectory } 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. + 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. extern/ is excluded because the payload + ships those trees separately. #> - 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') + '\' - ) - # Dot-paths (the submodule's .git pointer) are skipped by name, not by the hidden attribute: - # the payload zip drops that attribute, so the installed side would otherwise hash them. - $files = Get-ChildItem -LiteralPath $root -Recurse -File -Force | - Where-Object { - $full = $_.FullName - -not ($excludedPrefixes | Where-Object { $full.StartsWith($_, [StringComparison]::OrdinalIgnoreCase) }) -and - -not ($full.Substring($root.Length + 1) -split '\\' | Where-Object { $_.StartsWith('.') }) - } | - Sort-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') } - $sha = [Security.Cryptography.SHA256]::Create() + return Get-MkwSourceTreeFingerprint $AuroraDirectory @('extern', 'build') +} + +function Resolve-MkwNativePrebuiltDirectory([string]$DependenciesDirectory, [string]$ToolchainBin, + [string]$AuroraDirectory, [string]$ThirdPartyDirectory) { + <# + The shipped precompiled aurora/third-party package, verified against its recorded provenance + (exact compiler, exact flags, sources) before use. Anything unexpected falls back to building + from source. Shared with Build-Installer.ps1, which runs it against the staged payload under + powershell.exe so a package users would reject never ships. + #> + $package = Join-Path $DependenciesDirectory 'native_prebuilt' + $provenancePath = Join-Path $package 'provenance.json' + if (-not (Test-Path -LiteralPath $provenancePath -PathType Leaf)) { + Write-Host 'MKWCBUILD: No precompiled aurora package was shipped; building it from source' + return '' + } 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") + $provenance = Get-Content -LiteralPath $provenancePath -Raw | ConvertFrom-Json + if ($provenance.CompilerSha256 -ne (Get-MkwFileSha256 (Join-Path $ToolchainBin 'clang-22.exe'))) { + Write-Host 'MKWCBUILD: The precompiled aurora package was built by a different compiler; building it from source' + return '' } - $bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($builder.ToString())) - } finally { $sha.Dispose() } - return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '') + if ($provenance.FlagFingerprint -ne (Get-MkwNativeFlagFingerprint)) { + Write-Host 'MKWCBUILD: The precompiled aurora package was built with different flags; building it from source' + return '' + } + # The archives embed Dawn's headers and link its import library; a different Dawn is an + # ABI mismatch that only shows up as a crash inside the GPU backend. + $recordedDawn = $provenance.PSObject.Properties['DawnRuntimeSha256'] + if ($null -eq $recordedDawn -or + $recordedDawn.Value -ne (Get-MkwDawnRuntimeSha256 $DependenciesDirectory)) { + Write-Host 'MKWCBUILD: The precompiled aurora package was built against a different Dawn; building it from source' + return '' + } + $recordedAurora = $provenance.PSObject.Properties['AuroraSourceFingerprint'] + if ($null -eq $recordedAurora -or + $recordedAurora.Value -ne (Get-MkwAuroraSourceFingerprint $AuroraDirectory)) { + Write-Host 'MKWCBUILD: The precompiled aurora package was built from different aurora sources; building it from source' + return '' + } + $recordedThirdParty = $provenance.PSObject.Properties['ThirdPartySourceFingerprint'] + if ($null -eq $recordedThirdParty -or + $recordedThirdParty.Value -ne (Get-MkwThirdPartySourceFingerprint $ThirdPartyDirectory)) { + Write-Host 'MKWCBUILD: The precompiled aurora package was built from different vendored third-party sources; building it from source' + return '' + } + } catch { + Write-Host "MKWCBUILD: The precompiled aurora package could not be validated ($($_.Exception.Message)); building it from source" + return '' + } + Write-Host 'MKWCBUILD: Using the precompiled aurora and third-party libraries' + return $package } function ConvertTo-MkwCMakePath([string]$Path) { @@ -347,9 +411,9 @@ function Get-MkwNativeConfigureArguments { } } - if (-not [string]::IsNullOrWhiteSpace($NativePrebuiltDirectory)) { - $arguments += "-DMKW_NATIVE_PREBUILT_DIR=$(ConvertTo-MkwCMakePath $NativePrebuiltDirectory)" - } + # Always set, so a reused CMake cache can never keep a package this run rejected. + $arguments += if ([string]::IsNullOrWhiteSpace($NativePrebuiltDirectory)) { '-DMKW_NATIVE_PREBUILT_DIR=' } + else { "-DMKW_NATIVE_PREBUILT_DIR=$(ConvertTo-MkwCMakePath $NativePrebuiltDirectory)" } $arguments += $AdditionalArguments return $arguments