ARM64麒麟桌面自编译Qt 5.12.10:部署、排错与打包

发布时间:2026/10/28 8:41:58
ARM64麒麟桌面自编译Qt 5.12.10:部署、排错与打包

简介:面向麒麟操作系统 ARM64 平台开发者的 Qt 5.12.10 编译成果,已在联想昭阳 N4720Z 实机配合 Kylin-Desktop-V10-GFB-Release-020-ARM64 系统上完成构建并验证可用,适合国产化环境下做桌面应用开发、希望省去从源码编译 Qt 环节的工程师参考使用。包内共 2000 个文件,全部为 .h 头文件,涉及 QLocale 区域数据、各版本 OpenGL 函数封装与扩展声明、URL 顶级域数据、Valgrind 调试支持等模块,共同构成 Qt 开发和二次编译所需的头文件集合,压缩包约 84.62MB。目前已有 1075 人学习下载。对在麒麟 ARM 平台上开发界面、交叉编译或补齐 Qt 头文件依赖的读者来说,可直接获得与实机编译一致的头文件树,减少因架构差异引发的编译与链接报错,也便于对照排查缺失声明、版本不匹配等问题。

1. 一台 ARM64 麒麟桌面上的 Qt,为什么值得自己编一遍

拿到一台 ARM64 国产整机,预装 Kylin-Desktop-V10-GFB-Release-020-ARM64,项目代码锁死 qt 5.12.10,而系统源里给的是另一套版本,装上对不上、混着用又崩——这大概是 Linux arm64 桌面开发里最常见的开局。这份资源就是那台机器上编完的 Qt 5.12.10 产物:qmake、libQt5*.so、platforms 插件、mkspecs、qml 模块一整套,配上把它跑起来、以及从源码复现同一份产物的 configure 参数。适合三类人:被系统 Qt 版本卡住的应用开发者、要在 ARM64 上做交付打包的、以及想弄清本机编译和交叉编译边界的人。下面按「它是什么 → 怎么落地 → 怎么复现 → 坑在哪 → 怎么带走」推一遍。

2. 为什么在 ARM64 麒麟上首选自编译 Qt 5.12.10:版本、ABI 与依赖三件事

系统里已经有一套 Qt,为什么还要自己编一份放 /opt 下?这个问题必须先回答清楚,否则后面所有操作都像在瞎折腾。核心矛盾有三个:版本被发行版锁死、ABI 和编译器版本绑定、以及插件与第三方库的路径漂移。这三件事在 x86 上还能靠 apt 装个新版糊弄过去,在 ARM64 上往往没有现成包可选,只能自己动手。

2.1 系统自带 Qt 与自编译 Qt 的边界在哪

麒麟桌面 V10 这类发行版自带的 Qt,通常是为了桌面环境和系统组件服务的,版本偏保守,而且发行方会打补丁、裁剪模块。你apt list --installed | grep qt5看到的可能是一堆libqt5core5a、libqt5gui5的运行时包,开发用的qtbase5-dev未必装得上,即使装上,头文件里的QT_VERSION_STR也未必是你要的 5.12.10。

更麻烦的是「混用」这件事。系统 Qt 装在/usr/lib/aarch64-linux-gnu/,你的程序如果只把/opt/qt-5.12.10/lib加进LD_LIBRARY_PATH,链接期用了一套头文件、运行期加载了另一套.so,轻则样式表不生效、插件加载不到,重则启动直接段错误。这是我见过最多的「玄学崩溃」来源,没有之一。

所以边界要划清楚:系统 Qt 留给桌面环境,自编译 Qt 装到独立 prefix(比如/opt/qt-5.12.10),应用侧只认这个 prefix,两套东西井水不犯河水。判断标准很简单,执行qmake -query QT_INSTALL_PREFIX,输出必须是/opt/qt-5.12.10,不能是/usr。

2.2 ARM64 下 ABI、GCC 与 mkspec 的对齐

Qt 5.12.10 对 aarch64 的支持是完整的,qtbase/mkspecs/linux-aarch64-gnu-g++这个 mkspec 一直存在,但它解决的是「交叉编译」场景。在 ARM64 机器上本机编译时,反而应该用linux-g++,让 qmake 自己探测本机 g++。这两个选择搞反了,会出现-sysroot路径找不到、链接器参数多余、以及目标文件架构不匹配的报错。

