mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 01:00:14 +02:00
Merge upstream main (v0.2.31) into openxr-work
Brings in 25 upstream commits: Dolphin-compatible input expressions and GCPadNew.ini import, NAND setting.txt console identity, empty-MKW-save handling, rumble toggle, LLVM 22, and CI caching. The only conflict was runtime/CMakeLists.txt, where both sides appended test targets after mkw_platform_paths_tests. Both blocks are kept: the VR first-person test alongside upstream's NAND save/settings, SC serial, and input expression tests. runtime_config.h and settings_overlay.cpp auto-merged; the VR settings menu, recenter hotkey, and stereo/first-person init calls are intact alongside upstream's InputBindings wiring. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
a138670b37
46 files changed
+2866
-364
No files matched your search
@@ -282,7 +282,7 @@ foreach ($required in @('ToolkitFingerprint','TranslationFingerprint','NativeToo
|
||||
|
||||
$manifest = [ordered]@{
|
||||
SchemaVersion = 2
|
||||
ProductVersion = '0.2.26'
|
||||
ProductVersion = '0.2.31'
|
||||
ExpectedGameId = $pins.GameId
|
||||
ExpectedDolSha256 = $pins.DolSha256
|
||||
ExpectedRelSha256 = $pins.RelSha256
|
||||
|
||||
@@ -117,12 +117,13 @@ function Get-MkwProjectPins([string]$ProjectFile) {
|
||||
}
|
||||
|
||||
function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Description,
|
||||
[string]$LogPrefix = 'MKWCBUILD', [string]$StepId = '') {
|
||||
[string]$LogPrefix = 'MKWCBUILD', [string]$StepId = '', [bool]$WaitForProcessTree = $true) {
|
||||
<#
|
||||
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.
|
||||
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
|
||||
hand off to an extracted child that PowerShell's call operator would not wait for. Callers that
|
||||
need to avoid waiting on unrelated descendants can opt into the call-operator path.
|
||||
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.
|
||||
#>
|
||||
@@ -132,9 +133,14 @@ function Invoke-Checked([string]$FilePath, [string[]]$Arguments, [string]$Descri
|
||||
if ($_.Contains('"')) { throw "A native build argument contains an unsupported quote: $_" }
|
||||
'"' + $_ + '"'
|
||||
})
|
||||
$process = Start-Process -FilePath $FilePath -ArgumentList $quotedArguments `
|
||||
-NoNewWindow -Wait -PassThru
|
||||
$exitCode = $process.ExitCode
|
||||
if ($WaitForProcessTree) {
|
||||
$process = Start-Process -FilePath $FilePath -ArgumentList $quotedArguments `
|
||||
-NoNewWindow -Wait -PassThru
|
||||
$exitCode = $process.ExitCode
|
||||
} else {
|
||||
& $FilePath @Arguments
|
||||
$exitCode = $LASTEXITCODE
|
||||
}
|
||||
$global:LASTEXITCODE = $exitCode
|
||||
if ($exitCode -ne 0) { throw "$Description failed with exit code $exitCode." }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# Build the real Windows runtime with translated, entirely synthetic PowerPC code.
|
||||
# No game dump, game symbol map, REL, mod download, or existing generated/ output is used.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$PortableToolsDirectory = 'Launcher/artifacts/portable-tools',
|
||||
[string]$DependencySourceDirectory = 'Launcher/artifacts/dependencies',
|
||||
[string]$StageDirectory = 'build/recomp-test',
|
||||
[ValidateRange(1, 64)] [int]$Parallel = 3
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version 3.0
|
||||
. (Join-Path $PSScriptRoot 'NativeBuildFlags.ps1')
|
||||
|
||||
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
function Full([string]$Path) {
|
||||
if ([IO.Path]::IsPathRooted($Path)) { return [IO.Path]::GetFullPath($Path) }
|
||||
return [IO.Path]::GetFullPath((Join-Path $repoRoot $Path))
|
||||
}
|
||||
$portableTools = Full $PortableToolsDirectory
|
||||
$dependencies = Full $DependencySourceDirectory
|
||||
$stage = Full $StageDirectory
|
||||
$dotnet = (Get-Command dotnet -CommandType Application).Source
|
||||
$cmake = Join-Path $portableTools 'CMake/bin/cmake.exe'
|
||||
$ninja = Join-Path $portableTools 'Ninja/ninja.exe'
|
||||
$compilerBin = Join-Path $portableTools 'llvm-mingw/bin'
|
||||
Assert-File $cmake 'Pinned CMake (run Prepare-PortableTools.ps1 first)'
|
||||
Assert-File $ninja 'Pinned Ninja'
|
||||
Assert-File (Join-Path $dependencies 'cppwinrt/winrt/base.h') 'Pinned dependencies (run Prepare-Dependencies.ps1 first)'
|
||||
|
||||
# Refuse reuse so a developer's game translation or an earlier build cannot make
|
||||
# the test pass. Keep the staging tree after the run for diagnostics.
|
||||
if (Test-Path -LiteralPath $stage) { throw "Test stage already exists; choose a fresh -StageDirectory: $stage" }
|
||||
[IO.Directory]::CreateDirectory($stage) | Out-Null
|
||||
Write-Host "Synthetic recompilation workspace: $stage"
|
||||
|
||||
# Copy current sources, including uncommitted edits, but no ignored build output.
|
||||
# An isolated workspace preserves the developer's real generated/ directory.
|
||||
$sourceFiles = & git -C $repoRoot -c core.quotepath=false ls-files --cached --others --exclude-standard -- runtime aurora-main
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Could not enumerate runtime and Aurora sources.' }
|
||||
foreach ($relative in $sourceFiles | Sort-Object -Unique) {
|
||||
$destination = Join-Path $stage $relative
|
||||
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($destination)) | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $repoRoot $relative) -Destination $destination
|
||||
}
|
||||
|
||||
# One synthetic text section: li r3,40; addi r3,r3,2; nop; blr.
|
||||
# Native HLE wrappers also call these eight guest symbols directly. Give each
|
||||
# its own generated blr function so the real product can link without game code.
|
||||
# Keep this list explicit: a new unresolved guest dependency must fail the test.
|
||||
[uint32]$entry = 0x80001000L
|
||||
[uint32[]]$guestCallbacks = @(
|
||||
0x8012B830L, 0x801A0620L, 0x801A1ED8L, 0x801A961CL,
|
||||
0x801AADE0L, 0x801D8D30L, 0x801D9E94L, 0x8055531CL
|
||||
)
|
||||
$textSize = [int]($guestCallbacks[-1] - $entry + 4)
|
||||
$dataOffset = 0x100 + $textSize
|
||||
$dol = [byte[]]::new($dataOffset + 4)
|
||||
function Write-BigEndian32([int]$Offset, [uint32]$Value) {
|
||||
$dol[$Offset] = [byte](($Value -shr 24) -band 255)
|
||||
$dol[$Offset + 1] = [byte](($Value -shr 16) -band 255)
|
||||
$dol[$Offset + 2] = [byte](($Value -shr 8) -band 255)
|
||||
$dol[$Offset + 3] = [byte]($Value -band 255)
|
||||
}
|
||||
Write-BigEndian32 0x00 0x100 # text[0] file offset
|
||||
Write-BigEndian32 0x48 $entry # text[0] guest address
|
||||
Write-BigEndian32 0x90 $textSize # text[0] length (unreachable gaps are zero)
|
||||
Write-BigEndian32 0x1C $dataOffset # data[0] file offset
|
||||
Write-BigEndian32 0x64 0x80600000L # data[0] guest address
|
||||
Write-BigEndian32 0xAC 4 # data[0] length
|
||||
Write-BigEndian32 0xD8 0x80601000L # BSS address
|
||||
Write-BigEndian32 0xDC 32 # BSS length
|
||||
Write-BigEndian32 0xE0 $entry # entry point
|
||||
Write-BigEndian32 0x100 0x38600028 # li r3,40
|
||||
Write-BigEndian32 0x104 0x38630002 # addi r3,r3,2
|
||||
Write-BigEndian32 0x108 0x60000000 # nop
|
||||
Write-BigEndian32 0x10C 0x4E800020 # blr
|
||||
foreach ($address in $guestCallbacks) {
|
||||
Write-BigEndian32 ([int](0x100 + $address - $entry)) 0x4E800020
|
||||
}
|
||||
Write-BigEndian32 $dataOffset 0x12345678
|
||||
[IO.File]::WriteAllBytes((Join-Path $stage 'synthetic.dol'), $dol)
|
||||
$entryPoints = (@($entry) + $guestCallbacks | ForEach-Object { '0x{0:X8}' -f $_ }) -join ', '
|
||||
$functionMap = (@($entry) + $guestCallbacks | ForEach-Object { '{0:X8} func_{0:X8}' -f $_ }) -join "`n"
|
||||
[IO.File]::WriteAllText((Join-Path $stage 'synthetic-functions.txt'), $functionMap)
|
||||
$manifest = Join-Path $stage 'recomp.yml'
|
||||
[IO.File]::WriteAllText($manifest, @"
|
||||
schema_version: 1
|
||||
workspace_root: .
|
||||
project:
|
||||
id: ci-synthetic-dol
|
||||
display_name: CI Synthetic DOL
|
||||
memory:
|
||||
base: 0x80000000
|
||||
size: 0x01800000
|
||||
sda_base: 0x80600000
|
||||
sda2_base: 0x80600000
|
||||
inputs:
|
||||
dol:
|
||||
path: synthetic.dol
|
||||
translation:
|
||||
entry_points: [$entryPoints]
|
||||
function_map:
|
||||
path: synthetic-functions.txt
|
||||
allow_unsupported_instructions: false
|
||||
runtime:
|
||||
native_abi_directories: []
|
||||
native_registration_root: runtime/src
|
||||
output:
|
||||
root: generated
|
||||
"@)
|
||||
|
||||
$translatorProject = Join-Path $repoRoot 'translator/src/Translator.Cli/Translator.Cli.csproj'
|
||||
Invoke-Checked $dotnet @('build', $translatorProject, '-c', 'Release', '--disable-build-servers') 'Building the translator'
|
||||
$translator = Join-Path $repoRoot 'translator/src/Translator.Cli/bin/Release/net8.0/Translator.Cli.dll'
|
||||
$metadata = Join-Path $stage 'generated/base_translation_output.json'
|
||||
Invoke-Checked $dotnet @($translator, 'translate-recursive', '0x80001000', '--project', $manifest,
|
||||
'--output-metadata', $metadata, '--threads', "$Parallel") `
|
||||
'Translating the synthetic DOL'
|
||||
# Function-map seeds can be skipped by discovery; do not accept a partial fixture.
|
||||
$translated = Get-Content -LiteralPath $metadata -Raw | ConvertFrom-Json
|
||||
foreach ($address in @($entry) + $guestCallbacks) {
|
||||
if ($address -notin $translated.functions.entryPoint) {
|
||||
throw ('Synthetic function 0x{0:X8} was not translated.' -f $address)
|
||||
}
|
||||
}
|
||||
Invoke-Checked $dotnet @($translator, 'generate-data-init', '--project', $manifest) 'Generating synthetic data and runtime configuration'
|
||||
Invoke-Checked $dotnet @($translator, 'emit-build-shards', '--project', $manifest) 'Emitting the production build graph'
|
||||
|
||||
$nativeBuild = Join-Path $stage 'native-build'
|
||||
$oldPath = $env:PATH
|
||||
try {
|
||||
$env:PATH = Get-MkwToolchainPath $portableTools
|
||||
$configure = Get-MkwNativeConfigureArguments -SourceDirectory (Join-Path $stage 'runtime') -BuildDirectory $nativeBuild `
|
||||
-Ninja $ninja -CCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang.exe') `
|
||||
-CxxCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang++.exe') `
|
||||
-ResourceCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-windres.exe') `
|
||||
-DependenciesDirectory $dependencies -AdditionalArguments @('-DMKW_BUILD_PRODUCTS=ON')
|
||||
Invoke-Checked $cmake $configure 'Configuring the production Windows runtime' `
|
||||
-WaitForProcessTree $false
|
||||
Invoke-Checked $cmake @('--build', $nativeBuild, '--target', 'WiiCompiled', '--parallel', "$Parallel") `
|
||||
'Compiling and linking the synthetic product with the full runtime' `
|
||||
-WaitForProcessTree $false
|
||||
Assert-File (Join-Path $nativeBuild 'WiiCompiled.exe') 'Linked synthetic product'
|
||||
} finally {
|
||||
$env:PATH = $oldPath
|
||||
}
|
||||
Write-Host 'Synthetic recompilation passed (translation, data generation, runtime compilation, and product link).'
|
||||
@@ -6,7 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>WiiCompiled.Setup.Common.Cli</RootNamespace>
|
||||
<AssemblyName>WiiCompiled.Setup.Common.Cli</AssemblyName>
|
||||
<Version>0.2.22</Version>
|
||||
<Version>0.2.31</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Packaging-time helper: resolves (downloading if needed) the nodtool binary bundled by build-appimage.sh and Build-Installer.ps1</Description>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>WiiCompiled.Setup.Common</RootNamespace>
|
||||
<AssemblyName>WiiCompiled.Setup.Common</AssemblyName>
|
||||
<Version>0.2.22</Version>
|
||||
<Version>0.2.31</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Shared nodtool/Retro-WFC-payload logic used by both the Windows and Linux installers</Description>
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace WiiCompiled.Setup.Linux;
|
||||
internal static class ProductInfo
|
||||
{
|
||||
public const string Name = "WiiCompiled";
|
||||
public const string Version = "0.2.22";
|
||||
public const string Version = "0.2.31";
|
||||
}
|
||||
|
||||
/// <summary>One installed product's record inside install-state.json.</summary>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>WiiCompiled.Setup.Linux</AssemblyName>
|
||||
<RootNamespace>WiiCompiled.Setup.Linux</RootNamespace>
|
||||
<Version>0.2.22</Version>
|
||||
<Version>0.2.31</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Command-line installer and launcher for WiiCompiled on Linux</Description>
|
||||
|
||||
@@ -22,6 +22,9 @@ internal sealed class InstallerEngine
|
||||
var existing = new Installation(installDirectory);
|
||||
|
||||
PortableInstallHealing.HealMovedInstall(existing, _reporter);
|
||||
// Capture this before publishing anything. An existing installation may be repaired or
|
||||
// updated by this invocation, but those operations must not recreate the user's shortcuts.
|
||||
var firstInstall = !existing.IsPresent;
|
||||
var previousState = existing.ReadInstallState();
|
||||
using var scratch = Directory.Exists(installDirectory)
|
||||
? InstallScratchSpace.CreateInsideInstall(installDirectory, _reporter)
|
||||
@@ -139,7 +142,7 @@ internal sealed class InstallerEngine
|
||||
updatedState.RetroRewindInstalled, candidateRuntimeAssetsFingerprint,
|
||||
remainingCancellation);
|
||||
Publish(staging, installDirectory, canonicalRetroRoot, updatedState,
|
||||
releaseEntries, remainingCancellation);
|
||||
releaseEntries, remainingCancellation, createShortcuts: firstInstall);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,7 +164,8 @@ internal sealed class InstallerEngine
|
||||
|
||||
await PublishToolkitAndReconcileProductsAsync(existing, staging, workspace, manifest,
|
||||
previousState, options, canonicalRetroRoot, retroCompileInputs,
|
||||
publishGameAssets: reusableGameAssets is null, cancellationToken);
|
||||
publishGameAssets: reusableGameAssets is null, createShortcuts: firstInstall,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +190,7 @@ internal sealed class InstallerEngine
|
||||
string stagedWorkspace, PayloadManifest manifest, InstallState? previousState,
|
||||
InstallOptions options, string? canonicalRetroRoot,
|
||||
RetroRewindCompileInputs? retroCompileInputs,
|
||||
bool publishGameAssets, CancellationToken cancellationToken)
|
||||
bool publishGameAssets, bool createShortcuts, CancellationToken cancellationToken)
|
||||
{
|
||||
var installDirectory = existing.Root;
|
||||
|
||||
@@ -214,7 +218,8 @@ internal sealed class InstallerEngine
|
||||
if (publishGameAssets) AddComponent(entries, staging, installDirectory, "GameAssets");
|
||||
|
||||
Publish(staging, installDirectory, canonicalRetroRoot, state,
|
||||
entries, cancellationToken, progressPercent: 8, completionPercent: 10);
|
||||
entries, cancellationToken, progressPercent: 8, completionPercent: 10,
|
||||
createShortcuts: createShortcuts);
|
||||
|
||||
_reporter.Progress(InstallStages.BuildBase,
|
||||
"Producing the installed products with the published toolkit...", 11);
|
||||
@@ -277,7 +282,7 @@ internal sealed class InstallerEngine
|
||||
private void Publish(string staging, string installDirectory,
|
||||
string? canonicalRetroRoot, InstallState state,
|
||||
List<InstallTransactionEntry> entries, CancellationToken cancellationToken,
|
||||
int progressPercent = 95, int completionPercent = 99)
|
||||
int progressPercent = 95, int completionPercent = 99, bool createShortcuts = false)
|
||||
{
|
||||
entries.Add(InstallTransactionEntry.Directory(Path.Combine(staging, "licenses"),
|
||||
Path.Combine(installDirectory, "licenses")));
|
||||
@@ -322,7 +327,8 @@ internal sealed class InstallerEngine
|
||||
{
|
||||
ShellIntegration.RegisterUninstaller(installDirectory, state.RetroRewindInstalled);
|
||||
}
|
||||
ShellIntegration.CreateShortcuts(installDirectory);
|
||||
if (createShortcuts)
|
||||
ShellIntegration.CreateShortcuts(installDirectory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ internal static class PlatformChecks
|
||||
internal static class ProductInfo
|
||||
{
|
||||
public const string Name = "WiiCompiled";
|
||||
public const string Version = "0.2.26";
|
||||
public const string Version = "0.2.31";
|
||||
|
||||
/// <summary>
|
||||
/// The setup executable is copied into the installation under this name. It is the launcher and
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<AssemblyName>WiiCompiled.Setup</AssemblyName>
|
||||
<RootNamespace>WiiCompiled.Setup.Windows</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Version>0.2.26</Version>
|
||||
<Version>0.2.31</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Command-line installer and launcher for WiiCompiled</Description>
|
||||
|
||||
@@ -91,17 +91,11 @@ download_verified() {
|
||||
mv "$tmp" "$dest"
|
||||
}
|
||||
|
||||
# lib/bundled is part of the check so a toolchain prepared before runtime-library bundling existed
|
||||
# is rebuilt rather than reused.
|
||||
if [[ -x "$toolchain_dir/bin/clang" && -x "$toolchain_dir/bin/ninja" && -x "$toolchain_dir/bin/cmake" && -d "$toolchain_dir/lib/bundled" ]]; then
|
||||
if [[ -x "$toolchain_dir/bin/clang" && -x "$toolchain_dir/bin/ninja" && -x "$toolchain_dir/bin/cmake" ]]; then
|
||||
echo "prepare-portable-tools.sh: reusing existing toolchain at $toolchain_dir"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for tool in patchelf ldd strip; do
|
||||
command -v "$tool" >/dev/null || { echo "prepare-portable-tools.sh: '$tool' is required (apt-get install patchelf binutils libc-bin)" >&2; exit 1; }
|
||||
done
|
||||
|
||||
work="$destination/.building-toolchain-$arch"
|
||||
rm -rf "$work"
|
||||
mkdir -p "$work/bin" "$work/lib/$target_triple" "$work/include/$target_triple/c++/v1"
|
||||
@@ -190,98 +184,6 @@ echo "prepare-portable-tools.sh: staging ninja $ninja_version..."
|
||||
unzip -oq "$ninja_archive" -d "$work/bin"
|
||||
chmod +x "$work/bin/ninja"
|
||||
|
||||
# --- runtime libraries the llvm.org binaries expect the host to provide ---
|
||||
#
|
||||
# The official LLVM release binaries are dynamically linked against a few host libraries beyond
|
||||
# glibc: lld needs libxml2, clang/lld need zlib and zstd, and whatever those pull in. A user's
|
||||
# distro is not guaranteed to have them under the same soname - libxml2 2.14 renamed
|
||||
# libxml2.so.2 to libxml2.so.16, so on current Arch/Fedora a fresh install died with
|
||||
# "ld.lld: error while loading shared libraries: libxml2.so.2". The smoke test below never caught
|
||||
# it because the build machine has every one of them. So: copy the transitive closure of non-core
|
||||
# libraries next to the binaries and point the binaries at that directory through an RPATH, the
|
||||
# way AppImages ship any dynamically linked tool. glibc and its companions stay on the host: they
|
||||
# exist everywhere, and a bundled copy could only ever be older than the host's own.
|
||||
bundled_lib_dir="$work/lib/bundled"
|
||||
mkdir -p "$bundled_lib_dir"
|
||||
|
||||
is_host_provided_lib() {
|
||||
case "$1" in
|
||||
linux-vdso*|ld-linux*|libc.so*|libm.so*|libdl.so*|libpthread.so*|librt.so*|libresolv.so*|libutil.so*|\
|
||||
libgcc_s.so*|libstdc++.so*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Prints "soname path" for every dynamically resolved dependency of $1 that the host must not be
|
||||
# relied on for. ldd already reports the transitive closure, so no recursion is needed.
|
||||
non_host_deps() {
|
||||
local line soname path
|
||||
ldd "$1" 2>/dev/null | while read -r line; do
|
||||
[[ "$line" == *"=>"* ]] || continue
|
||||
soname=${line%% =>*}; soname=${soname## }
|
||||
path=${line#*=> }; path=${path%% (*}
|
||||
is_host_provided_lib "$soname" && continue
|
||||
echo "$soname $path"
|
||||
done
|
||||
}
|
||||
|
||||
# A dependency the build machine itself lacks cannot be bundled; say so instead of shipping a
|
||||
# toolchain that is broken everywhere. (Checked separately because a failure inside the
|
||||
# process-substituted reader above could not stop the script.)
|
||||
assert_deps_resolved() {
|
||||
if ldd "$1" 2>/dev/null | grep -q "not found"; then
|
||||
echo "prepare-portable-tools.sh: $(basename "$1") has dependencies missing on this machine:" >&2
|
||||
ldd "$1" | grep "not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Copies every non-core library $1 needs into lib/bundled, then does the same for each copy, so
|
||||
# a library's own dependencies (libxml2 -> ICU, ...) are bundled even if ldd's listing for the
|
||||
# executable did not already spell out the whole closure. The "already bundled" check ends the
|
||||
# recursion.
|
||||
bundle_deps_of() {
|
||||
local file=$1 soname path
|
||||
assert_deps_resolved "$file"
|
||||
while read -r soname path; do
|
||||
[[ -n "$soname" ]] || continue
|
||||
[[ -e "$bundled_lib_dir/$soname" ]] && continue
|
||||
cp -L "$path" "$bundled_lib_dir/$soname"
|
||||
chmod u+w "$bundled_lib_dir/$soname"
|
||||
# A bundled library must find its own dependencies beside itself: RUNPATH is not
|
||||
# inherited from the executable that loaded it.
|
||||
patchelf --set-rpath '$ORIGIN' "$bundled_lib_dir/$soname"
|
||||
bundle_deps_of "$bundled_lib_dir/$soname"
|
||||
done < <(non_host_deps "$file")
|
||||
}
|
||||
|
||||
bundle_runtime_libs() {
|
||||
local file=$1 existing
|
||||
bundle_deps_of "$file"
|
||||
# Prepend, keeping whatever RPATH the release build set (typically $ORIGIN/../lib).
|
||||
existing=$(patchelf --print-rpath "$file" 2>/dev/null || true)
|
||||
patchelf --set-rpath "\$ORIGIN/../lib/bundled${existing:+:$existing}" "$file"
|
||||
}
|
||||
|
||||
echo "prepare-portable-tools.sh: bundling the host runtime libraries the toolchain binaries depend on..."
|
||||
for binary in "$work/bin/clang-22" "$work/bin/lld" "$work/bin/llvm-ar" "$work/bin/cmake"; do
|
||||
bundle_runtime_libs "$binary"
|
||||
done
|
||||
echo "prepare-portable-tools.sh: bundled $(ls "$bundled_lib_dir" | wc -l) librar(y/ies): $(ls "$bundled_lib_dir" | tr '\n' ' ')"
|
||||
|
||||
# Fail here, on the build machine, if any non-core dependency still resolves to a host path:
|
||||
# that is exactly the file a user's machine may lack.
|
||||
for binary in "$work/bin/clang-22" "$work/bin/lld" "$work/bin/llvm-ar" "$work/bin/cmake" "$bundled_lib_dir"/*; do
|
||||
[[ -f "$binary" ]] || continue
|
||||
while read -r soname path; do
|
||||
[[ -n "$soname" ]] || continue
|
||||
if [[ "$path" != "$bundled_lib_dir/"* ]]; then
|
||||
echo "prepare-portable-tools.sh: $(basename "$binary") still resolves $soname from the host ($path); bundling failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
done < <(non_host_deps "$binary")
|
||||
done
|
||||
|
||||
cat > "$work/LICENSE.txt" <<EOF
|
||||
Portable build tools bundled by WiiCompiled
|
||||
|
||||
@@ -297,17 +199,12 @@ CMake $cmake_version
|
||||
Ninja $ninja_version
|
||||
https://github.com/ninja-build/ninja
|
||||
Apache License 2.0
|
||||
|
||||
Host runtime libraries bundled for the binaries above (lib/bundled), copied unmodified apart from
|
||||
their RPATH from the build machine's distribution packages; each is under its own license
|
||||
(libxml2: MIT, zlib: zlib, zstd: BSD 3-Clause, ICU: Unicode License, ...):
|
||||
$(ls "$bundled_lib_dir" | sed 's/^/ /')
|
||||
EOF
|
||||
|
||||
echo "prepare-portable-tools.sh: smoke-testing the toolchain..."
|
||||
smoke_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$smoke_dir"' EXIT
|
||||
cat > "$smoke_dir/t.cpp" <<'EOF'
|
||||
echo "prepare-portable-tools.sh: testing the toolchain..."
|
||||
test_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
cat > "$test_dir/t.cpp" <<'EOF'
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
int main() {
|
||||
@@ -317,23 +214,23 @@ int main() {
|
||||
return sum == 6 ? 0 : 1;
|
||||
}
|
||||
EOF
|
||||
"$work/bin/clang++" -std=c++20 -fuse-ld=lld "$smoke_dir/t.cpp" -o "$smoke_dir/t"
|
||||
"$smoke_dir/t"
|
||||
"$work/bin/clang++" -std=c++20 -fuse-ld=lld "$test_dir/t.cpp" -o "$test_dir/t"
|
||||
"$test_dir/t"
|
||||
|
||||
# Also exercised together through CMake+Ninja, exactly how local-build.sh drives them - a plain
|
||||
# clang++ invocation above would not catch a broken CMAKE_ROOT (Modules/Templates) or a Ninja that
|
||||
# can't find the compiler.
|
||||
cat > "$smoke_dir/CMakeLists.txt" <<'EOF'
|
||||
cat > "$test_dir/CMakeLists.txt" <<'EOF'
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(smoke CXX)
|
||||
add_executable(smoke t.cpp)
|
||||
project(test CXX)
|
||||
add_executable(test t.cpp)
|
||||
EOF
|
||||
"$work/bin/cmake" -S "$smoke_dir" -B "$smoke_dir/build" -G Ninja \
|
||||
"$work/bin/cmake" -S "$test_dir" -B "$test_dir/build" -G Ninja \
|
||||
-DCMAKE_MAKE_PROGRAM="$work/bin/ninja" -DCMAKE_CXX_COMPILER="$work/bin/clang++" >/dev/null
|
||||
"$work/bin/cmake" --build "$smoke_dir/build" >/dev/null
|
||||
"$smoke_dir/build/smoke"
|
||||
"$work/bin/cmake" --build "$test_dir/build" >/dev/null
|
||||
"$test_dir/build/test"
|
||||
|
||||
rm -rf "$smoke_dir"
|
||||
rm -rf "$test_dir"
|
||||
trap - EXIT
|
||||
|
||||
mv "$work" "$toolchain_dir"
|
||||
|
||||
Reference in new issue
Block a user