Skip to content

Commit acf46ed

Browse files
committed
fix: better cross-platform auto archs
This improves the default arch selection when targetting a different platform. Signed-off-by: Henry Schreiner <[email protected]>
1 parent 01504b8 commit acf46ed

File tree

2 files changed

+124
-14
lines changed

2 files changed

+124
-14
lines changed

cibuildwheel/architecture.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,25 @@
33
import functools
44
import platform as platform_module
55
import re
6+
import sys
67
from enum import Enum
78

89
from .typing import Final, Literal, PlatformName, assert_never
910

1011
PRETTY_NAMES: Final = {"linux": "Linux", "macos": "macOS", "windows": "Windows"}
1112

13+
ARCH_CATEGORIES: Final[dict[str, set[str]]] = {
14+
"32": {"i686", "x86"},
15+
"64": {"x86_64", "AMD64"},
16+
"arm": {"ARM64", "aarch64", "arm64"},
17+
}
18+
19+
PLATFORM_CATEGORIES: Final[dict[PlatformName, set[str]]] = {
20+
"linux": {"i686", "x86_64", "aarch64", "ppc64le", "s390x"},
21+
"macos": {"x86_64", "arm64", "universal2"},
22+
"windows": {"x86", "AMD64", "ARM64"},
23+
}
24+
1225

1326
@functools.total_ordering
1427
class Architecture(Enum):
@@ -56,41 +69,56 @@ def parse_config(config: str, platform: PlatformName) -> set[Architecture]:
5669

5770
@staticmethod
5871
def auto_archs(platform: PlatformName) -> set[Architecture]:
59-
native_architecture = Architecture(platform_module.machine())
60-
result = {native_architecture}
72+
native_machine = platform_module.machine()
73+
74+
# Cross-platform support. Used for --print-build-identifiers or docker builds.
75+
host_platform = (
76+
"windows"
77+
if sys.platform.startswith("win")
78+
else ("macos" if sys.platform.startswith("darwin") else "linux")
79+
)
6180

62-
if platform == "linux" and native_architecture == Architecture.x86_64:
81+
result = set()
82+
83+
# Replace native_machine with the matching machine for intel or arm
84+
if host_platform == platform:
85+
native_architecture = Architecture(native_machine)
86+
result.add(native_architecture)
87+
else:
88+
for arch_group in ARCH_CATEGORIES.values():
89+
if native_machine in arch_group:
90+
possible_archs = arch_group & PLATFORM_CATEGORIES[platform]
91+
if len(possible_archs) == 1:
92+
(cross_machine,) = possible_archs
93+
result.add(Architecture(cross_machine))
94+
95+
if platform == "linux" and Architecture.x86_64 in result:
6396
# x86_64 machines can run i686 containers
6497
result.add(Architecture.i686)
6598

66-
if platform == "windows" and native_architecture == Architecture.AMD64:
99+
if platform == "windows" and Architecture.AMD64 in result:
67100
result.add(Architecture.x86)
68101

69102
return result
70103

71104
@staticmethod
72105
def all_archs(platform: PlatformName) -> set[Architecture]:
73106
all_archs_map = {
74-
"linux": {
75-
Architecture.x86_64,
76-
Architecture.i686,
77-
Architecture.aarch64,
78-
Architecture.ppc64le,
79-
Architecture.s390x,
80-
},
81-
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
82-
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
107+
"linux": {Architecture[item] for item in PLATFORM_CATEGORIES["linux"]},
108+
"macos": {Architecture[item] for item in PLATFORM_CATEGORIES["macos"]},
109+
"windows": {Architecture[item] for item in PLATFORM_CATEGORIES["windows"]},
83110
}
84111
return all_archs_map[platform]
85112

86113
@staticmethod
87114
def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> set[Architecture]:
88-
archs_32 = {Architecture.i686, Architecture.x86}
115+
archs_32 = {Architecture[item] for item in ARCH_CATEGORIES["32"]}
89116
auto_archs = Architecture.auto_archs(platform)
90117

