Files
gen1recomp/scripts/build_love_macos.sh
T
2026-08-19 20:14:04 +02:00

152 lines
5.6 KiB
Bash
Executable File

#!/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"