From f125116924584a38eb2b417b3342e5769eeb9549 Mon Sep 17 00:00:00 2001 From: Alex-Wengg Date: Mon, 21 Sep 2026 11:33:19 -0400 Subject: [PATCH] feat(xcframework): add Mac Catalyst + x86_64 simulator slices FluidAudio consumers building for Mac Catalyst fail to link because the xcframework only ships ios-arm64, ios-arm64-simulator and macos-arm64_x86_64; xcodebuild refuses to satisfy a Catalyst build from the native macOS slice (reported by an Action Phrase developer against FluidAudio v0.15.x, which links NemoTextProcessing unconditionally on toolchains < 6.2). Build the two tier-2 Rust targets aarch64-apple-ios-macabi and x86_64-apple-ios-macabi, lipo them into one universal archive and add it as a fourth slice (ios-arm64_x86_64-maccatalyst). While here, add x86_64-apple-ios so the simulator slice is universal too (Intel Macs and Rosetta simulators were unsupported). Verified locally: the Catalyst archives carry LC_BUILD_VERSION platform 6 (arm64 minos 14.0, x86_64 minos 13.1), export the nemo_* FFI, and a C program compiled with -target arm64-apple-ios17.0-macabi links against the universal archive and returns correct ITN/TN output. xcodebuild -create-xcframework itself runs on CI (macos-latest); the workflow's pull_request path filter covers this script. --- .github/workflows/xcframework.yml | 7 ++++--- build-xcframework.sh | 34 +++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.github/workflows/xcframework.yml b/.github/workflows/xcframework.yml index 219d3f1..a9a54b7 100644 --- a/.github/workflows/xcframework.yml +++ b/.github/workflows/xcframework.yml @@ -1,8 +1,9 @@ name: XCFramework # Builds NemoTextProcessing.xcframework (macOS universal + iOS device + iOS -# simulator) so Apple consumers (e.g. FluidAudio) can link the native FFI via -# a SwiftPM `.binaryTarget(url:checksum:)` instead of hand-linking the lib. +# simulator universal + Mac Catalyst universal) so Apple consumers (e.g. +# FluidAudio) can link the native FFI via a SwiftPM `.binaryTarget(url:checksum:)` +# instead of hand-linking the lib. on: push: tags: ["v*"] @@ -27,7 +28,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: - targets: aarch64-apple-darwin,x86_64-apple-darwin,aarch64-apple-ios,aarch64-apple-ios-sim + targets: aarch64-apple-darwin,x86_64-apple-darwin,aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi - name: Build xcframework run: ./build-xcframework.sh diff --git a/build-xcframework.sh b/build-xcframework.sh index 94f47c7..190afa1 100755 --- a/build-xcframework.sh +++ b/build-xcframework.sh @@ -2,7 +2,12 @@ set -e # Build universal static library for Apple platforms -# Outputs: NemoTextProcessing.xcframework +# Outputs: NemoTextProcessing.xcframework with slices for macOS (arm64 + +# x86_64), iOS device (arm64), iOS Simulator (arm64 + x86_64) and Mac Catalyst +# (arm64 + x86_64). Rust targets required (rustup target add ...): +# aarch64-apple-darwin x86_64-apple-darwin aarch64-apple-ios +# aarch64-apple-ios-sim x86_64-apple-ios +# aarch64-apple-ios-macabi x86_64-apple-ios-macabi SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BUILD_DIR="$SCRIPT_DIR/build" @@ -23,6 +28,15 @@ cargo build --release --features "ffi,fst-engine" --target aarch64-apple-ios echo "Building for iOS Simulator (arm64)..." cargo build --release --features "ffi,fst-engine" --target aarch64-apple-ios-sim +echo "Building for iOS Simulator (x86_64)..." +cargo build --release --features "ffi,fst-engine" --target x86_64-apple-ios + +echo "Building for Mac Catalyst (arm64)..." +cargo build --release --features "ffi,fst-engine" --target aarch64-apple-ios-macabi + +echo "Building for Mac Catalyst (x86_64)..." +cargo build --release --features "ffi,fst-engine" --target x86_64-apple-ios-macabi + echo "Creating universal macOS library..." mkdir -p "$BUILD_DIR/macos" lipo -create \ @@ -30,6 +44,20 @@ lipo -create \ target/x86_64-apple-darwin/release/libtext_processing_rs.a \ -output "$BUILD_DIR/macos/libtext_processing_rs.a" +echo "Creating universal iOS Simulator library..." +mkdir -p "$BUILD_DIR/ios-simulator" +lipo -create \ + target/aarch64-apple-ios-sim/release/libtext_processing_rs.a \ + target/x86_64-apple-ios/release/libtext_processing_rs.a \ + -output "$BUILD_DIR/ios-simulator/libtext_processing_rs.a" + +echo "Creating universal Mac Catalyst library..." +mkdir -p "$BUILD_DIR/maccatalyst" +lipo -create \ + target/aarch64-apple-ios-macabi/release/libtext_processing_rs.a \ + target/x86_64-apple-ios-macabi/release/libtext_processing_rs.a \ + -output "$BUILD_DIR/maccatalyst/libtext_processing_rs.a" + echo "Creating XCFramework..." # NOTE: headers live in swift/include/CNemoTextProcessing/ so each slice gets # Headers/CNemoTextProcessing/module.modulemap. Xcode's ProcessXCFramework copies @@ -43,7 +71,9 @@ xcodebuild -create-xcframework \ -headers swift/include \ -library target/aarch64-apple-ios/release/libtext_processing_rs.a \ -headers swift/include \ - -library target/aarch64-apple-ios-sim/release/libtext_processing_rs.a \ + -library "$BUILD_DIR/ios-simulator/libtext_processing_rs.a" \ + -headers swift/include \ + -library "$BUILD_DIR/maccatalyst/libtext_processing_rs.a" \ -headers swift/include \ -output "$OUTPUT_DIR/NemoTextProcessing.xcframework"