如何选择默认的gcc和g++版本?

所以我已经安装了gcc-4.4和gcc-4.3(g++也是一样)。据我记得,Ubuntu中有一个工具可以为你设置符号链接,只要告诉它你想要的版本即可。然而,在最新版本中似乎不起作用,这让我感到失望。
root@nexus:~# update-alternatives --config gcc
update-alternatives: error: no alternatives for gcc.
root@nexus:~# update-alternatives --config cc
There is only one alternative in link group cc: /usr/bin/gcc
Nothing to configure.


root@nexus:~# dpkg -l | grep gcc | awk '{print $2}'
gcc
gcc-4.3
gcc-4.3-base
gcc-4.3-multilib
gcc-4.4
gcc-4.4-base
gcc-4.4-multilib
gcc-4.5-base
gcc-multilib
lib32gcc1
libgcc1

有什么想法吗?

2正如@Oli所解释的那样,这是一个糟糕的主意。Debian-devel邮件列表中有这样一段话:“我认为不应该使用替代方案进行版本控制。例如,我们既不在gcc上使用替代方案,也不在Python上使用替代方案。” https://lists.debian.org/debian-devel/2014/06/msg00381.html - hmijail
3好的,那么你们用什么来进行版本控制呢? - WillC
用户可以使用任何他们想要的用户空间代码:他们可以将gcc等链接/可执行文件放在$HOME/bin中,也可以将它们放入projectx/bin并通过脚本修改他们的PATH,将project/bin放在系统位置之前。在内核模块构建堆栈中插入未经测试/验证的编译器可能会引起问题:Ubuntu更新导致我的电脑出现了2500万多次谷歌搜索结果。 - ubfan1
9个回答

首先,删除当前的update-alternatives设置,针对gccg++
sudo update-alternatives --remove-all gcc 
sudo update-alternatives --remove-all g++

安装软件包

在安装 build-essential 后,似乎已经安装了 gcc-4.3 和 gcc-4.4。然而,我们可以明确地安装以下软件包:

sudo apt-get install gcc-4.3 gcc-4.4 g++-4.3 g++-4.4

安装备选项

默认情况下,符号链接 ccc++ 已经安装。我们将为 gccg++ 安装符号链接,然后将 ccc++ 分别链接到 gccg++。(请注意,102030 选项分别表示每个备选项的优先级,数字越大优先级越高。)

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.3 10
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 20

sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.3 10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.4 20

sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc 30
sudo update-alternatives --set cc /usr/bin/gcc

sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 30
sudo update-alternatives --set c++ /usr/bin/g++

配置备选项

最后一步是配置gccg++的默认命令。可以轻松地在4.3和4.4之间进行交互切换:

sudo update-alternatives --config gcc
sudo update-alternatives --config g++

或者使用脚本切换:
#!/bin/sh

if [ -z "$1" ]; then
    echo "usage: $0 version" 1>&2
    exit 1
fi

if [ ! -f "/usr/bin/gcc-$1" ] || [ ! -f "/usr/bin/g++-$1" ]; then
    echo "no such version gcc/g++ installed" 1>&2
    exit 1
fi

update-alternatives --set gcc "/usr/bin/gcc-$1"
update-alternatives --set g++ "/usr/bin/g++-$1"

7谢谢,所以你必须手动添加它们到update-alternatives中进行更新。如果我没记错的话,早期的Ubuntu版本会自动完成这个操作。 - Nils
1这对我来说特别有用,当我为不同的内核编译NVIDIA模块时。非常感谢您解释update-alternatives。 - earthmeLon
1谢谢!我使用了您的其他答案将版本从4.6更新到4.7。我想使用这个答案,但我不确定为什么您在某些命令后面加上像10 20 30这样的数字。您能解释一下吗? - Martin Drozdik
7根据manpage,数字是优先级。我想象一下,如果其中一个版本被卸载,它将使用这些优先级来确定哪个应该成为新的默认版本。 - Ibrahim
g++配置命令不起作用? - intrigued_66
1@Ibrahim:不,当你选择自动模式时,它们决定了被选择的内容。 - Cookie
1可以使用--slave选项来改进这个问题,因为通常你总是希望在gcc和g++之间使用相同的版本。 - alexmogavero
在Ubuntu 18.04上,不再适用于gcc-7和gcc-8。 - Werner Henze
1sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8 --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-4.8 - fchen
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 20 --slave /usr/bin/g++ g++ /usr/bin/g++-7 --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-7 - fchen
1你还需要从相应版本的gcc中获取arnmranlibgcovgcov-dumpgcov-tool - Maxim Egorushkin
重新配置其他工具,请参考例如https://stackoverflow.com/a/44217770。 - TooTone

