Skip to content

Add Android wheels support via cibuildwheel - #283

Merged
benoit74 merged 1 commit into
openzim:mainfrom
pawank925:android-wheels
Nov 2, 2026
Merged

benoit74 merged 1 commit into
openzim:mainfrom
pawank925:android-wheels

Conversation

@pawank925

@pawank925 pawank925 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Build Android arm64_v8a and x86_64 wheels with CPython 3.13+. Download prebuilt libzim Android binaries and adjust packaging/rpaths for the Android toolchain.

The libzim binaries published for Android are compiled without writer support (-Dwithout_writer=true), so these wheels are reader-only: the Android build sets LIBZIM_WITHOUT_WRITER=1, and libzim.writer is not available. README.md (docs index) and the libzim.writer API docs mention this limitation.

The Android job skips cibuildwheel tests: the Android testbed does not support the {project} placeholder used by the shared test command, and the suite relies on the writer module plus network downloads.

thanks to @benoit74 for suggestion

Closes #167

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.29%. Comparing base (93fba70) to head (c6484d3).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #283   +/-   ##
=======================================
  Coverage   95.29%   95.29%           
=======================================
  Files           1        1           
  Lines         638      638           
=======================================
  Hits          608      608           
  Misses         30       30           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pawank925
pawank925 force-pushed the android-wheels branch 2 times, most recently from b19f6e6 to 119d620 Compare October 23, 2026 04:57
Comment thread .github/workflows/CI-wheels.yaml Resolved
Comment thread .github/workflows/CI-wheels.yaml Resolved
Comment thread pyproject.toml Resolved
Comment thread setup.py Resolved
Comment thread .github/workflows/CI-wheels.yaml Resolved
@pawank925

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback.

  • Use a frozenset for Android triplets and a mapping dict (removed noqa).
  • Drop duplicate LIBZIM_DL_VERSION from [tool.cibuildwheel.android].
  • Build Android wheels in CI on PRs/pushes (removed experimental label and manual dispatch gate).

@pawank925
pawank925 marked this pull request as draft October 23, 2026 09:04
@pawank925
pawank925 marked this pull request as ready for review October 23, 2026 09:16
@pawank925

pawank925 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

hit a blocker while getting the Android wheel to actually build: the published Android libzim binaries are reader-only, so the extension cannot link its writer bindings.

Evidence (arm64 archives from download.openzim.org/release/libzim):

Archive include/zim/writer/ _ZN3zim6writer* symbols
libzim_android-arm64-9.2.3-2 yes 201
libzim_android-arm64-9.8.2-1 no 0

Linux/macOS archives still ship the writer headers and symbols.

libzim/libwrapper.cpp includes <zim/writer/item.h> and binds zim::writer::Creator, so with 9.8.2-1 the build fails at compile time (zim/writer/item.h: No such file or directory), and even if the headers were supplied it would fail at link because the symbols are absent.

Options:

  1. Publish Android libzim builds with writer enabled (preferred; consistent with other platforms).
  2. Build libzim from source for Android in this CI (cross-compiling xapian/ICU/zstd/lzma for the NDK). Large, likely out of scope for this PR.
  3. Ship a reader-only Android wheel by compiling out the writer API. Breaking API, not recommended.

@benoit74

Copy link
Copy Markdown
Contributor

@kelson42 any reason to have limited Android libzim builds to "reader-only"? Where should I open an issue?

@kelson42

Copy link
Copy Markdown
Contributor

@kelson42 any reason to have limited Android libzim builds to "reader-only"? Where should I open an issue?

A few releases ago, we have implemented - at the libzim level - the ability to compile/distribute the libzim only with the reader part (make no sense to embedds all the stuff related to the writer... if you never use it). libzim for Android has been the first to use this feature because... there is no writter. So "yes", this is a feature and not a bug.

@benoit74

Copy link
Copy Markdown
Contributor

Then we will need to work on #287 first, thank you.

pawank925 added a commit to pawank925/python-libzim that referenced this pull request Oct 28, 2026
Cython cannot conditionally parse `cdef extern` blocks, so declare the
writer API through a local shim header that only includes libzim's writer
headers when the library was built with writer support. Guard the writer
implementations (C++ wrappers, writer callbacks, Hint, Creator and the
libzim.writer registration) with LIBZIM_WITHOUT_WRITER, and detect that
build flag in setup.py once libzim is available.

