news 2026/9/16 10:19:05

Ubuntu 20.04源码编译安装GCC 12.2完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu 20.04源码编译安装GCC 12.2完整指南

开头

这两天帮同事排查一个编译问题,他新拉的项目代码直接报GCC version must be at least 11.0,一查系统里装的还是 Ubuntu 20.04 自带的 GCC 9.4。其实这种尴尬在 20.04 上太常见了:系统比较新,但系统自带编译器比较老,Python 3.12、TensorFlow、新版内核模块,还有一堆 C++20 特性的项目,都在把最低编译器版本往上抬。你问能不能直接apt install gcc-12?20.04 的官方源里根本没有,最省事的办法就是源码编译。

所以这篇文章就围绕一件事展开:在 Ubuntu 20.04 上,从源码编译安装最新版的 GCC(这里以 12.2 版本为例,方法完全适用于 12.3、13.x、14.x)。我会把从依赖准备、configure 参数、编译到环境变量切换的全过程掰开揉碎来讲,包括我实际踩过的坑和调试思路。如果你刚好需要给机器升级编译器,或者想搞明白为什么gcc -v已经显示新版本但编译出来的程序还是报错,这篇文章应该能帮到你。

1. 环境准备:编译前的三个关键认知

1.1 为什么 20.04 官方源里没有新 GCC

先说一个大家容易忽略的点:Ubuntu 20.04 是 2020 年发布的 LTS 版本,官方软件源里的 GCC 版本是 9.4,这个版本在发布时是稳定的,但软件源不会像滚动发行版那样持续跟进新版本。原因很简单,LTS 的核心策略是稳定优先,编译器这种底层工具一旦升级,可能导致整个系统里大量二进制包需要重编,这会破坏"五年不升级、一直稳定用"的承诺。

所以如果你需要新版本的 GCC,在 Ubuntu 上通常有三条路:

  • 使用 Ubuntu Toolchain PPA(ppa:ubuntu-toolchain-r/test),里面提供 gcc-12、gcc-13 等预编译包,适合懒得编译的人
  • 使用 Conda 或者其他第三方发行渠道
  • 源码编译安装

PPA 方案我平时也推荐,装完即用,但它的缺点是不可控——PPA 里的版本不会马上同步上游最新版,而且如果你想自定义编译特性(比如只编 C/C++ 前端、指定安装路径、静态链接某些库),PPA 完全帮不上忙。源码编译虽然耗时,但自由度最高,对版本有精确控制,这也是本文选择它的原因。

1.2 编译 GCC 到底需要什么依赖

编译 GCC 不是孤军奋战,它依赖三个 GNU 基础数学库:GMP(大数运算)、MPFR(浮点精度)、MPC(复数计算)。这三个库是 GCC 的"底盘",负责中间表示层面的数值计算和优化分析。如果系统里没有它们,GCC 的 configure 阶段会直接报错。

这里有个省事的技巧:GCC 源码目录里自带了这三个库的下载脚本(contrib/download_prerequisites),运行时它会自动下载并解压到源码树中。但我在国内网络环境下试过几次,下载速度不稳定,而且脚本下载的是固定版本,以后升级 GCC 还得重新搞。所以更推荐的做法是用系统自带的库包(libgmp-devlibmpfr-devlibmpc-dev)或者手动编译最新版。

在开始之前,先用下面的命令把基础工具链装好:

sudo apt update sudo apt install -y build-essential make flex bison libgmp-dev libmpfr-dev libmpc-dev

build-essential提供的是 gcc、g++、make、dpkg-dev 这些基础工具,虽然我们马上要换掉 gcc,但在编译新 gcc 的时候还得靠老 gcc 来"孵化"新 gcc,这就是经典的"先用自己编译自己"的自举过程,英文叫 bootstrap。flexbison是语法分析器生成器,编译 GCC 的解析器必须用到,别省。

1.3 磁盘、内存和时间的心里预期

源码编译不是一个"点一下等 10 秒"的过程。GCC 12.2 的源码包解压后大约 800MB,编译生成的中间文件会额外占 3-5GB。所以建议至少留出 8GB 磁盘空间,如果是 4GB 内存的小机器,编译时开太多并行任务极易 OOM。

时间方面,我实测过的数据是:8 核的机器并行编译(make -j8),大概需要 30-40 分钟;4 核大概 1 小时起步;如果是低配云主机(2 核),建议做好等 2 小时的准备。这个时间因人而异,不用焦虑,反正你不是唯一一个在等编译的人。

2. 源码下载与校验:一个被忽略的关键步骤

