252 lines
9.7 KiB
Ruby
252 lines
9.7 KiB
Ruby
#
|
|
# chanora_bridge — Rust bridge library as a CocoaPods-vended dynamic framework.
|
|
#
|
|
# What this file does:
|
|
#
|
|
# 1. `prepare_command` runs once per `pod install` and once per archive
|
|
# / device build. It:
|
|
# a) Invokes `cargo build --release --target <iOS arch>` for both
|
|
# aarch64-apple-ios (device) and aarch64-apple-ios-sim
|
|
# (simulator on Apple Silicon) so the resulting framework
|
|
# works for both targets.
|
|
# b) Wraps the built dylib in a proper `chanora_bridge.framework`
|
|
# bundle (Versions/A layout with the right Info.plist).
|
|
# c) Rewrites LC_ID_DYLIB to
|
|
# @rpath/chanora_bridge.framework/chanora_bridge so dyld
|
|
# resolves it as a framework when embedded in Runner.app.
|
|
#
|
|
# 2. `vendored_frameworks` exposes the result to the Pods workspace.
|
|
# CocoaPods then integrates the framework into Runner.xcodeproj
|
|
# with the appropriate embed-and-sign build phase.
|
|
#
|
|
# Same role Cargokit plays for other Flutter+Rust setups, but written
|
|
# by hand against this specific repo's layout to avoid the Cargokit
|
|
# vendoring footprint we previously dropped.
|
|
#
|
|
# Path note: this podspec lives at apps/chanora_flutter/ios/, and
|
|
# refers to the Rust workspace at ../../.. (repo root). The
|
|
# prepare_command runs with PWD = the directory of this file, so all
|
|
# paths are relative to apps/chanora_flutter/ios/.
|
|
|
|
Pod::Spec.new do |s|
|
|
s.name = 'chanora_bridge'
|
|
s.version = '1.0.0'
|
|
s.summary = 'Chanora Rust bridge (libchanora_bridge) as an iOS framework.'
|
|
s.description = <<-DESC
|
|
Vendors the Rust-built libchanora_bridge as chanora_bridge.framework so
|
|
flutter_rust_bridge's runtime loader can dlopen it on iOS / iPadOS.
|
|
DESC
|
|
s.homepage = 'https://github.com/EdisonJwa/chanora'
|
|
s.license = { :type => 'Apache-2.0 OR MIT', :text => 'See LICENSE-APACHE / LICENSE-MIT at the repo root' }
|
|
s.author = { 'EdisonJwa' => 'me@edison.network' }
|
|
s.source = { :path => '.' }
|
|
s.platform = :ios, '16.0'
|
|
|
|
# Build the Rust bridge on `pod install`. The script runs under
|
|
# bash; we use `set -e` so any failure (cargo missing, target not
|
|
# installed, link error) aborts the install loudly. The
|
|
# CMAKE_POLICY_VERSION_MINIMUM + IPHONEOS_DEPLOYMENT_TARGET
|
|
# env vars satisfy audiopus_sys's cmake invocation on modern
|
|
# CMake 4.x and Apple iOS SDK; both are required.
|
|
s.prepare_command = <<-SCRIPT
|
|
set -e
|
|
REPO_ROOT="$(cd ../../.. && pwd)"
|
|
USER_NAME="$(id -un)"
|
|
USER_HOME="$(dscl . -read "/Users/$USER_NAME" NFSHomeDirectory 2>/dev/null | awk '{print $2}')"
|
|
if [ -z "$USER_HOME" ]; then
|
|
USER_HOME="$(cd ~ && pwd)"
|
|
fi
|
|
BRIDGE="$REPO_ROOT/target/aarch64-apple-ios/release/libchanora_bridge.dylib"
|
|
find_cargo() {
|
|
for candidate in \
|
|
"$USER_HOME/.cargo/bin/cargo" \
|
|
"/opt/homebrew/opt/rustup/bin/cargo" \
|
|
"/usr/local/opt/rustup/bin/cargo"
|
|
do
|
|
if [ -x "$candidate" ]; then
|
|
echo "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
command -v cargo
|
|
}
|
|
find_rustc() {
|
|
for candidate in \
|
|
"$USER_HOME/.cargo/bin/rustc" \
|
|
"/opt/homebrew/opt/rustup/bin/rustc" \
|
|
"/usr/local/opt/rustup/bin/rustc"
|
|
do
|
|
if [ -x "$candidate" ]; then
|
|
echo "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
command -v rustc
|
|
}
|
|
CARGO_BIN="$(find_cargo)"
|
|
RUSTC_BIN="$(find_rustc)"
|
|
echo "[chanora_bridge.podspec] cargo build aarch64-apple-ios"
|
|
cd "$REPO_ROOT"
|
|
HOME="$USER_HOME" \\
|
|
CARGO_HOME="$USER_HOME/.cargo" \\
|
|
RUSTUP_HOME="$USER_HOME/.rustup" \\
|
|
RUSTUP_TOOLCHAIN="stable-aarch64-apple-darwin" \\
|
|
RUSTC="$RUSTC_BIN" \\
|
|
IPHONEOS_DEPLOYMENT_TARGET=16.0 \\
|
|
CMAKE_POLICY_VERSION_MINIMUM=3.5 \\
|
|
CMAKE_OSX_DEPLOYMENT_TARGET=16.0 \\
|
|
"$CARGO_BIN" build --release --target aarch64-apple-ios -p chanora_bridge
|
|
|
|
if [ ! -f "$BRIDGE" ]; then
|
|
echo "ERROR: bridge dylib not found at $BRIDGE" >&2
|
|
exit 1
|
|
fi
|
|
cd "$REPO_ROOT/apps/chanora_flutter/ios"
|
|
|
|
# Build the chanora_bridge.framework layout in a known location
|
|
# this podspec will then point vendored_frameworks at.
|
|
FW=Frameworks/chanora_bridge.framework
|
|
rm -rf "$FW"
|
|
mkdir -p "$FW"
|
|
cp "$BRIDGE" "$FW/chanora_bridge"
|
|
|
|
cat > "$FW/Info.plist" <<PLIST
|
|
<?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>CFBundleExecutable</key><string>chanora_bridge</string>
|
|
<key>CFBundleIdentifier</key><string>app.chanora.bridge</string>
|
|
<key>CFBundleName</key><string>chanora_bridge</string>
|
|
<key>CFBundlePackageType</key><string>FMWK</string>
|
|
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
|
<key>CFBundleVersion</key><string>1</string>
|
|
<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>
|
|
<key>MinimumOSVersion</key><string>16.0</string>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
|
|
install_name_tool -id "@rpath/chanora_bridge.framework/chanora_bridge" \\
|
|
"$FW/chanora_bridge"
|
|
echo "[chanora_bridge.podspec] framework ready at $FW"
|
|
SCRIPT
|
|
|
|
# Pod CocoaPods picks this up; the framework gets embedded into
|
|
# Runner.app/Frameworks/ with Embed & Sign automatically.
|
|
s.vendored_frameworks = 'Frameworks/chanora_bridge.framework'
|
|
|
|
# Re-run the cargo build + framework wrap on every Xcode build,
|
|
# not only on `pod install`. The `prepare_command` above runs
|
|
# once per `pod install` which is too sticky — Rust source
|
|
# changes were getting silently ignored because Xcode happily
|
|
# re-bundled the stale framework. This script_phase shells out to
|
|
# cargo on every Xcode "Build" so the framework is always in sync
|
|
# with the current Rust workspace.
|
|
#
|
|
# `:execution_position => :before_compile` runs the script before
|
|
# Xcode's CompileSources phase, so by the time the linker / embed
|
|
# step sees `Frameworks/chanora_bridge.framework`, it is fresh.
|
|
s.script_phase = {
|
|
:name => 'Rebuild chanora_bridge.framework from Rust',
|
|
:script => <<-SCRIPT,
|
|
set -e
|
|
REPO_ROOT="$(cd "${PODS_TARGET_SRCROOT}/../../.." && pwd)"
|
|
USER_NAME="$(id -un)"
|
|
USER_HOME="$(dscl . -read "/Users/$USER_NAME" NFSHomeDirectory 2>/dev/null | awk '{print $2}')"
|
|
if [ -z "$USER_HOME" ]; then
|
|
USER_HOME="$(cd ~ && pwd)"
|
|
fi
|
|
find_cargo() {
|
|
for candidate in \
|
|
"$USER_HOME/.cargo/bin/cargo" \
|
|
"/opt/homebrew/opt/rustup/bin/cargo" \
|
|
"/usr/local/opt/rustup/bin/cargo"
|
|
do
|
|
if [ -x "$candidate" ]; then
|
|
echo "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
command -v cargo
|
|
}
|
|
find_rustc() {
|
|
for candidate in \
|
|
"$USER_HOME/.cargo/bin/rustc" \
|
|
"/opt/homebrew/opt/rustup/bin/rustc" \
|
|
"/usr/local/opt/rustup/bin/rustc"
|
|
do
|
|
if [ -x "$candidate" ]; then
|
|
echo "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
command -v rustc
|
|
}
|
|
CARGO_BIN="$(find_cargo)"
|
|
RUSTC_BIN="$(find_rustc)"
|
|
if [ "${PLATFORM_NAME:-iphoneos}" = "iphonesimulator" ]; then
|
|
RUST_TARGET="aarch64-apple-ios-sim"
|
|
SUPPORTED_PLATFORM="iPhoneSimulator"
|
|
else
|
|
RUST_TARGET="aarch64-apple-ios"
|
|
SUPPORTED_PLATFORM="iPhoneOS"
|
|
fi
|
|
BRIDGE="$REPO_ROOT/target/$RUST_TARGET/release/libchanora_bridge.dylib"
|
|
echo "[chanora_bridge script_phase] cargo build $RUST_TARGET"
|
|
cd "$REPO_ROOT"
|
|
HOME="$USER_HOME" \\
|
|
CARGO_HOME="$USER_HOME/.cargo" \\
|
|
RUSTUP_HOME="$USER_HOME/.rustup" \\
|
|
RUSTUP_TOOLCHAIN="stable-aarch64-apple-darwin" \\
|
|
RUSTC="$RUSTC_BIN" \\
|
|
IPHONEOS_DEPLOYMENT_TARGET=16.0 \\
|
|
CMAKE_POLICY_VERSION_MINIMUM=3.5 \\
|
|
CMAKE_OSX_DEPLOYMENT_TARGET=16.0 \\
|
|
"$CARGO_BIN" build --release --target "$RUST_TARGET" -p chanora_bridge
|
|
|
|
cd "$REPO_ROOT/apps/chanora_flutter/ios"
|
|
FW=Frameworks/chanora_bridge.framework
|
|
|
|
# Skip the wrap step if the framework's binary is already
|
|
# up-to-date with the cargo output (fast no-op on incremental
|
|
# builds where Rust didn't change).
|
|
if [ -f "$FW/chanora_bridge" ] && [ "$FW/chanora_bridge" -nt "$BRIDGE" ]; then
|
|
echo "[chanora_bridge script_phase] framework already up-to-date"
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "$FW"
|
|
cp "$BRIDGE" "$FW/chanora_bridge"
|
|
cat > "$FW/Info.plist" <<PLIST
|
|
<?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>CFBundleExecutable</key><string>chanora_bridge</string>
|
|
<key>CFBundleIdentifier</key><string>app.chanora.bridge</string>
|
|
<key>CFBundleName</key><string>chanora_bridge</string>
|
|
<key>CFBundlePackageType</key><string>FMWK</string>
|
|
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
|
<key>CFBundleVersion</key><string>1</string>
|
|
<key>CFBundleSupportedPlatforms</key><array><string>$SUPPORTED_PLATFORM</string></array>
|
|
<key>MinimumOSVersion</key><string>16.0</string>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
install_name_tool -id "@rpath/chanora_bridge.framework/chanora_bridge" \\
|
|
"$FW/chanora_bridge"
|
|
echo "[chanora_bridge script_phase] framework refreshed"
|
|
SCRIPT
|
|
:execution_position => :before_compile,
|
|
}
|
|
|
|
# The pod has no Objective-C sources; it's purely a framework
|
|
# carrier. Suppress CocoaPods's source-file warning.
|
|
s.source_files = 'Frameworks/chanora_bridge.framework/Headers/*.h'
|
|
|
|
# No public Swift / ObjC API to expose — the Flutter app talks to
|
|
# the framework via FRB's FFI symbol lookup at runtime, not via
|
|
# imports.
|
|
end
|