编译器版本要留意。Qt 5.12 的 C++ 标准默认是 C++11,GCC 7 到 GCC 9 都稳;GCC 10 之后默认-fno-common,编某些老模块(尤其是qtdeclarative里的第三方 JS 引擎代码)会报重复符号。麒麟桌面 V10 一般配 GCC 7 或 8,正好是舒适区,这也是我愿意在这套系统上编 5.12 而不是更新版本的原因之一。

还有一个隐性约束:glibc 版本向下兼容不可逆。你在高版本 glibc 的机器上编出来的产物,拷到低版本 glibc 的机器上跑不起来,报GLIBC_2.xx not found。同一批 ARM64 设备如果系统镜像版本不一致,最好在最低版本的那台上编,或者干脆走容器/固定镜像。

2.3 依赖清单:xcb、fontconfig、OpenGL 三组必查项

编译能不能一次过,八成取决于依赖装没装全。ARM64 发行版的依赖包命名和 x86 基本一致,但可用性参差,尤其是一些-dev包在小众镜像源里可能缺。开编前先确认这三组:

组别关键包缺了会怎样
xcb 平台插件libxcb1-dev、libxcb-xinerama0-dev、libxcb-icccm4-dev、libxcb-image0-dev、libxcb-keysyms1-dev、libxcb-render-util0-dev、libxkbcommon-x11-dev编译期libqxcb.so直接不生成,或者生成后在运行期报 platform plugin 初始化失败
字体渲染libfontconfig1-dev、libfreetype6-dev中文显示成方块,或者 configure 阶段检测不到 fontconfig 而退回内置渲染
图形接口libgl1-mesa-dev、libegl1-mesa-dev、libgles2-mesa-dev报cannot find -lGL,或者 QOpenGLWidget 一创建就崩

用pkg-config --exists xcb && echo ok这类命令快速验证比 unzip 依赖包名猜要快得多。至于怎么一次性装全,第 4 章会给出完整命令行。

3. 把这份产物落到机器上:目录、环境变量与第一个可跑的 Widgets 程序

拿到编译好的 Qt 包,第一件事不是急着跑程序,而是先确认目录结构和环境变量对不对。这一步做扎实,后面能省掉一半的排查时间。

3.1 解包后先看清目录约定

Qt 的安装布局是固定的,bin/放 qmake 和 moc、uic、rcc 这些工具,lib/放 so 和 pkgconfig,include/放头文件,plugins/放平台、图片格式、SQL 驱动插件,qml/放 QML 模块,mkspecs/放编译配置模板。拿到包后先扫一眼:

# 假设产物解压到 /opt/qt-5.12.10 ls /opt/qt-5.12.10 # bin include lib mkspecs plugins qml translations # 确认关键文件在位 test -x /opt/qt-5.12.10/bin/qmake && echo "qmake ok" test -f /opt/qt-5.12.10/plugins/platforms/libqxcb.so && echo "xcb plugin ok" test -d /opt/qt-5.12.10/lib/cmake/Qt5 && echo "cmake config ok"

三条全中,说明这份产物是完整的。少任何一条都别急着往下走,libqxcb.so缺失意味着 xcb 依赖没装全就编了,cmake/Qt5缺失意味着 install 阶段被中断过。

mkspecs/这层特别容易被忽略。后面用 qmake 编应用时,QT_ARCH、QMAKE_CXX这些变量都是从mkspecs/qconfig.pri和mkspecs/qmodule.pri读出来的,这两个文件缺了或者内容不对,qmake 生成的 Makefile 会指向错误的编译器。

3.2 环境变量与 qmake 切换

Qt 的环境变量有一堆,但日常真正要动的就五个。写成独立脚本加载,比塞进~/.bashrc安全——后者会让整个登录会话都指向这份 Qt,系统自带的桌面程序可能被带偏。

# /opt/qt-5.12.10/env.sh export QT512=/opt/qt-5.12.10 export PATH=$QT512/bin:$PATH # 让 qmake / moc / uic 优先命中自编译版本 export LD_LIBRARY_PATH=$QT512/lib:$LD_LIBRARY_PATH # 运行期优先加载自编译 so export QT_PLUGIN_PATH=$QT512/plugins # 插件搜索路径,xcb/图片格式全靠它 export QML2_IMPORT_PATH=$QT512/qml # QML 模块路径,纯 Widgets 项目可不设 export PKG_CONFIG_PATH=$QT512/lib/pkgconfig:$PKG_CONFIG_PATH # 供第三方库通过 pkg-config 找到 Qt

