Skip to content
Open
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
129 changes: 129 additions & 0 deletions cep-XXXX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# CEP XXXX - Subdirs and virtual packages for iOS and Android

<table>
<tr><td> Title </td><td> Subdirs and virtual packages for iOS and Android </td>
<tr><td> Status </td><td> Draft </td></tr>
<tr><td> Author(s) </td><td> Wolf Vollprecht &lt;wolf@prefix.dev&gt;</td></tr>
<tr><td> Created </td><td> Jul 24, 2026</td></tr>
<tr><td> Updated </td><td> Jul 27, 2026</td></tr>
<tr><td> Discussion </td><td> NA </td></tr>
<tr><td> Implementation </td><td> https://github.com/conda/rattler (feat/ios-android-subdirs) </td></tr>
</table>

> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT",
"RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as
described in [RFC2119][RFC2119] when, and only when, they appear in all capitals, as shown here.
>
> More specifically, violations of a MUST or MUST NOT rule MUST result in an error. Violations of the
rules specified by any of the other all-capital terms MAY result in a warning, at discretion of the
implementation.

## Abstract

This CEP defines conda subdirs for packages targeting Apple's iOS and Google's Android, together
with two virtual packages, `__ios` and `__android`, for OS-version compatibility. It maps the PyPI
wheel-tag model for these platforms ([PEP 730] for iOS and [PEP 738] for Android) onto conda's
existing subdir and virtual-package machinery.

## Motivation

conda has no standard way to describe packages built for mobile operating systems. Python and the
wider native toolchain ecosystem now support iOS and Android as target platforms, and conda build
tools can produce packages for them. Common subdir and virtual-package names are needed so those
packages can be described and consumed consistently across implementations.

## Specification

### Subdirs

This CEP defines the following subdirs. As per [CEP 26](./cep-0026.md), each subdir is
single-architecture and follows the `<os>-<arch>` syntax.

#### iOS

| Subdir | Architecture | Platform variant |
| -------------------- | ------------ | ----------------------------- |
| `ios-arm64` | `arm64` | Device (`iphoneos`) |
| `iossimulator-arm64` | `arm64` | Simulator (`iphonesimulator`) |
| `iossimulator-64` | `x86_64` | Simulator (`iphonesimulator`) |

#### Android

| Subdir | Architecture | Android ABI |
| ----------------- | ------------ | ------------- |
| `android-aarch64` | `aarch64` | `arm64-v8a` |
| `android-armv7a` | `armv7a` | `armeabi-v7a` |
Comment on lines +54 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is there any benefit to diverging from the Android ABI naming here? At least in principle, there could be other 64-bit ARM ABIs in future; is there some benefit to using aarch64 instead of arm64-v8a and armv7a instead of armeabi-v7a?

@mhsmith mhsmith Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This follows the architecture naming of the existing conda platforms (hence x86 is simply "32" and "64"), which is probably more useful than following the Android names. In hindsight, I should probably have done the same when establishing the Android wheel tag format, and made it match the Linux architecture names, which would have saved me a bit of work in auditwheel.

There could theoretically be other ABI variants of ARM64 in the future, but I think Google will probably handle them the same way as they did with NEON instructions on ARM32: declare that they're required on all devices newer than a certain Android version. In that case, there would be no need for a separate subdir; packages that are affected can simply require a minimum Android version.

[ARM32 discussion moved below]

| `android-64` | `x86_64` | `x86_64` |
| `android-32` | `x86` | `x86` |

### Virtual packages

The following virtual packages extend [CEP 30](./cep-0030.md). Their build strings MUST be `0`.

#### `__ios`

`__ios` MUST be present when the target subdir is `ios-*` or `iossimulator-*`, and MUST NOT be
present otherwise. Its version MUST represent the iOS version available in the target environment,
for example `13.0`. A package requiring iOS 13.0 or newer depends on `__ios >=13.0`.

The version MUST be overridable with `CONDA_OVERRIDE_IOS` when set to a non-empty valid version
string. Without an override, an implementation MAY use a detected native version or a
build-tool-supplied target version; if neither is available, the version MUST be `0`. The override
MUST be ignored for other target subdirs.

