Skip to content

pyproject.toml: build only cp39 wheels per platform in cibuildwheel#39

Merged
RobertRostohar merged 1 commit into
pyocd:mainfrom
o-murphy:fix/cibuildwheel-redundant-builds
Jul 10, 2026
Merged

pyproject.toml: build only cp39 wheels per platform in cibuildwheel#39
RobertRostohar merged 1 commit into
pyocd:mainfrom
o-murphy:fix/cibuildwheel-redundant-builds

Conversation

@o-murphy

@o-murphy o-murphy commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Build wheels

Summary

Restricts cibuildwheel to a single CPython target (cp39-*) per platform/arch, instead of letting it iterate over the full default matrix (cp39...cp314, including free-threaded cp313t/cp314t).

libusb_bdist_wheel.get_tag() in setup.py already forces every wheel's tag to py3-none-<plat>, since the bundled extension never touches the CPython C API. That makes the resulting wheel compatible with any CPython 3.9+ interpreter (python_requires = >=3.9 in setup.cfg), free-threaded builds included — so building it once per platform/arch is enough.

Why

Without an explicit build selector, cibuildwheel provisions and invokes a build for every CPython target in its matrix, which is unnecessary work and makes the CI logs/matrix noisier than the actual output warrants.

Closes: #38

Verification

Ran cibuildwheel locally, scoped to a single platform, with both a regular and a free-threaded target explicitly listed:

CIBW_BUILD="cp39-manylinux_x86_64 cp314t-manylinux_x86_64" CIBW_ARCHS_LINUX=x86_64 cibuildwheel --platform linux
  • cp39-manylinux_x86_64 built normally (~20s).
  • cp314t-manylinux_x86_64 was skipped in ~0.1s: cibuildwheel detected the py3-none-manylinux2014_x86_64... wheel already produced by the cp39 build satisfies it (Found previously built wheel ..., that's compatible with cp314t-manylinux_x86_64. Skipping build step...).

No filename collision, no build failure, and no separate artifact for the free-threaded target — confirming cp39-* alone is sufficient and there's no need to list cp314t-* (or any other version) explicitly.

Full CI run on the fork with the reduced matrix, across Linux/macOS/Windows: https://github.com/o-murphy/libusb-pkg/actions/runs/29026824766

Corresponding pull_request-triggered run on this PR: https://github.com/pyocd/libusb-package/actions/runs/29027101609

Test plan

  • CI (build-wheels.yml) builds successfully on Linux/macOS/Windows with the reduced matrix
  • Published wheel per platform/arch is still tagged py3-none-<plat> and installs correctly on both a regular and a free-threaded (3.13t/3.14t) interpreter

Verified the second item locally with uv: installed the cp39-built wheel from wheelhouse/ into both a CPython 3.12 venv and a genuine free-threaded CPython 3.14.3 venv (sys._is_gil_enabled() == False), then loaded the bundled libusb-1.0.so via ctypes.CDLL and called libusb_get_version() in both — got 1.0.30.12037 in both cases. Confirms the wheel isn't just tag-compatible on paper but actually installs and the native library loads and runs on a free-threaded interpreter it was never built with.

@o-murphy

o-murphy commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

cibuildwheel: before vs. after build = "cp39-*"

Platform Before (build identifiers) Before (time) After (build identifiers) After (time)
ubuntu 28 11 min 4 10 min
windows 14 8 min 2 2 min
macos 14 2 min 2 1 min
Total 56 8

Before: run 28427229151 (last release)
After: run 29027101609 (this PR)

Published wheels (identical in both runs)

ubuntu-latest

  • libusb_package-*-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
  • libusb_package-*-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
  • libusb_package-*-py3-none-musllinux_1_2_x86_64.whl
  • libusb_package-*-py3-none-musllinux_1_2_aarch64.whl

windows-latest

  • libusb_package-*-py3-none-win_amd64.whl
  • libusb_package-*-py3-none-win32.whl

macos-latest

  • libusb_package-*-py3-none-macosx_11_0_arm64.whl
  • libusb_package-*-py3-none-macosx_10_9_x86_64.whl

Why the wheel count didn't change

The number of published wheels is set by the number of unique plat tags (manylinux_x86_64, manylinux_aarch64, musllinux_x86_64, musllinux_aarch64, win_amd64, win32, macosx_x86_64, macosx_arm64) — determined by the archs config, which this change doesn't touch. Hence 8 wheels either way.

The number of build identifiers (cp39-manylinux_x86_64, cp310-manylinux_x86_64, ..., cp314t-manylinux_x86_64, etc.) is how many times cibuildwheel attempts a build per plat. Without a build selector, it iterates over every CPython version for each plat (hence 28 on ubuntu = 4 plats × 7 versions).

Even before this fix, cibuildwheel never actually published duplicates: before each build it checks whether a wheel already in the output directory is tag-compatible with the current identifier. Since the tag is always py3-none-<plat> (compatible with any CPython version), every identifier after the first prints "Skipping build step" and produces nothing new. That's exactly what we confirmed locally with cp314t.

So: before, 28 identifiers on ubuntu → the first (cp39) actually builds, the remaining 24 are instantly skipped, still ending up with 4 files.
After, the identifier list only has 4 entries (one per plat) → all of them actually build, nothing to skip.

What changed isn't the output — it's the path to it. The redundant "check-then-skip" cycles (interpreter provisioning + compatibility check) for ~48 versions that never contributed anything are gone. This was most visible on Windows (8 min → 2 min), where provisioning each Python version was the most expensive part.

@RobertRostohar

Copy link
Copy Markdown
Collaborator

Thank you for providing such a detailed description, solution and testing it. Looks good to me.

@RobertRostohar
RobertRostohar merged commit f7d005c into pyocd:main Jul 10, 2026
3 checks passed
@o-murphy
o-murphy deleted the fix/cibuildwheel-redundant-builds branch July 10, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENHANCEMENT] Сibuildwheel builds redundant per-Python wheels despite forced py3-none tag

2 participants