From 21564e0931358935dd0d4abe42ef45a7041161d0 Mon Sep 17 00:00:00 2001 From: martipops Date: Thu, 8 Oct 2026 01:55:50 -0400 Subject: [PATCH 1/3] feat(macos): run the subcommand setup on macOS Lets the setup CLI that the Linux AppImage uses also run on macOS, so Wheel Wizard can drive it the same way. - Download the macOS nodtool release asset instead of the Linux one. - Build through local-build-macos.command, which takes the same flags. - Record the binary inside the published .app as the product executable. - Skip freedesktop .desktop entries, which macOS has no use for. - Write local-build.json from the macOS build script, matching local-build.sh, so Wheel Wizard can tell whether Retro Rewind is current. - Ignore native-build-macos-/, which the old pattern did not match. --- .gitignore | 2 +- .../NodToolProvider.cs | 9 +++++++++ .../WiiCompiled.Setup.Linux/BuildRunner.cs | 9 ++++++--- .../WiiCompiled.Setup.Linux/DesktopEntry.cs | 2 ++ Launcher/WiiCompiled.Setup.Linux/Program.cs | 4 +++- .../WiiCompiled.Setup.Linux.csproj | 2 +- Launcher/local-build-macos.command | 20 +++++++++++++++++-- 7 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 0409068c0..e9356af1d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ Code.pul /build/ /build-*/ /native-build/ -/native-build-macos/ +/native-build-macos*/ /local-products/ /dist/ /out/ diff --git a/Launcher/WiiCompiled.Setup.Common/NodToolProvider.cs b/Launcher/WiiCompiled.Setup.Common/NodToolProvider.cs index f58769565..cdee6e3dc 100644 --- a/Launcher/WiiCompiled.Setup.Common/NodToolProvider.cs +++ b/Launcher/WiiCompiled.Setup.Common/NodToolProvider.cs @@ -56,6 +56,15 @@ private static string AssetName() var other => throw new PlatformNotSupportedException($"No prebuilt nodtool release for Windows {other}"), }; } + if (OperatingSystem.IsMacOS()) + { + return RuntimeInformation.OSArchitecture switch + { + Architecture.X64 => "nodtool-macos-x86_64", + Architecture.Arm64 => "nodtool-macos-arm64", + var other => throw new PlatformNotSupportedException($"No prebuilt nodtool release for macOS {other}"), + }; + } return RuntimeInformation.OSArchitecture switch { Architecture.X64 => "nodtool-linux-x86_64", diff --git a/Launcher/WiiCompiled.Setup.Linux/BuildRunner.cs b/Launcher/WiiCompiled.Setup.Linux/BuildRunner.cs index 284310a12..8ea1a0e1e 100644 --- a/Launcher/WiiCompiled.Setup.Linux/BuildRunner.cs +++ b/Launcher/WiiCompiled.Setup.Linux/BuildRunner.cs @@ -17,8 +17,11 @@ public static async Task RunAsync( IInstallReporter reporter, CancellationToken cancellationToken) { - var script = Path.Combine(workspace, "Launcher", "local-build.sh"); - if (!File.Exists(script)) throw new FileNotFoundException("local-build.sh is missing", script); + // macOS has its own build script (clang from Xcode, .app bundles) that takes the same flags, + // minus the Linux-only toolchain overrides below, which it rejects as unknown options. + var script = Path.Combine(workspace, "Launcher", + OperatingSystem.IsMacOS() ? "local-build-macos.command" : "local-build.sh"); + if (!File.Exists(script)) throw new FileNotFoundException($"{Path.GetFileName(script)} is missing", script); var startInfo = new ProcessStartInfo("bash") { @@ -105,7 +108,7 @@ public static async Task RunAsync( if (process.ExitCode != 0) { - throw new InvalidOperationException($"local-build.sh failed (exit {process.ExitCode}). See diagnostics above."); + throw new InvalidOperationException($"{Path.GetFileName(script)} failed (exit {process.ExitCode}). See diagnostics above."); } } diff --git a/Launcher/WiiCompiled.Setup.Linux/DesktopEntry.cs b/Launcher/WiiCompiled.Setup.Linux/DesktopEntry.cs index 6ae642638..990bacc5a 100644 --- a/Launcher/WiiCompiled.Setup.Linux/DesktopEntry.cs +++ b/Launcher/WiiCompiled.Setup.Linux/DesktopEntry.cs @@ -15,6 +15,8 @@ private static string PathFor(string profile) => public static void Create(string profile, string displayName, string exePath) { + // macOS has no freedesktop menu; the installed .app bundle is the launchable entry itself. + if (OperatingSystem.IsMacOS()) return; // exePath is the installed native runtime binary itself (e.g. // .../Install/Base/WiiCompiled) - each profile already gets its own .desktop file here, // so there is no need to route through the setup tool's own launch-base/launch-retro diff --git a/Launcher/WiiCompiled.Setup.Linux/Program.cs b/Launcher/WiiCompiled.Setup.Linux/Program.cs index f3af18646..b5c11c727 100644 --- a/Launcher/WiiCompiled.Setup.Linux/Program.cs +++ b/Launcher/WiiCompiled.Setup.Linux/Program.cs @@ -196,7 +196,9 @@ await BuildRunner.RunAsync( foreach (var p in profiles) { var dir = p == "base" ? (baseInstallDir ?? installDir) : installDir; - var exeName = p == "base" ? "WiiCompiled" : "RetroRewind"; + var product = p == "base" ? "WiiCompiled" : "RetroRewind"; + // macOS builds a .app bundle (publish-app.command); launch/check-products want the binary inside it. + var exeName = OperatingSystem.IsMacOS() ? $"{product}.app/Contents/MacOS/{product}" : product; var displayName = p == "base" ? "WiiCompiled (base game)" : "WiiCompiled (Retro Rewind)"; state.Products.RemoveAll(r => r.Profile == p); state.Products.Add(new ProductInstallRecord diff --git a/Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj b/Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj index 2737c4c73..7f08cc5d5 100644 --- a/Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj +++ b/Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj @@ -8,7 +8,7 @@ WiiCompiled.Setup.Linux patchzy WiiCompiled - Command-line installer and launcher for WiiCompiled on Linux + Command-line installer and launcher for WiiCompiled on Linux and macOS embedded en true diff --git a/Launcher/local-build-macos.command b/Launcher/local-build-macos.command index 4c238fd2d..fde02a7ba 100755 --- a/Launcher/local-build-macos.command +++ b/Launcher/local-build-macos.command @@ -139,6 +139,22 @@ step configure-native 'Configuring the native toolchain' "$cmake_bin" -S "$workspace/runtime" -B "$native_build" -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_MAKE_PROGRAM="$ninja_bin" -DCMAKE_OSX_ARCHITECTURES="$macos_arch" -DCMAKE_OSX_DEPLOYMENT_TARGET="$macos_deployment_target" -DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs" -DAURORA_SDL3_PROVIDER=vendor targets=(); [[ "$profile" != retro-rewind ]] && targets+=(WiiCompiled); [[ "$profile" != base ]] && targets+=(RetroRewind) step compile "Compiling ${targets[*]} locally"; "$cmake_bin" --build "$native_build" --target "${targets[@]}" --parallel "$global_jobs" -if [[ "$profile" != retro-rewind ]]; then "$script_dir/macos/publish-app.command" --build-dir "$native_build" --product WiiCompiled --output-dir "${base_output_dir:-$output_dir}" --architecture "$macos_arch" --minimum-system-version "$macos_deployment_target"; fi -if (( builds_retro )); then "$script_dir/macos/publish-app.command" --build-dir "$native_build" --product RetroRewind --output-dir "$output_dir" --architecture "$macos_arch" --minimum-system-version "$macos_deployment_target"; fi +# Same provenance record local-build.sh writes; Wheel Wizard reads CodePulSha256 to tell whether Retro Rewind is current. +write_build_record() { + local code_pul_sha=null + [[ "$2" == retro-rewind ]] && code_pul_sha="\"$(sha256 "$retro_root/Binaries/Code.pul")\"" + cat > "$1/local-build.json" < Date: Thu, 8 Oct 2026 02:33:49 -0400 Subject: [PATCH 2/3] feat(macos): publish a self-extracting setup for frontends Wheel Wizard drives the setup CLI on Linux by downloading the AppImage from the release. Publish the macOS equivalent, WiiCompiled-Setup-macos.run, so it can do the same without the user installing Setup.pkg first. The .run is the Setup.pkg Resources plus the setup CLI for both architectures and a wiicompiled-setup entry point, behind a small shell header. --version answers without unpacking; anything else unpacks once per version into Application Support and runs the entry point from there. The entry point is the macOS counterpart of the AppImage's AppRun: apps launched from Finder only get /usr/bin:/bin:/usr/sbin:/sbin on PATH, so it passes every bundled tool to the CLI, syncs the packaged workspace, and checks for the Xcode Command Line Tools before an install. - build-setup-pkg.command takes --setup-arm64/--setup-x86_64 and --run-output. Setup.pkg itself is unchanged in content. - CI publishes the setup CLI self-contained, verifies the .run on both architectures and attaches it to the release. - Move setup.command's workspace sync into sync-workspace.command so both entry points share it and the same BuildWorkspace. --- .github/workflows/package.yml | 34 ++++++++++++++++++++++- Launcher/macos/build-setup-pkg.command | 35 ++++++++++++++++++++---- Launcher/macos/setup-run-header.sh | 26 ++++++++++++++++++ Launcher/macos/setup.command | 27 +----------------- Launcher/macos/sync-workspace.command | 35 ++++++++++++++++++++++++ Launcher/macos/wiicompiled-setup.command | 30 ++++++++++++++++++++ 6 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 Launcher/macos/setup-run-header.sh create mode 100755 Launcher/macos/sync-workspace.command create mode 100755 Launcher/macos/wiicompiled-setup.command diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 873f47882..6d93917fc 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -139,6 +139,18 @@ jobs: -p:PublishSingleFile=true \ -o Launcher/artifacts/macos/translator-x86_64 + - name: Publish self-contained setup CLI + shell: bash + run: | + dotnet publish Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj \ + -c Release -r osx-arm64 --self-contained true \ + -p:PublishSingleFile=true \ + -o Launcher/artifacts/macos/setup-arm64 + dotnet publish Launcher/WiiCompiled.Setup.Linux/WiiCompiled.Setup.Linux.csproj \ + -c Release -r osx-x64 --self-contained true \ + -p:PublishSingleFile=true \ + -o Launcher/artifacts/macos/setup-x86_64 + - name: Download pinned universal Ninja shell: bash run: | @@ -178,10 +190,13 @@ jobs: --nodtool-x86_64 Launcher/artifacts/macos/nodtool-x86_64 \ --translator-arm64 Launcher/artifacts/macos/translator-arm64/Translator.Cli \ --translator-x86_64 Launcher/artifacts/macos/translator-x86_64/Translator.Cli \ + --setup-arm64 Launcher/artifacts/macos/setup-arm64/WiiCompiled.Setup.Linux \ + --setup-x86_64 Launcher/artifacts/macos/setup-x86_64/WiiCompiled.Setup.Linux \ --cmake-root Launcher/artifacts/macos/cmake \ --ninja-arm64 Launcher/artifacts/macos/ninja/ninja \ --ninja-x86_64 Launcher/artifacts/macos/ninja/ninja \ --output Launcher/dist/WiiCompiled-Setup.pkg \ + --run-output Launcher/dist/WiiCompiled-Setup-macos.run \ ${package_version:+--version "$package_version"} - name: Verify package layout and architecture-specific tools @@ -204,6 +219,16 @@ jobs: lipo "$resources/tools/cmake/bin/cmake" -verify_arch arm64 x86_64 bash "$resources/setup.command" --help /usr/bin/arch -x86_64 /bin/bash "$resources/setup.command" --help + # The .run must answer --version without unpacking, then unpack once and run its entry point + # with each architecture's setup CLI. + run=Launcher/dist/WiiCompiled-Setup-macos.run + version=$("$run" --version) + HOME="$RUNNER_TEMP/run-home" "$run" check-products + unpacked="$RUNNER_TEMP/run-home/Library/Application Support/WiiCompiled/Setup/$version" + for arch in arm64 x86_64; do + lipo "$unpacked/tools/$arch/wiicompiled-setup" -verify_arch "$arch" + done + HOME="$RUNNER_TEMP/run-home" /usr/bin/arch -x86_64 /bin/bash "$run" check-products - uses: actions/upload-artifact@v7 with: @@ -212,6 +237,13 @@ jobs: if-no-files-found: error archive: false + - uses: actions/upload-artifact@v7 + with: + name: WiiCompiled-Setup-macos-run + path: Launcher/dist/WiiCompiled-Setup-macos.run + if-no-files-found: error + archive: false + # Publishes the packaged installers as a GitHub Release whenever a v* tag is pushed. Wheel Wizard # discovers updates from these releases, so the contract it relies on is enforced here: a full # (non-prerelease) release whose tag is v, carrying the expected platform assets, @@ -263,7 +295,7 @@ jobs: set -euo pipefail ls -lR artifacts assets=() - for name in WiiCompiled-Setup.exe WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage WiiCompiled-Setup.pkg; do + for name in WiiCompiled-Setup.exe WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage WiiCompiled-Setup.pkg WiiCompiled-Setup-macos.run; do found="$(find artifacts -type f -name "$name" | head -n 1)" [ -n "$found" ] && [ -s "$found" ] || { echo "::error::missing release asset $name"; exit 1; } assets+=("$found") diff --git a/Launcher/macos/build-setup-pkg.command b/Launcher/macos/build-setup-pkg.command index b5c0e1e31..b4632f103 100755 --- a/Launcher/macos/build-setup-pkg.command +++ b/Launcher/macos/build-setup-pkg.command @@ -9,7 +9,7 @@ fail() { printf 'build-setup-pkg.command: error: %s\n' "$*" >&2; exit 1; } copy_clean() { DITTONORSRC=1 ditto --norsrc --noqtn "$@"; } usage() { cat <<'EOF' -Usage: build-setup-pkg.command --nodtool-arm64 PATH --nodtool-x86_64 PATH --translator-arm64 PATH --translator-x86_64 PATH --cmake-root DIR --ninja-arm64 PATH --ninja-x86_64 PATH --output PKG [options] +Usage: build-setup-pkg.command --nodtool-arm64 PATH --nodtool-x86_64 PATH --translator-arm64 PATH --translator-x86_64 PATH --setup-arm64 PATH --setup-x86_64 PATH --cmake-root DIR --ninja-arm64 PATH --ninja-x86_64 PATH --output PKG [options] Creates a game-code-free WiiCompiled Setup.pkg. The supplied tools must be maintainer-verified, redistributable macOS artifacts for both arm64 and @@ -22,11 +22,12 @@ Developer ID Installer certificate. --workspace DIR Repository root (default: script's grandparent) --version VERSION Bundle/package version (default: 0.1.0) --installer-identity NAME Developer ID Installer identity for productbuild + --run-output FILE Also write the same bundle as a self-extracting .run for frontends EOF } script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -workspace=$(cd "$script_dir/../.." && pwd); nodtool_arm64=""; nodtool_x86_64=""; translator_arm64=""; translator_x86_64=""; cmake_root=""; ninja_arm64=""; ninja_x86_64=""; output=""; version=0.1.0; identity="" +workspace=$(cd "$script_dir/../.." && pwd); nodtool_arm64=""; nodtool_x86_64=""; translator_arm64=""; translator_x86_64=""; setup_arm64=""; setup_x86_64=""; cmake_root=""; ninja_arm64=""; ninja_x86_64=""; output=""; version=0.1.0; identity=""; run_output="" while (($#)); do case "$1" in --workspace) workspace=${2:-}; shift 2 ;; @@ -34,12 +35,15 @@ while (($#)); do --nodtool-x86_64) nodtool_x86_64=${2:-}; shift 2 ;; --translator-arm64) translator_arm64=${2:-}; shift 2 ;; --translator-x86_64) translator_x86_64=${2:-}; shift 2 ;; + --setup-arm64) setup_arm64=${2:-}; shift 2 ;; + --setup-x86_64) setup_x86_64=${2:-}; shift 2 ;; --cmake-root) cmake_root=${2:-}; shift 2 ;; --ninja-arm64) ninja_arm64=${2:-}; shift 2 ;; --ninja-x86_64) ninja_x86_64=${2:-}; shift 2 ;; --output) output=${2:-}; shift 2 ;; --version) version=${2:-}; shift 2 ;; --installer-identity) identity=${2:-}; shift 2 ;; + --run-output) run_output=${2:-}; shift 2 ;; -h|--help) usage; exit 0 ;; *) fail "unknown option: $1" ;; esac @@ -58,7 +62,7 @@ fi IFS=. read -r version_major version_minor version_patch <<< "$version" short_version="$version_major.${version_minor:-0}.${version_patch:-0}" for tool in pkgbuild productbuild ditto codesign lipo; do command -v "$tool" >/dev/null || fail "required macOS tool unavailable: $tool"; done -for tool_path in "$nodtool_arm64" "$nodtool_x86_64" "$translator_arm64" "$translator_x86_64" "$ninja_arm64" "$ninja_x86_64"; do [[ -x "$tool_path" ]] || fail 'each architecture-specific tool must name an executable'; done +for tool_path in "$nodtool_arm64" "$nodtool_x86_64" "$translator_arm64" "$translator_x86_64" "$setup_arm64" "$setup_x86_64" "$ninja_arm64" "$ninja_x86_64"; do [[ -x "$tool_path" ]] || fail 'each architecture-specific tool must name an executable'; done [[ -x "$cmake_root/bin/cmake" ]] || fail '--cmake-root must contain bin/cmake' require_arch() { local artifact=$1 arch=$2 label=$3 @@ -66,6 +70,7 @@ require_arch() { } require_arch "$nodtool_arm64" arm64 '--nodtool-arm64'; require_arch "$nodtool_x86_64" x86_64 '--nodtool-x86_64' require_arch "$translator_arm64" arm64 '--translator-arm64'; require_arch "$translator_x86_64" x86_64 '--translator-x86_64' +require_arch "$setup_arm64" arm64 '--setup-arm64'; require_arch "$setup_x86_64" x86_64 '--setup-x86_64' require_arch "$ninja_arm64" arm64 '--ninja-arm64'; require_arch "$ninja_x86_64" x86_64 '--ninja-x86_64' lipo "$cmake_root/bin/cmake" -verify_arch arm64 x86_64 >/dev/null 2>&1 || fail '--cmake-root/bin/cmake must be universal2' @@ -90,12 +95,13 @@ run_for_arch() { } for arch in arm64 x86_64; do if [[ "$arch" == arm64 ]]; then - nodtool=$nodtool_arm64; translator=$translator_arm64; ninja=$ninja_arm64 + nodtool=$nodtool_arm64; translator=$translator_arm64; setup=$setup_arm64; ninja=$ninja_arm64 else - nodtool=$nodtool_x86_64; translator=$translator_x86_64; ninja=$ninja_x86_64 + nodtool=$nodtool_x86_64; translator=$translator_x86_64; setup=$setup_x86_64; ninja=$ninja_x86_64 fi run_for_arch "$arch" "--nodtool-$arch" "$nodtool" --version run_for_arch "$arch" "--translator-$arch" "$translator" --help + run_for_arch "$arch" "--setup-$arch" "$setup" --version run_for_arch "$arch" "--ninja-$arch" "$ninja" --version done run_for_arch "$host_arch" '--cmake-root/bin/cmake' "$cmake_root/bin/cmake" --version @@ -142,6 +148,7 @@ APPLESCRIPT EOF chmod +x "$app/Contents/MacOS/WiiCompiledSetup" copy_clean "$script_dir/setup.command" "$resources/setup.command"; chmod +x "$resources/setup.command" +copy_clean "$script_dir/sync-workspace.command" "$resources/sync-workspace.command"; chmod +x "$resources/sync-workspace.command" # Copy only the build inputs. This deliberately avoids a maintainer's ignored # output directories, local disc extraction, and developer-only packaging. mkdir -p "$resources/workspace" @@ -168,6 +175,24 @@ copy_clean "$ninja_x86_64" "$resources/tools/x86_64/ninja"; chmod +x "$resources copy_clean "$workspace/LICENSE" "$resources/LICENSE" copy_clean "$workspace/THIRD-PARTY-NOTICES.md" "$resources/THIRD-PARTY-NOTICES.md" codesign --force --deep --sign - "$app" +if [[ -n "$run_output" ]]; then + # The same signed Resources plus the setup CLI and its wiicompiled-setup entry point, behind + # setup-run-header.sh, so Wheel Wizard can download and run it without installing the pkg. Only + # the .run carries the CLI; the pkg's own setup.command never uses it. + run_output=$(cd "$(dirname "$run_output")" && pwd)/$(basename "$run_output") + run_extra="$stage/run-extra" + mkdir -p "$run_extra/tools/arm64" "$run_extra/tools/x86_64" + copy_clean "$script_dir/wiicompiled-setup.command" "$run_extra/wiicompiled-setup" + copy_clean "$setup_arm64" "$run_extra/tools/arm64/wiicompiled-setup" + copy_clean "$setup_x86_64" "$run_extra/tools/x86_64/wiicompiled-setup" + chmod +x "$run_extra/wiicompiled-setup" "$run_extra/tools/"*/wiicompiled-setup + codesign --force --sign - "$run_extra/tools/"*/wiicompiled-setup + sed "s/@VERSION@/$version/" "$script_dir/setup-run-header.sh" > "$stage/setup.run" + COPYFILE_DISABLE=1 /usr/bin/tar -czf - -C "$resources" . -C "$run_extra" . >> "$stage/setup.run" + chmod +x "$stage/setup.run" + ditto "$stage/setup.run" "$run_output" + printf 'Created self-extracting setup: %s\n' "$run_output" +fi pkg="$stage/WiiCompiled-Setup-unsigned.pkg" DITTONORSRC=1 COPYFILE_DISABLE=1 pkgbuild --root "$stage/root" --identifier org.wiicompiled.setup --version "$version" --install-location / "$pkg" if [[ -n "$identity" ]]; then productbuild --sign "$identity" --package "$pkg" "$output"; else ditto "$pkg" "$output"; fi diff --git a/Launcher/macos/setup-run-header.sh b/Launcher/macos/setup-run-header.sh new file mode 100644 index 000000000..7d6d80ebc --- /dev/null +++ b/Launcher/macos/setup-run-header.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Header of WiiCompiled-Setup-macos.run, the self-extracting setup that frontends such as Wheel Wizard +# download like the Linux AppImage. build-setup-pkg.command fills in the version and appends the +# Setup.app Resources as a gzipped tar after the marker line. The bundle is unpacked once per version +# into Application Support, then its wiicompiled-setup entry point runs from there. +set -euo pipefail +version='@VERSION@' +if [[ $# -eq 1 && $1 == --version ]]; then printf '%s\n' "$version"; exit 0; fi + +setup_root="$HOME/Library/Application Support/WiiCompiled/Setup" +bundle="$setup_root/$version" +if [[ ! -x "$bundle/wiicompiled-setup" ]]; then + mkdir -p "$setup_root" + staging=$(mktemp -d "$setup_root/.unpack.XXXXXX") + trap 'rm -rf "$staging"' EXIT + payload_line=$(awk '/^__WIICOMPILED_PAYLOAD__$/ { print NR + 1; exit }' "$0") + tail -n +"$payload_line" "$0" | /usr/bin/tar -xzf - -C "$staging" + # Not safe against two first runs racing; frontends already run setup operations one at a time. + rm -rf "$bundle" + mv "$staging" "$bundle" + trap - EXIT + # Older versions are never run again once this one is unpacked. + find "$setup_root" -mindepth 1 -maxdepth 1 ! -name "$version" ! -name '.unpack.*' -exec rm -rf {} + +fi +exec "$bundle/wiicompiled-setup" "$@" +__WIICOMPILED_PAYLOAD__ diff --git a/Launcher/macos/setup.command b/Launcher/macos/setup.command index b3a31d02d..e3e8d467f 100755 --- a/Launcher/macos/setup.command +++ b/Launcher/macos/setup.command @@ -70,32 +70,7 @@ for tool in "$nodtool" "$translator" "$cmake_bin" "$ninja_bin"; do done mkdir -p "$support_root" "$products" -source_bundle_version="$workspace_source/.bundle-version" -workspace_bundle_version="$workspace/.bundle-version" -needs_workspace_refresh=0 -if [[ ! -f "$workspace/projects/mkwii/recomp.yml" ]]; then - needs_workspace_refresh=1 -elif [[ -f "$source_bundle_version" ]] && [[ ! -f "$workspace_bundle_version" || "$(<"$source_bundle_version")" != "$(<"$workspace_bundle_version")" ]]; then - needs_workspace_refresh=1 -fi - -if (( needs_workspace_refresh )); then - printf 'Preparing the local build workspace...\n' - if [[ ! -d "$workspace" ]]; then - /usr/bin/ditto "$workspace_source" "$workspace" - else - # Refresh only packaged source inputs. Assets and the staged Retro - # Rewind package belong to the user and stay in place. - for source in aurora-main projects runtime translator Launcher; do - rm -rf "$workspace/$source" - /usr/bin/ditto "$workspace_source/$source" "$workspace/$source" - done - /usr/bin/ditto "$source_bundle_version" "$workspace_bundle_version" - # A dependency provider can be cached in this directory, so make the - # refreshed sources configure from a clean native build tree. - rm -rf "$workspace/native-build-macos-arm64" "$workspace/native-build-macos-x86_64" - fi -fi +"$resources/sync-workspace.command" "$workspace_source" "$workspace" profile=base build_args=(--workspace "$workspace" --game "$game" --nodtool "$nodtool" --output-dir "$products") diff --git a/Launcher/macos/sync-workspace.command b/Launcher/macos/sync-workspace.command new file mode 100755 index 000000000..c377b48ed --- /dev/null +++ b/Launcher/macos/sync-workspace.command @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Copies the packaged build workspace into a writable per-user location, or refreshes its source +# inputs when the package version changed. Shared by setup.command (Setup.pkg) and the +# wiicompiled-setup entry point (WiiCompiled-Setup-macos.run). +set -euo pipefail + +[[ $# -eq 2 ]] || { printf 'Usage: sync-workspace.command PACKAGED-WORKSPACE USER-WORKSPACE\n' >&2; exit 1; } +workspace_source=$1; workspace=$2 + +source_bundle_version="$workspace_source/.bundle-version" +workspace_bundle_version="$workspace/.bundle-version" +needs_workspace_refresh=0 +if [[ ! -f "$workspace/projects/mkwii/recomp.yml" ]]; then + needs_workspace_refresh=1 +elif [[ -f "$source_bundle_version" ]] && [[ ! -f "$workspace_bundle_version" || "$(<"$source_bundle_version")" != "$(<"$workspace_bundle_version")" ]]; then + needs_workspace_refresh=1 +fi + +if (( needs_workspace_refresh )); then + printf 'Preparing the local build workspace...\n' + if [[ ! -d "$workspace" ]]; then + /usr/bin/ditto "$workspace_source" "$workspace" + else + # Refresh only packaged source inputs. Assets and the staged Retro + # Rewind package belong to the user and stay in place. + for source in aurora-main projects runtime translator Launcher; do + rm -rf "$workspace/$source" + /usr/bin/ditto "$workspace_source/$source" "$workspace/$source" + done + /usr/bin/ditto "$source_bundle_version" "$workspace_bundle_version" + # A dependency provider can be cached in this directory, so make the + # refreshed sources configure from a clean native build tree. + rm -rf "$workspace/native-build-macos-arm64" "$workspace/native-build-macos-x86_64" + fi +fi diff --git a/Launcher/macos/wiicompiled-setup.command b/Launcher/macos/wiicompiled-setup.command new file mode 100755 index 000000000..3f6bb0303 --- /dev/null +++ b/Launcher/macos/wiicompiled-setup.command @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Command-line entry point bundled in WiiCompiled-Setup-macos.run as wiicompiled-setup, for +# frontends such as Wheel Wizard. The macOS counterpart of the AppImage's AppRun: it runs the +# subcommand setup with the bundled tools, so nothing has to be on PATH. An app launched from +# Finder or the Dock only gets /usr/bin:/bin:/usr/sbin:/sbin. +set -euo pipefail + +resources=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +arch=$(uname -m) +case "$arch" in arm64|x86_64) ;; *) printf 'wiicompiled-setup: unsupported macOS architecture: %s\n' "$arch" >&2; exit 1 ;; esac +tools="$resources/tools/$arch" +# Shared with setup.command, so both entry points reuse one extraction and translation cache. +workspace="$HOME/Library/Application Support/WiiCompiled/BuildWorkspace" + +# Callers pass the subcommand first. Only install builds, so only it needs the compiler and a +# writable workspace; --version and launch-* stay fast. Output goes to stderr because +# --progress-json reserves stdout for its event stream. +if [[ "${1:-}" == install ]]; then + if ! /usr/bin/xcode-select -p >/dev/null 2>&1; then + /usr/bin/xcode-select --install >/dev/null 2>&1 || true + printf "wiicompiled-setup: Xcode Command Line Tools are required to compile WiiCompiled. Finish Apple's installer, then try again.\n" >&2 + exit 1 + fi + mkdir -p "$(dirname "$workspace")" + "$resources/sync-workspace.command" "$resources/workspace" "$workspace" >&2 +fi + +exec "$tools/wiicompiled-setup" --workspace "$workspace" \ + --translator-bin "$tools/Translator.Cli" --disc-tool-bin "$tools/nodtool" \ + --cmake "$resources/tools/cmake/bin/cmake" --ninja "$tools/ninja" "$@" From 800c0efc4f59208f3653bc4e5a433cabc3a3fa0b Mon Sep 17 00:00:00 2001 From: martipops Date: Thu, 8 Oct 2026 03:20:41 -0400 Subject: [PATCH 3/3] fix(macos): report an incomplete .run download clearly A truncated WiiCompiled-Setup-macos.run failed with tar's raw gzip error. Report that the download may be incomplete instead. This also covers a missing payload marker, since tail then fails the same pipeline. --- Launcher/macos/setup-run-header.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Launcher/macos/setup-run-header.sh b/Launcher/macos/setup-run-header.sh index 7d6d80ebc..1986f1bd4 100644 --- a/Launcher/macos/setup-run-header.sh +++ b/Launcher/macos/setup-run-header.sh @@ -14,7 +14,8 @@ if [[ ! -x "$bundle/wiicompiled-setup" ]]; then staging=$(mktemp -d "$setup_root/.unpack.XXXXXX") trap 'rm -rf "$staging"' EXIT payload_line=$(awk '/^__WIICOMPILED_PAYLOAD__$/ { print NR + 1; exit }' "$0") - tail -n +"$payload_line" "$0" | /usr/bin/tar -xzf - -C "$staging" + tail -n +"$payload_line" "$0" | /usr/bin/tar -xzf - -C "$staging" \ + || { printf 'WiiCompiled setup: could not unpack %s; the download may be incomplete\n' "$0" >&2; exit 1; } # Not safe against two first runs racing; frontends already run setup operations one at a time. rm -rf "$bundle" mv "$staging" "$bundle"