1. 为什么我坚持用 Qt 5.14.2 做静态交叉编译
1.1 静态链路到底解决什么问题
前阵子接了一个 ARM64 工控板的界面项目,板子存储空间不大,系统还是裁剪过的,没有包管理器,更没有 x86 开发机上那种"装个 Qt 就能跑"的便利条件。以前在板子上跑 Qt 程序,最怕的就是那一连串libQt5Core.so.5: cannot open shared object file的报错,Qt 的动态库少说也有十几二十个,拷少了缺这个,拷多了盘不够,版本一不对整个程序直接罢工。
静态交叉编译把这个问题一次性解决了:Qt 的 Core、Gui、Widgets 模块和平台插件在编译期就全部揉进可执行文件里,最后拿到板子上的就是一个独立的 ELF 文件。没有运行时依赖链,没有库版本地狱,拷走就能跑。对 HMI 类应用来说,这个价值太实在了。
当然,静态不是免费的午餐。编译时间变长、可执行文件体积变大、部分动态特性会受限制,但对一个追求"一个文件上板即跑"的嵌入式场景来说,这些代价完全值得。
1.2 版本选型:5.14.2 是 Qt5 时代最稳的锚点
肯定有人会问:现在 Qt 6 都出了那么久,为什么还盯着 5.14.2?
我是这么看的。Qt 5.15 是 Qt5 最后一个 LTS,但开源版源码包的获取方式在 5.15 之后变得绕,而很多工业项目恰恰需要干净可控的开源 qmake 构建链路,不想在其他环节上牵扯太多精力。Qt 6 全面转向 CMake,构建哲学变化很大,对既有 Qt5 代码库的迁移本身就是一个大工程,没必要为了追新把正在运行的项目推倒重来。
5.14.2 本身也非常成熟。它支持 C++11/14,qmake 工作流稳定,第三方资料全网到处都是,遇到的坑几乎都有前人踩过,适合做交叉编译这种"一步错步步错"的工程。我在这篇文章里就用 5.14.2 作为基准版本,完整跑一遍从零搭建静态交叉编译环境的流程,把这个事情彻底说透。
1.3 整体架构先说清楚
做交叉编译之前,脑子里必须有这样一幅图:
- 开发主机:x86_64 Linux,推荐 Ubuntu 20.04 或 22.04
- 交叉工具链:
aarch64-linux-gnu-gcc / g++,编译出的程序跑在 ARM64 上 - sysroot:目标板根文件系统的镜像目录,里面放着 ARM64 版 libc、内核头文件等
- Qt 源码:
qtbase-everywhere-src-5.14.2,只取 base 模块就能支撑 Qt Widgets 应用 - 目标形态:Qt 静态库 + 静态平台插件 + 你的业务代码,全部链进一个 ARM64 可执行文件
理解了这张图,后面每一步你都知道自己在干嘛,而不是机械地复制命令。
2. 工具链与 sysroot:比 Qt 本身更能决定成败
2.1 交叉工具链的安装
交叉编译的起点不是 Qt,而是能不能用正确的编译器编出能在 ARM64 上运行的程序。Ubuntu 官方仓库直接提供了完整的 aarch64 交叉工具链,安装很简单:
sudo apt update sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu这条命令会把aarch64-linux-gnu-gcc、aarch64-linux-gnu-g++、aarch64-linux-gnu-ld、aarch64-linux-gnu-ar等一系列工具都装好。验证一下版本:
aarch64-linux-gnu-g++ --version如果能正常打印版本号,说明工具链本身没问题。我这里故意用 Ubuntu 自带的工具链,而不是去折腾 Linaro 或者板厂定制 SDK,原因是通用性最高,踩坑最少。板厂 SDK 往往绑定了特定内核和交叉编译器版本,适合做量产固件,不适合我们这种"自己搭 Qt 环境"的场景。
2.2 sysroot 的三种来源
sysroot 是交叉编译最容易翻车的地方,它表示"目标系统在主机上的一个影子目录"。编译 Qt 时,头文件、库文件、链接脚本都要从这个影子目录里找,而不是从宿主机找。
我实际用过三种方式,各有利弊:
- 直接使用 Ubuntu 多架构目录:安装
libc6-dev-arm64-cross后,/usr/aarch64-linux-gnu就是一个可用的 sysroot,里面有 ARM64 版 libc 静态库、动态库和头文件。这是最简单的起步方式,适合验证流程。 - 用 debootstrap 拉一个完整的 arm64 rootfs:
sudo debootstrap --arch=arm64 bullseye /opt/sysroot-arm64这种方式可以得到一个纯正的目标系统根目录,还能用 chroot 进去装额外的开发包,比较接近目标板真实环境。
- 复用板厂 SDK 里的镜像目录:很多开发板 BSP 自带一个 rootfs,直接把里面的
/usr和/lib抽出来用。这种方式最贴近最终部署环境,但目录可能比较杂乱。
对于第一次尝试的人,我建议用第一种,干净利落:
sudo apt install libc6-dev-arm64-cross装完之后确认/usr/aarch64-linux-gnu目录存在,并且里面有lib、usr/include、usr/lib等子目录。
2.3 验证工具链和 sysroot 是否匹配
工具链和 sysroot 能不能一起干活,用一个小测试就能验证。写一个空的测试文件:
echo 'int main() { return 0; }' > test.cpp aarch64-linux-gnu-g++ test.cpp -o test_arm64 file test_arm64file输出里如果出现ELF 64-bit LSB executable, ARM aarch64,就说明编译器能编译出 ARM64 可执行文件。再进一步,把 sysroot 显式传进去:
aarch64-linux-gnu-g++ test.cpp -o test_arm64 --sysroot=/usr/aarch64-linux-gnu这一步能正常通过,说明头文件和库文件的搜索路径都对得上,后面给 Qt configure 传-sysroot参数时心里就有底了。
还有一个细节,某些发行版的交叉编译器会在链接时寻找ld-linux-aarch64.so.1这样的动态链接器,如果 sysroot 里只有lib/ld-linux-aarch64.so.1而没有对应的符号链接,链接可能报cannot find /lib/ld-linux-aarch64.so.1。遇到这种情况检查一下 sysroot 的 lib 目录,手动补上符号链接即可。
3. Qt 源码准备:mkspec 与 configure 就是这场戏的剧本
3.1 只取 qtbase 而不是把整个 everywhere 源码拿下来
很多人一上来就下载qt-everywhere-src-5.14.2.tar.xz,那个包包含所有模块,解压后十几个 G,编译起来时间长得让人怀疑人生。如果你只是做 Qt Widgets 程序,完全没必要。单独的qtbase-everywhere-src-5.14.2.tar.xz就包含 QtCore、QtGui、QtWidgets、QtNetwork 这些最核心的模块,足以覆盖绝大多数 HMI 场景。
wget https://download.qt.io/archive/qt/5.14/5.14.2/qtbase-everywhere-src-5.14.2.tar.xz tar xf qtbase-everywhere-src-5.14.2.tar.xz cd qtbase-everywhere-src-5.14.2先用 qtbase 打通整个链路,如果后面确实需要 Qt Charts、Qt Multimedia 这类模块,再用相同的 configure 思路叠加编译,复杂度可控得多。
3.2 自定义 linux-aarch64-gnu-g++ mkspec
Qt 编译目标平台的行为由 mkspec 定义,它告诉 qmake 和 configure:编译器叫什么、链接器怎么用、cflags 应该加什么。Qt 5.14.2 自带的 mkspecs 里有 32 位 ARM 的linux-arm-gnueabi-g++,没有现成的 aarch64 版本,所以我们要拷贝一份再改。
cp -r qtbase/mkspecs/linux-arm-gnueabi-g++ qtbase/mkspecs/linux-aarch64-gnu-g++然后修改qtbase/mkspecs/linux-aarch64-gnu-g++/qmake.conf,改成下面这样:
MAKEFILE_GENERATOR = UNIX TARGET_PLATFORM = unix TEMPLATE = app CONFIG += incremental QMAKE_INCREMENTAL_STYLE = sublib include(../common/linux.conf) include(../common/gcc-base-unix.conf) include(../common/g++-unix.conf) 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_LINK_C = aarch64-linux-gnu-gcc QMAKE_LINK_C_SHLIB = aarch64-linux-gnu-gcc QMAKE_AR = aarch64-linux-gnu-ar cqs QMAKE_NM = sh -c "aarch64-linux-gnu-nm -P \$(basename \$0)" QMAKE_STRIP = aarch64-linux-gnu-strip QMAKE_RANLIB = aarch64-linux-gnu-ranlib QMAKE_OBJCOPY = aarch64-linux-gnu-objcopy QMAKE_CFLAGS += -fPIC -O2 -march=armv8-a QMAKE_CXXFLAGS += -fPIC -O2 -march=armv8-a load(qt_config)这里的关键点:
QMAKE_CC/QMAKE_CXX指向交叉编译器,别写错名字-march=armv8-a是 ARM64 的基础指令集,如果你的目标芯片是 Cortex-A53、A72 这类常见核心,这个参数没问题;如果用到特定 SoC 的优化指令,可以换成-mcpu=cortex-a53之类的QMAKE_AR用cqs参数,静态库打包时安静且覆盖索引,这是交叉编译静态 Qt 的一个细节,少了可能导致链接时符号表找不到-fPIC对静态库不是强制要求,但保留它能让部分底层次序处理更省心,性能损失也几乎可以忽略
3.3 configure 参数逐个拆解
mkspec 备好之后,进入源码根目录执行 configure。这是整个手册的核心,我先把完整命令贴出来,再逐个解释为什么这么写:
./configure \ -prefix /opt/Qt5.14.2/5.14.2/aarch64-static \ -extprefix $HOME/qt-aarch64-static-install \ -xplatform linux-aarch64-gnu-g++ \ -release -static -optimize-size \ -opensource -confirm-license \ -make libs \ -nomake tests -nomake examples \ -no-opengl -no-xcb \ -no-dbus -no-glib -no-icu -no-iconv \ -no-openssl -no-cups \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz \ -linuxfb| 参数 | 含义 |
|---|---|
-prefix | Qt 在目标板上期望的安装路径,静态编译下主要影响 qmake 生成的路径变量 |
-extprefix | Qt 实际安装到开发主机上的位置,交叉编译时这里很关键 |
-xplatform | 指定我们刚创建的 mkspec 名称 |
-static | 生成静态库,这是本文的最终目标 |
-release -optimize-size | 编译发布版,优先优化体积 |
-make libs | 只构建库,不构建工具和插件之外的东西 |
-nomake tests -nomake examples | 砍掉测试和示例,大幅缩短编译时间 |
-no-opengl | 不使用 OpenGL,前提是你的应用不需要 QML/QtQuick |
-linuxfb | 启用 Linux Framebuffer 平台插件,这是无 X11、无 Wayland 环境下跑 Widgets 程序的方案 |
-prefix和-extprefix是交叉编译最容易混淆的一对。简单说:-prefix写的是"将来 Qt 在目标板上被放在哪里",-extprefix写的是"现在 Qt 在开发机上被安装到哪里"。由于静态编译最终不需要向目标板部署 Qt 库,-prefix更多是让 qmake 生成的配置信息保持合理,真正落地的是-extprefix。
-no-opengl这个开关值得多说一句。如果目标板没有 GPU 或你不需要 QtQuick,加这个可以避免 sysroot 里必须准备 EGL/GLES 头文件和库的麻烦。很多嵌入式板子的显卡驱动只有二进制闭源 so,静态交叉编译时链接这些库会遇到一堆意料之外的问题,能躲就躲。如果确实需要 OpenGL,后面我会在进阶章节讲怎么处理。
依赖库的处理我特意用了-qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz,意思是让 Qt 使用源码树里自带的这些第三方库并将其静态编译进来。这一步省掉了一大堆单独交叉编译依赖库的苦力活。不要一上来就去追求-system-zlib之类的系统库链接,那个看起来很专业,实际上每个库都要你手动交叉编译,任何一个版本不匹配都会把 configure 卡死。
3.4 首次进入 configure 时应该看到什么
执行 configure 后,脚本会花几分钟做编译探测。最后如果能看到类似这样的输出,基本说明环境配置成功了:
Qt is now configured for building. Just run 'make' once to create all the build artifacts.如果中途出现ERROR:开头的行,不要慌,把错误信息里提到的文件路径和缺失组件名记下来,多半是 sysroot 里缺了某个头文件或库。最常见的错误是crt1.o: No such file or directory,这是因为libc6-dev-arm64-cross没装或 sysroot 路径不对。看到这个错误先检查 sysroot,而不是重新去改 configure 参数。
4. 跑 make 和安装:一套可以直接照抄的实测命令
4.1 编译前最后检查
在我实际执行 make 之前,习惯性做三件事:
- 确认当前 shell 里
aarch64-linux-gnu-g++在 PATH 中 - 确认
$HOME/qt-aarch64-static-install目录可写 - 确认磁盘剩余空间大于 5GB,静态编译的中间文件并不小
df -h . which aarch64-linux-gnu-g++第三点很容易被忽略。Qt 静态编译的中间 .o 文件、.a 文件加起来体积很大,如果过编译的时候磁盘写满,报的错误会很莫名其妙,而且排查起来浪费时间。
4.2 正式执行 make
一切就绪后,直接跑:
make -j$(nproc)-j$(nproc)能让所有 CPU 核心都参与编译。但这里有个教训:如果你的机器内存小于 8GB,别盲目开满核心。Qt 编译是典型的高内存消耗任务,16 核机器上-j16很可能把内存吃满导致 OOM,编译器被内核杀掉,终端上只剩一行Killed。遇到这种情况,先用-j4甚至-j2跑,稳比快重要。
编译 qtbase 静态版在我的机器上大概需要二十分钟到四十分钟,取决于 CPU 性能。中断之后重新执行make是可以续传的,它只重建上次未完成的部分,这一点 Qt 的构建系统做得很老实,不需要每次从头来。
4.3 make install 与产物验证
编译完成后:
make install安装完成后,验证 Qt 的交叉 qmake 是否可用:
$HOME/qt-aarch64-static-install/bin/qmake -v正常输出类似于:
QMake version 3.1 Using Qt version 5.14.2 in /home/you/qt-aarch64-static-install我这里qmake -v显示的路径是$HOME/qt-aarch64-static-install,也就是-extprefix指定的安装目录。看到这个输出,说明 Qt 静态库已经成功构建并安装。
再检查一下静态库是不是真的静态的:
ls $HOME/qt-aarch64-static-install/lib/libQt5Widgets.a能列出libQt5Widgets.a就说明 Qt Widgets 是以静态库形式存在的。
4.4 编译中遇到的三个高频报错
报错 1:crt1.o: No such file or directory
原因:sysroot 里缺少目标系统的 C 运行时启动文件。多半是libc6-dev-arm64-cross没装全或者-sysroot参数指向了空目录。先确认/usr/aarch64-linux-gnu/usr/lib/aarch64-linux-gnu/crt1.o是否存在。
报错 2:GL/gl.h: No such file or directory
原因:sysroot 里没有 OpenGL 头文件。如果你根本不需要 OpenGL,把 configure 里的-no-opengl加上;如果确实需要,需要把板子的 EGL/GLES 开发头文件放进 sysroot。
报错 3:cannot find -lGL
原因:链接阶段找不到 OpenGL 库。同上,要么去掉 OpenGL 依赖,要么提供正确的静态libGL.a或链接路径。这条报错的坑在于,有时候 configure 阶段探测时没有 OpenGL 也让你过了,实际链接 app 时却要求-lGL,这种情况要在.pro文件里显式关闭相关模块或补全库路径。
这三个是我实战中遇到频率最高的,把 sysroot 和 OpenGL 这两个根源解决掉,其他报错基本都能顺藤摸瓜查出来。
5. 编译第一个静态 Qt 程序:从 qmake 到目标板
5.1 一个最小的 Qt Widgets 工程
环境搭好之后,用一个小工程验证全套链路。先准备myapp.pro:
QT += core widgets TARGET = myapp TEMPLATE = app CONFIG += c++11 SOURCES += main.cpp QTPLUGIN += qlinuxfb再准备main.cpp:
#include <QApplication> #include <QLabel> #include <QtPlugin> Q_IMPORT_PLUGIN(QFbIntegrationPlugin) int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label("Hello ARM64 Qt"); label.resize(320, 120); label.show(); return app.exec(); }这段代码里Q_IMPORT_PLUGIN(QFbIntegrationPlugin)非常关键。静态链接的 Qt 不会在运行时去/plugins/platforms目录动态加载libqlinuxfb.so,平台插件必须在编译期直接链进可执行文件。这个宏就是告诉编译器把QFbIntegrationPlugin这个平台的符号导入进来。
如果漏掉这行,编译出来的程序在板子上运行时会报:
This application failed to start because no Qt platform plugin could be initialized.所以,先别问为什么这么写,记住:静态 Qt 工程必须用Q_IMPORT_PLUGIN手动引入你需要的平台插件。这部分是静态交叉编译和动态编译最显著的区别。
5.2 用交叉 qmake 构建应用
export PATH=$HOME/qt-aarch64-static-install/bin:$PATH cd myapp qmake myapp.pro make这里有个容易踩的坑:export PATH之后,qmake不一定真的是新装的交叉 qmake。先用which qmake确认:
which qmake # 期望看到 /home/you/qt-aarch64-static-install/bin/qmake如果 which 结果指向系统自带的 Qt5 qmake,那是 x86_64 的,编译出来的程序在板子上根本跑不了。这类问题比编译报错更隐蔽,因为它能正常编译通过,只有在板子上跑的时候才会发现"不是 ARM64 架构"。
构建完成后:
file myapp输出里应该有:
ELF 64-bit LSB executable, ARM aarch64, statically linked看到static关键字,说明 Qt 是静态链进去的。如果你还额外用了-static-libstdc++之类的选项,这里也可能会显示statically linked。当然,如果系统 glibc 是动态链入的,这里可能显示部分动态标志,后面我会解释这是有意的选择。
5.3 部署到目标板的运行验证
把myapp拷到 ARM64 板子上,方式可以是 U 盘、UART 传文件或者网络。我的习惯是先用scp:
scp myapp root@192.168.1.100:/root/然后 ssh 到板子上执行:
chmod +x /root/myapp /root/myapp -platform linuxfb加-platform linuxfb是显式指定使用 Linux Framebuffer 插件,避免 Qt 自动探测平台时出岔子。
如果板子的/dev/fb0存在且帧缓冲初始化正常,屏幕上就能看到那个 "Hello ARM64 Qt" 的窗口。这个验证页面虽然简单,但它把工具链、sysroot、mkspec、configure、静态编译、插件导入、目标板运行验证整条链路都串起来了。
第一次跑通的那一刻,值得记录一下时间点,因为后面所有业务逻辑都是在这个基础之上展开的。
5.4 部署时还要注意什么
静态编译解决了 Qt 库的依赖问题,但有一些运行时数据依然不是"静态"的:
- 时区数据、ssl 证书这些系统级文件仍然依赖目标板
- 中文字体库不会自动进二进制,目标板没有对应字体会出现方块字
- 输入设备支持:如果要使用触摸屏,一般要另外交叉编译 tslib 或 evdev 相关支持
这些不是 Qt 静态编译能一揽子解决的,需要在部署阶段单独处理。我在下一节里专门讲字体和体积这两个最常被问到的日常问题。
6. 静态包说大不大,说小不小:优化与进阶实战
6.1 体积瘦身三板斧
静态编译最直观的代价就是体积。一个最简单的 Qt Widgets 程序,编译出来动辄十几兆。瘦身可以从以下几个方面入手:
第一,strip。安装 Qt 时已经带上了交叉 strip 工具:
aarch64-linux-gnu-strip --strip-unneeded myapp这一条命令通常能把可执行文件从 16MB 压到 12MB 左右,效果非常直接。
第二,configure 阶段控制模块和特性。-nomake tests -nomake examples只是砍掉了额外工程,真正影响库体积的是 Qt 的特性模块。可以在 configure 里追加:
-no-feature-gif -no-feature-ftp -no-feature-dns-lookup这类特性开关按需裁剪,Qt 5.14.2 提供了非常细粒度的-no-feature-*选项。不过我要提醒一句:刚开始别裁太狠,有些功能看起来没用,实际代码里可能间接依赖,等编译报错再去排查很费劲。建议先全量编译跑通,再逐步裁剪。
第三,-optimize-size参数在 configure 里已经加入,它让编译器按照-Os级别优化,在可执行文件体积和运行速度之间取了一个偏体积的平衡点。如果你对性能要求更高,可以把-optimize-size改成 `-optimize-ful...
6.2 中文字体和缺字方块问题
很多人费劲把静态 Qt 程序跑到板子上,结果界面上所有中文都变成了方块。这不是代码 bug,而是目标板系统里没有可用的中文字体文件。
几个解决思路:
- 把字体内嵌进 Qt 资源文件。把
NotoSansCJKsc-Regular.otf或一个精简的 ttf 放进.qrc,程序启动时用QFontDatabase::addApplicationFont(":/fonts/your_font.ttf")注册,再用对应字体族名设置全局字体。这是最稳妥的方案,不依赖目标板的文件系统。 - 给目标板补字体目录。把字体的拷贝到板子的
/usr/share/fonts下,设置环境变量QT_QPA_FONTDIR=/usr/share/fonts。如果 Qt 编译时带了 fontconfig,这一步更自然,但我通常不在静态编译里引入 fontconfig,因为它是又一个要交叉编译的依赖。 - 纯英文界面。如果业务允许,直接不做中文字体支持,省掉体积也省掉麻烦。
我个人强烈推荐第一种,字体内嵌资源,因为静态编译的意义就在于"一个文件走天下",字体放进资源文件才符合这个精神。
6.3 真正的全静态和 glibc 的纠缠
这篇文章标题叫"静态交叉编译",但我要诚实地告诉你:Qt 本身的静态只是绝大部分静态。因为链接器默认会把目标板系统的 libc、libstdc++ 作为动态依赖保留。要验证是否是"全静态",在板子上跑:
ldd ./myapp如果输出全是statically linked,说明连 glibc 也链进去了;如果还有几条libc.so.6、libstdc++.so.6,说明系统库还是动态的。
很多嵌入式板子的系统里自带 glibc 和 libstdc++ 动态库,所以 Qt 静态 + 系统库动态这个混合状态完全能跑,而且我建议保持这种状态。为什么会这样?因为想让二进制完全静态,需要在编译时加-static并确保 sysroot 里有齐全的 libc 静态库,像libc.a、libm.a这些。全静态链接少数 glibc 版本会出现 DNS 解析问题,比如程序里用QNetworkAccessManager访问域名时卡住不动,排查起来非常难受。
如果确实需要尽可能静的二进制,可以在.pro里追加:
QMAKE_LFLAGS += -static-libgcc -static-libstdc++先把 gcc 和 C++ 标准库静态链进,剩下 libc 保持动态。这是一个很好的折中方案,既能减少目标板的依赖,又规避了全静态 glibc 的 DNS 隐患。
6.4 给 Qt 加入 OpenSSL 支持扩展
很多 HMI 项目后期都会遇到 HTTPS 请求的需求,比如对接云平台、设备管理接口。Qt 的QNetworkAccessManager要支持 HTTPS,依赖 OpenSSL 库。静态编译场景下有两种处理方式:
一是-no-openssl,程序只能走 HTTP,开发测试够用但上线不够安全。
二是交叉编译 OpenSSL 并让 Qt 静态链接它。大致步骤:
wget https://www.openssl.org/source/openssl-1.1.1t.tar.gz tar xf openssl-1.1.1t.tar.gz cd openssl-1.1.1t ./Configure linux-aarch64 no-shared no-asm --prefix=/usr/aarch64-linux-gnu make -j$(nproc) sudo make install然后回到 Qt 的 configure,把原来的-no-openssl换成:
-openssl-linked -I/usr/aarch64-linux-gnu/include -L/usr/aarch64-linux-gnu/libno-shared保证了 OpenSSL 以静态库形式编译,这样 Qt 才能把它链进最终的可执行文件。注意这里没加-no-asm,如果交叉编译器对某些汇编指令支持不好,再加上去。
这一步会显著增加编译时间,也需要目标板的内核支持足够的新特性。作为扩展方案,我只在确有 HTTPS 需求时才会引入,绝大多数纯本地的 HMI 界面用不上。
6.5 一个实际的体积参考数据
我把一个带 QWidget、QPushButton 和 QLabel 的测试程序,以静态方式编译后做了简单测量:
| 状态 | 体积 |
|---|---|
| 正常编译,未 strip | 约 18MB |
| strip 之后 | 约 13MB |
| 裁剪部分 Qt feature 后 | 约 11MB |
不同模块使用情况差异很大,如果你只用 QtCore 不用 Widgets,体积会小很多。这个数据仅供参考,但它能帮你提前预估目标板的存储占用。
我在实际交付这类项目时还有一个习惯:版本号和时间戳在编译阶段写进程序里,方便后面在板子上用myapp --version确认固件版本。这个习惯在静态编译工程里尤其有用,因为二进制完全独立,没有任何库版本信息可查,唯一的追溯依据就是你编译进去的那几个数字。