91118
if bitness == "64":
92119
return auto_archs - archs_32
93120
elif bitness == "32":
121+
print(auto_archs, archs_32)
94122
return auto_archs & archs_32
95123
else:
96124
assert_never(bitness)

unit_test/architecture_test.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from __future__ import annotations
2+
3+
import platform as platform_module
4+
import sys
5+
6+
import pytest
7+
8+
from cibuildwheel.architecture import Architecture
9+
10+
11+
@pytest.fixture(
12+
params=[
13+
pytest.param(("linux", "linux", "x86_64", "64"), id="linux-64"),
14+
pytest.param(("linux", "linux", "i686", "32"), id="linux-32"),
15+
pytest.param(("linux", "linux", "aarch64", "arm"), id="linux-arm"),
16+
pytest.param(("macos", "darwin", "x86_64", "64"), id="macos-64"),
17+
pytest.param(("macos", "darwin", "arm64", "arm"), id="macos-arm"),
18+
pytest.param(("windows", "win32", "x86", "32"), id="windows-32"),
19+
pytest.param(("windows", "win32", "AMD64", "64"), id="windows-64"),
20+
pytest.param(("windows", "win32", "ARM64", "arm"), id="windows-arm"),
21+
]
22+
)
23+
def platform_machine(request, monkeypatch):
24+
platform_name, platform_value, machine_value, machine_name = request.param
25+
monkeypatch.setattr(sys, "platform", platform_value)
26+
monkeypatch.setattr(platform_module, "machine", lambda: machine_value)
27+
return platform_name, machine_name
28+
29+
30+
def test_arch_auto(platform_machine):
31+
platform_name, machine_name = platform_machine
32+
33+
arch_set = Architecture.auto_archs("linux")
34+
expected = {
35+
"32": {Architecture.i686},
36+
"64": {Architecture.x86_64, Architecture.i686},
37+
"arm": {Architecture.aarch64},
38+
}
39+
assert arch_set == expected[machine_name]
40+
41+
arch_set = Architecture.auto_archs("macos")
42+
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.arm64}}
43+
assert arch_set == expected[machine_name]
44+
45+
arch_set = Architecture.auto_archs("windows")
46+
expected = {
47+
"32": {Architecture.x86},
48+
"64": {Architecture.AMD64, Architecture.x86},
49+
"arm": {Architecture.ARM64},
50+
}
51+
assert arch_set == expected[machine_name]
52+
53+
54+
def test_arch_auto64(platform_machine):
55+
platform_name, machine_name = platform_machine
56+
57+
arch_set = Architecture.parse_config("auto64", "linux")
58+
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.aarch64}}
59+
assert arch_set == expected[machine_name]
60+
61+
arch_set = Architecture.parse_config("auto64", "macos")
62+
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.arm64}}
63+
assert arch_set == expected[machine_name]
64+
65+
arch_set = Architecture.parse_config("auto64", "windows")
66+
expected = {"32": set(), "64": {Architecture.AMD64}, "arm": {Architecture.ARM64}}
67+
assert arch_set == expected[machine_name]
68+
69+
70+
def test_arch_auto32(platform_machine):
71+
platform_name, machine_name = platform_machine
72+
73+
arch_set = Architecture.parse_config("auto32", "linux")
74+
expected = {"32": {Architecture.i686}, "64": {Architecture.i686}, "arm": set()}
75+
assert arch_set == expected[machine_name]
76+
77+
arch_set = Architecture.parse_config("auto32", "macos")
78+
assert arch_set == set()
79+
80+
arch_set = Architecture.parse_config("auto32", "windows")
81+
expected = {"32": {Architecture.x86}, "64": {Architecture.x86}, "arm": set()}
82+
assert arch_set == expected[machine_name]

0 commit comments

Comments
 (0)