在终端中执行:

gcc -v
g++ -v

好的,那部分相当简单。棘手的部分是,当你发出GCC命令时,它实际上是一个符号链接,指向你正在使用的任何版本的GCC。这意味着我们可以将GCC创建为指向我们想要的任何版本的GCC的符号链接。
  • 你可以看到这个符号链接:
ls -la /usr/bin | grep gcc-4.4
ls -la /usr/bin | grep g++-4.4
我们需要做的是删除GCC符号链接和G++符号链接,然后重新创建它们并链接到GCC 4.3和G++ 4.3。
rm /usr/bin/gcc
rm /usr/bin/g++

ln -s /usr/bin/gcc-4.3 /usr/bin/gcc
ln -s /usr/bin/g++-4.3 /usr/bin/g++
现在如果我们再次检查符号链接,我们会发现GCC和G++现在都链接到GCC 4.3和G++ 4.3。
ls -la /usr/bin/ | grep gcc
ls -la /usr/bin/ | grep g++
最后我们可以再次检查我们的GCC -v,并确保我们使用的是正确的版本。
gcc -v
g++ -v

1正是我在寻找的!忘记了可以将“gcc”符号链接到我安装的任何版本。谢谢! - CalebHC
更简单、更容易、更安全的方法! - sailfish009
使用 ls -la /usr/bin/g++4.4* 替代 ls -la /usr/bin | grep g++-4.4,这样更短、更快、更安全。 - phuclv
这是这里最好的答案。解释了它的工作原理等等。 - user997112

这真的是可取的吗?gcc版本之间存在ABI变化。使用一个版本(例如整个操作系统)编译某些东西,然后使用另一个版本编译其他东西,可能会引起冲突。

例如,内核模块应始终使用与编译内核所用的gcc版本相同的版本进行编译。考虑到这一点,如果您手动更改了/usr/bin/gcc之间的符号链接,以及您的Ubuntu版本中使用的版本,未来构建的DKMS模块可能会使用错误的gcc版本。

如果您只想使用不同版本的gcc构建东西,那很容易,即使使用makescripts也可以。例如,您可以在CC环境变量中传递gcc的版本:

CC="gcc-4.5" ./configure
CC="gcc-4.5" make

你在make命令上可能不需要它(配置脚本通常会引入它),但也无妨。

1谢谢你的评论。我知道 CC 变量,但这并不是真正的问题。 - Nils
2确实如此,但我已经解释过为什么gcc不是alternatives系统的一部分,以及为什么这并不特别可取。如果这两点都无法改变你的想法,那就手动完成吧。 - Oli
1之前是这样的吗?现在他们只是移除了它?!使用不同版本的gcc编译(用户空间)软件应该完全没问题... 这个讨论变得有点傻了.. - Nils
2你能解释一下为什么每次调用环境变量比系统范围的配置设置更可取吗?$ sudo apt-get install gcc-6 gcc-7 $ CC="gcc-7" ./configure <大量输出> $ make # 使用gcc-7 sudo update-alternatives gcc gcc-7会确保你不会意外切换ABI。 - kfsone

编辑:

这假设您已经首先安装了版本,例如:

sudo apt install gcc-4.9 g++-4.9

翻译:

对于那些懒得动手的人,这里有一个一行代码的方法,只需更改末尾的数字为您想要的版本即可。它将为gcc和/或g++进行更改。

ls -la /usr/bin/ | grep -oP "[\S]*(gcc|g\+\+)(-[a-z]+)*[\s]" | xargs bash -c 'for link in ${@:1}; do sudo ln -s -f "/usr/bin/${link}-${0}" "/usr/bin/${link}"; done' 4.9

在这个例子中,我切换到了4.9版本。
这个例子中没有错误检查之类的东西,所以在运行之前你可能想要检查一下将要运行的内容。只需在sudo之前添加echo即可。为了完整起见,我也提供了检查行:
ls -la /usr/bin/ | grep -oP "[\S]*(gcc|g\+\+)(-[a-z]+)*[\s]" | xargs bash -c 'for link in ${@:1}; do echo sudo ln -s -f "/usr/bin/${link}-${0}" "/usr/bin/${link}"; done' 4.9