PATH放最前是必须的,否则which qmake命中的还是/usr/bin/qmake。LD_LIBRARY_PATH属于「后悔药」性质——它能救急,但也是双刃剑,如果程序还依赖系统里其他库,可能会被这份 Qt 的间接依赖顶掉。第 6 章会用 rpath 替代它。

加载后立刻验证:

source /opt/qt-5.12.10/env.sh which qmake # 期望 /opt/qt-5.12.10/bin/qmake qmake -query QT_VERSION # 期望 5.12.10 qmake -query QT_INSTALL_PREFIX # 期望 /opt/qt-5.12.10 qmake -query QT_INSTALL_PLUGINS # 期望 /opt/qt-5.12.10/plugins # 再确认一下产物本身的架构标签和 SONAME readelf -d /opt/qt-5.12.10/lib/libQt5Core.so.5.12.10 | grep -E 'SONAME|RUNPATH' file /opt/qt-5.12.10/bin/qmake # 期望 aarch64 / ARM aarch64

qmake -query的输出是判断环境是否干净的黄金标准,比echo $LD_LIBRARY_PATH可靠得多。如果QT_VERSION显示的是 5.12.10 但QT_INSTALL_PREFIX指向/usr,说明这份 qmake 被编译成了错误的 prefix,得用qmake -set或者重编。

3.3 一个最小 Widgets 工程验证链路

环境变量对了,还要验证「编译 → 链接 → 运行 → 加载插件」这条完整链路。写个既能打印版本又能显示窗口的小程序,一次把这几件事全测了。

mkdir -p ~/qt-smoke && cd ~/qt-smoke cat > main.cpp <<'EOF' #include <QApplication> #include <QLabel> #include <QDebug> #include <QtGlobal> // QT_VERSION_STR #include <QSysInfo> // currentCpuArchitecture int main(int argc, char *argv[]) { QApplication app(argc, argv); // 打印编译期版本、运行期版本和 CPU 架构,三者必须一致 qDebug() << "compiled with Qt" << QT_VERSION_STR << "| runtime Qt" << qVersion() << "| arch" << QSysInfo::currentCpuArchitecture(); QLabel w(QStringLiteral("hello arm64 / Qt %1").arg(QT_VERSION_STR)); w.resize(360, 120); w.show(); return app.exec(); } EOF cat > smoke.pro <<'EOF' QT += widgets CONFIG += c++11 TARGET = smoke SOURCES += main.cpp EOF qmake smoke.pro && make -j4 && ./smoke

这段代码的用意有三层。第一层,QT_VERSION_STR是编译期常量,qVersion()是运行期函数返回值,两者打印出来不一致,说明头文件和 so 没对上,是最典型的混用信号。第二层,QSysInfo::currentCpuArchitecture()输出arm64才说明真跑在目标架构上,交叉编译配错时这里会露馅。第三层,窗口能弹出来,说明libqxcb.so加载成功、字体渲染链路通,反过来如果只有 qDebug 输出没窗口,问题就锁定在 platform plugin。

跑不通的话,按这个顺序看:先ldd ./smoke | grep "not found"查缺失库,再QT_DEBUG_PLUGINS=1 ./smoke看插件加载日志,最后strace -f -e trace=openat ./smoke 2>&1 | grep -i qt追文件打开路径。这三板斧下去,九成问题能定位。

4. 从源码复现同一份 Qt 5.12.10:configure 参数逐个拆

手上这份产物能用,但换台机器、换个内核小版本、或者想裁掉几个用不上的模块,就得自己重编一遍。Qt 5.12.10 的源码包在 ARM64 上编译大概需要几小时(视核数和模块数而定),所以 configure 阶段把参数想清楚,比编到一半发现漏了模块再回头强得多。

4.1 装依赖:apt 那一串到底装了什么

先确认包管理器。麒麟桌面 V10 是 Debian 系,用apt;如果你手上是服务器版分支,改dnf对应包名即可。下面是桌面版可直接抄的一条命令:

sudo apt update sudo apt install -y \ build-essential perl python3 git pkg-config \ libx11-xcb-dev libxcb1-dev libxcb-xinerama0-dev libxcb-icccm4-dev \ libxcb-image0-dev libxcb-keysyms1-dev libxcb-render-util0-dev \ libxcb-shape0-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev \ libfontconfig1-dev libfreetype6-dev libharfbuzz-dev \ libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev \ libssl-dev libsqlite3-dev libsm-dev libice-dev \ libcups2-dev libasound2-dev

