尧图精选

aarch64 上 Qt 5.14.2 静态编译实战:交叉编译与部署指南

🕒 发布时间:2026/9/20 2:27:27 📁 来源:尧图网络
1. 为什么要在 aarch64 上折腾 Qt 5.14.2 静态编译如果你手上有 Orange Pi CM5、树莓派这类 aarch64 开发板又恰好需要跑一个带界面的 Qt 程序大概率会遇到一个很现实的问题板子上的系统版本五花八门glibc 版本、OpenSSL 版本、字体配置各不相同动态链接过去的程序换个系统就跑不起来。静态编译 Qt 就是解决这类部署噩梦最直接的手段——把 Qt 库直接塞进可执行文件里拷过去就能跑不依赖目标机上的 Qt 运行库。Qt 5.14.2 这个版本有点特殊。它是 Qt 5.14 系列的最后一个补丁版本也是很多工业项目和嵌入式产品长期锁定的版本稳定性经过大量验证。而 aarch64 架构现在已经是主流嵌入式 Linux 的标配从国产开发板到各类边缘计算盒子基本都是这个架构。把这两者结合起来做静态交叉编译是很多做嵌入式 Qt 产品的团队绕不开的一道坎。这篇手册面向的是需要在 x86_64 主机上为 aarch64 目标板构建静态 Qt 的开发者。我会从工具链准备开始一路讲到 configure 参数怎么配、编译过程中会遇到哪些坑、怎么验证产物能不能用。整个过程我在多个项目里反复走过踩过的坑都会写清楚你照着做基本能一次跑通。需要提前说明的是静态编译 Qt 是个吃资源、吃时间的活。一台普通的开发机完整编译一遍大概需要 1 到 3 小时磁盘占用在 15GB 以上。所以开始之前先确认你的机器有足够的空间和时间别编译到一半磁盘满了那才叫难受。2. 交叉工具链的选型与主机环境准备2.1 工具链到底选哪个aarch64 的交叉工具链有好几个来源常见的有 Linaro 的 GCC 工具链、ARM 官方维护的 GNU Toolchain、以及各家芯片厂商自己打包的版本。选哪个不是随便挑的核心原则是工具链的 glibc 版本要低于或等于目标板系统的 glibc 版本。原因很简单静态编译虽然把 Qt 库静态链接进去了但 libc 本身通常还是动态链接的除非你连 libc 也静态化那样问题更多。如果你的工具链用的是 glibc 2.35而目标板只有 glibc 2.28程序跑起来就会报GLIBC_2.35 not found这类错误。我的建议是选 ARM 官方或 Linaro 的较新稳定版工具链比如gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu这个版本它对应的 glibc 是 2.33兼容性比较广。如果你明确知道目标板的系统版本也可以针对性选择。下载后解压到/opt目录下然后配置环境变量export TOOLCHAIN_PATH/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu export PATH$TOOLCHAIN_PATH/bin:$PATH export CROSS_COMPILEaarch64-none-linux-gnu-验证一下工具链是否可用aarch64-none-linux-gnu-gcc -v能正常输出版本信息就说明配置成功了。2.2 主机上必须装的依赖交叉编译 Qt 本身需要主机上有一套完整的构建工具。在 Ubuntu 或 Debian 上先装这些sudo apt-get install build-essential perl python3 git \ libgl1-mesa-dev libglu1-mesa-dev \ libxkbcommon-dev libxkbcommon-x11-dev \ libfontconfig1-dev libfreetype6-dev \ libssl-dev libdbus-1-dev \ libinput-dev libts-dev \ libmtdev-dev libudev-dev \ bison flex gperf这里有几个包容易被忽略但很关键。gperf是 Qt 构建过程中生成哈希表用的缺了会在编译到一半时报错。libxkbcommon-dev是处理键盘布局的如果你要编译 QtGui 模块就必须要。bison和flex是语法分析器生成工具Qt 的某些模块会用到。提示如果你打算编译 QtWebEngine依赖会多得多而且 aarch64 上编译 WebEngine 极其耗时且容易失败。除非项目强需求否则建议在 configure 阶段直接跳过。2.3 源码获取与目录规划Qt 5.14.2 的源码包可以从 Qt 官方归档下载文件名是qt-everywhere-src-5.14.2.tar.xz。下载后解压到一个空间充足的目录比如/home/yourname/qt-build/。我习惯把源码和构建目录分开这样出问题可以随时删掉构建目录重来不用重新解压源码mkdir -p ~/qt-build/src ~/qt-build/build ~/qt-build/install tar -xf qt-everywhere-src-5.14.2.tar.xz -C ~/qt-build/src源码解压后大概 5GB 左右构建目录会再占 10GB 以上安装目录 2GB 左右。磁盘规划要留够。3. configure 参数逐项拆解与配置策略3.1 静态编译的核心开关Qt 的 configure 脚本参数非常多但真正决定静态编译成败的就那么几个。先看最核心的一组./configure \ -prefix /home/yourname/qt-build/install \ -opensource -confirm-license \ -release \ -static \ -nomake examples -nomake tests \ -no-opengl \ -no-xcb \ -no-eglfs \ -linuxfb \ -qt-zlib -qt-libpng -qt-libjpeg \ -qt-freetype -qt-harfbuzz \ -no-iconv \ -skip qtwebengine \ -skip qtquickcontrols \ -skip qtquickcontrols2 \ -skip qtsensors \ -skip qtlocation \ -skip qtwayland \ -xplatform linux-aarch64-gnu-g逐项解释一下为什么这么配。-static是静态编译的总开关它会让 Qt 的库以.a静态库形式产出最终链接进你的应用程序。-release表示编译发布版本去掉调试符号体积更小、运行更快。-nomake examples -nomake tests跳过示例和测试能省下大量编译时间实测能减少 30% 以上的构建时长。-no-opengl这个要看你板子的实际情况。如果你的目标板没有 GPU 或者不需要 OpenGL 加速直接关掉能避免一堆 EGL 相关的依赖问题。如果板子有 Mali GPU 且你需要 OpenGL ES那就得保留并配置对应的 EGL 库路径。-no-xcb是因为嵌入式板子通常不跑 X11用-linuxfb直接走 framebuffer 更轻量。-no-eglfs也是同理除非你的板子有完整的 EGL 支持。3.2 第三方库的静态化处理静态编译 Qt 时第三方库的处理是个关键点。Qt 自带了一些第三方库的源码副本用-qt-zlib、-qt-libpng这类参数可以让 Qt 使用自带的版本并静态编译进去避免依赖目标机上的系统库。-qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz这几个参数的意思是zlib、libpng、libjpeg、freetype、harfbuzz 全部用 Qt 源码树里自带的版本静态编译。这样最终产物不依赖目标机上的这些库部署最省心。-no-iconv是因为 iconv 在静态链接时容易出问题而且现代系统基本都用 UTF-8不太需要 iconv 做字符集转换。3.3 xplatform 配置文件的定制-xplatform linux-aarch64-gnu-g这个参数指向的是qtbase/mkspecs/linux-aarch64-gnu-g/qmake.conf文件。Qt 5.14.2 默认没有这个目录需要你自己创建。在qtbase/mkspecs/下新建linux-aarch64-gnu-g目录然后创建qmake.confmkdir -p qtbase/mkspecs/linux-aarch64-gnu-g cat qtbase/mkspecs/linux-aarch64-gnu-g/qmake.conf EOF 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-none-linux-gnu-gcc QMAKE_CXX aarch64-none-linux-gnu-g QMAKE_LINK aarch64-none-linux-gnu-g QMAKE_LINK_SHLIB aarch64-none-linux-gnu-g QMAKE_AR aarch64-none-linux-gnu-ar cqs QMAKE_OBJCOPY aarch64-none-linux-gnu-objcopy QMAKE_NM aarch64-none-linux-gnu-nm -P QMAKE_STRIP aarch64-none-linux-gnu-strip load(qt_config) EOF同时创建qplatformdefs.hcat qtbase/mkspecs/linux-aarch64-gnu-g/qplatformdefs.h EOF #include ../linux-g/qplatformdefs.h EOF这个 mkspec 文件是整个交叉编译的枢纽编译器路径、链接器、默认平台插件都在这里定义。QT_QPA_DEFAULT_PLATFORM linuxfb决定了程序默认使用 framebuffer 作为显示后端。3.4 一个容易忽略的 sysroot 问题如果你的工具链带了 sysroot很多厂商工具链都带需要在 qmake.conf 里额外指定QMAKE_CFLAGS --sysroot$TOOLCHAIN_PATH/aarch64-none-linux-gnu/libc QMAKE_CXXFLAGS --sysroot$TOOLCHAIN_PATH/aarch64-none-linux-gnu/libc QMAKE_LFLAGS --sysroot$TOOLCHAIN_PATH/aarch64-none-linux-gnu/libc不带 sysroot 的工具链就不用加这几行。判断方法很简单看工具链目录下有没有libc或sysroot子目录。4. 编译过程中的典型报错与排查链路4.1 编译到一半报 gperf 缺失这个报错通常出现在编译qtbase的某个阶段提示gperf: command not found。原因就是主机上没装 gperf。解决办法sudo apt-get install gperf装完之后不需要重新 configure直接继续 make 就行。但如果你已经 make 到很后面了建议make clean后重新来避免中间产物状态不一致。4.2 找不到 libxkbcommon报错信息类似ERROR: Feature xkbcommon was enabled, but the pre-condition libs.xkbcommon failed。这说明主机上缺少 libxkbcommon 的开发包。装一下sudo apt-get install libxkbcommon-dev libxkbcommon-x11-dev如果你确实不需要键盘布局功能比如纯触摸屏应用也可以在 configure 时加-no-xkbcommon直接关掉。4.3 链接阶段报 undefined reference to__atomic_*这是 aarch64 上很典型的一个问题。某些原子操作在 aarch64 上需要链接libatomic。解决办法是在 qmake.conf 的链接标志里加上QMAKE_LFLAGS -latomic或者在 configure 时通过QMAKE_LFLAGS环境变量传入。这个坑我在两个不同项目里都遇到过表现是编译都过了最后链接 Qt 库的时候报一堆原子操作相关的未定义符号。4.4 编译 QtSerialPort 时 unknown module如果你在 configure 时没有-skip qtserialport但编译时又报unknown module(s) in qt: serialport通常是因为 qtserialport 模块的依赖没有满足。检查一下是不是缺少libudev-devsudo apt-get install libudev-dev如果确实不需要串口功能直接在 configure 里加-skip qtserialport最省事。4.5 内存不足导致编译中断Qt 的某些模块尤其是 qtdeclarative编译时非常吃内存单个编译单元可能占用 2GB 以上。如果你的机器内存小于 8GB建议限制并行编译任务数make -j4而不是make -j$(nproc)。虽然慢一点但不会因为 OOM 被系统杀掉编译进程。我试过在 16GB 内存的机器上用-j16编译结果在 qtdeclarative 阶段被 OOM killer 干掉白白浪费了一个多小时。5. 安装、验证与产物瘦身5.1 安装与目录结构确认编译完成后执行make install安装到之前-prefix指定的目录。安装完成后目录结构大致是这样install/ ├── bin/ ├── include/ ├── lib/ │ ├── libQt5Core.a │ ├── libQt5Gui.a │ ├── libQt5Widgets.a │ └── ... ├── mkspecs/ └── plugins/ └── platforms/ └── libqlinuxfb.a注意plugins/platforms/下的libqlinuxfb.a这是 linuxfb 平台插件的静态库。静态编译 Qt 时平台插件也是静态库形式需要在应用程序的.pro文件里显式导入。5.2 写一个最小验证程序建一个最简单的 Qt Widgets 程序来验证// main.cpp #include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello aarch64 static Qt); label.show(); return app.exec(); }.pro文件QT core gui widgets TARGET helloqt TEMPLATE app SOURCES main.cpp QMAKE_LFLAGS -static用安装好的 qmake 生成 Makefile/home/yourname/qt-build/install/bin/qmake helloqt.pro make编译出来的helloqt用file命令检查file helloqt应该显示ELF 64-bit LSB executable, ARM aarch64。再用ldd检查动态依赖aarch64-none-linux-gnu-ldd helloqt如果显示not a dynamic executable或者只有 libc、libm、libpthread 这几个基础库说明静态链接成功了。5.3 静态库瘦身与 strip静态编译出来的可执行文件体积通常很大一个简单的 Widgets 程序可能就有 15MB 以上。可以用 strip 去掉符号表aarch64-none-linux-gnu-strip helloqt体积能减少 30% 到 50%。如果还嫌大可以在 configure 时加-no-feature-*关掉不需要的特性比如-no-feature-cups、-no-feature-printdialog等。但要注意关太多特性可能导致某些功能不可用需要根据实际需求权衡。6. 部署到目标板与运行时注意事项6.1 字体和平台插件的处理静态编译的 Qt 程序虽然不依赖 Qt 库但运行时还是需要字体文件和平台插件配置。把编译好的程序拷到板子上后需要确保板子上有可用的字体文件通常在/usr/share/fonts/下设置QT_QPA_PLATFORMlinuxfb环境变量或者程序里通过qputenv设置如果 framebuffer 设备不是默认的/dev/fb0通过QT_QPA_FB_DRM或-platform linuxfb:fb/dev/fb1指定一个常见的启动脚本#!/bin/sh export QT_QPA_PLATFORMlinuxfb export QT_QPA_FONTDIR/usr/share/fonts/truetype/dejavu ./helloqt6.2 触摸屏校准如果你的板子带触摸屏linuxfb 平台下需要配置 tslib 或 libinput。Qt 5.14.2 支持通过QT_QPA_EGLFS_TSLIB或QT_QPA_FB_TSLIB环境变量启用 tslib。前提是编译 Qt 时链接了 tslib 库。如果触摸方向不对可以通过 tslib 的ts_calibrate工具校准或者设置TSLIB_CALIBFILE指向校准文件。6.3 一个实际项目中的经验我在一个基于 Orange Pi CM5 的工业 HMI 项目里用过这套方案。板子跑的是 Ubuntu 20.04 aarch64工具链用的是 gcc-arm-10.3Qt 5.14.2 静态编译。整个流程走下来最大的感受是configure 阶段的参数一定要一次配对不要反复试。因为每次 configure 后重新 make 都要花大量时间如果参数不对宁可多花十分钟想清楚也不要盲目试错。另外静态编译的 Qt 程序在启动速度上比动态链接的版本要快一些因为省去了动态库加载和符号解析的过程。但代价是每个可执行文件都比较大如果板子上要跑多个 Qt 程序磁盘占用会比较可观。最后提醒一点静态编译的 Qt 在许可证上属于 LGPL 的静态链接场景如果你的项目是商业闭源软件需要仔细评估许可证合规性。Qt 的开源版本在静态链接时有额外的义务要求这个在项目立项阶段就要考虑清楚。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →