Skip to content

Commit c93d51e

Browse files
authored
feat: add armv7l in auto_archs when running on aarch64 (pypa#2259)
* feat: add armv7l in auto_archs when running on aarch64 This depends on aarch32 EL0 support and thus is done conditionally. * ci(travis): move to Ubuntu 22.04 / cp312
1 parent c738289 commit c93d51e

14 files changed

+96
-50
lines changed

.travis.yml

+13-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
os: linux
2-
dist: focal
2+
dist: jammy
33
language: python
44

55
branches:
@@ -8,29 +8,19 @@ branches:
88

99
jobs:
1010
include:
11-
- name: Linux | x86_64 + i686 | Python 3.11
12-
python: 3.11
11+
- name: Linux | x86_64 + i686 | Python 3.12
12+
python: 3.12
1313
services: docker
1414
env: PYTHON=python
1515

16-
- name: Linux | arm64 | Python 3.11
17-
python: 3.11
16+
- name: Linux | arm64 | Python 3.12
17+
python: 3.12
1818
services: docker
19-
arch: arm64-graviton2
20-
group: edge
21-
virt: vm
19+
arch: arm64
2220
env: PYTHON=python
23-
# docker is outdated in the arm64-graviton2 vm focal image (19.x)
24-
# we need to upgrade to get >= 24.0
25-
addons:
26-
apt:
27-
sources:
28-
- sourceline: 'deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable'
29-
packages:
30-
- docker-ce docker-ce-cli containerd.io
3121

32-
- name: Linux | ppc64le | Python 3.11
33-
python: 3.11
22+
- name: Linux | ppc64le | Python 3.12
23+
python: 3.12
3424
services: docker
3525
arch: ppc64le
3626
allow_failure: True
@@ -40,16 +30,16 @@ jobs:
4030
# c.f. https://travis-ci.community/t/running-out-of-disk-space-quota-when-using-docker-on-ppc64le/11634
4131
- PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"'
4232

43-
- name: Windows | x86_64 | Python 3.11
33+
- name: Windows | x86_64 | Python 3.12
4434
os: windows
4535
language: shell
4636
before_install:
47-
- choco upgrade python3 -y --version 3.11.9 --limit-output --params "/InstallDir:C:\\Python311"
37+
- choco upgrade python3 -y --version 3.12.8 --limit-output --params "/InstallDir:C:\\Python312"
4838
env:
49-
- PYTHON=C:\\Python311\\python
39+
- PYTHON=C:\\Python312\\python
5040

51-
- name: Linux | s390x | Python 3.11
52-
python: 3.11
41+
- name: Linux | s390x | Python 3.12
42+
python: 3.12
5343
services: docker
5444
arch: s390x
5545
allow_failure: True

CI.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
This is a summary of the host Python versions and platforms covered by the different CI platforms:
22

3-
| | 3.11 | 3.12 | 3.13 |
4-
|---------|----------------------------------------------|---------------------------------------------|----------------|
5-
| Linux | Azure Pipelines / GitHub Actions / Travis CI | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions |
6-
| macOS | Azure Pipelines / GitLab¹ | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions |
7-
| Windows | Azure Pipelines / Travis CI | AppVeyor¹ / Cirrus CI / GitLab¹ | GitHub Actions |
3+
| | 3.11 | 3.12 | 3.13 |
4+
|---------|----------------------------------|---------------------------------------------------------|----------------|
5+
| Linux | Azure Pipelines / GitHub Actions | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ / Travis CI | GitHub Actions |
6+
| macOS | Azure Pipelines | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions |
7+
| Windows | Azure Pipelines | AppVeyor¹ / Cirrus CI / GitLab¹ / Travis CI | GitHub Actions |
88

99
> ¹ Runs a reduced set of tests to reduce CI load
1010
11-
Non-x86 architectures are covered on Travis CI using Python 3.11.
11+
Non-x86 architectures are covered on Travis CI using Python 3.12.

cibuildwheel/architecture.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import functools
44
import platform as platform_module
55
import re
6+
import shutil
7+
import subprocess
68
import sys
79
from collections.abc import Set
810
from enum import Enum
@@ -24,6 +26,19 @@
2426
]
2527

