Android updates (#2568)
* Add Python 3.14 for Android * Simplify GitHub Actions workflows * Update tests * Allow running `patchelf` even when the environment's `bin` directory is not on the PATH * Search in both the environment's bin directory and the PATH * Use `sysconfig.get_path` * Fix lint warning
This commit is contained in:
@@ -105,14 +105,6 @@ jobs:
|
|||||||
/usr/local/share/powershell
|
/usr/local/share/powershell
|
||||||
df -h
|
df -h
|
||||||
|
|
||||||
# https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
|
|
||||||
- name: Enable KVM for Android emulator
|
|
||||||
if: runner.os == 'Linux' && runner.arch == 'X64'
|
|
||||||
run: |
|
|
||||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
||||||
sudo udevadm control --reload-rules
|
|
||||||
sudo udevadm trigger --name-match=kvm
|
|
||||||
|
|
||||||
# for oci_container unit tests
|
# for oci_container unit tests
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ While cibuildwheel itself requires a recent Python version to run (we support th
|
|||||||
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
|
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
|
||||||
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
|
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
|
||||||
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | ✅⁴ |
|
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | ✅⁴ |
|
||||||
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | N/A |
|
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | ✅⁴ |
|
||||||
| CPython 3.14 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
|
| CPython 3.14 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | N/A |
|
||||||
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
||||||
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
||||||
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
|
||||||
|
|||||||
+39
-68
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
import copy
|
|
||||||
import difflib
|
import difflib
|
||||||
import logging
|
import logging
|
||||||
import operator
|
import operator
|
||||||
@@ -34,47 +33,20 @@ RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
|
|||||||
ArchStr = Literal["32", "64", "ARM64"]
|
ArchStr = Literal["32", "64", "ARM64"]
|
||||||
|
|
||||||
|
|
||||||
class ConfigWinCP(TypedDict):
|
class Config(TypedDict):
|
||||||
identifier: str
|
identifier: str
|
||||||
version: str
|
version: str
|
||||||
arch: str
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigWinPP(TypedDict):
|
class ConfigUrl(Config):
|
||||||
identifier: str
|
|
||||||
version: str
|
|
||||||
arch: str
|
|
||||||
url: str
|
url: str
|
||||||
|
|
||||||
|
|
||||||
class ConfigWinGP(TypedDict):
|
class ConfigPyodide(Config):
|
||||||
identifier: str
|
|
||||||
version: str
|
|
||||||
url: str
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigApple(TypedDict):
|
|
||||||
identifier: str
|
|
||||||
version: str
|
|
||||||
url: str
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigAndroid(TypedDict):
|
|
||||||
identifier: str
|
|
||||||
version: str
|
|
||||||
url: str
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigPyodide(TypedDict):
|
|
||||||
identifier: str
|
|
||||||
version: str
|
|
||||||
default_pyodide_version: str
|
default_pyodide_version: str
|
||||||
node_version: str
|
node_version: str
|
||||||
|
|
||||||
|
|
||||||
AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigAndroid | ConfigPyodide
|
|
||||||
|
|
||||||
|
|
||||||
# The following set of "Versions" classes allow the initial call to the APIs to
|
# The following set of "Versions" classes allow the initial call to the APIs to
|
||||||
# be cached and reused in the `update_version_*` methods.
|
# be cached and reused in the `update_version_*` methods.
|
||||||
|
|
||||||
@@ -106,7 +78,7 @@ class WindowsVersions:
|
|||||||
|
|
||||||
self.version_dict = {Version(v): v for v in cp_info["versions"]}
|
self.version_dict = {Version(v): v for v in cp_info["versions"]}
|
||||||
|
|
||||||
def update_version_windows(self, spec: Specifier) -> ConfigWinCP | None:
|
def update_version_windows(self, spec: Specifier) -> Config | None:
|
||||||
# Specifier.filter selects all non pre-releases that match the spec,
|
# Specifier.filter selects all non pre-releases that match the spec,
|
||||||
# unless there are only pre-releases, then it selects pre-releases
|
# unless there are only pre-releases, then it selects pre-releases
|
||||||
# instead (like pip)
|
# instead (like pip)
|
||||||
@@ -121,10 +93,9 @@ class WindowsVersions:
|
|||||||
flags = "t" if self.free_threaded else ""
|
flags = "t" if self.free_threaded else ""
|
||||||
version = versions[0]
|
version = versions[0]
|
||||||
identifier = f"cp{version.major}{version.minor}{flags}-{self.arch}"
|
identifier = f"cp{version.major}{version.minor}{flags}-{self.arch}"
|
||||||
return ConfigWinCP(
|
return Config(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=self.version_dict[version],
|
version=self.version_dict[version],
|
||||||
arch=self.arch_str,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +117,7 @@ class GraalPyVersions:
|
|||||||
|
|
||||||
self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r]
|
self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r]
|
||||||
|
|
||||||
def update_version(self, identifier: str, spec: Specifier) -> AnyConfig:
|
def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl:
|
||||||
if "x86_64" in identifier or "amd64" in identifier:
|
if "x86_64" in identifier or "amd64" in identifier:
|
||||||
arch = "x86_64"
|
arch = "x86_64"
|
||||||
elif "arm64" in identifier or "aarch64" in identifier:
|
elif "arm64" in identifier or "aarch64" in identifier:
|
||||||
@@ -172,11 +143,9 @@ class GraalPyVersions:
|
|||||||
|
|
||||||
if "macosx" in identifier:
|
if "macosx" in identifier:
|
||||||
arch = "x86_64" if "x86_64" in identifier else "arm64"
|
arch = "x86_64" if "x86_64" in identifier else "arm64"
|
||||||
config = ConfigApple
|
|
||||||
platform = "macos"
|
platform = "macos"
|
||||||
elif "win" in identifier:
|
elif "win" in identifier:
|
||||||
arch = "aarch64" if "arm64" in identifier else "x86_64"
|
arch = "aarch64" if "arm64" in identifier else "x86_64"
|
||||||
config = ConfigWinGP
|
|
||||||
platform = "windows"
|
platform = "windows"
|
||||||
else:
|
else:
|
||||||
msg = "GraalPy provides downloads for macOS and Windows and is included for manylinux"
|
msg = "GraalPy provides downloads for macOS and Windows and is included for manylinux"
|
||||||
@@ -191,7 +160,7 @@ class GraalPyVersions:
|
|||||||
and rf["name"].startswith(f"graalpy-{gpversion.major}")
|
and rf["name"].startswith(f"graalpy-{gpversion.major}")
|
||||||
)
|
)
|
||||||
|
|
||||||
return config(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{version.major}.{version.minor}",
|
version=f"{version.major}.{version.minor}",
|
||||||
url=url,
|
url=url,
|
||||||
@@ -223,7 +192,7 @@ class PyPyVersions:
|
|||||||
]
|
]
|
||||||
return urls[0] if urls else ""
|
return urls[0] if urls else ""
|
||||||
|
|
||||||
def update_version_windows(self, spec: Specifier) -> ConfigWinCP:
|
def update_version_windows(self, spec: Specifier) -> ConfigUrl:
|
||||||
releases = [r for r in self.releases if spec.contains(r["python_version"])]
|
releases = [r for r in self.releases if spec.contains(r["python_version"])]
|
||||||
releases = sorted(releases, key=operator.itemgetter("pypy_version"))
|
releases = sorted(releases, key=operator.itemgetter("pypy_version"))
|
||||||
releases = [r for r in releases if self.get_arch_file(r)]
|
releases = [r for r in releases if self.get_arch_file(r)]
|
||||||
@@ -239,14 +208,13 @@ class PyPyVersions:
|
|||||||
identifier = f"pp{version.major}{version.minor}-{version_arch}"
|
identifier = f"pp{version.major}{version.minor}-{version_arch}"
|
||||||
url = self.get_arch_file(release)
|
url = self.get_arch_file(release)
|
||||||
|
|
||||||
return ConfigWinPP(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{version.major}.{version.minor}",
|
version=f"{version.major}.{version.minor}",
|
||||||
arch=self.arch,
|
|
||||||
url=url,
|
url=url,
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_version_macos(self, spec: Specifier) -> ConfigApple:
|
def update_version_macos(self, spec: Specifier) -> ConfigUrl:
|
||||||
if self.arch not in {"64", "ARM64"}:
|
if self.arch not in {"64", "ARM64"}:
|
||||||
msg = f"'{self.arch}' arch not supported yet on macOS"
|
msg = f"'{self.arch}' arch not supported yet on macOS"
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
@@ -270,7 +238,7 @@ class PyPyVersions:
|
|||||||
if "" in rf["platform"] == "darwin" and rf["arch"] == arch
|
if "" in rf["platform"] == "darwin" and rf["arch"] == arch
|
||||||
)
|
)
|
||||||
|
|
||||||
return ConfigApple(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{version.major}.{version.minor}",
|
version=f"{version.major}.{version.minor}",
|
||||||
url=url,
|
url=url,
|
||||||
@@ -298,16 +266,11 @@ class CPythonVersions:
|
|||||||
uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
|
uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
|
||||||
self.versions_dict[version] = uri
|
self.versions_dict[version] = uri
|
||||||
|
|
||||||
def update_version_macos(
|
def update_version(self, identifier: str, spec: Specifier, file_ident: str) -> ConfigUrl | None:
|
||||||
self, identifier: str, version: Version, spec: Specifier
|
|
||||||
) -> ConfigApple | None:
|
|
||||||
# see note above on Specifier.filter
|
# see note above on Specifier.filter
|
||||||
unsorted_versions = spec.filter(self.versions_dict)
|
unsorted_versions = spec.filter(self.versions_dict)
|
||||||
sorted_versions = sorted(unsorted_versions, reverse=True)
|
sorted_versions = sorted(unsorted_versions, reverse=True)
|
||||||
|
|
||||||
macver = "x10.9" if version <= Version("3.8.9999") else "11"
|
|
||||||
file_ident = f"macos{macver}.pkg"
|
|
||||||
|
|
||||||
for new_version in sorted_versions:
|
for new_version in sorted_versions:
|
||||||
# Find the first patch version that contains the requested file
|
# Find the first patch version that contains the requested file
|
||||||
uri = self.versions_dict[new_version]
|
uri = self.versions_dict[new_version]
|
||||||
@@ -319,7 +282,7 @@ class CPythonVersions:
|
|||||||
|
|
||||||
urls = [rf["url"] for rf in file_info if file_ident in rf["url"]]
|
urls = [rf["url"] for rf in file_info if file_ident in rf["url"]]
|
||||||
if urls:
|
if urls:
|
||||||
return ConfigApple(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{new_version.major}.{new_version.minor}",
|
version=f"{new_version.major}.{new_version.minor}",
|
||||||
url=urls[0],
|
url=urls[0],
|
||||||
@@ -327,9 +290,17 @@ class CPythonVersions:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def update_version_macos(
|
||||||
|
self, identifier: str, version: Version, spec: Specifier
|
||||||
|
) -> ConfigUrl | None:
|
||||||
|
macver = "x10.9" if version <= Version("3.8.9999") else "11"
|
||||||
|
return self.update_version(identifier, spec, f"macos{macver}.pkg")
|
||||||
|
|
||||||
class AndroidVersions:
|
def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None:
|
||||||
# This should be replaced with official python.org downloads once they're available.
|
return self.update_version(identifier, spec, android_triplet(identifier))
|
||||||
|
|
||||||
|
|
||||||
|
class MavenVersions:
|
||||||
MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python"
|
MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python"
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -343,18 +314,16 @@ class AndroidVersions:
|
|||||||
assert isinstance(version_str, str), version_str
|
assert isinstance(version_str, str), version_str
|
||||||
self.versions.append(Version(version_str))
|
self.versions.append(Version(version_str))
|
||||||
|
|
||||||
def update_version_android(
|
def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None:
|
||||||
self, identifier: str, version: Version, spec: Specifier
|
|
||||||
) -> ConfigAndroid | None:
|
|
||||||
sorted_versions = sorted(spec.filter(self.versions), reverse=True)
|
sorted_versions = sorted(spec.filter(self.versions), reverse=True)
|
||||||
|
|
||||||
# Return a config using the highest version for the given specifier.
|
# Return a config using the highest version for the given specifier.
|
||||||
if sorted_versions:
|
if sorted_versions:
|
||||||
max_version = sorted_versions[0]
|
max_version = sorted_versions[0]
|
||||||
triplet = android_triplet(identifier)
|
triplet = android_triplet(identifier)
|
||||||
return ConfigAndroid(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=str(version),
|
version=f"{max_version.major}.{max_version.minor}",
|
||||||
url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz",
|
url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -390,11 +359,11 @@ class CPythonIOSVersions:
|
|||||||
if filename.endswith("-iOS-support"):
|
if filename.endswith("-iOS-support"):
|
||||||
self.versions_dict[version][int(build[1:])] = asset["browser_download_url"]
|
self.versions_dict[version][int(build[1:])] = asset["browser_download_url"]
|
||||||
|
|
||||||
def update_version_ios(self, identifier: str, version: Version) -> ConfigApple | None:
|
def update_version_ios(self, identifier: str, version: Version) -> ConfigUrl | None:
|
||||||
# Return a config using the highest build number for the given version.
|
# Return a config using the highest build number for the given version.
|
||||||
urls = [url for _, url in sorted(self.versions_dict.get(version, {}).items())]
|
urls = [url for _, url in sorted(self.versions_dict.get(version, {}).items())]
|
||||||
if urls:
|
if urls:
|
||||||
return ConfigApple(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=str(version),
|
version=str(version),
|
||||||
url=urls[-1],
|
url=urls[-1],
|
||||||
@@ -450,11 +419,11 @@ class AllVersions:
|
|||||||
self.windows_t_arm64 = WindowsVersions("ARM64", True)
|
self.windows_t_arm64 = WindowsVersions("ARM64", True)
|
||||||
self.windows_pypy_64 = PyPyVersions("64")
|
self.windows_pypy_64 = PyPyVersions("64")
|
||||||
|
|
||||||
self.macos_cpython = CPythonVersions()
|
self.cpython = CPythonVersions()
|
||||||
self.macos_pypy = PyPyVersions("64")
|
self.macos_pypy = PyPyVersions("64")
|
||||||
self.macos_pypy_arm64 = PyPyVersions("ARM64")
|
self.macos_pypy_arm64 = PyPyVersions("ARM64")
|
||||||
|
|
||||||
self.android = AndroidVersions()
|
self.maven = MavenVersions()
|
||||||
self.ios_cpython = CPythonIOSVersions()
|
self.ios_cpython = CPythonIOSVersions()
|
||||||
|
|
||||||
self.graalpy = GraalPyVersions()
|
self.graalpy = GraalPyVersions()
|
||||||
@@ -466,13 +435,12 @@ class AllVersions:
|
|||||||
version = Version(config["version"])
|
version = Version(config["version"])
|
||||||
spec = Specifier(f"=={version.major}.{version.minor}.*")
|
spec = Specifier(f"=={version.major}.{version.minor}.*")
|
||||||
log.info("Reading in %r -> %s @ %s", str(identifier), spec, version)
|
log.info("Reading in %r -> %s @ %s", str(identifier), spec, version)
|
||||||
orig_config = copy.copy(config)
|
config_update: Config | None = None
|
||||||
config_update: AnyConfig | None = None
|
|
||||||
|
|
||||||
# We need to use ** in update due to MyPy (probably a bug)
|
# We need to use ** in update due to MyPy (probably a bug)
|
||||||
if "macosx" in identifier:
|
if "macosx" in identifier:
|
||||||
if identifier.startswith("cp"):
|
if identifier.startswith("cp"):
|
||||||
config_update = self.macos_cpython.update_version_macos(identifier, version, spec)
|
config_update = self.cpython.update_version_macos(identifier, version, spec)
|
||||||
elif identifier.startswith("pp"):
|
elif identifier.startswith("pp"):
|
||||||
if "macosx_x86_64" in identifier:
|
if "macosx_x86_64" in identifier:
|
||||||
config_update = self.macos_pypy.update_version_macos(spec)
|
config_update = self.macos_pypy.update_version_macos(spec)
|
||||||
@@ -498,7 +466,10 @@ class AllVersions:
|
|||||||
elif "win_arm64" in identifier and identifier.startswith("cp"):
|
elif "win_arm64" in identifier and identifier.startswith("cp"):
|
||||||
config_update = self.windows_arm64.update_version_windows(spec)
|
config_update = self.windows_arm64.update_version_windows(spec)
|
||||||
elif "android" in identifier:
|
elif "android" in identifier:
|
||||||
config_update = self.android.update_version_android(identifier, version, spec)
|
# Python 3.13 is released by Chaquopy on Maven Central.
|
||||||
|
# Python 3.14 and newer have official releases on python.org.
|
||||||
|
versions = self.maven if identifier.startswith("cp313") else self.cpython
|
||||||
|
config_update = versions.update_version_android(identifier, spec)
|
||||||
elif "ios" in identifier:
|
elif "ios" in identifier:
|
||||||
config_update = self.ios_cpython.update_version_ios(identifier, version)
|
config_update = self.ios_cpython.update_version_ios(identifier, version)
|
||||||
elif "pyodide" in identifier:
|
elif "pyodide" in identifier:
|
||||||
@@ -507,11 +478,11 @@ class AllVersions:
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert config_update is not None, f"{identifier} not found!"
|
assert config_update is not None, f"{identifier} not found!"
|
||||||
|
if config_update != config:
|
||||||
|
log.info(" Updated %s to %s", config, config_update)
|
||||||
|
config.clear()
|
||||||
config.update(**config_update)
|
config.update(**config_update)
|
||||||
|
|
||||||
if config != orig_config:
|
|
||||||
log.info(" Updated %s to %s", orig_config, config)
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("--force", is_flag=True)
|
@click.option("--force", is_flag=True)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import re
|
|||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sysconfig
|
||||||
from collections.abc import Iterable, Iterator
|
from collections.abc import Iterable, Iterator
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from os.path import relpath
|
from os.path import relpath
|
||||||
@@ -205,8 +205,8 @@ def setup_env(
|
|||||||
build_env = build_options.environment.as_dictionary(build_env)
|
build_env = build_options.environment.as_dictionary(build_env)
|
||||||
build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
for command in ["python", "pip"]:
|
for command in ["python", "pip"]:
|
||||||
which = call("which", command, env=build_env, capture_stdout=True).strip()
|
command_path = call("which", command, env=build_env, capture_stdout=True).strip()
|
||||||
if which != f"{venv_dir}/bin/{command}":
|
if command_path != f"{venv_dir}/bin/{command}":
|
||||||
msg = (
|
msg = (
|
||||||
f"{command} available on PATH doesn't match our installed instance. If you "
|
f"{command} available on PATH doesn't match our installed instance. If you "
|
||||||
f"have modified PATH, ensure that you don't overwrite cibuildwheel's entry "
|
f"have modified PATH, ensure that you don't overwrite cibuildwheel's entry "
|
||||||
@@ -508,17 +508,28 @@ def repair_default(
|
|||||||
new_soname = soname_with_hash(src_path)
|
new_soname = soname_with_hash(src_path)
|
||||||
dst_path = libs_dir / new_soname
|
dst_path = libs_dir / new_soname
|
||||||
shutil.copyfile(src_path, dst_path)
|
shutil.copyfile(src_path, dst_path)
|
||||||
call("patchelf", "--set-soname", new_soname, dst_path)
|
call(which("patchelf"), "--set-soname", new_soname, dst_path)
|
||||||
|
|
||||||
for path in paths_to_patch:
|
for path in paths_to_patch:
|
||||||
call("patchelf", "--replace-needed", old_soname, new_soname, path)
|
call(which("patchelf"), "--replace-needed", old_soname, new_soname, path)
|
||||||
call(
|
call(
|
||||||
"patchelf",
|
which("patchelf"),
|
||||||
"--set-rpath",
|
"--set-rpath",
|
||||||
f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
|
f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
|
||||||
path,
|
path,
|
||||||
)
|
)
|
||||||
call(sys.executable, "-m", "wheel", "pack", unpacked_dir, "-d", repaired_wheel_dir)
|
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]]:
|
def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]:
|
||||||
|
|||||||
@@ -54,10 +54,17 @@ def get_nuget_args(
|
|||||||
@dataclasses.dataclass(frozen=True, kw_only=True)
|
@dataclasses.dataclass(frozen=True, kw_only=True)
|
||||||
class PythonConfiguration:
|
class PythonConfiguration:
|
||||||
version: str
|
version: str
|
||||||
arch: str
|
|
||||||
identifier: str
|
identifier: str
|
||||||
url: str | None = None
|
url: str | None = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arch(self) -> str:
|
||||||
|
return {
|
||||||
|
"win32": "32",
|
||||||
|
"win_amd64": "64",
|
||||||
|
"win_arm64": "ARM64",
|
||||||
|
}[self.identifier.split("-")[-1]]
|
||||||
|
|
||||||
|
|
||||||
def all_python_configurations() -> list[PythonConfiguration]:
|
def all_python_configurations() -> list[PythonConfiguration]:
|
||||||
config_dicts = resources.read_python_configs("windows")
|
config_dicts = resources.read_python_configs("windows")
|
||||||
|
|||||||
@@ -185,37 +185,37 @@ python_configurations = [
|
|||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
{ identifier = "cp38-win32", version = "3.8.10", arch = "32" },
|
{ identifier = "cp38-win32", version = "3.8.10" },
|
||||||
{ identifier = "cp38-win_amd64", version = "3.8.10", arch = "64" },
|
{ identifier = "cp38-win_amd64", version = "3.8.10" },
|
||||||
{ identifier = "cp39-win32", version = "3.9.13", arch = "32" },
|
{ identifier = "cp39-win32", version = "3.9.13" },
|
||||||
{ identifier = "cp39-win_amd64", version = "3.9.13", arch = "64" },
|
{ identifier = "cp39-win_amd64", version = "3.9.13" },
|
||||||
{ identifier = "cp310-win32", version = "3.10.11", arch = "32" },
|
{ identifier = "cp310-win32", version = "3.10.11" },
|
||||||
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
|
{ identifier = "cp310-win_amd64", version = "3.10.11" },
|
||||||
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" },
|
{ identifier = "cp311-win32", version = "3.11.9" },
|
||||||
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
|
{ identifier = "cp311-win_amd64", version = "3.11.9" },
|
||||||
{ identifier = "cp312-win32", version = "3.12.10", arch = "32" },
|
{ identifier = "cp312-win32", version = "3.12.10" },
|
||||||
{ identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
|
{ identifier = "cp312-win_amd64", version = "3.12.10" },
|
||||||
{ identifier = "cp313-win32", version = "3.13.7", arch = "32" },
|
{ identifier = "cp313-win32", version = "3.13.7" },
|
||||||
{ identifier = "cp313t-win32", version = "3.13.7", arch = "32" },
|
{ identifier = "cp313t-win32", version = "3.13.7" },
|
||||||
{ identifier = "cp313-win_amd64", version = "3.13.7", arch = "64" },
|
{ identifier = "cp313-win_amd64", version = "3.13.7" },
|
||||||
{ identifier = "cp313t-win_amd64", version = "3.13.7", arch = "64" },
|
{ identifier = "cp313t-win_amd64", version = "3.13.7" },
|
||||||
{ identifier = "cp314-win32", version = "3.14.0-rc2", arch = "32" },
|
{ identifier = "cp314-win32", version = "3.14.0-rc2" },
|
||||||
{ identifier = "cp314t-win32", version = "3.14.0-rc2", arch = "32" },
|
{ identifier = "cp314t-win32", version = "3.14.0-rc2" },
|
||||||
{ identifier = "cp314-win_amd64", version = "3.14.0-rc2", arch = "64" },
|
{ identifier = "cp314-win_amd64", version = "3.14.0-rc2" },
|
||||||
{ identifier = "cp314t-win_amd64", version = "3.14.0-rc2", arch = "64" },
|
{ identifier = "cp314t-win_amd64", version = "3.14.0-rc2" },
|
||||||
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
|
{ identifier = "cp39-win_arm64", version = "3.9.10" },
|
||||||
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
|
{ identifier = "cp310-win_arm64", version = "3.10.11" },
|
||||||
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
|
{ identifier = "cp311-win_arm64", version = "3.11.9" },
|
||||||
{ identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
|
{ identifier = "cp312-win_arm64", version = "3.12.10" },
|
||||||
{ identifier = "cp313-win_arm64", version = "3.13.7", arch = "ARM64" },
|
{ identifier = "cp313-win_arm64", version = "3.13.7" },
|
||||||
{ identifier = "cp313t-win_arm64", version = "3.13.7", arch = "ARM64" },
|
{ identifier = "cp313t-win_arm64", version = "3.13.7" },
|
||||||
{ identifier = "cp314-win_arm64", version = "3.14.0-rc2", arch = "ARM64" },
|
{ identifier = "cp314-win_arm64", version = "3.14.0-rc2" },
|
||||||
{ identifier = "cp314t-win_arm64", version = "3.14.0-rc2", arch = "ARM64" },
|
{ identifier = "cp314t-win_arm64", version = "3.14.0-rc2" },
|
||||||
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
|
{ identifier = "pp38-win_amd64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
|
||||||
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
|
{ identifier = "pp39-win_amd64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
|
||||||
{ identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
|
{ identifier = "pp310-win_amd64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
|
||||||
{ identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip" },
|
{ identifier = "pp311-win_amd64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip" },
|
||||||
{ identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-windows-amd64.zip" },
|
{ identifier = "gp311_242-win_amd64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-windows-amd64.zip" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[pyodide]
|
[pyodide]
|
||||||
@@ -228,6 +228,8 @@ python_configurations = [
|
|||||||
python_configurations = [
|
python_configurations = [
|
||||||
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.7/python-3.13.7-aarch64-linux-android.tar.gz" },
|
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.7/python-3.13.7-aarch64-linux-android.tar.gz" },
|
||||||
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.7/python-3.13.7-x86_64-linux-android.tar.gz" },
|
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.7/python-3.13.7-x86_64-linux-android.tar.gz" },
|
||||||
|
{ identifier = "cp314-android_arm64_v8a", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc2-aarch64-linux-android.tar.gz" },
|
||||||
|
{ identifier = "cp314-android_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc2-x86_64-linux-android.tar.gz" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[ios]
|
[ios]
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
|
|||||||
| Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 | | | |
|
| Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 | | | |
|
||||||
| Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 | | | cp312-pyodide_wasm32 |
|
| Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 | | | cp312-pyodide_wasm32 |
|
||||||
| Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-android_arm64_v8a<br/>cp313-android_x86_64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
|
| Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-android_arm64_v8a<br/>cp313-android_x86_64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
|
||||||
| Python 3.14 | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | cp314-ios_arm64_iphoneos<br/>cp314-ios_arm64_iphonesimulator<br/>cp314-ios_x86_64_iphonesimulator | |
|
| Python 3.14 | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | cp314-android_arm64_v8a<br/>cp314-android_x86_64 | cp314-ios_arm64_iphoneos<br/>cp314-ios_arm64_iphonesimulator<br/>cp314-ios_x86_64_iphonesimulator | |
|
||||||
| PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 | | | |
|
| PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 | | | |
|
||||||
| PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 | | | |
|
| PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 | | | |
|
||||||
| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 | | | |
|
| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 | | | |
|
||||||
|
|||||||
+8
-5
@@ -205,7 +205,6 @@ It also requires the following commands to be on the `PATH`:
|
|||||||
|
|
||||||
* `curl`
|
* `curl`
|
||||||
* `java` (or set the `JAVA_HOME` environment variable)
|
* `java` (or set the `JAVA_HOME` environment variable)
|
||||||
* `patchelf` (if the wheel links against any external libraries)
|
|
||||||
|
|
||||||
### Android version compatibility
|
### Android version compatibility
|
||||||
|
|
||||||
@@ -214,6 +213,11 @@ 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
|
for generated wheels. This will default to the minimum API level of the selected Python
|
||||||
version.
|
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
|
### Build frontend support
|
||||||
|
|
||||||
Android builds only support the `build` frontend. In principle, support for the
|
Android builds only support the `build` frontend. In principle, support for the
|
||||||
@@ -234,10 +238,9 @@ nested virtualization. CI platforms known to meet this requirement are:
|
|||||||
* GitHub Actions Linux x86_64
|
* GitHub Actions Linux x86_64
|
||||||
|
|
||||||
On Linux, the emulator needs access to the KVM virtualization interface. This may
|
On Linux, the emulator needs access to the KVM virtualization interface. This may
|
||||||
require adding your user to a group, or [changing your udev
|
require adding your user to a group, or changing your udev rules. On GitHub
|
||||||
rules](https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/).
|
Actions, cibuildwheel will do this automatically using the commands shown
|
||||||
If the emulator fails to start, try running `$ANDROID_HOME/emulator/emulator
|
[here](https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/).
|
||||||
-accel-check`.
|
|
||||||
|
|
||||||
The Android test environment can't support running shell scripts, so the
|
The Android test environment can't support running shell scripts, so the
|
||||||
[`test-command`](options.md#test-command) must be a Python command – see its
|
[`test-command`](options.md#test-command) must be a Python command – see its
|
||||||
|
|||||||
@@ -35,8 +35,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
platform: android
|
platform: android
|
||||||
- os: android-arm
|
- os: android-arm
|
||||||
runs-on: macos-latest
|
# GitHub Actions doesn’t currently support the Android emulator on any ARM
|
||||||
|
# runner. So we build on a non-ARM runner, which will skip the tests.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
platform: android
|
platform: android
|
||||||
|
archs: arm64_v8a
|
||||||
- os: ios
|
- os: ios
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
platform: ios
|
platform: ios
|
||||||
@@ -47,23 +50,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
# GitHub Actions can't currently run the Android emulator on macOS.
|
|
||||||
- name: Skip Android tests on macOS
|
|
||||||
if: matrix.os == 'android-arm'
|
|
||||||
run: echo "CIBW_TEST_COMMAND=" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
# https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
|
|
||||||
- name: Enable KVM for Android emulator
|
|
||||||
if: matrix.os == 'android-intel'
|
|
||||||
run: |
|
|
||||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
||||||
sudo udevadm control --reload-rules
|
|
||||||
sudo udevadm trigger --name-match=kvm
|
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v3.1.4
|
uses: pypa/cibuildwheel@v3.1.4
|
||||||
env:
|
env:
|
||||||
CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
|
CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
|
||||||
|
CIBW_ARCHS: ${{ matrix.archs || 'auto' }}
|
||||||
# Can also be configured directly, using `with:`
|
# Can also be configured directly, using `with:`
|
||||||
# with:
|
# with:
|
||||||
# package-dir: .
|
# package-dir: .
|
||||||
|
|||||||
+13
-1
@@ -1,7 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
@@ -353,11 +355,21 @@ def test_libcxx(tmp_path, capfd):
|
|||||||
project_dir = tmp_path / "project"
|
project_dir = tmp_path / "project"
|
||||||
output_dir = tmp_path / "output"
|
output_dir = tmp_path / "output"
|
||||||
|
|
||||||
|
# cibuildwheel should be able to run `patchelf` and `wheel` even when its
|
||||||
|
# environment's `bin` directory is not on the PATH.
|
||||||
|
non_venv_path = ":".join(
|
||||||
|
item for item in os.environ["PATH"].split(":") if Path(item) != Path(sys.executable).parent
|
||||||
|
)
|
||||||
|
|
||||||
# A C++ package should include libc++, and the extension module should be able to
|
# A C++ package should include libc++, and the extension module should be able to
|
||||||
# find it using DT_RUNPATH.
|
# find it using DT_RUNPATH.
|
||||||
new_c_project(setup_py_extension_args_add="language='c++'").generate(project_dir)
|
new_c_project(setup_py_extension_args_add="language='c++'").generate(project_dir)
|
||||||
script = 'import spam; print(", ".join(f"{s}: {spam.filter(s)}" for s in ["ham", "spam"]))'
|
script = 'import spam; print(", ".join(f"{s}: {spam.filter(s)}" for s in ["ham", "spam"]))'
|
||||||
cp313_test_env = {**cp313_env, "CIBW_TEST_COMMAND": f"python -c '{script}'"}
|
cp313_test_env = {
|
||||||
|
**cp313_env,
|
||||||
|
"CIBW_TEST_COMMAND": f"python -c '{script}'",
|
||||||
|
"PATH": non_venv_path,
|
||||||
|
}
|
||||||
|
|
||||||
# Including external libraries requires API level 24.
|
# Including external libraries requires API level 24.
|
||||||
with pytest.raises(CalledProcessError):
|
with pytest.raises(CalledProcessError):
|
||||||
|
|||||||
+1
-3
@@ -100,9 +100,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# The expected wheels were produced.
|
# The expected wheels were produced.
|
||||||
expected_wheels = utils.expected_wheels(
|
expected_wheels = utils.expected_wheels("spam", "0.1.0", platform="ios")
|
||||||
"spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
|
|
||||||
)
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
# The user was notified that the cross-build tool was found.
|
# The user was notified that the cross-build tool was found.
|
||||||
|
|||||||
+14
-3
@@ -261,9 +261,20 @@ def _expected_wheels(
|
|||||||
musllinux_versions = ["musllinux_1_2"]
|
musllinux_versions = ["musllinux_1_2"]
|
||||||
|
|
||||||
if platform == "pyodide" and python_abi_tags is None:
|
if platform == "pyodide" and python_abi_tags is None:
|
||||||
python_abi_tags = ["cp312-cp312", "cp313-cp313"]
|
python_abi_tags = [
|
||||||
elif platform in {"android", "ios"} and python_abi_tags is None:
|
"cp312-cp312",
|
||||||
python_abi_tags = ["cp313-cp313"]
|
"cp313-cp313",
|
||||||
|
]
|
||||||
|
elif platform == "android" and python_abi_tags is None: # noqa: SIM114
|
||||||
|
python_abi_tags = [
|
||||||
|
"cp313-cp313",
|
||||||
|
"cp314-cp314",
|
||||||
|
]
|
||||||
|
elif platform == "ios" and python_abi_tags is None:
|
||||||
|
python_abi_tags = [
|
||||||
|
"cp313-cp313",
|
||||||
|
"cp314-cp314",
|
||||||
|
]
|
||||||
elif python_abi_tags is None:
|
elif python_abi_tags is None:
|
||||||
python_abi_tags = [
|
python_abi_tags = [
|
||||||
"cp38-cp38",
|
"cp38-cp38",
|
||||||
|
|||||||
@@ -27,12 +27,8 @@ def patched_environment(
|
|||||||
|
|
||||||
|
|
||||||
def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
arch = "32"
|
|
||||||
environment: dict[str, str] = {}
|
environment: dict[str, str] = {}
|
||||||
|
configuration = PythonConfiguration(version="irrelevant", identifier="cp314-win32", url=None)
|
||||||
configuration = PythonConfiguration(
|
|
||||||
version="irrelevant", arch=arch, identifier="irrelevant", url=None
|
|
||||||
)
|
|
||||||
|
|
||||||
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
||||||
with patched_environment(monkeypatch, environment):
|
with patched_environment(monkeypatch, environment):
|
||||||
@@ -43,11 +39,9 @@ def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
arch = "64"
|
|
||||||
environment: dict[str, str] = {}
|
environment: dict[str, str] = {}
|
||||||
|
|
||||||
configuration = PythonConfiguration(
|
configuration = PythonConfiguration(
|
||||||
version="irrelevant", arch=arch, identifier="irrelevant", url=None
|
version="irrelevant", identifier="cp313-win_amd64", url=None
|
||||||
)
|
)
|
||||||
|
|
||||||
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
||||||
@@ -62,11 +56,9 @@ def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|||||||
detect_ci_provider() == CIProvider.azure_pipelines, reason="arm64 not recognised on azure"
|
detect_ci_provider() == CIProvider.azure_pipelines, reason="arm64 not recognised on azure"
|
||||||
)
|
)
|
||||||
def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
arch = "ARM64"
|
|
||||||
environment: dict[str, str] = {}
|
environment: dict[str, str] = {}
|
||||||
|
|
||||||
configuration = PythonConfiguration(
|
configuration = PythonConfiguration(
|
||||||
version="irrelevant", arch=arch, identifier="irrelevant", url=None
|
version="irrelevant", identifier="cp312-win_arm64", url=None
|
||||||
)
|
)
|
||||||
|
|
||||||
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
||||||
@@ -78,24 +70,16 @@ def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_env_set(tmp_path: Path) -> None:
|
def test_env_set(tmp_path: Path) -> None:
|
||||||
arch = "32"
|
|
||||||
environment = {"VSCMD_ARG_TGT_ARCH": "x64"}
|
environment = {"VSCMD_ARG_TGT_ARCH": "x64"}
|
||||||
|
configuration = PythonConfiguration(version="irrelevant", identifier="cp313t-win32", url=None)
|
||||||
configuration = PythonConfiguration(
|
|
||||||
version="irrelevant", arch=arch, identifier="irrelevant", url=None
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(FatalError, match="VSCMD_ARG_TGT_ARCH"):
|
with pytest.raises(FatalError, match="VSCMD_ARG_TGT_ARCH"):
|
||||||
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
||||||
|
|
||||||
|
|
||||||
def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
arch = "32"
|
|
||||||
environment = {"VSCMD_ARG_TGT_ARCH": ""}
|
environment = {"VSCMD_ARG_TGT_ARCH": ""}
|
||||||
|
configuration = PythonConfiguration(version="irrelevant", identifier="cp312-win32", url=None)
|
||||||
configuration = PythonConfiguration(
|
|
||||||
version="irrelevant", arch=arch, identifier="irrelevant", url=None
|
|
||||||
)
|
|
||||||
|
|
||||||
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
|
||||||
with patched_environment(monkeypatch, environment):
|
with patched_environment(monkeypatch, environment):
|
||||||
|
|||||||
Reference in New Issue
Block a user