clangd 是一款 C/C++ language server,可以很方便地进行代码跳转、提示、补全等,本文总结在 alios 7 上安装 clangd 的一些经验

根据 clangd 官网安装指南,有 3 种方式安装 clangd:

  1. apt / yum 等系统包管理工具
  2. 下载预编译的 binary 文件
  3. 手动从源码编译

因为系统的包管理工具可能会引入各种奇奇怪怪的依赖,因此首先选择从 binary 安装

从 binary 安装 clangd

github 下载源码并解压,运行 clangd --version ,发现如下报错信息:

1
clangd_12.0.0/bin/clangd: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by clangd_12.0.0/bin/clangd)

此时发动 google 大法,搜索到博客 Install Clangd on CentOS 7,发现博主在从 binary 安装 clangd 的时候遇到同样 GLIBC 版本太旧的问题,且没有解决:(

既然此路不通,天真的我决定换一种方式,从编码编译安装,从此走上一条更艰难的道路。。

clangd 源码编译安装

首先选择一个你喜欢的目录,比如 ~/downloads, 依次运行如下命令:

1
2
3
4
5
6
7
8
cd ~/downloads
git clone --depth=1 https://github.com/llvm/llvm-project.git
cd llvm-project

mkdir build && cd build
cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_INSTALL_PREFIX=~/tools/llvm -DCMAKE_BUILD_TYPE=Release ../llvm
make -j 16
make install

如果不出意外,编译好的二进制文件将会出现在 ~/tools/llvm。在 ~/tools/llvm下运行 clangd --version,可以看到版本信息,之后就可以愉快地使用 clangd 了。

然而万事总怕意外,编译时发现系统 GCC 版本太旧,无法编译,此时继续 google,发现cmake可以用 -DCMAKE_C_COMPILER 选项制定编译器,于是又走上了升级 GCC 版本的不归路。

编译安装 GCC

为了不搞坏系统 GCC 配置,我选择从源码单独编译一个 GCC 版本,参考 GCC 安装指南,运行以下命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cd ~/download
wget http://ftp.gnu.org/gnu/gcc/gcc-6.2.0/gcc-6.2.0.tar.gz
tar -zxvf gcc-6.2.0.tar.gz
cd gcc-6.2.0
./contrib/download_prerequisites
cd ..
mkdir gcc-6.2.0-build && cd gcc-6.2.0-build
../gcc-6.2.0/configure --prefix=$HOME/GCC-6.2.0 --enable-languages=c,c++,fortran,go -enable-checking=release -disable-multilib
make
make install

以上命令会把 GCC-6.2.0 安装在 prefix 选项指定的 $HOME/GCC-6.2.0 目录,安装成功后,继续使用以下命令编译 llvm。

1
2
cd ~/downloads/llvm-project/build
cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_C_COMPILER=$HOME/GCC-6.2.0/bin/gcc -DCMAKE_INSTALL_PREFIX=~/tools/llvm -DCMAKE_BUILD_TYPE=Release ../llvm

其中 -DCMAKE_C_COMPILER执行了我们刚刚编译好的 GCC。

不出意外,这次编译又失败了,报错 GLIBC 不存在,类似:

1
/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found

摔,这不就是从 binary 安装时遇到的报错信息吗?折腾一大圈又回到最初的起点,继续 google 大法。

升级 GLIBC

发现博客 ,于是开始更新 GLIBC++,依次运行如下命令:

1
2
# 检查动态库
strings /usr/lib64/libstdc++.so.6 | grep GLIBC

输出类似:

1
2
3
4
5
6
7
8
...
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH

总之,这里输出的动态库版本一定会比报错信息中的版本小,因为我们编译新版 GCC 时没有升级动态库。

首先找到编译GCC 时生成的动态库,运行 find ~/ -name "libstdc++.so*" :输出以下信息:

1
2
3
4
.../downloads/build-gcc-6.2.0/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
.../downloads/build-gcc-6.2.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.22 # 最新动态库
.../downloads/build-gcc-6.2.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so
.../downloads/build-gcc-6.2.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6

后缀最长的libstdc++.so.6.xxx 就是最新动态库,运行以下命令替换动态库:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
sudo cp ~/downloads/build-gcc-6.2.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.22 /usr/lib64/libstdc++.so.6.0.22-my

cd /usr/lib64

# 先用ll 看一下里面有啥
ll libstdc++*
# 输出,其中 libstdc++.so.6.0.22-my 是刚复制过来的
lrwxrwxrwx  1 root root       30 Nov 29 11:59 libstdc++.so.6 -> libstdc++.so.6.19
-rwxr-xr-x 10 root root   995848 Jun 13  2018 libstdc++.so.6.0.19
-rwxr-xr-x  1 root root 11509888 Nov 27 17:31 libstdc++.so.6.0.22-my
# 删除原来的软连接,注意一定不要删掉原来的文件,只是删除软连接 libstdc++.so.6
sudo rm -f libstdc++.so.6

# 替换软连接
ln -s libstdc++.so.6.0.22-my libstdc++.so.6

# 查看替换结果
ll libstdc++*
lrwxrwxrwx  1 root root       30 Nov 29 11:59 libstdc++.so.6 -> libstdc++.so.6.22-my
-rwxr-xr-x 10 root root   995848 Jun 13  2018 libstdc++.so.6.0.19
-rwxr-xr-x  1 root root 11509888 Nov 27 17:31 libstdc++.so.6.0.22-my

这样动态库就更新成功了。

经过这样一番折腾,切到 ~/downloads/llvm-project/build 重新编译 llvm,应该可以成功了(反正我是成功了),编译过程较慢,需要耐心等一下。

将 clangd 加入 PATH

编译成功后,~/tools/llvm 目录应该有一大堆二进制文件,运行以下命令验证编译是否成功

1
2
3
4
5
6
cd ~/tools/llvm
clangd --version
# 输出如下信息表示编译成功
clangd version 14.0.0 (http://github.com/llvm/llvm-project.git 6fa8f7beb1928dfad291314c8baf16374a1cc145)
Features: linux
Platform: x86_64-unknown-linux-gnu

之后需要将路径加入到PATH,才能在命令行执行 clangd 命令。

用 vi 打开 ~/.bash_profile ,输入PATH=$PATH:$HOME/tools/llvm,保存退出后执行 source ~/.bash_profile,即可在命令行直接执行 clangd。

在 vscode 中使用 clangd

经过这么一大圈折腾,终于可以在vscode 中愉快滴使用 clangd 了,首先安装 clangd 插件,并加入如下配置:

1
2
3
4
5
6
7
8
    "clangd.arguments": [
        "--compile-commands-dir=${workspaceFolder}/build_debug",
        "-j=12",
        "--background-index",
        "--clang-tidy",
        "--log=verbose",
        "--pretty",
    ],

注意,clangd 依赖 compile_commands.json 文件解析源码,因此需要在 --compile-commands-dir 选项配置你的文件路径。

如果你是用 cmake 构建项目,可以在cmake 时加入如下选项:

1
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1

即可生成 compile_commands.json 文件,其他构建方式自行google。

从此以后,程序员和 clangd 过上了幸福的生活。

总结

回顾整个安装过程,发现自己对 GCC 编译体系还是不够熟悉,明明可以在从 binary 安装时就更新 GLIBC 从而解决问题,硬是绕了这么一大圈,还是应该加强学习,埋头查报错信息不如仔细看看报错信息背后的知识体系,可能有事半功倍的作用。