已知资料:
- 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
# python
export PATH=/home/xys/opt/Python-3.7.1/bin:$PATH
然后在~/.zshrc中加入
# include ~/rc.public
if [ -f ~/rc.public ]; then
. ~/rc.public
fi
if [ -f ~/rc.public ]; then
. ~/rc.public
fi
bashrc类似,我还没加。
之后可以在终端输入python自动补全中发现python3.7程序。
- 编译安卓版本。以32位为例,
#!/bin/bash
# 使用android-ndk-r10e,其目录设置环境变量ANDROID_NDK。
# 我将当前目录定为环境变量
if [[ -z "$ANDROID_NDK" ]]; then
ANDROID_NDK=/opt/local/android-ndk-r10e
fi
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\nac_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中有包含这个调用)
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
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'
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
- http://www.srplab.com/cn/index.html CLE方案 用中间件的方式在不同平台执行脚本
- https://kivy.org/#home Kivy: Cross-platform Python Framework for NUI Development,用kv这个东西管理界面,用Python代码完成逻辑,从而让Python在各个平台上运行。
分享:
来源:oschina
链接:https://my.oschina.net/u/4000302/blog/3164086