centos7 配置 vscode+clangd 环境

背景

项目基于 C 语言,代码量庞大,并基于 vscode 的 C/C++ 插件实现代码跳转。

vscode C/C++ 插件

开发过程中发现代码补全,跳转等功能存在问题,经常出现匹配不上或者跳转不准确的问题。每次重新加载工程项目或者更新协议后需要重新更新缓存,更新总是会卡住,就算不卡住也要耗费四五个小时,严重影响了开发效率。

针对以上问题,有几个处理办法:

  1. 换个 IDE,弃用 vscode
  2. 弃用微软的 C/C++ 插件,选用 clangd 插件进行代码分析。相比于 C/C++,clangd具有全项目索引、代码跳转、变量重命名、更快的代码补全、提示信息、格式化代码等功能,内存占用和资源占用上也更具优势。

笔者选取了第二种方式,并开始了漫漫的踩坑之路。

首先,开发机(DevCloud) 基于 tLinux 2.2(centos 7),不提供 clangd 的 yum 包安装。然后,clangd 属于 llvm 的组件之一,llvm 官方并没有提供 centos7 的 pre-build binary,需要通过源码编译。

以下环境配置基于 GCC 7.1.0 + LLVM-12.0,其他版本尚未测试。

升级 cmake 版本

编译 llvm 需要 cmake 3.13 以上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 卸载旧有 cmake 版本
$ sudo yum remove cmake -y

# 从官网下载 cmake 源码进行编译
$ wget https://cmake.org/files/v3.15/cmake-3.15.0.tar.gz
$ tar xvf cmake-3.15.0.tar.gz
$ cd cmake-3.15.0
$ ./configure --prefix=/usr/local/cmake
$ make -j && make install

# 创建软连接
$ sudo ln -s /usr/local/cmake/bin/cmake /usr/bin/cmake

# 配置环境变量
$ vim /etc/profile
export CMAKE_HOME=/usr/local/cmake
export PATH=$PATH:$CMAKE_HOME/bin

# 检查 cmake 版本
$ cmake --version

升级 GCC 版本

为了不影响系统 GCC 配置(毕竟机器上还跑着其他 GCC 的项目),可以从源码单独编译一个 GCC 版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 最好选择 7.1 版本以上的,llvm推荐采用
$ cd ~/packages
$ wget http://ftp.gnu.org/gnu/gcc/gcc-7.1.0/gcc-7.1.0.tar.gz
$ tar -zxvf gcc-7.1.0.tar.gz

# 编译安装
$ cd gcc-7.1.0
$ ./contrib/download_prerequisites
$ cd ..
$ mkdir gcc-7.1.0-build && cd gcc-7.1.0-build
$ ../gcc-7.1.0/configure --prefix=$HOME/packages/GCC-7.1.0 --enable-languages=c,c++,fortran,go -enable-checking=release -disable-multilib

# 这一步耗时较久,请耐心等待
$ make -j
$ make install

GCC 安装目录在 $HOME/packages/GCC-7.1.0。

升级 GLIBC

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

clangd-12.0 需求 GLIBC >= v3.4.20,这里输出的版本号应该是不符合的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 找到编译 GCC 时生成的动态库
$ find ~/packages/gcc-7.1.0-build -name libstdc++.so*
/data/home/user00/packages/gcc-7.1.0-build/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/data/home/user00/packages/gcc-7.1.0-build/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.23
/data/home/user00/packages/gcc-7.1.0-build/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so
/data/home/user00/packages/gcc-7.1.0-build/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/data/home/user00/packages/gcc-7.1.0-build/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.23
/data/home/user00/packages/gcc-7.1.0-build/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so
/data/home/user00/packages/gcc-7.1.0-build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/data/home/user00/packages/gcc-7.1.0-build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.23
/data/home/user00/packages/gcc-7.1.0-build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so

