Clang 编译安装指南

Clang 是一个基于 LLVM 的 C/C++ 编译器,与 GCC 相比有一些优势

  • 编译速度比 GCC 快
  • 内存占用更小
  • 编译报错信息更友好
  • 工具链丰富:ASan, clangd, clang-tidy, clang-doc 等

如果 Linux 发行版比较新,可以直接用包管理器安装;但如果发行版比较老旧,就只能编译安装了。本文介绍一些编译安装 clang 的方法。

直接编译安装

Clang 是 LLVM 项目的一部分。LLVM 是一个编译器基础设施,它定义一种中间代码 (IR),并且能够将这种中间代码编译成各个平台 (x86, ARM, …) 的机器码。这在 LLVM 中称为编译器后端。而 clang 则是 C/C++ 的编译器前端,负责将 C/C++ 代码编译成 LLVM 中间代码。因此要编译安装 clang,我们就要编译 LLVM。

我们进入 LLVM 官网的下载页面 https://releases.llvm.org/ 下载 LLVM 源码。LLVM 10.0.0 之后提供整合包 llvm-projectLLVM 全家桶 下载,包含 clang 在内的各种 LLVM 组件。以最新的 20.1.0 为例,我们直接下载 LLVM 20.1.0 并解压

1
2
3
curl -LO https://github.com/llvm/llvm-project/releases/download/llvmorg-20.1.0/llvm-project-20.1.0.src.tar.xz
unxz llvm-project-20.1.0.src.tar.xz
tar -xf llvm-project-20.1.0.src.tar

LLVM 使用 CMake 构建,要求版本至少为 3.20.0。进入 llvm-project 目录,接着创建 build 目录,然后执行 cmake

1
2
3
4
5
cd llvm-project-20.1.0.src
mkdir build && cd build
cmake ../llvm -DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS='clang' \
-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'

CMake 这一步可以传入各种参数,这里介绍一些常用的参数

  • CMAKE_BUILD_TYPE 构建类型,可以为 Debug, Release, RelWithDebInfo, 或 MinSizeRel。默认为 Debug,一般设置为 Release
  • DLLVM_ENABLE_PROJECTS 启用的组件。llvm-project 中有很多组件,除了 clang,常用的还有
    • clang-tools-extra 额外工具集。包含 clangd, clang-tidy, clang-doc, clang-include-fixer 等。
    • lldb 调试器。Clang 编译的程序用 gdb 调试可能会遇到各种问题,最好用 lldb 调试。
    • lld 链接器,可替代 ld。
    此外还有 bolt, polly, libclc 等组件,具体可参见 LLVM 文档。多个组件之间用分号 ; 分隔。
  • DLLVM_ENABLE_RUNTIMES 启用的运行时组件。常用的组件有
    • compiler-rt 编译器运行时。如果要用 Sanitizer 系列工具(如 ASan),则必选。
    • libcxxlibcxxabi 是 LLVM 的 C++ 标准库实现。Clang 默认将程序链接到系统的 libstdc++,但如果要链接到 LLVM 的标准库 libc++,这两个必选
    • libunwind 实现堆栈展开的库。如果要链接到 libc++,则必选。
    此外还有 libc, llvm-libgcc, offload 等组件,具体可参见 LLVM 文档。多个组件之间用分号 ; 分隔。
  • CMAKE_INSTALL_PREFIX 安装路径前缀,默认为 /usr/local
  • CMAKE_C_COMPILERCMAKE_CXX_COMPILER 分别指定 C 和 C++ 的编译器,默认为 gccg++。后面我们会用到这两个参数。
  • LLVM_ENABLE_LIBCXX 是否链接到 libc++。默认链接到 libstdc++。后面我们会用到这个参数。

如果 LLVM 的各个依赖项都没有问题、这一步成功后,便可执行 make 开始构建

1
make -j8 # 根据机器情况调整线程数 

如果一切顺利,执行 sudo make install 即可完成安装。安装前可以执行 make check-clang 执行 clang 的测试用例,确认没有问题。

老旧发行版编译

要成功构建 LLVM 20.1.0,GCC 版本至少为 7.4。然而在一些老旧发行版(如 CentOS 7)中,GCC 版本并不能满足要求。为此我们需要先用系统的老版编译器编译一个新版编译器,再用新版编译器编译 LLVM。

1
2
3
4
5
6
7
8
9
10
11
12
# 下载 gcc-9.1.0
curl -LO https://ftp.gnu.org/gnu/gcc/gcc-9.1.0/gcc-9.1.0.tar.xz
unxz gcc-9.1.0.tar.xz
tar -xf gcc-9.1.0.tar

cd gcc-9.1.0

# 安装依赖
./contrib/download_prerequisites
./configure --prefix=${HOME}/toolchains # 安装到 ~/toolchains
make -j8
make install

这里我们编译的 gcc-9.1.0 是用于构建 LLVM 的“临时”编译器,我们不把它安装到系统目录 (/usr/local/),而是安装到 ~/toolchains。GCC 是系统编译器,不可随意升级,否则可能导致系统其它软件出现兼容性问题。

接着我们用刚刚编译的 gcc-9.1.0 构建 LLVM。

1
2
3
4
5
6
7
8
9
cd llvm-project-20.1.0.src
mkdir build && cd build
cmake ../llvm -DCMAKE_C_COMPILER=${HOME}/toolchains/bin/gcc \
-DCMAKE_CXX_COMPILER=${HOME}/toolchains/bin/g++ \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS='clang' \
-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'

