2、Python环境搭建:交叉编译Python解释器、在目标板上运行Python、依赖库管理
好,咱们进入正题。Python环境搭建,说白了就是让Python能在你的嵌入式目标板上跑起来。这事儿看着简单,但坑不少。我刚开始做的时候,以为跟PC上装个Python一样简单,结果折腾了两天才搞定。嗯,这里面的门道,我慢慢跟你说。
2.1 为什么需要交叉编译Python?
你想想看,你的目标板——比如一个ARM Cortex-A系列的开发板——它跑的是Linux系统,但资源有限。你不可能在板子上直接装个gcc去编译Python源码,那太慢了,而且很多板子压根儿没这条件。
所以,咱们得在强大的PC主机上,用交叉编译工具链,生成目标板能运行的Python解释器。这就是交叉编译的核心思想:在A平台上编译,在B平台上运行。
核心概念:交叉编译 = 主机(x86_64)编译 + 目标板(ARM/MIPS/RISC-V)运行。
我在项目中遇到过,有人直接把PC上的Python二进制文件拷贝到板子上,结果一运行就报“Exec format error”。说白了,就是架构不匹配。所以,这一步省不了。
2.2 交叉编译Python解释器
好,咱们开始动手。我以ARM64(aarch64)平台为例,工具链用通用的aarch64-linux-gnu-gcc。其他平台类似,换一下工具链前缀就行。
2.2.1 准备工作
首先,你得有交叉编译工具链。我个人习惯用Linaro提供的预编译工具链,稳定又省事。
# 下载并解压工具链(示例)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
tar -xf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
export PATH=$PATH:$(pwd)/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
然后,下载Python源码。我建议用Python 3.x版本,3.8以上比较稳定。
wget https://www.python.org/ftp/python/3.11.5/Python-3.11.5.tgz
tar -xzf Python-3.11.5.tgz
cd Python-3.11.5
2.2.2 配置交叉编译
这里有个关键点:Python的configure脚本需要指定主机和目标平台。我常用的配置参数如下:
mkdir build_arm64
cd build_arm64
../configure \
--host=aarch64-linux-gnu \
--build=x86_64-linux-gnu \
--prefix=/usr/local/python3-arm64 \
--enable-optimizations \
--disable-ipv6 \
ac_cv_file__dev_ptmx=no \
ac_cv_file__dev_ptc=no
解释一下几个关键参数:
--host:目标平台,这里是aarch64。--build:当前编译主机平台。--prefix:安装路径,编译完成后会放到这个目录。ac_cv_file__dev_ptmx=no:这是为了避免在交叉编译时检查本地设备文件,我踩过这个坑,不加的话配置会卡住。
注意:如果你在配置时遇到“checking for C compiler... not found”之类的错误,说明工具链没在PATH里,或者环境变量没设对。我曾经因为路径写错,浪费了半天时间。
2.2.3 编译与安装
配置没问题的话,直接编译:
make -j$(nproc)
make install DESTDIR=$(pwd)/output
编译完成后,所有文件会出现在output/usr/local/python3-arm64/目录下。你把这个目录整个拷贝到目标板上就行。
嗯,这里要注意:make -j后面的数字别太大,我一般用nproc自动检测核心数。但如果你主机内存不大,建议手动设小一点,比如-j4,否则容易编译到一半卡死。
2.3 在目标板上运行Python
把编译好的Python目录拷贝到目标板后,怎么运行呢?
我一般用NFS挂载或者scp直接传。假设你传到了/opt/python3-arm64目录下:
# 在目标板上执行
export PATH=/opt/python3-arm64/bin:$PATH
export LD_LIBRARY_PATH=/opt/python3-arm64/lib:$LD_LIBRARY_PATH
python3 --version
如果看到输出Python 3.11.5,恭喜你,成了!
小技巧:我习惯把这两行export写到/etc/profile或者~/.bashrc里,这样每次登录自动生效。省得每次都要手动敲一遍。
但有时候,你可能会遇到这种错误:
python3: error while loading shared libraries: libpython3.11.so.1.0: cannot open shared object file: No such file or directory
为什么会这样?说白了,就是动态链接库没找到。你检查一下LD_LIBRARY_PATH有没有包含/opt/python3-arm64/lib。如果还不行,试试用ldconfig刷新一下缓存。
2.4 依赖库管理
Python跑起来了,但你会发现,很多第三方库用不了。比如你想用requests、numpy,直接pip install会报错——因为pip默认会从源码编译,而你的目标板上没有gcc。
所以,依赖库也得交叉编译。这里我分享两种方法。
2.4.1 方法一:使用pip的交叉编译模式
Python 3.11之后,pip支持了--platform参数。你可以这样:
# 在主机上执行
pip3 download --only-binary=:all: --platform manylinux2014_aarch64 --python-version 3.11 -d ./packages requests numpy
然后,把./packages目录下的whl文件拷贝到目标板上,再用pip安装:
# 在目标板上执行
pip3 install --no-index --find-links ./packages requests numpy
这个方法简单,但前提是你要的库有预编译的ARM64版本。很多小众库没有,那就得用第二种方法。
2.4.2 方法二:手动交叉编译第三方库
对于没有预编译包的库,你得手动交叉编译。我以cffi为例:
# 设置交叉编译环境变量
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export AR=aarch64-linux-gnu-ar
export RANLIB=aarch64-linux-gnu-ranlib
# 下载并编译
pip3 download cffi
tar -xzf cffi-*.tar.gz
cd cffi-*
python3 setup.py build
python3 setup.py install --prefix=/opt/python3-arm64
这里有个坑:python3 setup.py build默认会使用主机上的Python配置。你需要确保sysconfig能正确识别交叉编译环境。我建议在编译前先设置:
export _PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata_aarch64_linux_gnu
这个变量名怎么来的?你可以去/opt/python3-arm64/lib/python3.11/_sysconfigdata*.py里找一下,找到对应你平台的那个文件名。
避坑指南:我曾经编译numpy时,因为BLAS库没交叉编译好,结果在目标板上跑矩阵运算直接段错误。后来我改用OpenBLAS的交叉编译版本才解决。所以,涉及C扩展的库,一定要确保所有底层依赖都交叉编译正确。
2.4.3 依赖库管理的最佳实践
我个人的做法是,维护一个requirements_arm64.txt文件,里面列出所有需要的库及其版本。然后写一个脚本,批量下载或编译。这样每次搭建新环境时,一条命令搞定。
# requirements_arm64.txt 示例
requests==2.31.0
numpy==1.25.2
cffi==1.15.1
pyserial==3.5
然后配合pip download或者自定义编译脚本,自动化处理。
| 库名 | 是否有预编译ARM64包 | 推荐处理方式 |
|---|---|---|
| requests | 是 | pip download --platform |
| numpy | 是(但需注意BLAS) | pip download 或手动编译 |
| cffi | 否 | 手动交叉编译 |
| pyserial | 是(纯Python) | 直接拷贝 |
嗯,总结一下。Python环境搭建,核心就是三步:交叉编译解释器、部署到目标板、管理依赖库。每一步都有细节,但只要你按部就班来,基本不会出大问题。我刚开始做的时候,光是配置参数就试了十几次,后来总结出上面这套流程,再也没翻过车。
下一节,咱们聊聊怎么用Python脚本控制嵌入式设备的GPIO和串口。那才是真正好玩的地方。