检查的输出应该类似于:
sudo ln -s -f /usr/bin/g++-4.9 /usr/bin/g++
sudo ln -s -f /usr/bin/gcc-4.9 /usr/bin/gcc
sudo ln -s -f /usr/bin/gcc-ar-4.9 /usr/bin/gcc-ar
sudo ln -s -f /usr/bin/gcc-nm-4.9 /usr/bin/gcc-nm
sudo ln -s -f /usr/bin/gcc-ranlib-4.9 /usr/bin/gcc-ranlib
sudo ln -s -f /usr/bin/x86_64-linux-gnu-g++-4.9 /usr/bin/x86_64-linux-gnu-g++
sudo ln -s -f /usr/bin/x86_64-linux-gnu-gcc-4.9 /usr/bin/x86_64-linux-gnu-gcc
sudo ln -s -f /usr/bin/x86_64-linux-gnu-gcc-ar-4.9 /usr/bin/x86_64-linux-gnu-gcc-ar
sudo ln -s -f /usr/bin/x86_64-linux-gnu-gcc-nm-4.9 /usr/bin/x86_64-linux-gnu-gcc-nm
sudo ln -s -f /usr/bin/x86_64-linux-gnu-gcc-ranlib-4.9 /usr/bin/x86_64-linux-gnu-gcc-ranlib

你可以之后检查版本。
gcc --version

半详细解释:
  • ls -la /usr/bin/ 列出/usr/bin中的所有文件。
  • | 将输出发送到下一个命令。
  • grep -oP 按行匹配搜索正则表达式。 o 只显示结果而不是整个匹配行。 P 告诉grep使用perl正则表达式。如果需要,可以阅读有关正则表达式的相关资料。
  • xargs 简单来说,它会收集通过管道传递给它的结果,并将它们全部发送到最后一个命令,即跟在xargs后面的命令。
  • bash 嗯,这是bash。 c 标志告诉它将字符串作为命令使用。在这个例子中,它循环遍历从xargs发送的参数,通过跳过第一个(0th)参数,也就是本例中跳过了4.9。循环中使用第0个参数来更改链接。
  • ln -s -f s 标志创建一个符号链接,f 强制先解除链接(如果需要的话)。

我想要从已安装的gcc/g++ 9.x切换到10.x版本:
1. 安装所需的新版本:
sudo apt install gcc-10
sudo apt install g++-10
切换到新版本:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10
  1. 测试使用:
gcc --version
g++ --version
老版本仍然安装在那里,以防你需要切换回去,只需使用第二个表单中的命令和你的旧版本!

1它有效,谢谢! - Dee

我通常也会将相关的gcc工具(如gcc-ar)配置为从属工具,这样你就可以一次性切换它们所有。
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.3 10 \
    --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-4.3 \
    --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-4.3 \
    --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-4.3

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 20 \
    --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-4.4 \
    --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-4.4 \
    --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-4.4

sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.3 10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.4 20

然后选择默认的一个:
sudo update-alternatives --config gcc
sudo update-alternatives --config g++

1这帮助我在11和GCC 12之间切换。 - JAMSHAID

在临时目录中创建一个符号链接怎么样:
mkdir x && PATH=$PWD/x:$PATH && ln -s /usr/bin/g++-7 $PWD/x/g++

你可以使用"alternatives"命令。 希望能对你有所帮助!
sudo apt install build-essential

sudo apt install gcc-8 g++-8 gcc-9 g++-9 gcc-10 g++-10

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 --slave /usr/bin/gcov gcov /usr/bin/gcov-10

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 80 --slave /usr/bin/g++ g++ /usr/bin/g++-8 --slave /usr/bin/gcov gcov /usr/bin/gcov-8

sudo update-alternatives --config gcc

https://snipboard.io/zE4B9D.jpg


完整的update-alternatives命令集(希望如此)用于gcc,包括cppg++gcc-argcc-nmgcc-ranlibgcovgcov-dumpgcov-toollto-dump和匹配的x86_64-linux-gnu-链接! 在Debian bookworm (testing)上实施/测试过 在复制/粘贴并按下⮐Enter之前,请务必再次检查

背景

