Skip to content

Add XCFramework build script with C-ABI wrapper#3

Open
olilarkin wants to merge 1 commit into
mainfrom
claude/swift-xcframework-script-lVAIw
Open

Add XCFramework build script with C-ABI wrapper#3
olilarkin wants to merge 1 commit into
mainfrom
claude/swift-xcframework-script-lVAIw

Conversation

@olilarkin

Copy link
Copy Markdown
Owner

Summary

  • New Scripts/build-xcframework.sh produces a self-contained StableAudioKit.xcframework with all transitive dependencies (mlx-swift, swift-sentencepiece, swift-numerics) libtool-merged into a single static framework binary, plus a per-slice mlx.metallib. Slices: macOS, iOS device/sim, visionOS device/sim.
  • New Sources/StableAudioKit/CInterop.swift exposes the pipeline as a C-ABI surface (@_cdecl) so the framework can be loaded from C/C++ apps in addition to Swift apps. The wrapper bridges StableAudioPipeline.generate's async actor method through a DispatchSemaphore so the C entry points are blocking.
  • New Scripts/xcframework-resources/ holds the public C header (StableAudioKit.h), framework module.modulemap (with link "c++" for sentencepiece), and Info.plist.tmpl.
  • Scripts/compile-mlx-metallib.sh now honors METAL_TARGET and MLX_SWIFT_DIR so it can be retargeted per slice during the XCFramework build.
  • The build script temporarily rewrites the ../mlx-swift path dependency in Package.swift to https://github.com/olilarkin/mlx-swift (override via MLXSWIFT_BRANCH) and restores the file on exit.

Usage

./Scripts/build-xcframework.sh
# -> build/xcframework/StableAudioKit.xcframework

C consumers #include <StableAudioKit/StableAudioKit.h>; Swift consumers import StableAudioKit and get the existing rich API. Weights and the SentencePiece tokenizer model are still user-supplied at runtime (the XCFramework ships code + mlx.metallib only).

Test plan

  • Run ./Scripts/build-xcframework.sh on macOS with Xcode 15+ and confirm build/xcframework/StableAudioKit.xcframework contains all 5 slices
  • Verify each slice's Modules/StableAudioKit.swiftmodule/ has a .swiftinterface (library evolution output)
  • Verify each slice contains mlx.metallib of non-zero size
  • Smoke-test from a small Swift consumer app: import StableAudioKit, run StableAudioPipeline.load(from:) + generate(_:)
  • Smoke-test from a small C consumer: stable_audio_pipeline_createstable_audio_generatestable_audio_write_wav
  • Confirm Package.swift is restored unchanged after the script runs (including after Ctrl-C mid-build)

https://claude.ai/code/session_01LgAV8cNyoB87u2zUVoTkpz


Generated by Claude Code

CInterop.swift exposes a C-ABI surface (@_cdecl) so the kit can be loaded
from C/C++ apps as well as Swift apps. The wrapper bridges the Swift
actor's async generate() through a semaphore so the C surface is blocking.

build-xcframework.sh archives the SPM package per Apple-platform slice
(macOS, iOS device/sim, visionOS device/sim), merges every transitive
.a (mlx-swift, sentencepiece, etc.) with libtool, compiles mlx.metallib
for the slice, assembles a static .framework bundle, and combines the
slices with xcodebuild -create-xcframework. It temporarily rewrites the
../mlx-swift path dependency to a public git URL during the build.

compile-mlx-metallib.sh now honors METAL_TARGET and MLX_SWIFT_DIR env
vars so it can be called per-slice from the XCFramework build.

https://claude.ai/code/session_01LgAV8cNyoB87u2zUVoTkpz

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1e360ab8f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +191 to +196
# Copy every per-arch artifact (swiftmodule/swiftdoc/swiftinterface/abi.json).
find "$best" -maxdepth 1 -type f \
\( -name "*.swiftmodule" -o -name "*.swiftdoc" \
-o -name "*.swiftinterface" -o -name "*.private.swiftinterface" \
-o -name "*.abi.json" -o -name "*.swiftsourceinfo" \) \
-exec cp {} "$fw/Modules/$MODULE_NAME.swiftmodule/" \;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include dependency Swift modules in the XCFramework

For Swift consumers, copying only StableAudioKit.swiftmodule leaves the generated .swiftinterface with unresolved public dependency types: AudioWriter.write(_ audio: MLXArray, ...) is public, so the interface imports/references MLX, but this script only libtool-merges the dependency binaries and never packages the dependency .swiftmodule/.swiftinterface files. A client that adds only this supposedly self-contained XCFramework and runs import StableAudioKit will fail to compile because it cannot load the MLX module; either hide those dependency types from the public API or include the required Swift modules alongside the framework.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants