mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-08 18:11:07 -04:00
279ce8328f
* macos: add x86_64 Intel support * (feat) Apple Silicon macOS CI building and Setup.pkg documentation (#118) * docs(macos): document Setup.pkg installation Add Apple Silicon macOS CI coverage for runtime configuration, substrate tests, and Setup.pkg packaging alongside the macOS installation instructions. * macos: pin Apple Silicon deployment target * fix(macos): restore Retro Rewind local builds * macos: package universal setup tools * ci: build macOS input expression tests * runtime: Do not force 14.0 minimum anymore new minimum is 12.0 * macos: support older libc++ algorithms * macos: allow undefined MTLLogStateDescriptor for older SDKs * tests: deflake input expression timing window test * build: Fix mac tests * translator: emit null statement after continuation labels for C++17 compatibility * feat(macos): add MetalFX spatial upscaling * Fix automatic music muting on macOS Co-Authored-By: Michael G <10155689+DarthMDev@users.noreply.github.com> Co-Authored-By: Daan Vervacke <23398694+DaanVervacke@users.noreply.github.com> * Address CodeRabbit review comments and integrate upstream TLS cmake * Remove version requirement for running package CI Co-Authored-By: Michael G <10155689+DarthMDev@users.noreply.github.com> * publish-app: fix dependency discovery from build dir, handle spaces in paths, and fail on missing deps * publish-app: preserve subdirectory suffix during @rpath dependency lookup * cmake: synchronize CMAKE_SYSTEM_PROCESSOR with CMAKE_OSX_ARCHITECTURES on macOS * fix rpaths --------- Co-authored-by: DarthM <mgracer48@yahoo.com> Co-authored-by: Michael G <10155689+DarthMDev@users.noreply.github.com> Co-authored-by: Daan Vervacke <23398694+DaanVervacke@users.noreply.github.com> Co-authored-by: patchzyy <64382339+patchzyy@users.noreply.github.com>
172 lines
7.7 KiB
Bash
Executable File
172 lines
7.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Turn one locally compiled macOS product into a self-contained .app bundle.
|
|
set -euo pipefail
|
|
|
|
fail() { printf 'publish-app.command: error: %s\n' "$*" >&2; exit 1; }
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: publish-app.command --build-dir DIR --product {WiiCompiled|RetroRewind} --output-dir DIR [options]
|
|
|
|
Copies a locally built product and its runtime assets into OUTPUT-DIR/<product>.app.
|
|
It bundles non-system dylibs, rewrites their install names, and ad-hoc signs the
|
|
result. This is suitable for local use; a release must replace ad-hoc signing
|
|
with the project's Developer ID signing and notarization process.
|
|
|
|
--architecture {arm64|x86_64} Required architecture of the compiled product (default: host)
|
|
--minimum-system-version VERSION App bundle minimum macOS version (default: 12.0)
|
|
EOF
|
|
}
|
|
|
|
build_dir=""; product=""; output_dir=""; architecture=$(uname -m); minimum_system_version=12.0
|
|
while (($#)); do
|
|
case "$1" in
|
|
--build-dir) build_dir=${2:-}; shift 2 ;;
|
|
--product) product=${2:-}; shift 2 ;;
|
|
--output-dir) output_dir=${2:-}; shift 2 ;;
|
|
--architecture) architecture=${2:-}; shift 2 ;;
|
|
--minimum-system-version) minimum_system_version=${2:-}; shift 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) fail "unknown option: $1" ;;
|
|
esac
|
|
done
|
|
[[ "$product" == WiiCompiled || "$product" == RetroRewind ]] || fail '--product must be WiiCompiled or RetroRewind'
|
|
[[ "$architecture" == arm64 || "$architecture" == x86_64 ]] || fail '--architecture must be arm64 or x86_64'
|
|
[[ "$minimum_system_version" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] || fail '--minimum-system-version must contain two or three period-separated integers'
|
|
for tool in codesign ditto install_name_tool lipo otool; do command -v "$tool" >/dev/null || fail "required macOS tool is unavailable: $tool"; done
|
|
[[ -x "$build_dir/$product" ]] || fail "missing compiled product: $build_dir/$product"
|
|
lipo "$build_dir/$product" -verify_arch "$architecture" || fail "compiled product is not $architecture: $build_dir/$product"
|
|
for asset in dsp_coef.bin initial_pipeline_cache.db cacert.pem wii_bootstrap; do [[ -e "$build_dir/$asset" ]] || fail "missing runtime asset: $build_dir/$asset"; done
|
|
|
|
app="$output_dir/$product.app"
|
|
macos="$app/Contents/MacOS"
|
|
frameworks="$app/Contents/Frameworks"
|
|
resources="$app/Contents/Resources"
|
|
rm -rf "$app"
|
|
mkdir -p "$macos" "$frameworks" "$resources"
|
|
cat > "$app/Contents/Info.plist" <<EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0"><dict>
|
|
<key>CFBundleDevelopmentRegion</key><string>en</string>
|
|
<key>CFBundleExecutable</key><string>$product</string>
|
|
<key>CFBundleIdentifier</key><string>org.wiicompiled.$product</string>
|
|
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
|
|
<key>CFBundleName</key><string>$product</string>
|
|
<key>CFBundlePackageType</key><string>APPL</string>
|
|
<key>CFBundleShortVersionString</key><string>0.1.0</string>
|
|
<key>CFBundleVersion</key><string>1</string>
|
|
<key>LSMinimumSystemVersion</key><string>$minimum_system_version</string>
|
|
<key>NSHighResolutionCapable</key><true/>
|
|
</dict></plist>
|
|
EOF
|
|
ditto "$build_dir/$product" "$macos/$product"
|
|
for asset in dsp_coef.bin initial_pipeline_cache.db cacert.pem wii_bootstrap; do
|
|
ditto "$build_dir/$asset" "$resources/$asset"
|
|
ln -s "../Resources/$asset" "$macos/$asset"
|
|
done
|
|
|
|
# Expand each image's rpaths before inheriting them, so @loader_path stays
|
|
# relative to the image that declared it rather than a descendant library.
|
|
expanded_rpaths() {
|
|
local target=$1 rpath
|
|
while IFS= read -r rpath; do
|
|
case "$rpath" in
|
|
@loader_path/*) rpath="$(dirname "$target")/${rpath#@loader_path/}" ;;
|
|
@loader_path) rpath="$(dirname "$target")" ;;
|
|
@executable_path/*) rpath="$build_dir/${rpath#@executable_path/}" ;;
|
|
@executable_path) rpath="$build_dir" ;;
|
|
esac
|
|
printf '%s\n' "$rpath"
|
|
done < <(otool -l "$target" | awk '
|
|
/LC_RPATH/ { rpath = 1; next }
|
|
rpath && /^[[:space:]]*path / {
|
|
sub(/^[[:space:]]*path[[:space:]]+/, "");
|
|
sub(/[[:space:]]+\(offset[[:space:]]+[0-9]+\)$/, "");
|
|
print;
|
|
rpath = 0;
|
|
}')
|
|
}
|
|
|
|
# Resolve a non-system dependency using the current image's rpaths followed
|
|
# by the inherited loader stack, matching dyld's dependency-chain search.
|
|
dependency_path() {
|
|
local current=$1 dependency=$2 search_rpaths=$3 rpath candidate
|
|
case "$dependency" in
|
|
/System/Library/*|/usr/lib/*)
|
|
return 1
|
|
;;
|
|
/*)
|
|
[[ -f "$dependency" ]] && { printf '%s\n' "$dependency"; return 0; }
|
|
;;
|
|
@loader_path/*)
|
|
candidate="$(dirname "$current")/${dependency#@loader_path/}"
|
|
[[ -f "$candidate" ]] && { printf '%s\n' "$candidate"; return 0; }
|
|
;;
|
|
@executable_path/*)
|
|
candidate="$build_dir/${dependency#@executable_path/}"
|
|
[[ -f "$candidate" ]] && { printf '%s\n' "$candidate"; return 0; }
|
|
;;
|
|
@rpath/*|*.dylib)
|
|
local subpath
|
|
if [[ "$dependency" == @rpath/* ]]; then
|
|
subpath="${dependency#@rpath/}"
|
|
else
|
|
subpath="$dependency"
|
|
fi
|
|
while IFS= read -r rpath; do
|
|
[[ -n "$rpath" ]] || continue
|
|
candidate="$rpath/$subpath"
|
|
[[ -f "$candidate" ]] && { printf '%s\n' "$candidate"; return 0; }
|
|
done <<< "$search_rpaths"
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
# Build a closure of non-system dylibs. System libraries remain system
|
|
# references, while every resolved dependency is copied beside the executable.
|
|
queue=("$build_dir/$product")
|
|
queue_rpaths=("")
|
|
while ((${#queue[@]})); do
|
|
current=${queue[0]}
|
|
inherited_rpaths=${queue_rpaths[0]}
|
|
queue=("${queue[@]:1}")
|
|
queue_rpaths=("${queue_rpaths[@]:1}")
|
|
current_rpaths=$(expanded_rpaths "$current")
|
|
if [[ -n "$inherited_rpaths" ]]; then
|
|
current_rpaths="${current_rpaths}${current_rpaths:+$'\n'}$inherited_rpaths"
|
|
fi
|
|
self_id=$(otool -D "$current" 2>/dev/null | tail -n +2 | sed -nE 's/^[[:space:]]*(.*)[[:space:]]*$/\1/p' || true)
|
|
while IFS= read -r dependency; do
|
|
[[ -z "$dependency" ]] && continue
|
|
[[ -n "$self_id" && "$dependency" == "$self_id" ]] && continue
|
|
case "$dependency" in
|
|
/System/Library/*|/usr/lib/*) continue ;;
|
|
esac
|
|
dep_path=$(dependency_path "$current" "$dependency" "$current_rpaths") || fail "unresolved non-system dependency: '$dependency' needed by '$current'"
|
|
name=$(basename "$dep_path")
|
|
if [[ ! -f "$frameworks/$name" ]]; then
|
|
ditto "$dep_path" "$frameworks/$name"
|
|
install_name_tool -id "@rpath/$name" "$frameworks/$name"
|
|
queue+=("$dep_path")
|
|
queue_rpaths+=("$current_rpaths")
|
|
fi
|
|
done < <(otool -L "$current" | tail -n +2 | sed -nE 's/^[[:space:]]*(.*)[[:space:]]+\(compatibility version .*/\1/p')
|
|
done
|
|
while IFS= read -r binary; do
|
|
while IFS= read -r old; do
|
|
name=$(basename "$old")
|
|
[[ -f "$frameworks/$name" ]] || continue
|
|
if [[ "$binary" == "$macos/$product" ]]; then
|
|
install_name_tool -change "$old" "@executable_path/../Frameworks/$name" "$binary"
|
|
else
|
|
install_name_tool -change "$old" "@loader_path/$name" "$binary"
|
|
fi
|
|
done < <(otool -L "$binary" | tail -n +2 | sed -nE 's/^[[:space:]]*(.*)[[:space:]]+\(compatibility version .*/\1/p')
|
|
done < <(find "$frameworks" -type f -print; printf '%s\n' "$macos/$product")
|
|
|
|
find "$frameworks" -type f -exec codesign --force --sign - {} +
|
|
codesign --force --deep --sign - "$app"
|
|
codesign --verify --deep --strict "$app"
|
|
printf 'MKWCBUILD:APP=%s\n' "$app"
|