2024年7月5日
Last updated on

compile_commands.json 简易科普与编译选项解释

作者:


compile_commands.json 记录每个翻译单元实际使用的编译命令。clangd、clang-tidy 需要它,才能知道源码对应的语言标准、宏、头文件目录和目标平台。一个文件可以有多条配置,数据库不是“全项目统一一组参数”。Clang 格式规范

看懂一条真实形状的记录

下面的 /work/demo 是示例工程路径,使用时换成自己的绝对路径:

[
{
"directory": "/work/demo/build",
"file": "/work/demo/src/main.cpp",
"arguments": [
"/usr/bin/clang++",
"-std=c++17",
"-I/work/demo/include",
"-DAPP_DEBUG=1",
"-Wall",
"-Wextra",
"-c",
"/work/demo/src/main.cpp",
"-o",
"main.cpp.o"
],
"output": "main.cpp.o"
}
]

directory 是编译时工作目录,相对路径都以它为基准;file 是这条命令对应的源文件;arguments 是已经拆好的参数数组,首项是编译器。output 可选,用来区分同一输入的不同编译输出。也可以用一个 command 字符串代替 arguments,但数组省掉了 shell 引号转义,Clang 官方更推荐它。字段定义

数据库通常由构建系统生成,长期手改很容易被下次配置覆盖。头文件多数没有独立记录,clangd 会参考包含它的源文件或推断编译命令。

CMake 工程怎么生成

如果项目已经有 CMakeLists.txt,用 Ninja 生成器配置并构建:

Terminal window
cmake -S . -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build

第一步生成 build/compile_commands.json;第二步让生成头文件等构建产物实际存在。这个开关适用于 Makefile 和 Ninja 生成器,不能套用到所有 IDE 生成器,Unity Build 也有额外限制。CMake 官方说明

CMake 生成方式按官方文档说明;下面的 clangd 检查使用手工最小数据库验证,不涵盖完整 CMake 或 Bazel 工程。Bazel 项目的路径不同,可以看 Bazel 编译数据库笔记。

常见选项,哪些会影响补全

选项作用排障时关注什么
-I/path添加普通头文件搜索目录相对目录是否基于 directory 存在
-isystem /path添加系统头文件目录系统头文件诊断处理与普通头文件不同
-iquote /path为 #include "..." 添加目录不参与尖括号头文件的这部分搜索
-DAPP_DEBUG=1定义宏编辑器与构建是否进入同一条件分支
-UAPP_DEBUG取消宏定义参数顺序是否把之前定义取消了
-std=c++17指定语言标准新语法是否因错误标准被标红
--target=... / --sysroot=...指定目标和工具链根目录交叉编译头文件是否匹配目标平台
-Wall -Wextra / -Werror启用警告 / 将警告视为错误严格警告有助于检查,但不保证程序没有缺陷

另外,-fstack-protector 会加入运行时栈保护代码,不能把它和 -Werror 合起来理解成“编译期能检出栈溢出”。-fno-omit-frame-pointer 用于保留帧指针、辅助栈回溯,也不等于所有配置下调用栈都完整。选项的具体可用性需要按 GCC/Clang 和目标平台核对。GCC 预处理选项、GCC 插桩选项

补全报错时先看编译命令

Terminal window
clangd --check=src/main.cpp --compile-commands-dir=build --log=verbose

日志出现 Compile command from CDB,说明选中了数据库记录;如果是 Generic fallback command,先修数据库位置或源文件路径,再查头文件。--compile-commands-dir 指向目录,不是 JSON 文件自身。clangd 排障说明

我排这类问题通常只看三件事:选没选中正确记录,参数中的路径存不存在,命令能不能在该工作目录单独编译。详细编辑器配置放在 clangd 与 VS Code 入门,完整阅读顺序见 C++ 与工具链导读。