2628

29+
def _check_aarch32_el0() -> bool:
30+
"""Check if running armv7l natively on aarch64 is supported"""
31+
if not sys.platform.startswith("linux"):
32+
return False
33+
if platform_module.machine() != "aarch64":
34+
return False
35+
executable = shutil.which("linux32")
36+
if executable is None:
37+
return False
38+
check = subprocess.run([executable, "uname", "-m"], check=False, capture_output=True, text=True)
39+
return check.returncode == 0 and check.stdout.startswith("armv")
40+
41+
2742
@functools.total_ordering
2843
class Architecture(Enum):
2944
value: str
@@ -114,9 +129,12 @@ def auto_archs(platform: PlatformName) -> set[Architecture]:
114129
return set() # can't build anything on this platform
115130
result = {native_arch}
116131

117-
if platform == "linux" and Architecture.x86_64 in result:
118-
# x86_64 machines can run i686 containers
119-
result.add(Architecture.i686)
132+
if platform == "linux":
133+
if Architecture.x86_64 in result:
134+
# x86_64 machines can run i686 containers
135+
result.add(Architecture.i686)
136+
elif Architecture.aarch64 in result and _check_aarch32_el0():
137+
result.add(Architecture.armv7l)
120138

121139
if platform == "windows" and Architecture.AMD64 in result:
122140
result.add(Architecture.x86)

examples/github-with-qemu.yml

+1-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ jobs:
2626
# configure cibuildwheel on Linux to build native archs ('auto'),
2727
# and to split the remaining architectures between the x86_64 and
2828
# ARM runners
29-
# armv7l can be built without QEMU on GitHub Actions ARM runners but that's
30-
# not the case on all ARM64 hardware hence 'auto armv7l' for native archs
31-
# on the GHA ARM64 runner
32-
CIBW_ARCHS_LINUX: ${{ runner.arch == 'X64' && 'auto ppc64le s390x' || 'auto armv7l' }}
29+
CIBW_ARCHS_LINUX: ${{ runner.arch == 'X64' && 'auto ppc64le s390x' || 'auto' }}
3330

3431
- uses: actions/upload-artifact@v4
3532
with:

examples/travis-ci-deploy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# commit, but will only push to PyPI on tagged commits.
33

44
os: linux
5-
dist: focal
5+
dist: jammy
66
language: python
77
python: "3.12"
88

examples/travis-ci-minimal.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
os: linux
2-
dist: focal
2+
dist: jammy
33
language: python
44
python: "3.12"
55

examples/travis-ci-test-and-deploy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# distribution is also created.
77

88
os: linux
9-
dist: focal
9+
dist: jammy
1010
language: python
1111
python: "3.12"
1212

test/test_manylinuxXXXX_only.py

+7
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def test(manylinux_image, tmp_path):
108108
if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
109109
# We don't have a manylinux_2_28+ image for i686
110110
add_env["CIBW_ARCHS"] = "x86_64"
111+
if platform.machine() == "aarch64":
112+
# We just have a manylinux_2_31 image for armv7l
113+
add_env["CIBW_ARCHS"] = "aarch64"
111114

112115
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
113116

@@ -150,4 +153,8 @@ def test(manylinux_image, tmp_path):
150153
# We don't have a manylinux_2_28+ image for i686
151154
expected_wheels = [w for w in expected_wheels if "i686" not in w]
152155

156+
if platform.machine() == "aarch64":
157+
# We just have a manylinux_2_31 image for armv7l
158+
expected_wheels = [w for w in expected_wheels if "armv7l" not in w]
159+
153160
assert set(actual_wheels) == set(expected_wheels)

test/test_musllinux_X_Y_only.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test(musllinux_image, tmp_path):
3838

