Xc's Blog

搭建Chrome的clangd indexer server

2022/09/09 Share

前言

最近的新工作是第一次在Mac上对进行Chrome的二次开发。作为一个VSCode爱好者,必须在VSCode上搭建一个用着顺手的开发环境。

Chrome使用了GN/Ninja作为构建系统,llvm/clang作为编译器。由于编辑过程比较复杂,自带的C/C++插件无法做到开箱即用,恰好llvm项目有一个clangd的子项目,提供语法分析的服务,而clangd恰好有VSCode的插件,而Chrome也很贴心地提供了clangd所需的compile_commands.json生成脚本

这个方案看起来很美好,但是在实际使用中,发现了一些问题:

  1. clangd有了compile_commands.json之后,除了会解析当前正在编辑的文件及其依赖,还会自动去解析项目中所有的文件,这个过程会非常耗时(大约七八个小时),这个过程还会占用大量的CPU资源,导致电脑风扇狂转,让本就散热捉急的MBP2019变得更加烫手。
  2. 分析完所有文件后,如果代码有大批量的更改,比如切换分支,对main分支进行rebase等等,clangd会自动重新分析,main分支一天的commit的代码更改,便会导致clangd对几乎所有的文件进行重新分析。

针对这个问题,clangd是有一个解决方案的,那就是clangd indexer server。只需要在本地生成一次clangd index文件,然后使用clangd index server加载,就组成了一台可以供多个客户端使用的clangd indexer server。这样就可以避免每次都要重新生成clangd index文件,而且可以在多个客户端共享。

虽然Chrome很复杂,但是Chrome也是可以搭建clangd index server的,Chrome官方就有也有自己的clangd index server。作为Chrome的二次开发者,我们是不能直接用Chrome的index server的,所以我们需要搭建自己的clangd index server。

一些挑战

  1. Chrome使用的llvm版本并不是release版本,而是Chrome自己内部编译的版本,而我们的Chrome也使用了我们自己内部定制编译的llvm,而这个定制版本的llvm是不包含clangd的,所以我们需要自己编译clangd。
  2. Chrome官方的clangd index server是针对Linux的,搜索了网上的内容,Mac版本的Chrome remote index并没有人分享过相关内容,需要自己蹚水。

准备工作

  1. 确保本地已经安装好了Chrome的编译环境,即已经编译过一次Chrome
  2. 安装cmake,git,ninja,brew

搭建步骤

确定clangd的版本和gRPC的版本,并且下载其源码

  1. 在src目录下执行 gclient sync
  2. 在shell中运行third_party/llvm-build/Release+Asserts/bin/clang --version 你会得到类似于以下的信息

    clang version 15.0.0 (https://github.com/llvm/llvm-project.git 4ba6a9c9f65bbc8bd06e3652cb20fd4dfc846137)
    Target: x86_64-apple-darwin21.6.0
    Thread model: posix
    InstalledDir: /Users/xc/repos/llvm-project/build/bin
    

    其中的4ba6a9c就是llvm的commit version

  3. 下载llvm源码,执行 git clone https://github.com/llvm/llvm-project
  4. 切换到llvm的commit version,执行git checkout 4ba6a9c(将4ba6a9c替换为你自己的commit version)
  5. 在llvm的clang-tools-extra/clangd/index/remote/README.md文件中,写有grpc的版本号,以及代码的下载方式(本文以v1.36.3为例)
  6. 下载grpc源码,执行 git clone -b v1.36.3 https://github.com/grpc/grpc

编译gRPC(作为clangd的依赖项)

在上一步中的README.md中,有编译gRPC的步骤,如果已经按照步骤编译过gRPC,可以跳过这一步。PS:以下命令都是在grpc的根目录下执行

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
# 使用brew安装编译gRPC所需的依赖
brew install autoconf automake libtool shtool

# 加载git submodule中的内容
git submodule update --init

mkdir build

# 下一步中我们会用GRPC_INSTALL_PATH来指定gRPC的安装路径,这里我们指定为~/.grpc_install
export GRPC_INSTALL_PATH=$HOME/.grpc_install

cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \ # Release模式
-DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_PATH \
-G Ninja
# CMAKE_BUILD_TYPE=Release选项开启Release模式,gRPC的性能会更好
# gRPC_INSTALL=ON选项可以在编译后对gRPC进行安装,
# gRPC_BUILD_TESTS=OFF选项可以避免编译gRPC的测试用例
# CMAKE_INSTALL_PREFIX=$GRPC_INSTALL_PATH 选项指定gRPC的安装路径
# -G Ninja选项可以使用ninja来编译gRPC

# 编译gRPC,并且安装到~/.grpc_install目录下
cmake --build build --target install

编译clangd

使用shell进入llvm的根目录,执行以下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
makedir build

# 使用上一步中的GRPC_INSTALL_PATH来指定gRPC的安装路径
export GRPC_INSTALL_PATH=$HOME/.grpc_install

cmake -S llvm -B build \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DCLANGD_ENABLE_REMOTE=On \
-DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_PATH \
-DGRPC_INSTALL_PATH=$GRPC_INSTALL_PATH \
-G Ninja
# -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" llvm默认只编译clang,这里我们需要编译clangd,所以需要加上clang-tools-extra
# -DCLANGD_ENABLE_REMOTE=On 开启clangd的remote模式(默认是关闭的)
# -DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_PATH 指定gRPC的安装路径

cmake --build build

生成compile_commands.json

生成ninja build的中间文件

生成clangd的remote index

TODO:未完待续…

中间遇到的坑

  1. 版本不匹配
    clangd和gRPC的版本不匹配,会导致编译失败。
    我因为这个原因,编译了好几次,最终才摸索出找到正确的匹配的版本号的方法

  2. 中间文件缺失

后记

CATALOG
  1. 1. 前言
  2. 2. 一些挑战
  3. 3. 准备工作
  4. 4. 搭建步骤
    1. 4.1. 确定clangd的版本和gRPC的版本,并且下载其源码
    2. 4.2. 编译gRPC(作为clangd的依赖项)
    3. 4.3. 编译clangd
    4. 4.4. 生成compile_commands.json
    5. 4.5. 生成ninja build的中间文件
    6. 4.6. 生成clangd的remote index
  5. 5. TODO:未完待续…
  6. 6. 中间遇到的坑
  7. 7. 后记