On a writer-less libzim the extension now builds and `import libzim.writer`
raises ImportError, which is what Android/reader-only wheels need (openzim#283).
Adds tests for lazy submodule loading and creator guards.

Fixes openzim#287
pawank925 added a commit to pawank925/python-libzim that referenced this pull request Oct 28, 2026
Cython cannot conditionally parse `cdef extern` blocks, so declare the
writer API through a local shim header that only includes libzim's writer
headers when the library was built with writer support. Guard the writer
implementations (C++ wrappers, writer callbacks, Hint, Creator and the
libzim.writer registration) with LIBZIM_WITHOUT_WRITER, and detect that
build flag in setup.py once libzim is available.

On a writer-less libzim the extension now builds and `import libzim.writer`
raises ImportError, which is what Android/reader-only wheels need (openzim#283).
Adds tests for lazy submodule loading and creator guards.

Fixes openzim#287
pawank925 added a commit to pawank925/python-libzim that referenced this pull request Oct 29, 2026
Cython cannot conditionally parse `cdef extern` blocks, so declare the
writer API through a local shim header that only includes libzim's writer
headers when the library was built with writer support. Guard the writer
implementations (C++ wrappers, writer callbacks, Hint, Creator and the
libzim.writer registration) with LIBZIM_WITHOUT_WRITER, and detect that
build flag in setup.py once libzim is available.

On a writer-less libzim the extension now builds and `import libzim.writer`
raises ImportError, which is what Android/reader-only wheels need (openzim#283).
The reader-only build can be forced with FORCE_LIBZIM_WITHOUT_WRITER=1 and
is covered by a dedicated CI job. Adds tests for lazy submodule loading and
creator guards.

Fixes openzim#287
pawank925 added a commit to pawank925/python-libzim that referenced this pull request Oct 29, 2026
Cython cannot conditionally parse `cdef extern` blocks, so declare the
writer API through a local shim header that only includes libzim's writer
headers when the library was built with writer support. Guard the writer
implementations (C++ wrappers, writer callbacks, Hint, Creator and the
libzim.writer registration) with LIBZIM_WITHOUT_WRITER, and let the build
select that path explicitly through the LIBZIM_WITHOUT_WRITER env var.

On a writer-less libzim the extension now builds and `import libzim.writer`
raises ImportError, which is what Android/reader-only wheels need (openzim#283).
The reader-only build is covered by a dedicated CI job. Adds tests for lazy
submodule loading and creator guards.

Fixes openzim#287
pawank925 added a commit to pawank925/python-libzim that referenced this pull request Oct 30, 2026
Cython cannot conditionally parse `cdef extern` blocks, so declare the
writer API through a local shim header that only includes libzim's writer
headers when the library was built with writer support. Guard the writer
implementations (C++ wrappers, writer callbacks, Hint, Creator and the
libzim.writer registration) with LIBZIM_WITHOUT_WRITER, and let the build
select that path explicitly through the LIBZIM_WITHOUT_WRITER env var.

On a writer-less libzim the extension now builds and `import libzim.writer`
raises ImportError, which is what Android/reader-only wheels need (openzim#283).
The reader-only build is covered by a dedicated CI job. Adds tests for lazy
submodule loading and creator guards.

Fixes openzim#287
@pawank925

Copy link
Copy Markdown
Contributor Author

@benoit74

@benoit74

Copy link
Copy Markdown
Contributor

@pawank925 build on Android is still failing ... please rebase on main now that other PR got merged (and squash commits).

Also please update documentation:

@pawank925

pawank925 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@benoit74 rebased on main (93fba70) and squashed to a single commit c6484d3.

The Android job was still failing for the reason noted earlier in this thread: the published Android libzim binaries are built with -Dwithout_writer=true (libzim CI builds android-arm and android-arm64 that way), so zim/writer/*.h are not shipped at all. My assumption that #292 alone would unblock the build was wrong: #292 makes reader-only builds possible, but the build still has to be told that it is reader-only.

The job now sets LIBZIM_WITHOUT_WRITER=1 and forwards it to the build with CIBW_ENVIRONMENT_PASS_ANDROID: "LIBZIM_DL_VERSION LIBZIM_WITHOUT_WRITER". Android builds run in a container, so the LIBZIM_DL_VERSION set on that step was never visible to setup.py (it silently fell back to the default); it is now passed explicitly, which also lets me drop the duplicated LIBZIM_DL_VERSION / CIBW_BUILD_VERBOSITY pointing out of line with the other jobs.

I also had to skip cibuildwheel tests for Android ([tool.cibuildwheel.android] test-skip = "*"). Otherwise the build would have failed right after compiling: the Android testbed rejects the {project} placeholder used by the shared test-command, and the suite anyway depends on the writer module (to create ZIMs) and on network downloads.

Documentation:

  • README.md (included by docs/index.md, so it shows up on the readthedocs index) now lists Android for arm64_v8a and x86_64 with CPython 3.13+ and states those wheels are reader-only.
  • the libzim.writer module docstring (rendered at api_reference/libzim.writer/) states the module is not available on Android.
  • the CHANGELOG entry mentions the reader-only limitation.

So Android wheels are reader-only until libzim ships Android binaries with writer support. CI runs for the squashed commit are queued and waiting for your approval, I will report back here.

@pawank925

Copy link
Copy Markdown
Contributor Author

@benoit74 done thanks!

@benoit74 benoit74 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

@benoit74
benoit74 merged commit 584adac into openzim:main Nov 2, 2026
75 checks passed
@pawank925
pawank925 deleted the android-wheels branch November 2, 2026 10:06
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.

Build and publish wheels for common Android architectures

3 participants