Ubuntu 22.04 更新 C++ 编译器
Ubuntu 22.04 默认使用 g++-11 和 clang++-14 作为默认的 g++ 和 clang++。
本文说明如何升级 C++ 编译器并设置默认版本,从而使用最新的 C++ 语言特性。
安装最新版本的 C++ 编译器
安装最新的 g++
sudo apt update
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt install gcc-12 g++-12 -y
sudo apt install gcc-13 g++-13 -y
安装最新的 clang++
参见 LLVM Debian/Ubuntu Packages 中的安装指南:
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
# sudo ./llvm.sh <version number> all
# or
# sudo ./llvm.sh all
sudo ./llvm.sh 18 all
切换编译器的默认版本
上述命令执行之后,我们会发现,g++ 仍然是 g++11,clang++ 仍然是 clang++-14。
这里介绍一下 update-alternatives,由于管理 Ubuntu 上软件版本的切换,使其多版本共存。语法:
注册可选的软件版本:
sudo update-alternatives --install link name path priority \
                        [--slave link name path]
切换默认的软件版本:
sudo update-alternatives --config name
注意
切换编译器 软件包可能有风险,建议只在个人计算机上使用。
切换 g++ 的默认版本
注册 gcc / g++ 版本:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 11 --slave /usr/bin/g++ g++ /usr/bin/g++-11
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 --slave /usr/bin/g++ g++ /usr/bin/g++-12
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 --slave /usr/bin/g++ g++ /usr/bin/g++-13
选择默认 gcc / g++ 版本:
sudo update-alternatives --config gcc
会弹出以下选项:
There are 3 choices for the alternative gcc (providing /usr/bin/gcc).
  Selection    Path             Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gcc-13   13        auto mode
  1            /usr/bin/gcc-11   11        manual mode
  2            /usr/bin/gcc-12   12        manual mode
  3            /usr/bin/gcc-13   13        manual mode
Press <enter> to keep the current choice[*], or type selection number:
输入序号即可。
切换 clang++ 的默认版本
clang / clang++ 是 LLVM 工具链的一部分,更新的时候,建议把 llvm 所有工具一起更新。
这里使用以下脚本 update-alternatives-clang.sh 进行批量注册:
#!/usr/bin/env bash
# Colors
RESET=$(tput sgr0)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
BOLD=$(tput bold)
# Get available versions from /lib/llvm-*
# You can also use specific versions, e.g. VERSIONS=("14" "17" "18"), but not recommend
VERSIONS=()
for dir in /lib/llvm-*; do
    if [[ -d "$dir" ]]; then
        version=$(basename "$dir" | cut -d'-' -f2)
        VERSIONS+=("$version")
    fi
done
# Loop through versions
for VERSION in "${VERSIONS[@]}"; do
    # Check if /lib/llvm-${VERSION} directory exists
    if [[ -d "/lib/llvm-${VERSION}" ]]; then
        # Scan paths and generate string
        alternative_string=""
        alternative_cmds=()
        for cmd in "/lib/llvm-${VERSION}/bin/"*; do
            if [[ -x "$cmd" ]] && [[ "$(basename "$cmd")" != "clang" ]]; then
                base_cmd=$(basename "$cmd")
                symlink="/usr/bin/${base_cmd}-${VERSION}"
                if [[ -x "${symlink}" ]]; then
                    alternative_cmds+=($(basename ${symlink}))
                    alternative_string+="--slave /usr/bin/${base_cmd} ${base_cmd} ${symlink} "
                fi
            fi
        done
        # Remove specific alternative configuration
        sudo update-alternatives --remove clang "/usr/bin/clang-${VERSION}" > /dev/null
        # Install alternatives
        install_command="sudo update-alternatives \
        --quiet \
        --install /usr/bin/clang clang /usr/bin/clang-${VERSION} ${VERSION} \
        ${alternative_string}"
        # Print the concatenated string
        echo "${BOLD}${GREEN}[Adding alternative /usr/bin/clang-${VERSION} ...]${RESET}"
        echo "Master command: clang-${VERSION}"
        echo "Slave commands: ${alternative_cmds[*]}"
        eval "$install_command"
        # Check eval command's return value
        if [[ $? -eq 0 ]]; then
            echo "${BOLD}${GREEN}[Adding alternative /usr/bin/clang-${VERSION}: succeeded]${RESET}"
        else
            echo "${BOLD}${RED}[Adding alternative /usr/bin/clang-${VERSION}: failed]${RESET}"
        fi
        echo ""
    else
        # Remove specific alternative configuration if /lib/llvm-${VERSION} directory does not exist
        sudo update-alternatives --remove clang "/usr/bin/clang-${VERSION}" &> /dev/null
    fi
done
clang_path=$(sudo update-alternatives --get-selections | grep ^clang | awk '{print $NF}')
echo "======================================================================"
echo "${GREEN}clang alternative is set to: ${clang_path}${RESET}"
echo "======================================================================"
# print helps
echo ""
echo "Info:"
num_versions=${#VERSIONS[@]}
if [[ num_versions -gt 1 ]]; then
    echo "  use '${GREEN}sudo update-alternatives --config clang${RESET}' to change default clang alternative"
fi
echo "  use '${GREEN}sudo update-alternatives --remove clang /usr/bin/clang-*${RESET}' to delete a clang alternative"
echo "  use '${GREEN}sudo update-alternatives --remove-all clang${RESET}' to delete all clang alternatives"
sudo bash update-alternatives-clang.sh
结果如下:
[Adding alternative /usr/bin/clang-14 ...]
Master command: clang-14
Slave commands: analyze-build-14 bugpoint-14 ...
[Adding alternative /usr/bin/clang-14: succeeded]
[Adding alternative /usr/bin/clang-18 ...]
Master command: clang-18
Slave commands: amdgpu-arch-18 analyze-build-18 ...
[Adding alternative /usr/bin/clang-18: succeeded]
======================================================================
clang alternative is set to: /usr/bin/clang-18
======================================================================
...
同上面的 gcc/g++ 版本切换,切换 clang / llvm 工具链版本命令如下:
sudo update-alternatives --config clang
会弹出以下选项:
There are 2 choices for the alternative clang (providing /usr/bin/clang).
  Selection    Path               Priority   Status
------------------------------------------------------------
* 0            /usr/bin/clang-18   18        auto mode
  1            /usr/bin/clang-14   14        manual mode
  2            /usr/bin/clang-18   18        manual mode
Press <enter> to keep the current choice[*], or type selection number:
输入序号即可。
