
简介这份压缩包面向需要在无外网或内网隔离环境中部署编译工具链的Linux运维与开发人员聚焦CentOS/RHEL 6等老系统的GCC离线安装难题提供成套RPM包与自动化脚本免去逐项搜寻依赖的麻烦适合具备基础包管理经验、需要在内网补齐编译环境的中级读者。压缩包共12个文件以11个rpm安装包和1个sh执行脚本为主整体约24.09MBRPM涵盖GCC、G、cpp、glibc头文件与开发库、kernel-headers以及mpfr、gmp、ppl、cloog等依赖脚本用于串联安装流程包体结构紧凑、便于随包分发。已有558人学习说明其在老旧服务器维护、教学实验环境搭建等场景中具有一定参考价值。读者可获得一套可离线落地的GCC 4.4.7工具链组件与配套安装脚本便于快速补齐编译环境、减少依赖冲突排查时间也可为内网软件源制作、批量部署和故障恢复提供素材。1. 内网机器装 GCC为什么“所有包脚本”比单个 tar.gz 更稳某内网开发机只带一版老 GCC编译一个 C 库时直接报#error要求 C17系统包管理器又连不上源。A同学的第一反应是拷一个 GCC 源码包进去结果卡在 gmp、mpfr、mpc 的版本检查上来回折腾两天。这份“gcc离线安装所有包以及脚本”解决的就是这个场景把 GCC 源码、依赖源码、校验清单、编译顺序和环境变量脚本收在一起让离线机器按固定顺序产出可用的工具链。它适合内网运维、离线 CI 节点、嵌入式工具链搭建也适合需要固定 GCC 版本复现构建的开发者。血泪经验是离线装 GCC 最怕的不是编译慢而是依赖顺序、动态库路径和 prefix 混用脚本的价值在于把这些动作固化成可重跑的流程而不是替你猜系统环境。2. 离线包清单与依赖拓扑把“所有包”拆成可核对的表“所有包”这四个字容易被误解成整个发行版仓库。对 GCC 离线安装来说它通常指让 GCC 从源码编译通过所需的最小依赖集合GCC 本体源码加上 gmp、mpfr、mpc、isl必要时再加 zlib。这个集合不是拍脑袋凑的而是由 GCC 的 configure 检查逻辑和构建链路决定的。先把依赖拓扑和目录规划理清后面脚本才不会写成一条“看起来能跑、换台机器就翻车”的长命令。2.1 GCC 源码包不是自包含的依赖拓扑先理清GCC 的源码包很大但它不会把数学库和优化库一起塞进来。gmp 提供大整数运算mpfr 建立在 gmp 之上做多精度浮点mpc 又依赖 gmp 和 mpfr 做复数运算isl 主要服务 Graphite 循环优化某些配置下 GCC 会检查它zlib 则和压缩调试信息、部分宿主库支持有关。GCC 的 configure 阶段会检查这些依赖的最低版本缺失时常见报错是Building GCC requires GMP 4.2, MPFR 3.1.0 and MPC 0.8.0。源码包里虽然有contrib/download_prerequisites这类辅助脚本但它的默认动作是联网下载内网环境用不了所以离线资源必须提前把源码包和校验清单准备好。选型上我倾向于把依赖装到独立前缀而不是直接塞进/usr。原因是内网机器上往往已经有一套系统 GCC系统库和自编译库混在一起后出问题很难判断是哪个libgmp被加载。独立前缀可以是/opt/offline-gcc/depsGCC 自己再装到/opt/offline-gcc/gcc/${GCC_VERSION}。这样做的好处是回滚简单依赖和编译器各自一个目录升级时保留旧目录即可。代价是编译 GCC 时要显式传--with-gmp、--with-mpfr、--with-mpc、--with-isl路径必须指向依赖的安装前缀不是源码目录。另一个要提前定的是静态库还是动态库。离线机器上动态库路径最容易出问题编译时找到了运行程序时又找不到libmpfr.so。常见做法是依赖库用--disable-shared --enable-static让 GCC 链接静态依赖减少运行期LD_LIBRARY_PATH的依赖。如果多个工具要共用依赖、磁盘空间紧张也可以开共享库但脚本里必须配套 rpath 或 ldconfig 配置。两种方案没有绝对优劣关键是别一半静态一半动态最后连自己都说不清加载的是哪一份。目录规划也要在动手前定好。源码目录、构建目录、安装目录必须分开GCC 不支持在源码目录里直接./configure否则构建产物和源码混在一起清理和重跑都很痛苦。我一般会留四个顶层目录src放压缩包build放解压后的构建树deps放依赖安装结果gcc放 GCC 安装结果。后面所有脚本变量都从这个根目录派生换机器时只改根路径和版本变量不动脚本主体。2.2 包清单表与校验先让文件“对得上”离线包最怕的不是编译报错而是压缩包在拷贝过程中损坏解压到一半才报xz: (stdin): Unexpected end of input。所以脚本第一步不是tar -xf而是校验。下面这张表是我会放在资源根目录的核对清单实际文件名里的版本号用变量替换不要写死。包名作用是否必需检查方式常见坑gcc-${GCC_VERSION}.tar.xzGCC 本体源码必需tar -tf看顶层目录xz -t测完整性顶层目录名和脚本变量不一致gmp-${GMP_VERSION}.tar.xz大整数、有理数运算必需检查gmp.h和libgmp.a只装了运行库没装开发头文件mpfr-${MPFR_VERSION}.tar.xz多精度浮点必需检查mpfr.h和libmpfr.ampfr 编译时找不到 gmpmpc-${MPC_VERSION}.tar.xz复数运算必需检查mpc.h和libmpc.a依赖 gmp/mpfr 的顺序颠倒isl-${ISL_VERSION}.tar.xzGraphite 循环优化视配置而定检查isl.h和libisl.aGCC configure 找不到 isl 时被静默跳过zlib-${ZLIB_VERSION}.tar.xz压缩相关支持可选检查zlib.h和libz.a和系统 zlib 混用导致链接冲突校验命令不要写成花哨的一行流离线环境里越直白越好。下面这段脚本先校验哈希再检查每个压缩包的顶层目录和压缩流完整性。sha256sum -c会从清单文件里逐行读取“哈希 相对路径”所以必须先cd到清单所在目录否则相对路径对不上。#!/usr/bin/env bash set -euo pipefail OFFLINE_ROOT/opt/offline-gcc SRC_DIR${OFFLINE_ROOT}/src CHECK_FILE${OFFLINE_ROOT}/sha256sums.txt # 1) 哈希校验文件损坏时在这里就停下不要带病解压 cd ${OFFLINE_ROOT} sha256sum -c ${CHECK_FILE} # 2) 检查压缩包顶层目录确认解压后目录名与脚本变量一致 for pkg in ${SRC_DIR}/*.tar.xz; do echo ${pkg} tar -tf ${pkg} | head -n 1 xz -t ${pkg} done这段脚本里OFFLINE_ROOT是资源根目录换成实际路径即可CHECK_FILE是哈希清单格式通常是一行一个文件路径相对于OFFLINE_ROOT。tar -tf只列出内容不真正解压用来确认压缩包顶层是gcc-${GCC_VERSION}/这类单目录结构xz -t测试压缩流能提前发现拷贝不完整。参数上唯一要改的是根路径和清单文件名不建议在脚本里写绝对哈希值否则换一个补丁版本就要改脚本。清单本身也要有规矩。哈希文件里不要出现绝对路径不要出现./和../混用否则sha256sum -c在不同 shell 下行为容易不一致。压缩包命名统一用包名-版本.tar.xz不要用gcc.tar.xz这种没有版本信息的名字。离线资源经常被拷来拷去半年后没人记得gcc.tar.xz是哪一版脚本也没法从文件名推导变量。版本变量集中在脚本头部文件名、目录名、安装前缀都从变量拼出来后面换版本只改变量。2.3 目录与版本变量把可变的都放到脚本头部离线安装脚本最容易犯的错是把路径和版本散落在几十行命令里。今天装在/opt/gcc明天换到/data/toolchain改漏一处就出现“编译用的是新目录、安装写进了旧目录”。更稳的做法是先定目录树再把所有可变项收进一个config.env或脚本头部。下面这棵目录树不是硬性标准但它把源码、构建、依赖、安装、日志和状态文件分开了排错时一眼能看出该看哪里。offline-gcc/ ├── src/ │ ├── gcc-${GCC_VERSION}.tar.xz │ ├── gmp-${GMP_VERSION}.tar.xz │ ├── mpfr-${MPFR_VERSION}.tar.xz │ ├── mpc-${MPC_VERSION}.tar.xz │ ├── isl-${ISL_VERSION}.tar.xz │ └── zlib-${ZLIB_VERSION}.tar.xz ├── scripts/ │ ├── 00-check-env.sh │ ├── 10-build-deps.sh │ ├── 20-build-gcc.sh │ ├── 30-env.sh │ └── 40-verify.sh ├── build/ ├── deps/ ├── gcc/ ├── logs/ ├── .state/ └── sha256sums.txt这棵树里deps是依赖安装前缀gcc是 GCC 安装前缀build放解压后的构建树logs收编译日志.state放断点标记。变量可以写成这样ROOT/opt/offline-gcc、DEPS_PREFIX${ROOT}/deps、GCC_PREFIX${ROOT}/gcc/${GCC_VERSION}。参数怎么改换安装盘就改ROOT换 GCC 版本就改GCC_VERSION和对应依赖版本小内存机器把JOBS从默认值改成 2 或 1。不要把DEPS_PREFIX设成/usr也不要把GCC_PREFIX设成/usr否则系统自带编译器和库会被覆盖回滚时没有后悔药。还有一个常见误用是“依赖装一份GCC 编译时又用系统另一份”。比如系统里有/usr/lib/libgmp.so你又装了${DEPS_PREFIX}/lib/libgmp.a但 configure 时只传了LDFLAGS没传--with-gmpGCC 可能一半用新头文件、一半链旧库最后报符号版本不匹配。正确做法是让--with-gmp、--with-mpfr、--with-mpc、--with-isl全部指向同一个${DEPS_PREFIX}并在config.log里确认它实际找到的路径。这个检查后面排错章节还会展开。3. 从源码到可用GCC 与 gmp/mpfr/mpc/isl 的编译顺序和参数依赖拓扑理清后真正落地就是编译顺序和参数。离线安装 GCC 不是“把最大那个包编译完就结束”而是先把依赖按顺序装到独立前缀再让 GCC 的 configure 找到它们。顺序错了mpfr 会因为找不到 gmp 停下路径传错了GCC 会在config.log里写一堆“checking for gmp... no”但终端只给你一句很短的报错。这一章按依赖编译、GCC configure、安装后环境接入三段拆开每段都给出可抄的脚本和参数说明。3.1 编译顺序gmp → mpfr → mpc → isl → gcc顺序不是玄学是依赖关系决定的。gmp 没有前置依赖先装mpfr 需要 gmp所以第二mpc 同时需要 gmp 和 mpfr第三isl 需要 gmp通常放在 mpc 之后、GCC 之前GCC 最后编译configure 时用--with-*指向前四个的安装前缀。zlib 视配置而定如果系统已经有开发头文件和静态库可以先用系统的如果系统版本太旧或缺失再单独编译到DEPS_PREFIX。下面这段脚本用函数化方式编译四个依赖。每个包解压到独立的build子目录--strip-components1去掉压缩包顶层目录避免出现build/gmp-${GMP_VERSION}/gmp-${GMP_VERSION}/configure这种双层路径。--disable-shared --enable-static让依赖以静态库形式安装减少运行期动态库路径问题gmp 额外加--enable-cxx是因为某些构建链路会用到它的 C 接口提前打开比后面补编译省事。#!/usr/bin/env bash set -euo pipefail SRC_DIR/opt/offline-gcc/src BUILD_DIR/opt/offline-gcc/build DEPS_PREFIX/opt/offline-gcc/deps JOBS${JOBS:-2} build_dep() { local name$1 local version$2 local extra_flags${3:-} local src${SRC_DIR}/${name}-${version}.tar.xz local build${BUILD_DIR}/${name}-${version} mkdir -p ${build} tar -xf ${src} -C ${build} --strip-components1 cd ${build} # 依赖统一进独立前缀静态库优先减少运行期找 .so 的麻烦 ./configure \ --prefix${DEPS_PREFIX} \ --disable-shared \ --enable-static \ ${extra_flags} \ configure-${name}.log 21 make -j${JOBS} make-${name}.log 21 make install install-${name}.log 21 } build_dep gmp ${GMP_VERSION} --enable-cxx build_dep mpfr ${MPFR_VERSION} build_dep mpc ${MPC_VERSION} build_dep isl ${ISL_VERSION}这段脚本的关键参数是--prefix、--disable-shared、--enable-static和JOBS。--prefix必须和 GCC configure 里的--with-*完全一致差一个斜杠都可能让 configure 找不到库。JOBS默认给 2 是保守值GCC 依赖库本身不算特别吃内存但同一台机器上可能还要跑别的构建任务直接-j$(nproc)容易把内存打满。日志重定向到configure-${name}.log、make-${name}.log失败时先看日志末尾不要只看终端最后一行。失败时看什么如果 gmp 的configure报 C 编译器不可用检查g是否存在如果 mpfr 报gmp.h not found说明 gmp 没装进${DEPS_PREFIX}或CPPFLAGS没带上如果 mpc 报libmpfr找不到检查 mpfr 的make install是否真的写进了${DEPS_PREFIX}/lib。常见误用是只编译不安装然后直接用源码目录去给下一个包传路径configure 能找到头文件但链接时找不到库最后报一堆 undefined reference。3.2 GCC configureprefix、依赖路径、语言集和 bootstrapGCC 的 configure 参数比依赖多但离线场景真正要盯住的是四类装到哪里、依赖在哪、编译哪些语言、要不要 bootstrap。--prefix决定安装目录--with-gmp、--with-mpfr、--with-mpc、--with-isl决定依赖路径--enable-languagesc,c决定语言集只装 C 和 C 能显著缩短构建时间--disable-multilib在不需要 32 位库时可以减少构建量。bootstrap 默认开启会用刚编出来的编译器再编一遍自己耗时长但能验证自举链路如果只是内网快速出工具链可以加--disable-bootstrap但要清楚这降低了构建可信度。#!/usr/bin/env bash set -euo pipefail SRC_DIR/opt/offline-gcc/src BUILD_DIR/opt/offline-gcc/build DEPS_PREFIX/opt/offline-gcc/deps GCC_PREFIX/opt/offline-gcc/gcc/${GCC_VERSION} GCC_SRC${SRC_DIR}/gcc-${GCC_VERSION}.tar.xz BUILD${BUILD_DIR}/gcc-${GCC_VERSION} mkdir -p ${BUILD} ${GCC_PREFIX} tar -xf ${GCC_SRC} -C ${BUILD} --strip-components1 cd ${BUILD} # 依赖路径指向 deps 安装前缀不是源码目录 ./configure \ --prefix${GCC_PREFIX} \ --enable-languagesc,c \ --disable-multilib \ --with-gmp${DEPS_PREFIX} \ --with-mpfr${DEPS_PREFIX} \ --with-mpc${DEPS_PREFIX} \ --with-isl${DEPS_PREFIX} \ --enable-checkingrelease \ configure-gcc.log 21 make -j${JOBS:-2} make-gcc.log 21 make install install-gcc.log 21参数说明--enable-languagesc,c按需加fortran、go等语言越多构建越久--disable-multilib只在不需要 32 位运行库时用需要 multilib 就删掉--enable-checkingrelease减少调试检查适合生产工具链--with-system-zlib可选系统 zlib 开发包完整时可以用否则让 GCC 用自带的压缩支持。--with-gmp等参数的值是安装前缀不是源码目录也不是lib子目录configure 会自己拼include和lib。如果 configure 失败先看configure-gcc.log末尾再搜config.log里的gmp、mpfr、mpc、isl。常见现象是终端只报configure: error: cannot find gmp但config.log里会写它试了哪些路径、编译哪个测试程序失败。另一个常见误用是只传LDFLAGS-L${DEPS_PREFIX}/lib不传--with-gmp结果头文件路径仍然不对编译测试程序时gmp.h找不到。把--with-*传全比事后补环境变量稳。3.3 安装后环境接入PATH、动态库和 rpathmake install成功不等于工具链可用。新 GCC 装在独立前缀下当前 shell 的PATH还指向系统旧编译器直接敲gcc可能还是旧版本。环境接入要做三件事把${GCC_PREFIX}/bin放到PATH前面如果依赖或 GCC 用了共享库把${GCC_PREFIX}/lib64和${GCC_PREFIX}/lib加进当前 shell 的LD_LIBRARY_PATH用gcc -v和gcc -print-search-dirs确认实际调用的路径。不要把这三件事直接写进/etc/profile否则所有登录用户、所有程序都会加载新库老程序可能因为GLIBCXX版本不匹配直接起不来。#!/usr/bin/env bash # 只在当前 shell 生效退出后不污染系统环境 export GCC_HOME/opt/offline-gcc/gcc/${GCC_VERSION} export PATH${GCC_HOME}/bin:${PATH} export LD_LIBRARY_PATH${GCC_HOME}/lib64:${GCC_HOME}/lib:${LD_LIBRARY_PATH:-} # 验证实际调用的编译器和搜索目录 command -v gcc gcc -v 21 | tail -n 5 gcc -print-search-dirs这段脚本里GCC_HOME是安装前缀PATH把新编译器放到最前LD_LIBRARY_PATH只对当前 shell 生效。lib64和lib都加上是因为不同配置下 GCC 可能把运行库装到其中一个目录用gcc -print-search-dirs能看到它实际搜索哪些路径。如果只给当前项目用更稳的做法是写一个env.sh在项目构建脚本里source它而不是全局 export。如果希望多用户共享可以用/etc/ld.so.conf.d/加一条路径再ldconfig但这需要 root而且会影响系统全局库搜索顺序。我的习惯是优先用 rpath编译业务程序时加-Wl,-rpath,${GCC_HOME}/lib64让程序自己记住库路径不依赖 shell 环境。代价是二进制文件里多一条 RUNPATH迁移到别的机器时如果路径不存在仍然会找不到库。两种方式各有边界离线工具链场景下先保证当前 shell 可控再考虑全局配置。4. 脚本化落地把离线安装拆成可重跑的五个阶段离线安装 GCC 的脚本不该写成一条几百行的“大泥球”。GCC 构建动辄几十分钟中途可能因为磁盘满、权限错、内存不足、压缩包损坏中断如果每次失败都从头再来时间成本很高。更合理的做法是把流程拆成检查、依赖、GCC、环境、验证五个阶段每个阶段有独立日志和状态标记失败时只重跑失败阶段。下面按主脚本、依赖函数、断点续跑三块拆开代码不追求花哨追求换台机器改几个变量就能跑。4.1 脚本分五段检查、解压、依赖、GCC、验证主脚本只做调度不把编译细节塞进来。它先确认基础命令存在再依次调用依赖编译、GCC 编译、环境加载和验证脚本。set -euo pipefail让管道中任何一段失败都能停下避免tee把错误吞掉ROOT用脚本所在目录反推资源包整体拷到别的路径也能用GCC_VERSION必须显式设置避免脚本猜错版本JOBS允许外部覆盖小内存机器可以传JOBS1。#!/usr/bin/env bash set -euo pipefail ROOT$(cd $(dirname $0)/.. pwd) LOG_DIR${ROOT}/logs STATE_DIR${ROOT}/.state GCC_VERSION${GCC_VERSION:?请设置 GCC_VERSION} JOBS${JOBS:-2} mkdir -p ${LOG_DIR} ${STATE_DIR} log() { printf [%s] %s\n $(date %F %T) $*; } die() { log ERROR: $*; exit 1; } need_cmd() { command -v $1 /dev/null 21 || die 缺少命令: $1 } for c in tar xz make gcc g m4 patch; do need_cmd $c done # 依赖和 GCC 分开跑失败时先看对应日志不要混在一起 ${ROOT}/scripts/10-build-deps.sh 21 | tee ${LOG_DIR}/deps.log ${ROOT}/scripts/20-build-gcc.sh 21 | tee ${LOG_DIR}/gcc.log log 构建完成加载环境并做基础验证 source ${ROOT}/scripts/30-env.sh ${ROOT}/scripts/40-verify.sh 21 | tee ${LOG_DIR}/verify.log参数说明ROOT是资源根目录由脚本位置反推GCC_VERSION必须由调用者设置避免默认值写死JOBS默认 2内存小于 4GB 的机器建议显式传 1need_cmd检查的是构建基础工具m4和patch在 GCC 构建里经常被忽略缺了会在很晚的阶段才报错。tee保留终端输出和日志文件排错时直接看logs/gcc.log最后几十行。常见误用是把source 30-env.sh本文还有配套的精品资源点击获取