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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions cibuildwheel/platforms/android.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from ..util.helpers import prepare_command
from ..util.packaging import find_compatible_wheel
from ..util.python_build_standalone import create_python_build_standalone_environment
from ..venv import constraint_flags, virtualenv
from ..venv import constraint_flags, find_uv, virtualenv


def android_triplet(identifier: str) -> str:
Expand Down Expand Up @@ -187,6 +187,13 @@ 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"]

# Create virtual environment
python_exe = create_python_build_standalone_environment(
Expand All @@ -197,14 +204,14 @@ def setup_env(
version=config.version, tmp_dir=build_path
)
build_env = virtualenv(
config.version, python_exe, venv_dir, dependency_constraint, use_uv=False
config.version, python_exe, venv_dir, dependency_constraint, use_uv=use_uv
)
create_cmake_toolchain(config, build_path, python_dir, build_env)

# 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"
for command in ["python", "pip"]:
for command in ["python"] if use_uv else ["python", "pip"]:
command_path = call("which", command, env=build_env, capture_stdout=True).strip()
if command_path != f"{venv_dir}/bin/{command}":
msg = (
Expand All @@ -219,11 +226,10 @@ def setup_env(
android_env = setup_android_env(config, python_dir, venv_dir, build_env)

# Install build tools
build_frontend = build_options.build_frontend
if build_frontend.name != "build":
if build_frontend not in {"build", "build[uv]"}:
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", "build", *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
Expand All @@ -243,13 +249,13 @@ def make_extra_environ(self) -> dict[str, str]:

pb = ProjectBuilder.from_isolated_env(AndroidEnv(), build_options.package_dir)
if pb.build_system_requires:
call("pip", "install", *pb.build_system_requires, env=build_env)
call(*pip, "install", *pb.build_system_requires, env=build_env)

requires_for_build = pb.get_requires_for_build(
"wheel", parse_config_settings(build_options.config_settings)
)
if requires_for_build:
call("pip", "install", *requires_for_build, env=build_env)
call(*pip, "install", *requires_for_build, env=build_env)

return build_env, android_env

Expand Down
34 changes: 27 additions & 7 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,39 @@ def docker_warmup_fixture(
@pytest.fixture(params=["pip", "build"])
def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
frontend = request.param
if get_platform() == "pyodide" and frontend == "pip":
marks = {m.name for m in request.node.iter_markers()}

platform = "pyodide" if "pyodide" in marks else get_platform()
if platform == "pyodide" and frontend == "pip":
pytest.skip("Can't use pip as build frontend for pyodide platform")

return {"CIBW_BUILD_FRONTEND": frontend}


@pytest.fixture
def build_frontend_env(build_frontend_env_nouv: dict[str, str]) -> dict[str, str]:
frontend = build_frontend_env_nouv["CIBW_BUILD_FRONTEND"]
if frontend != "build" or get_platform() == "pyodide" or find_uv() is None:
return build_frontend_env_nouv
@pytest.fixture(params=["pip", "build", "build[uv]"])
def build_frontend_env(request: pytest.FixtureRequest) -> dict[str, str]:
frontend = request.param
marks = {m.name for m in request.node.iter_markers()}
if "android" in marks:
platform = "android"
elif "ios" in marks:
platform = "ios"
elif "pyodide" in marks:
platform = "pyodide"
else:
platform = get_platform()

if platform in {"pyodide", "ios", "android"} and frontend == "pip":
pytest.skip(f"Can't use pip as build frontend for {platform}")
if platform == "pyodide" and frontend == "build[uv]":
pytest.skip("Can't use uv with pyodide yet")
uv_path = find_uv()
if uv_path is None and frontend == "build[uv]":
pytest.skip("Can't find uv, so skipping uv tests")
if uv_path is not None and frontend == "build" and platform not in {"android", "ios"}:
pytest.skip("No need to check build when uv is present")

return {"CIBW_BUILD_FRONTEND": "build[uv]"}
return {"CIBW_BUILD_FRONTEND": frontend}


@pytest.fixture
Expand Down
6 changes: 3 additions & 3 deletions test/test_android.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ def test_expected_wheels(tmp_path):
)


def test_frontend_good(tmp_path):
def test_frontend_good(tmp_path, build_frontend_env):
new_c_project().generate(tmp_path)
wheels = cibuildwheel_run(
tmp_path,
add_env={**cp313_env, "CIBW_BUILD_FRONTEND": "build"},
add_env={**cp313_env, **build_frontend_env},
)
assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{native_arch.android_abi}.whl"]


@pytest.mark.parametrize("frontend", ["build[uv]", "pip"])
@pytest.mark.parametrize("frontend", ["pip"])
def test_frontend_bad(frontend, tmp_path, capfd):
new_c_project().generate(tmp_path)
with pytest.raises(CalledProcessError):
Expand Down
Loading