2.1 从哪下载、怎么选下载源

GCC 官方下载地址是https://gcc.gnu.org/,所有发布版本都在那。不过国内直连官方站点的速度不太稳定,我一般是用清华或者华为的镜像源下载,速度快很多。比如要下载 12.2.0:

wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-12.2.0/gcc-12.2.0.tar.xz

如果你要更新版本,比如 13.2.0、14.1.0,只需要把路径里的版本号改掉即可。注意文件名是gcc-12.2.0.tar.xz,不是.tar.gz,两者压缩算法不同,tar.xz体积更小、解压时间稍长。

另外提一嘴,很多人不知道 GCC 的 release 分支在 GitHub 也有镜像,如果你在 GitHub 上操作比较熟,也可以从https://github.com/gcc-mirror/gcc拉取对应 tag 的代码。不过官方发布的 tarball 是经过完整测试的,稳定性更好,建议直接用 tarball 而不是 git clone。

2.2 校验文件完整性的靠谱姿势

下载完先别急着解压,建议花十几秒做个校验。GCC 官方在下载目录里提供了.sig签名文件和.sha256哈希文件,理论上可以这样验证:

echo "$(cat gcc-12.2.0.tar.xz.sha256) gcc-12.2.0.tar.xz" | sha256sum -c -

如果输出gcc-12.2.0.tar.xz: OK就说明文件完整。实际操作中,很多同学在这个环节偷懒,结果解压编译到一半才报"文件损坏"或者"压缩包意外结束",再回头重新下载反而更浪费时间。

2.3 解压与目录规划的"反直觉"建议

这里分享一个我自己的习惯:不要把编译过程放在源码目录里进行。GCC 官方推荐的做法是在源码树外面单独建一个 build 目录,好处是源码目录保持干净,编译产物不会污染源文件,以后想重新 configure 或者切换配置,直接清空 build 目录就行。

tar -xf gcc-12.2.0.tar.xz mkdir -p build-gcc && cd build-gcc

这个"外部构建"的方式,对于大项目来说简直是救命的。我第一次编译时直接在源码目录里执行 configure,后来想改参数重新配置,发现目录里到处都是 Makefile 和中间文件,根本分不清哪些是源码哪些是产物。从那以后,所有源码编译我都遵循这个习惯。

3. configure 配置:决定成败的参数选择

3.1 我的推荐配置参数

进入 build 目录后,开始配置。我推荐这样配:

../gcc-12.2.0/configure \ --prefix=/usr/local/gcc-12.2.0 \ --enable-languages=c,c++ \ --disable-multilib \ --enable-bootstrap

下面逐一解释每个参数:

--prefix=/usr/local/gcc-12.2.0指定安装路径。为什么不直接装到/usr/local?因为后续可能需要多版本共存,装到独立目录方便管理,想删哪个版本就删哪个目录,最干净。

--enable-languages=c,c++指定需要编译的语言前端。GCC 支持的远不止 C/C++,还包括 Fortran、Go、D 等。如果全不指定,默认会编译所有支持的语言——这会导致编译时间翻几倍,而一般开发只需要 C 和 C++。除非你明确需要 Fortran,否则就老老实实写上 c,c++。

--disable-multilib是很多人忽略但极其重要的参数。多库支持(multilib)允许在 64 位系统上同时生成 32 位和 64 位程序,默认是启用的。但这需要额外的 32 位依赖库,否则 configure 会报错;就算不报错,也会多编出一套 32 位运行时库,徒增编译时间和空间。对绝大多数人来说,64 位就够了,直接禁用是对的。

--enable-bootstrap启用三阶段自举编译。第一阶段用系统自带的 GCC 编出基础编译器,第二阶段用这个新编译器重新编译 GCC 源码,第三阶段再用第二轮的结果编一遍,最后还会比较第二轮和第三轮产物是否一致,以此验证编译器的正确性。这样编译出来的 GCC 更可靠,但代价是时间几乎翻倍。如果只是自己开发用,想加快速度,也可以改成--disable-bootstrap,我建议至少在第一次编译时打开它,稳妥。

3.2 依赖库路径的处理

前面我们提到已经通过 apt 安装好了libgmp-devlibmpfr-devlibmpc-dev,所以这里 configure 一般会自动找到它们。但不排除在一些精简系统上,这三个库安装在非标准路径(比如/usr/local/lib),此时 configure 会找不到,报错信息类似:

configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+

