Generate compile_commands.json for a CMake project
CMake writes compile_commands.json itself; turn that on before reaching
for Bear:
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build
CMake places the file in the build directory, the one passed to -B
(build above), not the source tree. Setting the same variable through a
CMakePresets.json cacheVariables entry, instead of the -D flag on
the command line, has the same effect. Point your editor at the file
this produces with Set up clangd for a project without
CMake, which covers --compile-commands-dir and the
.clangd CompilationDatabase key.
This is the better answer for the ordinary CMake project, and the rest of this page is about the cases where it is not enough.
Generators that ignore the variable
CMake’s own documentation for CMAKE_EXPORT_COMPILE_COMMANDS states:
“This option is implemented only by Makefile Generators and Ninja
Generators. It is ignored on other generators.” The Visual Studio and
Xcode generators fall into “other generators”: setting the variable with
either of those has no effect, and no compile_commands.json appears no
matter how the project builds. Reach for Bear instead, pointed at
whatever actually invokes the compiler (see Command-line
options for the -- split between
Bear’s own flags and the build command):
bear -- cmake --build build
This works regardless of which generator produced the build files; see How Bear works for why watching the build succeeds where the generator’s own export does not.
When the export does not match what actually ran
CMake’s export records the command it intends to run, generated at configure time. Three situations make that different from what the build actually executes:
- a compiler launcher in front of the compiler
(
CMAKE_C_COMPILER_LAUNCHER,CMAKE_CXX_COMPILER_LAUNCHER, or accache/distccsetup baked into the toolchain file); - a wrapper script standing in for the compiler;
- a custom command or custom target that compiles something outside CMake’s own compile rules.
Interception records what the build actually ran instead of what CMake planned:
bear -- cmake --build build
See Use Bear with ccache, distcc, or icecc for the launcher case in detail.
A superbuild, or CMake as one step of a larger build
When CMake is only part of the build - a superbuild that configures and
builds several subprojects, or a top-level Makefile that drives cmake
alongside other steps - CMake’s own export only ever covers the CMake
part. Run the whole thing under Bear instead, and every compiler
invocation ends up in the same database regardless of which step
produced it:
bear -- make
or, when the top-level driver is itself CMake:
bear -- cmake --build .
If the project does not use CMake
CMake is not the only build system that writes compile_commands.json
itself. Check whether yours already does before reaching for Bear:
- Ninja:
ninja -t compdb > compile_commands.json - Qbs:
qbs generate --generator clangdb - Waf: load the
clang_compilation_databasetool fromwscript, then runwaf clangdb - Bazel: no native flag, but Hedron’s Compile Commands Extractor does the same job as a third-party target
- Clang itself: pass
-MJ <fragment>.jsonto every invocation and concatenate the fragments into a single JSON array
Use your build system’s own export when it has one. Reach for Bear when it does not, or when what it exports does not match what actually ran.
clangd cannot find the file CMake wrote
compile_commands.json living in the build directory rather than the
project root is the usual reason clangd does not pick it up
automatically. See Set up clangd for a project without
CMake for how clangd searches and for pointing it at
the exact directory, and Bear produces an empty
compile_commands.json if the file exists
but is missing entries rather than missing entirely.
Related: Generate compile_commands.json for a Makefile project for the equivalent Bear-only workflow when there is no CMake export to fall back on, and the Recipes index.