diff --git a/.github/workflows/adb-emu.sh b/.github/workflows/adb-emu.sh new file mode 100755 index 000000000..d5e849f97 --- /dev/null +++ b/.github/workflows/adb-emu.sh @@ -0,0 +1,9 @@ +#!/bin/sh +adb push "$1" /data/local/tmp/ 1>/dev/null 2>/dev/null +if [ $# -eq 1 ]; then + adb shell /data/local/tmp/$(basename $1) +elif [ $# -eq 3 ]; then + adb push "$2" /data/local/tmp/ 1>/dev/null 2>/dev/null + adb shell /data/local/tmp/$(basename $1) /data/local/tmp/$(basename $2) /data/local/tmp/$(basename $3) + adb pull /data/local/tmp/$(basename $3) "$3" 1>/dev/null 2>/dev/null +fi diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..a6652e95b --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,88 @@ +name: "Android bulds" + +on: [push, pull_request] + +jobs: + build-android: + name: ${{ matrix.ndk-arch }} on ${{ matrix.arch }} Android + runs-on: macos-11 + strategy: + matrix: + ndk-arch: [x86_64] + arch: [x86_64] + api-level: [21] + target: [default] + include: + - ndk-arch: arm64-v8a + arch: x86_64 + api-level: 30 + target: google_apis + - ndk-arch: x86 + arch: x86 + api-level: 21 + target: default + - ndk-arch: armeabi-v7a + arch: x86_64 + api-level: 30 + target: google_apis + + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v2 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + ~/.android/debug.keystore + key: avd-${{ matrix.api-level }}-${{ matrix.target }}-${{ matrix.arch }} + + - name: run emulator to generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + profile: Galaxy Nexus + cores: 2 + sdcard-path-or-size: 100M + emulator-build: 7425822 # https://github.com/ReactiveCircus/android-emulator-runner/issues/160 + avd-name: test + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + working-directory: ./ + ndk: 21.0.6113669 + cmake: 3.10.2.4988404 + script: echo "Generated AVD snapshot for caching." + + - name: run action + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + profile: Galaxy Nexus + cores: 2 + ram-size: 2048M + sdcard-path-or-size: 100M + emulator-build: 7425822 # https://github.com/ReactiveCircus/android-emulator-runner/issues/160 + avd-name: test + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + working-directory: ./ + ndk: 21.0.6113669 + cmake: 3.10.2.4988404 + script: | + # ls -lR /Users/runner/Library/Android/sdk + adb devices + mkdir ../build + mkdir ../install-prefix + cd ../build + cmake -DCMAKE_INSTALL_PREFIX:PATH=../../install-prefix -DCMAKE_TOOLCHAIN_FILE=/Users/runner/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_ABI=${{ matrix.ndk-arch }} -DCMAKE_CROSSCOMPILING_EMULATOR=/Users/runner/work/python-cmake-buildsystem/python-cmake-buildsystem/.github/workflows/adb-emu.sh -DANDROID_ALLOW_UNDEFINED_SYMBOLS=On -DENABLE_DECIMAL=Off -DENABLE_CTYPES=Off -DENABLE_CODECS_JP=OFF -DENABLE_CODECS_KR=OFF -DENABLE_CODECS_TW=OFF -DENABLE_MULTIBYTECODEC=OFF -DENABLE_CODECS_CN=OFF -DENABLE_CODECS_HK=OFF -DENABLE_CODECS_ISO2022=OFF -DBUILD_EXTENSIONS_AS_BUILTIN=On -DANDROID_PLATFORM=android-21 ../python-cmake-buildsystem/ + cmake --build . -- VERBOSE=1 + cmake --build . --target install diff --git a/CMakeLists.txt b/CMakeLists.txt index be183d767..d97fdd916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -718,7 +718,7 @@ if(UNIX) # Makefile set(MAKEFILE_LDSHARED_FLAGS "-shared") - if(APPLE) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(MAKEFILE_LDSHARED_FLAGS "-dynamiclib -headerpad_max_install_names -undefined dynamic_lookup") endif() configure_file(cmake/makefile-variables.in diff --git a/README.rst b/README.rst index e59bb43d2..badef9693 100644 --- a/README.rst +++ b/README.rst @@ -255,6 +255,32 @@ options on the commandline with `-DOPTION=VALUE`, or use the "ccmake" gui. Then, this variable is used to initialize `CMAKE_OSX_SYSROOT`, `CMAKE_OSX_DEPLOYMENT_TARGET` and `MACOSX_DEPLOYMENT_TARGET` variables. +Cross-compiling for Android from Linux (unsupported) +.................................................... + +To build Python with Android NDK set up emulator, toolchain and ABI (see `Android CMake +Guide `_). + +.. code:: bash + + # Unix + cmake -DCMAKE_INSTALL_PREFIX:PATH=${HOME}/scratch/python-install -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_CROSSCOMPILING_EMULATOR=adb-emu.sh -DANDROID_ALLOW_UNDEFINED_SYMBOLS=On -DENABLE_DECIMAL=Off -DENABLE_CTYPES=Off -DBUILD_EXTENSIONS_AS_BUILTIN=On -DANDROID_PLATFORM=21 ../python-cmake-buildsystem + +adb-emu.sh sends executable configuration files and launches them on connected device or launched +emulator. Ensure device or emulator have same architecture you builds python: + +.. code:: bash + + #!/bin/sh + adb push "$1" /data/local/tmp/ 1>/dev/null 2>/dev/null + if [ $# -eq 1 ]; then + adb shell /data/local/tmp/$(basename $1) + elif [ $# -eq 3 ]; then + adb push "$2" /data/local/tmp/ 1>/dev/null 2>/dev/null + adb shell /data/local/tmp/$(basename $1) /data/local/tmp/$(basename $2) /data/local/tmp/$(basename $3) + adb pull /data/local/tmp/$(basename $3) "$3" 1>/dev/null 2>/dev/null + fi + Licenses -------- diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index a57ee8315..38b9a3596 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -270,7 +270,12 @@ check_include_files(grp.h HAVE_GRP_H) check_include_files(ieeefp.h HAVE_IEEEFP_H) check_include_files(inttypes.h HAVE_INTTYPES_H) # libffi and cpython check_include_files(io.h HAVE_IO_H) -check_include_files(langinfo.h HAVE_LANGINFO_H) +if (${CMAKE_SYSTEM_NAME} MATCHES "^Android") + set(HAVE_LANGINFO_H 0) # Android cann't link functions from langinfo.h +else() + check_include_files(langinfo.h HAVE_LANGINFO_H) +endif() + check_include_files(libintl.h HAVE_LIBINTL_H) check_include_files(libutil.h HAVE_LIBUTIL_H) check_include_files(linux/tipc.h HAVE_LINUX_TIPC_H) @@ -436,6 +441,11 @@ if(CMAKE_SYSTEM MATCHES "VxWorks\\-7$") set(HAVE_LIBUTIL 0) endif() +if(CMAKE_SYSTEM MATCHES "^Android") + set(LIBUTIL_EXPECTED 0) + set(HAVE_LIBUTIL 0) +endif() + if(LIBUTIL_EXPECTED) check_function_exists("openpty" HAVE_BUILTIN_OPENPTY) if(HAVE_BUILTIN_OPENPTY) @@ -1084,60 +1094,38 @@ endif() # Check for various properties of floating point # ####################################################################### - -# Check whether C doubles are little-endian IEEE 754 binary64 -set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_little_endian_double.c) -file(WRITE ${check_src} "#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; -} -") -python_platform_test_run( - DOUBLE_IS_LITTLE_ENDIAN_IEEE754 - "Checking whether C doubles are little-endian IEEE 754 binary64" - ${check_src} - DIRECT - ) - -# Check whether C doubles are big-endian IEEE 754 binary64 set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_big_endian_double.c) -file(WRITE ${check_src} "#include -int main() { - double x = 9006104071832581.0; - if (memcmp(&x, \"\\x43\\x3f\\xff\\x01\\x02\\x03\\x04\\x05\", 8) == 0) - return 0; - else - return 1; -} +file(WRITE ${check_src} " +double d = 90904234967036810337470478905505011476211692735615632014797120844053488865816695273723469097858056257517020191247487429516932130503560650002327564517570778480236724525140520121371739201496540132640109977779420565776568942592.0; ") -python_platform_test_run( - DOUBLE_IS_BIG_ENDIAN_IEEE754 - "Checking whether C doubles are big-endian IEEE 754 binary64" - ${check_src} - DIRECT - ) -# Check whether C doubles are ARM mixed-endian IEEE 754 binary64 -set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_mixed_endian_double.c) -file(WRITE ${check_src} "#include -int main() { - double x = 9006104071832581.0; - if (memcmp(&x, \"\\x01\\xff\\x3f\\x43\\x05\\x04\\x03\\x02\", 8) == 0) - return 0; - else - return 1; -} -") -python_platform_test_run( - DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 - "Checking doubles are ARM mixed-endian IEEE 754 binary64" - ${check_src} - DIRECT - ) +# TODO: factorize this try_compile statement +try_compile(DOUBLE_BIG_ENDIAN_TEST_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} + ${check_src} + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}" + "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" + COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/double_big_endian.bin) + +if(DOUBLE_BIG_ENDIAN_TEST_COMPILED) + file(READ ${CMAKE_CURRENT_BINARY_DIR}/double_big_endian.bin DOUBLE_BIG_ENDIAN_DATA) + string(FIND ${DOUBLE_BIG_ENDIAN_DATA} "noonsees" NOONSEES) + if(NOONSEES) + set(DOUBLE_IS_BIG_ENDIAN_IEEE754 1) + set(DOUBLE_IS_LITTLE_ENDIAN_IEEE754 0) + else() + string(FIND ${DOUBLE_BIG_ENDIAN_DATA} "seesnoon" SEESNOON) + if(SEESNOON) + set(DOUBLE_IS_BIG_ENDIAN_IEEE754 0) + set(DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1) + else() + message(WARNING "Could not determine if double precision floats endianness") + endif() + endif() +endif() # The short float repr introduced in Python 3.1 requires the # correctly-rounded string <-> double conversion functions from diff --git a/cmake/Extensions.cmake b/cmake/Extensions.cmake index d9adc80b1..8ea59607b 100644 --- a/cmake/Extensions.cmake +++ b/cmake/Extensions.cmake @@ -196,7 +196,7 @@ function(add_python_extension name) ) endif() - if(APPLE) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set_target_properties(${target_name} PROPERTIES LINK_FLAGS -Wl,-undefined,dynamic_lookup SUFFIX .so diff --git a/cmake/extensions/CMakeLists.txt b/cmake/extensions/CMakeLists.txt index a66b833ad..ab9253e32 100644 --- a/cmake/extensions/CMakeLists.txt +++ b/cmake/extensions/CMakeLists.txt @@ -164,8 +164,10 @@ if(PY_VERSION VERSION_GREATER_EQUAL "3.9") endif() # UNIX-only extensions -add_python_extension(fcntl REQUIRES UNIX SOURCES fcntlmodule.c) -add_python_extension(grp REQUIRES UNIX SOURCES grpmodule.c) +if(NOT CMAKE_SYSTEM MATCHES Android) + add_python_extension(fcntl REQUIRES UNIX SOURCES fcntlmodule.c) + add_python_extension(grp REQUIRES UNIX SOURCES grpmodule.c) +endif() set(nis_REQUIRES UNIX HAVE_LIBNSL) set(nis_LIBRARIES ${HAVE_LIBNSL}) @@ -500,7 +502,7 @@ elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 8) elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 4) if(HAVE_GCC_ASM_FOR_X87 AND (CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - AND NOT CMAKE_SYSTEM MATCHES SunOS + AND NOT CMAKE_SYSTEM MATCHES SunOS AND NOT CMAKE_SYSTEM MATCHES Android ) # solaris: problems with register allocation. # icc >= 11.0 works as well. diff --git a/cmake/lib/CMakeLists.txt b/cmake/lib/CMakeLists.txt index 82e8ec89c..d951bc89f 100644 --- a/cmake/lib/CMakeLists.txt +++ b/cmake/lib/CMakeLists.txt @@ -43,20 +43,22 @@ endif() # Generate grammar tables in install directory # XXX Should a custom target be added to generate file at built time ? -install(CODE "find_program( - PYTHON_EXECUTABLE python - HINTS \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${BIN_INSTALL_DIR} - NO_DEFAULT_PATH) -set(wrapper) -if(UNIX) - set(_envvar LD_LIBRARY_PATH) - if(APPLE) - set(_envvar DYLD_LIBRARY_PATH) +if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "^Android") + install(CODE "find_program( + PYTHON_EXECUTABLE python + HINTS \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${BIN_INSTALL_DIR} + NO_DEFAULT_PATH) + set(wrapper) + if(UNIX) + set(_envvar LD_LIBRARY_PATH) + if(APPLE) + set(_envvar DYLD_LIBRARY_PATH) + endif() + set(wrapper env \${_envvar}=\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${LIBPYTHON_LIBDIR}) endif() - set(wrapper env \${_envvar}=\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${LIBPYTHON_LIBDIR}) + execute_process(COMMAND \${wrapper} \${PYTHON_EXECUTABLE} -m lib2to3.pgen2.driver + \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PYTHONHOME}/lib2to3/Grammar.txt) + execute_process(COMMAND \${wrapper} \${PYTHON_EXECUTABLE} -m lib2to3.pgen2.driver + \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PYTHONHOME}/lib2to3/PatternGrammar.txt) + ") endif() -execute_process(COMMAND \${wrapper} \${PYTHON_EXECUTABLE} -m lib2to3.pgen2.driver - \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PYTHONHOME}/lib2to3/Grammar.txt) -execute_process(COMMAND \${wrapper} \${PYTHON_EXECUTABLE} -m lib2to3.pgen2.driver - \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PYTHONHOME}/lib2to3/PatternGrammar.txt) -") diff --git a/cmake/platform.c b/cmake/platform.c index 4c8278967..7530a33e3 100644 --- a/cmake/platform.c +++ b/cmake/platform.c @@ -18,16 +18,28 @@ char *PLATFORM_TRIPLET = #if defined(__linux__) # if defined(__x86_64__) && defined(__LP64__) +# if defined(__ANDROID_NDK__) + "x86_64-linux-android" +# else "x86_64-linux-gnu" +# endif # elif defined(__x86_64__) && defined(__ILP32__) "x86_64-linux-gnux32" # elif defined(__i386__) +# if defined(__ANDROID_NDK__) + "i686-linux-android" +# else "i386-linux-gnu" +# endif # elif defined(__aarch64__) && defined(__AARCH64EL__) # if defined(__ILP32__) "aarch64_ilp32-linux-gnu" # else +# if defined(__ANDROID_NDK__) + "aarch64-linux-android" +# else "aarch64-linux-gnu" +# endif # endif # elif defined(__aarch64__) && defined(__AARCH64EB__) # if defined(__ILP32__) @@ -45,7 +57,11 @@ char *PLATFORM_TRIPLET = # endif # elif defined(__ARM_EABI__) && !defined(__ARM_PCS_VFP) # if defined(__ARMEL__) +# if defined(__ANDROID_NDK__) + "arm-linux-androideabi" +# else "arm-linux-gnueabi" +# endif # else "armeb-linux-gnueabi" # endif diff --git a/cmake/python/CMakeLists.txt b/cmake/python/CMakeLists.txt index 7850be5ae..d6175b4e6 100644 --- a/cmake/python/CMakeLists.txt +++ b/cmake/python/CMakeLists.txt @@ -50,7 +50,7 @@ if(WIN32) ) endif() -if(UNIX AND PY_VERSION VERSION_GREATER "2.7.4") +if(UNIX AND PY_VERSION VERSION_GREATER "2.7.4" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "^Android") # Setup landmark allowing to run the interpreter from a build tree. See 'getpath.c' for details. set(_sysconfigdata_py "_sysconfigdata.py") if("${PY_VERSION}" VERSION_GREATER_EQUAL "3.6.0")