简介:gcc-9.5.0.tar.gz 是 GNU Compiler Collection 9.5.0 版本的完整源码压缩包,面向需要在 Linux/Unix 环境中编译、定制或研究编译器的开发者与学生,也适合高校编译原理课程与开源项目维护者使用。该版本为 GCC 9 系列的最后修订版,包含 C、C++、Objective-C、Fortran、Ada 等语言前端与后端实现,并附带 configure、Makefile 等构建脚本,可直接解压后按需配置、编译与安装。压缩包为 gzip 格式,大小约 122.2MB;文件总数与类型明细上游未提供,故不赘述,从文件名看内部为 gcc-9.5.0 单层目录。目前已有 170 人学习下载。对开发者而言,获取此源码既能深入理解编译器从配置、编译到安装的完整流程,也可按场景裁剪组件、增加自定义特性或用于二次移植;gcc 与 g++ 同时支持 C 和 C++ 语言,适合学习语言实现、工具链构建与自由软件开源协作;GCC 作为 GNU/Linux 系统事实上的标准编译器,其源码对构建自定义工具链和排查编译问题尤其有帮助,是进入编译器技术领域的可靠参考资料。
1. 为什么系统自带的 gcc 不是 9.5.0,你得自己编一份
系统预装的 gcc 往往被发行版版本锁死:Ubuntu 18.04 停在 7.5,CentOS 7.9 停在 4.8.5,而项目里可能写着-std=c++17 required。与其四处打补丁绕编译错误,不如直接把gcc-9.5.0.tar.gz拿下来从源码编一个干净版本。9.5.0 是 GCC 9 系的收尾版本,2022 年 5 月发布,修掉了 9.3、9.4 里一批错乱优化问题,在兼容老代码的同时完整支持 C++17,至今仍是很多 CI 镜像和嵌入式交叉工具链的基线。这篇文章面向被系统包管理器版本卡住、又不想冒险上 12/13 的从业者:从拿到 tar.gz 开始,把依赖、configure 参数、编译、多版本共存和排错串成一条能照做的稳妥路径。
2. 编译 GCC 9.5.0 前的三个决定:版本定位、依赖库与 configure 选型
很多人拿到gcc-9.5.0.tar.gz第一反应是解压、./configure && make,二十分钟后在报错堆里才意识到这玩意没那么简单。在动手之前,有三个决定会直接影响你后面会不会翻车。
2.1 GCC 9.5.0 的定位:9 系终版,C++17 的稳妥边界
GCC 的版本节奏是每年一个大版本,9.1.0 在 2019 年发布,之后 9.2、9.3、9.4 一路修补,9.5.0 是这条线的最终维护版。它和 10、11、12 这些“新世界”最大的区别在于:默认语言标准是 C++14,完整支持 C++17(-std=c++17),C++20 只能以实验性-std=c++2a方式使用。这个特性组合恰好卡在“老代码能编、新代码也能跑”的甜区。
为什么不直接上 12 或 13?最常见的理由是目标机器的 glibc 版本太旧。GCC 12 编出来的二进制会引用较新的 glibc 符号版本,把它拷到 CentOS 7.9 这类老系统上会直接version GLIBC_2.27 not found。GCC 9.5.0 的产物符号需求温和得多,这也是生产环境里“编译机可以新,运行机必须老”时首选 9 系的原因。另一个理由是 ABI 噪音:GCC 9 的 libstdc++ ABI 与 GCC 8 保持一致,老库、老驱动不需要跟着换。至于 LLVM、GCC、MSVC 三者怎么选,在业务代码层面可以争论,但内核模块、GNU 扩展语法、老工程的 Makefile 生态仍然以 GCC 为底座,手动编一份 9.5.0 是成本最低的兼容方案。
2.2 真正的隐藏难点:GMP、MPFR、MPC 三个数学库
编译器不是孤立存在的,GCC 在做常量折叠、浮点变换和复杂表达式化简时,依赖三个外部数学库:GMP 负责任意精度整数运算,MPFR 负责高精度浮点,MPC 负责复数运算。GCC 的 configure 脚本会检查它们是否存在以及版本是否达标,缺任何一个都会在配置阶段直接退出。它们本身也是独立项目,源码编译需要额外时间,所以这一步是新手和老手都容易卡住的地方。
处理这三者的常见做法有三条。第一条是依赖系统包管理器的开发包,Ubuntu 上用:
apt install -y libgmp-dev libmpfr-dev libmpc-devCentOS / Kylin 这类 yum 系上对应:
yum install -y gmp-devel mpfr-devel libmpc-devel第二条是手动编译三个库,再通过--with-gmp、--with-mpfr、--with-mpc告诉 GCC 去哪找;第三条最省事,直接使用 GCC 源码自带的一键脚本./contrib/download_prerequisites,它会把三个库的源码按 GCC 9.5.0 要求的版本拉下来解压到当前目录。我一般优先用第三条,因为它会固定到 GCC 官方验证过的版本组合,系统包里 libmpc 版本太旧导致 feature 探测行为变化,这类“玄学”问题能少一大半。
2.3 configure 前先想清楚的三件事
第一是前缀目录。不要默认装到/usr或/usr/local,把 9.5.0 装进独立目录,例如/opt/gcc-9.5.0。原因很现实:直接覆盖系统 gcc 会导致 glibc 头文件、libstdc++ 库和内核构建工具链全部错位,出问题后极难回溯。独立目录下,新旧版本可以共存,切坏了大不了改一个 PATH。第二是语言集,生产环境--enable-languages=c,c++基本够用,别顺手把 fortran、ada、go 全编进去,编译时间会翻倍。第三是 multilib 开关,如果你的目标机器不需要 32 位库,记得加--disable-multilib,否则 configure 会尝试配置 64 位/32 位双套库,遇到缺 32 位 glibc 开发包时又报一轮错。
3. 把 gcc-9.5.0.tar.gz 变成生产可用的编译器:下载、configure、make、install 一步步来
这个章节直接给可抄作业的流程。每步做完能验证什么、失败该看什么日志,我会一并说清。
3.1 下载与解压:镜像选择、断点续传与完整性校验
GCC 官方站点的下载速度在部分网络环境下不稳定,最常见的做法是走国内高校镜像。以清华 TUNA 镜像为例,进入gnu/gcc/目录找到gcc-9.5.0/,下载源码包:
wget -c https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.gz tar xf gcc-9.5.0.tar.gz cd gcc-9.5.0-c参数意味着断点续传,下载中途断网、ssh 断开都不用从头再来。网速过慢时不要反复删掉重下,保持wget -c多等一段时间即可。解压后建议看一眼gcc-9.5.0.tar.gz同级目录是否有.sig或.md5sum文件,有的话顺手校验一下;镜像站偶尔会同步不完整,压缩包损坏会在 tar 解压阶段报gzip: invalid compressed data,与其那时候排查,不如下载完先校验。
3.2 依赖处理:download_prerequisites 的机制与网络失败对策
进入源码目录后,第一步不是 configure,而是处理三个依赖库:
./contrib/download_prerequisites这个脚本会读取一份内部版本清单,从 GNU 官网下载对应版本的 GMP、MPFR、MPC,解压后统一重命名为不带版本号的gmp/、mpfr/、mpc/目录。GCC 的 configure 会在源码根目录优先寻找这些子目录,找到就直接使用,跳过系统库探测。好处是版本完全对齐 GCC 9.5.0 的预期,不受系统自带库版本干扰。
脚本执行中途卡住怎么办?最常见原因是下载超时。不要慌,脚本支持重复执行,已经解压好的目录会保留,重跑一次只补齐缺失部分。如果官网完全拉不动,方案是手动去镜像站下载gmp-*.tar.bz2、mpfr-*.tar.xz、mpc-*.tar.gz三个包,解压后手动把目录名改成gmp、mpfr、mpc,再重新执行 configure。注意目录名必须不带版本号,GCC 的构建脚本按固定目录名寻找依赖,这是很多人手动装依赖时最容易忽略的细节。
3.3 configure:最少参数组合、日志落盘和目录隔离
GCC 官方明确建议在源码目录之外单独建一个 build 目录,做 out-of-source 构建。好处是如果 configure 参数要调整,只需清空 build 目录,源码目录保持原样,不用重新解压。
mkdir -p /opt/gcc-9.5.0-build && cd /opt/gcc-9.5.0-build ../gcc-9.5.0/configure \ --prefix=/opt/gcc-9.5.0 \ --enable-languages=c,c++ \ --disable-multilib 2>&1 | tee /tmp/gcc-configure.log这里三个参数是生产环境的最小可用组合。--prefix=/opt/gcc-9.5.0决定安装根目录,方便后续多版本共存;--enable-languages=c,c++只编 C 和 C++ 编译器前端,省掉一半以上编译时间;--disable-multilib关闭 32 位库支持,在纯 64 位环境里能避开一堆头文件缺失检查和链接测试。tee这一节要重点说:2>&1 | tee /tmp/gcc-configure.log让标准输出和标准错误合并后同时打到屏幕和日志文件,configure 报错时,直接tail -n 50 /tmp/gcc-configure.log看最后一段就行,比在滚动屏里找错误高效得多。把日志输出到文件这个习惯,在后续 make 阶段更重要。
configure 成功退出的标志是最后出现checking for default Ada compiler... no之类的探测信息,随后回到 shell 提示符且退出码为 0。如果最后几行出现error:开头的内容,直接查日志对应的上下文。
3.4 make:并行度、bootstrap 与内存的取舍
configure 通过后进入编译阶段:
make -j4 2>&1 | tee /tmp/gcc-make.log-j4是经验值,不要盲目用-j$(nproc)。GCC 9.5.0 默认开启 bootstrap,也就是会连续编译三轮:第一轮用系统旧编译器编译新编译器,第二轮用新编译器再编译一遍自己,第三轮再用第二轮的产物编最终版本。三轮下来-j8对内存的峰值需求轻松超过 12GB,低配云主机直接 OOM。8GB 内存用-j4,4GB 内存用-j2,这是血泪经验换来的底线。
如果纯粹是为了尽快拿到一个能用的编译器,可以在 configure 阶段加--disable-bootstrap跳过第二轮、第三轮自检,总耗时大约能砍掉 40%。代价是最终二进制缺少“用新编译器编译自己”的验证,优化质量略糙,但对日常开发完全够用。到底是完整 bootstrap 还是快速构建,取决于这台机器是构建机还是单纯的开发机,我一般给 CI 构建机保留 bootstrap,给本地容器环境关掉。编译期间如果担心进度,不要反复打断,用另一个终端tail -n 20 /tmp/gcc-make.log观察输出即可。
3.5 make install 之后第一件事:用 gcc -v 验证真身
编译结束并确认没有error:后执行安装:
make install 2>&1 | tee /tmp/gcc-install.log安装完成后,验证一下装进去的东西是不是你想要的:
/opt/gcc-9.5.0/bin/gcc -v 2>&1 | tail -n 1正常输出是gcc version 9.5.0 (GCC)这样的版本行。同时确认/opt/gcc-9.5.0/bin/下存在gcc、g++、gfortran(如果你只启用了 c,c++,则只有前两个)。到这一步,tar.gz 已经变成真实可用的编译器了,但先别高兴太早,直接跑gcc -v大概率还是旧版本,这就是下一章要解决的问题。
4. 装完 gcc -v 还是旧版本?三种多版本共存方案与切换细节
这是搜索词“gcc升级后为啥还是旧版本”背后最常见的困惑。装完新 gcc 后,敲gcc -v看到的仍然是系统旧版本。原因不是安装失败,而是 shell 在执行命令时按 PATH 环境变量从左到右寻找gcc,/usr/bin/gcc排在/opt/gcc-9.5.0/bin前面。
4.1 先确认 which gcc,再谈版本不对
动手切换前,先执行:
which gcc这个命令会告诉你当前 shell 实际使用的 gcc 绝对路径。如果输出/usr/bin/gcc,说明 PATH 顺序里/usr/bin优先;如果输出/opt/gcc-9.5.0/bin/gcc,说明环境变量已经生效,只是你敲gcc -v的那个终端可能还没重新登录。不要凭感觉改文件,先定位再动手,能省下大量无用功。
4.2 方案一:PATH 注入,只对当前会话和脚本有效
最简单直接的方式是把新路径放到 PATH 最前面:
export PATH=/opt/gcc-9.5.0/bin:$PATH gcc -v注意顺序是/opt/gcc-9.5.0/bin在前,$PATH在后,这样新版本会覆盖旧版本。这个命令只对当前 shell 会话生效,想持久化就追加到~/.bashrc或/etc/profile.d/gcc-9.5.0.sh里。但这种方式有个边界:通过 systemd 启动的服务、sudo 执行的脚本不会读取你的~/.bashrc,它们仍然会找到/usr/bin/gcc。所以 PATH 方案适合交互式开发和单用户环境,不适合系统级服务。
4.3 方案二:update-alternatives 管理默认版本
Ubuntu、Debian 系的标准做法是 alternatives 机制,CentOS 7 也提供同名功能,只是命令叫alternatives。以 Ubuntu 为例:
sudo update-alternatives --install /usr/bin/gcc gcc /opt/gcc-9.5.0/bin/gcc 100 sudo update-alternatives --config gcc第一条命令把新 gcc 注册为/usr/bin/gcc的可选实现,优先级 100;第二条命令进入交互式选择界面,输入对应编号切换。系统级的 make、内核模块编译、驱动安装都会跟随这个选择。CentOS 7 上把命令前缀换成alternatives,参数完全一致。注意g++也要同样注册一次,否则g++ -v还是旧版。
4.4 方案三:/usr/local/bin 软链接与项目级约束
某些发行版的默认 PATH 里/usr/local/bin排在/usr/bin之前,利用这一点可以只做软链:
ln -sf /opt/gcc-9.5.0/bin/gcc /usr/local/bin/gcc ln -sf /opt/gcc-9.5.0/bin/g++ /usr/local/bin/g++这种方式的优点是改动最小,缺点是同一台机器上所有项目都被迫使用 9.5.0,无法按项目区分。更需要项目级隔离的场景,正确做法是在 Makefile 或构建脚本里显式指定编译器变量:
make CC=/opt/gcc-9.5.0/bin/gcc CXX=/opt/gcc-9.5.0/bin/g++这样其他开发者即使系统里只有旧 gcc,这个项目也能稳定用 9.5.0 构建。对于 CI 流水线,推荐这种方式,因为它不依赖 shell 环境,谁执行结果都一样。
5. 编译 GCC 9.5.0 的避坑清单:五个反复出现的真实场景
这一章是踩坑记录,每条按“现象 → 原因 → 解决”展开。我没法覆盖所有环境,但这五个场景大概能命中 80% 的失败现场。
5.1 configure 直接报缺 GMP/MPFR/MPC
现象:configure 执行到一半退出,屏幕或日志里出现configure: error: Building GCC requires GMP 4.2+,后面可能跟着MPFR或MPC的同类报错。原因:系统没有安装对应开发包,也没有在源码目录里找到 download_prerequisites 脚本解压出来的子目录。解决:先执行ls确认源码根目录下是否存在gmp/、mpfr/、mpc/三个目录,不存在就重新运行./contrib/download_prerequisites;如果系统网络受限,手动下载解压并按 3.2 节约定目录名放置;最偷懒的办法是 yum/apt 安装gmp-devel mpfr-devel libmpc-devel或libgmp-dev libmpfr-dev libmpc-dev,让 configure 走系统库路径。这里注意 Ubuntu 的libmpc-dev和 CentOS 的libmpc-devel都满足 GCC 9.5.0 的最低版本要求,不用额外折腾。
5.2 make 中途 collect2: fatal error: killed —— 内存不够
现象:make 运行十几分钟后,日志末尾出现collect2: fatal error: killed,进程被直接杀掉,重跑可能卡在同一位置。原因:链接阶段内存峰值过高,系统 OOM killer 介入;常发生在 bootstrap 模式配合过高-j并行度时。解决:先把并行度降下来,make -j2重跑,GNU make 会跳过已经编译好的目标文件继续,不用从头开始;如果仍然被杀,检查 swap 是否开启,free -h确认内存余量;再不行就在 configure 阶段加--disable-bootstrap重新配置,减少两轮编译的中间产物。这条对云主机用户尤其常见,4GB 内存跑-j$(nproc)是必炸配置。
5.3 CentOS 7.9 上用 gcc 4.8.5 编 9.5.0,bootstrap 到一半 ICE
现象:CentOS 7.9 上 configure 顺利通过,make 进行到 libstdc++ 阶段报internal compiler error,版本是系统自带的 4.8.5。原因:GCC 9.5.0 的源码大量使用 C++11/14 特性,4.8.5 对新语法支持不完整,编到复杂模板时崩溃。解决:最省事是给 configure 加--disable-bootstrap,让系统旧编译器只编译一次,绕开“新编译器编译自己”的阶段;如果想保留完整 bootstrap,先通过 SCL 安装 devtoolset-8 提供较新的 gcc 8.3.1,再用它作为基础编译器。需要注意devtoolset-8默认不会覆盖系统 gcc,使用时通过scl enable devtoolset-8 bash进入新环境,然后在那个环境里继续执行 make。
5.4 装完新 gcc,NVIDIA 驱动 / 内核模块编译反而报错
现象:按 4.3 节把系统默认 gcc 切到 9.5.0 后,编译内核模块或安装 NVIDIA 驱动时出现版本检查失败,例如The kernel was built with GCC 4.8.5, but the current compiler is GCC 9.5.0。原因:内核源码在编译时记录了当时的__GNUC__宏,驱动和模块的构建脚本会用当前 gcc 的版本宏与内核记录比对,不一致就拒绝继续。NVIDIA 535.54.03 这类驱动安装包对编译器版本尤其敏感。解决:编译驱动前临时切回系统自带 gcc,用update-alternatives --config gcc选回旧版本,或者执行make CC=/usr/bin/gcc指定编译器;驱动安装完再切回 9.5.0 编译业务代码。不要试图绕过版本检查,内核模块的编译器版本不一致会导致运行时符号错乱,表现为 insmod 报Unknown symbol。
5.5 Kylin V10 上 configure: error: cannot compute suffix of object files
现象:在 Kylin V10 或其他基于 CentOS 的国产化系统上,configure 早期直接报cannot compute suffix of object files: cannot compile,没有具体依赖信息。原因:通常是基础开发工具没装齐,系统里连gcc-c++、make都没有,configure 连一个最小的 C 程序都编译不出来。解决:先安装基础构建环境:
yum groupinstall -y "Development Tools" yum install -y gcc gcc-c++ make gmp-devel mpfr-devel libmpc-devel装完再重新执行 configure。这条的教训是:国产化系统镜像相比标准 CentOS 往往裁剪更多,拿到机器先跑gcc --version、make --version确认基础链存在,再进入 GCC 编译流程。
6. 让 9.5.0 真正顺手:ccache 缓存、特性验证与清理
编译器装好只是开始,日常使用里最影响体验的是重编译速度和特性边界。
6.1 用 ccache 缓存编译产物,二次编译不再等半小时
如果你经常需要调整 configure 参数或反复修改源码后重新编译,建议配置 ccache:
export CCACHE_DIR=/var/cache/ccache export CC="ccache gcc" export CXX="ccache g++" make -j4CCACHE_DIR指定缓存目录,CC/CXX用 ccache 包装编译器。第一次执行仍然接近全量编译,之后的重复构建会命中缓存,链接前的编译阶段大幅缩短。这个技巧对不启用 bootstrap 的构建效果尤其明显,对日常项目编译同样适用。注意 ccache 缓存的是预处理后的目标文件,源码任何一行变化都会导致对应文件缓存失效,所以它优化的是“同一份代码反复编”的场景。
6.2 验证 C++17 与 C++2a 特性:optional、if constexpr 现场测试
判断刚装的 9.5.0 是否真的能支撑项目里的 C++ 特性,最快的方式是写个临时文件现场编译:
cat > /tmp/test-gcc.cpp <<'EOF' #include <iostream> #include <optional> int main() { std::optional<int> v = 42; if constexpr (true) { std::cout << v.value() << std::endl; } return 0; } EOF /opt/gcc-9.5.0/bin/g++ -std=c++17 /tmp/test-gcc.cpp -o /tmp/test-gcc /tmp/test-gcc输出42表示 C++17 支持正常。如果想测更前沿的特性,GCC 9.5.0 里需要写成-std=c++2a,因为 9 系发布时 C++20 尚未定稿,-std=c++20这个选项在 GCC 10 才正式提供。这个细节经常让人误以为编译器装坏了,实际上只是版本边界问题。
6.3 清理与卸载:比 make uninstall 更安全的做法
make uninstall在 GCC 上可以执行,但如果--prefix指向/usr/local这种共享目录,它可能误删同前缀下其他软件的记录文件。更干净的做法是编译前规划好前缀目录,之后直接删除目录:
rm -rf /opt/gcc-9.5.0 /opt/gcc-9.5.0-build如果这台机器还要继续维护工具链,我一般把 ccache 目录单独留着,避免下一次重编译时冷启动。我现在的习惯是:任何服务器上装 gcc,先写一行gcc -v 2>&1 | tail -n 2存进部署笔记,再决定要不要动/usr/bin。生产环境里版本一致比版本新重要得多,搞清楚边界再动手,希望帮到你。
本文还有配套的精品资源,点击获取