分组说明一下:xcb 那一长串是libqxcb.so的编译前提,少一个.pc文件,configure 就会默默把 xcb 后端关掉,你编完发现没有桌面窗口能力;libfontconfig1-dev和libharfbuzz-dev决定中文和复杂文本的渲染质量;libgl1-mesa-dev三个是 OpenGL/EGL 支持,-opengl es2和默认动态检测都依赖它们;libssl-dev关系到QtNetwork的 HTTPS,版本别太老;libsqlite3-dev是 QtSql 内置驱动的可选依赖,不装也能编,但 SQLite 驱动会缺。

装完做一次预检,比编到一半才发现缺库强:

for p in xcb xkbcommon fontconfig freetype2 egl gl; do pkg-config --exists $p && echo "$p ok" || echo "$p MISSING" done

任何一行MISSING,都回到上面的安装命令里补对应的-dev包。

4.2 configure 参数表与取舍

Qt 的 configure 参数上百个,真正影响 ARM64 桌面产物的就下面这些。我按「必选 / 强烈建议 / 视情况」三档列,顺便说清每个参数为什么这么设。

参数作用在 ARM64 麒麟上的取舍
-prefix /opt/qt-5.12.10安装根目录必选。放/opt下和系统 Qt 隔离,卸载时rm -rf即可
-opensource -confirm-license开源协议确认必选。5.12 是 LGPLv3/GPLv3 双协议,确认后 configure 才能继续
-release发布版编译必选。调试版体积大、性能差,只在追 Qt 内部 bug 时才用-debug
-nomake examples -nomake tests跳过示例和测试强烈建议。省 30% 以上编译时间,示例对交付没价值
-skip qtwebengine跳过 WebEngine强烈建议。ARM64 上 v8 工具链麻烦,编一次可能吃掉大半时间和内存
-skip qtwebkit -skip qtdoc跳过 WebKit 和文档建议。QtWebKit 在 5.12 已废弃,文档模块对构建产物无用
-qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz使用捆绑的第三方库建议。避免系统库版本差异导致运行期行为不一致,代价是产物略大
-opengl es2指定 OpenGL ES 2.0视情况。国产 ARM64 显卡驱动对桌面 GL 支持参差,ES2 更稳;如果确认有完整 GL 驱动,可去掉让 configure 自动检测
-no-pch关闭预编译头建议。ARM64 上 PCH 偶尔引发内存暴涨,关掉更可控
-make libs -make tools只编库和工具建议。不带-make examples,产物干净
-sysroot <path>交叉编译根目录本机编译不要加。加了会去 sysroot 里找库,反而找不到系统库
-device linux-aarch64-gnu-g++交叉编译设备配置本机编译不要加。用了就要配-device-option CROSS_COMPILE=...

实际执行:

cd ~/src/qt-everywhere-src-5.12.10 ./configure -prefix /opt/qt-5.12.10 \ -opensource -confirm-license \ -release \ -nomake examples -nomake tests \ -skip qtwebengine -skip qtwebkit -skip qtdoc \ -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz \ -opengl es2 \ -no-pch \ -make libs -make tools \ 2>&1 | tee /tmp/qt-configure.log

configure 跑完最后几行会打印一份摘要,重点看三项:Build type是 release、Platform是linux-g++(不是linux-aarch64-gnu-g++)、OpenGL那一行写的是 es2 还是 desktop。摘要里如果出现XCB: no,立刻停下回去补 xcb 依赖,别硬着头皮编。

提示:configure 过程中任何WARNING都值得扫一眼,尤其是QFontDatabase: Using fontconfig这类,它直接决定运行时中文字体能不能找到。

4.3 编译、install 与产物自检

编译阶段最容易翻车的是内存和并行度。ARM64 开发板或整机常常是 4 核 8G,make -j8编到qtdeclarative就会触发 OOM killer,进程被SIGKILL,make只报一句Error 137,看不出原因。

# 并行度取核数的一半,内存紧张时更保守 JOBS=$(( $(nproc) / 2 )) [ $JOBS -lt 2 ] && JOBS=2 # 单条命令带日志,失败后可直接定位到具体目录 make -j$JOBS 2>&1 | tee /tmp/qt-make.log # 编译成功后再安装,install 阶段会写 /opt,需要 sudo sudo make install 2>&1 | tee /tmp/qt-install.log

