From df4b310eccab68193921a666a901ef079e19ef89 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 19 Oct 2024 20:07:58 -0700 Subject: [PATCH 01/50] Enable _sqlite3 --- android/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/build.sh b/android/build.sh index 3205faa..9eeabdf 100755 --- a/android/build.sh +++ b/android/build.sh @@ -207,6 +207,8 @@ echo ">>> Configuring Python for $abi" BZIP2_LIBS="-L$bzip2_install/lib -lbz2" \ LIBFFI_CFLAGS="-I$libffi_install/include" \ LIBFFI_LIBS="-L$libffi_install/lib -lffi" \ + LIBSQLITE3_CFLAGS="-I$sqlite_install/include" \ + LIBSQLITE3_LIBS="-L$sqlite_install/lib" \ --host=$host_triplet \ --build=$(./config.guess) \ --with-build-python=yes \ From f01f2e957b40964d75b40b4dad1a1cc4918d6a52 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 21 Oct 2024 11:52:10 -0700 Subject: [PATCH 02/50] Add libc++_shared.so to Android distro --- android/android-env.sh | 1 + android/build.sh | 3 +++ 2 files changed, 4 insertions(+) diff --git a/android/android-env.sh b/android/android-env.sh index 19f60bd..cd4d0e4 100644 --- a/android/android-env.sh +++ b/android/android-env.sh @@ -70,6 +70,7 @@ export NM="$toolchain/bin/llvm-nm" export RANLIB="$toolchain/bin/llvm-ranlib" export READELF="$toolchain/bin/llvm-readelf" export STRIP="$toolchain/bin/llvm-strip" +export LIBC_SHARED_SO="$toolchain/sysroot/usr/lib/${clang_triplet}/libc++_shared.so" # 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 diff --git a/android/build.sh b/android/build.sh index 9eeabdf..80bc4a2 100755 --- a/android/build.sh +++ b/android/build.sh @@ -230,6 +230,9 @@ make install \ echo ">>> Copying Python dependencies $abi" cp {$openssl_install,$sqlite_install}/lib/*_python.so $python_install/lib +echo ">>> Copying libc++_shared.so" +cp $LIBC_SHARED_SO $python_install/lib + echo ">>> Stripping dynamic libraries for $abi" find $python_install -type f -iname "*.so" -exec $STRIP --strip-unneeded {} \; From b9a660a27fe3aaa43e9decce8ac0ca74b06eaa2b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 21 Oct 2024 12:04:40 -0700 Subject: [PATCH 03/50] Fix NDK path to libc++_shared.so --- android/android-env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/android-env.sh b/android/android-env.sh index cd4d0e4..01e9701 100644 --- a/android/android-env.sh +++ b/android/android-env.sh @@ -70,7 +70,7 @@ export NM="$toolchain/bin/llvm-nm" export RANLIB="$toolchain/bin/llvm-ranlib" export READELF="$toolchain/bin/llvm-readelf" export STRIP="$toolchain/bin/llvm-strip" -export LIBC_SHARED_SO="$toolchain/sysroot/usr/lib/${clang_triplet}/libc++_shared.so" +export LIBC_SHARED_SO="$toolchain/sysroot/usr/lib/${host_triplet}/libc++_shared.so" # 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 From 8439c4bd513adc67bfbd82ecafab4ae8cda93c9f Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 5 Dec 2024 09:18:09 -0800 Subject: [PATCH 04/50] Android build updated from Chaquopy (#2) * Android build is in sync with Chaquopy * Python 3.12.7 * Fix downloads dir * Fix deps downloads * Android NDK r27c (27.2.12479018) * Re-build Python for iOS with xcrun fix --- .appveyor.yml | 20 +- android/abi-to-host.sh | 18 ++ android/android-env.sh | 76 ++++-- android/build.sh | 362 +++++++++++----------------- android/package-for-dart.sh | 17 +- android/patches/3.13_pending.patch | 39 +++ android/patches/bldlibrary.patch | 83 ++++--- android/patches/dynload_shlib.patch | 12 - android/patches/lfs.patch | 16 -- 9 files changed, 319 insertions(+), 324 deletions(-) create mode 100644 android/abi-to-host.sh create mode 100644 android/patches/3.13_pending.patch delete mode 100644 android/patches/dynload_shlib.patch delete mode 100644 android/patches/lfs.patch diff --git a/.appveyor.yml b/.appveyor.yml index 4c53162..81606cd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,7 +3,7 @@ skip_branch_with_pr: true environment: python_stack: python 3.12 - PYTHON_VERSION: 3.12.6 + PYTHON_VERSION: 3.12.7 PYTHON_VERSION_SHORT: 3.12 GITHUB_TOKEN: secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 @@ -12,16 +12,16 @@ environment: - job_name: Build Python for iOS and macOS APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma - - job_name: Build Python for Android - APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - NDK_VERSION: r27 + # - job_name: Build Python for Android + # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + # NDK_VERSION: r27 - - job_name: Build Python for Linux - APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - PYTHON_DIST_RELEASE: 20240909 + # - job_name: Build Python for Linux + # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + # PYTHON_DIST_RELEASE: 20241016 - - 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 @@ -44,7 +44,7 @@ for: - cd darwin # Build Python for iOS and macOS - - git clone --branch=$PYTHON_VERSION_SHORT https://github.com/beeware/Python-Apple-support.git + - git clone --branch=$PYTHON_VERSION_SHORT-fix-xcrun-aliases https://github.com/flet-dev/Python-Apple-support.git - mkdir -p dist - sh: | pushd Python-Apple-support diff --git a/android/abi-to-host.sh b/android/abi-to-host.sh new file mode 100644 index 0000000..6e717aa --- /dev/null +++ b/android/abi-to-host.sh @@ -0,0 +1,18 @@ +case ${abi:?} in + armeabi-v7a) + HOST=arm-linux-androideabi + ;; + arm64-v8a) + HOST=aarch64-linux-android + ;; + x86) + HOST=i686-linux-android + ;; + x86_64) + HOST=x86_64-linux-android + ;; + *) + echo "Unknown ABI: '$abi'" + exit 1 + ;; +esac diff --git a/android/android-env.sh b/android/android-env.sh index 01e9701..c8789b5 100644 --- a/android/android-env.sh +++ b/android/android-env.sh @@ -1,8 +1,29 @@ -fail() { +# This script must be sourced with the following variables already set: +: ${HOST:?} # GNU target triplet + +# You may also override the following: +: ${api_level:=24} # Minimum Android API level the build will run on +: ${PREFIX:-} # Path in which to find required libraries + +NDK_VERSION=r27c + +# Print all messages on stderr so they're visible when running within build-wheel. +log() { echo "$1" >&2 +} + +fail() { + log "$1" exit 1 } +# When moving to a new version of the NDK, carefully review the following: +# +# * https://developer.android.com/ndk/downloads/revision_history +# +# * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md +# where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: +# https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md if [[ -z "${NDK_HOME-}" ]]; then NDK_HOME=$HOME/ndk/$NDK_VERSION echo "NDK_HOME environment variable is not set." @@ -52,10 +73,10 @@ else echo "NDK home: $NDK_HOME" fi -if [ $host_triplet = "arm-linux-androideabi" ]; then +if [ $HOST = "arm-linux-androideabi" ]; then clang_triplet=armv7a-linux-androideabi else - clang_triplet=$host_triplet + clang_triplet=$HOST fi # These variables are based on BuildSystemMaintainers.md above, and @@ -63,14 +84,13 @@ fi 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 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" -export LIBC_SHARED_SO="$toolchain/sysroot/usr/lib/${host_triplet}/libc++_shared.so" # 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 @@ -79,28 +99,38 @@ for path in "$AR" "$AS" "$CC" "$CXX" "$LD" "$NM" "$RANLIB" "$READELF" "$STRIP"; 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" +export CFLAGS="-D__BIONIC_NO_PAGE_SIZE_MACRO" +export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,-z,max-page-size=16384" + +# 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). So any library that fails to +# build with this flag, would also fail to load at runtime. +LDFLAGS="$LDFLAGS -Wl,--no-undefined" + +# Many packages get away with omitting -lm on Linux, but Android is stricter. +LDFLAGS="$LDFLAGS -lm" -# Many packages get away with omitting this on standard Linux, but Android is stricter. -LDFLAGS+=" -lm" +# -mstackrealign is included where necessary in the clang launcher scripts which are +# pointed to by $CC, so we don't need to include it here. +if [ $HOST = "arm-linux-androideabi" ]; then + CFLAGS="$CFLAGS -march=armv7-a -mthumb" +fi + +if [ -n "${PREFIX:-}" ]; then + abs_prefix=$(realpath $PREFIX) + CFLAGS="$CFLAGS -I$abs_prefix/include" + LDFLAGS="$LDFLAGS -L$abs_prefix/lib" -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="$abs_prefix/lib/pkgconfig" +fi -export PKG_CONFIG="pkg-config --define-prefix" -export PKG_CONFIG_LIBDIR="$prefix/lib/pkgconfig" +# When compiling C++, some build systems will combine CFLAGS and CXXFLAGS, and some will +# use CXXFLAGS alone. +export CXXFLAGS=$CFLAGS -# conda-build variable name +# Use the same variable name as conda-build if [ $(uname) = "Darwin" ]; then export CPU_COUNT=$(sysctl -n hw.ncpu) else diff --git a/android/build.sh b/android/build.sh index 80bc4a2..99cc1cf 100755 --- a/android/build.sh +++ b/android/build.sh @@ -1,242 +1,154 @@ #!/bin/bash -set -eu +set -eu -o pipefail -python_version=${1:?} +script_dir=$(dirname $(realpath $0)) +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" +read version_major version_minor version_micro < <( + echo $version | sed -E 's/^([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 \2 \3/' +) +version_short=$version_major.$version_minor +version_no_pre=$version_major.$version_minor.$version_micro +version_int=$(($version_major * 100 + $version_minor)) + +PREFIX="$script_dir/install/android/$abi/python-${version}" +mkdir -p "$PREFIX" +PREFIX=$(realpath "$PREFIX") + +downloads=$script_dir/downloads 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 +cd $script_dir +. abi-to-host.sh +. android-env.sh -# 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" +# Download and unpack Python source code. +version_dir=$script_dir/build/$version +mkdir -p $version_dir +cd $version_dir +src_filename=Python-$version.tgz +wget -c https://www.python.org/ftp/python/$version_no_pre/$src_filename -# configure build environment -prefix=$python_build_dir -. $project_dir/android-env.sh - -cd $python_build_dir +build_dir=$version_dir/$abi +rm -rf $build_dir +tar -xf "$src_filename" +mv "Python-$version" "$build_dir" +cd "$build_dir" -# apply patches -echo ">>> Patching Python for $abi" -patches="dynload_shlib lfs soname" -if [ $python_version_int -le 311 ]; then +# Apply patches. +patches="" +if [ $version_int -le 311 ]; then patches+=" sysroot_paths" fi -if [ $python_version_int -ge 311 ]; then +if [ $version_int -eq 311 ]; then patches+=" python_for_build_deps" fi -if [ $python_version_int -ge 312 ]; then +if [ $version_int -le 312 ]; then + patches+=" soname" +fi +if [ $version_int -eq 312 ]; then patches+=" bldlibrary grp" fi +if [ $version_int -eq 313 ]; then + # TODO: remove this once it's merged upstream. + patches+=" 3.13_pending" +fi for name in $patches; do - patch -p1 -i $project_dir/patches/$name.patch + patch_file="$script_dir/patches/$name.patch" + echo "$patch_file" + patch -p1 -i "$patch_file" 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" \ - LIBSQLITE3_CFLAGS="-I$sqlite_install/include" \ - LIBSQLITE3_LIBS="-L$sqlite_install/lib" \ - --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 ">>> Copying libc++_shared.so" -cp $LIBC_SHARED_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 +# Remove any existing installation in the prefix. +rm -rf $PREFIX/{include,lib}/python$version_short +rm -rf $PREFIX/lib/libpython$version_short* + +# create VERSIONS support file +support_versions=$script_dir/support/$version_short/android/VERSIONS +mkdir -p $(dirname $support_versions) +echo ">>> Create VERSIONS file for android" +echo "Python version: $version" > $support_versions +echo "Build: 1" >> $support_versions +echo "Min android version: $api_level" >> $support_versions +echo "---------------------" >> $support_versions + +if [ $version_int -le 312 ]; then + # Download and unpack libraries needed to compile Python. For a given Python + # version, we must maintain binary compatibility with existing wheels. + libs="bzip2-1.0.8-2 libffi-3.4.4-3 sqlite-3.45.3-3 xz-5.4.6-1" + if [ $version_int -le 308 ]; then + libs+=" openssl-1.1.1w-3" + else + libs+=" openssl-3.0.15-4" + fi + + url_prefix="https://github.com/beeware/cpython-android-source-deps/releases/download" + for name_ver in $libs; do + IFS=- read lib_name lib_ver <<< "$name_ver" + url="$url_prefix/$name_ver/$name_ver-$HOST.tar.gz" + echo "$url" + + lib_dir="$script_dir/install/android/$abi/${lib_name}-${lib_ver}" + mkdir -p $lib_dir + lib_file=$downloads/${lib_name}-${lib_ver}-${abi}.tar.gz + curl -Lf "$url" -o $lib_file + tar -xf $lib_file -C $lib_dir + cp -R $lib_dir/* $PREFIX + echo "${lib_name}: $lib_ver" >> $support_versions + 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/$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. + cd "$build_dir" + cat > config.site <<-EOF + # Things that can't be autodetected when cross-compiling. + ac_cv_aligned_required=no # Default of "yes" changes hash function to FNV, which breaks Numba. + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + EOF + export CONFIG_SITE=$(pwd)/config.site + + configure_args="--host=$HOST --build=$(./config.guess) \ + --enable-shared --without-ensurepip --with-openssl=$PREFIX" + + # This prevents the "getaddrinfo bug" test, which can't be run when cross-compiling. + configure_args+=" --enable-ipv6" + + # Some of the patches involve missing Makefile dependencies, which allowed extension + # modules to be built before libpython3.x.so in parallel builds. In case this happens + # again, make sure there's no libpython3.x.a, otherwise the modules may end up silently + # linking with that instead. + if [ $version_int -ge 310 ]; then + configure_args+=" --without-static-libpython" + fi + + if [ $version_int -ge 311 ]; then + configure_args+=" --with-build-python=yes" + fi + + ./configure $configure_args + + make -j $CPU_COUNT + make install prefix=$PREFIX + + echo ">>> Replacing host platform" + sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $PREFIX/lib/python$version_short/config-$version_short/Makefile + +# Python 3.13 and later comes with an official Android build script. +else + mkdir -p cross-build/build + ln -s "$(which python$version_short)" cross-build/build/python + + Android/android.py configure-host "$HOST" + Android/android.py make-host "$HOST" + cp -a "cross-build/$HOST/prefix/"* "$PREFIX" +fi diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 759bf86..625deef 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -7,6 +7,9 @@ abi=${3:?} script_dir=$(dirname $(realpath $0)) +. abi-to-host.sh +. android-env.sh + # 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 @@ -23,6 +26,10 @@ 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 +# strip binaries +chmod u+w $(find $build_dir -name *.so) +$STRIP $(find $build_dir -name *.so) + # create libpythonbundle.so bundle_dir=$build_dir/libpythonbundle mkdir -p $bundle_dir @@ -47,8 +54,14 @@ zip -r ../libpythonbundle.so . cd - rm -rf $bundle_dir -# copy *.so from lib -cp $build_dir/lib/*.so $build_dir +# copy python*.so from lib +cp $build_dir/lib/libpython$python_version_short.so $build_dir + +# copy deps +for name in crypto ssl sqlite3; do + cp "$build_dir/lib/lib${name}_"python.so "$build_dir" +done + rm -rf $build_dir/lib # final archive diff --git a/android/patches/3.13_pending.patch b/android/patches/3.13_pending.patch new file mode 100644 index 0000000..83ab8e7 --- /dev/null +++ b/android/patches/3.13_pending.patch @@ -0,0 +1,39 @@ +diff --git a/Android/android-env.sh b/Android/android-env.sh +index 93372e3fe1c..94712602a23 100644 +--- a/Android/android-env.sh ++++ b/Android/android-env.sh +@@ -24,7 +24,7 @@ fail() { + # * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md + # where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: + # https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md +-ndk_version=26.2.11394342 ++ndk_version=27.1.12297006 + + ndk=$ANDROID_HOME/ndk/$ndk_version + if ! [ -e $ndk ]; then +@@ -58,8 +58,8 @@ for path in "$AR" "$AS" "$CC" "$CXX" "$LD" "$NM" "$RANLIB" "$READELF" "$STRIP"; + fi + done + +-export CFLAGS="" +-export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment" ++export CFLAGS="-D__BIONIC_NO_PAGE_SIZE_MACRO" ++export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,-z,max-page-size=16384" + + # Unlike Linux, Android does not implicitly use a dlopened library to resolve + # relocations in subsequently-loaded libraries, even if RTLD_GLOBAL is used +diff --git a/Android/android.py b/Android/android.py +index 8696d9eaeca..b3ee449ba43 100755 +--- a/Android/android.py ++++ b/Android/android.py +@@ -138,8 +138,8 @@ def make_build_python(context): + + def unpack_deps(host): + deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download" +- for name_ver in ["bzip2-1.0.8-1", "libffi-3.4.4-2", "openssl-3.0.15-0", +- "sqlite-3.45.1-0", "xz-5.4.6-0"]: ++ for name_ver in ["bzip2-1.0.8-2", "libffi-3.4.4-3", "openssl-3.0.15-4", ++ "sqlite-3.45.3-3", "xz-5.4.6-1"]: + filename = f"{name_ver}-{host}.tar.gz" + download(f"{deps_url}/{name_ver}/{filename}") + run(["tar", "-xf", filename]) diff --git a/android/patches/bldlibrary.patch b/android/patches/bldlibrary.patch index 4dda79f..72b0bac 100644 --- a/android/patches/bldlibrary.patch +++ b/android/patches/bldlibrary.patch @@ -1,44 +1,55 @@ ---- 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. +diff --git a/configure b/configure +index 1c75810d9e8..d883a00d548 100755 +--- a/configure ++++ b/configure +@@ -841,6 +841,7 @@ PY_ENABLE_SHARED + PLATLIBDIR + BINLIBDEST + LIBPYTHON ++MODULE_DEPS_SHARED + EXT_SUFFIX + ALT_SOABI + SOABI +@@ -24402,12 +24403,17 @@ LDVERSION='$(VERSION)$(ABIFLAGS)' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDVERSION" >&5 + printf "%s\n" "$LDVERSION" >&6; } +-# On Android and Cygwin the shared libraries must be linked with libpython. ++# Configure the flags and dependencies used when compiling shared modules. ++# Do not rename LIBPYTHON - it's accessed via sysconfig by package build ++# systems (e.g. Meson) to decide whether to link extension modules against ++# libpython. ++MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' ++LIBPYTHON='' + ++# 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='' +-else +- LIBPYTHON='' ++ MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" ++ LIBPYTHON="\$(BLDLIBRARY)" 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 @@ + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 0e64ccc5c21..c4217424508 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -2797,7 +2797,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h + + # force rebuild when header file or module build flavor (static/shared) is changed + MODULE_DEPS_STATIC=Modules/config.c +-MODULE_DEPS_SHARED=$(MODULE_DEPS_STATIC) $(EXPORTSYMS) ++MODULE_DEPS_SHARED=@MODULE_DEPS_SHARED@ + + MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h + MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h +diff --git a/Modules/makesetup b/Modules/makesetup +index f000c9cd673..3231044230e 100755 +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -286,7 +286,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | ;; esac rule="$file: $objs" diff --git a/android/patches/dynload_shlib.patch b/android/patches/dynload_shlib.patch deleted file mode 100644 index 4260a05..0000000 --- a/android/patches/dynload_shlib.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- 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/lfs.patch b/android/patches/lfs.patch deleted file mode 100644 index 4f32859..0000000 --- a/android/patches/lfs.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- 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 From 911fbc55822f250fa96d343e4ec9b927819e1bc6 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Tue, 10 Dec 2024 12:46:38 -0800 Subject: [PATCH 05/50] Include libssl and libcrypto to macOS framework bundle --- darwin/python-darwin-framework.exclude | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/darwin/python-darwin-framework.exclude b/darwin/python-darwin-framework.exclude index 5e5a095..fc41a22 100644 --- a/darwin/python-darwin-framework.exclude +++ b/darwin/python-darwin-framework.exclude @@ -8,6 +8,18 @@ macos-arm64_x86_64/Python.framework/Headers macos-arm64_x86_64/Python.framework/Versions/*/_CodeSignature macos-arm64_x86_64/Python.framework/Versions/*/Headers macos-arm64_x86_64/Python.framework/Versions/*/include -macos-arm64_x86_64/Python.framework/Versions/*/lib +macos-arm64_x86_64/Python.framework/Versions/*/lib/itcl* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libform* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libmenu* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libncurses* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libpanel* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libpython* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libtcl* +macos-arm64_x86_64/Python.framework/Versions/*/lib/libtk* +macos-arm64_x86_64/Python.framework/Versions/*/lib/pkgconfig +macos-arm64_x86_64/Python.framework/Versions/*/lib/python* +macos-arm64_x86_64/Python.framework/Versions/*/lib/sqlite* +macos-arm64_x86_64/Python.framework/Versions/*/lib/t* +macos-arm64_x86_64/Python.framework/Versions/*/lib/T* iphoneos iphonesimulator \ No newline at end of file From 520fa4881c0dba37858108b3ff52cb7508cd9c23 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Tue, 17 Dec 2024 13:37:49 -0800 Subject: [PATCH 06/50] Python 3.12.8 and back to beeware/Python-Apple-support --- .appveyor.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 81606cd..b80b7f2 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,7 +3,7 @@ skip_branch_with_pr: true environment: python_stack: python 3.12 - PYTHON_VERSION: 3.12.7 + PYTHON_VERSION: 3.12.8 PYTHON_VERSION_SHORT: 3.12 GITHUB_TOKEN: secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 @@ -12,16 +12,16 @@ environment: - job_name: Build Python for iOS and macOS APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma - # - job_name: Build Python for Android - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - # NDK_VERSION: r27 + - job_name: Build Python for Android + APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + NDK_VERSION: r27 - # - job_name: Build Python for Linux - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - # PYTHON_DIST_RELEASE: 20241016 + - job_name: Build Python for Linux + APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + PYTHON_DIST_RELEASE: 20241206 - # - 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 @@ -44,7 +44,7 @@ for: - cd darwin # Build Python for iOS and macOS - - git clone --branch=$PYTHON_VERSION_SHORT-fix-xcrun-aliases https://github.com/flet-dev/Python-Apple-support.git + - git clone --branch=$PYTHON_VERSION_SHORT https://github.com/beeware/Python-Apple-support.git - mkdir -p dist - sh: | pushd Python-Apple-support From b9613073d736f05c2b6942bdd7064f2ecce974f4 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 5 Feb 2025 10:27:39 -0800 Subject: [PATCH 07/50] MinimumOSVersion=13.0 --- darwin/xcframework_utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/xcframework_utils.sh b/darwin/xcframework_utils.sh index 66b6d94..01d16a2 100644 --- a/darwin/xcframework_utils.sh +++ b/darwin/xcframework_utils.sh @@ -31,7 +31,7 @@ create_plist() { iPhoneOS MinimumOSVersion - 12.0 + 13.0 CFBundleVersion 1 From d4c7bd3af55f38b1ffb922e12f477b6d31631650 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 5 Feb 2025 10:37:40 -0800 Subject: [PATCH 08/50] Fix Windows build --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b80b7f2..c78a6cb 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -140,8 +140,8 @@ for: build_script: - cd windows - - curl -OL https://www.python.org/ftp/python/3.12.5/python-3.12.5-amd64.exe - - start /wait python-3.12.5-amd64.exe /uninstall /quiet + - curl -OL https://www.python.org/ftp/python/3.12.8/python-3.12.8-amd64.exe + - start /wait python-3.12.8-amd64.exe /uninstall /quiet - curl -OL https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-amd64.exe - start /wait python-%PYTHON_VERSION%-amd64.exe /quiet From 76b52135ddb0b36da204faf54f8f3457e771f6f4 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 5 Feb 2025 11:56:46 -0800 Subject: [PATCH 09/50] Exclude ctypes/macholib/fetch_macholib from iOS distro --- darwin/python-darwin-stdlib.exclude | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/python-darwin-stdlib.exclude b/darwin/python-darwin-stdlib.exclude index 5df694e..b06a320 100644 --- a/darwin/python-darwin-stdlib.exclude +++ b/darwin/python-darwin-stdlib.exclude @@ -3,6 +3,7 @@ lib-dynload/_ctypes_test*.so lib-dynload/xxlimited*.so lib-dynload/_xxtestfuzz*.so config-* +ctypes/macholib/fetch_macholib* curses ensurepip idlelib From 178f047a196556ca931f80090f9b62e20f7a25fb Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 9 Feb 2025 11:04:48 -0800 Subject: [PATCH 10/50] Python 3.12.9 --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index c78a6cb..562da16 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,7 +3,7 @@ skip_branch_with_pr: true environment: python_stack: python 3.12 - PYTHON_VERSION: 3.12.8 + PYTHON_VERSION: 3.12.9 PYTHON_VERSION_SHORT: 3.12 GITHUB_TOKEN: secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 @@ -18,7 +18,7 @@ environment: - job_name: Build Python for Linux APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - PYTHON_DIST_RELEASE: 20241206 + PYTHON_DIST_RELEASE: 20250205 - job_name: Build Python for Windows APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 From 0001600c50a353f8e44bdf3b642310c72e80f9f3 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 15 Feb 2025 08:33:31 -0800 Subject: [PATCH 11/50] Update path in .origin --- darwin/package-ios-for-dart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/package-ios-for-dart.sh b/darwin/package-ios-for-dart.sh index 111a615..ed33860 100755 --- a/darwin/package-ios-for-dart.sh +++ b/darwin/package-ios-for-dart.sh @@ -46,7 +46,7 @@ find "$stdlib_dir/${archs[0]}/lib-dynload" -name "*.$dylib_ext" | while read ful "$stdlib_dir/${archs[1]}/lib-dynload" \ "$stdlib_dir/${archs[2]}/lib-dynload" \ $dylib_relative_path \ - "Frameworks/serious_python_darwin.framework/python-stdlib/lib-dynload" \ + "Frameworks/serious_python_darwin.framework/python.bundle/stdlib/lib-dynload" \ $python_frameworks_dir #break # run for one lib only - for tests done From 32f0bb515b9eed329deea450bb40cafcdf5c0004 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 23 Feb 2025 11:19:25 -0800 Subject: [PATCH 12/50] Add PrivacyInfo.xcprivacy to some frameworks, rebuild Darwin libs --- .appveyor.yml | 16 ++++++++-------- darwin/PrivacyInfo.xcprivacy | 17 +++++++++++++++++ darwin/package-ios-for-dart.sh | 4 ++++ darwin/xcframework_utils.sh | 10 ++++++++++ 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 darwin/PrivacyInfo.xcprivacy diff --git a/.appveyor.yml b/.appveyor.yml index 562da16..5fb7a9b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,16 +12,16 @@ environment: - job_name: Build Python for iOS and macOS APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma - - job_name: Build Python for Android - APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - NDK_VERSION: r27 + # - job_name: Build Python for Android + # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + # NDK_VERSION: r27 - - job_name: Build Python for Linux - APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - PYTHON_DIST_RELEASE: 20250205 + # - job_name: Build Python for Linux + # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + # PYTHON_DIST_RELEASE: 20250205 - - 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 diff --git a/darwin/PrivacyInfo.xcprivacy b/darwin/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..6d94afc --- /dev/null +++ b/darwin/PrivacyInfo.xcprivacy @@ -0,0 +1,17 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + NSPrivacyTrackingDomains + + NSPrivacyUsesNonStandardAPIs + + + \ No newline at end of file diff --git a/darwin/package-ios-for-dart.sh b/darwin/package-ios-for-dart.sh index ed33860..a983fcc 100755 --- a/darwin/package-ios-for-dart.sh +++ b/darwin/package-ios-for-dart.sh @@ -38,6 +38,10 @@ for arch in "${archs[@]}"; do rsync -av --exclude-from=$script_dir/python-darwin-stdlib.exclude $python_apple_support_root/install/iOS/$arch/python-*/lib/python$python_version_short/* $stdlib_dir/$arch done +echo "Copying privacy manifests..." +cp "$script_dir/PrivacyInfo.xcprivacy" "$stdlib_dir/${archs[0]}/lib-dynload/_hashlib.xcprivacy" +cp "$script_dir/PrivacyInfo.xcprivacy" "$stdlib_dir/${archs[0]}/lib-dynload/_ssl.xcprivacy" + echo "Converting lib-dynload to xcframeworks..." find "$stdlib_dir/${archs[0]}/lib-dynload" -name "*.$dylib_ext" | while read full_dylib; do dylib_relative_path=${full_dylib#$stdlib_dir/${archs[0]}/lib-dynload/} diff --git a/darwin/xcframework_utils.sh b/darwin/xcframework_utils.sh index 01d16a2..bdcfbcf 100644 --- a/darwin/xcframework_utils.sh +++ b/darwin/xcframework_utils.sh @@ -65,6 +65,11 @@ create_xcframework_from_dylibs() { create_plist $framework "org.python.$framework_identifier" $fd/Info.plist echo "$origin_prefix/$dylib_without_ext.fwork" > $fd/$framework.origin + # copy privacy manifest if any + if [ -f "$iphone_dir/$dylib_without_ext.xcprivacy" ]; then + cp "$iphone_dir/$dylib_without_ext.xcprivacy" "$fd/PrivacyInfo.xcprivacy" + fi + # creating "iphonesimulator" framework fd=iphonesimulator/$framework.framework mkdir -p $fd @@ -78,6 +83,11 @@ create_xcframework_from_dylibs() { create_plist $framework "org.python.$framework_identifier" $fd/Info.plist echo "$origin_prefix/$dylib_without_ext.fwork" > $fd/$framework.origin + # copy privacy manifest if any + if [ -f "$iphone_dir/$dylib_without_ext.xcprivacy" ]; then + mv "$iphone_dir/$dylib_without_ext.xcprivacy" "$fd/PrivacyInfo.xcprivacy" + fi + # merge frameworks info xcframework xcodebuild -create-xcframework \ -framework "iphoneos/$framework.framework" \ From 46f18c4642efbb9da60b5c64f9ca4afe1341b4af Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Tue, 4 Mar 2025 17:00:11 -0800 Subject: [PATCH 13/50] exclude header "pyport.h" --- darwin/Modules/module.modulemap | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/Modules/module.modulemap b/darwin/Modules/module.modulemap index cf58b60..f22f264 100644 --- a/darwin/Modules/module.modulemap +++ b/darwin/Modules/module.modulemap @@ -107,6 +107,7 @@ framework module Python { exclude header "pyconfig-x86_64.h" exclude header "pydtrace.h" exclude header "pyexpat.h" + exclude header "pyport.h" exclude header "structmember.h" exclude header "token.h" } \ No newline at end of file From f0e6bb058224c0c70ca94c1e1fa5b973b94d3521 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 28 Aug 2025 09:20:00 -0700 Subject: [PATCH 14/50] Use "v2" (SSE instructions) Python distributive on Linux --- .appveyor.yml | 12 ++++++------ linux/package-for-linux.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 5fb7a9b..7111b5d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,16 +9,16 @@ environment: secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 matrix: - - job_name: Build Python for iOS and macOS - APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma + # - job_name: Build Python for iOS and macOS + # APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma # - job_name: Build Python for Android # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c # NDK_VERSION: r27 - # - job_name: Build Python for Linux - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - # PYTHON_DIST_RELEASE: 20250205 + - job_name: Build Python for Linux + APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + PYTHON_DIST_RELEASE: 20250205 # - job_name: Build Python for Windows # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 @@ -112,7 +112,7 @@ for: build_script: - cd linux - - ./package-for-linux.sh x86_64 "_v3" + - ./package-for-linux.sh x86_64 "_v2" - ./package-for-linux.sh aarch64 "" # Push all archives to artifacts diff --git a/linux/package-for-linux.sh b/linux/package-for-linux.sh index 5c29211..74f6c34 100755 --- a/linux/package-for-linux.sh +++ b/linux/package-for-linux.sh @@ -2,7 +2,7 @@ PYTHON_ARCH=${1:?} PYTHON_ARCH_VER=${2:-""} DIST_FILE=cpython-${PYTHON_VERSION}+${PYTHON_DIST_RELEASE}-${PYTHON_ARCH}${PYTHON_ARCH_VER}-unknown-linux-gnu-install_only_stripped.tar.gz -curl -OL https://github.com/indygreg/python-build-standalone/releases/download/${PYTHON_DIST_RELEASE}/${DIST_FILE} +curl -OL https://github.com/astral-sh/python-build-standalone/releases/download/${PYTHON_DIST_RELEASE}/${DIST_FILE} mkdir -p $PYTHON_ARCH/build tar zxvf $DIST_FILE -C $PYTHON_ARCH/build From 324ef8dcd7f793c77bf9f9611d6734dc7dbb0278 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 28 Aug 2025 13:24:02 -0700 Subject: [PATCH 15/50] Update secure GitHub token in AppVeyor config Replaced the encrypted value of the GITHUB_TOKEN in .appveyor.yml to update credentials for CI builds. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7111b5d..649f758 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,7 +6,7 @@ environment: PYTHON_VERSION: 3.12.9 PYTHON_VERSION_SHORT: 3.12 GITHUB_TOKEN: - secure: 9SKIwc3VSfYJ5IChvNR74rlTF9BMbAfhCGu1/TmYJBMtC6lkY+UDDkZNK7rC9xnQFUxMrNgoo9kNcNAbKbU8XAcrSwkP2H4mX04FI7P+YbxfiWC8nVHhGNxR4LzO+GO0 + secure: 9SKIwc3VSfYJ5IChvNR74gtHacdLajprqIHsTkW2XMxUeJmqkxkATIyHlfClLEwNSHys4zS9OqmbaooO5YvuDzHsZKIIHXUuTJst06jylGkUWhFC3hozkRUoet9ZaTZZ matrix: # - job_name: Build Python for iOS and macOS From b6d4381726c3dd7e5b9673d3f1751bc1025a28c8 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 16 Nov 2025 09:33:33 -0800 Subject: [PATCH 16/50] Fix framework identifier formatting in xcframework_utils.sh Ensures that the generated framework identifier does not start with a dash and defaults to 'framework' if empty. This improves compatibility and prevents invalid identifiers when creating xcframeworks from dylibs. --- darwin/xcframework_utils.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/darwin/xcframework_utils.sh b/darwin/xcframework_utils.sh index bdcfbcf..aea6ad9 100644 --- a/darwin/xcframework_utils.sh +++ b/darwin/xcframework_utils.sh @@ -55,6 +55,10 @@ create_xcframework_from_dylibs() { dylib_without_ext=$(echo $dylib_relative_path | cut -d "." -f 1) framework=$(echo $dylib_without_ext | tr "/" ".") framework_identifier=${framework//_/-} + while [[ $framework_identifier == -* ]]; do + framework_identifier=${framework_identifier#-} + done + framework_identifier=${framework_identifier:-framework} # creating "iphoneos" framework fd=iphoneos/$framework.framework @@ -97,4 +101,4 @@ create_xcframework_from_dylibs() { # cleanup popd >/dev/null rm -rf "${tmp_dir}" >/dev/null -} \ No newline at end of file +} From f05b14cd792d85297ba3357e059dccf02ff928b0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 16 Nov 2025 09:34:30 -0800 Subject: [PATCH 17/50] Enable macOS build job in AppVeyor config Uncommented the job for building Python for iOS and macOS using the macos-sonoma worker image. The Linux build job was commented out, shifting focus to macOS builds. --- .appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 649f758..f30bc00 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,16 +9,16 @@ environment: secure: 9SKIwc3VSfYJ5IChvNR74gtHacdLajprqIHsTkW2XMxUeJmqkxkATIyHlfClLEwNSHys4zS9OqmbaooO5YvuDzHsZKIIHXUuTJst06jylGkUWhFC3hozkRUoet9ZaTZZ matrix: - # - job_name: Build Python for iOS and macOS - # APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma + - job_name: Build Python for iOS and macOS + APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma # - job_name: Build Python for Android # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c # NDK_VERSION: r27 - - job_name: Build Python for Linux - APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - PYTHON_DIST_RELEASE: 20250205 + # - job_name: Build Python for Linux + # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c + # PYTHON_DIST_RELEASE: 20250205 # - job_name: Build Python for Windows # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 From 11591f70df1c56f7b5c6b4ed0d3ea18dea264fef Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 16 Nov 2025 12:14:20 -0800 Subject: [PATCH 18/50] Exclude module.modulemap from framework headers Updated the packaging script and exclude file to remove module.modulemap from Python.framework Headers for both iOS and macOS targets. This helps prevent unwanted module map files from being included in the packaged frameworks. --- darwin/package-macos-for-dart.sh | 1 + darwin/python-darwin-framework.exclude | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/darwin/package-macos-for-dart.sh b/darwin/package-macos-for-dart.sh index 81803fb..8a339a7 100755 --- a/darwin/package-macos-for-dart.sh +++ b/darwin/package-macos-for-dart.sh @@ -29,6 +29,7 @@ rsync -av --exclude-from=$script_dir/python-darwin-framework.exclude $python_app cp -r $script_dir/Modules $frameworks_dir/Python.xcframework/macos-arm64_x86_64/Python.framework mkdir -p $frameworks_dir/Python.xcframework/macos-arm64_x86_64/Python.framework/Headers cp -r $python_apple_support_root/support/$python_version_short/macOS/Python.xcframework/macos-arm64_x86_64/Python.framework/Versions/$python_version_short/include/python$python_version_short/* $frameworks_dir/Python.xcframework/macos-arm64_x86_64/Python.framework/Headers +rm $frameworks_dir/Python.xcframework/macos-arm64_x86_64/Python.framework/Headers/module.modulemap # copy stdlibs rsync -av --exclude-from=$script_dir/python-darwin-stdlib.exclude $python_apple_support_root/install/macOS/macosx/python-*/Python.framework/Versions/Current/lib/python$python_version_short/* $stdlib_dir diff --git a/darwin/python-darwin-framework.exclude b/darwin/python-darwin-framework.exclude index fc41a22..afbea50 100644 --- a/darwin/python-darwin-framework.exclude +++ b/darwin/python-darwin-framework.exclude @@ -1,9 +1,13 @@ ios-arm64/bin ios-arm64/include ios-arm64/lib +ios-arm64/platform-config +ios-arm64/Python.framework/Headers/module.modulemap ios-arm64_x86_64-simulator/bin ios-arm64_x86_64-simulator/include ios-arm64_x86_64-simulator/lib +ios-arm64_x86_64-simulator/platform-config +ios-arm64_x86_64-simulator/Python.framework/Headers/module.modulemap macos-arm64_x86_64/Python.framework/Headers macos-arm64_x86_64/Python.framework/Versions/*/_CodeSignature macos-arm64_x86_64/Python.framework/Versions/*/Headers From afdce86bc3161e9867906609937f09ccf86384cd Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 16 Nov 2025 14:03:14 -0800 Subject: [PATCH 19/50] Fix CFBundlePackageType in generated plist Change CFBundlePackageType from 'APPL' to 'FMWK' in the create_plist function to correctly identify the bundle as a framework. --- darwin/xcframework_utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/xcframework_utils.sh b/darwin/xcframework_utils.sh index aea6ad9..413ffeb 100644 --- a/darwin/xcframework_utils.sh +++ b/darwin/xcframework_utils.sh @@ -23,7 +23,7 @@ create_plist() { CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType - APPL + FMWK CFBundleShortVersionString 1.0 CFBundleSupportedPlatforms From 7a8903534f7f6e5d98355b334ede3244fb17cc37 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:27:02 -0800 Subject: [PATCH 20/50] Migrate CI from AppVeyor to GitHub Actions Remove legacy .appveyor.yml and add a GitHub Actions workflow (build-python.yml) to build Python packages for iOS/macOS, Android, Linux and Windows. The new workflow runs on macOS, Ubuntu and Windows runners, sets PYTHON_VERSION to 3.12.12 (short: 3.12), produces and uploads per-platform artifacts, and includes a commented-out release publishing step. This migrates CI off AppVeyor and centralizes builds in GitHub Actions. --- .appveyor.yml | 162 ----------------------------- .github/workflows/build-python.yml | 148 ++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 162 deletions(-) delete mode 100644 .appveyor.yml create mode 100644 .github/workflows/build-python.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index f30bc00..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,162 +0,0 @@ - -skip_branch_with_pr: true - -environment: - python_stack: python 3.12 - PYTHON_VERSION: 3.12.9 - PYTHON_VERSION_SHORT: 3.12 - GITHUB_TOKEN: - secure: 9SKIwc3VSfYJ5IChvNR74gtHacdLajprqIHsTkW2XMxUeJmqkxkATIyHlfClLEwNSHys4zS9OqmbaooO5YvuDzHsZKIIHXUuTJst06jylGkUWhFC3hozkRUoet9ZaTZZ - - matrix: - - job_name: Build Python for iOS and macOS - APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma - - # - job_name: Build Python for Android - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - # NDK_VERSION: r27 - - # - job_name: Build Python for Linux - # APPVEYOR_BUILD_WORKER_IMAGE: ubuntu-gce-c - # PYTHON_DIST_RELEASE: 20250205 - - # - job_name: Build Python for Windows - # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - -matrix: - fast_finish: true - -stack: $python_stack - -install: -- python --version - -for: - # ====================================== - # Build Python for iOS and macOS - # ====================================== - - - matrix: - only: - - job_name: Build Python for iOS and macOS - - build_script: - - cd darwin - - # Build Python for iOS and macOS - - git clone --branch=$PYTHON_VERSION_SHORT https://github.com/beeware/Python-Apple-support.git - - mkdir -p dist - - sh: | - pushd Python-Apple-support - make iOS || exit 1 - tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . - make macOS || exit 1 - popd - - # Package for Dart - - ./package-ios-for-dart.sh Python-Apple-support $PYTHON_VERSION_SHORT - - ./package-macos-for-dart.sh Python-Apple-support $PYTHON_VERSION_SHORT - - # Push all archives to artifacts - - find dist -maxdepth 1 -type f -iname python-*.tar.gz -exec appveyor PushArtifact -DeploymentName python-darwin {} \; - - test: off - - deploy: - provider: GitHub - auth_token: $(GITHUB_TOKEN) - release: v$(PYTHON_VERSION_SHORT) - artifact: python-darwin - - # ====================================== - # Build Python for Android - # ====================================== - - - matrix: - only: - - job_name: Build Python for Android - - build_script: - - 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 Linux - # ====================================== - - - matrix: - only: - - job_name: Build Python for Linux - - build_script: - - cd linux - - ./package-for-linux.sh x86_64 "_v2" - - ./package-for-linux.sh aarch64 "" - - # Push all archives to artifacts - - ls - - find . -maxdepth 1 -type f -iname "python-linux-dart-*.tar.gz" -exec appveyor PushArtifact -DeploymentName python-linux {} \; - - test: off - - deploy: - provider: GitHub - auth_token: $(GITHUB_TOKEN) - release: v$(PYTHON_VERSION_SHORT) - artifact: python-linux - - # ====================================== - # Build Python for Windows - # ====================================== - - - matrix: - only: - - job_name: Build Python for Windows - - install: - - C:\Python312\python --version - - build_script: - - cd windows - - curl -OL https://www.python.org/ftp/python/3.12.8/python-3.12.8-amd64.exe - - start /wait python-3.12.8-amd64.exe /uninstall /quiet - - - curl -OL https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-amd64.exe - - start /wait python-%PYTHON_VERSION%-amd64.exe /quiet - - dir C:\python312-dist - - C:\python312-dist\python -m compileall -b C:\python312-dist\Lib - - 7z a -xr@exclude.txt python-windows-for-dart-%PYTHON_VERSION_SHORT%.zip C:\python312-dist\* - - test: off - - artifacts: - - path: windows\python-windows-for-dart-*.zip - name: python-windows - - deploy: - provider: GitHub - auth_token: $(GITHUB_TOKEN) - release: v$(PYTHON_VERSION_SHORT) - artifact: python-windows \ No newline at end of file diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml new file mode 100644 index 0000000..8145372 --- /dev/null +++ b/.github/workflows/build-python.yml @@ -0,0 +1,148 @@ +name: Build Python Packages + +on: + push: + branches: + - main + - master + workflow_dispatch: + +env: + PYTHON_VERSION: 3.12.12 + PYTHON_VERSION_SHORT: '3.12' + +jobs: + build-darwin: + name: Build Python for iOS and macOS + runs-on: macos-14 + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Show Python version + run: python --version + + - name: Build Python for iOS and macOS + working-directory: darwin + run: | + git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git + mkdir -p dist + + pushd Python-Apple-support + make iOS + tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . + make macOS + popd + + ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + + - name: Upload Darwin build artifacts + uses: actions/upload-artifact@v4 + with: + name: python-darwin + path: darwin/dist/python-*.tar.gz + if-no-files-found: error + + build-android: + name: Build Python for Android + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + - run: python --version + - working-directory: android + run: | + ./build-all.sh "$PYTHON_VERSION" + mkdir -p dist + tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support + ./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 + - uses: actions/upload-artifact@v4 + with: + name: python-android + path: android/dist/python-android-*.tar.gz + if-no-files-found: error + + build-linux: + name: Build Python for Linux + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - working-directory: linux + run: | + ./package-for-linux.sh x86_64 "_v2" + ./package-for-linux.sh aarch64 "" + - uses: actions/upload-artifact@v4 + with: + name: python-linux + path: linux/python-linux-dart-*.tar.gz + if-no-files-found: error + + build-windows: + name: Build Python for Windows + runs-on: windows-2022 + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Show bundled Python + run: C:\Python312\python --version + - name: Reinstall requested Python version + working-directory: windows + shell: cmd + run: | + curl -OL https://www.python.org/ftp/python/3.12.8/python-3.12.8-amd64.exe + start /wait python-3.12.8-amd64.exe /uninstall /quiet + curl -OL https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-amd64.exe + start /wait python-%PYTHON_VERSION%-amd64.exe /quiet + C:\python312-dist\python -m compileall -b C:\python312-dist\Lib + 7z a -xr@exclude.txt python-windows-for-dart-%PYTHON_VERSION_SHORT%.zip C:\python312-dist\* + - uses: actions/upload-artifact@v4 + with: + name: python-windows + path: windows/python-windows-for-dart-*.zip + if-no-files-found: error + +# publish-release: +# name: Publish Release Assets +# runs-on: ubuntu-latest +# needs: +# - build-darwin +# - build-android +# - build-linux +# - build-windows +# permissions: +# contents: write +# steps: +# - name: Download all build artifacts +# uses: actions/download-artifact@v4 +# with: +# pattern: python-* +# path: release-artifacts +# merge-multiple: true + +# - name: Publish all artifacts to release +# uses: softprops/action-gh-release@v2 +# with: +# tag_name: v${{ env.PYTHON_VERSION_SHORT }} +# files: release-artifacts/* +# fail_on_unmatched_files: true +# generate_release_notes: false +# draft: false +# prerelease: false From a09e212b02e23dec7406bc5b21b48af7603062ca Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:29:11 -0800 Subject: [PATCH 21/50] Run Python build on pushes to any branch Replace explicit branch list (main, master) with a wildcard ('**') in .github/workflows/build-python.yml so the workflow triggers on pushes to any branch. Keeps workflow_dispatch and other settings unchanged; this ensures CI runs for feature branches and other non-main branches. --- .github/workflows/build-python.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 8145372..983d7f3 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -3,8 +3,7 @@ name: Build Python Packages on: push: branches: - - main - - master + - '**' workflow_dispatch: env: From 3171ce57031832553654791732e9ff999e90a6c2 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:31:09 -0800 Subject: [PATCH 22/50] Use macos-15 runner in build workflow Bump GitHub Actions runner from macos-14 to macos-15 in the build-python workflow so CI uses the updated macOS image for building Python for iOS and macOS. --- .github/workflows/build-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 983d7f3..8bfc044 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -13,7 +13,7 @@ env: jobs: build-darwin: name: Build Python for iOS and macOS - runs-on: macos-14 + runs-on: macos-15 permissions: contents: write From 08578c6c009cc271eaec170f058aee35a49f2486 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:35:13 -0800 Subject: [PATCH 23/50] Update Windows Python installer in workflow Modify the Windows job in build-python.yml to install Python 3.12.10 directly. Removed the prior "Show bundled Python" step, renamed the step to "Install requested Python version", and simplified the installer commands by dropping the uninstall and dynamic %PYTHON_VERSION% download; the workflow now downloads and silently installs python-3.12.10 before compiling the stdlib and packaging the distribution. --- .github/workflows/build-python.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 8bfc044..0df64ad 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -100,16 +100,12 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - name: Show bundled Python - run: C:\Python312\python --version - - name: Reinstall requested Python version + - name: Install requested Python version working-directory: windows shell: cmd run: | - curl -OL https://www.python.org/ftp/python/3.12.8/python-3.12.8-amd64.exe - start /wait python-3.12.8-amd64.exe /uninstall /quiet - curl -OL https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-amd64.exe - start /wait python-%PYTHON_VERSION%-amd64.exe /quiet + curl -OL https://www.python.org/ftp/python/3.12.10/python-3.12.10-amd64.exe + start /wait python-3.12.10-amd64.exe /quiet C:\python312-dist\python -m compileall -b C:\python312-dist\Lib 7z a -xr@exclude.txt python-windows-for-dart-%PYTHON_VERSION_SHORT%.zip C:\python312-dist\* - uses: actions/upload-artifact@v4 From d4b804c2ea58f533d1560dc4c6f5a51a726a61d9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:39:19 -0800 Subject: [PATCH 24/50] Bump setup-python and add Windows Python var Add PYTHON_WINDOWS_VERSION env var and use it in the Windows job to download/install the matching Python installer instead of hardcoding 3.12.10. Also upgrade actions/setup-python from v5 to v6. These changes make the Windows packaging step configurable and update the setup action to the newer release. --- .github/workflows/build-python.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 0df64ad..a4acea7 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -8,6 +8,7 @@ on: env: PYTHON_VERSION: 3.12.12 + PYTHON_WINDOWS_VERSION: 3.12.10 PYTHON_VERSION_SHORT: '3.12' jobs: @@ -22,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} @@ -104,8 +105,8 @@ jobs: working-directory: windows shell: cmd run: | - curl -OL https://www.python.org/ftp/python/3.12.10/python-3.12.10-amd64.exe - start /wait python-3.12.10-amd64.exe /quiet + curl -OL https://www.python.org/ftp/python/${{ env.PYTHON_WINDOWS_VERSION }}/python-${{ env.PYTHON_WINDOWS_VERSION }}-amd64.exe + start /wait python-${{ env.PYTHON_WINDOWS_VERSION }}-amd64.exe /quiet C:\python312-dist\python -m compileall -b C:\python312-dist\Lib 7z a -xr@exclude.txt python-windows-for-dart-%PYTHON_VERSION_SHORT%.zip C:\python312-dist\* - uses: actions/upload-artifact@v4 From 0956883c55965c6fb9a872e66bd8d62f03620c67 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:41:32 -0800 Subject: [PATCH 25/50] Use PYTHON_VERSION_SHORT for setup-python Update the GitHub Actions workflow to pass env.PYTHON_VERSION_SHORT to actions/setup-python instead of env.PYTHON_VERSION. This ensures the setup action receives the short version string (e.g., "3.11") in the expected format when installing Python; other steps remain unchanged. --- .github/workflows/build-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index a4acea7..43c3f10 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v6 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: ${{ env.PYTHON_VERSION_SHORT }} - name: Show Python version run: python --version From 01f1a295f80be9070d14f0ee027ed44b65ba2437 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:50:12 -0800 Subject: [PATCH 26/50] Use env bash and set strict shell options Make shell usage explicit in CI and harden scripts for portability and safety. The workflow now sets shell: bash and calls scripts with bash; script shebangs were changed to #!/usr/bin/env bash and strict options (set -euo pipefail) were enabled. Minor fixes: ensure script invocations use bash and add missing trailing newlines. Affected files: .github/workflows/build-python.yml, android/build-all.sh, android/package-for-dart.sh, darwin/package-ios-for-dart.sh, darwin/package-macos-for-dart.sh, linux/package-for-linux.sh. --- .github/workflows/build-python.yml | 19 +++++++++++-------- android/build-all.sh | 8 ++++---- android/package-for-dart.sh | 6 +++--- darwin/package-ios-for-dart.sh | 6 +++--- darwin/package-macos-for-dart.sh | 6 +++--- linux/package-for-linux.sh | 5 ++++- 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 43c3f10..d77a01f 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -32,6 +32,7 @@ jobs: - name: Build Python for iOS and macOS working-directory: darwin + shell: bash run: | git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git mkdir -p dist @@ -42,8 +43,8 @@ jobs: make macOS popd - ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - name: Upload Darwin build artifacts uses: actions/upload-artifact@v4 @@ -64,13 +65,14 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - run: python --version - working-directory: android + shell: bash run: | - ./build-all.sh "$PYTHON_VERSION" + bash ./build-all.sh "$PYTHON_VERSION" mkdir -p dist tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support - ./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 + bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a + bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a + bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 - uses: actions/upload-artifact@v4 with: name: python-android @@ -85,9 +87,10 @@ jobs: steps: - uses: actions/checkout@v4 - working-directory: linux + shell: bash run: | - ./package-for-linux.sh x86_64 "_v2" - ./package-for-linux.sh aarch64 "" + bash ./package-for-linux.sh x86_64 "_v2" + bash ./package-for-linux.sh aarch64 "" - uses: actions/upload-artifact@v4 with: name: python-linux diff --git a/android/build-all.sh b/android/build-all.sh index 3297b77..c2db4c3 100755 --- a/android/build-all.sh +++ b/android/build-all.sh @@ -1,9 +1,9 @@ -#!/bin/bash -set -eu +#!/usr/bin/env bash +set -euo pipefail 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 + bash ./build.sh $python_version $abi +done diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 625deef..975c401 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -eu +#!/usr/bin/env bash +set -euo pipefail install_root=${1:?} python_version=${2:?} @@ -65,4 +65,4 @@ done 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 +tar -czf dist/python-android-dart-$python_version_short-$abi.tar.gz -C $build_dir . diff --git a/darwin/package-ios-for-dart.sh b/darwin/package-ios-for-dart.sh index a983fcc..9fdf41a 100755 --- a/darwin/package-ios-for-dart.sh +++ b/darwin/package-ios-for-dart.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -eu +#!/usr/bin/env bash +set -euo pipefail python_apple_support_root=${1:?} python_version=${2:?} @@ -72,4 +72,4 @@ rm -rf **/__pycache__ cd - # final archive -tar -czf dist/python-ios-dart-$python_version_short.tar.gz -C $build_dir . \ No newline at end of file +tar -czf dist/python-ios-dart-$python_version_short.tar.gz -C $build_dir . diff --git a/darwin/package-macos-for-dart.sh b/darwin/package-macos-for-dart.sh index 8a339a7..f4a7404 100755 --- a/darwin/package-macos-for-dart.sh +++ b/darwin/package-macos-for-dart.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -eu +#!/usr/bin/env bash +set -euo pipefail python_apple_support_root=${1:?} python_version=${2:?} @@ -43,4 +43,4 @@ rm -rf **/__pycache__ cd - # final archive -tar -czf dist/python-macos-dart-$python_version.tar.gz -C $build_dir . \ No newline at end of file +tar -czf dist/python-macos-dart-$python_version.tar.gz -C $build_dir . diff --git a/linux/package-for-linux.sh b/linux/package-for-linux.sh index 74f6c34..dbd5698 100755 --- a/linux/package-for-linux.sh +++ b/linux/package-for-linux.sh @@ -1,3 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + PYTHON_ARCH=${1:?} PYTHON_ARCH_VER=${2:-""} @@ -14,4 +17,4 @@ mkdir -p $PYTHON_ARCH/dist rsync -av --exclude-from=python-linux-dart.exclude $PYTHON_ARCH/build/python/* $PYTHON_ARCH/dist # archive -tar -czf python-linux-dart-$PYTHON_VERSION_SHORT-$PYTHON_ARCH.tar.gz -C $PYTHON_ARCH/dist . \ No newline at end of file +tar -czf python-linux-dart-$PYTHON_VERSION_SHORT-$PYTHON_ARCH.tar.gz -C $PYTHON_ARCH/dist . From 52ee1ce09beadc8ba933b40bb573826c9592ab2a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:51:48 -0800 Subject: [PATCH 27/50] Add PYTHON_DIST_RELEASE and fix version var Update CI workflow (.github/workflows/build-python.yml): remove quotes from PYTHON_VERSION_SHORT (use numeric 3.12) and add PYTHON_DIST_RELEASE=20260203 to track the Python distribution release. This adjusts environment variables used by the Python build jobs. --- .github/workflows/build-python.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index d77a01f..a991b9b 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -9,7 +9,8 @@ on: env: PYTHON_VERSION: 3.12.12 PYTHON_WINDOWS_VERSION: 3.12.10 - PYTHON_VERSION_SHORT: '3.12' + PYTHON_VERSION_SHORT: 3.12 + PYTHON_DIST_RELEASE: 20260203 jobs: build-darwin: From c75bf73e60a54b9e3b908808f818846d7bd9f95d Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 14:56:31 -0800 Subject: [PATCH 28/50] Bump Android NDK version to r29 Update Android NDK references from r27/r27c to r29 across the build scripts and docs. Changes update android/README.md, set NDK_VERSION=r29 in android/android-env.sh, and adjust the ndk_version in android/patches/3.13_pending.patch to 29.0.14206865 so builds use the newer NDK. --- android/README.md | 4 ++-- android/android-env.sh | 2 +- android/patches/3.13_pending.patch | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/README.md b/android/README.md index 37aa5ef..7c276ac 100644 --- a/android/README.md +++ b/android/README.md @@ -4,7 +4,7 @@ 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. +* Installs NDK r29 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 @@ -30,4 +30,4 @@ 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 +* https://github.com/GRRedWings/python3-android diff --git a/android/android-env.sh b/android/android-env.sh index c8789b5..b4f61fb 100644 --- a/android/android-env.sh +++ b/android/android-env.sh @@ -5,7 +5,7 @@ : ${api_level:=24} # Minimum Android API level the build will run on : ${PREFIX:-} # Path in which to find required libraries -NDK_VERSION=r27c +NDK_VERSION=r29 # Print all messages on stderr so they're visible when running within build-wheel. log() { diff --git a/android/patches/3.13_pending.patch b/android/patches/3.13_pending.patch index 83ab8e7..31dd579 100644 --- a/android/patches/3.13_pending.patch +++ b/android/patches/3.13_pending.patch @@ -7,7 +7,7 @@ index 93372e3fe1c..94712602a23 100644 # where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: # https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md -ndk_version=26.2.11394342 -+ndk_version=27.1.12297006 ++ndk_version=29.0.14206865 ndk=$ANDROID_HOME/ndk/$ndk_version if ! [ -e $ndk ]; then From 0ec297d1c44c01a182749ae227105593860a7b93 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 15:15:36 -0800 Subject: [PATCH 29/50] Revamp Windows build; disable other jobs Remove PYTHON_WINDOWS_VERSION and comment out the darwin, android and linux jobs in the build workflow. Replace the previous simple Windows installer step with a PowerShell-based flow that downloads CPython sources, builds PCbuild (Release and Debug), collects binaries, DLLs, libs and stdlib, compiles bytecode-only stdlib, and packages the result into a zip. Adds layout validation to fail early if expected files/directories are missing and preserves existing artifact upload for the windows job. --- .github/workflows/build-python.yml | 262 ++++++++++++++++++++--------- 1 file changed, 179 insertions(+), 83 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index a991b9b..444b023 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -8,95 +8,94 @@ on: env: PYTHON_VERSION: 3.12.12 - PYTHON_WINDOWS_VERSION: 3.12.10 PYTHON_VERSION_SHORT: 3.12 PYTHON_DIST_RELEASE: 20260203 jobs: - build-darwin: - name: Build Python for iOS and macOS - runs-on: macos-15 - permissions: - contents: write +# build-darwin: +# name: Build Python for iOS and macOS +# runs-on: macos-15 +# permissions: +# contents: write - steps: - - name: Checkout - uses: actions/checkout@v4 +# steps: +# - name: Checkout +# uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION_SHORT }} +# - name: Setup Python +# uses: actions/setup-python@v6 +# with: +# python-version: ${{ env.PYTHON_VERSION_SHORT }} - - name: Show Python version - run: python --version +# - name: Show Python version +# run: python --version - - name: Build Python for iOS and macOS - working-directory: darwin - shell: bash - run: | - git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git - mkdir -p dist +# - name: Build Python for iOS and macOS +# working-directory: darwin +# shell: bash +# run: | +# git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git +# mkdir -p dist - pushd Python-Apple-support - make iOS - tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . - make macOS - popd +# pushd Python-Apple-support +# make iOS +# tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . +# make macOS +# popd - bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" +# bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" +# bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - - name: Upload Darwin build artifacts - uses: actions/upload-artifact@v4 - with: - name: python-darwin - path: darwin/dist/python-*.tar.gz - if-no-files-found: error +# - name: Upload Darwin build artifacts +# uses: actions/upload-artifact@v4 +# with: +# name: python-darwin +# path: darwin/dist/python-*.tar.gz +# if-no-files-found: error - build-android: - name: Build Python for Android - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} - - run: python --version - - working-directory: android - shell: bash - run: | - bash ./build-all.sh "$PYTHON_VERSION" - mkdir -p dist - tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support - bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a - bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a - bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 - - uses: actions/upload-artifact@v4 - with: - name: python-android - path: android/dist/python-android-*.tar.gz - if-no-files-found: error +# build-android: +# name: Build Python for Android +# runs-on: ubuntu-latest +# permissions: +# contents: write +# steps: +# - uses: actions/checkout@v4 +# - uses: actions/setup-python@v5 +# with: +# python-version: ${{ env.PYTHON_VERSION }} +# - run: python --version +# - working-directory: android +# shell: bash +# run: | +# bash ./build-all.sh "$PYTHON_VERSION" +# mkdir -p dist +# tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support +# bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a +# bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a +# bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 +# - uses: actions/upload-artifact@v4 +# with: +# name: python-android +# path: android/dist/python-android-*.tar.gz +# if-no-files-found: error - build-linux: - name: Build Python for Linux - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - working-directory: linux - shell: bash - run: | - bash ./package-for-linux.sh x86_64 "_v2" - bash ./package-for-linux.sh aarch64 "" - - uses: actions/upload-artifact@v4 - with: - name: python-linux - path: linux/python-linux-dart-*.tar.gz - if-no-files-found: error +# build-linux: +# name: Build Python for Linux +# runs-on: ubuntu-latest +# permissions: +# contents: write +# steps: +# - uses: actions/checkout@v4 +# - working-directory: linux +# shell: bash +# run: | +# bash ./package-for-linux.sh x86_64 "_v2" +# bash ./package-for-linux.sh aarch64 "" +# - uses: actions/upload-artifact@v4 +# with: +# name: python-linux +# path: linux/python-linux-dart-*.tar.gz +# if-no-files-found: error build-windows: name: Build Python for Windows @@ -105,14 +104,111 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - name: Install requested Python version - working-directory: windows - shell: cmd + - name: Build CPython from sources and package for Dart + shell: pwsh run: | - curl -OL https://www.python.org/ftp/python/${{ env.PYTHON_WINDOWS_VERSION }}/python-${{ env.PYTHON_WINDOWS_VERSION }}-amd64.exe - start /wait python-${{ env.PYTHON_WINDOWS_VERSION }}-amd64.exe /quiet - C:\python312-dist\python -m compileall -b C:\python312-dist\Lib - 7z a -xr@exclude.txt python-windows-for-dart-%PYTHON_VERSION_SHORT%.zip C:\python312-dist\* + $ErrorActionPreference = "Stop" + + $pyVer = "${{ env.PYTHON_VERSION }}" + $pyShort = "${{ env.PYTHON_VERSION_SHORT }}" + $workspace = $env:GITHUB_WORKSPACE + + $srcRoot = Join-Path $workspace "windows\build" + $srcArchive = Join-Path $srcRoot "Python-$pyVer.tgz" + $srcDir = Join-Path $srcRoot "Python-$pyVer" + $pcbuildDir = Join-Path $srcDir "PCbuild\amd64" + + $packageRoot = Join-Path $workspace "windows\python-windows-for-dart-$pyShort" + $zipPath = Join-Path $workspace "windows\python-windows-for-dart-$pyShort.zip" + + New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null + + Write-Host "Downloading CPython source $pyVer" + Invoke-WebRequest -Uri "https://www.python.org/ftp/python/$pyVer/Python-$pyVer.tgz" -OutFile $srcArchive + tar -xf $srcArchive -C $srcRoot + + Push-Location $srcDir + cmd /c "PCbuild\build.bat -e -p x64 -c Release" + cmd /c "PCbuild\build.bat -e -p x64 -c Debug" + Pop-Location + + Remove-Item -Recurse -Force $packageRoot -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Force -Path "$packageRoot\DLLs", "$packageRoot\include", "$packageRoot\Lib", "$packageRoot\libs", "$packageRoot\Scripts" | Out-Null + + Copy-Item -Path "$srcDir\Include\*" -Destination "$packageRoot\include" -Recurse -Force + Copy-Item -Path "$srcDir\Lib\*" -Destination "$packageRoot\Lib" -Recurse -Force + + # Root binaries and symbols. + foreach ($name in @("LICENSE.txt", "NEWS.txt")) { + $src = Join-Path $srcDir $name + if (Test-Path $src) { + Copy-Item -Path $src -Destination $packageRoot -Force + } + } + + $rootFiles = @( + "python3.dll", + "python3_d.dll", + "python312.dll", + "python312_d.dll", + "python312_d.pdb", + "python_d.pdb", + "pythonw_d.pdb" + ) + foreach ($name in $rootFiles) { + $src = Join-Path $pcbuildDir $name + if (Test-Path $src) { + Copy-Item -Path $src -Destination $packageRoot -Force + } + } + + foreach ($name in @("vcruntime140.dll", "vcruntime140_1.dll")) { + $fromBuild = Join-Path $pcbuildDir $name + $fromSystem = Join-Path "$env:WINDIR\System32" $name + if (Test-Path $fromBuild) { + Copy-Item -Path $fromBuild -Destination $packageRoot -Force + } elseif (Test-Path $fromSystem) { + Copy-Item -Path $fromSystem -Destination $packageRoot -Force + } + } + + # Extension modules and supporting DLLs. + Get-ChildItem -Path $pcbuildDir -Filter "*.pyd" -File | Copy-Item -Destination "$packageRoot\DLLs" -Force + Get-ChildItem -Path $pcbuildDir -Filter "*.dll" -File | + Where-Object { $_.Name -notin @("python3.dll", "python3_d.dll", "python312.dll", "python312_d.dll", "vcruntime140.dll", "vcruntime140_1.dll") } | + Copy-Item -Destination "$packageRoot\DLLs" -Force + Get-ChildItem -Path $pcbuildDir -Filter "*.lib" -File | Copy-Item -Destination "$packageRoot\libs" -Force + + # Match existing packaging behavior: bytecode-only stdlib. + py -3 -m compileall -b "$packageRoot\Lib" + Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force + Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force + + # Fail fast if required layout entries are missing. + $requiredEntries = @( + "$packageRoot\DLLs", + "$packageRoot\include", + "$packageRoot\Lib", + "$packageRoot\libs", + "$packageRoot\Scripts", + "$packageRoot\LICENSE.txt", + "$packageRoot\NEWS.txt", + "$packageRoot\python3.dll", + "$packageRoot\python3_d.dll", + "$packageRoot\python312.dll", + "$packageRoot\python312_d.dll", + "$packageRoot\python312_d.pdb", + "$packageRoot\python_d.pdb", + "$packageRoot\pythonw_d.pdb" + ) + foreach ($entry in $requiredEntries) { + if (-not (Test-Path $entry)) { + throw "Missing required package entry: $entry" + } + } + + Remove-Item -Force $zipPath -ErrorAction SilentlyContinue + 7z a $zipPath "$packageRoot\*" - uses: actions/upload-artifact@v4 with: name: python-windows From ff063fdb486d8426e0653c73ead277277b917ae1 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 15:21:49 -0800 Subject: [PATCH 30/50] Show package dir on missing files; drop doc checks Remove LICENSE.txt and NEWS.txt from the required package entries so builds no longer fail when those optional doc files are absent. Also add an ls $packageRoot before throwing the missing-file error to print the package directory contents and aid debugging when required entries are missing. --- .github/workflows/build-python.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 444b023..5bf558e 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -191,8 +191,6 @@ jobs: "$packageRoot\Lib", "$packageRoot\libs", "$packageRoot\Scripts", - "$packageRoot\LICENSE.txt", - "$packageRoot\NEWS.txt", "$packageRoot\python3.dll", "$packageRoot\python3_d.dll", "$packageRoot\python312.dll", @@ -203,6 +201,7 @@ jobs: ) foreach ($entry in $requiredEntries) { if (-not (Test-Path $entry)) { + ls $packageRoot throw "Missing required package entry: $entry" } } From f7b24760a1397730f95b92fe8c92a984e71ed6bf Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 15:33:44 -0800 Subject: [PATCH 31/50] Prune unnecessary files from Windows Python package Centralize lists of files/dirs to remove and prune the Windows packaging output before zipping. Adds variables for lib dirs, DLL filename patterns, specific DLLs, and import libs to keep, then removes test/idlelib/tkinter/turtledemo dirs, unwanted *.pyd DLLs, listed DLL files, and non-kept .lib files from the package. This keeps the produced zip closer to the installer layout/size and makes cleanup lists easier to maintain. --- .github/workflows/build-python.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 5bf558e..2eaea8d 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -121,6 +121,12 @@ jobs: $packageRoot = Join-Path $workspace "windows\python-windows-for-dart-$pyShort" $zipPath = Join-Path $workspace "windows\python-windows-for-dart-$pyShort.zip" + # Cleanup/include lists kept in one place for visibility and maintenance. + $pruneLibDirs = @("test", "idlelib", "tkinter", "turtledemo") + $pruneDllPatterns = @("_test*.pyd", "_ctypes_test*.pyd", "_tkinter*.pyd", "xxlimited*.pyd") + $pruneDllFiles = @("tcl86t.dll", "tk86t.dll", "zlib1.dll", "pyshellext.dll", "pyshellext_d.dll") + $keepImportLibs = @("python3.lib", "python3_d.lib", "python312.lib", "python312_d.lib") + New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null Write-Host "Downloading CPython source $pyVer" @@ -179,6 +185,28 @@ jobs: Copy-Item -Destination "$packageRoot\DLLs" -Force Get-ChildItem -Path $pcbuildDir -Filter "*.lib" -File | Copy-Item -Destination "$packageRoot\libs" -Force + # Cleanup to keep package close to the original installer-based zip size/layout. + foreach ($dirName in $pruneLibDirs) { + $dirPath = Join-Path "$packageRoot\Lib" $dirName + if (Test-Path $dirPath) { + Remove-Item -Recurse -Force $dirPath + } + } + + foreach ($pattern in $pruneDllPatterns) { + Get-ChildItem -Path "$packageRoot\DLLs" -Filter $pattern -File -ErrorAction SilentlyContinue | Remove-Item -Force + } + foreach ($name in $pruneDllFiles) { + $filePath = Join-Path "$packageRoot\DLLs" $name + if (Test-Path $filePath) { + Remove-Item -Force $filePath + } + } + + Get-ChildItem -Path "$packageRoot\libs" -Filter "*.lib" -File | + Where-Object { $_.Name -notin $keepImportLibs } | + Remove-Item -Force + # Match existing packaging behavior: bytecode-only stdlib. py -3 -m compileall -b "$packageRoot\Lib" Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force From 73f3a5c6c59ff03025245c344020933a038b20c8 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 15:39:27 -0800 Subject: [PATCH 32/50] Prune extra lib dirs and pip packages Update the Windows packaging step to exclude additional standard-lib directories (ensurepip, pydoc_data, lib2to3) and to remove bundled packaging packages from site-packages. Adds $pruneSitePackagesPatterns (pip*, setuptools*, wheel*) and a loop that deletes matching entries under Lib/site-packages to reduce archive size and remove unnecessary tooling from the distributed Python zip. --- .github/workflows/build-python.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 2eaea8d..35ae25d 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -122,10 +122,11 @@ jobs: $zipPath = Join-Path $workspace "windows\python-windows-for-dart-$pyShort.zip" # Cleanup/include lists kept in one place for visibility and maintenance. - $pruneLibDirs = @("test", "idlelib", "tkinter", "turtledemo") + $pruneLibDirs = @("test", "idlelib", "tkinter", "turtledemo", "ensurepip", "pydoc_data", "lib2to3") $pruneDllPatterns = @("_test*.pyd", "_ctypes_test*.pyd", "_tkinter*.pyd", "xxlimited*.pyd") $pruneDllFiles = @("tcl86t.dll", "tk86t.dll", "zlib1.dll", "pyshellext.dll", "pyshellext_d.dll") $keepImportLibs = @("python3.lib", "python3_d.lib", "python312.lib", "python312_d.lib") + $pruneSitePackagesPatterns = @("pip*", "setuptools*", "wheel*") New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null @@ -192,6 +193,12 @@ jobs: Remove-Item -Recurse -Force $dirPath } } + $sitePackagesDir = Join-Path "$packageRoot\Lib" "site-packages" + if (Test-Path $sitePackagesDir) { + foreach ($pattern in $pruneSitePackagesPatterns) { + Get-ChildItem -Path $sitePackagesDir -Filter $pattern -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force + } + } foreach ($pattern in $pruneDllPatterns) { Get-ChildItem -Path "$packageRoot\DLLs" -Filter $pattern -File -ErrorAction SilentlyContinue | Remove-Item -Force From eb9e206a85243ac11bfad97cb68d7746564b74af Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 15:53:49 -0800 Subject: [PATCH 33/50] Move Windows Python packaging to script Replace the large inline PowerShell packaging block in .github/workflows/build-python.yml with a single invocation of windows/package-for-dart.ps1. Add the new packaging script (windows/package-for-dart.ps1) that encapsulates downloading, building and packaging CPython and uses a new exclude list (windows/python-windows-dart.exclude) to prune files. Remove obsolete files windows/exclude.txt and windows/unattend.xml. This centralizes packaging logic, simplifies the workflow file, and improves maintainability. --- .github/workflows/build-python.yml | 137 +--------------------------- windows/exclude.txt | 3 - windows/package-for-dart.ps1 | 133 +++++++++++++++++++++++++++ windows/python-windows-dart.exclude | 25 +++++ windows/unattend.xml | 23 ----- 5 files changed, 159 insertions(+), 162 deletions(-) delete mode 100644 windows/exclude.txt create mode 100644 windows/package-for-dart.ps1 create mode 100644 windows/python-windows-dart.exclude delete mode 100644 windows/unattend.xml diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 35ae25d..0936207 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -107,142 +107,7 @@ jobs: - name: Build CPython from sources and package for Dart shell: pwsh run: | - $ErrorActionPreference = "Stop" - - $pyVer = "${{ env.PYTHON_VERSION }}" - $pyShort = "${{ env.PYTHON_VERSION_SHORT }}" - $workspace = $env:GITHUB_WORKSPACE - - $srcRoot = Join-Path $workspace "windows\build" - $srcArchive = Join-Path $srcRoot "Python-$pyVer.tgz" - $srcDir = Join-Path $srcRoot "Python-$pyVer" - $pcbuildDir = Join-Path $srcDir "PCbuild\amd64" - - $packageRoot = Join-Path $workspace "windows\python-windows-for-dart-$pyShort" - $zipPath = Join-Path $workspace "windows\python-windows-for-dart-$pyShort.zip" - - # Cleanup/include lists kept in one place for visibility and maintenance. - $pruneLibDirs = @("test", "idlelib", "tkinter", "turtledemo", "ensurepip", "pydoc_data", "lib2to3") - $pruneDllPatterns = @("_test*.pyd", "_ctypes_test*.pyd", "_tkinter*.pyd", "xxlimited*.pyd") - $pruneDllFiles = @("tcl86t.dll", "tk86t.dll", "zlib1.dll", "pyshellext.dll", "pyshellext_d.dll") - $keepImportLibs = @("python3.lib", "python3_d.lib", "python312.lib", "python312_d.lib") - $pruneSitePackagesPatterns = @("pip*", "setuptools*", "wheel*") - - New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null - - Write-Host "Downloading CPython source $pyVer" - Invoke-WebRequest -Uri "https://www.python.org/ftp/python/$pyVer/Python-$pyVer.tgz" -OutFile $srcArchive - tar -xf $srcArchive -C $srcRoot - - Push-Location $srcDir - cmd /c "PCbuild\build.bat -e -p x64 -c Release" - cmd /c "PCbuild\build.bat -e -p x64 -c Debug" - Pop-Location - - Remove-Item -Recurse -Force $packageRoot -ErrorAction SilentlyContinue - New-Item -ItemType Directory -Force -Path "$packageRoot\DLLs", "$packageRoot\include", "$packageRoot\Lib", "$packageRoot\libs", "$packageRoot\Scripts" | Out-Null - - Copy-Item -Path "$srcDir\Include\*" -Destination "$packageRoot\include" -Recurse -Force - Copy-Item -Path "$srcDir\Lib\*" -Destination "$packageRoot\Lib" -Recurse -Force - - # Root binaries and symbols. - foreach ($name in @("LICENSE.txt", "NEWS.txt")) { - $src = Join-Path $srcDir $name - if (Test-Path $src) { - Copy-Item -Path $src -Destination $packageRoot -Force - } - } - - $rootFiles = @( - "python3.dll", - "python3_d.dll", - "python312.dll", - "python312_d.dll", - "python312_d.pdb", - "python_d.pdb", - "pythonw_d.pdb" - ) - foreach ($name in $rootFiles) { - $src = Join-Path $pcbuildDir $name - if (Test-Path $src) { - Copy-Item -Path $src -Destination $packageRoot -Force - } - } - - foreach ($name in @("vcruntime140.dll", "vcruntime140_1.dll")) { - $fromBuild = Join-Path $pcbuildDir $name - $fromSystem = Join-Path "$env:WINDIR\System32" $name - if (Test-Path $fromBuild) { - Copy-Item -Path $fromBuild -Destination $packageRoot -Force - } elseif (Test-Path $fromSystem) { - Copy-Item -Path $fromSystem -Destination $packageRoot -Force - } - } - - # Extension modules and supporting DLLs. - Get-ChildItem -Path $pcbuildDir -Filter "*.pyd" -File | Copy-Item -Destination "$packageRoot\DLLs" -Force - Get-ChildItem -Path $pcbuildDir -Filter "*.dll" -File | - Where-Object { $_.Name -notin @("python3.dll", "python3_d.dll", "python312.dll", "python312_d.dll", "vcruntime140.dll", "vcruntime140_1.dll") } | - Copy-Item -Destination "$packageRoot\DLLs" -Force - Get-ChildItem -Path $pcbuildDir -Filter "*.lib" -File | Copy-Item -Destination "$packageRoot\libs" -Force - - # Cleanup to keep package close to the original installer-based zip size/layout. - foreach ($dirName in $pruneLibDirs) { - $dirPath = Join-Path "$packageRoot\Lib" $dirName - if (Test-Path $dirPath) { - Remove-Item -Recurse -Force $dirPath - } - } - $sitePackagesDir = Join-Path "$packageRoot\Lib" "site-packages" - if (Test-Path $sitePackagesDir) { - foreach ($pattern in $pruneSitePackagesPatterns) { - Get-ChildItem -Path $sitePackagesDir -Filter $pattern -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force - } - } - - foreach ($pattern in $pruneDllPatterns) { - Get-ChildItem -Path "$packageRoot\DLLs" -Filter $pattern -File -ErrorAction SilentlyContinue | Remove-Item -Force - } - foreach ($name in $pruneDllFiles) { - $filePath = Join-Path "$packageRoot\DLLs" $name - if (Test-Path $filePath) { - Remove-Item -Force $filePath - } - } - - Get-ChildItem -Path "$packageRoot\libs" -Filter "*.lib" -File | - Where-Object { $_.Name -notin $keepImportLibs } | - Remove-Item -Force - - # Match existing packaging behavior: bytecode-only stdlib. - py -3 -m compileall -b "$packageRoot\Lib" - Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force - Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force - - # Fail fast if required layout entries are missing. - $requiredEntries = @( - "$packageRoot\DLLs", - "$packageRoot\include", - "$packageRoot\Lib", - "$packageRoot\libs", - "$packageRoot\Scripts", - "$packageRoot\python3.dll", - "$packageRoot\python3_d.dll", - "$packageRoot\python312.dll", - "$packageRoot\python312_d.dll", - "$packageRoot\python312_d.pdb", - "$packageRoot\python_d.pdb", - "$packageRoot\pythonw_d.pdb" - ) - foreach ($entry in $requiredEntries) { - if (-not (Test-Path $entry)) { - ls $packageRoot - throw "Missing required package entry: $entry" - } - } - - Remove-Item -Force $zipPath -ErrorAction SilentlyContinue - 7z a $zipPath "$packageRoot\*" + .\windows\package-for-dart.ps1 -PythonVersion "${{ env.PYTHON_VERSION }}" -PythonVersionShort "${{ env.PYTHON_VERSION_SHORT }}" - uses: actions/upload-artifact@v4 with: name: python-windows diff --git a/windows/exclude.txt b/windows/exclude.txt deleted file mode 100644 index 98cb92f..0000000 --- a/windows/exclude.txt +++ /dev/null @@ -1,3 +0,0 @@ -__pycache__ -*.py -*.exe \ No newline at end of file diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 new file mode 100644 index 0000000..1dca25c --- /dev/null +++ b/windows/package-for-dart.ps1 @@ -0,0 +1,133 @@ +param( + [Parameter(Mandatory = $true)] + [string]$PythonVersion, + + [Parameter(Mandatory = $true)] + [string]$PythonVersionShort +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$workspace = $env:GITHUB_WORKSPACE +if (-not $workspace) { + $workspace = Split-Path -Parent $PSScriptRoot +} + +$srcRoot = Join-Path $workspace "windows\build" +$srcArchive = Join-Path $srcRoot "Python-$PythonVersion.tgz" +$srcDir = Join-Path $srcRoot "Python-$PythonVersion" +$pcbuildDir = Join-Path $srcDir "PCbuild\amd64" + +$packageRoot = Join-Path $workspace "windows\python-windows-for-dart-$PythonVersionShort" +$zipPath = Join-Path $workspace "windows\python-windows-for-dart-$PythonVersionShort.zip" +$excludeListPath = Join-Path $workspace "windows\python-windows-dart.exclude" +$keepImportLibs = @("python3.lib", "python3_d.lib", "python312.lib", "python312_d.lib") + +New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null + +Write-Host "Downloading CPython source $PythonVersion" +Invoke-WebRequest -Uri "https://www.python.org/ftp/python/$PythonVersion/Python-$PythonVersion.tgz" -OutFile $srcArchive +tar -xf $srcArchive -C $srcRoot + +Push-Location $srcDir +cmd /c "PCbuild\build.bat -e -p x64 -c Release" +cmd /c "PCbuild\build.bat -e -p x64 -c Debug" +Pop-Location + +Remove-Item -Recurse -Force $packageRoot -ErrorAction SilentlyContinue +New-Item -ItemType Directory -Force -Path "$packageRoot\DLLs", "$packageRoot\include", "$packageRoot\Lib", "$packageRoot\libs", "$packageRoot\Scripts" | Out-Null + +Copy-Item -Path "$srcDir\Include\*" -Destination "$packageRoot\include" -Recurse -Force +Copy-Item -Path "$srcDir\Lib\*" -Destination "$packageRoot\Lib" -Recurse -Force + +# Root binaries and symbols. +foreach ($name in @("LICENSE.txt", "NEWS.txt")) { + $src = Join-Path $srcDir $name + if (Test-Path $src) { + Copy-Item -Path $src -Destination $packageRoot -Force + } +} + +$rootFiles = @( + "python3.dll", + "python3_d.dll", + "python312.dll", + "python312_d.dll", + "python312_d.pdb", + "python_d.pdb", + "pythonw_d.pdb" +) +foreach ($name in $rootFiles) { + $src = Join-Path $pcbuildDir $name + if (Test-Path $src) { + Copy-Item -Path $src -Destination $packageRoot -Force + } +} + +foreach ($name in @("vcruntime140.dll", "vcruntime140_1.dll")) { + $fromBuild = Join-Path $pcbuildDir $name + $fromSystem = Join-Path "$env:WINDIR\System32" $name + if (Test-Path $fromBuild) { + Copy-Item -Path $fromBuild -Destination $packageRoot -Force + } elseif (Test-Path $fromSystem) { + Copy-Item -Path $fromSystem -Destination $packageRoot -Force + } +} + +# Extension modules and supporting DLLs. +Get-ChildItem -Path $pcbuildDir -Filter "*.pyd" -File | Copy-Item -Destination "$packageRoot\DLLs" -Force +Get-ChildItem -Path $pcbuildDir -Filter "*.dll" -File | + Where-Object { $_.Name -notin @("python3.dll", "python3_d.dll", "python312.dll", "python312_d.dll", "vcruntime140.dll", "vcruntime140_1.dll") } | + Copy-Item -Destination "$packageRoot\DLLs" -Force +foreach ($name in $keepImportLibs) { + $src = Join-Path $pcbuildDir $name + if (Test-Path $src) { + Copy-Item -Path $src -Destination "$packageRoot\libs" -Force + } +} + +# Cleanup using exclude list. +if (-not (Test-Path $excludeListPath)) { + throw "Exclude list not found: $excludeListPath" +} +$excludePatterns = Get-Content $excludeListPath | + ForEach-Object { $_.Trim() } | + Where-Object { $_ -and -not $_.StartsWith("#") } +foreach ($pattern in $excludePatterns) { + $fullPattern = Join-Path $packageRoot $pattern + $matches = Get-ChildItem -Path $fullPattern -Force -ErrorAction SilentlyContinue + foreach ($item in $matches) { + Remove-Item -Path $item.FullName -Recurse -Force + } +} + +# Match existing packaging behavior: bytecode-only stdlib. +py -3 -m compileall -b "$packageRoot\Lib" +Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force +Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force + +# Fail fast if required layout entries are missing. +$requiredEntries = @( + "$packageRoot\DLLs", + "$packageRoot\include", + "$packageRoot\Lib", + "$packageRoot\libs", + "$packageRoot\Scripts", + "$packageRoot\python3.dll", + "$packageRoot\python3_d.dll", + "$packageRoot\python312.dll", + "$packageRoot\python312_d.dll", + "$packageRoot\python312_d.pdb", + "$packageRoot\python_d.pdb", + "$packageRoot\pythonw_d.pdb" +) +foreach ($entry in $requiredEntries) { + if (-not (Test-Path $entry)) { + Get-ChildItem $packageRoot + throw "Missing required package entry: $entry" + } +} + +Remove-Item -Force $zipPath -ErrorAction SilentlyContinue +7z a $zipPath "$packageRoot\*" diff --git a/windows/python-windows-dart.exclude b/windows/python-windows-dart.exclude new file mode 100644 index 0000000..886d977 --- /dev/null +++ b/windows/python-windows-dart.exclude @@ -0,0 +1,25 @@ +# Files and directories to exclude from Windows Dart package. +# Patterns are relative to package root: python-windows-for-dart-/ + +# Stdlib content not needed for embedded runtime package. +Lib/test +Lib/idlelib +Lib/tkinter +Lib/turtledemo +Lib/ensurepip +Lib/pydoc_data +Lib/lib2to3 +Lib/site-packages/pip* +Lib/site-packages/setuptools* +Lib/site-packages/wheel* + +# Optional/test/GUI extension modules and DLLs. +DLLs/_test*.pyd +DLLs/_ctypes_test*.pyd +DLLs/_tkinter*.pyd +DLLs/xxlimited*.pyd +DLLs/tcl86t.dll +DLLs/tk86t.dll +DLLs/zlib1.dll +DLLs/pyshellext.dll +DLLs/pyshellext_d.dll diff --git a/windows/unattend.xml b/windows/unattend.xml deleted file mode 100644 index 286ae72..0000000 --- a/windows/unattend.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - \ No newline at end of file From 103ce8075c1335da911dc96d0ee5e77f85e19c2f Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 16:01:33 -0800 Subject: [PATCH 34/50] Enable platform build and release jobs in CI Uncomment and enable CI jobs for Darwin, Android, and Linux builds and the publish-release workflow. Restores the full job steps (checkout, setup Python, build/package, and upload artifacts) and standardizes YAML formatting. Also updates the release tag to use the full PYTHON_VERSION when publishing assets. --- .github/workflows/build-python.yml | 202 ++++++++++++++--------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 0936207..b7bb32e 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -12,90 +12,90 @@ env: PYTHON_DIST_RELEASE: 20260203 jobs: -# build-darwin: -# name: Build Python for iOS and macOS -# runs-on: macos-15 -# permissions: -# contents: write + build-darwin: + name: Build Python for iOS and macOS + runs-on: macos-15 + permissions: + contents: write -# steps: -# - name: Checkout -# uses: actions/checkout@v4 + steps: + - name: Checkout + uses: actions/checkout@v4 -# - name: Setup Python -# uses: actions/setup-python@v6 -# with: -# python-version: ${{ env.PYTHON_VERSION_SHORT }} + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION_SHORT }} -# - name: Show Python version -# run: python --version + - name: Show Python version + run: python --version -# - name: Build Python for iOS and macOS -# working-directory: darwin -# shell: bash -# run: | -# git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git -# mkdir -p dist + - name: Build Python for iOS and macOS + working-directory: darwin + shell: bash + run: | + git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git + mkdir -p dist -# pushd Python-Apple-support -# make iOS -# tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . -# make macOS -# popd + pushd Python-Apple-support + make iOS + tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . + make macOS + popd -# bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" -# bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" -# - name: Upload Darwin build artifacts -# uses: actions/upload-artifact@v4 -# with: -# name: python-darwin -# path: darwin/dist/python-*.tar.gz -# if-no-files-found: error + - name: Upload Darwin build artifacts + uses: actions/upload-artifact@v4 + with: + name: python-darwin + path: darwin/dist/python-*.tar.gz + if-no-files-found: error -# build-android: -# name: Build Python for Android -# runs-on: ubuntu-latest -# permissions: -# contents: write -# steps: -# - uses: actions/checkout@v4 -# - uses: actions/setup-python@v5 -# with: -# python-version: ${{ env.PYTHON_VERSION }} -# - run: python --version -# - working-directory: android -# shell: bash -# run: | -# bash ./build-all.sh "$PYTHON_VERSION" -# mkdir -p dist -# tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -# bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a -# bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a -# bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 -# - uses: actions/upload-artifact@v4 -# with: -# name: python-android -# path: android/dist/python-android-*.tar.gz -# if-no-files-found: error + build-android: + name: Build Python for Android + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + - run: python --version + - working-directory: android + shell: bash + run: | + bash ./build-all.sh "$PYTHON_VERSION" + mkdir -p dist + tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support + bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a + bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a + bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 + - uses: actions/upload-artifact@v4 + with: + name: python-android + path: android/dist/python-android-*.tar.gz + if-no-files-found: error -# build-linux: -# name: Build Python for Linux -# runs-on: ubuntu-latest -# permissions: -# contents: write -# steps: -# - uses: actions/checkout@v4 -# - working-directory: linux -# shell: bash -# run: | -# bash ./package-for-linux.sh x86_64 "_v2" -# bash ./package-for-linux.sh aarch64 "" -# - uses: actions/upload-artifact@v4 -# with: -# name: python-linux -# path: linux/python-linux-dart-*.tar.gz -# if-no-files-found: error + build-linux: + name: Build Python for Linux + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - working-directory: linux + shell: bash + run: | + bash ./package-for-linux.sh x86_64 "_v2" + bash ./package-for-linux.sh aarch64 "" + - uses: actions/upload-artifact@v4 + with: + name: python-linux + path: linux/python-linux-dart-*.tar.gz + if-no-files-found: error build-windows: name: Build Python for Windows @@ -114,30 +114,30 @@ jobs: path: windows/python-windows-for-dart-*.zip if-no-files-found: error -# publish-release: -# name: Publish Release Assets -# runs-on: ubuntu-latest -# needs: -# - build-darwin -# - build-android -# - build-linux -# - build-windows -# permissions: -# contents: write -# steps: -# - name: Download all build artifacts -# uses: actions/download-artifact@v4 -# with: -# pattern: python-* -# path: release-artifacts -# merge-multiple: true + publish-release: + name: Publish Release Assets + runs-on: ubuntu-latest + needs: + - build-darwin + - build-android + - build-linux + - build-windows + permissions: + contents: write + steps: + - name: Download all build artifacts + uses: actions/download-artifact@v4 + with: + pattern: python-* + path: release-artifacts + merge-multiple: true -# - name: Publish all artifacts to release -# uses: softprops/action-gh-release@v2 -# with: -# tag_name: v${{ env.PYTHON_VERSION_SHORT }} -# files: release-artifacts/* -# fail_on_unmatched_files: true -# generate_release_notes: false -# draft: false -# prerelease: false + - name: Publish all artifacts to release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ env.PYTHON_VERSION }} + files: release-artifacts/* + fail_on_unmatched_files: true + generate_release_notes: false + draft: false + prerelease: false From 272f4873acd8e39cd82f9303f608c76d799111bb Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 17:18:30 -0800 Subject: [PATCH 35/50] Copy pyconfig.h into Windows package include Ensure the generated Windows pyconfig.h (from the PC directory) is present and packaged. The script now checks for the header and throws if missing, then copies it to the package include directory (overwriting if necessary). This guarantees the platform-specific pyconfig.h is bundled for consumers building against the package. --- windows/package-for-dart.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 index 1dca25c..0bd2b91 100644 --- a/windows/package-for-dart.ps1 +++ b/windows/package-for-dart.ps1 @@ -41,6 +41,13 @@ New-Item -ItemType Directory -Force -Path "$packageRoot\DLLs", "$packageRoot\inc Copy-Item -Path "$srcDir\Include\*" -Destination "$packageRoot\include" -Recurse -Force Copy-Item -Path "$srcDir\Lib\*" -Destination "$packageRoot\Lib" -Recurse -Force +# pyconfig.h is generated/platform-specific and lives under PC on Windows builds. +$pyconfigHeader = Join-Path $srcDir "PC\pyconfig.h" +if (-not (Test-Path $pyconfigHeader)) { + throw "Missing required header: $pyconfigHeader" +} +Copy-Item -Path $pyconfigHeader -Destination "$packageRoot\include\pyconfig.h" -Force + # Root binaries and symbols. foreach ($name in @("LICENSE.txt", "NEWS.txt")) { $src = Join-Path $srcDir $name From 3da5a6cb0ae0e64dcb6ffc5f5ca597f3f008aae0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 18:31:56 -0800 Subject: [PATCH 36/50] Setup Python and print version in workflow Add a Setup Python step (actions/setup-python@v6) that uses the PYTHON_VERSION_SHORT env var, and a step to show the Python versions (python --version and py -3 --version) before the Build CPython step. This ensures the runner has the expected Python interpreter available and makes it easier to debug/version-check the environment prior to building and packaging. --- .github/workflows/build-python.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index b7bb32e..ae815ba 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -104,6 +104,15 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION_SHORT }} + - name: Show Python version + shell: pwsh + run: | + python --version + py -3 --version - name: Build CPython from sources and package for Dart shell: pwsh run: | From e77e0174f42197cef5b1fac2903b3d4ddca42f55 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 18:33:43 -0800 Subject: [PATCH 37/50] Prune empty dirs and drop Scripts requirement Add a post-cleanup step to remove empty directories left after exclusions and cleanup (recursively, sorting by path length to remove nested dirs first). Also remove "$packageRoot\Scripts" from the required layout entries so the script no longer fails if that folder is absent. --- windows/package-for-dart.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 index 0bd2b91..e540608 100644 --- a/windows/package-for-dart.ps1 +++ b/windows/package-for-dart.ps1 @@ -114,13 +114,18 @@ py -3 -m compileall -b "$packageRoot\Lib" Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force +# Remove empty directories left after exclusions/cleanup. +Get-ChildItem -Path $packageRoot -Recurse -Directory | + Sort-Object { $_.FullName.Length } -Descending | + Where-Object { (Get-ChildItem -Path $_.FullName -Force | Measure-Object).Count -eq 0 } | + Remove-Item -Force + # Fail fast if required layout entries are missing. $requiredEntries = @( "$packageRoot\DLLs", "$packageRoot\include", "$packageRoot\Lib", "$packageRoot\libs", - "$packageRoot\Scripts", "$packageRoot\python3.dll", "$packageRoot\python3_d.dll", "$packageRoot\python312.dll", From 43cd474c07a554078786139e5aa51278f91545c8 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 18:37:48 -0800 Subject: [PATCH 38/50] Use python executable & multiline PS args Replace usage of the Windows py launcher with an explicit python invocation and format the PowerShell call to package-for-dart.ps1 across multiple lines. - workflow: print the python executable (python -c) instead of calling py -3 --version, and invoke the packaging script with PowerShell line continuations for readability. - script: switch compile step in windows/package-for-dart.ps1 from py -3 -m compileall to python -m compileall to avoid relying on the py launcher. These changes make the CI and packaging scripts use the active python interpreter directly and improve command readability in the workflow. --- .github/workflows/build-python.yml | 6 ++++-- windows/package-for-dart.ps1 | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index ae815ba..0567981 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -112,11 +112,13 @@ jobs: shell: pwsh run: | python --version - py -3 --version + python -c "import sys; print(sys.executable)" - name: Build CPython from sources and package for Dart shell: pwsh run: | - .\windows\package-for-dart.ps1 -PythonVersion "${{ env.PYTHON_VERSION }}" -PythonVersionShort "${{ env.PYTHON_VERSION_SHORT }}" + .\windows\package-for-dart.ps1 ` + -PythonVersion "${{ env.PYTHON_VERSION }}" ` + -PythonVersionShort "${{ env.PYTHON_VERSION_SHORT }}" - uses: actions/upload-artifact@v4 with: name: python-windows diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 index e540608..d90283b 100644 --- a/windows/package-for-dart.ps1 +++ b/windows/package-for-dart.ps1 @@ -110,7 +110,7 @@ foreach ($pattern in $excludePatterns) { } # Match existing packaging behavior: bytecode-only stdlib. -py -3 -m compileall -b "$packageRoot\Lib" +python -m compileall -b "$packageRoot\Lib" Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force From e4d0176266df1007327e62d10dee0cb2e2a85be8 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 8 Feb 2026 18:39:43 -0800 Subject: [PATCH 39/50] Force PCbuild to use configured Python Resolve the python executable via Get-Command and set PYTHON and PYTHON_FOR_BUILD environment variables (erroring if python is not in PATH). This ensures PCbuild helper scripts use the configured Python executable instead of the py.exe launcher before invoking PCbuild\build.bat. --- windows/package-for-dart.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 index d90283b..9f355b9 100644 --- a/windows/package-for-dart.ps1 +++ b/windows/package-for-dart.ps1 @@ -30,6 +30,14 @@ Write-Host "Downloading CPython source $PythonVersion" Invoke-WebRequest -Uri "https://www.python.org/ftp/python/$PythonVersion/Python-$PythonVersion.tgz" -OutFile $srcArchive tar -xf $srcArchive -C $srcRoot +# Force PCbuild helper scripts to use configured Python, not py.exe launcher. +$pythonFromPath = (Get-Command python).Source +if (-not $pythonFromPath) { + throw "python was not found in PATH" +} +$env:PYTHON = $pythonFromPath +$env:PYTHON_FOR_BUILD = $pythonFromPath + Push-Location $srcDir cmd /c "PCbuild\build.bat -e -p x64 -c Release" cmd /c "PCbuild\build.bat -e -p x64 -c Debug" From 9c1f9d62f64a235fef5b5c486ced801b5d0403d0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 9 Feb 2026 13:06:36 -0800 Subject: [PATCH 40/50] Use short Python version for release tag Update the GitHub Actions publish step to use env.PYTHON_VERSION_SHORT for the release tag_name instead of the full PYTHON_VERSION. This ensures generated release tags use the shorter/version identifier (e.g., 3.9) expected for release naming when publishing artifacts. --- .github/workflows/build-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 0567981..175c4f1 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -146,7 +146,7 @@ jobs: - name: Publish all artifacts to release uses: softprops/action-gh-release@v2 with: - tag_name: v${{ env.PYTHON_VERSION }} + tag_name: v${{ env.PYTHON_VERSION_SHORT }} files: release-artifacts/* fail_on_unmatched_files: true generate_release_notes: false From 4be77260216c2c134f9191ec2f10159ad4b4744b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 9 Feb 2026 19:53:04 -0800 Subject: [PATCH 41/50] Support Python 3.13 Android builds and packaging Bump CI to Python 3.13.12 and add logic to handle CPython 3.13+ changes: only arm64-v8a and x86_64 ABIs are supported by the official Android/android.py flow, so build scripts and the GitHub workflow now compute a version_int and conditionally skip armeabi-v7a for 3.13+. build.sh was updated to use CPython's Android tooling for >=3.13, set ANDROID_HOME/ANDROID_API_LEVEL/NDK linkage as needed, and enforce supported ABIs. The legacy 3.13_pending patch was removed. Packaging scripts for Android, iOS and macOS now require a host pythonX.Y to compile stdlib bytecode with an isolated interpreter (-I -m compileall -b) to avoid importing from target dirs. README updated to note the new CPython Android build flow and ABI support. --- .github/workflows/build-python.yml | 10 +++++-- android/README.md | 11 ++++--- android/build-all.sh | 11 ++++++- android/build.sh | 48 +++++++++++++++++++++++++----- android/package-for-dart.sh | 7 ++++- android/patches/3.13_pending.patch | 39 ------------------------ darwin/package-ios-for-dart.sh | 9 ++++-- darwin/package-macos-for-dart.sh | 9 ++++-- 8 files changed, 85 insertions(+), 59 deletions(-) delete mode 100644 android/patches/3.13_pending.patch diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 175c4f1..91610d9 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -7,8 +7,8 @@ on: workflow_dispatch: env: - PYTHON_VERSION: 3.12.12 - PYTHON_VERSION_SHORT: 3.12 + PYTHON_VERSION: 3.13.12 + PYTHON_VERSION_SHORT: 3.13 PYTHON_DIST_RELEASE: 20260203 jobs: @@ -71,8 +71,12 @@ jobs: mkdir -p dist tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a - bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 + read version_major version_minor < <(echo "$PYTHON_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') + version_int=$((version_major * 100 + version_minor)) + if [ $version_int -lt 313 ]; then + bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a + fi - uses: actions/upload-artifact@v4 with: name: python-android diff --git a/android/README.md b/android/README.md index 7c276ac..b213df1 100644 --- a/android/README.md +++ b/android/README.md @@ -3,24 +3,27 @@ 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. +* Build Python 3.x - specific or the last minor version. * Installs NDK r29 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 +* Python 3.13+ uses CPython's official `Android/android.py` build flow. ## Usage -To build the latest minor version of Python 3.12 for selected Android API: +To build Python for a specific ABI: ``` -./build.sh 3.12 arm64-v8a +./build.sh 3.13.12 arm64-v8a ``` To build all ABIs: ``` -./build-all.sh 3.12 +./build-all.sh 3.13.12 ``` +For Python 3.13+, official CPython Android tooling currently supports `arm64-v8a` and `x86_64`. + ## Credits Build process depends on: diff --git a/android/build-all.sh b/android/build-all.sh index c2db4c3..e536dd6 100755 --- a/android/build-all.sh +++ b/android/build-all.sh @@ -2,7 +2,16 @@ set -euo pipefail python_version=${1:?} -abis="arm64-v8a armeabi-v7a x86_64 x86" +read version_major version_minor < <( + echo "$python_version" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/' +) +version_int=$((version_major * 100 + version_minor)) + +if [ $version_int -ge 313 ]; then + abis="arm64-v8a x86_64" +else + abis="arm64-v8a armeabi-v7a x86_64 x86" +fi for abi in $abis; do bash ./build.sh $python_version $abi diff --git a/android/build.sh b/android/build.sh index 99cc1cf..c0bf25a 100755 --- a/android/build.sh +++ b/android/build.sh @@ -20,7 +20,11 @@ mkdir -p $downloads cd $script_dir . abi-to-host.sh -. android-env.sh +: ${api_level:=24} + +if [ $version_int -le 312 ]; then + . android-env.sh +fi # Download and unpack Python source code. version_dir=$script_dir/build/$version @@ -49,10 +53,6 @@ fi if [ $version_int -eq 312 ]; then patches+=" bldlibrary grp" fi -if [ $version_int -eq 313 ]; then - # TODO: remove this once it's merged upstream. - patches+=" 3.13_pending" -fi for name in $patches; do patch_file="$script_dir/patches/$name.patch" echo "$patch_file" @@ -145,9 +145,43 @@ if [ $version_int -le 312 ]; then # Python 3.13 and later comes with an official Android build script. else - mkdir -p cross-build/build - ln -s "$(which python$version_short)" cross-build/build/python + case "$abi" in + arm64-v8a|x86_64) + ;; + *) + echo "Python $version_short official Android build supports only: arm64-v8a, x86_64" + exit 1 + ;; + esac + + # CPython's Android tooling expects ANDROID_HOME and ANDROID_API_LEVEL. + export ANDROID_API_LEVEL="$api_level" + if [ -z "${ANDROID_HOME:-}" ]; then + if [ -d "$HOME/Library/Android/sdk" ]; then + export ANDROID_HOME="$HOME/Library/Android/sdk" + elif [ -d "$HOME/Android/Sdk" ]; then + export ANDROID_HOME="$HOME/Android/Sdk" + else + export ANDROID_HOME="$script_dir/android-sdk" + mkdir -p "$ANDROID_HOME" + fi + fi + + # Reuse NDK installed by this repo's older workflow by exposing it + # at the path expected by CPython's Android/android-env.sh. + if [ -z "${NDK_HOME:-}" ] && [ -d "$HOME/ndk/r29" ]; then + export NDK_HOME="$HOME/ndk/r29" + fi + cpython_ndk_version=$(sed -n 's/^ndk_version=//p' Android/android-env.sh | head -n1) + if [ -n "${NDK_HOME:-}" ] && [ -d "$NDK_HOME" ] && [ -n "${cpython_ndk_version:-}" ]; then + mkdir -p "$ANDROID_HOME/ndk" + if [ ! -e "$ANDROID_HOME/ndk/$cpython_ndk_version" ]; then + ln -s "$NDK_HOME" "$ANDROID_HOME/ndk/$cpython_ndk_version" + fi + fi + Android/android.py configure-build + Android/android.py make-build Android/android.py configure-host "$HOST" Android/android.py make-host "$HOST" cp -a "cross-build/$HOST/prefix/"* "$PREFIX" diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 975c401..0263894 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -13,6 +13,11 @@ 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 +python_bin=$(command -v "python$python_version_short" || true) +if [ -z "$python_bin" ]; then + echo "python$python_version_short is required to compile stdlib bytecode" + exit 1 +fi # create build dir build_dir=build/python-$python_version/$abi @@ -39,8 +44,8 @@ mv $build_dir/lib/python$python_version_short/lib-dynload $bundle_dir/modules # stdlib # stdlib_zip=$bundle_dir/stdlib.zip +"$python_bin" -I -m compileall -b "$build_dir/lib/python$python_version_short" 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__ diff --git a/android/patches/3.13_pending.patch b/android/patches/3.13_pending.patch deleted file mode 100644 index 31dd579..0000000 --- a/android/patches/3.13_pending.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/Android/android-env.sh b/Android/android-env.sh -index 93372e3fe1c..94712602a23 100644 ---- a/Android/android-env.sh -+++ b/Android/android-env.sh -@@ -24,7 +24,7 @@ fail() { - # * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md - # where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: - # https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md --ndk_version=26.2.11394342 -+ndk_version=29.0.14206865 - - ndk=$ANDROID_HOME/ndk/$ndk_version - if ! [ -e $ndk ]; then -@@ -58,8 +58,8 @@ for path in "$AR" "$AS" "$CC" "$CXX" "$LD" "$NM" "$RANLIB" "$READELF" "$STRIP"; - fi - done - --export CFLAGS="" --export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment" -+export CFLAGS="-D__BIONIC_NO_PAGE_SIZE_MACRO" -+export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,-z,max-page-size=16384" - - # Unlike Linux, Android does not implicitly use a dlopened library to resolve - # relocations in subsequently-loaded libraries, even if RTLD_GLOBAL is used -diff --git a/Android/android.py b/Android/android.py -index 8696d9eaeca..b3ee449ba43 100755 ---- a/Android/android.py -+++ b/Android/android.py -@@ -138,8 +138,8 @@ def make_build_python(context): - - def unpack_deps(host): - deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download" -- for name_ver in ["bzip2-1.0.8-1", "libffi-3.4.4-2", "openssl-3.0.15-0", -- "sqlite-3.45.1-0", "xz-5.4.6-0"]: -+ for name_ver in ["bzip2-1.0.8-2", "libffi-3.4.4-3", "openssl-3.0.15-4", -+ "sqlite-3.45.3-3", "xz-5.4.6-1"]: - filename = f"{name_ver}-{host}.tar.gz" - download(f"{deps_url}/{name_ver}/{filename}") - run(["tar", "-xf", filename]) diff --git a/darwin/package-ios-for-dart.sh b/darwin/package-ios-for-dart.sh index 9fdf41a..1921851 100755 --- a/darwin/package-ios-for-dart.sh +++ b/darwin/package-ios-for-dart.sh @@ -11,6 +11,11 @@ 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 +python_bin=$(command -v "python$python_version_short" || true) +if [ -z "$python_bin" ]; then + echo "python$python_version_short is required to compile stdlib bytecode" + exit 1 +fi # create build directory build_dir=build/python-$python_version @@ -63,9 +68,9 @@ for arch in "${archs[@]}"; do rm -rf $stdlib_dir/$arch done -# compile stdlib +# compile stdlib with an isolated interpreter, without importing from target stdlib dir. +"$python_bin" -I -m compileall -b "$stdlib_dir" cd $stdlib_dir -python -m compileall -b . find . \( -name '*.so' -or -name "*.$dylib_ext" -or -name '*.py' -or -name '*.typed' \) -type f -delete rm -rf __pycache__ rm -rf **/__pycache__ diff --git a/darwin/package-macos-for-dart.sh b/darwin/package-macos-for-dart.sh index f4a7404..913c872 100755 --- a/darwin/package-macos-for-dart.sh +++ b/darwin/package-macos-for-dart.sh @@ -9,6 +9,11 @@ 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 +python_bin=$(command -v "python$python_version_short" || true) +if [ -z "$python_bin" ]; then + echo "python$python_version_short is required to compile stdlib bytecode" + exit 1 +fi # create build directory build_dir=build/python-$python_version @@ -34,9 +39,9 @@ rm $frameworks_dir/Python.xcframework/macos-arm64_x86_64/Python.framework/Header # copy stdlibs rsync -av --exclude-from=$script_dir/python-darwin-stdlib.exclude $python_apple_support_root/install/macOS/macosx/python-*/Python.framework/Versions/Current/lib/python$python_version_short/* $stdlib_dir -# compile stdlib +# compile stdlib with an isolated interpreter, without importing from target stdlib dir. +"$python_bin" -I -m compileall -b "$stdlib_dir" cd $stdlib_dir -python -m compileall -b . find . \( -name '*.py' -or -name '*.typed' \) -type f -delete rm -rf __pycache__ rm -rf **/__pycache__ From 6c39e4682cdc871cb7eca4a76edc88a860730d1a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 9 Feb 2026 20:17:09 -0800 Subject: [PATCH 42/50] Make packaging scripts robust for Python versions Add resilience and version-awareness to Android and Windows packaging scripts. The Android script now creates a downloads directory. The Windows script derives a pythonTag from the short version to generalize DLL/import-lib names, searches multiple candidate locations for pyconfig.h with a clearer error message, and updates file selection to use the tagged names. Also use the chosen Python executable (& $pythonFromPath -I) when running compileall to ensure the correct interpreter is used. --- android/package-for-dart.sh | 2 ++ windows/package-for-dart.ps1 | 39 ++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 0263894..950cabf 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -6,6 +6,8 @@ python_version=${2:?} abi=${3:?} script_dir=$(dirname $(realpath $0)) +downloads=$script_dir/downloads +mkdir -p $downloads . abi-to-host.sh . android-env.sh diff --git a/windows/package-for-dart.ps1 b/windows/package-for-dart.ps1 index 9f355b9..fd81a30 100644 --- a/windows/package-for-dart.ps1 +++ b/windows/package-for-dart.ps1 @@ -18,11 +18,12 @@ $srcRoot = Join-Path $workspace "windows\build" $srcArchive = Join-Path $srcRoot "Python-$PythonVersion.tgz" $srcDir = Join-Path $srcRoot "Python-$PythonVersion" $pcbuildDir = Join-Path $srcDir "PCbuild\amd64" +$pythonTag = $PythonVersionShort -replace '\.', '' $packageRoot = Join-Path $workspace "windows\python-windows-for-dart-$PythonVersionShort" $zipPath = Join-Path $workspace "windows\python-windows-for-dart-$PythonVersionShort.zip" $excludeListPath = Join-Path $workspace "windows\python-windows-dart.exclude" -$keepImportLibs = @("python3.lib", "python3_d.lib", "python312.lib", "python312_d.lib") +$keepImportLibs = @("python3.lib", "python3_d.lib", "python$pythonTag.lib", "python${pythonTag}_d.lib") New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null @@ -49,10 +50,22 @@ New-Item -ItemType Directory -Force -Path "$packageRoot\DLLs", "$packageRoot\inc Copy-Item -Path "$srcDir\Include\*" -Destination "$packageRoot\include" -Recurse -Force Copy-Item -Path "$srcDir\Lib\*" -Destination "$packageRoot\Lib" -Recurse -Force -# pyconfig.h is generated/platform-specific and lives under PC on Windows builds. -$pyconfigHeader = Join-Path $srcDir "PC\pyconfig.h" -if (-not (Test-Path $pyconfigHeader)) { - throw "Missing required header: $pyconfigHeader" +# pyconfig.h location varies by CPython version/build layout. +$pyconfigCandidates = @( + (Join-Path $srcDir "PC\pyconfig.h"), + (Join-Path $srcDir "Include\pyconfig.h"), + (Join-Path $pcbuildDir "pyconfig.h") +) +$pyconfigHeader = $null +foreach ($candidate in $pyconfigCandidates) { + if (Test-Path $candidate) { + $pyconfigHeader = $candidate + break + } +} +if (-not $pyconfigHeader) { + $candidateList = $pyconfigCandidates -join ", " + throw "Missing required header. Checked: $candidateList" } Copy-Item -Path $pyconfigHeader -Destination "$packageRoot\include\pyconfig.h" -Force @@ -67,9 +80,9 @@ foreach ($name in @("LICENSE.txt", "NEWS.txt")) { $rootFiles = @( "python3.dll", "python3_d.dll", - "python312.dll", - "python312_d.dll", - "python312_d.pdb", + "python$pythonTag.dll", + "python${pythonTag}_d.dll", + "python${pythonTag}_d.pdb", "python_d.pdb", "pythonw_d.pdb" ) @@ -93,7 +106,7 @@ foreach ($name in @("vcruntime140.dll", "vcruntime140_1.dll")) { # Extension modules and supporting DLLs. Get-ChildItem -Path $pcbuildDir -Filter "*.pyd" -File | Copy-Item -Destination "$packageRoot\DLLs" -Force Get-ChildItem -Path $pcbuildDir -Filter "*.dll" -File | - Where-Object { $_.Name -notin @("python3.dll", "python3_d.dll", "python312.dll", "python312_d.dll", "vcruntime140.dll", "vcruntime140_1.dll") } | + Where-Object { $_.Name -notin @("python3.dll", "python3_d.dll", "python$pythonTag.dll", "python${pythonTag}_d.dll", "vcruntime140.dll", "vcruntime140_1.dll") } | Copy-Item -Destination "$packageRoot\DLLs" -Force foreach ($name in $keepImportLibs) { $src = Join-Path $pcbuildDir $name @@ -118,7 +131,7 @@ foreach ($pattern in $excludePatterns) { } # Match existing packaging behavior: bytecode-only stdlib. -python -m compileall -b "$packageRoot\Lib" +& $pythonFromPath -I -m compileall -b "$packageRoot\Lib" Get-ChildItem -Path "$packageRoot\Lib" -Recurse -File -Include *.py,*.typed | Remove-Item -Force Get-ChildItem -Path "$packageRoot\Lib" -Recurse -Directory -Filter __pycache__ | Remove-Item -Recurse -Force @@ -136,9 +149,9 @@ $requiredEntries = @( "$packageRoot\libs", "$packageRoot\python3.dll", "$packageRoot\python3_d.dll", - "$packageRoot\python312.dll", - "$packageRoot\python312_d.dll", - "$packageRoot\python312_d.pdb", + "$packageRoot\python$pythonTag.dll", + "$packageRoot\python${pythonTag}_d.dll", + "$packageRoot\python${pythonTag}_d.pdb", "$packageRoot\python_d.pdb", "$packageRoot\pythonw_d.pdb" ) From 534dfb99411a35452fbe9ec6667cd47cd2e95aca Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 9 Feb 2026 20:46:07 -0800 Subject: [PATCH 43/50] Migrate Android build to official Python 3.13 flow Switch Android build tooling to CPython's official Android/android.py flow and restrict support to Python 3.13.x and modern ABIs. Removed legacy cross-compile helpers, many version-specific patches, and android-env.sh; simplified abi mappings (drop armeabi-v7a/x86), updated build scripts to only handle arm64-v8a and x86_64, and added explicit checks/failures for unsupported versions/abis. CI packaging no longer includes armeabi-v7a for older Python versions. package-for-dart now locates NDK_HOME from common SDK locations (and fails if none found) and validates llvm-strip. README updated to reflect Python 3.13-only support. These changes clean up custom workarounds kept for older Python/NDK combinations and rely on upstream CPython Android tooling. --- .github/workflows/build-python.yml | 5 - android/README.md | 9 +- android/abi-to-host.sh | 6 - android/android-env.sh | 138 --------------- android/build-all.sh | 11 +- android/build.sh | 177 +++++--------------- android/package-for-dart.sh | 39 ++++- android/patches/bldlibrary.patch | 60 ------- android/patches/grp.patch | 16 -- android/patches/python_for_build_deps.patch | 16 -- android/patches/soname.patch | 17 -- android/patches/sysroot_paths.patch | 14 -- 12 files changed, 81 insertions(+), 427 deletions(-) delete mode 100644 android/android-env.sh delete mode 100644 android/patches/bldlibrary.patch delete mode 100644 android/patches/grp.patch delete mode 100644 android/patches/python_for_build_deps.patch delete mode 100644 android/patches/soname.patch delete mode 100644 android/patches/sysroot_paths.patch diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 91610d9..569d257 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -72,11 +72,6 @@ jobs: tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 - read version_major version_minor < <(echo "$PYTHON_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') - version_int=$((version_major * 100 + version_minor)) - if [ $version_int -lt 313 ]; then - bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a - fi - uses: actions/upload-artifact@v4 with: name: python-android diff --git a/android/README.md b/android/README.md index b213df1..e4690d6 100644 --- a/android/README.md +++ b/android/README.md @@ -1,16 +1,15 @@ # Python for Android -Scripts and CI jobs for building Python 3 for Android. +Scripts and CI jobs for building Python 3.13 for Android. * Can be run on both Linux and macOS. -* Build Python 3.x - specific or the last minor version. -* Installs NDK r29 or use pre-installed one with path configured by `NDK_HOME` variable. +* Builds Python 3.13.x only. * Creates Python installation with a structure suitable for https://github.com/flet-dev/mobile-forge -* Python 3.13+ uses CPython's official `Android/android.py` build flow. +* Uses CPython's official `Android/android.py` build flow. ## Usage -To build Python for a specific ABI: +To build Python for a specific ABI (`arm64-v8a` or `x86_64`): ``` ./build.sh 3.13.12 arm64-v8a diff --git a/android/abi-to-host.sh b/android/abi-to-host.sh index 6e717aa..69b7882 100644 --- a/android/abi-to-host.sh +++ b/android/abi-to-host.sh @@ -1,13 +1,7 @@ case ${abi:?} in - armeabi-v7a) - HOST=arm-linux-androideabi - ;; arm64-v8a) HOST=aarch64-linux-android ;; - x86) - HOST=i686-linux-android - ;; x86_64) HOST=x86_64-linux-android ;; diff --git a/android/android-env.sh b/android/android-env.sh deleted file mode 100644 index b4f61fb..0000000 --- a/android/android-env.sh +++ /dev/null @@ -1,138 +0,0 @@ -# This script must be sourced with the following variables already set: -: ${HOST:?} # GNU target triplet - -# You may also override the following: -: ${api_level:=24} # Minimum Android API level the build will run on -: ${PREFIX:-} # Path in which to find required libraries - -NDK_VERSION=r29 - -# Print all messages on stderr so they're visible when running within build-wheel. -log() { - echo "$1" >&2 -} - -fail() { - log "$1" - exit 1 -} - -# When moving to a new version of the NDK, carefully review the following: -# -# * https://developer.android.com/ndk/downloads/revision_history -# -# * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md -# where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: -# https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md -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 = "arm-linux-androideabi" ]; then - clang_triplet=armv7a-linux-androideabi -else - clang_triplet=$HOST -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 - -export CFLAGS="-D__BIONIC_NO_PAGE_SIZE_MACRO" -export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,-z,max-page-size=16384" - -# 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). So any library that fails to -# build with this flag, would also fail to load at runtime. -LDFLAGS="$LDFLAGS -Wl,--no-undefined" - -# Many packages get away with omitting -lm on Linux, but Android is stricter. -LDFLAGS="$LDFLAGS -lm" - -# -mstackrealign is included where necessary in the clang launcher scripts which are -# pointed to by $CC, so we don't need to include it here. -if [ $HOST = "arm-linux-androideabi" ]; then - CFLAGS="$CFLAGS -march=armv7-a -mthumb" -fi - -if [ -n "${PREFIX:-}" ]; then - abs_prefix=$(realpath $PREFIX) - CFLAGS="$CFLAGS -I$abs_prefix/include" - LDFLAGS="$LDFLAGS -L$abs_prefix/lib" - - export PKG_CONFIG="pkg-config --define-prefix" - export PKG_CONFIG_LIBDIR="$abs_prefix/lib/pkgconfig" -fi - -# When compiling C++, some build systems will combine CFLAGS and CXXFLAGS, and some will -# use CXXFLAGS alone. -export CXXFLAGS=$CFLAGS - -# Use the same variable name as conda-build -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 index e536dd6..1ae14a2 100755 --- a/android/build-all.sh +++ b/android/build-all.sh @@ -2,16 +2,7 @@ set -euo pipefail python_version=${1:?} -read version_major version_minor < <( - echo "$python_version" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/' -) -version_int=$((version_major * 100 + version_minor)) - -if [ $version_int -ge 313 ]; then - abis="arm64-v8a x86_64" -else - abis="arm64-v8a armeabi-v7a x86_64 x86" -fi +abis="arm64-v8a x86_64" for abi in $abis; do bash ./build.sh $python_version $abi diff --git a/android/build.sh b/android/build.sh index c0bf25a..8c9f7ff 100755 --- a/android/build.sh +++ b/android/build.sh @@ -9,23 +9,20 @@ read version_major version_minor version_micro < <( ) version_short=$version_major.$version_minor version_no_pre=$version_major.$version_minor.$version_micro -version_int=$(($version_major * 100 + $version_minor)) + +if [ "$version_short" != "3.13" ]; then + echo "This branch only supports Python 3.13.x for Android, got: $version" + exit 1 +fi PREFIX="$script_dir/install/android/$abi/python-${version}" mkdir -p "$PREFIX" PREFIX=$(realpath "$PREFIX") -downloads=$script_dir/downloads -mkdir -p $downloads - cd $script_dir . abi-to-host.sh : ${api_level:=24} -if [ $version_int -le 312 ]; then - . android-env.sh -fi - # Download and unpack Python source code. version_dir=$script_dir/build/$version mkdir -p $version_dir @@ -39,26 +36,6 @@ tar -xf "$src_filename" mv "Python-$version" "$build_dir" cd "$build_dir" -# Apply patches. -patches="" -if [ $version_int -le 311 ]; then - patches+=" sysroot_paths" -fi -if [ $version_int -eq 311 ]; then - patches+=" python_for_build_deps" -fi -if [ $version_int -le 312 ]; then - patches+=" soname" -fi -if [ $version_int -eq 312 ]; then - patches+=" bldlibrary grp" -fi -for name in $patches; do - patch_file="$script_dir/patches/$name.patch" - echo "$patch_file" - patch -p1 -i "$patch_file" -done - # Remove any existing installation in the prefix. rm -rf $PREFIX/{include,lib}/python$version_short rm -rf $PREFIX/lib/libpython$version_short* @@ -72,117 +49,43 @@ echo "Build: 1" >> $support_versions echo "Min android version: $api_level" >> $support_versions echo "---------------------" >> $support_versions -if [ $version_int -le 312 ]; then - # Download and unpack libraries needed to compile Python. For a given Python - # version, we must maintain binary compatibility with existing wheels. - libs="bzip2-1.0.8-2 libffi-3.4.4-3 sqlite-3.45.3-3 xz-5.4.6-1" - if [ $version_int -le 308 ]; then - libs+=" openssl-1.1.1w-3" +case "$abi" in + arm64-v8a|x86_64) + ;; + *) + echo "Python $version_short official Android build supports only: arm64-v8a, x86_64" + exit 1 + ;; +esac + +# CPython's Android tooling expects ANDROID_HOME and ANDROID_API_LEVEL. +export ANDROID_API_LEVEL="$api_level" +if [ -z "${ANDROID_HOME:-}" ]; then + if [ -d "$HOME/Library/Android/sdk" ]; then + export ANDROID_HOME="$HOME/Library/Android/sdk" + elif [ -d "$HOME/Android/Sdk" ]; then + export ANDROID_HOME="$HOME/Android/Sdk" else - libs+=" openssl-3.0.15-4" - fi - - url_prefix="https://github.com/beeware/cpython-android-source-deps/releases/download" - for name_ver in $libs; do - IFS=- read lib_name lib_ver <<< "$name_ver" - url="$url_prefix/$name_ver/$name_ver-$HOST.tar.gz" - echo "$url" - - lib_dir="$script_dir/install/android/$abi/${lib_name}-${lib_ver}" - mkdir -p $lib_dir - lib_file=$downloads/${lib_name}-${lib_ver}-${abi}.tar.gz - curl -Lf "$url" -o $lib_file - tar -xf $lib_file -C $lib_dir - cp -R $lib_dir/* $PREFIX - echo "${lib_name}: $lib_ver" >> $support_versions - 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/$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. - cd "$build_dir" - cat > config.site <<-EOF - # Things that can't be autodetected when cross-compiling. - ac_cv_aligned_required=no # Default of "yes" changes hash function to FNV, which breaks Numba. - ac_cv_file__dev_ptmx=no - ac_cv_file__dev_ptc=no - EOF - export CONFIG_SITE=$(pwd)/config.site - - configure_args="--host=$HOST --build=$(./config.guess) \ - --enable-shared --without-ensurepip --with-openssl=$PREFIX" - - # This prevents the "getaddrinfo bug" test, which can't be run when cross-compiling. - configure_args+=" --enable-ipv6" - - # Some of the patches involve missing Makefile dependencies, which allowed extension - # modules to be built before libpython3.x.so in parallel builds. In case this happens - # again, make sure there's no libpython3.x.a, otherwise the modules may end up silently - # linking with that instead. - if [ $version_int -ge 310 ]; then - configure_args+=" --without-static-libpython" - fi - - if [ $version_int -ge 311 ]; then - configure_args+=" --with-build-python=yes" - fi - - ./configure $configure_args - - make -j $CPU_COUNT - make install prefix=$PREFIX - - echo ">>> Replacing host platform" - sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $PREFIX/lib/python$version_short/config-$version_short/Makefile - -# Python 3.13 and later comes with an official Android build script. -else - case "$abi" in - arm64-v8a|x86_64) - ;; - *) - echo "Python $version_short official Android build supports only: arm64-v8a, x86_64" - exit 1 - ;; - esac - - # CPython's Android tooling expects ANDROID_HOME and ANDROID_API_LEVEL. - export ANDROID_API_LEVEL="$api_level" - if [ -z "${ANDROID_HOME:-}" ]; then - if [ -d "$HOME/Library/Android/sdk" ]; then - export ANDROID_HOME="$HOME/Library/Android/sdk" - elif [ -d "$HOME/Android/Sdk" ]; then - export ANDROID_HOME="$HOME/Android/Sdk" - else - export ANDROID_HOME="$script_dir/android-sdk" - mkdir -p "$ANDROID_HOME" - fi + export ANDROID_HOME="$script_dir/android-sdk" + mkdir -p "$ANDROID_HOME" fi +fi - # Reuse NDK installed by this repo's older workflow by exposing it - # at the path expected by CPython's Android/android-env.sh. - if [ -z "${NDK_HOME:-}" ] && [ -d "$HOME/ndk/r29" ]; then - export NDK_HOME="$HOME/ndk/r29" - fi - cpython_ndk_version=$(sed -n 's/^ndk_version=//p' Android/android-env.sh | head -n1) - if [ -n "${NDK_HOME:-}" ] && [ -d "$NDK_HOME" ] && [ -n "${cpython_ndk_version:-}" ]; then - mkdir -p "$ANDROID_HOME/ndk" - if [ ! -e "$ANDROID_HOME/ndk/$cpython_ndk_version" ]; then - ln -s "$NDK_HOME" "$ANDROID_HOME/ndk/$cpython_ndk_version" - fi +# Reuse NDK installed by this repo's older workflow by exposing it +# at the path expected by CPython's Android/android-env.sh. +if [ -z "${NDK_HOME:-}" ] && [ -d "$HOME/ndk/r29" ]; then + export NDK_HOME="$HOME/ndk/r29" +fi +cpython_ndk_version=$(sed -n 's/^ndk_version=//p' Android/android-env.sh | head -n1) +if [ -n "${NDK_HOME:-}" ] && [ -d "$NDK_HOME" ] && [ -n "${cpython_ndk_version:-}" ]; then + mkdir -p "$ANDROID_HOME/ndk" + if [ ! -e "$ANDROID_HOME/ndk/$cpython_ndk_version" ]; then + ln -s "$NDK_HOME" "$ANDROID_HOME/ndk/$cpython_ndk_version" fi - - Android/android.py configure-build - Android/android.py make-build - Android/android.py configure-host "$HOST" - Android/android.py make-host "$HOST" - cp -a "cross-build/$HOST/prefix/"* "$PREFIX" fi + +Android/android.py configure-build +Android/android.py make-build +Android/android.py configure-host "$HOST" +Android/android.py make-host "$HOST" +cp -a "cross-build/$HOST/prefix/"* "$PREFIX" diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 950cabf..08d5fc1 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -6,11 +6,44 @@ python_version=${2:?} abi=${3:?} script_dir=$(dirname $(realpath $0)) -downloads=$script_dir/downloads -mkdir -p $downloads . abi-to-host.sh -. android-env.sh + +if [ -z "${NDK_HOME:-}" ]; then + sdk_candidates="" + if [ -n "${ANDROID_HOME:-}" ]; then + sdk_candidates+="$ANDROID_HOME " + fi + if [ -d "$HOME/Library/Android/sdk" ]; then + sdk_candidates+="$HOME/Library/Android/sdk " + fi + if [ -d "$HOME/Android/Sdk" ]; then + sdk_candidates+="$HOME/Android/Sdk " + fi + if [ -d "$script_dir/android-sdk" ]; then + sdk_candidates+="$script_dir/android-sdk " + fi + + for sdk_root in $sdk_candidates; do + ndk_candidate=$(ls -d "$sdk_root"/ndk/* 2>/dev/null | sort -V | tail -n1 || true) + if [ -n "$ndk_candidate" ]; then + NDK_HOME="$ndk_candidate" + break + fi + done +fi + +if [ -z "${NDK_HOME:-}" ] || [ ! -d "$NDK_HOME" ]; then + echo "NDK_HOME is not set and no NDK was found in Android SDK locations." + exit 1 +fi + +toolchain=$(echo "$NDK_HOME"/toolchains/llvm/prebuilt/*) +STRIP="$toolchain/bin/llvm-strip" +if [ ! -x "$STRIP" ]; then + echo "llvm-strip not found at: $STRIP" + exit 1 +fi # build short Python version read python_version_major python_version_minor < <(echo $python_version | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') diff --git a/android/patches/bldlibrary.patch b/android/patches/bldlibrary.patch deleted file mode 100644 index 72b0bac..0000000 --- a/android/patches/bldlibrary.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/configure b/configure -index 1c75810d9e8..d883a00d548 100755 ---- a/configure -+++ b/configure -@@ -841,6 +841,7 @@ PY_ENABLE_SHARED - PLATLIBDIR - BINLIBDEST - LIBPYTHON -+MODULE_DEPS_SHARED - EXT_SUFFIX - ALT_SOABI - SOABI -@@ -24402,12 +24403,17 @@ LDVERSION='$(VERSION)$(ABIFLAGS)' - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDVERSION" >&5 - printf "%s\n" "$LDVERSION" >&6; } - --# On Android and Cygwin the shared libraries must be linked with libpython. -+# Configure the flags and dependencies used when compiling shared modules. -+# Do not rename LIBPYTHON - it's accessed via sysconfig by package build -+# systems (e.g. Meson) to decide whether to link extension modules against -+# libpython. -+MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' -+LIBPYTHON='' - -+# 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}" --else -- LIBPYTHON='' -+ MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" -+ LIBPYTHON="\$(BLDLIBRARY)" - fi - - -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 0e64ccc5c21..c4217424508 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -2797,7 +2797,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h - - # force rebuild when header file or module build flavor (static/shared) is changed - MODULE_DEPS_STATIC=Modules/config.c --MODULE_DEPS_SHARED=$(MODULE_DEPS_STATIC) $(EXPORTSYMS) -+MODULE_DEPS_SHARED=@MODULE_DEPS_SHARED@ - - MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h - MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h -diff --git a/Modules/makesetup b/Modules/makesetup -index f000c9cd673..3231044230e 100755 ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -286,7 +286,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | - ;; - 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/grp.patch b/android/patches/grp.patch deleted file mode 100644 index 70ab19b..0000000 --- a/android/patches/grp.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- 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/python_for_build_deps.patch b/android/patches/python_for_build_deps.patch deleted file mode 100644 index 820dd58..0000000 --- a/android/patches/python_for_build_deps.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- 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 deleted file mode 100644 index a348464..0000000 --- a/android/patches/soname.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- 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 deleted file mode 100644 index a649dbc..0000000 --- a/android/patches/sysroot_paths.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- 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('"') From a6b89712434e03e1fb2628e6a8c62f8ac78bf3b2 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 16:20:12 -0800 Subject: [PATCH 44/50] Derive PYTHON_VERSION_SHORT and validate env vars Remove the hardcoded PYTHON_VERSION_SHORT and instead derive it from PYTHON_VERSION in workflow steps (bash and PowerShell) so the short version is available via GITHUB_ENV. In linux/package-for-linux.sh, add required env var checks for PYTHON_VERSION and PYTHON_DIST_RELEASE, provide a fallback derivation for PYTHON_VERSION_SHORT, update the compile path to use the short version, and quote paths when creating the tarball. These changes make CI and packaging robust across Python patch releases and different runner shells. --- .github/workflows/build-python.yml | 20 ++++++++++++++++++-- linux/package-for-linux.sh | 15 +++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 569d257..91b5985 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -8,8 +8,7 @@ on: env: PYTHON_VERSION: 3.13.12 - PYTHON_VERSION_SHORT: 3.13 - PYTHON_DIST_RELEASE: 20260203 + PYTHON_DIST_RELEASE: 20260203 # https://github.com/astral-sh/python-build-standalone/releases jobs: build-darwin: @@ -21,6 +20,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - name: Setup Python uses: actions/setup-python@v6 @@ -60,6 +63,10 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} @@ -103,6 +110,11 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + - name: Derive short Python version + shell: pwsh + run: | + $parts = "${{ env.PYTHON_VERSION }}".Split(".") + "PYTHON_VERSION_SHORT=$($parts[0]).$($parts[1])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Setup Python uses: actions/setup-python@v6 with: @@ -135,6 +147,10 @@ jobs: permissions: contents: write steps: + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - name: Download all build artifacts uses: actions/download-artifact@v4 with: diff --git a/linux/package-for-linux.sh b/linux/package-for-linux.sh index dbd5698..b202c3a 100755 --- a/linux/package-for-linux.sh +++ b/linux/package-for-linux.sh @@ -4,17 +4,28 @@ set -euo pipefail PYTHON_ARCH=${1:?} PYTHON_ARCH_VER=${2:-""} +if [ -z "${PYTHON_VERSION:-}" ]; then + echo "PYTHON_VERSION is required (e.g. 3.13.12)" + exit 1 +fi +if [ -z "${PYTHON_DIST_RELEASE:-}" ]; then + echo "PYTHON_DIST_RELEASE is required" + exit 1 +fi + +PYTHON_VERSION_SHORT=${PYTHON_VERSION_SHORT:-$(echo "$PYTHON_VERSION" | cut -d. -f1,2)} + DIST_FILE=cpython-${PYTHON_VERSION}+${PYTHON_DIST_RELEASE}-${PYTHON_ARCH}${PYTHON_ARCH_VER}-unknown-linux-gnu-install_only_stripped.tar.gz curl -OL https://github.com/astral-sh/python-build-standalone/releases/download/${PYTHON_DIST_RELEASE}/${DIST_FILE} mkdir -p $PYTHON_ARCH/build tar zxvf $DIST_FILE -C $PYTHON_ARCH/build # compile lib -python -m compileall -b $PYTHON_ARCH/build/python/lib/python3.12 +python -m compileall -b "$PYTHON_ARCH/build/python/lib/python$PYTHON_VERSION_SHORT" # copy build to dist mkdir -p $PYTHON_ARCH/dist rsync -av --exclude-from=python-linux-dart.exclude $PYTHON_ARCH/build/python/* $PYTHON_ARCH/dist # archive -tar -czf python-linux-dart-$PYTHON_VERSION_SHORT-$PYTHON_ARCH.tar.gz -C $PYTHON_ARCH/dist . +tar -czf "python-linux-dart-$PYTHON_VERSION_SHORT-$PYTHON_ARCH.tar.gz" -C "$PYTHON_ARCH/dist" . From 7af62cad44cdd842be90f34043dc3832421bf31a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 16:35:58 -0800 Subject: [PATCH 45/50] Add legacy Android build support & patches Enable building older Python releases for Android using a legacy patched cross-build flow and add necessary patches and tooling. Changes include: - Add android/android-env.sh to configure NDK/r27d toolchain, environment variables, and cross-compile flags. - Add multiple patches (bldlibrary, grp, python_for_build_deps, soname, sysroot_paths) to fix cross-compilation, linking and packaging issues on Android. - Update build scripts to be version-aware: - .github workflow now packages armeabi-v7a for Python < 3.13. - android/build-all.sh selects ABIs based on Python version (3.13+ uses official tool, older versions include armeabi-v7a and x86). - android/build.sh applies patches and either uses the legacy cross-build flow (for <= 3.12) with prebuilt libs and custom configure/make/install steps or uses CPython's Android/android.py for 3.13+. - Set CCSHARED, CONFIG_SITE, sysroot include/lib paths and other flags needed for current NDKs and cross-compilation. - Add ABI-to-host mappings for armeabi-v7a and x86 in android/abi-to-host.sh. - Improve NDK detection in android/package-for-dart.sh to prefer locally installed NDKs. - Update android/README.md to document the two build flows (3.12 legacy vs 3.13+ official) and ABI support. These changes allow building and packaging older Python versions for a wider set of Android ABIs and fix several cross-compile/link/runtime issues encountered when targeting Android. --- .github/workflows/build-python.yml | 5 + android/README.md | 14 +- android/abi-to-host.sh | 6 + android/android-env.sh | 138 +++++++++++++++ android/build-all.sh | 11 +- android/build.sh | 179 +++++++++++++++----- android/package-for-dart.sh | 9 + android/patches/bldlibrary.patch | 60 +++++++ android/patches/grp.patch | 16 ++ android/patches/python_for_build_deps.patch | 16 ++ android/patches/soname.patch | 17 ++ android/patches/sysroot_paths.patch | 14 ++ 12 files changed, 436 insertions(+), 49 deletions(-) create mode 100644 android/android-env.sh create mode 100644 android/patches/bldlibrary.patch create mode 100644 android/patches/grp.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 diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 91b5985..ae47107 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -79,6 +79,11 @@ jobs: tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 + read version_major version_minor < <(echo "$PYTHON_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') + version_int=$((version_major * 100 + version_minor)) + if [ $version_int -lt 313 ]; then + bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a + fi - uses: actions/upload-artifact@v4 with: name: python-android diff --git a/android/README.md b/android/README.md index e4690d6..d933a65 100644 --- a/android/README.md +++ b/android/README.md @@ -1,15 +1,15 @@ # Python for Android -Scripts and CI jobs for building Python 3.13 for Android. +Scripts and CI jobs for building Python for Android. * Can be run on both Linux and macOS. -* Builds Python 3.13.x only. -* Creates Python installation with a structure suitable for https://github.com/flet-dev/mobile-forge -* Uses CPython's official `Android/android.py` build flow. +* Python 3.12 uses the legacy patched cross-build flow. +* Python 3.13+ uses CPython's official `Android/android.py` build flow. +* Creates Python installation with a structure suitable for https://github.com/flet-dev/mobile-forge. ## Usage -To build Python for a specific ABI (`arm64-v8a` or `x86_64`): +To build Python for a specific ABI: ``` ./build.sh 3.13.12 arm64-v8a @@ -21,7 +21,9 @@ To build all ABIs: ./build-all.sh 3.13.12 ``` -For Python 3.13+, official CPython Android tooling currently supports `arm64-v8a` and `x86_64`. +ABI support: +* Python 3.12: `arm64-v8a`, `armeabi-v7a`, `x86_64`, `x86` +* Python 3.13+: `arm64-v8a`, `x86_64` ## Credits diff --git a/android/abi-to-host.sh b/android/abi-to-host.sh index 69b7882..6e717aa 100644 --- a/android/abi-to-host.sh +++ b/android/abi-to-host.sh @@ -1,7 +1,13 @@ case ${abi:?} in + armeabi-v7a) + HOST=arm-linux-androideabi + ;; arm64-v8a) HOST=aarch64-linux-android ;; + x86) + HOST=i686-linux-android + ;; x86_64) HOST=x86_64-linux-android ;; diff --git a/android/android-env.sh b/android/android-env.sh new file mode 100644 index 0000000..dbfdb2c --- /dev/null +++ b/android/android-env.sh @@ -0,0 +1,138 @@ +# This script must be sourced with the following variables already set: +: ${HOST:?} # GNU target triplet + +# You may also override the following: +: ${api_level:=24} # Minimum Android API level the build will run on +: ${PREFIX:-} # Path in which to find required libraries + +NDK_VERSION=r27d + +# Print all messages on stderr so they're visible when running within build-wheel. +log() { + echo "$1" >&2 +} + +fail() { + log "$1" + exit 1 +} + +# When moving to a new version of the NDK, carefully review the following: +# +# * https://developer.android.com/ndk/downloads/revision_history +# +# * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md +# where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: +# https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md +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 = "arm-linux-androideabi" ]; then + clang_triplet=armv7a-linux-androideabi +else + clang_triplet=$HOST +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 + +export CFLAGS="-D__BIONIC_NO_PAGE_SIZE_MACRO" +export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,-z,max-page-size=16384" + +# 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). So any library that fails to +# build with this flag, would also fail to load at runtime. +LDFLAGS="$LDFLAGS -Wl,--no-undefined" + +# Many packages get away with omitting -lm on Linux, but Android is stricter. +LDFLAGS="$LDFLAGS -lm" + +# -mstackrealign is included where necessary in the clang launcher scripts which are +# pointed to by $CC, so we don't need to include it here. +if [ $HOST = "arm-linux-androideabi" ]; then + CFLAGS="$CFLAGS -march=armv7-a -mthumb" +fi + +if [ -n "${PREFIX:-}" ]; then + abs_prefix=$(realpath $PREFIX) + CFLAGS="$CFLAGS -I$abs_prefix/include" + LDFLAGS="$LDFLAGS -L$abs_prefix/lib" + + export PKG_CONFIG="pkg-config --define-prefix" + export PKG_CONFIG_LIBDIR="$abs_prefix/lib/pkgconfig" +fi + +# When compiling C++, some build systems will combine CFLAGS and CXXFLAGS, and some will +# use CXXFLAGS alone. +export CXXFLAGS=$CFLAGS + +# Use the same variable name as conda-build +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 index 1ae14a2..e536dd6 100755 --- a/android/build-all.sh +++ b/android/build-all.sh @@ -2,7 +2,16 @@ set -euo pipefail python_version=${1:?} -abis="arm64-v8a x86_64" +read version_major version_minor < <( + echo "$python_version" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/' +) +version_int=$((version_major * 100 + version_minor)) + +if [ $version_int -ge 313 ]; then + abis="arm64-v8a x86_64" +else + abis="arm64-v8a armeabi-v7a x86_64 x86" +fi for abi in $abis; do bash ./build.sh $python_version $abi diff --git a/android/build.sh b/android/build.sh index 8c9f7ff..e61db63 100755 --- a/android/build.sh +++ b/android/build.sh @@ -9,20 +9,23 @@ read version_major version_minor version_micro < <( ) version_short=$version_major.$version_minor version_no_pre=$version_major.$version_minor.$version_micro - -if [ "$version_short" != "3.13" ]; then - echo "This branch only supports Python 3.13.x for Android, got: $version" - exit 1 -fi +version_int=$(($version_major * 100 + $version_minor)) PREFIX="$script_dir/install/android/$abi/python-${version}" mkdir -p "$PREFIX" PREFIX=$(realpath "$PREFIX") +downloads=$script_dir/downloads +mkdir -p $downloads + cd $script_dir . abi-to-host.sh : ${api_level:=24} +if [ $version_int -le 312 ]; then + . android-env.sh +fi + # Download and unpack Python source code. version_dir=$script_dir/build/$version mkdir -p $version_dir @@ -36,6 +39,26 @@ tar -xf "$src_filename" mv "Python-$version" "$build_dir" cd "$build_dir" +# Apply patches. +patches="" +if [ $version_int -le 311 ]; then + patches+=" sysroot_paths" +fi +if [ $version_int -eq 311 ]; then + patches+=" python_for_build_deps" +fi +if [ $version_int -le 312 ]; then + patches+=" soname" +fi +if [ $version_int -eq 312 ]; then + patches+=" bldlibrary grp" +fi +for name in $patches; do + patch_file="$script_dir/patches/$name.patch" + echo "$patch_file" + patch -p1 -i "$patch_file" +done + # Remove any existing installation in the prefix. rm -rf $PREFIX/{include,lib}/python$version_short rm -rf $PREFIX/lib/libpython$version_short* @@ -45,47 +68,119 @@ support_versions=$script_dir/support/$version_short/android/VERSIONS mkdir -p $(dirname $support_versions) echo ">>> Create VERSIONS file for android" echo "Python version: $version" > $support_versions -echo "Build: 1" >> $support_versions -echo "Min android version: $api_level" >> $support_versions +echo "Build: custom" >> $support_versions +echo "Min Android version: $api_level" >> $support_versions echo "---------------------" >> $support_versions -case "$abi" in - arm64-v8a|x86_64) - ;; - *) - echo "Python $version_short official Android build supports only: arm64-v8a, x86_64" - exit 1 - ;; -esac - -# CPython's Android tooling expects ANDROID_HOME and ANDROID_API_LEVEL. -export ANDROID_API_LEVEL="$api_level" -if [ -z "${ANDROID_HOME:-}" ]; then - if [ -d "$HOME/Library/Android/sdk" ]; then - export ANDROID_HOME="$HOME/Library/Android/sdk" - elif [ -d "$HOME/Android/Sdk" ]; then - export ANDROID_HOME="$HOME/Android/Sdk" +if [ $version_int -le 312 ]; then + # Download and unpack libraries needed to compile Python. For a given Python + # version, we must maintain binary compatibility with existing wheels. + libs="bzip2-1.0.8-2 libffi-3.4.4-3 sqlite-3.45.3-3 xz-5.4.6-1" + if [ $version_int -le 308 ]; then + libs+=" openssl-1.1.1w-3" else - export ANDROID_HOME="$script_dir/android-sdk" - mkdir -p "$ANDROID_HOME" + libs+=" openssl-3.0.15-4" fi -fi -# Reuse NDK installed by this repo's older workflow by exposing it -# at the path expected by CPython's Android/android-env.sh. -if [ -z "${NDK_HOME:-}" ] && [ -d "$HOME/ndk/r29" ]; then - export NDK_HOME="$HOME/ndk/r29" -fi -cpython_ndk_version=$(sed -n 's/^ndk_version=//p' Android/android-env.sh | head -n1) -if [ -n "${NDK_HOME:-}" ] && [ -d "$NDK_HOME" ] && [ -n "${cpython_ndk_version:-}" ]; then - mkdir -p "$ANDROID_HOME/ndk" - if [ ! -e "$ANDROID_HOME/ndk/$cpython_ndk_version" ]; then - ln -s "$NDK_HOME" "$ANDROID_HOME/ndk/$cpython_ndk_version" + url_prefix="https://github.com/beeware/cpython-android-source-deps/releases/download" + for name_ver in $libs; do + IFS=- read lib_name lib_ver <<< "$name_ver" + url="$url_prefix/$name_ver/$name_ver-$HOST.tar.gz" + echo "$url" + + lib_dir="$script_dir/install/android/$abi/${lib_name}-${lib_ver}" + mkdir -p $lib_dir + lib_file=$downloads/${lib_name}-${lib_ver}-${abi}.tar.gz + curl -Lf "$url" -o $lib_file + tar -xf $lib_file -C $lib_dir + cp -R $lib_dir/* $PREFIX + echo "${lib_name}: $lib_ver" >> $support_versions + 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/$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. + cd "$build_dir" + cat > config.site <<-EOF + # Things that can't be autodetected when cross-compiling. + ac_cv_aligned_required=no # Default of "yes" changes hash function to FNV, which breaks Numba. + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + EOF + export CONFIG_SITE=$(pwd)/config.site + + configure_args="--host=$HOST --build=$(./config.guess) \ + --enable-shared --without-ensurepip --with-openssl=$PREFIX" + + # This prevents the "getaddrinfo bug" test, which can't be run when cross-compiling. + configure_args+=" --enable-ipv6" + + # Some of the patches involve missing Makefile dependencies, which allowed extension + # modules to be built before libpython3.x.so in parallel builds. In case this happens + # again, make sure there's no libpython3.x.a, otherwise the modules may end up silently + # linking with that instead. + if [ $version_int -ge 310 ]; then + configure_args+=" --without-static-libpython" fi -fi -Android/android.py configure-build -Android/android.py make-build -Android/android.py configure-host "$HOST" -Android/android.py make-host "$HOST" -cp -a "cross-build/$HOST/prefix/"* "$PREFIX" + if [ $version_int -ge 311 ]; then + configure_args+=" --with-build-python=yes" + fi + + ./configure $configure_args + + make -j $CPU_COUNT + make install prefix=$PREFIX + + echo ">>> Replacing host platform" + sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $PREFIX/lib/python$version_short/config-$version_short/Makefile +else + case "$abi" in + arm64-v8a|x86_64) + ;; + *) + echo "Python $version_short official Android build supports only: arm64-v8a, x86_64" + exit 1 + ;; + esac + + # CPython's Android tooling expects ANDROID_HOME and ANDROID_API_LEVEL. + export ANDROID_API_LEVEL="$api_level" + if [ -z "${ANDROID_HOME:-}" ]; then + if [ -d "$HOME/Library/Android/sdk" ]; then + export ANDROID_HOME="$HOME/Library/Android/sdk" + elif [ -d "$HOME/Android/Sdk" ]; then + export ANDROID_HOME="$HOME/Android/Sdk" + else + export ANDROID_HOME="$script_dir/android-sdk" + mkdir -p "$ANDROID_HOME" + fi + fi + + # Reuse already-installed NDK by exposing it at the location expected by + # CPython's Android/android-env.sh. + if [ -z "${NDK_HOME:-}" ] && [ -d "$HOME/ndk/r27d" ]; then + export NDK_HOME="$HOME/ndk/r27d" + fi + cpython_ndk_version=$(sed -n 's/^ndk_version=//p' Android/android-env.sh | head -n1) + if [ -n "${NDK_HOME:-}" ] && [ -d "$NDK_HOME" ] && [ -n "${cpython_ndk_version:-}" ]; then + mkdir -p "$ANDROID_HOME/ndk" + if [ ! -e "$ANDROID_HOME/ndk/$cpython_ndk_version" ]; then + ln -s "$NDK_HOME" "$ANDROID_HOME/ndk/$cpython_ndk_version" + fi + fi + + Android/android.py configure-build + Android/android.py make-build + Android/android.py configure-host "$HOST" + Android/android.py make-host "$HOST" + cp -a "cross-build/$HOST/prefix/"* "$PREFIX" +fi diff --git a/android/package-for-dart.sh b/android/package-for-dart.sh index 08d5fc1..70d840e 100755 --- a/android/package-for-dart.sh +++ b/android/package-for-dart.sh @@ -23,8 +23,17 @@ if [ -z "${NDK_HOME:-}" ]; then if [ -d "$script_dir/android-sdk" ]; then sdk_candidates+="$script_dir/android-sdk " fi + if [ -d "$HOME/ndk" ]; then + ndk_candidate=$(ls -d "$HOME/ndk"/* 2>/dev/null | sort -V | tail -n1 || true) + if [ -n "$ndk_candidate" ]; then + NDK_HOME="$ndk_candidate" + fi + fi for sdk_root in $sdk_candidates; do + if [ -n "${NDK_HOME:-}" ]; then + break + fi ndk_candidate=$(ls -d "$sdk_root"/ndk/* 2>/dev/null | sort -V | tail -n1 || true) if [ -n "$ndk_candidate" ]; then NDK_HOME="$ndk_candidate" diff --git a/android/patches/bldlibrary.patch b/android/patches/bldlibrary.patch new file mode 100644 index 0000000..72b0bac --- /dev/null +++ b/android/patches/bldlibrary.patch @@ -0,0 +1,60 @@ +diff --git a/configure b/configure +index 1c75810d9e8..d883a00d548 100755 +--- a/configure ++++ b/configure +@@ -841,6 +841,7 @@ PY_ENABLE_SHARED + PLATLIBDIR + BINLIBDEST + LIBPYTHON ++MODULE_DEPS_SHARED + EXT_SUFFIX + ALT_SOABI + SOABI +@@ -24402,12 +24403,17 @@ LDVERSION='$(VERSION)$(ABIFLAGS)' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDVERSION" >&5 + printf "%s\n" "$LDVERSION" >&6; } + +-# On Android and Cygwin the shared libraries must be linked with libpython. ++# Configure the flags and dependencies used when compiling shared modules. ++# Do not rename LIBPYTHON - it's accessed via sysconfig by package build ++# systems (e.g. Meson) to decide whether to link extension modules against ++# libpython. ++MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' ++LIBPYTHON='' + ++# 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}" +-else +- LIBPYTHON='' ++ MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" ++ LIBPYTHON="\$(BLDLIBRARY)" + fi + + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 0e64ccc5c21..c4217424508 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -2797,7 +2797,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h + + # force rebuild when header file or module build flavor (static/shared) is changed + MODULE_DEPS_STATIC=Modules/config.c +-MODULE_DEPS_SHARED=$(MODULE_DEPS_STATIC) $(EXPORTSYMS) ++MODULE_DEPS_SHARED=@MODULE_DEPS_SHARED@ + + MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h + MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h +diff --git a/Modules/makesetup b/Modules/makesetup +index f000c9cd673..3231044230e 100755 +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -286,7 +286,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | + ;; + 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/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/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('"') From 0a05ec7be50ea86b190b6be0ffc9f5a510249e57 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 17:14:10 -0800 Subject: [PATCH 46/50] Use Python 3.12.12 in CI workflow Update .github/workflows/build-python.yml to change PYTHON_VERSION from 3.13.12 to 3.12.12. PYTHON_DIST_RELEASE remains unchanged. This ensures the CI uses the 3.12.12 Python build. --- .github/workflows/build-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index ae47107..99336fa 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: env: - PYTHON_VERSION: 3.13.12 + PYTHON_VERSION: 3.12.12 PYTHON_DIST_RELEASE: 20260203 # https://github.com/astral-sh/python-build-standalone/releases jobs: From b83e652d986d5057543b9d9849f023d64677c4c9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 17:41:59 -0800 Subject: [PATCH 47/50] Introduce reusable Python build workflow Add a reusable workflow (.github/workflows/build-python-version.yml) that builds Python packages for Darwin, Android, Linux, and Windows and publishes release assets. Update build-python.yml to expose a workflow_dispatch input for selecting versions and to run the new workflow as a matrix over supported Python versions (3.12.12, 3.13.12, 3.14.3), inheriting secrets. This DRYs up per-version build logic and enables reuse via workflow_call. --- .github/workflows/build-python-version.yml | 180 +++++++++++++++++++ .github/workflows/build-python.yml | 190 +++------------------ 2 files changed, 205 insertions(+), 165 deletions(-) create mode 100644 .github/workflows/build-python-version.yml diff --git a/.github/workflows/build-python-version.yml b/.github/workflows/build-python-version.yml new file mode 100644 index 0000000..d62a05c --- /dev/null +++ b/.github/workflows/build-python-version.yml @@ -0,0 +1,180 @@ +name: Build Python Packages (Reusable) + +on: + workflow_call: + inputs: + python_version: + description: Full Python version (e.g. 3.13.12) + required: true + type: string + +env: + PYTHON_VERSION: ${{ inputs.python_version }} + PYTHON_DIST_RELEASE: 20260203 # https://github.com/astral-sh/python-build-standalone/releases + +jobs: + build-darwin: + name: Build Python for iOS and macOS + runs-on: macos-15 + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION_SHORT }} + + - name: Show Python version + run: python --version + + - name: Build Python for iOS and macOS + working-directory: darwin + shell: bash + run: | + git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git + mkdir -p dist + + pushd Python-Apple-support + make iOS + tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . + make macOS + popd + + bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" + + - name: Upload Darwin build artifacts + uses: actions/upload-artifact@v4 + with: + name: python-darwin-${{ env.PYTHON_VERSION_SHORT }} + path: darwin/dist/python-*.tar.gz + if-no-files-found: error + + build-android: + name: Build Python for Android + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + - run: python --version + - working-directory: android + shell: bash + run: | + bash ./build-all.sh "$PYTHON_VERSION" + mkdir -p dist + tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support + bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a + bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 + read version_major version_minor < <(echo "$PYTHON_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') + version_int=$((version_major * 100 + version_minor)) + if [ $version_int -lt 313 ]; then + bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a + fi + - uses: actions/upload-artifact@v4 + with: + name: python-android-${{ env.PYTHON_VERSION_SHORT }} + path: android/dist/python-android-*.tar.gz + if-no-files-found: error + + build-linux: + name: Build Python for Linux + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" + - working-directory: linux + shell: bash + run: | + bash ./package-for-linux.sh x86_64 "_v2" + bash ./package-for-linux.sh aarch64 "" + - uses: actions/upload-artifact@v4 + with: + name: python-linux-${{ env.PYTHON_VERSION_SHORT }} + path: linux/python-linux-dart-*.tar.gz + if-no-files-found: error + + build-windows: + name: Build Python for Windows + runs-on: windows-2022 + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Derive short Python version + shell: pwsh + run: | + $parts = "${{ env.PYTHON_VERSION }}".Split(".") + "PYTHON_VERSION_SHORT=$($parts[0]).$($parts[1])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION_SHORT }} + - name: Show Python version + shell: pwsh + run: | + python --version + python -c "import sys; print(sys.executable)" + - name: Build CPython from sources and package for Dart + shell: pwsh + run: | + .\windows\package-for-dart.ps1 ` + -PythonVersion "${{ env.PYTHON_VERSION }}" ` + -PythonVersionShort "${{ env.PYTHON_VERSION_SHORT }}" + - uses: actions/upload-artifact@v4 + with: + name: python-windows-${{ env.PYTHON_VERSION_SHORT }} + path: windows/python-windows-for-dart-*.zip + if-no-files-found: error + + publish-release: + name: Publish Release Assets + runs-on: ubuntu-latest + needs: + - build-darwin + - build-android + - build-linux + - build-windows + permissions: + contents: write + steps: + - name: Derive short Python version + shell: bash + run: | + echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" + - name: Download all build artifacts + uses: actions/download-artifact@v4 + with: + pattern: python-*-${{ env.PYTHON_VERSION_SHORT }} + path: release-artifacts + merge-multiple: true + + - name: Publish all artifacts to release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ env.PYTHON_VERSION_SHORT }} + files: release-artifacts/* + fail_on_unmatched_files: true + generate_release_notes: false + draft: false + prerelease: false diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 99336fa..86b751b 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -5,170 +5,30 @@ on: branches: - '**' workflow_dispatch: - -env: - PYTHON_VERSION: 3.12.12 - PYTHON_DIST_RELEASE: 20260203 # https://github.com/astral-sh/python-build-standalone/releases + inputs: + python_version: + description: 'Version to build (all, 3.12.12, 3.13.12, 3.14.3)' + required: true + default: all + type: choice + options: + - all + - 3.12.12 + - 3.13.12 + - 3.14.3 jobs: - build-darwin: - name: Build Python for iOS and macOS - runs-on: macos-15 - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Derive short Python version - shell: bash - run: | - echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION_SHORT }} - - - name: Show Python version - run: python --version - - - name: Build Python for iOS and macOS - working-directory: darwin - shell: bash - run: | - git clone --branch="$PYTHON_VERSION_SHORT" https://github.com/beeware/Python-Apple-support.git - mkdir -p dist - - pushd Python-Apple-support - make iOS - tar -czf ../dist/python-ios-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support -C . - make macOS - popd - - bash ./package-ios-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - bash ./package-macos-for-dart.sh Python-Apple-support "$PYTHON_VERSION_SHORT" - - - name: Upload Darwin build artifacts - uses: actions/upload-artifact@v4 - with: - name: python-darwin - path: darwin/dist/python-*.tar.gz - if-no-files-found: error - - build-android: - name: Build Python for Android - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - name: Derive short Python version - shell: bash - run: | - echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} - - run: python --version - - working-directory: android - shell: bash - run: | - bash ./build-all.sh "$PYTHON_VERSION" - mkdir -p dist - tar -czf dist/python-android-mobile-forge-$PYTHON_VERSION_SHORT.tar.gz install support - bash ./package-for-dart.sh install "$PYTHON_VERSION" arm64-v8a - bash ./package-for-dart.sh install "$PYTHON_VERSION" x86_64 - read version_major version_minor < <(echo "$PYTHON_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1 \2/') - version_int=$((version_major * 100 + version_minor)) - if [ $version_int -lt 313 ]; then - bash ./package-for-dart.sh install "$PYTHON_VERSION" armeabi-v7a - fi - - uses: actions/upload-artifact@v4 - with: - name: python-android - path: android/dist/python-android-*.tar.gz - if-no-files-found: error - - build-linux: - name: Build Python for Linux - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - working-directory: linux - shell: bash - run: | - bash ./package-for-linux.sh x86_64 "_v2" - bash ./package-for-linux.sh aarch64 "" - - uses: actions/upload-artifact@v4 - with: - name: python-linux - path: linux/python-linux-dart-*.tar.gz - if-no-files-found: error - - build-windows: - name: Build Python for Windows - runs-on: windows-2022 - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - name: Derive short Python version - shell: pwsh - run: | - $parts = "${{ env.PYTHON_VERSION }}".Split(".") - "PYTHON_VERSION_SHORT=$($parts[0]).$($parts[1])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION_SHORT }} - - name: Show Python version - shell: pwsh - run: | - python --version - python -c "import sys; print(sys.executable)" - - name: Build CPython from sources and package for Dart - shell: pwsh - run: | - .\windows\package-for-dart.ps1 ` - -PythonVersion "${{ env.PYTHON_VERSION }}" ` - -PythonVersionShort "${{ env.PYTHON_VERSION_SHORT }}" - - uses: actions/upload-artifact@v4 - with: - name: python-windows - path: windows/python-windows-for-dart-*.zip - if-no-files-found: error - - publish-release: - name: Publish Release Assets - runs-on: ubuntu-latest - needs: - - build-darwin - - build-android - - build-linux - - build-windows - permissions: - contents: write - steps: - - name: Derive short Python version - shell: bash - run: | - echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - - name: Download all build artifacts - uses: actions/download-artifact@v4 - with: - pattern: python-* - path: release-artifacts - merge-multiple: true - - - name: Publish all artifacts to release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ env.PYTHON_VERSION_SHORT }} - files: release-artifacts/* - fail_on_unmatched_files: true - generate_release_notes: false - draft: false - prerelease: false + build-matrix: + name: Build Python ${{ matrix.python_version }} + strategy: + fail-fast: false + matrix: + python_version: + - 3.12.12 + - 3.13.12 + - 3.14.3 + if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.python_version == 'all' || github.event.inputs.python_version == matrix.python_version }} + uses: ./.github/workflows/build-python-version.yml + with: + python_version: ${{ matrix.python_version }} + secrets: inherit From 47a594ccf97db99321aa076e9d995e5edde31d1a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 17:46:11 -0800 Subject: [PATCH 48/50] Make reusable Python workflow dispatchable Rename reusable workflow and add workflow_dispatch so the reusable workflow can be run manually with a Python version choice. Update PYTHON_VERSION env to read from either workflow_call inputs or workflow_dispatch event inputs. Remove duplicate workflow_dispatch inputs and the conditional in build-python.yml so the matrix always invokes the reusable workflow for each version (3.12.12, 3.13.12, 3.14.3). This simplifies manual runs and centralizes version handling in the reusable workflow. --- .github/workflows/build-python-version.yml | 14 ++++++++++++-- .github/workflows/build-python.yml | 12 ------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-python-version.yml b/.github/workflows/build-python-version.yml index d62a05c..9b39741 100644 --- a/.github/workflows/build-python-version.yml +++ b/.github/workflows/build-python-version.yml @@ -1,4 +1,4 @@ -name: Build Python Packages (Reusable) +name: Build Python Version on: workflow_call: @@ -7,9 +7,19 @@ on: description: Full Python version (e.g. 3.13.12) required: true type: string + workflow_dispatch: + inputs: + python_version: + description: Full Python version (e.g. 3.13.12) + required: true + type: choice + options: + - 3.12.12 + - 3.13.12 + - 3.14.3 env: - PYTHON_VERSION: ${{ inputs.python_version }} + PYTHON_VERSION: ${{ inputs.python_version || github.event.inputs.python_version }} PYTHON_DIST_RELEASE: 20260203 # https://github.com/astral-sh/python-build-standalone/releases jobs: diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 86b751b..a2320a1 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -5,17 +5,6 @@ on: branches: - '**' workflow_dispatch: - inputs: - python_version: - description: 'Version to build (all, 3.12.12, 3.13.12, 3.14.3)' - required: true - default: all - type: choice - options: - - all - - 3.12.12 - - 3.13.12 - - 3.14.3 jobs: build-matrix: @@ -27,7 +16,6 @@ jobs: - 3.12.12 - 3.13.12 - 3.14.3 - if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.python_version == 'all' || github.event.inputs.python_version == matrix.python_version }} uses: ./.github/workflows/build-python-version.yml with: python_version: ${{ matrix.python_version }} From a52998cedb2b3db17714c4b5a7ecafc6e2349e6a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 17:50:35 -0800 Subject: [PATCH 49/50] Use extracted Python interpreter for compileall Look up and use the Python binary from the extracted runtime (bin/python, then bin/python3, then bin/python) when running compileall. Fail with an error if no interpreter is found, and invoke compileall with -I to avoid importing site-packages. This makes the build use the bundled interpreter instead of relying on a system python. --- linux/package-for-linux.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/linux/package-for-linux.sh b/linux/package-for-linux.sh index b202c3a..2cffbf5 100755 --- a/linux/package-for-linux.sh +++ b/linux/package-for-linux.sh @@ -21,7 +21,18 @@ mkdir -p $PYTHON_ARCH/build tar zxvf $DIST_FILE -C $PYTHON_ARCH/build # compile lib -python -m compileall -b "$PYTHON_ARCH/build/python/lib/python$PYTHON_VERSION_SHORT" +build_python="$PYTHON_ARCH/build/python/bin/python$PYTHON_VERSION_SHORT" +if [ ! -x "$build_python" ]; then + build_python="$PYTHON_ARCH/build/python/bin/python3" +fi +if [ ! -x "$build_python" ]; then + build_python="$PYTHON_ARCH/build/python/bin/python" +fi +if [ ! -x "$build_python" ]; then + echo "Python interpreter not found in extracted runtime under $PYTHON_ARCH/build/python/bin" + exit 1 +fi +"$build_python" -I -m compileall -b "$PYTHON_ARCH/build/python/lib/python$PYTHON_VERSION_SHORT" # copy build to dist mkdir -p $PYTHON_ARCH/dist From cdb69b415f2c2cd9f2b91fb7a46a2bd8bf153d92 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 11 Feb 2026 17:56:11 -0800 Subject: [PATCH 50/50] Bump setup-python to v6; improve python detection Update GitHub Actions to use actions/setup-python@v6 in the workflow (added missing setup step and python --version checks). In linux/package-for-linux.sh, replace hard-coded build-path checks with command -v lookups: try python$PYTHON_VERSION_SHORT, then python3, then python. Use empty-string checks instead of -x and update the error message to indicate the host Python interpreter is missing for compileall. These changes make CI use the newer action and make runtime Python detection more robust across environments. --- .github/workflows/build-python-version.yml | 6 +++++- linux/package-for-linux.sh | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-python-version.yml b/.github/workflows/build-python-version.yml index 9b39741..3bb6f6c 100644 --- a/.github/workflows/build-python-version.yml +++ b/.github/workflows/build-python-version.yml @@ -79,7 +79,7 @@ jobs: shell: bash run: | echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} - run: python --version @@ -113,6 +113,10 @@ jobs: shell: bash run: | echo "PYTHON_VERSION_SHORT=$(echo "$PYTHON_VERSION" | cut -d. -f1,2)" >> "$GITHUB_ENV" + - uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION }} + - run: python --version - working-directory: linux shell: bash run: | diff --git a/linux/package-for-linux.sh b/linux/package-for-linux.sh index 2cffbf5..00c5b5c 100755 --- a/linux/package-for-linux.sh +++ b/linux/package-for-linux.sh @@ -21,15 +21,15 @@ mkdir -p $PYTHON_ARCH/build tar zxvf $DIST_FILE -C $PYTHON_ARCH/build # compile lib -build_python="$PYTHON_ARCH/build/python/bin/python$PYTHON_VERSION_SHORT" -if [ ! -x "$build_python" ]; then - build_python="$PYTHON_ARCH/build/python/bin/python3" +build_python=$(command -v "python$PYTHON_VERSION_SHORT" || true) +if [ -z "$build_python" ]; then + build_python=$(command -v python3 || true) fi -if [ ! -x "$build_python" ]; then - build_python="$PYTHON_ARCH/build/python/bin/python" +if [ -z "$build_python" ]; then + build_python=$(command -v python || true) fi -if [ ! -x "$build_python" ]; then - echo "Python interpreter not found in extracted runtime under $PYTHON_ARCH/build/python/bin" +if [ -z "$build_python" ]; then + echo "Host Python interpreter not found for compileall" exit 1 fi "$build_python" -I -m compileall -b "$PYTHON_ARCH/build/python/lib/python$PYTHON_VERSION_SHORT"