gh-156109: Allow static, non-framework iOS builds · python/cpython@14b7fc3 · GitHub
Skip to content

Commit 14b7fc3

Browse files
committed
gh-156109: Allow static, non-framework iOS builds
A shared Python on iOS has to be packaged as a framework for App Store Connect to accept it. A static libpython is linked into the app binary and loads nothing at runtime, so the requirement does not apply to it, but configure refused that configuration outright. Refuse it only where it cannot work: a shared build with no framework. The LINKFORSHARED and MODULE_DEPS_SHARED framework references are gated on enable_framework, as the Darwin arm above already does, since a build without a framework has nothing to link against. A static build does come with restrictions - it cannot load extension modules at runtime, and so cannot use binary wheels - so require the user to opt into each of them explicitly. On top of --disable-framework and --disable-shared, configure now errors unless MODULE_BUILDTYPE=static is set (there is no framework for a shared extension module to link against) and --disable-test-modules is given (some test modules must be built as shared libraries; see Modules/Setup.stdlib.in). Passing no framework option at all still errors, so a non-framework build stays an explicit opt-in. Document the build process and its limitations in a new section of Platforms/Apple/iOS/README.md, and note in Doc/using/ios.rst that the official iOS release artefact is a framework build.
1 parent 98bd716 commit 14b7fc3

6 files changed

Lines changed: 158 additions & 15 deletions

File tree

Doc/using/configure.rst

Lines changed: 15 additions & 0 deletions

Doc/using/ios.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ should ensure these stub binaries are on your path.
142142
Installing Python on iOS
143143
========================
144144

145+
The official iOS release artefact is a framework build, distributed as an
146+
``XCFramework``; this is the configuration described in the rest of this
147+
document, and the only one that supports binary extension modules. Static
148+
builds, where ``libpython`` and every extension module are linked directly into
149+
the app binary, are also possible, with limitations; see
150+
:source:`Platforms/Apple/iOS/README.md` for details.
151+
145152
Tools for building iOS apps
146153
---------------------------
147154

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
iOS builds may now be configured with ``--disable-framework``, producing a
2+
static ``libpython`` for embedding in an app binary. This configuration cannot
3+
load extension modules at runtime, and requires ``--disable-shared``,
4+
``MODULE_BUILDTYPE=static`` and ``--disable-test-modules`` to be requested
5+
explicitly. A shared iOS build must still be a framework build.

Platforms/Apple/iOS/README.md

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,15 @@ Python build for a single framework, the following options are available.
9090
installed. If `DIR` is not specified, the framework will be installed into
9191
a subdirectory of the `iOS/Frameworks` folder.
9292

93-
This argument *must* be provided when configuring iOS builds. iOS does not
94-
support non-framework builds.
93+
Either this argument or `--disable-framework` *must* be provided when
94+
configuring iOS builds; there is no default.
95+
96+
* `--disable-framework`
97+
98+
Build a static `libpython` for embedding directly in an app binary, instead
99+
of a `Python.framework`. This configuration comes with significant
100+
restrictions, each of which must be opted into explicitly; see [Building a
101+
static Python](#building-a-static-python) below.
95102

96103
* `--with-framework-name=NAME`
97104

@@ -113,9 +120,11 @@ framework to contain non-library content, so the iOS build will produce a
113120
The `lib` folder will be needed at runtime to support the Python library.
114121

115122
If you want to use Python in a real iOS project, you need to produce multiple
116-
`Python.framework` builds, one for each ABI and architecture. iOS builds of
117-
Python *must* be constructed as framework builds. To support this, you must
118-
provide the `--enable-framework` flag when configuring the build. The build
123+
`Python.framework` builds, one for each ABI and architecture. Unless you are
124+
statically linking Python into your app (see [Building a static
125+
Python](#building-a-static-python) below), iOS builds of Python *must* be
126+
constructed as framework builds. To support this, you must provide the
127+
`--enable-framework` flag when configuring the build. The build
119128
also requires the use of cross-compilation. The minimal commands for building
120129
Python for the ARM64 iOS simulator will look something like:
121130
```
@@ -216,6 +225,70 @@ target, provide the version number as part of the `--host` argument - for
216225
example, `--host=arm64-apple-ios15.4-simulator` would compile an ARM64
217226
simulator build with a deployment target of 15.4.
218227

228+
### Building a static Python
229+
230+
The official iOS release artefact is a framework build. However, if you are
231+
embedding Python in an app that links `libpython` at compile time, you can
232+
instead build a static `libpython3.x.a`, and link that archive directly into
233+
your app binary.
234+
235+
The App Store requirement that binary modules be packaged as signed frameworks
236+
does not apply to a static build, because a static build loads nothing at
237+
runtime; but for the same reason, this configuration cannot use *any* binary
238+
module that isn't compiled into the app binary. Every restriction that follows
239+
from that must be opted into explicitly at configure time:
240+
241+
* `--disable-framework` selects a non-framework build. It is an error to
242+
provide neither `--enable-framework` nor `--disable-framework`.
243+
244+
* `--disable-shared` is required. A shared iOS build must be a framework
245+
build, as an iOS app can only load a signed framework, never a bare dylib.
246+
247+
* `MODULE_BUILDTYPE=static` is required. There is no framework for a shared
248+
extension module to link against, so every extension module - including the
249+
ones in the standard library - must be linked into `libpython`.
250+
251+
* `--disable-test-modules` is required. Some test modules must be compiled as
252+
shared libraries (see `Modules/Setup.stdlib.in`), so they cannot be built in
253+
this configuration at all.
254+
255+
The minimal commands for a static build targeting ARM64 iOS devices are then:
256+
```
257+
export PATH="$(pwd)/Platforms/Apple/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
258+
./configure \
259+
--disable-framework \
260+
--disable-shared \
261+
--disable-test-modules \
262+
MODULE_BUILDTYPE=static \
263+
--host=arm64-apple-ios \
264+
--build=arm64-apple-darwin \
265+
--with-build-python=/path/to/python.exe
266+
make
267+
make install
268+
```
269+
This produces a `libpython3.x.a` containing the interpreter and the standard
270+
library's extension modules; `make install` installs that archive, along with
271+
the standard library's Python source, into the location given by `--prefix`.
272+
273+
#### Limitations of a static build
274+
275+
* **Binary wheels cannot be used.** There is no `libpython` dylib for a
276+
third-party extension module to link against, and a static Python has nothing
277+
to `dlopen` in any case. Pure Python wheels work as normal; any package with a
278+
C extension must be compiled into the app binary alongside `libpython`.
279+
280+
* **The standard library's extension modules are not loadable modules.** They
281+
live in the archive, not in `.framework` bundles in the app's `Frameworks`
282+
folder, so the packaging described in
283+
[Using Python on iOS](https://docs.python.org/3/using/ios.html) does not apply
284+
to them.
285+
286+
* **The test suite cannot be run as-is**, as the test modules are not built.
287+
288+
* This configuration is not covered by the `Platforms/Apple` build script, nor
289+
by CPython's CI. It is not the configuration used to produce official
290+
releases.
291+
219292
## Testing Python on iOS
220293

221294
### Testing a multi-architecture framework

configure

Lines changed: 27 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 26 additions & 5 deletions

0 commit comments

Comments
 (0)