How to generate a JSON compile commands database with CMake

Generating a compile_commands.json file, a JSON database of compile commands, using CMake isn’t something I run into as a web developer. But as somebody that’s recently gotten bitten by the game development bug, this was one of the first things I happened to have run into.

I’m not going to bore you too much with the details of my foray into game development. I’m trying to build a few small games, think Atari-era arcade games. The plan is to release them on Steam, and I’m focusing heavily on the native Steam Deck’s native screen resolution of 1280 by 800, as that will be my primary consumption device for these games.

Coincidentally, I’ve written games in the past, using web technologies as well as writing native games for Android and iOS. For a change of pace, I’m focusing on learning more about C++ this time around.

Syntax errors for days

Armed with my favorite text editor, Neovim, I set out to write my game. And within a few minutes, my syntax checking plugin, ALE, was not very happy at all:

The errors make sense, as it’s barking about things that aren’t explicitly in my code. In Node.js and TypeScript, developers must define exceptions for implied global variables, which I’m no stranger to.

But how the heck do I do this with a C++ program?

During some research, I found the missing piece to making clangd and other C++ syntax checkers happy would be to provide it with a JSON database of compile commands. This typically lives in a file named compile_commands.json, which can be generated when building your project.

While I had read that this file needs to be in the root of your project, I found that it was sufficient enough to leave the file inside of my build directory ./build. This is a personal choice of mine, but if I were to distribute my code, I’d probably move the file out of the build directory, so that it could be picked up by version control and shared. That way other engineers wouldn’t need to build the program before trying to edit the code.

Configuring CMake to export compile commands

So how does one create this compile_commands.json file? First, you will need to update your CMakeLists.txt file:

cmake_minimum_required(VERSION 3.16)
project(Game LANGUAGES CXX)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# Add this line, to enable compile command export
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Other stuff...
CMakeLists.txt

Alternatively you could pass this option along as arguments to the cmake command, but that’s out of scope for how I’m building my program.

Finally, with the new option set, we can run our cmake build commands:

% cmake -B build -DCMAKE_BUILD_TYPE=Release 

-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/josh/Code/Game/build

% cmake --build build --config Release 

[ 14%] Built target sfml-system
[ 48%] Built target sfml-window
[ 73%] Built target sfml-graphics
[ 74%] Built target Game
[ 82%] Built target sfml-network
[100%] Built target sfml-audio
Zsh

And inside of our build directory, mine being ./build there is now a JSON database of compile commands in the compile_commands.json file:

% head build/compile_commands.json       
[
{
  "directory": "/Users/josh/Code/Game/build",
  "command": "/Library/Developer/CommandLineTools/usr/bin/c++ -DSFML_STATIC -I/Users/josh/Code/Game/build/_deps/sfml-src/include -O3 -DNDEBUG -std=gnu++17 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.0.sdk -F/Users/josh/Code/Games/build/_deps/sfml-src/extlibs/libs-osx/Frameworks  -o CMakeFiles/Game.dir/src/main.cpp.o -c /Users/josh/Code/Game/src/main.cpp",
  "file": "/Users/josh/Code/Game/src/main.cpp",
  "output": "CMakeFiles/Game.dir/src/main.cpp.o"
},
{
  "directory": "/Users/josh/Code/Game/build/_deps/sfml-build/src/SFML/System",
  "command": "/Library/Developer/CommandLineTools/usr/bin/c++ -DSFML_STATIC -I/Users/josh/Code/Game/build/_deps/sfml-src/include -I/Users/josh/Code/Game/build/_deps/sfml-src/src -O3 -DNDEBUG -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.0.sdk -fvisibility=hidden  -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wdouble-promotion -Wformat=2 -Wnull-dereference -Wold-style-cast -Wpedantic -Wno-unknown-warning-option -o CMakeFiles/sfml-system.dir/Clock.cpp.o -c /Users/josh/Code/Game/build/_deps/sfml-src/src/SFML/System/Clock.cpp",
Zsh

With the compile commands JSON database created by CMake, my code is now free of all of the noisy errors and I’m free to resolve the actual errors in my code, and continue out my merry way.

Josh Sherman - The Man, The Myth, The Avatar

About Josh

Husband. Father. Pug dad. Musician. Founder of Holiday API, Head of Engineering and Emoji Specialist at Mailshake, and author of the best damn Lorem Ipsum Library for PHP.


If you found this article helpful, please consider buying me a coffee.