Skip to content

compile_commands.json gallery

Published: at 06:00 PM

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!

TL;DR -> Conclusions

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.

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 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):

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:

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).

ComplianceEase of useWorks out of the box
4/55/55/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.

ComplianceEase of useWorks out of the box
5/53/55/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.

ComplianceEase of useWorks out of the box
4/55/55/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…

ComplianceEase of useWorks out of the box
4/52/53/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"}
]
ComplianceEase of useWorks out of the box
5/55/55/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).

ComplianceEase of useWorks out of the box
5/52/53/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.

ComplianceEase of useWorks out of the box
4/54/55/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.

ComplianceEase of useWorks out of the box
5/55/55/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.

ComplianceEase of useWorks out of the box
5/55/54/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.

ComplianceEase of useWorks out of the box
5/55/55/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

VS Code Makefile Tools Version

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.

ComplianceEase of useWorks out of the box
4/53/55/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.

ComplianceEase of useWorks out of the box
4/55/55/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

MSVC Clang Power Tools Version

Instructions

Once the extension is installed, simply right click on a target in your solution and select Clang Power Tools->Export Compilation Database:

MSVC Clang Power Tools Version

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).

ComplianceEase of useWorks out of the box
4/54/55/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.

ComplianceEase of useWorks out of the box
4/53/55/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.

ComplianceEase of useWorks out of the box
4/55/55/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.

ComplianceEase of useWorks out of the box
4/54/55/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.

ComplianceEase of useWorks out of the box
4/53/55/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"
    }
]
ComplianceEase of useWorks out of the box
5/55/55/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.

ComplianceEase of useWorks out of the box
4/54/55/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"
  }
]
ComplianceEase of useWorks out of the box
5/54/55/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.
ComplianceEase of useWorks out of the box
4/55/53/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.

ComplianceEase of useWorks out of the box
5/55/55/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:

Useful tools for handling compilation databases

Further reading

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!

image description
Rustensteg, Vienna, AT, © 2015
Hasselblad 503CX, Ilford HP5+