跳到主要内容

配置clangd lsp server

·2414 字·5 分钟
在编写tuna项目时,不想在源文件内使用绝对路径去引入头文件,因此想要为clangd语言服务器提供配置参数。在了解的过程中,可配置clangd按照哪个编译器去分析代码、按照哪个标准解析、并可设置clangd寻找头文件的路径等等

问题引入

由于在build.zig文件中,使用了addIncludePath(b.path("include")),告诉zig去项目根目录下去找所需要的头文件,但是在源文件内,我也想使用"tuna/core/config.hpp"这种格式,不想使用../../类似这种的方式去引入头文件,看着很混乱,这时候就需要配置一下语言服务器clangd。 根据自己的了解,可通过配置文件的方式去配置clangd。

  • 在当前用户的根目录下的.config目录下创建一个clangd目录,并在其目录创建一个config.yaml配置文件,将配置参数写在文件内。这一种配置的方式是用户级配置,它会作用于使用clangd解析cpp源文件和头文件的所有项目。暂时不用它。
  • 还有一种是项目级配置,它只作用于当前项目。需要在项目的根目录创建一个.clangd文件。
    一般将通用的配置内容写在~/.config/clangd/config.yaml,项目专用参数放在项目根目录下的.clangd文件中。但是这是在基于GNU/LINUX。Macos中是需要在~/Library/Preferences/clangd/这个目录下创建config.yaml。Windows中是%LocalAppData%\clangd\目录下创建config.yaml。

在tuna目录下创建一个.clangd文件,并添加以下内容

1
2
3
4
5
6
7
8
CompileFlags:
  Compiler: clang++
  Add:
    - -std=c++14
    - -Iinclude
    - -Wall
    - -Wextra
    - -Wpedantic
  • CompileFlags:表明下面这些是clangd分析C++文件时使用的编译参数
  • Compiler:告诉clangd按照clang的方式去解析项目中的C14代码
  • Add:表示给clangd额外添加编译参数
    • -std=c14:告诉clangd解析当前.cc/.hpp文件时按照C14标准解析
    • -Iinclude:这是我需要的编译参数,告诉clangd去项目根目录下的include目录搜索头文件
    • 下面是一些常见的编译参数了 写入到.clangd文件之后,在src/core目录下,打开config.cc文件,引入"tuna/core/config.hpp",检查是否出现错误
      01
      通过图片可以看出,还是不行,但是不清楚是没加载.clangd文件,还是编译参数配置错误。需要进行检测,使用clangd –check 在项目根目录下执行以下命令
1
clangd --check=src/core/config.cc --log=verbose

查看核心输出结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
I[22:40:21.179] Ubuntu clangd version 20.1.2 (0ubuntu1~24.04.2)
I[22:40:21.179] Features: linux+grpc
I[22:40:21.179] PID: 135174
I[22:40:21.179] Working directory: /home/parallels/tuna
I[22:40:21.179] argv[0]: clangd
I[22:40:21.179] argv[1]: --check=src/core/config.cc
I[22:40:21.179] argv[2]: --log=verbose
V[22:40:21.179] User config file is /home/parallels/.config/clangd/config.yaml
I[22:40:21.180] Entering check mode (no LSP server)
I[22:40:21.180] Testing on source file /home/parallels/tuna/src/core/config.cc
V[22:40:21.180] config note at /home/parallels/tuna/.clangd:1:0: Parsing config fragment
V[22:40:21.180] config note at /home/parallels/tuna/.clangd:1:0: Parsed 1 fragments from file
V[22:40:21.180] Config fragment: compiling /home/parallels/tuna/.clangd:1 -> 0x0000AB95F991E020 (trusted=false)
V[22:40:21.180] Config fragment: compiling <unknown>:0 -> 0x0000AB95F991D420 (trusted=false)
I[22:40:21.180] Loading compilation database...
I[22:40:21.180] Failed to find compilation database for /home/parallels/tuna/src/core/config.cc
I[22:40:21.180] Generic fallback command is: [/home/parallels/tuna/src/core] /usr/lib/llvm-19/bin/clang++ --driver-mode=g++ -std=c++14 -Iinclude -Wall -Wpedantic -Wextra -resource-dir=/usr/lib/llvm-20/lib/clang/20 -- /home/parallels/tuna/src/core/config.cc
I[22:40:21.180] Parsing command...

在输出的信息中,唯有这一段最是关键。从中可以看出,clangd成功读取到了.clangd文件。但是此时clangd继续执行,去加载编译数据库,最后发现没有找到编译数据库(Failed to find compilation database),也就是说没有从编译数据库中找到这个config.cc文件的真实编译命令。编译数据库在C、C++工程里,最常见的文件名是compile_commands.json,这里面记录了每个源文件所在的目录和编译这个源文件所需要的编译命令。

这个compilation database的作用就是让clangd准确的知道这个文件在真实项目里到底是如何被编译的。 但是在当前项目中,我使用的是zig构建系统,没有使用cmake和makefile,因此也无法生成compile_commands.json。

一般编译数据库可通过cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON生成,还可通过bear生成。因此,只要项目编译成功,生成编译数据库。clangd就可以知道所需要的头文件去哪里寻找,但是当前项目是没有这个编译数据库的。 因此它自己构造了一个fallback command,但是fallback command没有真实构建系统提供的工作目录,所以clangd选择了一个近似目录,也就是输出信息中的/home/parallels/tuna/src/core。

也就是说它会将这个目录当作编译命令的工作目录,因此在这个目录下执行编译命令,那么-Iinclude就会按/home/parallels/tuna/src/core/include去解释,那么都没有这个目录,如何去寻找头文件呢?真正的头文件是在/home/parallels/tuna/include下,所以clangd报错的原因在这里。

问题解决

当前的tuna项目中,不采用cmake和bear去生成编译数据库。那么换个思路,既然知道它是自己构建了一条fallback command,在这条编译命令中依然会传递-I选项,那么就将-I带的头文件搜索路径,设置为绝对路径/home/parallels/tuna/include。这样无论你如何自己构建fallback command,无论在tuna项目中的哪个源目录下,都会去这个绝对路径下去寻找,否则使用相对路径的话,fallback command自定义的根目录不一定是项目的根目录,那么就不会找到头文件真正所在的目录。

1
2
3
4
5
6
7
8
CompileFlags:
  Compiler: clang++
  Add:
    - -std=c++14
    - -I/home/parallels/tuna/include
    - -Wall
    - -Wpedantic
    - -Wextra

再次使用以下命令进行检查

1
clangd --check=src/core/config.cc --log=verbose

再次查看输出信息

1
2
3
I[23:18:32.453] Failed to find compilation database for /home/parallels/tuna/src/core/config.cc
I[23:18:32.453] Generic fallback command is: [/home/parallels/tuna/src/core] /usr/lib/llvm-19/bin/clang++ --driver-mode=g++ -std=c++14 -I/home/parallels/tuna/include -Wall -Wpedantic -Wextra -resource-dir=/usr/lib/llvm-20/lib/clang/20 -- /home/parallels/tuna/src/core/config.cc
I[23:18:32.453] Parsing command...

虽然还是找不到编译数据库,但是在新构建的fallback command中,-I后变成了绝对路径,再次使用nvim打开config.cc查看是否报错

02
完美解决