cibuildwheel¶
cibuildwheel simplifies the creation of Python Wheels for the different platforms and Python versions through Continuous Integration (CI) workflows. More precisely it builds manylinux, macOS 10.9+, and Windows wheels for CPython and PyPy with GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, or GitLab CI/CD.
In addition, it bundles shared library dependencies on Linux and macOS through auditwheel and delocate.
Finally, the tests can also run against the wheels.
To build Linux, macOS, and Windows wheels, create a
.github/workflows/build_wheels.yml file in your GitHub repo:
name: Build
on:
push:
branches: [main]
release:
types:
- published
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: {}
releaseis executed when a tagged version is transferred.
See also
workflow_dispatchallows you to click a button in the graphical user interface to trigger a build. This is perfect for manually testing wheels before a release, as you can easily download them from artifacts.
See also
Now the wheels can be built with:
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
# macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Build wheels
uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1
This runs the CI workflow with the following default settings:
package-dir: .output-dir: wheelhouseconfig-file: "{package}/pyproject.toml"
See also
Now you can finally upload the artefacts of both jobs to the PyPI:
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
To build wheels for Linux, macOS, and Windows with
GitLab CI/CD,
create a .gitlab-ci.yml file in your repository:
linux:
image: python:3.13
services:
- name: docker:dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
command: ["dockerd-entrypoint.sh"]
variables:
DOCKER_HOST: tcp://docker:2375/
DOCKER_DRIVER: overlay2
# See https://github.com/docker-library/docker/pull/166
DOCKER_TLS_CERTDIR: ""
# 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" || $CI_COMMIT_BRANCH =~ /^gitlab/'
variables:
CIBW_ENABLE: "all"
script:
- curl -sSL https://get.docker.com/ | sh
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- python -m pip install -U pip
- python -m pip install -e. pytest-custom-exit-code --group test
- python ./bin/run_tests.py
windows:
variables:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH =~ /^gitlab/'
variables:
# Everything except graalpy. GraalPy is JVM-based and very slow to
# start; on the small (2-core) GitLab SaaS Windows runners,
# virtualenv's interpreter query for graalpy.exe times out, failing
# the build. (Other platforms run the full "all" group.)
CIBW_ENABLE: "cpython-prerelease pypy pypy-eol pyodide-eol pyodide-prerelease"
script:
- python -m pip install -U pip
- python -m pip install -e. pytest-custom-exit-code --group test
- python bin\run_tests.py
tags:
- saas-windows-medium-amd64
macos:
image: macos-15-xcode-16
variables:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH =~ /^gitlab/'
variables:
CIBW_ENABLE: "all"
script:
- python3 -m pip install -U pip
- python3 -m pip install -e. pytest-custom-exit-code --group test
- python3 ./bin/run_tests.py
tags:
- saas-macos-medium-m1
Options¶
cibuildwheel can be configured either via environment variables or via a
configuration file such as pyproject.toml, for example:
[tool.cibuildwheel]
test-requires = "pytest"
test-command = "pytest {project}/tests"
build-verbosity = 1
# support Universal2 for Apple Silicon:
macos.archs = [ "auto", "universal2" ]
macos.test-skip = [ "*universal2:arm64" ]
See also
Examples¶
Coverage.py: .github/workflows/kit.yml
matplotlib: .github/workflows/cibuildwheel.yml
psutil: .github/workflows/build.yml