diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index 83c52ab8..a1528844 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -152,6 +152,16 @@ def setup_setuptools_cross_compile( # set the platform name map_plat = {"32": "win32", "64": "win-amd64", "ARM64": "win-arm64"} plat_name = map_plat[python_configuration.arch] + + # Set environment variable so that setuptools._distutils.get_platform() + # identifies the target, not the host + vscmd_arg_tgt_arch = {"32": "x86", "64": "x64", "ARM64": "arm64"} + current_tgt_arch = vscmd_arg_tgt_arch[python_configuration.arch] + if (env.get("VSCMD_ARG_TGT_ARCH") or current_tgt_arch) != current_tgt_arch: + msg = f"VSCMD_ARG_TGT_ARCH must be set to {current_tgt_arch!r}, got {env['VSCMD_ARG_TGT_ARCH']!r}. If you're setting up MSVC yourself (e.g. using vcvarsall.bat or msvc-dev-cmd), make sure to target the right architecture. Alternatively, run cibuildwheel without configuring MSVC, and let the build backend handle it." + raise errors.FatalError(msg) + env["VSCMD_ARG_TGT_ARCH"] = current_tgt_arch + # (This file must be default/locale encoding, so we can't pass 'encoding') distutils_cfg.write_text( textwrap.dedent( diff --git a/pyproject.toml b/pyproject.toml index c8f3ca48..94772e9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ test = [ "pytest-timeout", "pytest-xdist", "pytest>=6", + "setuptools", "tomli_w", "validate-pyproject", ] @@ -140,6 +141,8 @@ disallow_untyped_decorators = true [[tool.mypy.overrides]] module = [ "setuptools", + "setuptools._distutils", # needed even if only directly import setuptools._distutils.util + "setuptools._distutils.util", "pytest", # ignored in pre-commit to speed up check "bashlex", "bashlex.*", diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py new file mode 100644 index 00000000..dfcc7d87 --- /dev/null +++ b/unit_test/get_platform_test.py @@ -0,0 +1,92 @@ +import contextlib +import sys +from pathlib import Path +from typing import Dict + +import pytest +import setuptools._distutils.util + +from cibuildwheel.errors import FatalError +from cibuildwheel.util import CIProvider, detect_ci_provider +from cibuildwheel.windows import PythonConfiguration, setup_setuptools_cross_compile + +# monkeypatching os.name is too flaky. E.g. It works on my machine, but fails in pipeline +if not sys.platform.startswith("win"): + pytest.skip("Windows-only tests", allow_module_level=True) + + +@contextlib.contextmanager +def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: Dict[str, str]): + with monkeypatch.context() as mp: + for envvar, val in environment.items(): + mp.setenv(name=envvar, value=val) + yield + + +def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + arch = "32" + environment: Dict[str, str] = {} + + configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + + setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) + with patched_environment(monkeypatch, environment): + target_platform = setuptools._distutils.util.get_platform() + + assert environment["VSCMD_ARG_TGT_ARCH"] == "x86" + assert target_platform == "win32" + + +def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + arch = "64" + environment: Dict[str, str] = {} + + configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + + setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) + with patched_environment(monkeypatch, environment): + target_platform = setuptools._distutils.util.get_platform() + + assert environment["VSCMD_ARG_TGT_ARCH"] == "x64" + assert target_platform == "win-amd64" + + +@pytest.mark.skipif( + detect_ci_provider() == CIProvider.azure_pipelines, reason="arm64 not recognised on azure" +) +def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + arch = "ARM64" + environment: Dict[str, str] = {} + + configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + + setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) + with patched_environment(monkeypatch, environment): + target_platform = setuptools._distutils.util.get_platform() + + assert environment["VSCMD_ARG_TGT_ARCH"] == "arm64" + assert target_platform == "win-arm64" + + +def test_env_set(tmp_path: Path): + arch = "32" + environment = {"VSCMD_ARG_TGT_ARCH": "x64"} + + configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + + with pytest.raises(FatalError, match="VSCMD_ARG_TGT_ARCH"): + setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) + + +def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + arch = "32" + environment = {"VSCMD_ARG_TGT_ARCH": ""} + + configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + + setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) + with patched_environment(monkeypatch, environment): + target_platform = setuptools._distutils.util.get_platform() + + assert environment["VSCMD_ARG_TGT_ARCH"] == "x86" + assert target_platform == "win32"