Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 0b36ed8

Browse files
committed
Merge branch 'main' into opt
2 parents 08d0d3f + 9690269 commit 0b36ed8

12 files changed

+246
-72
lines changed

README.rst

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ Python Android Support
22
======================
33

44
This is a meta-package for building a version of CPython that can be embedded
5-
into an Android project. It supports Python versions 3.6, 3.7, and 3.8.
5+
into an Android project. It supports Python versions 3.7 through 3.10
6+
(inclusive); experimental support for Py3.11 also exists.
67

78
It works by downloading, patching, and building CPython and selected pre-
89
requisites, packaging them as linkable dynamic libraries, and packaging
@@ -45,10 +46,15 @@ When you do a local build, you can use the ``support_package = ...`` configurati
4546
option in a briefcase app's ``pyproject.toml`` to point the app at your local
4647
support library.
4748

48-
You can run ``python3 test_all_extensions_built.py dist/Python-*-Android-support.zip``
49+
You can run ``python3 test_all_extensions_built.py dist/Python-*-Android-support.custom.zip``
4950
to quickly validate that the expected compiled extension modules are available for a
5051
given build.
5152

53+
You can also use the `Python Support testbed
54+
<https://github.com/beeware/Python-support-testbed>` project to validate that
55+
the core pieces of the support package (including modules that have historically
56+
been problematic) are all present and working.
57+
5258
To run the CPython test suite within an app context, you can add this code to a
5359
briefcase app::
5460

@@ -68,5 +74,3 @@ briefcase app::
6874

6975
Note that you must modify the ``pythonhome`` excludes list for this to work properly,
7076
which will make the support package larger.
71-
72-
.. _can be downloaded: https://briefcase-support.org/python?platform=android&version=3.7

main.sh

