Platforms¶
Linux¶
System requirements¶
If you've got Docker installed on your development machine, you can run a Linux build.
Tip
You can run the Linux build on any platform. Even Windows can run Linux containers these days, but there are a few hoops to jump through. Check this document for more info.
Because the builds are happening in manylinux Docker containers, they're perfectly reproducible.
The only side effect to your system will be docker images being pulled.
Build containers¶
Linux wheels are built in manylinux/musllinux containers to provide binary compatible wheels on Linux, according to PEP 600 / PEP 656. Because of this, when building with cibuildwheel on Linux, a few things should be taken into account:
-
Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using
yumformanylinux2014,apt-getformanylinux_2_31,dnfformanylinux_2_28andapkformusllinux_1_1ormusllinux_1_2, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.cibuildwheelsupports this by providing theenvironmentandbefore-alloptions to setup the build environment inside the running container. -
The project directory is copied into the container as
/project, the output directory for the wheels to be copied out is/output. In general, this is handled transparently bycibuildwheel. For a more finegrained level of control however, the root of the host file system is mounted as/host, allowing for example to access shared files, caches, etc. on the host file system. Note that/hostis not available on CircleCI and GitLab CI due to their Docker policies. -
Alternative Docker images can be specified with the
manylinux-*-image/musllinux-*-imageoptions to allow for a custom, preconfigured build environment for the Linux builds. See options for more details.
macOS¶
System requirements¶
You need to have native build tools installed. Use xcode-select --install to install the Xcode command line tools.
Because the builds are happening without full isolation, there might be some differences compared to CI builds (Xcode version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. Cache folder: /Users/Matt/Library/Caches/cibuildwheel). You can override the cache folder using the CIBW_CACHE_PATH environment variable.
Warning
cibuildwheel uses official python.org macOS installers for CPython but those can only be installed globally.
In order not to mess with your system, cibuildwheel won't install those if they are missing. Instead, it will error out with a message to let you install the missing CPython:
Error: CPython 3.9 is not installed.
cibuildwheel will not perform system-wide installs when running outside of CI.
To build locally, install CPython 3.9 on this machine, or, disable this version of Python using CIBW_SKIP=cp39-macosx_*
Download link: https://www.python.org/ftp/python/3.9.8/python-3.9.8-macosx10.9.pkg
macOS Version Compatibility¶
macOS allows you to specify a "deployment target" version that will ensure backwards compatibility with older versions of macOS. For most projects, the way to do this is to set the MACOSX_DEPLOYMENT_TARGET environment variable.
macOS builds will honor the MACOSX_DEPLOYMENT_TARGET environment variable to control the minimum supported macOS version for generated wheels. The lowest value you can set MACOSX_DEPLOYMENT_TARGET is as follows:
| Arch | Python version range | Minimum target |
|---|---|---|
| Intel | CPython 3.8-3.11 | 10.9 |
| Intel | CPython 3.12+ | 10.13 |
| AS | CPython or PyPy | 11 |
| Intel | PyPy 3.8 | 10.13 |
| Intel | PyPy 3.9+ | 10.15 |
If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
Note
For Rust-based extensions, Rustc requires MACOSX_DEPLOYMENT_TARGET to be at
least 10.12. However, cibuildwheel defaults to 10.9 for
Intel / CPython 3.8-3.11 builds. Users must manually set
MACOSX_DEPLOYMENT_TARGET to 10.12 or higher when building Rust extensions.
macOS architectures¶
cibuildwheel supports both native builds and cross-compiling between arm64 (Apple Silicon) and x86_64 (Intel) architectures, including the cross-compatible universal2 format. By default, macOS builds will build a single architecture wheel, using the build machine's architecture.
If you need to support both x86_64 and Apple Silicon, you can use the macos.archs setting to specify the architectures you want to build, or the value universal2 to build a multi-architecture wheel. cibuildwheel will test x86_64 wheels (or the x86_64 slice of a universal2 wheel) when running on Apple Silicon hardware using Rosetta 2 emulation, but it is not possible to test Apple Silicon wheels on x86_64 hardware.
Overview of Mac architectures¶
x86_64¶
The traditional wheel for Apple, loads on Intel machines, and on Apple Silicon when running Python under Rosetta 2 emulation.
Due to a change in naming, Pip 20.3+ (or an installer using packaging 20.5+) is required to install a binary wheel on macOS Big Sur.
arm64¶
The native wheel for macOS on Apple Silicon.
universal2¶
This wheel contains both architectures, causing it to be up to twice the size (data files do not get doubled, only compiled code).
The dual-architecture universal2 has a few benefits, but a key benefit
to a universal wheel is that a user can bundle these wheels into an
application and ship a single binary.
However, if you have a large library, then you might prefer to ship
the two single-arch wheels instead - x86_64 and arm64. In rare cases,
you might want to build all three, but in that case, pip will not download
the universal wheels, because it prefers the most specific wheel
available.
What to provide?¶
Opinions vary on which of arch-specific or universal2 wheels are best - some packagers prefer universal2 because it's one wheel for all Mac users, so simpler, and easier to build into apps for downstream users. However, because they contain code for both architectures, their file size is larger, meaning they consume more disk space and bandwidth, and are harder to build for some projects.
See GitHub issue 1333 for more discussion.
How?¶
It's easiest to build x86_64 wheels on x86_64 runners, and arm64 wheels on arm64 runners.
On GitHub Actions, macos-14 runners are arm64, and macos-15-intel runners are x86_64. So all you need to do is ensure both are in your build matrix.
Cross-compiling¶
If your CI provider doesn't offer arm64 runners yet, or you want to create universal2, you'll have to cross-compile. Cross-compilation can be enabled by adding extra archs to the CIBW_ARCHS_MACOS option - e.g. CIBW_ARCHS_MACOS="x86_64 universal2". Cross-compilation is provided by Xcode toolchain v12.2+.
Regarding testing,
- On an arm64 runner, it is possible to test
x86_64wheels and both parts of auniversal2wheel using Rosetta 2 emulation. - On an
x86_64runner, arm64 code can be compiled but it can't be tested.cibuildwheelwill raise a warning to notify you of this - these warnings can be silenced by skipping testing on these platforms:test-skip = ["*_arm64", "*_universal2:arm64"].
Note
If your project uses Poetry as a build backend, cross-compiling on macOS does not currently work. In some cases arm64 wheels can be built but their tags will be incorrect, with the platform tag showing x86_64 instead of arm64.
As a workaround, the tag can be fixed before running delocate to repair the wheel. The wheel tags command is ideal for this. See this workflow for an example usage of wheel tags.
Windows¶
System requirements¶
You must have native build tools (i.e., Visual Studio) installed.
Because the builds are happening without full isolation, there might be some differences compared to CI builds (Visual Studio version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. Cache folder: C:\Users\Matt\AppData\Local\pypa\cibuildwheel\Cache). You can override the cache folder using the CIBW_CACHE_PATH environment variable.
Windows ARM64 builds¶
cibuildwheel supports cross-compiling ARM64 wheels on all Windows runners, but a native ARM64 runner is required for testing. On non-native runners, tests for ARM64 wheels will be automatically skipped with a warning. Add "*-win_arm64" to your test-skip setting to suppress the warning.
Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in cibuildwheel's windows.py before building. Currently, setuptools>=65.4.1 and setuptools_rust are the only supported backends.
By default, ARM64 is not enabled when running on non-ARM64 runners. Use CIBW_ARCHS to select it.
Pyodide/WebAssembly¶
Pyodide is offered as an experimental feature in cibuildwheel.
System requirements¶
Pyodide builds require a Linux or macOS machine.
Specifying a pyodide build¶
You must target pyodide with --platform pyodide (or use --only on the identifier).
Choosing a Pyodide version¶
It is also possible to target a specific Pyodide version by setting the pyodide-version option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the pyodide-build version. A list is available in Pyodide's cross-build environments metadata file, which can be viewed more easily by installing pyodide-build from PyPI and using pyodide xbuildenv search --all to see a compatibility table.
If there are pre-releases available for a newer Pyodide version, the pyodide-prerelease enable can be used to include pre-release versions.
Running tests¶
Currently, it's recommended to run tests using a python -m entrypoint, rather than a command line entrypoint, or a shell script. This is because custom entrypoints have some issues in the Pyodide virtual environment. For example, pytest may not work as a command line entrypoint, but will work as a python -m pytest entrypoint.
Android¶
Prerequisites¶
cibuildwheel can build Android wheels on any POSIX platform supported by the Android development tools, which currently means Linux x86_64, macOS ARM64 or macOS x86_64. Any of these platforms can be used to build wheels for any Android architecture supported by Python. However, testing wheels has additional requirements: see the section below.
If you already have an Android SDK, export the ANDROID_HOME environment variable to
point at its location. Otherwise, here's how to install it:
- Download the "Command line tools" from https://developer.android.com/studio.
- Create a directory
android-sdk/cmdline-tools, and unzip the command line tools package into it. - Rename
android-sdk/cmdline-tools/cmdline-toolstoandroid-sdk/cmdline-tools/latest. export ANDROID_HOME=/path/to/android-sdk
cibuildwheel will automatically use the SDK's sdkmanager to install any packages it
needs.
It also requires the following commands to be on the PATH:
curljava(or set theJAVA_HOMEenvironment variable)
Android version compatibility¶
Android builds will honor the ANDROID_API_LEVEL environment variable to set the
minimum supported API level
for generated wheels. This will default to the minimum API level of the selected Python
version.
If the repair-wheel-command adds any libraries to
the wheel, then ANDROID_API_LEVEL must be at least 24. This is already the default
when building for Python 3.14 and later, but you may need to set it when building for
Python 3.13.
Build frontend support¶
Android builds only support the build frontend. In principle, support for the
build[uv] frontend should be possible, but uv doesn't currently have support for
cross-platform builds, and doesn't have
support for iOS or Android wheel tags.
Tests¶
Tests are executed on a Gradle-managed emulator matching the architecture of the build machine – for example, if you're building on an ARM64 machine, then you can test an ARM64 wheel. Wheels of other architectures can still be built, but testing will automatically be skipped.
Running an emulator requires the build machine to either be bare-metal or support nested virtualization. CI platforms known to meet this requirement are:
- GitHub Actions Linux x86_64
On Linux, the emulator needs access to the KVM virtualization interface. This may require adding your user to a group, or changing your udev rules. On GitHub Actions, cibuildwheel will do this automatically using the commands shown here.
The Android test environment can't support running shell scripts, so the
test-command must be a Python command – see its
documentation for details.
If your package has dependencies which haven't been released on PyPI yet, you may want
to use the environment option to set PIP_EXTRA_INDEX_URL
to one of the following URLs:
iOS¶
System requirements¶
You must be building on a macOS machine, with Xcode installed. The Xcode installation must have an iOS SDK available, with all license agreements agreed to by the user. To check if an iOS SDK is available, open the Xcode settings panel, and check the Platforms tab. This will also ensure that license agreements have been acknowledged.
Building iOS wheels also requires a working macOS Python installation. See the notes on macOS builds for details about configuration of the macOS environment.
Specifying an iOS build¶
iOS is effectively 2 platforms - physical devices, and simulators. While the API for these two platforms are identical, the ABI is not compatible, even when dealing with a device and simulator with the same CPU architecture. For this reason, the architecture specification for iOS builds includes both the CPU architecture and the ABI that is being targeted. There are three possible values for architecture on iOS; the values match those used by sys.implementation._multiarch when running on iOS (with hyphens replaced with underscores, matching wheel filename normalization):
arm64_iphoneos(for physical iOS devices);arm64_iphonesimulator(for iOS simulators running on Apple Silicon macOS machines); andx86_64_iphonesimulator(for iOS simulators running on Intel macOS machines).
By default, cibuildwheel will build all wheels for the CPU architecture of the build machine. You can build all wheels for all architectures by specifying --archs all.
If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use configuration overrides with a select clause that targets the specific ABI or architecture. For example, consider the following example:
[tool.cibuildwheel.ios]
test-sources = ["tests"]
test-requires = ["pytest"]
[[tool.cibuildwheel.overrides]]
select = "*_iphoneos"
environment.PATH = "/path/to/special/device/details:..."
[[tool.cibuildwheel.overrides]]
select = "*-ios_arm64_*"
inherit.test-requires = "append"
test-requires = ["arm64-testing-helper"]
This configuration would:
- Specify a
test-sourcesandtest-requiresfor all iOS targets; - Add a
PATHsetting that will be used on physical iOS devices; and - Add
arm64-testing-helperto the test environment for all ARM64 iOS devices (whether simulator or device).
iOS version compatibility¶
iOS builds will honor the IPHONEOS_DEPLOYMENT_TARGET environment variable to set the minimum supported API version for generated wheels. This will default to 13.0 if the environment variable isn't set.
Cross platform builds¶
iOS builds are cross platform builds, as it not possible to run compilers and other build tools "on device". The pre-compiled iOS binaries used to support iOS builds include tooling that can convert any virtual environment into a cross platform virtual environment - that is, an environment that can run binaries on the build machine (macOS), but, if asked, will respond as if it is an iOS machine. This allows pip, build, and other build tools to perform iOS-appropriate behaviour.
Build frontend support¶
iOS builds support both the pip and build build frontends. In principle, support for uv with the build[uv] frontend should be possible, but uv doesn't currently have support for cross-platform builds, and doesn't have support for iOS (or Android) tags.
Build environment¶
The environment used to run builds does not inherit the full user environment - in particular, PATH is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force PATH to a "known minimal" path, that includes only the bare system utilities, and the iOS compiler toolchain.
If your project requires additional tools to build (such as cmake, ninja, or rustc), those tools must be explicitly declared as cross-build tools using xbuild-tools. Any tool used by the build process must be included in the xbuild-tools list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes cmake, and the cmake script invokes magick to perform some image transformations, both cmake and magick must be included in your cross-build tools list.
Tests¶
If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
The iOS test environment can't support running shell scripts, so the test-command value must be specified as if it were a command line being passed to python -m ....
The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running python -m <test-command>.