LD_LIBRARY_PATH=${HOME}/toolchains/lib:${HOME}/toolchains/lib64 make -j8

这里我们用 -DCMAKE_C_COMPILER-DCMAKE_CXX_COMPILER 指定 C/C++ 编译器的完整路径,也就是 gcc-9.1.0 的安装路径 ~/toolchains/ 下的 bin/gccbin/g++。LLVM 构建过程中会执行编译出来的工具,这些工具都依赖于 gcc-9.1.0 的 C++ 运行库。因此我们要用环境变量 LD_LIBRARY_PATH 指定动态库路径,确保它们能正常运行。

因为这样构建的 LLVM 工具链都依赖于 gcc-9.1.0 的运行库,我们要设置好 LD_LIBRARY_PATH 才能正常运行它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ bin/clang --version # 直接运行通常会出现 libstdc++ 不兼容的报错
bin/clang: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by bin/clang)

$ LD_LIBRARY_PATH=${HOME}/toolchains/lib:${HOME}/toolchains/lib64 bin/clang --version # 需要指定 gcc-9.1.0 的动态库路径
clang version 20.1.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/luyuhuang/llvm-project-20.1.0.src/build/bin

$ LD_LIBRARY_PATH=${HOME}/toolchains/lib:${HOME}/toolchains/lib64 ldd bin/clang
linux-vdso.so.1 => (0x00007fffe95c9000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f470cbbb000)
librt.so.1 => /lib64/librt.so.1 (0x00007f470c9b3000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f470c7af000)
libz.so.1 => /lib64/libz.so.1 (0x00007f470c599000)
libstdc++.so.6 => /home/luyuhuang/toolchains/lib64/libstdc++.so.6 (0x00007f470c1c0000)
libm.so.6 => /lib64/libm.so.6 (0x00007f470bebe000)
libgcc_s.so.1 => /home/luyuhuang/toolchains/lib64/libgcc_s.so.1 (0x00007f470bca6000)
libc.so.6 => /lib64/libc.so.6 (0x00007f470b8e2000)
/lib64/ld-linux-x86-64.so.2 (0x00007f470cdd7000)

Bootstrap

前面使用 gcc-9.1.0 编译的 LLVM 工具链虽然可以运行,但是却依赖于 gcc-9.1.0 的运行库。由于系统的 C++ 运行库不能随意升级,我们不便将 gcc-9.1.0 的运行库安装到系统中。这里比较合适的做法是让 clang 做一次 bootstrap(自举),用 gcc-9.1.0 编译的 clang 构建 LLVM,并链接到 LLVM 的 C++ 运行库(也就是 libc++)。

1
2
3
4
5
6
7
8
9
10
11
cd llvm-project-20.1.0.src
mkdir build1 && cd build1 # 创建一个新的构建目录
cmake ../llvm -DCMAKE_C_COMPILER= $(realpath ../build)/bin/clang \ # 使用 ../build 目录下,用 gcc-9.1.0 编译的 clang 构建
-DCMAKE_CXX_COMPILER= $(realpath ../build)/bin/clang++ \
-DLLVM_ENABLE_LIBCXX=ON \ # 使用 LLVM 的 libc++
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS='clang' \
-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'

LD_LIBRARY_PATH=${HOME}/toolchains/lib:${HOME}/toolchains/lib64:$(realpath ../build)/lib/x86_64-unknown-linux-gnu make -j8

这里我们则是将编译器路径设置成前面用 gcc-9.1.0 编译的 clang 的路径。同时设置 -DLLVM_ENABLE_LIBCXX=ON 链接到 LLVM 的 libc++。最后注意环境变量 LD_LIBRARY_PATH 除了需要指定 gcc-9.1.0 的运行库路径之外,还需要指定前面 gcc-9.1.0 编译的 LLVM 的运行库路径。

构建完成后,执行 sudo make install 安装即可。由于 LLVM 20.1.0 的 C++ 运行库位于 lib/x86_64-unknown-linux-gnu/(更老版本的 LLVM 则直接在 lib/ 里),我们通常需要再配置下动态库搜索路径。

1
2
echo /usr/local/lib/x86_64-unknown-linux-gnu >> /etc/ld.so.conf
ldconfig

这样安装的 clang 就能正常运行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ clang --version
clang version 20.1.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin

$ ldd /usr/local/bin/clang
linux-vdso.so.1 => (0x00007ffc6d57c000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fca001ae000)
librt.so.1 => /lib64/librt.so.1 (0x00007fc9fffa6000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fc9ffda2000)
libm.so.6 => /lib64/libm.so.6 (0x00007fc9ffaa0000)
libz.so.1 => /lib64/libz.so.1 (0x00007fc9ff88a000)
libc++.so.1 => /usr/local/lib/x86_64-unknown-linux-gnu/libc++.so.1 (0x00007fc9ff583000)
libc++abi.so.1 => /usr/local/lib/x86_64-unknown-linux-gnu/libc++abi.so.1 (0x00007fc9ff33b000)
libunwind.so.1 => /usr/local/lib/x86_64-unknown-linux-gnu/libunwind.so.1 (0x00007fc9ff12e000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc9fef18000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc9feb54000)
/lib64/ld-linux-x86-64.so.2 (0x00007fca0ad52000)
libatomic.so.1 => /lib64/libatomic.so.1 (0x00007fc9fe94c000)

参考资料:


Clang 编译安装指南
https://luyuhuang.tech/2025/03/30/clang.html
Author
Luyu Huang
Posted on
March 30, 2025
Licensed under