简介:面向Linux运维、开发人员及离线环境使用者,这份gcc_rpm.tar.gz是一套面向CentOS 6/EL6类x86_64系统的GCC 4.4.7离线RPM依赖包集合,专为解决无外网或网络受限服务器上无法在线安装编译器的问题。压缩包内收录了gcc、gcc-c++、cpp、glibc-devel、kernel-headers、libstdc++-devel等22个rpm安装包,并包含mpfr、ppl、cloog-ppl等数学与优化支撑库,覆盖编译C/C++程序所需的核心编译器、头文件与底层依赖;同时附带一键安装脚本install_gcc.sh与Readme.md说明文档,可按照脚本自动完成依赖安装与版本验证,也可根据说明手动处理特殊环境差异。整个资源共24个文件,压缩后大小约24.11MB,体量紧凑,便于在离线内网间快速拷贝与分发。目前已有3991人学习下载,足见其在众多初始化环境搭建场景中被反复使用。借助这套包,读者无需逐一下载排查依赖,即可在隔离网络内快速部署可用的GCC编译环境,提升部署效率,减少踩坑时间。
1. 一个 gcc_rpm.tar.gz,解决的是离线装 GCC 的硬仗
如果你也遇到过这种场景:内网服务器没有任何外网权限,yum 源连不上,但项目要在上面编译 C/C++ 代码,机器上却连 gcc 都没有——那这份 gcc_rpm.tar.gz 就是为你准备的。它本质上是一个把编译链所需的 rpm 包和依赖打成一包的离线资源,典型内容包括 gcc、gcc-c++、libgcc、glibc-devel 以及对应的 cpp 和 kernel-headers 等一组相互依赖的 rpm 文件。适合两类人:一类是要在内网裸机上快速拉起编译环境的一线运维和开发,另一类是需要在多台离线机器上重复搭建同版本编译链、不想逐台在线安装的交付实施人员。它的核心价值不是省一次网速,而是把依赖关系一并锁死,让离线安装从“碰运气”变成“按清单执行”。
2. 拆包与依赖盘点:先看清里面是什么,再决定怎么装
拿到手的 gcc_rpm.tar.gz 是一个黑匣子,直接解压安装是最常见也最容易翻车的做法。正确的打开方式是先温和地检查,再动手,这个习惯能帮你避免后面一连串的依赖地狱。本地的 RPM 包不像源码包那样解压就能用,它的安装顺序、依赖满足情况、与系统已有 glibc 的兼容性,都会影响最终结果。
2.1 解压前的三重检查:完整性、清单、路径污染
第一步不是 tar,而是确认包本身没损坏,也要看清内部文件会不会在解压时污染当前目录。我一般会按下面的顺序来:
# 1. 校验压缩包完整性,md5sum 和 sha256sum 都过一遍 md5sum gcc_rpm.tar.gz sha256sum gcc_rpm.tar.gz # 2. 预览包内文件清单,不解压,只列目录树 tar tzf gcc_rpm.tar.gz # 3. 确认包内所有文件都在单独的子目录下,而不是散落在根目录 tar tzf gcc_rpm.tar.gz | awk -F'/' '{print $1}' | sort -u这三条命令的逻辑并不复杂:前两条确认文件没在传输过程中损坏,第三条则是检查压缩包内是“一个顶层文件夹”还是“一堆文件平铺”。后者在解压时会把 rpm 文件直接撒进当前目录,严重时还会覆盖同名文件,属于必须提前排除的隐患。实际操作中,我曾经见过有的人在 /root 下直接解压,结果包内一个叫 localtime 的配置文件把系统时区给顶掉了,那已经是另一个维度的灾难了。所以第三条命令输出如果只有一行目录名,说明结构正常;如果输出多行或者直接是 .rpm 后缀,那就要先建子目录再解压。
2.2 依赖清单:为什么不能闭眼 rpm -ivh *.rpm
假设包目录是 gcc_rpms,里面是十几个 rpm。新手最容易犯的错是用一条 rpm -ivh *.rpm 试图把全部装上,结果报一屏的依赖缺失,然后开始手动找包。原因很好理解:rpm 安装时会立刻检查依赖是否满足,而通配符展开后,安装顺序是字典序,不是依赖顺序。gcc-c++ 排在 gcc 前面极常见,于是 gcc-c++ 找 gcc 的时候,gcc 还没装上,必然失败。
正确的做法是在安装之前,先逐个查看 rpm 的依赖关系:
# 解压到一个独立目录,避免污染当前环境 mkdir -p ~/offline-gcc && tar xzf gcc_rpm.tar.gz -C ~/offline-gcc # 选中核心包,查看它依赖什么 rpm -qpR ~/offline-gcc/gcc-c++-*.rpm # 查看它提供什么,确认包本身能提供哪些被依赖的库 rpm -qp --provides ~/offline-gcc/gcc-c++-*.rpm命令里的 -q 表示查询,-p 表示对未安装的 rpm 文件查询,-R 列出依赖,--provides 列出它向外提供的符号和库。这个步骤建议对每个 rpm 都跑一遍,然后把输出汇总成一张依赖表。常见做法是写个循环脚本,把所有 rpm 的依赖打到一个文件里,再用 sort 和 uniq 去重,这样整个包的有效依赖一目了然。这个包如果来源正规,通常内部依赖是闭环的,但 glibc 和 libgcc 这类基础库经常依赖系统已有的版本,一旦发现系统 glibc 版本低于包内要求,后面的安装基本不可能成功,这时候就得考虑从系统镜像源里找配套版本,而不是硬装。
典型的包内清单会是下面这个样子,具体以 tar tzf 输出为准:
| 文件名 | 作用 | 注意事项 |
|---|---|---|
| gcc-*.rpm | 核心 C 编译器 | 依赖 cpp 和 glibc-devel |
| gcc-c++-*.rpm | C++ 编译器 | 依赖 gcc 与 libstdc++-devel |
| cpp-*.rpm | C 预处理器 | gcc 的硬依赖 |
| glibc-devel-*.rpm | 开发头文件和静态库 | 依赖 glibc 主包 |
| libgcc-*.rpm | 底层运行时库 | 几乎必须最先装 |
| kernel-headers-*.rpm | 内核头文件 | 某些场景下 glibc-devel 需要 |
提示:如果包里只有两三个 rpm,说明作者已经预合并了依赖;如果十几个,则说明是完整依赖链。前者在安装时几乎不会出问题,后者反而要重点检查版本兼容性。
3. 本地 yum 仓库搭建与安装执行:把依赖交给工具,而不是赌运气
依赖盘点清楚之后,安装路径有两条:一条是搭本地 yum 源,用 yum 来自动解决依赖,这是我最推荐的生产环境做法;另一条是按依赖顺序手动 rpm 安装,适合包很小或临时验证的场景。两条路都需要把包先解压到固定目录。
3.1 用 createrepo 构建本地 yum 源:一劳永逸的推荐做法
只要机器上有 python 和 createrepo 工具(没有的话可以先用下面第 3.2 节的临时方案),就可以把解压出来的目录变成 yum 源,之后无论装哪个包,依赖都由 yum 自行处理:
# 安装 createrepo(如果系统连不上外网,先从包内找或者用已有 rpm) yum install -y createrepo # 生成仓库元数据 createrepo ~/offline-gcc # 编写本地 repo 配置文件 cat > /etc/yum.repos.d/offline-gcc.repo <<'EOF' [offline-gcc] name=Offline GCC RPMS baseurl=file:///root/offline-gcc enabled=1 gpgcheck=0 EOF # 清理缓存并验证仓库可用 yum clean all yum repolistcreaterepo 的作用是扫描目录内所有 rpm,生成 repodata 元数据,让 yum 能识别这个文件夹为一个软件仓库。repo 文件里的 baseurl 用的是 file:// 协议,指向本地绝对路径,enabled=1 表示启用该仓库,gpgcheck=0 表示跳过 GPG 签名校验——离线内网环境下通常没有导入签名 Key,这里设为 0 最常见。注意 baseurl 路径必须与 createrepo 执行时用的目录完全一致,写错或写相对路径都会导致 yum 报 “Cannot retrieve repository metadata”。完成后直接用 yum 安装你要的东西:
yum install -y gcc gcc-c++此时 yum 会在本地仓库内自行寻找 gcc 和 gcc-c++ 的所有依赖,按依赖顺序解析安装。这也是整套流程里最省心的部分:依赖解析交给工具,你只需要确保包内依赖闭环。用 yum 还有一层好处,后续如果机器上要装其他编译相关软件(比如 make、autoconf 的离线包),可以把它们也扔进 ~/offline-gcc 并重新执行 createrepo,变成一个不断生长的内网软件仓库。
3.2 手动 rpm 安装的兜底方案:什么时候才用
本地 yum 源是首选,但如果包内只有三五个 rpm,或者系统里的 yum 本身就有问题,手动按依赖顺序安装才是更快的路径。手动安装的核心是顺序:先装底层库,再装编译器本体。
cd ~/offline-gcc # 先安装运行时库,再装预处理器,最后装 gcc 和 gcc-c++ rpm -ivh libgcc-*.rpm rpm -ivh cpp-*.rpm rpm -ivh glibc-devel-*.rpm kernel-headers-*.rpm rpm -ivh gcc-*.rpm rpm -ivh gcc-c++-*.rpm # 遇到依赖缺失时,先查询是哪个包提供的 rpm -qp --provides *.rpm | grep -i "缺失的关键字"这里的顺序不是固定的,实际以第 2 章查到的依赖表为准。rpm -ivh 的 i 表示安装,v 显示详情,h 显示进度条;用 -Uvh 则是升级安装,如果系统里已有旧版本 gcc,-Uvh 更合适,它会把旧包替换掉。需要特别提的是 --nodeps 参数。出问题时,很容易听到有人说“加个 --nodeps 强装”,这确实能装上去,但代价是安装完成后 gcc 可能因为找不到 libmpfr.so 或 libmpc.so 而编译失败,而且这种失败往往是在你编译第一个正经项目时才暴露——到时候排查起来远比安装时更痛苦。我的习惯是:只有在依赖缺失的包完全不参与编译主链路(比如只缺一个文档相关的 rpm)时才用 --nodeps,编译器主链路必须依赖完整。
4. 安装后的三重验证:从命令可用到项目可编译
很多人装完 gcc,看到 which gcc 有输出就认为大功告成,这是严重的误判。离线安装的 rpm 包经常出现版本与系统 glibc 不匹配、头文件路径不对、默认库缺失等问题,而这些在 gcc -v 阶段根本不会暴露。我一般会做三层验证,每一层解决一个层面的问题。
4.1 版本与链接验证:看 gcc 到底是不是你想要的
# 验证版本,重点看 gcc version 那一行 gcc -v g++ -v # 验证编译器的实际调用路径 which gcc ls -l /usr/bin/gcc # 验证安装出来的包确实来自刚才的 rpm rpm -qa | grep -E '^gcc|^gcc-c++|^libgcc|^cpp'gcc -v 的输出值得多看几眼:最后一行会显示 configure 时的配置参数,其中 --with-arch 和 --with-abi 决定生成二进制兼容哪种 CPU 架构。如果包是用较新的 CPU 特性编译的,拿到老服务器上即使安装成功,编译时也可能报非法指令。所以这一步不是看看版本号就完了,要确认配置参数与目标机器 CPU 匹配。rpm -qa 的过滤则能确认安装来源——如果你看到同名的包却来自系统自带 repo,那说明刚才安装可能被系统原有版本覆盖了,这在已经有部分编译环境的机器上偶尔会出现。
4.2 编译冒烟测试:从 hello world 到多文件项目
编译器本身没问题,不代表头文件和库路径没问题。写个带系统调用的程序来实测是必须的:
# 写一个使用 math 库的测试程序,顺带验证链接路径 cat > test.c <<'EOF' #include <stdio.h> #include <math.h> int main(void) { printf("sqrt(2)=%.6f\n", sqrt(2.0)); return 0; } EOF # 编译并链接数学库 gcc test.c -o test -lm -Wall # 运行验证 ./test命令里的 -lm 是链接 libm.so 数学库,-Wall 打开常见警告。这个测试的价值在于:如果头文件路径不对会报 fatal error: math.h: No such file or directory;如果库路径不对会报 cannot find -lm;如果 glibc 版本不兼容会在运行时直接 segfault。三个典型卡点一次覆盖。更进一步,建议再拿一个真实的小项目试 make——因为 make 涉及环境变量 CC 的读取和 include 路径的传递,很多环境问题只有走到 make 这一步才暴露:
# 如果项目里有 Makefile,强制指定编译器再跑一次 make clean make CC=gcc CXX=g++ -j$(nproc)用 -j 并行编译能加速,但并行也会把内存吃满,小机器建议把 nproc 换成实际核数的一半。这一步通过,基本可以判定这套编译链在“编译”这个层面是可用的。
4.3 多版本切换:用 alternatives 管理不冲突的 GCC
如果机器上将来还要装另一个版本的 gcc(比如为了兼容旧代码),直接覆盖安装一定会出问题。RPM 体系里的标准做法是用 alternatives 机制管理:
# 查看当前 gcc 版本 gcc --version # 注册为新版本(路径必须是实际二进制路径) alternatives --install /usr/bin/gcc gcc /usr/local/gcc-4.8/bin/gcc 50 alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 80 # 手动切换 alternatives --config gccalternatives 的机制不复杂:/usr/bin/gcc 本身是一个符号链接,指向当前选中的版本;install 后面的数字是优先级,数字大的默认生效;--config 可以在已注册的多个版本间手动选择。同一套机制也适用于 g++ 和 cpp,记得分别注册。这一步在生产环境里很有价值,特别是部署老项目时,编译器的降级往往比改代码更快。
5. 避坑与常见问题:离线装 GCC 最容易翻车的五个现场
离线安装的坑,很多不是 rpm 本身的问题,而是环境差异导致的。以下五条是我实际迭代中踩过、也帮别人排过的典型问题,按“现象 → 原因 → 解决”写清楚。
5.1 缺少 libmpfr.so.4 导致 gcc 执行报错
现象:rpm 全部安装成功,gcc -v 却报 error while loading shared libraries: libmpfr.so.4: cannot open shared object file。原因:gcc 运行时依赖的 mpfr 库版本号与系统里已有版本对不上,常见于系统自带 libmpfr.so.6 而包内 gcc 编译时链接的是 .so.4。解决:先看 gcc 链接的是哪个库,用 ldd /usr/bin/gcc 查具体依赖,再去包内或系统镜像源里找对应版本的 mpfr rpm;如果找不到,下载一个同版本的 mpfr 源码包内网编译安装并导出 LD_LIBRARY_PATH。这种事情最容易出现在老包配新系统或者新包配老系统上,所以在第 2 章解压后先跑 rpm -qpR 看依赖,这一步真不是走过场。
5.2 configure: C compiler cannot create executables
现象:编译某个开源软件时,configure 阶段报这个错并退出。原因:缺少完整的编译工具链,最常见的是没有安装 glibc-devel,导致缺少 crt1.o 和 crti.o 这些启动文件;也可能因为 --nodeps 强装后系统里 gcc 和 glibc-devel 版本不匹配。解决:确认 glibc-devel 和 kernel-headers 是否安装(rpm -qa 过滤查),补齐后再跑 configure;如果确认都装了,用 gcc -v 的配置参数对比系统 glibc 版本,差异过大就要换包。这个报错最迷惑人的点在于它提示“编译错误”,但问题往往不在 gcc 本身,而在启动文件缺失。这台机器如果之前装过精简版系统,通常就是第 2 类原因。
5.3 安装时提示 file /usr/bin/gcc conflicts between attempted installs
现象:rpm 安装时报 conflict,文件冲突,安装中止。原因:系统里已经存在一个 gcc(通常是发行版自带的),新 rpm 包里的 /usr/bin/gcc 与旧包文件冲突,rpm 默认不会覆盖已存在文件。解决:确认现有版本与目标版本哪个更合适——如果不打算保留旧的,先 rpm -e 移除旧包,注意同时检查哪些包依赖旧 gcc;如果两个都要留,把新包解压到 /opt 下的独立目录,用 alternatives 注册切换,而不是强行覆盖。我见过有人用 rpm -ivh --replacefiles 强压上去,结果 /usr/bin/gcc 变成了一个新版本的二进制,但配套的 headers 还在旧位置,最后编译任何东西都缺头文件。
5.4 明明装了 gcc-c++,却提示 g++: Command not found
现象:gcc 能用,g++ 报 not found。原因:包内 gcc-c++ 没有安装成功,或者安装时依赖顺序错误导致 g++ 链接文件没生成;还有一种可能是 gcc-c++ 依赖的 libstdc++-devel 没装上,g++ 主程序虽然落盘,但运行即报错。解决:先 rpm -qa | grep g++ 确认包是否真的在;再查 /usr/bin/g++ 是否为软链接、指向是否存在;如果包不在,按第 3.2 节的顺序重装 gcc-c++ 并确保 libstdc++-devel 提前装好。这里有一个非常隐蔽的情况:某些精简包里的 gcc-c++ 被作者拆掉了,只留了 gcc 和 cpp,而 README 里没写,等用户真的编译 C++ 时才发现。
5.5 编译成功但运行报段错误,且每次位置不同
现象:gcc -lm 编译通过,但 ./test 跑起来时不时段错误。原因:编译器版本与系统 glibc 不兼容,生成的可执行文件使用了高版本 glibc 里才有的符号(如 __printf_chk 的某些实现),运行时的低版本 glibc 解析失败;另一个常见原因是包内 gcc 是另一个发行版打包的,rpm 安装时不检测 ABI 兼容性。解决:用 ldd ./test 查看实际链接的 glibc 版本,再用 objdump -T /lib64/libc.so.6 | grep 相关符号确认系统是否提供;如果确认是版本差,最稳妥的出路是找一个与目标系统同发行版的 gcc 离线包,不要继续在这个包上折腾。这种问题最考验排查耐心,因为它不是稳定的报错,很容易被误判为代码 bug;而根源往往在“这个 gcc 是给另一个系统打的”,平台不匹配。
6. 把整套安装固化成一个脚本:下次用时不再碰运气
手动操作跑通一遍后,最重要的收尾是把过程固化成可复用脚本,而不是把经验留在脑子里。离线安装这种活儿,隔两三个月再装一批机器,操作细节基本都会忘光,脚本才是真正的“后悔药”。下面是我常用的最小脚本骨架:
#!/bin/bash # install_offline_gcc.sh —— 离线安装 gcc_rpm 资源包 # 用法: ./install_offline_gcc.sh /path/to/gcc_rpm.tar.gz set -euo pipefail PKG="$1" WORKDIR="/opt/offline-gcc-install" rm -rf "$WORKDIR" && mkdir -p "$WORKDIR" tar xzf "$PKG" -C "$WORKDIR" --strip-components=1 # 生成本地仓库 createrepo "$WORKDIR" || yum install -y createrepo # 配置临时 repo cat > /etc/yum.repos.d/offline-gcc.repo <<EOF [offline-gcc] name=Offline GCC RPMS baseurl=file://$WORKDIR enabled=1 gpgcheck=0 EOF yum clean all yum install -y gcc gcc-c++ # 验证 gcc -v g++ -v # 冒烟测试 cat > /tmp/smoke.c <<'EOF' #include <stdio.h> int main(void) { puts("smoke ok"); return 0; } EOF gcc /tmp/smoke.c -o /tmp/smoke && /tmp/smoke echo "GCC installed: $(gcc --version | head -1)"脚本里 set -euo pipefail 的含义是:任何命令失败立即退出、变量必须事先定义、管道中任一环节失败视为整体失败,这三条加在一起,保证脚本不会带着中间错误硬往下跑。--strip-components=1 在解压时去掉顶层目录,让 rpm 文件直接铺在 WORKDIR 下,方便 createrepo 扫描。如果你不想在 /etc/yum.repos.d 里留痕迹,脚本跑完可以加一句 rm -f /etc/yum.repos.d/offline-gcc.repo,仓库目录也可以保留,之后想补装其他包随时能重建。从那以后,我每次在新机器上搭编译环境,都强制自己走一遍“先查包内依赖、再建本地源、最后跑冒烟测试”的流程,不再凭手感做事。这套脚本连同验证步骤折腾完,再奇怪的 gcc 离线包也能在十分钟内判断出能不能用。希望帮到你。
本文还有配套的精品资源,点击获取