From a0a78b42bc835a408961bcb44765520f26a13ad3 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 16 Dec 2025 11:56:10 +0000 Subject: [PATCH 01/26] Use auditwheel on Android --- bin/generate_schema.py | 17 +- cibuildwheel/platforms/android.py | 152 +++--------------- .../resources/cibuildwheel.schema.json | 15 +- .../resources/constraints-python310.txt | 2 + .../resources/constraints-python311.txt | 2 + .../resources/constraints-python312.txt | 2 + .../resources/constraints-python313.txt | 2 + .../resources/constraints-python314.txt | 2 + .../resources/constraints-python38.txt | 2 + .../resources/constraints-python39.txt | 2 + cibuildwheel/resources/constraints.in | 1 + cibuildwheel/resources/constraints.txt | 2 + docs/options.md | 4 +- pyproject.toml | 2 - 14 files changed, 68 insertions(+), 139 deletions(-) diff --git a/bin/generate_schema.py b/bin/generate_schema.py index 611d19e5a..f6f6072bd 100755 --- a/bin/generate_schema.py +++ b/bin/generate_schema.py @@ -366,14 +366,15 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]: "ios": as_object(not_linux), } -oses["linux"]["properties"]["repair-wheel-command"] = { - **schema["properties"]["repair-wheel-command"], - "default": "auditwheel repair -w {dest_dir} {wheel}", -} -oses["macos"]["properties"]["repair-wheel-command"] = { - **schema["properties"]["repair-wheel-command"], - "default": "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}", -} +for os_name, command in [ + ("linux", "auditwheel repair -w {dest_dir} {wheel}"), + ("macos", "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"), + ("android", "auditwheel repair --ldpaths {ldpaths} -w {dest_dir} {wheel}"), +]: + oses[os_name]["properties"]["repair-wheel-command"] = { + **schema["properties"]["repair-wheel-command"], + "default": command, + } del oses["linux"]["properties"]["dependency-versions"] diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 229401e4f..63eb3d770 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -1,15 +1,10 @@ -import csv -import hashlib import os import platform import re import shlex import shutil import subprocess -import sysconfig -from collections.abc import Iterable, Iterator from dataclasses import dataclass -from os.path import relpath from pathlib import Path from pprint import pprint from runpy import run_path @@ -18,8 +13,6 @@ from build import ProjectBuilder from build.env import IsolatedEnv -from elftools.common.exceptions import ELFError -from elftools.elf.elffile import ELFFile from filelock import FileLock from .. import errors, platforms # pylint: disable=cyclic-import @@ -228,10 +221,19 @@ def setup_env( android_env = setup_android_env(config, python_dir, venv_dir, build_env) # Install build tools - if build_frontend not in {"build", "build[uv]"}: + # TODO: use an official auditwheel version once + # https://github.com/pypa/auditwheel/pull/643 has been released, and add it to the + # constraints files. + tools = [ + "auditwheel @ git+https://github.com/mhsmith/auditwheel@android", + "patchelf", + ] + if build_frontend in {"build", "build[uv]"}: + tools.append("build") + else: msg = "Android requires the build frontend to be 'build'" raise errors.FatalError(msg) - call(*pip, "install", "build", *constraint_flags(dependency_constraint), env=build_env) + call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be @@ -321,6 +323,8 @@ def localized_vars( if isinstance(final, str): final = final.replace(orig_prefix, str(prefix)) + # By default we build against the same API level as Python itself, but this can + # be overridden with an environment variable. if key == "ANDROID_API_LEVEL": if api_level := build_env.get(key): final = int(api_level) @@ -445,9 +449,20 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: repaired_wheel_dir.mkdir() if state.options.repair_command: + toolchain = Path(state.android_env["CC"]).parent.parent + ldpaths = ":".join( + # In the future, we may use this to implement PEP 725 by installing + # libraries in {state.python_dir}/prefix/lib or elsewhere, and adding that + # location to ldpaths. + [ + # For libc++_shared. + f"{toolchain}/sysroot/usr/lib/{state.android_env['CIBW_HOST_TRIPLET']}", + ] + ) shell( prepare_command( state.options.repair_command, + ldpaths=ldpaths, wheel=built_wheel, dest_dir=repaired_wheel_dir, package=state.options.package_dir, @@ -456,7 +471,7 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: env=state.build_env, ) else: - repair_default(state.android_env, built_wheel, repaired_wheel_dir) + shutil.move(built_wheel, repaired_wheel_dir) repaired_wheels = list(repaired_wheel_dir.glob("*.whl")) if len(repaired_wheels) == 0: @@ -471,106 +486,6 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: return repaired_wheel -def repair_default( - android_env: dict[str, str], built_wheel: Path, repaired_wheel_dir: Path -) -> None: - """ - Adds libc++ to the wheel if anything links against it. In the future this should be - moved to auditwheel and generalized to support more libraries. - """ - if (match := re.search(r"^(.+?)-", built_wheel.name)) is None: - msg = f"Failed to parse wheel filename: {built_wheel.name}" - raise errors.FatalError(msg) - wheel_name = match[1] - - unpacked_dir = repaired_wheel_dir / "unpacked" - unpacked_dir.mkdir() - shutil.unpack_archive(built_wheel, unpacked_dir, format="zip") - - # Some build systems are inconsistent about name normalization, so don't assume the - # dist-info name is identical to the wheel name. - record_paths = list(unpacked_dir.glob("*.dist-info/RECORD")) - if len(record_paths) != 1: - msg = f"{built_wheel.name} contains {len(record_paths)} dist-info/RECORD files; expected 1" - raise errors.FatalError(msg) - - old_soname = "libc++_shared.so" - paths_to_patch = [] - for path, elffile in elf_file_filter( - unpacked_dir / filename - for filename, *_ in csv.reader(record_paths[0].read_text().splitlines()) - ): - if (dynamic := elffile.get_section_by_name(".dynamic")) and any( # type: ignore[no-untyped-call] - tag.entry.d_tag == "DT_NEEDED" and tag.needed == old_soname - for tag in dynamic.iter_tags() - ): - paths_to_patch.append(path) - - if not paths_to_patch: - shutil.copyfile(built_wheel, repaired_wheel_dir / built_wheel.name) - else: - # Android doesn't support DT_RPATH, but supports DT_RUNPATH since API level 24 - # (https://github.com/aosp-mirror/platform_bionic/blob/master/android-changes-for-ndk-developers.md). - if int(sysconfig_print('get_config_vars()["ANDROID_API_LEVEL"]', android_env)) < 24: - msg = f"Adding {old_soname} requires ANDROID_API_LEVEL to be at least 24" - raise errors.FatalError(msg) - - toolchain = Path(android_env["CC"]).parent.parent - src_path = toolchain / f"sysroot/usr/lib/{android_env['CIBW_HOST_TRIPLET']}/{old_soname}" - - # Use the same library location as auditwheel would. - libs_dir = unpacked_dir / (wheel_name + ".libs") - libs_dir.mkdir() - new_soname = soname_with_hash(src_path) - dst_path = libs_dir / new_soname - shutil.copyfile(src_path, dst_path) - call(which("patchelf"), "--set-soname", new_soname, dst_path) - - for path in paths_to_patch: - call(which("patchelf"), "--replace-needed", old_soname, new_soname, path) - call( - which("patchelf"), - "--set-rpath", - f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}", - path, - ) - call(which("wheel"), "pack", unpacked_dir, "-d", repaired_wheel_dir) - - -# If cibuildwheel was called without activating its environment, its scripts directory -# will not be on the PATH. -def which(cmd: str) -> str: - scripts_dir = sysconfig.get_path("scripts") - result = shutil.which(cmd, path=scripts_dir + os.pathsep + os.environ["PATH"]) - if result is None: - msg = f"Couldn't find {cmd!r} in {scripts_dir} or on the PATH" - raise errors.FatalError(msg) - return result - - -def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]: - """Filter through an iterator of filenames and load up only ELF files""" - for path in paths: - if not path.name.endswith(".py"): - try: - with open(path, "rb") as f: - candidate = ELFFile(f) # type: ignore[no-untyped-call] - yield path, candidate - except ELFError: - pass # Not an ELF file - - -def soname_with_hash(src_path: Path) -> str: - """Return the same library filename as auditwheel would""" - shorthash = hashlib.sha256(src_path.read_bytes()).hexdigest()[:8] - src_name = src_path.name - base, ext = src_name.split(".", 1) - if not base.endswith(f"-{shorthash}"): - return f"{base}-{shorthash}.{ext}" - else: - return src_name - - def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: test_command = state.options.test_command if not (test_command and state.options.test_selector(state.config.identifier)): @@ -600,12 +515,7 @@ def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: ) platform_args = ( - ["--python-platform", android_triplet(state.config.identifier)] - if use_uv - else [ - "--platform", - sysconfig_print("get_platform()", state.android_env).replace("-", "_"), - ] + ["--python-platform", android_triplet(state.config.identifier)] if use_uv else [] ) # Install the wheel and test-requires. @@ -690,13 +600,3 @@ def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: *test_args, env=state.build_env, ) - - -def sysconfig_print(method_call: str, env: dict[str, str]) -> str: - return call( - "python", - "-c", - f'import sysconfig; print(sysconfig.{method_call}, end="")', - env=env, - capture_stdout=True, - ) diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json index 2c52aada4..a1b1ca6a4 100644 --- a/cibuildwheel/resources/cibuildwheel.schema.json +++ b/cibuildwheel/resources/cibuildwheel.schema.json @@ -1161,7 +1161,20 @@ "$ref": "#/properties/pyodide-version" }, "repair-wheel-command": { - "$ref": "#/properties/repair-wheel-command" + "description": "Execute a shell command to repair each built wheel.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "title": "CIBW_REPAIR_WHEEL_COMMAND", + "default": "auditwheel repair --ldpaths {ldpaths} -w {dest_dir} {wheel}" }, "test-command": { "$ref": "#/properties/test-command" diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt index 698481a26..94340a328 100644 --- a/cibuildwheel/resources/constraints-python310.txt +++ b/cibuildwheel/resources/constraints-python310.txt @@ -18,6 +18,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt index c3b366c6b..f2ab400ce 100644 --- a/cibuildwheel/resources/constraints-python311.txt +++ b/cibuildwheel/resources/constraints-python311.txt @@ -16,6 +16,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt index c3b366c6b..f2ab400ce 100644 --- a/cibuildwheel/resources/constraints-python312.txt +++ b/cibuildwheel/resources/constraints-python312.txt @@ -16,6 +16,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt index c3b366c6b..f2ab400ce 100644 --- a/cibuildwheel/resources/constraints-python313.txt +++ b/cibuildwheel/resources/constraints-python313.txt @@ -16,6 +16,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt index c3b366c6b..f2ab400ce 100644 --- a/cibuildwheel/resources/constraints-python314.txt +++ b/cibuildwheel/resources/constraints-python314.txt @@ -16,6 +16,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt index c7fe84056..c11f8eaa8 100644 --- a/cibuildwheel/resources/constraints-python38.txt +++ b/cibuildwheel/resources/constraints-python38.txt @@ -18,6 +18,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.0.1 # via -r cibuildwheel/resources/constraints.in platformdirs==4.3.6 diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt index 634899421..9b6f7e7fb 100644 --- a/cibuildwheel/resources/constraints-python39.txt +++ b/cibuildwheel/resources/constraints-python39.txt @@ -18,6 +18,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.4.0 diff --git a/cibuildwheel/resources/constraints.in b/cibuildwheel/resources/constraints.in index 50bfabb6e..713c93ee2 100644 --- a/cibuildwheel/resources/constraints.in +++ b/cibuildwheel/resources/constraints.in @@ -1,4 +1,5 @@ pip build +patchelf delocate virtualenv diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt index c3b366c6b..f2ab400ce 100644 --- a/cibuildwheel/resources/constraints.txt +++ b/cibuildwheel/resources/constraints.txt @@ -16,6 +16,8 @@ packaging==25.0 # via # build # delocate +patchelf==0.17.2.4 + # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 diff --git a/docs/options.md b/docs/options.md index c94cb0424..098bb6c36 100644 --- a/docs/options.md +++ b/docs/options.md @@ -893,8 +893,7 @@ Default: - on Linux: `'auditwheel repair -w {dest_dir} {wheel}'` - on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'` -- on Android: There is no default command, but cibuildwheel will add `libc++` to the - wheel if anything links against it. Setting a command will replace this behavior. +- on Android: `'auditwheel repair --ldpaths {ldpaths} -w {dest_dir} {wheel}'` - on other platforms: `''` A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them. @@ -905,6 +904,7 @@ The following placeholders must be used inside the command and will be replaced - `{wheel}` for the absolute path to the built wheel - `{dest_dir}` for the absolute path of the directory where to create the repaired wheel - `{delocate_archs}` (macOS only) comma-separated list of architectures in the wheel. +- `{ldpaths}` (Android only) colon-separated list of directories to search for external libraries. You can use the `{package}` or `{project}` placeholders in your `repair-wheel-command` to refer to the package being built or the project root, respectively. diff --git a/pyproject.toml b/pyproject.toml index 6e8345586..642c6f62b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,8 +45,6 @@ dependencies = [ "filelock", "humanize", "packaging>=20.9", - # patchelf is used for Android - "patchelf; (sys_platform == 'linux' or sys_platform == 'darwin') and (platform_machine == 'x86_64' or platform_machine == 'arm64' or platform_machine == 'aarch64')", "platformdirs", "pyelftools>=0.29", "wheel>=0.33.6", From 2996c101cd488eda3d1310a1ac510cb405b51750 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 16 Dec 2025 17:56:19 +0000 Subject: [PATCH 02/26] Add auditwheel command to defaults --- bin/generate_schema.py | 2 ++ cibuildwheel/resources/defaults.toml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/bin/generate_schema.py b/bin/generate_schema.py index f6f6072bd..c1ac0af38 100755 --- a/bin/generate_schema.py +++ b/bin/generate_schema.py @@ -15,6 +15,8 @@ parser.add_argument("--schemastore", action="store_true", help="Generate schema_store version") args = parser.parse_args() +# The defaults in the schema are only used for documentation and IDE support. They +# should match the values in defaults.toml, which are used by cibuildwheel itself. starter = """ $schema: http://json-schema.org/draft-07/schema# $id: https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/resources/cibuildwheel.schema.json diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml index 78895bf9a..60a41e183 100644 --- a/cibuildwheel/resources/defaults.toml +++ b/cibuildwheel/resources/defaults.toml @@ -1,3 +1,6 @@ +# These are the defaults used by cibuildwheel itself. They should match the values in +# generate_schema.py, which are used for documentation and IDE support. + [tool.cibuildwheel] build = "*" skip = "" @@ -60,6 +63,7 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest [tool.cibuildwheel.windows] [tool.cibuildwheel.android] +repair-wheel-command = "auditwheel repair --ldpaths {ldpaths} -w {dest_dir} {wheel}" [tool.cibuildwheel.ios] From f650f03c3647d1ce975a76472936fd528e8a297a Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 16 Dec 2025 18:00:21 +0000 Subject: [PATCH 03/26] pkgconfig fixes --- cibuildwheel/platforms/android.py | 11 +++++++++-- cibuildwheel/resources/constraints-python310.txt | 2 ++ cibuildwheel/resources/constraints-python311.txt | 2 ++ cibuildwheel/resources/constraints-python312.txt | 2 ++ cibuildwheel/resources/constraints-python313.txt | 2 ++ cibuildwheel/resources/constraints-python314.txt | 2 ++ cibuildwheel/resources/constraints-python38.txt | 6 +++++- cibuildwheel/resources/constraints-python39.txt | 6 +++++- cibuildwheel/resources/constraints.in | 7 ++++++- cibuildwheel/resources/constraints.txt | 2 ++ 10 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 63eb3d770..fd7410099 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -169,6 +169,12 @@ def setup_target_python(config: PythonConfiguration, build_path: Path) -> Path: python_dir = build_path / "python" python_dir.mkdir() shutil.unpack_archive(python_tgz, python_dir) + + # Work around https://github.com/python/cpython/issues/138800. This can be removed + # once we've updated to Python versions that include the fix. + pc_path = python_dir / f"prefix/lib/pkgconfig/python-{config.version}.pc" + pc_path.write_text(pc_path.read_text().replace("$(BLDLIBRARY)", f"-lpython{config.version}")) + return python_dir @@ -227,6 +233,7 @@ def setup_env( tools = [ "auditwheel @ git+https://github.com/mhsmith/auditwheel@android", "patchelf", + "pkgconf", ] if build_frontend in {"build", "build[uv]"}: tools.append("build") @@ -408,7 +415,7 @@ def before_build(state: BuildState) -> None: shell_prepared( state.options.before_build, build_options=state.options, - env=state.build_env, + env=state.android_env, ) @@ -511,7 +518,7 @@ def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: shell_prepared( state.options.before_test, build_options=state.options, - env=state.build_env, + env=state.android_env, ) platform_args = ( diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt index 94340a328..c9572c564 100644 --- a/cibuildwheel/resources/constraints-python310.txt +++ b/cibuildwheel/resources/constraints-python310.txt @@ -22,6 +22,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt index f2ab400ce..b7d84a988 100644 --- a/cibuildwheel/resources/constraints-python311.txt +++ b/cibuildwheel/resources/constraints-python311.txt @@ -20,6 +20,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt index f2ab400ce..b7d84a988 100644 --- a/cibuildwheel/resources/constraints-python312.txt +++ b/cibuildwheel/resources/constraints-python312.txt @@ -20,6 +20,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt index f2ab400ce..b7d84a988 100644 --- a/cibuildwheel/resources/constraints-python313.txt +++ b/cibuildwheel/resources/constraints-python313.txt @@ -20,6 +20,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt index f2ab400ce..b7d84a988 100644 --- a/cibuildwheel/resources/constraints-python314.txt +++ b/cibuildwheel/resources/constraints-python314.txt @@ -20,6 +20,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt index c11f8eaa8..f84244449 100644 --- a/cibuildwheel/resources/constraints-python38.txt +++ b/cibuildwheel/resources/constraints-python38.txt @@ -11,7 +11,9 @@ distlib==0.4.0 filelock==3.16.1 # via virtualenv importlib-metadata==8.5.0 - # via build + # via + # build + # pkgconf macholib==1.16.4 # via delocate packaging==25.0 @@ -22,6 +24,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.0.1 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.2.0.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.3.6 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt index 9b6f7e7fb..16ebf871b 100644 --- a/cibuildwheel/resources/constraints-python39.txt +++ b/cibuildwheel/resources/constraints-python39.txt @@ -11,7 +11,9 @@ distlib==0.4.0 filelock==3.19.1 # via virtualenv importlib-metadata==8.7.0 - # via build + # via + # build + # pkgconf macholib==1.16.4 # via delocate packaging==25.0 @@ -22,6 +24,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.4.3.post2 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.4.0 # via virtualenv pyproject-hooks==1.2.0 diff --git a/cibuildwheel/resources/constraints.in b/cibuildwheel/resources/constraints.in index 713c93ee2..562df62cf 100644 --- a/cibuildwheel/resources/constraints.in +++ b/cibuildwheel/resources/constraints.in @@ -1,5 +1,10 @@ pip build +virtualenv + +# Android patchelf +pkgconf + +# macOS delocate -virtualenv diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt index f2ab400ce..b7d84a988 100644 --- a/cibuildwheel/resources/constraints.txt +++ b/cibuildwheel/resources/constraints.txt @@ -20,6 +20,8 @@ patchelf==0.17.2.4 # via -r cibuildwheel/resources/constraints.in pip==25.3 # via -r cibuildwheel/resources/constraints.in +pkgconf==2.5.1.post0 + # via -r cibuildwheel/resources/constraints.in platformdirs==4.5.1 # via virtualenv pyproject-hooks==1.2.0 From 5f509e28959426633518e8ca532940d840e38e80 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 18 Dec 2025 00:31:54 +0000 Subject: [PATCH 04/26] Pre-import ctypes before monkey-patching in _cross_venv --- cibuildwheel/resources/_cross_venv.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cibuildwheel/resources/_cross_venv.py b/cibuildwheel/resources/_cross_venv.py index 40dfaca5f..0c50dcce2 100644 --- a/cibuildwheel/resources/_cross_venv.py +++ b/cibuildwheel/resources/_cross_venv.py @@ -14,6 +14,9 @@ def initialize() -> None: if not (host_triplet := os.environ.get("CIBW_HOST_TRIPLET")): return + # Pre-import any modules which would fail to import after the monkey-patching. + import ctypes # noqa: F401, PLC0415 - uses get_config_var("LDLIBRARY") + # os ###################################################################### def cross_os_uname() -> os.uname_result: return os.uname_result( From 9d560aea34da8de0b053caa40ed27b0675faacf4 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 9 Jan 2026 17:09:27 +0000 Subject: [PATCH 05/26] Set PKG_CONFIG and PKG_CONFIG_RELOCATE_PATHS variables --- cibuildwheel/platforms/android.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index fd7410099..68f3d918a 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -242,6 +242,12 @@ def setup_env( raise errors.FatalError(msg) call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) + # android-env.sh sets PKG_CONFIG="pkg-config --define-prefix", but some build + # systems can't handle arguments in that variable. Since we have a known version + # of pkgconf, it's safe to use PKG_CONFIG_RELOCATE_PATHS instead. + build_env["PKG_CONFIG"] = call("which", "pkgconf", env=build_env, capture_stdout=True).strip() + build_env["PKG_CONFIG_RELOCATE_PATHS"] = "1" + # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be # affected by the target platform. However, the requirements must be installed From 93053e7f795b009f25c0a3c16e8543b251e4f935 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 9 Jan 2026 17:18:40 +0000 Subject: [PATCH 06/26] Initial attempt at using mzakharo/android-gfortran --- .pre-commit-config.yaml | 2 +- cibuildwheel/platforms/android.py | 11 +++++++- .../resources/{ => android}/_cross_venv.pth | 0 .../resources/{ => android}/_cross_venv.py | 0 .../resources/android/fortran-entrypoint.sh | 9 ++++++ .../resources/android/fortran-shim.sh | 28 +++++++++++++++++++ .../resources/android/fortran.dockerfile | 15 ++++++++++ 7 files changed, 63 insertions(+), 2 deletions(-) rename cibuildwheel/resources/{ => android}/_cross_venv.pth (100%) rename cibuildwheel/resources/{ => android}/_cross_venv.py (100%) create mode 100755 cibuildwheel/resources/android/fortran-entrypoint.sh create mode 100755 cibuildwheel/resources/android/fortran-shim.sh create mode 100644 cibuildwheel/resources/android/fortran.dockerfile diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e0bbcd7b..5e0bec153 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: - id: mypy name: mypy 3.11 on cibuildwheel/ args: ["--python-version=3.11"] - exclude: ^cibuildwheel/resources/_cross_venv.py$ # Requires Python 3.13 or later + exclude: ^cibuildwheel/resources/android/_cross_venv.py$ # Requires Python 3.13 or later additional_dependencies: &mypy-dependencies - bracex - build diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 68f3d918a..ce97bf481 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -367,7 +367,7 @@ def setup_android_env( ) -> dict[str, str]: site_packages = next(venv_dir.glob("lib/python*/site-packages")) for suffix in ["pth", "py"]: - shutil.copy(resources.PATH / f"_cross_venv.{suffix}", site_packages) + shutil.copy(resources.PATH / f"android/_cross_venv.{suffix}", site_packages) sysconfigdata_path = Path( shutil.copy( @@ -407,6 +407,9 @@ def setup_android_env( for key in ["CFLAGS", "CXXFLAGS"]: android_env[key] += " " + opt + # Create shims which install additional build tools on first use. + setup_fortran(android_env) + # Format the environment so it can be pasted into a shell when debugging. for key, value in sorted(android_env.items()): if os.environ.get(key) != value: @@ -415,6 +418,12 @@ def setup_android_env( return android_env +def setup_fortran(env: dict[str, str]) -> None: + Path(f"{env['VIRTUAL_ENV']}/bin/gfortran").symlink_to( + resources.PATH / "android/fortran-shim.sh", + ) + + def before_build(state: BuildState) -> None: if state.options.before_build: log.step("Running before_build...") diff --git a/cibuildwheel/resources/_cross_venv.pth b/cibuildwheel/resources/android/_cross_venv.pth similarity index 100% rename from cibuildwheel/resources/_cross_venv.pth rename to cibuildwheel/resources/android/_cross_venv.pth diff --git a/cibuildwheel/resources/_cross_venv.py b/cibuildwheel/resources/android/_cross_venv.py similarity index 100% rename from cibuildwheel/resources/_cross_venv.py rename to cibuildwheel/resources/android/_cross_venv.py diff --git a/cibuildwheel/resources/android/fortran-entrypoint.sh b/cibuildwheel/resources/android/fortran-entrypoint.sh new file mode 100755 index 000000000..d9fb27d9a --- /dev/null +++ b/cibuildwheel/resources/android/fortran-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -eu + +cwd="${1:?}" +shift + +cd "/host$cwd" +exec /root/*-linux-android-4.9/bin/*-linux-android-gfortran "$@" diff --git a/cibuildwheel/resources/android/fortran-shim.sh b/cibuildwheel/resources/android/fortran-shim.sh new file mode 100755 index 000000000..b1b8b3d01 --- /dev/null +++ b/cibuildwheel/resources/android/fortran-shim.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Pre-built copies of the Android Fortran compiler are only available for Linux x86_64, +# so we use Docker to allow it to run on macOS as well. +# +# TODO: only run docker build once, then replace this file with a different script. +set -eu + +cwd="$(pwd)" +cd "$(dirname "$(realpath "$0")")" + +arch="$(echo "$CIBW_HOST_TRIPLET" | sed 's/-.*//')" +tag="cibw-android-fortran-$arch" + +docker build \ + --platform linux/amd64 -f fortran.dockerfile -t "$tag" --build-arg arch="$arch" . + +# On macOS, Docker can only mount certain host directories by default. +if [ "$(uname)" = "Darwin" ]; then + mount_args="-v /Users:/host/Users -v /tmp:/host/tmp" +else + mount_args="-v /:/host" +fi + +# TODO mount /Users, /home, /private and /tmp if they exist, then pass working dir using +# -w. Then no entry point script is needed. +# shellcheck disable=SC2086 +docker run --rm --platform linux/amd64 $mount_args "$tag" "$cwd" "$@" diff --git a/cibuildwheel/resources/android/fortran.dockerfile b/cibuildwheel/resources/android/fortran.dockerfile new file mode 100644 index 000000000..f0abcd1b0 --- /dev/null +++ b/cibuildwheel/resources/android/fortran.dockerfile @@ -0,0 +1,15 @@ +FROM debian:trixie + +WORKDIR /root + +RUN apt-get update && \ + apt-get -y install bzip2 wget + +ARG arch +RUN if [ "${arch:?}" = "aarch64" ]; then arch="arm64"; fi && \ + filename="gcc-$arch-linux-x86_64.tar.bz2" && \ + wget "https://github.com/mzakharo/android-gfortran/releases/download/r21e/$filename" && \ + tar -xf "$filename" + +COPY fortran-entrypoint.sh ./ +ENTRYPOINT ["./fortran-entrypoint.sh"] From 926da78ea745e41845516e2a9bb3809d46a97008 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sat, 10 Jan 2026 18:22:50 +0000 Subject: [PATCH 07/26] Switch to using termux/ndk-toolchain-clang-with-flang --- cibuildwheel/platforms/android.py | 14 +- .../resources/android/fortran-entrypoint.sh | 9 -- .../resources/android/fortran-shim.sh | 28 ---- .../resources/android/fortran.dockerfile | 15 -- .../resources/android/fortran_shim.py | 151 ++++++++++++++++++ 5 files changed, 162 insertions(+), 55 deletions(-) delete mode 100755 cibuildwheel/resources/android/fortran-entrypoint.sh delete mode 100755 cibuildwheel/resources/android/fortran-shim.sh delete mode 100644 cibuildwheel/resources/android/fortran.dockerfile create mode 100644 cibuildwheel/resources/android/fortran_shim.py diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index ce97bf481..a146a8e0e 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -4,6 +4,7 @@ import shlex import shutil import subprocess +import sys from dataclasses import dataclass from pathlib import Path from pprint import pprint @@ -419,9 +420,16 @@ def setup_android_env( def setup_fortran(env: dict[str, str]) -> None: - Path(f"{env['VIRTUAL_ENV']}/bin/gfortran").symlink_to( - resources.PATH / "android/fortran-shim.sh", - ) + # "flang-new" is the standard executable name for our current version of Flang. In + # future versions this will change to "flang" + # (https://blog.llvm.org/posts/2025-03-11-flang-new/). + shim_in = resources.PATH / "android/fortran_shim.py" + shim_out = Path(env["VIRTUAL_ENV"]) / "bin/flang-new" + + # The hashbang line runs the shim in cibuildwheel's own virtual environment, so it + # has access to utility functions for downloading and caching files. + shim_out.write_text(f"#!{sys.executable}\n\n" + shim_in.read_text()) + shim_out.chmod(0o755) def before_build(state: BuildState) -> None: diff --git a/cibuildwheel/resources/android/fortran-entrypoint.sh b/cibuildwheel/resources/android/fortran-entrypoint.sh deleted file mode 100755 index d9fb27d9a..000000000 --- a/cibuildwheel/resources/android/fortran-entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -set -eu - -cwd="${1:?}" -shift - -cd "/host$cwd" -exec /root/*-linux-android-4.9/bin/*-linux-android-gfortran "$@" diff --git a/cibuildwheel/resources/android/fortran-shim.sh b/cibuildwheel/resources/android/fortran-shim.sh deleted file mode 100755 index b1b8b3d01..000000000 --- a/cibuildwheel/resources/android/fortran-shim.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# -# Pre-built copies of the Android Fortran compiler are only available for Linux x86_64, -# so we use Docker to allow it to run on macOS as well. -# -# TODO: only run docker build once, then replace this file with a different script. -set -eu - -cwd="$(pwd)" -cd "$(dirname "$(realpath "$0")")" - -arch="$(echo "$CIBW_HOST_TRIPLET" | sed 's/-.*//')" -tag="cibw-android-fortran-$arch" - -docker build \ - --platform linux/amd64 -f fortran.dockerfile -t "$tag" --build-arg arch="$arch" . - -# On macOS, Docker can only mount certain host directories by default. -if [ "$(uname)" = "Darwin" ]; then - mount_args="-v /Users:/host/Users -v /tmp:/host/tmp" -else - mount_args="-v /:/host" -fi - -# TODO mount /Users, /home, /private and /tmp if they exist, then pass working dir using -# -w. Then no entry point script is needed. -# shellcheck disable=SC2086 -docker run --rm --platform linux/amd64 $mount_args "$tag" "$cwd" "$@" diff --git a/cibuildwheel/resources/android/fortran.dockerfile b/cibuildwheel/resources/android/fortran.dockerfile deleted file mode 100644 index f0abcd1b0..000000000 --- a/cibuildwheel/resources/android/fortran.dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM debian:trixie - -WORKDIR /root - -RUN apt-get update && \ - apt-get -y install bzip2 wget - -ARG arch -RUN if [ "${arch:?}" = "aarch64" ]; then arch="arm64"; fi && \ - filename="gcc-$arch-linux-x86_64.tar.bz2" && \ - wget "https://github.com/mzakharo/android-gfortran/releases/download/r21e/$filename" && \ - tar -xf "$filename" - -COPY fortran-entrypoint.sh ./ -ENTRYPOINT ["./fortran-entrypoint.sh"] diff --git a/cibuildwheel/resources/android/fortran_shim.py b/cibuildwheel/resources/android/fortran_shim.py new file mode 100644 index 000000000..04f8792b5 --- /dev/null +++ b/cibuildwheel/resources/android/fortran_shim.py @@ -0,0 +1,151 @@ +# Based on +# https://github.com/kivy/python-for-android/blob/develop/pythonforandroid/recipes/fortran/__init__.py + +import os +import re +import shutil +import sys +from itertools import chain +from pathlib import Path + +from filelock import FileLock + +from cibuildwheel.util.file import CIBW_CACHE_PATH, download + +RELEASE_URL = "https://github.com/termux/ndk-toolchain-clang-with-flang/releases/download" +RELEASE_VERSION = "r27c" + +# The compiler is built for Linux x86_64, so we use Docker on macOS. +DOCKER_IMAGE = "debian:trixie" + + +def main() -> None: + cache_dir = CIBW_CACHE_PATH / f"flang-android-{RELEASE_VERSION}" + with FileLock(f"{cache_dir}.lock"): + if not cache_dir.exists(): + download_flang(cache_dir) + + flang_dir = Path(__file__).parents[2] / "flang" + with FileLock(f"{flang_dir}.lock"): + if not flang_dir.exists(): + setup_flang(cache_dir, flang_dir) + + run_flang(flang_dir) + + +def download_flang(cache_dir: Path) -> None: + tmp_dir = Path(f"{cache_dir}.tmp") + if tmp_dir.exists(): + shutil.rmtree(tmp_dir) + tmp_dir.mkdir(parents=True) + + for archive_name, src, dst in [ + ( + f"package-flang-{arch}.tar.bz2", + f"build-{arch}-install", + f"sysroot/usr/lib/{arch}-linux-android", + ) + for arch in ["aarch64", "x86_64"] + ] + [ + ("package-install.tar.bz2", "out/install/linux-x86/clang-dev", ""), + ("package-flang-host.tar.bz2", "build-host-install", ""), + ]: + archive_path = tmp_dir / archive_name + download(f"{RELEASE_URL}/{RELEASE_VERSION}/{archive_name}", archive_path) + shutil.unpack_archive(archive_path, tmp_dir) + archive_path.unlink() + + (tmp_dir / dst).mkdir(parents=True, exist_ok=True) + for src_path in (tmp_dir / src).iterdir(): + src_path.rename(tmp_dir / dst / src_path.name) + + tmp_dir.rename(cache_dir) + + +def setup_flang(cache_dir: Path, flang_dir: Path) -> None: + toolchain_dir = Path(os.environ["CC"]).parents[1] + ndk_dir = toolchain_dir.parents[3] + clang_ver_ndk = clang_ver(ndk_dir) + + clang_ver_cache = clang_ver(cache_dir) + if clang_ver_cache != clang_ver_ndk: + msg = f"Flang uses Clang {clang_ver_cache}, but NDK uses Clang {clang_ver_ndk}" + raise ValueError(msg) + + # Merge the Flang tree with the parts of the NDK it uses. + tmp_dir = Path(f"{flang_dir}.tmp") + if tmp_dir.exists(): + shutil.rmtree(tmp_dir) + tmp_dir.mkdir(parents=True) + + merge_path(cache_dir, tmp_dir) + merge_path(toolchain_dir, tmp_dir, f"lib/clang{clang_ver_ndk}/lib") + merge_path(toolchain_dir, tmp_dir, "sysroot") + + tmp_dir.rename(flang_dir) + + +def clang_ver(toolchain_dir: Path) -> str: + versions = [p.name for p in (toolchain_dir / "lib/clang").iterdir()] + assert len(versions) == 1 + return versions[0] + + +# The merged tree is more than 1 GB, so use symlinks to avoid copying. +def merge_path(src_dir: Path, dst_dir: Path, rel_path: str | None = None) -> None: + if rel_path is None: + for p in src_dir.iterdir(): + merge_path(src_dir, dst_dir, p.name) + return + + if not dst_dir.exists(): + dst_dir.mkdir() + elif dst_dir.is_dir(): + if dst_dir.is_symlink(): + old_src_dir = dst_dir.readlink() + dst_dir.unlink() + dst_dir.mkdir() + for p in old_src_dir.iterdir(): + (dst_dir / p.name).symlink_to(p) + else: + msg = f"{dst_dir} is not a directory" + raise ValueError(msg) + + prefix, sep, suffix = rel_path.partition("/") + if sep: + merge_path(src_dir / prefix, dst_dir / prefix, suffix) + else: + dst_path = dst_dir / rel_path + if dst_path.exists(): + merge_path(src_dir / rel_path, dst_dir / rel_path) + else: + dst_path.symlink_to(src_dir / rel_path) + + +def run_flang(flang_dir: Path) -> None: + match = re.fullmatch(r"(.+)-clang", os.environ["CC"]) + assert match is not None + args = [f"{flang_dir}/bin/flang-new", f"--target={match[1]}", *sys.argv[1:]] + + if sys.platform == "linux": + pass + elif sys.platform == "darwin": + args = [ + *["docker", "run"], + *chain.from_iterable( + # Docker on macOS only allows certain directories to be mounted as volumes, + # by default, but they include all the locations we're likely to need. + ["-v", f"{path}:{path}"] + for path in ["/private", "/Users", "/tmp"] + ), + *["-w", str(Path.cwd()), "--entrypoint", args[0], DOCKER_IMAGE, *args[1:]], + ] + else: + msg = f"unknown platform: {sys.platform}" + raise ValueError(msg) + + os.execvp(args[0], args) + + +if __name__ == "__main__": + main() From e996cd717319a6fb941d73321a2b24fe8d69434c Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 11 Jan 2026 00:48:01 +0000 Subject: [PATCH 08/26] Move PKG_CONFIG variables from build_env to android_env --- cibuildwheel/platforms/android.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index a146a8e0e..f0be50dc1 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -224,9 +224,6 @@ def setup_env( raise errors.FatalError(msg) call(command, "--version", env=build_env) - # Construct an altered environment which simulates running on Android. - android_env = setup_android_env(config, python_dir, venv_dir, build_env) - # Install build tools # TODO: use an official auditwheel version once # https://github.com/pypa/auditwheel/pull/643 has been released, and add it to the @@ -243,11 +240,8 @@ def setup_env( raise errors.FatalError(msg) call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) - # android-env.sh sets PKG_CONFIG="pkg-config --define-prefix", but some build - # systems can't handle arguments in that variable. Since we have a known version - # of pkgconf, it's safe to use PKG_CONFIG_RELOCATE_PATHS instead. - build_env["PKG_CONFIG"] = call("which", "pkgconf", env=build_env, capture_stdout=True).strip() - build_env["PKG_CONFIG_RELOCATE_PATHS"] = "1" + # Construct an altered environment which simulates running on Android. + android_env = setup_android_env(config, python_dir, venv_dir, build_env) # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be @@ -411,6 +405,12 @@ def setup_android_env( # Create shims which install additional build tools on first use. setup_fortran(android_env) + # `android.py env` returns PKG_CONFIG="pkg-config --define-prefix", but some build + # systems can't handle arguments in that variable. Since we have a known version + # of pkgconf, it's safe to use PKG_CONFIG_RELOCATE_PATHS instead. + android_env["PKG_CONFIG"] = call("which", "pkgconf", env=build_env, capture_stdout=True).strip() + android_env["PKG_CONFIG_RELOCATE_PATHS"] = "1" + # Format the environment so it can be pasted into a shell when debugging. for key, value in sorted(android_env.items()): if os.environ.get(key) != value: From bceed3c11d4a5e94561ed069de61f6d20edc7318 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 11 Jan 2026 00:54:02 +0000 Subject: [PATCH 09/26] Simplify flang installation --- cibuildwheel/platforms/android.py | 6 +- .../resources/android/fortran_shim.py | 120 ++++++------------ 2 files changed, 43 insertions(+), 83 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index f0be50dc1..cc33b07f5 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -420,9 +420,8 @@ def setup_android_env( def setup_fortran(env: dict[str, str]) -> None: - # "flang-new" is the standard executable name for our current version of Flang. In - # future versions this will change to "flang" - # (https://blog.llvm.org/posts/2025-03-11-flang-new/). + # In case there's any autodetection based on the executable name, use the same name + # as the real executable (see fortran_shim.run_flang) shim_in = resources.PATH / "android/fortran_shim.py" shim_out = Path(env["VIRTUAL_ENV"]) / "bin/flang-new" @@ -430,6 +429,7 @@ def setup_fortran(env: dict[str, str]) -> None: # has access to utility functions for downloading and caching files. shim_out.write_text(f"#!{sys.executable}\n\n" + shim_in.read_text()) shim_out.chmod(0o755) + env["FC"] = str(shim_out) def before_build(state: BuildState) -> None: diff --git a/cibuildwheel/resources/android/fortran_shim.py b/cibuildwheel/resources/android/fortran_shim.py index 04f8792b5..00cb41422 100644 --- a/cibuildwheel/resources/android/fortran_shim.py +++ b/cibuildwheel/resources/android/fortran_shim.py @@ -1,6 +1,3 @@ -# Based on -# https://github.com/kivy/python-for-android/blob/develop/pythonforandroid/recipes/fortran/__init__.py - import os import re import shutil @@ -12,8 +9,11 @@ from cibuildwheel.util.file import CIBW_CACHE_PATH, download +# In the future we might pick a different Flang release depending on the NDK version, +# but so far all Python versions use the same NDK version, so there's no need. RELEASE_URL = "https://github.com/termux/ndk-toolchain-clang-with-flang/releases/download" RELEASE_VERSION = "r27c" +ARCHS = ["aarch64", "x86_64"] # The compiler is built for Linux x86_64, so we use Docker on macOS. DOCKER_IMAGE = "debian:trixie" @@ -25,12 +25,7 @@ def main() -> None: if not cache_dir.exists(): download_flang(cache_dir) - flang_dir = Path(__file__).parents[2] / "flang" - with FileLock(f"{flang_dir}.lock"): - if not flang_dir.exists(): - setup_flang(cache_dir, flang_dir) - - run_flang(flang_dir) + run_flang(cache_dir) def download_flang(cache_dir: Path) -> None: @@ -39,101 +34,66 @@ def download_flang(cache_dir: Path) -> None: shutil.rmtree(tmp_dir) tmp_dir.mkdir(parents=True) - for archive_name, src, dst in [ - ( - f"package-flang-{arch}.tar.bz2", - f"build-{arch}-install", - f"sysroot/usr/lib/{arch}-linux-android", - ) - for arch in ["aarch64", "x86_64"] - ] + [ - ("package-install.tar.bz2", "out/install/linux-x86/clang-dev", ""), - ("package-flang-host.tar.bz2", "build-host-install", ""), + for archive_name in [f"package-flang-{arch}.tar.bz2" for arch in ARCHS] + [ + "package-flang-host.tar.bz2", + "package-install.tar.bz2", ]: archive_path = tmp_dir / archive_name download(f"{RELEASE_URL}/{RELEASE_VERSION}/{archive_name}", archive_path) shutil.unpack_archive(archive_path, tmp_dir) archive_path.unlink() - (tmp_dir / dst).mkdir(parents=True, exist_ok=True) - for src_path in (tmp_dir / src).iterdir(): - src_path.rename(tmp_dir / dst / src_path.name) - - tmp_dir.rename(cache_dir) - + # Merge the extracted trees together, along with the necessary parts of the NDK. Based on + # https://github.com/kivy/python-for-android/blob/develop/pythonforandroid/recipes/fortran/__init__.py) + flang_toolchain = tmp_dir / "toolchain" + (tmp_dir / "out/install/linux-x86/clang-dev").rename(flang_toolchain) -def setup_flang(cache_dir: Path, flang_dir: Path) -> None: - toolchain_dir = Path(os.environ["CC"]).parents[1] - ndk_dir = toolchain_dir.parents[3] - clang_ver_ndk = clang_ver(ndk_dir) - - clang_ver_cache = clang_ver(cache_dir) - if clang_ver_cache != clang_ver_ndk: - msg = f"Flang uses Clang {clang_ver_cache}, but NDK uses Clang {clang_ver_ndk}" + ndk_toolchain = Path(os.environ["CC"]).parents[1] + if (clang_ver_flang := clang_ver(flang_toolchain)) != ( + clang_ver_ndk := clang_ver(ndk_toolchain) + ): + msg = f"Flang uses Clang {clang_ver_flang}, but NDK uses Clang {clang_ver_ndk}" raise ValueError(msg) - # Merge the Flang tree with the parts of the NDK it uses. - tmp_dir = Path(f"{flang_dir}.tmp") - if tmp_dir.exists(): - shutil.rmtree(tmp_dir) - tmp_dir.mkdir(parents=True) + clang_lib_path = f"lib/clang/{clang_ver_ndk}/lib" + shutil.rmtree(flang_toolchain / clang_lib_path) - merge_path(cache_dir, tmp_dir) - merge_path(toolchain_dir, tmp_dir, f"lib/clang{clang_ver_ndk}/lib") - merge_path(toolchain_dir, tmp_dir, "sysroot") + for src, dst in [ + (f"{tmp_dir}/build-{arch}-install", f"sysroot/usr/lib/{arch}-linux-android") + for arch in ARCHS + ] + [ + (f"{tmp_dir}/build-host-install", ""), + (f"{ndk_toolchain}/{clang_lib_path}", clang_lib_path), + (f"{ndk_toolchain}/sysroot", "sysroot"), + ]: + shutil.copytree(src, flang_toolchain / dst, symlinks=True, dirs_exist_ok=True) - tmp_dir.rename(flang_dir) + flang_toolchain.rename(cache_dir) + shutil.rmtree(tmp_dir) -def clang_ver(toolchain_dir: Path) -> str: - versions = [p.name for p in (toolchain_dir / "lib/clang").iterdir()] +def clang_ver(toolchain: Path) -> str: + versions = [p.name for p in (toolchain / "lib/clang").iterdir()] assert len(versions) == 1 return versions[0] -# The merged tree is more than 1 GB, so use symlinks to avoid copying. -def merge_path(src_dir: Path, dst_dir: Path, rel_path: str | None = None) -> None: - if rel_path is None: - for p in src_dir.iterdir(): - merge_path(src_dir, dst_dir, p.name) - return - - if not dst_dir.exists(): - dst_dir.mkdir() - elif dst_dir.is_dir(): - if dst_dir.is_symlink(): - old_src_dir = dst_dir.readlink() - dst_dir.unlink() - dst_dir.mkdir() - for p in old_src_dir.iterdir(): - (dst_dir / p.name).symlink_to(p) - else: - msg = f"{dst_dir} is not a directory" - raise ValueError(msg) - - prefix, sep, suffix = rel_path.partition("/") - if sep: - merge_path(src_dir / prefix, dst_dir / prefix, suffix) - else: - dst_path = dst_dir / rel_path - if dst_path.exists(): - merge_path(src_dir / rel_path, dst_dir / rel_path) - else: - dst_path.symlink_to(src_dir / rel_path) - - -def run_flang(flang_dir: Path) -> None: - match = re.fullmatch(r"(.+)-clang", os.environ["CC"]) +def run_flang(cache_dir: Path) -> None: + match = re.fullmatch(r".+/(.+)-clang", os.environ["CC"]) assert match is not None - args = [f"{flang_dir}/bin/flang-new", f"--target={match[1]}", *sys.argv[1:]] + target = match[1] + + # In a future Flang version the executable name will change to "flang" + # (https://blog.llvm.org/posts/2025-03-11-flang-new/). + args = [f"{cache_dir}/bin/flang-new", f"--target={target}", *sys.argv[1:]] if sys.platform == "linux": pass elif sys.platform == "darwin": args = [ - *["docker", "run"], + *["docker", "run", "--platform", "linux/amd64"], *chain.from_iterable( - # Docker on macOS only allows certain directories to be mounted as volumes, + # Docker on macOS only allows certain directories to be mounted as volumes # by default, but they include all the locations we're likely to need. ["-v", f"{path}:{path}"] for path in ["/private", "/Users", "/tmp"] From 488fd25a7c25f95a33b2e8cc1aa1b12bca749c6f Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 11 Jan 2026 19:02:01 +0000 Subject: [PATCH 10/26] Add cross build files for NumPy --- cibuildwheel/platforms/android.py | 104 +++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index cc33b07f5..34da62660 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -22,6 +22,7 @@ from ..logger import log from ..options import BuildOptions, Options from ..selector import BuildSelector +from ..typing import PathOrStr from ..util import resources from ..util.cmd import call, shell from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file @@ -35,6 +36,15 @@ "x86_64": "x86_64-linux-android", } +CROSS_BUILD_FILES = { + "numpy": [ + "numpy/_core/include/numpy/numpyconfig.h", + "numpy/_core/include/numpy/_numpyconfig.h", + "numpy/_core/lib/libnpymath.a", + "numpy/random/lib/libnpyrandom.a", + ] +} + def parse_identifier(identifier: str) -> tuple[str, str]: match = re.fullmatch(r"cp(\d)(\d+)-android_(.+)", identifier) @@ -130,6 +140,7 @@ def build(options: Options, tmp_path: Path) -> None: state = BuildState( config, build_options, build_path, python_dir, build_env, android_env ) + setup_cross_build_files(state) compatible_wheel = find_compatible_wheel(built_wheels, config.identifier) if compatible_wheel: @@ -143,7 +154,7 @@ def build(options: Options, tmp_path: Path) -> None: built_wheel = build_wheel(state) repaired_wheel = repair_wheel(state, built_wheel) - test_wheel(state, repaired_wheel, build_frontend=build_options.build_frontend.name) + test_wheel(state, repaired_wheel) output_wheel: Path | None = None if compatible_wheel is None: @@ -189,13 +200,7 @@ def setup_env( * android_env, which uses the environment while simulating running on Android. """ log.step("Setting up build environment...") - build_frontend = build_options.build_frontend.name - use_uv = build_frontend == "build[uv]" - uv_path = find_uv() - if use_uv and uv_path is None: - msg = "uv not found" - raise AssertionError(msg) - pip = ["pip"] if not use_uv else [str(uv_path), "pip"] + use_uv, pip = find_pip(build_options) # Create virtual environment python_exe = create_python_build_standalone_environment( @@ -233,7 +238,7 @@ def setup_env( "patchelf", "pkgconf", ] - if build_frontend in {"build", "build[uv]"}: + if build_options.build_frontend.name in {"build", "build[uv]"}: tools.append("build") else: msg = "Android requires the build frontend to be 'build'" @@ -241,7 +246,7 @@ def setup_env( call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) # Construct an altered environment which simulates running on Android. - android_env = setup_android_env(config, python_dir, venv_dir, build_env) + android_env = setup_android_env(config, python_dir, build_env) # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be @@ -358,9 +363,9 @@ def localized_vars( def setup_android_env( - config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str] + config: PythonConfiguration, python_dir: Path, build_env: dict[str, str] ) -> dict[str, str]: - site_packages = next(venv_dir.glob("lib/python*/site-packages")) + site_packages = find_site_packages(build_env) for suffix in ["pth", "py"]: shutil.copy(resources.PATH / f"android/_cross_venv.{suffix}", site_packages) @@ -432,6 +437,59 @@ def setup_fortran(env: dict[str, str]) -> None: env["FC"] = str(shim_out) +# Although the build environment must be installed for the build platform, some packages +# contain platform-specific files which should be replaced with their Android +# equivalents. We do this using a similar technique to Pyodide: +# * https://github.com/pyodide/pyodide-build/blob/v0.30.2/pyodide_build/recipe/builder.py#L638 +# * https://github.com/pyodide/pyodide-recipes/blob/20250606/packages/numpy/meta.yaml#L28 +def setup_cross_build_files(state: BuildState) -> None: + _, pip = find_pip(state.options) + cbf_dir = state.build_path / "cross_build_files" + cbf_dir.mkdir() + + for requirement in call(*pip, "freeze", env=state.build_env, capture_stdout=True).splitlines(): + name, _, _ = requirement.strip().partition("==") + cross_build_files = CROSS_BUILD_FILES.get(name.lower(), []) + if cross_build_files: + pip_install_android(state, cbf_dir, "--no-deps", requirement) + for cbf in cross_build_files: + if (cbf_dir / cbf).exists(): + shutil.copy( + cbf_dir / cbf, + find_site_packages(state.build_env) / cbf, + ) + else: + log.warning(f"{cbf_dir / cbf} does not exist") + + +def pip_install_android(state: BuildState, target: Path, *args: PathOrStr) -> None: + use_uv, pip = find_pip(state.options) + call( + *pip, + "install", + "--only-binary=:all:", + *(["--python-platform", android_triplet(state.config.identifier)] if use_uv else []), + "--target", + target, + *args, + env=state.android_env, + ) + + +def find_site_packages(env: dict[str, str]) -> Path: + return next(Path(env["VIRTUAL_ENV"]).glob("lib/python*/site-packages")) + + +def find_pip(build_options: BuildOptions) -> tuple[bool, list[str]]: + use_uv = build_options.build_frontend.name == "build[uv]" + uv_path = find_uv() + if use_uv and uv_path is None: + msg = "uv not found" + raise AssertionError(msg) + pip = ["pip"] if not use_uv else [str(uv_path), "pip"] + return use_uv, pip + + def before_build(state: BuildState) -> None: if state.options.before_build: log.step("Running before_build...") @@ -516,19 +574,12 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: return repaired_wheel -def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: +def test_wheel(state: BuildState, wheel: Path) -> None: test_command = state.options.test_command if not (test_command and state.options.test_selector(state.config.identifier)): return log.step("Testing wheel...") - use_uv = build_frontend == "build[uv]" - uv_path = find_uv() - if use_uv and uv_path is None: - msg = "uv not found" - raise AssertionError(msg) - pip = ["pip"] if not use_uv else [str(uv_path), "pip"] - native_arch = arch_synonym(platform.machine(), platforms.native_platform(), "android") if state.config.arch != native_arch: log.warning( @@ -544,23 +595,14 @@ def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None: env=state.android_env, ) - platform_args = ( - ["--python-platform", android_triplet(state.config.identifier)] if use_uv else [] - ) - # Install the wheel and test-requires. site_packages_dir = state.build_path / "site-packages" site_packages_dir.mkdir() - call( - *pip, - "install", - "--only-binary=:all:", - *platform_args, - "--target", + pip_install_android( + state, site_packages_dir, f"{wheel}{state.options.test_extras}", *state.options.test_requires, - env=state.android_env, ) # Copy test-sources. From 43310361c86b5bb9e4f0fa061838d0abb1a3b007 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Mon, 12 Jan 2026 12:19:17 +0000 Subject: [PATCH 11/26] Add ldpaths entry for libomp --- cibuildwheel/platforms/android.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 34da62660..deba02b74 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -538,13 +538,17 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: if state.options.repair_command: toolchain = Path(state.android_env["CC"]).parent.parent + triplet = android_triplet(state.config.identifier) ldpaths = ":".join( - # In the future, we may use this to implement PEP 725 by installing - # libraries in {state.python_dir}/prefix/lib or elsewhere, and adding that - # location to ldpaths. - [ - # For libc++_shared. - f"{toolchain}/sysroot/usr/lib/{state.android_env['CIBW_HOST_TRIPLET']}", + # Pass ldpaths to help auditwheel find compiler libraries. If we implement + # PEP 725 in the future to provide non-Python libraries, we'll need to add + # their location here. + str(next(Path(toolchain).glob(path))) + for path in [ + # libc++_shared + f"sysroot/usr/lib/{triplet}", + # libomp + f"lib/clang/*/lib/linux/{triplet.split('-')[0]}", ] ) shell( From cfda2c35c46e932abfac7f9469c595521634e191 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 29 Jan 2026 18:43:17 +0000 Subject: [PATCH 12/26] Add `--rm` to docker command line --- cibuildwheel/resources/android/fortran_shim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cibuildwheel/resources/android/fortran_shim.py b/cibuildwheel/resources/android/fortran_shim.py index 00cb41422..ea2d3a472 100644 --- a/cibuildwheel/resources/android/fortran_shim.py +++ b/cibuildwheel/resources/android/fortran_shim.py @@ -91,7 +91,7 @@ def run_flang(cache_dir: Path) -> None: pass elif sys.platform == "darwin": args = [ - *["docker", "run", "--platform", "linux/amd64"], + *["docker", "run", "--rm", "--platform", "linux/amd64"], *chain.from_iterable( # Docker on macOS only allows certain directories to be mounted as volumes # by default, but they include all the locations we're likely to need. From d175c12437ce432c469517714c7bec0328b340f1 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 29 Jan 2026 19:07:30 +0000 Subject: [PATCH 13/26] Make Rust and Fortran shims consistent --- cibuildwheel/platforms/android.py | 9 ++------- cibuildwheel/resources/android/fortran_shim.py | 3 +++ .../resources/{_rust_shim.py => android/rust_shim.py} | 0 3 files changed, 5 insertions(+), 7 deletions(-) rename cibuildwheel/resources/{_rust_shim.py => android/rust_shim.py} (100%) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index b5fec5a18..d1e1c71ad 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -5,7 +5,6 @@ import shutil import subprocess import sys -from collections.abc import MutableMapping from dataclasses import dataclass from pathlib import Path from pprint import pprint @@ -428,11 +427,7 @@ def setup_android_env( return android_env -def setup_rust( - config: PythonConfiguration, - python_dir: Path, - env: MutableMapping[str, str], -) -> None: +def setup_rust(config: PythonConfiguration, python_dir: Path, env: dict[str, str]) -> None: cargo_target = android_triplet(config.identifier) # CARGO_BUILD_TARGET is the variable used by Cargo and setuptools_rust @@ -452,7 +447,7 @@ def setup_rust( venv_bin = Path(env["VIRTUAL_ENV"]) / "bin" for tool in ["cargo", "rustup"]: shim_path = venv_bin / tool - shutil.copy(resources.PATH / "_rust_shim.py", shim_path) + shutil.copy(resources.PATH / "android/rust_shim.py", shim_path) shim_path.chmod(0o755) diff --git a/cibuildwheel/resources/android/fortran_shim.py b/cibuildwheel/resources/android/fortran_shim.py index ea2d3a472..62dba8bf0 100644 --- a/cibuildwheel/resources/android/fortran_shim.py +++ b/cibuildwheel/resources/android/fortran_shim.py @@ -1,3 +1,6 @@ +# This file intentionally has no hashbang line in the source: cibuildwheel will add it +# above this comment when the file is deployed. + import os import re import shutil diff --git a/cibuildwheel/resources/_rust_shim.py b/cibuildwheel/resources/android/rust_shim.py similarity index 100% rename from cibuildwheel/resources/_rust_shim.py rename to cibuildwheel/resources/android/rust_shim.py From f188500a73e86bc8da983745b8640e146ab7a23c Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 29 Jan 2026 19:16:25 +0000 Subject: [PATCH 14/26] Update documentation --- docs/options.md | 2 +- docs/platforms.md | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/options.md b/docs/options.md index 098bb6c36..ff272345c 100644 --- a/docs/options.md +++ b/docs/options.md @@ -904,7 +904,7 @@ The following placeholders must be used inside the command and will be replaced - `{wheel}` for the absolute path to the built wheel - `{dest_dir}` for the absolute path of the directory where to create the repaired wheel - `{delocate_archs}` (macOS only) comma-separated list of architectures in the wheel. -- `{ldpaths}` (Android only) colon-separated list of directories to search for external libraries. +- `{ldpaths}` (Android only) colon-separated list of directories to search for external libraries. cibuildwheel will set this to include any necessary locations in the NDK. To add your own locations, use the `LD_LIBRARY_PATH` environment variable. You can use the `{package}` or `{project}` placeholders in your `repair-wheel-command` to refer to the package being built or the project root, respectively. diff --git a/docs/platforms.md b/docs/platforms.md index 6e46ec958..9574dde2f 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -214,11 +214,6 @@ minimum supported [API level](https://developer.android.com/tools/releases/platf for generated wheels. This will default to the minimum API level of the selected Python version. -If the [`repair-wheel-command`](options.md#repair-wheel-command) adds any libraries to -the wheel, then `ANDROID_API_LEVEL` must be at least 24. This is already the default -when building for Python 3.14 and later, but you may need to set it when building for -Python 3.13. - ### Build frontend support Android builds only support the `build` frontend. In principle, support for the From 3e806fa9f6e2f44dccbbac4638beb4e2cda5fb24 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 22 Mar 2026 11:45:10 +0000 Subject: [PATCH 15/26] Default to API level 24 on all Python versions --- cibuildwheel/platforms/android.py | 7 +++---- docs/platforms.md | 5 +++-- test/test_android.py | 22 ++++++++++++---------- test/utils.py | 12 +++++++----- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index d1e1c71ad..de0c4308b 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -336,11 +336,10 @@ def localized_vars( if isinstance(final, str): final = final.replace(orig_prefix, str(prefix)) - # By default we build against the same API level as Python itself, but this can - # be overridden with an environment variable. + # By default we build against API level 24, for the reasons explained in platforms.md, + # but this can be overridden with an environment variable. if key == "ANDROID_API_LEVEL": - if api_level := build_env.get(key): - final = int(api_level) + final = int(build_env.get(key, "24")) # Build systems vary in whether FLAGS variables are read from sysconfig, and if so, # whether they're replaced by environment variables or combined with them. Even diff --git a/docs/platforms.md b/docs/platforms.md index 9574dde2f..51ae2d0f5 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -211,8 +211,9 @@ It also requires the following commands to be on the `PATH`: Android builds will honor the `ANDROID_API_LEVEL` environment variable to set the minimum supported [API level](https://developer.android.com/tools/releases/platforms) -for generated wheels. This will default to the minimum API level of the selected Python -version. +for generated wheels. This defaults to 24, which is supported by [99% of active +devices](https://dl.google.com/android/studio/metadata/distributions.json), and is the first +version to support RUNPATH, which auditwheel needs in order to graft external libraries. ### Build frontend support diff --git a/test/test_android.py b/test/test_android.py index eb16e2b13..b584be0d7 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -125,7 +125,7 @@ def test_frontend_good(tmp_path, build_frontend_env): tmp_path, add_env={**cp313_env, **build_frontend_env, "CIBW_TEST_COMMAND": "python -m site"}, ) - assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{native_arch.android_abi}.whl"] + assert wheels == [f"spam-0.1.0-cp313-cp313-android_24_{native_arch.android_abi}.whl"] @pytest.mark.parametrize("frontend", ["pip"]) @@ -162,7 +162,7 @@ def test_archs(tmp_path, capfd): ), }, ) - assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{arch.android_abi}.whl" for arch in archs] + assert wheels == [f"spam-0.1.0-cp313-cp313-android_24_{arch.android_abi}.whl" for arch in archs] stdout, stderr = capfd.readouterr() lines = (line for line in stdout.splitlines() if line.startswith("Hello from")) @@ -470,16 +470,18 @@ def test_libcxx(tmp_path, capfd): "PATH": non_venv_path, } - # Including external libraries requires API level 24. + # Including external libraries requires API level 24. This is enforced by auditwheel. with pytest.raises(CalledProcessError): - cibuildwheel_run(project_dir, add_env=cp313_test_env, output_dir=output_dir) - assert "libc++_shared.so requires ANDROID_API_LEVEL to be at least 24" in capfd.readouterr().err - - wheels = cibuildwheel_run( - project_dir, - add_env={**cp313_test_env, "ANDROID_API_LEVEL": "24"}, - output_dir=output_dir, + cibuildwheel_run( + project_dir, + add_env={**cp313_test_env, "ANDROID_API_LEVEL": "21"}, + output_dir=output_dir, + ) + assert ( + "Grafting libraries with RUNPATH requires API level 24 or higher" in capfd.readouterr().err ) + + wheels = cibuildwheel_run(project_dir, add_env=cp313_test_env, output_dir=output_dir) assert len(wheels) == 1 names = ZipFile(output_dir / wheels[0]).namelist() libcxx_names = [ diff --git a/test/utils.py b/test/utils.py index 6acdc8044..001c02ea0 100644 --- a/test/utils.py +++ b/test/utils.py @@ -169,6 +169,7 @@ def expected_wheels( musllinux_versions: list[str] | None = None, macosx_deployment_target: str | None = None, iphoneos_deployment_target: str | None = None, + android_api_level: int | None = None, machine_arch: str | None = None, platform: str | None = None, python_abi_tags: list[str] | None = None, @@ -193,6 +194,9 @@ def expected_wheels( if iphoneos_deployment_target is None: iphoneos_deployment_target = os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") + if android_api_level is None: + android_api_level = int(os.environ.get("ANDROID_API_LEVEL", "24")) + architectures = [machine_arch] if not single_arch: if platform == "linux" and full_auto: @@ -218,6 +222,7 @@ def expected_wheels( musllinux_versions=musllinux_versions, macosx_deployment_target=macosx_deployment_target, iphoneos_deployment_target=iphoneos_deployment_target, + android_api_level=android_api_level, platform=platform, python_abi_tags=python_abi_tags, include_universal2=include_universal2, @@ -234,6 +239,7 @@ def _expected_wheels( musllinux_versions: list[str] | None, macosx_deployment_target: str, iphoneos_deployment_target: str, + android_api_level: int, platform: str, python_abi_tags: list[str] | None, include_universal2: bool, @@ -388,11 +394,7 @@ def _expected_wheels( platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2") elif platform == "android": - api_level = { - "cp313-cp313": 21, - "cp314-cp314": 24, - }[python_abi_tag] - platform_tags = [f"android_{api_level}_{machine_arch}"] + platform_tags = [f"android_{android_api_level}_{machine_arch}"] elif platform == "ios": if machine_arch == "x86_64": From b9391ae580aefbe0762d76b6a68488f848266e60 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 22 Mar 2026 12:03:11 +0000 Subject: [PATCH 16/26] Clarify comments --- bin/generate_schema.py | 2 +- cibuildwheel/resources/defaults.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/generate_schema.py b/bin/generate_schema.py index e68e19c96..98b594957 100755 --- a/bin/generate_schema.py +++ b/bin/generate_schema.py @@ -15,7 +15,7 @@ parser.add_argument("--schemastore", action="store_true", help="Generate schema_store version") args = parser.parse_args() -# The defaults in the schema are only used for documentation and IDE support. They +# The defaults in the schema are used by external tools for validation and IDE support. They # should match the values in defaults.toml, which are used by cibuildwheel itself. starter = """ $schema: http://json-schema.org/draft-07/schema# diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml index 60a41e183..041978153 100644 --- a/cibuildwheel/resources/defaults.toml +++ b/cibuildwheel/resources/defaults.toml @@ -1,5 +1,5 @@ # These are the defaults used by cibuildwheel itself. They should match the values in -# generate_schema.py, which are used for documentation and IDE support. +# generate_schema.py, which are used by external tools for validation and IDE support. [tool.cibuildwheel] build = "*" From 0ce01a1831dfdc558adbaf4fa2e3c28085f6fdef Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 22 Mar 2026 13:28:06 +0000 Subject: [PATCH 17/26] Cleanups --- cibuildwheel/platforms/android.py | 16 ++++++---------- test/test_android.py | 5 ++++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index b3ca81be4..3af7e27e8 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -229,6 +229,9 @@ def setup_env( raise errors.FatalError(msg) call(command, "--version", env=build_env) + # Construct an altered environment which simulates running on Android. + android_env = setup_android_env(config, python_dir, build_env) + # Install build tools # TODO: use an official auditwheel version once # https://github.com/pypa/auditwheel/pull/643 has been released, and add it to the @@ -240,14 +243,8 @@ def setup_env( ] if build_options.build_frontend.name in {"build", "build[uv]"}: tools.append("build") - else: - msg = "Android requires the build frontend to be 'build'" - raise errors.FatalError(msg) call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) - # Construct an altered environment which simulates running on Android. - android_env = setup_android_env(config, python_dir, build_env) - # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be # affected by the target platform. However, the requirements must be installed @@ -336,8 +333,7 @@ def localized_vars( if isinstance(final, str): final = final.replace(orig_prefix, str(prefix)) - # By default we build against API level 24, for the reasons explained in platforms.md, - # but this can be overridden with an environment variable. + # See platforms.md for the reason why we use this default API level. if key == "ANDROID_API_LEVEL": final = int(build_env.get(key, "24")) @@ -567,8 +563,8 @@ def build_wheel(state: BuildState) -> Path: env=state.android_env, ) case x: - msg = f"Invalid build backend {x!r}" - raise AssertionError(msg) + msg = f"Android requires the build frontend to be 'build' or 'uv', not {x!r}" + raise errors.FatalError(msg) built_wheels = list(built_wheel_dir.glob("*.whl")) if len(built_wheels) != 1: diff --git a/test/test_android.py b/test/test_android.py index b584be0d7..84c4e68be 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -136,7 +136,10 @@ def test_frontend_bad(frontend, tmp_path, capfd): tmp_path, add_env={**cp313_env, "CIBW_BUILD_FRONTEND": frontend}, ) - assert "Android requires the build frontend to be 'build'" in capfd.readouterr().err + assert ( + f"Android requires the build frontend to be 'build' or 'uv', not '{frontend}'" + in capfd.readouterr().err + ) @needs_emulator From ff7942c89bde2bbe5d0c501d6c2fb70f7d3d335a Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 22 Mar 2026 20:13:01 +0000 Subject: [PATCH 18/26] Fix tests: * Set up Android env after installing pkgconf * Add tests for successfully using an older API level * Previous commit's auditwheel failure is fixed in the auditwheel PR --- cibuildwheel/platforms/android.py | 6 +++--- test/test_android.py | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 3af7e27e8..7124f016c 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -229,9 +229,6 @@ def setup_env( raise errors.FatalError(msg) call(command, "--version", env=build_env) - # Construct an altered environment which simulates running on Android. - android_env = setup_android_env(config, python_dir, build_env) - # Install build tools # TODO: use an official auditwheel version once # https://github.com/pypa/auditwheel/pull/643 has been released, and add it to the @@ -245,6 +242,9 @@ def setup_env( tools.append("build") call(*pip, "install", *tools, *constraint_flags(dependency_constraint), env=build_env) + # Construct an altered environment which simulates running on Android. + android_env = setup_android_env(config, python_dir, build_env) + # Build-time requirements must be queried within android_env, because # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be # affected by the target platform. However, the requirements must be installed diff --git a/test/test_android.py b/test/test_android.py index 84c4e68be..fc57fb90d 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -474,18 +474,15 @@ def test_libcxx(tmp_path, capfd): } # Including external libraries requires API level 24. This is enforced by auditwheel. + cp313_android_21_env = {**cp313_test_env, "ANDROID_API_LEVEL": "21"} with pytest.raises(CalledProcessError): - cibuildwheel_run( - project_dir, - add_env={**cp313_test_env, "ANDROID_API_LEVEL": "21"}, - output_dir=output_dir, - ) + cibuildwheel_run(project_dir, add_env=cp313_android_21_env, output_dir=output_dir) assert ( "Grafting libraries with RUNPATH requires API level 24 or higher" in capfd.readouterr().err ) wheels = cibuildwheel_run(project_dir, add_env=cp313_test_env, output_dir=output_dir) - assert len(wheels) == 1 + assert wheels == [f"spam-0.1.0-cp313-cp313-android_24_{native_arch.android_abi}.whl"] names = ZipFile(output_dir / wheels[0]).namelist() libcxx_names = [ name for name in names if re.fullmatch(r"spam\.libs/libc\+\+_shared-[0-9a-f]{8}\.so", name) @@ -493,12 +490,12 @@ def test_libcxx(tmp_path, capfd): assert len(libcxx_names) == 1 assert "ham: 1, spam: 0" in capfd.readouterr().out - # A C package should not include libc++. + # A C package should not include libc++, and can therefore use an older API level. rmtree(project_dir) rmtree(output_dir) new_c_project().generate(project_dir) - wheels = cibuildwheel_run(project_dir, add_env=cp313_env, output_dir=output_dir) - assert len(wheels) == 1 + wheels = cibuildwheel_run(project_dir, add_env=cp313_android_21_env, output_dir=output_dir) + assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{native_arch.android_abi}.whl"] for name in ZipFile(output_dir / wheels[0]).namelist(): assert ".libs" not in name From 1bb5e306f7df1363154c2a366669edd554fa2b14 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 10 Apr 2026 23:00:46 +0100 Subject: [PATCH 19/26] Update how-it-works diagram --- docs/data/how-it-works.png | Bin 93464 -> 100324 bytes docs/diagram.html | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data/how-it-works.png b/docs/data/how-it-works.png index bbbc2a301be45486cd350eb2496956ef500e55b0..5c6a1fcd5d4a03892eac76a3d2ec2538efd4ce6a 100644 GIT binary patch literal 100324 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

