From 3984c0a94fa2398915ebb8de2ac0e1c85c8227e3 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:12:30 +0100 Subject: [PATCH 01/22] Allow CIBW_ENABLE to control the wheels built in testing --- test/conftest.py | 10 ++++++++++ test/test_0_basic.py | 11 +++++++---- test/utils.py | 19 +++++++++++-------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 8c0c9504c..7ae363f26 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,4 +1,5 @@ import json +import os import subprocess from collections.abc import Generator @@ -28,6 +29,15 @@ def pytest_addoption(parser: pytest.Parser) -> None: default=False, help="macOS cp38 uses the universal2 installer", ) + parser.addoption( + "--enable", + action="append", + default=["cypython-prerelease", "cpython-freethreading"], + help="add an EnableGroup to the test run", + ) + + # default to just cpython + os.environ.setdefault("CIBW_ENABLE", "cpython-freethreading cpython-prerelease") def docker_warmup(request: pytest.FixtureRequest) -> None: diff --git a/test/test_0_basic.py b/test/test_0_basic.py index 6a7732031..3b4f19437 100644 --- a/test/test_0_basic.py +++ b/test/test_0_basic.py @@ -61,16 +61,19 @@ def test_sample_build(tmp_path, capfd): logger.step_end() -def test_build_identifiers(tmp_path): +@pytest.mark.parametrize( + "enable_setting", ["", "cpython-prerelease", "pypy", "cpython-freethreading"] +) +def test_build_identifiers(tmp_path, enable_setting, monkeypatch): project_dir = tmp_path / "project" basic_project.generate(project_dir) + monkeypatch.setenv("CIBW_ENABLE", enable_setting) + # check that the number of expected wheels matches the number of build # identifiers expected_wheels = utils.expected_wheels("spam", "0.1.0") - build_identifiers = utils.cibuildwheel_get_build_identifiers( - project_dir, prerelease_pythons=True - ) + build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir) assert len(expected_wheels) == len(build_identifiers), ( f"{expected_wheels} vs {build_identifiers}" ) diff --git a/test/utils.py b/test/utils.py index d999e1b40..716d3c459 100644 --- a/test/utils.py +++ b/test/utils.py @@ -46,7 +46,8 @@ def cibuildwheel_get_build_identifiers( - project_path: Path, env: dict[str, str] | None = None, *, prerelease_pythons: bool = False + project_path: Path, + env: dict[str, str] | None = None, ) -> list[str]: """ Returns the list of build identifiers that cibuildwheel will try to build @@ -55,9 +56,6 @@ def cibuildwheel_get_build_identifiers( cmd = [sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)] if env is None: env = os.environ.copy() - env["CIBW_ENABLE"] = "cpython-freethreading pypy" - if prerelease_pythons: - env["CIBW_ENABLE"] += " cpython-prerelease" cmd_output = subprocess.run( cmd, @@ -121,8 +119,6 @@ def cibuildwheel_run( _update_pip_cache_dir(env) - env["CIBW_ENABLE"] = " ".join(EnableGroup.all_groups()) - if single_python: env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION) @@ -222,6 +218,8 @@ def _expected_wheels( # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel # so we'll merge them below as python_abi_tag + enable_groups = {EnableGroup(e) for e in os.environ["CIBW_ENABLE"].split()} + if manylinux_versions is None: manylinux_versions = { "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"], @@ -242,10 +240,15 @@ def _expected_wheels( "cp311-cp311", "cp312-cp312", "cp313-cp313", - "cp313-cp313t", ] - if machine_arch in ["x86_64", "i686", "AMD64", "aarch64", "arm64"]: + if EnableGroup.CPythonFreeThreading in enable_groups: + python_abi_tags += [ + "cp313-cp313t", + ] + + pypy_archs = ["x86_64", "i686", "AMD64", "aarch64", "arm64"] + if EnableGroup.PyPy in enable_groups and machine_arch in pypy_archs: python_abi_tags += [ "pp38-pypy38_pp73", "pp39-pypy39_pp73", From 4110cc0ebcee337826ca45c56a063b8e91df582d Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:06:36 +0100 Subject: [PATCH 02/22] Set CIBW_ENABLE using PR labels --- .github/workflows/test.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 675831734..383d712ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,11 @@ on: - main - 2.x pull_request: + types: + - opened + - synchronize + - reopened + - labeled paths-ignore: - 'docs/**' - .pre-commit-config.yaml @@ -52,6 +57,15 @@ jobs: - uses: astral-sh/setup-uv@v5 + - uses: joerick/pr-labels-action@v1.0.9 + - name: Set CIBW_ENABLE + run: | + CIBW_ENABLE = "cpython-prerelease cpython-freethreading" + if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then + CIBW_ENABLE += " pypy" + fi + echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV + # free some space to prevent reaching GHA disk space limits - name: Clean docker images if: runner.os == 'Linux' From bff57c2e932a213fc86cf5ac8a1f558aa57d261a Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:10:40 +0100 Subject: [PATCH 03/22] Add docs --- docs/contributing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing.md b/docs/contributing.md index 4eea79599..86cd49bdf 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -82,6 +82,8 @@ A few notes- - Running the macOS integration tests requires _system installs_ of Python from python.org for all the versions that are tested. We won't attempt to install these when running locally, but you can do so manually using the URL in the error message that is printed when the install is not found. +- The 'enable groups' run by default are just 'cpython-prerelease' and 'cpython-freethreading'. You can add other groups like pypy or graalpy by setting the [CIBW_ENABLE](options.md#enable) environment variable. On GitHub PRs, you can add a label to the PR to enable these groups. + #### Running pytest directly More advanced users might prefer to invoke pytest directly. Set up a [dev environment](#setting-up-a-dev-environment), then, From 4c307034ed391b21e819e5b1ad9e7e185a874de4 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:19:57 +0100 Subject: [PATCH 04/22] Build everything on the main branch --- .github/workflows/test.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 383d712ba..231c2fb8b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,10 +59,15 @@ jobs: - uses: joerick/pr-labels-action@v1.0.9 - name: Set CIBW_ENABLE + shell: bash run: | - CIBW_ENABLE = "cpython-prerelease cpython-freethreading" - if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then - CIBW_ENABLE += " pypy" + if [[ "${{ github.ref_name }}" == "main" ]]; then + CIBW_ENABLE="cpython-prerelease cpython-freethreading pypy" + else + CIBW_ENABLE="cpython-prerelease cpython-freethreading" + if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then + CIBW_ENABLE+=" pypy" + fi fi echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV From 2145c864d5df0accd4b3ccb30fa1756432695e3d Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:43:26 +0100 Subject: [PATCH 05/22] Add CIBW_ENABLE=all option This was mostly for use in the `main` building case, because otherwise it's maybe a bit too easy to forget to update this file when adding an enable group --- .github/workflows/test.yml | 2 +- cibuildwheel/options.py | 6 ++++-- cibuildwheel/selector.py | 17 +++++++++++++++++ docs/options.md | 2 +- test/utils.py | 2 +- unit_test/main_tests/main_options_test.py | 9 +++++++++ 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 231c2fb8b..90e09b6a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,7 +62,7 @@ jobs: shell: bash run: | if [[ "${{ github.ref_name }}" == "main" ]]; then - CIBW_ENABLE="cpython-prerelease cpython-freethreading pypy" + CIBW_ENABLE="all" else CIBW_ENABLE="cpython-prerelease cpython-freethreading" if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index b0177e0c9..e2d69b252 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -632,8 +632,10 @@ def globals(self) -> GlobalOptions: "enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND ) try: - enable = {EnableGroup(group) for group in enable_groups.split()} - enable.update(EnableGroup(command_line_group) for command_line_group in args.enable) + enable = { + *EnableGroup.parse_option_value(enable_groups), + *EnableGroup.parse_option_value(" ".join(args.enable)), + } except ValueError as e: msg = f"Failed to parse enable group. {e}. Valid group names are: {', '.join(g.value for g in EnableGroup)}" raise errors.ConfigurationError(msg) from e diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py index 0e9736f53..17eb6cbb2 100644 --- a/cibuildwheel/selector.py +++ b/cibuildwheel/selector.py @@ -37,6 +37,23 @@ class EnableGroup(StrEnum): def all_groups(cls) -> frozenset["EnableGroup"]: return frozenset(cls) + @classmethod + def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]: + """ + Parses a string of space-separated values into a set of EnableGroup + members. The string may contain group names or "all". + """ + result = set() + for group in value.strip().split(): + if group == "all": + return cls.all_groups() + try: + result.add(cls(group)) + except ValueError: + msg = f"Unknown enable group: {group}" + raise ValueError(msg) from None + return frozenset(result) + @dataclass(frozen=True, kw_only=True) class BuildSelector: diff --git a/docs/options.md b/docs/options.md index de4317b4d..4214e6501 100644 --- a/docs/options.md +++ b/docs/options.md @@ -317,7 +317,7 @@ values are: The build identifiers for those variants have a `t` suffix in their `python_tag` (e.g. `cp313t-manylinux_x86_64`). - `pypy`: Enable PyPy. - +- `all`: Enable all of the above. !!! caution `cpython-prerelease` is provided for testing purposes only. It is not diff --git a/test/utils.py b/test/utils.py index 716d3c459..1d0888bab 100644 --- a/test/utils.py +++ b/test/utils.py @@ -218,7 +218,7 @@ def _expected_wheels( # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel # so we'll merge them below as python_abi_tag - enable_groups = {EnableGroup(e) for e in os.environ["CIBW_ENABLE"].split()} + enable_groups = EnableGroup.parse_option_value(os.environ.get("CIBW_ENABLE", "")) if manylinux_versions is None: manylinux_versions = { diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index 48723213b..26807fdfe 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -437,6 +437,15 @@ def test_enable(method, intercepted_build_args, monkeypatch): assert enable_groups == frozenset([EnableGroup.PyPy]) +def test_enable_all(intercepted_build_args, monkeypatch): + monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "all"]) + + main() + + enable_groups = intercepted_build_args.args[0].globals.build_selector.enable + assert enable_groups == EnableGroup.all_groups() + + def test_enable_arg_inherits(intercepted_build_args, monkeypatch): monkeypatch.setenv("CIBW_ENABLE", "pypy") monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"]) From 95366825e22c721619e57b832dbfe6be06572e0f Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 21:49:08 +0100 Subject: [PATCH 06/22] Remove dead code --- test/conftest.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 7ae363f26..f595f90e9 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -29,12 +29,6 @@ def pytest_addoption(parser: pytest.Parser) -> None: default=False, help="macOS cp38 uses the universal2 installer", ) - parser.addoption( - "--enable", - action="append", - default=["cypython-prerelease", "cpython-freethreading"], - help="add an EnableGroup to the test run", - ) # default to just cpython os.environ.setdefault("CIBW_ENABLE", "cpython-freethreading cpython-prerelease") From 5d5e187930bf2ad4c7fca0410fac316a76d00faa Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 22:00:29 +0100 Subject: [PATCH 07/22] Make unit tests robust to the value of CIBW_ENABLE --- unit_test/main_tests/main_options_test.py | 2 ++ unit_test/option_prepare_test.py | 1 + 2 files changed, 3 insertions(+) diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index 26807fdfe..15e412498 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -422,6 +422,8 @@ def test_debug_traceback(monkeypatch, method, capfd): @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"]) def test_enable(method, intercepted_build_args, monkeypatch): + monkeypatch.delenv("CIBW_ENABLE", raising=False) + if method == "command_line": monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy"]) elif method == "env_var": diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py index 4a24190c1..c38470dc1 100644 --- a/unit_test/option_prepare_test.py +++ b/unit_test/option_prepare_test.py @@ -51,6 +51,7 @@ def ignore_context_call(*args, **kwargs): @pytest.mark.usefixtures("mock_build_container", "fake_package_dir") def test_build_default_launches(monkeypatch): monkeypatch.setattr(sys, "argv", [*sys.argv, "--platform=linux"]) + monkeypatch.delenv("CIBW_ENABLE", raising=False) main() From 75d3daf9d41ae50bbba86c90b895b7fba51aed3d Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 13 Apr 2025 22:02:50 +0100 Subject: [PATCH 08/22] Fix tests that explicitly choose pypy --- test/test_abi_variants.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py index ec529849d..103623d8a 100644 --- a/test/test_abi_variants.py +++ b/test/test_abi_variants.py @@ -43,7 +43,8 @@ def test_abi3(tmp_path): add_env={ # free_threaded and PyPy do not have a Py_LIMITED_API equivalent, just build one of those # also limit the number of builds for test performance reasons - "CIBW_BUILD": f"cp39-* cp310-* pp310-* {single_python_tag}-* cp313t-*" + "CIBW_BUILD": f"cp39-* cp310-* pp310-* {single_python_tag}-* cp313t-*", + "CIBW_ENABLE": "all", }, ) @@ -183,6 +184,7 @@ def test_abi_none(tmp_path, capfd): "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test", # limit the number of builds for test performance reasons "CIBW_BUILD": "cp38-* cp{}{}-* cp313t-* pp310-*".format(*utils.SINGLE_PYTHON_VERSION), + "CIBW_ENABLE": "all", }, ) From b0f8650e13179c5d240ff59900008bd5a1d5ad79 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Tue, 15 Apr 2025 18:24:01 +0100 Subject: [PATCH 09/22] Fix test expectation --- test/test_abi_variants.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py index 103623d8a..5c11fb3f8 100644 --- a/test/test_abi_variants.py +++ b/test/test_abi_variants.py @@ -35,33 +35,33 @@ def test_abi3(tmp_path): project_dir = tmp_path / "project" limited_api_project.generate(project_dir) - single_python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION) - # build the wheels actual_wheels = utils.cibuildwheel_run( project_dir, add_env={ # free_threaded and PyPy do not have a Py_LIMITED_API equivalent, just build one of those # also limit the number of builds for test performance reasons - "CIBW_BUILD": f"cp39-* cp310-* pp310-* {single_python_tag}-* cp313t-*", + "CIBW_BUILD": "cp39-* cp310-* pp310-* cp312-* cp313t-*", "CIBW_ENABLE": "all", }, ) # check that the expected wheels are produced - expected_wheels = utils.expected_wheels("spam", "0.1.0") if utils.platform == "pyodide": - # there's only 1 possible configuration for pyodide, the single_python_tag one - expected_wheels = [ - w.replace(f"{single_python_tag}-{single_python_tag}", "cp310-abi3") - for w in expected_wheels - ] + # there's only 1 possible configuration for pyodide, cp312 + expected_wheels = utils.expected_wheels("spam", "0.1.0", python_abi_tags=["cp310-abi3"]) else: - expected_wheels = [ - w.replace("cp310-cp310", "cp310-abi3") - for w in expected_wheels - if "-cp39" in w or "-cp310" in w or "-pp310" in w or "-cp313t" in w - ] + expected_wheels = utils.expected_wheels( + "spam", + "0.1.0", + python_abi_tags=[ + "cp39-cp39", + "cp310-abi3", # <-- ABI3, works with 3.10 and 3.12 + "cp313-cp313t", + "pp310-pypy310_pp73", + ], + ) + assert set(actual_wheels) == set(expected_wheels) From afbc0ca907167330309f322a85a2e51e0acfc65b Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sat, 19 Apr 2025 23:01:56 +0100 Subject: [PATCH 10/22] Don't expect impossible wheels in expected_wheels --- test/utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/utils.py b/test/utils.py index 1d0888bab..5250502cf 100644 --- a/test/utils.py +++ b/test/utils.py @@ -23,6 +23,8 @@ EMULATED_ARCHS: Final[list[str]] = sorted( arch.value for arch in (Architecture.all_archs("linux") - Architecture.auto_archs("linux")) ) +PYPY_ARCHS = ["x86_64", "i686", "AMD64", "aarch64", "arm64"] + SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12) _AARCH64_CAN_RUN_ARMV7: Final[bool] = Architecture.aarch64.value not in EMULATED_ARCHS and { @@ -247,8 +249,7 @@ def _expected_wheels( "cp313-cp313t", ] - pypy_archs = ["x86_64", "i686", "AMD64", "aarch64", "arm64"] - if EnableGroup.PyPy in enable_groups and machine_arch in pypy_archs: + if EnableGroup.PyPy in enable_groups: python_abi_tags += [ "pp38-pypy38_pp73", "pp39-pypy39_pp73", @@ -256,6 +257,9 @@ def _expected_wheels( "pp311-pypy311_pp73", ] + if machine_arch not in PYPY_ARCHS: + python_abi_tags = [tag for tag in python_abi_tags if not tag.startswith("pp")] + if single_python: python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION) python_abi_tags = [ From 512d9e4374a8e504aabdc53319aee0fafb0c1744 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sat, 19 Apr 2025 23:02:21 +0100 Subject: [PATCH 11/22] Simplify logic in expected_wheels --- test/utils.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/utils.py b/test/utils.py index 5250502cf..5d620ba31 100644 --- a/test/utils.py +++ b/test/utils.py @@ -270,13 +270,6 @@ def _expected_wheels( ) ] - if platform == "pyodide": - assert len(python_abi_tags) == 1 - python_abi_tag = python_abi_tags[0] - platform_tag = "pyodide_2024_0_wasm32" - yield f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl" - return - for python_abi_tag in python_abi_tags: platform_tags = [] @@ -321,6 +314,10 @@ def _expected_wheels( if include_universal2: platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2") + + elif platform == "pyodide": + platform_tags = ["pyodide_2024_0_wasm32"] + else: msg = f"Unsupported platform {platform!r}" raise Exception(msg) From 77fd56a9cafcae3055861ce4fe1ae626bcd25f00 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 20:40:00 +0100 Subject: [PATCH 12/22] CircleCI- run with CIBW_ENABLE=all only on the main branch --- .circleci/config.yml | 55 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c8bef6f6..b799c8a52 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,27 @@ -version: 2 +version: 2.1 + +commands: + cibw_prepare_environment: + description: "Prepare the environment for testing." + steps: + - run: + name: Prepare the environment. + command: bash .circleci/prepare.sh + cibw_run_tests: + description: "Runs tests, with CIBW_ENABLE=all on the main branch" + steps: + - run: + name: Test + command: | + if [ "${CIRCLE_BRANCH}" == "main" ]; then + echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." + export CIBW_ENABLE=all + else + echo "INFO: CIBW_ENABLE not set for this branch test run." + fi + + venv/bin/python ./bin/run_tests.py + no_output_timeout: 30m jobs: osx-python3.12: @@ -9,14 +32,8 @@ jobs: PYTHON: python3 steps: - checkout - - - run: - name: Prepare the environment. - command: bash .circleci/prepare.sh - - run: - name: Test. - command: venv/bin/python ./bin/run_tests.py - no_output_timeout: 30m + - cibw_prepare_environment + - cibw_run_tests linux-python3.12: docker: @@ -29,14 +46,8 @@ jobs: steps: - checkout - setup_remote_docker - - - run: - name: Prepare the environment. - command: bash .circleci/prepare.sh - - run: - name: Test. - command: venv/bin/python ./bin/run_tests.py - no_output_timeout: 30m + - cibw_prepare_environment + - cibw_run_tests linux-aarch64: machine: @@ -49,14 +60,8 @@ jobs: PYTEST_ADDOPTS: -k "unit_test or main_tests or test_0_basic or test_docker_images" steps: - checkout - - - run: - name: Prepare the environment. - command: bash .circleci/prepare.sh - - run: - name: Test. - command: venv/bin/python ./bin/run_tests.py - no_output_timeout: 30m + - cibw_prepare_environment + - cibw_run_tests workflows: version: 2 From 1bb7bdf69a3be66208f81a702336d4c8c7aeafa1 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 20:44:46 +0100 Subject: [PATCH 13/22] Azure pipelines - run with CIBW_ENABLE=all on main branch --- azure-pipelines.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index eaf93958e..4ec93c1e0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,6 +16,12 @@ jobs: docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. + if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then + echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." + export CIBW_ENABLE=all + else + echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))." + fi python ./bin/run_tests.py - job: macos_311 @@ -27,6 +33,12 @@ jobs: - bash: | python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. + if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then + echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." + export CIBW_ENABLE=all + else + echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))." + fi python ./bin/run_tests.py - job: windows_311 @@ -39,4 +51,10 @@ jobs: - bash: | python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. + if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then + echo "INFO: Exporting CIBW_ENABLE=all for main branch test run." + export CIBW_ENABLE=all + else + echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))." + fi python ./bin/run_tests.py From cf55018eb8342e8b4d79f6b0fb03833ae07b880e Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 20:48:07 +0100 Subject: [PATCH 14/22] Update gitlab to run CIBW_ENABLE=all on main --- .gitlab-ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecf0361fb..5ee98dd54 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,6 +13,10 @@ linux: # skip all but the basic tests # (comment the below line in a PR to debug a Gitlab-specific issue) PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + variables: + CIBW_ENABLE: "all" script: - curl -sSL https://get.docker.com/ | sh - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all @@ -26,6 +30,10 @@ windows: PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code before_script: - choco install python -y --version 3.12.4 + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + variables: + CIBW_ENABLE: "all" script: - py -m pip install dependency-groups - py -m pip install -e. pytest-custom-exit-code $(py -m dependency_groups test) @@ -37,6 +45,10 @@ macos: image: macos-14-xcode-15 variables: PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + variables: + CIBW_ENABLE: "all" script: - python3 -m pip install dependency-groups - python3 -m dependency_groups test | xargs python3 -m pip install -e. pytest-custom-exit-code From fac47c42f5a76f3a27b85001fce33012e9498ad2 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 20:50:28 +0100 Subject: [PATCH 15/22] Set CIBW_ENABLE=all on travis - it only runs on main anyway --- .travis.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec2365a09..e4d1d72a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,13 +14,17 @@ jobs: - name: Linux | x86_64 + i686 | Python 3.12 python: 3.12 services: docker - env: PYTHON=python + env: + - PYTHON=python + - CIBW_ENABLE=all - name: Linux | arm64 | Python 3.12 python: 3.12 services: docker arch: arm64 - env: PYTHON=python + env: + - PYTHON=python + - CIBW_ENABLE=all - name: Linux | ppc64le | Python 3.12 python: 3.12 @@ -32,6 +36,7 @@ jobs: # skip test_manylinuxXXXX_only, it uses too much disk space # c.f. https://travis-ci.community/t/running-out-of-disk-space-quota-when-using-docker-on-ppc64le/11634 - PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"' + - CIBW_ENABLE=all - name: Windows | x86_64 | Python 3.12 os: windows @@ -40,13 +45,16 @@ jobs: - choco upgrade python3 -y --version 3.12.8 --limit-output --params "/InstallDir:C:\\Python312" env: - PYTHON=C:\\Python312\\python + - CIBW_ENABLE=all - name: Linux | s390x | Python 3.12 python: 3.12 services: docker arch: s390x allow_failure: True - env: PYTHON=python + env: + - PYTHON=python + - CIBW_ENABLE=all install: - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi From f4fe206532ccfcfc198317c60d9769b4964c8fe9 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 22:36:51 +0100 Subject: [PATCH 16/22] Fix job name error on CircleCI --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b799c8a52..13bcbe069 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ commands: no_output_timeout: 30m jobs: - osx-python3.12: + osx-python312: macos: xcode: 15.4.0 resource_class: macos.m1.medium.gen1 @@ -35,7 +35,7 @@ jobs: - cibw_prepare_environment - cibw_run_tests - linux-python3.12: + linux-python312: docker: - image: cimg/python:3.12 environment: @@ -67,6 +67,6 @@ workflows: version: 2 all-tests: jobs: - - osx-python3.12 - - linux-python3.12 + - osx-python312 + - linux-python312 - linux-aarch64 From 848113d9487d8505d9a0dd9dac2aeb02432999eb Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 16:26:41 +0100 Subject: [PATCH 17/22] Fix tests for graalpy --- test/test_abi_variants.py | 1 + test/utils.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py index 7035671e7..62f03850f 100644 --- a/test/test_abi_variants.py +++ b/test/test_abi_variants.py @@ -59,6 +59,7 @@ def test_abi3(tmp_path): "cp310-abi3", # <-- ABI3, works with 3.10 and 3.12 "cp313-cp313t", "pp310-pypy310_pp73", + "graalpy311-graalpy242_311_native", ], ) diff --git a/test/utils.py b/test/utils.py index 7fa3b3340..452d002fb 100644 --- a/test/utils.py +++ b/test/utils.py @@ -24,6 +24,7 @@ arch.value for arch in (Architecture.all_archs("linux") - Architecture.auto_archs("linux")) ) PYPY_ARCHS = ["x86_64", "i686", "AMD64", "aarch64", "arm64"] +GRAALPY_ARCHS = ["x86_64", "AMD64", "aarch64", "arm64"] SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12) @@ -258,6 +259,11 @@ def _expected_wheels( "pp311-pypy311_pp73", ] + if EnableGroup.GraalPy in enable_groups: + python_abi_tags += [ + "graalpy311-graalpy242_311_native", + ] + if machine_arch == "ARM64" and platform == "windows": # no CPython 3.8 on Windows ARM64 python_abi_tags = [t for t in python_abi_tags if not t.startswith("cp38")] @@ -265,6 +271,9 @@ def _expected_wheels( if machine_arch not in PYPY_ARCHS: python_abi_tags = [tag for tag in python_abi_tags if not tag.startswith("pp")] + if machine_arch not in GRAALPY_ARCHS: + python_abi_tags = [tag for tag in python_abi_tags if not tag.startswith("graalpy")] + if single_python: python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION) python_abi_tags = [ From e7d89213a097d496eef07b51188e34d9e04b6411 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 16:27:31 +0100 Subject: [PATCH 18/22] Update the test configuration to use the label --- .github/workflows/test.yml | 7 +++++-- test/conftest.py | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ab3217ab..26d9b63bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,10 +64,13 @@ jobs: if [[ "${{ github.ref_name }}" == "main" ]]; then CIBW_ENABLE="all" else - CIBW_ENABLE="cpython-prerelease cpython-freethreading" + CIBW_ENABLE="cpython-prerelease cpython-freethreading cpython-experimental-riscv64" if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then CIBW_ENABLE+=" pypy" fi + if [[ -n "$GITHUB_PR_LABEL_CI_GRAALPY" ]]; then + CIBW_ENABLE+=" graalpy" + fi fi echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV @@ -99,7 +102,7 @@ jobs: env: CIBW_ARCHS_MACOS: x86_64 universal2 arm64 CIBW_BUILD_FRONTEND: 'build[uv]' - CIBW_ENABLE: "cpython-prerelease cpython-freethreading pypy graalpy" + CIBW_ENABLE: "all" - name: Run a sample build (GitHub Action, only) uses: ./ diff --git a/test/conftest.py b/test/conftest.py index 93697949f..210f91b20 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -31,7 +31,9 @@ def pytest_addoption(parser: pytest.Parser) -> None: ) # default to just cpython - os.environ.setdefault("CIBW_ENABLE", "cpython-freethreading cpython-prerelease") + os.environ.setdefault( + "CIBW_ENABLE", "cpython-freethreading cpython-prerelease cpython-experimental-riscv64" + ) def docker_warmup(request: pytest.FixtureRequest) -> None: From f70db476cb300b8a27d7af75bcc0cba9d7a83adc Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 16:40:21 +0100 Subject: [PATCH 19/22] Remove duplication of default value. Make it affect sample build too --- .github/workflows/test.yml | 8 +++++--- test/conftest.py | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 26d9b63bf..7a2b6a05e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,9 +62,12 @@ jobs: shell: bash run: | if [[ "${{ github.ref_name }}" == "main" ]]; then - CIBW_ENABLE="all" + CIBW_ENABLE=all else - CIBW_ENABLE="cpython-prerelease cpython-freethreading cpython-experimental-riscv64" + # get the default CIBW_ENABLE value from the test module + CIBW_ENABLE=$(python -c 'import sys, test.conftest as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)') + + # if this is a PR, check for labels if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then CIBW_ENABLE+=" pypy" fi @@ -102,7 +105,6 @@ jobs: env: CIBW_ARCHS_MACOS: x86_64 universal2 arm64 CIBW_BUILD_FRONTEND: 'build[uv]' - CIBW_ENABLE: "all" - name: Run a sample build (GitHub Action, only) uses: ./ diff --git a/test/conftest.py b/test/conftest.py index 210f91b20..8c3e15221 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -13,6 +13,9 @@ from .utils import EMULATED_ARCHS, platform +# default to just cpython +DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64" + def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption( @@ -30,10 +33,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: help="macOS cp38 uses the universal2 installer", ) - # default to just cpython - os.environ.setdefault( - "CIBW_ENABLE", "cpython-freethreading cpython-prerelease cpython-experimental-riscv64" - ) + os.environ.setdefault("CIBW_ENABLE", DEFAULT_CIBW_ENABLE) def docker_warmup(request: pytest.FixtureRequest) -> None: From 0169ccbbda80cf0c96c38d2b24e3a72f61b2f688 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 16:52:05 +0100 Subject: [PATCH 20/22] Move the action to after deps are installed --- .github/workflows/test.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7a2b6a05e..d860ef40b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,6 +57,22 @@ jobs: - uses: astral-sh/setup-uv@v6 + # free some space to prevent reaching GHA disk space limits + - name: Clean docker images + if: runner.os == 'Linux' + run: | + docker system prune -a -f + df -h + + # for oci_container unit tests + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + + - name: Install dependencies + run: | + uv sync --no-dev --group test + - uses: joerick/pr-labels-action@v1.0.9 - name: Set CIBW_ENABLE shell: bash @@ -65,7 +81,7 @@ jobs: CIBW_ENABLE=all else # get the default CIBW_ENABLE value from the test module - CIBW_ENABLE=$(python -c 'import sys, test.conftest as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)') + CIBW_ENABLE=$(uv run --no-sync python -c 'import sys, test.conftest as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)') # if this is a PR, check for labels if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then @@ -77,22 +93,6 @@ jobs: fi echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV - # free some space to prevent reaching GHA disk space limits - - name: Clean docker images - if: runner.os == 'Linux' - run: | - docker system prune -a -f - df -h - - # for oci_container unit tests - - name: Set up QEMU - if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v3 - - - name: Install dependencies - run: | - uv sync --no-dev --group test - - name: Generate a sample project run: | uv run --no-sync -m test.test_projects test.test_0_basic.basic_project sample_proj From 9ec25ebc3b7918ce9a19db0e2ec6951715b4eba9 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Wed, 7 May 2025 10:51:23 +0100 Subject: [PATCH 21/22] GraalPy workaround for this assumption --- test/test_0_basic.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/test_0_basic.py b/test/test_0_basic.py index 3b4f19437..d0503c105 100644 --- a/test/test_0_basic.py +++ b/test/test_0_basic.py @@ -1,8 +1,10 @@ +import os import textwrap import pytest from cibuildwheel.logger import Logger +from cibuildwheel.selector import EnableGroup from . import test_projects, utils @@ -38,11 +40,13 @@ def test(tmp_path, build_frontend_env, capfd): expected_wheels = utils.expected_wheels("spam", "0.1.0") assert set(actual_wheels) == set(expected_wheels) - # Verify pip warning not shown - captured = capfd.readouterr() - for stream in (captured.err, captured.out): - assert "WARNING: Running pip as the 'root' user can result" not in stream - assert "A new release of pip available" not in stream + enable_groups = EnableGroup.parse_option_value(os.environ.get("CIBW_ENABLE", "")) + if EnableGroup.GraalPy not in enable_groups: + # Verify pip warning not shown + captured = capfd.readouterr() + for stream in (captured.err, captured.out): + assert "WARNING: Running pip as the 'root' user can result" not in stream + assert "A new release of pip available" not in stream @pytest.mark.skip(reason="to keep test output clean") From 48c32270d04918cf3378a7d36881a0b4007ae1e8 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Wed, 7 May 2025 11:47:24 +0100 Subject: [PATCH 22/22] Make unit test resilient to changing CIBW_ENABLE --- unit_test/main_tests/main_options_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index 3cab6c5cf..731d10adc 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -329,6 +329,7 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo @pytest.mark.usefixtures("platform", "intercepted_build_args", "allow_empty") def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys): monkeypatch.setenv(selector, pattern) + monkeypatch.delenv("CIBW_ENABLE", raising=False) if selector == "CIBW_BUILD": with pytest.raises(SystemExit) as ex: