如何用CMake生成多份compile_commands.json?解决头文件补全失效问题
我之前也碰到过一模一样的问题——用CMake生成的编译数据库只覆盖src目录,include里的头文件完全没有自动补全,折腾了好一阵才搞定,给你分享几个实用的方案:
一、让CMake为include目录的头文件生成编译条目
问题的根源是compile_commands.json里只记录了实际被编译的源文件(也就是src里的.cpp/.c文件)的编译参数,include里的头文件因为没有被直接编译,所以没有对应的条目,LSP(比如clangd)没法识别它们的上下文。
解决这个最简单的办法是让CMake把include里的头文件也当作“虚拟编译目标”处理,这样就会在数据库里生成对应的条目:
# 在根目录的CMakeLists.txt末尾添加 file(GLOB_RECURSE PROJECT_HEADERS include/*.h include/*.hpp) add_custom_target( project_headers SOURCES ${PROJECT_HEADERS} )
这个project_headers是个不会实际编译的空目标,但CMake会把所有头文件的路径和项目的包含参数写入compile_commands.json,LSP就能识别到include目录的文件了。
二、直接给LSP配置额外的包含路径
如果不想改动CMakeLists,也可以直接在Vim的LSP客户端里添加include目录的路径,强制让补全工具识别:
- 如果你用的是clangd(最常用的C/C++补全后端),可以在项目根目录创建
.clangd文件:
CompileFlags: Add: [-I./include]
- 要是用coc.nvim的coc-clangd插件,就打开
:CocConfig,添加:
{ "clangd.arguments": ["-I${workspaceFolder}/include"] }
这种方法不需要修改CMake配置,适合快速解决问题。
三、关于生成多份compile_commands.json的问题
CMake本身是每个构建目录生成一份编译数据库的,比如你如果分别在build/Debug和build/Release下运行cmake,每个目录都会有自己的compile_commands.json,对应不同的构建配置。
如果是想把同一份数据库复制到多个目录(比如src和include各放一份),可以用CMake的 post-build 命令自动复制:
# 替换成你的实际目标名称 add_custom_command( TARGET your_main_target POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_SOURCE_DIR}/src/ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_SOURCE_DIR}/include/ )
不过说实话,更推荐的做法是让LSP直接指向build目录里的那份数据库,比如在Vim里设置clangd的参数:
let g:clangd_args = ['-compile-commands-dir=' . getcwd() . '/build']
这样不用复制文件,每次构建后数据库自动更新,LSP也能实时获取最新的编译参数。
内容的提问来源于stack exchange,提问作者Spencer Duball




