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 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 35 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_device = "arm64-iphoneos"
arm64_simulator = "arm64-iphonesimulator"
x86_64_simulator = "x86_64-iphonesimulator"

@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_simulator
else:
return Architecture.arm64_simulator
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,17 @@ 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 architectures
result = {
Architecture.x86_64_simulator,
Architecture.arm64_simulator,
Architecture.arm64_device,
}

return result

@staticmethod
Expand All @@ -150,6 +175,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_simulator,
Architecture.arm64_simulator,
Architecture.arm64_device,
},
}
return all_archs_map[platform]

Expand Down
Loading
Loading