1. 项目概述与核心痛点
先说结论:Qt5.14.2在aarch64架构下做静态交叉编译,这件事本身不难,难的是把整条链路理清楚。我见过太多人卡在同一个地方——configure参数抄了一堆,编到一半报错,然后就开始在论坛里翻帖子,越翻越乱。这篇文章就是把我从零到一跑通的全过程,包括踩过的坑和最终验证过的参数组合,完整记录下来。
为什么要盯住Qt 5.14.2这个版本?它是LTS(长期支持)版本里一个非常微妙的节点:qmake还在正常发力,CMake支持也已经成熟,同时又赶在Qt 6大改之前,很多老项目的依赖还锚定在这一代。再加上aarch64在嵌入式工控、边缘网关、国产化板卡上越来越普及,静态编译能省掉目标板上动态库的部署麻烦,直接扔一个可执行文件上去就跑,这对产线部署来说是实实在在的效率提升。
适合看这篇文章的读者,大概是这几类:
- 手里有aarch64开发板(RK3588、树莓派4B 64位系统、飞腾派等),想把Qt应用塞进去的
- 做嵌入式产品交付,希望目标机上不依赖Qt运行时的
- 被各种缺库、链接失败、ELF格式不对折磨,想找一份能通读到底的完整参考的
先说清楚,这篇不涉及具体商业板卡的BSP定制,而是聚焦在"通用的aarch64 Linux + Qt 5.14.2 + 静态链接"这条链路本身。原理通了,换板子只是改交叉工具链路径的事。
2. 交叉编译工具链与环境准备
2.1 选对工具链:aarch64-linux-gnu-gcc
交叉编译的第一步不是下载Qt源码,而是先把交叉编译器准备好。市面上常见的选择有这么几个:Linaro提供的aarch64-linux-gnu-gcc,Ubuntu自带的gcc-aarch64-linux-gnu包,以及各家板卡厂商提供的定制工具链(比如Rockchip的交叉编译器)。
我的建议很直接:优先用Ubuntu官方源装的gcc-aarch64-linux-gnu,除非你的目标板子的BSP指定了特定版本。原因有三个:依赖好解决(libc-dev-arm64-cross会自动装),版本更新有保障,和Qt 5.14.2的兼容性经过大量用户验证。
安装命令如下,在Ubuntu 20.04/22.04上均可:
sudo apt update sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu装完以后检查一下版本:
aarch64-linux-gnu-gcc --version理论上你会看到类似gcc version 9.x或者11.x的输出。这里有个关键点:host端的gcc版本和交叉编译器的版本不必一致,但交叉编译器的版本会影响你能用多新的C++标准库特性。
提示:如果你用Ubuntu 22.04,交叉编译器是gcc-11,编Qt 5.14.2时会遇到一个编译错误,后面第5节我会专门讲怎么处理。
2.2 构建sysroot:目标板的文件系统根
交叉编译的难点在于:你编译出的程序是跑在aarch64上的,而不是跑在x86_64上的。编译器在编译时不仅需要头文件,还需要尝试链接目标架构的libc、libstdc++等基础库。这些文件和x86_64宿主机的库完全不同,如果让交叉编译器误用了宿主机的库,链接阶段就会爆出一堆奇怪的错误。
为了让交叉编译时能找到正确的头文件和库文件,需要准备一个目录结构类似目标系统根目录的文件夹,这个目录叫sysroot。装完gcc-aarch64-linux-gnu之后,默认的sysroot路径通常是:
/usr/aarch64-linux-gnu检查一下这个目录:
ls /usr/aarch64-linux-gnu/lib你会看到aarch64架构下的libc.so.6、libm.so.6、libstdc++.so.6这些库。这些就是后续Qt configure时“探测目标环境”的基础。一般情况下不需要额外修改sysroot,除非你的板卡的libc版本和宿主机里这个交叉包自带的libc版本差异巨大——这种情况建议还是用板卡厂商提供的工具链更稳妥。
2.3 下载Qt 5.14.2源码
下载源码时有两个选择:官方在线安装器,或者直接下载源码包。静态交叉编译必须用源码编译安装,因为在线安装器提供的二进制包是针对host机架构的,不能直接拿来做交叉编译。
我习惯直接下载源码包,下载地址在官方仓库,文件名是qt-everywhere-src-5.14.2.tar.xz,大小约500MB左右:
wget https://download.qt.io/archive/qt/5.14/5.14.2/single/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有人可能会问:为什么不用git clone?git仓库虽然也能拿到5.14.2分支,但需要额外跑init-repository拉子模块,而且子模块版本不完全锁定,容易和网络上的教程对不上号。源码包更保险,解压即用。
2.4 目标平台依赖库的准备
这里重点说一个容易踩的坑:Qt对依赖库的处理是“按需探测”的。也就是说,configure阶段会检测目标系统里有没有某些库,有就启用对应功能,没有就静默禁用或者当作“外部依赖缺失”处理。典型例子是:
- libfontconfig、libfreetype:字体渲染,没有它Qt Widgets程序显示中文会出问题
- libsqlite3:Qt SQL模块的SQLite驱动插件,它是通过系统库动态加载的
- libcups:打印支持,没有也能编过,只是打印相关功能不可用
- libssl:网络模块的TLS支持,Qt5.14之前默认用的是OpenSSL,需要你有目标板对应架构的libssl
对于静态编译,这些依赖库必须在configure之前就准备好,并且最好也做成静态库(.a文件),否则即使Qt主库静态编出来了,链接最终应用时还是会报“找不到-lfontconfig”之类的错误。
我准备sysroot依赖的方法是:直接在目标板上(或者用debootstrap模拟目标架构环境)把需要的库文件拷贝到交叉编译的sysroot里。具体操作在3.2节细说。
3. Qt静态编译的关键配置与执行
3.1 configure参数深度解析
configure是整条编译链的核心。误解最多的就是这里的参数组合。我先把最终跑通的完整命令放出来,然后逐个讲解每个参数的作用:
./configure \ -prefix /opt/Qt5.14.2-aarch64-static \ -static \ -release \ -opensource \ -confirm-license \ -xplatform linux-aarch64-gnu-g++ \ -sysroot /usr/aarch64-linux-gnu \ -nomake examples \ -nomake tests \ -no-compile-examples \ -no-opengl \ -no-eglfs \ -no-xcb \ -no-glib \ -no-pkg-config \ -skip qtwebengine \ -skip qtdeclarative \ -skip qtlocation \ -skip qtmultimedia \ -skip qtscript \ -skip qtsensors \ -skip qtserialbus \ -skip qtwayland \ -skip qtwebview \ -skip qtquickcontrols \ -skip qtquickcontrols2 \ -skip qtgraphicaleffects \ -skip qtvirtualkeyboard \ -skip qtspeech \ -skip qtgamepad \ -skip qtpurchasing \ -skip qtserialport \ -skip qtcharts \ -skip qtdatavis3d \ -skip qtnetworkauth \ -skip qtpim \ -skip qtqa \ -skip qtsvg \ -skip qttools \ -skip qttranslations \ -skip qtxmlpatterns \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -qt-freetype \ -qt-harfbuzz \ -qt-pcre \ -qt-sql-sqlite \ -no-feature-cups \ -no-feature-printdialog \ -no-feature-printer \ -no-feature-printpreviewwidget \ -openssl-linked \ -I /usr/aarch64-linux-gnu/include \ -L /usr/aarch64-linux-gnu/lib这一堆参数看着吓人,但拆开来看就很清晰了:
-static:让Qt生成静态库(libQt5Core.a这种,而不是libQt5Core.so)-release:不编debug版,缩小编译体积、加快速度-xplatform linux-aarch64-gnu-g++:告诉Qt用哪个mkspec。Qt 5.14自带的mkspec里已经有这个平台定义了,路径在qtbase/mkspecs/linux-aarch64-gnu-g++,里面默认的编译器就是aarch64-linux-gnu-gcc-sysroot:指定sysroot路径,让configure阶段检测到的库和头文件都从这个路径找-nomake examples -nomake tests -no-compile-examples:不编示例和测试,节省的时间是用小时计的-no-opengl -no-eglfs -no-xcb:如果你的目标板只有串口控制台或简单framebuffer显示,这些显示后端都用不上,禁用可以避免一堆X11/EGL依赖-no-pkg-config:这个非常重要。Qt configure跨平台编译时,pkg-config默认会去找宿主机的.pc文件,极容易引入x86_64的库路径,产生架构错乱。直接禁用它,让Qt只按-I和-L参数找依赖-skip系列:Qt 5.14有几十个模块,像qtwebengine这种体积巨大且编译依赖极重的模块,在静态交叉编译时几乎必挂,直接跳过-qt-*系列:让Qt使用自带的第三方库(zlib、libpng、libjpeg、freetype、harfbuzz、pcre),而不是去探测系统库。静态编译时这是最稳妥的方案——依赖全部编进Qt库里-qt-sql-sqlite:把SQLite驱动编进Qt SQL模块,省得运行时还要拷插件-openssl-linked:这里要注意,Qt 5.14的OpenSSL支持默认是运行时动态加载的。静态编译时想用系统OpenSSL,就要改成-linked方式。如果你的应用不需要HTTPS,这个参数可以不写,省掉openssl依赖
注意:
-no-feature-cups这类参数是feature级控制,你以为它是禁用一个功能,实际上它是在编译时直接裁剪相关代码路径。对于静态编译,尽量裁剪不需要的功能,能显著缩小最终可执行文件的体积。
3.2 依赖库移植到sysroot的具体步骤
我以libfontconfig和libfreetype为例说明依赖库怎么放进sysroot。这里的核心思路是:在目标板(或者用QEMU模拟的aarch64系统)上,把需要的库文件找到,拷贝到交叉编译器的sysroot目录下。
如果你的目标板已经跑起来了,直接在板子上执行:
dpkg -L libfontconfig1 | grep "\.so" dpkg -L libfreetype6 | grep "\.so"把输出的.so文件(包括软链指向的真实文件)scp回本机,放到/usr/aarch64-linux-gnu/lib/下。
但静态编译需要的是.a静态库。如果你目标板上只装了动态库,那么还需要安装对应的-dev包:
sudo apt install libfontconfig1-dev libfreetype6-dev安装完成后再在板子上找.a文件:
dpkg -L libfontconfig1-dev | grep "\.a" dpkg -L libfreetype6-dev | grep "\.a"同样的方式拷贝回本机sysroot。注意:拷贝时保持目录结构,头文件放到/usr/aarch64-linux-gnu/include/,库文件(.a和.so)放到/usr/aarch64-linux-gnu/lib/。
这步如果不做,后果很直接:configure时Qt检测不到fontconfig,字体会退回使用自带freetype去渲染,显示效果会大打折扣,而且Qt源码里依赖fontconfig的代码会被条件编译剪掉,后面想加回来就得从头再编一遍。
3.3 执行configure与常见报错
执行上面的configure命令后,正常情况它会跑几分钟,输出一堆检测摘要。看到如下字样基本就是成功了:
Qt is now configured for building如果中途报错,八成是以下两种:
第一种,找不到编译器。检查aarch64-linux-gnu-g++是否在PATH里,如果没在,就在configure前把工具链路径加进去:
export PATH=/usr/bin:$PATH注意/usr/bin这个目录是Ubuntu官方交叉编译器默认安装位置,如果你用的是板卡厂商的工具链,路径完全不一样。
第二种,某个依赖库检测不过。configure的日志里会明确打印ERROR: ... not found,这时候根据错误提示去sysroot里补对应的头文件或库文件。
经验:configure过程输出非常长,建议执行时加个日志重定向
./configure ... 2>&1 | tee configure_qt5.14.2_aarch64.log。后面出问题了翻日志比瞎猜快得多。
3.4 make与make install
configure通过之后就是编译环节:
make -j$(nproc)这里有个细节:对于交叉编译,-j并发数不能盲目拉满。我实测过,32核的机器编译Qt时如果开32线程,内存占用能飙到16GB以上,小内存机器会直接OOM。建议按物理核数的1.5倍来,比如8核机器用-j12。
整个Qt base模块(qtbase)的编译耗时,在8核机器上大约40分钟到1小时,如果跳过了那些重型模块,总时间能控制在2小时内。编完后执行:
make install注意安装路径由-prefix参数决定,我这里是/opt/Qt5.14.2-aarch64-static。安装完成后检查:
ls /opt/Qt5.14.2-aarch64-static/lib/你应该能看到libQt5Core.a、libQt5Widgets.a等一堆静态库文件。如果看到的是.so文件,说明configure参数没生效,回去查-static有没有写对。
4. 应用层如何链接这套静态Qt
4.1 交叉qmake的使用方式
Qt安装完成后,真正落地使用时,用的不是宿主机的qmake,而是交叉编译版qmake。路径在:
/opt/Qt5.14.2-aarch64-static/bin/qmake建立交叉编译环境,我习惯写一个环境变量脚本,文件名随意,比如setenv_qt_aarch64.sh:
export QT_ROOT=/opt/Qt5.14.2-aarch64-static export PATH=$QT_ROOT/bin:$PATH export ARCH=aarch64 export CROSS_COMPILE=aarch64-linux-gnu- export CC=${CROSS_COMPILE}gcc export CXX=${CROSS_COMPILE}g++ export AR=${CROSS_COMPILE}ar export LD=${CROSS_COMPILE}ld export RANLIB=${CROSS_COMPILE}ranlib export STRIP=${CROSS_COMPILE}strip export PKG_CONFIG_PATH=每次开新终端,先source setenv_qt_aarch64.sh。这个脚本有几个关键点:
PKG_CONFIG_PATH要置空,否则系统pkg-config路径会干扰静态链接时的依赖查找CROSS_COMPILE前缀让后续调用的交叉工具链版本统一QT_ROOT让qmake能找到Qt自己的安装前缀
4.2 一个最小Qt Widgets程序的手动编译示例
写一个最小窗口程序,看看静态编译的实际效果。新建一个目录,放两个文件:
main.cpp内容:
#include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label("Hello from Qt 5.14.2 static on aarch64!"); label.resize(400, 200); label.show(); return app.exec(); }hello.pro内容:
QT += widgets SOURCES += main.cpp TARGET = hello然后执行:
source setenv_qt_aarch64.sh mkdir build && cd build qmake ../hello.pro make编译过程如果顺利,最终会在build目录生成一个hello的可执行文件。关键验证手段是看文件格式和动态依赖:
file hello readelf -d hello | grep NEEDEDfile输出里应该显示ELF 64-bit LSB executable, ARM aarch64,readelf输出的NEEDED列表里应该只有一个libc.so.6和libstdc++.so.6这种基础库,绝对不应该出现libQt5Widgets.so这种动态依赖。如果NEEDED里还能看到libQt5开头的东西,说明你链接的Qt库还是动态编译版本,不是静态库版本。
用ls -lh看一下这个hello文件,大小通常在2MB到5MB之间,这比动态编译加一堆.so搬板子的方案简洁太多了。
5. 编译过程中的疑难杂症与解决方案
5.1 GCC 11导致的编译错误专治
前面提到Ubuntu 22.04自带的交叉编译器是GCC 11,而Qt 5.14.2的代码是用GCC 9时代的标准写的,编译时会撞上几个C++标准相关问题。最经典的一个报错出现在qtbase的qglobal.h里:
qglobal.h: error: 'numeric_limits' is not a member of 'std'或者类似<limits>头文件找不到的问题。原因是Qt 5.14.2在某些头文件里没有显式include<limits>,GCC 9的预处理链恰好通过其他头文件间接引入了它,GCC 11改动后间接包含关系失效了。
解决办法是改mkspec,在qtbase/mkspecs/linux-aarch64-gnu-g++/qmake.conf里给QMAKE_CXXFLAGS加一行:
QMAKE_CXXFLAGS += -include limits这样等于让编译器在每个编译单元开头强制include limits头文件,暴力但有效。
还有人会遇到另一个GCC 11相关报错,和std::result_of被废弃有关。Qt源码里有些地方用了Q_DECLARE_TYPEINFO配合std::result_of,GCC 11把std::result_of标记为deprecated之后,Qt的-Werror会让这些警告直接变成错误。如果遇到,去qmake.conf里把-Werror去掉:
QMAKE_CFLAGS -= -Werror QMAKE_CXXFLAGS -= -Werror5.2 链接阶段报undefined reference的处理
应用编译好,链接时如果报一大堆undefined reference to 'Fc*'或者undefined reference to 'FT_*',说明依赖库的静态库没在链接路径里。Qt自身link的时候同样会遇到。
解决办法分两步。第一步确保sysroot里确实存在对应的.a文件,比如:
ls /usr/aarch64-linux-gnu/lib/libfontconfig.a ls /usr/aarch64-linux-gnu/lib/libfreetype.a如果不存在,去目标板拷或者用交叉工具链重新编一个。
第二步,在链接应用时,手动把依赖库加进去。一种做法是在.pro文件里加LIBS:
LIBS += -L/usr/aarch64-linux-gnu/lib -lfontconfig -lfreetype另一种更省事的做法是在configure阶段直接用-qt-freetype,让Qt完全使用自带的freetype,这样就不会有对外部libfreetype的依赖问题。fontconfig没法用-qt-方式内置,所以如果确定不需要系统字体管理能力,干脆在configure时通过-no-fontconfig禁掉它,省心很多。
5.3 哪些Qt模块最容易在静态交叉编译时翻车
说几个我实际踩过的深水区:
qtwebengine是最难啃的骨头。它内部用Chromium的构建系统,对交叉编译的支持非常有限,对工具链版本和Python版本有苛刻要求,静态编译基本等于给自己找不痛快。所以除非你的产品非要内嵌网页展示,否则直接-skip qtwebengine。
qtdeclarative(QML模块)本身可以编,但它运行时要依赖一个qmlcachegen工具,交叉编译时这个工具会被编译成x86_64版本然后交叉环境里可能找不到。5.14.2这个版本有个已知问题:在静态编译时,qmlcachegen的架构检查会误判,导致一部分QML运行时编译不过去。如果你的应用只用Widgets,skip掉qtdeclarative能省一个小时编译时间。
qtserialport和qtcharts这两个模块交叉编译本身没问题,但如果你的应用里用到了,要注意静态链接时模块插件(plugin)的处理。静态编译下Qt模块插件默认是编译进库里的,不会自动生成插件目录,需要手动在main函数里调用Q_IMPORT_PLUGIN宏来注册,否则会出现“程序运行起来功能没反应”的诡异现象。
5.4 终极排查工具:ldd、readelf和交叉版file
整个静态交叉编译链路上,我最推荐你熟练掌握的三个排查命令:
file:看ELF文件架构是否正确。file a.out确认是ARM aarch64而不是x86-64,这能立刻判断是否编译到了错误架构readelf -d xxx | grep NEEDED:看动态依赖列表。如果依赖列表干净,说明确实是静态链接;如果出现libQt5开头的项,说明Qt库路径配置错了aarch64-linux-gnu-readelf -A xxx:看是否有硬浮点或软浮点ABI问题。Qt 5.14默认armv8-a架构,如果目标板是armv7的老平台,二者ABI不匹配,运行时会直接报Illegal instruction
这三个命令配合使用,能解决90%的“编译过了但跑不起来”的问题。
6. 静态编译产物的部署与运行验证
6.1 把编译产物拷贝到目标板的注意事项
静态编译最大的价值就在这里:拷贝一个文件,就能运行整个Qt应用。但有几个细节需要注意。
首先,那个单独的可执行文件依赖的libc和libstdc++版本必须和目标板的系统版本匹配。比如你在Ubuntu 20.04的交叉环境里编出来的程序,目标板如果还在用CentOS 7(glibc 2.17)就会报version GLIBC_2.28 not found。这种问题静态编译也躲不开,因为libc.so.6不可能是纯静态的。解决办法有两种:一是在更老的发行版里做交叉编译,二是目标板升级内核和rootfs。
其次是运行时资源目录。Qt应用运行时需要找qt.conf指定的资源路径,否则会默认在可执行文件同目录下找plugins、qml、translations这些目录。如果你的程序不在代码里硬编码资源路径,纯静态编译后这些插件目录也需要一起拷贝到目标板,否则会出现“程序能启动但窗口渲染不出来”的情况。一个稳妥的做法是在可执行文件同目录放一个qt.conf,内容:
[Paths] Prefix = . Plugins = plugins最后,静态编译的二进制对CPU特性有隐含要求。编译器默认生成的ARMv8-A代码需要目标板的CPU支持。树莓派4B、RK3399、RK3588这些aarch64芯片都没问题,但如果目标板是老旧的Cortex-A53低配环境,需要额外注意编译参数里是否要加-march=armv8-a+crc这类兼容选项。
6.2 目标板上运行验证清单
部署到目标板之后,我按顺序做这几件事验证:
# 1. 确认二进制可执行 chmod +x hello # 2. 确认依赖干净 ldd hello正常输出只有:
linux-vdso.so.1 (0x0000ffff...) libstdc++.so.6 => /usr/lib/aarch64-linux-gnu/libstdc++.so.6 libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6接着运行:
QT_QPA_PLATFORM=offscreen ./hello先用offscreen平台跑一下,如果程序能正常起进程且不崩溃,说明基础Qt运行时没问题。如果显示有问题,再用QT_QPA_PLATFORM=linuxfb或者eglfs去适配实际显示后端。
如果用到字体,检查一下系统里有没有中文字体,如果没有,程序里用QFont指定字体的地方会输出警告。静态编译时字体文件不会被带进程序里,除非你用QResource把字体嵌到二进制里。我一般建议把一部份常用字体直接打包到程序的资源文件(.qrc)中,一劳永逸。
6.3 体积优化策略
静态编译的可执行文件体积总是一个绕不开的话题。一个最简单的Qt Widgets程序带-static编译出来通常在3MB到6MB之间,看着还好,但如果加上Qt Charts、Qt Network、Qt SVG这些模块,体积会迅速膨胀到20MB以上。
我实测有效的瘦身手段有这么几个:
- 编译时加
-no-feature-*裁剪不需要的代码路径,比如-no-feature-printdialog、-no-feature-concurrent这类 - 链接应用时加
-Wl,--gc-sections让链接器去除未引用的代码段 - 对最终可执行文件执行
aarch64-linux-gnu-strip hello,去掉符号表
三条叠加下来,体积减一半是常态。注意strip必须在交叉编译工具链下执行,用host的x86_64 strip会直接把文件搞坏。
7. 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| configure时提示cannot find -lGL | 启用了OpenGL相关模块但目标板没有libGL | 加-no-opengl重新configure |
| make过程中大量c++ internal compiler error | 并发数太高导致内存吃紧,或工具链有bug | 降低-j参数;考虑换用Linaro工具链 |
链接应用时undefined reference toqt_plugin_* | 静态插件未注册 | 在main中使用Q_IMPORT_PLUGIN手动导入 |
| 程序放板子上提示Executor format错误 | 编出来是x86_64架构 | 检查qmake是否是交叉版的,确认PATH环境变量 |
| 运行时报找不到libQt5Core.so.5 | 实际链接到了动态Qt库 | 确认configure时有-static,重新make install |
| configure提示The test for linking against libssl failed | OpenSSL版本不匹配或sysroot缺库 | 加-no-openssl跳过TLS,或手动把正确版本libssl放入sysroot |
| 程序跑起来中文全部显示豆腐块 | 系统无中文字体 | 在.qrc里嵌入字体,或目标板安装字体 |
这表里每一条都是我和同事实际遇到过的,尤其是静态插件那个问题,很多人折腾半天以为是代码bug,实际上只是少了一行宏注册。
8. 一些个人心得
关于Qt 5.14.2的aarch64静态交叉编译,最后说几点我的体会。
第一,不要迷信一次configure就能成功。整个编译链路里最容易出问题的就是依赖库检测。每加一个新模块,就多一组系统依赖需要处理。正确做法是先按最小集(qtbase)跑通,再逐步加模块,每次加完都能编过再继续。一上来就全部编译,报错了根本不知道是哪个依赖出了幺蛾子。
第二,日志保存是个好习惯。我在每一步输出都留了一份日志文件存档,后面遇到新的问题翻旧日志,常常能找到蛛丝马迹。比如某个库到底是configure时被Skip掉还是编译时才报错,在这两份日志里表现得完全不一样。
第三,交叉编译工具链的版本极其敏感。我用Ubuntu 20.04的gcc 9编译整个Qt库一次性通过,换到Ubuntu 22.04的gcc 11就需要加-include limits的补丁。如果你的项目对时间要求紧,直接上20.04配合gcc 9是最省心的组合。
第四,这个方案真正的爽点在于交付。可执行文件加几个资源文件,打包成一个tar包就能分发到任意一台同架构板子上跑,不用装Qt runtime,不用配环境变量,不用处理.so之间的兼容性。对于产品批量部署来说,这带来的工作量下降不是一点点。
我倒不是建议所有场景都走静态编译——如果你的产品还需要动态加载第三方插件,静态方案会让事情变复杂。但对于那些“写一个工具性应用、跑在固定硬件上、不想被运行时环境束缚”的场景,这套Qt 5.14.2 + aarch64静态交叉编译的方案,我个人觉得是当前阶段性价比很高的选择。按上面的流程走一遍,两三天内你就能得到一套自己可控的交叉编译环境,后续维护和调试也有据可依。