Skip to content

recipe: pyzbar 0.1.9 (+ flet-libzbar, flet-libiconv)#82

Merged
ndonkoHenri merged 8 commits into
mainfrom
pyzbar
Jun 19, 2026
Merged

recipe: pyzbar 0.1.9 (+ flet-libzbar, flet-libiconv)#82
ndonkoHenri merged 8 commits into
mainfrom
pyzbar

Conversation

@ndonkoHenri

Copy link
Copy Markdown

Adds pyzbar 0.1.9 for iOS and Android as a 3-recipe chain, plus two reusable forge/CI capabilities the chain needed:

  • source.url for Python packagesPythonPackageBuilder now honors an explicit source.url, so wheels-only / no-sdist packages (like pyzbar) can be built.
  • build.before_all — recipes declare their own host build-tool setup instead of the CI workflow hardcoding per-recipe apt/brew installs.

Requested by flet-dev/flet#3563.

Recipes

Recipe What
flet-libiconv 1.17 GNU libiconv as a static, PIC archive, Android-only (platforms: [android]). zbar's QR decoder #includes <iconv.h> unconditionally and Android bionic has no iconv at API 24; iOS/macOS use the system libiconv.
flet-libzbar 0.23.93 libzbar as a shared lib (Pattern H — a pure-Python wrapper dlopens it at runtime). Built from the mchehab/zbar git archive (no release tarball), so build.sh runs autoreconf. Android links the static flet-libiconv; iOS uses the system libiconv.
pyzbar 0.1.9 Pure-Python ctypes wrapper. No PyPI sdist → built from the GitHub tag archive via source.url. patches/mobile.patch fixes the loader: ctypes.util.find_library('zbar') returns None on iOS/Android, so it loads the bundled libzbar (opt/lib/libzbar.fwork on iOS, libzbar.so on Android).