Error 137就是 128+9,SIGKILL的信号号,看到这个数字直接去dmesg -T | tail -30找oom-killer,能找到具体是哪个 g++ 进程被杀。解决要么加 swap,要么降并行度,要么分模块编(cd qtbase && make,然后逐个模块推进)。

install 完成后做三项自检,和 3.1 节的检查呼应起来:

test -x /opt/qt-5.12.10/bin/qmake && echo "qmake ok" ls /opt/qt-5.12.10/plugins/platforms/ # 应看到 libqxcb.so /opt/qt-5.12.10/bin/qmake -query QT_VERSION # 应输出 5.12.10 # 插件自身的依赖是否齐全,这一步能提前暴露运行期问题 ldd /opt/qt-5.12.10/plugins/platforms/libqxcb.so | grep "not found"

最后一条命令输出为空才叫干净。有not found的话,按缺失的库名反查apt-file search(需先apt install apt-file && apt-file update),补装对应的运行时包,不需要重新编译 Qt,因为插件是动态加载的。

5. 避坑与排查:ARM64 麒麟上编 Qt 的五个翻车现场

这一节全是血泪经验,每条按「现象 → 原因 → 解决」写,能对号入座就别自己再试一遍。

第一条:make 编到一半报Error 137,进程无声消失。

现象是编译日志突然中断,最后一行是make[3]: *** [Makefile:xxx: .obj/xxx.o] Error 137,没有任何错误输出。原因是 OOM killer 介入,ARM64 上qtdeclarative里的 qml 引擎代码模板展开后单文件编译峰值内存能超过 2G,4 核 8G 的机器开-j8直接顶到天花板。解决办法是把并行度降到nproc/2,同时在/etc/fstab里加 4G swap 文件并swapon,编完再降回来。判断是否真是 OOM,执行dmesg -T | grep -i "killed process",能看到被杀的 g++ PID 和占用内存。

第二条:应用启动报This application failed to start because no Qt platform plugin could be initialized。

现象是窗口没出来,终端只有这一行提示,加QT_DEBUG_PLUGINS=1后能看到Cannot load library /opt/qt-5.12.10/plugins/platforms/libqxcb.so: (libxcb-xinerama.so.0: cannot open shared object file)。原因是编译期装了 xcb 的 dev 包,但运行环境缺对应的运行时 so——dev 包和运行时包在 Debian 系里是分开的。解决办法是apt install libxcb-xinerama0(按提示缺失的库名逐个装),或者更彻底地ldd libqxcb.so | grep "not found"一次列全,批量补装。这个问题在把程序拷到另一台机器时尤其常见,第 6 章的打包方案就是为了根治它。

第三条:程序能跑,但界面样式全丢,QSS 不生效。

现象是控件渲染成最朴素的灰白风格,QApplication::setStyle也无效。原因是QT_PLUGIN_PATH指向了/opt/qt-5.12.10/plugins,但程序运行期实际加载的是系统 Qt 的styles/libqgtk3.so或libqclearlooks.so,版本不匹配导致插件加载失败后静默降级。排查方法是QT_DEBUG_PLUGINS=1 ./app 2>&1 | grep -i style,看它到底试图从哪个路径加载。解决是确保LD_LIBRARY_PATH和QT_PLUGIN_PATH指向同一份 Qt,二者必须成对设置,只设一个就会出这类「半生效」的怪现象。

第四条:编译期cannot find -lGL,但libgl1-mesa-dev明明装了。

现象是链接某个 OpenGL 相关模块时报-lGL找不到,dpkg -l | grep mesa显示包已装。原因是 ARM64 上 Mesa 的 GL 库在部分发行版里只提供libGL.so.1运行时,libGL.so这个开发用软链没建,或者被放在了非标准路径。解决是先find / -name "libGL.so*" 2>/dev/null确认实际位置,如果只有.so.1没有.so,建软链sudo ln -s /usr/lib/aarch64-linux-gnu/libGL.so.1 /usr/lib/aarch64-linux-gnu/libGL.so;如果 configure 时明确用-opengl es2,那就不该链-lGL,检查是不是某个第三方库的.pc文件里硬写了-lGL,用pkg-config --libs逐项排。

第五条:编译成功,但 QML 程序启动报module "QtQuick" is not installed。

