mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-20 04:31:09 +02:00
build(mac): establish LÖVE 12 Metal parity
This commit is contained in:
@@ -26,6 +26,64 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
macos-changes:
|
||||
name: detect macOS changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changed: ${{ steps.paths.outputs.changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- id: paths
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
||||
HEAD_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(conf\.lua$|scripts/(build\.sh|build_love_macos\.sh|run\.sh|setup\.sh)$|Play-Mac\.command$|mobile/macos/|src/core/SaveData\.lua$|\.github/workflows/(ci|release)\.yml$)'; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
macos-build:
|
||||
name: macOS LÖVE 12 build
|
||||
needs: macos-changes
|
||||
if: needs.macos-changes.outputs.changed == 'true'
|
||||
runs-on: ${{ fromJSON(github.repository == 'bryanthaboi/gen1recomp' && '["self-hosted", "macOS"]' || '"macos-latest"') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Build LÖVE 12 macOS runtime
|
||||
run: scripts/build_love_macos.sh --fetch
|
||||
- name: Build and verify macOS app
|
||||
env:
|
||||
LOVE_APP: ${{ github.workspace }}/.bazinga/love12/love.app
|
||||
MAC_STAGE_DIR: ${{ runner.temp }}/gen1recomp-mac-stage
|
||||
run: |
|
||||
set -euo pipefail
|
||||
scripts/build.sh mac --no-notarize --identity - --version 0.0.0
|
||||
unzip -tqq 'dist/mac/gen1recomp++-macos.zip'
|
||||
app="$MAC_STAGE_DIR/gen1recomp++.app"
|
||||
[ -d "$app" ]
|
||||
[ -x "$app/Contents/MacOS/gen1recomp++" ]
|
||||
[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app/Contents/Info.plist")" = 'gen1recomp++' ]
|
||||
[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleDisplayName' "$app/Contents/Info.plist")" = 'gen1recomp++' ]
|
||||
[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$app/Contents/Info.plist")" = 'com.theboisclub.gen1recompplusplus' ]
|
||||
[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$app/Contents/Info.plist")" = 0.0.0 ]
|
||||
codesign --verify --deep --strict "$app"
|
||||
otool -L "$app/Contents/Frameworks/love.framework/love" | grep -q '/Metal.framework/'
|
||||
- name: Upload macOS build
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gen1recomp++-macos
|
||||
path: dist/mac/gen1recomp++-macos.zip
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
ios-changes:
|
||||
name: detect iOS changes
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -344,9 +344,16 @@ jobs:
|
||||
echo "Identities available to codesign:"
|
||||
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
|
||||
|
||||
- name: Build LÖVE 12 macOS runtime
|
||||
run: |
|
||||
set -euo pipefail
|
||||
scripts/build_love_macos.sh --fetch
|
||||
|
||||
- name: Build macOS + Windows + Linux
|
||||
env:
|
||||
GEN1TLS_DLL: ${{ github.workspace }}/dist/native/win-x64/gen1tls.dll
|
||||
LOVE_APP: ${{ github.workspace }}/.bazinga/love12/love.app
|
||||
MAC_STAGE_DIR: ${{ runner.temp }}/gen1recomp-mac-stage
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Sign in-build (identity auto-detected from the temp keychain);
|
||||
@@ -428,8 +435,8 @@ jobs:
|
||||
set -a; . "$ci_dir/notary.env"; set +a
|
||||
echo "::add-mask::$APPLE_APP_PASSWORD"
|
||||
|
||||
app=".bazinga/work/gen1recomp.app"
|
||||
zip="dist/mac/gen1recomp-macos.zip"
|
||||
app="$RUNNER_TEMP/gen1recomp-mac-stage/gen1recomp++.app"
|
||||
zip="dist/mac/gen1recomp++-macos.zip"
|
||||
[ -d "$app" ] || { echo "::error::signed app not found at $app"; exit 1; }
|
||||
if [ -z "${APPLE_ID:-}" ] || [ -z "${APPLE_APP_PASSWORD:-}" ] || [ -z "${APPLE_TEAM_ID:-}" ]; then
|
||||
echo "::error::notary.env is missing APPLE_ID / APPLE_APP_PASSWORD / APPLE_TEAM_ID."
|
||||
@@ -474,7 +481,7 @@ jobs:
|
||||
outdir="dist/release"
|
||||
rm -rf "$outdir"
|
||||
mkdir -p "$outdir"
|
||||
cp "dist/mac/gen1recomp-macos.zip" "$outdir/gen1recomp-${v}-macos.zip"
|
||||
cp "dist/mac/gen1recomp++-macos.zip" "$outdir/gen1recomp++-${v}-macos.zip"
|
||||
cp "dist/win/gen1recomp-win64.zip" "$outdir/gen1recomp-${v}-windows.zip"
|
||||
cp "dist/linux/gen1recomp-linux.zip" "$outdir/gen1recomp-${v}-linux.zip"
|
||||
|
||||
|
||||
+13
-18
@@ -27,9 +27,15 @@ ask() { # ask "question" -> yes by default
|
||||
printf '\n \033[1mPokémon Red - LÖVE2D port\033[0m\n\n'
|
||||
|
||||
have_love() {
|
||||
command -v love >/dev/null 2>&1 && return 0
|
||||
[ -x "/Applications/love.app/Contents/MacOS/love" ] && return 0
|
||||
[ -x "$HOME/Applications/love.app/Contents/MacOS/love" ] && return 0
|
||||
local app version
|
||||
for app in ".bazinga/love12/love.app" "/Applications/love12.app" "$HOME/Applications/love12.app" \
|
||||
"/Applications/love.app" "$HOME/Applications/love.app"; do
|
||||
[ -x "$app/Contents/MacOS/love" ] || continue
|
||||
version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$app/Contents/Info.plist" 2>/dev/null || true)"
|
||||
printf '%s' "$version" | grep -Eq '^12(\.|$)' || continue
|
||||
otool -L "$app/Contents/Frameworks/love.framework/love" 2>/dev/null \
|
||||
| grep -q '/Metal.framework/' && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -57,22 +63,11 @@ if ! command -v python3 >/dev/null 2>&1; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------- Homebrew
|
||||
if ! have_love && ! command -v brew >/dev/null 2>&1 \
|
||||
&& [ ! -x /opt/homebrew/bin/brew ] && [ ! -x /usr/local/bin/brew ]; then
|
||||
warn "LÖVE (the game engine) is not installed; the easiest installer is Homebrew"
|
||||
if ask "Install Homebrew now? (asks for your macOS password)"; then
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \
|
||||
|| { err "Homebrew install failed"; pause_exit 1; }
|
||||
else
|
||||
warn "OK, download LÖVE 11.x yourself from https://love2d.org,"
|
||||
warn "drop love.app into /Applications, then run this again."
|
||||
pause_exit 1
|
||||
fi
|
||||
if ! have_love && ! command -v xcodebuild >/dev/null 2>&1; then
|
||||
warn "LÖVE 12 is not installed and Xcode is required to build it for macOS."
|
||||
warn "Install Xcode from the App Store, launch it once, then run this again."
|
||||
pause_exit 1
|
||||
fi
|
||||
# make brew visible in THIS shell (fresh installs aren't on PATH yet)
|
||||
[ -x /opt/homebrew/bin/brew ] && eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
[ -x /usr/local/bin/brew ] && eval "$(/usr/local/bin/brew shellenv)"
|
||||
|
||||
# ------------------------------------------------------------------- build
|
||||
echo
|
||||
|
||||
@@ -194,8 +194,9 @@ By default the game keeps your save, options, and the private ROM-derived
|
||||
data cache in your OS's normal per-user app data folder. To keep everything
|
||||
next to the game instead (handy for a USB stick or portable drive you carry
|
||||
between computers), drop an empty file named `portable.txt` next to the app
|
||||
(next to `gen1recomp.app`/`.exe`, or next to `main.lua`/`conf.lua` when
|
||||
running from source), then launch the game. Portable mode is desktop-only
|
||||
(next to `gen1recomp++.app` on macOS or `gen1recomp.exe` on Windows, or next
|
||||
to `main.lua`/`conf.lua` when running from source), then launch the game.
|
||||
Portable mode is desktop-only
|
||||
(Windows, Linux, macOS); it has no effect on Android or iOS, where the app
|
||||
runs from a read-only package.
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
a5522634f1e581a1ebab73bf3ab4bd7a853b7a3e
|
||||
@@ -0,0 +1 @@
|
||||
97d4465e8c81099f79696ea4b4bb8b1f9083bc1a
|
||||
@@ -0,0 +1 @@
|
||||
12.0
|
||||
@@ -0,0 +1,19 @@
|
||||
# macOS build
|
||||
|
||||
The desktop app uses the pinned LÖVE 12 runtime built from the LÖVE source
|
||||
tree and the matching Apple dependency repository. The runtime enables Metal
|
||||
and is fused into the branded `gen1recomp++.app` bundle.
|
||||
|
||||
Build the runtime and desktop app from the repository root:
|
||||
|
||||
```bash
|
||||
scripts/build_love_macos.sh --fetch
|
||||
LOVE_APP="$PWD/.bazinga/love12/love.app" scripts/build.sh mac --no-notarize --identity -
|
||||
```
|
||||
|
||||
The source and dependency revisions are recorded in `LOVE_SOURCE_REF` and
|
||||
`APPLE_DEPENDENCIES_REF`. Delete `.bazinga/love12/source` or use `--clean`
|
||||
when changing those pins.
|
||||
|
||||
The packaged executable is `gen1recomp++` inside `gen1recomp++.app`, and the
|
||||
bundle declares LÖVE 12.0 compatibility.
|
||||
+64
-20
@@ -8,7 +8,7 @@
|
||||
# [--notary-profile NAME] [--no-notarize]
|
||||
# [--release] # ios only: release config instead of debug
|
||||
#
|
||||
# Output: dist/mac/gen1recomp-macos.zip
|
||||
# Output: dist/mac/gen1recomp++-macos.zip
|
||||
# dist/win/gen1recomp-win64.zip
|
||||
# dist/linux/gen1recomp-linux.zip (fused x86_64 AppImage)
|
||||
# dist/android/debug/*.apk (full gradle output stays under
|
||||
@@ -27,14 +27,18 @@ ENTITLEMENTS="$ROOT/scripts/macos-entitlements.plist"
|
||||
|
||||
APP_NAME="gen1recomp"
|
||||
BUNDLE_ID="com.theboisclub.pokemonred"
|
||||
MAC_APP_NAME="gen1recomp++"
|
||||
MAC_BUNDLE_ID="com.theboisclub.gen1recompplusplus"
|
||||
LOVE_VERSION="11.5"
|
||||
LOVE_MAC_VERSION="12.0"
|
||||
LOVE_MAC_VERSION="$(tr -d '[:space:]' < "$ROOT/mobile/macos/LOVE_VERSION" 2>/dev/null || echo 12.0)"
|
||||
LOVE_MAC_APP="$HERE/love12/love.app"
|
||||
VERSION="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo dev)"
|
||||
VERSION_EXPLICIT=false
|
||||
IDENTITY=""
|
||||
TARGET="all"
|
||||
NOTARY_PROFILE="notary-profile"
|
||||
NOTARIZE=true
|
||||
FETCH_MAC_RUNTIME=false
|
||||
IOS_RELEASE=false
|
||||
IOS_IPA=false
|
||||
|
||||
@@ -42,6 +46,22 @@ say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
|
||||
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
strip_bundle_metadata() {
|
||||
local bundle="$1"
|
||||
xattr -rc "$bundle"
|
||||
xattr -rd com.apple.FinderInfo "$bundle" 2>/dev/null || true
|
||||
xattr -rd 'com.apple.fileprovider.fpfs#P' "$bundle" 2>/dev/null || true
|
||||
}
|
||||
|
||||
valid_love12_app() {
|
||||
local app="$1" version
|
||||
[ -x "$app/Contents/MacOS/love" ] || return 1
|
||||
version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$app/Contents/Info.plist" 2>/dev/null || true)"
|
||||
printf '%s' "$version" | grep -Eq '^12(\.|$)' || return 1
|
||||
otool -L "$app/Contents/Frameworks/love.framework/love" 2>/dev/null \
|
||||
| grep -q '/Metal.framework/'
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
mac|win|linux|android|ios|all) TARGET="$1" ;;
|
||||
@@ -49,6 +69,7 @@ while [ $# -gt 0 ]; do
|
||||
--identity) IDENTITY="$2"; shift ;;
|
||||
--notary-profile) NOTARY_PROFILE="$2"; shift ;;
|
||||
--no-notarize) NOTARIZE=false ;;
|
||||
--fetch) FETCH_MAC_RUNTIME=true ;;
|
||||
--release) IOS_RELEASE=true ;;
|
||||
--ipa) IOS_IPA=true ;;
|
||||
*) fail "unknown argument: $1" ;;
|
||||
@@ -151,8 +172,21 @@ make_ico() { # $1 = output .ico path
|
||||
# --------------------------------------------------------------- macOS
|
||||
build_mac() {
|
||||
say "building macOS app"
|
||||
local love_app="${LOVE_APP:-/Applications/love12.app}"
|
||||
[ -d "$love_app" ] || fail "LÖVE.app not found at $love_app (install it or set LOVE_APP=/path/to/love.app)"
|
||||
local love_app="${LOVE_APP:-}"
|
||||
if [ -z "$love_app" ]; then
|
||||
for candidate in "$LOVE_MAC_APP" "/Applications/love12.app" "$HOME/Applications/love12.app" \
|
||||
"/Applications/love.app" "$HOME/Applications/love.app"; do
|
||||
if [ -d "$candidate" ] && valid_love12_app "$candidate"; then
|
||||
love_app="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ ! -d "$love_app" ] && [ -z "${LOVE_APP:-}" ] && $FETCH_MAC_RUNTIME; then
|
||||
"$ROOT/scripts/build_love_macos.sh" --fetch
|
||||
love_app="$LOVE_MAC_APP"
|
||||
fi
|
||||
[ -d "$love_app" ] || fail "LÖVE 12 Metal app not found; run scripts/build_love_macos.sh --fetch or set LOVE_APP=/path/to/love.app"
|
||||
local love_version
|
||||
love_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$love_app/Contents/Info.plist" 2>/dev/null || true)
|
||||
printf '%s' "$love_version" | grep -Eq '^12(\.|$)' \
|
||||
@@ -163,22 +197,30 @@ build_mac() {
|
||||
| grep -q '/Metal.framework/' \
|
||||
|| fail "macOS build requires a LÖVE runtime linked to Metal at $love_app"
|
||||
|
||||
local stage_dir
|
||||
stage_dir="$(mktemp -d /tmp/gen1recomp-mac.XXXXXX)"
|
||||
local out_app="$stage_dir/$APP_NAME.app"
|
||||
local stage_dir="${MAC_STAGE_DIR:-${RUNNER_TEMP:-/tmp}/gen1recomp-mac-stage}"
|
||||
local out_app="$stage_dir/$MAC_APP_NAME.app"
|
||||
mkdir -p "$stage_dir"
|
||||
rm -rf "$out_app"
|
||||
ditto --norsrc "$love_app" "$out_app"
|
||||
|
||||
local love_executable="$out_app/Contents/MacOS/love"
|
||||
local app_executable="$out_app/Contents/MacOS/$MAC_APP_NAME"
|
||||
[ -f "$love_executable" ] || fail "LÖVE app is missing Contents/MacOS/love"
|
||||
mv "$love_executable" "$app_executable"
|
||||
|
||||
# drop any bundled placeholder .love and fuse ours in
|
||||
find "$out_app/Contents/Resources" -maxdepth 1 -name '*.love' -delete
|
||||
cp "$LOVE_FILE" "$out_app/Contents/Resources/game.love"
|
||||
|
||||
local plist="$out_app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleName $APP_NAME" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleName string $APP_NAME" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $APP_NAME" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleDisplayName string $APP_NAME" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleName $MAC_APP_NAME" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleName string $MAC_APP_NAME" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $MAC_APP_NAME" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleDisplayName string $MAC_APP_NAME" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleExecutable $MAC_APP_NAME" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleExecutable string $MAC_APP_NAME" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $MAC_BUNDLE_ID" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $MAC_BUNDLE_ID" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string $VERSION" "$plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" "$plist" 2>/dev/null \
|
||||
@@ -200,7 +242,10 @@ build_mac() {
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIconFile OS X AppIcon" "$plist" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleIconFile string 'OS X AppIcon'" "$plist"
|
||||
|
||||
xattr -rc "$out_app"
|
||||
strip_bundle_metadata "$out_app"
|
||||
|
||||
rm -rf "$WORK/$MAC_APP_NAME.app"
|
||||
ln -s "$out_app" "$WORK/$MAC_APP_NAME.app"
|
||||
|
||||
local id="$IDENTITY"
|
||||
if [ -z "$id" ]; then
|
||||
@@ -223,9 +268,9 @@ build_mac() {
|
||||
warn "keychain profile '$NOTARY_PROFILE' not found/working, skipping notarization."
|
||||
warn "set it up with: xcrun notarytool store-credentials \"$NOTARY_PROFILE\" --apple-id ... --team-id ... --password ..."
|
||||
else
|
||||
local notarize_zip="$stage_dir/$APP_NAME-notarize.zip"
|
||||
local notarize_zip="$stage_dir/$MAC_APP_NAME-notarize.zip"
|
||||
rm -f "$notarize_zip"
|
||||
(cd "$stage_dir" && ditto -c -k --keepParent "$APP_NAME.app" "$notarize_zip")
|
||||
(cd "$stage_dir" && ditto -c -k --keepParent "$MAC_APP_NAME.app" "$notarize_zip")
|
||||
say "submitting to Apple notary service (this can take a few minutes)"
|
||||
xcrun notarytool submit "$notarize_zip" --keychain-profile "$NOTARY_PROFILE" --wait
|
||||
say "stapling notarization ticket"
|
||||
@@ -234,12 +279,11 @@ build_mac() {
|
||||
fi
|
||||
fi
|
||||
|
||||
xattr -rc "$out_app"
|
||||
strip_bundle_metadata "$out_app"
|
||||
|
||||
local zip_out="$DIST/mac/$APP_NAME-macos.zip"
|
||||
local zip_out="$DIST/mac/$MAC_APP_NAME-macos.zip"
|
||||
rm -f "$zip_out"
|
||||
(cd "$stage_dir" && ditto -c -k --norsrc --keepParent "$APP_NAME.app" "$zip_out")
|
||||
rm -rf "$stage_dir"
|
||||
(cd "$stage_dir" && ditto -c -k --norsrc --keepParent "$MAC_APP_NAME.app" "$zip_out")
|
||||
say "macOS build: $zip_out"
|
||||
}
|
||||
|
||||
|
||||
Executable
+151
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
MACOS_DIR="$ROOT/mobile/macos"
|
||||
CACHE="$ROOT/.bazinga/love12"
|
||||
SOURCE_DIR="${LOVE_SOURCE_DIR:-$CACHE/source}"
|
||||
RUNTIME_APP="${LOVE_APP_OUTPUT:-$CACHE/love.app}"
|
||||
BUILD_DIR="$CACHE/build"
|
||||
LOVE_VERSION="$(tr -d '[:space:]' < "$MACOS_DIR/LOVE_VERSION")"
|
||||
LOVE_SOURCE_REF="$(tr -d '[:space:]' < "$MACOS_DIR/LOVE_SOURCE_REF")"
|
||||
APPLE_DEPENDENCIES_REF="$(tr -d '[:space:]' < "$MACOS_DIR/APPLE_DEPENDENCIES_REF")"
|
||||
LOVE_SOURCE_REPO="${LOVE_SOURCE_REPO:-https://github.com/love2d/love.git}"
|
||||
APPLE_DEPENDENCIES_REPO="${APPLE_DEPENDENCIES_REPO:-https://github.com/love2d/love-apple-dependencies.git}"
|
||||
|
||||
FETCH=false
|
||||
CLEAN=false
|
||||
|
||||
say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
|
||||
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
strip_bundle_metadata() {
|
||||
local bundle="$1"
|
||||
xattr -rc "$bundle"
|
||||
xattr -rd com.apple.FinderInfo "$bundle" 2>/dev/null || true
|
||||
xattr -rd 'com.apple.fileprovider.fpfs#P' "$bundle" 2>/dev/null || true
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--fetch) FETCH=true ;;
|
||||
--clean) CLEAN=true ;;
|
||||
-h|--help)
|
||||
printf '%s\n' 'usage: scripts/build_love_macos.sh [--fetch] [--clean]'
|
||||
exit 0
|
||||
;;
|
||||
*) fail "unknown argument: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$(uname -s)" = "Darwin" ] || fail "macOS LÖVE builds require Darwin"
|
||||
command -v git >/dev/null 2>&1 || fail "git is required to fetch LÖVE sources"
|
||||
command -v xcodebuild >/dev/null 2>&1 || fail "xcodebuild is required to build LÖVE for macOS"
|
||||
command -v xattr >/dev/null 2>&1 || fail "xattr is required to normalize the runtime bundle"
|
||||
|
||||
source_ready() {
|
||||
[ -d "$SOURCE_DIR/platform/xcode/love.xcodeproj" ] \
|
||||
&& [ -d "$SOURCE_DIR/platform/xcode/macosx/Frameworks/Lua.framework" ] \
|
||||
&& [ -d "$SOURCE_DIR/platform/xcode/shared/Frameworks/SDL3.xcframework" ]
|
||||
}
|
||||
|
||||
source_is_pinned() {
|
||||
[ "$(git -C "$SOURCE_DIR" rev-parse HEAD 2>/dev/null || true)" = "$LOVE_SOURCE_REF" ] \
|
||||
&& [ -f "$SOURCE_DIR/.gen1recomp-apple-dependencies-ref" ] \
|
||||
&& [ "$(tr -d '[:space:]' < "$SOURCE_DIR/.gen1recomp-apple-dependencies-ref")" = "$APPLE_DEPENDENCIES_REF" ]
|
||||
}
|
||||
|
||||
fetch_repo() {
|
||||
local repo_url="$1"
|
||||
local ref="$2"
|
||||
local destination="$3"
|
||||
mkdir -p "$destination"
|
||||
git -C "$destination" init -q
|
||||
git -C "$destination" remote add origin "$repo_url"
|
||||
git -C "$destination" fetch --depth 1 origin "$ref"
|
||||
git -C "$destination" checkout --detach -q FETCH_HEAD
|
||||
}
|
||||
|
||||
fetch_sources() {
|
||||
local tmp
|
||||
tmp="$(mktemp -d "$CACHE/fetch.XXXXXX")"
|
||||
say "fetching LÖVE source $LOVE_SOURCE_REF"
|
||||
fetch_repo "$LOVE_SOURCE_REPO" "$LOVE_SOURCE_REF" "$tmp/love"
|
||||
say "fetching Apple dependencies $APPLE_DEPENDENCIES_REF"
|
||||
fetch_repo "$APPLE_DEPENDENCIES_REPO" "$APPLE_DEPENDENCIES_REF" "$tmp/dependencies"
|
||||
mkdir -p "$tmp/love/platform/xcode/macosx/Frameworks" "$tmp/love/platform/xcode/shared"
|
||||
cp -R "$tmp/dependencies/macOS/Frameworks/." "$tmp/love/platform/xcode/macosx/Frameworks/"
|
||||
cp -R "$tmp/dependencies/shared/." "$tmp/love/platform/xcode/shared/"
|
||||
rm -rf "$SOURCE_DIR"
|
||||
mkdir -p "$(dirname "$SOURCE_DIR")"
|
||||
mv "$tmp/love" "$SOURCE_DIR"
|
||||
printf '%s\n' "$APPLE_DEPENDENCIES_REF" > "$SOURCE_DIR/.gen1recomp-apple-dependencies-ref"
|
||||
rm -rf "$tmp"
|
||||
say "LÖVE source ready at $SOURCE_DIR"
|
||||
}
|
||||
|
||||
if $CLEAN; then
|
||||
[ -z "${LOVE_SOURCE_DIR:-}" ] \
|
||||
|| fail "--clean cannot be used with LOVE_SOURCE_DIR"
|
||||
rm -rf "$SOURCE_DIR" "$BUILD_DIR" "$RUNTIME_APP"
|
||||
fi
|
||||
|
||||
if ! source_ready || ! source_is_pinned; then
|
||||
if ! $FETCH; then
|
||||
fail "pinned LÖVE 12 sources are missing at $SOURCE_DIR; run scripts/build_love_macos.sh --fetch"
|
||||
fi
|
||||
[ -z "${LOVE_SOURCE_DIR:-}" ] \
|
||||
|| fail "LOVE_SOURCE_DIR is not the pinned LÖVE commit $LOVE_SOURCE_REF"
|
||||
fetch_sources
|
||||
fi
|
||||
|
||||
PROJECT="$SOURCE_DIR/platform/xcode/love.xcodeproj"
|
||||
rm -rf "$BUILD_DIR" "$RUNTIME_APP"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
say "building LÖVE 12 macOS runtime"
|
||||
xcodebuild \
|
||||
-quiet \
|
||||
-project "$PROJECT" \
|
||||
-target love-macosx \
|
||||
-configuration Release \
|
||||
-sdk macosx \
|
||||
SYMROOT="$BUILD_DIR" \
|
||||
OBJROOT="$BUILD_DIR/Intermediates" \
|
||||
ARCHS="arm64 x86_64" \
|
||||
ONLY_ACTIVE_ARCH=NO \
|
||||
MACOSX_DEPLOYMENT_TARGET=12.0 \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGNING_REQUIRED=NO \
|
||||
CODE_SIGN_IDENTITY=-
|
||||
|
||||
BUILT_APP="$BUILD_DIR/Release/love.app"
|
||||
[ -d "$BUILT_APP" ] || fail "xcodebuild produced no LÖVE app at $BUILT_APP"
|
||||
ditto --norsrc "$BUILT_APP" "$RUNTIME_APP"
|
||||
strip_bundle_metadata "$RUNTIME_APP"
|
||||
|
||||
version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$RUNTIME_APP/Contents/Info.plist" 2>/dev/null || true)"
|
||||
version_re="$(printf '%s' "$LOVE_VERSION" | sed 's/\./\\./g')"
|
||||
printf '%s' "$version" | grep -Eq "^${version_re}(\.|$)" \
|
||||
|| fail "built runtime reports LÖVE version '$version'"
|
||||
[ -x "$RUNTIME_APP/Contents/MacOS/love" ] \
|
||||
|| fail "built runtime is missing Contents/MacOS/love"
|
||||
[ -f "$RUNTIME_APP/Contents/Frameworks/love.framework/love" ] \
|
||||
|| fail "built runtime is missing love.framework"
|
||||
otool -L "$RUNTIME_APP/Contents/Frameworks/love.framework/love" \
|
||||
| grep -q '/Metal.framework/' \
|
||||
|| fail "built LÖVE runtime is not linked to Metal"
|
||||
|
||||
archs="$(lipo -archs "$RUNTIME_APP/Contents/MacOS/love")"
|
||||
printf '%s' "$archs" | grep -qw arm64 \
|
||||
|| fail "built LÖVE app is missing arm64"
|
||||
printf '%s' "$archs" | grep -qw x86_64 \
|
||||
|| fail "built LÖVE app is missing x86_64"
|
||||
framework_archs="$(lipo -archs "$RUNTIME_APP/Contents/Frameworks/love.framework/love")"
|
||||
printf '%s' "$framework_archs" | grep -qw arm64 \
|
||||
|| fail "built LÖVE framework is missing arm64"
|
||||
printf '%s' "$framework_archs" | grep -qw x86_64 \
|
||||
|| fail "built LÖVE framework is missing x86_64"
|
||||
|
||||
say "LÖVE 12 macOS runtime: $RUNTIME_APP"
|
||||
+24
-2
@@ -32,8 +32,30 @@ find_love() {
|
||||
return 1
|
||||
}
|
||||
|
||||
LOVE_BIN="$(find_love)" \
|
||||
|| fail "LÖVE not found, run scripts/setup.sh (or install from https://love2d.org)"
|
||||
find_love12() {
|
||||
local app version
|
||||
for app in "${LOVE_APP:-}" "$ROOT/.bazinga/love12/love.app" "/Applications/love12.app" "$HOME/Applications/love12.app" \
|
||||
"/Applications/love.app" "$HOME/Applications/love.app"; do
|
||||
[ -n "$app" ] || continue
|
||||
if [ -x "$app/Contents/MacOS/love" ]; then
|
||||
version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$app/Contents/Info.plist" 2>/dev/null || true)"
|
||||
if printf '%s' "$version" | grep -Eq '^12(\.|$)' \
|
||||
&& otool -L "$app/Contents/Frameworks/love.framework/love" 2>/dev/null | grep -q '/Metal.framework/'; then
|
||||
echo "$app/Contents/MacOS/love"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
LOVE_BIN="$(find_love12)" \
|
||||
|| fail "LÖVE 12 with Metal not found, run scripts/setup.sh or scripts/build_love_macos.sh --fetch"
|
||||
else
|
||||
LOVE_BIN="$(find_love)" \
|
||||
|| fail "LÖVE not found, run scripts/setup.sh (or install from https://love2d.org)"
|
||||
fi
|
||||
|
||||
# SDL2 on Wayland crashes during desktop drag-and-drop in certain compositors;
|
||||
# default to X11/XWayland when available to ensure rock-solid drag-drop stability.
|
||||
|
||||
+25
-4
@@ -70,11 +70,32 @@ find_love() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if LOVE_BIN="$(find_love)"; then
|
||||
valid_love12_app() {
|
||||
local app="$1" version
|
||||
[ -x "$app/Contents/MacOS/love" ] || return 1
|
||||
version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$app/Contents/Info.plist" 2>/dev/null || true)"
|
||||
printf '%s' "$version" | grep -Eq '^12(\.|$)' || return 1
|
||||
otool -L "$app/Contents/Frameworks/love.framework/love" 2>/dev/null \
|
||||
| grep -q '/Metal.framework/'
|
||||
}
|
||||
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
if valid_love12_app "$ROOT/.bazinga/love12/love.app"; then
|
||||
say "LÖVE 12 found: $ROOT/.bazinga/love12/love.app"
|
||||
elif valid_love12_app "/Applications/love12.app"; then
|
||||
say "LÖVE 12 found: /Applications/love12.app"
|
||||
elif valid_love12_app "$HOME/Applications/love12.app"; then
|
||||
say "LÖVE 12 found: $HOME/Applications/love12.app"
|
||||
elif valid_love12_app "/Applications/love.app"; then
|
||||
say "LÖVE 12 found: /Applications/love.app"
|
||||
elif valid_love12_app "$HOME/Applications/love.app"; then
|
||||
say "LÖVE 12 found: $HOME/Applications/love.app"
|
||||
else
|
||||
say "building LÖVE 12 for macOS"
|
||||
"$ROOT/scripts/build_love_macos.sh" --fetch
|
||||
fi
|
||||
elif LOVE_BIN="$(find_love)"; then
|
||||
say "LÖVE found: $LOVE_BIN"
|
||||
elif [ "$(uname -s)" = "Darwin" ] && command -v brew >/dev/null 2>&1; then
|
||||
say "installing LÖVE via Homebrew"
|
||||
brew install --cask love
|
||||
else
|
||||
fail "LÖVE 11.x is not installed; install it from https://love2d.org"
|
||||
fi
|
||||
|
||||
@@ -140,7 +140,7 @@ function SaveData.gameFolders()
|
||||
local src = love.filesystem.getSource and love.filesystem.getSource()
|
||||
local sbd = love.filesystem.getSourceBaseDirectory
|
||||
and love.filesystem.getSourceBaseDirectory()
|
||||
-- A packaged macOS build nests the game inside gen1recomp.app/Contents/
|
||||
-- A packaged macOS build nests the game inside gen1recomp++.app/Contents/
|
||||
-- Resources, so getSource()/getSourceBaseDirectory() point INSIDE the
|
||||
-- bundle -- not where the player drops portable.txt (next to the .app).
|
||||
-- Recover the folder containing the .app so a packaged app finds its
|
||||
|
||||
@@ -15,7 +15,7 @@ love = love or require("tests.love_stub")
|
||||
-- scripts/build.sh mac layout: the game is an archive inside the .app, the
|
||||
-- player's portable folder is the one holding the .app.
|
||||
local PORTABLE = "/Users/p/Games"
|
||||
local APP = PORTABLE .. "/gen1recomp.app"
|
||||
local APP = PORTABLE .. "/gen1recomp++.app"
|
||||
local SOURCE = APP .. "/Contents/Resources/game.love"
|
||||
local BASE = APP .. "/Contents/MacOS"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user