我不是程序员或编码人员,但我管理着一些Linux服务器(CentOS和Debian),并在家里运行Debian测试版。通常我只需使用apt install安装某些东西。很少需要执行./configure && make && make install之类的操作,所以除此之外我对此没有太多了解。在此之前,我只知道gccg++,但我发现我的系统上至少有15个与GNU编译器集合相关的链接,每个链接都指向相应的二进制文件或其他链接。
今天,我已经安装了GCC 12,并尝试构建一些东西,但这是一些旧代码,所以GCC 12太新了。时间过得真快,我还记得GCC 4很常见!当我检查时,我的系统也有版本8、9、10和11。然而,当我将GCC更新为指向版本11的二进制文件时,什么都没有改变。我意识到gcc-11现在甚至不再是一个二进制文件。它现在也是一个链接,指向x86_64-linux-gnu-gcc-11这个目标二进制文件。事实上,所有的二进制文件现在都以x86_64-linux-gnu-开头,以-vers结尾,并且有一个相应的通用链接,以x86_64-linux-gnu-开头。
然后,我想出了这个命令,用于安装可以通过update-alternatives命令切换的备选项。只需将开头的vers变量的值替换为您已安装的版本对应的值,优先级将基于版本,因此版本12的优先级为120,版本11的优先级为110,依此类推。最终,每个版本都会更新20个链接!
vers=<vers>; update-alternatives \
  --install /usr/bin/gcc gcc /usr/bin/gcc-"${vers}" "${vers}"0 \
  --slave /usr/bin/x86_64-linux-gnu-gcc x86_64-linux-gnu-gcc /usr/bin/x86_64-linux-gnu-gcc-"${vers}" \
  --slave /usr/bin/g++ g++ /usr/bin/g++-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-g++ x86_64-linux-gnu-g++ /usr/bin/x86_64-linux-gnu-g++-"${vers}" \
  --slave /usr/bin/cpp cpp /usr/bin/cpp-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-cpp x86_64-linux-gnu-cpp /usr/bin/x86_64-linux-gnu-cpp-"${vers}" \
  --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcc-ar x86_64-linux-gnu-gcc-ar /usr/bin/x86_64-linux-gnu-gcc-ar-"${vers}" \
  --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcc-nm x86_64-linux-gnu-gcc-nm /usr/bin/x86_64-linux-gnu-gcc-nm-"${vers}" \
  --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcc-ranlib x86_64-linux-gnu-gcc-ranlib /usr/bin/x86_64-linux-gnu-gcc-ranlib-"${vers}" \
  --slave /usr/bin/gcov gcov /usr/bin/gcov-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcov x86_64-linux-gnu-gcov /usr/bin/x86_64-linux-gnu-gcov-"${vers}" \
  --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcov-dump x86_64-linux-gnu-gcov-dump /usr/bin/x86_64-linux-gnu-gcov-dump-"${vers}" \
  --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-gcov-tool x86_64-linux-gnu-gcov-tool /usr/bin/x86_64-linux-gnu-gcov-tool-"${vers}" \
  --slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-"${vers}" \
  --slave /usr/bin/x86_64-linux-gnu-lto-dump x86_64-linux-gnu-lto-dump /usr/bin/x86_64-linux-gnu-lto-dump-"${vers}"

单行:
vers=<vers>; update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-"${vers}" "${vers}"0 --slave /usr/bin/x86_64-linux-gnu-gcc x86_64-linux-gnu-gcc /usr/bin/x86_64-linux-gnu-gcc-"${vers}" --slave /usr/bin/g++ g++ /usr/bin/g++-"${vers}" --slave /usr/bin/x86_64-linux-gnu-g++ x86_64-linux-gnu-g++ /usr/bin/x86_64-linux-gnu-g++-"${vers}" --slave /usr/bin/cpp cpp /usr/bin/cpp-"${vers}" --slave /usr/bin/x86_64-linux-gnu-cpp x86_64-linux-gnu-cpp /usr/bin/x86_64-linux-gnu-cpp-"${vers}" --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcc-ar x86_64-linux-gnu-gcc-ar /usr/bin/x86_64-linux-gnu-gcc-ar-"${vers}" --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcc-nm x86_64-linux-gnu-gcc-nm /usr/bin/x86_64-linux-gnu-gcc-nm-"${vers}" --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcc-ranlib x86_64-linux-gnu-gcc-ranlib /usr/bin/x86_64-linux-gnu-gcc-ranlib-"${vers}" --slave /usr/bin/gcov gcov /usr/bin/gcov-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcov x86_64-linux-gnu-gcov /usr/bin/x86_64-linux-gnu-gcov-"${vers}" --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcov-dump x86_64-linux-gnu-gcov-dump /usr/bin/x86_64-linux-gnu-gcov-dump-"${vers}" --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-"${vers}" --slave /usr/bin/x86_64-linux-gnu-gcov-tool x86_64-linux-gnu-gcov-tool /usr/bin/x86_64-linux-gnu-gcov-tool-"${vers}" --slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-"${vers}" --slave /usr/bin/x86_64-linux-gnu-lto-dump x86_64-linux-gnu-lto-dump /usr/bin/x86_64-linux-gnu-lto-dump-"${vers}"