Acord/scripts/ios/build.sh

179 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
source "$ROOT/scripts/_build-dirs.sh"
cd "$ROOT"
case "$(uname -s)" in
Darwin) ;;
*) echo "wrong platform: $(uname -s) — iOS build requires macOS" >&2; exit 1;;
esac
# defaults to simulator, pass "device" for physical hardware
TARGET="${1:-sim}"
case "$TARGET" in
sim)
RUST_TARGET="aarch64-apple-ios-sim"
SDK_NAME="iphonesimulator"
SWIFT_TARGET="arm64-apple-ios17.0-simulator"
;;
device)
RUST_TARGET="aarch64-apple-ios"
SDK_NAME="iphoneos"
SWIFT_TARGET="arm64-apple-ios17.0"
;;
*)
echo "usage: $0 [sim|device]" >&2
exit 2
;;
esac
# ACORD_IOS_CONFIG=debug flips to dev profile + debug swiftc flags
CONFIG="${ACORD_IOS_CONFIG:-release}"
case "$CONFIG" in
release)
CARGO_FLAGS=(--release)
CARGO_PROFILE_DIR="release"
SWIFT_OPT_FLAGS=(-O)
;;
debug)
CARGO_FLAGS=()
CARGO_PROFILE_DIR="debug"
SWIFT_OPT_FLAGS=(-Onone -g -D DEBUG)
;;
*)
echo "ACORD_IOS_CONFIG must be release or debug (got '$CONFIG')" >&2
exit 2
;;
esac
BUILD="$ROOT/build"
APP="$BUILD/ios/Acord.app"
RUST_LIB="$CARGO_TARGET_DIR/$RUST_TARGET/$CARGO_PROFILE_DIR"
SDK="$(xcrun --sdk "$SDK_NAME" --show-sdk-path)"
# force apple clang over esp-clang on PATH
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
export IPHONEOS_DEPLOYMENT_TARGET=17.0
echo "Building Rust workspace for $RUST_TARGET ($CONFIG)..."
cargo build "${CARGO_FLAGS[@]}" --target "$RUST_TARGET" -p acord-viewport
if [ ! -f "$RUST_LIB/libacord_viewport.a" ]; then
echo "ERROR: libacord_viewport.a not found at $RUST_LIB" >&2
exit 1
fi
# build flat app bundle
rm -rf "$APP"
mkdir -p "$APP"
cp "$ROOT/ios/Info.plist" "$APP/Info.plist"
# regenerate icon PNGs from SVG, compile asset catalog via actool
bash "$ROOT/scripts/ios/generate-icons.sh"
ACTOOL_PARTIAL="$BUILD/ios/actool-partial-info.plist"
mkdir -p "$BUILD/ios"
echo "Compiling asset catalog..."
xcrun actool "$ROOT/ios/Assets.xcassets" \
--compile "$APP" \
--platform "$SDK_NAME" \
--minimum-deployment-target 17.0 \
--app-icon AppIcon \
--output-partial-info-plist "$ACTOOL_PARTIAL" \
--target-device ipad \
--target-device iphone \
>/dev/null
# merge the actool partial plist (icon name + variants) into Info.plist.
if [ -f "$ACTOOL_PARTIAL" ]; then
/usr/libexec/PlistBuddy -c "Merge $ACTOOL_PARTIAL" "$APP/Info.plist" 2>/dev/null || true
fi
RUST_FLAGS=(-import-objc-header "$ROOT/viewport/include/acord.h" -L "$RUST_LIB" -lacord_viewport)
echo "Compiling Swift ($CONFIG)..."
xcrun -sdk "$SDK_NAME" swiftc \
-target "$SWIFT_TARGET" \
-sdk "$SDK" \
"${RUST_FLAGS[@]}" \
-framework UIKit \
-framework SwiftUI \
-framework QuartzCore \
-framework Metal \
-framework MetalKit \
-framework CoreGraphics \
-framework CoreFoundation \
-framework Photos \
-framework AVFoundation \
-framework UniformTypeIdentifiers \
"${SWIFT_OPT_FLAGS[@]}" \
-o "$APP/Acord" \
"$ROOT"/ios/src/*.swift
if [ "$TARGET" = "sim" ]; then
codesign --force --sign - "$APP"
else
# device build: embed provisioning profile + sign with a real identity.
PROFILE="${ACORD_IOS_PROFILE:-$HOME/Downloads/All.mobileprovision}"
if [ ! -f "$PROFILE" ]; then
echo "ERROR: provisioning profile not found at $PROFILE" >&2
echo " set ACORD_IOS_PROFILE to point at a valid .mobileprovision" >&2
exit 1
fi
cp "$PROFILE" "$APP/embedded.mobileprovision"
ENT="$BUILD/ios/entitlements.plist"
security cms -D -i "$PROFILE" 2>/dev/null \
| plutil -extract Entitlements xml1 -o "$ENT" - \
|| { echo "ERROR: could not extract entitlements from profile" >&2; exit 1; }
# match a keychain identity against the profile's DeveloperCertificates
TMPDIR_PROF="$(mktemp -d)"
PROFILE_PLIST="$TMPDIR_PROF/profile.plist"
security cms -D -i "$PROFILE" > "$PROFILE_PLIST" 2>/dev/null
PROFILE_SHAS=""
for i in 0 1 2 3 4 5 6 7 8 9; do
if ! plutil -extract "DeveloperCertificates.$i" raw -o "$TMPDIR_PROF/c$i.b64" "$PROFILE_PLIST" >/dev/null 2>&1; then
break
fi
base64 -D -i "$TMPDIR_PROF/c$i.b64" -o "$TMPDIR_PROF/c$i.cer"
sha=$(openssl x509 -inform der -in "$TMPDIR_PROF/c$i.cer" -fingerprint -noout 2>/dev/null \
| sed 's/.*=//;s/://g')
PROFILE_SHAS="$PROFILE_SHAS $sha"
done
KEYCHAIN_SHAS=$(security find-identity -v 2>/dev/null \
| awk '/[0-9A-F]{40}/ {gsub(/[^0-9A-F]/, "", $2); print $2}')
IDENTITY=""
for s in $PROFILE_SHAS; do
if echo "$KEYCHAIN_SHAS" | grep -qi "^$s$"; then
IDENTITY="$s"
break
fi
done
rm -rf "$TMPDIR_PROF"
if [ -z "$IDENTITY" ]; then
echo "ERROR: no codesigning identity in your keychain matches any cert in the profile" >&2
echo " profile certs:$PROFILE_SHAS" >&2
exit 1
fi
echo "Signing with $IDENTITY..."
codesign --force \
--sign "$IDENTITY" \
--entitlements "$ENT" \
--options runtime \
--timestamp=none \
"$APP"
fi
echo "Built: $APP"