fix(darwin): framework-ize ctypes .dylib libs so they load on the iOS simulator#223
Open
ndonkoHenri wants to merge 1 commit into
Open
fix(darwin): framework-ize ctypes .dylib libs so they load on the iOS simulator#223ndonkoHenri wants to merge 1 commit into
.dylib libs so they load on the iOS simulator#223ndonkoHenri wants to merge 1 commit into
Conversation
… simulator
The darwin site-packages sync only converted Python C-extension *.so files
into device+simulator xcframeworks (dylib_ext=so); plain *.dylib shared libs
(the kind a ctypes wrapper dlopens — e.g. llama-cpp-python's libllama +
libggml*) were skipped and shipped straight from the archs[0]=iphoneos DEVICE
slice. On the simulator that dylib then fails to dlopen:
mach-o file (...libllama.dylib), but incompatible platform
(have 'iOS', need 'iOS-simulator')
C-extensions pass because their .so is framework-ized into a fat (device +
simulator) xcframework and Xcode extracts the right per-slice binary; .dylib
libs never got that treatment.
Teach create_xcframework_from_dylibs to take the file extension as a parameter
and iterate both `so` and `dylib` in sync_site_packages.sh, so .dylib libs also
become a device+fat-simulator xcframework + a .fwork pointer, exactly like .so.
For .dylib, PRESERVE the original install-name (gate the `install_name_tool -id`
rewrite on ext=so). Multi-lib ctypes packages — libllama depends on
@rpath/libggml*.dylib — then resolve their siblings: the wrapper preloads the
dependency libs with RTLD_GLOBAL and dyld matches them by their preserved
install-name. The .so path is untouched (ext=so keeps the -id rewrite), so
C-extensions (numpy, stdlib) are byte-for-byte identical.
Guards: `-type f` skips SONAME symlink chains; conversion is skipped when a lib
has no simulator slice (avoids an empty `lipo`), leaving it as-is.
Tested with llama-cpp-python on the iOS simulator: import + native ctypes calls
+ real GGUF inference all pass (previously failed with the platform mismatch).
numpy (.so) unchanged; the device xcframework slice was validated statically
(platform 2, install-names preserved, inter-lib deps intact).
There was a problem hiding this comment.
Pull request overview
Updates the Darwin site-packages packaging pipeline so ctypes-loaded .dylib libraries (not just CPython .so extensions) are converted into per-slice xcframework-backed frameworks for iOS, allowing them to load correctly on the iOS simulator.
Changes:
- Extend site-packages conversion to process both
*.soand*.dylibartifacts during iOS staging. - Parameterize the xcframework helper by extension and skip
install_name_tool -idrewriting for.dylibto preserve install-names. - Add a guard to leave device-only artifacts unconverted when no simulator slice exists.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/serious_python_darwin/darwin/xcframework_utils.sh |
Adds extension-parameter support, simulator-slice existence guard, and conditional install-name rewriting for .so only. |
src/serious_python_darwin/darwin/sync_site_packages.sh |
Expands the conversion loop to framework-ize both .so and .dylib files into xcframeworks during iOS packaging. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Python package that ships plain
.dylibshared libraries loaded viactypes(rather than a CPython.soextension) fails todlopenon the iOS simulator:C-extensions (numpy, stdlib) work fine on the simulator; only
.dylib-shipping packages fail.Root cause
The darwin site-packages sync framework-izes only
*.sofiles (xcframework_utils.sh: dylib_ext=so). Each.sobecomes a device+simulator xcframework + a.fworkpointer, and Xcode extracts the correct per-slice binary — that's why C-extensions load on both device and simulator. Plain*.dylibfiles are skipped entirely and copied straight from the device base (sync_site_packages.shcopiesarchs[0] = iphoneos.arm64), so a simulator app ends up with a device-platform dylib.The simulator/device intent never reaches this layer:
flet build ipaandflet build ios-simulatorboth callserious_python … --platform iOS; they only diverge later atflutter build ios --simulator. So the fix has to make the.dylibpath platform-correct on its own.Fix (2 files,
serious_python_darwin/darwin/)sync_site_packages.sh: iterate bothsoanddylibin the conversion loop, passing the extension to the helper.xcframework_utils.sh: take the extension as a parameter and use it for the simulator-slice globs, so.dyliblibs get the same device+fat-simulator xcframework +.fworkas.so..dylib(gate theinstall_name_tool -idrewrite onext=so). This is the key to multi-lib ctypes packages: e.g.libllamahasLC_LOAD_DYLIB @rpath/libggml.dylib. Keeping the install-name lets a wrapper that preloads its dependency libs withRTLD_GLOBALsatisfy those loads via dyld's already-loaded-image match. Rewriting the id to the framework path (the.sobehavior) would break that.Non-regression / blast radius
.sopath is byte-for-byte unchanged (ext=sokeeps the id-rewrite) — verified numpy/stdlib frameworks are identical..dylib. Theflet-lib*ctypes recipes all ship a single unversionedlib*.soon purpose, so nothing shipping today produces a.dylib— this is purely additive..dylibrecipes:-type fskips SONAME symlink chains, and conversion is skipped when a lib has no simulator slice (avoids an emptylipo), leaving it as-is.Testing
llama-cpp-pythonon the iOS simulator (arm64):import+ native ctypes calls + real GGUF inference all pass (previously the platform-mismatchdlopenfailure). Verified against a normally-built app on both the published1.0.1darwin scripts and this branch's scripts..sonon-regression: numpy frameworks unchanged (id still rewritten to the framework path).ios-arm64:platform 2, install-names preserved, inter-lib deps intact). Device runtime / code-signing not verified on hardware — the.dylib→framework path uses the same Xcode embed/sign as the existing.soframework path.Proposed CHANGELOG change