diff --git a/Android/README.rst b/Android/README.rst new file mode 100644 index 00000000000000..0d13115bf17b8d --- /dev/null +++ b/Android/README.rst @@ -0,0 +1,320 @@ +======================== +Python on Android README +======================== + +.. contents:: + +This document provides a quick overview of the Python build system for Android +on a linux platform [1]_. + + +- The ``Android/makesetup`` script creates a Makefile and its command line + arguments are passed directly to Python ``configure``. Although the + ``--prefix`` command line argument may be omitted, it does not make much sense + as ``/usr/local`` does not exist on Android. One may run ``makesetup`` for + multiple (api, architecture) combinations in the same directory. + + The targets of this Makefile allow to: + + - Cross-compile Python with external libraries. + - Build a distribution to be copied on an Android device on an existing + Android application. + - Run the Python interpreter on the Android emulator using an adb_ shell, the + interpreter runs with readline enabled. + - Run a Python command on the Android emulator. + - Start a gdb remote debugging session of a python process running on the + emulator. + - Run the buildbottest. + + +Requirements +============ + +- A linux platform [1]_ +- The Android NDK, android-ndk-r14 is required. The NDK is distributed by + Android as a zip file at `NDK downloads`_. The android-ndk-r14 needs 2.8 Gb of + disk space. +- The Android SDK (see `Installation of the SDK`_) to run any emulator Makefile + target. The SDK with the system images of the armv7, arm64, x86 and x86_64 + architectures for API 24 requires about 11 Gb of disk space. +- A Java 8 JRE is required by the SDK. +- GNU make, find, xargs, md5sum, awk, sed, zip and unzip. +- The native compiler of the build platform to build the native Python. +- wget or curl for downloading the external libraries unless those files are + downloaded by other means and copied by hand to build/external-libraries. + + +Environment variables +===================== +The ``Android/makesetup`` script uses the following environment variables: + +- ``ANDROID_NDK_ROOT``, the location where the Android NDK has been installed. + The default location is ``$HOME/android/android-ndk`` when this variable is + not set and this is where the buildbots expect to find the NDK. +- ``ANDROID_API``, the target API level for the cross-compilation. This is also + the API level of the emulator AVD [2]_. The default value is 24. +- ``ANDROID_ARCH``, the target architecture for the cross-compilation. This is + also the architecture of the AVD. It may be armv7, arm64, x86 or x86_64. See + `ABI Management`_ for the corresponding ABIs. The default value is x86_64. + +The ``WITH_LIBRARIES`` variable is a comma separated list of the external +libraries that are linked with extension modules when the build is done with the +Makefile built by ``makesetup``. This list may include the libffi, ncurses, +openssl, readline or sqlite library. When this environment variable is not set +the readline and ncurses libraries are used. + +When building for the emulator, the following environment variables are used by +``makesetup``: + +- ``ANDROID_SDK_ROOT``, the location where the Android SDK has been installed. + The default location is ``$HOME/android/android-sdk`` and this is where the + buildbots expect to find the SDK. + +- ``EMULATOR_CMD_LINE_OPTIONS``, the emulator command line options (see + `Start the Emulator from the Command Line`_). Some useful options: + + * ``-use-system-libs``, use the system libstdc++ instead of the version + bundled with the emulator system. Use it if the emulator does not start. + * ``-no-window``, run the emulator on a server that has no display. + +- ``PYTHON_DEVPT_VERSION``, the current Python development version (e.g. 3.8). + This variable is used by the buildbots to allow for running simultaneous + emulator instances with both the current development version and the + maintenance version. It is not useful otherwise and when not set or + incorrectly set, simultaneous emulator instances can still be be run provided + that each one runs with a distinct architecture. + +The Makefile generated by makesetup is run according to the values of these +environment variables at the time it has been generated. + + +Makefile targets +================ +Build targets +^^^^^^^^^^^^^ +*build* + * Compile the native Python interpreter and cross-compile the external + libraries and python. This is the default target. + +*dist* + * Make a distribution consisting of: + + - The machine-specific Python library zip file. + - The Python standard library zip file. + +*distclean* + * Kill the emulator if it is running. + * Make things clean, before making a distribution. + * Removes the DESTDIR directory where the external libraries have been + copied and where the cross-compiled Python has been installed by the + command ``make DESTDIR=$(PY_DESTDIR) install`` run on Python own Makefile. + The external libraries will not be rebuilt on the next make invocation. + +*clean* + Remove everything for the given (api, architecture) except the AVD. + +Emulator targets +^^^^^^^^^^^^^^^^ + +When the AVD is being created by one of the targets, it is not necessary to +answer the following question printed on the screen at that time:: + + Do you wish to create a custom hardware profile? [no] + +Upon starting the emulator, python may fail to start with the error ``No module +named 'encodings'`` because ``/sdcard`` is not mounted yet. This does not happen +with the ``install`` target, but a user running the ``python`` or ``adb_shell`` +target must ensure that python is not run too early after the emulator has been +started. + +*install* + Make a distribution, create the AVD if it does not exist, start the emulator + after having wiped out previous data and install the content of the two zip + files on the emulator. Then start an adb_ shell (see the ``adb_shell`` + target description below). + + Run ``make distclean install`` to get an install from scratch. + +*python* + Start the emulator if needed and run the python command defined by + ``PYTHON_ARGS``. This variable is set on make command line or as an + environment variable when make is run. Quotes in the command are interpreted + both by the shell when interpreting the make command line and by make + itself, so they must be escaped properly such as in this example:: + + $ make python PYTHON_ARGS="-c 'print(\\\"Hello world.\\\")'" + +*adb_shell* + Start the emulator if needed and create an adb_ shell on the emulator. + + At the first shell prompt a message is printed giving the shell command to + run to source a shell script that sets the environment variables needed to + run the Python interpreter. The script mainly does: + + - Set ``PATH`` and ``LD_LIBRARY_PATH``. + - Set ``HOME`` to ``sys.exec_prefix``. + - Set miscellaneous stuff such as the terminal type, the terminal width and + the readline inputrc configuration file. + - Change the current directory to ``$HOME``. + + After sourcing this script one can run the Python interpreter. + +*kill_emulator* + Kill the emulator. Useful when the emulator refuses to be shutdown from its + GUI or when there is no GUI. + +*avdclean* + Remove the AVD. This is the proper way to remove an AVD, do not just remove + the corresponding directory in the avd/ directory because Android maintains + also some information on the AVD in ~/.android/avd. + +*gdb* + Start a gdb remote debugging session of a python process running on the + emulator. There must be a unique python process running on the emulator. + + This requires that Python 2 is installed on the build platform and that + ``python2`` is found in the ``$PATH``. + + The debugging session can be customized with the following variables set + on the command line (or as environment variables) to the value ``yes`` + (for example ``GDB_PYTHON=yes make gdb``): + + - ``GDB_PYTHON=yes`` + Import the `libpython module`_ in gdb and get detailed information of + the PyObject(s) at the cost of speed. + + - ``GDB_LOGGING=yes`` + Setup logging in gdb and have the output of all the gdb commands also + redirected to ./gdb.log. + + - ``GDB_SIGILL=yes`` + Work around the problem that gdb fails with SIGILL in + ``__dl_notify_gdb_of_libraries()`` whenever a library is loaded when + debugging on the armv7 platforms. + + +Using the emulator +================== +The ``makesetup`` script creates a Makefile. Running the Makefile requires that +both the NDK and the SDK be installed (see `Installation of the SDK`_). The +`Makefile targets`_ section lists the available targets. For example, to build +Python and install it on the emulator:: + + $ /path/to/python_src/Android/makesetup --with-pydebug && make install + +Another example, testing the socket module:: + + $ /path/to/python_src/Android/makesetup && make python PYTHON_ARGS="-m test test_socket" + +The first execution of this statement takes quite a while. It creates the +Makefile for the given (api, architecture), as set by the ``$ANDROID_API`` and +``$ANDROID_ARCH`` environment variables at the time the Makefile was generated, +builds a native Python interpreter if it does not exist yet (used later for the +cross-compilation of Python on Android) and builds the readline and ncurses +external libraries for Android. It then cross-compiles Python for Android, +linking extension modules with the external libraries and builds the zip +distributions, creates an AVD, starts the emulator, installs the Python +distribution on the emulator and finally runs the python command. Note that the +Android emulators are very slow except for the x86 and x86_64 architectures when +the processor of the build platform is itself of the x86 family. + +The next runs of:: + + $ make python PYTHON_ARGS="-m test test_socket" + +are much faster. The external libraries do not have to be rebuilt again, the AVD +is already created and if there is no change in the source code then the native +Python and Python on Android do not have to be rebuilt, but all the other steps +enumerated above are run again except the Makefile creation. + + +Installation of the SDK +======================= +The SDK provides the tools and system images to run the emulator. The emulator +uses a specific AVD for each given (api, architecture) combination and system +images must be added to the SDK for each one. One may install and manage the SDK +with `Android Studio`_ but the prefered method is with the command line. + +The remaining part of this section describes the installation of the SDK and its +management with the ``sdkmanager`` command line tool, through an example that +installs the SDK and four system images for armv7, arm64, x86 and x86_64 at API +24: + +- Download the basic Android command line tools by looking for a section named + *Get just the command line tools* at the end of the `Android Studio`_ page. + Unzip the downloaded file into the ``$ANDROID_SDK_ROOT`` (possibly not yet + created) directory:: + + $ unzip sdk-tools-linux-xxx.zip -d $ANDROID_SDK_ROOT + + See the `sdkmanager manual`_. + +- List the installed packages (with an internet connection this gives also the + list of available packages):: + + $ $ANDROID_SDK_ROOT/tools/bin/sdkmanager --list + + The package names are truncated in the list output by this command, one must + add the ``--verbose`` option to the command to get the full package names. + + Substitute ``';'`` in a package name with ``'/'`` to get the relative path to + ``ANDROID_SDK_ROOT`` where this package is installed. + +- Create a file named ``package_file`` that lists the packages to be installed + and whose content, following our example, is listed below. This file **must + not** contain **any empty line** or the command just fails with a cryptic + message:: + + build-tools;25.0.3 + platform-tools + emulator + platforms;android-24 + system-images;android-24;default;arm64-v8a + system-images;android-24;default;armeabi-v7a + system-images;android-24;default;x86 + system-images;android-24;default;x86_64 + + A minimun installation consists of the first three packages in this list, plus + a ``platforms`` package for a given API and a system image in this API. + +- Install the packages:: + + $ $ANDROID_SDK_ROOT/tools/bin/sdkmanager --verbose --package_file=package_file + +- For reference, here is the output of ``sdkmanager --list`` after those + packages have been installed from scratch in december 2017:: + + Installed packages: + Path | Version | Description | Location + ------- | ------- | ------- | ------- + build-tools;25.0.3 | 25.0.3 | Android SDK Build-Tools 25.0.3 | build-tools/25.0.3/ + emulator | 26.1.4 | Android Emulator | emulator/ + patcher;v4 | 1 | SDK Patch Applier v4 | patcher/v4/ + platform-tools | 26.0.2 | Android SDK Platform-Tools | platform-tools/ + platforms;android-24 | 2 | Android SDK Platform 24 | platforms/android-24/ + system-images;a...ult;arm64-v8a | 7 | ARM 64 v8a System Image | system-images/android-24/default/arm64-v8a/ + system-images;a...ult;armeabi-v7a | 7 | ARM EABI v7a System Image | system-images/android-24/default/armeabi-v7a/ + system-images;a...ult;x86 | 8 | Intel x86 Atom System Image | system-images/android-24/default/x86/ + system-images;a...ult;x86_64 | 8 | Intel x86 Atom_64 System Image | system-images/android-24/default/x86_64/ + tools | 26.1.1 | Android SDK Tools | tools/ + + +.. [1] A 64-bit linux distribution capable of running 32-bit applications with + GNU C Library (glibc) 2.19 or later, see the `Android Studio`_ system + requirements section. It is not necessary to install Android Studio. + +.. [2] Android Virtual Device. This is the image run by the emulator. It is + specific to each (api, architecture) and it holds also the configuration of + the emulator. + + +.. _termux: https://termux.com/ +.. _adb: https://developer.android.com/studio/command-line/adb.html +.. _`libpython module`: https://github.com/python/cpython/blob/master/Tools/gdb/libpython.py +.. _`NDK downloads`: https://developer.android.com/ndk/downloads/index.html +.. _`Android Studio`: https://developer.android.com/studio/index.html +.. _`sdkmanager manual`: https://developer.android.com/studio/command-line/sdkmanager.html +.. _`Start the Emulator from the Command Line`: https://developer.android.com/studio/run/emulator-commandline.html +.. _`ABI Management`: https://developer.android.com/ndk/guides/abis.html + +.. vim:filetype=rst:tw=80:ts=8:sts=2:sw=2:et: diff --git a/Android/build-config b/Android/build-config new file mode 100644 index 00000000000000..2e68c6786ad682 --- /dev/null +++ b/Android/build-config @@ -0,0 +1,95 @@ +# Android/build-config + +ndk_version=14 + +# Set default values if not defined. +: ${ANDROID_NDK_ROOT:=$HOME/android/android-ndk} +: ${ANDROID_SDK_ROOT:=$HOME/android/android-sdk} +: ${ANDROID_API:=24} +: ${ANDROID_ARCH:=x86_64} + +build_config_error () +{ + echo "Error: $1" >&2 + exit 1 +} + +! test -d "$ANDROID_NDK_ROOT" && build_config_error "\$ANDROID_NDK_ROOT ($ANDROID_NDK_ROOT) is not a directory." +version=$(cat "$ANDROID_NDK_ROOT/source.properties" | \ + sed -e "s/^.*Pkg.Revision[ \t]*=[ \t]*\([0-9]\+\).*$\|^.*$/\1/" -e "/^$/d") +test "$version" != $ndk_version && + build_config_error "The installed NDK version is $version but version $ndk_version is required." +unset ndk_version version + +test $((ANDROID_API)) -lt 21 && \ + build_config_error "\$ANDROID_API ($ANDROID_API) must be 21 or greater." +test $((ANDROID_API)) -lt 24 && test "$ANDROID_ARCH" = arm64 && \ + build_config_error "Cannot build or run arm64 at an API level less than 24." + +# No NDK has been released at SDK API level 22, use NDK API 21 instead. +test "$ANDROID_API" = 22 && ANDROID_API=21 + +test -n "$PY_SRCDIR" && ANDROID_BUILD=$($PY_SRCDIR/config.guess) +gcc_version=4.9 +case "$ANDROID_ARCH" in + armv7) + ANDROID_HOST=arm-linux-androideabi + APP_ABI=armeabi-v7a + target=armv7-none-linux-androideabi + TOOLCHAIN=$ANDROID_HOST-$gcc_version + SYSROOT="$ANDROID_NDK_ROOT/platforms/android-$ANDROID_API/arch-arm" + ;; + arm64) + ANDROID_HOST=aarch64-linux-android + APP_ABI=arm64-v8a + target=aarch64-none-linux-android + TOOLCHAIN=$ANDROID_HOST-$gcc_version + ;; + x86) + ANDROID_HOST=i686-linux-android + APP_ABI=x86 + target=i686-none-linux-androideabi + TOOLCHAIN=x86-$gcc_version + ;; + x86_64) + ANDROID_HOST=x86_64-linux-android + APP_ABI=x86_64 + target=x86_64-none-linux-androideabi + TOOLCHAIN=x86_64-$gcc_version + ;; + *) + build_config_error "ANDROID_ARCH must be set to armv7, arm64, x86 or x86_64." + ;; +esac +unset gcc_version +if test "$ANDROID_ARCH" != armv7; then + SYSROOT="$ANDROID_NDK_ROOT/platforms/android-$ANDROID_API/arch-$ANDROID_ARCH" +fi + +machine=$(uname -m) +kernel=$(uname -s | tr A-Z a-z) +tool_path () +{ + echo "$ANDROID_NDK_ROOT/toolchains/$1/prebuilt/${kernel}-${machine}" +} +TOOLCHAIN=$(tool_path $TOOLCHAIN) + +# Build variables. +CC="$(tool_path llvm)/bin/clang -target $target -gcc-toolchain $TOOLCHAIN" +AR="$TOOLCHAIN/bin/$ANDROID_HOST-ar" +LD="$TOOLCHAIN/bin/$ANDROID_HOST-ld" +RANLIB="$TOOLCHAIN/bin/$ANDROID_HOST-ranlib" +READELF="$TOOLCHAIN/bin/$ANDROID_HOST-readelf" +STRIP="$TOOLCHAIN/bin/$ANDROID_HOST-strip --strip-debug --strip-unneeded" +unset target machine kernel tool_path + +# We need to set '--sysroot' both in CFLAGS and CPPFLAGS: +# * configure needs to have '--sysroot' set in CPPFLAGS when it runs CPP. +# * Extension modules are not compiled by distutils with CPPFLAGS. +ndk_flags="--sysroot=$ANDROID_NDK_ROOT/sysroot -D__ANDROID_API__=$ANDROID_API -isystem $ANDROID_NDK_ROOT/sysroot/usr/include/$ANDROID_HOST" +CPPFLAGS="$CPPFLAGS $ndk_flags" +CFLAGS="$CFLAGS $ndk_flags" +LDFLAGS="$LDFLAGS --sysroot=$SYSROOT" +unset ndk_flags + +ccache -V >/dev/null 2>&1 && { CC="ccache $CC"; CFLAGS="$CFLAGS -Wno-parentheses-equality"; } diff --git a/Android/build.mk b/Android/build.mk new file mode 100644 index 00000000000000..c6414863842353 --- /dev/null +++ b/Android/build.mk @@ -0,0 +1,245 @@ +# Android/build.mk + +# Directory names. +py_version := $(shell cat $(py_srcdir)/configure | \ + sed -e "s/^PACKAGE_VERSION=['\"]*\([0-9]*\.[0-9]*\)['\"]*$$\|^.*$$/\1/" -e "/^$$/d") +export BUILD_DIR := $(MAKEFILE_DIR)build +export DIST_DIR := $(MAKEFILE_DIR)dist +avd_dir := $(MAKEFILE_DIR)avd +native_build_dir := $(BUILD_DIR)/python-native +py_name := python$(py_version) +export BUILD_TYPE := android-$(ANDROID_API)-$(ANDROID_ARCH) +py_host_dir := $(BUILD_DIR)/$(py_name)-$(BUILD_TYPE) +export PY_EXTDIR := $(BUILD_DIR)/$(py_name)-extlibs-$(BUILD_TYPE) +PY_DESTDIR := $(BUILD_DIR)/$(py_name)-install-$(BUILD_TYPE) +abiflags = $(shell echo $$(cat $(py_host_dir)/Makefile | \ + sed -e "s/^ABIFLAGS[= \t]*\(.*\)[ \t]*$$\|^.*$$/\1/" -e "/^$$/d")) +py_fullname = python$(py_version)$(abiflags) +export STDLIB_DIR := lib/python$(py_version) + + +ROOT_MAKE := $(MAKE) -f Makefile-$(BUILD_TYPE) + +# Target variables names. +python := $(py_host_dir)/python +config_status := $(py_host_dir)/config.status +export PYTHON_ZIP := $(DIST_DIR)/$(py_name)-$(BUILD_TYPE).zip +export PY_STDLIB_ZIP := $(DIST_DIR)/$(py_name)-$(BUILD_TYPE)-stdlib.zip + + +# Rules. +build: $(python) +dist: $(PYTHON_ZIP) $(PY_STDLIB_ZIP) + +include $(py_srcdir)/Android/emulator.mk + +$(native_build_dir)/Modules/Setup: $(py_srcdir)/Modules/Setup.dist + cp $(py_srcdir)/Modules/Setup.dist $(native_build_dir)/Modules/Setup + +$(native_build_dir)/config.status: $(py_srcdir)/configure + @# Run distclean upon the switch to a new Python release. + @# 'Touch' the 'Makefile' target first to prevent a useless run of configure. + @cur=$$(cat $(py_srcdir)/configure | sed -e "s/^VERSION=\([0-9]\+\.[0-9]\+\)$$\|^.*$$/\1/" -e "/^$$/d"); \ + prev=$$cur; \ + if test -f "$(native_build_dir)/python"; then \ + prev=$$($(native_build_dir)/python -c "from sys import version_info; print('.'.join(str(i) for i in version_info[:2]))"); \ + fi; \ + if test "$$prev" != "$$cur"; then \ + echo "---> Cleanup before the switch from Python version $$prev to $$cur"; \ + $(MAKE) -C $(native_build_dir) -t Makefile; \ + $(MAKE) -C $(native_build_dir) distclean; \ + fi + @echo "---> Run the native configure script." + mkdir -p $(native_build_dir) + cd $(native_build_dir); $(py_srcdir)/configure + +native_python: $(native_build_dir)/config.status $(native_build_dir)/Modules/Setup + @echo "---> Build the native interpreter." + $(MAKE) -C $(native_build_dir) + +# Target-specific exported variables. +build configure host python_dist setup hostclean: \ + export PATH := $(native_build_dir):$(PATH) +external_libraries: export CC := $(CC) +external_libraries: export AR := $(AR) +external_libraries: export LD := $(LD) +external_libraries: export RANLIB := $(RANLIB) +external_libraries: export READELF := $(READELF) +external_libraries: export CFLAGS := $(CFLAGS) +external_libraries: export LDFLAGS := $(LDFLAGS) +external_libraries: export ANDROID_ARCH := $(ANDROID_ARCH) +external_libraries: + mkdir -p $(BUILD_DIR)/external-libraries +ifdef WITH_LIBFFI + $(MAKE) -C $(py_srcdir)/Android/external-libraries libffi +endif +ifdef WITH_NCURSES + $(MAKE) -C $(py_srcdir)/Android/external-libraries ncurses +endif +ifdef WITH_READLINE + $(MAKE) -C $(py_srcdir)/Android/external-libraries readline +endif +ifdef WITH_SQLITE + $(MAKE) -C $(py_srcdir)/Android/external-libraries sqlite +endif + +openssl: +ifdef WITH_OPENSSL + mkdir -p $(BUILD_DIR)/external-libraries + $(MAKE) -C $(py_srcdir)/Android/external-libraries -f Makefile.openssl +endif + +$(config_status): $(makefile) $(py_srcdir)/configure + mkdir -p $(py_host_dir) + @#echo "---> Ensure that nl_langinfo is broken." + @#cd $(py_host_dir); PY_SRCDIR=$(py_srcdir) $(py_srcdir)/Android/tools/nl_langinfo.sh + @echo "---> Run configure for $(BUILD_TYPE)." + cd $(py_host_dir); \ + PKG_CONFIG_PATH=$(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/lib/pkgconfig \ + CPPFLAGS=-I$(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/include \ + LDFLAGS=-L$(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/lib \ + $(py_srcdir)/configure-android \ + --prefix=$(SYS_PREFIX) --exec-prefix=$(SYS_EXEC_PREFIX) \ + $(config_args) + +$(python): native_python prefixes external_libraries openssl $(config_status) + $(ROOT_MAKE) host + +configure: native_python external_libraries openssl + @rm -f $(config_status) + $(ROOT_MAKE) $(config_status) + +prefixes: + $(eval PREFIXES := $(shell cd $(py_srcdir)/Android/tools; $(python_cmd) \ + -c 'from android_utils import parse_prefixes; parse_prefixes("$(config_args)")')) + $(eval export SYS_PREFIX := $(word 1, $(PREFIXES))) + $(eval export SYS_EXEC_PREFIX := $(word 2, $(PREFIXES))) + @echo "---> Prefixes are $(SYS_PREFIX), $(SYS_EXEC_PREFIX)." + +disabled_modules: setup_tmp := $(py_host_dir)/Modules/_Setup.tmp +disabled_modules: setup_file := $(py_host_dir)/Modules/Setup +disabled_modules: + cp $(py_srcdir)/Modules/Setup.dist $(setup_tmp) + echo "*disabled*" >> $(setup_tmp) + echo "_uuid" >> $(setup_tmp) + echo "grp" >> $(setup_tmp) + echo "_crypt" >> $(setup_tmp) + @if test -f $(setup_file); then \ + if test $$(md5sum $(setup_tmp) | awk '{print $$1}') != \ + $$(md5sum $(setup_file) | awk '{print $$1}'); then \ + cp $(setup_tmp) $(setup_file); \ + fi \ + fi + rm $(setup_tmp) + +host: disabled_modules + @echo "---> Build Python for $(BUILD_TYPE)." + @if test -f $(config_status); then \ + $(MAKE) -C $(py_host_dir) all; \ + else \ + echo "Error: please run 'make config', missing $(config_status)"; \ + false; \ + fi + +python_dist: $(python) + @echo "---> Install Python for $(BUILD_TYPE)." + $(MAKE) DESTDIR=$(PY_DESTDIR) -C $(py_host_dir) install > make_install.log + cp --no-dereference $(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/lib/*.so* $(PY_DESTDIR)/$(SYS_EXEC_PREFIX)/lib + chmod u+w $(PY_DESTDIR)/$(SYS_EXEC_PREFIX)/lib/*.so* + tdir=$(SYS_EXEC_PREFIX)/share/terminfo/l; mkdir -p $(PY_DESTDIR)/$$tdir && \ + cp $(PY_EXTDIR)/$$tdir/linux $(PY_DESTDIR)/$$tdir + if test -d "$(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/etc"; then \ + cp -r $(PY_EXTDIR)/$(SYS_EXEC_PREFIX)/etc $(PY_DESTDIR)/$(SYS_EXEC_PREFIX); \ + fi + +$(PYTHON_ZIP): python_dist + @echo "---> Zip the machine-specific Python library." + mkdir -p $(DIST_DIR) + rm -f $(PYTHON_ZIP) + + cd $(PY_DESTDIR)/$(SYS_EXEC_PREFIX)/bin; \ + ln -sf python3 python + + mkdir -p $(PY_DESTDIR)/$(SYS_EXEC_PREFIX)/etc; \ + cp $(py_srcdir)/Android/resources/inputrc $(PY_DESTDIR)/$(SYS_EXEC_PREFIX)/etc + + cd $(PY_DESTDIR)/$(SYS_EXEC_PREFIX); \ + $(STRIP) bin/python$(py_version); \ + chmod 755 lib/*.so*; find . -type f -name "*.so*" -print0 | xargs -0 $(STRIP) + + @# Remove the first '/' to avoid unzip warnings and an exit code of '1'. + cd $(PY_DESTDIR); export SYS_EXEC_PREFIX=$${SYS_EXEC_PREFIX#/}; \ + zip -g $(PYTHON_ZIP) $$SYS_EXEC_PREFIX/bin/python3 \ + $$SYS_EXEC_PREFIX/bin/python $$SYS_EXEC_PREFIX/bin/python$(py_version); \ + zip -rg $(PYTHON_ZIP) \ + $$SYS_EXEC_PREFIX/include/$(py_fullname)/pyconfig.h \ + $$SYS_EXEC_PREFIX/$(STDLIB_DIR)/ \ + $$SYS_EXEC_PREFIX/share/terminfo/l/linux \ + $$SYS_EXEC_PREFIX/etc/ \ + -x \*failed.so + + # Zip the shared libraries excluding symlinks. + cd $(PY_DESTDIR); export SYS_EXEC_PREFIX=$${SYS_EXEC_PREFIX#/}; \ + libs=""; \ + for l in $$SYS_EXEC_PREFIX/lib/*.so*; do \ + test ! -h $$l && libs="$$libs $$l"; \ + done; \ + zip -g $(PYTHON_ZIP) $$libs + +$(PY_STDLIB_ZIP): python_dist + @echo "---> Zip the Python library." + mkdir -p $(DIST_DIR) + rm -f $(PY_STDLIB_ZIP) + cd $(PY_DESTDIR); export SYS_PREFIX=$${SYS_PREFIX#/}; \ + zip -Drg $(PY_STDLIB_ZIP) $$SYS_PREFIX/$(STDLIB_DIR)* \ + -x \*.so \*.pyo \*opt-\*.pyc + +# Build and install a third-party extension module from the setup.py directory: +# make -f /path/to/Makefile/generated/by/makesetup setup +setup: required + CROSS_BUILD_PYTHON_DIR=$(PY_DESTDIR)/$(SYS_EXEC_PREFIX) \ + $(native_python_exe) setup.py install; \ + +# Make things clean, before making a distribution. +distclean: kill_emulator hostclean + rm -f $(PYTHON_ZIP) + rm -f $(PY_STDLIB_ZIP) + rm -f $(DIST_DIR)/*.sh + -rmdir $(DIST_DIR) + rm -f $(BUILD_DIR)/python + rm -rf $(PY_DESTDIR) + +hostclean: + -$(MAKE) -C $(py_host_dir) distclean + +# Remove everything for the given ANDROID_API and ANDROID_ARCH except the avd. +clean: distclean + rm -rf $(BUILD_DIR)/python-native + rm -rf $(py_host_dir) + rm -rf $(BUILD_DIR)/external-libraries/*-$(BUILD_TYPE) + -rmdir $(BUILD_DIR)/external-libraries + rm -rf $(PY_EXTDIR) + -rmdir $(BUILD_DIR) + -rmdir $(avd_dir) + rm -rf $(DIST_DIR)/gdb/android-$(ANDROID_API)-$(ANDROID_ARCH) + -rmdir $(DIST_DIR)/gdb + -rmdir $(DIST_DIR) + rm Makefile Makefile-android-$(ANDROID_API)-$(ANDROID_ARCH) + + +# Tool names. +wget := $(shell which wget 2>/dev/null) +curl := $(shell which curl 2>/dev/null) +ifeq (curl, $(findstring curl, $(curl))) + export DOWNLOAD := $(curl) -O +else ifeq (wget, $(findstring wget, $(wget))) + export DOWNLOAD := $(wget) +else + $(warning *** Cannot download the external libraries with wget or curl, \ + please download those libraries to the 'Android/external-libraries' \ + directory.) +endif + +.PHONY: build configure prefixes disabled_modules host native_python \ + external_libraries openssl setup \ + dist python_dist distclean hostclean clean diff --git a/Android/emulator.mk b/Android/emulator.mk new file mode 100644 index 00000000000000..70c7c25551f2a8 --- /dev/null +++ b/Android/emulator.mk @@ -0,0 +1,151 @@ +# Android/emulator.mk + +avd_name := $(BUILD_TYPE) +native_python_exe := $(native_build_dir)/python +python_cmd := $(native_python_exe) -B + +# This variable is used to differentiate the emulators when a buildbot is +# running both the 3.x and the maintenance versions. +python_devpt_version := 3.8 +ifdef PYTHON_DEVPT_VERSION + python_devpt_version := $(PYTHON_DEVPT_VERSION) +endif + +ifeq ($(py_version), $(python_devpt_version)) + ifeq ($(ANDROID_ARCH), x86) + export CONSOLE_PORT := 5554 + else ifeq ($(ANDROID_ARCH), x86_64) + export CONSOLE_PORT := 5556 + else ifeq ($(ANDROID_ARCH), armv7) + export CONSOLE_PORT := 5558 + else ifeq ($(ANDROID_ARCH), arm64) + export CONSOLE_PORT := 5560 + endif +else + ifeq ($(ANDROID_ARCH), x86) + export CONSOLE_PORT := 5562 + else ifeq ($(ANDROID_ARCH), x86_64) + export CONSOLE_PORT := 5564 + else ifeq ($(ANDROID_ARCH), armv7) + export CONSOLE_PORT := 5566 + else ifeq ($(ANDROID_ARCH), arm64) + export CONSOLE_PORT := 5568 + endif +endif +export ADB := $(ANDROID_SDK_ROOT)/platform-tools/adb + + +# Rules. +required: exists_python_cmd prefixes + +exists_python_cmd: + @if test ! -f "$(native_python_exe)"; then \ + echo "Error: the native python executable is missing"; \ + exit 1; \ + fi + +adb_shell: required _emulator + @echo "---> Run an adb shell." + $(python_cmd) $(py_srcdir)/Android/tools/python_shell.py + @$(ADB) -s emulator-$(CONSOLE_PORT) shell + +# The 'all' target is used by the buildbots. +# Abort when the emulator is already running so that we install on a clean +# emulator. +all: wipe_data := -wipe-data +all: export PY_SRCDIR := $(py_srcdir) +all: dist is_emulator_listening _emulator + @echo "---> Install Python on the avd and start the emulator." + $(python_cmd) $(py_srcdir)/Android/tools/install.py + +ensurepip: + @echo "---> Install pip." + @$(python_cmd) $(py_srcdir)/Android/tools/python_shell.py \ + -W ignore::DeprecationWarning -m ensurepip --upgrade --default-pip + +# Hack to fix a pip bug looping infinitely on SELinux platforms that restrict +# access to hard links. +pip_hack: + @echo "---> Install pip hack." + @pip_lockfile_py=$(SYS_EXEC_PREFIX)/lib/$(py_name)/site-packages/pip/_vendor/lockfile/__init__.py; \ + $(ADB) -s emulator-$(CONSOLE_PORT) pull $$pip_lockfile_py $(DIST_DIR)/pip_lockfile_py; \ + echo "from . import mkdirlockfile" >> $(DIST_DIR)/pip_lockfile_py; \ + echo "LockFile = mkdirlockfile.MkdirLockFile" >> $(DIST_DIR)/pip_lockfile_py; \ + $(ADB) -s emulator-$(CONSOLE_PORT) push $(DIST_DIR)/pip_lockfile_py $$pip_lockfile_py; \ + rm $(DIST_DIR)/pip_lockfile_py + +install: all ensurepip pip_hack + @echo "---> Run an adb shell." + $(python_cmd) $(py_srcdir)/Android/tools/python_shell.py + @$(ADB) -s emulator-$(CONSOLE_PORT) shell + +ifneq ($(PYTHON_ARGS), ) +python: required _emulator + @echo "---> Run ." + $(python_cmd) $(py_srcdir)/Android/tools/python_shell.py "$(PYTHON_ARGS)" +endif + +pythoninfo: required _emulator + @echo "---> Run pythoninfo." + @ndk_version=$$(sed -n -e 's/Pkg.Revision\s*=\s*\(\S*\).*/\1/p' \ + $(ANDROID_NDK_ROOT)/source.properties); \ + echo "Python debug information"; \ + echo "========================"; \ + echo "NDK version: $$ndk_version"; echo + $(python_cmd) $(py_srcdir)/Android/tools/python_shell.py -m test.pythoninfo + +buildbottest: export PATH := $(native_build_dir):$(PATH) +buildbottest: required _emulator + @echo "---> Run buildbottest." + -@if which pybuildbot.identify >/dev/null 2>&1; then \ + pybuildbot.identify "CC='$(CC)'" "CXX='$(CXX)'"; \ + fi + export TESTRUNNER="$(python_cmd) $(py_srcdir)/Android/tools/python_shell.py \ + $(TESTPYTHONOPTS) $(SYS_EXEC_PREFIX)/bin/run_tests.py"; \ + $(MAKE) -C $(py_host_dir) TESTRUNNER="$$TESTRUNNER" \ + TESTOPTS="$(TESTOPTS)" TESTTIMEOUT="$(TESTTIMEOUT)" \ + buildbottest + +gdb: export APP_ABI := $(APP_ABI) +gdb: export PY_HOST_DIR := $(py_host_dir) +gdb: export LIB_DYNLOAD = $(py_host_dir)/$(shell cat $(py_host_dir)/pybuilddir.txt) +gdb: prefixes + @echo "---> Start gdb." + $(py_srcdir)/Android/tools/ndk_gdb.py $(ANDROID_API) $(py_srcdir) python + +$(avd_dir)/$(avd_name): + @echo "---> Create the avd." + mkdir -p $(avd_dir) + echo "no" | $(ANDROID_SDK_ROOT)/tools/bin/avdmanager create avd --force --name $(avd_name) \ + --package "system-images;android-$(ANDROID_API);default;$(APP_ABI)" \ + --abi default/$(APP_ABI) --path $(avd_dir)/$(avd_name) + +kill_emulator: required + @echo "---> Kill the emulator." + -$(python_cmd) $(py_srcdir)/Android/tools/kill_emulator.py $(CONSOLE_PORT) + +is_emulator_listening: required + @echo "---> Check that an emulator is not currently running." + cd $(py_srcdir)/Android/tools; \ + $(python_cmd) -c "import sys, android_utils; \ + sys.exit(android_utils.is_emulator_listening($(CONSOLE_PORT)))" + +_emulator: required $(avd_dir)/$(avd_name) + @echo "---> Start the emulator if not already running." + $(python_cmd) $(py_srcdir)/Android/tools/start_emulator.py -avd $(avd_name) \ + -port $(CONSOLE_PORT) $(wipe_data) $(EMULATOR_CMD_LINE_OPTIONS) & + @ echo "---> Waiting for device to be ready." + @$(ADB) -s emulator-$(CONSOLE_PORT) wait-for-device shell getprop init.svc.bootanim; \ + echo "---> Device ready." + +avdclean: + @echo "---> Remove the AVD." + if test -d "$(avd_dir)/$(avd_name)"; then \ + $(ANDROID_SDK_ROOT)/tools/bin/avdmanager delete avd --name $(avd_name); \ + rm -rf $(avd_dir)/$(avd_name); \ + fi + -rmdir $(avd_dir) + +.PHONY: exists_python_cmd adb_shell all install python pythoninfo buildbottest gdb \ + kill_emulator is_emulator_listening _emulator avdclean required \ + ensurepip pip_hack diff --git a/Android/external-libraries/Makefile b/Android/external-libraries/Makefile new file mode 100644 index 00000000000000..e2f41cb9b0dd61 --- /dev/null +++ b/Android/external-libraries/Makefile @@ -0,0 +1,91 @@ +# Android/external-libraries/Makefile + +NCURSES := ncurses-6.0 +READLINE := readline-6.3 +SQLITE := sqlite3 +SQLITE_FNAME := sqlite-autoconf-3080803 +LIBFFI := libffi-3.2.1 +BUILD_DIR := $(BUILD_DIR)/external-libraries +py_install_dir := $(PY_EXTDIR)/$(SYS_EXEC_PREFIX) + +libffi: libffi_header $(py_install_dir)/lib/libffi.a +libffi_header: + @echo "---> Build the $(LIBFFI) external library." +ncurses: ncurses_header $(py_install_dir)/lib/libncursesw.a +ncurses_header: + @echo "---> Build the $(NCURSES) external library." +readline: readline_header $(py_install_dir)/lib/libreadline.a +readline_header: + @echo "---> Build the $(READLINE) external library." +sqlite: sqlite_header $(py_install_dir)/lib/libsqlite3.a +sqlite_header: + @echo "---> Build the sqlite external library (from $(SQLITE_FNAME).tar.gz)." + +$(BUILD_DIR)/$(NCURSES).tar.gz: + cd $(BUILD_DIR); \ + $(DOWNLOAD) http://ftp.gnu.org/pub/gnu/ncurses/$(NCURSES).tar.gz +$(BUILD_DIR)/$(READLINE).tar.gz: + cd $(BUILD_DIR); \ + $(DOWNLOAD) ftp://ftp.gnu.org/gnu/readline/$(READLINE).tar.gz +$(BUILD_DIR)/$(SQLITE_FNAME).tar.gz: + cd $(BUILD_DIR); \ + $(DOWNLOAD) https://sqlite.org/2015/$(SQLITE_FNAME).tar.gz +$(BUILD_DIR)/$(LIBFFI).tar.gz: + cd $(BUILD_DIR); \ + $(DOWNLOAD) ftp://sourceware.org/pub/libffi/$(LIBFFI).tar.gz + +$(py_install_dir)/lib/libncursesw.a: $(BUILD_DIR)/$(NCURSES).tar.gz + rm -rf $(BUILD_DIR)/$(NCURSES)-$(BUILD_TYPE) + tar xzf $< -C $(BUILD_DIR) + cd $(BUILD_DIR)/$(NCURSES); patch -p0 < $(CURDIR)/patches/ncurses.patch + mv $(BUILD_DIR)/$(NCURSES) $(BUILD_DIR)/$(NCURSES)-$(BUILD_TYPE) + cd $(BUILD_DIR)/$(NCURSES)-$(BUILD_TYPE); \ + ./configure --host=$(ANDROID_HOST) --build=$(ANDROID_BUILD) \ + --prefix=$(py_install_dir) \ + --without-ada --without-cxx --without-manpages \ + --without-progs --without-tests \ + --without-termlib --enable-widec \ + --with-shared --without-shlib-version --without-gpm + $(MAKE) -C $(BUILD_DIR)/$(NCURSES)-$(BUILD_TYPE) all install + @echo Fix symlinks for Python _curses and _curses_panel extensions. + cd $(py_install_dir)/include; cp -lf ncursesw/*.h . + +$(py_install_dir)/lib/libreadline.a: $(BUILD_DIR)/$(READLINE).tar.gz + rm -rf $(BUILD_DIR)/$(READLINE)-$(BUILD_TYPE) + tar xzf $< -C $(BUILD_DIR) + mv $(BUILD_DIR)/$(READLINE) $(BUILD_DIR)/$(READLINE)-$(BUILD_TYPE) + cd $(BUILD_DIR)/$(READLINE)-$(BUILD_TYPE); \ + autoreconf -i; \ + bash_cv_wcwidth_broken=no \ + CFLAGS="$(CFLAGS) -DANDROID" \ + ./configure --host=$(ANDROID_HOST) --build=$(ANDROID_BUILD) \ + --prefix=$(py_install_dir) \ + --disable-shared --with-curses=$(BUILD_DIR)/$(NCURSES)-$(BUILD_TYPE) + $(MAKE) -C $(BUILD_DIR)/$(READLINE)-$(BUILD_TYPE) all install + +$(py_install_dir)/lib/libsqlite3.a: $(BUILD_DIR)/$(SQLITE_FNAME).tar.gz + rm -rf $(BUILD_DIR)/$(SQLITE)-$(BUILD_TYPE) + tar xzf $< -C $(BUILD_DIR) + mv $(BUILD_DIR)/$(SQLITE_FNAME) $(BUILD_DIR)/$(SQLITE)-$(BUILD_TYPE) + cd $(BUILD_DIR)/$(SQLITE)-$(BUILD_TYPE); \ + ./configure --host=$(ANDROID_HOST) --build=$(ANDROID_BUILD) \ + --prefix=$(py_install_dir) \ + --disable-shared + $(MAKE) -C $(BUILD_DIR)/$(SQLITE)-$(BUILD_TYPE) all install + + +$(py_install_dir)/lib/libffi.a: $(BUILD_DIR)/$(LIBFFI).tar.gz + rm -rf $(BUILD_DIR)/$(LIBFFI)-$(BUILD_TYPE) + tar xzf $< -C $(BUILD_DIR) + cd $(BUILD_DIR)/$(LIBFFI); patch -p1 < $(CURDIR)/patches/libffi-mmap-exec.patch +ifeq ($(ANDROID_ARCH), armv7) + cd $(BUILD_DIR)/$(LIBFFI); patch -p1 < $(CURDIR)/patches/libffi-stmeqia.patch +endif + mv $(BUILD_DIR)/$(LIBFFI) $(BUILD_DIR)/$(LIBFFI)-$(BUILD_TYPE) + cd $(BUILD_DIR)/$(LIBFFI)-$(BUILD_TYPE); \ + ./configure --host=$(ANDROID_HOST) --build=$(ANDROID_BUILD) \ + --prefix=$(py_install_dir) \ + --disable-shared + -$(MAKE) -C $(BUILD_DIR)/$(LIBFFI)-$(BUILD_TYPE) all install install-pkgconfigDATA + +.PHONY: all libffi_header ncurses_header sqlite_header readline_header diff --git a/Android/external-libraries/Makefile.openssl b/Android/external-libraries/Makefile.openssl new file mode 100644 index 00000000000000..d8e05e1a01ca2f --- /dev/null +++ b/Android/external-libraries/Makefile.openssl @@ -0,0 +1,45 @@ +# Android/external-libraries/Makefile.openssl + +OPENSSL := openssl-1.1.0g + +# See openssl INSTALL and Configurations/10-main.conf files. +export ANDROID_NDK := $(ANDROID_NDK_ROOT) +export CROSS_SYSROOT := $(SYSROOT) +export CROSS_COMPILE := $(ANDROID_HOST)- + +# Append to the current PATH so that ccache remains first in PATH. +export PATH := $(PATH):$(TOOLCHAIN)/bin + +ifeq ($(ANDROID_ARCH), x86) + export OPENSSL_TARGET := android-x86 +else ifeq ($(ANDROID_ARCH), x86_64) + export OPENSSL_TARGET := android64 +else ifeq ($(ANDROID_ARCH), armv7) + export OPENSSL_TARGET := android-armeabi +else ifeq ($(ANDROID_ARCH), arm64) + export OPENSSL_TARGET := android64-aarch64 +endif + +BUILD_DIR := $(BUILD_DIR)/external-libraries +py_install_dir := $(PY_EXTDIR)/$(SYS_EXEC_PREFIX) +BUILD_OPENSSL := $(py_install_dir)/lib/libcrypto.so + +all: openssl_header $(BUILD_OPENSSL) +openssl_header: + @echo "---> Build the $(OPENSSL) external library." + +$(BUILD_DIR)/$(OPENSSL).tar.gz: + cd $(BUILD_DIR); \ + $(DOWNLOAD) https://www.openssl.org/source/$(OPENSSL).tar.gz + +$(BUILD_OPENSSL): $(BUILD_DIR)/$(OPENSSL).tar.gz + rm -rf $(BUILD_DIR)/$(OPENSSL)-$(BUILD_TYPE) + tar xzf $< + mv $(OPENSSL) $(BUILD_DIR)/$(OPENSSL)-$(BUILD_TYPE) + cd $(BUILD_DIR)/$(OPENSSL)-$(BUILD_TYPE); \ + ./Configure $(OPENSSL_TARGET) shared no-ssl3 no-engine \ + --prefix=$(py_install_dir) --openssldir=$(py_install_dir)/etc/ssl; \ + $(MAKE); \ + $(MAKE) install_sw install_ssldirs + +.PHONY: all openssl_header diff --git a/Android/external-libraries/patches/libffi-mmap-exec.patch b/Android/external-libraries/patches/libffi-mmap-exec.patch new file mode 100644 index 00000000000000..6c8b821d7c1f63 --- /dev/null +++ b/Android/external-libraries/patches/libffi-mmap-exec.patch @@ -0,0 +1,14 @@ +diff --git a/configure b/configure +index c6da467..1b176fc 100755 +--- a/configure ++++ b/configure +@@ -18530,7 +18530,8 @@ case "$target" in + $as_echo "#define FFI_EXEC_TRAMPOLINE_TABLE 1" >>confdefs.h + + ;; +- *-apple-darwin1* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris*) ++ *-apple-darwin1* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris* \ ++| *-linux-android*) + + $as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h + diff --git a/Android/external-libraries/patches/libffi-stmeqia.patch b/Android/external-libraries/patches/libffi-stmeqia.patch new file mode 100644 index 00000000000000..b6b3bff7bd71c1 --- /dev/null +++ b/Android/external-libraries/patches/libffi-stmeqia.patch @@ -0,0 +1,13 @@ +diff --git a/src/arm/sysv.S b/src/arm/sysv.S +index 541bbe9..c2b9f0e 100644 +--- a/src/arm/sysv.S ++++ b/src/arm/sysv.S +@@ -396,7 +396,7 @@ LSYM(Lbase_args): + beq LSYM(Lepilogue_vfp) + + cmp r3, #FFI_TYPE_SINT64 +- stmeqia r2, {r0, r1} ++ stmiaeq r2, {r0, r1} + beq LSYM(Lepilogue_vfp) + + cmp r3, #FFI_TYPE_FLOAT diff --git a/Android/external-libraries/patches/ncurses.patch b/Android/external-libraries/patches/ncurses.patch new file mode 100644 index 00000000000000..a8b83fd444c405 --- /dev/null +++ b/Android/external-libraries/patches/ncurses.patch @@ -0,0 +1,22 @@ +--- mk-1st.awk Mon Oct 24 14:25:54 2016 +0200 ++++ mk-1st.awk Mon Oct 24 14:27:10 2016 +0200 +@@ -173,7 +173,7 @@ + printf "\t$(MK_SHARED_LIB) $(%s_OBJS) $(%s) $(LDFLAGS)\n", objs, shlib_list + } + function sharedlinks(directory) { +- if ( ShlibVer != "auto" && ShlibVer != "cygdll" && ShlibVer != "msysdll" && ShlibVer != "mingw" ) { ++ if ( ShlibVer != "auto" && ShlibVer != "cygdll" && ShlibVer != "msysdll" && ShlibVer != "mingw" && DoLinks != "no") { + printf "\tcd %s && (", directory + if ( DoLinks == "reverse" ) { + if ( ShlibVer == "rel" ) { +diff -r fad0ecf7a896 ncurses/base/lib_screen.c +--- ncurses/base/lib_screen.c Wed Jan 11 14:23:13 2017 +0100 ++++ ncurses/base/lib_screen.c Wed Jan 11 14:25:22 2017 +0100 +@@ -859,6 +859,7 @@ + } + PUTS("\n"); + } ++ code = OK; + } + #else + /* diff --git a/Android/makesetup b/Android/makesetup new file mode 100755 index 00000000000000..785588c8f402e4 --- /dev/null +++ b/Android/makesetup @@ -0,0 +1,95 @@ +#! /bin/sh +# Build a Makefile that is used to build Python and run Python on the Android +# emulator. + +print_header () +{ + cat >> $makefile <<-EOF + EMULATOR_CMD_LINE_OPTIONS := $EMULATOR_CMD_LINE_OPTIONS + PYTHON_DEVPT_VERSION := $PYTHON_DEVPT_VERSION + + EOF +} + +print_footer () +{ + cat >> $makefile <<-EOF + + config_args := $@ + py_srcdir := $PY_SRCDIR + makefile := $makefile + # Cannot use \$CURDIR as, because of some distutils bug, the Makefile must + # also be run from the setup.py directory when building third-party + # extension modules. + MAKEFILE_DIR := \$(dir \$(abspath \$(lastword \$(MAKEFILE_LIST)))) + include \$(py_srcdir)/Android/build.mk + EOF +} + +list_ext_libraries () +{ + # Parse the WITH_LIBRARIES environment variable. + libs="" + if test -n "$WITH_LIBRARIES"; then + save_IFS=$IFS; IFS=',' + set $WITH_LIBRARIES + for opt; do + case $opt in + libffi) libs="$libs WITH_LIBFFI" ;; + ncurses) libs="$libs WITH_NCURSES" ;; + openssl) libs="$libs WITH_OPENSSL" ;; + readline) libs="$libs WITH_READLINE" ;; + sqlite) libs="$libs WITH_SQLITE" ;; + *) + echo "Error: '$opt' is an unknown library name." + echo "The available libraries are libffi, ncurses, openssl, readline and sqlite." + exit 2 ;; + esac + done + IFS=$save_IFS + else + libs="WITH_NCURSES WITH_READLINE" + fi + echo $libs +} + +# Source the Android variables. +PY_SRCDIR="$(cd "$(dirname "$0")/.." && pwd)" +. $PY_SRCDIR/Android/build-config + +defined_variables="APP_ABI CC AR LD RANLIB READELF STRIP \ + CPPFLAGS LDFLAGS CFLAGS" +exported_variables="ANDROID_NDK_ROOT ANDROID_SDK_ROOT TOOLCHAIN \ + ANDROID_API ANDROID_ARCH \ + ANDROID_HOST ANDROID_BUILD SYSROOT" +makefile=Makefile-android-$ANDROID_API-$ANDROID_ARCH +if test -f Makefile; then + test ! -h Makefile && build_config_error \ + "Refusing to remove the Makefile, this is not a symlink." +fi +rm -f Makefile +with_libraries=$(list_ext_libraries) + +# Build the Makefile. +cat > $makefile <> $makefile +done +for var in $defined_variables; do + eval value=\$$var + echo "$var := $value" >> $makefile +done +for var in $exported_variables; do + eval value=\$$var + echo "export $var := $value" >> $makefile +done +print_footer "$@" + +ln -s $makefile Makefile +echo "Makefile and $makefile built successfully." diff --git a/Android/resources/inputrc b/Android/resources/inputrc new file mode 100644 index 00000000000000..044bc33f1ce6de --- /dev/null +++ b/Android/resources/inputrc @@ -0,0 +1,12 @@ +# 'linux' terminal: +# Page up, page down. +"\e[5~": beginning-of-history +"\e[6~": end-of-history +# , . +"\e[1;5D": backward-word +"\e[1;5C": forward-word + +# , . +"\C-p":reverse-search-history +"\C-n":forward-search-history +set completion-query-items 100 diff --git a/Android/tools/android_utils.py b/Android/tools/android_utils.py new file mode 100644 index 00000000000000..eefa739753d273 --- /dev/null +++ b/Android/tools/android_utils.py @@ -0,0 +1,143 @@ +"""Android utils.""" + +import sys +import os +import time +import random +import subprocess +import socket +import errno +import argparse +from subprocess import PIPE, STDOUT + +class AndroidError(BaseException): pass + +_adb_cmd = None +def get_adb_cmd(): + global _adb_cmd + if _adb_cmd is None: + _adb_cmd = [os.environ['ADB'], + '-s', 'emulator-%s' % os.environ['CONSOLE_PORT']] + return _adb_cmd + +def is_emulator_listening(port): + """Check if an emulator is listening on 'port'.""" + try: + sock = socket.create_connection(('localhost', port)) + sock.close() + print('An emulator is currently listening on port %d.' % port, + file=sys.stderr) + return 1 + except OSError as e: + if e.errno == errno.ECONNREFUSED: + return 0 + raise + +def run_subprocess(*args, verbose=True): + print(' '.join(args)) + proc = subprocess.run(args, check=True, stdout=PIPE, stderr=PIPE) + if proc.stdout and verbose: + print(proc.stdout.decode()) + if proc.stderr and verbose: + print(proc.stderr.decode(), file=sys.stderr) + +_android_api = None +def get_android_api(): + global _android_api + if _android_api is None: + proc = subprocess.run(get_adb_cmd() + ['shell', + 'getprop ro.build.version.sdk'], + check=True, stdout=PIPE, stderr=PIPE) + _android_api = int(proc.stdout) + return _android_api + +MOUNT_SDCARD_TIME = 600 +KEY_MSG = 'random key found: ' +_first_invocation = True + +def adb_shell(cmd, wait_for_sdcard=False): + global _first_invocation + + if not (wait_for_sdcard and _first_invocation): + run_subprocess(*get_adb_cmd(), 'shell', cmd) + return + _first_invocation = False + + # Use a random key to find out if the shell command was successfull as the + # adb shell does not report the exit status of the executed command. + key = str(random.random()) + args = get_adb_cmd() + ['shell', + '{ %s; } && echo "%s%s"' % (cmd.strip(';'), KEY_MSG, key)] + key = key.encode(encoding='ascii') + + starttime = curtime = time.time() + stdout = '' + try: + while 1: + proc = subprocess.run(args, stdout=PIPE, stderr=STDOUT) + stdout = proc.stdout + rc = proc.returncode + if stdout is None: + stdout = '' + if ((rc == 0 and key in stdout) or + (rc != 0 and get_android_api() < 24) or + (rc != 0 and b'no devices/emulators found' in stdout)): + break + if curtime >= starttime + MOUNT_SDCARD_TIME: + break + + if curtime == starttime: + print('%s shell %s' % (' '.join(get_adb_cmd()), cmd), end='', + flush=True) + else: + print('.', end='', flush=True) + time.sleep(.500) + curtime = time.time() + except KeyboardInterrupt: + raise AndroidError('\nKeyboardInterrupt: adb_shell("%s") = %d <%s>' % + (cmd, rc, stdout)) + + if curtime == starttime: + print('%s shell %s' % (' '.join(get_adb_cmd()), cmd)) + + if stdout: + for line in stdout.decode().split('\n'): + if not line.startswith(KEY_MSG): + print(line.strip('\r')) + if not key in stdout or rc != 0: + raise AndroidError('Error: adb_shell("%s") = %d' % (cmd, rc)) + +def adb_push_to_dir(src, dest): + """Push src to a directory on Android.""" + print('Please wait: pushing %s to %s...' % (src, dest), flush=True) + run_subprocess(*get_adb_cmd(), 'push', src, dest, verbose=False) + +def adb_pull(remote, dest): + run_subprocess(*get_adb_cmd(), 'pull', remote, dest, verbose=False) + +def run_script(script_path): + """Push a script to the emulator and run it.""" + try: + bin_dir = os.path.join(os.environ['SYS_EXEC_PREFIX'], 'bin') + adb_push_to_dir(script_path, bin_dir) + path = os.path.join(bin_dir, os.path.basename(script_path)) + print() + print('Running %s' % path, flush=True) + subprocess.run(get_adb_cmd() + ['shell', 'sh %s' % path]) + finally: + try: + os.unlink(script_path) + except OSError: + pass + +def parse_prefixes(cmd_line): + """ Parse the prefixes of a configure command line.""" + parser = argparse.ArgumentParser() + parser.add_argument('--prefix') + parser.add_argument('--exec-prefix') + args, unknown = parser.parse_known_args(cmd_line.split()) + if args.prefix is None: + args.prefix = '/usr/local' + if args.exec_prefix is None: + args.exec_prefix = args.prefix + print(args.prefix, args.exec_prefix) diff --git a/Android/tools/install.py b/Android/tools/install.py new file mode 100644 index 00000000000000..7fe3bd7aea46bd --- /dev/null +++ b/Android/tools/install.py @@ -0,0 +1,47 @@ +"""Install Python.""" + +import sys +import os +import subprocess +import tempfile +from os.path import join, commonprefix + +from android_utils import (run_subprocess, adb_push_to_dir, AndroidError, + adb_shell) + +def adb_push_from_zip(zippath, sys_prefix): + with tempfile.TemporaryDirectory() as tmpdir: + run_subprocess('unzip', '-q', zippath, '-d', tmpdir) + + # On the first call to adb_push_to_dir() where 'dest' is a directory + # on the sdcard, the mkdir command is attempted until /sdcard is + # mounted read/write on emulator start up. + dest = sys_prefix + sdcard = '/sdcard' + wait = True if commonprefix([sdcard, dest]) == sdcard else False + adb_shell('mkdir -p %s' % dest, wait_for_sdcard=wait) + + src = join(tmpdir, sys_prefix[1:] if + os.path.isabs(sys_prefix) else sys_prefix) + adb_push_to_dir(src, os.path.split(dest)[0]) + +def install(): + stdlib_path = os.environ['STDLIB_DIR'] + + adb_push_from_zip(os.environ['PY_STDLIB_ZIP'], os.environ['SYS_PREFIX']) + adb_push_from_zip(os.environ['PYTHON_ZIP'], os.environ['SYS_EXEC_PREFIX']) + srcdir = os.environ.get('PY_SRCDIR') + if srcdir: + # Push the script run by buildbottest. + bin_dir = join(os.environ['SYS_EXEC_PREFIX'], 'bin') + adb_push_to_dir(join(srcdir, 'Tools/scripts/run_tests.py'), bin_dir) + +if __name__ == "__main__": + try: + install() + except subprocess.CalledProcessError as e: + print('CalledProcessError: Command %(cmd)s: stdout=<%(output)s> ' + 'stderr=<%(stderr)s>' % e.__dict__, file=sys.stderr) + sys.exit(1) + except AndroidError as e: + sys.exit(e) diff --git a/Android/tools/kill_emulator.py b/Android/tools/kill_emulator.py new file mode 100644 index 00000000000000..e025a0ac801092 --- /dev/null +++ b/Android/tools/kill_emulator.py @@ -0,0 +1,34 @@ +"""Kill the emulator.""" + +import sys +import os +import telnetlib + +def kill_emulator(port): + try: + with telnetlib.Telnet('localhost', port) as tn: + idx, _, bytes_read = tn.expect([b'Android Console'], timeout=5) + if idx != 0: + if bytes_read: + print(bytes_read, file=sys.stderr) + return 'Timed out, not an Android Console' + with open(os.path.expanduser('~/.emulator_console_auth_token'), + 'rb') as f: + token = f.read() + tn.write(b'auth ' + token + b'\n') + tn.write(b'kill\n') + print(tn.read_all().decode('ascii'), file=sys.stderr) + except OSError as e: + return e + +if __name__ == "__main__": + err = None + try: + port = int(sys.argv[1]) + except (ValueError, IndexError) as e: + err = e + else: + err = kill_emulator(port) + if err is not None: + print('Error: Cannot telnet to the Android Console: %s.' % err) + sys.exit(1) diff --git a/Android/tools/ndk_gdb.py b/Android/tools/ndk_gdb.py new file mode 100755 index 00000000000000..06aaccb53a9537 --- /dev/null +++ b/Android/tools/ndk_gdb.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python2 + +"""Wrapper around the Android ndk-gdb.py script.""" + +import sys +import os +import re +import importlib +import atexit +import tempfile +from os.path import join, basename + +pgm = basename(sys.argv[0]) +if sys.version_info >= (3,): + raise NotImplementedError('%s does not run on Python3.' % pgm) + +# Import the Android ndk-gdb module. +opsys_info = os.uname() +sys.path.append(join(os.environ['ANDROID_NDK_ROOT'], 'prebuilt', + '%s-%s' % (opsys_info[0].lower(), opsys_info[4]), + 'bin')) +ndk_gdb = importlib.import_module('ndk-gdb') +_generate_gdb_script = ndk_gdb.generate_gdb_script + +def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, + target_pid, run_cmd, debug_socket, port, user=None): + """Start gdbserver in the background and forward necessary ports.""" + + assert target_pid is None or run_cmd is None + + # Push gdbserver to the target. + if gdbserver_local_path is not None: + device.push(gdbserver_local_path, gdbserver_remote_path) + + # start_gdbserver() is monkey-patched because, starting with API level 24, + # the 'shell' user cannot bind to a unix socket anymore so we use a tcp + # socket instead since 'adbd', the daemon running on the emulator that + # does the port forwarding, runs as a 'shell' user. + debug_socket = '5040' + gdbserver_cmd = [gdbserver_remote_path, "--once", + ":{}".format(debug_socket)] + + if target_pid is not None: + gdbserver_cmd += ["--attach", str(target_pid)] + else: + gdbserver_cmd += run_cmd + + device.forward("tcp:{}".format(port), "tcp:{}".format(debug_socket)) + atexit.register(lambda: device.forward_remove("tcp:{}".format(port))) + gdbserver_cmd = ["su", "0"] + gdbserver_cmd + + gdbserver_output_path = join(tempfile.gettempdir(), "gdbclient.log") + print("Redirecting gdbserver output to {}".format(gdbserver_output_path)) + gdbserver_output = file(gdbserver_output_path, 'w') + return device.shell_popen(gdbserver_cmd, stdout=gdbserver_output, + stderr=gdbserver_output) + +def find_project(args): + return join(os.environ['DIST_DIR'], 'gdb', + 'android-%(ANDROID_API)s-%(ANDROID_ARCH)s' % os.environ) + +def get_app_data_dir(args, package_name): + return os.environ['SYS_EXEC_PREFIX'] + +def get_gdbserver_path(args, package_name, app_data_dir, arch): + app_gdbserver_path = "{}/lib/gdbserver".format(app_data_dir) + cmd = ["ls", app_gdbserver_path, "2>/dev/null"] + cmd = ndk_gdb.gdbrunner.get_run_as_cmd(package_name, cmd) + (rc, _, _) = args.device.shell_nocheck(cmd) + if rc == 0: + ndk_gdb.log("Found app gdbserver: {}".format(app_gdbserver_path)) + return app_gdbserver_path + + # We need to upload our gdbserver + ndk_gdb.log("App gdbserver not found at {}, uploading.".format(app_gdbserver_path)) + local_path = "{}/prebuilt/android-{}/gdbserver/gdbserver" + local_path = local_path.format(ndk_gdb.NDK_PATH, arch) + remote_path = "/data/local/tmp/{}-gdbserver".format(arch) + args.device.push(local_path, remote_path) + + # get_run_as_cmd() is monkey-patched to avoid copying gdbserver to the + # data directory: '"run-as", package_name' fails because package_name is + # not a valid application on the device. + + ndk_gdb.log("Uploaded gdbserver to {}".format(remote_path)) + return remote_path + +_make_variables = { + 'APP_ABI': os.environ['APP_ABI'], + 'TARGET_OUT': ('./obj/local/%s' % os.environ['ANDROID_ARCH']), + 'APP_STL': 'system', +} +def dump_var(args, variable, abi=None): + return _make_variables[variable] + +_py_srcdir = None +_SOLIB_SEARCH_PATH = r'^\s*(?Pset\s+solib-search-path)\s*(?P\S+)\s*$' +_re_solibpath = re.compile(_SOLIB_SEARCH_PATH) + +def generate_gdb_script(args, sysroot, binary_path, app_64bit, + connect_timeout=5): + assert _py_srcdir is not None + gdb_commands = _generate_gdb_script(args, sysroot, binary_path, app_64bit) + cmds = gdb_commands.split('\n') + + # Add Python symbols to solib_search_path. + for idx, line in enumerate(cmds): + matchobj = _re_solibpath.match(line) + if matchobj: + cmds[idx] = ('set solib-search-path %s:%s:%s' % + (matchobj.group('arg'), os.environ['PY_HOST_DIR'], + os.environ['LIB_DYNLOAD'])) + break + + if os.environ.get('GDB_PYTHON') == 'yes': + cmds.append('python import sys; ' + + 'sys.path.append("%s/Tools/gdb"); ' % _py_srcdir + + 'import libpython') + + if os.environ.get('GDB_LOGGING') == 'yes': + cmds.append('set logging file gdb.log') + cmds.append('set logging overwrite off') + cmds.append('set logging on') + + # Workaround SIGILL in __dl_notify_gdb_of_libraries when a library is + # loaded on armv7. + if os.environ.get('GDB_SIGILL') == 'yes': + cmds.append('break __dl_rtld_db_dlactivity') + cmds.append('commands') + cmds.append('silent') + cmds.append('return') + cmds.append('sharedlibrary') + cmds.append('continue') + cmds.append('end') + + cmds.append('directory %s' % _py_srcdir) + return '\n'.join(cmds) + +def main(api_level, src_dir, pgm_name): + global _py_srcdir + _py_srcdir = src_dir + + # Monkey-patch the ndk-gdb module. + ndk_gdb.find_project = find_project + ndk_gdb.get_app_data_dir = get_app_data_dir + ndk_gdb.dump_var = dump_var + ndk_gdb.generate_gdb_script = generate_gdb_script + ndk_gdb.get_gdbserver_path = get_gdbserver_path + ndk_gdb.gdbrunner.start_gdbserver = start_gdbserver + + sys.argv = [pgm, '--verbose', '--adb', os.environ['ADB'], + '--attach', pgm_name, '--stdcxx-py-pr', 'none'] + ndk_gdb.main() + +def usage(): + print('Usage: %s api_level py_srcdir program_name_to_attach_to' % pgm) + sys.exit(2) + +if __name__ == "__main__": + if len(sys.argv) != 4: + usage() + main(*sys.argv[1:]) diff --git a/Android/tools/nl_langinfo.sh b/Android/tools/nl_langinfo.sh new file mode 100755 index 00000000000000..42aeb8bfe13be0 --- /dev/null +++ b/Android/tools/nl_langinfo.sh @@ -0,0 +1,29 @@ +# Test that langinfo.h is broken as expected. + +cat > nl_langinfo.c < + +void foo() +{ + nl_langinfo(1); +} +EOF + +# XXX This is broken on systems where the Python source dir is read-only. +. $PY_SRCDIR/Android/build-config +if ! $CC $CFLAGS -Werror=implicit-function-declaration -c nl_langinfo.c; then + rc=$? + echo "langinfo.h is broken as expected." >&2 +else + rc=$? + echo "Assertion error: langinfo.h is not broken in this new version of the NDK." + echo "Please update Include/pyport.h and revert the changes made in issue 28596." +fi +rm -f nl_langinfo.c nl_langinfo.o +exit $rc diff --git a/Android/tools/python_shell.py b/Android/tools/python_shell.py new file mode 100644 index 00000000000000..1596414b581aca --- /dev/null +++ b/Android/tools/python_shell.py @@ -0,0 +1,124 @@ +"""Python shell on Android. + +When python_shell.py is run without parameters, build a shell script, install it +with adb on Android and print a message explaining how to source the script in +order to set the environment variables. +When run with parameters, add to the shell script a statement to run python +with those parameters, install the script with adb and run the script on +Android in an adb shell session. +""" + +import sys +import os +import string +import textwrap +import shutil +import subprocess +import tempfile + +from android_utils import (adb_push_to_dir, run_script, AndroidError, + adb_pull) + +# Maximum file name size on nearly all current file systems. +MAX_FNAME_SIZE = 255 +USAGE = """ +=========================================================================== + +Set the environment by sourcing python_shell.sh with the following command: + '. ${SYS_EXEC_PREFIX}/bin/python_shell.sh' + +=========================================================================== + +""" + +def build_script(argv=None, result_fname=None): + script = """ + # Set the environment variables and change the current directory to + # SYS_EXEC_PREFIX. + export HOME=$SYS_EXEC_PREFIX + export PATH=$SYS_EXEC_PREFIX/bin:$$PATH + export LD_LIBRARY_PATH=$SYS_EXEC_PREFIX/lib + export TMPDIR=$SYS_EXEC_PREFIX/tmp + mkdir -p $$TMPDIR + export TERM=linux + export TERMINFO=$SYS_EXEC_PREFIX/share/terminfo + export INPUTRC=$SYS_EXEC_PREFIX/etc/inputrc + cd $SYS_EXEC_PREFIX + + """ + script = textwrap.dedent(script) + if argv is None: + script_name = 'python_shell.sh' + + # The adb shell starts up with an annoying 80 characters width, use + # the current terminal width instead if available. + os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns) + script += '# Set the terminal width.\n' + script += 'export COLUMNS=$COLUMNS\n' + else: + args = ''.join(map(lambda c: c if c.isalnum() else '_', + '_'.join(argv))) + s = 'python_%s.sh' % args + l = len(s) + if l > MAX_FNAME_SIZE: + slice = MAX_FNAME_SIZE // 2 - 2 + s = s[:slice] + '____' + s[l - slice:] + script_name = s + if result_fname is not None: + script += 'mkdir -p tmp\n' + script += '> tmp/%s\n' % result_fname + script += 'python %s\n' % ' '.join(argv) + if result_fname is not None: + script += 'echo -n $$? > tmp/%s\n' % result_fname + + script_path = os.path.join(os.environ['DIST_DIR'], script_name) + with open(script_path, 'w') as f: + f.write(string.Template(script).substitute(os.environ)) + os.chmod(script_path, 0o775) + return script_path + +def main(): + # The adb shell is mksh (The MirBSD Korn Shell) and it would be possible + # to update its configuration file at /system/etc/mkshrc on Android to add + # those environment variables by remounting /system read-write when root + # access rights are available. It is more robust to ask the user to source + # python_shell.sh instead. + # See + # http://stackoverflow.com/questions/11950131/android-adb-shell-ash-or-ksh. + if len(sys.argv) == 1: + script_path = build_script() + bin_dir = os.path.join(os.environ['SYS_EXEC_PREFIX'], 'bin') + adb_push_to_dir(script_path, bin_dir) + print(string.Template(USAGE).substitute(os.environ)) + return 0 + + try: + fd, fn = tempfile.mkstemp() + os.close(fd) + basename = os.path.split(fn)[1] + script_path = build_script(sys.argv[1:], basename) + run_script(script_path) + adb_pull(os.path.join(os.environ['SYS_EXEC_PREFIX'], + 'tmp', basename), fn) + with open(fn) as f: + result = f.read() + if not result: + raise AndroidError('Error: no return code from %s' % + os.path.basename(script_path)) + return int(result) + finally: + try: + os.unlink(fn) + except OSError: + pass + +if __name__ == "__main__": + try: + st = main() + except subprocess.CalledProcessError as e: + print('CalledProcessError: Command %(cmd)s: stdout=<%(output)s> ' + 'stderr=<%(stderr)s>' % e.__dict__, file=sys.stderr) + st = 1 + except AndroidError as e: + st = e + sys.exit(st) diff --git a/Android/tools/start_emulator.py b/Android/tools/start_emulator.py new file mode 100644 index 00000000000000..7b6366f97c4230 --- /dev/null +++ b/Android/tools/start_emulator.py @@ -0,0 +1,26 @@ +"""Start the emulator if not already running.""" + +import sys +import os +import argparse + +from android_utils import run_subprocess, is_emulator_listening + +def start_emulator(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--avd', required=True) + parser.add_argument('--port', type=int, required=True) + # Double the '-' prefix for the processing by argparse. + args, unknown = parser.parse_known_args( + [('-' + arg if arg.startswith('-') else arg) + for arg in argv]) + + if not is_emulator_listening(args.port): + # Start the emulator. + emulator = os.path.join(os.environ['ANDROID_SDK_ROOT'], 'emulator', + 'emulator') + run_subprocess(emulator, *argv) + return 0 + +if __name__ == "__main__": + sys.exit(start_emulator(sys.argv[1:])) diff --git a/configure-android b/configure-android new file mode 100755 index 00000000000000..142d5cf0f456cb --- /dev/null +++ b/configure-android @@ -0,0 +1,34 @@ +#! /bin/sh +# A wrapper around the Python configure script to set the Android build +# variables and invoke configure. + +# Source the Android variables. +PY_SRCDIR="$(cd "$(dirname "$0")" && pwd)" +. $PY_SRCDIR/Android/build-config + +# The 'ac_cv_little_endian_double=yes' configure options have been verified by +# running the following Python configure.ac code snippet on each Android +# architecture and examining its exit status: +# #include +# int main() { +# double x = 9006104071832581.0; +# if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0) +# return 0; +# else +# return 1; +# } +case "$ANDROID_ARCH" in + x86*|arm*) + little_endian_double="ac_cv_little_endian_double=yes" + ;; +esac + +# Run Python configure. +$PY_SRCDIR/configure \ + CC="$CC" AR="$AR" LD="$LD" RANLIB="$RANLIB" READELF="$READELF" \ + CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS" \ + --host=$ANDROID_HOST --build=$ANDROID_BUILD --enable-shared \ + --enable-ipv6 ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no \ + --without-ensurepip \ + $little_endian_double \ + "$@"