From 32dcc380b9f73d30083d1d9ea15556dd440324a8 Mon Sep 17 00:00:00 2001 From: ri Date: Wed, 5 Nov 2025 10:52:46 +0800 Subject: [PATCH 1/3] feat(android): add Rust cross-compilation setup for Android environment --- cibuildwheel/platforms/android.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index dca2f5117..abf24d810 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -7,7 +7,7 @@ import shutil import subprocess import sysconfig -from collections.abc import Iterable, Iterator +from collections.abc import Iterable, Iterator, MutableMapping from dataclasses import dataclass from os.path import relpath from pathlib import Path @@ -340,6 +340,8 @@ def localized_vars( def setup_android_env( config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str] ) -> dict[str, str]: + setup_rust_cross_compile(config, build_env) + 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) @@ -390,6 +392,25 @@ def setup_android_env( return android_env +def setup_rust_cross_compile( + python_configuration: PythonConfiguration, + env: MutableMapping[str, str], +) -> None: + cargo_target = android_triplet(python_configuration.identifier) + call("rustup", "target", "add", cargo_target) + + # CARGO_BUILD_TARGET is the variable used by Cargo and setuptools_rust + if env.get("CARGO_BUILD_TARGET"): + if env["CARGO_BUILD_TARGET"] != cargo_target: + log.notice("Not overriding CARGO_BUILD_TARGET as it has already been set") + # No message if it was set to what we were planning to set it to + elif cargo_target: + log.notice(f"Setting CARGO_BUILD_TARGET={cargo_target} for cross-compilation") + env["CARGO_BUILD_TARGET"] = cargo_target + else: + log.warning(f"Unable to configure Rust cross-compilation for architecture {cargo_target}") + + def before_build(state: BuildState) -> None: if state.options.before_build: log.step("Running before_build...") From 22fe90bc972c11ddbb2cff0753d42dba142f0e29 Mon Sep 17 00:00:00 2001 From: ri Date: Mon, 10 Nov 2025 14:45:32 +0800 Subject: [PATCH 2/3] feat(android): set the Cargo target linker --- cibuildwheel/platforms/android.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index abf24d810..803069f20 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -340,8 +340,6 @@ def localized_vars( def setup_android_env( config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str] ) -> dict[str, str]: - setup_rust_cross_compile(config, build_env) - 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) @@ -384,6 +382,9 @@ def setup_android_env( for key in ["CFLAGS", "CXXFLAGS"]: android_env[key] += " " + opt + # Cargo target linker need to be specified after CC is set + setup_rust_cross_compile(config, 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: @@ -405,8 +406,15 @@ def setup_rust_cross_compile( log.notice("Not overriding CARGO_BUILD_TARGET as it has already been set") # No message if it was set to what we were planning to set it to elif cargo_target: - log.notice(f"Setting CARGO_BUILD_TARGET={cargo_target} for cross-compilation") + cargo_target_linker_env_name = ( + f"CARGO_TARGET_{cargo_target.upper().replace('-', '_')}_LINKER" + ) + log.notice( + f"Setting CARGO_BUILD_TARGET={cargo_target} and {cargo_target_linker_env_name} for cross-compilation" + ) env["CARGO_BUILD_TARGET"] = cargo_target + # CC has already been set by calling android.py (it calls android-env.sh) + env[f"{cargo_target_linker_env_name}"] = env["CC"] else: log.warning(f"Unable to configure Rust cross-compilation for architecture {cargo_target}") From 2a6798ea9d3bd7cc9fafe2f46dd6fbe4d8ea9ab3 Mon Sep 17 00:00:00 2001 From: ri Date: Mon, 10 Nov 2025 14:57:55 +0800 Subject: [PATCH 3/3] feat(android): add PYO3 cross-compilation setup to set PYO3_CROSS_LIB_DIR to link against libpython3.x.so explicitly --- cibuildwheel/platforms/android.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index 803069f20..307289dae 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -385,6 +385,8 @@ def setup_android_env( # Cargo target linker need to be specified after CC is set setup_rust_cross_compile(config, android_env) + setup_PYO3_cross_compile(python_dir, 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: @@ -419,6 +421,21 @@ def setup_rust_cross_compile( log.warning(f"Unable to configure Rust cross-compilation for architecture {cargo_target}") +def setup_PYO3_cross_compile( + python_dir: Path, + env: MutableMapping[str, str], +) -> None: + # All Python extension modules must therefore be explicitly linked against libpython3.x.so when building for Android. + # See: https://peps.python.org/pep-0738/#linkage + # For projects using PyO3, this requires setting PYO3_CROSS_LIB_DIR to the directory containing libpython3.x.so. + # See: https://pyo3.rs/v0.27.1/building-and-distribution.html#cross-compiling + if env.get("PYO3_CROSS_LIB_DIR"): + log.notice("Not overriding PYO3_CROSS_LIB_DIR as it has already been set") + else: + env["PYO3_CROSS_LIB_DIR"] = str(python_dir / "prefix" / "lib") + log.notice("Setting PYO3_CROSS_LIB_DIR for PyO3 cross-compilation") + + def before_build(state: BuildState) -> None: if state.options.before_build: log.step("Running before_build...")