Skip to content

嵌入式Linux-基于I.MX6ULL移植cpython

约 1408 字大约 5 分钟

Linux嵌入式Python

2025-05-26

cpython的移植相比起其他的基础库略显复杂,我们需要先编译它的前置库,再编译cpython,老样子,我们的工作环境如下:

  • 正点原子I,MX6ULL ALPHA开发板
  • Docker Ubuntu 25.04 上位机
  • Linux 6.12.3
  • zlib 1.3.1
  • libffi 3.4.8
  • ncurses 6.5
  • cpython 3.13.3

一,什么是 cpython

在讲如何移植cpython之前,我们肯定需要了解它是什么,如果你对该内容已经有所了解,可以直接跳过本节

Python作为解释型编程语言,其在运行时一定要有名为解释器的程序来解析和执行Python代码,cpython就是Python的官方解释器,同时,它也是Python名义上的标准。

除了cpython之外,Python还有很多第三方解释器:

  • pypy:针对性能优化的解释器。
  • jython:针对Java环境的解释器。
  • micropython:针对微控制器环境的解释器。
  • RustPython:用Rust编写的解释器。

二,移植依赖库

根据笔者的整理,cpython需要zliblibffincurses三个前置库。

1. zlib

zlib相对较好移植,我们先下载它的源码:

git clone https://github.com/madler/zlib.git
cd zlib
git checkout -b release_1_3_1 v1.3.1

注意,zlib已经支持了cmake构建,因此我们采用cmake构建:

cmake -S . -B build \
  -D CMAKE_BUILD_TYPE=Release -D CMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
  -DINSTALL_BIN_DIR=/tmp/zlib_build/bin \
  -DINSTALL_LIB_DIR=/tmp/zlib_build/lib \
  -DINSTALL_MAN_DIR=/tmp/zlib_build/share/man/ \
  -DINSTALL_PKGCONFIG_DIR=/tmp/zlib_build/lib/pkgconfig \
  -DINSTALL_INC_DIR=/tmp/zlib_build/include

我们指定构建Release版本,编译器为arm-linux-gnueabihf-gcc,并指定安装路径,实际编译时,请将路径指向你的安装目录。

如果没有出现错误,我们就可以开始编译和安装:

cmake --build build
cmake --install build

2. libffi

最近的libffi版本有相当严重的构建bug(至少包含了3.4.6和3.4.8两个版本),为了规避该问题,我们需要先下载其release中的源码并解压:

wget https://github.com/libffi/libffi/releases/download/v3.4.8/libffi-3.4.8.tar.gz
tar -xvf libffi-3.4.8.tar.gz

接着,克隆源码:

git clone https://github.com/libffi/libffi.git
cd libffi
git checkout -b release_3_4_8 v3.4.8

进入源码目录,先进行autogen(确保libtoolautoconfautomakem4已安装):

./autogen.sh

接下来,最重要的,我们要把提前下载的源码中的Makefile.in复制到libffi目录下:

cp ../libffi-3.4.8/Makefile.in .

为什么?因为libffi不兼容新的autoconf,导致其生成了错误的Makefile.in,详情请参考Issue#853

然后,我们再运行configure,同样,--prefix指向你的安装目录:

./configure --host=arm-linux-gnueabihf --prefix=/tmp/libffi_build

编译并安装:

make -j
make install

3. ncurses

ncurses源码保存在gnu的镜像中,我们直接下载:

wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.5.tar.gz
tar -xvf ncurses-6.5.tar.gz
cd ncurses-6.5

进行如下配置,同样,--prefix指向你的安装目录:

./configure --prefix=/tmp/ncurses_build --host=arm-linux-gnueabihf \
  --with-shared --without-debug --without-ada --enable-pc-files \
  --with-cxx-binding --with-cxx-shared --enable-ext-colors --enable-ext-mouse \
  --enable-overwrite --without-progs

这里的参数笔者不做详细解释,读者可以参考./configure --help

编译并安装:

make -j
make install

安装后,我们需要创建libncurseswlibncurses的符号链接,虽然python用不到它们,但其他程序可能需要(比如bash):

cd /tmp/ncurses_build/lib
ln -s libncursesw.so.6.5 libncurses.so.6.5
ln -s libncursesw.so.6 libncurses.so.6
ln -s libncursesw.so libncurses.so
ln -s libncursesw.a libncurses.a
ln -s libncurses++w.so.6.5 libncurses++.so.6.5
ln -s libncurses++w.so.6 libncurses++.so.6
ln -s libncurses++w.so libncurses++.so
ln -s libncurses++w.a libncurses++.a

三,移植 cpython

我们下载cpython的源码:

git clone https://github.com/python/cpython
cd cpython
git checkout -b release_3_13_3 v3.13.3

在配置之前,我们需要手动修改Makefile.pre.in,因为现在的cpython实际上是不支持交叉编译的!

Makefile.pre.in中,找到并注释如下内容:

cpython修改

光凭上面的注释我们也能猜到这是为什么,这个地方会引用构建好的python测试模块,但我们是交叉编译的,这东西不能执行!

接着,我们进行配置:

CFLAGS="..." LDFLAGS="..." ./configure \
  --host=arm-linux-gnueabihf --build=x86_64-linux-gnu \
  --prefix=/tmp/cpython_build --with-build-python --enable-shared \
  --enable-ipv6 ac_cv_file__dev_ptmx=0 ac_cv_file__dev_ptc=0 \
  --enable-optimizations --disable-test-modules --with-ensurepip=no

关于CFLAGSLDFLAGS我们需要做的实际上只是把刚刚构建的依赖传送给编译器,这里的工作就是把includelib目录添加到编译器的搜索路径中,例如:

CFLAGS="-I/tmp/zlib_build/include -I/tmp/libffi_build/include -I/tmp/ncurses_build/include -L/tmp/zlib_build/lib -L/tmp/libffi_build/lib -L/tmp/ncurses_build/lib"
LDFLAGS="-L/tmp/zlib_build/lib -L/tmp/libffi_build/lib -L/tmp/ncurses_build/lib"

当然,如果你将他们安装到了相同的路径中,就不必添加如此多的路径了。

--build参数实际是构建机器的架构(上位机),这里笔者不确定这种写法是否正确,但是可以正常编译。

ac_cv_file__dev_ptmx=0 ac_cv_file__dev_ptc=0表示不检查/dev/ptmx/dev/ptc文件,因为我们的上位机很有可能没有这两个文件,会导致编译失败。

最后,我们禁用了test模块和pip,因为我们不需要它。

编译并安装:

make -j
make install

四,验证

将所有文件拷贝到根文件系统上,上电测试:

cpython测试

一般情况下,解释器可能会报错,它会导致python的交互终端缺失部分功能,不过不影响使用:

cpython报错

这是因为缺少terminfo,安装ncurses时,该文件夹会被一同安装,我们在/etc/profile中添加如下内容:

export TERMINFO=/usr/lib/terminfo

笔者的terminfo位于此处,读者可以根据实际情况修改。

五,总结

笔者的构建流程通过Makefile的方式托管在imx6ull-dev仓库,包括构建环境的Dockerfile和所有的构建脚本,仅供参考。