From 396191745f211771aa703bc19a2860791ca070e0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 27 Sep 2024 10:01:52 -0700 Subject: [PATCH] Build Python for Android --- .appveyor.yml | 44 +++- android/.gitignore | 13 ++ android/README.md | 33 +++ android/android-env.sh | 107 +++++++++ android/build-all.sh | 9 + android/build.sh | 237 ++++++++++++++++++++ android/package-for-dart.sh | 55 +++++ android/patches/bldlibrary.patch | 49 ++++ android/patches/dynload_shlib.patch | 12 + android/patches/grp.patch | 16 ++ android/patches/lfs.patch | 16 ++ android/patches/python_for_build_deps.patch | 16 ++ android/patches/soname.patch | 17 ++ android/patches/sysroot_paths.patch | 14 ++ android/python-android-dart.exclude | 27 +++ 15 files changed, 656 insertions(+), 9 deletions(-) create mode 100644 android/.gitignore create mode 100644 android/README.md create mode 100644 android/android-env.sh create mode 100755 android/build-all.sh create mode 100755 android/build.sh create mode 100755 android/package-for-dart.sh create mode 100644 android/patches/bldlibrary.patch create mode 100644 android/patches/dynload_shlib.patch create mode 100644 android/patches/grp.patch create mode 100644 android/patches/lfs.patch create mode 100644 android/patches/python_for_build_deps.patch create mode 100644 android/patches/soname.patch create mode 100644 android/patches/sysroot_paths.patch create mode 100644 android/python-android-dart.exclude diff --git a/.appveyor.yml b/.appveyor.yml index 296b609..5c866fa 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,13 +4,16 @@ skip_branch_with_pr: true environment: python_stack: python 3.12 PYTHON_VERSION: 3.12.6 + GITHUB_TOKEN: + secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 matrix: - # - job_name: Build Python for Linux - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu + - job_name: Build Python for Android + APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + NDK_VERSION: r27 - - job_name: Build Python for Windows - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + # - job_name: Build Python for Windows + # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 matrix: fast_finish: true @@ -19,19 +22,42 @@ stack: $python_stack for: # ====================================== - # Build Python for Linux + # Build Python for Android # ====================================== - matrix: only: - - job_name: Build Python for Linux + - job_name: Build Python for Android install: - - echo install + - read ver_maj ver_min < <(echo $PYTHON_VERSION | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') + - export PYTHON_VERSION_SHORT="$ver_maj.$ver_min" build_script: - - sh: | - echo build + - cd android + + # Build all Python ABIs + - ./build-all.sh $PYTHON_VERSION + + # Package support package for use with mobile-forge + - mkdir -p dist + - tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support + + # Package individual ABIs for use with serious_python Flutter package + - ./package-for-dart.sh install $PYTHON_VERSION arm64-v8a + - ./package-for-dart.sh install $PYTHON_VERSION armeabi-v7a + - ./package-for-dart.sh install $PYTHON_VERSION x86_64 + + # Push all archives to artifacts + - find dist -maxdepth 1 -type f -iname python-android-*.tar.gz -exec appveyor PushArtifact -DeploymentName python-android {} \; + + test: off + + deploy: + provider: GitHub + auth_token: $(GITHUB_TOKEN) + release: v$(PYTHON_VERSION_SHORT) + artifact: python-android # ====================================== # Build Python for Windows diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..96992bd --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +.envrc +.vscode/ +build +dist +downloads +install +local +support +*.dist-info +__pycache__ +*.log +*.gz +*.DS_Store \ No newline at end of file diff --git a/android/README.md b/android/README.md new file mode 100644 index 0000000..37aa5ef --- /dev/null +++ b/android/README.md @@ -0,0 +1,33 @@ +# Python for Android + +Scripts and CI jobs for building Python 3 for Android. + +* Can be run on both Linux and macOS. +* Build Python 3.12 - specific or the last minor version. +* Installs NDK r26d or use pre-installed one with path configured by `NDK_HOME` variable. +* Creates Python installation with a structure suitable for https://github.com/flet-dev/mobile-forge + +## Usage + +To build the latest minor version of Python 3.12 for selected Android API: + +``` +./build.sh 3.12 arm64-v8a +``` + +To build all ABIs: + +``` +./build-all.sh 3.12 +``` + +## Credits + +Build process depends on: +* https://github.com/beeware/cpython-android-source-deps + +Based on the work from: +* https://github.com/chaquo/chaquopy/tree/master/target +* https://github.com/beeware/Python-Android-support +* https://github.com/beeware/cpython-android-source-deps +* https://github.com/GRRedWings/python3-android \ No newline at end of file diff --git a/android/android-env.sh b/android/android-env.sh new file mode 100644 index 0000000..19f60bd --- /dev/null +++ b/android/android-env.sh @@ -0,0 +1,107 @@ +fail() { + echo "$1" >&2 + exit 1 +} + +if [[ -z "${NDK_HOME-}" ]]; then + NDK_HOME=$HOME/ndk/$NDK_VERSION + echo "NDK_HOME environment variable is not set." + if [ ! -d $NDK_HOME ]; then + echo "Installing NDK $NDK_VERSION to $NDK_HOME" + + if [ $(uname) = "Darwin" ]; then + seven_zip=$downloads/7zip/7zz + if ! test -f $seven_zip; then + echo "Installing 7-zip" + mkdir -p $(dirname $seven_zip) + cd $(dirname $seven_zip) + curl -#OL https://www.7-zip.org/a/7z2301-mac.tar.xz + tar -xf 7z2301-mac.tar.xz + cd - + fi + + ndk_dmg=android-ndk-$NDK_VERSION-darwin.dmg + if ! test -f $downloads/$ndk_dmg; then + echo ">>> Downloading $ndk_dmg" + curl -#L -o $downloads/$ndk_dmg https://dl.google.com/android/repository/$ndk_dmg + fi + + cd $downloads + $seven_zip x -snld $ndk_dmg + mkdir -p $(dirname $NDK_HOME) + mv Android\ NDK\ */AndroidNDK*.app/Contents/NDK $NDK_HOME + rm -rf Android\ NDK\ * + cd - + else + ndk_zip=android-ndk-$NDK_VERSION-linux.zip + if ! test -f $downloads/$ndk_zip; then + echo ">>> Downloading $ndk_zip" + curl -#L -o $downloads/$ndk_zip https://dl.google.com/android/repository/$ndk_zip + fi + cd $downloads + unzip -oq $ndk_zip + mkdir -p $(dirname $NDK_HOME) + mv android-ndk-$NDK_VERSION $NDK_HOME + cd - + echo "NDK installed to $NDK_HOME" + fi + else + echo "NDK $NDK_VERSION is already installed in $NDK_HOME" + fi +else + echo "NDK home: $NDK_HOME" +fi + +if [ $host_triplet = "arm-linux-androideabi" ]; then + clang_triplet=armv7a-linux-androideabi +else + clang_triplet=$host_triplet +fi + +# These variables are based on BuildSystemMaintainers.md above, and +# $NDK_HOME/build/cmake/android.toolchain.cmake. +toolchain=$(echo $NDK_HOME/toolchains/llvm/prebuilt/*) +export AR="$toolchain/bin/llvm-ar" +export AS="$toolchain/bin/llvm-as" +export CC="$toolchain/bin/${clang_triplet}$api_level-clang" +export CXX="${CC}++" +export LD="$toolchain/bin/ld" +export NM="$toolchain/bin/llvm-nm" +export RANLIB="$toolchain/bin/llvm-ranlib" +export READELF="$toolchain/bin/llvm-readelf" +export STRIP="$toolchain/bin/llvm-strip" + +# The quotes make sure the wildcard in the `toolchain` assignment has been expanded. +for path in "$AR" "$AS" "$CC" "$CXX" "$LD" "$NM" "$RANLIB" "$READELF" "$STRIP"; do + if ! [ -e "$path" ]; then + fail "$path does not exist" + fi +done + +# Use -idirafter so that package-specified -I directories take priority. For example, +# grpcio provides its own BoringSSL headers which must be used rather than our OpenSSL. +export CFLAGS="-idirafter ${prefix:?}/include" +export LDFLAGS="-L${prefix:?}/lib -Wl,--build-id=sha1 -Wl,--no-rosegment" + +# Many packages get away with omitting this on standard Linux, but Android is stricter. +LDFLAGS+=" -lm" + +case $abi in + armeabi-v7a) + CFLAGS+=" -march=armv7-a -mthumb" + ;; + x86) + # -mstackrealign is unnecessary because it's included in the clang launcher script + # which is pointed to by $CC. + ;; +esac + +export PKG_CONFIG="pkg-config --define-prefix" +export PKG_CONFIG_LIBDIR="$prefix/lib/pkgconfig" + +# conda-build variable name +if [ $(uname) = "Darwin" ]; then + export CPU_COUNT=$(sysctl -n hw.ncpu) +else + export CPU_COUNT=$(nproc) +fi diff --git a/android/build-all.sh b/android/build-all.sh new file mode 100755 index 0000000..3297b77 --- /dev/null +++ b/android/build-all.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -eu + +python_version=${1:?} +abis="arm64-v8a armeabi-v7a x86_64 x86" + +for abi in $abis; do + ./build.sh $python_version $abi +done \ No newline at end of file diff --git a/android/build.sh b/android/build.sh new file mode 100755 index 0000000..3205faa --- /dev/null +++ b/android/build.sh @@ -0,0 +1,237 @@ +#!/bin/bash +set -eu + +python_version=${1:?} +abi=${2:?} +NDK_VERSION=r27 +api_level=24 + +bzip2_version=1.0.8-1 +xz_version=5.4.6-0 +libffi_version=3.4.4-2 +openssl_version=3.0.15-0 +sqlite_version=3.45.2-0 + +os=android +build=custom + +project_dir=$(dirname $(realpath $0)) +downloads=$project_dir/downloads + +# build short Python version +read python_version_major python_version_minor < <(echo $python_version | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') +if [[ $python_version =~ ^[0-9]+\.[0-9]+$ ]]; then + python_version=$(curl --silent "https://www.python.org/ftp/python/" | sed -nr "s/^.*\"($python_version_major\.$python_version_minor\.[0-9]+)\/\".*$/\1/p" | sort -rV | head -n 1) + echo "Python version: $python_version" +fi +python_version_short=$python_version_major.$python_version_minor +python_version_int=$(($python_version_major * 100 + $python_version_minor)) + +curl_flags="--disable --fail --location --create-dirs --progress-bar" +mkdir -p $downloads + +case $abi in + armeabi-v7a) + host_triplet=arm-linux-androideabi + ;; + arm64-v8a) + host_triplet=aarch64-linux-android + ;; + x86) + host_triplet=i686-linux-android + ;; + x86_64) + host_triplet=x86_64-linux-android + ;; + *) + fail "Unknown ABI: '$abi'" + ;; +esac + +# create VERSIONS support file +support_versions=$project_dir/support/$python_version_short/$os/VERSIONS +mkdir -p $(dirname $support_versions) +echo ">>> Create VERSIONS file for $os" +echo "Python version: $python_version " > $support_versions +echo "Build: $build" >> $support_versions +echo "Min $os version: $api_level" >> $support_versions +echo "---------------------" >> $support_versions +echo "libFFI: $libffi_version" >> $support_versions +echo "BZip2: $bzip2_version" >> $support_versions +echo "OpenSSL: $openssl_version" >> $support_versions +echo "XZ: $xz_version" >> $support_versions + +# BZip2 +# =============== +bzip2_install=$project_dir/install/$os/$abi/bzip2-$bzip2_version +bzip2_lib=$bzip2_install/lib/libbz2.a +bzip2_filename=bzip2-$bzip2_version-$host_triplet.tar.gz + +echo ">>> Download BZip2 for $abi" +curl $curl_flags -o $downloads/$bzip2_filename \ + https://github.com/beeware/cpython-android-source-deps/releases/download/bzip2-$bzip2_version/$bzip2_filename + +echo ">>> Install BZip2 for $abi" +mkdir -p $bzip2_install +tar zxvf $downloads/$bzip2_filename -C $bzip2_install +touch $bzip2_lib + +# XZ (LZMA) +# ================= +xz_install=$project_dir/install/$os/$abi/xz-$xz_version +xz_lib=$xz_install/lib/liblzma.a +xz_filename=xz-$xz_version-$host_triplet.tar.gz + +echo ">>> Download XZ for $abi" +curl $curl_flags -o $downloads/$xz_filename \ + https://github.com/beeware/cpython-android-source-deps/releases/download/xz-$xz_version/$xz_filename + +echo ">>> Install XZ for $abi" +mkdir -p $xz_install +tar zxvf $downloads/$xz_filename -C $xz_install +touch $xz_lib + +# LibFFI +# ================= +libffi_install=$project_dir/install/$os/$abi/libffi-$libffi_version +libffi_lib=$libffi_install/lib/libffi.a +libffi_filename=libffi-$libffi_version-$host_triplet.tar.gz + +echo ">>> Download LibFFI for $abi" +curl $curl_flags -o $downloads/$libffi_filename \ + https://github.com/beeware/cpython-android-source-deps/releases/download/libffi-$libffi_version/$libffi_filename + +echo ">>> Install LibFFI for $abi" +mkdir -p $libffi_install +tar zxvf $downloads/$libffi_filename -C $libffi_install +touch $libffi_lib + +# OpenSSL +# ================= +openssl_install=$project_dir/install/$os/$abi/openssl-$openssl_version +openssl_lib=$openssl_install/lib/libssl.a +openssl_filename=openssl-$openssl_version-$host_triplet.tar.gz + +echo ">>> Download OpenSSL for $abi" +curl $curl_flags -o $downloads/$openssl_filename \ + https://github.com/beeware/cpython-android-source-deps/releases/download/openssl-$openssl_version/$openssl_filename + +echo ">>> Install OpenSSL for $abi" +mkdir -p $openssl_install +tar zxvf $downloads/$openssl_filename -C $openssl_install +touch $openssl_lib + +# SQLite +# ================= +sqlite_install=$project_dir/install/$os/$abi/sqlite-$sqlite_version +sqlite_lib=$sqlite_install/lib/libsqlite3.la +sqlite_filename=sqlite-$sqlite_version-$host_triplet.tar.gz + +echo ">>> Download SQLite for $abi" +curl $curl_flags -o $downloads/$sqlite_filename \ + https://github.com/beeware/cpython-android-source-deps/releases/download/sqlite-$sqlite_version/$sqlite_filename + +echo ">>> Install SQLite for $abi" +mkdir -p $sqlite_install +tar zxvf $downloads/$sqlite_filename -C $sqlite_install +touch $sqlite_lib + +# Python +# =============== + +build_dir=$project_dir/build/$os/$abi +python_build_dir=$project_dir/build/$os/$abi/python-$python_version +python_install=$project_dir/install/$os/$abi/python-$python_version +python_lib=$sqlite_install/lib/libpython$python_version_short.a +python_filename=Python-$python_version.tgz + +echo ">>> Download Python for $abi" +curl $curl_flags -o $downloads/$python_filename \ + https://www.python.org/ftp/python/$python_version/$python_filename + +echo ">>> Unpack Python for $abi" +rm -rf $build_dir +mkdir -p $build_dir +tar zxvf $downloads/$python_filename -C $build_dir +mv $build_dir/Python-$python_version $python_build_dir +touch $python_build_dir/configure + +echo ">>> Configuring Python build environment for $abi" + +# configure build environment +prefix=$python_build_dir +. $project_dir/android-env.sh + +cd $python_build_dir + +# apply patches +echo ">>> Patching Python for $abi" +patches="dynload_shlib lfs soname" +if [ $python_version_int -le 311 ]; then + patches+=" sysroot_paths" +fi +if [ $python_version_int -ge 311 ]; then + patches+=" python_for_build_deps" +fi +if [ $python_version_int -ge 312 ]; then + patches+=" bldlibrary grp" +fi +for name in $patches; do + patch -p1 -i $project_dir/patches/$name.patch +done + +# Add sysroot paths, otherwise Python 3.8's setup.py will think libz is unavailable. +CFLAGS+=" -I$toolchain/sysroot/usr/include" +LDFLAGS+=" -L$toolchain/sysroot/usr/lib/$host_triplet/$api_level" + +# The configure script omits -fPIC on Android, because it was unnecessary on older versions of +# the NDK (https://bugs.python.org/issue26851). But it's definitely necessary on the current +# version, otherwise we get linker errors like "Parser/myreadline.o: relocation R_386_GOTOFF +# against preemptible symbol PyOS_InputHook cannot be used when making a shared object". +export CCSHARED="-fPIC" + +# Override some tests. +cat > config.site <>> Configuring Python for $abi" +./configure \ + LIBLZMA_CFLAGS="-I$xz_install/include" \ + LIBLZMA_LIBS="-L$xz_install/lib -llzma" \ + BZIP2_CFLAGS="-I$bzip2_install/include" \ + BZIP2_LIBS="-L$bzip2_install/lib -lbz2" \ + LIBFFI_CFLAGS="-I$libffi_install/include" \ + LIBFFI_LIBS="-L$libffi_install/lib -lffi" \ + --host=$host_triplet \ + --build=$(./config.guess) \ + --with-build-python=yes \ + --prefix="$python_install" \ + --enable-ipv6 \ + --with-openssl="$openssl_install" \ + --enable-shared \ + --without-ensurepip \ + 2>&1 | tee -a ../python-$python_version.config.log + +echo ">>> Building Python for $abi" +make all \ + 2>&1 | tee -a ../python-$python_version.build.log + +echo ">>> Installing Python for $abi" +make install \ + 2>&1 | tee -a ../python-$python_version.install.log + +echo ">>> Copying Python dependencies $abi" +cp {$openssl_install,$sqlite_install}/lib/*_python.so $python_install/lib + +echo ">>> Stripping dynamic libraries for $abi" +find $python_install -type f -iname "*.so" -exec $STRIP --strip-unneeded {} \; + +echo ">>> Replacing host platform" +sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $python_install/lib/python$python_version_short/config-$python_version_short/Makefile + +# the end! \ No newline at end of file diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh new file mode 100755 index 0000000..759bf86 --- /dev/null +++ b/android/package-for-dart.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -eu + +install_root=${1:?} +python_version=${2:?} +abi=${3:?} + +script_dir=$(dirname $(realpath $0)) + +# build short Python version +read python_version_major python_version_minor < <(echo $python_version | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') +python_version_short=$python_version_major.$python_version_minor + +# create build dir +build_dir=build/python-$python_version/$abi +rm -rf $build_dir +mkdir -p $build_dir +build_dir=$(realpath $build_dir) + +# create dist dir +mkdir -p dist + +# copy files to build +rsync -av --exclude-from=$script_dir/python-android-dart.exclude $install_root/android/$abi/python-$python_version/* $build_dir + +# create libpythonbundle.so +bundle_dir=$build_dir/libpythonbundle +mkdir -p $bundle_dir + +# modules with *.so files +mv $build_dir/lib/python$python_version_short/lib-dynload $bundle_dir/modules + +# stdlib +# stdlib_zip=$bundle_dir/stdlib.zip +cd $build_dir/lib/python$python_version_short +python -m compileall -b . +find . \( -name '*.so' -or -name '*.py' -or -name '*.typed' \) -type f -delete +rm -rf __pycache__ +rm -rf **/__pycache__ +# zip -r $stdlib_zip . +cd - +mv $build_dir/lib/python$python_version_short $bundle_dir/stdlib + +# compress libpythonbundle +cd $bundle_dir +zip -r ../libpythonbundle.so . +cd - +rm -rf $bundle_dir + +# copy *.so from lib +cp $build_dir/lib/*.so $build_dir +rm -rf $build_dir/lib + +# final archive +tar -czf dist/python-android-dart-$python_version_short-$abi.tar.gz -C $build_dir . \ No newline at end of file diff --git a/android/patches/bldlibrary.patch b/android/patches/bldlibrary.patch new file mode 100644 index 0000000..4dda79f --- /dev/null +++ b/android/patches/bldlibrary.patch @@ -0,0 +1,49 @@ +--- Python-3.12.0-original/configure 2023-11-22 09:33:49 ++++ Python-3.12.0/configure 2023-11-22 10:13:05 +@@ -7476,6 +7476,7 @@ + case $ac_sys_system in + CYGWIN*) + LDLIBRARY='libpython$(LDVERSION).dll.a' ++ BLDLIBRARY='-L. -lpython$(LDVERSION)' + DLLLIBRARY='libpython$(LDVERSION).dll' + ;; + SunOS*) +@@ -24374,7 +24375,7 @@ + # On Android and Cygwin the shared libraries must be linked with libpython. + + if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then +- LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" ++ LIBPYTHON="$BLDLIBRARY" + else + LIBPYTHON='' + fi +--- Python-3.12.0-original/Modules/makesetup 2023-10-02 12:48:14 ++++ Python-3.12.0/Modules/makesetup 2023-11-22 10:11:40 +@@ -86,18 +86,6 @@ + # Newline for sed i and a commands + NL='\ + ' +- +-# Setup to link with extra libraries when making shared extensions. +-# Currently, only Cygwin needs this baggage. +-case `uname -s` in +-CYGWIN*) if test $libdir = . +- then +- ExtraLibDir=. +- else +- ExtraLibDir='$(LIBPL)' +- fi +- ExtraLibs="-L$ExtraLibDir -lpython\$(LDVERSION)";; +-esac + + # Main loop + for i in ${*-Setup} +@@ -286,7 +274,7 @@ + ;; + esac + rule="$file: $objs" +- rule="$rule; \$(BLDSHARED) $objs $libs $ExtraLibs -o $file" ++ rule="$rule; \$(BLDSHARED) $objs $libs \$(LIBPYTHON) -o $file" + echo "$rule" >>$rulesf + done + done diff --git a/android/patches/dynload_shlib.patch b/android/patches/dynload_shlib.patch new file mode 100644 index 0000000..4260a05 --- /dev/null +++ b/android/patches/dynload_shlib.patch @@ -0,0 +1,12 @@ +--- a/Python/dynload_shlib.c ++++ b/Python/dynload_shlib.c +@@ -66,7 +66,8 @@ _PyImport_FindSharedFuncptr(const char *prefix, + char pathbuf[260]; + int dlopenflags=0; + +- if (strchr(pathname, '/') == NULL) { ++ // Chaquopy disabled: this interferes with our workaround in importer.prepare_dlopen. ++ if (0 && strchr(pathname, '/') == NULL) { + /* Prefix bare filename with "./" */ + PyOS_snprintf(pathbuf, sizeof(pathbuf), "./%-.255s", pathname); + pathname = pathbuf; diff --git a/android/patches/grp.patch b/android/patches/grp.patch new file mode 100644 index 0000000..70ab19b --- /dev/null +++ b/android/patches/grp.patch @@ -0,0 +1,16 @@ +--- Python-3.12.0-original/configure 2023-11-20 18:40:13 ++++ Python-3.12.0/configure 2023-11-20 19:06:42 +@@ -28545,6 +28545,13 @@ + py_cv_module__scproxy=n/a + py_cv_module_spwd=n/a + ;; #( ++ ++ # Chaquopy: we can't build the grp module, because getgrent and setgrent aren't ++ # available until API level 26. ++ Linux-android) ++ py_cv_module_grp=n/a ++ ;; ++ + Emscripten|WASI) : + + diff --git a/android/patches/lfs.patch b/android/patches/lfs.patch new file mode 100644 index 0000000..4f32859 --- /dev/null +++ b/android/patches/lfs.patch @@ -0,0 +1,16 @@ +--- a/configure ++++ b/configure +@@ -8373,7 +8373,12 @@ $as_echo "#define HAVE_HTOLE64 1" >>confdefs.h + + fi + +-use_lfs=yes ++# Chaquopy: changed "yes" to "no". _LARGEFILE_SOURCE has no effect on Android, and ++# _FILE_OFFSET_BITS=64 has no effect on 64-bit ABIs, but on 32-bit ABIs it causes many critical ++# functions to disappear on API levels older than 24. See ++# https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md. ++use_lfs=no ++ + # Don't use largefile support for GNU/Hurd + case $ac_sys_system in GNU*) + use_lfs=no diff --git a/android/patches/python_for_build_deps.patch b/android/patches/python_for_build_deps.patch new file mode 100644 index 0000000..820dd58 --- /dev/null +++ b/android/patches/python_for_build_deps.patch @@ -0,0 +1,16 @@ +--- a/Makefile.pre.in 2022-10-24 17:35:39.000000000 +0000 ++++ b/Makefile.pre.in 2022-11-01 18:20:18.472102145 +0000 +@@ -292,7 +292,12 @@ + PYTHON_FOR_BUILD=@PYTHON_FOR_BUILD@ + # Single-platform builds depend on $(BUILDPYTHON). Cross builds use an + # external "build Python" and have an empty PYTHON_FOR_BUILD_DEPS. +-PYTHON_FOR_BUILD_DEPS=@PYTHON_FOR_BUILD_DEPS@ ++# ++# Chaquopy: Was PYTHON_FOR_BUILD_DEPS from the configure script, which is empty when ++# cross-compiling (https://github.com/python/cpython/pull/93977). But this means that in ++# parallel builds, the sharedmods target can start running before libpython is available ++# (https://github.com/beeware/briefcase-android-gradle-template/pull/55). ++PYTHON_FOR_BUILD_DEPS=$(LDLIBRARY) + + # Single-platform builds use Programs/_freeze_module.c for bootstrapping and + # ./_bootstrap_python Programs/_freeze_module.py for remaining modules diff --git a/android/patches/soname.patch b/android/patches/soname.patch new file mode 100644 index 0000000..a348464 --- /dev/null +++ b/android/patches/soname.patch @@ -0,0 +1,17 @@ +--- Python-3.12.0-original/configure 2023-11-20 19:18:30 ++++ Python-3.12.0/configure 2023-11-21 08:37:46 +@@ -7492,7 +7492,13 @@ + LDLIBRARY='libpython$(LDVERSION).so' + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} +- INSTSONAME="$LDLIBRARY".$SOVERSION ++ ++ # Chaquopy: the Android Gradle plugin will only package libraries whose names end ++ # with ".so". ++ if [ $ac_sys_system != "Linux-android" ]; then ++ INSTSONAME="$LDLIBRARY".$SOVERSION ++ fi ++ + if test "$with_pydebug" != yes + then + PY3LIBRARY=libpython3.so diff --git a/android/patches/sysroot_paths.patch b/android/patches/sysroot_paths.patch new file mode 100644 index 0000000..a649dbc --- /dev/null +++ b/android/patches/sysroot_paths.patch @@ -0,0 +1,14 @@ +--- Python-3.11.0rc1-original/setup.py 2022-08-05 14:45:18.000000000 +0000 ++++ Python-3.11.0rc1/setup.py 2022-09-15 18:11:38.898125188 +0000 +@@ -166,6 +166,11 @@ + for var_name in make_vars: + var = sysconfig.get_config_var(var_name) + if var is not None: ++ # Chaquopy: also detect -L and -I. ++ for path in re.findall(r'-[LI]\s*(\S+)', var): ++ if os.path.isdir(path): ++ dirs.append(path) ++ + m = re.search(r'--sysroot=([^"]\S*|"[^"]+")', var) + if m is not None: + sysroot = m.group(1).strip('"') diff --git a/android/python-android-dart.exclude b/android/python-android-dart.exclude new file mode 100644 index 0000000..42e6b31 --- /dev/null +++ b/android/python-android-dart.exclude @@ -0,0 +1,27 @@ +# Files to exclude for Android distro +# +lib/*.a +lib/*.la +lib/python*/lib-dynload/_test*.so +lib/python*/lib-dynload/_ctypes_test*.so +lib/python*/lib-dynload/xxlimited*.so +lib/python*/lib-dynload/_xxtestfuzz*.so +lib/python*/curses +lib/python*/ensurepip +lib/python*/idlelib +lib/python*/pydoc_data +lib/python*/test +lib/python*/tkinter +lib/python*/turtle* +lib/python*/wsgiref +man +share +*/__pycache__ +# +# +# Other files that we don't need in a mobile distro +lib/python*/config-* +lib/pkgconfig +lib/python*/site-packages +include +bin \ No newline at end of file