现象是纯 Widgets 程序正常,一切到 QML 就报模块找不到。原因是-skip参数用过头,把qtdeclarative也跳了,或者QML2_IMPORT_PATH没设。检查/opt/qt-5.12.10/qml/目录是否存在且里面有QtQuick.2/,没有就说明qtdeclarative没编进去。解决是重新 configure,去掉可能误伤的-skip项(-skip qtwebengine不影响 qtdeclarative,可以放心保留),只针对确实不需要的模块跳过。另外QML2_IMPORT_PATH和QT_PLUGIN_PATH要一起设,QML 的模块解析和插件扫描是两条独立路径。

6. 分发与校验:把 Qt 5.12.10 一起带走

自己机器上跑通不算完,交付给别人的 ARM64 设备时,对方不可能也装一份/opt/qt-5.12.10。常规做法是把应用和它用到的 Qt 库、插件打进同一个目录,用 rpath 和qt.conf让程序自己找到邻居,彻底摆脱LD_LIBRARY_PATH。

6.1 目录布局与 qt.conf

先定布局,这是所有后续操作的基础:

demo-app/ ├── bin/demo ├── lib/ # libQt5Core.so.5 等运行时 so ├── plugins/ # platforms/、imageformats/、styles/ ├── qml/ # 用到 QML 才需要 └── qt.conf

qt.conf放在bin/同级,Qt 启动时会优先读它,覆盖编译期写死的路径:

[Paths] Prefix = .. Plugins = plugins Libraries = lib Qml2Imports = qml

Prefix = ..是相对bin/demo的,所以整个目录可以随便挪位置,拷到/opt、/home或者 U 盘都能跑,这是它比环境变量强的地方。

6.2 用 patchelf 写死 rpath

库的搜索路径用 rpath 解决,比LD_LIBRARY_PATH可靠,因为它跟着可执行文件走:

cd demo-app # 给主程序和每个 so 都写上 $ORIGIN 相对的 rpath patchelf --set-rpath '$ORIGIN/../lib' bin/demo for so in lib/*.so.5.12.10; do patchelf --set-rpath '$ORIGIN' "$so" done # 插件目录深一层,rpath 要回到 lib for p in plugins/platforms/*.so plugins/imageformats/*.so; do patchelf --set-rpath '$ORIGIN/../../lib' "$p" done

$ORIGIN是 ELF 的动态链接器变量,表示「当前文件所在目录」,所以bin/demo里的$ORIGIN/../lib就是demo-app/lib,而plugins/platforms/libqxcb.so里的$ORIGIN/../../lib也是demo-app/lib。这个写法让整个目录完全位置无关。

至于收集哪些 so,别手动抄,用ldd反查最稳:

ldd bin/demo | awk '/=>/ {print $3}' | grep -v '^/lib\|^/usr/lib' | xargs -I{} cp -L {} lib/

grep -v那一段是过滤掉 glibc、libstdc++ 这些系统必带的库,它们必须用目标机的版本,打进包里反而会引发GLIBC版本冲突。

6.3 干净环境下的验证

打包完不能在自己机器上跑一遍就交付,因为你的LD_LIBRARY_PATH会掩盖所有路径问题。用env -i清空环境变量再跑:

env -i HOME=$HOME DISPLAY=$DISPLAY ./bin/demo

能起来,说明 rpath 和qt.conf都生效了。再补一刀,验证没有意外引用到系统 Qt:

ldd bin/demo | grep -i "libQt5" # 应全部指向 demo-app/lib 下的路径 readelf -d bin/demo | grep -E 'RPATH|RUNPATH'

如果ldd里混进了/usr/lib/aarch64-linux-gnu/libQt5Core.so.5,说明某个 so 的 rpath 没写对,或者lib/里少了那个库文件,回到 6.2 补。

注意:patchelf会修改 ELF 头,某些签名校验或完整性检查的发行环境可能因此报错,交付前跟目标环境确认一下是否有这类限制。

这套流程我踩过最深的一次坑,是给一台 ARM64 设备交付 QML 程序,qt.conf里漏了Qml2Imports那一行,结果对方现场报QtQuick is not installed,我在自己机器上怎么都复现不了——因为我的QML2_IMPORT_PATH一直开着。从那以后我每次打包完,都会先env -i清环境跑一遍,再unset掉所有QT_开头的变量跑第二遍,两遍都过才发出去。希望帮到你。

本文还有配套的精品资源,点击获取