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 ELF binary format. It does not use glibc, instead providing its own C
library implementation called Bionic. As a result, it is generally not library implementation called Bionic. As a result, it is generally not
binary-compatible with any other Linux distribution, even if the architecture 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, 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 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 Android, recent versions (since 3.6) can already be compiled for Android with
minimal patching. minimal patching.
.. _738-os-versions:
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% <https://dl.google.com/android/studio/metadata/distributions.json>`__, only 60%
of devices were on that version or newer. of devices were on that version or newer.
For Python 3.13 we therefore propose the minimum Android version to be 6.0 For Python 3.13 we therefore propose the minimum Android version to be 5.0
(API level 23). This would support 98% of active devices, and would (API level 21), which was released in 2014. According to the statistics above,
allow us to rely on a number of `dynamic linker improvements this would cover 99% of active devices.
<https://android.googlesource.com/platform/bionic/+/refs/heads/master/android-changes-for-ndk-developers.md>`__
which simplify the use of dynamic libraries.
Development tools Development tools
@ -107,14 +107,15 @@ are:
linker (lld), and headers for all the system libraries. linker (lld), and headers for all the system libraries.
Binary compatibility between libraries compiled with different versions of the 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 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. 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. 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 example, NDK r26 supports :ref:`API levels <738-os-versions>` 21 to 34.
for an older Android version will usually keep on working indefinitely on However, binaries compiled for an older Android version will usually keep on
newer versions; exceptions to this rule are only made for security reasons. 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. * 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 `less than 10% and steadily falling
<https://github.com/chaquo/chaquopy/issues/709#issuecomment-1744541892>`__. <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 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 support for ARM32 code). Although cross-architecture emulation is possible, it
has much worse performance and stability, which is why the ``armeabi-v7a`` 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 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. developers, and is not available to the typical end user.
For these reasons, the primary way of running Python on Android will be by For these reasons, the recommended way of running Python on Android will be by
loading ``libpython3.x.so`` into the main app process. Although there will also loading ``libpython3.x.so`` into the main app process. A ``python3.x``
be a ``python3.x`` executable linked against ``libpython3.x.so``, this is only executable will not be officially supported on this platform.
for debugging, not production use.
Specification Specification
@ -215,16 +215,23 @@ Linkage
------- -------
For the reasons discussed in `App lifecycle`_, Python will be included in the 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 app as a dynamic ``libpython3.x.so`` library which can be loaded into an app
be linked against this library. This allows using the using ``dlopen``.
``-Wl,--no-undefined`` option to detect missing symbols at build time, which can
be a significant time-saver. 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 An extension module linked against ``libpython3.x.so`` cannot be loaded by an
executable that has been statically linked against ``libpython3.x.a``. executable that has been statically linked against ``libpython3.x.a``.
Therefore, a static ``libpython3.x.a`` library will not be supported on Android. Therefore, a static ``libpython3.x.a`` library will not be supported on Android.
This is the same pattern used by CPython on Windows. 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 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 a directory tree containing co-located .py, .pyc and .so files can be handled by
Python's standard importer. Python's standard importer.
@ -233,28 +240,33 @@ Python's standard importer.
Standard library Standard library
---------------- ----------------
Unsupported modules
'''''''''''''''''''
A number of standard library modules will not be supported on Android because A number of standard library modules will not be supported on Android because
the underlying C APIs are not available: the underlying C APIs are not available:
* ``curses`` and ``readline`` * ``curses`` and ``readline``
* ``dbm.gnu`` and ``dbm.ndbm`` * ``dbm.gnu`` and ``dbm.ndbm``
* ``grp`` and ``spwd`` * ``grp``
* ``multiprocessing`` although subprocesses in general are allowed (see `App * ``multiprocessing`` although subprocesses in general are allowed (see `App
lifecycle`_), Android does not support any part of the `System V IPC API lifecycle`_), Android does not support any part of the `System V IPC API
<https://man7.org/linux/man-pages/man7/sysvipc.7.html>`__. <https://man7.org/linux/man-pages/man7/sysvipc.7.html>`__.
* ``tkinter`` and ``turtle`` these would require an Android build of Tk * ``tkinter`` and ``turtle`` these would require an Android build of Tk
itself, which is not officially supported. itself, which is not officially supported.
Platform identification
-----------------------
``sys`` ``sys``
''''''' '''''''
``sys.platform`` will return ``"android"``. Although Android is based on Linux, ``sys.platform`` will return ``"android"``. Although Android is based on Linux,
it differs in enough significant ways that a separate name is justified. 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`` ``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 In addition, a ``platform.android_ver()`` method will be added, which returns a
namedtuple containing the following: namedtuple containing the following:
* ``release`` - Android version, as a string (e.g. ``"14"``) * ``release`` - Android version of the device, as a string (e.g. ``"14"``)
* ``api_level`` - Android API level, as an integer (e.g. ``34``) * ``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 * ``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``. 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`` ``os``
'''''' ''''''
@ -313,8 +338,8 @@ Packaging
Android wheels will use tags in the format ``android_<api-level>_<abi>``. For Android wheels will use tags in the format ``android_<api-level>_<abi>``. For
example: example:
* ``android_23_arm64_v8a`` * ``android_21_arm64_v8a``
* ``android_23_x86_64`` * ``android_21_x86_64``
For the meaning of ``<api-level>``, see `OS versions`_. In the context of 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 the wheel tag, it indicates the minimum Android version that was selected when