CMake

Generate a compilation database with CMake

CMake can generate a compilation database at configuration. When using a CMake project you have two ways of generating it:

  • Using the CMAKE_EXPORT_COMPILE_COMMANDS variable globally (from CMake 3.5): https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html

  • Using the EXPORT_COMPILE_COMMANDS target property for the specific target you want to analyze (from CMake 3.20): https://cmake.org/cmake/help/latest/prop_tgt/EXPORT_COMPILE_COMMANDS.html#prop_tgt:EXPORT_COMPILE_COMMANDS

Compilation database generation with CMake is only available with Makefiles and Ninja generators. Any other generator will ignore these variables and the compilation database will not be generated.

1

Enable compile commands globally

You can enable global generation either from the CMake command line:

Command
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .

or by setting it in the root CMakeLists.txt:

CMakeLists.txt
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
2

First, force the global generation off:

CMakeLists.txt
set(CMAKE_EXPORT_COMPILE_COMMANDS OFF)

Then, enable it for the specific target:

CMakeLists.txt
set_target_properties(mytarget PROPERTIES EXPORT_COMPILE_COMMANDS ON)

When running CMake, compile_commands.json is generated in the build directory. For example, running:

Command
cmake -B build -DCMAKE_BUILD_TYPE=Release .

will produce ./build/compile_commands.json.

In beLow, enter the CMake command as the "Configure script" when asked; beLow will find the compile_commands.json file by name.

Try not to leave any stale compile_commands.json files in your project root if you can generate them at configuration or build time. A leftover file may be used in priority and cause issues if it is out of date or uses absolute paths (in copy mode).

Windows specificities

On Windows, CMake's compile-commands generation can produce commands with unexpected escaping of backslashes and quotes. A common problematic case is when injecting include file names via preprocessor definitions.

Example C/C++ usage you might try:

source.c / source.cpp
#include LIB_HEADER_FILE

And in CMakeLists.txt:

CMakeLists.txt (problematic)
add_definitions(-DLIB_HEADER_FILE="lib.h")

CMake may generate an over-escaped entry in compile_commands.json like -DLIB_HEADER_FILE=\"lib.h\", which breaks the intended injection.

There are two ways to address this:

  • Modify the compilation database after generation with a script (not recommended).

  • Prefer rewriting the macro so the compiler adds the quotes. This is the recommended approach.

Recommended fix in source:

source.c / source.cpp
#define INCLUDE_FILE(x) #x
#include INCLUDE_FILE(LIB_HEADER_FILE)

Then, in your CMakeLists.txt, define the macro without quotes:

add_definitions(-DLIB_HEADER_FILE=lib.h)

or for a target:

target_compile_definitions(<your-target> PUBLIC LIB_HEADER_FILE=lib.h)

With this approach, after running CMake configuration the generated compilation database will be correct.

Last updated 1 month ago