背景
项目基于 C 语言,代码量庞大,并基于 vscode 的 C/C++ 插件实现代码跳转。
![vscode C/C++ 插件]()
开发过程中发现代码补全,跳转等功能存在问题,经常出现匹配不上或者跳转不准确的问题。每次重新加载工程项目或者更新协议后需要重新更新缓存,更新总是会卡住,就算不卡住也要耗费四五个小时,严重影响了开发效率。
针对以上问题,有几个处理办法:
- 换个 IDE,弃用 vscode
- 弃用微软的 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 以上。
| 12
 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 版本。
| 12
 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
| 12
 
 | # 检查动态库strings /usr/lib64/libstdc++.so.6 | grep GLIBC
 
 | 
clangd-12.0 需求 GLIBC >= v3.4.20,这里输出的版本号应该是不符合的。
| 12
 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
| 12
 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 文件。生成方法有以下三种:
- 如果通过 cmake 方式编译项目,在 - CMakeLists.txt文件中 添加- set(CMAKE_EXPORT_COMPILECOMMANDS ON),之后- cd build && cmake ..,可以发现在- build目录下已经生成了- compile_commands.json文件
 
- 如果是基于 make 方式来编译,那么可以先安装 - pip install compiledb,之后在当前目录下运行(1)- compiledb -n make -C build(2)- compiledb make -C build这两个命令中的其中一个来生成- compile_commands.json文件,其中前者不会执行真正的make编译命令。
 | 12
 
 | $ pip install compiledb$ compiledb -n make -C build
 
 |  
 
- 如果是基于其他方式,可以使用 - https://github.com/rizsotto/Bear项目中的方式来生成对应的- compile_commands.json文件
 
参考资料
- centos升级和安装cmake - 知乎
- 在 alios7 上安装 clangd - 知乎
- 使用clangd替代c/c++配置vscode c++项目 - 知乎
- LLVM+Clang+centos编译第一个openmp程序_静坐思己过的博客-程序员宝宝 - 程序员宝宝
- llvm编译失败几句话总结 - voyage1969 - 博客园
- How to choose llvm prebuild version under centos7.2 - Questions - Apache TVM Discuss