解决方式有两个:一是用--with-gmp=/path --with-mpfr=/path --with-mpc=/path显式指定路径;二是把这三个库的源码放到 GCC 源码目录下,命名成gmpmpfrmpc,GCC 的构建系统会自动编译它们。不过现在主流 Ubuntu 系统的默认路径都是/usr/include/usr/lib/x86_64-linux-gnu,只要 apt 装的,基本一步到位。

还有一个小坑:某些系统预装的是libmpc-dev版本太老(比如 1.0 甚至 0.8),GCC 高版本会要求 MPC 1.0.1 以上。遇到这种情况,最好还是用源码方式单独编译一个最新版 MPC 并指定路径,比硬着头皮降级 GCC 版本更靠谱。

3.3 环境变量与编译器选择

configure 在执行时,会默认使用当前 PATH 里的gcc来编译 GCC 自身的启动代码(也就是 bootstrap 的第一阶段)。如果你 PATH 里有多个 gcc(比如 Conda 的 gcc、或者别的编译工具链),建议在 configure 前先清一下环境变量:

export CC=/usr/bin/gcc export CXX=/usr/bin/g++

否则你可能会遇到"configure: error: C compiler cannot create executables"或者莫名其妙的版本不匹配错误。这一步平时不起眼,但一旦出错,排查起来会非常痛苦,因为报错信息往往不是直接指向编译器路径的问题。

4. 编译与安装:最耗时也最容易出问题的阶段

4.1 并行任务数与内存的关系

configure 通过后,执行:

make -j$(nproc)

nproc返回 CPU 核心数,-j指定并行任务数。但这里我建议不要无脑用满所有核心。GCC 本身就是个"内存杀手",我在这台 16G 内存的机器上用-j16编译,内存峰值能冲到 12G 左右;如果在 4G 内存的机器上开满 8 核,几乎必死无疑,表现是系统卡死或者一个 OOM killer 把 cc1plus 进程给杀了。

最稳妥的方式是:先用free -h看内存,然后根据内存大小决定并行任务数。我的经验公式是:并行数不要超过(内存大小 / 1.5G)。比如 8G 内存,建议-j4-j5;16G 内存再考虑 8 核以上。

编译期间可以盯着终端,正常情况下会滚动输出大量编译日志,不用管它,只要不报错就行。不过建议把日志重定向到文件里留个底:

make -j$(nproc) 2>&1 | tee build.log

这样就算编译中途断了,翻日志也能快速定位问题,不用瞪大眼睛从头到尾看终端。

4.2 三大常见编译失败场景

第一个常见失败场景是内存不足。日志里会出现大量virtual memory exhausted或者直接被系统 kill 掉,前面说了,处理方式是降-j参数。第二个是磁盘空间不足。源码包加中间文件加最终安装文件,隐含几个 G 的空间,如果/tmp或当前目录所在分区不够,可能会在链接阶段报错。第三个是系统自带 GCC 版本过低。如果你的 Ubuntu 是 20.04 之前的老版本,系统 GCC 可能在 7 以下,此时编译 GCC 12 会因为缺少 C++11 支持而失败。处理方式就是先升级系统 GCC,或者用更老的 GCC 版本作为 bootstrap 编译器。

另外还有一个比较隐蔽的问题:如果启用了--enable-bootstrap,第二阶段编译时会用新编出来的编译器去编整个 GCC 源码,此时如果源码被改动过(比如解压后手动打了补丁),可能导致最终结果显示比较失败。所以编译期间最好别用编辑器去动源码目录里的文件。

4.3 make install 与安装验证

当 make 完成后(没有任何 error),执行安装:

sudo make install

安装时间很快,几分钟内完成。装完后验证:

/usr/local/gcc-12.2.0/bin/gcc --version

如果输出版本号gcc (GCC) 12.2.0,说明安装成功。

这里额外提醒一下:make install默认会把可执行文件装到--prefix/bin,库文件装到--prefix/lib64,头文件装到--prefix/include。不需要单独执行ldconfig,因为这个路径不在系统默认的/usr/lib目录里,不会干扰系统原有环境,这种隔离式安装正是我们要的效果。

5. 升级后还是旧版本?多版本共存与环境变量实操

5.1 为什么gcc -v还是旧版本

不少人在安装新 GCC 后,打开终端执行gcc -v,发现系统还是显示老版本,于是怀疑安装失败了。其实没失败,只是 PATH 的问题。终端在执行gcc时,会从$PATH环境变量里从头到尾挨个目录找,找到第一个gcc就停了。通常/usr/bin在 PATH 里排在很前面,而/usr/local/gcc-12.2.0/bin排在后面甚至不在 PATH 里,所以 shell 默认找到了/usr/bin/gcc(也就是 GCC 9.4)。

