
简介面向 Ubuntu 18.04 环境下需要离线部署 C/C 编译工具链的开发者与运维人员这套压缩包把 gcc 7.3 及其配套依赖整理成可直接用 dpkg 安装的本地材料主要解决无法访问外网、内网机房或在线软件源失效时难以安装编译器的问题。包内共 25 个文件其中 24 个 deb 包构成主体涵盖以 gcc-7、cpp-7、binutils 为核心的编译器组件以 libgcc-7-dev 为代表的开发头文件libasan4、libtsan0、libubsan0 等运行时检测库以及 libmpc3、libisl19 等数学依赖基本覆盖了从编译驱动到运行支持的完整链条另有 1 个 sh 脚本可用于简化批量安装流程。整个压缩包大约 20.05MB体积适中便于拷贝和分发。对需要复现旧版本编译环境或在隔离网络中搭建构建节点的读者来说只要按依赖顺序依次安装就能得到可用的 gcc 7也能借此理清 gcc 各组件之间的依赖层级减少版本错配带来的反复试错。目前已有 3205 人学习下载。1. 为什么 Ubuntu 18.04 上还得手动备一份 gcc7 的离线包在 ubuntu18.04 上重新装回 gcc7最常发生在三种机器上一是内网编译机仓库不可达apt 只能干看着二是被同事“升级过一轮”的开发机gcc 被切到 9 或 10老代码一编译就报error: ‘auto_ptr’ is not a member of ‘std’三是打包成镜像的 CI 节点基础镜像被裁掉了一堆-dev包。这三种场景的共同点是你手里必须有.deb实物并且得会用 dpkg 把它按顺序喂进去。这份ubuntu18.04gcc.zip就是干这个的——一整组针对 18.04 的 gcc-7 及其依赖包解压后可以直接dpkg -i不用联网。它适合两类人被离线环境卡住、需要在半小时内把编译链恢复的运维和构建工程师以及要给老项目锁死 gcc-7 版本、不希望全局编译器被别的东西改掉的开发者。下面把它拆开看。2. 拆包看结构gcc7 离线包里到底装了什么拿到一个压缩包第一步不是急着装而是搞清楚里面有哪些 deb、它们互相依赖什么、版本后缀对不对得上本机。这一步做扎实后面能少折腾两小时。2.1 依赖链gcc-7 从来不是单独一个 deb很多人对gcc的直觉是“一个编译器就是一个包”翻车就翻在这里。Ubuntu 把 gcc 拆得非常细gcc-7本体只是个驱动程序driver负责解析命令行、拼参数、调用cc1/cc1plus做真正的前端处理再调用as汇编、collect2和ld链接。所以它必然依赖binutils也必然依赖与自己严格同版本的cpp-7、gcc-7-base、libgcc-7-dev。再往上写 C 的话还需要g-7和libstdc-7-dev编译任何真实项目都离不开libc6-dev提供的头文件和启动文件crt1.o、crti.o。下表是我拆这类包时最关注的一份清单按“缺了会怎样”排序包名作用缺了之后的表现gcc-7-base版本元数据、license、公共文件后续包 preinst 直接失败cpp-7C 预处理器gcc-7: error trying to exec cc1gcc-7编译器驱动与 cc1命令不存在libgcc-7-devlibgcc.a、crtbegin.o等链接期cannot find crtbegin.og-7C 驱动与 cc1plus无法编译 .cpplibstdc-7-devC 标准库头文件与静态库fatal error: iostream: No such filebinutilsas / ld / arcannot find -lc、汇编失败注意libgcc-7-dev和libstdc-7-dev这两个包名里的-dev后缀它们装的是编译期需要的东西运行期只需要libgcc1和libstdc6而这两个在 18.04 基础系统里本来就有。搞混这一点就会出现在编译机上装了一堆运行时包、却依然报头文件找不到的怪事。2.2 版本号后缀是判断“能不能装”的第一证据Ubuntu 的包版本号里藏着一个关键信息~18.04这个后缀。它表示这个包是专门为 18.04 重新构建的链接的是 18.04 的 glibc2.27。如果压缩包里混进了~20.04或者~16.04的包装上去可能不报错但运行期会在GLIBC_2.29 not found上炸掉而且报错位置通常在业务代码里极难定位。解压之后先看一眼再动手# 解压到临时目录不要直接在家目录里铺开 mkdir -p /tmp/gcc7-inspect cd /tmp/gcc7-inspect unzip -q ~/Downloads/ubuntu18.04gcc.zip -d ./pkg # 列出所有 deb先看文件名里的版本后缀 ls -1 ./pkg/*.deb | sed s#.*/## | sortunzip -q的-q是静默模式包大时不会刷屏-d ./pkg指定输出目录避免把一堆 deb 撒在当前目录。文件名里一般形如gcc-7_7.5.0-3ubuntu1~18.04_amd64.deb~18.04和amd64这两段就是你需要的判断依据。如果是arm64的包混进 x86 机器dpkg 会直接拒绝反倒好办麻烦的是架构对但发行版不对的情况。接着逐个读 control 字段这一步比看文件名可靠得多因为文件名可以改control 里的Depends不会骗人# 逐个读取 control 关键字段包名、版本、架构、依赖 for f in ./pkg/*.deb; do echo $(basename $f) dpkg-deb -f $f Package Version Architecture Depends donedpkg-deb -f是读 control 字段的专用开关后面跟字段名即可可以一次跟多个。Depends里会写成像cpp-7 ( 7.5.0-3~), gcc-7-base ( 7.5.0-3ubuntu1~18.04), binutils ( 2.26)这样的形式注意那个号——它表示版本必须完全相等这也是为什么gcc-7和gcc-7-base必须来自同一个压缩包、同一批次混用两个来源的包几乎必挂。2.3 一次性把依赖缺口算出来别用 dpkg 试错新手最容易犯的错是抓一个 deb 就dpkg -i报错了再装下一个来回十几轮。dpkg 的依赖检查是“装的时候才查”没有预演习惯的话中间态会一直处于 half-configured越弄越乱。正确做法是先做一次静态比对把包里所有Depends抽出来去重再和本机已安装包列表求差集缺什么一目了然。cd /tmp/gcc7-inspect # 1) 抽出所有 deb 声明的依赖去掉版本约束和可选分支去重排序 for f in ./pkg/*.deb; do dpkg-deb -f $f Depends; done \ | tr , \n \ | sed s/(.*//; s/|.*// \ | tr -d \ | grep -v ^$ \ | sort -u need.txt # 2) 导出本机已安装包名 dpkg-query -W -f${Package}\n | sort -u have.txt # 3) 求差集need 里有、have 里没有的就是缺口 comm -23 need.txt have.txt三段管道的作用分别是tr , \n把逗号分隔的依赖拆成一行一个sed s/(.*//砍掉括号里的版本约束s/|.*//处理“或”关系的备选分支比如default-libc-dev | libc6-dev取前者即可tr -d 清掉残留空格。最后comm -23需要两个输入都已排序所以前面必须带sort -u否则结果会是错的——这是 comm 最常见的误用。输出为空说明本机基础依赖齐全直接装包即可输出里出现libisl15、libmpc3、libmpfr6、zlib1g这类数学库和压缩库说明压缩包里应该也带了检查一下是不是漏解压了某个子目录。如果缺失的是binutils或libc6-dev这种基础包优先从本机 ISO 或本地源补而不是从别处拷一个高版本的过来——版本一高libc6会被连带升级整机风险陡增。另外可以先用--no-act让 dpkg 走一遍解析流程但不落盘# 空跑只解析不写入用来提前看 dpkg 会抱怨什么 sudo dpkg --no-act -i ./pkg/*.deb 21 | head -40--no-act等价--dry-run只打印“将会做什么”不会真的解包和写文件。它的依赖检查不如真实安装严格所以别把它当权威结论但用来提前发现“包名拼错”“架构不符”这类低级问题足够了。3. 安装顺序dpkg -i 一次过还是逐个来搞清楚依赖之后安装本身其实只有一条命令。但这条命令的写法有讲究写错了就会得到一堆dependency problems prevent configuration。3.1 为什么顺序重要preinst 和配置时机的差别dpkg 装一个包分两步先unpack解包到文件系统再configure跑preinst/postinst脚本、写状态库。依赖检查发生在 configure 阶段。也就是说如果你一个一个装装gcc-7的时候cpp-7还没 configuredpkg 就会把这个包留在unpacked状态等你装完cpp-7还得回头dpkg --configure gcc-7非常啰嗦。而dpkg -i a.deb b.deb c.deb这种一次传多个文件的写法dpkg 会在同一批次内先把所有包 unpack再按依赖关系排序 configure。这就是为什么“一次过”反而比“一个一个来”稳。正确的一次性写法cd /tmp/gcc7-inspect/pkg # 一次性把所有 deb 交给 dpkg让它自己排 configure 顺序 sudo dpkg -i ./*.deb./*.deb由 shell 展开成完整文件列表dpkg 拿到的是全部包。注意这里不能用-R递归去指向上级目录-R只处理目录里直接存在的 deb行为和通配符并不等价混用容易漏包。如果包数量很多、想显式控制“基础包先上”可以分批但保证每批内部自洽# 第一批纯数据与基础库无外部依赖 sudo dpkg -i gcc-7-base_*.deb libisl15_*.deb libmpc3_*.deb libmpfr6_*.deb # 第二批预处理与后端 sudo dpkg -i cpp-7_*.deb binutils_*.deb libgcc-7-dev_*.deb # 第三批驱动与 C 前端 sudo dpkg -i gcc-7_*.deb g-7_*.deb libstdc-7-dev_*.deb分三批的依据是依赖方向第一批没有任何对包内其他成员的依赖第二批只依赖第一批第三批依赖前两批。这样即使中途某批失败系统也不会卡在互相等待的中间态重试成本低。还有一种保底写法是加--force-depends# 仅作为最后手段强行忽略依赖检查 sudo dpkg -i --force-depends ./gcc-7_*.deb sudo dpkg --configure -a--force-depends会让 dpkg 跳过依赖校验强行装代价是可能留下一个“装上了但跑不起来”的包。我的建议是只在确认缺失的那个依赖对当前用途完全无影响时才用比如缺的是一个文档包编译器本身的依赖缺口一律不许强装否则后面编译报的错会离真正原因十万八千里。3.2 离线机器上 apt 帮不了你别指望 -f install网上一半的教程会让你在报错后跑sudo apt-get -f install。这条命令在联网机器上确实好用它会自动补齐缺失依赖。但在内网离线机上apt 找不到源只会返回一堆Failed to fetch然后把你的包状态搅得更乱。离线场景的替代方案是dpkg --configure -a加手工补包# 查看当前处于异常状态的包unpacked / half-configured sudo dpkg --audit # 尝试把所有未完成配置的包配置完 sudo dpkg --configure -adpkg --audit是排查“上次装到一半断了”的第一条命令它会列出所有处于iUunpacked、iFhalf-configured、iHhalf-installed状态的包。--configure -a会按依赖顺序重跑这些包的 postinst。跑完之后再dpkg --audit一次输出为空才算干净。3.3 装完必做的三条验证装完不验证等于没装。三条命令分别从包状态、可执行文件、头文件三个层面确认# 1) 包状态第二列应为 ii已安装且已配置 dpkg -l | grep -E gcc-7|cpp-7|g\\-7|libgcc-7|libstdc\\-7 # 2) 可执行文件是否就位版本是否符合预期 /usr/bin/gcc-7 --version | head -1 /usr/bin/g-7 --version | head -1 # 3) 这个可执行文件到底属于哪个包防止被别的包“借位” dpkg -S /usr/bin/gcc-7第一条里dpkg -l的第二列是状态码ii才是正常iU表示只解包没配置rc表示已删除但残留配置。第二条要确认版本号是7.5.0这一支如果显示的是 7.4 或者干脆找不到文件说明压缩包里的包没被正确解压或装到了别的前缀。第三条dpkg -S是查文件归属输出应该指向gcc-7如果指向gcc-9说明你机器上存在路径冲突需要进一步查。再补一条更实用的检查——直接编译一个最小程序把预处理、编译、汇编、链接四步全走一遍cat /tmp/probe.c EOF #include stdio.h int main(void) { printf(%s\n, __VERSION__); return 0; } EOF gcc-7 /tmp/probe.c -o /tmp/probe /tmp/probe能打印出版本串说明cc1、as、collect2、ld、libc启动文件这条链路全部打通。这一步比看--version有价值得多--version只证明驱动在不证明后端在。4. 多版本共存把 gcc7 挂进 alternatives 并按项目切换装好了gcc-7但敲gcc出来的还是别的版本这是最高频的困惑。原因很简单/usr/bin/gcc是个软链接由update-alternatives管理你装了gcc-7不等于系统会用它。4.1 alternatives 的机制与优先级数字怎么定update-alternatives维护的是/etc/alternatives/下的一组二级链接结构是/usr/bin/gcc→/etc/alternatives/gcc→/usr/bin/gcc-7。注册时给的那个优先级数字只在auto模式下决定默认选中谁——数字大的赢。它不是“版本高低”的意思纯粹是个权重。所以优先级怎么定取决于你的意图。如果这台机器是给老项目用的希望默认就是 7那就给 70 以上的数字并把别的版本压下去如果只是临时备着给个小数字、需要时手动--config切换即可。slavery从链接是这套机制里最省事的部分注册gcc的时候把g、gcov一起挂上切换时三个一起变不会出现“gcc 是 7、g 还是 9”这种 ABI 混搭的灾难。4.2 注册并切换# 注册 gcc-7同时把 g、gcov 作为从链接绑定 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ --slave /usr/bin/g g /usr/bin/g-7 \ --slave /usr/bin/gcov gcov /usr/bin/gcov-7 # 查看当前候选与模式 update-alternatives --query gcc # 交互式切换 sudo update-alternatives --config gcc--install后面依次是主链接路径、alternatives 名字、实际文件路径、优先级。名字这里是gcc是这套链接的逻辑标识后面所有操作都靠它。--slave的三个参数是从链接路径、从链接名、从文件路径——从链接不能独立切换只能跟着主链接走这正是我们要的效果。--query会输出Status、Best、Value三个关键行。Status: auto表示按优先级自动选manual表示被--config锁定过。如果你的机器上Status是manual且Value指向 gcc-9那--install加进去也不会生效必须先--config切回来。# 确认最终指向 readlink -f /usr/bin/gcc readlink -f /usr/bin/greadlink -f会一路跟到底输出真实文件路径。两条命令的输出里版本号必须一致这是排查 ABI 混用的第一道关卡。4.3 不改全局在项目层指定编译器我个人的习惯是不轻易动全局 alternatives。一台机器上往往同时有好几个项目全局切一次别人的构建就挂了。更稳的做法是在项目里指定编译器让版本跟着代码走。Makefile 里就两行# 项目级锁定编译器不依赖系统默认 CC : gcc-7 CXX : g-7 CXXFLAGS -stdc14 -D_GLIBCXX_USE_CXX11_ABI0_GLIBCXX_USE_CXX11_ABI0这个宏值得单独说一句。gcc5 之后 libstdc 引入了新的std::stringABIcxx11 ABI而 gcc-7 默认开启它。如果你的项目要链接第三方预编译好的.so尤其是那些用老编译器构建的就会出现undefined reference to std::__cxx11::basic_string...这种链接错误。这时候把 ABI 开关关掉比去找第三方要新版本快得多。CMake 项目则在配置阶段传变量注意必须是首次配置时传改完要清空 build 目录否则 CMakeCache 里存着旧的编译器路径# 首次配置时指定之后改需先删 build 目录 cmake -S . -B build \ -DCMAKE_C_COMPILER/usr/bin/gcc-7 \ -DCMAKE_CXX_COMPILER/usr/bin/g-7 cmake --build build -j$(nproc)-S和-B是 CMake 3.13 之后的新写法分别指源码目录和构建目录比进目录再cmake ..清晰。-j$(nproc)按 CPU 核数并行编译老项目头文件多的时候能省不少时间。4.4 验证从版本串到 ABI 符号光看--version不够得让编译器自己把版本、标准、ABI 都吐出来cat /tmp/abi.cpp EOF #include iostream #include string int main() { std::string s probe; std::cout compiler: __VERSION__ \n std: __cplusplus \n string size: sizeof(s) \n; return 0; } EOF g-7 -stdc17 /tmp/abi.cpp -o /tmp/abi /tmp/abi重点看string size。在开启 cxx11 ABI 的情况下64 位下std::string通常是 32 字节关掉 ABI 后是 8 字节本质上是个指针。这个数字能一秒判断当前编译单元用的是哪套 ABI比翻编译日志快得多。再确认运行期库的符号版本跟编译期对得上# 看系统 libstdc 支持到哪个 CXXABI 版本 strings /usr/lib/x86_64-linux-gnu/libstdc.so.6 | grep -E ^CXXABI_1\.3\.[0-9]$ | sort -V | tail -3strings从二进制里抽可打印字符串grep筛出 ABI 版本标记sort -V按版本号语义排序不能用普通sort否则1.3.9会排在1.3.11后面。gcc-7 编译出来的代码最高会用到CXXABI_1.3.11附近如果系统库只到1.3.9那就得把libstdc6一起升级否则程序一启动就报version CXXABI_1.3.11 not found。5. 避坑与排查dpkg 依赖断裂、ABI 混用、alternatives 抢位的五个现场这一章是我这几年在离线机上反复踩过的坑按出现频率从高到低排。每条都按“看到什么 → 为什么 → 怎么办”写遇到时直接对号入座。5.1 现象dependency problems prevent configuration of gcc-7报错里还会跟着一行Package cpp-7 is not configured yet.然后整个安装就停在那里。原因是 dpkg 是逐个 configure 的当你把 deb 一个一个装、或者压缩包解压不完整导致少了某个成员时gcc-7的 configure 阶段会检查依赖是否已经处于installed状态没满足就拒绝配置包被留在iU。解决办法分两步。先确认压缩包成员是否齐全ls ./pkg/*.deb | wc -l再跟 2.1 节的清单对一遍。齐全的话用一次性安装覆盖重来sudo dpkg -i ./pkg/*.debdpkg 会重新按依赖排序配置。如果还是不行sudo dpkg --configure -a兜底。不要用apt-get -f install离线机上它只会让状态更乱。5.2 现象装完敲gcc --version还是旧版本或者干脆command not found--version显示旧版本说明/usr/bin/gcc的软链接没指向 gcc-7alternatives 没注册或注册了但处于manual锁定态。command not found则更严重通常是 alternatives 被删过、/etc/alternatives/gcc成了断链。先查状态update-alternatives --query gcc看Value和Status两行。Status: manual且Value指向旧版本就sudo update-alternatives --config gcc手动选。如果是断链先sudo update-alternatives --remove gcc /usr/bin/gcc-7清掉坏记录再重新--install。还有一种极少见的情况/usr/bin/gcc被某个自行编译安装的 gcc 覆盖成了实体文件不是软链接这时候 alternatives 完全失效得先手动删掉那个实体文件再重新注册。5.3 现象链接期报undefined reference to std::__cxx11::basic_string...编译阶段一切正常一到链接就炸报错里全是std::__cxx11::开头的长符号。这是典型的 cxx11 ABI 不匹配你的代码用 gcc-7 默认 ABI 编译但链接的第三方.so是用老 ABI对应_GLIBCXX_USE_CXX11_ABI0构建的两边的std::string内存布局完全不同符号名也不同自然对不上。解决方向有两个能拿到第三方源码就重编一遍拿不到就在编译选项里加-D_GLIBCXX_USE_CXX11_ABI0让整个项目退回老 ABI。加之前先确认项目里所有翻译单元都加了只加一半会得到更难查的运行时崩溃——因为链接能过但对象布局不一致。5.4 现象/usr/bin/ld: cannot find -lstdc或cannot find crtbegin.o这两种报错指向同一个根因只装了gcc-7没装g-7和libgcc-7-dev。crtbegin.o属于libgcc-7-devlibstdc.so的开发符号链接属于libstdc-7-dev。用dpkg -L libgcc-7-dev | grep crtbegin确认文件在不在dpkg -L libstdc-7-dev | grep libstdc.so确认开发链接在不在。如果包状态是ii但文件缺失说明安装过程被中断过sudo dpkg --configure -a或sudo dpkg -i --force-overwrite重装该包。另外提醒一点libstdc6运行库和libstdc-7-dev开发库是两个包前者在基础系统里就有后者必须从压缩包里装别看到libstdc前缀就以为已经装了。5.5 现象dpkg was interrupted, you must manually run dpkg --configure -a这条提示一般出现在你上次装到一半按了 Ctrl-C、或者 SSH 断线、或者机器恰好断电之后。此时 dpkg 的锁文件和状态库都处于不一致状态任何dpkg -i或apt操作都会被拒绝。按提示走就行但顺序要对先sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock清锁只有在确认没有别的 dpkg 进程在跑时才这么做用ps aux | grep dpkg确认再sudo dpkg --configure -a最后sudo dpkg --audit确认没有残留异常包。清锁这一步别乱来——如果真有另一个 apt 进程在后台跑删锁会导致状态库损坏修复成本远高于等它跑完。6. 进阶把这份包做成可复用的内网安装脚本单次装好只是解决一台机器。真正省时间的是把它固化成一个脚本扔到内网任何一台 18.04 上都能跑跑完自己报告结果。脚本我一般写成四段环境校验、解压清点、安装、验证输出。#!/usr/bin/env bash # install-gcc7.sh —— 内网 18.04 机器上的 gcc-7 离线安装 set -euo pipefail ZIP${1:?用法: ./install-gcc7.sh /path/to/ubuntu18.04gcc.zip} WORK$(mktemp -d) trap rm -rf $WORK EXIT # 1) 只允许 18.04版本不对直接退出避免装出 ABI 烂摊子 . /etc/os-release [ $VERSION_ID 18.04 ] || { echo 仅支持 18.04当前 $VERSION_ID; exit 1; } # 2) 解压并清点数量少于预期就中止 unzip -q $ZIP -d $WORK/pkg COUNT$(find $WORK/pkg -name *.deb | wc -l) echo 解出 deb 包: $COUNT 个 [ $COUNT -ge 6 ] || { echo 包数量异常检查压缩包完整性; exit 1; } # 3) 一次性安装失败则打印未配置的包 dpkg -i $WORK/pkg/*.deb || { dpkg --audit; exit 1; } # 4) 注册 alternatives 并输出验证结果 update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ --slave /usr/bin/g g /usr/bin/g-7 /dev/null printf #include stdio.h\nint main(void){puts(__VERSION__);return 0;}\n $WORK/p.c gcc-7 $WORK/p.c -o $WORK/p $WORK/p几个关键点值得说明。set -euo pipefail让脚本遇到未定义变量、管道中任一段失败、任意命令非零退出时立刻中断避免半途失败还继续往下跑。trap ... EXIT保证无论正常结束还是异常退出都清理临时目录否则/tmp里会堆一堆解压残留。${1:?...}是参数必填语法不给参数时直接打印用法并退出比跑到一半才发现路径是空的好。最后那句printf生成探针程序并编译运行是整个脚本的“验收环节”——前面的命令都可能返回 0但只有真的编译出可执行文件、跑起来打印出版本串才证明工具链是通的。脚本之外我还会在同目录留一份MANIFEST.txt记录这次装的每个包的包名和完整版本号# 生成清单出问题时能一秒对上是哪个包惹的祸 dpkg-query -W -f${Package} ${Version} ${Status}\n \ gcc-7 g-7 cpp-7 gcc-7-base libgcc-7-dev libstdc-7-dev \ MANIFEST.txt为什么非要这个清单因为半年后你回来排查一台机器--version只能告诉你 7.5.0告诉不了你当初装的是7.5.0-3ubuntu1~18.04还是某个自己重编的版本而版本号差一位依赖的gcc-7-base就可能对不上重装时又是一轮鸡同鸭讲。有了清单直接照着版本号去压缩包里挑对应的 deb几分钟搞定。最后提一个容易被忽略的验证技巧装完之后用dpkg -S反查所有 gcc 相关可执行文件的归属把结果跟清单对照。如果发现某个文件属于一个意料之外的包比如cc1来自gcc-9而不是cpp-7的gcc-7说明系统里存在多套工具链交叉污染这种污染在编译大型项目时会以各种离奇的方式暴露——比如某个.o文件明明编译成功了链接时却报一堆莫名其妙的未定义符号。早发现早清干净。我以前图快装完只看一眼gcc-7 --version就去编译业务代码结果在一个混合了 18.04 和 20.04 包的内网镜像上栽了大跟头编译期全过运行期GLIBCXX_3.4.26 not found查了整整一个下午才定位到是 base 镜像里混进了一个高版本的libstdc6。从那以后我每次装完这套离线包都强制走一遍“清点 → 比对 → 探针编译 → 生成 MANIFEST”这四步一步都不跳。希望帮到你。本文还有配套的精品资源点击获取