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

Commit abfa4b2

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. Omitting the framework option behaves as --disable-framework, as it does on every other platform. 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 them explicitly. A non-framework iOS build 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). Those two options have defaults that point the other way, so they must be typed; the errors lead with --enable-framework, so that a forgotten framework option does not read as an invitation to build static. 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 abfa4b2

6 files changed

Lines changed: 142 additions & 15 deletions

File tree

Doc/using/configure.rst

Lines changed: 10 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 without a framework, producing a static
2+
``libpython`` for embedding in an app binary. Such a build cannot load extension
3+
modules at runtime, and requires ``MODULE_BUILDTYPE=static`` and
4+
``--disable-test-modules`` to be requested explicitly. A shared iOS build must
5+
still be a framework build.

Platforms/Apple/iOS/README.md

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,13 @@ 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+
This argument is required for any iOS build that will be distributed, and
94+
for any build that needs to load binary extension modules.
95+
96+
Omitting it builds a static `libpython` for embedding directly in an app
97+
binary, instead of a `Python.framework`. That configuration comes with
98+
significant restrictions; see [Building a static
99+
Python](#building-a-static-python) below.
95100

96101
* `--with-framework-name=NAME`
97102

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

115120
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
121+
`Python.framework` builds, one for each ABI and architecture. Unless you are
122+
statically linking Python into your app (see [Building a static
123+
Python](#building-a-static-python) below), iOS builds of Python *must* be
124+
constructed as framework builds. To support this, you must provide the
125+
`--enable-framework` flag when configuring the build. The build
119126
also requires the use of cross-compilation. The minimal commands for building
120127
Python for the ARM64 iOS simulator will look something like:
121128
```
@@ -216,6 +223,69 @@ target, provide the version number as part of the `--host` argument - for
216223
example, `--host=arm64-apple-ios15.4-simulator` would compile an ARM64
217224
simulator build with a deployment target of 15.4.
218225

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

221291
### Testing a multi-architecture framework

configure

Lines changed: 25 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: 20 additions & 5 deletions

0 commit comments

Comments
 (0)