# 替换动态库
# 1. 检查当前动态库
$ ll /usr/lib64/libstdc++*
lrwxrwxrwx 1 root root 18 Jun 19 2018 /usr/lib64/libstdc++.so.5 -> libstdc++.so.5.0.7
-rwxr-xr-x 1 root root 830776 Mar 6 2015 /usr/lib64/libstdc++.so.5.0.7
lrwxrwxrwx 1 root root 30 May 20 09:57 /usr/lib64/libstdc++.so.6 -> /usr/lib64/libstdc++.so.6.0.19
-rwxr-xr-x 1 root root 995840 Jan 9 2020 /usr/lib64/libstdc++.so.6.0.19
# 2. 复制动态库
$ sudo cp /data/home/user00/packages/gcc-7.1.0-build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.23 /usr/lib64/
# 3. 删除软链,只删除软链!!不要动 libstdc++.so.6.0.19
$ sudo rm -f /usr/lib64/libstdc++.so.6
# 4. 创建新的软链
$ sudo ln -s /usr/lib64/libstdc++.so.6.0.23 /usr/lib64/libstdc++.so.6
# 5. 再次检查当前动态库
$ ll /usr/lib64/libstdc++*
lrwxrwxrwx 1 root root 18 Jun 19 2018 /usr/lib64/libstdc++.so.5 -> libstdc++.so.5.0.7
-rwxr-xr-x 1 root root 830776 Mar 6 2015 /usr/lib64/libstdc++.so.5.0.7
lrwxrwxrwx 1 root root 30 May 20 09:57 /usr/lib64/libstdc++.so.6 -> /usr/lib64/libstdc++.so.6.0.23
-rwxr-xr-x 1 root root 995840 Jan 9 2020 /usr/lib64/libstdc++.so.6.0.19
-rwxr-xr-x 1 root root 11410559 May 20 09:56 /usr/lib64/libstdc++.so.6.0.23
$ strings /usr/lib64/libstdc++.so.6 | grep GLIBC

编译安装 llvm-clang

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 获取源码
$ wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/llvm-project-12.0.0.src.tar.xz
$ tar xvf llvm-project-12.0.0.src.tar.xz
$ cd llvm-project-12.0.0.src && mkdir build && cd build

# 注意指定 gcc 和 g++ 路径
$ cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_C_COMPILER=$HOME/packages/GCC-7.1.0/bin/gcc -DCMAKE_CXX_COMPILER=$HOME/packages/GCC-7.1.0/bin/c++ -DCMAKE_INSTALL_PREFIX=~/packages/llvm -DCMAKE_BUILD_TYPE=Release -std=c++11 ../llvm

# -j 指定并行编译,最好只指定核数的一半,不然会卡死
$ make -j4
$ make install

# 创建软链
$ sudo ln -s /data/home/user00/packages/llvm/bin/clangd /usr/bin/clangd

# 查看版本号
$ clangd --version
clangd version 12.0.0

vscode 配置 clangd

应用商店中安装 clangd 插件。

vscode clangd 插件

注意 C/C++ 插件和 clangd 插件不能同时启用,需要先禁用 C/C++ 插件。

clangd是基于 compile_commands.json 文件来完成对项目的解析,所以需要针对项目生成 compile_commands.json 文件。生成方法有以下三种:

  1. 如果通过 cmake 方式编译项目,在 CMakeLists.txt 文件中 添加 set(CMAKE_EXPORT_COMPILECOMMANDS ON),之后 cd build && cmake ..,可以发现在 build 目录下已经生成了 compile_commands.json 文件

  2. 如果是基于 make 方式来编译,那么可以先安装 pip install compiledb,之后在当前目录下运行(1) compiledb -n make -C build (2) compiledb make -C build 这两个命令中的其中一个来生成 compile_commands.json 文件,其中前者不会执行真正的make编译命令。

    1
    2
    $ pip install compiledb
    $ compiledb -n make -C build
  3. 如果是基于其他方式,可以使用 https://github.com/rizsotto/Bear 项目中的方式来生成对应的 compile_commands.json 文件

参考资料

  1. centos升级和安装cmake - 知乎
  2. 在 alios7 上安装 clangd - 知乎
  3. 使用clangd替代c/c++配置vscode c++项目 - 知乎
  4. LLVM+Clang+centos编译第一个openmp程序_静坐思己过的博客-程序员宝宝 - 程序员宝宝
  5. llvm编译失败几句话总结 - voyage1969 - 博客园
  6. How to choose llvm prebuild version under centos7.2 - Questions - Apache TVM Discuss