已知资料:

http://www.srplab.com/cn/files/others/compile/cross_compiling_python_for_android.html Cross Compiling Python for Android

https://m.2cto.com/kf/201511/448789.html 在arm上使用python-2.7.10

上述资料主要是在移植python2.7版本,具体移植python3的版本资料并不多。

起初打算移植python3.5.6版本,但是发现python3.5.6的移植性似乎不好,在解决完python主程序和libpython3.5.so的编译后,所有扩展均因为各种找不到c函数或者找不到python的对象或函数名之类的问题无法编译。后决定编译python3.7.1版本。记录如下。

编译记录:

源码从python.org下载。

首先编译Linux版本的Python3.7.1并安装。我选择安装到~/opt目录下,以加入环境变量的形式替代系统使用的python3.5。命令如下:

~/projects tar xvf ~/Download/Python-3.7.1.tar.xz -C .

~/projects cd Python-3.7.1

~/projects/Python-3.7.1 mkdir build.pc

~/projects/Python-3.7.1 cd build.pc

~/projects/Python-3.7.1/build.pc ../configure --prefix=/home/xys/opt/Python-3.7.1

~/projects/Python-3.7.1/build.pc make

~/projects/Python-3.7.1/build.pc make install

将python加入环境变量。由于我同时使用bash和zsh,于是做了两个动作:

首先建立公共的rc脚本~/rc.public,加入

#!/bin/sh

# python

export PATH=/home/xys/opt/Python-3.7.1/bin:$PATH

然后在~/.zshrc中加入

# include ~/rc.public

if [ -f ~/rc.public ]; then

. ~/rc.public

fi

bashrc类似,我还没加。

之后可以在终端输入python自动补全中发现python3.7程序。

编译安卓版本。以32位为例,

#!/bin/bash

# 使用android-ndk-r10e,其目录设置环境变量ANDROID_NDK。

# 我将当前目录定为环境变量

WORKSPACE=`pwd`

if [[ -z "$ANDROID_NDK" ]]; then

ANDROID_NDK=/opt/local/android-ndk-r10e

fi

ANDROID_SYSROOT=$ANDROID_NDK/platforms/android-21/arch-arm

ANDROID64_SYSROOT=$ANDROID_NDK/platforms/android-21/arch-arm64

cd $WORKSPACE

echo "building with $CMAKE_SYSTEM, processor=$CMAKE_SYSTEM_PROCESSOR, pwd=$WORKSPACE"

# generate Makefile

if [[ "$CMAKE_SYSTEM_PROCESSOR" =~ "x86" ]]; then

# x86 or x86_64

mkdir -p build.pc

cd build.pc

../configure --enable-shared --enable-ipv6 --prefix=$WORKSPACE/out/x86

elif [[ "$CMAKE_SYSTEM_PROCESSOR" =~ "armv7" ]]; then

# 64位时这里要修改为$ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin

export PATH=$ANDROID_NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:$ANDROID_NDK:$ANDROID_NDK/tools:/usr/local/bin:/usr/bin:/bin:$PATH

# 64位时这里要修改为"aarch64-linux-android-"

CROSS_COMPILER_PREFIX="arm-linux-androideabi-"

# 64位时要改成aarch64

export ARCH="armeabi"

# android 4.4及之后版本有要求可执行程序为PIE程序

export CC="${CROSS_COMPILER_PREFIX}gcc --sysroot=$ANDROID_SYSROOT -pie -fPIE"

export CXX="${CROSS_COMPILER_PREFIX}g++ --sysroot=$ANDROID_SYSROOT -pie -fPIE"

export CPP="${CROSS_COMPILER_PREFIX}gcc -E --sysroot=$ANDROID_SYSROOT -pie -fPIE"

export AS="${CROSS_COMPILER_PREFIX}as"

export LD="${CROSS_COMPILER_PREFIX}ld --sysroot=$ANDROID_SYSROOT -pie -fPIE"

export GDB="${CROSS_COMPILER_PREFIX}gdb"

export STRIP="${CROSS_COMPILER_PREFIX}strip"

export RANLIB="${CROSS_COMPILER_PREFIX}ranlib"

export OBJCOPY="${CROSS_COMPILER_PREFIX}objcopy"

export OBJDUMP="${CROSS_COMPILER_PREFIX}objdump"

export AR="${CROSS_COMPILER_PREFIX}ar"

export NM="${CROSS_COMPILER_PREFIX}nm"

export READELF="${CROSS_COMPILER_PREFIX}readelf"

export M4=m4

export TARGET_PREFIX=$CROSS_COMPILER_PREFIX

export CONFIG_SITE="config.site"

# In python 3.7.1, we must define "__ANDROID_API__", we set the value=21

export CFLAGS="-D__ANDROID_API__=21"

