pyproject.toml: build only cp39 wheels per platform in cibuildwheel#39
Conversation
cibuildwheel: before vs. after
|
| 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.whllibusb_package-*-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whllibusb_package-*-py3-none-musllinux_1_2_x86_64.whllibusb_package-*-py3-none-musllinux_1_2_aarch64.whl
windows-latest
libusb_package-*-py3-none-win_amd64.whllibusb_package-*-py3-none-win32.whl
macos-latest
libusb_package-*-py3-none-macosx_11_0_arm64.whllibusb_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.
|
Thank you for providing such a detailed description, solution and testing it. Looks good to me. |
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-threadedcp313t/cp314t).libusb_bdist_wheel.get_tag()insetup.pyalready forces every wheel's tag topy3-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.9insetup.cfg), free-threaded builds included — so building it once per platform/arch is enough.Why
Without an explicit
buildselector, 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:
cp39-manylinux_x86_64built normally (~20s).cp314t-manylinux_x86_64was skipped in ~0.1s: cibuildwheel detected thepy3-none-manylinux2014_x86_64...wheel already produced by thecp39build 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 listcp314t-*(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/29027101609Test plan
build-wheels.yml) builds successfully on Linux/macOS/Windows with the reduced matrixpy3-none-<plat>and installs correctly on both a regular and a free-threaded (3.13t/3.14t) interpreterVerified the second item locally with
uv: installed thecp39-built wheel fromwheelhouse/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 bundledlibusb-1.0.soviactypes.CDLLand calledlibusb_get_version()in both — got1.0.30.12037in 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.