Skip to content

recipe: cffi 2.1.1 with working ffi.callback() on iOS - #129

Merged
ndonkoHenri merged 3 commits into
mainfrom
cffi-ios-callbacks
Oct 22, 2026
Merged

ndonkoHenri merged 3 commits into
mainfrom
cffi-ios-callbacks

Conversation

@ndonkoHenri

@ndonkoHenri ndonkoHenri commented Oct 22, 2026 •

Copy link
Copy Markdown

What

cffi 2.1.1 with ffi.callback() working on iOS, plus two CI fixes: testing rebuilt flet-lib* wheels, and pinned Ubuntu runners.

Why

On iOS every ffi.callback() raised MemoryError: Cannot allocate write+execute memory for ffi.callback(), and variadic ABI-mode calls returned garbage on arm64. cffi uses libffi's closure API on Apple only when FFI_AVAILABLE_APPLE is defined, which only Apple's own macOS ffi.h does. With the upstream libffi our iOS builds use, cffi falls back to its own allocator, which maps writable+executable memory, and iOS refuses. libffi itself needs no such memory there (arm64 uses trampoline tables, which is why CPython's ctypes callbacks work).

Upstream fixed this in python-cffi/cffi#265, merged but not in any release yet.

Changes

  • recipes/cffi: 2.1.1, build 1, new ios-ffi-closure-alloc.patch enabling ffi_closure_alloc / ffi_prep_closure_loc / ffi_prep_cif_var under TARGET_OS_IPHONE (Android's preprocessed source is byte-identical). mobile.patch regenerated for 2.1.1, no behaviour change.
    • 2.1.1 rather than rebuilding 2.0.0: PyPI ships official cffi 2.1.1 iOS wheels (cp313-cp315) without the fix, and they outrank 2.0.0. At the same version, our build tag wins.
    • The patch is a TARGET_OS_IPHONE variant rather than #265 verbatim: forge's non-interactive patch reverse-applies a hunk it sees as already present, so a future bump to a release containing #265 would silently remove the fix. This variant lands as dead code instead. Drop it once a release includes #265.
    • Tests: a C-invoked callback (qsort), a callback from a native thread, 3000 closures allocated and freed, and a variadic snprintf. Four of five fail on the old wheels.
  • recipes/soundfile: file-object reads (sf.read(io.BytesIO(...))) now work on iOS. Removed the test asserting the iOS MemoryError, dropped the iOS skip, updated README, example README and a comment.
  • CI: dist-test/ now bumps py3-none (build.sh flet-lib*) wheels to build 9999 too, and Android's loop also matches abi3. Before, a rebuilt flet-lib* at an already-published version and build number could lose to its published copy in the device test.
  • CI: Ubuntu runners pinned to ubuntu-24.04 (what ubuntu-latest resolves to today). ubuntu-latest will move to 26.04, which would change the Android lane (NDK builds, KVM emulator) for every recipe at once; macOS is already pinned to macos-26.

Validation

  • On-device CI, Android + iOS, Python 3.12/3.13/3.14 (packages=cffi:,soundfile:, prebuild_recipes=cffi): all green on the fork. Note soundfile's iOS tests need the new cffi, so they only pass when cffi is built in the same run or published.
  • iOS simulator, before/after: callbacks raise MemoryError and snprintf returns garbage on forge 2.0.0-1 and PyPI 2.1.1; all pass with this build.
  • Not run: physical iOS devices, x86_64 simulator.

Consumer notes

Apps get working ffi.callback() on iOS automatically once cffi 2.1.1-1 is on pypi.flet.dev; for soundfile that means in-memory reads work on both platforms. Details: recipes/soundfile/README.md.

cffi only uses libffi's closure API on Apple when Apple's own ffi.h defines
FFI_AVAILABLE_APPLE; with our upstream libffi it falls back to a W+X mmap that
iOS refuses, so every ffi.callback() raised MemoryError and variadic calls
returned garbage on arm64. ios-ffi-closure-alloc.patch enables libffi's
trampoline-table closures on iOS (upstream python-cffi #265, unreleased).

2.1.1 rather than a 2.0.0 rebuild: PyPI ships official cffi 2.1.1 iOS wheels
(cp313-cp315) without the fix, and they outrank 2.0.0. Build 1 wins the tie.

soundfile: file-object reads now work on iOS, so the MemoryError tripwire test
goes and the README stops calling the limit permanent.
dist-test renamed only cp-tagged wheels, so a rebuilt flet-lib* at an already
published version and build number could lose to its published copy in the
device test. Android's loop also missed abi3; both loops now match
cpXY-cpXY, cpXY-abi3 and py3-none.
@ndonkoHenri ndonkoHenri changed the title recipe: cffi 2.1.1 with working ffi.callback() on iOS recipe: cffi 2.1.1 with working ffi.callback() on iOS Oct 22, 2026
ubuntu-latest will move to 26.04. The Android lane (NDK builds, KVM emulator)
runs there, so that move would change every recipe's CI at once and look like
recipe breakage. Pin it, like macOS is pinned to macos-26, and bump on purpose.
@ndonkoHenri
ndonkoHenri merged commit b38989f into main Oct 22, 2026
8 of 20 checks passed
@ndonkoHenri
ndonkoHenri deleted the cffi-ios-callbacks branch October 22, 2026 17:35
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