+33-22
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,40 @@ function build_one_abi() {
8282
esac
8383

8484
local PYTHON_SOVERSION="${PYTHON_VERSION}"
85-
if [ "$PYTHON_VERSION" = "3.7" ] || [ "$PYTHON_VERSION" = "3.6" ] ; then
86-
# 3.6 and 3.7 use 3.6m/3.7m
85+
local PYTHON_EXTRA_CONFIGURE_FLAGS=""
86+
if [ "$PYTHON_VERSION" = "3.7" ] ; then
87+
# 3.7 uses 3.7m
8788
PYTHON_SOVERSION="${PYTHON_VERSION}m"
8889
fi
8990

91+
if [ "$PYTHON_VERSION" = "3.11" ] ; then
92+
# 3.11 requires --with-build-python
93+
PYTHON_EXTRA_CONFIGURE_FLAGS="--with-build-python=python3.11"
94+
fi
95+
9096
# We use Docker to run the build. We rely on Docker's build cache to allow the
9197
# build to be speedy if nothing changed, approx 1-2 seconds for a no-op build.
9298
# The cache persists even if no Docker "tags" point to the image.
9399
#
94100
# We do also use a temporary tag name so that we can use `rsync` to pull some
95101
# data out of the image.
96102
#
97-
# For debugging the Docker image, you can use the name python-android-support-local:latest.
98-
TAG_NAME="python-android-support-local:$(python3 -c 'import random; print(random.randint(0, 1e16))')"
99-
DOCKER_BUILDKIT=1 docker build --tag ${TAG_NAME} --tag python-android-support-local:latest \
103+
# For debugging the Docker image, you can use the name python-android-support-<version>:<abi>.
104+
# e.g. python-android-support-3.7:arm64-v8a
105+
TAG_NAME="python-android-support-${PYTHON_VERSION}:${TARGET_ABI_SHORTNAME}"
106+
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 --tag ${TAG_NAME} \
100107
--build-arg PYTHON_VERSION="${PYTHON_VERSION}" --build-arg PYTHON_SOVERSION="${PYTHON_SOVERSION}" \
101108
--build-arg COMPRESS_LEVEL="${COMPRESS_LEVEL}" --build-arg COMPILER_TRIPLE="${COMPILER_TRIPLE}" \
102109
--build-arg OPENSSL_BUILD_TARGET="$OPENSSL_BUILD_TARGET" --build-arg TARGET_ABI_SHORTNAME="$TARGET_ABI_SHORTNAME" \
103110
--build-arg TOOLCHAIN_TRIPLE="$TOOLCHAIN_TRIPLE" --build-arg ANDROID_API_LEVEL="$ANDROID_API_LEVEL" \
111+
--build-arg PYTHON_EXTRA_CONFIGURE_FLAGS="$PYTHON_EXTRA_CONFIGURE_FLAGS" \
104112
-f python.Dockerfile .
105113
# Extract the build artifacts we need to create our zip file.
106114
docker run -v "${PWD}"/build/"${PYTHON_VERSION}"/:/mnt/ --rm --entrypoint rsync "$TAG_NAME" -a /opt/python-build/approot/. /mnt/.
107115
# Extract header files
108116
docker run -v "${PWD}"/build/"${PYTHON_VERSION}"/app/include/:/mnt/ --rm --entrypoint rsync "$TAG_NAME" -a /opt/python-build/built/python/include/ /mnt/
117+
# Extract log files
118+
docker run -v "${PWD}"/build/"${PYTHON_VERSION}"/logs/:/mnt/ --rm --entrypoint rsync "$TAG_NAME" -a /opt/python-build/logs/ /mnt/
109119

110120
# Docker creates files as root; reown as the local user
111121
fix_permissions
@@ -114,8 +124,6 @@ function build_one_abi() {
114124
mv "${PWD}"/build/"${PYTHON_VERSION}"/app/include/python"${PYTHON_SOVERSION}"/pyconfig.h "${PWD}"/build/"${PYTHON_VERSION}"/app/include/python"${PYTHON_SOVERSION}"/pyconfig-${TARGET_ABI_SHORTNAME}.h
115125
# Inject a platform-agnostic pyconfig.h wrapper.
116126
cp "${PWD}/patches/all/pyconfig.h" "${PWD}"/build/"${PYTHON_VERSION}"/app/include/python"${PYTHON_SOVERSION}"/
117-
# Remove temporary local tag.
118-
docker rmi "$TAG_NAME" > /dev/null
119127
}
120128

121129
# Download a file into downloads/$name/$filename and verify its sha256sum.
@@ -173,7 +181,7 @@ function main() {
173181
# DEFAULT_* variables for inclusion into help output.
174182
local DEFAULT_VERSION="3.7"
175183
local VERSION="$DEFAULT_VERSION"
176-
local DEFAULT_TARGET_ABIS="x86,x86_64,armeabi-v7a,arm64-v8a"
184+
local DEFAULT_TARGET_ABIS="x86_64,arm64-v8a"
177185
local TARGET_ABIS="$DEFAULT_TARGET_ABIS"
178186
local DEFAULT_COMPRESS_LEVEL="8"
179187
local COMPRESS_LEVEL="$DEFAULT_COMPRESS_LEVEL"
@@ -200,15 +208,15 @@ function main() {
200208
201209
Build ZIP file of Python resources for Android, including CPython compiled as a .so.
202210
203-
-v: Specify Python version to build. For example: -v 3.6
211+
-v: Specify Python version to build. For example: -v 3.7
204212
Default: ${DEFAULT_VERSION}
205213
206214
-a: Specify Android ABIs to build, separated by commas. For example: -a x86,arm64-v8a
207215
Default: ${TARGET_ABIS}
208216
209217
-n: Specify build number. If specified, this gets added to the filename prepended by a dot.
210-
For example, -n b5 would create Python-3.6-Android-support.b5.zip when building Python 3.6.
211-
By default, e.g. for Python 3.6, we generate the file Python-3.6-Android-support.zip.
218+
For example, -n b5 would create Python-3.X-Android-support.b5.zip when building Python 3.X.
219+
By default, e.g. for Python 3.X, we generate the file Python-3.X-Android-support.custom.zip.
212220
213221
-z: Specify compression level to use when creating ZIP files. For example, -z 0 is fastest.
214222
Default: ${DEFAULT_COMPRESS_LEVEL}
@@ -221,7 +229,7 @@ Build ZIP file of Python resources for Android, including CPython compiled as a
221229
local BUILD_TAG
222230
if [ -z "${BUILD_NUMBER:-}" ]; then
223231
echo "Building untagged build"
224-
BUILD_TAG=""
232+
BUILD_TAG=".custom"
225233
else
226234
echo "Building ${BUILD_NUMBER}"
227235
BUILD_TAG=".${BUILD_NUMBER}"
@@ -230,27 +238,30 @@ Build ZIP file of Python resources for Android, including CPython compiled as a
230238
echo "Downloading compile-time dependencies."
231239

232240
download jdk "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u242-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u242b08.tar.gz" "f39b523c724d0e0047d238eb2bb17a9565a60574cf651206c867ee5fc000ab43"
233-
download ndk "https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip" "8381c440fe61fcbb01e209211ac01b519cd6adf51ab1c2281d5daad6ca4c8c8c"
234-
download openssl "https://www.openssl.org/source/openssl-1.1.1i.tar.gz" "e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242"
241+
download ndk "https://dl.google.com/android/repository/android-ndk-r23b-linux.zip" "c6e97f9c8cfe5b7be0a9e6c15af8e7a179475b7ded23e2d1c1fa0945d6fb4382"
242+
download openssl "https://www.openssl.org/source/openssl-1.1.1o.tar.gz" "9384a2b0570dd80358841464677115df785edb941c71211f75076d72fe6b438f"
235243
download libffi "https://github.com/libffi/libffi/releases/download/v3.3/libffi-3.3.tar.gz" "72fba7922703ddfa7a028d513ac15a85c8d54c8d67f55fa5a4802885dc652056"
236244
download xz "https://tukaani.org/xz/xz-5.2.5.tar.gz" "f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10"
237245
download bzip2 "https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz" "ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269"
238-
download sqlite3 "http://archive.ubuntu.com/ubuntu/pool/main/s/sqlite3/sqlite3_3.11.0.orig.tar.xz" "79fb8800b8744337d5317270899a5a40612bb76f81517e131bf496c26b044490"
239-
download rubicon-java "https://github.com/beeware/rubicon-java/archive/v0.2.5.tar.gz" "23ec17bb5cafec87e286b308f3b0cdd9eb004f59610c9bb37cacfa98ee1ea11c"
246+
download sqlite3 "https://github.com/sqlite/sqlite/archive/refs/tags/version-3.35.0.zip" "f85ba70e340428fbf45ed1bf390ddcc622c7f8f4b30e60d063c6b6f8d78924ae"
247+
download rubicon-java "https://github.com/beeware/rubicon-java/archive/v0.2.6.tar.gz" "0386d84182b347c0e64947579c3e853e9b72d375094fa6d00942f9a7635ca6d1"
240248

241249
echo "Downloading Python version."
242250
case "$VERSION" in
243-
3.6)
244-
download "python-3.6" "https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tar.xz" "70953a9b5d6891d92e65d184c3512126a15814bee15e1eff2ddcce04334e9a99"
245-
;;
246251
3.7)
247-
download "python-3.7" "https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tar.xz" "91923007b05005b5f9bd46f3b9172248aea5abc1543e8a636d59e629c3331b01"
252+
download "python-3.7" "https://www.python.org/ftp/python/3.7.13/Python-3.7.13.tar.xz" "99f106275df8899c3e8cb9d7c01ce686c202ef275953301427194693de5bef84"
248253
;;
249254
3.8)
250-
download "python-3.8" "https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tar.xz" "ddcc1df16bb5b87aa42ec5d20a5b902f2d088caa269b28e01590f97a798ec50a"
255+
download "python-3.8" "https://www.python.org/ftp/python/3.8.13/Python-3.8.13.tar.xz" "6f309077012040aa39fe8f0c61db8c0fa1c45136763299d375c9e5756f09cf57"
251256
;;
252257
3.9)
253-
download "python-3.9" "https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tar.xz" "991c3f8ac97992f3d308fefeb03a64db462574eadbff34ce8bc5bb583d9903ff"
258+
download "python-3.9" "https://www.python.org/ftp/python/3.9.12/Python-3.9.12.tar.xz" "2cd94b20670e4159c6d9ab57f91dbf255b97d8c1a1451d1c35f4ec1968adf971"
259+
;;
260+
3.10)
261+
download "python-3.10" "https://www.python.org/ftp/python/3.10.4/Python-3.10.4.tar.xz" "80bf925f571da436b35210886cf79f6eb5fa5d6c571316b73568343451f77a19"
262+
;;
263+
3.11)
264+
download "python-3.11" "https://www.python.org/ftp/python/3.11.0/Python-3.11.0b1.tar.xz" "dccac9b03dd3fe5cd10bc547579eb0be81a1d8971ec2a866b03dec5391f5ad25"
254265
;;
255266
*)
256267
echo "Invalid Python version: $VERSION"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
From 86b7aee31eb66b99e2e9ff55f83cb2e6393dd8d3 Mon Sep 17 00:00:00 2001
2+
From: Asheesh Laroia <[email protected]>
3+
Date: Fri, 19 Jun 2020 16:47:25 -0700
4+
Subject: [PATCH] Parse certificate authority certs from Android
5+
/etc/security/cacerts directory
6+
7+
---
8+
Lib/ssl.py | 11 +++++++++++
9+
Lib/test/test_ssl.py | 1 +
10+
2 files changed, 12 insertions(+)
11+
12+
diff --git a/Lib/ssl.py b/Lib/ssl.py
13+
index 0726caee49..3878bdd8fa 100644
14+
--- a/Lib/ssl.py
15+
+++ b/Lib/ssl.py
16+
@@ -572,6 +572,17 @@ class SSLContext(_SSLContext):
17+
if sys.platform == "win32":
18+
for storename in self._windows_cert_stores:
19+
self._load_windows_store_certs(storename, purpose)
20+
+ if os.path.exists('/etc/security/cacerts'):
21+
+ certs = []
22+
+ for basename in os.listdir('/etc/security/cacerts'):
23+
+ with open('/etc/security/cacerts/' + basename) as fd:
24+
+ s = fd.read()
25+
+ if 'END CERTIFICATE' not in s:
26+
+ continue
27+
+ lines = s.split('\n')
28+
+ line_end_certificate = [i for i, line in enumerate(lines) if 'END CERTIFICATE' in line][0]
29+
+ certs.append('\n'.join(lines[0:line_end_certificate+1]))
30+
+ self.load_verify_locations(None, None, '\n'.join(certs))
31+
self.set_default_verify_paths()
32+
33+
if hasattr(_SSLContext, 'minimum_version'):
34+
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
35+
index 0bc0a8c452..3c79debf3e 100644
36+
--- a/Lib/test/test_ssl.py
37+
+++ b/Lib/test/test_ssl.py
38+
@@ -1580,6 +1580,7 @@ class ContextTests(unittest.TestCase):
39+
@unittest.skipIf(sys.platform == "win32", "not-Windows specific")
40+
@unittest.skipIf(IS_LIBRESSL, "LibreSSL doesn't support env vars")
41+
def test_load_default_certs_env(self):
42+
+ raise unittest.SkipTest("Skipping this test for Python within an Android app")
43+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
44+
with support.EnvironmentVarGuard() as env:
45+
env["SSL_CERT_DIR"] = CAPATH
46+
--
47+
2.27.0
48+