解决方式很简单,把新版本的 bin 目录放到 PATH 最前面。在~/.bashrc末尾添加:

export PATH=/usr/local/gcc-12.2.0/bin:$PATH export LD_LIBRARY_PATH=/usr/local/gcc-12.2.0/lib64:$LD_LIBRARY_PATH

然后source ~/.bashrc,再执行gcc -v,看到的版本号就会变成 12.2.0。

要说明的是,我见过一部分教程喜欢用update-alternatives来切换 gcc 版本。update-alternatives确实是一个管理多版本工具链的好工具,适合那种不想改 PATH 的场景。但说实话,对于编译器这种依赖一大堆随附库的工具链,PATH 方式更直观,切换也更快。不过用update-alternatives有一点好处:它能把新版本的 gcc 也注册为系统的ccgccg++等默认链接,某些构建系统(比如一些自动配置的 CMake 工程)在扫描编译器时会走/usr/bin/gcc这种固定路径,这时候update-alternatives才有不可替代的作用。

5.2 动态库找不到的坑

第一个坑:新版 GCC 编译出来的程序,运行时可能会报error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory。原因很简单,新版 libstdc++ 库在/usr/local/gcc-12.2.0/lib64下,不在系统默认搜索路径里。上面的LD_LIBRARY_PATH加上就好了。但如果你不想全局设置这个变量,可以只对这个程序设置:

LD_LIBRARY_PATH=/usr/local/gcc-12.2.0/lib64 ./your_program

第二个坑:如果你希望所有程序都能自动找到新库,可以把新库目录写进/etc/ld.so.conf.d/gcc-12.2.0.conf,内容就是路径,然后执行sudo ldconfig。这种方式对系统全局更友好,适合服务器管理员。但要注意:改了之后系统里其他依赖老 libstdc++ 的程序可能会被新库覆盖,引发二进制不兼容。所以一般个人开发环境,我还是建议用LD_LIBRARY_PATH精确控制,避免"殃及池鱼"。

5.3 多版本共存的实际场景

实际工作中,机器上同时存在多个 GCC 是非常正常的。比如系统的 GCC 9 还在给某些老项目服务,新项目用 GCC 12 编译,两边井水不犯河水。

我通常的管理方式是:每个版本独立安装到自己的目录,然后在~/.bashrc里用变量切换。比如:

alias gcc12="/usr/local/gcc-12.2.0/bin/gcc" alias g++12="/usr/local/gcc-12.2.0/bin/g++"

这样平时默认还是系统 GCC,需要新版本时显式调用 gcc12。或者直接在项目目录里使用 CMake 时指定:

cmake -DCMAKE_C_COMPILER=/usr/local/gcc-12.2.0/bin/gcc \ -DCMAKE_CXX_COMPILER=/usr/local/gcc-12.2.0/bin/g++

这种方式最适合"旧系统新项目"的迁移场景,也是最可控的。

6. 常见问题与排查技巧实录

6.1 configure 阶段报错 GMP/MPFR/MPC

如果你 configure 时遇到上面提到的 GMP/MPFR/MPC 缺失或版本太旧的问题,建议优先升级系统的这三个库包:

sudo apt install --only-upgrade libgmp-dev libmpfr-dev libmpc-dev

如果 apt 里没有更高版本(Ubuntu 20.04 默认仓库里这三个库版本其实够用),再考虑源码编译这三个库,然后 configure 时加上--with-gmp=/usr/local/gmp --with-mpfr=/usr/local/mpfr --with-mpc=/usr/local/mpc。源码编译它们的时间很短,每个大约 1-2 分钟,别恐惧。

6.2 编译过程中 cc1plus 进程被杀

这个问题在低内存机器上非常高发。现象是编译到中途,终端冒出Killed,然后整个编译进程结束了。用dmesg | tail -20看日志,通常能看到oom-kill相关的记录。

解决方案按优先级排:

  1. 降低并行度:make -j2甚至make -j1
  2. 关闭不必要的进程:浏览器、IDE 全关掉,把所有可用的内存留给编译
  3. 临时增加 swap:可以创建一个 4GB 的 swapfile 应急
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile

这是临时方案,编译完可以关掉 swap:sudo swapoff /swapfile,然后删掉文件。

6.3 编译完成后 gcc 版本显示正常,但编译 C++ 代码报头文件缺失

