V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ryanking8215
V2EX  ›  Python

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

  •  3
     
  •   ryanking8215 ·
    ryanking8215 · 2016-02-26 15:55:53 +08:00 · 7183 次点击
    这是一个创建于 3211 天前的主题,其中的信息可能已经有所发展或是发生改变。

    环境

    一块 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 个方法可以找到。

    • 指定标准库
      通过环境变量 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
    等。

    仅供参考。

    3 条回复    2016-02-26 17:26:08 +08:00
    congeec
        1
    congeec  
       2016-02-26 16:13:30 +08:00 via iPhone
    两个问题:
    1. 楼主试过 iOS 么?
    2. micropython 试过么?
    ryanking8215
        2
    ryanking8215  
    OP
       2016-02-26 16:17:38 +08:00
    @congeec
    1. iOS 没试过
    2. micropython 没试过,粗略看过,没记错的话是在单片机上的实现,不带 os 的。
    cmheia
        3
    cmheia  
       2016-02-26 17:26:08 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1099 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 23:30 · PVG 07:30 · LAX 15:30 · JFK 18:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.