Qt 5.14.2 aarch64静态交叉编译完整手册:从x86到ARM部署

发布时间:2026/9/19 4:45:14
Qt 5.14.2 aarch64静态交叉编译完整手册:从x86到ARM部署 从 x86 开发机交叉编译一份 Qt再推送到 aarch64 嵌入式设备上跑界面这个流程我前前后后折腾了小两周。网上资料多是零散片段不是缺了 sysroot 配置就是 static 参数没写全照着抄一遍大概率翻车。这次我把 Qt 5.14.2 的 aarch64 静态交叉编译完整走了一遍从工具链、源码、configure 参数到常见报错整理成这篇手册。适合嵌入式 Linux 开发者、需要在 ARM 设备上部署 Qt 界面的同事以及被“unknown module”“cannot find -lGL”这类问题卡住的人。1. 为什么非要做静态交叉编译场景与取舍1.1 动态库方案的真实痛点我手上这台目标设备用的是 aarch64 处理器文件系统精简得只剩基础工具flash 空间按 MB 算。最开始我图省事在开发机上编好 Qt 动态库打了一整包 .tar.gz 传上去结果现场直接给我上了一课设备自带的 glibc 版本和编译机不一致运行时报version GLIBC_2.29 not foundQt 依赖的 libxcb、libGL 在精简系统里更是不可能齐。哪怕你把 Qt 的 so 文件都带上还得处理 rpath、ldconfig、插件目录稍有不慎程序起不来。嵌入式现场没有调试条件时这种问题非常要命。静态交叉编译的思路其实很朴素把 Qt 自身和它依赖的 zlib、libpng、libjpeg、pcre 这些第三方库全部编译成.a静态库然后在编译最终应用时把 Qt 库直接链接进可执行文件。这样目标设备上除了 Linux 内核、基础的 C/C 运行库之外不再需要任何 Qt 相关动态库。部署时一个可执行文件拷过去直接./app -platform linuxfb就能跑升级也只是替换单个文件依赖问题彻底消失。对量产设备和现场维护来说这种确定性的价值远高于省下来的那点存储空间。1.2 静态编译的适用范围与代价不过静态编译也不是免费午餐先泼点冷水。代价主要有三个体积、内存和编译时间。体积上一个最简单的 Qt Widgets 程序静态链接后二进制普遍在 20MB 到 30MB 之间。原因很简单你不需要的模块虽然没被全部链接进去但 Qt Core、Qt GUI、Qt Widgets 这几个基础库本身就很大链接器按需提取目标文件后依然会带到相当多代码。有人会说“那我不用 Widgets只用 QML”省不了多少因为 QML 运行时也是一大坨。内存同理静态程序的启动通常会把需要的代码段全部映射进内存实测一个中等复杂度界面静态版常驻内存可能比动态版多 30% 左右。如果你做的是内存只有 128MB 的深嵌入式设备这就不太划算了。所以我的判断标准是设备 flash 或内存极其紧张且你可以接受现场维护脚本把整个动态库目录一起升级那动态方案可能更合适但如果你和我一样更在意部署可靠性和升级简单性静态几乎是最好的选择。另外静态编译也意味着 Qt 模块一定要裁剪。下面配置里我会跳过一堆用不到的模块这既是减少编译时间也是控制最终静态库体积的关键。2. 搭建交叉编译环境2.1 安装 aarch64 交叉工具链整个环境我建议在 Ubuntu 20.04 LTS 上搭建。不是迷信老版本而是 Qt 5.14.2 发布时主要验证的编译器就是 GCC 9 系列20.04 默认的 gcc-9 交叉工具链和它配合最稳。你要是在 Ubuntu 22.04 或更新系统上做gcc-12 对 C 标准的处理更严格Qt 源码里某些老代码可能会冒出 warning 被当 error还得额外加参数绕过没必要给自己加戏。安装交叉工具链很简单sudo apt update sudo apt install -y build-essential git python3 gperf bison flex \ gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完后验证一下aarch64-linux-gnu-gcc --version正常会输出aarch64-linux-gnu-gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0这一行。工具链的名字前缀是aarch64-linux-gnu-后面所有命令都用这个前缀很方便。除了编译器本体还需要确认/usr/aarch64-linux-gnu目录存在它本质上是交叉编译的 sysroot包含了目标设备运行所需的基本 glibc 头文件和库。2.2 准备 sysroot 和基础依赖sysroot 是交叉编译时头文件与目标库的查找根目录。编译器看到--sysroot/usr/aarch64-linux-gnu就会在这个目录下找usr/include、usr/lib等子目录。Ubuntu 的交叉工具链安装后默认自带一套最小的 sysroot但只有基本 C 库没有 OpenGL、X11 这类东西。如果你 configure 的时候开了 OpenGL 或 xcb配置阶段就会因为找不到头文件和库而失败。这里有两个处理思路。一是从目标设备上把整个根文件系统拷贝出来放到/opt/sysroot-aarch64然后通过--sysroot指定。这种最贴近真实运行环境缺点是 rootfs 可能很大而且拷贝过程中软链接容易坏排查麻烦。二是让 Qt 自己携带第三方库源码用-qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -qt-harfbuzz这组参数让 Qt 在编译过程中顺带把依赖的 zlib、png、jpeg、pcre 一起编译成静态库外部 sysroot 只需要提供最基础的 C/C 库。我强烈建议非必要不折腾 sysroot第二种方式能解决 90% 的依赖问题。唯一需要单独处理的常见外部依赖是 OpenSSL。如果你程序里要跑 HTTPS要么把它静态编译进 sysroot要么在 configure 时用-openssl-linked指定交叉编译好的 OpenSSL 库路径。如果只需要 http 协议那就可以直接-no-openssl跳过。2.3 下载 Qt 5.14.2 源码包Qt 官方提供 all-in-one 的源码包qt-everywhere-src-5.14.2.tar.xz下载地址在download.qt.io/archive/qt/5.14/5.14.2/。整个包大约 500MB解压后好几个 GB注意留足磁盘空间。wget https://download.qt.io/archive/qt/5.14/5.14.2/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源码包里面已经包含了 Qt Base、Qt SerialPort、Qt Charts 等大部分模块的源码所以后面遇到unknown module(s) in QT: serialport时不需要重新下载东西直接进到对应子目录单独编译安装就行这个坑我在第 5 节详细说。3. configure 参数逐条拆解3.1 核心编译选项编译 Qt 前最关键的步骤就是 configure参数决定了最终库的形态。我把核心参数列成表格每一项为什么要这么写下面详细说。配置项作用我的选择-static编译出静态库而不是动态库-static-xplatform linux-aarch64-gnu-g指定目标平台为 aarch64必须-platform linux-g主机编译工具默认用 x86 g默认-release编译 release 版本不开调试符号-release-no-opengl禁用 OpenGL 模块看需求-no-xcb不编译 xcb 平台插件嵌入式常用-qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -qt-harfbuzz使用 Qt 自带第三方库源码强烈建议-skip ...跳过不用的模块按需-static是整个过程的灵魂漏了它后面全白做。-xplatform linux-aarch64-gnu-g告诉 configure 使用 Qt 自带的 mkspec 文件这个 mkspec 位于qtbase/mkspecs/linux-aarch64-gnu-g里面已经写好了 aarch64 编译器的名字和默认参数。有人会问既然-xplatform指定了目标平台为什么还要-platform linux-g这是因为 Qt 编译过程中有些工具是在主机上运行并生成的比如 moc、rcc、uic它们必须用本机编译器先编译出 x86 版本而目标库才用交叉编译器。两个参数各管一头缺一不可。3.2 模块裁剪与保持Qt 源码包默认模块非常多而嵌入式设备通常用不到 3D、WebEngine、多媒体这些大块头。不裁剪的话静态库体积会爆炸编译时间也可能拖到几个钟头。configure 提供了-skip参数可以跳过指定模块。我常用的跳过列表如下-skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtspeech -skip qtsvg -skip qttools -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns这里有个细节如果你的界面需要加载 SVG 图片qtsvg不能跳过把-skip qtsvg从列表里去掉就行。我上面给出的默认配置是跳过的因为嵌入式设备里我基本都是用 PNG没必要为 SVG 增加体积。还要注意qttools这个模块它包含 lupdate、lrelease、linguist 等工具做国际化时会用到。交叉编译 qttools 有时会碰到 X11 相关依赖比较麻烦我的做法是-skip qttools后面需要 lrelease 时单独在宿主机上编译一个 x86 版本的 Qt 工具链来生成 .qm 文件这样两边不打架。另外注意别把qtserialport加进 skip 列表否则第 5 节的 serialport 就不会被编译出来。3.3 一个可以直接抄的 configure 命令下面是我实际跑通的完整 configure./configure -prefix /opt/qt5.14.2-aarch64-static \ -opensource -confirm-license \ -release -static \ -platform linux-g \ -xplatform linux-aarch64-gnu-g \ -no-opengl -no-xcb -no-glib -no-iconv \ -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre -qt-harfbuzz \ -nomake examples -nomake tests \ -skip qt3d -skip qtactiveqt -skip qtandroidextras \ -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d \ -skip qtdeclarative -skip qtdoc -skip qtgamepad \ -skip qtlocation -skip qtlottie -skip qtmacextras \ -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing \ -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 \ -skip qtquicktimeline -skip qtremoteobjects -skip qtscript \ -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtspeech \ -skip qtsvg -skip qttools -skip qtwayland -skip qtwebchannel \ -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets \ -skip qtwebview -skip qtxmlpatterns \ -make libs -make tools 21 | tee configure.log-prefix是安装路径我放在/opt/qt5.14.2-aarch64-static后缀 static 是为了和本机动态版区分。-nomake examples -nomake tests不编译示例和测试省时间。-no-glib -no-iconv关闭 glib 和 iconv 依赖嵌入式环境通常没有这两样没必要让 configure 去检查。-no-xcb很关键我们最终目标板没有 X11跑的是 framebuffer不关掉 xcb 的话 configure 会去找一堆 X11 开发库在 sysroot 里大概率找不到。-make libs -make tools确保只生成库文件和必要的命令行工具qmake 等而不是生成全部示例。执行上面命令后屏幕会滚动很久结束后一定看一眼最后输出的配置摘要确认Build type: static、Platform: linux-aarch64-gnu-g这两行是你想要的结果并且没有红色的 error。4. 编译与安装实测记录4.1 从 make 到 make installconfigure 无误后编译阶段反倒是最机械的。直接make -j8 21 | tee build.log关于-j参数我要多说一句。很多人喜欢直接-j$(nproc)把机器所有核都跑满。Qt 整个源码树的编译对内存消耗非常大16 核机器开-j16很可能直接 OOM。我实测 8 核 i7 机器用-j8编译整个配置大概 45 分钟到 1 小时期间内存占用接近 10GB。如果你机器内存只有 8GB建议-j4慢一点至少不会崩。编译完成后执行make install 21 | tee install.log这会把文件装到/opt/qt5.14.2-aarch64-static目录下。4.2 检查安装结果安装完别急着走先检查目录结构ls /opt/qt5.14.2-aarch64-static/lib你会看到里面是libQt5Core.a、libQt5Gui.a、libQt5Widgets.a这类.a文件而不是.so。这才是静态库的标志。同时bin/目录下会有qmake和lrelease这些是用 x86 编译器生成的工具可以在开发机上直接运行。然后验证 Qt 平台插件也进来了。静态编译下插件不是以.so形式存在于plugins/platforms/而是编译成.a静态库链接时会通过一个叫qt_plugin_instance的机制被拉进最终程序。如果你用了 framebuffer 方案plugins/platforms/下应该能看到libqlinuxfb.a。这是后面目标板上能不能正常显示的关键。4.3 用交叉编译的 qmake 编译一个最小测试程序安装完成后写个最简单的 Widgets 程序验证整套工具链。main.cpp#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello aarch64 Static Qt); label.resize(240, 80); label.show(); return app.exec(); }test.proQT core gui widgets CONFIG static TARGET hello TEMPLATE app SOURCES main.cpp编译时指定交叉编译的 qmake 路径export PATH/opt/qt5.14.2-aarch64-static/bin:$PATH qmake test.pro make -j4编译出来的hello就是 aarch64 架构的静态二进制。用 readelf 检查一下动态依赖aarch64-linux-gnu-readelf -d hello你会看到除了libstdc、libc、libm这些系统库之外没有libQt5Core.so、libQt5Widgets.so的身影说明 Qt 已经全部静态进去了。把这个文件和目标板上对拷过去跑./hello -platform linuxfb板上如果有 framebuffer 设备/dev/fb0就能看到窗口。5. 常见问题专题与排查手册5.1 serialport 模块缺失的解决办法开发中经常遇到.pro文件里写了QT serialport编译时报Project ERROR: unknown module(s) in QT: serialport这个错误的核心原因是当前 Qt 实例的 module 配置里没有 serialport。虽然 Qt 5.14.2 的源码包里包含了 qtserialport但如果你 configure 时没有显式启用它默认生成的模块清单里可能就不带。解决办法不是重编整个 Qt而是单独编译 qtserialport 模块再安装进去。在源码根目录执行cd qt-everywhere-src-5.14.2/qtserialport /opt/qt5.14.2-aarch64-static/bin/qmake make -j4 make install注意先用你自己的prefix路径下的 qmake 来生成 Makefile别用系统 qmake。安装完成后再检查/opt/qt5.14.2-aarch64-static/mkspecs/modules目录下是否有qt_lib_serialport.pri文件如果有说明模块注册成功。回到你的应用目录重新qmake makeserialport 模块就能被识别了。5.2 OpenGL 和 GL 链接失败有个报错频率极高cannot find -lGLESv2 cannot find -lGL出现这个问题十有八九是 configure 阶段没有把 OpenGL 相关选项关干净。Qt 在 Linux 上默认会尝试编译 OpenGL 相关代码但交叉编译的 sysroot 里没有包含 GL 库于是链接阶段失败。解决办法是在 configure 时加-no-opengl同时在代码里不要依赖 QOpenGLWidget 等类。如果你的业务确实需要 OpenGL ES 渲染那就必须在 sysroot 中提供对应的 Mali/Adreno 厂商 GLES 头文件和.so库并且 configure 参数要改成-opengl es2这属于另一个深坑这里不展开。5.3 Qt 库版本不匹配还有一类报错长这样cannot mix incompatible Qt library (5.15.3) with this library (5.15.2)这类问题几乎都出在环境变量或路径污染上。开发机上往往既装了系统的 Qt又装了我们要的交叉编译 Qt如果 PATH 设置错误或者.qmake.stash缓存了之前某个 Qt 的路径就可能导致应用在 include 头文件时拿到 5.15.3 的定义链接时却指向 5.14.2 的库。我的排查习惯是先which qmake确认当前用的是不是/opt/qt5.14.2-aarch64-static/bin/qmake再检查pkg-config --modversion Qt5Core确保 pkg-config 环境变量没有指向宿主机 Qt 目录。最后删掉项目目录下的.qmake.stash、Makefile等缓存文件重新 qmake。5.4 静态编译后的部署技巧与国际化程序静态编译完部署单文件虽然方便但有一个副作用插件没法动态加载了。比如 framebuffer 平台插件动态版 Qt 下只要运行时指定-platform linuxfb插件会从plugins/platforms/目录加载。静态版不行必须在.pro文件里显式声明QTPLUGIN qlinuxfb CONFIG static这样链接时才会把 linuxfb 插件拉进可执行文件。如果用了 gif 图片支持可能还需要QTPLUGIN qgif。总之凡是动态版下依赖.so插件才能用的功能静态版都要在构建时声明这点最容易漏。最后讲下国际化。嵌入式设备上的静态程序依然可以用 Qt 的翻译机制关键是用对工具链。由于交叉编译的 qttools 被我们跳过了lrelease 工具不在安装目录下我通常在宿主机上用系统 Qt 的 lrelease 把.ts文件生成.qmlrelease zh_CN.ts -qm zh_CN.qm然后代码里加载翻译文件QTranslator trans; if (trans.load(:/i18n/zh_CN.qm)) { app.installTranslator(trans); }注意trans.load的路径如果.qm文件是通过.qrc资源文件嵌入二进制的就要用:/i18n/zh_CN.qm如果程序运行时从外部目录读取就用QFileInfo判断文件存在后再 load避免找不到文件导致黑屏。5.5 一张速查表帮你少踩坑错误现象可能原因解决方案Project ERROR: unknown module(s) in QT: serialportserialport 模块未编译安装进入 qtserialport 子目录交叉编译并 make installcannot find -lGLESv2/cannot find -lGLOpenGL 相关库缺失configure 加-no-openglcannot mix incompatible Qt library (5.15.3) with this library (5.15.2)环境变量污染多个 Qt 版本混用确认which qmake清理缓存重编运行时Could not find the Qt platform plugin linuxfb静态插件未链接.pro 里加QTPLUGIN qlinuxfbconfigure 时报没有 aarch64 编译器工具链未安装或 mkspec 写错安装 gcc-aarch64-linux-gnu检查-xplatform编译过程中 OOM 崩溃多核编译内存耗尽降低-j参数或增加 swap我在实际编译过程中还踩过一个小坑把-platform linux-g漏了默认 configure 可能用交叉编译器去编译 moc结果 moc 运行时直接 Segmentation fault。这个参数虽然不起眼但千万别省。另外一个经验是每次 configure 前先make distclean清空上一次缓存尤其是改 configure 参数时否则旧配置会残留干扰新构建。整个流程走下来我最大的体会是静态交叉编译这事60% 的功夫在 configure20% 在环境变量剩下 20% 是对着日志排查。所以 configure.log、build.log、install.log 这三个日志文件一定要保存好出问题时搜索日志关键字比重新猜配置高效得多。如果你也刚好在折腾 Qt 在 aarch64 设备上的静态编译按这份手册走一遍应该能少熬几个通宵。