1. 为什么值得折腾 Qt 5.14.2 的 aarch64 静态交叉编译
第一次在 ARM 板子上跑 Qt 程序的人,大概率都经历过这样的场景:在 x86 的 Ubuntu 虚拟机上装好 Qt Creator,编译出一个可执行文件,拷到开发板上,运行,然后看到一行冷冰冰的error while loading shared libraries: libQt5Core.so.5: cannot open shared object file。板子上的系统可能是个精简的根文件系统,根本没有 Qt 的运行时库,你也不可能把整个 Qt 的 so 文件全塞进去。这时候静态编译就成了最省心的方案——把所有依赖打进一个可执行文件,拷过去就能跑。
Qt 5.14.2 这个版本在嵌入式圈子里被大量使用,原因很实际:它足够稳定,对 C++11/14 支持完善,而且很多芯片厂商的 BSP 里默认就带这个版本。aarch64 架构则是当前主流 ARM 开发板(比如瑞芯微 RK3588、全志 H616、树莓派 4B 等)的标准配置。把这两个东西组合起来做静态交叉编译,本质上就是在一台 x86_64 的 Linux 主机上,用一套 ARM 的工具链,编译出能在 aarch64 目标板上独立运行的 Qt 程序。
这件事的价值在于:你只需要维护一个可执行文件,不需要在目标板上安装任何 Qt 库,部署成本极低。适合谁看?适合那些正在做嵌入式 HMI、工业控制面板、车载终端、智能家居中控屏的开发者,也适合想从零理解交叉编译工具链、sysroot、configure 参数这些概念的进阶学习者。整个过程会踩不少坑,但踩完之后你对整个构建体系的理解会上一个台阶。
2. 整体方案设计与工具链选型思路
2.1 静态编译和动态编译的本质区别
动态编译时,Qt 的各个模块被编译成.so共享库,你的程序在运行时通过动态链接器去加载这些库。好处是多个程序可以共享同一份库,节省磁盘和内存;坏处是目标环境必须存在这些库,而且版本要匹配。静态编译则是把用到的 Qt 代码直接链接进最终的可执行文件,生成一个体积较大但完全自包含的二进制。
静态编译在嵌入式场景下的优势非常明显:部署简单、不依赖目标板的库环境、不会出现版本冲突。但代价也要说清楚:可执行文件体积会显著增大(一个简单的窗口程序可能从几百 KB 变成十几 MB),而且如果多个程序都用 Qt,每个程序都会带一份 Qt 代码,内存占用会叠加。所以静态编译适合“程序数量少、部署环境受限”的场景,不适合“一个板子上跑十几个 Qt 程序”的场景。
2.2 工具链的选择:为什么推荐官方或厂商提供的 aarch64 工具链
交叉编译的核心是工具链,它决定了你编译出来的代码能不能在目标板上跑。aarch64 的工具链主要有几个来源:Linaro 发布的 GNU 工具链、芯片厂商(如瑞芯微、全志)随 BSP 提供的工具链、以及各大 Linux 发行版仓库里的gcc-aarch64-linux-gnu。
我的建议是优先使用芯片厂商 BSP 里自带的工具链,因为它的 glibc 版本、内核头文件版本和目标板的根文件系统是匹配的。如果你用了一个 glibc 版本比目标板还新的工具链,编译出来的程序在板子上会因为找不到对应版本的符号而报错。如果拿不到厂商工具链,退而求其次用 Linaro 的版本,但一定要确认它的 glibc 版本不高于目标板的 glibc 版本。
工具链的命名通常类似aarch64-linux-gnu-gcc,前缀aarch64-linux-gnu-就是所谓的“三元组”。你在配置 Qt 时需要把这个前缀告诉它,Qt 的 configure 脚本会自动去找对应的 gcc、g++、ar、ranlib 等工具。
2.3 sysroot 的作用和准备方式
sysroot 是“系统根目录”的缩写,它是一个包含了目标板头文件和库文件的目录。交叉编译时,编译器需要知道目标系统的头文件在哪里(比如stdio.h、pthread.h),链接器需要知道目标系统的库在哪里(比如libc.so、libpthread.so)。这些信息都通过--sysroot参数指定。
sysroot 的来源有两种:一种是从目标板的根文件系统里直接拷贝出来,另一种是使用工具链自带的 sysroot。厂商工具链通常自带一个 sysroot,路径类似.../aarch64-linux-gnu/libc。如果你用的是发行版仓库里的工具链,sysroot 一般在/usr/aarch64-linux-gnu下面。准备 sysroot 时要特别注意:里面的库必须是目标板实际使用的版本,否则链接阶段可能通过,但运行阶段会出问题。
2.4 静态编译 Qt 的整体流程概览
整个流程可以拆成几个阶段:准备主机环境(安装必要的构建工具)→ 准备工具链和 sysroot → 下载 Qt 源码 → 配置 configure 参数 → 编译并安装 → 在 Qt Creator 里配置交叉编译套件 → 编写测试程序验证。
每个阶段都有坑。比如主机环境里如果缺少python、perl、bison、flex这些工具,configure 会直接失败;configure 参数如果写错,可能编译到一半才报错;编译过程可能持续一两个小时,中途因为某个模块失败而前功尽弃。所以下面我会把每个环节的关键点和避坑经验都讲清楚。
3. 主机环境准备与工具链配置实操
3.1 主机系统选择和基础依赖安装
主机系统我推荐 Ubuntu 20.04 或 22.04 的 x86_64 版本,这两个版本的软件包比较新,构建 Qt 5.14.2 不会遇到太多兼容性问题。如果你用的是 CentOS 7.9,也能做,但它的 gcc 版本偏老(默认 4.8),需要额外升级到 gcc 7 以上,否则 Qt 5.14.2 的某些 C++14 特性会编译失败。
基础依赖的安装命令如下(以 Ubuntu 为例):
sudo apt update sudo apt install -y build-essential perl python3 git \ libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev \ libx11-dev libxext-dev libxrender-dev libxcb1-dev \ libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev \ libfreetype6-dev libpng-dev libjpeg-dev libssl-dev \ bison flex gperf ruby这些包分别对应:编译工具链、脚本解释器、OpenGL 开发库、X11 相关头文件、字体和图像库、SSL 支持、以及语法分析工具。少装一个都可能在 configure 或编译阶段报错。
注意:如果你打算编译 QtWebEngine 模块,还需要额外安装
libnss3-dev、libasound2-dev、libdbus-1-dev等一大堆依赖。但嵌入式场景一般用不到 WebEngine,可以在 configure 时用-skip qtwebengine跳过,能省下大量编译时间。
3.2 交叉编译工具链的获取与验证
假设你从芯片厂商那里拿到了工具链压缩包,解压到一个固定目录,比如/opt/toolchain/aarch64-linux-gnu。然后把它加入 PATH:
export PATH=/opt/toolchain/aarch64-linux-gnu/bin:$PATH export CROSS_COMPILE=aarch64-linux-gnu-验证工具链是否可用:
aarch64-linux-gnu-gcc --version aarch64-linux-gnu-g++ --version如果能看到版本信息,说明工具链本身没问题。接下来验证它能不能编译出一个 aarch64 的可执行文件:
echo 'int main(){return 0;}' > test.c aarch64-linux-gnu-gcc test.c -o test_arm file test_armfile命令的输出应该显示ELF 64-bit LSB executable, ARM aarch64。如果显示的是 x86_64,说明你的 PATH 里 x86 的 gcc 优先级更高,需要检查环境变量。
3.3 sysroot 的整理和路径确认
如果工具链自带 sysroot,找到它的路径,通常在.../aarch64-linux-gnu/libc或者.../sysroot下面。确认里面有usr/include、usr/lib、lib这些目录。
如果工具链没有自带 sysroot,你需要从目标板的根文件系统里拷贝。假设目标板的根文件系统挂载在/mnt/target,那么:
mkdir -p /opt/sysroot cp -a /mnt/target/usr/include /opt/sysroot/usr/ cp -a /mnt/target/usr/lib /opt/sysroot/usr/ cp -a /mnt/target/lib /opt/sysroot/拷贝完成后,检查一下/opt/sysroot/usr/lib里有没有libc.so、libpthread.so这些关键库。如果没有,说明拷贝不完整,需要重新检查目标板的根文件系统。
实操心得:sysroot 里的库文件如果是符号链接,拷贝时要用
cp -a保留链接属性,否则链接阶段会因为找不到实际文件而报错。我曾经因为用了cp -r导致所有符号链接都变成了实际文件的副本,链接时一堆undefined reference,排查了半天才发现是这个问题。
3.4 环境变量的持久化配置
为了避免每次打开终端都要重新设置环境变量,建议把它们写进~/.bashrc或者一个单独的脚本文件里:
# ~/.bashrc 末尾追加 export TOOLCHAIN_PATH=/opt/toolchain/aarch64-linux-gnu export SYSROOT_PATH=/opt/sysroot export PATH=$TOOLCHAIN_PATH/bin:$PATH export CROSS_COMPILE=aarch64-linux-gnu-这样每次登录 shell 时都会自动生效。如果你同时维护多个目标板的工具链,建议用不同的脚本文件来切换,避免混淆。
4. Qt 5.14.2 源码配置与编译全流程
4.1 源码下载与目录结构说明
Qt 5.14.2 的源码可以从 Qt 官方下载页面获取,文件名类似qt-everywhere-src-5.14.2.tar.xz。下载后解压:
tar -xf qt-everywhere-src-5.14.2.tar.xz cd qt-everywhere-src-5.14.2解压后的目录里包含了几十个子模块,比如qtbase、qtdeclarative、qtmultimedia、qtserialport等。每个子模块都可以单独配置和编译,但通常我们会用顶层的configure脚本一次性配置所有需要的模块。
注意:Qt 5.14.2 的源码包比较大,解压后可能占用几个 GB 的磁盘空间。编译过程中还会产生大量的中间文件,建议预留至少 20 GB 的磁盘空间。
4.2 configure 参数逐项解析
configure 是整个过程中最关键的一步,参数写错了后面全白搭。下面是一个针对 aarch64 静态编译的典型配置命令:
./configure \ -prefix /opt/qt5.14.2-aarch64-static \ -opensource -confirm-license \ -release \ -static \ -no-shared \ -nomake examples -nomake tests \ -skip qtwebengine \ -xplatform linux-aarch64-gnu-g++ \ -sysroot $SYSROOT_PATH \ -no-opengl \ -no-xcb \ -no-eglfs \ -qt-zlib -qt-libpng -qt-libjpeg \ -no-feature-accessibility \ -no-feature-cups \ -no-feature-printdialog \ -no-feature-printer \ -no-feature-printsupport \ -no-feature-sql \ -no-feature-testlib \ -no-feature-xml \ -no-icu \ -no-glib \ -no-dbus \ -no-gui \ -no-widgets这个配置看起来很长,但每一项都有明确的目的。-prefix指定安装路径,编译完成后make install会把所有文件装到这个目录。-static和-no-shared是静态编译的核心开关。-nomake examples -nomake tests跳过示例和测试代码,能节省大量编译时间。-skip qtwebengine跳过 WebEngine 模块,这个模块依赖太多,嵌入式场景基本用不上。
-xplatform linux-aarch64-gnu-g++指定使用哪个平台配置文件。Qt 源码的qtbase/mkspecs/目录下有很多预定义的平台配置,但通常没有完全匹配你工具链的,所以需要自己创建一个。这个后面会详细讲。
-sysroot指定 sysroot 路径。-no-opengl -no-xcb -no-eglfs是禁用图形后端相关的选项,具体要禁用哪些取决于你的目标板有没有 GPU 和显示系统。如果你的板子有 framebuffer,可以保留-linuxfb;如果有 EGLFS,可以保留-eglfs。
-qt-zlib -qt-libpng -qt-libjpeg表示使用 Qt 自带的 zlib、libpng、libjpeg 源码来编译,而不是依赖 sysroot 里的版本。这样做的好处是避免 sysroot 里缺少这些库或者版本不兼容的问题。
-no-feature-*系列是禁用一些用不到的功能模块,能减小最终库的体积。-no-icu -no-glib -no-dbus是禁用国际化、GLib 和 D-Bus 支持,这些在嵌入式场景下通常不需要。
实操心得:configure 参数不是越多越好,每禁用一个功能都要确认你的程序确实用不到。比如你禁用了
-no-gui -no-widgets,那你就只能写控制台程序,不能写窗口程序。我曾经为了减小体积禁用了-no-feature-xml,结果后来发现 Qt 的某些内部模块依赖 XML,导致编译失败,又得重新配置。
4.3 自定义 mkspec 平台配置文件
Qt 的mkspecs目录下每个子目录对应一个平台配置,里面最重要的是qmake.conf文件。我们需要创建一个linux-aarch64-gnu-g++目录,并写入适合我们工具链的配置:
mkdir -p qtbase/mkspecs/linux-aarch64-gnu-g++然后创建qtbase/mkspecs/linux-aarch64-gnu-g++/qmake.conf,内容如下:
MAKEFILE_GENERATOR = UNIX CONFIG += incremental QMAKE_INCREMENTAL_STYLE = sublib include(../common/linux.conf) include(../common/gcc-base-unix.conf) include(../common/g++-unix.conf) QT_QPA_DEFAULT_PLATFORM = linuxfb QMAKE_CC = aarch64-linux-gnu-gcc QMAKE_CXX = aarch64-linux-gnu-g++ QMAKE_LINK = aarch64-linux-gnu-g++ QMAKE_LINK_SHLIB = aarch64-linux-gnu-g++ QMAKE_AR = aarch64-linux-gnu-ar cqs QMAKE_OBJCOPY = aarch64-linux-gnu-objcopy QMAKE_NM = aarch64-linux-gnu-nm -P QMAKE_STRIP = aarch64-linux-gnu-strip QMAKE_CFLAGS += -march=armv8-a -mtune=cortex-a53 QMAKE_CXXFLAGS += -march=armv8-a -mtune=cortex-a53 load(qt_config)QT_QPA_DEFAULT_PLATFORM = linuxfb指定默认的图形后端是 Linux Framebuffer。如果你的板子用的是 EGLFS,改成eglfs。-march=armv8-a -mtune=cortex-a53是针对 Cortex-A53 核心的优化参数,如果你的板子用的是其他核心(比如 Cortex-A72、Cortex-A76),需要相应调整。
4.4 编译过程中的资源监控和常见中断处理
配置完成后,用make -j$(nproc)开始编译。编译时间取决于主机的 CPU 核心数和磁盘速度,通常需要 1 到 3 个小时。建议用-j参数充分利用多核,但不要超过实际核心数,否则会因为内存不足而触发 OOM。
编译过程中可以用htop监控 CPU 和内存使用情况。如果发现内存占用接近上限,可以减少并行任务数,比如make -j4。
常见的编译中断原因有几个:一是某个模块的依赖没有满足,报错信息里会提示缺少什么头文件或库;二是磁盘空间不足,No space left on device;三是工具链的某个工具找不到,比如aarch64-linux-gnu-g++: command not found。遇到报错不要慌,先看错误信息的第一行和最后几行,通常能定位到问题所在。
注意:如果编译到一半失败了,修复问题后重新执行
make即可,它会从上次中断的地方继续,不会从头开始。但如果修改了 configure 参数,就需要先make distclean清理,再重新 configure。
4.5 安装与产物目录结构确认
编译完成后执行make install,所有文件会被安装到-prefix指定的目录。安装完成后,检查一下目录结构:
/opt/qt5.14.2-aarch64-static/ ├── bin/ │ ├── qmake │ ├── moc │ ├── rcc │ └── uic ├── include/ │ ├── QtCore/ │ ├── QtGui/ │ └── ... ├── lib/ │ ├── libQt5Core.a │ ├── libQt5Gui.a │ └── ... └── mkspecs/ └── linux-aarch64-gnu-g++/lib目录下应该是一堆.a静态库文件,而不是.so动态库。如果看到.so文件,说明静态编译没有生效,需要检查 configure 参数里的-static和-no-shared是否写对了。
5. Qt Creator 交叉编译套件配置与项目验证
5.1 Qt Creator 的安装和版本选择
Qt Creator 的版本选择比较灵活,不需要和 Qt 库版本严格对应。我一般用 Qt Creator 4.11 到 4.15 之间的版本,这些版本对 Qt 5.14.2 的支持都很好。可以从 Qt 官方下载页面下载独立的 Qt Creator 安装包,不需要装整个 Qt SDK。
安装完成后,打开 Qt Creator,进入工具→选项→Kits,开始配置交叉编译套件。
5.2 配置编译器、调试器和 Qt 版本
在Kits页面里,需要配置几个东西:
编译器:在Compilers标签页里,添加C和C++两个编译器,路径分别指向aarch64-linux-gnu-gcc和aarch64-linux-gnu-g++。
调试器:在Debuggers标签页里,添加aarch64-linux-gnu-gdb。如果你的工具链里没有 gdb,可以先用主机的 gdb 代替,但调试目标板程序时会有问题。
Qt 版本:在Qt Versions标签页里,添加我们编译好的 qmake,路径是/opt/qt5.14.2-aarch64-static/bin/qmake。添加后 Qt Creator 会自动识别出版本信息。
Kits:在Kits标签页里,新建一个 Kit,把Device type设为Generic Linux Device,Compiler选刚才添加的 aarch64 编译器,Debugger选 aarch64 的 gdb,Qt version选刚才添加的 Qt 5.14.2 静态版本。
5.3 创建测试项目并验证静态链接
配置好 Kit 后,新建一个 Qt Widgets Application 项目,选择刚才配置的 Kit。项目创建后,在.pro文件里加上:
QMAKE_LFLAGS += -static然后编译。编译完成后,用file命令检查生成的可执行文件:
file build-test-Desktop_Qt_5_14_2_static-Release/test输出应该显示ELF 64-bit LSB executable, ARM aarch64。再用ldd检查动态依赖:
aarch64-linux-gnu-ldd test如果是静态链接,ldd会显示not a dynamic executable或者statically linked。如果显示了一堆libQt5Core.so.5 => not found,说明静态链接没有生效,需要检查.pro文件里的配置和 Kit 的设置。
5.4 部署到目标板并运行
把生成的可执行文件拷贝到目标板,可以用scp、U 盘或者 NFS 挂载。拷贝后在目标板上赋予执行权限:
chmod +x test ./test如果程序正常启动并显示窗口,说明整个流程走通了。如果报错cannot execute binary file: Exec format error,说明编译出来的不是 aarch64 格式,需要检查工具链配置。如果报错No such file or directory,可能是动态链接器路径不对,静态编译一般不会有这个问题。
实操心得:目标板上如果没有 X11 或 Wayland,Qt 程序需要指定图形后端。可以在运行前设置环境变量
export QT_QPA_PLATFORM=linuxfb,或者export QT_QPA_PLATFORM=eglfs,具体用哪个取决于你的板子支持哪种。如果设置错了,程序会报This application failed to start because no Qt platform plugin could be initialized。
6. 常见问题排查与避坑经验实录
6.1 configure 阶段报错排查
configure 阶段最常见的报错是缺少依赖。比如:
ERROR: Feature 'xcb' was enabled, but the pre-condition 'features.thread && libs.xcb' failed.这说明 XCB 相关的库没有找到。解决办法是安装libxcb1-dev等包,或者在 configure 时加上-no-xcb禁用 XCB。
另一个常见报错是:
ERROR: Cannot find license file.这是因为没有加-opensource -confirm-license参数。Qt 5.14.2 是开源版本,必须明确接受开源许可。
还有一类报错和 Python 有关:
ERROR: Python is required to build Qt.Qt 的构建系统需要 Python 2 或 Python 3。Ubuntu 20.04 默认只有 Python 3,需要确保python3在 PATH 里,并且创建一个python的符号链接指向python3。
6.2 编译阶段报错排查
编译阶段最常见的报错是undefined reference,这通常是 sysroot 里的库版本不匹配导致的。比如:
undefined reference to `pthread_create'这说明链接器找不到 pthread 库。解决办法是在.pro文件里加上LIBS += -lpthread,或者在 configure 时确保-no-feature-thread没有被误加。
另一个常见问题是内存不足:
virtual memory exhausted: Cannot allocate memory这是因为make -j的并行数太高,每个编译进程都占用大量内存。解决办法是减少并行数,比如make -j2,或者增加主机的交换空间。
6.3 运行阶段报错排查
运行阶段最常见的问题是图形后端初始化失败:
This application failed to start because no Qt platform plugin could be initialized.解决办法是设置正确的QT_QPA_PLATFORM环境变量。如果板子用的是 Framebuffer,设为linuxfb;如果用的是 EGLFS,设为eglfs。如果板子根本没有图形系统,那就只能写控制台程序。
另一个问题是字体缺失:
QFontDatabase: Cannot find font directory /usr/lib/fonts静态编译的 Qt 程序需要字体文件才能显示文字。解决办法是把字体文件拷贝到目标板的某个目录,然后设置QT_QPA_FONTDIR环境变量指向该目录。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| configure 报缺少 xcb | 未安装 libxcb 开发包 | 安装 libxcb1-dev 或加 -no-xcb |
| 编译报 undefined reference | sysroot 库版本不匹配 | 检查 sysroot 里的库版本,确保与目标板一致 |
| 编译报内存不足 | make -j 并行数过高 | 减少并行数或增加交换空间 |
| 运行报 no Qt platform plugin | 图形后端未指定 | 设置 QT_QPA_PLATFORM 环境变量 |
| 运行报字体缺失 | 目标板没有字体文件 | 拷贝字体并设置 QT_QPA_FONTDIR |
| 可执行文件格式错误 | 工具链配置错误 | 用 file 命令检查,确保是 ARM aarch64 |
| 静态链接未生效 | .pro 文件缺少 -static | 在 .pro 里加 QMAKE_LFLAGS += -static |
6.5 独家避坑技巧汇总
第一个技巧:在 configure 之前,先用工具链编译一个最简单的 C++ 程序,确认工具链和 sysroot 都没问题。这一步能提前暴露大部分环境问题,避免在 Qt 编译到一半时才发现。
第二个技巧:把 configure 命令写成一个 shell 脚本,每次重新配置时直接执行脚本,避免手敲参数出错。脚本里还可以加上日志输出,方便排查问题。
第三个技巧:编译过程中如果某个模块反复失败,可以先用-skip跳过它,等主要模块编译完成后再单独编译那个模块。比如-skip qtmultimedia,等 QtCore 和 QtGui 编译好了再单独进qtmultimedia目录编译。
第四个技巧:静态编译的 Qt 程序体积很大,可以用aarch64-linux-gnu-strip去掉符号表,能减小 30% 到 50% 的体积。在.pro文件里加上QMAKE_STRIP = aarch64-linux-gnu-strip即可。
第五个技巧:如果目标板的 glibc 版本比较老,而工具链的 glibc 版本比较新,可以在编译时加上-D_GNU_SOURCE和-D_DEFAULT_SOURCE,有时候能绕过一些符号版本问题。但这只是权宜之计,根本解决办法还是用匹配的工具链。
7. 静态编译产物体积优化与模块裁剪
7.1 按需裁剪 Qt 模块
静态编译最大的痛点就是体积。一个默认配置的 Qt Widgets 程序,静态编译后可能有 15 到 20 MB。如果加上 QtNetwork、QtSql 等模块,体积会更大。裁剪的原则是:只编译程序实际用到的模块。
在 configure 阶段,可以用-skip跳过不需要的模块。比如你的程序只用 QtCore 和 QtGui,就可以跳过 QtNetwork、QtSql、QtXml、QtMultimedia 等。常见的可跳过模块包括:
qtwebengine:依赖太多,嵌入式基本不用qtmultimedia:音视频处理,除非需要否则跳过qtquick3d:3D 渲染,除非需要否则跳过qtcharts:图表模块,除非需要否则跳过qtdatavis3d:3D 数据可视化,除非需要否则跳过
7.2 编译选项对体积的影响
除了裁剪模块,编译选项也会影响体积。-release比-debug小很多,因为 debug 版本包含了大量调试信息。-no-feature-*系列选项可以禁用一些用不到的功能,比如-no-feature-accessibility禁用无障碍支持,-no-feature-cups禁用打印支持。
另外,-no-icu禁用国际化支持,能显著减小体积,因为 ICU 库本身就有几十 MB。如果你的程序不需要多语言支持,这个选项一定要加上。
7.3 strip 和压缩的实操方法
编译完成后,用aarch64-linux-gnu-strip去掉可执行文件的符号表和调试信息:
aarch64-linux-gnu-strip --strip-all test如果还想进一步压缩,可以用upx工具:
upx --best testUPX 能把可执行文件压缩到原来的 30% 到 50%,运行时自动解压。但 UPX 对某些嵌入式系统可能有兼容性问题,使用前需要测试。
注意:strip 之后可执行文件就无法用 gdb 调试了,所以建议保留一份未 strip 的版本用于调试,strip 后的版本用于发布。
8. 从静态编译到实际项目落地的经验总结
8.1 静态编译在真实项目中的适用边界
静态编译不是万能的。它适合“程序数量少、部署环境受限、对启动速度要求不高”的场景。如果你的项目需要频繁更新 Qt 库,或者多个程序共享 Qt 库,那动态编译更合适。
另外,静态编译的 Qt 程序在启动时会比动态编译的慢一些,因为所有代码都需要从磁盘加载到内存。对于启动速度敏感的场景,需要权衡一下。
还有一个容易被忽略的问题:静态编译的 Qt 程序如果使用了插件(比如图像格式插件、平台插件),这些插件也需要静态编译进去。Qt 提供了一套静态插件导入机制,需要在代码里用Q_IMPORT_PLUGIN宏显式导入。如果忘了导入,运行时会报“找不到插件”的错误。
8.2 交叉编译环境的可复现性管理
交叉编译环境很容易变得混乱:工具链版本、sysroot 内容、configure 参数、环境变量,任何一个变了都可能导致编译结果不同。为了保证可复现性,建议把整个环境用 Docker 或者脚本管理起来。
我的做法是写一个setup_env.sh脚本,里面包含所有环境变量的设置和依赖安装命令。再写一个build_qt.sh脚本,里面包含完整的 configure 和 make 命令。这样换一台机器,执行这两个脚本就能重建整个环境。
8.3 后续升级 Qt 版本的注意事项
如果以后要升级到 Qt 5.15.x 或 Qt 6.x,需要注意几个变化。Qt 5.15 的 configure 系统做了一些调整,部分参数名称变了。Qt 6 则完全切换到了 CMake 构建系统,configure 脚本虽然还在,但底层已经是 CMake 了。
升级时建议先在新的目录里重新编译,不要覆盖旧的安装目录。这样如果新版本有问题,可以随时切回旧版本。另外,Qt 6 对 C++ 标准的要求提高到了 C++17,工具链的 gcc 版本也需要相应升级。
我个人在实际操作中的体会是:交叉编译这件事,环境准备占 70% 的时间,编译占 20%,排查问题占 10%。把环境准备做扎实了,后面会顺利很多。每次遇到报错,先看错误信息,再查文档,最后才去搜索。大部分问题都能从错误信息里找到线索,关键是不要慌,一步步排查。