From b6f325c21a8f75436e4ca2ac13a3cbcf8627ea5e Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Tue, 26 Jan 2021 21:52:02 +0000 Subject: [PATCH] Implement CIBW_TEST_SKIP for individual archs of a universal2 wheel --- cibuildwheel/macos.py | 64 ++++++++++++++++++++++------------------ docs/options.md | 5 ++++ test/test_macos_archs.py | 42 ++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 31 deletions(-) diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 00aca372..666b037e 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -8,6 +8,8 @@ import tempfile from pathlib import Path from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, cast +from typing_extensions import Literal + from .architecture import Architecture from .environment import ParsedEnvironment from .logger import log @@ -342,37 +344,43 @@ def build(options: BuildOptions) -> None: if options.test_command and options.test_selector(config.identifier): machine_arch = platform.machine() - testing_archs: List[str] = [] + testing_archs: List[Literal['x86_64', 'arm64']] = [] - if machine_arch == 'x86_64': - if config.identifier.endswith('_arm64'): - log.warning(unwrap(''' - While arm64 wheels can be built on x86_64, they cannot be tested. The - ability to test the arm64 wheels will be added in a future release of - cibuildwheel, once Apple Silicon CI runners are widely available. - ''')) - testing_archs = [] - elif config.identifier.endswith('_universal2'): - log.warning(unwrap(''' - While universal2 wheels can be built on x86_64, the arm64 part of them - cannot currently be tested. The ability to test the arm64 part of a - universal2 wheel will be added in a future release of cibuildwheel, once - Apple Silicon CI runners are widely available. - ''')) - testing_archs = ['x86_64'] - else: - testing_archs = ['x86_64'] - elif machine_arch == 'arm64': - if config.identifier.endswith('_x86_64'): - # testing using rosetta2 emulation - testing_archs = ['x86_64'] - elif config.identifier.endswith('_universal2'): - # testing the x86_64 using rosetta2 emulation - testing_archs = ['arm64', 'x86_64'] - else: - testing_archs = ['arm64'] + if config.identifier.endswith('_arm64'): + testing_archs = ['arm64'] + elif config.identifier.endswith('_universal2'): + testing_archs = ['x86_64', 'arm64'] + else: + testing_archs = ['x86_64'] for testing_arch in testing_archs: + if config.identifier.endswith('_universal2'): + arch_specific_identifier = f'{config.identifier}:{testing_arch}' + if not options.test_selector(arch_specific_identifier): + continue + + if machine_arch == 'x86_64' and testing_arch == 'arm64': + if config.identifier.endswith('_arm64'): + log.warning(unwrap(''' + While arm64 wheels can be built on x86_64, they cannot be tested. The + ability to test the arm64 wheels will be added in a future release of + cibuildwheel, once Apple Silicon CI runners are widely available. To + silence this warning, set `CIBW_TEST_SKIP: *-macosx_arm64`. + ''')) + elif config.identifier.endswith('_universal2'): + log.warning(unwrap(''' + While universal2 wheels can be built on x86_64, the arm64 part of them + cannot currently be tested. The ability to test the arm64 part of a + universal2 wheel will be added in a future release of cibuildwheel, once + Apple Silicon CI runners are widely available. To silence this warning, + set `CIBW_TEST_SKIP: *-macosx_universal2:arm64`. + ''')) + else: + raise RuntimeError('unreachable') + + # skip this test + continue + log.step('Testing wheel...' if testing_arch == machine_arch else f'Testing wheel on {testing_arch}...') # set up a virtual environment to install and test from, to make sure diff --git a/docs/options.md b/docs/options.md index 1b80c58f..f255cf28 100644 --- a/docs/options.md +++ b/docs/options.md @@ -544,11 +544,16 @@ CIBW_TEST_EXTRAS: test,qt This will skip testing on any identifiers that match the given skip patterns (see [`CIBW_SKIP`](#build-skip)). This can be used to mask out tests for wheels that have missing dependencies upstream that are slow or hard to build, or to mask up slow tests on emulated architectures. +With macOS `universal2` wheels, you can also skip the the individual archs inside the wheel using an `:arch` suffix. For example, `cp39-macosx_universal2:x86_64` or `cp39-macosx_universal2:arm64`. + #### Examples ```yaml # Will avoid testing on emulated architectures CIBW_TEST_SKIP: "*-manylinux_{aarch64,ppc64le,s390x}" + +# Skip trying to test arm64 builds on Intel Macs +CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64" ``` diff --git a/test/test_macos_archs.py b/test/test_macos_archs.py index 45522be0..bd26a42e 100644 --- a/test/test_macos_archs.py +++ b/test/test_macos_archs.py @@ -1,6 +1,6 @@ import platform import subprocess -from typing import Tuple, cast +from typing import Tuple import pytest @@ -22,8 +22,8 @@ def get_xcode_version() -> Tuple[int, int]: lines = output.splitlines() _, version_str = lines[0].split() - version = tuple(int(x) for x in version_str.split('.')) - return cast(Tuple[int, int], version) + version_parts = version_str.split('.') + return (int(version_parts[0]), int(version_parts[1])) def test_cross_compiled_build(tmp_path): @@ -81,3 +81,39 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2): expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' not in w] assert set(actual_wheels) == set(expected_wheels) + + +@pytest.mark.parametrize('skip_arm64_test', [False, True]) +def test_universal2_testing(tmp_path, capfd, skip_arm64_test): + if utils.platform != 'macos': + pytest.skip('this test is only relevant to macos') + if get_xcode_version() < (12, 0): + pytest.skip('this test only works with Xcode 12 or greater') + if platform.machine() != 'x86_64': + pytest.skip('this test only works on x86_64') + + project_dir = tmp_path / 'project' + basic_project.generate(project_dir) + + actual_wheels = utils.cibuildwheel_run(project_dir, add_env={ + 'CIBW_BUILD': 'cp39-*', + 'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''', + 'CIBW_ARCHS': 'universal2', + 'CIBW_TEST_SKIP': '*_universal2:arm64' if skip_arm64_test else '', + }) + + captured = capfd.readouterr() + + if platform.machine() == 'x86_64': + assert 'running tests on x86_64' in captured.out + assert 'running tests on arm64' not in captured.out + + warning_message = 'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested' + if skip_arm64_test: + assert warning_message not in captured.err + else: + assert warning_message in captured.err + + expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' in w] + + assert set(actual_wheels) == set(expected_wheels)