恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Qt aarch64 静态交叉编译全流程解析:从工具链到部署
首页
资讯中心
/
Qt aarch64 静态交叉编译全流程解析:从工具链到部署
Qt aarch64 静态交叉编译全流程解析:从工具链到部署
发布时间:2026/9/24 23:24:20
1. 项目概述与整体方案解读1.1 为什么要做 Qt aarch64 静态交叉编译做嵌入式 Linux 图形应用开发的工程师几乎都会撞上同一个问题板子拿到手交叉编译工具链装好了程序也写好了结果往板子上一跑不是报缺库就是报平台插件找不到。更折腾的是目标板上部署动态库、配置环境变量、处理依赖关系每一步都在消耗有限的调试时间。我这次整理的是一套基于 Qt 5.14.2 对 aarch64 架构进行静态交叉编译的完整流程。所谓静态编译就是把 Qt 库直接编进可执行文件里生成一个不依赖目标板上任何 Qt 动态库的独立二进制。这么做的好处非常直观拷一个文件到板子上就能跑不用管 /usr/lib 下有没有 Qt 系列 .so也不用担心多人协作时有人漏装运行库。代价是生成的文件体积偏大但对比排查依赖问题的痛苦这个体积开销完全值得。Qt 5.14.2 这个版本是我个人专门选的。它属于 5.14 LTS 系列的最后一个补丁版本bug 修复得最干净又不像 Qt 6 那套构建系统改动太大社区资料、交叉编译经验帖都非常丰富。真踩到坑搜索引擎一搜基本都有解决方案。对生产项目来说稳比新重要得多。1.2 静态编译与动态编译的取舍很多刚接触交叉编译的朋友会问既然动态编译部署时拷一堆库也能跑为什么非要静态编译这里有个重要区别动态编译省的是磁盘和内存但费的是部署和排查的时间静态编译恰恰相反编译时费时间费磁盘部署时却能省掉一大半麻烦。动态库方案在调试阶段确实很顺改了源码重新 make 一下把新 .so push 过去就行。但等到产品要交付或者板子要批量刷机时依赖链问题就来了。Qt 的依赖不止 Qt 自己背后还拉着 zlib、libpng、libjpeg、fontconfig、xcb 这一大串任何一个版本对不上都可能在运行时弹奇怪的错误。静态编译把这些问题一次性解决。最终产物是一个理论上自带一切的 ELF 文件拷到目标板直接 chmod x 就能运行。另外在纯内网环境、离线部署的场景下静态编译也有不可替代的优势——你不需要在目标板上用 yum 或 apt 安装任何依赖包。1.3 方案选型背后的几个关键决策这套方案里我做了几个关键选择每个都踩过坑才定下来先说明白方便大家按需调整宿主机系统使用 x86_64 的 CentOS 7.9 / Ubuntu 20.04 均可我这里以 CentOS 7.9 为例。不需要在 aarch64 机器上原生编译一台 x86 主机配合交叉工具链即可。交叉编译工具链用 aarch64-linux-gnu-gcc 9.x 系列。太老的 4.8 编 Qt 5.14 会报 C11 标准相关的问题太新的 12.x 又可能出现 glibc 版本兼容的隐性问题。显示后端选择 linuxfb 作为默认平台插件。如果想上 xcb静态编译时要把 libxcb 全家桶一起交叉编译进去工作量翻倍对大多数嵌入式场景不值。强制静态链接让 Qt 内部全部使用 -static 编译第三方依赖库也全部编成 .a确保最终可执行文件不依赖任何 .so。这几个决策的共性是尽量走成熟、经社区验证的路径避免在工具链兼容性上花太多时间。2. 交叉编译环境搭建与工具链细节2.1 宿主机环境准备开始之前先确认宿主机的基础环境。我用的是一台 CentOS 7.9 x86_64 的服务器8 核 16G 内存磁盘剩余空间留了至少 30G。Qt 静态全量编译挺能吃磁盘的中间产物加上源码解压20G 是很保守的估计。先更新系统基础工具保证有 make、gcc、g、perl、python 等常用组件yum install -y epel-release yum groupinstall -y Development Tools yum install -y wget tar bzip2 flex bison gperf \ libX11-devel libXext-devel libXrender-devel \ mesa-libGL-devel libicu-devel openssl-devel注意这里装的 mesa、openssl 是给宿主机上的辅助工具用的后面交叉编译 Qt 时不会直接用这些 x86 的库但 configure 脚本在检测某些特性时可能会调用宿主机编译器做测试缺了这些基础开发库会有莫名其妙的报错。2.2 交叉编译工具链安装工具链的选择是整个项目的地基我一开始图省事用过 Linaro 老版本结果编译 Qt 源码时频繁报错回头排查发现是编译器太老对 C14/C17 特性支持不完整。后来换成了 aarch64-linux-gnu-gcc 9.3整个流程顺畅了很多。安装方式分两种任选其一# 方式一直接用发行版源推荐省事 yum install -y gcc-aarch64-linux-gnu gcc-c-aarch64-linux-gnu # 方式二手动下载交叉工具链版本可控性更强 # 这里以 ARM 官方提供的 GNU toolchain 为例 wget https://developer.arm.com/-/media/Files/downloads/gnu-a/9.3-2020.03/\ gcc-arm-9.3-2020.03-x86_64-aarch64-none-linux-gnu.tar.xz tar -xf gcc-arm-9.3-2020.03-x86_64-aarch64-none-linux-gnu.tar.xz mv gcc-arm-9.3-2020.03-x86_64-aarch64-none-linux-gnu /opt/ export PATH/opt/gcc-arm-9.3-2020.03-x86_64-aarch64-none-linux-gnu/bin:$PATH安装完工具链后先写个 helloworld 验证交叉编译是否能正常工作cat hello.c EOF #include stdio.h int main() { printf(aarch64 cross compile OK\n); return 0; } EOF aarch64-linux-gnu-gcc -o hello hello.c file hello # 输出里必须包含 ELF 64-bit LSB executable, ARM aarch64如果 file 命令输出的是 x86-64说明工具链没配对或者 PATH 有问题。这一步没通过之前别碰 Qt先排查环境。2.3 关键字工具链前缀与 sysroot交叉编译工具链有两个概念必须理解清楚不然 configure 时容易懵工具链前缀比如 aarch64-linux-gnu-它只是编译器名字的前缀。编译器本身跑在 x86 宿主机上生成的目标代码却是 aarch64。sysroot相当于目标板根文件系统的一个缩影。编译器在找头文件和库时会以 sysroot 目录为根而不是宿主机 /usr。比如目标板上的 /usr/include/zlib.h在宿主机上就是 $(SYSROOT)/usr/include/zlib.h。如果用发行版源安装的交叉工具链sysroot 一般自动配置好了路径可以用下面的方式查aarch64-linux-gnu-gcc -print-sysroot # 通常输出类似 /usr/aarch64-linux-gnu 或类似路径后面编译第三方依赖库时记得把 --sysroot 参数传给 configure否则很容易编出 x86 的库到 Qt configure 阶段链路全断。这是很多新手最容易踩的坑。3. Qt 5.14.2 源码获取与目录规划3.1 源码下载与校验Qt 5.14.2 在 Qt 官方的 archive 目录下可以找到国内环境如果下载慢可以换用国内镜像站原理一样校验哈希确认文件完整即可wget https://download.qt.io/archive/qt/5.14/5.14.2/\ single/qt-everywhere-src-5.14.2.tar.xz # 或者用国内镜像 # wget https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.14/5.14.2/single/\ # qt-everywhere-src-5.14.2.tar.xz sha256sum qt-everywhere-src-5.14.2.tar.xz tar -xf qt-everywhere-src-5.14.2.tar.xz cd qt-everywhere-src-5.14.2Qt Everywhere 源码包里的内容非常全从 qmake 构建系统到 QtCore、QtGui、QtWidgets、QtNetwork 等模块源码都在一起。正因为全编译前必须通过 configure 选择性地裁剪模块否则全量静态编译的耗时你绝对不想体验第二次。3.2 目录规划与输出路径设计我的习惯是在家目录下建立一套清晰的目录结构方便后面统一管理mkdir -p ~/qt-aarch64/{src,install,tools,work}src存放 Qt 源码和第三方库源码install编译产出目录最终 Qt 库和可执行程序安装在这里tools手工编译的第三方静态库work实际的构建目录存放 configure 产生的 Makefile 等Qt 官方推荐在源码树外构建这样源码可以保持干净随时可以重新配置。我实际操作下来在 work 目录下跑 configure 更保险因为 Qt 的源码目录如果带有之前 configure 的残留很容易出现各种缓存问题。4. 依赖库准备静态编译的重头戏4.1 为什么要先编第三方库Qt 的 GUI 部分不是空中楼阁它依赖一批底层库来处理 PNG、JPEG、字体渲染、压缩等基础功能。动态编译时这些库通常由系统包管理安装但在静态交叉编译场景下必须手工把每个依赖库用交叉工具链编成 .a 静态库再让 Qt 的 configure 找到它们。很多人失败就失败在这个环节直接从宿主机的 /usr/lib/x86_64-linux-gnu 拷贝 .a 文件链接架构不对一报错就是几十条。老老实实交叉编译第三方库后面反而顺利。4.2 依赖库清单与版本选择这里列一下我实际用到的库全部以静态库方式编译依赖库版本说明zlib1.2.11压缩基础库几乎必选libpng1.6.37PNG 图像支持libjpegjpeg-9dJPEG 图像支持openssl1.1.1k若需要 QtNetwork SSL 支持tslib1.21触摸屏校准视硬件决定注意一下版本取舍。openssl 我没选 3.x因为 Qt 5.14.2 的 SSL 模块对 3.0 的支持并不好configure 检测的兼容性存在不少坑用 1.1.1 系列省心得多。4.3 一个标准依赖库的交叉编译流程以 zlib 为例展示交叉编译的标准姿势cd ~/qt-aarch64/src wget https://zlib.net/zlib-1.2.11.tar.gz tar -xf zlib-1.2.11.tar.gz cd zlib-1.2.11 CCaarch64-linux-gnu-gcc \ ARaarch64-linux-gnu-ar \ RANLIBaarch64-linux-gnu-ranlib \ ./configure --static --prefix/opt/aarch64-sysroot/usr make -j$(nproc) make install注意 configure 时不加 --host 参数但通过设置 CC、AR、RANLIB 环境变量指定了交叉工具链。zlib 的构建系统比较老了这种方式是它官方支持的标准做法。为保险起见编译完查一下生成的 libz.a 架构file /opt/aarch64-sysroot/usr/lib/libz.a # 不应出现 x86-64 字样libpng、libjpeg、openssl 的编译流程基本一致不同点在于它们的 configure 参数更多需要显式指定 --hostaarch64-linux-gnu# libpng CCaarch64-linux-gnu-gcc ./configure \ --hostaarch64-linux-gnu \ --prefix/opt/aarch64-sysroot/usr \ --enable-static --disable-shared # openssl ./Configure linux-aarch64 \ --prefix/opt/aarch64-sysroot/usr \ --openssldir/opt/aarch64-sysroot/usr/ssl \ no-sharedopenssl 的 Configure 比较特殊它有自己的 target 配置表直接用 linux-aarch64 这个目标名就行不需要走 autoconf 那套。4.4 编译第三方库的几个心得整个第三方库准备过程大概是三个晚上踩坑总结出来的分享几个关键经验第一统一安装前缀很重要。我把所有库装到 /opt/aarch64-sysroot/usr 下这样 Qt configure 时可以统一通过 --sysroot 和 -I/-L 参数找到它们不用每个库单独指定路径。第二遇到undefined reference链接错误时先检查库的架构再看库的依赖顺序。静态库之间是有依赖顺序的比如链接 -lpng 还要带上 -lz如果顺序不对即使有 libz.a 也会报 undefined reference。第三千万别图省事用宿主机上的 libpng 头文件。不同架构的头文件虽然语法差不多但部分宏定义和 ABI 配置不一样混用会导致结构体大小不一致这类 bug 排查起来极其痛苦。用 sysroot 里的头文件和库是配套的才能保证二进制兼容。5. 配置 qmake 交叉编译平台文件5.1 理解 Qt 的 mkspec 机制Qmake 是 Qt 自己的构建系统它通过 mkspecMake Specification来了解当前编译目标的平台特性。Qt 源码中预先提供了很多 mkspec 目录比如 linux-g、win32-msvc 等。交叉编译时我们需要在 mkspec 目录下新建一个自定义平台文件告诉 qmake编译器叫 aarch64-linux-gnu-g库路径是 sysroot 下的路径链接方式要静态。这个过程中最核心的是两个文件qmake.conf指定编译器和关键编译链接参数qplatformdefs.h一般直接 include 参考平台的定义5.2 新建 linux-aarch64-gnu 平台文件Qt 5.14.2 自带的 mkspec 目录里其实有 linux-aarch64-gnu-g 这个目录但它是为动态编译设计的。我们要改造成静态编译最稳妥的方案是复制一份出来修改cd qt-everywhere-src-5.14.2 cp -r qtbase/mkspecs/linux-aarch64-gnu-g \ qtbase/mkspecs/linux-aarch64-static编辑 qtbase/mkspecs/linux-aarch64-static/qmake.conf# # qmake configuration for cross-compiling # with aarch64-linux-gnu-g # MAKEFILE_GENERATOR UNIX CONFIG incremental QMAKE_INCREMENTAL_STYLE sublib include(../common/linux.conf) include(../common/gcc-base-unix.conf) include(../common/g-unix.conf) # 修改编译器前缀和 sysroot QMAKE_CC aarch64-linux-gnu-gcc --sysroot/opt/aarch64-sysroot QMAKE_CXX aarch64-linux-gnu-g --sysroot/opt/aarch64-sysroot QMAKE_LINK aarch64-linux-gnu-g --sysroot/opt/aarch64-sysroot QMAKE_LINK_SHLIB aarch64-linux-gnu-g --sysroot/opt/aarch64-sysroot # 强制静态链接 QMAKE_LFLAGS -static QMAKE_LFLAGS_PLUGIN -static QMAKE_CFLAGS -fPIC QMAKE_CXXFLAGS -fPIC # ar 工具 QMAKE_AR aarch64-linux-gnu-ar cqs QMAKE_OBJCOPY aarch64-linux-gnu-objcopy QMAKE_NM aarch64-linux-gnu-nm QMAKE_STRIP aarch64-linux-gnu-strip # 指定 sysroot QMAKE_INCDIR /opt/aarch64-sysroot/usr/include QMAKE_LIBDIR /opt/aarch64-sysroot/usr/lib load(qt_config)这里 -fPIC 是个小小的权衡。纯静态编译理论上不需要 PIC但 Qt 内部某些模块默认加了 -fPIC 编译选项不统一加上的话会出现 relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used when making a shared object 这一类链接错误。吃一堑长一智直接在全局加上。-static 链接标志是整个静态化的开关确保最终的 Qt 库文件都是 .a。5.3 qplatformdefs.h 的处理复制 linux-aarch64-gnu-g 目录后qplatformdefs.h 已经存在里面 include 了通用 Linux 平台定义。static 编译不需要对这个文件做特殊改动保持原样即可。真正的关键是前一步确认 qmake.conf 里的编译器路径都对。我见过有人改了编译器名忘记改 --sysroot结果所有的依赖库头文件都灵异地指到了宿主机路径报错还特别隐晦。6. configure 配置详解与完整命令6.1 configure 命令的完整参数进入 work 构建目录执行 configure。这是我最终调通的完整命令cd ~/qt-aarch64/work ../src/qt-everywhere-src-5.14.2/configure \ -v \ -opensource \ -confirm-license \ -xplatform linux-aarch64-static \ -static \ -release \ -prefix /opt/qt-aarch64/install \ -no-opengl \ -no-eglfs \ -no-xcb \ -linuxfb \ -no-feature-accessibility \ -no-feature-xkbcommon \ -no-feature-xinput2 \ -no-feature-eglfs \ -no-compile-examples \ -no-gtk \ -no-cups \ -no-iconv \ -no-icu \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -openssl-linked \ -I/opt/aarch64-sysroot/usr/include \ -L/opt/aarch64-sysroot/usr/lib参数很多一个也不能少逐个解释一下-v打印详细配置过程方便查错-opensource -confirm-license接受开源协议免交互-xplatform linux-aarch64-static指定使用刚才创建的 mkspec-static构建静态库-release只编译 release 版本省一半时间-prefix安装路径-no-opengl -no-eglfs禁用 OpenGL 相关模块嵌入式大多用不到-linuxfb启用 linuxfb 平台插件帧缓冲设备直接绘制-no-feature-...禁用用不到的功能模块减少编译量-qt-zlib -qt-libpng -qt-libjpeg这三个参数的意思是使用刚才交叉编译的第三方库-openssl-linked启用 OpenSSL 支持-I / -L告诉 configure 依赖库的位置有个容易混淆的点-qt-zlib 和 -qt-libpng 在这里的实际含义是让 Qt 优先使用我们准备的第三方静态库路径而不是 Qt 源码里自带的那些两者不能混为一谈。如果我们的 /opt/aarch64-sysroot/usr 里已经有了交叉编译好的 .a 库这条路就能走通。6.2 内存与并行度控制configure 前的源码树检查很考验 CPU。configure 脚本会编译几十个小测试程序来探测编译器特性如果某个测试程序因为缺少系统头文件失败configure 会直接中止并给出错误信息。configure 本身默认单线程耗时不长。真正耗时的是后面的 make 环节。Qt 5.14.2 全模块编译在我的 8 核 16G 机器上大约耗时 40 到 60 分钟全量如果内存紧张建议这样make -j4-j 参数别拉满。静态编译时每个编译单元吃内存都比较大-j8 有时候会直接把 16G 内存打满触发 OOM反而是 -j4 更稳定。编译过程中可以随时用 top 看内存余量。6.3 configure 阶段常见报错报错一找不到平台文件ERROR: The host system architecture x86_64 does not match the target architecture aarch64这种情况一般是没有正确指定 -xplatformconfigure 误用宿主机的 linux-g 配置。检查 mkspec 目录名是否拼写正确。报错二GL 相关失败ERROR: Feature opengl was enabled, but pre-conditions not met.参数里 -no-opengl 的作用就是避免这个错误。除非目标板明确支持 GPU 加速并配好了 EGL否则嵌入式环境直接禁用。报错三zlib 检测失败The test for linking against zlib will fail.这个报错常出现在 -I / -L 路径没有生效的情况下。检查 /opt/aarch64-sysroot/usr/lib/libz.a 是否存在再用 -v 看看 verbose 日志里 compiler output 的具体错误。7. 编译、安装与链接全过程7.1 编译阶段的重要观察点configure 成功之后会生成若干 Makefile。这时先不要直接 make先看一眼 work 目录下的 config.summary 文件确认关键配置项符合预期grep -E Platform|OpenGL|linuxfb|XCB|Qt modules config.summary确保 XCB 是 nolinuxfb 是 yesOpenGL 是 no。如果和预期不符先回 configure 阶段纠偏不要带着错误的配置往下走。普通编译过程make -j4 21 | tee build.log建议把输出存到日志文件编译出错时可以精确回溯到第一个 error 出现的位置。Qt 的编译错误信息量和格式都比较复杂日志文件是排查问题的第一手依据。编译结束后安装到指定前缀make install安装完成后重点检查这些文件是否正常生成ls /opt/qt-aarch64/install/ # 目录下应有 bin、lib、include、mkspecs 等 find /opt/qt-aarch64/install/lib -name *.a | head -20 # libQt5Core.a、libQt5Gui.a、libQt5Widgets.a 必须存在静态编译的产出物应该全是 .a如果看到 .so 就要回查 qmake.conf 里的 -static 是否生效。7.2 写一个测试程序验证工具链链路安装完成之后写一个最小 QWidget 程序验证整条链路是否可用#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello aarch64 Qt Static); label.resize(320, 120); label.show(); return app.exec(); }关键一步用我们编译出来的交叉 qmake 生成 Makefileexport PATH/opt/aarch64-sysroot/bin:/opt/qt-aarch64/install/bin:$PATH qmake make编译结束后用 file 检查产物file hello # 输出ELF 64-bit LSB executable, ARM aarch64, statically linked确认包含 statically linked 字样。再查看目标板的可执行文件传播方式如果这一步 ok就可以把 hello 直接拷贝到开发板运行。7.3 部署到目标板运行验证把 hello 拷贝到目标板后有几种运行方式# 方式一指定 linuxfb 平台插件 ./hello -platform linuxfb # 方式二如果配置了 QWS 兼容层Qt 5 中已废弃 ./hello -qws在 linuxfb 平台下运行时如果界面没有显示最常见原因是 framebuffer 设备节点权限问题。确认目标板 /dev/fb0 存在并且当前用户有读写权限ls -l /dev/fb0 # 如果权限不够用 root 运行 sudo ./hello -platform linuxfb如果开发板带触摸屏还需要在运行时通过环境变量指定输入设备export QT_QPA_FB_DRM1 export QT_QPA_FB_TSLIB1 export TSLIB_TSDEVICE/dev/input/event1 ./hello -platform linuxfb这里 tslib 的配置需要和硬件实际输入事件编号对应具体用哪个 event 节点可以在板子上通过 cat /proc/bus/input/devices 查看。8. 运行时细节与二次打包优化8.1 字体问题的处理静态编译的程序跑起来界面中文字体直接显示成方格方块这是非常普遍的坑且不只在静态编译环境出现。原因是目标板的 Linux 环境缺字库Qt 的字体数据库在系统字库里找不到预设的字形。解决方式有两种一是把 PC 平台的字体文件拷贝到目标板放到 /usr/share/fonts 或其他 Qt 搜索路径通过设置环境变量指定export QT_QPA_FONTDIR/opt/fonts二是在代码里用 QFontDatabase 加载本地字体文件比如常见的思源黑体或文泉驿正黑QFontDatabase database; QString fontPath /opt/fonts/SourceHanSansCN-Regular.ttf; QString fontFamily database.fontFamily(0).toString(); // 加载后返回字体族名 QFont font(fontFamily, 12); app.setFont(font);更稳妥的做法是静态程序自带的资源目录里放一份精简的 TTF 文件启动时通过 QResource 直接加载这样连外部字体文件路径都省了。8.2 裁剪体积静态编译出来的 hello 动不动几百 MB 不是新鲜事。我实测过纯 QWidget 应用包含 QtCore、QtGui、QtWidgets 的完整静态链接产物七百多MB是正常的。这不影响功能但会让拷贝和刷机变得痛苦。如果确实需要瘦身有两条路线第一条路线从编译选项上裁剪。configure 阶段把用不到的模块全关掉比如 -no-gui、-no-network 可以让产物体积大幅下降但这对 GUI 应用不适用。可行的做法是裁剪 QtWidgets 内部特性比如 -no-feature-widgets-completer、-no-feature-texthtmlparser 这一类能把部分代码段排除出静态库。第二条路线是链接期裁段。用提供的链接器参数让未被引用的目标文件不被链接进最终可执行文件QMAKE_LFLAGS -Wl,--gc-sections QMAKE_CXXFLAGS -ffunction-sections -fdata-sections它的原理是在编译阶段把每个函数、每个数据对象放到独立的 section 中链接时丢弃未被引用的 section。对 Qt 这种代码量很大的库实测能砍掉 20% 到 30% 的体积。风险是如果某些地方通过函数指针间接引用、被丢掉的代码实际上是运行期需要的链接器不会报错只在运行阶段出现奇怪的崩溃。加了这些参数之后必须回归测试所有功能模块。8.3 运行环境变量配置模板给目标板准备一个启动脚本把常用的 Qt 环境变量一次性配置好#!/bin/sh export QT_QPA_PLATFORMlinuxfb export QT_QPA_FB_DRM1 export QT_QPA_FONTDIR/opt/fonts if [ -c /dev/input/event1 ]; then export TSLIB_TSDEVICE/dev/input/event1 export QT_QPA_FB_TSLIB1 fi cd /opt/app exec ./hello -platform linuxfb这个脚本里最值得注意的就是最后 exec 替代。使用 exec 后shell 进程被应用替换systemd 或 init 系统监控的 pid 保持在同一个进程上方便后面做进程守护与异常重启。9. 易错环节排查与优化路径9.1 常见错误速查表整个搭建过程中我整理了若干高频问题放到一张表里方便快速定位。错误现象根本原因解决方案configure 报 requires a C11 compiler交叉工具链过老升级到 gcc 9.x 及以上链接期 undefined reference 成堆第三方库架构不符或依赖顺序不对file 检查 .a 架构调整 -l 顺序运行时报 cannot mix incompatible Qt library可执行文件里混入宿主机 Qt 头文件全部统一用交叉 qmake 编译linuxfb 无法打开 /dev/fb0设备节点权限不足chmod 666 /dev/fb0 或 root 运行应用启动后黑屏无输出字体缺失 / 屏参配置不对设置 QT_QPA_FONTDIR、检查 framebuffer 参数无法找到 platform plugin linuxfb未启用 linuxfb 模块重新 configure 并检查 config.summarymake 编译中 OOM 被杀并行度过高make -j4 甚至 -j2其中 cannot mix incompatible Qt library 这个错误值得单独提一下。它的意思是当前编译程序时引用的 Qt 头文件是 5.6.1 版本附带的而链接指向的库却是其他版本。这个问题的根源几乎都出在系统里安装了多套 Qt、PATH 环境变量没有指对我们编译出的 Qt。检查方法# 查看 qmake 实际指向 which qmake qmake -v # 查看 qmake 对应的 Qt 版本和路径确认指向 /opt/qt-aarch64/install/bin/qmake9.2 编译效率的优化如果编译速度慢得无法忍受除了并行的 -j 参数还可以关闭不必要的 Qt 模块。比如只保留 qtbase加参数-skip qt3d -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d \ -skip qtdeclarative -skip qtgamepad -skip qtlocation \ -skip qtmultimedia -skip qtpurchasing -skip qtquickcontrols \ -skip qtscript -skip qtsensors -skip qtserialbus -skip qtserialport \ -skip qtspeech -skip qtsvg -skip qttools -skip qttranslations \ -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets这里注意如果项目用到了 Qt Charts 或 Qt Data Visualization对应模块就不能 skip。没有使用 GUI 增强模块的纯基础应用只编 qtbase 可以把时间压缩到二十多分钟。9.3 给新手的几条建议这套静态交叉编译的方案虽然折腾但只要按顺序走一遍后面每次出活都会很省心。我建议第一次动手的人记住三条原则第一每一步都要验证。工具链编译完先跑 file 验证第三方库编完再跑 file 验证Qt configure 完看 config.summary 验证。每层都确认没问题再往上叠出错时才能快速定位层次。第二日志一定要留。编译 zlib 的日志、编译 Qt 的 build.log、configure 的 config.log全部保存下来。几天后想起某个参数想回头核对没有日志只能重新猜。第三不要试图一次搞完。这事的复杂度决定了出问题是必然的心态上要接受逐步排查的安排。第一次接触时预留两三天专门做环境比逼自己在半天内跑完更现实。我做完这套环境后后续每个新项目都是直接把编译好的 Qt 安装目录拷到新机器上用一次性配好收益非常可观。