这种情况多半是头文件搜索路径没对上。新版 GCC 的头文件在/usr/local/gcc-12.2.0/include/c++/12.2.0,如果你直接调用新 gcc 编译一个 include 了<iostream>的程序,它应该能自动找到。但如果用-nostdinc或者自定义了 CPATH 环境变量,可能就乱了。

排查思路:

echo | /usr/local/gcc-12.2.0/bin/g++ -E -v -

这条命令会输出 g++ 的 include 搜索路径,检查里面有没有新版本的头文件目录。如果没有,检查C_INCLUDE_PATHCPLUS_INCLUDE_PATH环境变量,避免它们把搜索路径带偏。

6.4 老项目编译突然报错,换回旧版本解决

不到万不得已别把系统默认 gcc 整个替换掉。Ubuntu 系统和很多软件包在编译安装时依赖系统自带的工具链,如果你把/usr/bin/gcc的软链接改成了新版本,可能导致系统包管理的某些脚本行为异常。所以前面我反复强调独立目录安装 + PATH 切换,就是这个原因。

如果老项目必须用老 gcc,但是 PATH 里已经是新版,可以在项目编译时强制指定编译器路径,而不是改全局环境,这样最安全。

结尾

最后一次重编 GCC 12.2,前后我用了大概一个下午,中间踩的坑主要集中在三块:configure 参数不熟悉导致反复重试、编译过程内存不足导致进程被杀、装好后 PATH 没配好导致"升级了个寂寞"。后来我把这三步的套路固定下来,新机器装 GCC 基本一次过。我个人体会是,源码编译本身不复杂,但每一步都有讲究,尤其是环境变量和依赖,宁可多花几分钟提前规划,也不要编到一半再回头拆墙。如果你也正好卡在这里,按着上面的流程走一遍,应该能省掉不少弯路。如果编译过程中遇到别的奇怪报错,欢迎翻翻 build.log 再对照上面列出的场景排查,大概率能找到一个方向。最后再啰嗦一句:装好之后一定要跑一下gcc --version确认版本,不然你永远不知道自己在用的是哪个编译器。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 10:18:29

SpringBoot+Vue构建企业级多媒体素材库实践

1. 项目概述&#xff1a;企业级多媒体素材库的核心价值在数字化转型浪潮中&#xff0c;多媒体素材管理已成为企业内容生产的核心痛点。我们团队基于SpringBootVueMyBatis技术栈开发的这套系统&#xff0c;正是为了解决企业级用户在海量图片、视频、文档等非结构化数据管理中的三…

作者头像 李华
网站建设 2026/9/16 10:17:54

OPC UA作为AI工业Agent统一基座的实操路径

1. 这不是一场“选边站”&#xff0c;而是一场接口标准的生存博弈最近在几个工业自动化工程师群和AI Agent开发者的 Slack 频道里&#xff0c;反复看到一句话&#xff1a;“Skills广场刚上线&#xff0c;MCP协议文档还没读完&#xff0c;Rules规范草案又更新了——OPC到底该往哪…

作者头像 李华
网站建设 2026/9/16 10:17:52

Flutter weather_pack迁移HarmonyOS实战指南

1. 项目背景与核心价值作为一名长期深耕移动端开发的工程师&#xff0c;我最近在鸿蒙生态中遇到了一个有趣的挑战&#xff1a;如何将Flutter生态中成熟的weather_pack气象库无缝迁移到HarmonyOS平台。这个需求源于我们正在开发的一款全场景生活服务应用&#xff0c;需要为鸿蒙用…

作者头像 李华
网站建设 2026/9/16 10:16:57

arcpy自动化脚本提升GIS数据处理效率的实战指南

1. 项目概述&#xff1a;为什么arcpy代码能让你效率翻倍&#xff1f;在GIS行业摸爬滚打这些年&#xff0c;我见过太多同行把时间浪费在重复性操作上。上周刚遇到个典型案例&#xff1a;某规划院同事花了整整三天手动导出200个乡镇的用地统计表&#xff0c;而用arcpy脚本其实20分…

作者头像 李华
网站建设 2026/9/16 10:16:00

电机参数如何影响控制难度:电感、惯量与温漂的实战解析

1. 电机不是“越大力越好”&#xff1a;控制难度的本质是动态响应与能量边界的博弈很多人第一次接触电机控制时&#xff0c;下意识会觉得&#xff1a;“功率越大、转速越高、扭矩越猛&#xff0c;电机就越‘高级’。”这种想法在选型阶段就埋下了失控的种子。我带过三届自动化专…

作者头像 李华
网站建设 2026/9/16 10:15:24

AI机器视觉工业质检实战:从样本采集到模型部署全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华