最近在给客户交付一套基于Qt5的界面程序,目标平台是银河麒麟V10的ARM版,而且是完全的物理离线环境。折腾了整整两天,光是gcc和Qt5的依赖问题就来回撞了好几次墙。一边翻系统源一边手动补包,最后总算是把从编译器到Qt Creator的整条链路理顺了。这里把完整流程和踩过的坑一次性整理出来,给同样需要在银河麒麟ARM版上离线做Qt5开发的朋友做个参考。
这篇内容主要面向三类人:刚拿到麒麟ARM设备、准备从零搭建Qt环境的嵌入式开发者;需要在信创/隔离网环境下交付项目的工程团队;以及被“Qt5无法拖拽文件”“gcc升级后还是旧版本”这类问题折磨过、正在到处搜索解决方案的人。你不需要提前准备太多,只要有一台银河麒麟V10 ARM设备、一个可用的离线安装包来源(系统ISO镜像或另一台联网机器),按照下面的步骤就能把环境完整搭起来。
1. 开始之前:先搞清楚你拿到的到底是个什么环境
1.1 系统版本与CPU架构自查命令
很多人在第一步就走错了方向。拿到机器不问架构、不看系统版本,直接用一个x86世界里的安装包往ARM机器上怼,结果自然是各种not found和cannot execute binary file报错。银河麒麟V10有x86版也有ARM版,同样是“V10”,两者软件生态的安装方式差别非常大。
动手装任何东西之前,先执行下面三组命令,把系统的家底盘清楚:
# 查看操作系统具体版本和代号 cat /etc/os-release # 查看内核版本和架构 uname -a # 查看CPU架构详细信息 lscpu | grep Architecture我在实际项目里拿到的是麒麟V10的ARM版,uname -m输出为aarch64,这说明系统跑的是64位ARM指令集。注意,aarch64和armv7l虽然都叫ARM,但前者是64位、后者是32位,软件包完全不通用。如果你是armv7l,下面讲到的arm64版deb包也用不了,需要单独找armhf版本。
另外用cat /etc/os-release看清楚系统基于哪个上游版本。银河麒麟V10有的基于Ubuntu,有的基于Debian,虽然包管理工具都是apt/dpkg,但软件源配置和依赖版本会有差异。我看到VERSION_CODENAME是focal,就知道它基本兼容Ubuntu 20.04的软件包,这为后面从联网机器拉取deb包提供了重要依据。
1.2 判断离线程度:三种网络形态决定安装策略
“离线安装”这个词其实很笼统,不同离线程度对应完全不同的操作路线。我先根据现场情况把离线环境分为三类,你对照自己手头的机器来判断:
- 完全物理隔离:机器不出网,没有内网源,只有一个U盘或光盘可用。这种情况最麻烦,所有软件包都得提前在外面准备好。
- 有内网软件源:机器能访问公司内部的apt源,只是不能上外网。这种情况最简单,直接改一下
sources.list指向内网源,apt install基本畅通无阻。 - 无源但有系统ISO镜像:机器不能联网,但是你有银河麒麟的安装光盘或ISO文件。这中等情况可以通过挂载ISO搭建一个本地源,装gcc、基础Qt5库都没问题。
我这次的现场属于第一种,机器连内网源都没有,只有一个清明上河图一样的ISO镜像。所以下面的方案会以“有ISO”和“无ISO”两条线来讲,一条是挂载ISO搭本地源,另一条是借助联网机器打包deb再拷贝进场。
先说一个直觉判断:如果只靠U盘随便拷贝几个.run安装包进去就想搞定Qt5开发环境,基本不可行。Qt5开发依赖庞大,gcc需要配套的libc6-dev、binutils,Qt5需要libxcb、libGL、fontconfig等一堆运行时库,缺一个都能让你编译失败或者程序起不来。正确思路是“准备一个完整可用的软件包集合”,而不是“拷贝一个安装程序进去”。
2. 离线环境下的gcc:先从依赖地狱里爬出来
2.1 招式一:挂载ISO搭本地源,能救80%的场
银河麒麟V10的ISO镜像里带了一部分常用软件包,虽然不一定有完整的Qt5开发套件,但gcc、g++、make、binutils这些基础编译工具大概率齐。如果你手头有ISO文件或光盘,我建议优先用这个方案,因为它最省事、最不容易出依赖遗漏。
先把ISO文件拷进系统(或用光驱挂载),然后执行:
sudo mkdir -p /mnt/kylin sudo mount -o loop /path/to/Kylin-Desktop-V10-ARM.iso /mnt/kylin挂载完以后,在/etc/apt/sources.list.d/下新建一个本地源文件:
echo "deb [trusted=yes] file:///mnt/kylin focal main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/kylin-local.list sudo apt update这里有个坑要提醒:[trusted=yes]参数一定要加,因为本地源没有Release签名,不加的话apt update会直接拒绝使用,报NO_PUBKEY或InRelease相关错误。另外如果ISO解压出来的目录结构不是标准的Ubuntu源结构(比如只有/pool和/dists),你需要确认dists目录下确实存在focal这个代号,如果不是,就把sources.list里的focal改成实际的目录名。
搞定本地源之后,安装编译器就变成一条命令的事:
sudo apt install gcc g++ make装完验证一下:
gcc --version g++ --version这个方法能覆盖绝大多数“只需要gcc能用来编译”的场景。如果你做的是纯Qt Widgets项目,后面再补Qt5的包也能通过这个源装上一部分。但如果ISO里没有你要的包,就得用第二种方法。
2.2 招式二:同架构联网机器打包deb,一次拉齐依赖树
ISO源覆盖不了的情况太常见了。麒麟V10的ISO镜像体积有限,里面不可能把所有Qt5开发包都塞进去,这个时候就需要借助另一台“同架构、同系统版本”的联网机器,把目标包和它的整个依赖树全部下载成deb,再拷贝到离线机上安装。
先找一台能联网的aarch64机器(如果实在没有,用虚拟机跑一个Ubuntu ARM版也可以,但系统版本尽量贴近),配置好麒麟源或Ubuntu源,然后执行:
# 先安装一个递归查询依赖的工具 sudo apt install apt-rdepends # 查看目标包会引入多少依赖,心里有个数 apt-cache depends gcc g++ make # 使用--print-uris把所有deb包的下载地址打出来 apt-get install --reinstall --print-uris -y gcc g++ make libc6-dev--print-uris会把每个包的实际下载链接列出来,你可以按这个清单逐个下载。但更省事的办法是直接用apt-get download配合递归依赖列表,一条命令拉全部:
sudo apt-get install -y --print-uris gcc g++ make | grep "^\'http" | awk '{print $1}' | tr -d "'" > /tmp/deb_urls.txt然后借助wget逐个下载,或者直接用下面这个更实用的命令把deb包统一放到一个目录:
mkdir /tmp/offline_debs && cd /tmp/offline_debs # 方式A:如果你能联网且使用apt,直接下载指定包 apt-get download $(apt-rdepends gcc g++ make libc6-dev 2>/dev/null | grep -v "^ " ) # 方式B:如果依赖关系太乱,就直接手工把关键包下载下来再逐个试 apt-get download gcc g++ make libc6-dev binutils linux-libc-dev cpp注意apt-rdepends输出里那些带Depends:缩进的部分才是依赖包名,直接取第一列即可。下载完把整个offline_debs目录拷贝到U盘,拿到离线机上。
离线机上安装deb包时,我最开始直接sudo dpkg -i *.deb,结果一团糟。因为dpkg按照文件名字母顺序安装,不会主动解析依赖顺序,经常装到一半报某个依赖还没有配置。正确的做法是分两步:先忽略依赖顺序套一层,再用apt-get -f install来自动修正。
cd /tmp/offline_debs sudo dpkg -i --force-depends *.deb sudo apt-get -f install -y--force-depends可以跳过依赖检查先把所有包装上,最后apt-get -f install会自动把缺失的依赖关系补齐。实测这个方法比手工按依赖顺序不停dpkg -i要靠谱得多。如果最后还报缺包,那说明你在打包阶段漏了依赖,回到联网机器上用apt-cache depends逐个对照补一次就好。
2.3 gcc装完还是旧版本?PATH和软链的坑
gcc装完以后,很多人的第一反应是gcc --version验证,结果发现版本号和预期不符,甚至还是系统自带的旧版本。这个问题我见过太多次了,一般来说有三个原因:
第一,系统里本来就带了一个旧版gcc,新装的gcc被装到了不同路径。用which gcc看一下实际调用的路径,如果输出的是/usr/bin/gcc,而新版本装到了/usr/local/bin/gcc,说明是PATH顺序问题。执行echo $PATH看一下/usr/local/bin和/usr/bin哪个靠前。
第二,安装后系统存在多个gcc版本,可以通过update-alternatives手动管理:
sudo update-alternatives --config gcc sudo update-alternatives --config g++这个命令会把系统里所有gcc版本列出来,你选择默认要用的那个即可。
第三,环境变量没有重新加载。source /etc/profile或者重新登录一次终端,不要用旧的bash窗口去验证,很多时候版本没变就是这个低级原因。
还有一个更隐蔽的坑:ARM机器上如果libc6-dev没有正确安装,gcc即使能运行,编译任何C程序都会报fatal error: stdio.h: No such file or directory。这是因为gcc本身正常,但标准头文件和glibc开发文件缺失。遇到这种报错不用怀疑gcc装坏了,第一反应应该是补libc6-dev。
3. ARM版Qt5离线安装的思路和实操
3.1 为什么在ARM上不要执着于Qt官网的.run包
很多第一次接触ARM Linux Qt开发的人,第一反应就是去Qt官网下载安装包。这里我必须泼一盆冷水:Qt官方的Linux离线安装包,绝大多数只有x86_64版本,官网那个qt-opensource-linux-x64-5.14.2.run在ARM机器上根本运行不了。
Qt官方提供的是在线安装器,确实支持aarch64,但离线环境用不了在线安装器。官方离线.run安装包又只面向桌面x86架构,ARM版几乎没有对应物。所以“在ARM上找Qt官方离线包”这件事本身就是一个死胡同,不要在这个方向上浪费时间。
在银河麒麟ARM版上装Qt5,最靠谱的路线还是走系统包管理器。麒麟是基于Debian系的系统,软件源里有现成的Qt5开发套件。虽然版本可能不是最新的(比如5.12、5.15之类),但对大多数工业界面、嵌入式应用来说已经完全够用,而且和系统库的兼容性最好,不需要你手动去配置运行时库路径。
3.2 用apt/离线deb装好qtbase5-dev和全套工具链
如果你之前搭好了本地源,或者能访问内网源,直接一条命令装齐:
sudo apt install qtbase5-dev qtbase5-dev-tools qttools5-dev-tools qtcreator libqt5svg5-dev libgl1-mesa-dev libxkbcommon-dev这里几个包的作用我分别说明一下:
qtbase5-dev:Qt5的核心开发包,包含了QtCore、QtGui、QtWidgets这几个主力模块的头文件和链接库,没有它你连#include <QWidget>这关都过不去。qtbase5-dev-tools:提供moc、uic、rcc这些Qt专用工具。Qt的元对象系统依赖moc生成代码,缺了它编译任何带Q_OBJECT的类都会报错。qttools5-dev-tools:提供designer、linguist、assistant等辅助工具,如果你想在可视化界面里拖控件,这个包必须装。qtcreator:Qt开发IDE。libqt5svg5-dev:SVG格式支持,做界面设计经常用到。libgl1-mesa-dev和libxkbcommon-dev:这两个是很多Qt5程序编译时的隐性依赖。Qt的GUI底层依赖OpenGL库和键盘输入处理库,缺了它们编译时可能出现找不到GL/gl.h或者链接失败,虽然报错信息不直接指向Qt,但就是这个原因。
如果你是完全离线、只能用deb包迁移的环境,那就在联网机器上用前面第2.2节的思路,把上面这一串包连同依赖一起拉下来:
mkdir /tmp/qt5_debs && cd /tmp/qt5_debs apt-get download $(apt-rdepends qtbase5-dev qtbase5-dev-tools qttools5-dev-tools qtcreator libqt5svg5-dev libgl1-mesa-dev libxkbcommon-dev 2>/dev/null | grep -v "^ ")这条命令拉下来的包数量会非常多,因为Qt5的运行时库依赖一堆xcb、freetype、fontconfig等图形栈组件。打包时耐心等它下载完,然后整个目录带进离线机,用同样的dpkg -i --force-depends *.deb加apt-get -f install -y处理。
安装完成后,可以用一个简单的命令验证Qt5的核心组件是否就位:
dpkg -l | grep qt5 qmake --version如果qmake --version能输出类似QMake version 3.1 Using Qt version 5.15.2 in /usr/lib/aarch64-linux-gnu/qt5的内容,说明Qt5核心已经安装成功。
3.3 小范围内的选型建议:哪些包必须装,哪些踩过才知道
首次搭建Qt环境的人很容易陷入两个极端:要么只装一个qtbase5-dev就开干,等到具体模块缺了再手忙脚乱补包;要么一口气把qt5-*全部装掉,结果系统里塞满用不上的东西。结合我在麒麟ARM上跑Qt5的实际经验,给你一个参考清单:
必要组件(不装基本跑不起来):
qtbase5-dev:核心开发包qtbase5-dev-tools:moc等编译工具qtcreator:IDE
强烈建议安装(业务开发高频用到):
qttools5-dev-tools:designer和linguistlibqt5svg5-dev:SVG图标和图片libqt5printsupport5和对应的开发包:如果需要做打印功能libqt5serialport5-dev:如果做串口调试工具libqt5networkauth5-dev:如果需要OAuth之类认证流程
按需安装(用到再补):
libqt5charts5-dev:图表绘制libqt5webenginewidgets5:嵌入式浏览器组件(体积巨大,对资源占用也高,非必要别装)libqt5multimedia5:多媒体播放
有一点你可能会踩坑:Qt5的版本和系统桌面环境(UKUI)之间的配合。麒麟的UKUI桌面本身基于Qt开发,系统里通常自带了某种版本的Qt5运行库,但开发头文件很可能没装。你通过apt额外安装qtbase5-dev时,如果版本比系统自带的运行时库版本略新或略旧,一般不会冲突,APT会把依赖关系自动处理好。怕的是你手动下载一个非系统源的Qt编译产物去覆盖,那就容易把桌面环境搞崩。所以在这个平台上的第一原则:能用系统源装就用系统源装,不要自己下载编译包覆盖。
4. Qt Creator装配与构建套件(Kit)配置
4.1 安装Qt Creator,版本别盲目追新
sudo apt install qtcreator装好的版本可能不是Qt官方最新版,这反而不是坏事。Qt Creator和Qt库的版本对应关系比较灵活,新版Creator通常可以兼容多个Qt5小版本,但反向不一定成立——特别老的Creator可能不识别新版本qmake生成的工程格式。
在麒麟这种比较克制的系统源里,Creator版本往往比官方最新版落后一两个大版本,但对开发Qt5 Widgets程序毫无影响。我甚至建议不要自己去Qt官网下载一个很新的Creator二进制包,因为新版Creator对GLIBC版本有要求,ARM版银河麒麟的glibc如果不够新,直接起不来。
安装完以后启动一下,命令行输入:
qtcreator如果启动过程中遇到缺少libxcb-xinerama0之类的报错(这个报错在离线环境很常见,因为Qt Creator依赖xcb的扩展库),先去补装:
sudo apt install libxcb-xinerama0 libxcb-cursor04.2 Kit配置:qmake、gcc、gdb三件套
Qt Creator装上以后,真正的关口是构建套件(Kit)配置。打开菜单“工具” → “选项” → “Kits”(部分中文版叫“构建套件”),你会看到左侧有“Qt版本”、“编译器”、“调试器”等页签。这三个页签必须都配置正确,Kit前面的状态图标才会变成绿色,否则编译会报一堆莫名其妙的问题。
先看“Qt版本”页签:正常情况Qt Creator会自动扫描到系统安装的qmake,路径通常为/usr/lib/aarch64-linux-gnu/qt5/bin/qmake。如果列表为空,点击“添加”,手动选择qmake路径。这里要特别嘱咐一句:看清楚路径里的aarch64-linux-gnu,如果你不小心把x86架构机器的qmake路径配到这里,Creator会直接报“qmake无法执行”。
再看“编译器”页签:点击“添加” → “GCC” → “C++”,在“编译器路径”里填/usr/bin/g++,名称随便写,比如GCC-ARM64。C编译器同理填/usr/bin/gcc。如果Creator自动检测到了,你也可以直接用它检测出来的结果,但要确认架构列显示的是aarch64,不是x86_64。
最后在“调试器”页签添加/usr/bin/gdb。这个步骤很多人会忽略,导致能编译能运行,但一打断点就报“No debugger”。如果系统里没有gdb,先sudo apt install gdb装上。
三个组件都就位以后,回到“Kits”页签新建一个Kit,名称建议写成Kylin-ARM64-Qt5,Qt版本选刚才添加的那个,编译器C/C++都选GCC-ARM64,调试器选gdb,CMake工具如果有需要也可以指定。保存之后看到Kit前面的状态是绿色对勾,说明它已经可用了。如果显示红色警告,先在下方“错误/警告”信息栏查看具体原因,最常见的就是“没有设置Qt版本”和“编译器中C和C++没有匹配到同一套工具”。
4.3 第一个程序跑起来之前,先过一遍这几个细节
新环境上跑第一个Qt程序,最容易在下面四个细节上翻车。我在麒麟ARM上第一次建工程时,就因为在模板选择上多犹豫了一下。
第一,工程模板选择。新建工程时,如果选择“Qt Widgets Application”,cmake和qmake两套构建系统都能用。我建议在麒麟上用qmake,原因很简单:qmake和apt安装的Qt5集成度更好,路径配置更省心,几乎不需要额外指定任何参数。CMake虽然也能用,但要确保Kit里CMake路径配置正确,而且CMake在解析Qt5的Qt5_DIR时容易因为路径不对报错。
第二,编译器基线和-fPIC问题。ARM Linux上编译共享库或可执行文件时,qmake会自动加上-fPIC,一般不需要你手工干预。但如果你用纯命令行gcc去编译Qt程序,而不是通过qmake或Creator,很容易漏掉编译选项导致链接错误。新手阶段建议全程走Creator的构建按钮,不要自己手搓命令行。
第三,程序运行时提示找不到libQt5Widgets.so.5的解决方案。这种情况一般不是安装有问题,而是运行时库路径没被找到。用apt安装的Qt5库都在/usr/lib/aarch64-linux-gnu/目录下,系统的ldconfig默认是能搜到的。如果你手动把编译好的程序拷贝到别的机器上运行,才需要考虑LD_LIBRARY_PATH或rpath问题。在本机调试阶段遇到这个报错,先执行sudo ldconfig刷新缓存。
第四,检查~/.bashrc里是否有人为设置过QTDIR或LD_LIBRARY_PATH环境变量,如果指向了不存在的目录,会影响Qmake和Creator的自动检测。我在一台机器上就遇到有人配了个过时的QTDIR=/opt/Qt5.9.2,导致一切自动检测都失效。检查方法:
echo $QTDIR echo $LD_LIBRARY_PATH有任何残留配置,先注释掉再重启Creator。
5. 现场常见故障排查与实用技能补充
5.1 Qt5无法拖拽文件到界面的原因和对策
“Qt5无法拖拽文件”这个问题,在银河麒麟ARM平台上非常典型,而且网上答案碎片化严重,这里我把排查路径完整捋一遍。
首先要明确:拖拽分为“从系统文件管理器拖文件到你的程序窗口”和“在你的程序内部拖控件”两种。前者依赖系统级拖拽协议,后者依赖Qt内部的事件系统。用户通常抱怨的是前者。
在麒麟V10的UKUI桌面上,Qt5程序无法接收系统文件拖拽,我排查下来主要是这四个原因:
第一,程序没有调用setAcceptDrops(true)。这是最常见的新手错误。QWidget默认不接受drop事件,你必须在构造函数或初始化函数里显式开启:
setAcceptDrops(true);然后在类里重写dragEnterEvent和dropEvent两个函数,并正确调用event->acceptProposedAction(),否则文件拖进来不会有任何响应。
第二,平台插件没有走X11而走了Wayland。Qt5在Linux下默认会根据当前桌面环境选择平台插件,如果系统的图形会话是Wayland,而你的程序没有使用Wayland适配层,拖拽功能就很容易失效。麒麟V10默认的登录会话基本是X11,但如果你通过某些远程软件或切换过显示服务器,就可能在Wayland环境下运行。遇到这种问题,强制指定XCB插件启动:
export QT_QPA_PLATFORM=xcb ./your_app第三,以root身份运行导致拖拽受限。在麒麟上很多用户喜欢sudo ./your_app,但某些桌面组件的拖拽在root权限下会被窗口管理器限制。如果程序非要用root权限,可以先试试不用root运行,看拖拽是否恢复,从而确认是不是权限问题。
第四,事件过滤器拦截了拖拽事件。如果程序里给控件安装了eventFilter,并且在过滤器里吞掉了QEvent::DragEnter或Drop,那无论如何设置setAcceptDrops都不生效。排查方法是暂时关闭事件过滤器,逐一确认。
从我的经验看,第三和第四个原因在真实项目里出现频率很高,尤其是root运行的问题,很多测试工程师会在root环境下得出“拖拽功能坏了”的结论,实际上切回普通用户一切正常。
5.2 编译报错与运行时缺库的定位方法
Qt5开发时遇到编译错误,先别急着重新安装整个Qt,大部分问题定位一下就能解决。
编译期最常报的错是fatal error: QWidget: No such file or directory。这个报错出现,只能说明头文件搜索路径没配上。用qmake构建时,检查.pro文件里是否确实包含了QT += widgets。如果漏写,qmake生成的头文件路径里就不会包含QtWidgets模块,编译才会找不到头文件。
如果报的是undefined reference to vtable之类的链接错误,基本可以断定Q_OBJECT宏所在的类的头文件没有跑过moc。这在qmake工程里通常是因为把含Q_OBJECT的类写在了.cpp文件里(虽然现在moc也能处理这种情况,但需要额外配置QMAKE_MOC),或者忘了在.pro文件里声明对应的头文件。Qt的规则是:头文件里有Q_OBJECT,这个头文件必须出现在HEADERS变量里,moc才会处理它。
运行期缺库的报错长这样:
error while loading shared libraries: libQt5Widgets.so.5: cannot open shared object file: No such file or directory第一反应不应该是重装Qt,而是先看这个库是否存在:
ldconfig -p | grep Qt5Widgets find /usr/lib/aarch64-linux-gnu -name "libQt5Widgets*"如果库存在但程序仍然报“找不到”,多半是程序里设置了LD_LIBRARY_PATH把系统库路径覆盖了,或者程序是用rpath固定了错误的库目录。执行一下ldd ./your_app | grep Qt5能看到实际链接到的路径。如果库真的不存在,说明开发包装了但运行时库没装,补装对应的libqt5widgets5以及libqt5gui5即可。
5.3 顺手解决几个高频小问题:中文字体、信号槽结构体、GDB数据断点
开发过程中还有几个高频问题,几乎每台新机器上都会被问一遍,我在这里一起处理掉。
第一个是中文乱码和方块字。在麒麟ARM上跑Qt5程序,界面显示中文全部变成方块,这是典型的字体缺失。系统可能带了基本的中文字体,但Qt5默认字体回退没有选择到它们。解决办法是安装开源中文字体:
sudo apt install fonts-wqy-microhei fonts-wqy-zenhei fonts-noto-cjk安装完成后重启程序,如果还不行,在代码里显式设置字体:
QApplication::setFont(QFont("Noto Sans CJK SC", 10));第二是信号槽传递结构体的问题。很多人在Qt5里自定义了一个结构体,然后用信号槽传递,编译能过,但槽函数收到的数据是空的,甚至连接不生效。原因很简单:跨线程队列连接时,结构体需要注册到Qt的元类型系统。在结构体定义下面加一行:
Q_DECLARE_METATYPE(MyStruct)然后在连接前注册类型:
qRegisterMetaType<MyStruct>("MyStruct");如果是跨线程信号槽,连接时最好显式使用Qt::QueuedConnection,避免因为连接类型自动判断错误而丢失信号。
第三是Qt Creator里怎么下数据断点。这个问题问的人也很多。在Qt Creator的调试模式下,右键左侧断点边栏,选择“添加数据断点”,在弹出的编辑框里输入要监视的内存地址。更常用的做法是在断点命中后,在“调试器” → “监视” 表达式列表里右键变量,选择“在内存地址处设置数据断点”。数据断点在排查栈溢出、数组越界和指针被意外改写时非常有用。注意数据断点只在GDB后端下可用,而且ARM Linux上硬件数据断点数量有限,一般同时只能设4个,设太多会提示资源不足。
写在最后的小经验
这套环境我在两台不同的麒麟ARM设备上各装了一遍,第二次明显比第一次快了很多。最大的体会是:离线安装的成功率,基本取决于你在“打包阶段”是否把依赖树一次理清。与其在目标机器上反复试错、逐个补包,不如在联网机器上把所有依赖一次性用apt-rdepends拉干净,哪怕打包体积大一点,也比现场缺库强十倍。
另外还想分享一个小习惯:每次装完一个阶段(比如gcc装完、Qt5装完),都随手做一个dpkg -l > /tmp/pkg_manifest.txt的快照。如果后续环境被改坏了,对照快照排查比从头重装快得多。这个习惯在隔离环境交付项目时尤其有用,客户现场的运维看到你能直接告诉他要补哪个包,会省掉很多沟通成本。