ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

aarch64 上 Qt 5.14.2 静态交叉编译实战:工具链、sysroot 与 configure 全解析

2026/9/20 18:29:11 拓冰建站 浏览量
aarch64 上 Qt 5.14.2 静态交叉编译实战:工具链、sysroot 与 configure 全解析 1. 为什么要在 aarch64 上折腾 Qt 5.14.2 静态编译如果你手上有 Orange Pi CM5、树莓派这类 aarch64 开发板又恰好要做一套需要独立分发的 Qt 界面程序那你大概率绕不开“静态交叉编译”这条路。动态编译出来的可执行文件扔到板子上跑十有八九会报error while loading shared libraries: libQt5Core.so.5: cannot open shared object file。原因很简单目标板上的 Qt 运行库版本和你编译时用的不一致或者压根没装。静态编译就是把 Qt 的核心库直接塞进可执行文件里拷过去就能跑不用在板子上再装一堆依赖。Qt 5.14.2 这个版本有点特殊。它是 Qt 5.14 系列的最后一个补丁版本稳定性在 5.14 里算是最好的同时它又比 5.12 系列多了不少新特性比如对 C17 的更好支持、QML 的一些性能改进。但麻烦的地方在于Qt 从 5.15 开始官方就不再提供离线安装包了5.14.2 是最后几个还能拿到完整离线安装包的版本之一。所以很多人做嵌入式项目会卡在 5.14.2 这个版本上既想要它的稳定性又不想升级到 5.15 去折腾在线安装。静态交叉编译这件事本质上是在宿主机比如 x86_64 的 Ubuntu上用一套针对 aarch64 的交叉编译工具链把 Qt 源码编译成 aarch64 架构的静态库然后再用这套静态库去编译你的应用程序。整个过程涉及三个关键角色宿主机、交叉编译工具链、目标板。宿主机负责干活工具链负责把代码翻译成 aarch64 能懂的机器码目标板负责最终运行。三者之间的架构差异就是所有坑的根源。我见过太多人在这件事上翻车不是因为技术有多难而是因为细节太多、文档太散。网上搜到的教程要么是 Qt 5.12 的要么是动态编译的要么工具链版本对不上。所以这篇东西我打算把从零搭建的完整链路拆开把每个环节的“为什么”和“怎么做”都讲清楚让你少走点弯路。2. 工具链选型为什么是 gcc-arm-10.3 而不是别的2.1 工具链版本与 Qt 5.14.2 的匹配逻辑交叉编译工具链的选择直接决定了后面 Qt 编译能不能过。Qt 5.14.2 官方文档里推荐的 GCC 版本是 5.3 到 9.x但那是针对 x86 的。做 aarch64 交叉编译你得用aarch64-linux-gnu-gcc这一套。我试过好几个版本最后锁定在gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu这个工具链上。为什么是 10.3 而不是更新的 11.x 或 12.x因为 Qt 5.14.2 的源码里有些老旧的汇编代码和 C 特性在 GCC 10 以上版本编译时会遇到-Werror导致的编译中断。GCC 10.3 刚好在“支持 C17 足够好”和“不会因为新警告把老代码卡死”之间找到了平衡点。再新的工具链比如 GCC 11编译 Qt 5.14.2 的qtbase时会在qsimd_p.h里报一堆-Wdeprecated-copy的警告然后因为 Qt 默认开了-Werror直接编译失败。另一个原因是 glibc 版本。这个工具链自带的 glibc 是 2.33而大多数 aarch64 开发板比如 Orange Pi CM5 默认的 Ubuntu 22.04的 glibc 是 2.35。静态编译时glibc 的版本兼容性其实不是大问题因为静态链接会把 libc 也打进去。但如果你后面要链接一些动态的系统库glibc 版本差异就会跳出来咬你一口。所以工具链的 glibc 版本最好和目标板接近2.33 和 2.35 之间差异不大基本能兼容。2.2 工具链的获取与环境变量配置工具链的下载地址我一般从 ARM 官方开发者网站拿。下载下来是个.tar.xz包解压到/opt目录下sudo tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt解压完目录结构是/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/里面有一堆aarch64-none-linux-gnu-*开头的可执行文件。注意这里的工具链前缀是aarch64-none-linux-gnu-不是常见的aarch64-linux-gnu-。这个none表示没有指定具体的厂商是 ARM 官方工具链的命名习惯。你在配置 Qt 的-device-option时得用这个前缀。环境变量配置我习惯写进~/.bashrcexport PATH/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH export CROSS_COMPILEaarch64-none-linux-gnu- export ARCHarm64配完记得source ~/.bashrc然后验证一下aarch64-none-linux-gnu-gcc -v如果能看到gcc version 10.3.1 20210621之类的输出说明工具链就位了。注意不要用apt install gcc-aarch64-linux-gnu装的那个工具链。Ubuntu 仓库里的版本通常是 9.x 或 11.x而且缺少一些 Qt 编译需要的头文件比如linux/input.h的某些定义。用官方工具链省心。2.3 sysroot 的提取与必要性交叉编译 Qt 时sysroot是个绕不开的概念。简单说sysroot就是目标板根文件系统的一个副本里面包含了目标板上的头文件和库文件。Qt 编译时会去sysroot里找libz.so、libpng.so这些依赖库。如果你不指定sysrootQt 就会去宿主机的/usr/lib里找 x86 的库链接阶段直接报架构不匹配。提取sysroot有两种方式。一种是从目标板直接rsync过来rsync -avz --delete root192.168.1.100:/lib /opt/sysroot/ rsync -avz --delete root192.168.1.100:/usr/include /opt/sysroot/usr/ rsync -avz --delete root192.168.1.100:/usr/lib /opt/sysroot/usr/另一种是用工具链自带的sysroot。ARM 官方工具链里其实已经包含了一个基础的sysroot在/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/目录下。这个sysroot包含了 glibc 和基本的系统库但缺少一些开发板特有的库比如libmali、libcedar这些。如果你只是编译 Qt 核心库用工具链自带的sysroot就够了。但如果你要编译 Qt 的qtmultimedia模块就得从板子上把相关的库拷过来。我一般会建一个/opt/aarch64-sysroot目录把工具链自带的sysroot拷进去再从板子上补充一些必要的库sudo mkdir -p /opt/aarch64-sysroot sudo cp -a /opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/* /opt/aarch64-sysroot/然后从板子上把/usr/include和/usr/lib里你需要的部分拷过来。注意不要整个/usr/lib都拷那里面东西太多而且有些库是动态链接的拷过来反而会干扰静态编译。只拷你确定需要的比如libz、libpng、libjpeg这些。3. Qt 5.14.2 源码的配置与裁剪策略3.1 源码获取与目录规划Qt 5.14.2 的源码包官方下载地址是https://download.qt.io/archive/qt/5.14/5.14.2/single/文件名是qt-everywhere-src-5.14.2.tar.xz。这个包包含了 Qt 的所有模块解压后大概 5 个 G。如果你只需要核心模块可以只编译qtbase但大多数项目至少还需要qtsvg、qtdeclarative这些所以直接下完整包更省事。解压到一个独立目录比如/opt/qt-5.14.2-srcsudo tar -xf qt-everywhere-src-5.14.2.tar.xz -C /opt sudo mv /opt/qt-everywhere-src-5.14.2 /opt/qt-5.14.2-src编译目录和源码目录要分开这是 Qt 官方推荐的做法。在源码目录外面建一个build目录mkdir -p /opt/qt-5.14.2-build cd /opt/qt-5.14.2-build这样做的好处是如果编译失败你可以直接删掉build目录重新来不用重新解压源码。3.2 configure 参数逐条拆解Qt 的configure脚本参数极多但做静态交叉编译核心就那么十几个。下面是我在多次实践中总结出来的一套参数先贴出来再逐条解释/opt/qt-5.14.2-src/configure \ -prefix /opt/qt-5.14.2-aarch64-static \ -opensource \ -confirm-license \ -release \ -static \ -nomake examples \ -nomake tests \ -no-opengl \ -no-xcb \ -no-eglfs \ -no-linuxfb \ -no-kms \ -no-glib \ -no-icu \ -no-cups \ -no-pch \ -no-dbus \ -no-feature-accessibility \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -qt-freetype \ -qt-harfbuzz \ -qt-pcre \ -qt-sqlite \ -skip qtwebengine \ -skip qtwebview \ -skip qtquick3d \ -skip qtcharts \ -skip qtdatavis3d \ -skip qtnetworkauth \ -skip qtpurchasing \ -skip qtscript \ -skip qtspeech \ -skip qtvirtualkeyboard \ -skip qtwayland \ -skip qtwebchannel \ -skip qtwebglplugin \ -skip qtwebsockets \ -skip qtwinextras \ -skip qtx11extras \ -skip qtxmlpatterns \ -device-option CROSS_COMPILEaarch64-none-linux-gnu- \ -device-option DISTROopeneuler \ -sysroot /opt/aarch64-sysroot \ -platform linux-g \ -xplatform linux-aarch64-gnu-g-prefix指定安装路径编译完成后make install会把所有静态库和头文件装到这个目录下。-static是核心告诉 Qt 编译静态库。-release表示编译发布版不带调试符号体积更小。-no-opengl这个参数要特别小心。如果你的板子有 GPU并且你需要 Qt Quick 的硬件加速那就不能加这个参数。但如果你只是用 Qt Widgets 做界面或者板子的 GPU 驱动不完善加上-no-opengl可以避免一堆 EGL 相关的编译错误。我遇到过 Orange Pi CM5 上 Mali GPU 驱动和 Qt 的 EGL 接口不兼容的情况最后就是靠-no-opengl绕过去的。-no-xcb、-no-eglfs、-no-linuxfb、-no-kms这几个是把所有和显示后端相关的选项都关掉。静态编译时这些后端会引入大量动态库依赖关掉它们能让编译过程干净很多。你的应用程序最终怎么显示用QPA插件比如linuxfb或eglfs这些插件可以在应用程序运行时动态加载不需要在 Qt 编译时静态链接进去。-qt-zlib、-qt-libpng、-qt-libjpeg、-qt-freetype、-qt-harfbuzz、-qt-pcre、-qt-sqlite这一串是让 Qt 使用它自带的第三方库版本而不是去sysroot里找系统库。这样做的好处是版本可控不会因为系统库版本差异导致链接失败。坏处是编译时间变长因为 Qt 要把这些库也编译一遍。但静态编译本来就是个耗时活多这几分钟无所谓。-skip那一长串是跳过你不需要的模块。Qt 5.14.2 默认会编译所有模块但很多模块比如qtwebengine体积巨大而且依赖复杂编译起来极其耗时。如果你不需要浏览器引擎直接-skip qtwebengine能省下至少一个小时的编译时间。-device-option CROSS_COMPILEaarch64-none-linux-gnu-这个参数是告诉 Qt 的qmake配置交叉编译工具链的前缀是什么。-device-option DISTROopeneuler是我自己加的用来标记目标系统的发行版其实不影响编译只是方便后面查日志。-sysroot /opt/aarch64-sysroot指定sysroot路径。-platform linux-g表示宿主机平台是 Linux用 g 编译。-xplatform linux-aarch64-gnu-g表示目标平台是 aarch64 Linux用 g 编译。这个linux-aarch64-gnu-g是 Qt 源码里自带的mkspec在qtbase/mkspecs/linux-aarch64-gnu-g/目录下。你可以打开qmake.conf看看里面定义了QMAKE_CC、QMAKE_CXX这些变量默认就是aarch64-linux-gnu-gcc。如果你的工具链前缀是aarch64-none-linux-gnu-那就得改这个文件或者用-device-option CROSS_COMPILE覆盖。3.3 裁剪模块的取舍逻辑哪些模块该跳过哪些该保留这取决于你的项目需求。但有几个模块我建议无论如何都跳过模块建议理由qtwebengine跳过依赖 Chromium编译需要几十 G 内存耗时数小时qtwebview跳过依赖 qtwebengineqtquick3d跳过依赖 OpenGL静态编译下问题多qtcharts按需如果不用图表跳过能省不少时间qtdatavis3d跳过同上且依赖 OpenGLqtscript跳过已废弃新项目不该用qtvirtualkeyboard按需如果不需要虚拟键盘跳过qtwayland跳过除非你的板子用 Wayland 显示服务器qtwebsockets按需如果不用 WebSocket跳过qtx11extras跳过X11 相关嵌入式一般不用保留的模块至少要有qtbase、qtsvg、qtdeclarative如果你用 QML、qttools如果需要linguist这些工具。qtbase是核心包含了 QtCore、QtGui、QtWidgets、QtNetwork 这些最常用的库。qtsvg用于显示 SVG 图片很多界面素材都是 SVG 格式建议保留。4. 编译过程中的典型报错与排查链路4.1 第一个坑Project ERROR: Unknown module(s) in QT: serialport这个报错太常见了热词里都出现了好几次。原因是你用了QT serialport但 Qt 编译时没有编译qtserialport模块。qtserialport在 Qt 5.14.2 里是一个独立模块默认不编译。你需要在configure时加上-qt-serialport或者确保没有-skip qtserialport。但更隐蔽的情况是你确实编译了qtserialport但你的.pro文件里写的是QT serialport而qtserialport的模块名在qmake里是serialport不是qtserialport。这个命名不一致是 Qt 的历史遗留问题。检查方法是看qtbase/mkspecs/modules/目录下有没有qt_lib_serialport.pri文件。如果没有说明qtserialport没编译进去。解决办法重新configure确保没有-skip qtserialport然后make module-qtbase之后单独make module-qtserialport。如果还是不行检查sysroot里有没有libudev的开发头文件因为qtserialport在 Linux 下依赖libudev来枚举串口设备。4.2 第二个坑cannot mix incompatible Qt library (5.15.3) with this library (5.15.2)这个报错通常出现在你编译完静态 Qt 后用qmake生成 Makefile 时。原因是你的PATH里同时存在多个 Qt 版本的qmake。比如你系统里装了 Qt 5.15.3 的动态版/usr/bin/qmake指向它而你的静态 Qt 的qmake在/opt/qt-5.14.2-aarch64-static/bin/qmake。你调用qmake时系统优先找到了/usr/bin/qmake于是生成的 Makefile 里链接的是 5.15.3 的库但你的代码是用 5.14.2 的头文件编译的版本不匹配。解决办法永远用绝对路径调用你的静态qmake/opt/qt-5.14.2-aarch64-static/bin/qmake your_project.pro或者把静态 Qt 的bin目录放到PATH最前面export PATH/opt/qt-5.14.2-aarch64-static/bin:$PATH然后which qmake确认一下指向的是静态版本。4.3 第三个坑error: numeric_limits is not a member of std这个报错是 GCC 10 的典型问题。Qt 5.14.2 的某些源码文件里用了std::numeric_limits但没有#include limits。在 GCC 9 及以下版本这个头文件可能被其他头文件间接包含了所以能编译过。但 GCC 10 收紧了头文件包含规则不再间接包含limits于是报错。解决办法在报错的文件开头手动加上#include limits。但 Qt 源码文件太多一个个改太麻烦。更优雅的方式是在configure时加上-no-pch禁用预编译头然后给编译器加一个全局的-include limits参数export CXXFLAGS-include limits export CFLAGS-include limits这样每个编译单元都会自动包含limits不用改源码。4.4 第四个坑链接时undefined reference to pthread_create静态编译时pthread 库的链接顺序很重要。Qt 的qmake生成的 Makefile 里-lpthread可能出现在-lQt5Core之前导致链接器找不到pthread_create的定义。解决办法是在.pro文件里显式加上LIBS -lpthread -ldl -lrt并且确保这些库出现在 Qt 库之后。如果还不行在qmake生成的 Makefile 里手动调整LIBS变量的顺序把-lpthread放到最后。5. 静态库的安装与应用程序的编译验证5.1make install之后的目录结构编译完成后执行make install所有静态库和头文件会装到-prefix指定的目录下。目录结构大概是/opt/qt-5.14.2-aarch64-static/ ├── bin/ │ ├── qmake │ ├── moc │ ├── uic │ └── rcc ├── include/ │ ├── QtCore/ │ ├── QtGui/ │ └── ... ├── lib/ │ ├── libQt5Core.a │ ├── libQt5Gui.a │ ├── libQt5Widgets.a │ └── ... └── mkspecs/ ├── linux-aarch64-gnu-g/ └── modules/lib目录下的.a文件就是静态库。你的应用程序链接时会把这些.a文件里的代码直接拷贝到可执行文件里。所以最终生成的可执行文件会比较大一个简单的 Qt Widgets 程序静态链接后大概 10 到 20 MB。这是正常的因为 QtCore、QtGui、QtWidgets 的代码都被打进去了。5.2 用静态 qmake 编译一个测试程序写一个最简单的 Qt 程序来验证// 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 hello_qt_static TEMPLATE app SOURCES main.cpp编译/opt/qt-5.14.2-aarch64-static/bin/qmake hello_qt_static.pro make -j$(nproc)编译完成后用file命令检查生成的可执行文件file hello_qt_static输出应该是ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, ...。注意statically linked这个关键词说明静态链接成功了。然后把这个文件拷到目标板上运行scp hello_qt_static root192.168.1.100:/tmp/ ssh root192.168.1.100 /tmp/hello_qt_static如果板子上有显示服务器比如linuxfb或eglfs你应该能看到一个窗口显示 Hello, aarch64 static Qt!。如果没有显示服务器程序会报QPA插件加载失败。这时候你需要指定QT_QPA_PLATFORM环境变量export QT_QPA_PLATFORMlinuxfb或者export QT_QPA_PLATFORMeglfs具体用哪个取决于你的板子支持哪种显示后端。5.3 静态编译下的插件加载问题静态编译时Qt 的插件比如QPA插件、图片格式插件默认不会被链接进可执行文件。你需要用Q_IMPORT_PLUGIN宏显式导入。比如要导入linuxfb插件#include QtPlugin Q_IMPORT_PLUGIN(QLinuxFbIntegrationPlugin)然后在.pro文件里加上QTPLUGIN qlinuxfb这样qmake会自动把libqlinuxfb.a链接进来。如果你不导入插件程序运行时会报This application failed to start because no Qt platform plugin could be initialized。图片格式插件也一样。如果你需要显示 PNG 图片得导入qpng插件Q_IMPORT_PLUGIN(QPngPlugin)QTPLUGIN qpng这个坑我踩过好几次。动态编译时插件是运行时从plugins目录加载的静态编译时没有这个目录必须显式导入。6. 几个容易被忽略的细节与个人经验6.1 编译时间与并行度控制Qt 5.14.2 完整编译在 8 核机器上大概需要 40 到 60 分钟。如果你跳过了qtwebengine这些大模块时间能缩短到 20 到 30 分钟。用make -j$(nproc)可以并行编译但注意内存消耗。每个编译进程大概占 500 MB 到 1 GB 内存8 核并行就是 4 到 8 GB。如果你的机器内存不够make会因为 OOM 被 kill。这时候得降低并行度比如make -j4。我一般会在configure之后先make -j$(nproc)跑一遍如果中途报 OOM再make -j4继续。make支持断点续传已经编译好的目标文件不会重新编译。6.2sysroot里的符号链接问题从板子上rsync过来的sysroot里面很多库文件是符号链接。比如libz.so.1指向libz.so.1.2.11而libz.so又指向libz.so.1。如果你用cp而不是rsync -a符号链接会变成实际文件的拷贝导致sysroot体积暴增而且链接时可能找不到正确的版本。所以一定要用rsync -a或cp -a保留符号链接。另外sysroot里的usr/include目录可能包含一些和宿主机冲突的头文件。比如linux/input.h宿主机上的版本和目标板上的版本可能不一样。Qt 编译时会优先用sysroot里的头文件但有些头文件比如stddef.h是编译器自带的不会从sysroot找。这个优先级顺序是编译器内置头文件 sysroot头文件 宿主机头文件。如果你遇到头文件版本冲突检查一下是不是sysroot里的头文件覆盖了编译器需要的。6.3 静态编译后的可执行文件体积优化静态链接的 Qt 程序体积大是正常的。但你可以通过一些手段减小体积用-Os而不是-O2编译优化体积而不是速度。在.pro文件里加上CONFIG optimize_size。用strip命令去掉符号表aarch64-none-linux-gnu-strip hello_qt_static。如果不需要 Qt 的某些功能可以在configure时用-no-feature-*关掉。比如-no-feature-accessibility关掉无障碍支持能省几百 KB。我实测过一个简单的 Qt Widgets 程序静态编译后 15 MBstrip之后降到 12 MB。如果再关掉一些不用的特性能降到 10 MB 以下。对于嵌入式设备来说这个体积完全可以接受。6.4 交叉编译工具链的env工具链与unity工具链热词里提到了env工具链和unity工具链这两个其实是不同场景下的工具链封装。env工具链通常是指用环境变量来配置交叉编译参数的一套脚本比如export CCaarch64-none-linux-gnu-gcc这样。unity工具链则是指 Unity 引擎的交叉编译工具链和 Qt 不是一回事。如果你在做 Qt 项目不用管这两个直接用 ARM 官方工具链就行。但如果你确实需要在一个统一的环境里管理多个工具链可以写一个env.sh脚本在里面设置PATH、CROSS_COMPILE、SYSROOT这些变量然后source env.sh切换环境。这样比每次手动export要方便。6.5 关于qt-everywhere-src-5.15.10的交叉编译热词里有人搜qt-everywhere-src-5.15.10 交叉编译。Qt 5.15.10 是 5.15 系列的最后一个开源版本编译方法和 5.14.2 基本一样但有几个区别5.15 默认用 CMake 构建虽然configure脚本还在但有些模块的构建系统已经迁移到 CMake 了。如果你用configure脚本它会在后台调用 CMake。这可能导致一些参数传递不一致的问题。我的建议是如果你不是必须用 5.15就留在 5.14.2省去 CMake 迁移带来的麻烦。6.6 在 CentOS 7.9 aarch64 上编译的注意事项热词里提到了centos 7.9 aarch64 yum。CentOS 7.9 的 aarch64 版本默认的 GCC 是 4.8.5太老了编译 Qt 5.14.2 会报一堆 C14 特性不支持的错误。你需要先升级 GCC或者用devtoolset来启用新版本的 GCC。但devtoolset在 aarch64 上的支持不如 x86 完善有些包可能没有 aarch64 版本。所以如果你在 CentOS 7.9 aarch64 上做交叉编译宿主机最好还是用 Ubuntu 20.04 或 22.04工具链支持更完整。6.7 静态编译与boost库的交叉编译如果你的项目同时用了 Boost 库Boost 也需要交叉编译成 aarch64 静态库。Boost 的交叉编译比 Qt 简单用b2工具指定toolsetgcc和address-model64就行./b2 toolsetgcc architecturearm address-model64 \ target-oslinux abiaapcs \ --with-system --with-thread --with-filesystem \ linkstatic runtime-linkstatic \ --prefix/opt/boost-aarch64-static install注意runtime-linkstatic这表示 Boost 也静态链接 C 运行时。这样你的最终可执行文件就不会依赖libstdc.so。7. 从编译到部署的完整链路回顾整个流程走下来最耗时的其实是configure和make这两个阶段。configure大概 2 到 3 分钟make视模块多少20 到 60 分钟不等。如果中途报错排查和重新编译的时间可能翻倍。所以我的建议是第一次编译时先用最小配置跑通只编译qtbase确认工具链和sysroot没问题再逐步加上其他模块。这样能把问题范围缩小避免一次性面对几十个报错。部署到目标板时记得把QT_QPA_PLATFORM环境变量设对。如果你的板子用linuxfb还要确认/dev/fb0存在并且当前用户有权限访问。有些板子的fb0默认只有root能访问你需要把用户加到video组或者用chmod改权限。最后静态编译的 Qt 程序在目标板上运行时不需要设置LD_LIBRARY_PATH也不需要安装 Qt 运行库。这是它最大的优势。但代价是可执行文件体积大而且如果 Qt 有安全更新你得重新编译整个程序不能只更新库。所以静态编译适合那些部署环境受限、不方便安装运行库的场景比如工业控制设备、车载终端、自助服务机这些。如果你只是做内部测试动态编译加LD_LIBRARY_PATH可能更灵活。我个人在实际操作中的体会是静态交叉编译 Qt 这件事第一次做会觉得很繁琐但把流程跑通之后后面就是重复劳动。关键是把工具链、sysroot、configure参数这三样东西固定下来写成一个脚本下次直接跑脚本就行。我现在的做法是把整个编译过程写成一个build_qt_static.sh里面包含下载、解压、configure、make、install所有步骤换一台机器也能一键复现。这样即使过半年再回来做也不用重新回忆那些参数。