ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Qt 5.14.2 aarch64静态交叉编译完整指南

Qt 5.14.2 aarch64静态交叉编译完整指南 前阵子做了一块ARM64开发板的图形应用移植目标板跑的是精简版Linux系统内存不大磁盘空间也吃紧但应用必须带完整的Qt界面。最开始的想法是动态链接后来仔细一算依赖库加起来快两百兆再加上系统里还得装一堆xcb、fontconfig的运行库实在是折腾不起。最后决定走Qt 5.14.2的aarch64静态交叉编译路线整个过程从零开始踩了不少坑也整理出一套能复现的完整流程今天把它整理成手册分享出来。这篇东西主要解决什么问题呢就是在x86_64的PC上用交叉编译工具链把Qt 5.14.2源码编译成aarch64架构下的静态库再把你的Qt程序也编译成单个静态链接的可执行文件直接拷到目标板上就能跑不依赖目标板上的任何动态库。适合刚接触嵌入式Linux和Qt移植的开发者也适合那些正准备做国产化替代、ARM平台应用迁移的团队参考。1. 整体方案设计与版本选型思路1.1 为什么选择Qt 5.14.2这个版本Qt的版本线很多5.15是最后一个支持Win7的版本6.x系列又做了比较大的架构调整但对于嵌入式Linux和aarch64交叉编译来说5.14.2是一个非常稳妥的选择。首先它是LTS版本中的一个维护版2020年3月发布修复了大量已知问题稳定性已经过多年验证。其次5.14.x的configure参数体系非常成熟对交叉编译的兼容性很好网上能找到的参考资料也最多遇到问题容易排查。还有一个很实际的考虑是离线安装和离线构建。官方在线安装器对新版Qt支持得比较好但离线安装包动辄几个GB而且很多镜像是走在线下载的。5.14.2的源码包可以直接从官方仓库完整下载体积可控编译时的依赖也相对清晰。很多内网开发环境的机器根本无法访问外网这种时候能从源码包、工具链deb包、sysroot tar包把这些材料一次性备齐工作就能顺利进行下去这一点在后面的环境准备章节里也会重点说。1.2 静态交叉编译相比动态编译的优劣分析静态交叉编译的意思是在编译阶段就把Qt库、依赖库、还有你的业务代码全部链接进同一个可执行文件里目标运行时不再需要任何Qt动态库。好处很明显第一部署极度简化一个文件拷过去就能跑第二避免了目标板上动态库版本冲突这类经典问题第三对精简版Linux系统特别友好哪怕rootfs里面没有xcb、fontconfig这些库只要有最基础的libc和内核就能运行。但代价也摆在那里。静态编译出的二进制体积会明显偏大一个简单的Qt Widgets程序release静态编译加strip之后大约在10到20MB如果用到Qt WebEngine这种重型模块体积会直逼上百MB。另外静态编译在运行时加载插件的方式和动态版不一样比如平台插件qlinuxfb、qminimal这些都需要在编译时通过配置项明确编进去否则程序启动时会报could not find or load the Qt platform plugin。做技术选型的时候我建议先确认目标板到底需要什么。如果rootfs空间充足动态编译更灵活如果像我这样要求单文件部署、对系统侵入性越少越好静态编译就是最合适的选择。aarch64静态编译在这条路上比x86_64麻烦一些主要麻烦在工具链匹配和依赖库的交叉编译上这正是本手册接下来要解决的核心问题。1.3 热词背后的需求洞察离线、移植与模拟环境从近期的搜索热词看很多人都在关注“qt5.14.2离线安装包下载”和“qt5.14.2安装教程”说明有大量开发者在隔离环境里做Qt开发碰到了下载和安装的硬门槛。另外“nginx aarch64移植”、“phantomjs aarch64下载”这些词也从侧面反映出aarch64平台的需求正在爆发式增长不只是传统的嵌入式设备连服务器端的应用也在往ARM64上迁移。还有一个词比较特殊“使用gem5在aarch64架构下运行spec2006”。gem5是一个计算机体系结构模拟器很多做CPU架构验证的同学会在模拟环境里跑SPEC2006基准测试这种场景同样需要在x86主机上交叉编译出aarch64体系的可执行文件。虽然我的项目用的是真实开发板但交叉编译的基本原理、工具链选择、sysroot准备方式是完全互通的。这篇手册里讲的很多内容放在gem5模拟环境里同样适用差别只是目标运行环境从真实板子换成了模拟器。2. 环境准备与工具链搭建2.1 主机环境要求与软件清单先说我用的主机环境Ubuntu 20.04 x86_648核16GB内存。理论上任何Linux发行版都能做Debian系相对省事因为aarch64交叉编译器、sysroot这些材料都可以直接通过包管理器拿到。主机上需要安装的基础工具包括build-essential提供make、gcc、g等基础构建工具python 2或3Qt 5.14.2的构建脚本对Python有依赖实测Python 3可正常工作perlQt构建过程中的同步和脚本工具libclang-dev如果用qdoc或某些模块会用到纯编译qtbase可以跳过bison、flex、gperf部分Qt模块自动生成代码时需要aarch64交叉编译工具链我优先推荐两条路。第一是用Ubuntu官方源里的gcc-aarch64-linux-gnu和g-aarch64-linux-gnu版本是9.3匹配5.14.2完全没问题安装一句话搞定。第二是用Linaro提供的aarch64-linux-gnu工具链版本可以选7.x或10.x特点是工具链更全包含sysroot。我实际使用的是Ubuntu源的工具链稳定性和glibc兼容性都很好。如果你的目标板厂商提供了专用工具链比如某些国产平台要求用特定gcc版本那就以厂商为准但配置方法是一样的。安装命令参考sudo apt update sudo apt install -y build-essential python perl git sudo apt install -y gcc-aarch64-linux-gnu g-aarch64-linux-gnu安装完成后可以用aarch64-linux-gnu-gcc -v确认版本出现gcc version 9.3.0这类输出说明工具链正常。这里有个细节Ubuntu源的工具链自带一个基础的sysroot路径在/usr/aarch64-linux-gnu里面有一部分基础库但远不足以支撑Qt完整编译后面还需要自己补齐目标平台的头文件和库。注意工具链的glibc版本一定要留意。如果目标板的系统版本较老用高版本工具链编译出的二进制在目标板上可能会报GLIBCXX_x.x.x not found之类的错误。最稳妥的办法是从目标板上直接拷贝/lib和/usr/include等关键目录来构建sysroot保证二进制依赖的glibc版本和板子完全一致。2.2 sysroot的两种构建方式sysroot是交叉编译里最容易让人懵的概念。简单说sysroot就是以目标板文件系统为蓝本整理出来的一套“目标机头文件库文件”目录树交叉编译器在编译链接时会把sysroot当作目标机的根目录/来查找头文件和库。我在项目里试过两种构建sysroot的方式各有适用场景。第一种是复制法通过NFS或SD卡把目标板的/lib、/usr/lib、/usr/include等目录拷贝到主机的某一路径下然后通过--sysroot参数指定给编译器。这种方式的优点是100%还原目标环境不会出现glibc版本不匹配的问题缺点是如果目标板是精简系统某些开发用头文件可能缺失还需要额外补齐。第二种是debootstrap/multistrap法在主机上用debootstrap直接生成一个aarch64架构的最小rootfs类似sudo apt install debootstrap sudo debootstrap --archarm64 --foreign bionic /opt/aarch64-sysrootdebootstrap生成的rootfs结构很干净包含完整的libc、libstdc还能通过chroot方式往里安装额外依赖库。缺点是需要网络下载大量软件包内网环境不一定方便另外生成的rootfs里包含很多用不到的文件需要手动精简。我个人推荐第一种复制法原因只有一个和真实目标环境一致。Qt静态编译最怕的就是链接进去的glibc版本比目标板新这种问题在运行时非常难排查很有可能你写一个hello world都能跑但Qt一启动就段错误。直接从板子上拷贝 sysroot 可以从根源上杜绝这类问题。2.3 sysroot目录结构与基础库核对无论用哪种方式最终sysroot的目录结构都要像下面这样/opt/aarch64-sysroot/ ├── lib ├── usr/ │ ├── include │ └── lib其中/opt/aarch64-sysroot/lib和/usr/lib是目标板的库目录/usr/include是头文件目录。为了后续Qt交叉编译的configure检查能通过我建议重点确认以下几个基础库是否存在libc.so、libm.so、libdl.so、librt.so、libpthread.solibstdc.solibgcc_s.sold-linux-aarch64.so.1还需要确认libc.so是否是一个文本链接脚本在交叉编译时它经常会被gcc用-shared的方式处理。复制sysroot时最好保留所有.so符号链接不能只拷贝二进制文件。准备完成后可以写一个最小的交叉编译测试程序验证工具链和sysroot是否正常echo int main() { return 0; } test.c aarch64-linux-gnu-gcc --sysroot/opt/aarch64-sysroot test.c -o test file test输出显示ELF 64-bit LSB executable, ARM aarch64说明环境基本可用。这里有个容易被忽略的点编译出来的test程序先用aarch64-linux-gnu-readelf -d test看一下动态依赖如果输出只有libc.so.6和ld-linux-aarch64.so.1说明工具链自身工作正常后续Qt编译如果报错可以排除基础环境的问题。3. Qt源码准备与依赖矩阵分析3.1 Qt源码获取与目录裁剪思路Qt 5.14.2的源码可以从官方Git仓库或者发布tarball获取。我建议直接下载发布tarball原因有两个第一Git仓库动辄几个GBclone慢还占磁盘第二tarball结构干净版本锁定不会被后续commit影响。关键下载内容如下qtbase-everywhere-src-5.14.2.tar.xz核心模块必选qtsvg-everywhere-src-5.14.2.tar.xzSVG图标支持按需qtimageformats-everywhere-src-5.14.2.tar.xz额外图片格式按需qtdeclarative-everywhere-src-5.14.2.tar.xzQML支持如果纯Widgets应用可以不要下载后统一解压到源码目录比如/opt/qt-src/。这里说的“裁剪思路”是指Qt是个大工程不需要把所有模块都编译进去静态编译时多一个模块就多一份链接时间和二进制体积。qtbase是无论如何都绕不开的因为它包含了Qt Core、Qt GUI、Qt Widgets这些最基础的库。像Qt WebEngine这种重型模块除非业务确实需要否则建议直接放弃它不仅在交叉编译时依赖巨大需要ninja、gn、clang等工具链静态编译的体积也会让人崩溃。3.2 依赖库的取舍与交叉编译策略Qt在Linux上的依赖主要分为三块基础C/C库、图形相关库、字体相关库。基础C/C库即glibc和libstdc已包含在sysroot中。图形相关库的完整依赖是libxcb、xcb-util系列、libxkbcommon、libx11如果还要跑Wayland窗口还需要libwayland-dev。字体相关则是fontconfig和freetype。静态交叉编译时这些依赖库的处理方式有三种使用sysroot中已有的动态库编译后应用程序在运行时动态加载这些库。这种方式看起来和“静态”矛盾但其实很常见——静态Qt库只是把Qt源码编译成静态库外部的系统库仍然可以动态链接。把依赖库也交叉编译成静态库全部链接进最终可执行文件。体积更大但部署最彻底。绕过图形依赖使用Qt自带的QPA平台插件比如linuxfb、minimal、eglfs这样就不需要xcb和fontconfig构建和部署都极大简化。对于大多数嵌入式板子来说思路3是最优解。用得最多的是linuxfb平台插件它直接操作Linux的framebuffer设备不需要X11、不需要Wayland编译配置里指定-no-xcb -no-xkbcommon -no-fontconfig就能把一大串依赖全砍掉。但这里要做一个权衡如果你的目标板要跑复杂GUI比如需要透明窗口、多窗口层级管理、复杂字体渲染linuxfb的表现会远不如xcb。linuxfb本质上是一个最简平台不支持GPU加速不支持复杂的窗口系统特性。这种情况下我建议老老实实把xcb、fontconfig这些依赖交叉编译成aarch64版本虽然工程量更大但Qt界面效果更接近桌面体验。3.3 配置文件与工具链适配Qt交叉编译需要告诉构建系统用哪个编译器、目标平台是什么、sysroot在哪。Qt的交叉编译一般通过编写一个qmake.conf文件放在qtbase/mkspecs/devices/linux-aarch64-gnu-g目录下实现。做法是复制现有的linux-arm-gnueabi-g目录改成aarch64专用配置cp -r qtbase/mkspecs/devices/linux-arm-gnueabi-g qtbase/mkspecs/devices/linux-aarch64-gnu-g编辑qmake.conf核心内容如下MAKEFILE_GENERATOR UNIX TARGET_PLATFORM unix TEMPLATE app CONFIG qt warn_on release incremental link_prl QT core gui 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_INCDIR /opt/aarch64-sysroot/usr/include QMAKE_LIBDIR /opt/aarch64-sysroot/usr/lib QMAKE_LIBS -lz -lbz2 -lpthread -ldl QMAKE_INCDIR_X11 /opt/aarch64-sysroot/usr/include QMAKE_LIBDIR_X11 /opt/aarch64-sysroot/usr/lib QMAKE_INCDIR_EGL /opt/aarch64-sysroot/usr/include QMAKE_LIBDIR_EGL /opt/aarch64-sysroot/usr/lib QMAKE_CFLAGS --sysroot/opt/aarch64-sysroot QMAKE_CXXFLAGS --sysroot/opt/aarch64-sysroot QMAKE_LFLAGS --sysroot/opt/aarch64-sysroot这里要注意--sysroot参数必须同时出现在CFLAGS、CXXFLAGS和LFLAGS中否则会出现编译能过链接失败的情况。另外如果目标板使用厂商专用工具链将上面的aarch64-linux-gnu-前缀替换成对应交叉编译前缀即可。重要qmake.conf里的include路径顺序也有讲究。Qt configure在检测X11或者OpenGL时会按照QMAKE_INCDIR指定的路径查找头文件。如果主机Ubuntu系统也装了X11开发库路径顺序不对可能导致Qt错误地找到x86_64的头文件编译出来的库会带上主机架构的二进制这种错误通常要到链接阶段才会暴露排查相当痛苦。4. Qt源码交叉编译配置与构建实操4.1 configure参数的逐项解释进入qtbase目录后核心工作是配置configure参数。我整理了一份经过完整验证的参数清单先看配置命令cd /opt/qt-src/qtbase-everywhere-src-5.14.2 ./configure \ -prefix /opt/qt-aarch64-static \ -release \ -opensource \ -confirm-license \ -static \ -no-opengl \ -no-icu \ -no-xcb \ -no-xkbcommon \ -no-fontconfig \ -no-feature-dbus \ -nomake examples \ -nomake tests \ -xplatform devices/linux-aarch64-gnu-g \ -sysroot /opt/aarch64-sysroot \ -no-gbm \ -no-eglfs逐个说下这些参数的含义和选型原因-prefix指定Qt静态库的安装路径编译完成后这些库会复制到这里后续你的程序通过qmake找到这个Qt版本的库。-release编译release版本不生成调试符号体积更小。-opensource -confirm-license接受开源协议免去交互式确认。-static目标编译静态Qt库这是整个任务的核心参数。-no-opengl大多数嵌入式板子没有完整的OpenGL实现打开OpenGL反而会引入复杂的依赖。如果你的板子支持GPU并需要eglfs可能需要改成-opengl es2但前提是sysroot里已经有对应的GLES库。-no-icuICU库主要给Qt WebEngine等复杂模块使用纯Widgets应用用不到关闭后能省大量编译时间。-no-xcb -no-xkbcommon -no-fontconfig关闭X11和复杂字体渲染依赖配合linuxfb平台插件使用。-no-feature-dbus如果目标板不需要进程间总线通信关掉可以避免静态链接时引入一堆dbus相关代码。-nomake examples -nomake tests跳过示例和测试代码的编译节省时间。-xplatform devices/linux-aarch64-gnu-g指定交叉编译目标平台配置就是上一步创建的mkspec目录。-sysroot指向目标板的文件系统根目录。-no-gbm -no-eglfs关闭基于DRM/GBM的显示后端不影响linuxfb使用。实际执行时systme会提示模块特性检查结果建议手动查看输出中是否有错误标记。我第一次编译时没有关闭dbusconfigure报错了就是因为在sysroot里没有找到dbus的库文件。从我这个经验看configure阶段遇到依赖缺失优先从明确关闭不用的模块开始排查。4.2 常见平台插件的配置方式静态编译时平台插件是整个体系中容易踩坑的部分。Qt程序启动时需要通过QPA插件确定“在哪里画窗口”常用的几个平台插件如下插件名适用场景静态编译时如何启用linuxfb直接写/dev/fb0适合无图形环境的嵌入式板子默认随qtbase编译无需额外参数minimal内存中的虚拟窗口适合无屏环境的测试默认随qtbase编译eglfs使用OpenGL ES渲染到全屏适合带GPU的板子需要配置-opengl es2 -eglfsxcb完整的X11窗口系统适合桌面级ARM环境需要完整的xcb依赖库且需要显式保留-xcb我这个项目选用linuxfb所以configure参数里保留了默认的插件编译。有一个关键点静态编译的程序找到可用的平台插件依赖的是“静态插件链接”机制需要在你的项目.pro文件里增加QTPLUGIN和静态插件的初始化代码否则即使Qt库里编译了linuxfb插件程序启动时依然报找不到插件。具体写法下面第5章会展开。4.3 构建与安装过程实录configure成功之后构建就是按部就班的操作make -j8 make install在8核机器上qtbase的完整编译大概需要40到60分钟。如果中途失败先不要急着清理重来看一下具体是哪个模块失败。常见的失败集中在src/plugins/platforms和src/gui这样的大模块失败原因大多是某个QMAKE_CFLAGS里的路径不对或者系统头文件缺失。修复后可以重新执行configureQt的构建系统会跳过已完成的模块增量编译比想象中快很多。构建完成后检查Qt静态库是否安装成功ls /opt/qt-aarch64-static/lib输出里应该有libQt5Core.a、libQt5Gui.a、libQt5Widgets.a等静态库同时/opt/qt-aarch64-static/bin里应该有交叉编译版的qmake和moc、uic等工具。之后如果还需要其他Qt模块比如qtsvg进入对应的源码目录用刚安装的Qt qmake来配置构建export PATH/opt/qt-aarch64-static/bin:$PATH cd /opt/qt-src/qtsvg-everywhere-src-5.14.2 qmake -r make -j8 make install这里能复用一套qmake实例编译出的模组会自动安装到之前的前缀路径下。注意全部模块安装完成前不要轻易改掉QT_VERSION对应的mkspec配置。后续新增模块时如果显示Project ERROR: Unknown module(s) in QT: svg多半是qmake缓存了旧配置执行make clean并重新运行qmake即可解决。5. 应用静态编译与部署验证5.1 在项目中启用静态Qt库环境已经就绪接下来是编译业务应用。假设你的工程有一个传统的.pro文件那么在编译业务程序前需要在.pro里明确指定两个关键配置QT core gui widgets CONFIG static同时为了让QPA静态插件被正确链接还需要加入插件相关的配置QTPLUGIN qlinuxfb然后在main.cpp中加入静态插件导入代码#include QtPlugin Q_IMPORT_PLUGIN(qlinuxfb) int main(int argc, char *argv[]) { QApplication app(argc, argv); // ... return app.exec(); }如果不加Q_IMPORT_PLUGIN即便.pro里写了QTPLUGIN链接器也不会主动把linuxfb插件编进来因为插件库没有显式被任何符号引用会被当作无用代码丢弃。这是初学者最容易忽略的一个点我最初编译出的程序在板子上启动时始终报“no such file or directory”和“could not find platform plugin xcb”排查了半天最后才意识到是插件没有静态链接进去。5.2 交叉编译业务程序与链接检查在应用源码目录下执行export PATH/opt/qt-aarch64-static/bin:$PATH qmake app.pro make -j8正常情况下会生成一个不带后缀的可执行文件。接下来做必要的检查file app输出应当为ELF 64-bit LSB executable, ARM aarch64。再检查动态依赖aarch64-linux-gnu-readelf -d app | grep NEEDED如果看到的结果是类似0x0000000000000001 (NEEDED) Shared library: [libstdc.so.6] 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]说明最终可执行文件只依赖基础的C/C运行库Qt相关库已经全部静态链入。如果在这一步看到libQt5Core.so.5这样的输出说明静态链接配置失败需要回查.pro的CONFIG和QMAKE_LFLAGS。一个实用的后续步骤是用strip去掉符号表能显著减小体积aarch64-linux-gnu-strip app一个包括Qt Widgets基础控件的应用strip前大约25MBstrip后能压到15MB左右。5.3 离线部署到目标板与运行验证把编译好的可执行文件拷贝到目标板最简单的做法是U盘、SD卡或者网络传输。在目标板上执行./app -platform linuxfb如果程序能够正常显示Qt窗口说明整个静态交叉编译链路已经打通。如果启动失败优先查看提示信息是出现在动态加载阶段还是QPA插件初始化阶段。动态加载阶段的错误通常提示缺少.so文件说明链接检查没做到位QPA初始化阶段的错误则大多是插件相关确认是否传入了-platform linuxfb或者插件是否真的静态链入了。我在这块实际遇到过一个问题linuxfb默认使用/dev/fb0作为显示设备如果目标板的framebuffer设备路径不同比如是/dev/fb1程序启动会直接报Failed to open framebuffer /dev/fb0。解决办法是在启动命令中显式指定./app -platform linuxfb -plugin linuxfb:fb/dev/fb15.4 目标板为精简系统的额外部署技巧静态二进制的一大好处是基本不需要操心依赖但有两个例外第一是glibc版本前面提到过这依赖于sysroot的构建方式第二是系统基础环境比如locale环境变量和时区数据。Qt内部在某些字符串和日期处理上还是会参考locale设置如果目标板环境变量为空界面可能出现中文乱码或排序错乱。建议在启动脚本里固定环境变量export LC_ALLC export QT_QPA_FONTDIR/usr/share/fonts/truetype如果目标板没有字体文件界面文字会显示成方框这时候需要把字体文件一起部署到对应路径下。很多嵌入式板子为了省空间根本没有中文字体较早准备一个体积合适的ttf文件会更省心。6. 常见问题与排查技巧实录6.1 常见典型问题速查表整个过程中我遇到的典型问题整理成一张表给读者对照使用现象位置原因分析解决办法configure报The specified system/device configuration is not supportedconfigure阶段mkspec目录没配对或qmake.conf路径错误确认-xplatform devices/linux-aarch64-gnu-g指向的目录存在且有正确的qmake.confconfigure提示GLES/OpenGL not foundconfigure阶段sysroot中缺少相关的头文件按需关闭opengl或补齐libgles2-mesa-dev、libegl1-mesa-dev等交叉编译包编译报cannot find -lGLqtbase构建Qt仍试图链接OpenGL库但sysroot里没有检查configure参数是否有-no-opengl或确认GL库已安装编译报error: bits/libc-header-start.h: No such file or directoryqtbase构建sysroot中头文件路径不对或缺少libc6-dev-arm64-cross检查sysroot/usr/include/$(TARGET_MULTIARCH)目录是否存在应用启动报could not find or load the Qt platform plugin xcb应用运行静态插件未链接或启动时带入错误的platform参数在main.cpp中Q_IMPORT_PLUGIN(qlinuxfb)运行时指定-platform linuxfb应用启动报Failed to open framebuffer /dev/fb0应用运行板子显示设备不是fb0或fb0无权限改用实际设备路径增加-plugin linuxfb:fb/dev/fb1应用启动即段错误应用运行glibc版本不匹配或sysroot结构不完整用目标板拷贝的sysroot重新编译整个工具链二进制体积过大部署阶段静态链接导致Qt全量代码进包用strip瘦身关闭不需要的Qt模块特性如qml、quick键盘输入没有反应应用运行linuxfb对linux input设备依赖指定确保启动时输入设备节点存在并检查权限上表基本覆盖了从configure到部署各阶段的常见坑。其实很多问题在configure阶段就埋下了比如sysroot头文件不完整会一路拖到编译中期才爆出来浪费大量时间。所以我建议 configure 完成后先检查完整输出日志中是否有error或missing关键字不要直接make。6.2 静态编译的size优化实操建议静态编译最大的痛点就是体积。如果一个几十MB的Qt静态二进制让部署变得困难可以从这几个方向瘦身第一个是用strip这个基本无损。只是把符号表和调试信息去掉功能完全不受影响。第二个是关闭不需要的Qt feature就是configure时追加-no-feature-*参数。Qt支持几百个这样细粒度的特性开关像-no-feature-printdialog、-no-feature-texthtmlparser可以减少不少代码链入但要注意业务代码是否用了对应API关掉会编译报错或运行异常。第三个方法是重新审视Qt configure时不需要的模块。比如项目中只需要Qt Widgets那-skip qtdeclarative -skip qtquickcontrols2 -skip qtwebengine这些参数就能在编译阶段直接跳过QML和WebEngine相关模块编译时间和最终依赖都会明显减少。最后还有个有点“野路子”但很实用的方法用upx对最终可执行文件做压缩aarch64-linux-gnu-upx appUPX压缩对纯静态程序效果不错Qt应用从15MB压到6MB左右很常见。注意UPX的aarch64支持依赖于版本运行时需要解压启动时间会增加几百毫秒是否能接受得根据自己的场景判断。6.3 与仿真环境的兼容性说明前面提到gem5模拟器这类场景交叉编译出来的aarch64二进制是可以在gem5的SESyscall Emulation模式下运行的前提是二进制是静态链接或者依赖库路径都能找得到。由于gem5的SE模式不加载真实rootfs纯静态链接的Qt程序反而是最理想的测试载体这也是为什么很多体系结构研究组的基准测试都要求静态编译。如果你的目标是gem5建议在Qt configure时尽量少依赖外部库linuxfbminimal平台就够了。另外gem5对多线程、内存分配行为与真实处理器差异较大Qt程序在模拟器上运行速度会非常慢建议用release编译并做好超时控制。我这里没有深入实测Qt在gem5里的表现但从交叉编译的机制看只要你的程序不依赖特殊设备节点用静态编译的aarch64二进制做基准测试是完全没有问题的。最后再说一个我个人的经验。Qt静态交叉编译这件事最难的不是某个单独的技术点而是“整条链路的联通感”。工具链、sysroot、qmake配置、静态插件、运行时参数任何一环断裂结果都是一个跑不起来的二进制。刚开始踩坑的时候我也动过换成动态交叉编译的念头但坚持把configure日志逐行看完、把sysroot和mkspec的关系彻底搞明白之后后续凡是换平台、换模块都能很快上手。如果你正在做类似平台建议留出至少一整天的环境搭建时间过程中多用file、readelf、ldd这些基础工具确认产物属性它们能帮你省掉大量的猜测时间。还有一个小技巧分享给你们交叉编译环境搭建好之后把整套工具链的安装命令、sysroot路径、qmake.conf全部写到一个shell脚本里塞进公司或自己的知识库里。静态交叉编译的配置细节非常多一周不碰可能就会忘有了脚本就能随时一键恢复环境比翻聊天记录和收藏夹靠谱得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进