Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for building iOS wheels. #2286

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
acf4eb2
Add support for building iOS wheels.
freakboy3742 Feb 17, 2025
e992cef
Replace use of system() in test binary module.
freakboy3742 Feb 25, 2025
a450fba
Restored the 'minimal' approach of the minimal examples.
freakboy3742 Feb 25, 2025
ac8ab09
Split out platform details into standalone pages, and expand iOS plat…
freakboy3742 Feb 25, 2025
254b909
More doc corrections.
freakboy3742 Feb 25, 2025
a7a7b67
Merge branch 'main' into ios-support
freakboy3742 Feb 25, 2025
cead9fb
Bump support package to include fix for python/cpython#130292
freakboy3742 Feb 25, 2025
7b8b31e
Ensure iOS tests are all run on the same xdist worker.
freakboy3742 Feb 26, 2025
9c495a8
More iOS documentation tweaks.
freakboy3742 Feb 26, 2025
b622638
Merge branch 'main' into ios-support
freakboy3742 Mar 2, 2025
457d46a
Factor out common xcode version test utility.
freakboy3742 Mar 2, 2025
05ac4ae
Simplify iOS to a single platform with an expanded interpretation of …
freakboy3742 Mar 2, 2025
3b37676
I guess I should update the iOS tests as well...
freakboy3742 Mar 2, 2025
32b9203
Additional safety for missing iOS test output.
freakboy3742 Mar 2, 2025
c19bd4a
Remove DYLD_LIBRARY_PATH from the iOS environment.
freakboy3742 Mar 3, 2025
cc51dcc
Make test-sources mandatory for iOS builds.
freakboy3742 Mar 5, 2025
be267bd
Updates and clarifications to documentation.
freakboy3742 Mar 5, 2025
5420faf
Clarify what a slice is.
freakboy3742 Mar 6, 2025
fcf6f54
Normalize use of underscores in platform name.
freakboy3742 Mar 6, 2025
c4c88a7
Modify auto target to be matching CPU only.
freakboy3742 Mar 6, 2025
b542fb5
Use consistent ordering of platforms in examples.
freakboy3742 Mar 6, 2025
361b31a
Use consistent naming in iOS archiectures.
freakboy3742 Mar 6, 2025
a995526
Placate the linter.
freakboy3742 Mar 6, 2025
dca2338
Miscellaneous cleanups picked up by @joerick's review.
freakboy3742 Mar 6, 2025
8f2cb91
Correct the list of expected wheels.
freakboy3742 Mar 6, 2025
26f217c
Correct which 'native' we're actually checking.
freakboy3742 Mar 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ 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<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | Pyodide |
|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅⁴ |
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | 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 |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | 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 |
| | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>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 |
| 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 |

<sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
<sup>² Windows arm64 support is experimental.</sup><br>
Expand All @@ -55,18 +55,19 @@ 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 |
|-----------------|-------|-------|---------|-----------|-----------|-------------|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² |
| Travis CI | ✅ | | ✅ | ✅ | | |
| AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | |
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
|-----------------|-------|-------|---------|-----------|-----------|-------------|-----|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | |
| Travis CI | ✅ | | ✅ | ✅ | | | |
| AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² | |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | | |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | | |

<sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#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.</sup><br>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup>

<!--intro-end-->

Expand All @@ -75,6 +76,7 @@ Example setup

To build manylinux, musllinux, macOS, and Windows wheels on GitHub Actions, you could use this `.github/workflows/wheels.yml`:

<!--generic-github-start-->
```yaml
name: Build

Expand Down Expand Up @@ -102,12 +104,14 @@ jobs:
# 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
```
<!--generic-github-end-->

For more information, including PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the [documentation](https://cibuildwheel.pypa.io) and the [examples](https://github.com/pypa/cibuildwheel/tree/main/examples).

Expand Down
1 change: 1 addition & 0 deletions bin/generate_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]:
"windows": as_object(not_linux),
"macos": as_object(not_linux),
"pyodide": as_object(not_linux),
"ios": as_object(not_linux),
}

oses["linux"]["properties"]["repair-wheel-command"] = {
Expand Down
3 changes: 3 additions & 0 deletions bin/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@
sys.executable,
"-m",
"pytest",
"--dist",
"loadgroup",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows the build to run all the iOS tests on the same machine; see the iOS tests for the reason.

f"--numprocesses={args.num_processes}",
"-x",
"--durations",
"0",
"--timeout=2400",
"test",
"-vv",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generates a lot more output - but when a test fails, it helps to have as much context as possible. It was difficult to diagnose some of the iOS failures with the low verbosity output.

]

