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 生成器配置并构建:
cmake -S . -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ONcmake --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 插桩选项
补全报错时先看编译命令
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++ 与工具链导读。