Forge / CI changes

  • src/forge/build.pyPythonPackageBuilder.download_source_url honors source.url when present (else resolves the PyPI sdist as before); + a local hint surfacing a recipe's before_all. Additive — recipes without source.url/before_all are unaffected.
  • src/forge/schema/meta-schema.yaml — new build.before_all (string or array; Jinja-rendered).
  • .ci/read_meta.py — generalized into one field reader: the existing matrix summary line plus a <dotted.field> [android|ios] mode (used to read build.before_all).
  • .github/workflows/build-wheels-version.yml — a generic "run recipe before_all" step (runs each built recipe's declared host setup — prebuild deps + the package), and the prebuild_recipes loop now honors declared platforms. The workflow no longer hardcodes recipe-specific installs.

flet-libzbar's before_all is the first user: autoconf/automake/libtool/gettext/autopoint on Android (apt), and on iOS brew install … + a conditional brew link gettext for the keg-only autopoint.

Validation

  • Builds green across the matrix (android + iOS × Python 3.12 / 3.13 / 3.14).
  • On-device tests pass on 3.12 — Android emulator (x86_64, API 28) and iOS simulator — loading libzbar through the patched ctypes loader and decoding a greyscale buffer; Android arm64 also validated locally.
  • The before_all mechanism was validated end-to-end (flet-libzbar builds via the recipe-declared host setup; no autotools left hardcoded in CI).

Native libzbar for the upcoming pyzbar recipe. Pattern H (ctypes-loaded shared
lib): ships opt/lib/libzbar.so so serious-python surfaces it into Android
jniLibs and an iOS embedded framework, for pyzbar to dlopen at runtime.

- source: mchehab/zbar GitHub archive (no release tarball is published) — only
  configure.ac is shipped, so build.sh runs autoreconf to generate ./configure.
- core decoder only (--without x/gtk/qt/imagemagick/jpeg/python/java/dbus,
  --disable-video/doc); Android shared + .so collapse, iOS static -> hand-linked
  shared (Darwin host triplet since config.sub lacks apple-ios).

First attempt; iterating locally + on CI.
zbar's QR decoder (qrcode/qrdectxt.c) #includes <iconv.h> and calls
iconv_open/iconv unconditionally (no HAVE_ICONV guard). Android bionic has no
iconv at API 24, so libzbar fails to compile there. Add a static, PIC GNU
libiconv 1.17 (flet-libiconv, platforms: [android]) and declare it as a host
requirement of flet-libzbar (Android-gated). forge auto-injects the host-dep
include/lib, so zbar AM_ICONV finds it and folds libiconv.a into libzbar.so.

Verified locally (android arm64-v8a): flet-libiconv + flet-libzbar build; the
resulting libzbar.so is self-contained (NEEDED = libm/libdl/libc only, no
external libiconv.so, no libc++_shared). iOS uses the system libiconv.
pyzbar (QR/barcode reader) is a pure-Python ctypes wrapper around libzbar.

forge: PythonPackageBuilder.download_source_url now honors an explicit
source.url (GitHub tag archive) when present, else resolves the PyPI sdist as
before. Enables wheels-only / no-sdist Python packages (pyzbar publishes no
sdist). SimplePackageBuilder already did this; this brings the Python builder in
line. Additive — packages without source.url are unaffected.

pyzbar recipe:
- source.url = NaturalHistoryMuseum/pyzbar GitHub tag archive (no PyPI sdist).
- patches/mobile.patch: ctypes.util.find_library("zbar") returns None on
  iOS/Android; patch the loader to dlopen the bundled libzbar from the
  flet-libzbar wheel (opt/lib/libzbar.fwork on iOS, libzbar.so on Android).
- requirements.host flet-libzbar (both platforms) -> Requires-Dist via fix_wheel.
- tests: load libzbar + scan a blank Y800 buffer (no camera/files/network).

Verified locally: full chain builds on android arm64-v8a (flet-libiconv ->
flet-libzbar -> pyzbar). NOTE for CI: the inter-recipe host deps need
prebuild_recipes (or publishing flet-libiconv/flet-libzbar first); a plain push
fails to resolve flet-libiconv across isolated matrix jobs.
… platforms

flet-libzbar autoreconfs the zbar git archive (only configure.ac shipped), whose
configure.ac uses AM_GNU_GETTEXT -> autopoint. Install autoconf/automake/libtool
+ gettext/autopoint in the build step (apt on Android, brew + gettext-on-PATH on
the macOS/iOS runner; gettext is keg-only).

Also: the prebuild_recipes loop now respects each recipe's declared platforms
(same gating the matrix applies), so an android-only lib (flet-libiconv) is not
forced to build on iOS (where it failed at config.sub arm64-apple-ios-simulator).
Recipes now declare their own host build-tool setup in build.before_all instead
of the CI workflow hardcoding apt/brew installs per recipe, so the workflow stays
recipe-agnostic and recipes remain self-contained as more are added.

- schema: new build.before_all (string or array; Jinja-rendered, can branch on
  sdk).
- read_meta.py: generalized into one field reader -- keeps the matrix summary
  line (back-compat) and adds a "<dotted.field> [android|ios]" mode used to read
  build.before_all per platform (instead of a separate script).
- build-wheels-version.yml: revert the recipe-specific apt/brew block to baseline
  (sqlite3, pkg-config); add a generic "Run recipe before_all" step that runs
  each built recipe's before_all. It loops PREBUILD_RECIPES + FORGE_PACKAGES
  because the recipe needing it (flet-libzbar) is a prebuild dep, not the matrix
  package.
- forge: surface a recipe's before_all locally as a hint (forge never runs it).
- flet-libzbar: declares its autotools+gettext setup (apt on Android; brew plus
  "brew link --force gettext" for keg-only autopoint on iOS).
Resolve .ci/read_meta.py: keep the generalized field reader (summary line + dotted-field mode for build.before_all), adopt mains build.number default of 1. meta-schema.yaml and build-wheels-version.yml auto-merged cleanly - mains zizmor hardening (SHA pins, permissions, persist-credentials, Summarize step, push-to-main test skip) and the before_all mechanism coexist.
@ndonkoHenri ndonkoHenri merged commit 5fc8d75 into main Jun 19, 2026
15 of 25 checks passed
@ndonkoHenri ndonkoHenri deleted the pyzbar branch June 19, 2026 12:51
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