笨办法移植 python3.5.1 到 arm 平台

2016-02-26 15:55:53 +08:00
 ryanking8215

环境

一块 Conext A9 的板子,移植最新的 python ,版本 3.5.1 。

交叉编译

交叉工具链是arm-xxx-linux-, xxx替换成你们自己的即可,这里我就不写出了,是开发板厂商提供的工具。
按照常规的编译方法:

./configure --prefix=`pwd`/mybuild --host=arm-xxx-linux

会报错,按照提示,网上找了一圈,需要指定如下参数

echo ac_cv_file__dev_ptmx=no > config.site
echo ac_cv_file__dev_ptc=no >> config.site

env CONFIG_SITE=config.site ./configure ......

就是指定 ac(autoconf)的信息,因为交叉编译环境下,无法自动探测到这些信息,需要手动指定。
这下能生成 Makefile 了。
make开始编译,过一会又报错了,发现是 pgen 无法执行。pgen是目标环境(arm)的,无法在开发环境(x86)下执行。
网上找了一圈,靠谱点的是为 python 源码包打补丁,但是好像 3.2 之后就不没补丁包了。
那不是没辙了,要不怎么说笨办法呢, Makefile 里注释掉需要执行 PGEN , freeze_import_lib 的代码,就一路顺畅了。

Module/Setup.dst

有些 module 是用 c 写的,那么在这个文件里你可以决定哪些模块可以静态链接进 python 执行文件,或者作为动态库。
也可以按需注释和释放各 module, 减小 size 。

setup.py

依赖外部第三方库的实现在这里, sqlite, cursers 等。
这里单独讲 sqlite 的编译

# The sqlite interface
        sqlite_setup_debug = False   # verbose debug prints from this script?

        # We hunt for #define SQLITE_VERSION "n.n.n"
        # We need to find >= sqlite version 3.0.8
        sqlite_incdir = sqlite_libdir = None
        sqlite_inc_paths = [ '/usr/include',
                             '/usr/include/sqlite',
                             '/usr/include/sqlite3',
                             '/usr/local/include',
                             '/usr/local/include/sqlite',
                             '/usr/local/include/sqlite3',
                             ]
        if cross_compiling:
            sqlite_inc_paths = [
                    '/home/xxx/lib/sqlite-3.6.3/mybuild/include',   # 这里填入事先编译好的 sqlite3 的头文件
                    ]

其他库类似。

其他

我的 python 是静态编译的,可执行文件在 8M-9M , strip 一下到 3M-4M.
祭出 upx 大法,再压缩一下到 1.6M ,可以接受。另外调整 upx 压缩率 1-9,最后的大小差不多, 1 还小一点,速度还快。

浮点数表示问题

这样编译后的 python ,浮点数表示是使用 legacy ,小数会用 10 位表示,因为交叉编译时没有指定大小端,所以有这个问题,解决方法就是
为浮点数指定大小端。

echo ac_cv_little_endian_double=yes >> config.site
# 重新编译一下

为此还发了帖子:见 http://www.v2ex.com/t/255038

标准库

指定标准库目录

如果执行 python 会报错:

Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: Py_Initialize: Unable to get the locale encoding
ImportError: No module named 'encodings'

Current thread 0xb6d60000 (most recent call first):
Aborted

这是没找到标准库的原因。有 2 个方法可以找到。

裁剪

按需裁剪,但是发现 3.5 上模块依赖严重,最不能忍的就是 urllib 依赖 email ,我只用 urllib ,不用 email ,有点浪费。
可以写一个 test.py , import 工程中将会用到的所有的库,边删边测。
按照我这边的需求, asyncio , concurrent 啥的不用,差不多 3.5M 左右。实际情况会比这个大差不多一倍,因为pycache,
你这边跑测试,会在标准库下生成pycache,写个脚本删除所有的pycache目录。

编译

标准库只需要 pyc 即可,不需要 py 文件。我们可以使用 compileall 模块对目标文件进行编译。

python3 -m compileall -b <DIR>

"-b “表示使用 legacy 方式去编译,对于 py 会生成对应的 pyc, 否则生成在pycache里, 光pycache的文件不清楚如何使用,所以用 legacy 方式。
写个脚本把各目录下的 py 文件删除即可。
注意
这个脚本需要在板上运行, 一般裁剪过的库不会带compileall模块,所以这里你要切换到完整的标准库。
理论上讲 python 生成的 bytecode 是一样的,所以应该可以在 x86 上执行,但要注意 python 是一个版本的,我没试过。

打包

编译后的标准库在 2.6M 左右。但如果还能再小更好。
python 支持 zip 方法 import 库。
需要到标准库目录里面去执行 zip -r p1.zip *, 将python3.5的软链接指向该文件
python 程序需要 zlib 支持,在 Setup.dist 里释放,并在编译时指定预先交叉编译好的 zlib 的库

问题
但是使用 zip 压缩后, lib-dyload/下的模块都 load 不到,会报错,貌似是 bug 。以失败告终。

后记

还有几个问题:

参考文档

http://blog.csdn.net/dahai19800703/article/details/7599463
http://www.geekfan.net/7441/
http://blog.csdn.net/shuxiao9058/article/details/7026205
http://www.2cto.com/kf/201305/208310.html
等。

仅供参考。

7027 次点击
所在节点    Python
3 条回复
congeec
2016-02-26 16:13:30 +08:00
两个问题:
1. 楼主试过 iOS 么?
2. micropython 试过么?
ryanking8215
2016-02-26 16:17:38 +08:00
@congeec
1. iOS 没试过
2. micropython 没试过,粗略看过,没记错的话是在单片机上的实现,不带 os 的。
cmheia
2016-02-26 17:26:08 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/259317

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX