1. 为什么要在麒麟ARM系统上折腾离线Qt环境如果你正在读这篇文章大概率是遇到了这样一个场景手头有一台搭载麒麟操作系统的ARM架构机器可能是飞腾、鲲鹏或者麒麟芯片的终端这台机器处在内网环境里没有外网连接但你需要在上面跑一个基于Qt的图形界面程序。这时候你会发现平时在x86架构的Ubuntu上一条apt install qt5-default就能搞定的事情到了这里完全行不通——没有网络没有现成的ARM架构Qt二进制包甚至连编译工具链都得自己想办法搬进去。我自己第一次遇到这个需求的时候是在一个国产化替代的项目现场。客户给了一台银河麒麟V10的ARM机器要求在上面部署一套Qt开发的监控界面。当时我天真地以为不就是装个Qt嘛能有多难。结果从下午两点折腾到晚上十一点中间踩了无数坑才终于把环境跑通。这篇文章就是把我那天的经验完整地梳理出来从依赖包的下载、传输、安装到编译配置、环境变量设置再到各种稀奇古怪的报错处理全部讲清楚。麒麟ARM系统离线安装Qt环境这件事核心难点其实不在Qt本身而在于三个字依赖链。ARM架构的软件包生态远不如x86丰富很多在x86上理所当然存在的库在ARM上要么版本不对要么根本没有预编译包。再加上麒麟系统基于国产化的需求做了一些定制包管理器的源配置、系统库的路径都可能和标准Linux发行版有差异。所以整个安装过程更像是在做一道精密的外科手术每一步都得小心翼翼。这篇文章适合谁看如果你是有一定Linux基础需要在国产化平台上做Qt开发的工程师那这篇内容可以直接拿来当操作手册用。如果你是对ARM架构交叉编译感兴趣的技术爱好者里面关于依赖分析和工具链配置的思路同样有参考价值。即使你之前没接触过麒麟系统只要跟着步骤走也能把环境搭起来。注意本文所有操作均基于合法合规的软件开发场景涉及的软件包均来自官方或开源渠道。请确保你的使用场景符合相关软件许可协议。2. 动手之前环境确认与整体方案设计2.1 先搞清楚你的机器到底缺什么在开始下载任何东西之前有一件事必须先做确认目标机器的系统版本、架构和已有的库。这一步看起来简单但我见过太多人跳过这步结果下载了一堆不匹配的包白白浪费时间。登录到麒麟系统上打开终端依次执行下面几条命令# 查看系统版本信息 cat /etc/os-release # 查看内核版本和架构 uname -a # 查看CPU架构详情 lscpu | grep -E Architecture|Model name # 查看已安装的Qt相关包 dpkg -l | grep -i qt # 查看系统已安装的图形库依赖 ldconfig -p | grep -E libGL|libX11|libxcb以银河麒麟V10为例典型的输出会显示aarch64架构系统基于Debian体系使用dpkg和apt作为包管理工具。这一点很关键因为它决定了你后续下载依赖包时应该找.deb格式的文件而不是.rpm。我当时的机器上已经预装了一些Qt5的运行库版本是5.12.8但开发环境头文件、qmake、编译工具完全没有。这就意味着我需要额外安装qtbase5-dev、qt5-qmake、qtcreator等开发包。如果你的机器上连运行库都没有那需要安装的包会更多。2.2 离线安装的整体思路离线安装Qt环境本质上就是解决一个“搬运”问题把原本需要从网络仓库下载的软件包提前在另一台有网络的机器上下载好然后通过U盘或内网传输到目标机器上安装。整个流程可以拆成四个阶段第一阶段依赖分析。在目标机器上确定需要哪些包包括Qt的核心库、开发工具、编译器等。这一步的关键是搞清楚包与包之间的依赖关系因为离线安装时apt无法自动解决依赖缺一个包就会导致安装失败。第二阶段包下载。在一台有网络的、架构相同的机器上下载所有需要的.deb包。这里有个坑下载机的架构必须和目标机一致都是ARM64。如果你在x86机器上下载ARM的包需要用apt-get download配合正确的源或者直接用wget从镜像站拉取。第三阶段传输与安装。把下载好的包传到目标机器用dpkg -i批量安装。安装顺序很重要被依赖的包要先装。第四阶段环境配置与验证。设置PATH、LD_LIBRARY_PATH等环境变量编译一个测试程序验证环境是否可用。2.3 方案选型为什么选择系统包而非源码编译在离线环境下安装Qt其实有两条路可走一是下载系统包.deb安装二是下载Qt源码在目标机器上编译。我两种方式都试过最终推荐系统包方案原因如下。源码编译Qt在ARM机器上非常耗时。我试过在一台飞腾FT-2000/4的机器上编译Qt 5.15.2完整编译花了将近四个小时而且中间因为缺少各种开发库比如libxcb-xinerama0-dev、libfontconfig1-dev反复中断。更麻烦的是编译过程中如果缺少某个可选依赖Qt会自动跳过对应的模块导致最终编译出来的Qt功能不全比如缺少serialport模块或者sqlite驱动。系统包方案的优势在于包的依赖关系已经被维护者处理好了你只需要按顺序安装即可安装速度快通常几分钟就能搞定版本经过发行版测试稳定性有保障。缺点是有时候系统源里的Qt版本比较老比如麒麟V10默认源里是Qt 5.12.8如果你需要Qt 5.15的新特性可能得另想办法。实操心得如果你的项目对Qt版本没有硬性要求优先用系统包。如果确实需要特定版本可以考虑在x86机器上交叉编译Qt然后把编译好的库和工具链整体搬到ARM机器上。交叉编译的环境配置我会在后面的章节详细讲。3. 依赖包下载在联网机器上把东西备齐3.1 准备一台同架构的联网机器下载ARM64的.deb包最省事的办法是找一台同样运行麒麟系统的ARM机器并且这台机器能连外网。如果手头没有这样的机器退而求其次可以用一台ARM架构的服务器装个Ubuntu只要架构是aarch64就行因为.deb包本身是架构相关的但发行版之间的包通常可以通用前提是glibc版本兼容。如果连ARM机器都没有那就只能在一台x86机器上通过apt-get download配合--arch参数来下载ARM包。具体做法是修改/etc/apt/sources.list添加ARM64的源然后执行# 在x86机器上下载ARM64架构的包 apt-get download qtbase5-dev:arm64不过这种方式有时候会因为依赖解析的问题失败不如直接在ARM机器上操作来得稳妥。3.2 用apt-rdepends分析完整依赖树确定需要哪些包最可靠的方法是使用apt-rdepends工具。这个工具可以递归地列出一个包的所有依赖。在联网的ARM机器上执行# 安装apt-rdepends工具 sudo apt install apt-rdepends # 分析qtbase5-dev的完整依赖树 apt-rdepends qtbase5-dev | grep -v ^ | sort -u qt_deps_list.txt这样得到的qt_deps_list.txt文件里包含了所有需要的包名。但要注意这里面有些包是系统已经预装的不需要重复下载。你可以在目标麒麟机器上执行dpkg -l导出已安装包列表然后做差集运算筛选出真正需要下载的包。我当时的做法更直接一些先在目标机器上尝试安装看apt报缺哪些包然后把这些包名记下来到联网机器上下载。虽然笨了点但很有效。具体操作是在目标机器上执行# 模拟安装查看缺失的依赖 sudo apt install --simulate qtbase5-dev qt5-qmake qtcreator--simulate参数会让apt只做模拟不实际安装但会输出所有需要下载的包。把输出里的包名提取出来就是你的下载清单。3.3 批量下载deb包的脚本手动一个个下载太慢写个脚本批量处理。在联网的ARM机器上把包名列表存到pkg_list.txt里每行一个包名然后执行#!/bin/bash # download_debs.sh # 批量下载deb包及其依赖 DOWNLOAD_DIR./qt_offline_packages mkdir -p $DOWNLOAD_DIR cd $DOWNLOAD_DIR while read -r pkg; do echo 正在下载: $pkg apt-get download $pkg 21 | tee -a download.log done ../pkg_list.txt echo 下载完成共下载 $(ls -1 *.deb | wc -l) 个包这个脚本会把所有.deb文件下载到qt_offline_packages目录下。下载完成后把这个目录整体拷贝到U盘或者通过内网传到目标机器上。注意apt-get download只下载指定的包本身不会自动下载依赖。所以你的pkg_list.txt必须包含所有依赖包。这就是为什么前面要用apt-rdepends或者--simulate来生成完整的包列表。3.4 那些容易被遗漏的关键包根据我的经验下面这些包在离线安装Qt时最容易被漏掉单独列出来提醒一下包名作用遗漏后果libqt5core5aQt核心库所有Qt程序无法运行libqt5gui5Qt GUI库图形界面无法显示libqt5widgets5Qt Widgets模块窗口程序编译失败libqt5serialport5串口模块串口通信功能不可用qtbase5-devQt基础开发头文件无法编译Qt程序qt5-qmakeqmake构建工具无法生成Makefilelibqt5sql5-sqliteSQLite数据库驱动数据库功能报错libxcb-xinerama0XCB扩展库Qt程序启动时报xcb错误libfontconfig1字体配置库界面字体显示异常libgl1-mesa-devOpenGL开发库需要OpenGL的程序编译失败特别是libxcb-xinerama0这个包我在三个不同的项目现场都遇到过它缺失导致Qt程序无法启动的问题。报错信息通常是qt.qpa.plugin: Could not load the Qt platform plugin xcb看起来像是Qt插件的问题实际上根源是系统缺少XCB的某个扩展库。4. 离线安装实操从dpkg到环境变量4.1 批量安装deb包的正确姿势把下载好的包传到目标机器后进入包所在目录执行批量安装。这里有个技巧直接用dpkg -i *.deb可能会因为安装顺序问题报依赖错误更好的做法是用dpkg的--force-depends参数先强制安装所有包然后再用apt install -f修复依赖关系。# 进入包目录 cd /path/to/qt_offline_packages # 批量安装所有deb包 sudo dpkg -i --force-depends *.deb # 修复可能的依赖问题 sudo apt install -f # 如果apt install -f因为离线无法工作手动按依赖顺序重新安装 # 通常的顺序是基础库 - 核心库 - 开发工具如果apt install -f因为离线环境无法下载缺失的包而失败那就需要手动处理。先用dpkg -l | grep -v ^ii查看哪些包处于非正常安装状态然后根据报错信息逐个安装缺失的依赖。我自己的习惯是先把所有包按名称排序然后分批安装先装libqt5core5a、libqt5gui5这类基础库再装qtbase5-dev这类开发包最后装qtcreator这类工具。这样虽然麻烦一点但成功率更高。4.2 验证Qt是否安装成功安装完成后用几个简单的命令验证# 查看qmake版本 qmake --version # 查看Qt库文件位置 ls /usr/lib/aarch64-linux-gnu/libQt5Core.so* # 查看Qt插件目录 ls /usr/lib/aarch64-linux-gnu/qt5/plugins/ # 查看已安装的Qt模块 ls /usr/lib/aarch64-linux-gnu/qt5/mkspecs/modules/如果qmake --version能正常输出版本信息说明Qt的基本环境已经就绪。如果提示qmake: command not found那可能是qt5-qmake包没有安装成功或者qmake的可执行文件不在PATH里。可以用dpkg -L qt5-qmake | grep qmake找到它的实际位置。4.3 环境变量配置让Qt工具链随手可用系统包安装的Qtqmake通常位于/usr/bin/qmake或者/usr/lib/qt5/bin/qmake。如果不在PATH里需要手动添加。编辑~/.bashrc或者/etc/profile加入以下内容# Qt环境变量配置 export QT_DIR/usr/lib/aarch64-linux-gnu/qt5 export PATH$QT_DIR/bin:$PATH export LD_LIBRARY_PATH$QT_DIR/lib:$LD_LIBRARY_PATH export QT_PLUGIN_PATH$QT_DIR/plugins export QML2_IMPORT_PATH$QT_DIR/qml保存后执行source ~/.bashrc使配置生效。这里重点说一下LD_LIBRARY_PATH和QT_PLUGIN_PATH这两个变量。LD_LIBRARY_PATH告诉系统去哪里找Qt的动态库如果设置不对运行Qt程序时会报error while loading shared libraries: libQt5Core.so.5: cannot open shared object file。QT_PLUGIN_PATH告诉Qt去哪里找平台插件如果设置不对程序启动时会报This application failed to start because no Qt platform plugin could be initialized。实操心得有些麒麟系统上/etc/ld.so.conf.d/目录下已经有Qt库的配置这种情况下不需要额外设置LD_LIBRARY_PATH。可以用ldconfig -p | grep Qt5确认系统是否能找到Qt库。如果能找到就不要重复设置避免路径冲突。4.4 编译一个测试程序验证环境环境变量配好后写一个最简单的Qt程序测试一下。创建一个test_qt.cpp文件#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Qt on Kylin ARM works!); label.show(); return app.exec(); }然后创建一个test_qt.pro文件QT widgets SOURCES test_qt.cpp执行编译qmake test_qt.pro make ./test_qt如果弹出一个显示“Qt on Kylin ARM works!”的窗口恭喜你环境搭建成功。如果编译时报错根据错误信息对照后面的问题排查章节处理。5. 常见问题与排查技巧实录5.1 Qt程序启动报xcb插件加载失败这是离线安装Qt后最常见的问题没有之一。报错信息通常是qt.qpa.plugin: Could not load the Qt platform plugin xcb in even though it was found. This application failed to start because no Qt platform plugin could be initialized.这个问题的根源是系统缺少XCB相关的库。Qt的xcb平台插件依赖一系列XCB扩展库包括libxcb-xinerama0、libxcb-icccm4、libxcb-image0、libxcb-keysyms1、libxcb-randr0、libxcb-render-util0、libxcb-shape0、libxcb-xkb1、libxkbcommon-x11-0等。在离线环境下这些库可能没有随Qt一起安装。排查方法是先用ldd查看xcb插件的依赖ldd /usr/lib/aarch64-linux-gnu/qt5/plugins/platforms/libqxcb.so | grep not found输出中所有显示not found的库就是缺失的。把这些库名记下来到联网机器上下载对应的.deb包传到目标机器安装即可。5.2 编译时报“Unknown module(s) in QT: serialport”这个报错说明你的Qt环境里没有安装serialport模块。在离线环境下serialport模块需要单独安装libqt5serialport5-dev包。这个包不在qtbase5-dev的依赖列表里很容易被遗漏。解决方法是在联网机器上下载libqt5serialport5-dev和libqt5serialport5两个包传到目标机器安装。安装完成后用ls /usr/lib/aarch64-linux-gnu/qt5/mkspecs/modules/ | grep serialport确认模块是否注册成功。类似的模块缺失问题还可能出现在sql、multimedia、websockets等模块上。排查方法是一样的在.pro文件里用到的QT xxx对应的模块必须有对应的-dev包安装。5.3 qmake生成的Makefile里路径不对有时候qmake生成的Makefile里引用的头文件路径或者库路径是错的导致make时报“找不到头文件”或者“找不到库”。这种情况通常是因为系统里存在多个Qt版本qmake指向了错误的那个。用which qmake和qmake -query查看当前qmake的实际路径和配置信息。如果发现qmake指向的不是你期望的版本可以通过修改PATH环境变量的顺序来切换或者直接用绝对路径调用正确的qmake。# 查看qmake的详细配置 qmake -query # 如果系统有多个qmake用绝对路径指定 /usr/lib/aarch64-linux-gnu/qt5/bin/qmake test_qt.pro5.4 常见问题速查表问题现象可能原因解决方法qmake: command not foundqt5-qmake未安装或不在PATH安装qt5-qmake添加PATH无法加载xcb插件缺少XCB扩展库安装libxcb-xinerama0等库Unknown module serialport缺少serialport开发包安装libqt5serialport5-dev找不到libQt5Core.so.5LD_LIBRARY_PATH未设置设置LD_LIBRARY_PATH或运行ldconfig编译时找不到QApplication头文件qtbase5-dev未安装安装qtbase5-dev程序运行字体显示为方块缺少中文字体安装fonts-wqy-zenhei等字体包make报错“cannot find -lGL”缺少OpenGL开发库安装libgl1-mesa-devQt Creator无法启动缺少依赖库用ldd检查并补齐依赖5.5 几个容易踩的坑和独家技巧第一个坑是包版本不匹配。麒麟V10的系统源里Qt的版本是5.12.8但如果你从Ubuntu的源里下载了5.15.2的包安装后可能会出现库版本冲突。表现是程序能编译但运行时报symbol lookup error。解决方法是确保所有Qt相关的包都来自同一个源版本号一致。第二个坑是依赖包的依赖。你用apt-rdepends分析出来的依赖树可能只覆盖了直接依赖和二级依赖更深层的依赖可能被遗漏。我的做法是在目标机器上先装一遍把所有报错信息收集起来然后针对性地补充下载。虽然多花点时间但能确保不漏包。第三个技巧是用dpkg-deb -c查看包内容。有时候你不确定某个包是否已经安装或者想确认某个文件属于哪个包可以用这个命令# 查看deb包的内容列表 dpkg-deb -c package.deb # 查看某个已安装包的文件列表 dpkg -L package_name # 查看某个文件属于哪个包 dpkg -S /path/to/file第四个技巧是保留一份完整的离线包仓库。把这次下载的所有.deb包整理好按类别放到不同的目录里下次再遇到类似需求时直接拿来用。我自己的离线包仓库里Qt相关的包大概有80多个覆盖了从Qt 5.9到5.15的多个版本省去了很多重复下载的时间。6. 进阶话题交叉编译与自定义Qt版本6.1 什么时候需要交叉编译系统包方案虽然简单但有两个局限一是Qt版本受限于系统源二是无法定制Qt的编译选项。如果你的项目需要Qt 5.15的新特性或者需要裁剪Qt的功能以减小体积那就得考虑交叉编译。交叉编译的思路是在一台性能较强的x86机器上用ARM交叉编译工具链编译Qt源码生成ARM架构的库和工具然后整体搬到目标机器上。这样做的好处是编译速度快x86机器性能通常比ARM开发板强而且可以完全控制编译选项。6.2 交叉编译工具链的选择ARM交叉编译工具链有几种选择Linaro的gcc-linaro-aarch64-linux-gnu、ARM官方的arm compiler、以及各芯片厂商提供的定制工具链。对于麒麟系统推荐使用Linaro的GCC工具链兼容性最好。下载工具链后解压到/opt目录然后把bin目录加入PATHexport PATH/opt/gcc-linaro-aarch64-linux-gnu/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- export ARCHarm64验证工具链是否可用aarch64-linux-gnu-gcc --version6.3 配置Qt的交叉编译选项下载Qt源码后进入源码目录执行configure脚本。关键的配置参数包括./configure \ -prefix /opt/qt5-arm \ -opensource \ -confirm-license \ -release \ -xplatform linux-aarch64-gnu-g \ -no-opengl \ -no-xcb \ -qt-libjpeg \ -qt-libpng \ -qt-zlib \ -nomake examples \ -nomake tests \ -skip qtwebengine这里重点解释几个参数-xplatform指定目标平台的mkspec需要根据你的工具链名称选择-no-opengl和-no-xcb在无图形界面的嵌入式场景下可以去掉但如果需要GUI就得保留-skip qtwebengine跳过WebEngine模块的编译因为这个模块编译极其耗时且依赖复杂。配置完成后执行make -j$(nproc)开始编译。编译时间取决于机器性能x86机器上通常1到2小时可以完成。6.4 把交叉编译的Qt部署到目标机器编译完成后执行make installQt会被安装到-prefix指定的目录。把这个目录整体打包传到目标机器上然后设置环境变量指向这个目录export QT_DIR/opt/qt5-arm export PATH$QT_DIR/bin:$PATH export LD_LIBRARY_PATH$QT_DIR/lib:$LD_LIBRARY_PATH export QT_PLUGIN_PATH$QT_DIR/plugins这种方式的优点是Qt版本和编译选项完全可控缺点是需要自己处理所有依赖库的交叉编译。比如Qt的sqlite驱动依赖libsqlite3你需要先用交叉工具链编译libsqlite3再编译Qt的sqlite插件。依赖链越长工作量越大。实操心得交叉编译Qt时建议先用-no-opengl -no-xcb的最小配置编译一遍确认工具链没问题再逐步添加需要的模块。一次性开启所有模块很容易因为某个依赖缺失导致配置失败排查起来很麻烦。7. 写在最后一些个人体会折腾麒麟ARM系统的离线Qt环境最深的体会就是准备工作比安装本身重要得多。我见过太多人拿到机器就开始装装到一半发现缺包然后又得回到联网机器上重新下载来回折腾。如果一开始就花半小时把依赖分析清楚把包下载齐全后面的安装可能十分钟就搞定了。另一个体会是离线环境的包管理要有版本意识。麒麟系统的glibc版本、Qt版本、编译器版本之间是有对应关系的。比如麒麟V10 SP1的glibc是2.28如果你下载的Qt包是在glibc 2.31环境下编译的安装后可能会报GLIBC_2.31 not found。所以下载包的时候尽量选择和目标系统版本接近的源。最后分享一个我自己的习惯每次做完离线部署我都会把这次用到的所有包、配置文件、操作步骤整理成一个文档存在项目的docs目录里。下次再遇到类似需求直接翻文档就行不用重新踩一遍坑。这个习惯帮我省下了大量重复劳动的时间也推荐给你。