From 07fab811d6814480e05dba7520d8af075d2b3ed8 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Wed, 31 Jan 2024 16:35:06 +0000 Subject: [PATCH 1/6] Fix Android linker issues --- Modules/makesetup | 14 +------------- configure | 11 +++++++++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Modules/makesetup b/Modules/makesetup index f000c9cd67310e..8bb971b152a522 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -87,18 +87,6 @@ esac 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} do @@ -286,7 +274,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/configure b/configure index 7b2119ff7f4f78..ceaba10460f706 100755 --- a/configure +++ b/configure @@ -7296,6 +7296,7 @@ printf "%s\n" "#define Py_ENABLE_SHARED 1" >>confdefs.h case $ac_sys_system in CYGWIN*) LDLIBRARY='libpython$(LDVERSION).dll.a' + BLDLIBRARY='-L. -lpython$(LDVERSION)' DLLLIBRARY='libpython$(LDVERSION).dll' ;; SunOS*) @@ -7312,7 +7313,13 @@ printf "%s\n" "#define Py_ENABLE_SHARED 1" >>confdefs.h LDLIBRARY='libpython$(LDVERSION).so' BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} - INSTSONAME="$LDLIBRARY".$SOVERSION + + # 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 @@ -23918,7 +23925,7 @@ printf "%s\n" "$LDVERSION" >&6; } # On Android and Cygwin the shared libraries must be linked with libpython. if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then - LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" + LIBPYTHON="$BLDLIBRARY" else LIBPYTHON='' fi From 5f7c39ec25eb3fb7fe4c9dc8cfbd54055a923818 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 8 Feb 2024 15:29:44 +0000 Subject: [PATCH 2/6] More Android linker fixes --- Makefile.pre.in | 8 +++----- configure | 8 ++++++-- configure.ac | 16 +++++++++++++--- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index aad637876ead80..23d81a569cd222 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -854,11 +854,9 @@ $(LIBRARY): $(LIBRARY_OBJS) $(AR) $(ARFLAGS) $@ $(LIBRARY_OBJS) libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) - if test $(INSTSONAME) != $(LDLIBRARY); then \ - $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) + if test $(INSTSONAME) != $@; then \ $(LN) -f $(INSTSONAME) $@; \ - else \ - $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ fi libpython3.so: libpython$(LDVERSION).so @@ -2875,7 +2873,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/configure b/configure index 1d5721e5de1b37..1f2bd3bd77c041 100755 --- a/configure +++ b/configure @@ -835,6 +835,7 @@ PY_ENABLE_SHARED PLATLIBDIR BINLIBDEST LIBPYTHON +MODULE_DEPS_SHARED EXT_SUFFIX ALT_SOABI SOABI @@ -7317,7 +7318,7 @@ printf "%s\n" "#define Py_ENABLE_SHARED 1" >>confdefs.h # The Android Gradle plugin will only package libraries whose names end # with ".so". - if [ $ac_sys_system != "Linux-android" ]; then + if test "$ac_sys_system" != "Linux-android"; then INSTSONAME="$LDLIBRARY".$SOVERSION fi @@ -12763,7 +12764,6 @@ then then CCSHARED="-fPIC"; else CCSHARED="+z"; fi;; - Linux-android*) ;; Linux*|GNU*) CCSHARED="-fPIC";; Emscripten*|WASI*) if test "x$enable_wasm_dynamic_linking" = xyes @@ -23931,10 +23931,14 @@ LDVERSION='$(VERSION)$(ABIFLAGS)' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDVERSION" >&5 printf "%s\n" "$LDVERSION" >&6; } + +MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' + # 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="$BLDLIBRARY" + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" else LIBPYTHON='' fi diff --git a/configure.ac b/configure.ac index e121e893a1d0d9..e07786ab806968 100644 --- a/configure.ac +++ b/configure.ac @@ -1318,6 +1318,7 @@ if test $enable_shared = "yes"; then case $ac_sys_system in CYGWIN*) LDLIBRARY='libpython$(LDVERSION).dll.a' + BLDLIBRARY='-L. -lpython$(LDVERSION)' DLLLIBRARY='libpython$(LDVERSION).dll' ;; SunOS*) @@ -1334,7 +1335,13 @@ if test $enable_shared = "yes"; then LDLIBRARY='libpython$(LDVERSION).so' BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} - INSTSONAME="$LDLIBRARY".$SOVERSION + + # The Android Gradle plugin will only package libraries whose names end + # with ".so". + if test "$ac_sys_system" != "Linux-android"; then + INSTSONAME="$LDLIBRARY".$SOVERSION + fi + if test "$with_pydebug" != yes then PY3LIBRARY=libpython3.so @@ -3291,7 +3298,6 @@ then then CCSHARED="-fPIC"; else CCSHARED="+z"; fi;; - Linux-android*) ;; Linux*|GNU*) CCSHARED="-fPIC";; Emscripten*|WASI*) AS_VAR_IF([enable_wasm_dynamic_linking], [yes], [ @@ -5845,10 +5851,14 @@ AC_MSG_CHECKING([LDVERSION]) LDVERSION='$(VERSION)$(ABIFLAGS)' AC_MSG_RESULT([$LDVERSION]) +AC_SUBST([MODULE_DEPS_SHARED]) +MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' + # On Android and Cygwin the shared libraries must be linked with libpython. AC_SUBST([LIBPYTHON]) if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then - LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" + LIBPYTHON="$BLDLIBRARY" + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" else LIBPYTHON='' fi From d0ad0df7d6ff9aaae8a168face9e51de8d32a11c Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 16 Feb 2024 20:57:46 +0000 Subject: [PATCH 3/6] Add Android build script and README --- Android/README.md | 64 +++++++++++++ Android/android-env.sh | 87 ++++++++++++++++++ Android/android.py | 201 +++++++++++++++++++++++++++++++++++++++++ Makefile.pre.in | 2 +- Modules/makesetup | 2 +- configure | 59 +++++++----- configure.ac | 69 ++++++++------ 7 files changed, 430 insertions(+), 54 deletions(-) create mode 100644 Android/README.md create mode 100644 Android/android-env.sh create mode 100755 Android/android.py diff --git a/Android/README.md b/Android/README.md new file mode 100644 index 00000000000000..b63de15adc9c0d --- /dev/null +++ b/Android/README.md @@ -0,0 +1,64 @@ +# Python for Android + +These instructions are only needed if you're planning to compile Python for +Android yourself. Most users should *not* need to do this. If you're looking to +use Python on Android, one of the following tools will provide a much more +approachable user experience: + +* [Briefcase](https://briefcase.readthedocs.io), from the BeeWare project +* [Builddozer](https://buildozer.readthedocs.io), from the Kivy project +* [Chaquopy](https://chaquo.com/chaquopy/) + + +## Prerequisites + +Export the `ANDROID_HOME` environment variable to point at your Android SDK. If +you don't already have the SDK, here's how to install it: + +* Download the "Command line tools" from . +* Create a directory `android-sdk/cmdline-tools`, and unzip the command line + tools package into it. +* Rename `android-sdk/cmdline-tools/cmdline-tools` to + `android-sdk/cmdline-tools/latest`. +* `export ANDROID_HOME=/path/to/android-sdk` + + +## Building + +Building for Android requires doing a cross-build where you have a "build" +Python to help produce an Android build of CPython. This procedure has been +tested on Linux and macOS. + +The easiest way to do a build is to use the `android.py` script. You can either +have it perform the entire build process from start to finish in one step, or +you can do it in discrete steps that mirror running `configure` and `make` for +each of the two builds of Python you end up producing. + +The discrete steps for building via `android.py` are: + +```sh +./android.py configure-build +./android.py make-build +./android.py configure-host HOST +./android.py make-host HOST +``` + +To see the possible values of HOST, run `./android.py configure-host --help`. + +Or to do it all in a single command, run: + +```sh +./android.py build HOST +``` + +In the end you should have a build Python in `cross-build/build`, and an Android +build in `cross-build/HOST `. + +You can use `--` as a separator for any of the `configure`-related commands -- +including `build` itself -- to pass arguments to the underlying `configure` +call. For example, if you want a pydebug build that also caches the results from +`configure`, you can do: + +```sh +./android.py build HOST -- -C --with-pydebug +``` diff --git a/Android/android-env.sh b/Android/android-env.sh new file mode 100644 index 00000000000000..9f3ff1f6f318eb --- /dev/null +++ b/Android/android-env.sh @@ -0,0 +1,87 @@ +# This script must be sourced with the following variables already set: +: ${ANDROID_HOME:?} # Path to Android SDK +: ${HOST:?} # GNU target triplet + +# You may also override the following: +: ${api_level:=21} # Minimum Android API level the build will run on +: ${PREFIX:-} # Path in which to find required libraries + + +# 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 +ndk_version=26.2.11394342 + +ndk=$ANDROID_HOME/ndk/$ndk_version +if ! [ -e $ndk ]; then + log "Installing NDK: this may take several minutes" + yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;$ndk_version" +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/build/cmake/android.toolchain.cmake. +toolchain=$(echo $ndk/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="" +export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment" + +# 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+=" -march=armv7-a -mthumb" +fi + +if [ -n "${PREFIX:-}" ]; then + abs_prefix=$(realpath $PREFIX) + CFLAGS+=" -I$abs_prefix/include" + LDFLAGS+=" -L$abs_prefix/lib" + + export PKG_CONFIG="pkg-config --define-prefix" + export PKG_CONFIG_LIBDIR="$abs_prefix/lib/pkgconfig" +fi + +# 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/android.py b/Android/android.py new file mode 100755 index 00000000000000..96bca2357be10b --- /dev/null +++ b/Android/android.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import shutil +import subprocess +import sys +import sysconfig +from os.path import relpath +from pathlib import Path + +SCRIPT_NAME = Path(__file__).name +CHECKOUT = Path(__file__).parent.parent +CROSS_BUILD_DIR = CHECKOUT / "cross-build" + + +def delete_if_exists(path): + if path.exists(): + print(f"Deleting {path} ...") + shutil.rmtree(path) + + +def subdir(name, *, clean=None): + path = CROSS_BUILD_DIR / name + if clean: + delete_if_exists(path) + if not path.exists(): + if clean is None: + sys.exit( + f"{path} does not exist. Create it by running the appropriate " + f"`configure` subcommand of {SCRIPT_NAME}.") + else: + path.mkdir(parents=True) + return path + + +def run(command, *, host=None, **kwargs): + env = os.environ.copy() + if host: + env_script = CHECKOUT / "Android/android-env.sh" + env_output = subprocess.run( + f"HOST={host}; " + f"PREFIX={subdir(host)}/prefix; " + f". {env_script}; " + f"export", + check=True, shell=True, text=True, stdout=subprocess.PIPE + ).stdout + + for line in env_output.splitlines(): + # We don't require every line to match, as there may be some other + # output from installing the NDK. + if match := re.search( + "^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line + ): + key, value = match[2], match[3] + if env.get(key) != value: + print(line) + env[key] = value + + if env == os.environ: + raise ValueError(f"Found no variables in {env_script.name} output:\n" + + env_output) + + print(">", " ".join(map(str, command))) + try: + subprocess.run(command, check=True, env=env, **kwargs) + except subprocess.CalledProcessError as e: + sys.exit(e) + + +def build_python_path(): + """The path to the build Python binary.""" + build_dir = subdir("build") + binary = build_dir / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError("Unable to find `python(.exe)` in " + f"{build_dir}") + + return binary + + +def configure_build_python(context): + os.chdir(subdir("build", clean=context.clean)) + + command = [relpath(CHECKOUT / "configure")] + if context.args: + command.extend(context.args) + run(command) + + +def make_build_python(context): + os.chdir(subdir("build")) + run(["make", "-j", str(os.cpu_count())]) + + +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-1", "openssl-3.0.13-0", + "sqlite-3.45.1-0", "xz-5.4.6-0"]: + filename = f"{name_ver}-{host}.tar.gz" + run(["wget", f"{deps_url}/{name_ver}/{filename}"]) + run(["tar", "-xf", filename]) + os.remove(filename) + + +def configure_host_python(context): + host_dir = subdir(context.host, clean=context.clean) + + prefix_dir = host_dir / "prefix" + if not prefix_dir.exists(): + prefix_dir.mkdir() + os.chdir(prefix_dir) + unpack_deps(context.host) + + build_dir = host_dir / "build" + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + command = [ + # Basic cross-compiling configuration + relpath(CHECKOUT / "configure"), + f"--host={context.host}", + f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}", + f"--with-build-python={build_python_path()}", + "--without-ensurepip", + + # Android always uses a shared libpython. + "--enable-shared", + "--without-static-libpython", + + # Dependent libraries. The others are found using pkg-config: see + # android-env.sh. + f"--with-openssl={prefix_dir}", + ] + + if context.args: + command.extend(context.args) + run(command, host=context.host) + + +def make_host_python(context): + host_dir = subdir(context.host) + os.chdir(host_dir / "build") + run(["make", "-j", str(os.cpu_count())], host=context.host) + run(["make", "install", f"prefix={host_dir}/prefix"], host=context.host) + + +def build_all(context): + steps = [configure_build_python, make_build_python, configure_host_python, + make_host_python] + for step in steps: + step(context) + + +def clean_all(context): + delete_if_exists(CROSS_BUILD_DIR) + + +def main(): + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand") + build = subcommands.add_parser("build", help="Build everything") + configure_build = subcommands.add_parser("configure-build", + help="Run `configure` for the " + "build Python") + make_build = subcommands.add_parser("make-build", + help="Run `make` for the build Python") + configure_host = subcommands.add_parser("configure-host", + help="Run `configure` for Android") + make_host = subcommands.add_parser("make-host", + help="Run `make` for Android") + clean = subcommands.add_parser("clean", help="Delete files and directories " + "created by this script") + for subcommand in build, configure_build, configure_host: + subcommand.add_argument( + "--clean", action="store_true", default=False, dest="clean", + help="Delete any relevant directories before building") + for subcommand in build, configure_host, make_host: + subcommand.add_argument( + "host", metavar="HOST", + choices=["aarch64-linux-android", "x86_64-linux-android"], + help="Host triplet: choices=[%(choices)s]") + for subcommand in build, configure_build, configure_host: + subcommand.add_argument("args", nargs="*", + help="Extra arguments to pass to `configure`") + + context = parser.parse_args() + dispatch = {"configure-build": configure_build_python, + "make-build": make_build_python, + "configure-host": configure_host_python, + "make-host": make_host_python, + "build": build_all, + "clean": clean_all} + dispatch[context.subcommand](context) + + +if __name__ == "__main__": + main() diff --git a/Makefile.pre.in b/Makefile.pre.in index 23d81a569cd222..f5a13556430751 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -41,7 +41,7 @@ AR= @AR@ READELF= @READELF@ SOABI= @SOABI@ LDVERSION= @LDVERSION@ -LIBPYTHON= @LIBPYTHON@ +MODULE_LDFLAGS=@MODULE_LDFLAGS@ GITVERSION= @GITVERSION@ GITTAG= @GITTAG@ GITBRANCH= @GITBRANCH@ diff --git a/Modules/makesetup b/Modules/makesetup index 8bb971b152a522..d41b6640bb5186 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -274,7 +274,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | ;; esac rule="$file: $objs" - rule="$rule; \$(BLDSHARED) $objs $libs \$(LIBPYTHON) -o $file" + rule="$rule; \$(BLDSHARED) $objs $libs \$(MODULE_LDFLAGS) -o $file" echo "$rule" >>$rulesf done done diff --git a/configure b/configure index 1f2bd3bd77c041..0a3166dbe9840e 100755 --- a/configure +++ b/configure @@ -834,7 +834,7 @@ LIBPL PY_ENABLE_SHARED PLATLIBDIR BINLIBDEST -LIBPYTHON +MODULE_LDFLAGS MODULE_DEPS_SHARED EXT_SUFFIX ALT_SOABI @@ -13627,7 +13627,13 @@ then : else $as_nop if test "$cross_compiling" = yes then : + +# "yes" changes the hash function to FNV, which breaks Numba. +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_aligned_required=no +else ac_cv_aligned_required=yes +fi else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -22004,7 +22010,9 @@ else $as_nop if test "$cross_compiling" = yes then : -if test "${enable_ipv6+set}" = set; then +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_buggy_getaddrinfo="no" +elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" else ac_cv_buggy_getaddrinfo=yes @@ -23931,16 +23939,14 @@ LDVERSION='$(VERSION)$(ABIFLAGS)' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDVERSION" >&5 printf "%s\n" "$LDVERSION" >&6; } - -MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' - # On Android and Cygwin the shared libraries must be linked with libpython. + +MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' +MODULE_LDFLAGS='' if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then - LIBPYTHON="$BLDLIBRARY" MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" -else - LIBPYTHON='' + MODULE_LDFLAGS="\$(BLDLIBRARY)" fi @@ -26671,24 +26677,28 @@ CPPFLAGS=$ac_save_cppflags { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} -if test "x$cross_compiling" = xyes; then - if test "${ac_cv_file__dev_ptmx+set}" != set; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptmx" >&5 +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no +else + if test "x$cross_compiling" = xyes; then + if test "${ac_cv_file__dev_ptmx+set}" != set; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptmx" >&5 printf %s "checking for /dev/ptmx... " >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not set" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not set" >&5 printf "%s\n" "not set" >&6; } - as_fn_error $? "set ac_cv_file__dev_ptmx to yes/no in your CONFIG_SITE file when cross compiling" "$LINENO" 5 - fi - if test "${ac_cv_file__dev_ptc+set}" != set; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptc" >&5 + as_fn_error $? "set ac_cv_file__dev_ptmx to yes/no in your CONFIG_SITE file when cross compiling" "$LINENO" 5 + fi + if test "${ac_cv_file__dev_ptc+set}" != set; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptc" >&5 printf %s "checking for /dev/ptc... " >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not set" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not set" >&5 printf "%s\n" "not set" >&6; } - as_fn_error $? "set ac_cv_file__dev_ptc to yes/no in your CONFIG_SITE file when cross compiling" "$LINENO" 5 + as_fn_error $? "set ac_cv_file__dev_ptc to yes/no in your CONFIG_SITE file when cross compiling" "$LINENO" 5 + fi fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptmx" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptmx" >&5 printf %s "checking for /dev/ptmx... " >&6; } if test ${ac_cv_file__dev_ptmx+y} then : @@ -26709,12 +26719,12 @@ then : fi -if test "x$ac_cv_file__dev_ptmx" = xyes; then + if test "x$ac_cv_file__dev_ptmx" = xyes; then printf "%s\n" "#define HAVE_DEV_PTMX 1" >>confdefs.h -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptc" >&5 + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /dev/ptc" >&5 printf %s "checking for /dev/ptc... " >&6; } if test ${ac_cv_file__dev_ptc+y} then : @@ -26735,10 +26745,11 @@ then : fi -if test "x$ac_cv_file__dev_ptc" = xyes; then + if test "x$ac_cv_file__dev_ptc" = xyes; then printf "%s\n" "#define HAVE_DEV_PTC 1" >>confdefs.h + fi fi if test $ac_sys_system = Darwin diff --git a/configure.ac b/configure.ac index e07786ab806968..e047a0257ec5f8 100644 --- a/configure.ac +++ b/configure.ac @@ -3567,7 +3567,13 @@ int main(void) }]])], [ac_cv_aligned_required=no], [ac_cv_aligned_required=yes], -[ac_cv_aligned_required=yes]) +[ +# "yes" changes the hash function to FNV, which breaks Numba. +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_aligned_required=no +else + ac_cv_aligned_required=yes +fi]) ]) if test "$ac_cv_aligned_required" = yes ; then AC_DEFINE([HAVE_ALIGNED_REQUIRED], [1], @@ -5275,7 +5281,9 @@ int main(void) [ac_cv_buggy_getaddrinfo=no], [ac_cv_buggy_getaddrinfo=yes], [ -if test "${enable_ipv6+set}" = set; then +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_buggy_getaddrinfo="no" +elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" else ac_cv_buggy_getaddrinfo=yes @@ -5851,16 +5859,14 @@ AC_MSG_CHECKING([LDVERSION]) LDVERSION='$(VERSION)$(ABIFLAGS)' AC_MSG_RESULT([$LDVERSION]) +# On Android and Cygwin the shared libraries must be linked with libpython. AC_SUBST([MODULE_DEPS_SHARED]) +AC_SUBST([MODULE_LDFLAGS]) MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' - -# On Android and Cygwin the shared libraries must be linked with libpython. -AC_SUBST([LIBPYTHON]) +MODULE_LDFLAGS='' if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then - LIBPYTHON="$BLDLIBRARY" MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" -else - LIBPYTHON='' + MODULE_LDFLAGS="\$(BLDLIBRARY)" fi @@ -6492,28 +6498,35 @@ CPPFLAGS=$ac_save_cppflags AC_MSG_NOTICE([checking for device files]) dnl NOTE: Inform user how to proceed with files when cross compiling. -if test "x$cross_compiling" = xyes; then - if test "${ac_cv_file__dev_ptmx+set}" != set; then - AC_MSG_CHECKING([for /dev/ptmx]) - AC_MSG_RESULT([not set]) - AC_MSG_ERROR([set ac_cv_file__dev_ptmx to yes/no in your CONFIG_SITE file when cross compiling]) - fi - if test "${ac_cv_file__dev_ptc+set}" != set; then - AC_MSG_CHECKING([for /dev/ptc]) - AC_MSG_RESULT([not set]) - AC_MSG_ERROR([set ac_cv_file__dev_ptc to yes/no in your CONFIG_SITE file when cross compiling]) +dnl Some cross-compile builds are predictable; they won't ever +dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. +if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no +else + if test "x$cross_compiling" = xyes; then + if test "${ac_cv_file__dev_ptmx+set}" != set; then + AC_MSG_CHECKING([for /dev/ptmx]) + AC_MSG_RESULT([not set]) + AC_MSG_ERROR([set ac_cv_file__dev_ptmx to yes/no in your CONFIG_SITE file when cross compiling]) + fi + if test "${ac_cv_file__dev_ptc+set}" != set; then + AC_MSG_CHECKING([for /dev/ptc]) + AC_MSG_RESULT([not set]) + AC_MSG_ERROR([set ac_cv_file__dev_ptc to yes/no in your CONFIG_SITE file when cross compiling]) + fi fi -fi -AC_CHECK_FILE([/dev/ptmx], [], []) -if test "x$ac_cv_file__dev_ptmx" = xyes; then - AC_DEFINE([HAVE_DEV_PTMX], [1], - [Define to 1 if you have the /dev/ptmx device file.]) -fi -AC_CHECK_FILE([/dev/ptc], [], []) -if test "x$ac_cv_file__dev_ptc" = xyes; then - AC_DEFINE([HAVE_DEV_PTC], [1], - [Define to 1 if you have the /dev/ptc device file.]) + AC_CHECK_FILE([/dev/ptmx], [], []) + if test "x$ac_cv_file__dev_ptmx" = xyes; then + AC_DEFINE([HAVE_DEV_PTMX], [1], + [Define to 1 if you have the /dev/ptmx device file.]) + fi + AC_CHECK_FILE([/dev/ptc], [], []) + if test "x$ac_cv_file__dev_ptc" = xyes; then + AC_DEFINE([HAVE_DEV_PTC], [1], + [Define to 1 if you have the /dev/ptc device file.]) + fi fi if test $ac_sys_system = Darwin From 90eec1e8af398f5345f7ecb87980f4b4b6185d17 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 16 Feb 2024 21:34:10 +0000 Subject: [PATCH 4/6] Fix build on Linux --- Android/android-env.sh | 10 +++++----- Android/android.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Android/android-env.sh b/Android/android-env.sh index 9f3ff1f6f318eb..3ce3e035cfb8fe 100644 --- a/Android/android-env.sh +++ b/Android/android-env.sh @@ -61,19 +61,19 @@ done export CFLAGS="" export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment" -# Many packages get away with omitting this on standard Linux, but Android is stricter. -LDFLAGS+=" -lm" +# 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+=" -march=armv7-a -mthumb" + CFLAGS="$CFLAGS -march=armv7-a -mthumb" fi if [ -n "${PREFIX:-}" ]; then abs_prefix=$(realpath $PREFIX) - CFLAGS+=" -I$abs_prefix/include" - LDFLAGS+=" -L$abs_prefix/lib" + 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" diff --git a/Android/android.py b/Android/android.py index 96bca2357be10b..3f58710148cd05 100755 --- a/Android/android.py +++ b/Android/android.py @@ -11,7 +11,7 @@ from pathlib import Path SCRIPT_NAME = Path(__file__).name -CHECKOUT = Path(__file__).parent.parent +CHECKOUT = Path(__file__).resolve().parent.parent CROSS_BUILD_DIR = CHECKOUT / "cross-build" @@ -40,6 +40,7 @@ def run(command, *, host=None, **kwargs): if host: env_script = CHECKOUT / "Android/android-env.sh" env_output = subprocess.run( + f"set -eu; " f"HOST={host}; " f"PREFIX={subdir(host)}/prefix; " f". {env_script}; " From ec59dd1c454db99b18ddf260de930ff30d5cd2c1 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 16 Feb 2024 22:32:31 +0000 Subject: [PATCH 5/6] Add news entry --- .../next/Build/2024-02-16-22-26-00.gh-issue-71052.HEfpsm.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2024-02-16-22-26-00.gh-issue-71052.HEfpsm.rst diff --git a/Misc/NEWS.d/next/Build/2024-02-16-22-26-00.gh-issue-71052.HEfpsm.rst b/Misc/NEWS.d/next/Build/2024-02-16-22-26-00.gh-issue-71052.HEfpsm.rst new file mode 100644 index 00000000000000..470567f2e2f82c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-02-16-22-26-00.gh-issue-71052.HEfpsm.rst @@ -0,0 +1,2 @@ +Add instructions and a script to build for Android, and fix related build +issues. From a646e2b7c2f3ca5ce5c7dfbefdb031218c8868f4 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Mon, 19 Feb 2024 15:26:20 +0000 Subject: [PATCH 6/6] Fix typos --- Android/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Android/README.md b/Android/README.md index b63de15adc9c0d..5ed186e06e3951 100644 --- a/Android/README.md +++ b/Android/README.md @@ -6,7 +6,7 @@ use Python on Android, one of the following tools will provide a much more approachable user experience: * [Briefcase](https://briefcase.readthedocs.io), from the BeeWare project -* [Builddozer](https://buildozer.readthedocs.io), from the Kivy project +* [Buildozer](https://buildozer.readthedocs.io), from the Kivy project * [Chaquopy](https://chaquo.com/chaquopy/) @@ -52,10 +52,10 @@ Or to do it all in a single command, run: ``` In the end you should have a build Python in `cross-build/build`, and an Android -build in `cross-build/HOST `. +build in `cross-build/HOST`. -You can use `--` as a separator for any of the `configure`-related commands -- -including `build` itself -- to pass arguments to the underlying `configure` +You can use `--` as a separator for any of the `configure`-related commands – +including `build` itself – to pass arguments to the underlying `configure` call. For example, if you want a pydebug build that also caches the results from `configure`, you can do: