Skip to content

Apple: build dart_bridge as a dynamic framework (1.6.0) - #11

Merged
FeodorFitsner merged 1 commit into
mainfrom
release-1.6.0
Jul 25, 2026
Merged

Apple: build dart_bridge as a dynamic framework (1.6.0)#11
FeodorFitsner merged 1 commit into
mainfrom
release-1.6.0

Conversation

@FeodorFitsner

Copy link
Copy Markdown
Contributor

Summary

Builds dart_bridge.xcframework for Apple platforms as a dynamic framework (a .framework with a dylib) instead of a static library. Bumps the version to 1.6.0.

Why

The FFI entry points — serious_python_run, DartBridge_InitDartApiDL, DartBridge_EnqueueMessage, PyInit_dart_bridge — are resolved at runtime via dlsym (Dart's DynamicLibrary.process() and Python's import dart_bridge).

A static library linked into the host app executable does not export its symbols: an iOS executable exports nothing to the dynamic symbol table by default, and the release build strips local symbols. So dlsym(RTLD_DEFAULT) couldn't find them, and release/device builds crashed at startup with:

Failed to lookup symbol 'serious_python_run': dlsym(RTLD_DEFAULT, serious_python_run): symbol not found

Debug/simulator builds don't dead-strip, so they appeared to work — the failure was release/archive (device) only. This is exactly why Android already worked (its CMake build is add_library(... SHARED) → a dynamic .so that exports its symbols) while iOS did not.

A dynamic framework exports its default-visibility symbols and is loaded as its own image, so dlsym resolves them — bringing Apple in line with Android.

What changed

  • apple/build_xcframework.sh: compile the three C sources per slice, then link a dylib (-dynamiclib, install_name @rpath/dart_bridge.framework/dart_bridge) and wrap it in a .framework (flat layout for iOS device/simulator, versioned for macOS), then -create-xcframework from the frameworks. Py_*/Dart_* stay undefined (-undefined dynamic_lookup) and bind at load time from the app's embedded Python.framework and the Dart runtime — the same deferral the static build relied on at app link time, now at load time.
  • CMakeLists.txt: version → 1.6.0.
  • CHANGELOG.md: added (documents the change + consumer impact).

The Linux/Windows/Android builds are unchanged. The output artifact name (dist/dart_bridge-apple.xcframework.zip) and the CI build-apple job are unchanged.

Consumer impact

serious_python_darwin embeds + signs the framework (as it already does for Python.xcframework) instead of static-linking it — no -all_load/-force_load retention or dead-strip keep-alive needed. A matching serious_python change lands separately.

Verified

Built the iOS-device slice locally and confirmed the dylib's export trie contains serious_python_run, DartBridge_InitDartApiDL, DartBridge_EnqueueMessage, PyInit_dart_bridge with install_name @rpath/dart_bridge.framework/dart_bridge. End-to-end: a Flet iOS release app that embeds it boots on-device and resolves the symbols (previously crashed at startup).

Note

-undefined dynamic_lookup warns "deprecated on iOS." It works and matches how CPython extension modules link, but App Store acceptance is worth confirming; the alternative is linking the dylib against Python.framework explicitly.

build_xcframework.sh now produces dart_bridge.xcframework as a dynamic
framework (a .framework with a dylib) instead of a static library.

The FFI entry points (serious_python_run, DartBridge_*, PyInit_dart_bridge)
are resolved at runtime via dlsym (Dart's DynamicLibrary.process() and
Python's 'import dart_bridge'). A static library linked into the host app
executable does not export its symbols -- an iOS executable exports nothing
by default and the release build strips local symbols -- so
dlsym(RTLD_DEFAULT) could not find them and release/device builds crashed at
startup with 'Failed to lookup symbol serious_python_run'. A dynamic
framework exports its default-visibility symbols and is loaded as its own
image, so dlsym resolves them, matching the Android .so (CMake SHARED) that
already worked. Only release/archive builds were affected; debug/simulator
don't dead-strip and appeared to work.

Py_*/Dart_* are left undefined (-undefined dynamic_lookup) and bound at load
time from the app's Python.framework and the Dart runtime. Linux/Windows/
Android are unchanged (already dynamic).
@FeodorFitsner
FeodorFitsner merged commit fb09ebe into main Jul 25, 2026
34 checks passed
@FeodorFitsner
FeodorFitsner deleted the release-1.6.0 branch July 25, 2026 20:13
FeodorFitsner added a commit to flet-dev/serious-python that referenced this pull request Jul 25, 2026
…240)

* Bump to 4.4.0: dart_bridge as a dynamic framework on Apple platforms

Fixes built iOS apps crashing at startup with 'Failed to lookup symbol
serious_python_run: dlsym(RTLD_DEFAULT, serious_python_run): symbol not
found'.

dart_bridge's FFI entry points (serious_python_run, DartBridge_*,
PyInit_dart_bridge) are resolved at runtime via dlsym -- from Dart through
DynamicLibrary.process() and from Python through 'import dart_bridge'. It
previously shipped as a static archive linked into the host app executable,
and an iOS executable exports nothing to the dynamic symbol table by default
(release builds also strip local symbols), so those lookups failed. Only
release/archive device builds were affected: debug/simulator don't
dead-strip. Android was never affected -- its dart_bridge is a dynamic .so.

dart_bridge 1.6.0 now builds dart_bridge.xcframework as a dynamic framework
(flet-dev/dart-bridge#11), so it is embedded + signed into the app like
Python.xcframework and its symbols stay exported. Removes what a loaded
image no longer needs: the SwiftPM -all_load/-force_load retention, the
CocoaPods -all_load, and the plugin's dead-strip keep-alive references.

Re-pins the bundled python-build snapshot to 20260725 (dart_bridge
1.5.1 -> 1.6.0, Pyodide 3.14 314.0.2 -> 314.0.3). Python versions
(3.12.13 / 3.13.14 / 3.14.6) are unchanged and all platform runtimes are
byte-identical to 20260720.

Also bumps .fvmrc to Flutter 3.44.8.

* Re-pin dart_bridge to 1.6.1 (macOS framework symlink fix)

python-build's 20260725 manifest was updated in place to dart_bridge 1.6.1,
so regenerate the version tables against it.

1.6.1 zips the Apple xcframework with -y so the macOS slice's versioned
framework symlinks (Versions/Current -> A, dart_bridge and Resources ->
Versions/Current/...) survive publishing. Under 1.6.0 they were followed and
stored as real files/directories, producing a malformed bundle that macOS
codesign rejected ('Couldn't resolve framework symlink for
.../Versions/Current', 'code object is not signed at all'). iOS was
unaffected -- its slices use a flat layout with no symlinks. See
flet-dev/dart-bridge#12.

Binaries are unchanged from 1.6.0; only the artifact packaging differs.
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.

1 participant