export CXXFLAGS="-D__ANDROID_API__=21"

export CPPFLAGS="-D__ANDROID_API__=21"

# export STRIP="${CROSS_COMPILER_PREFIX}strip --strip-unneeded"

mkdir -p build/android

cd build/android

echo -e "ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no" > config.site

# 64位时--host要改成aarch64-linux,prefix和上面的builddir随便

../../configure LDFLAGS="-Wl,--allow-shlib-undefined" --host=arm-linux --build=x86_64-linux-gnu --enable-shared --prefix=$WORKSPACE/out/android --enable-ipv6

编译过程中几乎只有一处c代码报错:

../Modules/posixmodule.c:6903:9: error: implicit declaration of function "wait3" [-Werror=implicit-function-declaration]

pid = wait3(&status, options, &ru);

^

进入代码,补充wait3的定义。(因为ndk提供的接口中没有公开wait3函数调用,但是安卓的bionic libc中有包含这个调用)

#if defined(HAVE_SYS_RESOURCE_H)

#include /resource.h>

#endif

+#ifdef __ANDROID_API__

+

+# ifndef wait3

+ extern pid_t wait3(WAIT_TYPE *status, int option, struct rusage *usage);

+# endif

+#endif+

继续编译python,编译完成时会给出已经编译的模块(extension)和未编译的模块。

INFO: Could not locate ffi libs and/or headers

Python build finished successfully!

The necessary bits to build these optional modules were not found:

_bz2 _curses _curses_panel

_dbm _gdbm _hashlib

_lzma _sqlite3 _ssl

_tkinter _uuid nis

readline spwd

To find the necessary bits, look in setup.py in detect_modules() for the module"s name.

The following modules found by detect_modules() in setup.py, have been

built by the Makefile instead, as configured by the Setup files:

_abc atexit pwd

time

Failed to build these modules:

_crypt _ctypes

Could not build the ssl module!

Python requires an OpenSSL 1.0.2 or 1.1 compatible libssl with X509_VERIFY_PARAM_set1_host().

LibreSSL 2.6.4 and earlier do not provide the necessary APIs, https://github.com/libressl-portable/portable/issues/381

make install,将生成的bin目录合并至安卓设备的/system/bin下,将lib目录合并至设备的/system/lib下。执行效果:

130|(略):/ # pyth python3 python3.7 python3.7m python3-config python3.7-config python3.7m-config 130|(略):/ # python3 Python 3.7.1 (default, Nov 6 2018, 20:11:05) [GCC 4.9 20140827 (prerelease)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import base64 >>> base64.b64encode(b"abcd") b"YWJjZA==" >>> base64.b64decode(b"YWJjZA==") b"abcd" >>> (略):/ #

注意:

根据编译python3.5.6的经验:

Ubuntu 16.04安装的版本为python3.5.2,相比于3.5.6版本,其主程序在weakref.py/_weakref.c中少了_remove_dead_weakref()函数等。因此python在交叉编译,使用系统自带python3.5执行下列命令的过程中,会出现找不到函数的异常:

$ _PYTHON_PROJECT_BASE=/home/xys/projects/Python-3.5.6/build.android _PYTHON_HOST_PLATFORM=linux-arm PYTHONPATH=../Lib:../Lib/plat-linux python3.5 -S -m sysconfig --generate-posix-vars

Could not import runpy module

Traceback (most recent call last):

File "../Lib/runpy.py", line 14, in

import importlib.machinery # importlib first so we can test #15386 via -m

File "../Lib/importlib/__init__.py", line 57, in

import types

File "../Lib/types.py", line 166, in

import functools as _functools

File "../Lib/functools.py", line 23, in

from weakref import WeakKeyDictionary

File "../Lib/weakref.py", line 12, in

from _weakref import (

ImportError: cannot import name "_remove_dead_weakref"

因此,交叉编译Python的时候最好保证电脑上的Python版本和要编译的Python版本一致,即先编译安装Linux版本,再编译arm版本。

已查阅得知的其他方案

这种方案采用crystax ndk (1)( BSD 2-clause license协议),编译时在安卓app项目中嵌入libpython3.5m.so,从而实现Android Java ↔ JNI ↔ Python的相互调用。

这种方案采用 CLE(Common Language Extension)提供的解决方案,同样在安卓app项目中嵌入libpython3.5m.so,从而实现Android Java ↔ JNI ↔ Python的相互调用。

资料

https://www.crystax.net/android/ndk CrystaX NDK,据说在Android NDK的基础上做了很多改进

https://github.com/joaoventura/pybridge joaoventura/pybridge: Reuse Python code in native Android applications

https://kivy.org/#home Kivy: Cross-platform Python Framework for NUI Development,用kv这个东西管理界面,用Python代码完成逻辑,从而让Python在各个平台上运行。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