3939
# build the wheels
4040
add_env = {
41-
"CIBW_SKIP": "*-manylinux*",
41+
"CIBW_SKIP": "*-manylinux* *_armv7l",
4242
"CIBW_MUSLLINUX_X86_64_IMAGE": musllinux_image,
4343
"CIBW_MUSLLINUX_I686_IMAGE": musllinux_image,
4444
"CIBW_MUSLLINUX_AARCH64_IMAGE": musllinux_image,
@@ -54,4 +54,5 @@ def test(musllinux_image, tmp_path):
5454
musllinux_versions=[musllinux_image],
5555
single_python=True,
5656
)
57+
expected_wheels = [w for w in expected_wheels if "armv7l" not in w]
5758
assert set(actual_wheels) == set(expected_wheels)

test/test_testing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_uname(self):
6868
# See #336 for more info.
6969
bits = struct.calcsize("P") * 8
7070
if bits == 32:
71-
self.assertIn(platform.machine(), ["i686", "wasm32"])
71+
self.assertIn(platform.machine(), ["i686", "armv7l","armv8l", "wasm32"])
7272
'''
7373

7474

test/utils.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
import pytest
1919

2020
from cibuildwheel.architecture import Architecture
21+
from cibuildwheel.ci import CIProvider, detect_ci_provider
2122
from cibuildwheel.util.file import CIBW_CACHE_PATH
2223

2324
EMULATED_ARCHS: Final[list[str]] = sorted(
2425
arch.value for arch in (Architecture.all_archs("linux") - Architecture.auto_archs("linux"))
2526
)
2627
SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12)
2728

29+
_AARCH64_CAN_RUN_ARMV7: Final[bool] = Architecture.aarch64.value not in EMULATED_ARCHS and {
30+
None: Architecture.armv7l.value not in EMULATED_ARCHS,
31+
CIProvider.travis_ci: False,
32+
}.get(detect_ci_provider(), True)
33+
2834
platform = os.environ.get("CIBW_PLATFORM", "")
2935
if platform:
3036
pass
@@ -173,7 +179,7 @@ def expected_wheels(
173179
machine_arch = "aarch64"
174180

175181
if manylinux_versions is None:
176-
if machine_arch == "armv7l":
182+
if machine_arch in ("armv7l", "aarch64"):
177183
manylinux_versions = ["manylinux_2_17", "manylinux2014", "manylinux_2_31"]
178184
elif machine_arch == "x86_64":
179185
manylinux_versions = [
@@ -252,14 +258,23 @@ def expected_wheels(
252258
if platform == "linux":
253259
architectures = [arch_name_for_linux(machine_arch)]
254260

255-
if machine_arch == "x86_64" and not single_arch:
256-
architectures.append("i686")
261+
if not single_arch:
262+
if machine_arch == "x86_64":
263+
architectures.append("i686")
264+
elif (
265+
machine_arch == "aarch64"
266+
and sys.platform.startswith("linux")
267+
and not python_abi_tag.startswith("pp")
268+
and _AARCH64_CAN_RUN_ARMV7
269+
):
270+
architectures.append("armv7l")
257271

258272
if len(manylinux_versions) > 0:
259273
platform_tags = [
260274
".".join(
261275
f"{manylinux_version}_{architecture}"
262276
for manylinux_version in manylinux_versions
277+
if (manylinux_version, architecture) != ("manylinux_2_31", "aarch64")
263278
)
264279
for architecture in architectures
265280
]

unit_test/architecture_test.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import annotations
22

33
import platform as platform_module
4+
import shutil
45
import sys
56

67
import pytest
78

9+
import cibuildwheel.architecture
810
from cibuildwheel.architecture import Architecture
911

1012

@@ -24,6 +26,7 @@ def platform_machine(request, monkeypatch):
2426
platform_name, platform_value, machine_value, machine_name = request.param
2527
monkeypatch.setattr(sys, "platform", platform_value)
2628
monkeypatch.setattr(platform_module, "machine", lambda: machine_value)
29+
monkeypatch.setattr(cibuildwheel.architecture, "_check_aarch32_el0", lambda: True)
2730
return platform_name, machine_name
2831

2932

@@ -34,7 +37,7 @@ def test_arch_auto(platform_machine):
3437
expected = {
3538
"32": {Architecture.i686},
3639
"64": {Architecture.x86_64, Architecture.i686},
37-
"arm": {Architecture.aarch64},
40+
"arm": {Architecture.aarch64, Architecture.armv7l},
3841
}
3942
assert arch_set == expected[machine_name]
4043

@@ -71,7 +74,7 @@ def test_arch_auto32(platform_machine):
7174
platform_name, machine_name = platform_machine
7275

7376
arch_set = Architecture.parse_config("auto32", "linux")
74-
expected = {"32": {Architecture.i686}, "64": {Architecture.i686}, "arm": set()}
77+
expected = {"32": {Architecture.i686}, "64": {Architecture.i686}, "arm": {Architecture.armv7l}}
7578
assert arch_set == expected[machine_name]
7679

7780
arch_set = Architecture.parse_config("auto32", "macos")
@@ -80,3 +83,18 @@ def test_arch_auto32(platform_machine):
8083
arch_set = Architecture.parse_config("auto32", "windows")
8184
expected = {"32": {Architecture.x86}, "64": {Architecture.x86}, "arm": set()}
8285
assert arch_set == expected[machine_name]
86+
87+
88+
def test_arch_auto_no_aarch32(monkeypatch):
89+
monkeypatch.setattr(sys, "platform", "linux")
90+
monkeypatch.setattr(platform_module, "machine", lambda: "aarch64")
91+
monkeypatch.setattr(shutil, "which", lambda *args, **kwargs: None)
92+
93+
arch_set = Architecture.parse_config("auto", "linux")
94+
assert arch_set == {Architecture.aarch64}
95+
96+
arch_set = Architecture.parse_config("auto64", "linux")
97+
assert arch_set == {Architecture.aarch64}
98+
99+
arch_set = Architecture.parse_config("auto32", "linux")
100+
assert len(arch_set) == 0

unit_test/main_tests/conftest.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pytest
1010

11-
from cibuildwheel import __main__, linux, macos, pyodide, windows
11+
from cibuildwheel import __main__, architecture, linux, macos, pyodide, windows
1212
from cibuildwheel.util import file
1313

1414

@@ -44,8 +44,8 @@ def ignore_call(*args, **kwargs):
4444
monkeypatch.setattr(linux, "build", fail_on_call)
4545
monkeypatch.setattr(macos, "build", fail_on_call)
4646
monkeypatch.setattr(pyodide, "build", fail_on_call)
47-
4847
monkeypatch.setattr(Path, "mkdir", ignore_call)
48+
monkeypatch.setattr(architecture, "_check_aarch32_el0", lambda: True)
4949

5050

5151
@pytest.fixture(autouse=True)

unit_test/oci_container_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ def test_local_image(
555555
) -> None:
556556
if (
557557
detect_ci_provider() in {CIProvider.travis_ci}
558-
and pm in {"s390x", "ppc64le"}
558+
and pm != "x86_64"
559559
and platform != DEFAULT_OCI_PLATFORM
560560
):
561561
pytest.skip("Skipping test because docker on this platform does not support QEMU")
@@ -585,7 +585,7 @@ def test_local_image(
585585
def test_multiarch_image(container_engine, platform):
586586
if (
587587
detect_ci_provider() in {CIProvider.travis_ci}
588-
and pm in {"s390x", "ppc64le"}
588+
and pm != "x86_64"
589589
and platform != DEFAULT_OCI_PLATFORM
590590
):
591591
pytest.skip("Skipping test because docker on this platform does not support QEMU")

0 commit comments

Comments
 (0)