This post presents minimal steps required to generate Clang-compatible compilation database file for several existing C/C++ build systems, example compile_commands.json
generated by each build system based on the same minimal project, as well as discussion of any caveats and quirks that the generated compilation database may have.
Yes, I know what you’re probably thinking, given how straightforward (albeit sometimes a little vague) JSON compilation database specification is - how many different ways can there actually be to serialize it into JSON? Well, we’ll see…
Oh, and as a bonus, we’ll learn how to setup a C++ hello world project in 17 different build systems!
Table of contents
Open Table of contents
Motivation
As the author of clang-uml - a Clang-based UML diagram generator for C++ - one of the major source of issues I get from users (other than my bugs) is related to compilation databases: either generation of compile_commands.json
itself or some issues with the compiler flags in the generated database, resulting in walls of Clang errors (e.g. fatal error: 'stddef.h' file not found
).
In order to make my life a little easier, I created a repository - compile_commands_gallery - which collects steps necessary to generate compilation database using several C++ build systems, so that I can hopefully reproduce an issue faster.
This post is a summary of what I’ve learned when trying to generate and use the compilation database using these build systems from the same hello world C++17 project.
Compilation database overview
In a nutshell, a compilation database is an LLVM concept, which allows any Clang-based tool to load a list of compilation commands used to build a set of C/C++/ObjC/CUDA translation units (not necessarily generated by a Clang compiler), and based on these commands parse the code and build an Abstract Syntax Tree. The AST can then be used by such a tool to perform static analysis of the code (e.g. clang-tidy), provide code completion (e.g. clangd), optimize include directives (e.g. include-what-you-use) and more. In practice, the most common format in which such database is represented is a simple JSON array, as described in the JSON Compilation Database Format Specification.
Most Clang tools do not accept a path to the JSON file itself, but rather a directory, where they will look for compile_commands.json
file, so if the compilation database file has a different name it won’t be found.
Additionally, if compile_commands.json
cannot be found, Clang library will detect a file called compile_flags.txt
if present, and apply all flags (one per line) to each translation unit passed in the arguments to the tool. This is a so-called fixed compilation database, and is quite convenient for simple projects, or when performing a one-off action on a single translation unit.
Gallery
Each subsection here represents a single build system. In case, it doesn’t have builtin support for compilation databases, it is further divided into subsections for each plugin or external tool that supports it. All examples were generated on the same trivial hello world project with the following structure:
- src/
- hello.cc
- hello.h
- <build system specific files>
src/hello.h
:
#ifndef __HELLO_H__
#define __HELLO_H__
#include <string_view>
struct Hello {
auto hello() const -> std::string_view { return "Hello, world!"; }
};
#endif // __HELLO_H__
src/hello.cc
:
#include <iostream>
#include "hello.h"
auto main() -> int {
Hello h;
std::cout << h.hello() << '\n';
return 0;
}
The project requires at least C++17. All Linux examples were generated in a reference Docker container based on Fedora 41: bkryza/compile-commands-gallery:20241202
. Most examples also work on macos and only MSVC-specific were tested on Windows 10. All generated compilation databases were tested with clang-tidy 19
and clang-uml 0.5.6
.
For each of the build systems, a subsection contains:
- build system version
- setup instructions for our hello world project using a given build system
- instructions how to generate the compilation database
- presentation of the obtained JSON file
- discussion of major issues - if any
- and finally my subjective rating of the overall experience
Build systems compilation database support is rated in 3 categories: compliance with the specification, ease of use and whether it works out of the box or some post processing is necessary. The points are deducted for several totally arbitrary reasons, including (but not limited to):
- no builtin compilation database support and requires an extension
- every extra step necessary to generate or post-process the JSON file
- using
command
instead ofarguments
by default (as preferred by specification) - generating database file with different name than
compile_commands.json
- relative path in
directory
field
The build systems are presented in a - more or less - alphabetical order.
Instructions for installing the build systems are not included here, but can be found in this Dockerfile. In fact, if you would like to try any of the Linux build systems hands-on, just run:
$ docker run --rm -it bkryza/compile-commands-gallery:20241202
# and then e.g.:
./make.lua cmake
# or to generate it manually
cd cmake
The image will set you back around ~2GB in storage though…
Some things to watch out for, when observing an exhibit:
- Does the generated database uses
command
string orarguments
array? - Are the paths relative or absolute?
- Is the generated file name
compile_commands.json
by default or something else? - Does the compilation database contain only source files or does it also contain headers?
- Is the
directory
field absolute or relative? Does it point to the root of project or to the build directory? - Is the first element of
arguments
array the compiler executable? - How the JSON is formatted - the items are presented as-generated without any formatting.
Now that we’re ready for the actual exhibition - let’s go.
B2
B2 (also known as Boost.Build) is mostly familiar to Boost developers, however it is a fully fledged build system that can be used for any project.
Version
$ b2 --version
B2 5.3.0 (OS=LINUX, jobs=32)
Setup
Create file Jamfile
in project root directory:
exe hello : src/hello.cc : <cxxstd>17 ;
Instructions
B2
doesn’t require any special settings in the build system, simply invoke:
b2 --command-database=json
and a ready to use compile_commands.json
file will be in the current directory.
Result
And here is our first specimen:
[
{
"command": "\"g++\" -std=c++17 -fPIC -O0 -fno-inline -Wall -g -c -o \"bin/gcc-14/debug/cxxstd-17-iso/src/hello.o\" \"src/hello.cc\"",
"directory": "/compile_commands_gallery/b2",
"file": "src/hello.cc",
"output": "bin/gcc-14/debug/cxxstd-17-iso/src/hello.o"
}
]
The compilation database works out of the box, however 1 point is deducted for using command
field instead of the recommended arguments
(according to the specification).
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 5/5 | 5/5 |
Bazel
Bazel as of now doesn’t have builtin support for compilation database, however it’s rich plugin ecosystem solves that problem.
Version
$ bazel --version
bazel 7.4.1
Setup
Create the following files in the project root:
BUILD
:
cc_binary(
name = "hello",
srcs = ["src/hello.cc", "src/hello.h"],
includes = ["src/"],
cxxopts = ["-std=c++17"],
visibility = ["//visibility:public"]
)
MODULE.bazel
:
# empty for now
hedronvision/bazel-compile-commands-extractor
Hedron’s Compile Commands Extractor for Bazel is currently probably the most feature rich compilation database plugin for Bazel.
Instructions
Depending on your specific Bazel setup you may need to read carefully through the instructions here. Here’s what worked for me.
Add the following to the MODULE.bazel
:
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override(
module_name = "hedron_compile_commands",
remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
commit = "0e990032f3c5a866e72615cf67e5ce22186dcb97",
)
and the following at the top of the BUILD
file:
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
refresh_compile_commands(
name = "refresh_compile_commands",
# Without this, compilation database will include headers
exclude_headers = "all",
# Without this, compilation database will include plugins' sources
targets = {"//:hello": ""},
)
Finally, run:
bazel run :refresh_compile_commands
Result
[
{
"file": "src/hello.cc",
"arguments": [
"/usr/bin/clang-19",
"-U_FORTIFY_SOURCE",
"-fstack-protector",
"-Wall",
"-Wthread-safety",
"-Wself-assign",
"-Wunused-but-set-parameter",
"-Wno-free-nonheap-object",
"-fcolor-diagnostics",
"-fno-omit-frame-pointer",
"-std=c++14",
"-MD",
"-MF",
"bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.d",
"-frandom-seed=bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.o",
"-fPIC",
"-iquote",
".",
"-iquote",
"bazel-out/k8-fastbuild/bin",
"-iquote",
"external/bazel_tools",
"-iquote",
"bazel-out/k8-fastbuild/bin/external/bazel_tools",
"-isystem",
"src",
"-isystem",
"bazel-out/k8-fastbuild/bin/src",
"-std=c++17",
"-no-canonical-prefixes",
"-Wno-builtin-macro-redefined",
"-D__DATE__=\"redacted\"",
"-D__TIMESTAMP__=\"redacted\"",
"-D__TIME__=\"redacted\"",
"-c",
"src/hello.cc",
"-o",
"bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.o"
],
"directory": "/compile_commands_gallery/bazel_bazel_compile_commands_extractor"
}
]
That’s a lot of compiler flags that are added by default to a project that has 1 simple target, but I guess that’s the Bazel way? The only worrying thing here is that in addition to specified by me -std=c++17
flag, Bazel added -std=c++14
for some reason (I’m assuming here it was Bazel not Hedron’s plugin itself).
Other than that, the compilation database works just fine.
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 3/5 | 5/5 |
kiron1/bazel-compile-commands
kiron1/bazel-compile-commands is a tool for Bazel, which allows to generate compilation database from a Bazel project without the need to modify build system files.
Version
$ bazel-compile-commands --version
bazel-compile-commands: 0
$ git -C bazel-compile-commands describe --always --tags --abbrev=10
v0.12.0
Instructions
bazel-compile-commands
is simply a binary installed in the system, so once installed it can be invoked in the Bazel project root as simply as:
$ bazel-compile-commands
and produces the compile_commands.json
in the current directory.
Result
[{"directory":"/root/.cache/bazel/_bazel_root/3cc8ec045c5114576334d753374782f7/execroot/_main","command":"/usr/bin/clang-19 -U_FORTIFY_SOURCE -fstack-protector -Wall -Wthread-safety -Wself-assign -Wunused-but-set-parameter -Wno-free-nonheap-object -fcolor-diagnostics -fno-omit-frame-pointer -std=c++14 -MD -MF bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.d -frandom-seed=bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.o -fPIC -iquote . -iquote bazel-out/k8-fastbuild/bin -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/bin/external/bazel_tools -isystem src -isystem bazel-out/k8-fastbuild/bin/src -std=c++17 -no-canonical-prefixes -Wno-builtin-macro-redefined -D__DATE__=\"redacted\" -D__TIMESTAMP__=\"redacted\" -D__TIME__=\"redacted\" -c src/hello.cc -o bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.o","file":"src/hello.cc","output":"bazel-out/k8-fastbuild/bin/_objs/hello/hello.pic.o"}]
Again, a lot of flags in the command, but it works without a problem. Please note how directory points to the users’ .cache/bazel
directory instead of project source, which can be problematic when trying to match project sources to compilation commands (no such file as /root/.cache/bazel/_bazel_root/3cc8ec045c5114576334d753374782f7/execroot/_main/src/hello.cc
actually exists).
One point deducted for default use of command
. It does have an -a
flag for using the arguments
array, however in that case it is added alongside the command
field. Now, I don’t think it is explicitly disallowed by the specification to have both command
and arguments
in one entry, which says here: Either arguments or command is required
- but IMO it is a stretch.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 5/5 | 5/5 |
Buck2
Buck2, is yet another Starlark-based build system, created by Meta.
Version
$ buck2 --version
buck2 419a9096a4442bd5 <build-id>
Setup
Create a single file BUCK
in the project root:
cxx_binary(
name = "hello",
srcs = ["src/hello.cc"],
headers = ["src/hello.h"],
)
and call:
buck2 init
Instructions
Buck2
supports compilation database generation out of the box, however generation and use are not strictly speaking trivial. First, we need to build the compilation database for a specific target like this:
buck2 build "//:hello[compilation-database]"
The generated JSON file is nested very, very deep in the build directory hierarchy. One way to find it is:
buck2 targets --show-json-output "//:hello[compilation-database]" | jq -r '."root//:hello[compilation-database]"' | xargs -I {} cp {} .
The jq
invocation on the output is necessary, because targets
command does not simply return the path, but we have to extract it first from a JSON object. Then we can either copy it to the root directory, or extract the directory (e.g. using dirname
), and pass to the Clang tool we’re using.
Result
[
{
"file": "./src/hello.cc",
"directory": ".",
"arguments": [
"clang++",
"-Xclang",
"-fdebug-compilation-dir",
"-Xclang",
".",
"-fcolor-diagnostics",
"-I",
"buck-out/v2/gen/root/904931f735703749/__hello__/buck-private-headers",
"-c",
"src/hello.cc"
]
}
]
Unfortunately, that’s not the end of it, as it’s directory
property is relative, which means that even clang-tidy
will not accept this file as is.
We can fix it for instance like this:
jq --arg PWD_DIR ${PWD} '.[] |= (.directory = "\($PWD_DIR)")' compile_commands.json | sponge compile_commands.json
Yes, I know that it’s possible to write a
trivial
Starlark function and put it somewhere that will do all of the above, but I am yet to learn that language…
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 2/5 | 3/5 |
build2
build2 is a build system behind the cppget.org C++ package repository. Although the current stable 0.17
version doesn’t seem to support compilation databases yet, the currently staging release 0.18
does, so let’s give it a try.
Version
$ b --version
build2 0.18.0-a.0.2164f1544357
libbutl 0.18.0-a.0.98bef49e83a6
host x86_64-linux-gnu
Copyright (c) 2014-2024 the build2 authors.
This is free software released under the MIT license.
Setup
Create the following files:
build/bootstrap.build
:
project = hello
using version
using config
using test
using install
using dist
build/root.build
:
using cxx
hxx{*}: extension = h
cxx{*}: extension = cc
src/buildfile
:
exe{hello}: cxx{hello.cc}
buildfile
:
./: {src/} manifest
manifest
:
: 1
name: hello
version: 0.1.0
summary: hello C++ executable
license: MIT
in the project root and call:
b configure config.cxx.std=17 config.cxx=clang++-19
Instructions
As of version 0.18.0
, build2 will support compilation database natively, so it is possible to simply call:
b config.cc.compiledb=compile_commands
and we have compile_commands.json
in the current directory (.json
suffix is appended automatically by b
command).
Alternatively, you can set it as a permanent config option:
b configure config.cc.compiledb=compile_commands
Result
[
{"output":"/compile_commands_gallery/build2/src/hello.o","file":"/compile_commands_gallery/build2/src/hello.cc","arguments":["/usr/bin/clang++-19","-std=c++1z","-Wno-unqualified-std-cast-call","-fdiagnostics-color","-finput-charset=UTF-8","-o","/compile_commands_gallery/build2/src/hello.o","-c","-x","c++","/compile_commands_gallery/build2/src/hello.cc"],"directory":"/compile_commands_gallery/build2"}
]
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 5/5 |
Clang++ (-MJ)
If for some reason your build system doesn’t support compilation databases, and you’re using clang++
anyway, it is actually possible to generate the compilation database directly from Clang compiler.
Version
$ clang++-19 --version
clang version 19.1.4 (Fedora 19.1.4-1.fc41)
Target: x86_64-redhat-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang++.cfg
Instructions
All that is necessary is to add an option -MJ <json_filename>
to the compiler command, and it will output each command in JSON format to the specified file. The only problem is that each command is printed to a separate file, so it’s best to name the <json_filename>
based on the translation unit. For instance:
clang++-19 -MJ hello.o.json -Wall -std=c++17 -Isrc -o hello.o -c src/hello.cc
and the hello.o.json
has now following contents:
{ "directory": "/compile_commands_gallery/clang_mj", "file": "src/hello.cc", "output": "hello.o", "arguments": ["/usr/bin/clang-19", "-xc++", "src/hello.cc", "-o", "hello.o", "--gcc-triple=x86_64-redhat-linux", "--driver-mode=g++", "-Wall", "-std=c++17", "-I", "src", "-c", "--target=x86_64-redhat-linux-gnu"]},
All resulting files must be merged into a single compile_commands.json
before they can be used. For our hello world project the following command works:
cat hello.o.json | sed 's/},$/}/' | jq -s . | sponge compile_commands.json
Actually, LLVM’s JSON parser will accept the file with the trailing comma after the last array element, even though it is not valid JSON, so for multiple files you can simply merge all command JSON files into a single file and wrap it in [
and ]
(but beware of xkcd:1172).
Result
[
{
"directory": "/compile_commands_gallery/clang_mj",
"file": "src/hello.cc",
"output": "hello.o",
"arguments": [
"/usr/bin/clang-19",
"-xc++",
"src/hello.cc",
"-o",
"hello.o",
"--gcc-triple=x86_64-redhat-linux",
"--driver-mode=g++",
"-Wall",
"-std=c++17",
"-I",
"src",
"-c",
"--target=x86_64-redhat-linux-gnu"
]
}
]
This is a low level mechanism, not meant to be easy or convenient to use, but if there is no other way it is a possibility (it also works on Windows with clang-cl.exe
).
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 2/5 | 3/5 |
CMake
If you’re one of the lucky people who are using CMake on Linux
or macos
, you get compile_commands.json
almost for free (almost - i.e. you only need to make sure you’re using Unix Makefiles
or Ninja
generator, memorize a single CMake option CMAKE_EXPORT_COMPILE_COMMANDS
, remember to set it to ON
somewhere in your CMakeLists.txt
or add -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
to your command line and that’s it):
Version
$ cmake --version
cmake version 3.31.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).
Setup
Create CMakeLists.txt
in the project root directory:
cmake_minimum_required(VERSION 3.15)
project(hello)
add_executable(hello src/hello.cc)
Instructions
Put this somewhere in the CMakeLists.txt
:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
and run cmake
. The generated compile_commands.json
is now in your build directory. Alternatively, run cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
without changing CMakeLists.txt
.
Result
[
{
"directory": "/compile_commands_gallery/cmake/debug",
"command": "/usr/bin/clang++-19 -o CMakeFiles/hello.dir/src/hello.cc.o -c /compile_commands_gallery/cmake/src/hello.cc",
"file": "/compile_commands_gallery/cmake/src/hello.cc",
"output": "CMakeFiles/hello.dir/src/hello.cc.o"
}
]
Generated database file works out of the box with most Clang tools. It uses
absolute paths everywhere except output
. It also uses the deprecated command
instead of arguments
, so may require some fancy escaping.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 4/5 | 5/5 |
FASTBuild
FASTBuild has compilation database support included so no extra information needs to be added to the build files.
Version
$ fbuild -version
FASTBuild v1.13 - Copyright 2012-2024 Franta Fulin - https://www.fastbuild.org
Setup
Create a fbuild.bff
file in the project root:
.Compiler = '/usr/bin/clang++'
.Librarian = '/usr/bin/clang++'
.CompilerOptions = '-std=c++17 -Wall -Werror -c -o %2 %1'
.LibrarianOptions = ''
.Linker = '/usr/bin/clang++'
.LinkerOptions = '-o %2 %1'
ObjectList( 'hello-lib' )
{
.CompilerInputPattern = '*.cc'
.CompilerInputPath = 'src\'
.CompilerOutputPath = 'out\'
}
Executable('hello')
{
.Libraries = { "hello-lib" }
.LinkerOutput = 'out\hello'
}
Alias('all')
{
.Targets = { 'hello' }
}
Instructions
Simply call:
fbuild -compdb
and the compile_commands.json
is placed in the current directory.
Result
[
{
"directory": "/compile_commands_gallery/fastbuild",
"file": "/compile_commands_gallery/fastbuild/src/hello.cc",
"output": "/compile_commands_gallery/fastbuild/out/hello.o",
"arguments": ["/usr/bin/clang++", "-std=c++17", "-Wall", "-Werror", "-c", "-o", "/compile_commands_gallery/fastbuild/out/hello.o", "/compile_commands_gallery/fastbuild/src/hello.cc"]
}
]
No complaints here.
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 5/5 |
Make
make
doesn’t (as of yet) support compilation database generation, however there are tools that can extract the compilation commands from the make
output.
Version
$ make --version
GNU Make 4.4.1
Built for x86_64-redhat-linux-gnu
Copyright (C) 1988-2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Setup
Create file Makefile
in the project root:
hello: src/hello.cc src/hello.h
$(CXX) -std=c++17 -o hello src/hello.cc
clean:
rm -f hello compile_commands.json
Bear
One of the more popular make
compilation database extractors is Bear.
Version
$ bear --version
bear 3.1.5
Instructions
Simply invoke bear
and add your make
build command after --
:
bear -- make hello
and we have compile_commands.json
in our current directory. One caveat here is that Bear does not work in incremental mode, since it extracts the commands from the compiler output, the compiler commands must show up in the output. So make sure to clean your target before regenerating the compilation database.
Also, beware of a little UX trap, if you spend 20 minutes building your project to generate the compilation database, and then you inadvertently run bear
again, it will overwrite your compile_commands.json
with an empty array:
$ bear -- make hello
make: 'hello' is up to date.
$ cat compile_commands.json
[]
Result
[
{
"arguments": [
"/usr/bin/clang++-18",
"-c",
"-std=c++17",
"-o",
"hello",
"src/hello.cc"
],
"directory": "/home/bartek/devel/compile_commands_json_gallery/make_bear",
"file": "/home/bartek/devel/compile_commands_json_gallery/make_bear/src/hello.cc",
"output": "/home/bartek/devel/compile_commands_json_gallery/make_bear/hello"
},
{
"arguments": [
"/usr/lib/llvm-18/bin/clang",
"-cc1",
"-triple",
"x86_64-pc-linux-gnu",
"-emit-obj",
"-mrelax-all",
"-dumpdir",
"hello-",
"-disable-free",
"-clear-ast-before-backend",
"-disable-llvm-verifier",
"-discard-value-names",
"-main-file-name",
"-mrelocation-model",
"pic",
"-pic-level",
"2",
"-pic-is-pie",
"-mframe-pointer=all",
"-fmath-errno",
"-ffp-contract=on",
"-fno-rounding-math",
"-mconstructor-aliases",
"-funwind-tables=2",
"-target-cpu",
"x86-64",
"-tune-cpu",
"generic",
"-debugger-tuning=gdb",
"-fdebug-compilation-dir=/home/bartek/devel/compile_commands_json_gallery/make_bear",
"-fcoverage-compilation-dir=/home/bartek/devel/compile_commands_json_gallery/make_bear",
"-resource-dir",
"/usr/lib/llvm-18/lib/clang/18",
"-internal-isystem",
"/usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14",
"-internal-isystem",
"/usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14",
"-internal-isystem",
"/usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward",
"-internal-isystem",
"/usr/lib/llvm-18/lib/clang/18/include",
"-internal-isystem",
"/usr/local/include",
"-internal-isystem",
"/usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include",
"-internal-externc-isystem",
"/usr/include/x86_64-linux-gnu",
"-internal-externc-isystem",
"/include",
"-internal-externc-isystem",
"/usr/include",
"-std=c++17",
"-fdeprecated-macro",
"-ferror-limit",
"19",
"-fgnuc-version=4.2.1",
"-fskip-odr-check-in-gmf",
"-fcxx-exceptions",
"-fexceptions",
"-fcolor-diagnostics",
"-dwarf-debug-flags",
"/usr/lib/llvm-18/bin/clang --driver-mode=g++ -std=c++17 -o hello src/hello.cc -dumpdir hello-",
"-faddrsig",
"-D__GCC_HAVE_DWARF2_CFI_ASM=1",
"-x",
"c++",
"-o",
"/tmp/hello-f52f3e.o",
"src/hello.cc"
],
"directory": "/home/bartek/devel/compile_commands_json_gallery/make_bear",
"file": "/home/bartek/devel/compile_commands_json_gallery/make_bear/src/hello.cc",
"output": "/tmp/hello-f52f3e.o"
}
]
clang-tidy
doesn’t like this:
$ clang-tidy-19 src/hello.cc
4181 warnings generated.
8362 warnings and 20 errors generated.
Error while processing /compile_commands_gallery/make_bear/src/hello.cc.
error: too many errors emitted, stopping now [clang-diagnostic-error]
error: unknown argument '-clear-ast-before-backend'; did you mean '-Xclang -clear-ast-before-backend'? [clang-diagnostic-error]
...
The problem here is that there are 2 compilation commands for src/hello.cc
, the second
one being an internal clang
command which is ingested by Bear and added to the database.
This is a known issue, and one way to solve it is to add config file like this:
bear.json
:
{
"compilation": {
"compilers_to_exclude": ["/usr/lib/llvm-19/bin/clang", "/usr/bin/clang-19"]
}
}
to exclude unwanted entries, and invoke Bear like this:
$ bear --config bear.json -- make clean hello
and all is well:
[
{
"arguments": [
"/usr/bin/clang++-19",
"-c",
"-std=c++17",
"-o",
"hello",
"src/hello.cc"
],
"directory": "/compile_commands_gallery/make_bear",
"file": "/compile_commands_gallery/make_bear/src/hello.cc",
"output": "/compile_commands_gallery/make_bear/hello"
}
]
Much better. Conclusion, rather obvious, is that Bear’s output depends on the compiler used and how it can deal with it’s output.
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 4/5 |
compiledb
compiledb is a Python command line application similar to Bear, which extracts the compilation commands from the compiler output and generates compile_commands.json
file.
Version
$ pip3 list | grep compiledb
compiledb 0.10.1
Instructions
Simply call compiledb
followed by your build command:
compiledb make
Result
[
{
"directory": "/compile_commands_gallery/make_compiledb",
"arguments": [
"/usr/bin/clang++-19",
"-std=c++17",
"-o",
"hello",
"src/hello.cc"
],
"file": "src/hello.cc"
}
]
No issues.
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 5/5 |
VS Code Makefile Tools
Visual Studio Code is a popular IDE supporting C/C++, and since recently one of it’s extensions Makefile Tools provides compilation database functionality.
Version
$ code --version
1.95.3
f1a4fb101478ce6ec82fe9627c43efbf9e98c813
x64
Setup
Create the following files:
Makefile
:
hello: src/hello.cc src/hello.h
clang++-19 -std=c++17 -o hello src/hello.cc
clean:
rm -f hello compile_commands.json
.vscode/settings.json
:
{
}
Instructions
Add the following option to .vscode/settings.json
: "makefile.compileCommandsPath": "compile_commands.json"
, i.e.:
.vscode/settings.json
:
{
"makefile.compileCommandsPath": "compile_commands.json"
}
or enable it in the GUI.
Now rebuild the project and the compile_commands.json
should be in the root project directory.
Result
[
{
"command": "clang++-19 -std=c++17 -o hello src/hello.cc",
"directory": "/home/bartek/devel/compile_commands_json_gallery/make_vscode_makefile_tools",
"file": "/home/bartek/devel/compile_commands_json_gallery/make_vscode_makefile_tools/src/hello.cc"
}
]
The generated compile_commands.json
works, it uses command
instead of arguments
, however it is not clear to me how it is updated. After running Clean and build target
it just disappeared and I had to restart VS Code and remove .cache
folder to get it back.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 3/5 | 5/5 |
Meson Build
Meson Build is a Python based build system, designed to be very fast and easy to use.
Version
$ meson --version
1.6.0
Setup
Create meson.build
file in project root:
project('hello', 'cpp')
executable('hello', 'src/hello.cc')
Instructions
Meson has currently compilation database support baked in, so all we need to do is run:
meson setup builddir
and the compile_commands.json
is in the builddir
directory.
Result
[
{
"directory": "/compile_commands_gallery/mesonbuild/builddir",
"command": "/usr/bin/clang++-19 -Ihello.p -I. -I.. -fdiagnostics-color=always -D_GLIBCXX_ASSERTIONS=1 -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -O0 -g -MD -MQ hello.p/src_hello.cc.o -MF hello.p/src_hello.cc.o.d -o hello.p/src_hello.cc.o -c ../src/hello.cc",
"file": "../src/hello.cc",
"output": "hello.p/src_hello.cc.o"
}
]
Again, the only nitpick is that the generated JSON uses command
rather than arguments
. Otherwise no issues.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 5/5 | 5/5 |
MSVC
Microsoft Visual Studio doesn’t support Clang compilation database directly, however there are third-party tools that can generate it.
Version
$ MSBuild.exe --version
MSBuild version 17.12.6+db5f6012c for .NET Framework
17.12.6.51805
Setup
The setup is common for the MSVC examples, simply add the following files:
hello.sln
:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35521.163 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hello.vcxproj", "{A6CD8AC1-CBBF-4D4D-94A5-C83A7EB789AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A6CD8AC1-CBBF-4D4D-94A5-C83A7EB789AE}.Debug|x64.ActiveCfg = Debug|x64
{A6CD8AC1-CBBF-4D4D-94A5-C83A7EB789AE}.Debug|x64.Build.0 = Debug|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
hello.vcxproj
:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{A6CD8AC1-CBBF-4D4D-94A5-C83A7EB789AE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\hello.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\hello.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
hello.vcxproj.filters
:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cc</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\hello.cc">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\hello.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
Clang Power Tools
Clang Power Tools is a Visual Studio extension, which provides several useful Clang related functionalities directly from the IDE.
Version
Instructions
Once the extension is installed, simply right click on a target in your solution and select Clang Power Tools->Export Compilation Database
:
and your solution root directory will contain the compile_commands.json
file.
Result
[
{
"directory": "C:/compile_commands_gallery/msvc_clang_power_tools/",
"command": "\"C:/Users/bkryz/AppData/Roaming/ClangPowerTools/LLVM_Lite/Bin/clang++.exe\" -x c++ \"C:/compile_commands_gallery/msvc_clang_power_tools/src/hello.cc\" -std=c++17 -Wall -fms-compatibility-version=19.10 -Wmicrosoft -Wno-invalid-token-paste -Wno-unknown-pragmas -Wno-unused-value -fsyntax-only \"-D_MT\" \"-D_DLL\" \"-D_DEBUG\" \"-D_CONSOLE\" \"-D_DEBUG_FUNCTIONAL_MACHINERY\" -isystem\"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/include\" -isystem\"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/atlmfc/include\" -isystem\"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/VS/include\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.10240.0/ucrt\" -isystem\"C:/Program Files (x86)/Windows Kits/8.1/Include/um\" -isystem\"C:/Program Files (x86)/Windows Kits/8.1/Include/shared\" -isystem\"C:/Program Files (x86)/Windows Kits/8.1/Include/winrt\"",
"file": "C:/compile_commands_gallery/msvc_clang_power_tools/src/hello.cc"
}
]
Here we can see, how using arguments
array instead of command
would make the escaping less tedious. However what is nice about this plugin, is that the flags are translated from MSVC to Clang, and the compiler is set to clang++
, which means we can adjust the flags in the tool configuration (e.g. in clangd or clang-uml).
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 4/5 | 5/5 |
0xabu/MsBuildCompileCommandsJson
MsBuildCompileCommandsJson is an implementation of MSBuild Logger interface, which allows to invoke custom code for every build event, and this way can produce the compilation database.
Version
$ git -C MsBuildCompileCommandsJson describe --tags --always --abbrev=10
d9b1decdc6
Setup
Since this is a single file .NET project, we have to first get the source and build it:
$ git clone https://github.com/0xabu/MsBuildCompileCommandsJson.git
$ cd MsBuildCompileCommandsJson
$ dotnet build
$ cd ..
$ cp MsBuildCompileCommandsJson/bin/Debug/netstandard2.0/CompileCommandsJson.dll .
Instructions
Once we have the logger DLL, we can use it with msbuild.exe
like this:
$ msbuild.exe -logger:"$PWD/CompileCommandsJson.dll"
and compile_commands.json
is in the current directory.
Result
[
{"directory": "C:\\compile_commands_gallery\\msvc_msbuildcompilecommandsjson",
"command": "\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.42.34433\\bin\\HostX64\\x64\\CL.exe\" /c /I\"C:\\vcpkg\\installed\\x64-windows\\include\" /ZI /JMC /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo\"hello\\x64\\Debug\\\\\" /Fd\"hello\\x64\\Debug\\vc143.pdb\" /external:W3 /Gd /TP /FC /errorReport:queue src\\hello.cc",
"file": "src\\hello.cc"}
]
The generated file works out of the box. One thing to notice here, is that the compiler and flags are in MSVC style, so it’s not possible to add Clang-specific GNU style flags (e.g. -fparse-all-comments
), which can be an issue for some Clang-tools.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 3/5 | 5/5 |
Ninja
Ninja, even though usually used via CMake, can generate compile_commands.json
itself just fine.
Version
$ ninja --version
1.12.1
Setup
Create build.ninja
file in project root:
cxx = clang++-19
cxxflags = -std=c++17 -Wall -Wextra -O2
srcdir = src
outdir = build
rule compile
command = $cxx $cxxflags -c $in -o $out
description = Compiling $in
rule link
command = $cxx $in -o $out
description = Linking $out
build $outdir/hello.o: compile $srcdir/hello.cc
build hello: link $outdir/hello.o
Do not forget to copy that new line at the end of
build.ninja
file…
Instructions
Assuming you have a valid build.ninja
file for your project just type:
ninja -t compdb > compile_commands.json
Result
[
{
"directory": "/compile_commands_gallery/ninja",
"command": "clang++-19 -std=c++17 -Wall -Wextra -O2 -c src/hello.cc -o build/hello.o",
"file": "src/hello.cc",
"output": "build/hello.o"
},
{
"directory": "/compile_commands_gallery/ninja",
"command": "clang++-19 build/hello.o -o hello",
"file": "build/hello.o",
"output": "hello"
}
]
Easy-peasy. As usual, 1 point less for using command
.
This item in the gallery has a characteristic feature of including the linker command as well for some reason.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 5/5 | 5/5 |
Premake
Premake is a Lua based build configuration system. It doesn’t support compilation database generation out of the box, but there are fortunately plugins that can be used.
Version
$ premake5 --version
premake5 (Premake Build Script Generator) 5.0.0-dev
Setup
Create file premake5.lua
in the root directory:
workspace "hello"
configurations { "Debug", "Release" }
project "hello"
kind "ConsoleApp"
language "C++"
targetdir "bin/%{cfg.buildcfg}"
files { "src/hello.h", "src/hello.cc" }
filter "configurations:Debug"
defines { "DEBUG" }
symbols "On"
filter "configurations:Release"
defines { "NDEBUG" }
optimize "On"
MattBystrin/premake-ecc
premake-ecc is an implementation of a Premake module that supports compile commands generation.
Version
$ git -C ecc describe --tags --always --abbrev=10
b3726d5268
Instructions
Premake5 plugin system does not require adding anything to the build files, however you need to clone the plugin source into your project directory (alternatively you can build your own version of Premake5 locally and embed it as module directly):
git clone https://github.com/MattBystrin/premake-ecc.git ecc
premake5 ecc
and add at the top of premake5.lua
:
require "ecc"
Result
[
{
"arguments": [
"g++",
"-MD",
"-MP",
"-DDEBUG",
"-g",
"-c",
"-o",
"obj/hello.o",
"src/hello.cc"
],
"directory": "/compile_commands_gallery/premake_ecc",
"file": "/compile_commands_gallery/premake_ecc/src/hello.cc",
"output": "/compile_commands_gallery/premake_ecc/obj/hello.o"
},
{
"arguments": [
"g++",
"-MD",
"-MP",
"-DDEBUG",
"-g",
"-c",
"-o",
"obj/hello.o",
"src/hello.h"
],
"directory": "/compile_commands_gallery/premake_ecc",
"file": "/compile_commands_gallery/premake_ecc/src/hello.h",
"output": "/compile_commands_gallery/premake_ecc/obj/hello.o"
},
]
This plugin automatically includes headers as separate translation units. Now, depending on the Clang tool this is either a good thing or a bad thing.
But wait a minute, is that a trailing comma after the last array element, or am I seeing things?
$ cat compile_commands.json | jq .
jq: parse error: Expected another array element at line 34, column 1
Strangely enough, Clang-tools parse this file just fine, but this is not a valid JSON - 1 point off for compliance.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 4/5 | 5/5 |
tarruda/premake-export-compile-commands
Another option for Premake is premake-export-compile-commands.
Version
$ git -C export-compile-commands describe --tags --always --abbrev=10
f32b896382
Instructions
Similar to ecc
, the plugin must be cloned or downloaded into the project directory first.
git clone https://github.com/tarruda/premake-export-compile-commands export-compile-commands
premake5 export-compile-commands
and add at the top of premake5.lua
:
require "export-compile-commands"
Result
In case of this plugin, the compilation database is placed in compile_commands/debug.json
file, so it has to be renamed first to compile_commands.json
to be automatically discovered by Clang-tools.
[
{
"directory": "/compile_commands_gallery/premake_premake_export_compile_commands",
"file": "/compile_commands_gallery/premake_premake_export_compile_commands/src/hello.cc",
"command": "cc -MD -MP -DDEBUG -g -o /compile_commands_gallery/premake_premake_export_compile_commands/obj/hello.o -MF /compile_commands_gallery/premake_premake_export_compile_commands/obj/hello.d -c /compile_commands_gallery/premake_premake_export_compile_commands/src/hello.cc"
}
]
Also, it uses command
instead of recommended arguments
.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 3/5 | 5/5 |
Qbs
Qbs is a QML based build system, successor to qmake.
Version
$ qbs --version
2.4.2
Setup
Create a single QML file called hello.qbs
in the project root:
CppApplication {
name: "hello"
files: "src/hello.cc"
}
Instructions
Qbs supports compilation databases directly, so assuming you have a properly defined project:
qbs generate --generator clangdb
and the compile_commands.json
appears in the default/
directory.
Result
[
{
"arguments": [
"/usr/bin/g++",
"-g",
"-O0",
"-Wall",
"-Wextra",
"-m64",
"-pipe",
"-fexceptions",
"-fvisibility=default",
"-fPIC",
"-o",
"/compile_commands_gallery/qbs/default/hello.aaf4c61d/f27fede2220bcd32/hello.cc.o",
"-c",
"/compile_commands_gallery/qbs/src/hello.cc"
],
"directory": "/compile_commands_gallery/qbs/default",
"file": "/compile_commands_gallery/qbs/src/hello.cc"
}
]
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 5/5 |
SCons
SCons is a Python based multilanguage build tool.
Version
$ scons --version
SCons by Steven Knight et al.:
SCons: v4.8.1.08661ed4c552323ef3a7f0ff1af38868cbabb05e, Tue, 03 Sep 2024 17:46:32 -0700, by bdbaddog on M1Dog2021
SCons path: ['/usr/local/lib/python3.13/site-packages/SCons']
Copyright (c) 2001 - 2024 The SCons Foundation
Setup
Create SConstruct
file in the root directory:
env = Environment()
env.Program('src/hello.cc')
Instructions
Scons requires enabling the compilation_db
tool in its Environment
instance, one way to do this is to put the following Python code in the SConstruct
file:
env.Tool('compilation_db')
env.CompilationDatabase()
after that compile_commands.json
file is just another build target, so it can be generated by invoking:
scons compile_commands.json
Result
[
{
"command": "g++ -o src/hello.o -c src/hello.cc",
"directory": "/compile_commands_gallery/scons",
"file": "src/hello.cc",
"output": "src/hello.o"
}
]
1 point off for using command
and another for having to modify build system files.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 4/5 | 5/5 |
Waf
Waf is another Python based build system, used most notably by the Samba project.
Version
$ waf --version
waf 2.1.4 (627780cbb74b86b31016c822dcf2b0bfcbb337cb)
Setup
Create a wscript
file in the project root directory:
out = 'build'
def options(opt):
opt.load('compiler_cxx')
def configure(conf):
conf.load('compiler_cxx')
def build(bld):
bld.program(source='src/hello.cc', target='app', cxxflags=['-std=c++17'])
Instructions
Waf requires to enable the generation of the compilation database either in the options
or configure
functions specified in wscript
file, for example:
def configure(conf):
conf.load('compiler_cxx')
conf.load('clang_compilation_database')
after that we can just call:
waf configure clangdb
and compile_commands.json
appears in the build directory specified by global out
variable.
Result
[
{
"arguments": [
"/usr/bin/clang++-19",
"-std=c++17",
"../src/hello.cc",
"-c",
"-o/compile_commands_gallery/waf/build/src/hello.cc.1.o"
],
"directory": "/compile_commands_gallery/waf/build",
"file": "../src/hello.cc"
}
]
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 4/5 | 5/5 |
XCode
XCode is the default development environment on macos
, which is based on Apple’s custom LLVM port referred to as Apple Clang. The problem is that most of the Clang tooling works only with vanilla LLVM, which usually is installed on macos
using Homebrew. In the below example we will try to generate compile_commands.json
from XCode C++ project and make them work with LLVM from Homebrew.
Version
$ xcodebuild -version
Xcode 16.0
Build version 16A242d
Setup
Create the following XCode build system files in the project directory… yeah, not a chance, just download them from here. Alternatively, create a new XCode C++ console application project from scratch. Just make sure to set C++ version to C++17 exactly and disable all C++ module features (unfortunately just setting C++17 does not do that).
xcpretty
xcpretty is a versatile formatter for xcodebuild
command, including support for generating compilation databases.
Version
$ xcpretty --version
0.4.0
Instructions
Simply run:
$ xcodebuild | xcpretty --report json-compilation-database -o compile_commands.json
and compilation database will be in compile_commands.json
.
Result
[{"command":"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x c++ -ivfsstatcache /var/folders/dc/xy51mvn56zd_qlrdrkcy5nwh0000gn/C/com.apple.DeveloperTools/16.0-16A242d/Xcode/SDKStatCaches.noindex/macosx15.0-24A336-b74dfec4bc4e9d23bc944d435a01a688.sdkstatcache -fmessage-length\\=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit\\=0 -fno-color-diagnostics -Wno-trigraphs -Wno-missing-field-initializers -Wno-missing-prototypes -Werror\\=return-type -Wdocumentation -Wunreachable-code -Wquoted-include-in-framework-header -Werror\\=deprecated-objc-isa-usage -Werror\\=objc-root-class -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wno-newline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wunguarded-availability @/Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/arm64/82b82416624d2658e5098eb0a28c15c5-common-args.resp -MMD -MT dependencies -MF /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/arm64/hello.d --serialize-diagnostics /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/arm64/hello.dia -c /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/src/hello.cc -o /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/arm64/hello.o","file":"/Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/src/hello.cc","directory":"/"},{"command":"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x c++ -ivfsstatcache /var/folders/dc/xy51mvn56zd_qlrdrkcy5nwh0000gn/C/com.apple.DeveloperTools/16.0-16A242d/Xcode/SDKStatCaches.noindex/macosx15.0-24A336-b74dfec4bc4e9d23bc944d435a01a688.sdkstatcache -fmessage-length\\=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit\\=0 -fno-color-diagnostics -Wno-trigraphs -Wno-missing-field-initializers -Wno-missing-prototypes -Werror\\=return-type -Wdocumentation -Wunreachable-code -Wquoted-include-in-framework-header -Werror\\=deprecated-objc-isa-usage -Werror\\=objc-root-class -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wno-newline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wunguarded-availability @/Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/x86_64/82b82416624d2658e5098eb0a28c15c5-common-args.resp -MMD -MT dependencies -MF /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/x86_64/hello.d --serialize-diagnostics /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/x86_64/hello.dia -c /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/src/hello.cc -o /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/build/xcode_xcpretty.build/Release/xcode_xcpretty.build/Objects-normal/x86_64/hello.o","file":"/Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/src/hello.cc","directory":"/"}]
And there I thought that Bazel added a lot of flags…
Anyway, there are unfortunately several more serious problems here. First of all, the compilation command is repeated twice for some reason, so let’s just keep the first entry:
$ cat compile_commands.json | jq '.[0]' | jq -s | sponge compile_commands.json
However, now when running clang-tidy
from Homebrew, we get some wierd error:
$ SDKROOT=`xcrun --show-sdk-path` /opt/homebrew/Cellar/llvm/19.1.4/bin/clang-tidy $PWD/src/hello.cc
Error while processing /Users/bartek/devel/compile_commands_gallery/xcode_xcpretty/src/hello.cc.
error: unable to handle compilation, expected exactly one compiler job in ...
It turns out that the original LLVM (not Apple Clang), doesn’t recognize -ivfsstatcache
so we have to get rid of it first:
$ sed -E 's/\-ivfsstatcache.*\.sdkstatcache//g' compile_commands.json | sponge compile_commands.json
and finally it works:
$ SDKROOT=`xcrun --show-sdk-path` /opt/homebrew/Cellar/llvm/19.1.4/bin/clang-tidy $PWD/src/hello.cc
Suppressed 8342 warnings (8342 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Compliance | Ease of use | Works out of the box |
---|---|---|
4/5 | 5/5 | 3/5 |
xmake
xmake is - for a change - a Lua based build system.
Version
$ xmake --version
xmake v2.9.5+20240916, A cross-platform build utility based on Lua
Copyright (C) 2015-present Ruki Wang, tboox.org, xmake.io
_
__ ___ __ __ __ _| | ______
\ \/ / | \/ |/ _ | |/ / __ \
> < | \__/ | /_| | < ___/
/_/\_\_|_| |_|\__ \|_|\_\____|
by ruki, xmake.io
👉 Manual: https://xmake.io/#/getting_started
🙏 Donate: https://xmake.io/#/sponsor
Setup
Create a single Lua file called xmake.lua
in the project root directory:
target("hello")
set_kind("binary")
add_files("src/*.cc")
Instructions
xmake
supports compilation databases out of the box, just type:
xmake project -k compile_commands
and compile_commands.json
appears in the current directory.
Result
[
{
"directory": "/compile_commands_gallery/xmake",
"arguments": ["/usr/bin/clang++-19", "-c", "-Qunused-arguments", "-m64", "-o", "build/.objs/hello/linux/x86_64/release/src/hello.cc.o", "src/hello.cc"],
"file": "src/hello.cc"
}]
All good.
Compliance | Ease of use | Works out of the box |
---|---|---|
5/5 | 5/5 | 5/5 |
Conclusions
Now what to make of all this? Not sure. The variety of possible ways compilation databases can be generated seems to be almost exhausted by the presented build systems. There probably is some lesson here, but I guess when you realize how each of these build systems differ from each other - maybe it is not all that surprising…
Anyway, more seriously, as long as the generated compile_commands.json
eventually works - there’s no problem, right? What does it matter how many steps are necessary to make it work? Well, if you’re a Clang-tool developer, generating compile_commands.json
is the first (and often the major) hoop your users have to jump through to even try your tool at all - that’s why from my perspective it’s important whether a given build system can generate a working compilation database out of the box or not.
Finally, the key technical points you should get from this text, especially if you are using or developing a Clang-based tool. If you need to do some pre- or post-processing of the compilation database in addition to what clang::tooling::JSONCompilationDatabase
implementation does, do not assume that:
- the paths are absolute or relative, they can be either (even in the same JSON)
- the compilation database entries contain only source files (e.g.
cpp
) - they can include headers, as well as linker commands - there is only one entry for each translation unit, a given source can be part of multiple targets or multiple entries can be related to different build stages
- the first item in arguments array is the compiler binary (it can be a launcher such as
ccache
) directory
/file
points to an actual source file location - directory often points to some build directory, where sources are not physically present- only
command
orarguments
is present - there can be both (Quiz: What will the current implementation of clang::Tooling::JSONCompilationDatabase choose if bothcommand
andarguments
are present?) - and finally, do not assume that it is a valid JSON, Clang certainly doesn’t
Useful tools for handling compilation databases
- compdb - Python tool which will add compilation commands for headers matching existing translation units, in case a specific Clang tool needs that
Further reading
- JSON Compilation Database Format Specification - official specification of compilation database format
- Sarcasm tips documentation - some more practical information and links on the compilation databases
- Analysis of C and C++ projects based on JSON Compilation Database - another listing of compilation database support across several build systems
Afterword
If some build system or extension with compilation database support is missing or there is an easier/better way to generate the compilation database than the one I presented, please create a PR here, and I’ll do my best to check it and update this post (and possibly revise the rating ;-)).
P.S. In case it was not immediately obvious, the ratings were meant as the humoristic part of the post, please don’t take them personally… As a Clang-tool developer I truly appreciate everyone who enabled compilation database support in their build system or created an extension for existing one!
—