资讯动态

Qt 5.14.2 aarch64静态交叉编译环境搭建全攻略

发布时间:2026/9/16 10:47:18 来源:尧图企业网站定制
做嵌入式Linux开发的朋友迟早会遇到一件事手里有一块aarch64架构的板子可能是RK3399、树莓派4B也可能是某个国产工业核心板要在上面跑Qt界面。如果设备资源紧张、又不想在目标板上折腾一堆动态库依赖那么“Qt静态交叉编译”这条路基本绕不开。Qt 5.14.2是我在实际项目中用得最稳的版本之一本文就完整记录从零搭建aarch64静态交叉编译环境的全过程包括工具链选择、sysroot准备、configure参数逐项拆解、编译安装以及应用部署全是实际操作中趟过坑之后沉淀下来的经验适合正在搭建嵌入式Qt开发环境、或准备做国产化/ARM平台软件移植的开发者参考。1. 整体思路与技术选型动手之前先把思路理清楚这个项目表面上是“编译一个Qt库”实际上是一整套交叉编译工具链的整合工作任何一个环节出错都会让后续编译功亏一篑。1.1 为什么选Qt 5.14.2Qt版本非常多5.9、5.12、5.15、6.x各有拥趸。我选5.14.2是因为它在稳定性和新特性之间拿捏得比较好支持aarch64平台已经很成熟mkspecs目录下直接有linux-aarch64-gnu-g可用不需要自己修改太多配置同时又没有6.x那么严格的编译要求。另外5.14.2是LTS版本官方持续维护过一段时间社区资料也相对丰富遇到问题搜一下基本都有答案。需要说明的是Qt 6.x之后对静态编译、插件系统的处理方式有些变化很多旧工程的pro文件需要改动对于从零搭建嵌入式环境的项目来说5.14.2省去很多麻烦。1.2 静态编译与动态编译的取舍在讲具体步骤之前先把静态和动态两条路线说清楚。动态编译是Qt默认方式生成的可执行程序在目标板上运行时需要加载libQt5Core.so、libQt5Gui.so等一堆动态库。好处是多个程序可以共享库文件体积小坏处是目标板上必须把这些库和插件部署到位LD_LIBRARY_PATH、qt.conf、插件目录稍有不对程序起不来。静态编译则是把Qt核心库、插件全都打包进可执行文件里生成一个“大而全”的单一二进制。优点非常明显目标板上不需要安装任何Qt运行时环境拷贝过去直接执行程序启动不受系统库状态影响部署流程从“拷贝一堆文件”简化成“拷一个文件”。缺点也客观存在可执行文件体积会膨胀到几MB甚至几十MB编译时间更长但嵌入式设备存储空间通常以GB计这点体积成本可以接受。我个人的实践建议是如果你的产品要批量部署到几十上百台设备上静态编译绝对是省心方案如果只是开发调试阶段动态编译迭代更快。1.3 交叉编译的整体流程交叉编译的本质是“在x86主机上用aarch64工具链编译出能在ARM64设备上运行的程序”。Qt库本身也是一个大型C项目所以整个流程可以拆成这么几大块准备交叉编译工具链gcc/g、binutils、sysroot准备Qt源码包用configure配置Qt的编译选项编译并安装Qt库到指定目录用交叉编译的qmake编译自己的应用程序把生成的可执行文件部署到目标板验证后面的章节严格按照这个流程展开每个环节我都会标出容易踩的坑。2. 环境准备与工具链搭建2.1 主机系统与基础依赖我用的主机是Ubuntu 20.04 x86_64这也是目前嵌入式开发用得最多的发行版。Qt 5.14.2的编译依赖不少工具先安装基础包sudo apt update sudo apt install build-essential flex bison libglib2.0-dev \ libfontconfig1-dev libfreetype6-dev libxkbcommon-dev \ libegl1-mesa-dev libssl-dev python3 perl git以上是编译常用的宿主机依赖注意这里装的是x86版库只是给Qt编译过程中的宿主工具用的不会混进aarch64产物。2.2 安装aarch64交叉编译工具链Ubuntu软件源里直接有aarch64工具链安装非常简单sudo apt install gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完检查版本aarch64-linux-gnu-gcc --version正常情况下会显示类似gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)的信息。这里有个重要的经验工具链的gcc版本要尽量和Qt 5.14.2时代匹配。Qt 5.14.2发布于2020年那年主流的gcc版本是9.x和10.x用Ubuntu 20.04自带的gcc-9系列编译基本不会遇到语法兼容问题。如果你用的是Ubuntu 22.04默认gcc是11.x编译某些老版本第三方库时可能会报错但编译Qt 5.14.2本身一般没问题。2.3 sysroot的准备sysroot是交叉编译里最容易被忽略、却最容易出问题的一环。简单说sysroot就是目标板的根文件系统(rootfs)的一个精简拷贝里面包含aarch64版本的C库、C标准库、内核头文件等交叉编译器在链接时会到sysroot里找依赖库。准备sysroot有两种常用方法方法一从运行中的ARM64设备拷贝如果你手里已经有一台能正常跑Ubuntu/Debian系统的ARM64设备比如树莓派4B可以直接把根文件系统同步到主机mkdir -p ~/aarch64-sysroot sudo rsync -avxH root树莓派IP:/ ~/aarch64-sysroot注意排除/proc、/sys、/dev、/tmp、/run等虚拟文件系统目录。方法二用debootstrap从零构建如果手头没有现成的ARM64设备可以用debootstrap直接拉取一个基础rootfssudo apt install debootstrap sudo mkdir ~/aarch64-sysroot sudo debootstrap --archarm64 --includelibfontconfig1-dev,libfreetype6-dev,libxkbcommon-dev,libegl1-mesa-dev,libssl-dev focal ~/aarch64-sysroot http://ports.ubuntu.com/ubuntu-ports/我用的是focal20.04跟主机的Ubuntu版本保持一致避免出现sysroot和主机的库版本不匹配的问题。debootstrap会拉取几百MB的软件包耗时取决于网络耐心等待。无论用哪种方法sysroot准备完成之后建议检查一下关键文件是否存在ls ~/aarch64-sysroot/lib/aarch64-linux-gnu/libc.so.6 ls ~/aarch64-sysroot/usr/lib/aarch64-linux-gnu/libstdc.so.6有了工具链和sysroot交叉编译的“地基”就算打好了。3. Qt源码获取与configure配置3.1 下载Qt 5.14.2源码包Qt官方提供完整的源码包命名为qt-everywhere-src-5.14.2.tar.xz体积在500MB左右。下载地址是Qt官方archive目录wget https://download.qt.io/archive/qt/5.14/5.14.2/single/qt-everywhere-src-5.14.2.tar.xz如果官网下载慢可以找国内镜像如清华TUNA、中科大USTC把域名换成镜像地址即可。解压tar -xf qt-everywhere-src-5.14.2.tar.xz cd qt-everywhere-src-5.14.2进到源码目录能看到模块很多qtbase、qtdeclarative、qtquickcontrols2、qtwebsockets等其中qtbase是最核心的模块包含QtCore、QtGui、QtWidgets等必备组件。其他模块按需编译不需要的用-skip跳过。3.2 依赖处理策略能省则省能编则编这是Qt交叉编译中比较关键的一步我先说说思路。第一类依赖是zlib、libpng、libjpeg这些基础图像压缩库。Qt配置时用-qt-zlib、-qt-libpng、-qt-libjpeg选项可以让Qt直接使用源码包自带的库编译静态版不需要自己单独交叉编译这能省不少功夫。注意不能跟-system-zlib混用因为后者要求sysroot里已经有aarch64版本的zlib而且静态链接zlib时版本不一致还可能出怪问题。第二类依赖是freetype和fontconfig用于字体渲染。如果目标板需要显示中文freetype基本必须。如果sysroot里已经装好了debootstrap的include参数里可以加libfreetype6-devQt会自动检测到如果没有可以用-qt-freetype让Qt自己编译。第三类依赖是OpenSSL。如果程序不需要HTTPS网络访问直接在configure里加-no-openssl省事如果需要必须手动交叉编译OpenSSL的aarch64静态库这里我建议先-no-openssl跑通全流程后期需要再加一步一步来减少变量。第四类是ICU用于Unicode和国际化支持。Qt默认会自动检测ICU但静态交叉编译时ICU是个大块头编译时间长、体积大。如果程序不涉及复杂的文本布局、没有多语言动态切换需求直接加-no-icu跳过能省一大半编译时间。总体策略就是能省则省用-no跳过不必要的模块能编则编用-qt让Qt编译自带的依赖不系统依赖不依赖sysroot里的动态库。3.3 configure参数逐个拆解cd到源码目录后创建一个构建目录不建议直接在源码目录里配置构建虽然Qt允许但分开更干净然后运行configure。完整命令如下mkdir build cd build ../configure \ -prefix /opt/Qt5.14.2-static-aarch64 \ -release \ -opensource \ -confirm-license \ -static \ -xplatform linux-aarch64-gnu-g \ -sysroot ~/aarch64-sysroot \ -nomake examples \ -nomake tests \ -skip qtwebengine \ -skip qtwebview \ -skip qt3d \ -skip qtcanvas3d \ -skip qtpurchasing \ -skip qtgamepad \ -skip qtspeech \ -skip qtserialbus \ -skip qtnetworkauth \ \ -no-icu \ -no-openssl \ -no-xcb \ -no-feature-cups \ -no-feature-printpreview \ -no-gstreamer \ -no-pulseaudio \ -no-alsa \ -no-dbus \ \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -qt-freetype \ \ -linuxfb \ -eglfs对关键参数逐个说明-static核心中的核心这个参数决定编译静态版Qt。让生成的libQt5Core.a等静态库取代.so动态库。-xplatform linux-aarch64-gnu-g告诉Qt用指定的交叉平台配置文件。这个mkspec在qtbase/mkspecs/linux-aarch64-gnu-g目录下Qt 5.14.2自带。-sysroot ~/aarch64-sysroot指定交叉编译时的根文件系统路径。-prefix /opt/Qt5.14.2-static-aarch64最终Qt库安装目录在主机上的路径同时也是交叉编译时qmake写入的环境路径。-no-xcb禁用X11的XCB支持。大多数嵌入式设备没有X Window环境用linuxfb或eglfs这类Qt内置平台插件更合适。-linuxfb和-eglfs这两个是Qt在嵌入式平台下最常用的显示后端插件。linuxfb适合简单的framebuffer设备eglfs适合带GPU、支持OpenGL ES的设备。都编进去后面运行程序时通过-platform参数切换灵活很多。-skip系列这些模块要么是网页浏览器这类巨大的组件qtwebengine要么在嵌入式场景用不到的组件qtgamepad、qtspeech等跳过能大幅缩短编译时间。-no-feature-cups禁用打印功能嵌入式设备基本不需要。这个configure命令敲下去之后会进行一组检测期间可能弹出交互询问是否接受开源许可协议因为我们提前加了-confirm-license它会自动接受。配置过程中要注意看终端的输出特别是开头会有一段Systems architecture和Platform的确认信息确认显示aarch64相关字样。如果在这个阶段报错了99%是依赖或工具链环境问题建议停下来先解决不要硬着头皮往下走。4. 完整编译流程与安装4.1 编译前的检查configure成功的标志是生成了Makefile和libQt5Core等模块的配置。在正式make之前先花30秒做几项检查能省后面很多麻烦。第一个检查host工具是否在编译Qt时正常构建。Qt交叉编译时会先为本机编译一些小工具如moc、uic、rcc这些是后续构建过程中必须的宿主程序。用-nomake examples和-nomake tests已经跳过了大量宿主目标的编译但核心工具还是会在编译头几分钟内自动构建。如果tools编译失败往往是宿主gcc版本太新或头文件缺失回头补装依赖即可。第二个检查链接器是否能找到aarch64版本的libc和libstdc。在build目录下有一个qmake程序交叉版编译一个小测试程序验证echo int main(){return 0;} /tmp/test.cpp ~/aarch64-sysroot/usr/bin/g ... # 简写实际操作中我更喜欢直接检查sysroot里的动态库文件权限和符号表file ~/aarch64-sysroot/lib/aarch64-linux-gnu/libc.so.6显示ELF 64-bit LSB shared object, ARM aarch64就说明sysroot的架构没问题。第三个检查build/config.summary文件这里记录了所有模块的启停、依赖检测结果。重点看FontConfig、LibPNG、JPEG、OpenGL等大项确认已经按预期启用或禁用。这一步很值得做因为Qt的README不会告诉你实际检测到了什么只有config.summary会。4.2 make编译的过程与时间一切确认无误后开始正式编译。在build目录下执行make -j$(nproc)nproc返回主机的CPU核心数比如8核机器就是make -j8。并行编译能显著压缩编译时间但也别开太多线程默认nproc即可太激进内存会被拉满。Qt 5.14.2在-skip了不少大模块之后的编译量依然不小qtbase一个模块就有上千个源文件加上qtdeclarative、qtquickcontrols2、qtconnectivity如果没skip等完整编译大约需要1到3小时取决于机器性能。我在一台8核16GB的虚拟机里实测约一个半小时物理机通常更快。编译过程中会有大量编译错误或警告滚动输出。大部分warning可以忽略比如Qt在gcc 9下产生的deprecated警告但error只要出现就必须处理。常见的error输出形式In file included from /path/to/header.h:55, from /path/to/source.cpp:23: /path/to/header.h:78:37: error: EINVAL was not declared in this scope这类报错大概率是缺少所需头文件或相应宏定义检查编译命令里的-I路径是否包含~/aarch64-sysroot/usr/include。Qt的configure脚本一般会自动加这个路径但如果sysroot路径有特殊字符或权限问题就可能漏掉。如果遇到这种错误可以设置环境变量重跑configureexport CFLAGS--sysroot$HOME/aarch64-sysroot export CXXFLAGS--sysroot$HOME/aarch64-sysroot export LDFLAGS--sysroot$HOME/aarch64-sysroot这是我在前几次构建中踩过的坑总以为Qt交叉编译的configure已经处理好了sysroot的传递但偶尔某些模块的子编译比如qtdeclarative的第三方库还需要手动指定。经验是遇到奇怪的找不到头文件或找不到符号的错误优先检查sysroot路径和编译器前缀成功率最高。4.3 make install与目录结构编译完成之后终端显示make[1]: Leaving directory且无error执行安装sudo make install安装过程通常几分钟因为主要是拷贝已经生成的头文件、静态库、mkspecs到-prefix指定目录。安装完成后检查一下ls /opt/Qt5.14.2-static-aarch64/lib/目录下应该有大量.a结尾的静态库文件比如libQt5Core.a libQt5Gui.a libQt5Widgets.a libQt5Network.a ...同时lib目录下还会有plugins和qml子目录里面同样是.a静态版本的插件库和QML模块。Qt静态编译后的插件并不会像动态编译那样在运行时从.so文件加载而是通过代码静态导入机制嵌入到程序里这块后面专门讲。还需要确认bin目录下有交叉编译使用的qmake工具ls /opt/Qt5.14.2-static-aarch64/bin/qmake file /opt/Qt5.14.2-static-aarch64/bin/qmakefile输出应该显示ELF 64-bit LSB executable, x86-64因为qmake是宿主工具在Ubuntu主机上运行。这个qmake虽然运行在x86_64上但它内部包含的mkspecs和默认编译器路径都指向了aarch64工具链所以它能生成正确的交叉编译Makefile。从这份安装目录里我们已经得到了一个“可交叉编译、可静态链接”的Qt SDK接下来用它编译一个真正的应用验证。5. 使用交叉Qt编译并部署应用5.1 配置工程文件Qt工程的构建一般通过qmake或CMake驱动。qmake方式.pro文件是Qt老牌且最常用的方式咱们这里以gtk示例工程为例。假设现在有一个简单的Qt Widgets工程叫hello_qt目录结构为hello_qt/ hello_qt.pro main.cppmain.cpp里面是一个标准的QApplication QLabel显示Hello Qt on ARM64窗口的代码这里不贴全代码了重点说pro文件里需要关心的配置。在hello_qt.pro中增加QT core gui widgets TARGET hello_qt TEMPLATE app CONFIG staticCONFIG static让qmake在链接阶段把Qt的静态库都拉进来。如果工程里用到network模块就写QT network不用额外处理静态库选项qmake会自动把对应.a库链接进来。5.2 手动编译而非qmake的坑直接用上面的qmake生成Makefile编译确实能成功因为Qt自带的mkspec已经配置了aarch64-gnu-g编译器。但在实际生产中我强烈建议不要直接使用安装目录里的qmake来构建应用而是建立一个交叉编译的环境脚本这样可以灵活调整编译器和标志位。创建一个setenv-aarch64.sh脚本#!/bin/bash export QT_BASE/opt/Qt5.14.2-static-aarch64 export CROSS_COMPILEaarch64-linux-gnu- export SYSROOT~/aarch64-sysroot export PATH$QT_BASE/bin:$PATH export CXX$CROSS_COMPILE-g export CC$CROSS_COMPILE-gcc export CXXFLAGS--sysroot$SYSROOT export LDFLAGS--sysroot$SYSROOT然后source setenv-aarch64.sh cd hello_qt qmake hello_qt.pro make5.3 静态链接与插件导入如果只是用了QtCore/QtGui/QtWidgets这些基础模块上述步骤生成的hello_qt已经可以运行在目标板上了。但一旦用到了平台插件比如linuxfb或eglfs由于Qt是静态编译这些插件不会在运行时被加载必须在代码里显式导入。在main.cpp的#include QApplication之前加一行#include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)如果你是eglfsQ_IMPORT_PLUGIN(QEglFSIntegrationPlugin)如果程序同时用了Qt的svg、imageformats、platforms等多类插件qmake会自动添加支持的插件导入宏。不过保险起见编译完之后用nm快速检查一下符号表是否包含了插件aarch64-linux-gnu-nm hello_qt | grep -E qt_static_plugin|QLinuxFbIntegrationPlugin能看到类似qt_static_plugin_QLinuxFbIntegrationPlugin的符号说明插件已经成功嵌入到可执行文件里。这一步最容易出问题。很多人静态交叉编译完Qt用qmake生成程序后丢到板子上一跑报No such file or directory或者直接崩溃就是因为没有加Q_IMPORT_PLUGIN。遗漏插件后的错误日志通常类似This application failed to start because no Qt platform plugin could be initialized.看到这句话就是插件没导进来非常典型。5.4 部署到目标板与运行验证编译完成后的可执行文件在hello_qt目录下。用file确认架构file hello_qt输出应该是ELF 64-bit LSB executable, ARM aarch64。拷贝到目标板scp hello_qt root目标板IP:/root/在目标板上执行./hello_qt -platform linuxfb如果你用的板子有显示屏且framebuffer设备正常屏幕上会直接显示窗口。有些板子需要指定fb设备节点./hello_qt -platform linuxfb:fbdev/dev/fb0用eglfs的话./hello_qt -platform eglfs如果板子带GPU且eglfs驱动正确界面渲染会更流畅但eglfs对一些GPU的兼容性不如linuxfb好所以我建议两个插件都编进去实测哪个好用哪个。6. 常见问题与排查技巧整个静态交叉编译链路比较长踩坑是常态。我把实际遇到过的、以及同行交流中高频出现的问题整理成下面表格附带排查思路。6.1 工具链与sysroot问题问题现象可能原因解决办法configure报错找不到libGL.sosysroot里缺少OpenGL库在sysroot用debootstrap额外安装libgl1-mesa-dev或强制-no-opengl无GPU设备适用链接时大量undefined reference to std::__cxx11gcc版本和libstdc头文件版本不匹配检查sysroot里的libstdc.so.6对应版本尽量用与gcc匹配的rootfs编译C代码时报错gnu/stubs-32.h: No such filesysroot里缺少32位兼容文件一般不会遇到如果遇到说明工具链与sysroot的体系不匹配overlayfs或nfs挂载rootfs时符号链接断裂sysroot里的软链接在同步/拷贝时丢失用rsync同步时加上-a参数保留符号链接拷贝时禁止用cp -r乱跑6.2 configure与编译问题问题现象可能原因解决办法configure提示Could not find qmake specmkspecs路径错误或平台名写错确认-xplatform后跟的名称与qtbase/mkspecs目录下的名称完全一致编译中途报错QMAKE_DEFAULT_LIBDIRS相关vsysroot环境变量未影响qmake的默认路径设置QMAKE_DEFAULT_LIBDIRS/usr/lib/aarch64-linux-gnu环境变量再configure较少见编译完成后某些模块缺失skip参数误杀或模块本身与静态不兼容检查config.summary与configure日志重新按需调整skipmake时主机工具moc/rcc编译失败宿主机缺libxcb/glib等开发包按第2章安装完整依赖后重新清理build目录再configure6.3 链接与应用运行问题问题现象可能原因解决办法链接时cannot find -lGLESv2eglfs按需依赖OpenGL ES库-no-opengl或sysroot里补装libgles2-mesa-dev运行时报No platform plugin could be initialized静态插件未导入在main.cpp里加Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)程序能启动但中文全是方块Qt未编译freetype或缺少字体字体文件确保configure包含-qt-freetype并在目标板放一个中文字体如思源黑体到/usr/share/fontsQt也支持通过QApplication::setFont加载QObject::moveToThread崩溃某些线程特性需要ICU支持静态编译且-no-icu可能导致部分Unicode/线程细节异常建议保留系统默认的ICU或尽量不用高级文本处理目标板运行出现Segmentation fault代码或Qt的glibc版本与目标板实际系统不匹配用strace定位段错误位置优先检查sysroot与目标板系统版本是否一致6.4 排查问题的一个通用思路遇到编译或运行问题先别急着上网搜索完整报错按照这个顺序排查效率最高确认工具链能编译最简单的hello world程序并成功运行在目标板上这个排除了工具链和sysroot自身的问题。查看configure过程中输出的config.summary确认Qt模块启停是否符合预期排除模块缺失。对运行问题先在目标板执行ldd ./hello_qt如果输出包含not found且程序启动失败说明有动态库依赖混进来了——这本身不正常静态编译程序理论上不依赖Qt动态库只依赖基础C库。用Qt自带的verbose输出例如设置QT_DEBUG_PLUGINS1环境变量运行程序它会打印所有插件的加载路径和加载结果可以快速定位插件问题。最后再考虑网络搜索报错信息此时你能提供更多的上下文。这个方法能覆盖90%以上的问题。剩下的疑难杂症往往和特定硬件平台、特定GPU驱动相关需要你用gdb结合具体场景调试但那些就属于比较深度的工程问题了。7. 从零搭建的几点心得整个流程走通之后回头看静态交叉编译Qt真正的工作量不在编译本身而在环境的一致性和依赖的收口。sysroot一定要和目标板上运行的rootfs保持同源用debootstrap生成sysroot时最好用跟目标板相同或相近的发行版版本否则就算编译成功运行阶段也可能因为glibc版本差异碰到各种诡异问题。工具链的gcc版本也别求新Qt 5.14.2的时代里有大量用户都在用gcc 7到9的版本这套组合经过了充分验证稳定压倒一切。静态编译后可执行文件确实大一个简单Widgets程序不带图标资源也有12MB左右但换来的是部署时的绝对省心。实际项目中我把Qt程序和它的资源文件打包成一个自解压脚本目标板上一键部署运维成本极低。另外建议给Qt的编译环境做一个一键构建脚本把configure命令、make、make install全都固化下来下次换一台机器或者重装系统之后跑一遍脚本就能恢复整个交叉编译环境。我当时在这个项目里就吃了文档只记步骤、没记脚本的亏后来痛定思痛把用到的所有环境变量和编译选项都整理进了仓库之后无论是给同事搭环境还是CI流水线构建都省了很多事。这套环境搭建完后续在aarch64平台上做应用开发就只是写代码、调界面的工作了。Qt的跨平台能力在这种场景下体现得很彻底同样的代码桌面Linux上运行调试ARM64板上打包部署开发体验和交付效率都能兼顾。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价