资讯详情

资讯详情

Qt 5.14.2 aarch64 静态交叉编译完整实战手册

Qt5.14.2 的 aarch64 静态交叉编译是我这两年折腾嵌入式 Linux 项目时做过最“磨人”但也最“上头”的一件事。先说结论整个过程从下载源码到最终产出一个静态链接好的 Qt 可执行文件正常情况下需要大半天如果对工具链或 sysroot 不熟踩坑时间可以轻松翻倍。这篇手册就是把我踩过的坑、筛查过的参数、验证过的命令全部整理出来给准备在 aarch64 板子上跑 Qt 应用的朋友一条能直接走通的路。适合遇到以下场景的人参考手里有一块 ARM64 开发板树莓派、瑞芯微 RK 系列、全志、飞腾等需要在宿主机x86 的 Ubuntu上交叉编译出 Qt 程序再拷到板子里跑而且希望最终程序不依赖板子上的 Qt 动态库做到“一个二进制文件丢过去就能运行”。静态交叉编译的核心价值就在这里部署简单、环境隔离、不污染目标系统。1. 方案选型思考为什么选 Qt 5.14.2、静态编译和独立 sysroot动手之前先聊清楚方案怎么来的这样后面配参数时才不会一脸懵。1.1 Qt 5.14.2 版本定位Qt 5.14.2 是 The Qt Company 在 2020 年发布的一个开源补丁版本。它属于 Qt 5.14 系列这个系列有几个很实际的优势对嵌入式 Linux 的 platform 插件linuxfb、eglfs、minimal支持成熟稳定嵌入式场景大量使用。5.15 之后尽管也有开源版但部分模块对商业用户和开源用户划分得更清晰很多团队为了“省事”继续钉在 5.14 LTS 语义上。5.14.2 对 C11/C14 支持良好第三方库如 OpenSSL 1.1.1、zlib 1.2.11 等版本都兼容良好。它可以在相对老旧的 Ubuntu 20.04 宿主机上直接编译工具链要求不高GCC 9 就能满足。从我个人的项目经历来看只要是跑在 aarch64 嵌入式设备上的 Qt Widgets 或 Qt Quick非 WebEngine应用5.14.2 基本是“低风险、高成功率”的选择。如果你要用 Qt WebEngine那我建议直接换 Qt 5.15 或 6.x因为 WebEngine 在 5.14 里已经不太好编译了架构上也不是为静态编译准备的。1.2 静态编译的收益与代价静态编译意味着把所有 Qt 库直接链进你的可执行文件目标板子上不需要安装任何 Qt 运行库。代价也很明显可执行文件体积大。一个简单的 Widgets 程序release 静态编译后通常 15MB 到 25MB 左右如果引入了 Qt Widgets、Qt Network、Qt Sql 等模块30MB 也有可能。编译时间更长。Qt 自身需要完整编译成静态库.a工程代码每次改动重链接时间也高一些。部分与平台相关模块如 xcb、DBus、ICU在静态编译时特别容易出兼容问题所以需要合理裁剪。但在无人值守的嵌入式设备、工业控制板卡、或者“不想折腾目标系统环境”的场景里静态编译带来的部署便利是动态编译没法比的。1.3 交叉编译关键思路宿主机 目标 sysroot Qt 源码交叉编译说白了就是“在 A 机器上编译出 B 机器能运行的程序”。那问题来了编译器aarch64-linux-gnu-gcc知道目标 CPU 是 ARM64但它怎么找到目标系统的头文件和库文件答案是 sysroot。sysroot 就是一个“目标系统的根目录”里面放着 /usr/include、/usr/lib 等目录。交叉编译时编译器不去看宿主机的 /usr/include而是去 sysroot 里找。所以宿主机x86_64 Ubuntu负责跑编译工具链和 Qt 编译脚本。目标 sysroot一个通过 debootstrap 创建的 ARM64 Ubuntu 根文件系统集体积约 400MB 到 1GB里面包含 aarch64 版本的 glibc、zlib、libpng 等基础库。Qt 源码解压在宿主机上通过 configure 指定“架构平台为 linux-aarch64-gnu-g”all 输出目录安装到宿主机某个前缀路径如 /opt/qt5.14.2-aarch64-static生成的都是 aarch64 的静态库。这三者的关系理顺了后面每一步都只是充实细节。注意很多人一上来就执行 ./configure 然后 make结果各种“cannot find -lGL”“mkspecs 找不到”报错扑面而来。核心原因就是没提前把 sysroot 和工具链的关系想清楚。2. 环境准备宿主机、工具链与目标根文件系统2.1 宿主机系统与基础工具我用的宿主机是 Ubuntu 20.04.6 LTS x86_64。其他发行版也可以但命令和包名会不同建议新手直接照搬 Ubuntu 20.04 或 22.04。需要的基础包sudo apt update sudo apt install -y build-essential cmake git wget tar xz-utils \ python3 python3-pip \ libssl-dev \ flex bison \ pkg-config \ qemu-user-static \ debootstrapqemu-user-static 后面会用来跑 ARM64 的 debootstrap 二级引导这个千万别漏。查看宿主机的架构和编译器uname -m gcc --version只要输出是 x86_64、gcc 9.x 或 11.x就没问题。如果你宿主机是 ARM64比如某些 ARM 服务器那就不叫“交叉”编译了直接本机编译更省事。2.2 安装 aarch64 交叉编译工具链Ubuntu 20.04 官方源里就有现成的 aarch64 工具链版本 9.3sudo apt install -y gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完后验证aarch64-linux-gnu-gcc --version # 输出类似 # aarch64-linux-gnu-gcc (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0这套工具链基于 glibc 2.31对应 Ubuntu 20.04 的 ARM64 根文件系统版本是匹配的。如果你用目标板厂商提供的私有工具链比如 Linaro GCC 7.5 或 Buildroot 生成的工具链思路一样但 sysroot 路径和 glibc 版本对应关系要重新核对。交叉工具链安装后/usr/aarch64-linux-gnu 目录里已经自带了一个基础 sysrootls /usr/aarch64-linux-gnu # 里面包含 include、lib 等目录这就是最简单的 sysroot但里面只有基础 C/C 运行库其他第三方库zlib、libpng、openssl 等需要我们自己补进去。2.3 用 debootstrap 制作一个完整的 ARM64 sysroot直接用 /usr/aarch64-linux-gnu 做 sysroot 不完整装 Qt 依赖时经常缺头文件、缺 .so 软链。最佳实践是用 debootstrap 创建一个干净的 ARM64 Ubuntu 根文件系统。创建目录sudo mkdir -p /opt/aarch64-sysroot sudo chown $USER /opt/aarch64-sysroot执行 debootstrap这里用到 qemu-user-staticUbuntu 的 debootstrap 会自动结合 binfmt 去模拟执行 ARM64 的维护脚本sudo debootstrap --archarm64 --variantminbase focal /opt/aarch64-sysroot http://ports.ubuntu.com/ubuntu-ports/参数说明--archarm64 指定目标架构。--variantminbase 只安装最小系统够编译库就行。focal 对应 Ubuntu 20.04。源使用 Ubuntu Ports因为 ARM64 的软件包不在普通 ubuntu.com 源里。这个过程会下载约 100MB 到 200MB 的网络包取决于网络情况。完成后检查ls /opt/aarch64-sysroot/etc/ # 应该能看到 os-release 等文件接着往这个基础 sysroot 里装 Qt 编译需要的 ARM64 开发包。方式有两种直接 chroot 进去用 apt 安装。用 apt-get download 下载 .deb手工解包进去。方法 1 更省事。先准备 chroot 环境sudo cp /usr/bin/qemu-aarch64-static /opt/aarch64-sysroot/usr/bin/ sudo chroot /opt/aarch64-sysroot /bin/bash进入之后export DEBIAN_FRONTENDnoninteractive echo deb http://ports.ubuntu.com/ubuntu-ports/ focal main universe /etc/apt/sources.list apt update然后安装需要的基础开发包apt install -y \ libc6-dev \ zlib1g-dev \ libssl-dev \ libpng-dev \ libjpeg-dev \ libfreetype6-dev \ libfontconfig1-dev \ libsqlite3-dev \ libicu-dev \ libdbus-1-dev \ libglib2.0-dev \ libegl1-mesa-dev \ libgles2-mesa-dev \ libgbm-dev \ libinput-dev \ libxkbcommon-dev \ libxcb1-dev这一套装下来sysroot 里的依赖基本齐了。退出 chrootexit注意在 chroot 安装时如果板子最终运行环境不需要某些显示服务比如纯 framebuffer 场景xcb、xkbcommon 等可以不装。但为了后续扩展性我建议先把 libxkbcommon-dev 装上xcb 相关的库对静态编译不友好后面配置 Qt 时我会直接禁掉。2.4 目录规划与版本对照建议所有路径固定下来避免后期配置混乱项目路径Qt 源码目录/opt/src/qt-everywhere-opensource-src-5.14.2Qt 安装前缀/opt/Qt5.14.2-aarch64-staticARM64 sysroot/opt/aarch64-sysroot交叉编译工具链/usr/bin/aarch64-linux-gnu-*Qt 源码可以从 Qt 官方 archive 页下载 qt-everywhere-opensource-src-5.14.2.tar.xz约 500MB 左右解压后约 2GB 以上注意磁盘空间至少留 20GB编译 Qt 静态库很占空间。3. sysroot 依赖补全与 Qt configure 前的准备3.1 检查 sysroot 中的软链是否完整很多人在编译依赖库时碰到 “cannot find -lGL” 这类错误往往不是真的缺 libGL.so而是 sysroot 里只有 libGL.so.1没有开发用的软链 libGL.so。在 chroot 里安装的 dev 包会自动生成软链所以一般问题不大。但我遇到过 debootstrap chroot 后某些库的 .so 软链没更新到最新版本这时可以手动修复。最典型的例子是 libGLls -l /opt/aarch64-sysroot/usr/lib/aarch64-linux-gnu/libGL.so如果没有就检查对应版本文件后手动创建sudo ln -s libGL.so.1 /opt/aarch64-sysroot/usr/lib/aarch64-linux-gnu/libGL.so同样的手法适用于 libEGL、libGLESv2、libgbm 等。3.2 交叉编译第三方依赖库zlib、openssl、libpng虽然 sysroot 里已经装了大部分库的 aarch64 版本但为了减少不确定因素我建议把下面这几个关键库自己源码编译一遍以静态库的形式放进 sysroot。理由很简单用系统 .deb 装的多半是动态库Qt 静态编译时依赖外部库如果外部库是动态的那“全静态”的目标就泡汤了。先编 zlib它是 Qt 的基础依赖wget https://zlib.net/zlib-1.2.11.tar.gz tar xzf zlib-1.2.11.tar.gz cd zlib-1.2.11 CCaarch64-linux-gnu-gcc \ ARaarch64-linux-gnu-ar \ RANLIBaarch64-linux-gnu-ranlib \ ./configure --prefix/opt/aarch64-sysroot/usr --static make -j$(nproc) sudo make install这样生成的 libz.a 会直接放进 sysroot 的 /usr/lib/aarch64-linux-gnu 和 /usr/include 中。注意 zlib 的 configure 脚本比较特殊不能用标准的“--host”参数直接给它 CC 环境变量就行。接下来是 OpenSSL 1.1.1主要给 Qt Network 的 SSL 支持用wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz tar xzf openssl-1.1.1w.tar.gz cd openssl-1.1.1w ./Configure linux-aarch64 \ --prefix/opt/aarch64-sysroot/usr \ --openssldir/opt/aarch64-sysroot/usr/ssl \ shared \ no-asm make -j$(nproc) sudo make install_sw注意这里 OpenSSL 用了 shared 方式。其实 Qt 支持 OpenSSL 动态加载编译 Qt 时设了 -openssl-linked 才会硬链。为了减少静态链的复杂度我建议编译 Qt 时使用 -openssl-runtime构建阶段不直接链 OpenSSL运行阶段也尽量让 Qt 加载板子自己的 libssl.so。libpng 和 libjpeg、freetype、fontconfig 则直接用 sysroot 里的 dev 包就行不用全手工编。Qt configure 时会通过 pkg-config 或直接检查头文件/库文件来确认这些依赖是否存在只要 sysroot 里装好了 arm64 版本Qt 的交叉编译脚本能自动找到。3.3 设置交叉编译环境变量建议写一个环境变量脚本每次编译前 source 它vim /opt/setenv-aarch64.sh内容如下export PATH/usr/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- export CC${CROSS_COMPILE}gcc export CXX${CROSS_COMPILE}g export AR${CROSS_COMPILE}ar export AS${CROSS_COMPILE}as export LD${CROSS_COMPILE}ld export RANLIB${CROSS_COMPILE}ranlib export STRIP${CROSS_COMPILE}strip export SYSROOT/opt/aarch64-sysroot export PKG_CONFIG_PATH/opt/aarch64-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig export PKG_CONFIG_SYSROOT_DIR/opt/aarch64-sysroot export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS1 export PKG_CONFIG_ALLOW_SYSTEM_LIBS1 export QT_PREFIX/opt/Qt5.14.2-aarch64-static export PATH$QT_PREFIX/bin:$PATHPKG_CONFIG_SYSROOT_DIR 这个变量非常关键它告诉 pkg-config 所有依赖路径都要自动加上 sysroot 前缀防止交叉编译时误抓到宿主机的 x86 库。4. Qt 5.14.2 源码编译详细步骤4.1 修改 mkspecs 架构文件Qt 源码的 mkspecs 目录下没有现成的 linux-aarch64-gnu-g但有通用的 linux-g 和 linux-arm-gnueabi-g 可以参考。我们需要手动创建一个cd /opt/src/qt-everywhere-opensource-src-5.14.2 cp -r mkspecs/linux-arm-gnueabi-g mkspecs/linux-aarch64-gnu-g然后编辑 mkspecs/linux-aarch64-gnu-g/qmake.confvim mkspecs/linux-aarch64-gnu-g/qmake.conf关键内容修改为MAKEFILE_GENERATOR UNIX 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_AR aarch64-linux-gnu-ar cqs QMAKE_OBJCOPY aarch64-linux-gnu-objcopy QMAKE_NM aarch64-linux-gnu-nm QMAKE_STRIP aarch64-linux-gnu-strip QMAKE_INCDIR /opt/aarch64-sysroot/usr/include QMAKE_INCDIR_QT /opt/Qt5.14.2-aarch64-static/include QMAKE_LIBDIR /opt/aarch64-sysroot/usr/lib/aarch64-linux-gnu QMAKE_LIBDIR_QT /opt/Qt5.14.2-aarch64-static/lib QMAKE_LIBS -lz -lpng -ljpeg -lfontconfig -lfreetype load(qt_config)这里 QMAKE_LIBS 行不是必须的但我在实际编译时发现 Qt 的某些配置检查比如 configure 里的 freetype 检查会漏掉 sysroot 里的库路径手动加上之后问题少很多。同时在 mkspecs/linux-aarch64-gnu-g/qplatformdefs.h 中保持引用通用头文件即可cat mkspecs/linux-aarch64-gnu-g/qplatformdefs.h #include ../../linux-g/qplatformdefs.h4.2 configure 关键参数详解回到 Qt 源码根目录执行配置前先确认环境变量已经 sourcesource /opt/setenv-aarch64.sh ./configure --help /tmp/qt_configure_help.txt然后使用下述参数./configure \ -static \ -release \ -opensource \ -confirm-license \ -xplatform linux-aarch64-gnu-g \ -prefix /opt/Qt5.14.2-aarch64-static \ -nomake examples \ -nomake tests \ -no-compile-examples \ -skip qtwebengine \ -skip qtwebview \ -skip qt3d \ -skip qtdoc \ -skip qtlocation \ -skip qtwayland \ -skip qtsensors \ -skip qtserialbus \ -skip qtsvg \ -skip qttools \ -skip qttranslations \ -skip qtxmlpatterns \ -no-opengl \ -no-xcb \ -no-icu \ -no-dbus \ -no-ssl \ -no-feature-xcb \ -qt-zlib \ -qt-pcre \ -qt-libpng \ -qt-libjpeg \ -qt-freetype \ -no-fontconfig \ -no-glib \ -no-cups \ -no-tslib \ -no-pch \ -no-avx \ -no-avx2 \ -no-mips_dsp \ -no-mips_dspr2 \ -optimize-size \ -silent这些参数逐个说下选择理由-static编译静态 Qt 库。这是整个项目的核心目标。-release只编译 release 版本不编 debug大幅缩短时间。-opensource -confirm-license接受开源协议避免交互卡住。-xplatform linux-aarch64-gnu-g指定刚才创建的交叉架构文件。-prefix安装目录。-nomake examples -nomake tests不编示例和测试省时间省空间。-skip 一系列模块把用不到的 Qt 模块全部跳过。特别是 qtwebengine它体积巨大且对 aarch64 静态编译很不友好。-no-opengl如果你的界面是纯 QWidget 或 QML 2D 场景不需要 OpenGL。如果板子支持 EGLFS 且需要 GPU 加速则留 -opengl es2但依赖更复杂新手不建议。-no-xcb静态编译 xcb 插件容易出各种幺蛾子xcb 依赖 xcb libs、xkbcommon 等嵌入式场景默认用 linuxfb、eglfs 或 minimal 就够了。-no-icuICU 库体积庞大中文处理若无特殊要求Qt 自带的 QString 在处理 UTF-8 时足够用。如果用了 Qt Quick Controls 2 的部分复杂排版可能有点影响但大部分场景没事。-no-icu 之后QRegularExpression 不支持 Unicode 属性类但基础正则完全够用。-no-ssl先完全关闭 SSL 支持。如果后面应用需要 https再回过来调 -openssl-runtime避免一开始就被 OpenSSL 交叉编译问题卡住。-qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-freetype让 Qt 使用自带的第三方源码库。这样可以保证所有图像、字体、压缩相关的代码都是静态编进 Qt 库中的避免依赖 sysroot 的动态库。-no-fontconfig嵌入式环境字体配置简单用 Qt 自带 freetype 即可。不过要注意没有 fontconfig 时 Qt 的字体匹配策略会退化为直接加载指定字体文件需要在代码里设置 QFontDatabase 或通过环境变量 QT_QPA_FONTDIR 指定字体目录。-no-feature-xcb再次确保 xcb 相关功能被彻底移除。-optimize-size静态编译时体积优化很重要能让最终二进制小一些。configure 执行完看到类似 “Qt is now configured for building” 的提示就说明交叉配置通过了。此时还会生成一个 config.summary我强烈建议你打开看看确认里面各种 “OpenSSL: no”“xkbcommon: no” 都和预期一致。4.3 make 编译与安装编译 Qt 静态库比较耗时建议开启多核make -j$(nproc) 21 | tee /tmp/qt_build.log这个步骤通常需要 30 到 90 分钟取决于机器性能。如果中途报错不要整段重跑先查 log 尾部定位。我遇到过的几个编译期问题“cannot find -lGL”说明 configure 阶段没有正确处理 OpenGL 库。解决办法是去掉 -no-opengl 改为 -no-opengl 后仍然报错实际上如果明确不使用 OpenGL应确保 configure 参数里同时有 -no-opengl 且 sysroot 不要安装 libegl1-mesa-dev 等包。版本不一致会造成 configure 自动检测到 EGL 试图启用。最干净的方案是 sysroot 中不装任何 GL 相关 dev 包只用 -no-opengl。“collect2: error: ld returned 1 exit status” 伴随 undefined reference to qt_version_tag这个一般出现在最终应用链接而不是 Qt 编译中。解决方式是在编译应用时加 -fuse-ldbfd 或直接使用 aarch64-linux-gnu-g 作为链接器同时确保 Qt 的 lib 路径放在最前。编译卡在 qtwebengine检查你是否真的传了 -skip qtwebengine。如果没跳过它的下载和编译是噩梦所以务必检查配置参数。编译完成后安装sudo make install安装完成后检查ls /opt/Qt5.14.2-aarch64-static/lib/libQt5Core.a # 应该能看到 libQt5Core.a 等静态库到这里Qt aarch64 静态交叉编译环境就算建好了。5. 第一个 aarch64 静态 Qt 程序从 qmake 到部署5.1 编写测试程序并交叉编译先写一个最简单的 QWidget 程序验证环境// main.cpp #include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello aarch64 Qt Static); label.resize(400, 200); label.show(); return app.exec(); }创建工程文件# test.pro QT widgets SOURCES main.cpp TARGET hello_qt然后交叉编译source /opt/setenv-aarch64.sh cd /path/to/test /opt/Qt5.14.2-aarch64-static/bin/qmake test.pro make这里有个细节qmake 从 Qt 安装前缀里的二进制会生成一个 Makefile它内部引用的编译器路径来自我们在 qmake.conf 里写的 QMAKE_CC、QMAKE_CXX 变量所以编译时不用手动指定编译器。最终生成 hello_qt检查它的架构file hello_qt输出里应该包含 “ELF 64-bit LSB executable, ARM aarch64”。再检查动态依赖aarch64-linux-gnu-readelf -d hello_qt | grep NEEDED正常情况下只有基础的 libc、libm、libgcc_s 等系统库没有 libQt5Core.so、libQt5Widgets.so。5.2 部署到目标板与运行验证把 hello_qt 传到板子上可以直接用 scp、U 盘或者网络共享。运行前做准备export QT_QPA_PLATFORMlinuxfb export QT_QPA_FONTDIR/usr/share/fonts/truetype/dejavu ./hello_qt如果板子有 HDMI 或 LVDS 屏且不依赖 GPUlinuxfb 平台插件是最稳妥的选择。如果板子启用了 DRM/KMS也可以尝试export QT_QPA_PLATFORMeglfs ./hello_qt静态编译的 linuxfb 插件已经编码进 libQt5PlatformSupport.a 里运行时不需要额外的 .so 插件文件这也是静态编译带来的部署便利之一。5.3 体积优化与 strip编译出来的 hello_qt 可能很大比如 20MB 甚至更多。可以用 aarch64-linux-gnu-strip 去掉符号表aarch64-linux-gnu-strip --strip-unneeded hello_qtstrip 之后通常能减小 30% 到 50% 体积。再配合 upx 之类的压缩工具体积还能进一步缩减。注意 UPX 对 aarch64 架构的支持不如 x86 完善我实际试过有时压缩后运行不稳定所以项目要求稳定性时建议只 strip不上 UPX。6. 常见问题与坑我踩过的 7 个关键点6.1 问题排查速查表现象常见原因解决办法configure 时报 “Compiler cannot create executables”工具链与 sysroot 不匹配或 CC 环境变量未设置检查交叉编译环境变量确认 source 正确cannot find -lz / -lpngsysroot 中缺少对应静态库或软链在 sysroot chroot 中安装 dev 包或手动编译静态库cannot find -lGLsysroot 中安装 GL 开发包但 Qt 配置为 -no-opengl 或反之统一配置不用则 sysroot 不装 GL dev 包并加 -no-openglxcb 相关报错静态编译 xcb 插件依赖过多库使用 -no-xcb用 linuxfb 或 eglfsqmake 生成的 Makefile 里编译器路径是 x86 的 gccqmake.conf 中 QMAKE_CC/QMAKE_CXX 设置错误改回 aarch64-linux-gnu-gcc/g运行时找不到字体没有 fontconfigQt 无法自动找字体文件通过 QT_QPA_FONTDIR 指定字体目录程序运行时报 “could not find or load the Qt platform plugin linuxfb”动态编译时的经典错误但静态编译若裁剪过度也可能出现静态编译时确认 platform 插件已编入可用 QT_DEBUG_PLUGINS1 查看6.2 独家避坑心得第一Qt 源码 configure 阶段千万别嫌参数多就不写。做嵌入式静态编译config 参数就是“保命符”。少一个 -skip 模块编译时间就可能多半小时少一个 -no-xcb后面可能牵出一串无法链接的 xcb 符号。第二sysroot 里不要频繁增删 dev 包。我一开始反复 install/remove 各种交叉编译库结果软链混乱之后 make 时不时冒出怪异错误。后来我用了一台干净的 Ubuntu 20.04 虚拟机单独跑构建才彻底稳定下来。建议你也专门准备一个“构建专用环境”别在常用开发环境里搞。第三如果 configure 时出现了自动检测到本机库的情况可以用 -qt-* 系列参数强制 Qt 使用内置代码而不要让 configure 去 sysroot 外部搜索。另外 configure 里的 -I 和 -L 参数不会自动加 sysroot 前缀所以尽量用 -qt-* 来限制依赖搜索范围。第四关于 qmake 的版本信息编译应用时容易出现 Qt 库版本不匹配问题。确保环境变量里 PATH 的 /opt/Qt5.14.2-aarch64-static/bin 在最前面否则系统可能有另一个 x86 版本的 qmake 被优先找到生成错误 Makefile。6.3 一个容易忽略的 hidden 坑sysroot 里 /lib 与 /usr/lib 的软链Ubuntu 的 ARM64 根文件系统里/lib 通常是 /usr/lib 的软链/lib/aarch64-linux-gnu 或 /usr/lib/aarch64-linux-gnu 都有可能被编译器搜索。Qt 的 qmake.conf 里我写了 QMAKE_LIBDIR 指向 /usr/lib/aarch64-linux-gnu但链接器默认搜索顺序还包括 /lib/aarch64-linux-gnu。如果某些库只存在于其中一个目录就会导致找不到。建议把两个目录都配置进去QMAKE_LIBDIR /opt/aarch64-sysroot/usr/lib/aarch64-linux-gnu /opt/aarch64-sysroot/lib/aarch64-linux-gnu对应的 include 目录也类似QMAKE_INCDIR /opt/aarch64-sysroot/usr/include这个细节很多教程不提但实际踩到过好几次。如果编译某个依赖库时出现 “/usr/include/xxx.h: No such file or directory”多半就是 include 路径只配置了系统默认路径没把 sysroot 的路径传进去。7. 扩展静态链接其他第三方库实际项目中很少只靠 Qt 几个模块就能搞定多少会用到第三方库比如 curl、protobuf、sqlite 等。静态交叉编译思路是一样的把第三方库编成 .a放进 sysroot然后在工程的 .pro 文件里用 LIBS -lxxx 链接进去。以前段时间我在 RK3588 板子上做一个数据采集工具为例除了 Qt Widgets还需要 MQTT 通信。选择了 mosquitto 库交叉编译步骤wget https://mosquitto.org/files/source/mosquitto-1.6.15.tar.gz tar xzf mosquitto-1.6.15.tar.gz cd mosquitto-1.6.15 make CCaarch64-linux-gnu-gcc \ CXXaarch64-linux-gnu-g \ ARaarch64-linux-gnu-ar \ STRIPaarch64-linux-gnu-strip \ LDFLAGS--sysroot/opt/aarch64-sysroot \ CFLAGS-I/opt/aarch64-sysroot/usr/include \ WITH_TLSno \ WITH_SRVno sudo make install DESTDIR/opt/aarch64-sysroot然后工程的 .pro 文件里加LIBS -lmosquitto INCLUDEPATH /opt/aarch64-sysroot/usr/include这种“先编第三方库再编 Qt 应用”的顺序也是我在实际项目中验证过的最稳妥方式。8. 最后再分享一点实际经验这套 Qt 5.14.2 aarch64 静态交叉编译环境我在多个项目上反复用过RK3288、RK3399、树莓派 3B、树莓派 4B 上都跑通了。最让我意外的是同样的静态编译产物在很多板子上能直接运行基本不用像动态链接方案那样为不同的板子准备不同的库集合。这也是我为什么坚持“哪怕编译时间长也要上静态”。如果你打算长期做 aarch64 嵌入式 Qt 开发我建议把这篇手册里的所有路径、参数、脚本固化成一个 Dockerfile 或脚本仓库。这样以后不管是换机器还是移交同事都能最多半小时重建整个环境。另外有个小技巧编译 Qt 静态库时建议关闭系统的 “prelink” 服务和自适应文件系统索引服务它们会在编译过程中频繁访问文件导致 I/O 瓶颈。我第一编译 Qt 时没关速度慢了将近一倍。这篇文章没有写到的细节比如特定板子 EGLFS 的显示适配、Qt Quick 场景的静态编译资源处理、QSS 资源裁剪等网上资料也比较零散。如果你在某个具体环节卡住了把 configure 的输出和 config.summary 发给我看我们可以一起排查。毕竟交叉编译这东西最怕的就是“看起来编译过了跑起来全是问题”。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →