一块 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 是用 c 写的,那么在这个文件里你可以决定哪些模块可以静态链接进 python 执行文件,或者作为动态库。
也可以按需注释和释放各 module, 减小 size 。
依赖外部第三方库的实现在这里, 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 个方法可以找到。
指定标准库
通过环境变量 PYTHONHOME 来指明,这里碰到的坑是 PYTHONHOME 指定的不是标准库的路径,标准库路径是 $PYTHONHOME/lib/python3.5
,当时调了好久才发现是这样的。
放在固定目录下
比如你的 python 放在/opt/bin/
下,那么/opt/lib/python3.5
就放你的标准库, python 就能跑起来了。
或者直接放在 /usr/lib/python3.5 下,应该也是可以的。
按需裁剪,但是发现 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 。以失败告终。
还有几个问题:
PGEN 等没执行是否会有后遗症
zip 压缩后的标准库没办法 import lib-dyload 下的模块
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
等。
仅供参考。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.