Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: better cross-platform auto archs #1266

Merged
merged 2 commits into from Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 33 additions & 3 deletions cibuildwheel/architecture.py
Expand Up @@ -3,12 +3,19 @@
import functools
import platform as platform_module
import re
import sys
from enum import Enum

from .typing import Final, Literal, PlatformName, assert_never

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

ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
{"linux": "x86_64", "macos": "x86_64", "windows": "AMD64"},
{"linux": "i686", "macos": None, "windows": "x86"},
{"linux": "aarch64", "macos": "arm64", "windows": "ARM64"},
]


@functools.total_ordering
class Architecture(Enum):
Expand Down Expand Up @@ -56,14 +63,37 @@ def parse_config(config: str, platform: PlatformName) -> set[Architecture]:

@staticmethod
def auto_archs(platform: PlatformName) -> set[Architecture]:
native_architecture = Architecture(platform_module.machine())
native_machine = platform_module.machine()

# Cross-platform support. Used for --print-build-identifiers or docker builds.
host_platform: PlatformName = (
"windows"
if sys.platform.startswith("win")
else ("macos" if sys.platform.startswith("darwin") else "linux")
)

native_architecture = Architecture(native_machine)

# we might need to rename the native arch to the machine we're running
# on, as the same arch can have different names on different platforms
if host_platform != platform:
for arch_synonym in ARCH_SYNONYMS:
if native_machine == arch_synonym.get(host_platform):
synonym = arch_synonym[platform]

if synonym is None:
# can't build anything on this platform
return set()

native_architecture = Architecture(synonym)

result = {native_architecture}

if platform == "linux" and native_architecture == Architecture.x86_64:
if platform == "linux" and Architecture.x86_64 in result:
# x86_64 machines can run i686 containers
result.add(Architecture.i686)

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

return result
Expand Down
82 changes: 82 additions & 0 deletions unit_test/architecture_test.py
@@ -0,0 +1,82 @@
from __future__ import annotations

import platform as platform_module
import sys

import pytest

from cibuildwheel.architecture import Architecture


@pytest.fixture(
params=[
pytest.param(("linux", "linux", "x86_64", "64"), id="linux-64"),
pytest.param(("linux", "linux", "i686", "32"), id="linux-32"),
pytest.param(("linux", "linux", "aarch64", "arm"), id="linux-arm"),
pytest.param(("macos", "darwin", "x86_64", "64"), id="macos-64"),
pytest.param(("macos", "darwin", "arm64", "arm"), id="macos-arm"),
pytest.param(("windows", "win32", "x86", "32"), id="windows-32"),
pytest.param(("windows", "win32", "AMD64", "64"), id="windows-64"),
pytest.param(("windows", "win32", "ARM64", "arm"), id="windows-arm"),
]
)
def platform_machine(request, monkeypatch):
platform_name, platform_value, machine_value, machine_name = request.param
monkeypatch.setattr(sys, "platform", platform_value)
monkeypatch.setattr(platform_module, "machine", lambda: machine_value)
return platform_name, machine_name


def test_arch_auto(platform_machine):
platform_name, machine_name = platform_machine

arch_set = Architecture.auto_archs("linux")
expected = {
"32": {Architecture.i686},
"64": {Architecture.x86_64, Architecture.i686},
"arm": {Architecture.aarch64},
}
assert arch_set == expected[machine_name]

arch_set = Architecture.auto_archs("macos")
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.arm64}}
assert arch_set == expected[machine_name]

arch_set = Architecture.auto_archs("windows")
expected = {
"32": {Architecture.x86},
"64": {Architecture.AMD64, Architecture.x86},
"arm": {Architecture.ARM64},
}
assert arch_set == expected[machine_name]


def test_arch_auto64(platform_machine):
platform_name, machine_name = platform_machine

arch_set = Architecture.parse_config("auto64", "linux")
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.aarch64}}
assert arch_set == expected[machine_name]

arch_set = Architecture.parse_config("auto64", "macos")
expected = {"32": set(), "64": {Architecture.x86_64}, "arm": {Architecture.arm64}}
assert arch_set == expected[machine_name]

arch_set = Architecture.parse_config("auto64", "windows")
expected = {"32": set(), "64": {Architecture.AMD64}, "arm": {Architecture.ARM64}}
assert arch_set == expected[machine_name]


def test_arch_auto32(platform_machine):
platform_name, machine_name = platform_machine

arch_set = Architecture.parse_config("auto32", "linux")
expected = {"32": {Architecture.i686}, "64": {Architecture.i686}, "arm": set()}
assert arch_set == expected[machine_name]

arch_set = Architecture.parse_config("auto32", "macos")
assert arch_set == set()

arch_set = Architecture.parse_config("auto32", "windows")
expected = {"32": {Architecture.x86}, "64": {Architecture.x86}, "arm": set()}
assert arch_set == expected[machine_name]