#### `__android`

`__android` MUST be present when the target subdir is `android-*`, and MUST NOT be present otherwise.
Its version MUST represent the Android API level available in the target environment, for example
`21`. A package requiring API level 21 or newer depends on `__android >=21`.

The version MUST be overridable with `CONDA_OVERRIDE_ANDROID` when set to a non-empty valid version
string. Without an override, an implementation MAY use a detected native API level or a
build-tool-supplied target API level; if neither is available, the version MUST be `0`. The override
MUST be ignored for other target subdirs.

The `__unix` virtual package MUST be present for every subdir defined by this CEP.

## Rationale

- **Separate iOS device and simulator subdirs.** iOS provides one API but two incompatible ABIs:
`iphoneos` for devices and `iphonesimulator` for simulators. Even when both use `arm64`, a binary
built for one cannot run on the other, and a fat binary cannot span the two ABIs. The existing
`osx-*` subdirs are also unsuitable because iOS and macOS have significant platform differences
despite both using the Darwin kernel.
- **No separate iPadOS subdirs.** iPadOS is not distinct from iOS for development purposes. Binaries
built for the `iphoneos` and `iphonesimulator` ABIs can also be deployed to iPads.
- **The `ios` and `iossimulator` OS tokens.** Apple's `iphoneos` and `iphonesimulator` names identify
vendor ABIs. The conda token identifies the operating system, so `ios` follows CPython's platform
name and includes iPads. The `simulator` suffix records the ABI split without implying an
iPhone-only target.
- **Version in a virtual package.** Encoding the minimum OS version in the subdir, as PyPI does in a
wheel tag, would multiply the number of subdirs and move compatibility resolution out of the
solver. `__ios` and `__android` reuse conda's existing version-compatibility mechanism.

## Rejected ideas

- **`ios-simulator-*` subdirs.** A second dash does not conform to CEP 26. The simulator variant is
therefore folded into the OS token.
- **An `ios-64` subdir.** Apple does not provide an x86_64 iOS device ABI.
- **Using `armv7l` for Android.** Android's `armeabi-v7a` uses the softfp calling convention and
Bionic, unlike the ABI represented by `linux-armv7l`. The `armv7a` token keeps these incompatible
targets distinct.
Comment on lines +109 to +111

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Even if it does use the softfp calling convention, it is still armv7l (ARM version 7, little-endian), and the kernel reports it as such. The incompatible targets can be expressed by having different OS names: android-armv7l vs linux-armv7l.

In any case, the Android 32-bit platforms aren't officially supported by Python, so I'd suggest leaving them out for now.

- **Using `linux-*` for Android.** Modeling Bionic as a C standard-library variant in `linux-*` would
put incompatible binaries in the same subdir and rely on every package declaring an exact standard
library constraint. Android also has its own userspace ABI, dynamic linker, and platform APIs, so
it receives separate subdirs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm missing some bits mentioned in PEP 730's Rationale here, namely:

  • Why two ABIs for iOS, and why not simply osx
  • Why no iPadOS
  • Why ios and iossimulator instead of the classic iphoneos vendor string

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 9e16e31. The rationale now explains that device and simulator are incompatible ABIs even on arm64 and that macOS is a distinct platform despite also using Darwin; that iPadOS has no separate development ABI; and that conda folds PEP 730’s separate OS/ABI fields into ios and iossimulator, avoiding an iPhone-only name while conforming to CEP 26.

## References

- [PEP 730 - Adding iOS as a supported platform][PEP 730]
- [PEP 738 - Adding Android as a supported platform][PEP 738]
- Reference implementation: conda/rattler, branch `feat/ios-android-subdirs`

## Copyright

All CEPs are explicitly [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/).

[RFC2119]: https://www.ietf.org/rfc/rfc2119.txt
[PEP 730]: https://peps.python.org/pep-0730/
[PEP 738]: https://peps.python.org/pep-0738/