1.前言
-
交叉编译的含义
:在平台A上编译出平台B的可执行文件,例如:在x86-64上编译出运行在arm64上的可执行文件。 -
为什么要交叉编译
:- 平台B性能较弱,编译速度比较慢;
- 平台B是裸机或实时操作系统,无法编译代码;
- 出于各种原因,导致手头没有平台B的硬件设备;
2.交叉编译的方式分类
- 平台B运行的是自制Linux系统
- 基于qemu+chroot交叉编译(没用过)
- 基于sysroot交叉编译(编译速度😍,部署速度😥)
- 平台B运行的是发行版Linux系统
- 基于qemu+chroot交叉编译(没用过)
- 基于sysroot交叉编译(编译速度😍,部署速度😍)
- 基于docker交叉编译(编译速度😥,部署速度😍😍)
3.交叉编译环境部署流程
3.1. 基于sysroot交叉编译
- 思路:把平台B的/lib、/usr/lib、/usr/include拷贝到平台A的sysroot文件夹中,编译的时候从sysroot文件夹寻找依赖。
- 注意❗:拷贝/lib、/usr/lib、/usr/include的时候,需要保留共享库的软连接,绝对路径的软连接需要修改成相对路径链接。
- 缺点:
- 为平台B运行的是自制Linux系统时,因为各种各样的依赖库都需要交叉编译,此时该方法部署速度慢;
- 似乎不适用于类似ROS2这种有自己的构建工具(colcon)的情况。
3.1.1. 拷贝命令可以参考👇
user_name = 平台B的用户名
platform_B_ip_address = 平台B的ip地址
$ mkdir -p your_sysroot
$ rsync -avz --rsync-path="sudo rsync" --delete user_name@platform_B_ip_address:/lib your_sysroot
$ rsync -avz --rsync-path="sudo rsync" --delete user_name@platform_B_ip_address:/usr/include your_sysroot/usr
$ rsync -avz --rsync-path="sudo rsync" --delete user_name@platform_B_ip_address:/usr/lib your_sysroot/usr
3.1.2.共享库的“绝对路径软连接”改成“相对路径软连接”
$ sudo apt-get install symlinks
$ symlinks -rc your_sysroot
3.2. 基于docker交叉编译
缺陷: 对于大型的项目,编译速度堪忧
- 思路:使用buildx构建跨平台的镜像并运行跨平台的容器,例如在x86-64上运行arm64的docker容器,需要编译的代码通过卷映射到容器中访问。
- 注意❗:使用docker需要访问外网,代理配置比较麻烦
- 缺点:
- 对于大型的项目,编译速度堪忧;
- docker上网比较麻烦,需要代理配置,包括:docker的代理配置、容器内部的代理配置。
3.2.1.docker安装
参考 docker engine 的 Install Docker Engine on Ubuntu 文档
3.2.2.docker代理配置
端口改成你自己的代理端口
$ sudo mkdir /etc/systemd/system/docker.service.d
$ sudo touch /etc/systemd/system/docker.service.d/proxy.conf
$ sudo vim /etc/systemd/system/docker.service.d/proxy.conf
# 把👇的内容拷贝进proxy.conf, 代理端口7897需要根据自己的情况设置
[Service]
Environment="HTTP_PROXY=localhost:7897"
Environment="HTTPS_PROXY=localhost:7897"
3.2.3.docker多平台构建
参考 docker engine 的 Multi-platform builds 文档
经博主实践,推荐做法👇,示例:
# 将 buildx 设置为默认构建器
docker buildx install
# 安装 QEMU 模拟支持多架构
docker run --privileged --rm tonistiigi/binfmt --install all
# 下载镜像
docker pull --platform linux/arm64 ubuntu:22.04
完整的操作过程可以参考博主的B站视频第2、3集以及视频对应的git仓库
4.补充
如果需要自行定制linux系统,推荐平台B用NFS挂载存放在平台A上的rootfs,这样在平台A中交叉编译平台B的代码时,可以直接从rootfs中寻找共享库。