369 lines
15 KiB
ReStructuredText
369 lines
15 KiB
ReStructuredText
PEP: 730
|
|
Title: Adding iOS as a supported platform
|
|
Author: Russell Keith-Magee <russell@keith-magee.com>
|
|
Sponsor: Ned Deily <nad@python.org>
|
|
Discussions-To: https://discuss.python.org/t/pep730-adding-ios-as-a-supported-platform/35854
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Created: 09-Oct-2023
|
|
Python-Version: 3.13
|
|
|
|
Abstract
|
|
========
|
|
|
|
This PEP proposes adding iOS as a supported platform in CPython. The initial
|
|
goal is to achieve Tier 3 support for Python 3.13. This PEP describes the
|
|
technical aspects of the changes that are required to support iOS. It also
|
|
describes the project management concerns related to adoption of iOS as a Tier 3
|
|
platform.
|
|
|
|
Motivation
|
|
==========
|
|
|
|
Over the last 15 years, mobile platforms have become increasingly important
|
|
parts of the computing landscape. iOS is one of two operating systems that
|
|
control the vast majority of these devices. However, there is no official
|
|
support for iOS in CPython.
|
|
|
|
The `BeeWare Project <https://beeware.org>`__ and `Kivy <https://kivy.org>`__
|
|
have both supported iOS for almost 10 years. This support has been able to
|
|
generate applications that have been accepted for publication in the iOS App
|
|
Store. This demonstrates the technical feasibility of iOS support.
|
|
|
|
It is important for the future of Python as a language that it is able to be
|
|
used on any hardware or OS that has widespread adoption. If Python cannot be
|
|
used a on a platform that has widespread use, adoption of the language will be
|
|
impacted as potential users will adopt other languages that *do* provide support
|
|
for these platforms.
|
|
|
|
Rationale
|
|
=========
|
|
|
|
Development landscape
|
|
---------------------
|
|
|
|
iOS provides a single API, but 2 distinct ABIs - ``iphoneos`` (physical
|
|
devices), and ``iphonesimulator``. Each of these ABIs can be provided on
|
|
multiple CPU architectures. At time of writing, Apple officially supports
|
|
``arm64`` on the device ABI, and ``arm64`` and ``x86_64`` are supported on the
|
|
simulator ABI.
|
|
|
|
As with macOS, iOS supports the creation of "fat" binaries that contains
|
|
multiple CPU architectures. However, fat binaries *cannot* span ABIs. That is,
|
|
it is possible to have a fat *simulator* binary, and a fat *device* binary, but
|
|
it is not possible to create a single fat "iOS" binary that covers both
|
|
simulator and device needs. To support distribution of a single development
|
|
artefact, Apple uses an "XCframework" structure - a wrapper around multiple ABIs
|
|
that implement a common API.
|
|
|
|
iOS runs on a Darwin kernel, similar to macOS. However, there is a need to
|
|
differentiate between macOS and iOS at an implementation level, as there are
|
|
significant platform differences between iOS and macOS.
|
|
|
|
iOS code is compiled for compatibility against a minimum iOS version.
|
|
|
|
POSIX compliance
|
|
----------------
|
|
|
|
iOS is broadly a POSIX platform. However, similar to WASI/Emscripten, there are
|
|
POSIX APIs that exist on iOS, but cannot be used; and POSIX APIs that don't
|
|
exist at all.
|
|
|
|
Most notable of these is the fact that iOS does not provide any form of
|
|
multiprocess support. ``fork`` and ``spawn`` both *exist* in the iOS API;
|
|
however, if they are invoked, the invoking iOS process stops, and the new
|
|
process doesn't start.
|
|
|
|
Unlike WASI/Emscripten, threading *is* supported on iOS.
|
|
|
|
There are also significant limits to socket handling. Due to process sandboxing,
|
|
there is no availability of interprocess communication via socket. However,
|
|
sockets for network communication *are* available.
|
|
|
|
Dynamic libraries
|
|
-----------------
|
|
|
|
The iOS `App Store guidelines
|
|
<https://developer.apple.com/app-store/review/guidelines>`__ allow apps to be
|
|
written in languages other than Objective C or Swift. However, they have very
|
|
strict guidelines about the structure of apps that are submitted for
|
|
distribution.
|
|
|
|
iOS apps can use dynamically loaded libraries; however, there are very strict
|
|
requirements on how dynamically loaded content is packaged for use on iOS:
|
|
|
|
* Dynamic binary content must be compiled as dynamic libraries, not shared
|
|
objects or binary bundles.
|
|
|
|
* They must be packaged in the app bundle as Frameworks.
|
|
|
|
* Each Framework can only contain a single dynamic library.
|
|
|
|
* The Framework *must* be contained in the iOS App's ``Frameworks`` folder.
|
|
|
|
* A Framework may not contain any non-library content.
|
|
|
|
This imposes some constraints on the operation of CPython. It is not possible
|
|
store binary modules in the ``lib-dynload`` and/or ``site-packages`` folders;
|
|
they must be stored in the app's Frameworks folder, with each module wrapped in
|
|
a Framework. This also means that the common assumption that a Python module can
|
|
construct the location of a binary module by using the ``__file__`` attribute of
|
|
the Python module no longer holds.
|
|
|
|
As with macOS, compiling a dynamic library requires the use of the ``--undefined
|
|
dynamic_lookup`` option to avoid linking libPython into every binary module.
|
|
This option currently raises a deprecation warning when it is used. This warning
|
|
*was* previously raised on macOS builds as well; however, responses from Apple
|
|
staff suggests this was unintentional, and they `did not intend to break the
|
|
CPython ecosystem by removing this option
|
|
<https://developer.apple.com/forums/thread/719961>`__. It is difficult to judge
|
|
whether iOS support would fall under the same umbrella.
|
|
|
|
Distribution
|
|
------------
|
|
|
|
Adding iOS as a Tier 3 platform only requires adding support for compiling an
|
|
iOS-compatibile code with an unpatched CPython code checkout. It does not
|
|
require production of officially distributed iOS artefacts for use by end-users.
|
|
|
|
If/when iOS is updated to Tier 2 or 1 support, there should be a process for
|
|
producing iOS distribution artefacts. This could be in the form of an "embedded
|
|
distribution" analogous to the Windows embedded distribution, or as a Cocoapod
|
|
or Swift Package that could be added to an Xcode project.
|
|
|
|
Console and interactive usage
|
|
-----------------------------
|
|
|
|
Distribution of a traditional CPython REPL or interactive "python.exe" should
|
|
not be considered a goal of this work.
|
|
|
|
Mobile devices (including iOS) do not provide a TTY-style console. They do not
|
|
provide ``stdin``, ``stdout`` or ``stderr``. iOS provides a system log, and it
|
|
is possible to install a redirection so that all ``stdout`` and ``stderr``
|
|
content is redirected to the system log; but there is no analog for
|
|
``stdin``.
|
|
|
|
In addition, iOS places restrictions on downloading additional code at runtime
|
|
(as this behavior would be functionally indistinguishable from trying to work
|
|
around App Store review). As a result, a traditional "create a virtual
|
|
environment and pip install" development experience will not be viable on iOS.
|
|
|
|
It is *possible* to build an native iOS application that provides a REPL
|
|
interface. This would be closer to an IDLE-style user experience; however,
|
|
Tkinter cannot be used on iOS, so any app would require a ground-up rewrite. The
|
|
iOS app store already contains several examples of apps in this category (e.g.,
|
|
`Pythonista <http://www.omz-software.com/pythonista/>`__ and `Pyto
|
|
<https://pyto.readthedocs.io/>`__). The focus of this work would be to provide
|
|
an embedded distribution that IDE-style native interfaces could utilize, not a
|
|
user-facing "app" interface to iOS on Python.
|
|
|
|
Specification
|
|
=============
|
|
|
|
Platform identification
|
|
-----------------------
|
|
|
|
``sys``
|
|
'''''''
|
|
|
|
``sys.platform`` will identify as ``"ios"`` on both simulator and physical
|
|
devices.
|
|
|
|
``sys.implementation._multiarch`` will describe the ABI and CPU architecture:
|
|
|
|
* ``"iphoneos-arm64"`` for ARM64 devices
|
|
* ``"iphonesimulator-arm64"`` for ARM64 simulators
|
|
* ``"iphonesimulator-x86_64"`` for x86_64 simulators
|
|
|
|
``sys.implementation`` will also have an additional attribute - ``_simulator`` -
|
|
storing a Boolean that is ``True`` if the device running the app is a simulator.
|
|
This attribute would not exist on non-iOS platforms.
|
|
|
|
``platform``
|
|
''''''''''''
|
|
|
|
Platform will be used as the primary mechanism for retrieving OS and device
|
|
details.
|
|
|
|
* ``platform.system()`` - ``"iOS"``
|
|
|
|
* ``platform.node()`` - the user-provided name of the device, as returned by the
|
|
``[[UIDevice currentDevice] systemName]`` system call (e.g.,
|
|
``"Janes-iPhone"``). For simulated devices, this will be the name of the
|
|
development computer running the simulator.
|
|
|
|
* ``platform.release()`` - the iOS version number, as a string (e.g., ``"16.6.1"``)
|
|
|
|
* ``platform.machine()`` - The device model returned by ``[[UIDevice
|
|
currentDevice] model]`` (e.g., ``"iPhone13,2"``); or ``"iPhoneSimulator"`` for
|
|
simulated devices.
|
|
|
|
All other values will be as returned by ``os.uname()``
|
|
|
|
``os``
|
|
''''''
|
|
|
|
``os.uname()`` will return the raw result of a POSIX ``uname()`` call. This will
|
|
result in the following values:
|
|
|
|
* ``sysname`` - ``"Darwin"``
|
|
|
|
* ``release`` - The Darwin kernel version (e.g., ``"22.6.0"``)
|
|
|
|
``sysconfig``
|
|
'''''''''''''
|
|
|
|
The ``sysconfig`` module will use the minimum iOS version as part of
|
|
``sysconfig.get_platform()`` identifier (e.g., ``"iOS-12.0-iphoneos-arm64"``).
|
|
The ``sysconfigdata_name`` and Config makefile will follow the same patterns as
|
|
existing platforms (using ``sys.platform``, ``sys.implementation._multiarch``
|
|
etc.) to construct identifiers.
|
|
|
|
Subprocess support
|
|
------------------
|
|
|
|
iOS will leverage the pattern for disabling subprocesses established by
|
|
WASI/Emscripten. The ``subprocess`` module will raise an exception if an attempt
|
|
is made to start a subprocess, and ``os.fork`` and ``os.spawn`` calls will raise
|
|
an ``OSError``.
|
|
|
|
Dynamic module loading
|
|
----------------------
|
|
|
|
To accommodate iOS dynamic loading, the ``importlib`` bootstrap will be extended
|
|
to add a metapath finder that can convert a request for a Python binary module
|
|
into a Framework location. This finder will only be installed if ``sys.platform
|
|
== "ios"``.
|
|
|
|
This finder will convert a Python module name (e.g., ``foo.bar._whiz``) into a
|
|
unique Framework name by replacing the dots with underscores (i.e.,
|
|
``foo_bar__whiz.framework``). A framework is a directory; the finder will look
|
|
for ``_whiz.dylib`` in that directory.
|
|
|
|
CI resources
|
|
------------
|
|
|
|
GitHub Actions is able to host iOS simulators on their macOS machines, and the
|
|
iOS simulator can be controlled by scripting environments. The free tier
|
|
currently only provides x86_64 macOS machines; however ARM64 runners `have
|
|
recently become available on paid plans <https://github.blog/
|
|
2023-10-02-introducing-the-new-apple-silicon-powered-m1-macos-larger-runner-for-github-actions/>`__.
|
|
|
|
If GitHub Actions resources are insufficient or not viable for cost reasons,
|
|
Anaconda has offered to provide resources to support CI requirements.
|
|
|
|
Packaging
|
|
---------
|
|
|
|
iOS will not provide a "universal" wheel format. Instead, wheels will be
|
|
provided for each ABI-arch combination. At present, no binary merging is
|
|
required. There is only one on-device architecture; and simulator binaries are
|
|
not considered to be distributable artefacts, so only one architecture is needed
|
|
to build a simulator.
|
|
|
|
iOS wheels will use tags:
|
|
|
|
* ``iOS_12_0_iphoneos_arm64``
|
|
* ``iOS_12_0_iphonesimulator_arm64``
|
|
* ``iOS_12_0_iphonesimulator_x86_64``
|
|
|
|
In these tags, "12.0" is the minimum supported iOS version. The choice of
|
|
minimum supported iOS version is a decision of whoever compiles CPython for iOS.
|
|
At time of writing, iOS 12.0 exposes most significant iOS features, while
|
|
reaching near 100% of devices.
|
|
|
|
These wheels can include binary modules in-situ (i.e., co-located with the
|
|
Python source, in the same way as wheels for a desktop platform); however, they
|
|
will need to be post-processed as binary modules need to be moved into the
|
|
"Frameworks" location for distribution. This can be automated with an Xcode
|
|
build step.
|
|
|
|
PEP 11 Update
|
|
-------------
|
|
|
|
:pep:`11` will be updated to include the three iOS ABIs:
|
|
|
|
* ``aarch64-apple-ios``
|
|
* ``aarch64-apple-ios-simulator``
|
|
* ``x86_64-apple-ios-simulator``
|
|
|
|
Ned Deily will serve as the initial core team contact for these ABIs.
|
|
|
|
Backwards Compatibility
|
|
=======================
|
|
|
|
Adding a new platform does not introduce any backwards compatibility concerns to
|
|
CPython itself.
|
|
|
|
There may be some backwards compatibility implications on the projects that have
|
|
historically provided CPython support (i.e., BeeWare and Kivy) if the final form
|
|
of any CPython patches don't align with the patches they have historically used.
|
|
|
|
Although not strictly a backwards compatibility issue, there *is* a platform
|
|
adoption consideration. Although CPython itself may support iOS, if it is
|
|
unclear how to produce iOS-compatible wheels, and prominent libraries like
|
|
cryptography, Pillow, and NumPy don't provide iOS wheels, the ability of the
|
|
community to adopt Python on iOS will be limited. Therefore, it will be
|
|
necessary to clearly document how projects can add iOS builds to their CI and
|
|
release tooling. Adding iOS support to tools like `crossenv
|
|
<https://crossenv.readthedocs.io/>`__ and `cibuildwheel
|
|
<https://cibuildwheel.readthedocs.io/>`__ may be one way to achieve this.
|
|
|
|
Security Implications
|
|
=====================
|
|
|
|
Adding iOS as a new platform does not add any security implications.
|
|
|
|
How to Teach This
|
|
=================
|
|
|
|
The education needs related to this PEP mostly relate to how end-users can add
|
|
iOS support to their own Xcode projects. This can be accomplished with
|
|
documentation and tutorials on that process. The need for this documentation
|
|
will increase if/when support raises from Tier 3 to Tier 2 or 1; however, this
|
|
transition should also be accompanied with simplified deployment artefacts (such
|
|
as a Cocoapod or Swift package) that are integrated with Xcode development.
|
|
|
|
Reference Implementation
|
|
========================
|
|
|
|
The BeeWare `Python-Apple-support
|
|
<https://github.com/beeware/Python-Apple-support>`__ repository contains a
|
|
reference patch and build tooling to compile a distributable artefact.
|
|
|
|
`Briefcase <https://briefcase.readthedocs.org>`__ provides a reference
|
|
implementation of code to execute test suites on iOS simulators. The `Toga
|
|
Testbed <https://github.com/beeware/toga/tree/main/testbed>`__ is an example of
|
|
a test suite that is executed on the iOS simulator using GitHub Actions.
|
|
|
|
Rejected Ideas
|
|
==============
|
|
|
|
``sys.implementation._simulator`` availability
|
|
----------------------------------------------
|
|
|
|
The ``_simulator`` attribute could be provided on *all* platforms, returning
|
|
``False``. However, the attribute has no use outside of an iOS context.
|
|
|
|
Open Issues
|
|
===========
|
|
|
|
On-device testing
|
|
-----------------
|
|
|
|
CI testing on simulators can be accommodated reasonably easily.
|
|
On-device testing is much harder, as availability of device farms that could be
|
|
configured to provide Buildbots or Github Actions runners is limited.
|
|
|
|
However, on device testing may not be necessary. As a data point - Apple's Xcode
|
|
Cloud solution doesn't provide on-device testing. They rely on the fact that the
|
|
API is consistent between device and simulator, and ARM64 simulator testing is
|
|
sufficient to reveal CPU-specific issues.
|
|
|
|
Copyright
|
|
=========
|
|
|
|
This document is placed in the public domain or under the CC0-1.0-Universal
|
|
license, whichever is more permissive.
|