* Add Android to resource files * Add Android to miscellaneous places * Add Android documentation * Docs cleanups * Add Android platform module; implement top-level structure and target Python installation * Implement setup_env and build_wheel * lru-dict build working * Alter prefix in sysconfigdata file; fix various issues with FLAGS variables * Implement Android testing * Add type annotations to _cross_venv * Revert Python 3.8 to pip 25.0.1 * Make test-sources required on Android * Add Android integration tests * Test cleanups * Add test of all available Python versions * Update test-sources and test-command behavior to match iOS * Documentation cleanups * Replace Builder class with a set of global functions * Rename "env" to "build_env" * Remove Chaquopy repository from default pip command line * Move native_platform to platforms module * Fix parse_config_settings Co-authored-by: Joe Rickerby <joerick@mac.com> * Add unit tests for parse_config_settings and arch_synonym * Make `shell_prepared` arguments keyword-only, and add tests for the commands that use it * Replace `importlib.util.spec_from_file_location` with `runpy.run_path` * Use python-build-standalone * Update Android Python * Enable KVM in Linux CI * Move KVM code to test_android.py * Use Java 17 on Azure * Install emulator if necessary before running -accel-check * Free up additional disk space on Linux runners * Add sudo * Skip emulator tests on CI platforms that don't support it * Download Android Python from Maven Central * Free up more disk space on Linux runners * fix: minor fixups Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * Set sysconfig._BASE_PREFIX to support sysconfig.get_path("include") * Get ANDROID_API_LEVEL from the build environment, not cibuildwheel's own environment * Correct relative path of test-sources * Pass a CMake toolchain file to the build * Add "repair" step which adds libc++ to the wheel when necessary * Add missing needs_emulator decorator * Provide useful error message if ANDROID_HOME is not set * Remove use of HOST environment variable * Update to Python 3.15.5 * Fix PyLint warnings, clarify comment * Group common arguments into a dataclass * Handle environment variables containing newlines * Discourage the use of `pytest` test commands without `python -m` * Use single quotes in user-visible messages * Improve testing documentation * Pass wheel filename to `log.build_end` * In GitHub Actions example, skip Android tests on macOS * Correct relative paths in `patchelf --set-rpath` * Clarify `test-sources` docs * Update to Python 3.13.5+20250722.214220 --------- Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: Joe Rickerby <joerick@mac.com> Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
import dataclasses
|
|
import shlex
|
|
import typing
|
|
from collections.abc import Sequence
|
|
from typing import Literal, Self, get_args
|
|
|
|
from .logger import log
|
|
from .util.helpers import parse_key_value_string
|
|
|
|
BuildFrontendName = Literal["pip", "build", "build[uv]"]
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
class BuildFrontendConfig:
|
|
name: BuildFrontendName
|
|
args: Sequence[str] = ()
|
|
|
|
@classmethod
|
|
def from_config_string(cls, config_string: str) -> Self:
|
|
config_dict = parse_key_value_string(config_string, ["name"], ["args"])
|
|
name = " ".join(config_dict["name"])
|
|
if name not in get_args(BuildFrontendName):
|
|
names = ", ".join(repr(n) for n in get_args(BuildFrontendName))
|
|
msg = f"Unrecognised build frontend {name!r}, must be one of {names}"
|
|
raise ValueError(msg)
|
|
|
|
name = typing.cast(BuildFrontendName, name)
|
|
|
|
args = config_dict.get("args") or []
|
|
return cls(name=name, args=args)
|
|
|
|
def options_summary(self) -> str | dict[str, str]:
|
|
if not self.args:
|
|
return self.name
|
|
else:
|
|
return {"name": self.name, "args": repr(self.args)}
|
|
|
|
|
|
def _get_verbosity_flags(level: int, frontend: BuildFrontendName) -> list[str]:
|
|
if level < 0:
|
|
if frontend == "pip":
|
|
return ["-" + -level * "q"]
|
|
|
|
msg = f"build_verbosity {level} is not supported for {frontend} frontend. Ignoring."
|
|
log.warning(msg)
|
|
|
|
if level > 0:
|
|
if frontend == "pip":
|
|
return ["-" + level * "v"]
|
|
if level > 1:
|
|
return ["-" + (level - 1) * "v"]
|
|
|
|
return []
|
|
|
|
|
|
def _split_config_settings(config_settings: str) -> list[str]:
|
|
config_settings_list = shlex.split(config_settings)
|
|
return [f"-C{setting}" for setting in config_settings_list]
|
|
|
|
|
|
# Based on build.__main__.main.
|
|
def parse_config_settings(config_settings_str: str) -> dict[str, str | list[str]]:
|
|
config_settings: dict[str, str | list[str]] = {}
|
|
for arg in shlex.split(config_settings_str):
|
|
setting, _, value = arg.partition("=")
|
|
existing_value = config_settings.get(setting)
|
|
if existing_value is None:
|
|
config_settings[setting] = value
|
|
elif isinstance(existing_value, str):
|
|
config_settings[setting] = [existing_value, value]
|
|
else:
|
|
existing_value.append(value)
|
|
|
|
return config_settings
|
|
|
|
|
|
def get_build_frontend_extra_flags(
|
|
build_frontend: BuildFrontendConfig, verbosity_level: int, config_settings: str
|
|
) -> list[str]:
|
|
return [
|
|
*_split_config_settings(config_settings),
|
|
*build_frontend.args,
|
|
*_get_verbosity_flags(verbosity_level, build_frontend.name),
|
|
]
|