* Fix a typo: pyoodide ➡️ pyodide * Add `pyodide_build_version` attribute * Add version to xbuildenv log step * Add version to Emscripten log step * Use `pyodide-build`'s version for updating constraints * Bump Pyodide constraints by updating `pyodide-build` * Add a schema for `pyodide-version` * Update Pyodide constraints * Bump `pyodide-build` to new 0.29.0 * Test out another Pyodide identifier * Update outdated Pyodide constraints * Add Pyodide version to temp directory name * Remove Pyodide 0.26.1 from build configurations * Retrieve + validate + install specific xbuildenvs * Test wheel builds with Pyodide 0.26.2 * Add correct Pyodide version to identifier temp dir * Don't pre-call Pyodide xbuildenv search * Fetch just the stable Pyodide versions * Refactor search + validation + install into one step * Move all of it under a lock * Reorder xbuildenv installation * Add env and cwd to xbuildenv search call * Temporarily lower to 0.26.2 target * Separate out search, validate, install; again * Run xbuildenv search in `CIBW_CACHE_PATH` * Remove prior `PYODIDE_ROOT` env vars, copy envs * Validate doesn't need to depend on searching * Add file lock when searching xbuildenvs * Test the original version: 0.26.1 * Update Pyodide constraints * Update constraints for `pyodide-build` 0.29.0 again * Bump Pyodide from version 0.26.1 ➡️ version 0.26.4 * Add note on compatibility for macOS + other archs * Note Pyodide version for Pyodide identifier * Docs about `CIBW_PYODIDE_VERSION` * Don't fetch just the stable versions * Discard a variable that's not used later * Rename `search_xbuildenv` ➡️ `get_xbuildenv_versions` * `validate_xbuildenv` ➡️ `validate_xbuildenv_version` * Replace ordered comment, add newline * Replace sentence on macOS support Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com> * Capitalise: "pyodide" ➡️ "Pyodide" Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com> * "work" ➡️ "may succeed" Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com> * Add another job to test a custom Pyodide version * Handle "v"-prefixed + non-prefixed versions * Convert to a proper toml-able option, and remove some hardcoded versions This removes the enscripten and pyodide-build version specs from pyproject.toml - pyodide-build is spec'd in the constraints file, and the emscripten version can be read from the pyodide-build output. * Add a schema entry * Add docs for CIBW_PYODIDE_VERSION * Rephrase * Add tests for pyodide-version * Apply suggestions from code review * Add python_build_standalone util * Hook up to python-build-standalone, removing dependency on host python * Remove python hard-code in action.yml * Add log step * Add workaround for https://github.com/pyodide/pyodide-build/issues/143 * Add emscripten pytest test * Remove unneeded checks * Fix a pytest invoke for emscripten * Generate pyodide-build constraints from the pinned pyodide version * Remove pyodide python-build-standalone workaround * Fixup paths from newer version of pyodide-build * Fix/skip some failing tests * Use `python -m pytest` on pyodide, even on Linux * Docs fixes * Don't call the github API at runtime, cache the release assets instead * Ignore pylint false positive * Add version auto-updating for pyodide * Add support for pyodide 3.13. * Fix tests for multiple pyodide wheels * Remove workaround for unreleased pyodide-build * Rename to "test_pyodide" * Fix pathname confusion * Remove extra github actions job * Fix expectation for test_abi_none * Fix the custom_repair_wheel test to actually have clashing names * Fix pinned version test * Document test-command limitation * Remove pyodide 0.28.0a1 for now * Update constraints files * Docs/test fixes post removing pyodide cp313 * Fix ABI test expectation * Docs improvements * Improve some comments * Remove logic duplication * remove pyodide special casing * Refactor constraints code to use a utility script, circumventing import issues * chore: nicer nox env Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * fix: typo in variable name found by copilot Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * Apply suggestions from code review * Some more Pyodide version updates in the docs section * We haven't released Pyodide v0.27.6 yet * Back to the Github URL for cross-build-environments * `pyodide-build`, not `emsdk` for Windows skips Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com> * Move to a separate `_json_request` function Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com> * Rename "retries" ➡️ "retry_count" * Add some type hints * Copy env vars before `UV_CUSTOM_COMPILE_COMMAND` * Use `HTTPError.headers.get` instead * Remove extra end quote * Change download tests URL to `https://badssl.com/` Co-Authored-By: Joe Rickerby <1244307+joerick@users.noreply.github.com> * Download size changes, too * Use jsdelivr for Github asset mirroring * Bump to Pyodide v0.27.6 * Fix unit tests * Move to pyodide v0.27.6 again * Bump to pyodide-build 0.30.4 * Use new URL for cross-build environments metadata Co-authored-by: Joe Rickerby <joerick@mac.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com> Co-authored-by: Joe Rickerby <joerick@mac.com> Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: Joe Rickerby <1244307+joerick@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
cibuildwheel
Python wheels are great. Building them across Mac, Linux, Windows, on multiple versions of Python, is not.
cibuildwheel is here to help. cibuildwheel runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
What does it do?
While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
| macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux musllinux x86_64 |
manylinux musllinux i686 |
manylinux musllinux aarch64 |
manylinux musllinux ppc64le |
manylinux musllinux s390x |
manylinux musllinux armv7l |
iOS | Pyodide | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | ✅⁴ |
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | N/A |
| CPython 3.14³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | N/A |
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A |
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A |
| PyPy 3.11 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A | N/A |
| GraalPy 24.2 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | N/A | ✅¹ | N/A | N/A | N/A | N/A | N/A |
¹ PyPy & GraalPy are only supported for manylinux wheels.
² Windows arm64 support is experimental.
³ Free-threaded mode requires opt-in using CIBW_ENABLE.
⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use --platform pyodide to build.
⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.
- Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython, PyPy, and GraalPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, CircleCI, GitLab CI, and Cirrus CI
- Bundles shared library dependencies on Linux and macOS through auditwheel and delocate
- Runs your library's tests against the wheel-installed version of your library
See the cibuildwheel 1 documentation if you need to build unsupported versions of Python, such as Python 2.
Usage
cibuildwheel runs inside a CI service. Supported platforms depend on which service you're using:
| Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS | |
|---|---|---|---|---|---|---|---|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ | |
| Travis CI | ✅ | ✅ | ✅ | ||||
| CircleCI | ✅ | ✅ | ✅ | ✅ | ✅³ | ||
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | ✅³ | |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅³ |
¹ Requires emulation, distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.
² Uses cross-compilation. It is not possible to test arm64 on this CI platform.
³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.
Example setup
To build manylinux, musllinux, macOS, and Windows wheels on GitHub Actions, you could use this .github/workflows/wheels.yml:
name: Build
on: [push, pull_request]
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest]
steps:
- uses: actions/checkout@v4
# Used to host cibuildwheel
- uses: actions/setup-python@v5
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==3.0.0b1
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
# to supply options, put them in 'env', like:
# env:
# CIBW_SOME_OPTION: value
# ...
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
For more information, including PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the documentation and the examples.
How it works
The following diagram summarises the steps that cibuildwheel takes on each platform.
Explore an interactive version of this diagram in the docs.
Options
| Option | Description | |
|---|---|---|
| Build selection | CIBW_PLATFORM |
Override the auto-detected target platform |
CIBW_BUILD CIBW_SKIP |
Choose the Python versions to build | |
CIBW_ARCHS |
Change the architectures built on your machine by default. | |
CIBW_PROJECT_REQUIRES_PYTHON |
Manually set the Python compatibility of your project | |
CIBW_PRERELEASE_PYTHONS |
Enable building with pre-release versions of Python if available | |
| Build customization | CIBW_BUILD_FRONTEND |
Set the tool to use to build, either "pip" (default for now) or "build" |
CIBW_ENVIRONMENT |
Set environment variables needed during the build | |
CIBW_ENVIRONMENT_PASS_LINUX |
Set environment variables on the host to pass-through to the container during the build. | |
CIBW_BEFORE_ALL |
Execute a shell command on the build system before any wheels are built. | |
CIBW_BEFORE_BUILD |
Execute a shell command preparing each wheel's build | |
CIBW_XBUILD_TOOLS |
Binaries on the path that should be included in an isolated cross-build environment. | |
CIBW_REPAIR_WHEEL_COMMAND |
Execute a shell command to repair each built wheel | |
CIBW_MANYLINUX_*_IMAGECIBW_MUSLLINUX_*_IMAGE |
Specify alternative manylinux / musllinux Docker images | |
CIBW_CONTAINER_ENGINE |
Specify which container engine to use when building Linux wheels | |
CIBW_DEPENDENCY_VERSIONS |
Specify how cibuildwheel controls the versions of the tools it uses | |
| Testing | CIBW_TEST_COMMAND |
Execute a shell command to test each built wheel |
CIBW_BEFORE_TEST |
Execute a shell command before testing each wheel | |
CIBW_TEST_SOURCES |
Files and folders from the source tree that are copied into an isolated tree before running the tests | |
CIBW_TEST_REQUIRES |
Install Python dependencies before running the tests | |
CIBW_TEST_EXTRAS |
Install your wheel for testing using extras_require | |
CIBW_TEST_SKIP |
Skip running tests on some builds | |
| Other | CIBW_BUILD_VERBOSITY |
Increase/decrease the output of pip wheel |
These options can be specified in a pyproject.toml file, as well; see configuration.
Working examples
Here are some repos that use cibuildwheel.
| Name | CI | OS | Notes |
|---|---|---|---|
| scikit-learn | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions. | ||
| pytorch-fairseq | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. | ||
| NumPy | The fundamental package for scientific computing with Python. | ||
| duckdb | DuckDB is an analytical in-process SQL database management system | ||
| Tornado | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. | ||
| NCNN | ncnn is a high-performance neural network inference framework optimized for the mobile platform | ||
| Matplotlib | The venerable Matplotlib, a Python library with C++ portions | ||
| MyPy | The compiled version of MyPy using MyPyC. | ||
| Prophet | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. | ||
| Kivy | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
ℹ️ That's just a handful, there are many more! Check out the Working Examples page in the docs.
Legal note
Since cibuildwheel repairs the wheel with delocate or auditwheel, it might automatically bundle dynamically linked libraries from the build machine.
It helps ensure that the library can run without any dependencies outside of the pip toolchain.
This is similar to static linking, so it might have some license implications. Check the license for any code you're pulling in to make sure that's allowed.
Changelog
v3.0.0
Not yet released, but available for testing.
Note - when using a beta version, be sure to check the latest docs, rather than the stable version, which is still on v2.X.
v3.0.0b1
19 May 2025
-
🌟 Adds the ability to build wheels for iOS! Set the
platformoption toioson a Mac with the iOS toolchain to try it out! -
🌟 Adds support for the GraalPy interpreter! Enable for your project using the
enableoption. (#1538) -
✨ Adds CPython 3.14 support, under the
enableoptioncpython-prerelease. This version of cibuildwheel uses 3.14.0b1.While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag. (#2390)
-
✨ Adds the test-sources option. [discussion about the test cwd change and how to use to come!]
-
✨ Added
dependency-versionsinline syntax (#2123) -
🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
-
🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
-
⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the
enableoption topypyorpypy-eol. -
⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
-
📚 A reorganisation of the docs, and numerous updates (#2280)
v2.23.3
26 April 2025
- 🛠 Dependency updates, including Python 3.13.3 (#2371)
v2.23.2
24 March 2025
- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
- 🛠 Dependency updates, including a manylinux update that fixes an 'undefined symbol' error in gcc-toolset (#2334)
v2.23.1
15 March 2025
- ⚠️ Added warnings when the shorthand values
manylinux1,manylinux2010,manylinux_2_24, andmusllinux_1_1are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in this file. (#2312) - 🛠 Dependency updates, including a manylinux update which fixes an issue with rustup. (#2315)
That's the last few versions.
ℹ️ Want more changelog? Head over to the changelog page in the docs.
Contributing
For more info on how to contribute to cibuildwheel, see the docs.
Everyone interacting with the cibuildwheel project via codebase, issue tracker, chat rooms, or otherwise is expected to follow the PSF Code of Conduct.
Maintainers
- Joe Rickerby @joerick
- Yannick Jadoul @YannickJadoul
- Matthieu Darbois @mayeut
- Henry Schreiner @henryiii
- Grzegorz Bokota @Czaki
Credits
cibuildwheel stands on the shoulders of giants.
- ⭐️ @matthew-brett for multibuild and matthew-brett/delocate
- @PyPA for the manylinux Docker images pypa/manylinux
- @ogrisel for wheelhouse-uploader and
run_with_env.cmd
Massive props also to-
- @zfrenchee for help debugging many issues
- @lelit for some great bug reports and contributions
- @mayeut for a phenomenal PR patching Python itself for better compatibility!
- @czaki for being a super-contributor over many PRs and helping out with countless issues!
- @mattip for his help with adding PyPy support to cibuildwheel
See also
Another very similar tool to consider is matthew-brett/multibuild. multibuild is a shell script toolbox for building a wheel on various platforms. It is used as a basis to build some of the big data science tools, like SciPy.
If you are building Rust wheels, you can get by without some of the tricks required to make GLIBC work via manylinux; this is especially relevant for cross-compiling, which is easy with Rust. See maturin-action for a tool that is optimized for building Rust wheels and cross-compiling.
