简介:面向在隔离网络或离线环境中部署GCC的Linux运维工程师与开发者,这套离线安装资源将GCC完整编译工具链及其全部依赖打包为可直接安装的RPM集合与自动化脚本,解决无网络时无法通过yum安装的难题。压缩包共12个文件,包含11个rpm与1个sh脚本,覆盖gcc、gcc-c++、cpp、glibc-devel、mpfr、cloog-ppl等核心组件,其中头文件与依赖库均已齐备,整体大小仅24.09MB。安装脚本会先检查系统版本与rpm工具,再利用rpm2cpio解压RPM、按依赖顺序安装,并自动清理临时文件,整个过程无需外网。已有556人浏览学习,适合CentOS/RHEL等基于RPM的Linux发行版,尤其适用于远程服务器、教育机房等受限网络场景。通过这套资源可以一次获取完整依赖链,免去逐一下载RPM的繁复工作,显著提升离线部署效率。
1. 离线装 GCC:难点永远不在 GCC 本身
做了几年系统环境搭建,GitHub 上随手拉个开源项目来编译,本地却报gcc: command not found,这种事情碰到的次数两只手数不过来。更麻烦的环境是那些不允许外网访问的服务器,Yum 源配了三天三夜都连不上,最后只能选择离线安装 GCC。真正的问题在于,GCC 不像普通工具那样一个 RPM 包就完事,它有一堆依赖包要一起装上,那才是让人崩溃的地方。
很多人以为离线安装 GCC 就是找个gcc.rpm拷过去rpm -ivh就好,结果装到一半发现缺libmpfr.so.4、缺cpp、缺gcc-c++,然后开始孤独地在网上找依赖包,找到一个装一个,装完报错,再找下一个。这种打地鼠式的安装过程,GCC 没装好,心态先崩了。
这篇笔记讲的就是怎么把 GCC 离线安装需要的所有 RPM 包装齐、怎么用脚本一键处理依赖顺序,以及装坏了怎么救回来。适合那些给内网服务器搭编译环境、被 GCC 依赖折磨过的开发者,照着操作就能把 C/C++ 编译环境跑起来。
2. 版本选择与环境确认:先把最关键的三个点摸清楚
2.1 查系统版本和架构:这是后面所有步骤的地基
拿到一台离线服务器,先不要急着找安装包,花三分钟确认系统的版本和架构。同样的 GCC 包,在 CentOS 7 和 CentOS 8 上完全不通用,x86_64 和 aarch64 也不通用,版本选错了,后面所有动作都是白费。
# 查看操作系统版本 cat /etc/redhat-release # 查看内核版本(必要时) uname -r # 查看系统架构 uname -m这一步决定了后面在联网机器上下载 RPM 包时,该选哪个版本的源、哪个架构的目录。比如cat /etc/redhat-release显示CentOS Linux release 7.9.2009 (Core),那么下载依赖时就要找 el7 的源;uname -m返回x86_64,就只下载 x86_64 的包,千万不要下载 i686 的包来凑数。整个离线安装流程中,只要架构和系统版本对不上,后边的 rpm 安装阶段就会出现各种奇怪的依赖冲突。
2.2 确认 glibc 版本:最容易忽略的隐藏依赖
GCC 本身不是孤立运行的,它编译出来的程序要链接 glibc,而 GCC 的运行也依赖 glibc 的某些版本特性。在高版本的 CentOS 8 / Rocky Linux 上装老 GCC,通常问题不大;但在 CentOS 7 上强行安装从 CentOS 8 仓库里拉下来的高版本 GCC,就极大概率会因为 glibc 版本过旧而报GLIBC_2.18 not found之类的错误。
# 查看 glibc 版本 ldd --version | head -1常见做法是:glibc 版本决定了 GCC 版本的上限。CentOS 7.9 自带的 glibc 是 2.17,装 GCC 9.x 或 10.x 问题不大,但想从源码编译 GCC 12,某些特性就会遇到问题。选择 GCC 版本时,先看一下目标服务器上的 glibc 版本,这能帮你避开后面五分之一的坑。
2.3 选 GCC 版本:RPM 方式还是源码编译方式
离线安装 GCC 有两种主流路线,选择哪种,取决于你目标服务器的系统版本和你的耐心。
RPM 方式适合系统自带的软件源里能凑齐依赖的场景,比如 CentOS 7 装 GCC 4.8.5,CentOS 8 装 GCC 8.5.0,这些官方源里都有现成的包,离线时只需要把依赖包全部下载好,按顺序安装即可。
源码编译方式适合需要更高版本 GCC 的场景,比如在 CentOS 7 上装 GCC 10.3.0,就得下载 GCC 源码包,然后在有网环境里把 gmp、mpfr、mpc 这三个依赖库的源码也准备好,到目标机器上一股脑编译。但源码编译耗时很长,一台 2 核 4G 的机器编译 GCC 10 可能需要四五十分钟,而且对内存有要求,如果内存不够 2G,编译过程很容易被 OOM killer 干掉。
一般我的做法是:系统自带的 GCC 版本满足需求就直接用 RPM 方式,省时省力;开发项目要求支持 C++17 / C++20,比如要装 GCC 8 以上才能编译的软件,那就老老实实走源码编译路线。
3. 在有网机器上准备离线安装包:把所有依赖一次捞干净
3.1 用 yumdownloader 下载依赖包列表
准备离线包需要在另一台同版本、同架构、能联网的机器上进行。假设目标服务器是 CentOS 7.9 x86_64,那就找一台同样是 CentOS 7.9 x86_64 的联网机器,配好 yum 源后执行下面的操作。
先安装yumdownloader工具,它属于yum-utils包:
# 在有网机器上安装 yum-utils 工具包 yum install -y yum-utils然后先模拟一次安装,把依赖关系弄清楚。yum deplist会列出指定包的依赖关系,虽然不一定拿到完整的递归依赖,但至少可以让你心里有个底。
3.2 用 yumdownloader 配合 --resolve 一次性拉全依赖包
yumdownloader的--resolve参数会自动下载所有依赖包,这是最省事的方式,远比手工查依赖列表再逐个 wget 靠谱。
# 创建存放 RPM 的目录 mkdir -p /data/gcc-rpms cd /data/gcc-rpms # 下载 gcc、gcc-c++ 以及所有依赖,不安装 yumdownloader --resolve --destdir=/data/gcc-rpms gcc gcc-c++ make命令执行完以后,/data/gcc-rpms目录下会有一堆 RPM 文件,包括gcc-4.8.5、gcc-c++-4.8.5、libstdc++-devel、cpp、glibc-devel、libgomp等。其中make是编译时必需的,如果目标机器上连 make 也没有,一定要让它一起下载进来。
有一个坑要注意:yumdownloader --resolve只会下载目标包的依赖,但如果 target 包本身已经被安装在当前联网机器上,它就不会重复下载。遇到这种情况,需要先确认这台机器上确实没有装过 gcc,或者在命令里加--downloadonly并配合临时清空本地安装状态,否则你会发现下载出来的 RPM 目录里少了一半东西。
最保险的检查方式,是在下载之后逐个确认 RPM 文件名里是否都带上了版本号,并且手动核对目录里是否有libmpc、libmpfr这几个关键库,没有的话就得手动补下载:
# 如果缺 gcc 的核心数学依赖库,单独补下载 yumdownloader --destdir=/data/gcc-rpms gmp gmp-devel mpfr mpfr-devel libmpc libmpc-devel这几个库是 GCC 源码编译和运行时的数学运算基础,RPM 安装方式同样需要它们。
3.3 用 createrepo 生成本地 yum 源目录
把所有 RPM 下载到服务器后,直接把这一堆 RPM 拿去rpm -ivh是可以装的,但顺序必须严格正确,一旦顺序错了,依赖检查就会中断。这里我推荐先在有网机器上把这一堆 RPM 制作成本地 yum 源,这样到目标机器上后用yum install就能自动处理依赖顺序,彻底告别手动调顺序的苦日子。
# 安装 createrepo 工具 yum install -y createrepo # 在 RPM 目录下生成 repodata 元数据 createrepo /data/gcc-rpms执行完后,/data/gcc-rpms目录下除了 RPM 文件,还会多出一个repodata目录,这个目录记录了这个 yum 源里所有包的元数据和依赖关系。之后把这整个目录打包拷贝到离线服务器上,通过配置本地 yum 源文件,就可以像在线 yum 一样直接安装 GCC 了。
3.4 打包拷贝到离线服务器
# 打包整个 RPM 目录,包含 repodata tar czf gcc-offline-rpms.tar.gz /data/gcc-rpms拷贝 tar 包到离线目标服务器后解压,目标服务器上不需要安装 createrepo,只需要配置 yum 源指向这个解压出来的目录即可。
如果是选择源码编译路线,那么在有网机器上下载的是源码包,而不是 RPM:
# GCC 官方源码包,按需选 10.3.0 / 9.5.0 等版本 wget https://ftp.gnu.org/gnu/gcc/gcc-10.3.0/gcc-10.3.0.tar.gz # 下载 gcc 自带的依赖库源码包 wget https://ftp.gnu.org/gnu/gmp/gmp-6.2.1.tar.xz wget https://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.xz wget https://ftp.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz注意,GCC 源码包解压后有个contrib/download_prerequisites脚本,它会自动帮你下载 gmp、mpfr、mpc 三个依赖库,但那是针对联网环境的。离线环境下,只能手动把这几个源码包一起拷过去。
4. 离线安装实践:本地 yum 源方式与源码编译两条路都走通
4.1 配置本地 yum 源:三步搞定
在离线服务器上解压 RPM 包后,要做的是配置 yum 源。假设我们把 tar 包解压到了/opt/gcc-rpms:
# 解压 RPM 包 tar xzf gcc-offline-rpms.tar.gz -C /opt/配置文件写在/etc/yum.repos.d/local.repo:
[gcc-local] name=GCC Local Repository baseurl=file:///opt/gcc-rpms enabled=1 gpgcheck=0这里gpgcheck=0是必须的,因为离线 RPM 包没有经过 GPG 签名校验,如果保留gpgcheck=1可能会因为找不到签名信息而拒绝安装。另外要注意,baseurl必须指向包含repodata的那一层目录。
配置完成后,先清理 yum 缓存再检查源是否可用:
yum clean all yum repolist如果输出里出现了gcc-local,说明本地源已经生效。
4.2 用 yum 自动安装 GCC:依赖顺序交给 yum 处理
这一步就很简单了,直接像在线安装一样执行:
yum install -y gcc gcc-c++ makeyum 会扫描/opt/gcc-rpms里的所有 RPM,自动识别依赖关系,按照依赖顺序安装。整个过程不需要手动指定任何 RPM 包顺序,之前用rpm -ivh各种报错的场景彻底消失了。
如果是安装完成后,想确认 gcc 版本,用gcc --version查看,应该会得到 4.8.5 之类的系统版本号。
4.3 源码编译 GCC:配置三步和参数取舍
如果目标场景要求 GCC 10 或更高版本,RPM 方式就搞不定了,这时要走源码编译。先把源码包解压好,然后统一编译安装 gmp、mpfr、mpc 三个依赖库。
# 创建统一的安装前缀目录,方便后面配置环境变量和清理 mkdir -p /usr/local/gcc-10.3.0 # 分别编译三个依赖库,指定安装到同样的前缀目录 tar xf gmp-6.2.1.tar.xz cd gmp-6.2.1 ./configure --prefix=/usr/local/gcc-10.3.0 make -j4 make install cd .. tar xf mpfr-4.1.0.tar.xz cd mpfr-4.1.0 ./configure --prefix=/usr/local/gcc-10.3.0 --with-gmp=/usr/local/gcc-10.3.0 make -j4 make install cd .. tar xf mpc-1.2.1.tar.gz cd mpc-1.2.1 ./configure --prefix=/usr/local/gcc-10.3.0 --with-gmp=/usr/local/gcc-10.3.0 --with-mpfr=/usr/local/gcc-10.3.0 make -j4 make install cd ..关于make -j4:-j4表示用 4 个并行任务编译,理论上能显著缩短编译时间。但这里有个取舍,如果目标服务器是 2 核 CPU,用-j4反而会拖慢速度甚至把内存吃满,一般按核数减一比较合适,2 核机器用-j2即可。
mpfr 和 mpc 的 configure 必须显式指定依赖库的安装路径,不指定的话它们会去/usr/local/lib下找 gmp,找不到就报错后退出。
依赖库装完后,接着编译 GCC 本体:
tar xf gcc-10.3.0.tar.gz cd gcc-10.3.0 # 创建独立的编译目录,GCC 官方推荐不要直接在源码目录里编译 mkdir build cd build # 配置 GCC,指定依赖库路径和安装位置 ../configure \ --prefix=/usr/local/gcc-10.3.0 \ --with-gmp=/usr/local/gcc-10.3.0 \ --with-mpfr=/usr/local/gcc-10.3.0 \ --with-mpc=/usr/local/gcc-10.3.0 \ --enable-languages=c,c++ \ --disable-multilib make -j4 make install这里--enable-languages=c,c++指定了只编译 C 和 C++ 编译器,如果省略这个参数,GCC 默认会把所有支持的语言都编译一遍,包括 Fortran、Go 等,时间翻倍还不一定用得上。--disable-multilib是告诉 GCC 只生成 64 位版本,不生成 32 位兼容库,如果没有 32 位需求,加上这个参数能省不少编译时间。
整个编译过程耗时的确很长。CentOS 7 的 2 核 4G 机器编译 GCC 10,普遍要跑四五十分钟,中间如果内存不足,会被系统直接杀进程。一般做法是编译之前free -h看一眼内存,低于 2G 就考虑加 swap。
4.4 脚本化整个源码编译过程
把上面所有步骤串成一个脚本,离线环境下一键执行:
#!/bin/bash # 一键离线编译安装 GCC 10.3.0 # 适用:CentOS 7 x86_64 离线服务器 # 用法:先把源码包放到 /opt/gcc-src 下,再执行脚本 set -e GCC_VERSION=10.3.0 PREFIX=/usr/local/gcc-10.3.0 SRC_DIR=/opt/gcc-src CPU_NUM=$(nproc) # 减去 1 避免把 CPU 完全占满 if [ "$CPU_NUM" -gt 1 ]; then MAKE_JOBS=$((CPU_NUM - 1)) else MAKE_JOBS=1 fi mkdir -p "$PREFIX" cd "$SRC_DIR" # 解压 gmp mpfr mpc 并编译安装 for pkg in gmp-6.2.1 mpfr-4.1.0 mpc-1.2.1; do tar xf "$pkg".tar.* 2>/dev/null || true done脚本写到这里有一个细节:tar xf "$pkg".tar.*这行用了通配符来兼容.tar.xz和.tar.gz两种格式,但如果目录里同时存在多个匹配文件,tar 可能无法正确处理。更稳妥的做法是用ls拿到具体的文件名再解压,或者直接用tar xf时明确指定完整文件名,不要偷懒依赖通配符。
编译安装三个依赖库后,再解压 GCC 源码包并编译安装,最后把export PATH=/usr/local/gcc-10.3.0/bin:$PATH写入/etc/profile.d/gcc.sh,保证新开的 shell 里能直接找到新版 gcc。
4.5 环境变量与动态库配置
源码编译的 GCC 安装完成后,如果不做环境变量配置,直接执行gcc --version看到的仍然是系统自带的旧版 gcc。这时候必须显式把新版的路径放到 PATH 前面。
# 写入系统环境变量配置 cat > /etc/profile.d/gcc.sh << 'EOF' export PATH=/usr/local/gcc-10.3.0/bin:$PATH export LD_LIBRARY_PATH=/usr/local/gcc-10.3.0/lib64:/usr/local/gcc-10.3.0/lib:$LD_LIBRARY_PATH EOF # 立即生效 source /etc/profile.d/gcc.shLD_LIBRARY_PATH很关键。新版 GCC 编译出来的程序,运行时需要找到新版 libstdc++.so.6,如果你不在LD_LIBRARY_PATH里加入新版本 lib 目录,编译出来的程序在别的机器上跑会报libstdc++.so.6: version GLIBCXX_3.4.26 not found。这个坑非常典型,解决方案就是让运行环境能提前找到新版动态库。
5. 离线安装避坑指南:三个真实的踩坑记录
5.1 用 rpm -ivh 手动安装导致依赖地狱
现象:把下载下来的 RPM 包直接拿到服务器上rpm -ivh gcc-*.rpm,结果报错libmpfr.so.4()(64bit) is needed by gcc-...,于是手动装 libmpfr,装完又报libmpc.so.3()(64bit) is needed by...,陷入无限循环。
原因:RPM 包之间的依赖是链式的,rpm -ivh不会自动解析依赖关系,只会检查当前系统里是否已经存在依赖项。一次性把几十个 RPM 全塞给 rpm 命令时,它虽然会尝试按顺序安装,但依赖链一旦出现环状或顺序错误,就直接卡死。
解决:不要跟 rpm 命令的依赖解析硬碰硬。把 RPM 目录配置成本地 yum 源后,用yum install gcc一条命令让 yum 自己处理依赖顺序。yum 是基于 RPM 的更高层工具,它能根据 rpmdb 的依赖关系计算出合理的安装顺序,这是最省心的路径。
5.2 源里缺少 gcc-c++ 导致 C++ 程序无法编译
现象:离线安装完 gcc 后,C 代码能正常编译,可g++或者编译.cpp文件时报g++: command not found。
原因:yumdownloader --resolve gcc只下载了gcc包及其依赖,而 gcc 包里并不含 G++,G++ 属于gcc-c++包。gcc-c++不是gcc的依赖,所以不会被 yumdownloader 自动拉下来。
解决:下载 RPM 包时,命令写成yumdownloader --resolve gcc gcc-c++ make,把 gcc-c++ 和 make 一起放进列表。同样的道理,如果之后要编译 Fortran,还要单独下载gcc-gfortran。别指望 gcc 一个包覆盖所有语言,这是新手最容易漏掉的地方。
5.3 源码编译 GCC 中途被 OOM killer 杀死
现象:编译 GCC 源码时,终端突然输出Killed,没有任何错误信息,dmesg | tail里看到Out of memory: Kill process。
原因:GCC 编译过程中,尤其是 C++ 编译器编译自身时,内存占用非常恐怖。一台 1G 内存的机器编译 GCC 10,稍不注意内存就耗尽,系统会强制杀掉最占内存的进程。
解决:先看内存再决定是否编译。free -h如果 total 少于 2G,建议先增加 swap:dd if=/dev/zero of=/swapfile bs=1M count=4096 && mkswap /swapfile && swapon /swapfile临时加 4G swap,或者干脆换 RPM 安装路线,源码编译等升级硬件后再做。
5.4 新 GCC 编译出来的程序在旧系统上报 GLIBCXX 版本错误
现象:在 CentOS 7 上用新编译的 GCC 10 编译了一个程序,拷贝到另一台 CentOS 7 机器运行时,报./a.out: /usr/lib64/libstdc++.so.6: version GLIBCXX_3.4.26 not found。
原因:新 GCC 编译的程序链接的是新版 libstdc++.so.6 里的符号,而目标机器的系统库路径里只有旧版本的 libstdc++.so.6,缺少新符号表,所以报错。
解决:两个办法任选其一。一是静态链接,编译时加-static-libstdc++ -static-libgcc,把 C++ 标准库打进可执行文件;二是把新版的libstdc++.so.6拷贝到目标机器上,用LD_LIBRARY_PATH指向新 lib 目录。第一种方式更干净,省得在目标机器上折腾环境。
6. 装完怎么验证跑通:从版本检查到真实编译实测
6.1 验证 GCC 本体安装状态
装完 GCC 后,第一件事是验证版本号和你预期的是否一致,同时确认相关工具链是否都齐了。
# 检查 gcc 版本和 g++ 版本 gcc --version g++ --version # 检查编译相关的几个核心辅助工具是否就位 whereis make whereis ld whereis as预期输出里 gcc 和 g++ 的版本号应该完全一致,比如都是 10.3.0 或者都是 4.8.5。如果 gcc 有版本而 g++ 提示找不到命令,说明 gcc-c++ 没有装成功,需要回头检查安装过程。
然后编译一个最简单的 Hello World 验证基本编译链路:
# 写一个 3 行的测试代码 cat > /tmp/hello.c << 'EOF' #include <stdio.h> int main() { printf("GCC offline install test passed!\n"); return 0; } EOF gcc /tmp/hello.c -o /tmp/hello /tmp/hello如果输出GCC offline install test passed!,说明编译器本体、汇编器、链接器、C 标准库的头文件路径和动态库路径都配置正确了。这一步能排除 90% 的环境问题。
6.2 验证 C++ 编译能力与标准库运行
C 能编译不代表 C++ 也能编译,所以单独再测一次:
cat > /tmp/hello.cpp << 'EOF' #include <iostream> int main() { std::cout << "C++ compiler works." << std::endl; return 0; } EOF g++ /tmp/hello.cpp -o /tmp/hello_cpp /tmp/hello_cpp这里有一个值得知道的细节:如果/tmp/hello_cpp运行时报libstdc++.so.6: version GLIBCXX_3.4.x not found,说明新 g++ 编译出的二进制运行时找到了旧的 libstdc++,需要回到第 4 章配置LD_LIBRARY_PATH或者用-static-libstdc++重新编译。
然后再验证一下 GCC 10 的 C++17 特性支持情况,这是很多人装新 GCC 的真实原因:
cat > /tmp/check_cpp17.cpp << 'EOF' #include <string_view> #include <iostream> int main() { std::string_view sv("GCC17"); std::cout << sv << std::endl; return 0; } EOF g++ -std=c++17 /tmp/check_cpp17.cpp -o /tmp/check_cpp17 /tmp/check_cpp17能输出GCC17,说明 C++17 标准库头文件和运行时都具备,可以放心交给开发者去编译项目了。
从那次把离线 GCC 环境反复装坏又救回来之后,我每配置完一套编译环境,都会强制自己走一遍这几条验证命令,尤其是 C 和 C++ 分开测、再测一次高版本特性,确认没问题才交付出去。一套环境跑不通,后面所有基于它的项目都会白白浪费精力,提前花三分钟验证,省下的是后面一整天的排查时间。希望这篇笔记里的流程和踩坑记录能帮到你,少走几段弯路。
拿到资料,照着装,装完跑一遍验证,就能确定这套环境能不能收了。
本文还有配套的精品资源,点击获取