Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/CI-wheels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,27 @@ jobs:
name: wheels-${{ matrix.os }}
path: ./wheelhouse/*.whl

build_wheels_android:
name: Build Android wheels
runs-on: ubuntu-26.04
env:
LIBZIM_WITHOUT_WRITER: "1"

steps:
- uses: actions/checkout@v7

- name: Build Android wheels
Comment thread
pawank925 marked this conversation as resolved.
Resolved
uses: pypa/cibuildwheel@v4.3
env:
CIBW_PLATFORM: android
CIBW_BUILD: "cp313-android_arm64_v8a cp313-android_x86_64 cp314-android_arm64_v8a cp314-android_x86_64 cp315-android_arm64_v8a cp315-android_x86_64"
CIBW_ENVIRONMENT_PASS_ANDROID: "LIBZIM_DL_VERSION LIBZIM_WITHOUT_WRITER"

- uses: actions/upload-artifact@v7
with:
name: wheels-android
path: ./wheelhouse/*.whl

build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add Android wheels support via cibuildwheel for arm64_v8a and x86_64 with CPython 3.13+; these wheels are reader-only since libzim is compiled without writer support on Android (#167)

### Changed

- Relax `setuptools`/`wheel`/`cython` build dependency pins to `>=floor,<next major>` ranges instead of exact versions, easing downstream packaging; `delocate` stays exactly pinned (#288)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ Our [PyPI wheels](https://pypi.org/project/libzim/) bundle a [recent release](ht
- GNU/Linux for `x86_64`, `armhf` and `aarch64`
- Linux+musl for `x86_64` and `aarch64`
- Windows for `x64`
- Android for `arm64_v8a` and `x86_64` with CPython 3.13+

Wheels are available for CPython only (but can be built for Pypy).

Free-threaded CPython is supported: the extension declares itself free-threading compatible, so importing it no longer forces the GIL back on. Only few methods support the GIL to be disabled; you must still prevent concurrent access to shared objects yourself (see [Thread safety](#thread-safety)).

Users on other platforms can install the source distribution (see [Building](#Building) below).

Writer support is decided when the extension is built, not when it is installed: there is no install-time extra for it. Set `LIBZIM_WITHOUT_WRITER=1` when building against a libzim compiled without writer support; such a build exposes the reader, search, suggestion, illustration and version modules only. Import `libzim.writer` in a `try`/`except ImportError` block to detect writer support at runtime.
Writer support is decided when the extension is built, not when it is installed: there is no install-time extra for it. Set `LIBZIM_WITHOUT_WRITER=1` when building against a libzim compiled without writer support; such a build exposes the reader, search, suggestion, illustration and version modules only. Import `libzim.writer` in a `try`/`except ImportError` block to detect writer support at runtime. Android wheels are built without writer support, since libzim is compiled without it for Android.


## Contributions
Expand Down
2 changes: 2 additions & 0 deletions libzim/libzim.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,8 @@ IF not LIBZIM_WITHOUT_WRITER:
IF not LIBZIM_WITHOUT_WRITER:
writer_module_doc = """libzim writer module

Not available on Android, where libzim is built without writer support.

- Creator to create ZIM files
- Item to store ZIM articles metadata
- ContentProvider to store an Item's content
Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ classifiers = [
"Typing :: Stubs Only",
"Operating System :: MacOS",
"Operating System :: POSIX",
"Operating System :: Android",
]

[project.urls]
Expand Down Expand Up @@ -118,6 +119,7 @@ libzim = [
"*.pyi",
"libzim.9.dylib",
"libzim.so.9",
"libzim.so",
"zim-9.dll",
"icuuc74.dll",
"icutu74.dll",
Expand Down Expand Up @@ -153,6 +155,10 @@ archs = ["x86_64", "arm64"]
# > requires changing wheel names
test-skip = "*_x86_64"

[tool.cibuildwheel.android]
archs = ["arm64_v8a", "x86_64"]
test-skip = "*"

[tool.hatch.build]
exclude = [
"/.github",
Expand Down
49 changes: 39 additions & 10 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import zipfile
from ctypes.util import find_library
from pathlib import Path
from typing import ClassVar

from Cython.Build import cythonize
from Cython.Distutils.build_ext import new_build_ext as build_ext
Expand All @@ -50,11 +51,20 @@ class Config:
# windows
_msvc_debug: bool = bool(os.getenv("MSVC_DEBUG"))

supported_platforms = { # noqa: RUF012
supported_platforms: ClassVar[dict[str, list[str]]] = {
"Darwin": ["x86_64", "arm64"],
"Linux": ["x86_64", "aarch64"],
"Linux-musl": ["x86_64", "aarch64"],
"Windows": ["amd64"],
"Android": ["arm64_v8a", "x86_64"],
}

_android_triplets: ClassVar[frozenset[str]] = frozenset(
{"aarch64-linux-android", "x86_64-linux-android"}
)
_android_arch_map: ClassVar[dict[str, str]] = {
"aarch64-linux-android": "arm64_v8a",
"x86_64-linux-android": "x86_64",
}

base_dir: pathlib.Path = Path(__file__).parent
Expand Down Expand Up @@ -93,19 +103,27 @@ def is_nightly(self) -> bool:

@property
def platform(self) -> str:
"""Platform building for: Darwin, Linux"""
"""Platform building for: Darwin, Linux, Android"""
if os.getenv("CIBW_HOST_TRIPLET") in self._android_triplets:
return "Android"
return sysplatform.system()

@property
def platform_libc(self) -> str:
"""Platform adjusted for libc variant: Darwin, Linux, Linux-musl"""
if self.platform == "Android":
return "Android"
if self.platform == "Linux" and self.is_musl:
return "Linux-musl"
return self.platform

@property
def arch(self) -> str:
# macOS x86_64|arm64 - linux x86_64|aarch64
# macOS x86_64|arm64 - linux x86_64|aarch64 - android arm64_v8a|x86_64

host_triplet = os.getenv("CIBW_HOST_TRIPLET", "")
if host_triplet in self._android_triplets:
return self._android_arch_map[host_triplet]

# when using cibuildwheel on macOS to cross-compile,
# `_PYTHON_HOST_PLATFORM` contains
Expand Down Expand Up @@ -134,6 +152,7 @@ def libzim_fname(self):
"Darwin": f"libzim.{self.libzim_major}.dylib",
"Linux": f"libzim.so.{self.libzim_major}",
"Windows": f"zim-{self.libzim_major}.dll",
"Android": "libzim.so",
}[self.platform]

@property
Expand All @@ -149,6 +168,8 @@ def archive_format(self):
@property
def is_musl(self) -> bool:
"""whether running on a musl system (Alpine)"""
if self.platform == "Android":
return False
ps = subprocess.run(
["/usr/bin/env", "ldd", "--version"],
capture_output=True,
Expand Down Expand Up @@ -183,13 +204,19 @@ def get_download_filename(self, arch: str | None = None) -> str:
if self.platform == "Windows" and arch == "amd64":
arch = "x86_64"

lzplatform = {"Darwin": "macos", "Linux": "linux", "Windows": "win"}.get(
self.platform
)
if self.platform == "Android":
android_arch_map = {"arm64_v8a": "arm64", "x86_64": "x86_64"}
arch = android_arch_map.get(arch, arch)
lzplatform = "android"
variant = ""
else:
lzplatform = {"Darwin": "macos", "Linux": "linux", "Windows": "win"}.get(
self.platform
)

variant = ""
if self.platform == "Linux":
variant = "-musl" if self.is_musl else "-manylinux"
variant = ""
if self.platform == "Linux":
variant = "-musl" if self.is_musl else "-manylinux"

if self.is_latest_nightly:
version_suffix = ""
Expand Down Expand Up @@ -448,7 +475,9 @@ def get_cython_extension() -> list[Extension]:
include_dirs.append("include")
library_dirs = ["libzim"]

if config.platform != "Windows":
if config.platform == "Android":
runtime_library_dirs = ["$ORIGIN/libzim/"]
elif config.platform != "Windows":
runtime_library_dirs = (
[f"@loader_path/libzim/{config.libzim_fname}"]
if config.platform == "Darwin"
Expand Down
Loading