# # chanora_bridge — Rust bridge library as a CocoaPods-vended dynamic framework # (macOS edition). # # What this file does: # # 1. `prepare_command` runs once per `pod install` and once per archive # / device build. It: # a) Invokes `cargo build --release` for both `aarch64-apple-darwin` # (Apple Silicon) and `x86_64-apple-darwin` (Intel). # b) Uses `lipo` to merge the two into a universal binary. # c) Wraps the universal dylib in a proper `chanora_bridge.framework` # bundle (Versions/A layout with the right Info.plist). # d) Rewrites LC_ID_DYLIB to # @rpath/chanora_bridge.framework/Versions/A/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 phase. # # Path note: this podspec lives at apps/chanora_flutter/macos/, 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/macos/. require_relative 'macos_deployment_target' Pod::Spec.new do |s| s.name = 'chanora_bridge' s.version = '1.0.0' s.summary = 'Chanora Rust bridge (libchanora_bridge) as a macOS 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 macOS. 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 = :osx, MACOS_BRIDGE_DEPLOYMENT_TARGET # 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. s.prepare_command = <<-SCRIPT set -e REPO_ROOT="$(cd ../../.. && pwd)" BRIDGE_ARM64="$REPO_ROOT/target/aarch64-apple-darwin/release/libchanora_bridge.dylib" BRIDGE_X86_64="$REPO_ROOT/target/x86_64-apple-darwin/release/libchanora_bridge.dylib" UNIVERSAL="$REPO_ROOT/target/universal/release/libchanora_bridge.dylib" echo "[chanora_bridge.podspec] cargo build aarch64-apple-darwin" cd "$REPO_ROOT" PATH="$HOME/.cargo/bin:$PATH" \\ MACOSX_DEPLOYMENT_TARGET=#{MACOS_BRIDGE_DEPLOYMENT_TARGET} \\ CMAKE_POLICY_VERSION_MINIMUM=3.5 \\ LIBOPUS_STATIC=1 \\ LIBOPUS_NO_PKG=1 \\ cargo build --release --target aarch64-apple-darwin -p chanora_bridge echo "[chanora_bridge.podspec] cargo build x86_64-apple-darwin" PATH="$HOME/.cargo/bin:$PATH" \\ MACOSX_DEPLOYMENT_TARGET=#{MACOS_BRIDGE_DEPLOYMENT_TARGET} \\ CMAKE_POLICY_VERSION_MINIMUM=3.5 \\ LIBOPUS_STATIC=1 \\ LIBOPUS_NO_PKG=1 \\ cargo build --release --target x86_64-apple-darwin -p chanora_bridge if [ ! -f "$BRIDGE_ARM64" ]; then echo "ERROR: arm64 dylib not found at $BRIDGE_ARM64" >&2 exit 1 fi if [ ! -f "$BRIDGE_X86_64" ]; then echo "ERROR: x86_64 dylib not found at $BRIDGE_X86_64" >&2 exit 1 fi # Create universal binary with lipo. mkdir -p "$(dirname "$UNIVERSAL")" lipo -create "$BRIDGE_ARM64" "$BRIDGE_X86_64" -output "$UNIVERSAL" echo "[chanora_bridge.podspec] universal binary created" cd "$REPO_ROOT/apps/chanora_flutter/macos" # Build the chanora_bridge.framework layout (Versions/A) in a known # location this podspec will then point vendored_frameworks at. FW=Frameworks/chanora_bridge.framework rm -rf "$FW" mkdir -p "$FW/Versions/A/Resources" ln -sfh A "$FW/Versions/Current" ln -sfh Versions/Current/Resources "$FW/Resources" cp "$UNIVERSAL" "$FW/Versions/A/chanora_bridge" ln -sfh Versions/Current/chanora_bridge "$FW/chanora_bridge" cat > "$FW/Versions/A/Resources/Info.plist" < CFBundleExecutablechanora_bridge CFBundleIdentifierapp.chanora.bridge CFBundleNamechanora_bridge CFBundlePackageTypeFMWK CFBundleShortVersionString1.0.0 CFBundleVersion1 CFBundleSupportedPlatformsMacOSX MinimumOSVersion#{MACOS_BRIDGE_DEPLOYMENT_TARGET} PLIST install_name_tool -id "@rpath/chanora_bridge.framework/Versions/A/chanora_bridge" \\ "$FW/Versions/A/chanora_bridge" echo "[chanora_bridge.podspec] framework ready at $FW" SCRIPT # Pod CocoaPods picks this up; the framework gets embedded into # Runner.app/Contents/Frameworks/ 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)" BRIDGE_ARM64="$REPO_ROOT/target/aarch64-apple-darwin/release/libchanora_bridge.dylib" BRIDGE_X86_64="$REPO_ROOT/target/x86_64-apple-darwin/release/libchanora_bridge.dylib" UNIVERSAL="$REPO_ROOT/target/universal/release/libchanora_bridge.dylib" echo "[chanora_bridge script_phase] cargo build aarch64-apple-darwin" cd "$REPO_ROOT" PATH="$HOME/.cargo/bin:$PATH" \ MACOSX_DEPLOYMENT_TARGET=#{MACOS_BRIDGE_DEPLOYMENT_TARGET} \ CMAKE_POLICY_VERSION_MINIMUM=3.5 \ LIBOPUS_STATIC=1 \ LIBOPUS_NO_PKG=1 \ cargo build --release --target aarch64-apple-darwin -p chanora_bridge echo "[chanora_bridge script_phase] cargo build x86_64-apple-darwin" PATH="$HOME/.cargo/bin:$PATH" \ MACOSX_DEPLOYMENT_TARGET=#{MACOS_BRIDGE_DEPLOYMENT_TARGET} \ CMAKE_POLICY_VERSION_MINIMUM=3.5 \ LIBOPUS_STATIC=1 \ LIBOPUS_NO_PKG=1 \ cargo build --release --target x86_64-apple-darwin -p chanora_bridge cd "$REPO_ROOT/apps/chanora_flutter/macos" 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/Versions/A/chanora_bridge" ] && [ "$FW/Versions/A/chanora_bridge" -nt "$BRIDGE_ARM64" ] && [ "$FW/Versions/A/chanora_bridge" -nt "$BRIDGE_X86_64" ]; then echo "[chanora_bridge script_phase] framework already up-to-date" exit 0 fi # Create universal binary with lipo. mkdir -p "$(dirname "$UNIVERSAL")" lipo -create "$BRIDGE_ARM64" "$BRIDGE_X86_64" -output "$UNIVERSAL" rm -rf "$FW" mkdir -p "$FW/Versions/A/Resources" ln -sfh A "$FW/Versions/Current" ln -sfh Versions/Current/Resources "$FW/Resources" cp "$UNIVERSAL" "$FW/Versions/A/chanora_bridge" ln -sfh Versions/Current/chanora_bridge "$FW/chanora_bridge" cat > "$FW/Versions/A/Resources/Info.plist" < CFBundleExecutablechanora_bridge CFBundleIdentifierapp.chanora.bridge CFBundleNamechanora_bridge CFBundlePackageTypeFMWK CFBundleShortVersionString1.0.0 CFBundleVersion1 CFBundleSupportedPlatformsMacOSX MinimumOSVersion#{MACOS_BRIDGE_DEPLOYMENT_TARGET} PLIST install_name_tool -id "@rpath/chanora_bridge.framework/Versions/A/chanora_bridge" \ "$FW/Versions/A/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