patches/3.10/Setup.local

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*disabled*
2+
3+
_gdbm
4+
_dbm
5+
grp

patches/3.10/series

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
01_python_ssl_module_add_android_certificates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
From 86b7aee31eb66b99e2e9ff55f83cb2e6393dd8d3 Mon Sep 17 00:00:00 2001
2+
From: Asheesh Laroia <[email protected]>
3+
Date: Fri, 19 Jun 2020 16:47:25 -0700
4+
Subject: [PATCH] Parse certificate authority certs from Android
5+
/etc/security/cacerts directory
6+
7+
---
8+
Lib/ssl.py | 11 +++++++++++
9+
Lib/test/test_ssl.py | 1 +
10+
2 files changed, 12 insertions(+)
11+
12+
diff --git a/Lib/ssl.py b/Lib/ssl.py
13+
index 0726caee49..3878bdd8fa 100644
14+
--- a/Lib/ssl.py
15+
+++ b/Lib/ssl.py
16+
@@ -572,6 +572,17 @@ class SSLContext(_SSLContext):
17+
if sys.platform == "win32":
18+
for storename in self._windows_cert_stores:
19+
self._load_windows_store_certs(storename, purpose)
20+
+ if os.path.exists('/etc/security/cacerts'):
21+
+ certs = []
22+
+ for basename in os.listdir('/etc/security/cacerts'):
23+
+ with open('/etc/security/cacerts/' + basename) as fd:
24+
+ s = fd.read()
25+
+ if 'END CERTIFICATE' not in s:
26+
+ continue
27+
+ lines = s.split('\n')
28+
+ line_end_certificate = [i for i, line in enumerate(lines) if 'END CERTIFICATE' in line][0]
29+
+ certs.append('\n'.join(lines[0:line_end_certificate+1]))
30+
+ self.load_verify_locations(None, None, '\n'.join(certs))
31+
self.set_default_verify_paths()
32+
33+
if hasattr(_SSLContext, 'minimum_version'):
34+
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
35+
index 0bc0a8c452..3c79debf3e 100644
36+
--- a/Lib/test/test_ssl.py
37+
+++ b/Lib/test/test_ssl.py
38+
@@ -1580,6 +1580,7 @@ class ContextTests(unittest.TestCase):
39+
@unittest.skipIf(sys.platform == "win32", "not-Windows specific")
40+
@unittest.skipIf(IS_LIBRESSL, "LibreSSL doesn't support env vars")
41+
def test_load_default_certs_env(self):
42+
+ raise unittest.SkipTest("Skipping this test for Python within an Android app")
43+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
44+
with support.EnvironmentVarGuard() as env:
45+
env["SSL_CERT_DIR"] = CAPATH
46+
--
47+
2.27.0
48+

patches/3.11/Setup.local

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*disabled*
2+
3+
_gdbm
4+
_dbm
5+
grp

patches/3.11/series

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
01_python_ssl_module_add_android_certificates

patches/3.7/Setup.local

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*disabled*
2+
3+
_gdbm
4+
_dbm
5+
grp

patches/3.8/Setup.local

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*disabled*
2+
3+
_gdbm
4+
_dbm
5+
grp

patches/3.9/Setup.local

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*disabled*
2+
3+
_gdbm
4+
_dbm
5+
grp

0 commit comments

Comments
 (0)