相信不少做国产化适配的朋友都遇到过这个场景拿到一台银河麒麟Kylin V10尤其是ARM64架构的机器系统自带的Qt版本要么太老要么缺模块想自己编译一个Qt 5.12.12结果被各种依赖问题折腾得焦头烂额。我自己在给某单位的终端设备做应用迁移时就在这块踩了整整两周的坑——不是报GLIBCXX版本不对就是编译到一半提示找不到xcb库又或者搞定了编译却在运行时弹出一堆libQt5XcbQpa.so找不到的幺蛾子。这篇文章就是来填这些坑的。我会把银河麒麟V10含ARM64下从零编译Qt 5.12.12的完整过程、编译选项优化思路、以及最容易被忽略的依赖管理策略全部梳理一遍。无论你是刚接触国产系统开发的新手还是被交叉编译折磨的老手这篇文章都能让你少走弯路直接抄作业。1. 环境梳理与编译思路先看清对手再动手1.1 银河麒麟Kylin V10的系统底细在动手编译之前务必要先搞清楚目标机器的系统版本和硬件架构。目前市面上最常见的银河麒麟版本是Kylin Linux Advanced Server release V10这个版本分两种内核路线一种是基于鲲鹏、飞腾等ARM64芯片的另一种是跑在x86_64 Intel/AMD平台上的。两者的依赖库体系、编译器默认参数、甚至/usr/lib下so文件的路径都有差异搞混了很容易在后续编译时踩到完全摸不着头脑的链接错误。我一般先用这几条命令把环境摸清# 查看系统版本 cat /etc/kylin-release # 查看内核架构 uname -m # 查看CPU具体型号确认是否支持特定指令集 lscpu | grep -i architecture lscpu | grep -i model.name这里有一个重点如果你用的是ARM64版本的麒麟那么很多第三方预编译包比如部分Pypi源里的二进制wheel、某些闭源商业库的Linux版都没有对应的aarch64版本。这意味着你不仅要编译Qt本身可能还需要连带编译它的部分第三方依赖比如libwebp、libjasper等。所以在规划阶段就要做好“这会是一次源码级编译之旅”的心理准备。1.2 为什么是Qt 5.12.12而不是其他版本很多朋友会问既然都2025年了为什么不用Qt 6锅不在Qt 6本身而在生态。很多做国产化适配的业务系统尤其是涉及保密、国防、政务领域的项目立项时锁定的技术栈就是Qt 5.12.x LTS。Qt 5.12是LTS版本而且5.12.12是5.12系列的最终维护版本修复了大量已知CVE漏洞是“版本锁定”场景下最稳妥的收官选择。更重要的是Qt 5.12.12兼容老旧的OpenGL 2.1 / OpenGL ES 2.0硬件平台设计这在部分国产GPU驱动上依然是常态而Qt 6对OpenGL和RHI的新要求会让很多国产显卡驱动直接罢工。所以别看不起这个老将在兼容性面前它比新版本能打多了。1.3 编译优化前置明确你的定位既然是“编译优化与依赖管理”我们得先定两个基调如果你只是在本机开发调试那编译优化目标就是“快速编译 稳定运行”不需要为了极致性能牺牲编译时间。如果你的目标是将Qt作为运行时库分发到配置较低的终端设备上那就得考虑-O2甚至-O3优化、去掉调试符号、裁剪不需要的模块。我这次实际操作的目标环境是银河麒麟V10ARM64飞腾FT-2000/4核处理器8GB内存最终产物要部署到同配置的终端上。所以我的优化策略是本机用默认参数快速编译打包发布时再启用更激进的优化参数。这样既能快速迭代又能保证交付质量的稳定性。2. 依赖管理搞定编译前90%的烦心事2.1 用系统包管理器把基础依赖一次性装齐银河麒麟V10是基于Debian体系具体是Ubuntu 18.04/20.04的变种开发的。这意味着它可以用apt来管理软件包并且绝大多数Debian源里的依赖包都能直接安装。但是麒麟默认的软件源有时候不太全我强烈建议在编译Qt前先检查一下/etc/apt/sources.list里源是否可用并且执行一次apt update。保险起见我们把编译Qt所需的基础依赖分成两类。第一类是编译工具链第二类是Qt运行时和GUI所需的系统库。这里我给出一个实测可用的安装命令合集# 第一步安装基础编译工具链 sudo apt-get install -y build-essential g gcc make cmake ninja-build perl python3 # 第二步安装Qt GUI/XCB相关依赖这步最关键缺了必报错 sudo apt-get install -y libxcb1-dev libxcb-xkb-dev libxcb-xinerama0-dev \ libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev \ libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev \ libxcb-xkb1 libxcb-xv0-dev libxcb-glx0-dev # 第三步安装X11和OpenGL开发库 sudo apt-get install -y libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev \ libxi-dev libxrender-dev libxkbcommon-dev libxkbcommon-x11-dev \ libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev \ mesa-common-dev libfontconfig1-dev libfreetype6-dev libdbus-1-dev \ libharfbuzz-dev libssl-dev libudev-dev在实际操作中libxcb-*系列依赖是最容易出问题的。因为有的麒麟系统默认源里的libxcb-util-dev包名是带版本号的比如libxcb-util1-dev如果安装时提示找不到包用apt search libxcb先查一下实际可用的包名。2.2 离线环境下的依赖处理把坑提前排掉很多国产化项目是物理隔离的内网环境没法直接apt install。这时候处理依赖的策略有两种第一种最省事在一台能上网的同架构麒麟机器上用apt download把依赖全部下载下来然后拷贝到内网机器上手动安装。比如你可以# 在一台联网且同系统版本、同架构的机器上执行 sudo apt-get install --download-only -y libxcb1-dev libxcb-xkb-dev ... # 下载的deb包会存放在 /var/cache/apt/archives/ 下我通常会在部署脚本里把这些deb包拷到内网机然后执行sudo dpkg -i *.deb。如果遇到依赖顺序问题导致dpkg报错就多执行几遍sudo apt-get install -f来修复依赖。第二种更稳妥直接用apt-get build-dep -y qt5-qmake之类的方式把Qt的编译依赖自动拉取下来。不过这个命令在麒麟源里有时候会提示找不到对应的源码包需要提前apt-get source配置好源码源deb-src。无论哪种方式有一个原则是共通的离线环境下的依赖管理本质上就是提前做一次“依赖清单审计”。我建议你在联网机器上先跑一遍编译把完整依赖列表导出成文本文件再放到内网机上照着装。比对着报错一个个找要省心十倍。3. 编译配置与参数优化既要跑得快也要不被坑3.1 解压源码与配置configure参数拿到qt-everywhere-src-5.12.12.tar.xz约500MB后建议放到一个空间充裕的目录下比如/opt或/home目录。解压后进入源码根目录创建一个build目录来存放编译中间产物避免污染源码目录tar -xf qt-everywhere-src-5.12.12.tar.xz cd qt-everywhere-src-5.12.12 mkdir build cd build # 在build目录下执行配置 ../configure \ -prefix /opt/Qt5.12.12 \ -opensource -confirm-license \ -release \ -shared \ -nomake examples \ -nomake tests \ -qt-xcb \ -xcb-xlib \ -no-avx \ -no-avx2这里几个关键参数我逐一说明-prefix /opt/Qt5.12.12指定安装路径。个人建议不要用默认的/usr/local/Qt-5.12.12这种路径因为之后你需要给编译好的Qt授予写权限放在home或/opt下管理更灵活。-qt-xcb强制使用Qt自带的xcb插件而不是依赖系统的xcb库版本。这个参数在麒麟上能避免很多“xcb版本太旧导致Qt无法启动”的问题。-no-avx和-no-avx2这个我必须提醒一下。很多人在x86_64机器上为了性能会不加这个参数但我们在做国产化适配分发时不能保证目标机器一定支持AVX指令集比如部分老式兆芯、海光CPU或虚拟机环境就不支持。为了兼容性我干脆在编译期禁用AVX牺牲一点点本地性能换来部署到任何机器都能跑的安全感。3.2 优化关键点并行编译与CPU指令集配置完成后就是最期待的编译阶段。直接执行make -j$(nproc)是不够的因为nproc只统计物理核心数对于编译这种CPU密集型任务还需要考虑每个核心的负载能力。我实测下来在飞腾FT-20004核上直接用-j4编译大概需要40~50分钟用完-j84核8线程后时间压缩到30分钟左右。不过这里有个血泪教训并行编译时内存消耗非常大如果你机器只有4GB内存-j8大概率会触发OOMOut Of Memory导致编译进程被内核直接杀死。给一个稳妥的方案# 先查看CPU核心数和内存 nproc free -h # 如果是4核8GB内存的机器推荐用 -j4 # 如果内存有16GB可以用 -j8 make -j4 21 | tee build.log另外提一句如果你确信目标部署的机器CPU较新比如飞腾D2000或者鲲鹏920可以在configure阶段加一个-marcharmv8-acrccrypto之类的参数来针对ARMv8指令集做优化。但对于通用分发的场景我不建议在configure里塞具体的-march参数因为Qt内部很多模块的编译会使用动态链接和运行时检测你把指令集锁死反而可能让部分功能崩溃。3.3 编译过程中的资源监控与异常处理执行make之后千万不要就干等着。我会另开一个终端监控CPU、内存和磁盘IOtop # 查看CPU占用 df -h # 确认磁盘剩余空间为什么强调磁盘因为Qt编译的中间文件极其占空间实测完整编译一次源码1.2GB build目录8GB 安装产物3GB加起来12GB起步。如果你机器剩余空间少于15GB建议先做磁盘清理否则编译到一半就报No space left on device前功尽弃。在编译过程中如果遇到某个模块报错终止别慌先看build.log里最后的报错信息。90%的情况都是缺少某个开发包用apt-file工具也能查apt-file search xxx.h。补装依赖后不需要重新执行configure直接在build目录下继续执行make就可以Qt构建系统是支持断点续传的。4. 安装、环境变量与依赖部署让Qt真正跑起来4.1 make install与ldconfig配置编译完并确认没有致命错误后执行安装sudo make install安装完成后/opt/Qt5.12.12目录下就会生成bin、lib、plugins、qml、include等子目录。此时还差最后一步让系统能够找到Qt的动态链接库。这里有两种方式方式一修改ld.so.conf文件。在/etc/ld.so.conf.d/下新建一个qt5.12.12.conf文件内容写入/opt/Qt5.12.12/lib然后执行sudo ldconfig。这种方式对全系统生效适合你登录用户和系统服务都要用Qt的场景。方式二只对当前用户生效修改~/.bashrc加入export QTDIR/opt/Qt5.12.12 export PATH$QTDIR/bin:$PATH export LD_LIBRARY_PATH$QTDIR/lib:$LD_LIBRARY_PATH export QT_PLUGIN_PATH$QTDIR/plugins export PKG_CONFIG_PATH$QTDIR/lib/pkgconfig:$PKG_CONFIG_PATHsource ~/.bashrc后执行qmake -v如果输出QMake version 3.1 Using Qt version 5.12.12 in /opt/Qt5.12.12/lib恭喜你Qt已经成功跑起来了。4.2 运行时依赖的“黑盒清单”检查很多时候编译安装完qmake能运行但真正跑一个带GUI的程序时提示Failed to load platform plugin xcb。这说明运行时依赖没配全。这里我分享一个排查利器lddldd /opt/Qt5.12.12/plugins/platforms/libqxcb.so | grep not found这个命令会列出libqxcb.so所有缺失的动态库。如果输出一堆not found说明系统缺一堆X11相关库。这时回到第二章节把缺失的库名对着apt install就行。还有一个坑麒麟V10自带的glibc版本是2.28而Qt 5.12.12最低要求是2.17所以这块通常没问题。但是要注意如果系统里装了Anaconda或者其它自带的Python环境它们可能自带了一套较老或较新的libstdc.so.6会通过LD_LIBRARY_PATH影响Qt运行。我遇到过用conda run启动Qt程序时莫名其妙崩溃排查半天发现是conda里的libstdc版本冲突加上LD_LIBRARY_PATH后被优先加载了。5. 常见问题与避坑实录都是真金白银踩出来的5.1 unknown module(s) in qt: serialport这是群里被问烂的问题。现象是在.pro文件里写了QT serialport但qmake报错Project ERROR: Unknown module(s) in QT: serialport。根因很简单Qt 5.12.12默认没有编译SerialPort模块它属于qtserialport独立模块需要单独下载源码编译。解决方案# 下载对应版本的qtserialport源码注意分支要匹配5.12.12 git clone --branch v5.12.12 https://github.com/qt/qtserialport.git cd qtserialport # 需要先设置好PATH确保用的是我们刚编译的qmake export PATH/opt/Qt5.12.12/bin:$PATH # 直接编译并安装 mkdir build cd build qmake ../qtserialport.pro make -j4 sudo make install安装完成后再次执行qtchooser -print-env或者直接用/opt/Qt5.12.12/bin/qmake然后在Qt Creator里选择正确的Qt版本重新构建项目serialport模块就出现了。5.2 Qt::WA_TranslucentBackground和XCB的“王八蛋”黑屏问题还有一个我在银河麒麟上经常遇到的问题程序在Ubuntu上运行正常但到了麒麟V10上窗口背景全黑或者半透明失效。原因还是xcb插件在系统合成器兼容性上的老问题。我当时的解法有两条路如果应用可以接受非半透明那就删掉所有和WA_TranslucentBackground相关的代码自适应就好如果必须支持半透明那么需要检查平台是否开启了GPU加速合成。试着在启动程序前设置环境变量export QT_XCB_FORCE_SOFTWARE_OPENGL1 ./myappQT_XCB_FORCE_SOFTWARE_OPENGL1会强制Qt使用软件OpenGL渲染虽然性能有一定损失但能有效避免国产显卡驱动和Qt现代渲染引擎之间的兼容性黑屏问题。5.3 cmake找不到Qt5Config.cmake很多项目现在用CMake而不是qmake。编译完Qt后使用CMake配置项目时常出现CMake Error: The following variables are used in this project, but they are set to NOTFOUND. Qt5_DIR-NOTFOUND处理方法是在CMakeLists.txt里设置CMAKE_PREFIX_PATHcmake -DCMAKE_PREFIX_PATH/opt/Qt5.12.12 ..或者在系统环境变量里加export CMAKE_PREFIX_PATH/opt/Qt5.12.12:$CMAKE_PREFIX_PATH如果还报错检查/opt/Qt5.12.12/lib/cmake目录下有无对应模块的文件夹。如果缺失大概率是configure时使用-nomake examples之类的参数意外砍掉了一些模块可以补编译模块或重新configure。5.4 ARM64下OpenGL链接失败的怪状在ARM64麒麟上经常出现GLEW或者OpenGL的相关函数链接不上。这类问题的根源在于很多ARM设备上系统只装了libGLESv2.so而没有完整版本的libGL.so。而Qt 5.12.12默认会去链接桌面级OpenGL库。解决方法是在configure阶段明确指定ES2模式../configure -opengl es2 ...如果项目本身依赖的是libGL.so而系统只提供libGLESv2.so那么可以做一个软链接骗过链接器sudo ln -s /usr/lib/aarch64-linux-gnu/libGLESv2.so.2 /usr/lib/aarch64-linux-gnu/libGL.so但这个方法不一定总灵验最稳妥的还是确认项目本身能适配OpenGL ES。国产显卡的GL驱动实现一般都很不完善用ES2模式反而更稳定。6. 编译产物压缩与发布部署从开发机到终端的最后一步6.1 裁剪Qt库体积让部署包缩小一半编译完成的Qt全量安装体积至少在3GB以上。如果直接拷贝到终端设备既不现实也不体面。这里推荐用linuxdeployqt或者windeployqt的Linux版本来精简发布内容。我的简化流程是先在你开发机上编译出一个完成的应用可执行文件然后创建一个目录结构mkdir -p AppDir/usr/bin mkdir -p AppDir/usr/lib # 拷贝可执行程序 cp /path/to/your/app AppDir/usr/bin/ # 递归拷贝Qt所需的动态库和插件 linuxdeployqt AppDir/usr/bin/app -qmldir/your/qml/path -bundle-non-qt-libs不过需要注意linuxdeployqt对新版Qt 5.12支持得不错但偶尔会漏拷一些平台插件尤其是libqxcb.so。我每次都会在部署包里手动检查一次plugins/platforms目录是否存在且包含libqxcb.so。6.2 用patchelf设置RPATH彻底告别“找不到库”这一步对于发布部署是最关键的。很多时候你把AppDir打包拷到别的机器虽然把Qt库放到了usr/lib下但程序仍然找不到。这是因为程序默认只在LD_LIBRARY_PATH和系统路径下找库。使用patchelf将RPATH设置为相对路径确保无论程序由哪个用户启动、放在什么路径下都能自动找到同目录下的Qt库patchelf --set-rpath $ORIGIN/../lib AppDir/usr/bin/app # 检查是否设置成功 patchelf --print-rpath AppDir/usr/bin/app设置$ORIGIN之后程序会在自己所在目录的相对路径下寻找库这比依赖环境变量要可靠得多。把这个带RPATH的AppDir整个打包成tar.gz分发到目标机器上解压即用完美实现免安装运行。6.3 部署到目标机后快速自检清单每次部署到新机器上我都会花2分钟做一次自检避免远程排查的麻烦# 1. 确认程序能找到库 ldd /path/to/app/usr/bin/app | grep not found # 2. 确认xcb平台插件能被加载 QT_DEBUG_PLUGINS1 /path/to/app/usr/bin/app # 3. 强制软件渲染排除显卡驱动问题 QT_XCB_FORCE_SOFTWARE_OPENGL1 /path/to/app/usr/bin/app如果第1步没输出not found第2步没有报Failed to load platform plugin第3步能弹出正常窗口那这个包就可以放心交付了。7. 个人实测心得与后续可扩展方向在银河麒麟上编译Qt这趟浑水我算是蹚了个遍。回头总结最核心的心得就三条第一依赖不牢地动山摇——编译前用apt把依赖清点清楚比编译时缺啥补啥高效十倍第二什么时候都要留个后手——编译参数里别写死-marchxxx发布包设置好RPATH永远用兼容性换稳定性第三软件渲染是国产平台的救命稻草——遇到显示异常先上QT_XCB_FORCE_SOFTWARE_OPENGL1再谈别的。最后再分享一个小技巧。如果你在编译过程中反复调整configure参数可以创建一个编译脚本固化下来方便以后自动化构建。我会把前面提到的所有环境变量、configure和make命令整合到一个build_qt.sh里哪怕是新换一台机器只要脚本跑一遍就能得到同样环境、同样版本的Qt构建产物。这就属于一劳永逸的事了。如果后面有空我打算继续写一篇《银河麒麟下Qt应用集成第三方库OpenCV/Halcon的踩坑笔记》毕竟光一个Qt主库跑通不算什么真正的国产化之痛都在这些边边角角的集成细节里。