if sys.platform.startswith("linux") and args.run_podman:
Expand Down
16 changes: 11 additions & 5 deletions cibuildwheel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import Any, Protocol, TextIO, assert_never

import cibuildwheel
import cibuildwheel.ios
import cibuildwheel.linux
import cibuildwheel.macos
import cibuildwheel.pyodide
Expand Down Expand Up @@ -93,13 +94,14 @@ def main_inner(global_options: GlobalOptions) -> None:

parser.add_argument(
"--platform",
choices=["auto", "linux", "macos", "windows", "pyodide"],
choices=["auto", "linux", "macos", "windows", "pyodide", "ios"],
default=None,
help="""
Platform to build for. Use this option to override the
auto-detected platform. Specifying "macos" or "windows" only works
on that operating system, but "linux" works on all three, as long
as Docker/Podman is installed. Default: auto.
Platform to build for. Use this option to override the auto-detected
platform. Specifying "macos" or "windows" only works on that
operating system. "linux" works on any desktop OS, as long as
Docker/Podman is installed. "pyodide" only works on linux and macOS.
"ios" only work on macOS. Default: auto.
""",
)

Expand Down Expand Up @@ -240,6 +242,8 @@ def _compute_platform_only(only: str) -> PlatformName:
return "windows"
if "pyodide_" in only:
return "pyodide"
if "ios_" in only:
return "ios"
msg = f"Invalid --only='{only}', must be a build selector with a known platform"
raise errors.ConfigurationError(msg)

Expand Down Expand Up @@ -301,6 +305,8 @@ def get_platform_module(platform: PlatformName) -> PlatformModule:
return cibuildwheel.macos
if platform == "pyodide":
return cibuildwheel.pyodide
if platform == "ios":
return cibuildwheel.ios
assert_never(platform)


Expand Down
45 changes: 40 additions & 5 deletions cibuildwheel/architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"macos": "macOS",
"windows": "Windows",
"pyodide": "Pyodide",
"ios": "iOS",
}

ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
Expand Down Expand Up @@ -63,6 +64,12 @@ class Architecture(StrEnum):
# WebAssembly
wasm32 = auto()

# iOS "multiarch" architectures that include both
# the CPU architecture and the ABI.
arm64_iphoneos = auto()
arm64_iphonesimulator = auto()
x86_64_iphonesimulator = auto()

@staticmethod
def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":
result = set()
Expand All @@ -89,8 +96,8 @@ def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":

@staticmethod
def native_arch(platform: PlatformName) -> "Architecture | None":
if platform == "pyodide":
return Architecture.wasm32
native_machine = platform_module.machine()
native_architecture = Architecture(native_machine)

# Cross-platform support. Used for --print-build-identifiers or docker builds.
host_platform: PlatformName = (
Expand All @@ -99,8 +106,18 @@ def native_arch(platform: PlatformName) -> "Architecture | None":
else ("macos" if sys.platform.startswith("darwin") else "linux")
)

native_machine = platform_module.machine()
native_architecture = Architecture(native_machine)
if platform == "pyodide":
return Architecture.wasm32
elif platform == "ios":
# Can only build for iOS on macOS. The "native" architecture is the
# simulator for the macOS native platform.
if host_platform == "macos":
if native_architecture == Architecture.x86_64:
return Architecture.x86_64_iphonesimulator
else:
return Architecture.arm64_iphonesimulator
else:
return None

# we might need to rename the native arch to the machine we're running
# on, as the same arch can have different names on different platforms
Expand Down Expand Up @@ -131,9 +148,22 @@ def auto_archs(platform: PlatformName) -> "set[Architecture]":
elif Architecture.aarch64 in result and _check_aarch32_el0():
result.add(Architecture.armv7l)

if platform == "windows" and Architecture.AMD64 in result:
elif platform == "windows" and Architecture.AMD64 in result:
result.add(Architecture.x86)

elif platform == "ios":
# iOS defaults to building all targets with the same CPU architecture
# as the native simulator architecture
if native_arch == Architecture.x86_64_iphonesimulator:
result = {
Architecture.x86_64_iphonesimulator,
}
else:
result = {
Architecture.arm64_iphonesimulator,
Architecture.arm64_iphoneos,
}

return result

@staticmethod
Expand All @@ -150,6 +180,11 @@ def all_archs(platform: PlatformName) -> "set[Architecture]":
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
"pyodide": {Architecture.wasm32},
"ios": {
Architecture.x86_64_iphonesimulator,
Architecture.arm64_iphonesimulator,
Architecture.arm64_iphoneos,
},
}
return all_archs_map[platform]

Expand Down
Loading
Loading