PEP 738: Android support, draft 2 (GH-3651)

This commit is contained in:
Malcolm Smith 2024-02-13 15:16:35 +00:00 committed by GitHub
parent 53c3d1a703
commit fda02b5757
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 54 additions and 29 deletions

View File

@ -52,7 +52,8 @@ Android is broadly a POSIX platform, based on a Linux kernel and the
ELF binary format. It does not use glibc, instead providing its own C
library implementation called Bionic. As a result, it is generally not
binary-compatible with any other Linux distribution, even if the architecture
matches.
matches. It also has its own filesystem layout which doesn't resemble any other
Unix.
However, Android's source-compatibility with Linux is quite good. In its early years,
the C library was very incomplete, but most of the gaps were filled by around
@ -64,6 +65,7 @@ This is also true of CPython. Although it has never officially supported
Android, recent versions (since 3.6) can already be compiled for Android with
minimal patching.
.. _738-os-versions:
OS versions
-----------
@ -89,11 +91,9 @@ version still receiving security updates was API level 30, but according to
<https://dl.google.com/android/studio/metadata/distributions.json>`__, only 60%
of devices were on that version or newer.
For Python 3.13 we therefore propose the minimum Android version to be 6.0
(API level 23). This would support 98% of active devices, and would
allow us to rely on a number of `dynamic linker improvements
<https://android.googlesource.com/platform/bionic/+/refs/heads/master/android-changes-for-ndk-developers.md>`__
which simplify the use of dynamic libraries.
For Python 3.13 we therefore propose the minimum Android version to be 5.0
(API level 21), which was released in 2014. According to the statistics above,
this would cover 99% of active devices.
Development tools
@ -107,14 +107,15 @@ are:
linker (lld), and headers for all the system libraries.
Binary compatibility between libraries compiled with different versions of the
NDK is generally very good, but for reproducbility it would be best for each
NDK is generally very good, but for reproducibility it would be best for each
Python version to stick with one NDK version throughout its life. For Python
3.13, this would be the current NDK long-term support version, r26.
Each NDK version can be set to target any of a wide range of Android versions.
For example, NDK r26 supports API levels 21 to 34. However, binaries compiled
for an older Android version will usually keep on working indefinitely on
newer versions; exceptions to this rule are only made for security reasons.
For example, NDK r26 supports :ref:`API levels <738-os-versions>` 21 to 34.
However, binaries compiled for an older Android version will usually keep on
working indefinitely on newer versions; exceptions to this rule are only made
for security reasons.
* Gradle is the tool used to build complete, deployable apps.
@ -150,7 +151,7 @@ For Python 3.13 we propose that Tier 3 support will only cover the 64-bit platfo
`less than 10% and steadily falling
<https://github.com/chaquo/chaquopy/issues/709#issuecomment-1744541892>`__.
It would also be more difficult to cover with an automated test, since there
It would also be more difficult to cover with a reliable buildbot, since there
are no native hosts available for the emulator (ARM64 Macs don't have hardware
support for ARM32 code). Although cross-architecture emulation is possible, it
has much worse performance and stability, which is why the ``armeabi-v7a``
@ -182,10 +183,9 @@ guaranteed to be supported in future Android versions.
Android does provide a command-line shell, but this is intended only for use by
developers, and is not available to the typical end user.
For these reasons, the primary way of running Python on Android will be by
loading ``libpython3.x.so`` into the main app process. Although there will also
be a ``python3.x`` executable linked against ``libpython3.x.so``, this is only
for debugging, not production use.
For these reasons, the recommended way of running Python on Android will be by
loading ``libpython3.x.so`` into the main app process. A ``python3.x``
executable will not be officially supported on this platform.
Specification
@ -215,16 +215,23 @@ Linkage
-------
For the reasons discussed in `App lifecycle`_, Python will be included in the
app as a dynamic ``libpython3.x.so`` library. All Android extension modules should
be linked against this library. This allows using the
``-Wl,--no-undefined`` option to detect missing symbols at build time, which can
be a significant time-saver.
app as a dynamic ``libpython3.x.so`` library which can be loaded into an app
using ``dlopen``.
Unlike Linux, Android does not implicitly use a dlopened library to resolve
relocations in subsequently-loaded libraries, `even if RTLD_GLOBAL is used
<https://github.com/android/ndk/issues/1244#issuecomment-620310397>`__. All
Python extension modules must therefore be explicitly linked against
``libpython3.x.so`` when building for Android.
An extension module linked against ``libpython3.x.so`` cannot be loaded by an
executable that has been statically linked against ``libpython3.x.a``.
Therefore, a static ``libpython3.x.a`` library will not be supported on Android.
This is the same pattern used by CPython on Windows.
This approach also allows using the ``-Wl,--no-undefined`` option to detect
missing symbols at build time, which can be a significant time-saver.
Unlike iOS, Android allows dynamic libraries to be loaded from any location, so
a directory tree containing co-located .py, .pyc and .so files can be handled by
Python's standard importer.
@ -233,28 +240,33 @@ Python's standard importer.
Standard library
----------------
Unsupported modules
'''''''''''''''''''
A number of standard library modules will not be supported on Android because
the underlying C APIs are not available:
* ``curses`` and ``readline``
* ``dbm.gnu`` and ``dbm.ndbm``
* ``grp`` and ``spwd``
* ``grp``
* ``multiprocessing`` although subprocesses in general are allowed (see `App
lifecycle`_), Android does not support any part of the `System V IPC API
<https://man7.org/linux/man-pages/man7/sysvipc.7.html>`__.
* ``tkinter`` and ``turtle`` these would require an Android build of Tk
itself, which is not officially supported.
Platform identification
-----------------------
``sys``
'''''''
``sys.platform`` will return ``"android"``. Although Android is based on Linux,
it differs in enough significant ways that a separate name is justified.
When embedded in an Android app, the C-level stdio streams are not connected to
anything. So in this mode, ``sys.stdout`` and ``sys.stderr`` will be redirected
to the system `Logcat <https://developer.android.com/studio/debug/logcat>`__,
which can be viewed with the Android development tools. ``sys.stdin`` will
always return EOF.
``platform``
''''''''''''
@ -269,11 +281,24 @@ by ``os.uname()``, with the exception of:
In addition, a ``platform.android_ver()`` method will be added, which returns a
namedtuple containing the following:
* ``release`` - Android version, as a string (e.g. ``"14"``)
* ``api_level`` - Android API level, as an integer (e.g. ``34``)
* ``release`` - Android version of the device, as a string (e.g. ``"14"``)
* ``api_level`` - :ref:`API level <738-os-versions>` of the device, as an
integer (e.g. ``34``)
* ``min_api_level`` - Minimum API level this build of Python can run on, as
an integer (e.g. ``23``). This is the same as ``sys.getandroidapilevel``.
* ``model`` - the model name of the device, as a string (e.g. ``"Pixel 7"``).
* ``manufacturer`` - `manufacturer
<https://developer.android.com/reference/android/os/Build#MANUFACTURER>`__ of
the device, as a string (e.g. ``"Google"``)
* ``model`` - `model name
<https://developer.android.com/reference/android/os/Build#MODEL>`__ of the
device, as a string (e.g. ``"Pixel 7"``)
* ``device`` - `device name
<https://developer.android.com/reference/android/os/Build#DEVICE>`__ of the
device, as a string (e.g. ``"panther"``)
Which one of ``model`` and ``device`` is more likely to be unique, and which one
is more likely to resemble the marketing name, varies between different
manufacturers.
``os``
''''''
@ -313,8 +338,8 @@ Packaging
Android wheels will use tags in the format ``android_<api-level>_<abi>``. For
example:
* ``android_23_arm64_v8a``
* ``android_23_x86_64``
* ``android_21_arm64_v8a``
* ``android_21_x86_64``
For the meaning of ``<api-level>``, see `OS versions`_. In the context of
the wheel tag, it indicates the minimum Android version that was selected when