Files
chanora/tools/build-ios.sh
T

206 lines
7.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Chanora iOS build helper.
#
# Run this on a macOS host that has already completed §4 of
# docs/release/ios-build.md:
# - Xcode 26+ with Command Line Tools
# - Rust stable with aarch64-apple-ios, aarch64-apple-ios-sim,
# x86_64-apple-ios targets
# - Flutter 3.41.9 stable on PATH
# - CocoaPods on PATH
# - Pre-built libopus.a for each iOS arch (the script in §4.3 of the
# docs leaves them in /tmp/opus-ios-<arch>-wrap/lib/libopus.a)
#
# Usage (from the repo root ~/chanora):
# ./tools/build-ios.sh
# ./tools/build-ios.sh --no-codesign # unsigned Runner.app only
# ./tools/build-ios.sh --regenerate-bindings # rerun FRB codegen first
# ./tools/build-ios.sh --version v0.2.0-beta.1
#
# Produces:
# target/aarch64-apple-ios/release/libchanora_bridge.a
# target/aarch64-apple-ios-sim/release/libchanora_bridge.a
# target/x86_64-apple-ios/release/libchanora_bridge.a
# target/universal-ios-sim/libchanora_bridge.a
# target/ChanoraBridge.xcframework/
# apps/chanora_flutter/build/ios/iphoneos/Runner.app/
# apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa (if signed)
# chanora-<version>-ios.ipa (renamed copy)
set -euo pipefail
VERSION="v0.2.0-beta.1"
SKIP_CODESIGN=0
REGEN=0
EXPORT_METHOD="development"
EXPORT_OPTIONS_PLIST=""
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2;;
--no-codesign) SKIP_CODESIGN=1; shift;;
--regenerate-bindings) REGEN=1; shift;;
--export-method) EXPORT_METHOD="$2"; shift 2;;
--export-options-plist) EXPORT_OPTIONS_PLIST="$2"; shift 2;;
*) echo "unknown arg: $1" >&2; exit 2;;
esac
done
cd "$(dirname "$0")/.."
REPO_ROOT="$(pwd)"
bar() { printf '%s\n' '========================================================================'; }
bar
echo "Chanora iOS build"
echo "Repo root : $REPO_ROOT"
echo "Version : $VERSION"
echo "Sign : $([ "$SKIP_CODESIGN" = 1 ] && echo no || echo "yes (export-method=$EXPORT_METHOD)")"
bar
# ---------- 1. Verify toolchain ----------
echo "[1/7] Verify toolchain"
for cmd in xcodebuild xcrun cargo rustc flutter pod lipo; do
if ! command -v "$cmd" >/dev/null; then
echo " ERROR: '$cmd' not on PATH. See docs/release/ios-build.md §4." >&2
exit 1
fi
printf " %-10s : %s\n" "$cmd" "$($cmd --version 2>&1 | head -1)"
done
# Verify the three Rust iOS targets are installed.
for tgt in aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios; do
if ! rustup target list --installed | grep -q "^$tgt$"; then
echo " Adding rustup target $tgt..."
rustup target add "$tgt"
fi
done
# Verify the pre-built libopus.a for each arch is available.
for arch in arm64 arm64-simulator x86_64; do
if [[ ! -f "/tmp/opus-ios-${arch}-wrap/lib/libopus.a" ]]; then
echo " ERROR: /tmp/opus-ios-${arch}-wrap/lib/libopus.a not found." >&2
echo " See §4.3 of docs/release/ios-build.md to build libopus first." >&2
exit 1
fi
done
# ---------- 2. (Optional) regenerate FRB bindings ----------
if [[ $REGEN -eq 1 ]]; then
echo "[2/7] Regenerate flutter_rust_bridge bindings"
if ! command -v flutter_rust_bridge_codegen >/dev/null; then
echo " flutter_rust_bridge_codegen not on PATH; cargo install it first." >&2
exit 1
fi
flutter_rust_bridge_codegen generate
else
echo "[2/7] Skipping FRB codegen (use --regenerate-bindings if api.rs changed)"
fi
# ---------- 3. Add ios/ platform folder if missing ----------
FLUTTER_APP="$REPO_ROOT/apps/chanora_flutter"
if [[ ! -d "$FLUTTER_APP/ios" ]]; then
echo "[3/7] Add iOS platform to apps/chanora_flutter"
(cd "$FLUTTER_APP" && flutter create --platforms=ios .)
echo " NOTE: edit ios/Podfile to pin platform :ios, '13.0' (DEC-003)"
echo " NOTE: edit ios/Runner/Info.plist to add NSMicrophoneUsageDescription"
echo " NOTE: set Team + Bundle Identifier in Xcode Signing & Capabilities"
else
echo "[3/7] iOS platform already present in apps/chanora_flutter"
fi
# ---------- 4. Build Rust libs for the three iOS triples ----------
echo "[4/7] Build Rust libs for iOS"
export LIBOPUS_STATIC=1
export LIBOPUS_NO_PKG=1
build_one() {
local TARGET=$1
local OPUS_DIR=$2
echo " -> $TARGET"
export LIBOPUS_LIB_DIR="$OPUS_DIR"
export CMAKE_POLICY_VERSION_MINIMUM=3.5
export IPHONEOS_DEPLOYMENT_TARGET=13.0
rm -rf "target/$TARGET/release/build/audiopus_sys-"*
cargo build --release --target "$TARGET" -p chanora_bridge
}
build_one aarch64-apple-ios /tmp/opus-ios-arm64-wrap
build_one aarch64-apple-ios-sim /tmp/opus-ios-arm64-simulator-wrap
build_one x86_64-apple-ios /tmp/opus-ios-x86_64-wrap
# ---------- 5. lipo + xcodebuild -create-xcframework ----------
echo "[5/7] lipo simulator libs + create XCFramework"
mkdir -p target/universal-ios-sim
lipo -create \
target/aarch64-apple-ios-sim/release/libchanora_bridge.a \
target/x86_64-apple-ios/release/libchanora_bridge.a \
-output target/universal-ios-sim/libchanora_bridge.a
echo " Universal sim: $(file target/universal-ios-sim/libchanora_bridge.a)"
rm -rf target/ChanoraBridge.xcframework
xcodebuild -create-xcframework \
-library target/aarch64-apple-ios/release/libchanora_bridge.a \
-library target/universal-ios-sim/libchanora_bridge.a \
-output target/ChanoraBridge.xcframework
echo " XCFramework at: $REPO_ROOT/target/ChanoraBridge.xcframework"
# ---------- 6. flutter build ios / ipa ----------
echo "[6/7] flutter pub get + pod install + flutter build"
cd "$FLUTTER_APP"
flutter pub get
(cd ios && pod install)
if [[ $SKIP_CODESIGN -eq 1 ]]; then
flutter build ios --release --no-codesign
APP_PATH="$FLUTTER_APP/build/ios/iphoneos/Runner.app"
echo " Unsigned .app: $APP_PATH"
else
if [[ -n "$EXPORT_OPTIONS_PLIST" ]]; then
if [[ -f "$REPO_ROOT/$EXPORT_OPTIONS_PLIST" ]]; then
EXPORT_OPTIONS_PLIST="$REPO_ROOT/$EXPORT_OPTIONS_PLIST"
elif [[ ! -f "$EXPORT_OPTIONS_PLIST" ]]; then
echo " ERROR: export options plist not found: $EXPORT_OPTIONS_PLIST" >&2
exit 1
fi
flutter build ipa --release --export-method "$EXPORT_METHOD" --export-options-plist "$EXPORT_OPTIONS_PLIST"
else
flutter build ipa --release --export-method "$EXPORT_METHOD"
fi
IPA_PATH="$FLUTTER_APP/build/ios/ipa/chanora_flutter.ipa"
if [[ ! -f "$IPA_PATH" ]]; then
echo " ERROR: expected $IPA_PATH, did not find one." >&2
ls -la "$FLUTTER_APP/build/ios/ipa/" 2>/dev/null || true
exit 1
fi
fi
# ---------- 7. Rename signed ipa, print final paths ----------
echo "[7/7] Package release artefact"
cd "$REPO_ROOT"
FINAL_IPA="$HOME/chanora-$VERSION-ios.ipa"
if [[ $SKIP_CODESIGN -ne 1 ]]; then
cp "$FLUTTER_APP/build/ios/ipa/chanora_flutter.ipa" "$FINAL_IPA"
shasum -a 256 "$FINAL_IPA"
fi
bar
echo "iOS build complete."
bar
echo "Artefacts:"
echo " Rust libs:"
echo " target/aarch64-apple-ios/release/libchanora_bridge.a"
echo " target/aarch64-apple-ios-sim/release/libchanora_bridge.a"
echo " target/x86_64-apple-ios/release/libchanora_bridge.a"
echo " target/universal-ios-sim/libchanora_bridge.a"
echo " XCFramework:"
echo " target/ChanoraBridge.xcframework/"
if [[ $SKIP_CODESIGN -eq 1 ]]; then
echo " Unsigned app bundle:"
echo " apps/chanora_flutter/build/ios/iphoneos/Runner.app/"
else
echo " Signed .ipa:"
echo " apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa"
echo " $FINAL_IPA"
fi