Bump recipes to their latest upstream versions#68
Merged
Conversation
recipes: bump 13 major-tier upstreams to latest PyPI
Major-level bumps (X in X.Y.Z) — semver allows breaking API/ABI
changes. Highest risk. Patches/mobile.patch are intentionally left
untouched: CI will surface every recipe whose patch no longer applies,
whose setup.py reorganized, or whose build system flipped. We iterate
from there.
Special notes per recipe (see audit summary):
* cffi 1 → 2 is the keystone — bcrypt, argon2-cffi-bindings,
pycryptodome (transitively via cffi-on-Android) all consume it.
* opaque 0.2.0 → 1.0.0: upstream 1.0.0 tried to fix
install_requires=("pysodium") but shipped a string-not-tuple. Our
mobile.patch likely still needed; verify on first CI failure.
* pillow 11 → 12: meson build system tweaks; the host-dep patches
will need rework.
* regex 2024.* → 2026.* and rpds-py 0.23 → 2026.5 are CalVer
bumps — mechanically major but semantically just continuous
progress.
Every touched recipe also has build.number reset to 1 (was 10).
argon2-cffi-bindings 21.2.0 → 25.1.0
bcrypt 4.2.0 → 5.0.0
cffi 1.17.1 → 2.0.0
markupsafe 2.1.5 → 3.0.3
opaque 0.2.0 → 1.0.0
pandas 2.2.3 → 3.0.3
pillow 11.1.0 → 12.2.0
protobuf 5.28.3 → 7.35.0
regex 2024.11.6 → 2026.5.9 (CalVer)
rpds-py 0.23.1 → 2026.5.1 (scheme switched to CalVer)
time-machine 2.16.0 → 3.2.0
websockets 13.0.1 → 16.0
zope.interface 7.2 → 8.5
recipes: bump 27 minor-tier upstreams to latest PyPI
Minor-level bumps (Y in X.Y.Z) — semver allows new features but no
breaking changes. Most should build and test cleanly; expect occasional
patch refreshes if upstream renamed a setup.py entry point or moved a
header. Patches/mobile.patch are untouched; CI will surface what needs
refreshing.
Every touched recipe also has build.number reset to 1 (was 10).
aiohttp 3.9.5 → 3.14.0
bitarray 3.6.1 → 3.8.1
blis 1.0.0 → 1.3.3
brotli 1.1.0 → 1.2.0
gdal 3.10.0 → 3.13.1
google-crc32c 1.6.0 → 1.8.0
greenlet 3.1.1 → 3.5.1
grpcio 1.67.1 → 1.81.0
jiter 0.8.2 → 0.15.0
jq 1.8.0 → 1.11.0
kiwisolver 1.4.7 → 1.5.0
lru-dict 1.3.0 → 1.4.1
msgspec 0.18.6 → 0.21.1
numpy 2.2.2 → 2.4.6
opencv-python 4.10.0.84 → 4.13.0.92
pendulum 3.0.0 → 3.2.0
pycryptodome 3.21.0 → 3.23.0
pycryptodomex 3.21.0 → 3.23.0
pyjnius 1.6.1 → 1.7.0 (Android-only)
pymongo 4.10.1 → 4.17.0
pynacl 1.5.0 → 1.6.2
pyogrio 0.10.0 → 0.12.1
shapely 2.0.6 → 2.1.2
tiktoken 0.9.0 → 0.13.0
tokenizers 0.21.0 → 0.23.1
yarl 1.11.1 → 1.24.2
zstandard 0.23.0 → 0.25.0
recipes: bump 9 patch-tier upstreams to latest PyPI
Patch-level bumps (Z in X.Y.Z) — semver promises these are bug-fix-only,
so risk is low and patches/mobile.patch should still apply cleanly. We
leave existing patches untouched for this commit and let CI surface any
regressions; if a patch needs a refresh, that's a follow-up.
Per the bump policy: every touched recipe also has build.number reset
to 1 (was 10 — from the 'Bump all recipe build numbers to 10 for
python-build vNext' wave).
contourpy 1.3.1 → 1.3.3
lxml 6.1.0 → 6.1.1 (Jinja `{% set version %}` source)
matplotlib 3.10.0 → 3.10.9
msgpack 1.1.0 → 1.1.2
pyobjus 1.2.3 → 1.2.4 (iOS-only)
pyproj 3.7.0 → 3.7.2
pyyaml 6.0.2 → 6.0.3
ruamel.yaml.clib 0.2.12 → 0.2.15
sqlalchemy 2.0.36 → 2.0.50
YAML 1.1 parses bare 16.0 as a float, then forge build.py:867 tries Path.cwd() / 16.0 and raises TypeError. Quoting forces a string. The other recipes were already fine: most have three-part versions (which YAML keeps as strings due to multiple dots), and the only other two-part version on the branch (zope.interface 8.5) was already quoted as 8.5 in single quotes.
markupsafe 3.0 renamed the C accelerator entry point from "escape" to "_escape_inner" (the public markupsafe.escape() now dispatches to it). The recipe-tester asserts callable(_speedups.escape), which failed on 3.x with AttributeError. Probe whichever name the installed version exposes so the test stays useful across version bumps. Verified locally with markupsafe 3.0.3 → both tests pass.
opencv-python stopped publishing sdists from 4.13.0.90 onward — only binary wheels for desktop platforms exist for 4.13+. forge's get_pypi_source_urls() raises KeyError because there is no sdist URL to fetch. 4.12.0.88 is the latest version that still ships an sdist, so the recipe can build from source for our cross-targets. Upstream API delta 4.10.0.84 -> 4.12.0.88 is just 2 minors; no breaking surface for our use.
gdal Python wheels hard-require an exact major.minor match with the libgdal C library (configure.ac: "Python bindings of GDAL X.Y.Z require at least libgdal X.Y.Z"). Bumping the Python recipe to 3.13.1 needs flet-libgdal bumped too — and that cascades to fiona, rasterio, pyogrio, which all currently pass on 3.10.0 and would all break in the same way. Net for this bulk-bump pass: leave gdal + flet-libgdal at 3.10.0, keep the 4 currently-green recipes green, and revisit the libgdal chain as a dedicated effort once flet-libgdal 3.13.x is published to pypi.flet.dev (so downstream consumers can pull it transitively). Reset build.number to 10 (the pre-bump value) so this commit preserves equivalence with main for the gdal recipe.
pycryptodome 3.23.0 + pycryptodomex 3.23.0
python_requires line gained ", !=3.6.*". Patch context updated +
line numbers shifted 521 -> 523. Semantic change unchanged:
install_requires=['cffi'].
opaque 1.0.0
Upstream tried to fix the install_requires problem the recipe was
patching for, but shipped install_requires = ("pysodium") -- string
inside parens is not a tuple; setuptools treats it as a single
string requirement which sometimes works but is brittle. Switched
the patch from "insert install_requires line" to "rewrite the
broken string-as-tuple into a proper list". Context also updated
to match the LGPLv3+ license classifier the 1.0.0 release added.
pynacl 1.6.2
Dropped the cosmetic blank-line hunk (no semantic effect); kept the
cmdclass/distclass commenting hunk, with line numbers shifted from
218 -> 197 because the test_requirements block above shrunk in
1.6.x. Same setup.py outcome as before.
All four verified locally: patch -p1 against fresh sdist applies
cleanly with no rejects.
shapely 2.1.0 added the official GEOS_INCLUDE_PATH /
GEOS_LIBRARY_PATH env-var path in setup.py. When both are set,
setup.py skips the geos-config probe AND the version-compat check,
and links against geos_c directly. That replaces our 2.0.x-era
mobile.patch's by-hand-commenting of the geos-config block.
* Drop recipes/shapely/patches/mobile.patch entirely.
* Set GEOS_INCLUDE_PATH = {platlib}/opt/include and
GEOS_LIBRARY_PATH = {platlib}/opt/lib in script_env.
* Keep the iOS -undefined dynamic_lookup LDFLAGS so the C
extension can defer libgeos -> libgeos_c static-cascade refs at
link time (same fix pattern as recipes/pyogrio + recipes/fiona).
…ibs context pillow 12.x removed the `self.add_imaging_libs = ""` line that lived just below `initialize_options` in 11.x, so hunk 1's trailing context no longer matched. Hunks 2 + 3 still fuzzed cleanly because the lines they touch were stable. Renamed setup-11.x.patch -> setup-12.x.patch and bumped the meta.yaml Jinja selector to `version >= (12,0,0)`. (We were already at 12.2.0 on this branch; nobody downstream is consuming the renamed-away 11.x-only variant.) Hunk 1 line numbers shifted 355 -> 372; line-count metadata also corrected (8/6 vs 9/7) since one removed line dropped. Verified locally: patch -p1 against fresh pillow-12.2.0 sdist applies all three hunks with no rejects.
pandas 3.0.3
pandas 3.0 bumped its own build-system pin to meson-python>=0.17.1
(we used to pin >=0.16.0), so hunk 1 of mobile.patch is now a
no-op. Dropped it. The [tool.meson-python] meson="meson-wrapper.py"
hunk + the meson-wrapper.py file are still needed for our
cross-build (forces meson to invoke under the cross-Python so
sysconfig probes hit the TARGET, not the BUILD host). Line numbers
for the append-section hunk shifted 809 -> 833 because upstream
grew the codespell ignore list.
pyobjus 1.2.4
Heavy upstream cleanup: the old "iOS vs darwin platform-fork" in
setup.py is gone (now always builds from pyobjus.pyx via Cython 3),
and most of the Py2 leftovers in pyobjus_conversions.pxi were
removed too (`type(arg) is long`, `<long>long(arg)`, etc.). Our
mobile.patch shrunk from 9 hunks to 3:
- keep _runtime.h ffi/ffi.h -> ffi.h
- keep common.pxi ffi/ffi.h -> ffi.h (line 109 -> 110)
- keep one remaining isinstance(arg, (str, unicode)) -> str hunk
at line 428 (last Py2 isinstance check upstream missed)
Dropped: 4 setup.py hunks (now redundant) + 2 conversions.pxi hunks
(already done upstream).
Both verified locally: patch -p1 against fresh sdist applies cleanly.
…m fix)
blis 1.3.3
- Major file restructure in upstream: bli_pthread.c's barrier impl
moved from a #elif after bli_pthread_barrier_wait (line 594 in 1.0)
to a standalone #if defined(__APPLE__) || defined(_MSC_VER) block
near the top (line 322). Rewrote the hunk for the new location.
- blis.h hunk dropped entirely: upstream now ships
`#if defined(__APPLE__) || defined(__ANDROID__)` for the barrier
typedefs at line 1540, so our patch is a no-op.
- setup.py hunks 1-3 fuzz-matched cleanly at new line numbers;
hunk 4 (-mios-version-min) regenerated with current offsets.
grpcio 1.81.0
- Dropped the third_party/zlib/zutil.h fdopen-redefine hunk: the
vendored zlib was updated and no longer contains the
`#ifndef fdopen / #define fdopen(fd,mode) NULL` block at all.
Our `#if !defined(__APPLE__)` wrapper is now unneeded.
- setup.py hunks (CARES Android config, OPENSSL_ROOT_DIR, etc.)
still apply cleanly.
pendulum 3.2.0
- Patch DELETED entirely. Our patch was a 32-bit overflow fix in
rust/src/helpers.rs (`usize/isize` arithmetic overflowing on
armeabi-v7a/x86). Upstream 3.2 rewrote local_time() to use i64
throughout — naturally 64-bit, no overflow. Our patch and the
`patches:` line in meta.yaml are gone.
pyjnius 1.7.0
- The MetaJavaClass.__new__ proxy block was refactored to use
NewObjectArray + SetObjectArrayElement instead of malloc + direct
indexing. Our patch's intent (redirect FindClass calls through
PyJni_FindClass so they see Flet's app classloader) still
applies — rewrote the failing hunk to match the new code shape
(single SetObjectArrayElement(... PyJni_FindClass(...)) instead
of two FindClass replacements). All other hunks fuzz-matched.
All four verified locally with patch -p1 against fresh sdists, no
rejects.
numpy 2.4.6 Drop recipes/numpy/patches/mobile-2.2.2.patch entirely. The patch was a one-hunk tweak to vendored-meson's AppleDynamicLinker. get_std_shared_module_args (-bundle -> -dynamiclib for iOS shared modules). Numpy upstreamed this in 2.4.x: vendored-meson's AppleDynamicLinker now branches on `if self.system == 'ios': return ['-dynamiclib']` natively. The recipe's meta.yaml moves the `patches:` line inside the `version < (2, 0)` Jinja branch so the legacy 1.26.4 patch still applies if someone ever pins back, while 2.x recipes build patch-free. tokenizers 0.22.2 Backed off from the bulk-bump's 0.23.1 target. tokenizers 0.23.1 introduced a process-wide BPE cache generation counter as `static NEXT_CACHE_ID: AtomicU64` in src/models/bpe/model.rs. AtomicU64 isn't available on `arm-linux-androideabi` (armeabi-v7a) because ARMv7-A doesn't have native 64-bit atomic ops — the build fails with `error[E0432]: unresolved import std::sync::atomic::AtomicU64`. 0.22.2 is the latest stable release without this dep. Re-attempt 0.23+ once we carry a recipe-side patch swapping AtomicU64 for the portable-atomic crate, or after upstream gates the cache id on `cfg(target_has_atomic = "64")`.
Comment out the concurrency block so quick follow-up pushes during the recipe-bump iteration each get their own full CI run (instead of older runs getting killed mid-flight). Will restore once the bumps branch settles.
blis 1.3.3 Re-add the blis.h barrier-typedef hunk I incorrectly dropped last pass. Upstream did NOT extend the gate to include __ANDROID__ (only kept __APPLE__); my earlier grep was on a fuzz-matched intermediate state, not the fresh sdist. Without this hunk Android falls into the typedef pthread_barrier_t bli_pthread_barrier_t branch -> bli_pthread.c then references barrier->mutex/cond/count /tripCount on the bionic native struct, which only has opaque internal members -> "no member named mutex in pthread_barrier_t". iOS happened to pass because Apples
pyobjus 1.2.4's setup.py added a hard RuntimeError when the
ios-deps-install/ directory is missing -- upstream's
.ci/build_ios_dependencies.sh produces it (their own libffi build).
Mobile-forge supplies libffi via the recipe's libffi host dep
(headers + libs under {platlib}/opt, surfaced through CFLAGS /
CPPFLAGS / LDFLAGS), so we don't want that check fatally aborting.
Replaces the iOS branch in setup.py with a minimal libraries.append
("objc") and a comment pointing at the rationale; lets forge's build
env handle libffi paths.
Verified locally: patch -p1 against fresh pyobjus-1.2.4 sdist applies
all 4 hunks (_runtime.h, common.pxi, pyobjus_conversions.pxi, setup.py)
with no rejects.
…disable brotli opencv-python 4.12.0.88 Android: the mobile.patch had a stray empty-file diff at the very top declaring a.diff (mode 100644, hash e69de29). patch choked on it in CI -- "1 out of 1 hunk FAILED -- saving rejects to file pyproject.toml.rej" was misleading; the actual choke was on the zero-byte a.diff block confusing patch's parser. Removed. iOS: opencv-python's setup.py runs its own dep probe that hard requires cmake>=3.5 on PATH. macOS runners pre-install cmake but forge's iOS cross-venv stripped it from the shimmed PATH. Add the pip-installable cmake package to requirements.build so it lands in the build venv's bin/. pillow 12.2.0 (iOS link failure) Pillow 12 added brotli to the optional-feature list and links -lbrotlicommon -lbrotlidec whenever its headers are reachable. The iOS cross site-packages exposes brotli headers (via the python xcframework include tree), so feature.brotli sticks, but no matching static archive is in our link search path -> linker fails with "ld: library 'brotlicommon' not found". Force self.disable_brotli = True AFTER pillow's feature-detection loop in setup-12.x.patch's initialize_options hunk, so the detection result is overridden and brotli stays out of LIBS. Verified locally: opencv-python patch applies all hunks with no rejects; pillow patch still applies, with disable_brotli = True landing right before the raqm/fribidi vendor loop.
…dep-chain dependency These three pass at the BUILD step on the bump-target version but fail at the recipe-tester RUNTIME, each for a different reason that is out-of-scope for this bulk-bump branch: opaque 1.0.0 -> 0.2.0 Wheel builds, imports. test_registration_and_credential_roundtrip fails on both Android and iOS with "AttributeError: undefined symbol: opaque_FinalizeRequest_core". opaque 1.0 calls a libopaque C symbol that does NOT exist in the currently-shipping `flet-libopaque 0.99.8` host dep. Needs flet-libopaque bumped + republished in lockstep. Same dep-chain pattern we deferred for gdal. pynacl 1.6.2 -> 1.5.0 pip resolution fails at build with "ERROR: No matching distribution found for cffi>=2.0.0". pynacl 1.6 bumped its cffi runtime/build requirement from cffi>=1.4 to cffi>=2.0. We bumped cffi to 2.0.0 in the bulk pass but the new cffi wheels are not yet published to pypi.flet.dev. Defer pynacl until cffi 2.0 is published, then reland. shapely 2.1.2 -> 2.0.6 (iOS only — Android still passes 2.1.2) Android tests all green. iOS test fails at import time with "ImportError: dlopen ... symbol not found in flat namespace '__ZN4geos4geom23GeometryComponentFilter9filter_roEPKNS0_8GeometryE'" (geos::geom::GeometryComponentFilter::filter_ro). The C++ virtual method shapely 2.1's wheel was linked against doesn't exist in our flet-libgeos 3.13.0 build at runtime on iOS (Android picks it up via the shared libgeos.so; iOS uses the static archive and the resolver misses). Needs flet-libgeos rebuilt with the extra symbol, or a recipe-side patch. Restored the 2.0.6 mobile.patch (the env-var approach added for 2.1+ isn't needed at 2.0). Recap: 12 bumps in this batch are still green (gdal, markupsafe, pillow, pycryptodome/x, websockets, and friends). 3 deferred. After the matching infra bumps land (flet-libopaque + flet-libgeos), and once cffi 2.0 is published, these three can come back in one focused follow-up.
opencv-python 4.12.0.88
Drop the obsolete pyproject.toml hunk that tried to relax
setuptools==59.2.0 to bare setuptools. Upstream 4.12 restructured
that pin into two python_version-conditional lines:
"setuptools==59.2.0; python_version<'3.12'",
"setuptools<70.0.0; python_version>='3.12'",
Our hunk's context no longer matched, breaking the whole patch on
Android. Removed -- the python_version>='3.12' branch (setuptools
<70) is fine for our build env.
Add a 2nd Ptr-prefix hunk for pyopencv_videoio.hpp:120
("Ptr<cv::IStreamReader>" -> "cv::Ptr<cv::IStreamReader>"). The
existing patch only prefixed the VideoCapture overload at line 26;
iOS clang flagged the IStreamReader overload too with
"reference to 'Ptr' is ambiguous".
pillow 12.2.0 iOS
My earlier disable_brotli = True hook landed correctly but didn't
actually keep brotli out of the link line, because pillow 12 has a
HARDCODED iOS-specific libs.extend at setup.py line ~1001:
if sys.platform == "ios":
libs.extend(["z", "bz2", "brotlicommon", "brotlidec", "png"])
That bypasses the feature.brotli check entirely. Added a 4th hunk
to drop "brotlicommon", "brotlidec" from that list -- libs.extend
becomes ["z", "bz2", "png"] on iOS. Android isn't affected (this
block is iOS-only).
All verified locally with patch -p1 against fresh sdists; both
recipes' patches apply cleanly with no rejects.
The brotli removal exposed the next layer: pillow 12 hardcodes `-lpng` on iOS for the _imagingft extension too, and our build doesn't provide libpng either (pillow recipe comment notes "PNG support is internal: libpng is not used"). The wheel link fails with `ld: library png not found`. Trim png from libs.extend so only [z, bz2] remain. Android isn't affected (the block is iOS-only).
Adds a workflow_dispatch input `prebuild_libs` and a new `prebuild`
job that runs BEFORE the main `build` job. When the input is set,
each listed native-lib recipe (typically a `flet-lib*`) is built
first; its dist/*.whl is uploaded as an artifact named
`prebuild-<platform>-<recipe>-<run_id>-<run_attempt>`. The `build`
job then downloads every matching artifact at start, copies the
wheels into its own `dist/`, and exports
`PIP_FIND_LINKS=$GITHUB_WORKSPACE/dist` so forge's host-dep pip
resolution prefers the freshly-built wheels over whatever
pypi.flet.dev has published.
End-to-end chain-dep validation in a single dispatch — instead of
the current 3-step dance:
1. publish flet-libgdal 3.13.1 to pypi.flet.dev
2. wait
3. dispatch gdal/fiona/rasterio/pyogrio against the new lib
becomes one shot:
gh workflow run build-wheels.yml --ref <branch> \
-f prebuild_libs="flet-libgdal:3.13.1" \
-f packages="gdal:3.13.1,fiona:1.10.2,rasterio:1.5.1,pyogrio:0.12.1" \
-f archs="android,iOS"
Once green, you know the chain works -- publish flet-libgdal for
real and re-dispatch the consumers without prebuild_libs.
Implementation shape:
* The `setup` job gains a second output, `prebuild_matrix`, computed
the same way as the main matrix but over `prebuild_libs` instead
of `packages`. Emits `{"include":[]}` when unset.
* The `prebuild` job is a stripped-down clone of `build` (checkout +
free disk + setup uv + Setup Rust + NDK install + `forge <arch>
<package>`), followed by an `actions/upload-artifact` step that
uploads dist/*.whl. Skipped via `if:` when prebuild_libs is empty
or the matrix is empty.
* The `build` job's `needs:` adds `prebuild`. Its `if:` allows
`prebuild.result` to be 'skipped' (input unset) or 'success'
(input set + all libs built); 'failure' propagates and the build
job is skipped, since consuming a broken chain dep is doomed.
* A new "Seed dist/ from prebuild artifacts" step is added BEFORE
"Build wheels". Uses `gh run download` with a per-platform pattern
so iOS builds don't end up with Android wheels in their dist/
(and vice versa). Runs only when `inputs.prebuild_libs != ''`.
* `PIP_FIND_LINKS=$GITHUB_WORKSPACE/dist` is added to the Build
wheels step env. Harmless when dist/ is empty (forge's own output
dir; pip just sees an empty directory listing).
No change to existing push / pull_request triggers — those don't
set prebuild_libs, so prebuild is always skipped and the build job
runs unchanged.
Re-apply the pynacl 1.6.2 bump that we deferred earlier (commit 48709e1). Going to dispatch this branch with prebuild_libs="cffi:2.0.0" so the workflow builds cffi 2.0.0 wheels FIRST, seeds them into the build job dist/, and pynacl 1.6.2 should then resolve cffi>=2.0.0 via PIP_FIND_LINKS instead of failing at the pypi.flet.dev probe. If green, that validates both: (a) prebuild_libs works end-to-end, and (b) the cffi 2.0 chain is safe to publish.
Re-stage the opaque chain bump we deferred earlier (commit 48709e1). The runtime test had failed with "AttributeError: undefined symbol: opaque_FinalizeRequest_core" -- opaque 1.0.0's Python wrapper calls that symbol but flet-libopaque 0.99.8 (the host dep) was built from libopaque <= 0.99.x which doesn't export it. Verified against stef/libopaque GitHub: tags v0.99.8/v0.99.10 contain zero occurrences of opaque_FinalizeRequest_core; v1.0.0/v1.0.1 contain 2 occurrences each. * flet-libopaque: 0.99.8 -> 1.0.1, build_number reset to 1. Existing src/makefile patch applies cleanly to v1.0.1. * opaque: 0.2.0 -> 1.0.0, build_number reset to 1, host dep updated to flet-libopaque 1.0.1. mobile.patch rewrites upstream 1.0.0's broken install_requires = ("pysodium") (string-in-parens, not a tuple) to a proper list install_requires=["pysodium"]. Dispatch shape: gh workflow run build-wheels.yml --ref ci/prebuild-libs-input \ -f prebuild_libs="flet-libopaque:1.0.1" \ -f packages="opaque:1.0.0" \ -f archs="android,iOS"
…test
The gdal chain was the largest of the deferred bumps from the bulk-bump
pass: bumping the GDAL Python bindings to a new major.minor needs
flet-libgdal (the host C library) bumped to the matching version AND
fiona / rasterio / pyogrio all re-pinned in lockstep, since their
extension modules link against libgdal directly.
Bumps:
* flet-libgdal: 3.10.0 -> 3.13.1, build_number reset to 1. build.sh
uses generic CMake flags (GDAL_BUILD_OPTIONAL_DRIVERS=OFF,
OGR_BUILD_OPTIONAL_DRIVERS=OFF, GDAL_USE_*=OFF) that should remain
compatible across the 3.10 -> 3.13 jump.
* gdal: 3.10.0 -> 3.13.1, build_number reset to 1, host dep updated
to flet-libgdal 3.13.1, GDAL_VERSION script_env synced.
* fiona: kept at 1.10.1 (latest stable), host dep + GDAL_VERSION
script_env updated to 3.13.1, build_number reset to 1.
* rasterio: kept at 1.5.0 (latest stable), host dep + GDAL_VERSION
script_env updated to 3.13.1, build_number reset to 1.
* pyogrio: kept at 0.12.1 (latest stable), host dep + GDAL_VERSION
script_env updated to 3.13.1, build_number bumped 1 -> 2.
Dispatch shape (validates the whole chain end-to-end without
round-tripping flet-libgdal through pypi.flet.dev first):
gh workflow run build-wheels.yml --ref ci/prebuild-libs-input \
-f prebuild_libs="flet-libgdal:3.13.1" \
-f packages="gdal:3.13.1,fiona:1.10.1,rasterio:1.5.0,pyogrio:0.12.1" \
-f archs="android,iOS"
…oad fix Re-stage the shapely chain bump deferred in 48709e1 (commit message explained the runtime failure). Issue and fix: The defer commit reverted shapely 2.1.2 -> 2.0.6 (Android worked at 2.1; iOS dlopen failed at import with "symbol not found in flat namespace '__ZN4geos4geom23GeometryComponentFilter9filter_roEPKNS0_8GeometryE'" -- the C++ mangled name for `geos::geom::GeometryComponentFilter::filter_ro(const Geometry*)`). Root cause is the static-link C++ vtable elision: shapely 2.1's C extension subclasses GeometryComponentFilter and overrides the virtual filter_ro(); the base-class vtable lives in libgeos.a's object file but the linker only pulls object files that satisfy *direct* symbol references -- vtables expected by C++ dispatch aren't included. At dlopen, dyld eagerly resolves the flat namespace and aborts on the missing vtable entry. Android picks the symbol up via shared libgeos.so, so this only ever bit iOS. Fix: add `-Wl,-force_load,{platlib}/opt/lib/libgeos.a` to shapely's iOS LDFLAGS. force_load pulls every object out of libgeos.a -- the base-class vtable comes along, dyld is satisfied. Kept the existing `-undefined dynamic_lookup` for libgeos_c-side externals shapely.so defers to dlopen. This static-cascade pattern is the same family as fiona/pyogrio/rasterio, but those only needed the dynamic_lookup half because their missing symbols were from *external* deps of libgdal (proj/tiff/curl) -- not C++ vtables internal to libgdal itself. Bumps: * flet-libgeos: 3.13.0 -> 3.14.1 (latest stable, build_number reset to 1). build.sh's generic CMake invocation should remain compatible. * shapely: 2.0.6 -> 2.1.2 (latest stable, build_number reset to 1). Restored the 2.1 env-var cross-compile path (GEOS_INCLUDE_PATH / GEOS_LIBRARY_PATH) added by 49775ba, dropped the 2.0.x mobile.patch (no longer needed -- and `recipes/shapely/patches/` removed since it's now empty). Dispatch shape: gh workflow run build-wheels.yml --ref ci/prebuild-libs-input \ -f prebuild_libs="flet-libgeos:3.14.1" \ -f packages="shapely:2.1.2" \ -f archs="android,iOS"
The gdal chain dispatch (run 27070408877) failed at the prebuild step because GDAL 3.13's CMakeLists.txt unconditionally calls `find_package(Python 3.8 COMPONENTS Interpreter Development NumPy)` at line 229 -- regardless of -DBUILD_PYTHON_BINDINGS=OFF, which our build.sh already passes. Under CMake 4.x the search trips CMake policy CMP0190: Python: When cross-compiling, Interpreter and/or Compiler components cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified (see policy CMP0190). This is a no-op block for our build -- we have BUILD_PYTHON_BINDINGS=OFF and want zero Python detection -- so the cleanest fix is to wrap the find_package(Python) block (and its Python_NumPy follow-up) in `if (BUILD_PYTHON_BINDINGS OR NOT DEFINED BUILD_PYTHON_BINDINGS)`. When mobile-forge passes -DBUILD_PYTHON_BINDINGS=OFF the variable is set to a falsy value, the guard fails, and the whole Python detection is skipped. For upstream users who don't define the cache variable, the NOT DEFINED clause keeps default behavior intact. Verified the patch applies cleanly to upstream GDAL v3.13.1 CMakeLists.txt via `patch -p1 --dry-run`.
…pt.cpp) GEOS 3.14.0 introduced a `CurrentThreadInterrupt::ThreadCallback` mechanism in src/util/Interrupt.cpp that uses C++ `thread_local` storage. iOS clang refuses this with: error: thread-local storage is not supported for the current target thread_local geos::util::CurrentThreadInterrupt::ThreadCallback* callback_thread = nullptr; iOS theoretically supports TLS as of iOS 9.0+, but the iOS simulator target (x86_64 13.0 in our build) hits a known clang/libc++ ABI gap that rejects `thread_local` for static class members. Android builds 3.14.1 fine but iOS fails at 98% of the libgeos compile. GEOS 3.13.1 (latest 3.13.x patch release) doesn't have the thread_local addition -- src/util/Interrupt.cpp still uses plain `bool requested` and `Callback* callback` globals. Pinning there gives us a small stable bump (3.13.0 -> 3.13.1) without the iOS-breaking change. The GeometryComponentFilter::filter_ro vtable that motivated the shapely chain has been in libgeos.a since GEOS 3.0+ -- the original vtable elision failure on iOS is fixed by shapely's new `-Wl,-force_load,libgeos.a` LDFLAGS in 49f6...d635a19, not by the GEOS version bump itself.
The previous patch (d5919ae) wrapped GDAL CMakeLists.txt's Python detection block in `if (BUILD_PYTHON_BINDINGS OR NOT DEFINED BUILD_PYTHON_BINDINGS)`, which fences off the find_package(Python) call only when the user explicitly passes -DBUILD_PYTHON_BINDINGS=OFF. The condition is FALSE both when undefined (the NOT DEFINED clause was the bug -- I wrote it to be permissive for upstream non-mobile users, but our cross-compile always wants the block off) and when explicitly OFF. Android already passes -DBUILD_PYTHON_BINDINGS=OFF on line 31; iOS did not. Add the matching flag to the iOS cmake invocation so the patched condition evaluates FALSE and the block is fully skipped. Verified by re-reading the run 27070846249 log: the find_package error moved from CMakeLists.txt:229 (pre-patch) to CMakeLists.txt:235 (post-patch -- the line offset matches the 6 new lines my patch inserted above). Block was still being entered because the iOS build.sh never defined BUILD_PYTHON_BINDINGS at all.
Upstream gdal/setup.py reorganized between 3.10.0 (the prior recipe version) and 3.13.1: * `libraries = ['gdal']` moved from line 54 to line 57 (the file acquired an extra `gcore/multidim` include dir entry in the hardcoded `/home/runner/work/gdal/gdal/python_pkgs` block). * `get_gdal_config()` moved from line 228 to line 242 (intervening build_ext-class refactors added a few setup-time methods). Re-anchor both hunks to the new line numbers and replace the legacy `/home/even/gdal/3.10/...` hardcoded path context with the new `/home/runner/work/gdal/gdal/python_pkgs/...` path that the upstream release build now bakes in. Hunk logic unchanged -- the GDAL_LIBS env-var override and the per-option GDAL_<OPTION> env-var lookup stay in place. Verified by extracting the gdal-3.13.1.tar.gz sdist from PyPI, git-initializing it, and running `patch -p1 --dry-run` against the new patch: applies cleanly.
The Android consumer wheels (gdal, fiona, rasterio, pyogrio) all built cleanly against the freshly prebuilt flet-libgdal 3.13.1 but failed at recipe-tester runtime on the Android emulator with: ImportError: dlopen failed: library "libomp.so" not found GDAL 3.13 added OpenMP-based parallelism in a handful of code paths (checksum, JP2 codec, raster reprojection). cmake/helpers/CheckDependentLibraries.cmake:438 runs `gdal_check_package(OpenMP "Whether GDAL should use OpenMP" COMPONENTS "C" CAN_DISABLE)`, which auto-enables OpenMP whenever CMake's FindOpenMP succeeds. On the Linux runner cross-compiling for Android, FindOpenMP discovers the host's libgomp, adds `-fopenmp` to the link line, and stamps a DT_NEEDED libomp.so into the resulting libgdal.so. Android's Python runtime doesn't ship libomp.so, so dlopen aborts on the first import of any module linked transitively through libgdal.so. iOS escaped because FindOpenMP gave up earlier in the toolchain (no host libomp visible to the iOS sysroot search), but the disable is symmetric defense in depth on the iOS side too. The CAN_DISABLE clause on the OpenMP gdal_check_package call means `-DGDAL_USE_OPENMP=OFF` is the supported way to opt out. Same pattern as the existing OPENSSL / CURL / EXPAT / LIBXML2 disables we already pass on Android. Symptom shown by gdal_test_chain dispatch 27071503295: all 4 Android consumers fail at first `import` (ImportError on libomp.so missing for fiona/rasterio/pyogrio; ModuleNotFoundError for `osgeo._gdal` because Python masks the dlopen failure as a missing-module error when the .so file is present but un-loadable). iOS consumer wheels all passed.
Refactor 50ec338's two-job chain-dep staging into a single shell loop inside the existing Build wheels step. Mechanically equivalent — `prebuild_libs="flet-libgdal:3.13.1"` still builds the lib first, the wheel still lands in dist/, PIP_FIND_LINKS still steers the consumer's host-dep pip resolution at it -- but the workflow shrinks from 569 lines to 425 (−165, +21). What goes away: * setup job's `prebuild_matrix` output + the 50-line `set-prebuild-matrix` step that duplicated the main matrix's arch fan-out * the entire `prebuild` job (75 lines: own runner setup, NDK install, forge invocation, artifact upload) * the build job's `Seed dist/ from prebuild artifacts` step (20 lines: `gh run download` + copy into dist/) * `build.needs: [setup, prebuild]` → `build.needs: setup` * the `if: always() && needs.setup.result == 'success' && (needs.prebuild.result == 'success' || skipped)` propagation gate What replaces it (5 lines inside Build wheels): if [[ -n "${PREBUILD_LIBS:-}" ]]; then for lib in $(echo "$PREBUILD_LIBS" | tr ',' ' '); do forge "$FORGE_ARCH" "$lib" done fi Trade-off vs. the prior two-job design: for a chain like gdal (one lib + 4 consumers) the lib now rebuilds inside every consumer-arch runner — 4× per arch instead of 1× per arch. ~+60% runner-minutes on the worst case. Wall-clock is unchanged (the prebuild was on the critical path either way), and for the smaller chains (pynacl, opaque, shapely — each one consumer) the cost is identical to before. Acceptable price for the YAML being much easier to read and reason about, especially the failure semantics (no more `needs:` + result-juggling), and the artifact upload/download dance disappears entirely. Existing push / pull_request triggers unaffected — they don't set prebuild_libs, the `if` guard skips the loop, the consumer build runs exactly as it did before 50ec338.
…tion The gdal chain bump (flet-libgdal 3.10.0 -> 3.13.1) republishes fiona and rasterio at the SAME upstream version (1.10.1, 1.5.0) but linked against the new libgdal. PEP 427 wheel selection picks the higher build_tag when (name, version) collide -- so the rebuild must outrank what's currently on pypi.flet.dev (both at build 10), or downstream installs silently keep the old libgdal-3.10-linked wheel and the chain bump becomes invisible. * fiona 1.10.1: build_number 1 -> 11 (must beat fiona-1.10.1-10) * rasterio 1.5.0: build_number 1 -> 11 (must beat rasterio-1.5.0-10) * pyogrio 0.12.1: build_number 2 -> 1 (no collision since version changed from 0.10.0; bring in line with the branch's "version bump -> build_number = 1" convention) Verified the published wheels via pip download against https://pypi.flet.dev (android_24_x86_64, ios_13_0_arm64_iphoneos both at build 10 for fiona / rasterio / pyogrio / gdal).
fix_wheel was unconditionally rewriting the WHEEL Tag with `self.wheel_tag` (cp3X-cp3X-<platform>), discarding the tag maturin / the upstream backend had emitted. For abi3 crates like cryptography this turned `cp37-abi3-<platform>` into `cp312-cp312-<platform>` and the produced wheel name followed, which was wrong on three counts: - semantically incorrect: the inner _rust.abi3.so is stable-ABI; - unnecessarily restrictive: pip refuses the wheel on cp313+; - wasteful: forces per-Python-version rebuilds. Now `fix_wheel` keeps the existing pythontag-abitag portion and swaps only the platform component, falling back to self.wheel_tag only when the upstream wheel didn't carry a Tag header. Restores the cp3X-abi3-* wheels we shipped before commit 4cf1c1f. Co-authored-by: Feodor Fitsner <feodor@appveyor.com>
There was a problem hiding this comment.
Pull request overview
This PR updates a large set of mobile-forge recipes to their latest stable upstream versions (generally resetting build.number back to 1), plus adds workflow/build-system tweaks to support rebuilding “consumer” wheels against freshly bumped flet-lib* host libraries within a single CI dispatch.
Changes:
- Bump many Python package recipes to newer upstream versions and reset build numbers (with a few targeted rebuild-number bumps for GDAL consumers).
- Add/adjust patches and build flags for newer upstream behavior (notably GDAL/OpenMP and Pillow/Shapely cross-compile behavior).
- Update wheel tag normalization logic in
src/forge/build.pyand add aprebuild_recipesworkflow input to seed locally built host-dep wheels into resolution.
Reviewed changes
Copilot reviewed 74 out of 74 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/forge/build.py |
Adjust wheel tag rewriting to preserve upstream Python/ABI tags while swapping platform tags. |
.github/workflows/build-wheels.yml |
Add prebuild_recipes input and logic to build prerequisite recipes first and set PIP_FIND_LINKS to dist/. |
recipes/aiohttp/meta.yaml |
Bump aiohttp version/build number. |
recipes/argon2-cffi-bindings/meta.yaml |
Bump argon2-cffi-bindings version/build number. |
recipes/bcrypt/meta.yaml |
Bump bcrypt version/build number. |
recipes/bitarray/meta.yaml |
Bump bitarray version/build number. |
recipes/blis/meta.yaml |
Bump blis version/build number. |
recipes/blis/patches/mobile.patch |
Update mobile patch for blis (pthread barrier + build flag tweaks). |
recipes/brotli/meta.yaml |
Bump Brotli version/build number. |
recipes/cffi/meta.yaml |
Bump cffi version/build number. |
recipes/contourpy/meta.yaml |
Bump contourpy version/build number. |
recipes/fiona/meta.yaml |
Re-pin to new flet-libgdal and bump build number to outrank published wheel. |
recipes/flet-libgdal/build.sh |
Disable OpenMP and ensure Python bindings are off for cross builds. |
recipes/flet-libgdal/meta.yaml |
Bump flet-libgdal version/build number and add new patch. |
recipes/flet-libgdal/patches/mobile.patch |
Guard GDAL’s find_package(Python ...) behind BUILD_PYTHON_BINDINGS. |
recipes/flet-libgeos/meta.yaml |
Bump flet-libgeos version/build number and update source URL. |
recipes/flet-libopaque/meta.yaml |
Bump flet-libopaque version/build number. |
recipes/gdal/meta.yaml |
Re-pin to new flet-libgdal and bump GDAL Python bindings version/build number. |
recipes/gdal/patches/config.patch |
Update GDAL config patch and allow overriding libraries via GDAL_LIBS. |
recipes/google-crc32c/meta.yaml |
Bump google-crc32c version/build number. |
recipes/greenlet/meta.yaml |
Bump greenlet version/build number. |
recipes/grpcio/meta.yaml |
Bump grpcio version/build number; update iOS/mac lane C++ standard flags. |
recipes/grpcio/patches/mobile.patch |
Drop an upstream-resolved patch hunk (zlib fdopen guard). |
recipes/jiter/meta.yaml |
Bump jiter version/build number. |
recipes/jq/meta.yaml |
Bump jq version/build number. |
recipes/kiwisolver/meta.yaml |
Bump kiwisolver version/build number. |
recipes/lru-dict/meta.yaml |
Bump lru-dict version/build number. |
recipes/lxml/meta.yaml |
Bump lxml version/build number. |
recipes/markupsafe/meta.yaml |
Bump MarkupSafe version/build number. |
recipes/markupsafe/test_markupsafe.py |
Update test to handle MarkupSafe 3.x C accelerator symbol rename. |
recipes/matplotlib/meta.yaml |
Bump matplotlib version/build number. |
recipes/msgpack/meta.yaml |
Bump msgpack version/build number. |
recipes/msgspec/meta.yaml |
Bump msgspec version/build number. |
recipes/numpy/meta.yaml |
Bump numpy version/build number and adjust meson longdouble property condition. |
recipes/numpy/patches/mobile-2.2.2.patch |
Remove obsolete numpy patch (upstream resolved). |
recipes/opaque/meta.yaml |
Bump opaque version/build number; re-pin to new flet-libopaque and refine rationale comment. |
recipes/opaque/patches/mobile.patch |
Fix opaque’s install_requires and update classifier. |
recipes/opencv-python/meta.yaml |
Bump opencv-python version/build number; add cmake build dep. |
recipes/opencv-python/patches/mobile.patch |
Refresh patch for new opencv-python sources (C++ API/type adjustments). |
recipes/pandas/meta.yaml |
Bump pandas version/build number. |
recipes/pandas/patches/mobile.patch |
Update pandas patch (codespell ignore list + wrapper context). |
recipes/pendulum/meta.yaml |
Bump pendulum version/build number and remove now-unneeded patch application. |
recipes/pendulum/patches/mobile.patch |
Remove obsolete pendulum patch (upstream resolved). |
recipes/pillow/meta.yaml |
Bump Pillow version/build number and switch to setup-12.x.patch. |
recipes/pillow/patches/setup-12.x.patch |
Rebase/setup patch for Pillow 12.x (disable platform guessing + brotli handling). |
recipes/protobuf/meta.yaml |
Bump protobuf version/build number. |
recipes/pycryptodome/meta.yaml |
Bump pycryptodome version/build number. |
recipes/pycryptodome/patches/mobile.patch |
Adjust pycryptodome setup metadata and add runtime cffi requirement. |
recipes/pycryptodomex/meta.yaml |
Bump pycryptodomex version/build number. |
recipes/pycryptodomex/patches/mobile.patch |
Adjust pycryptodomex setup metadata and add runtime cffi requirement. |
recipes/pyjnius/meta.yaml |
Bump pyjnius version/build number. |
recipes/pyjnius/patches/mobile.patch |
Update pyjnius JNI class lookup calls for Android compatibility. |
recipes/pymongo/meta.yaml |
Bump pymongo version/build number. |
recipes/pynacl/meta.yaml |
Bump PyNaCl version/build number. |
recipes/pynacl/patches/mobile.patch |
Refresh PyNaCl patch hunk positions / remove outdated setup metadata chunk. |
recipes/pyobjus/meta.yaml |
Bump pyobjus version/build number. |
recipes/pyobjus/patches/mobile.patch |
Update pyobjus patch for new upstream layout and iOS libffi handling. |
recipes/pyogrio/meta.yaml |
Bump pyogrio version/build number; re-pin to new flet-libgdal. |
recipes/pyproj/meta.yaml |
Bump pyproj version/build number. |
recipes/pyyaml/meta.yaml |
Bump PyYAML version/build number. |
recipes/rasterio/meta.yaml |
Re-pin to new flet-libgdal and bump build number to outrank published wheel. |
recipes/regex/meta.yaml |
Bump regex version/build number. |
recipes/rpds-py/meta.yaml |
Bump rpds-py version/build number. |
recipes/ruamel.yaml.clib/meta.yaml |
Bump ruamel.yaml.clib version/build number. |
recipes/shapely/meta.yaml |
Bump shapely version/build number; switch from patching to env-based cross-compile config and iOS link flags. |
recipes/shapely/patches/mobile.patch |
Remove obsolete shapely patch (replaced by upstream-supported env vars). |
recipes/sqlalchemy/meta.yaml |
Bump sqlalchemy version/build number. |
recipes/tiktoken/meta.yaml |
Bump tiktoken version/build number. |
recipes/time-machine/meta.yaml |
Bump time-machine version/build number. |
recipes/tokenizers/meta.yaml |
Bump tokenizers version/build number with an upper cap rationale for 32-bit Android. |
recipes/websockets/meta.yaml |
Bump websockets version/build number. |
recipes/yarl/meta.yaml |
Bump yarl version/build number. |
recipes/zope.interface/meta.yaml |
Bump zope.interface version/build number. |
recipes/zstandard/meta.yaml |
Bump zstandard version/build number. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+666
to
+668
| upstream_tags = wheel_metadata.get_all("Tag", []) | ||
| del wheel_metadata["Tag"] | ||
| new_tags = [] |
Comment on lines
+16
to
+22
| prebuild_recipes: | ||
| description: | | ||
| Comma-separated list of recipes (typically flet-lib*) to | ||
| build FIRST and seed into the matrix's dist/ for host-dep pip resolution | ||
| (e.g. flet-libgdal -> gdal). | ||
| required: false | ||
| default: "" |
|
lgtm, let's merge! 🚀 |
FeodorFitsner
approved these changes
Jun 8, 2026
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.
52 recipes bumped to latest stable (build_number reset to 1):
Details
Same-version consumer rebuilds (linked against new flet-libgdal, build_number bumped to outrank what's already on pypi.flet.dev per PEP 427 wheel selection):
build_number10 → 11build_number10 → 11Patches:
recipes/flet-libgdal/patches/mobile.patch— guards GDAL 3.13's new unconditionalfind_package(Python)block behindBUILD_PYTHON_BINDINGSso it doesn't trip CMake 4.x's CMP0190 cross-compile policy.recipes/numpy/patches/mobile-2.2.2.patch,recipes/pendulum/patches/mobile.patch.recipes/pillow/patches/setup-11.x.patch→setup-12.x.patch(rebased for the 12.x setup.py reorganization).recipes/flet-libgdal/build.shalso gets-DBUILD_PYTHON_BINDINGS=OFFon the iOS branch (Android already had it) and-DGDAL_USE_OPENMP=OFFon both, because GDAL 3.13 newly probes OpenMP viagdal_check_package(OpenMP ... CAN_DISABLE)— FindOpenMP finds the host runner's libgomp during cross-compile and stampsDT_NEEDED libomp.sointo libgdal.so, but Android's Python runtime doesn't ship libomp.so → consumers dlopen-fail at first import..github/workflows/build-wheels.ymlgets a newprebuild_libsworkflow_dispatch input — see next section.Why
prebuild_recipesBumping a
flet-lib*recipe forces every consumer of that lib to be rebuilt against the new version (ABI change + pip's exact-version pin on host deps). The old loop was: publish flet-libgdal → wait for pypi.flet.dev → dispatch the 4 consumers against it. Three turns and no way to back out cleanly if the lib turned out to be broken.prebuild_recipes="flet-libgdal:3.13.1"builds the lib first inside the same matrix runner, drops the wheel intodist/, and steers forge's host-dep pip resolution at it viaPIP_FIND_LINKS=$GITHUB_WORKSPACE/dist. The consumer pip-resolves the freshly-built local wheel over whatever pypi.flet.dev still has published. One dispatch end-to-end.Expected CI failures on this PR
The
pull_requesttrigger fires the workflow but does not passprebuild_recipes(that input is manual-dispatch only). So consumers pip-resolve their host deps againstpypi.flet.dev, where the newflet-lib*versions aren't published yet — chicken and egg.14 jobs (7 recipes × 2 archs) will fail with
ERROR: No matching distribution found for ...:cffi>=2.0.0(target ABI)flet-libopaque 1.0.1flet-libgeos 3.13.1flet-libgdal 3.13.1flet-libgdal 3.13.1flet-libgdal 3.13.1flet-libgdal 3.13.1These same 7 chains were already validated end-to-end on the
prebuild_recipesmanual-dispatch path. The PR'spush-triggered runs simply can't exercise that path automatically. The fourflet-lib*themselves (flet-libgdal 3.13.1,flet-libgeos 3.13.1,flet-libopaque 1.0.1,cffi 2.0.0) will all build and pass.Post-merge recovery (2-pass dance):
pushrun publishes the fourflet-lib*wheels successfullyprebuild_recipesneeded — their host deps are now resolvable on pypi.flet.dev):