cibuildwheel Documentation

repository·main·Indexed 25 days ago

https://github.com/pypa/cibuildwheel

A tool for automating the building and testing of Python wheels across multiple platforms (macOS, Linux, Windows, etc.) and Python implementations (CPython, PyPy, GraalPy) within CI environments such as GitHub Actions, Azure Pipelines, CircleCI, and GitLab CI. It handles shared library dependency bundling using auditwheel, delocate, and delvewheel, and supports configuration via pyproject.toml or CIBW_ environment variables.

Tokens
32.7K
Snippets
63
Records
154
Agent score
80%

What's inside cibuildwheel

  1. What is cibuildwheel?

    main
    cibuildwheel is a tool designed to run on CI servers (GitHub Actions, Azure Pipelines, CircleCI, and GitLab CI) to automate the building and testing of Python wheels across multiple platforms, including macOS, Linux, and Windows. It supports various Python implementations like CPython, PyPy, and GraalPy, and handles shared library dependency bundling using tools like auditwheel (Linux), delocate (macOS), and delvewheel (Windows).
  2. Configure macOS deployment target and architectures

    main

    Deployment Target

    Use the MACOSX_DEPLOYMENT_TARGET environment variable to ensure backwards compatibility. cibuildwheel will cap your value to the lowest supported version for your specific architecture and Python version.

    Note for Rust users: Rustc requires MACOSX_DEPLOYMENT_TARGET to be at least 10.12. Since cibuildwheel may default to 10.9 for certain Intel/CPython builds, you must manually set it to 10.12 or higher.

    Architectures

    cibuildwheel supports x86_64 (Intel), arm64 (Apple Silicon), and universal2 (both architectures in one wheel).

    • Specifying Architectures: Use the macos.archs setting or CIBW_ARCHS_MACOS environment variable (e.g., CIBW_ARCHS_MACOS="x86_64 universal2").
    • Cross-compiling: Supported via Xcode toolchain v12.2+.
    • Testing Limitations:
      • On arm64 runners: You can test x86_64 and universal2 wheels using Rosetta 2.
      • On x86_64 runners: You can compile arm64 code, but you cannot test it. To suppress warnings when testing is impossible, use test-skip = ["*_arm64", "*_universal2:arm64"].
    • Poetry Warning: Cross-compiling on macOS does not currently work with Poetry. You may need to manually fix wheel tags using the wheel tags command.
  3. Security considerations when using cibuildwheel

    main

    Building and testing wheels with cibuildwheel executes arbitrary code from your project and its dependencies. Even when using OCI containers or Pyodide, there are no security guarantees; the code being built has full access to the environment invoking cibuildwheel.

    To maintain security hygiene:

    • Keep the job that builds distributions separate from the job that uploads them to PyPI.
    • Handle secrets and credentials with care and rotate them regularly.
    • Follow the principle of least privilege when granting permissions.
    • Do not store sensitive data on CI runners.
  4. Inherit and extend existing options in overrides

    main

    When using [[tool.cibuildwheel.overrides]], you can control how tables and lists are merged with the existing configuration using the inherit key. This prevents you from having to repeat all original values when you only want to add or change one.

    [[tool.cibuildwheel.overrides]]
    select = "cp311*"
    inherit.test-command = "prepend"
    test-command = ["pyproject-before"]
    
    [[tool.cibuildwheel.overrides]]
    select = "cp311*"
    inherit.environment = "append"
    environment.NEWVAR = "Added!"
  5. Manage cross-build tools for iOS

    main

    Because iOS builds are cross-platform, the build environment uses a minimal PATH to avoid linking macOS-specific libraries into iOS binaries.

    If your build process requires external tools (e.g., cmake, ninja, rustc, or even sub-dependencies like magick called by cmake), you must explicitly declare them using the xbuild-tools option. Every tool used anywhere in the build chain must be included in this list.

  6. Use overrides to apply settings based on selectors

    main

    The [[tool.cibuildwheel.overrides]] array allows you to apply specific settings to builds that match a select string. The select string references a build identifier (not the wheel name).

    Rules for overrides:

    • Order: Overrides are applied in order. Later matches override earlier ones if multiple selectors match.
    • Precedence: Environment variables always override static configuration (including overrides).
    • Limitations: You cannot override output-dir, build, skip, test_skip selectors, or architectures.
    • Platform Side-effects: On Linux, changing before-all or the image will trigger the launch of new containers.
    [tool.cibuildwheel.linux]
    before-all = "yum install mylib"
    test-command = "echo 'installed'"
    
    [[tool.cibuildwheel.overrides]]
    select = "*-musllinux*"
    before-all = "apk add mylib"
  7. Use placeholders in cibuildwheel options

    main

    Some options support placeholders that are substituted by cibuildwheel before they are used. Common placeholders include:

    • {project}
    • {package}
    • {wheel}

    If you need to use the literal name of a placeholder (e.g., you want the string {project} to appear literally in a command), prefix it with a hash character: #{project}. This is only necessary for strings that would otherwise be modified by substitution.

  8. Security warning for cibuildwheel users

    main

    WARNING: Arbitrary Code Execution

    Building and testing wheels executes arbitrary code from your project and its dependencies. Although cibuildwheel uses OCI containers and Pyodide for some builds, these provide no security guarantees. The code you're building and testing has full access to the environment that's invoking cibuildwheel.

    Best Practices:

    • Keep the job that builds distributions separate from the job that uploads them to PyPI.
    • Handle secrets and credentials with care and rotate them regularly.
    • Follow the principle of least privilege when granting permissions.
    • Do not store sensitive data on CI runners.
  9. Understand Pyodide release types in cibuildwheel

    main

    cibuildwheel categorizes Pyodide releases into three types, which determine how users access them via the CIBW_ENABLE flag:

    • Stable: The most recent full Pyodide release (e.g., 0.29.x / cp313). These are enabled by default and require no special flags.
    • Prerelease: Alpha, beta, or RC releases using the next CPython version (e.g., 314.0.0a1 / cp314). Users must opt in using CIBW_ENABLE: pyodide-prerelease.
    • End-of-life (EoL): Older stable Pyodide releases that are no longer the current stable. These are accessible via CIBW_ENABLE: pyodide-eol for users needing to build for older ABIs.
  10. Build CPython ABI3 (Limited API) wheels

    main

    ABI3 wheels use the CPython Limited API to ensure forward compatibility across Python versions.

    To build them, you must configure your build backend (e.g., setuptools) to compile using the Limited API. cibuildwheel automatically runs abi3audit after the repair step to verify that the resulting wheel does not violate the stable ABI. If violations are found, the build will fail.

  11. Configure cibuildwheel with GitHub Actions

    main

    To build manylinux, musllinux, macOS, and Windows wheels using GitHub Actions, you can use a workflow file that defines a matrix of operating systems. You must install cibuildwheel via pip and then run it using python -m cibuildwheel. Options for the build can be passed via environment variables prefixed with CIBW_.

    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, windows-11-arm, macos-15-intel, macos-latest]
    
        steps:
          - uses: actions/checkout@v6
            with:
              persist-credentials: false
    
          # Used to host cibuildwheel
          - uses: actions/setup-python@v6
    
          - name: Install cibuildwheel
            run: python -m pip install cibuildwheel==4.1.1
    
          - 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@v6
            with:
              name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
              path: ./wheelhouse/*.whl
  12. Update cibuildwheel automatically using Dependabot

    main

    To avoid breaking builds due to unpinned versions, use Dependabot to manage updates.

    Option 1: GitHub Action

    If using pypa/cibuildwheel as a GitHub Action, pin to a minor version (e.g., @v4.1) to receive patch updates automatically. Use Dependabot for the github-actions ecosystem.

    Option 2: Requirement files

    If using cibuildwheel via pip, create a dedicated requirements file (e.g., requirements-cibw.txt) containing only the cibuildwheel pin. Use Dependabot for the pip ecosystem.

    # .github/dependabot.yml for GitHub Actions
    version: 2
    updates:
      - package-ecosystem: "github-actions"
        directory: "/"
        schedule:
          interval: "weekly"
    # requirements-cibw.txt
    cibuildwheel==4.1.1
    # .github/dependabot.yml for pip requirements
    version: 2
    updates:
      - package-ecosystem: "pip"
        directory: "/"
        schedule:
          interval: "daily"