XY}1isLNX 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 >!MqSDwn=Ydc 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 Date: Sat, 11 Apr 2026 02:38:52 +0100 Subject: [PATCH 20/26] Add tests for repair errors --- cibuildwheel/platforms/android.py | 38 +++++++++++++++---------------- test/test_android.py | 32 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 2c713f40d..f27a5fc56 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -91,6 +91,14 @@ def shell_prepared(command: str, *, build_options: BuildOptions, env: dict[str, ) +def glob1(base: Path, pattern: str) -> Path: + results = list(base.glob(pattern)) + if len(results) != 1: + msg = f"{base} contains {len(results)} paths matching '{pattern}'; expected 1" + raise errors.FatalError(msg) + return results[0] + + def before_all(options: Options, python_configurations: list[PythonConfiguration]) -> None: before_all_options = options.build_options(python_configurations[0].identifier) if before_all_options.before_all: @@ -366,7 +374,7 @@ def setup_android_env( sysconfigdata_path = Path( shutil.copy( - next(python_dir.glob("prefix/lib/python*/_sysconfigdata_*.py")), + glob1(python_dir, "prefix/lib/python*/_sysconfigdata_*.py"), site_packages, ) ) @@ -499,7 +507,7 @@ def pip_install_android(state: BuildState, target: Path, *args: PathOrStr) -> No def find_site_packages(env: dict[str, str]) -> Path: - return next(Path(env["VIRTUAL_ENV"]).glob("lib/python*/site-packages")) + return glob1(Path(env["VIRTUAL_ENV"]), "lib/python*/site-packages") def find_pip(build_options: BuildOptions) -> tuple[bool, list[str]]: @@ -566,12 +574,7 @@ def build_wheel(state: BuildState) -> Path: msg = f"Android requires the build frontend to be 'build' or 'uv', not {x!r}" raise errors.FatalError(msg) - built_wheels = list(built_wheel_dir.glob("*.whl")) - if len(built_wheels) != 1: - msg = f"{built_wheel_dir} contains {len(built_wheels)} wheels; expected 1" - raise errors.FatalError(msg) - built_wheel = built_wheels[0] - + built_wheel = glob1(built_wheel_dir, "*.whl") if built_wheel.name.endswith("none-any.whl"): raise errors.NonPlatformWheelError() return built_wheel @@ -583,18 +586,14 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: repaired_wheel_dir.mkdir() if state.options.repair_command: + # Tell auditwheel the locations of compiler libraries. toolchain = Path(state.android_env["CC"]).parent.parent triplet = android_triplet(state.config.identifier) ldpaths = ":".join( - # Pass ldpaths to help auditwheel find compiler libraries. If we implement - # PEP 725 in the future to provide non-Python libraries, we'll need to add - # their location here. - str(next(Path(toolchain).glob(path))) - for path in [ - # libc++_shared - f"sysroot/usr/lib/{triplet}", - # libomp - f"lib/clang/*/lib/linux/{triplet.split('-')[0]}", + str(glob1(toolchain, pattern)) + for pattern in [ + f"lib/clang/*/lib/linux/{triplet.split('-')[0]}", # libomp + f"sysroot/usr/lib/{triplet}", # libc++_shared ] ) shell( @@ -615,8 +614,9 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: if len(repaired_wheels) == 0: raise errors.RepairStepProducedNoWheelError() if len(repaired_wheels) != 1: - msg = f"{repaired_wheel_dir} contains {len(repaired_wheels)} wheels; expected 1" - raise errors.FatalError(msg) + raise errors.RepairStepProducedMultipleWheelsError( + [rw.name for rw in repaired_wheels], + ) repaired_wheel = repaired_wheels[0] if repaired_wheel.name.endswith("none-any.whl"): diff --git a/test/test_android.py b/test/test_android.py index 75950296a..bce9dcc29 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -516,6 +516,38 @@ def test_libcxx(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: assert ".libs" not in name +@pytest.mark.parametrize( + ("script", "error"), + [ + ("", "did not produce a wheel"), + ("touch $dest_dir/one.whl $dest_dir/two.whl", "produced multiple wheels"), + ("touch $dest_dir/one-0.0.1-py3-none-any.whl", "pure Python wheel was generated"), + ], +) +def test_repair_error( + script: str, error: str, tmp_path: Path, capfd: pytest.CaptureFixture[str] +) -> None: + new_c_project().generate(tmp_path) + repair_path = tmp_path / "repair.sh" + repair_path.write_text( + dedent( + f"""\ + #!/bin/sh + dest_dir=$1 + {script} + """ + ) + ) + repair_path.chmod(0o755) + + with pytest.raises(CalledProcessError): + cibuildwheel_run( + tmp_path, + add_env={**cp313_env, "CIBW_REPAIR_WHEEL_COMMAND": f"{repair_path} {{dest_dir}}"}, + ) + assert error in capfd.readouterr().err + + @needs_emulator def test_setuptools_rust(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """ From 5d4073b883d3a3e1d31d55a287ab25269e21a7b9 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 16 Apr 2026 21:42:28 +0100 Subject: [PATCH 21/26] Add more repair tests --- cibuildwheel/platforms/android.py | 16 +++++----- test/test_android.py | 50 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 1734cdd56..587903df4 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -93,14 +93,6 @@ def shell_prepared(command: str, *, build_options: BuildOptions, env: dict[str, ) -def glob1(base: Path, pattern: str) -> Path: - results = list(base.glob(pattern)) - if len(results) != 1: - msg = f"{base} contains {len(results)} paths matching '{pattern}'; expected 1" - raise errors.FatalError(msg) - return results[0] - - def before_all(options: Options, python_configurations: list[PythonConfiguration]) -> None: before_all_options = options.build_options(python_configurations[0].identifier) if before_all_options.before_all: @@ -516,6 +508,14 @@ def find_site_packages(env: dict[str, str]) -> Path: return glob1(Path(env["VIRTUAL_ENV"]), "lib/python*/site-packages") +def glob1(base: Path, pattern: str) -> Path: + results = list(base.glob(pattern)) + if len(results) != 1: + msg = f"{base} contains {len(results)} paths matching '{pattern}'; expected 1" + raise errors.FatalError(msg) + return results[0] + + def find_pip(build_options: BuildOptions) -> tuple[bool, list[str]]: use_uv = build_options.build_frontend.name in {"build[uv]", "uv"} uv_path = find_uv() diff --git a/test/test_android.py b/test/test_android.py index bce9dcc29..d5752393a 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -516,6 +516,56 @@ def test_libcxx(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: assert ".libs" not in name +@needs_emulator +def test_repair_none(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: + new_c_project(setup_py_extension_args_add="language='c++'").generate(tmp_path) + with pytest.raises(CalledProcessError): + cibuildwheel_run( + tmp_path, + add_env={ + **cp313_env, + "CIBW_REPAIR_WHEEL_COMMAND": "", + "CIBW_TEST_COMMAND": "python -c 'import spam'", + }, + ) + assert 'dlopen failed: library "libc++_shared.so" not found' in capfd.readouterr().err + + +def test_repair_ldpaths(tmp_path: Path) -> None: + new_c_project().generate(tmp_path) + repair_path = tmp_path / "repair.py" + repair_path.write_text( + dedent( + """\ + #!/usr/bin/env python + import shutil + import sys + from pathlib import Path + + assert len(sys.argv) == 4, sys.argv + ldpaths = list(map(Path, sys.argv[1].split(":"))) + dest_dir = sys.argv[2] + wheel = sys.argv[3] + + for name in ["libc++_shared.so", "libomp.so"]: + assert any((lp / name).exists() for lp in ldpaths), (name, ldpaths) + + shutil.copy(wheel, dest_dir) + """ + ) + ) + repair_path.chmod(0o755) + + wheels = cibuildwheel_run( + tmp_path, + add_env={ + **cp313_env, + "CIBW_REPAIR_WHEEL_COMMAND": f"{repair_path} {{ldpaths}} {{dest_dir}} {{wheel}}", + }, + ) + assert wheels == [f"spam-0.1.0-cp313-cp313-android_24_{native_arch.android_abi}.whl"] + + @pytest.mark.parametrize( ("script", "error"), [ From d65ce9329744d5a321187e4c388a805d84d8a2a5 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 17 Apr 2026 01:46:58 +0100 Subject: [PATCH 22/26] Add test for Meson and Fortran --- .../resources/android/fortran_shim.py | 22 ++++------ test/test_android.py | 44 ++++++++++++++++++- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/cibuildwheel/resources/android/fortran_shim.py b/cibuildwheel/resources/android/fortran_shim.py index 62dba8bf0..b3e4978a3 100644 --- a/cibuildwheel/resources/android/fortran_shim.py +++ b/cibuildwheel/resources/android/fortran_shim.py @@ -5,7 +5,6 @@ import re import shutil import sys -from itertools import chain from pathlib import Path from filelock import FileLock @@ -88,21 +87,18 @@ def run_flang(cache_dir: Path) -> None: # In a future Flang version the executable name will change to "flang" # (https://blog.llvm.org/posts/2025-03-11-flang-new/). - args = [f"{cache_dir}/bin/flang-new", f"--target={target}", *sys.argv[1:]] + flang_args = [f"{cache_dir}/bin/flang-new", f"--target={target}", *sys.argv[1:]] if sys.platform == "linux": - pass + args = flang_args elif sys.platform == "darwin": - args = [ - *["docker", "run", "--rm", "--platform", "linux/amd64"], - *chain.from_iterable( - # Docker on macOS only allows certain directories to be mounted as volumes - # by default, but they include all the locations we're likely to need. - ["-v", f"{path}:{path}"] - for path in ["/private", "/Users", "/tmp"] - ), - *["-w", str(Path.cwd()), "--entrypoint", args[0], DOCKER_IMAGE, *args[1:]], - ] + args = ["docker", "run", "--rm", "--platform", "linux/amd64"] + for path in ["/private", "/Users", "/tmp"]: + # Docker on macOS only allows certain directories to be mounted as volumes + # by default, but they include all the locations we're likely to need. + args += ["-v", f"{path}:{path}"] + args += ["--workdir", str(Path.cwd())] + args += ["--entrypoint", flang_args[0], DOCKER_IMAGE, *flang_args[1:]] else: msg = f"unknown platform: {sys.platform}" raise ValueError(msg) diff --git a/test/test_android.py b/test/test_android.py index d5752393a..0d7945c93 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -13,7 +13,7 @@ import pytest -from .test_projects import new_c_project +from .test_projects import new_c_project, new_meson_project from .utils import cibuildwheel_run, expected_wheels pytestmark = pytest.mark.android @@ -598,6 +598,48 @@ def test_repair_error( assert error in capfd.readouterr().err +# This also tests integration with pkgconf, because Meson uses it to find Python. +@needs_emulator +def test_meson(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: + # Alter spam.filter to return the value of a Fortran function. + new_meson_project( + spam_c_top_level_add="int fortran_func_();", + spam_c_function_add="sts = fortran_func_();", + ).generate(tmp_path) + + # TODO: remove once meson-python has a release with Android support. + pyproject_path = tmp_path / "pyproject.toml" + pyproject_path.write_text( + pyproject_path.read_text().replace( + "meson-python", "meson-python @ git+https://github.com/mesonbuild/meson-python@main" + ) + ) + + # Add Fortran code. + meson_build_path = tmp_path / "meson.build" + meson_build_path.write_text( + meson_build_path.read_text() + .replace("'c'", "'c', 'fortran'") + .replace("'spam.c'", "'spam.c', 'fortran.f90', link_language: 'fortran'") + ) + (tmp_path / "fortran.f90").write_text( + dedent( + """\ + integer*4 function fortran_func() + fortran_func = 42 + end + """ + ) + ) + + script = 'import spam; print(f"fortran_func: {spam.filter("")}")' + cibuildwheel_run( + tmp_path, + add_env={**cp313_env, "CIBW_TEST_COMMAND": f"python -c '{script}'"}, + ) + assert "fortran_func: 42" in capfd.readouterr().out + + @needs_emulator def test_setuptools_rust(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """ From db2a50e76b674e22cff9ac1ddb61894e1faa7280 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 17 Apr 2026 03:18:46 +0100 Subject: [PATCH 23/26] Add test for cross build files --- cibuildwheel/platforms/android.py | 6 ++- test/test_android.py | 65 +++++++++++++++++++++++++++++-- test/test_projects/setuptools.py | 3 +- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 587903df4..5219c9616 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -221,6 +221,9 @@ def setup_env( ) create_cmake_toolchain(config, build_path, python_dir, build_env) + # See platforms.md for the reason why we use this default API level. + build_env.setdefault("ANDROID_API_LEVEL", "24") + # Apply custom environment variables, and check environment is still valid build_env = build_options.environment.as_dictionary(build_env) build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" @@ -339,9 +342,8 @@ def localized_vars( if isinstance(final, str): final = final.replace(orig_prefix, str(prefix)) - # See platforms.md for the reason why we use this default API level. if key == "ANDROID_API_LEVEL": - final = int(build_env.get(key, "24")) + final = int(build_env[key]) # Build systems vary in whether FLAGS variables are read from sysconfig, and if so, # whether they're replaced by environment variables or combined with them. Even diff --git a/test/test_android.py b/test/test_android.py index 0d7945c93..5a6768c24 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -438,13 +438,24 @@ def test_verbosity(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: @needs_emulator def test_api_level(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: - project = new_c_project() + project = new_c_project( + # Check that the the compiler options are set correctly. + spam_c_top_level_add=dedent( + """\ + #if __ANDROID_API__ != 33 + #error Wrong API level + #endif + """ + ) + ) project.files["pyproject.toml"] = dedent( """\ [build-system] requires = ["setuptools"] [tool.cibuildwheel] + # Test setting API level in pyproject.toml (test_libcxx covers setting + # it in the outer environment.) android.environment.ANDROID_API_LEVEL = "33" android.environment.PIP_EXTRA_INDEX_URL = "https://chaquo.com/pypi-13.1" """ @@ -490,7 +501,12 @@ def test_libcxx(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: } # Including external libraries requires API level 24. This is enforced by auditwheel. - cp313_android_21_env = {**cp313_test_env, "ANDROID_API_LEVEL": "21"} + cp313_android_21_env = { + **cp313_test_env, + # Test setting API level in the outer environment (test_api_level covers setting + # it in pyproject.toml.) + "ANDROID_API_LEVEL": "21", + } with pytest.raises(CalledProcessError): cibuildwheel_run(project_dir, add_env=cp313_android_21_env, output_dir=output_dir) assert ( @@ -632,12 +648,53 @@ def test_meson(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: ) ) - script = 'import spam; print(f"fortran_func: {spam.filter("")}")' + script = 'import spam; print(f"result: {spam.filter("")}")' cibuildwheel_run( tmp_path, add_env={**cp313_env, "CIBW_TEST_COMMAND": f"python -c '{script}'"}, ) - assert "fortran_func: 42" in capfd.readouterr().out + assert "result: 42" in capfd.readouterr().out + + +@needs_emulator +def test_cross_build_files(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: + # Verify that we've replaced the correct files by compiling against a non-trivial + # function from libnpymath.a. + new_c_project( + setup_py_add=dedent( + """\ + import numpy as np + np_include = np.get_include() + np_lib = f"{np_include}/../lib" + libraries.append("npymath") + """ + ), + setup_py_extension_args_add="include_dirs=[np_include], library_dirs=[np_lib]", + spam_c_top_level_add="#include ", + spam_c_function_add="sts = npy_float_to_half(42);", + ).generate(tmp_path) + + (tmp_path / "pyproject.toml").write_text( + dedent( + """\ + [build-system] + requires = ["setuptools", "numpy==2.3.2"] + """ + ) + ) + + script = 'import spam; print(f"result: {spam.filter(""):#x}")' + cibuildwheel_run( + tmp_path, + add_env={ + **cp313_env, + # TODO: remove this once there are official Android NumPy wheels on PyPI. + "PIP_EXTRA_INDEX_URL": "https://chaquo.com/pypi-test", + "CIBW_ARCHS": "all", # Include both native and non-native archs. + "CIBW_TEST_COMMAND": f"python -c '{script}'", + }, + ) + assert "result: 0x5140" in capfd.readouterr().out @needs_emulator diff --git a/test/test_projects/setuptools.py b/test/test_projects/setuptools.py index 30b33caf8..13375e981 100644 --- a/test/test_projects/setuptools.py +++ b/test/test_projects/setuptools.py @@ -9,9 +9,10 @@ from setuptools import setup, Extension +libraries = [] + {{ setup_py_add }} -libraries = [] # Emscripten fails if you pass -lc... # See: https://github.com/emscripten-core/emscripten/issues/16680 if sys.platform.startswith('linux') and "emscripten" not in os.environ.get("_PYTHON_HOST_PLATFORM", ""): From b9bbcc863e443d3e57e18816c1139b44b3775c2c Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Mon, 27 Apr 2026 22:18:28 +0100 Subject: [PATCH 24/26] Improve test_api_level error message --- test/test_android.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_android.py b/test/test_android.py index 5a6768c24..6d450fe8a 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -443,7 +443,8 @@ def test_api_level(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: spam_c_top_level_add=dedent( """\ #if __ANDROID_API__ != 33 - #error Wrong API level + #error Unexpected API level; the following syntax error will show the actual value: + __ANDROID_API__ #endif """ ) From 77ce08bfcd4d7ee0e017ed5bde1313d593e69ea8 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Mon, 27 Apr 2026 23:09:28 +0100 Subject: [PATCH 25/26] Add xbuild-files option --- README.md | 3 +- bin/generate_schema.py | 4 ++ cibuildwheel/options.py | 10 ++++ cibuildwheel/platforms/android.py | 40 +++++--------- .../resources/cibuildwheel.schema.json | 52 +++++++++++++++++++ cibuildwheel/resources/defaults.toml | 7 +++ cibuildwheel/util/helpers.py | 4 +- docs/configuration.md | 4 +- docs/options.md | 29 +++++++++++ test/test_android.py | 2 +- unit_test/options_test.py | 48 +++++++++++++++++ unit_test/utils_test.py | 22 ++++++++ 12 files changed, 193 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 2caeeaa76..ec78216e3 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf | | [`before-all`](https://cibuildwheel.pypa.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. | | | [`before-build`](https://cibuildwheel.pypa.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build | | | [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) | Binaries on the path that should be included in an isolated cross-build environment. | +| | [`xbuild-files`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-files) | Platform-specific files in the build environment | | | [`repair-wheel-command`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each built wheel | | | [`manylinux-*-image`
`musllinux-*-image`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) | Specify manylinux / musllinux container images | | | [`container-engine`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine) | Specify the container engine to use when building Linux wheels | @@ -170,7 +171,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf | | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build | - + These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/). diff --git a/bin/generate_schema.py b/bin/generate_schema.py index 09a736e68..c6d0b3a4d 100755 --- a/bin/generate_schema.py +++ b/bin/generate_schema.py @@ -197,6 +197,9 @@ xbuild-tools: description: Binaries on the path that should be included in an isolated cross-build environment type: string_array + xbuild-files: + description: Platform-specific files in the build environment + type: string_table_array pyodide-version: type: string description: Specify the version of Pyodide to use @@ -314,6 +317,7 @@ before-all: {"$ref": "#/$defs/inherit"} before-build: {"$ref": "#/$defs/inherit"} xbuild-tools: {"$ref": "#/$defs/inherit"} + xbuild-files: {"$ref": "#/$defs/inherit"} before-test: {"$ref": "#/$defs/inherit"} config-settings: {"$ref": "#/$defs/inherit"} container-engine: {"$ref": "#/$defs/inherit"} diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index 972c7b2c6..b4293e341 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -113,6 +113,7 @@ class BuildOptions: before_all: str before_build: str | None xbuild_tools: list[str] | None + xbuild_files: dict[str, list[str]] repair_command: str manylinux_images: dict[str, str] | None musllinux_images: dict[str, str] | None @@ -764,6 +765,14 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions: if xbuild_tools == ["\u0000"]: xbuild_tools = None + xbuild_files = parse_key_value_string( + self.reader.get( + "xbuild-files", + option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False), + ), + kw_arg_names=["*"], + ) + test_sources = shlex.split( self.reader.get( "test-sources", option_format=ListFormat(sep=" ", quote=shlex.quote) @@ -908,6 +917,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions: before_all=before_all, build_verbosity=build_verbosity, xbuild_tools=xbuild_tools, + xbuild_files=xbuild_files, repair_command=repair_command, environment=environment, dependency_constraints=dependency_constraints, diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 5219c9616..9c2b6b55f 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -15,6 +15,7 @@ from build import ProjectBuilder from build.env import IsolatedEnv from filelock import FileLock +from packaging.utils import canonicalize_name from cibuildwheel import errors, platforms # pylint: disable=cyclic-import from cibuildwheel.architecture import Architecture, arch_synonym @@ -38,15 +39,6 @@ "x86_64": "x86_64-linux-android", } -CROSS_BUILD_FILES = { - "numpy": [ - "numpy/_core/include/numpy/numpyconfig.h", - "numpy/_core/include/numpy/_numpyconfig.h", - "numpy/_core/lib/libnpymath.a", - "numpy/random/lib/libnpyrandom.a", - ] -} - def parse_identifier(identifier: str) -> tuple[str, str]: match = re.fullmatch(r"cp(\d)(\d+)-android_(.+)", identifier) @@ -142,7 +134,7 @@ def build(options: Options, tmp_path: Path) -> None: state = BuildState( config, build_options, build_path, python_dir, build_env, android_env ) - setup_cross_build_files(state) + setup_xbuild_files(state) compatible_wheel = find_compatible_wheel(built_wheels, config.identifier) if compatible_wheel: @@ -467,29 +459,25 @@ def setup_fortran(env: dict[str, str]) -> None: env["FC"] = str(shim_out) -# Although the build environment must be installed for the build platform, some packages -# contain platform-specific files which should be replaced with their Android -# equivalents. We do this using a similar technique to Pyodide: -# * https://github.com/pyodide/pyodide-build/blob/v0.30.2/pyodide_build/recipe/builder.py#L638 -# * https://github.com/pyodide/pyodide-recipes/blob/20250606/packages/numpy/meta.yaml#L28 -def setup_cross_build_files(state: BuildState) -> None: +def setup_xbuild_files(state: BuildState) -> None: _, pip = find_pip(state.options) - cbf_dir = state.build_path / "cross_build_files" - cbf_dir.mkdir() + xbf_dir = state.build_path / "xbuild_files" + xbf_dir.mkdir() for requirement in call(*pip, "freeze", env=state.build_env, capture_stdout=True).splitlines(): name, _, _ = requirement.strip().partition("==") - cross_build_files = CROSS_BUILD_FILES.get(name.lower(), []) - if cross_build_files: - pip_install_android(state, cbf_dir, "--no-deps", requirement) - for cbf in cross_build_files: - if (cbf_dir / cbf).exists(): + xbuild_files = state.options.xbuild_files.get(canonicalize_name(name), []) + if xbuild_files: + log.step(f"Installing xbuild-files for {name}...") + pip_install_android(state, xbf_dir, "--no-deps", requirement) + for xbf in xbuild_files: + if (xbf_dir / xbf).exists(): shutil.copy( - cbf_dir / cbf, - find_site_packages(state.build_env) / cbf, + xbf_dir / xbf, + find_site_packages(state.build_env) / xbf, ) else: - log.warning(f"{cbf_dir / cbf} does not exist") + log.warning(f"{xbf_dir / xbf} does not exist") def pip_install_android(state: BuildState, target: Path, *args: PathOrStr) -> None: diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json index ff8eed744..d8a686995 100644 --- a/cibuildwheel/resources/cibuildwheel.schema.json +++ b/cibuildwheel/resources/cibuildwheel.schema.json @@ -431,6 +431,34 @@ ], "title": "CIBW_XBUILD_TOOLS" }, + "xbuild-files": { + "description": "Platform-specific files in the build environment", + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "patternProperties": { + ".+": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + ], + "title": "CIBW_XBUILD_FILES" + }, "pyodide-version": { "type": "string", "description": "Specify the version of Pyodide to use", @@ -644,6 +672,9 @@ "xbuild-tools": { "$ref": "#/$defs/inherit" }, + "xbuild-files": { + "$ref": "#/$defs/inherit" + }, "before-test": { "$ref": "#/$defs/inherit" }, @@ -766,6 +797,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -884,6 +918,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -960,6 +997,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -1023,6 +1063,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -1099,6 +1142,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -1162,6 +1208,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, @@ -1238,6 +1287,9 @@ "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "xbuild-files": { + "$ref": "#/properties/xbuild-files" + }, "pyodide-version": { "$ref": "#/properties/pyodide-version" }, diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml index 041978153..722dbcaa8 100644 --- a/cibuildwheel/resources/defaults.toml +++ b/cibuildwheel/resources/defaults.toml @@ -53,6 +53,13 @@ musllinux-s390x-image = "musllinux_1_2" musllinux-armv7l-image = "musllinux_1_2" musllinux-riscv64-image = "musllinux_1_2" +[tool.cibuildwheel.xbuild-files] +numpy = [ + "numpy/_core/include/numpy/_numpyconfig.h", + "numpy/_core/include/numpy/numpyconfig.h", + "numpy/_core/lib/libnpymath.a", + "numpy/random/lib/libnpyrandom.a", +] [tool.cibuildwheel.linux] repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}" diff --git a/cibuildwheel/util/helpers.py b/cibuildwheel/util/helpers.py index 6e17cb2eb..7420f629e 100644 --- a/cibuildwheel/util/helpers.py +++ b/cibuildwheel/util/helpers.py @@ -104,7 +104,7 @@ def parse_key_value_string( if kw_arg_names is None: kw_arg_names = [] - all_field_names = [*positional_arg_names, *kw_arg_names] + all_field_names = None if ("*" in kw_arg_names) else [*positional_arg_names, *kw_arg_names] shlexer = shlex.shlex(key_value_string, posix=True, punctuation_chars=";") shlexer.commenters = "" @@ -121,7 +121,7 @@ def parse_key_value_string( # check to see if the option name is specified field_name, sep, first_value = field[0].partition(":") if sep: - if field_name not in all_field_names: + if (all_field_names is not None) and (field_name not in all_field_names): msg = f"Failed to parse {key_value_string!r}. Unknown field name {field_name!r}" raise ValueError(msg) diff --git a/docs/configuration.md b/docs/configuration.md index c35c697fb..2506cdfd0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -58,8 +58,8 @@ cibuildwheel to run tests, add the following YAML to your CI config file: You can configure cibuildwheel with a config file, such as `pyproject.toml`. Options have the same names as the environment variable overrides, but are placed in `[tool.cibuildwheel]` and are lower case, with dashes, following -common [TOML](https://toml.io) practice. Anything placed in subsections `linux`, `windows`, -`macos`, or `pyodide` will only affect those platforms. Lists can be used +common [TOML](https://toml.io) practice. Anything placed in subsections +named after a platform will only affect those platforms. Lists can be used instead of strings for items that are naturally a list. Multiline strings also work just like in the environment variables. Environment variables will take precedence if defined. diff --git a/docs/options.md b/docs/options.md index 7f2d6a2b0..b81eeb454 100644 --- a/docs/options.md +++ b/docs/options.md @@ -902,6 +902,35 @@ Platform-specific environment variables are also available on platforms that use ``` +### `xbuild-files` {: #xbuild-files env-var toml} +> Platform-specific files in the build environment + +When cross-compiling a package, any dependencies in its [`build-system.requires`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) are installed for the build platform. However, some dependencies contain platform-specific files such as headers and static libraries, which must correspond to the target platform. + +This option maps a [normalized](https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization) package name to a list of paths within that package. If the package is present in the build environment, then a matching version will be downloaded for the target platform, and used to overwrite the given paths within the build environment. + +The default value of this option includes [paths from popular packages](configuration.md#configuration-file). + +Platform-specific environment variables are also available:
+ `CIBW_XBUILD_FILES_ANDROID` + +#### Examples + +!!! tab examples "pyproject.toml" + + ```toml + [tool.cibuildwheel.xbuild-files] + package1 = ["some/header.h", "some/library.a"] + package2 = ["other/header.h"] + ``` + +!!! tab examples "Environment variables" + + ```yaml + CIBW_XBUILD_FILES: "package1: some/header.h some/library.a; package2: other/header.h" + ``` + + ### `repair-wheel-command` {: #repair-wheel-command env-var toml} > Execute a shell command to repair each built wheel diff --git a/test/test_android.py b/test/test_android.py index 6d450fe8a..e8a98cc58 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -658,7 +658,7 @@ def test_meson(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: @needs_emulator -def test_cross_build_files(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: +def test_xbuild_files(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # Verify that we've replaced the correct files by compiling against a non-trivial # function from libnpymath.a. new_c_project( diff --git a/unit_test/options_test.py b/unit_test/options_test.py index 0c42b1f9a..b5e61601f 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -697,3 +697,51 @@ def test_xbuild_tools_handling(tmp_path: Path, definition: str, expected: list[s local = options.build_options("cp313-ios_13_0_arm64_iphoneos") assert local.xbuild_tools == expected + + +DEFAULT_XBUILD_FILES = { + "numpy": [ + "numpy/_core/include/numpy/_numpyconfig.h", + "numpy/_core/include/numpy/numpyconfig.h", + "numpy/_core/lib/libnpymath.a", + "numpy/random/lib/libnpyrandom.a", + ] +} + + +@pytest.mark.parametrize( + ("definition", "expected"), + [ + ("", DEFAULT_XBUILD_FILES), + ('xbuild-files = ""', {}), + ('xbuild-files = "package1: file1"', {"package1": ["file1"]}), + ( + "xbuild-files = \"package1: 'file1 with space'; package2: file2a file2b\"", + {"package1": ["file1 with space"], "package2": ["file2a", "file2b"]}, + ), + ], +) +def test_xbuild_files(tmp_path: Path, definition: str, expected: dict[str, list[str]]) -> None: + args = CommandLineArguments.defaults() + args.package_dir = tmp_path + + pyproject_toml: Path = tmp_path / "pyproject.toml" + pyproject_toml.write_text( + textwrap.dedent( + f"""\ + [tool.cibuildwheel] + {definition} + """ + ) + ) + + options = Options(platform="ios", command_line_arguments=args, env={}) + assert options.build_options(None).xbuild_files == expected + + pyproject_toml.write_text( + "[tool.cibuildwheel.xbuild-files]\n" + + "\n".join(f"{package} = {files}" for package, files in expected.items()) + ) + + options = Options(platform="ios", command_line_arguments=args, env={}) + assert options.build_options(None).xbuild_files == expected diff --git a/unit_test/utils_test.py b/unit_test/utils_test.py index 3ce67e273..7bc585dd8 100644 --- a/unit_test/utils_test.py +++ b/unit_test/utils_test.py @@ -212,6 +212,28 @@ def test_parse_key_value_string() -> None: } +def test_parse_key_value_string_unknown_name() -> None: + # Unknown fields are not allowed by default. + with pytest.raises(ValueError, match=r"Failed to parse 'key: value'. Unknown field name 'key'"): + parse_key_value_string("key: value") + + # Unknown fields can be enabled by passing "*". + assert parse_key_value_string( + "key: value", + kw_arg_names=["*"], + ) == { + "key": ["value"], + } + + assert parse_key_value_string( + "key1: value1a value1b; key2: value2", + kw_arg_names=["*"], + ) == { + "key1": ["value1a", "value1b"], + "key2": ["value2"], + } + + def test_flexible_version_comparisons() -> None: assert FlexibleVersion("2.0") == FlexibleVersion("2") assert FlexibleVersion("2.0") < FlexibleVersion("2.1") From 82437e761c0712983e5b74d0d75c31d37c5980cf Mon Sep 17 00:00:00 2001 From: mayeut Date: Sat, 2 May 2026 10:25:36 +0200 Subject: [PATCH 26/26] use pypa/auditwheel@main --- cibuildwheel/platforms/android.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 9c2b6b55f..818426f46 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -235,7 +235,7 @@ def setup_env( # https://github.com/pypa/auditwheel/pull/643 has been released, and add it to the # constraints files. tools = [ - "auditwheel @ git+https://github.com/mhsmith/auditwheel@android", + "auditwheel @ git+https://github.com/pypa/auditwheel@main", "patchelf", "pkgconf", ]