Generate compile_commands.json for a Makefile project
Run this from the directory holding the Makefile:
bear -- make
Bear runs make exactly as it would run on its own, watches every
command it executes, and writes compile_commands.json next to it, one
entry per compiled source file. Nothing about the Makefile changes.
Make has no built-in way to export a compilation database (unlike CMake
or Meson, which do; see the home page if that is your
build system instead), so intercepting the real build is the standard
way to get one for a Make project. For the full walkthrough of a first
run, including what the output looks like, see Getting started with
Bear; this page covers everything beyond that
first run.
Autotools: ./configure and make
Run configure on its own, then build under Bear:
./configure
bear -- make
This is enough whenever preload is the active interception method:
configure only needs to run so it can write a Makefile, and make is
the command Bear needs to watch.
In wrapper mode a build step that discovers the compiler on its own has to run under Bear too, or it never sees the wrapper. See Configure Bear for which mode is the default on your platform and how to force one or the other. Practically, the wrapper-mode requirement means running the configure step under Bear as well, and discarding its output:
bear -- ./configure
make clean
bear -- make
Do not combine ./configure and make into a single Bear run (for
example bear -- sh -c './configure && make', or a Makefile target that
re-runs configure itself): configure’s throwaway toolchain probes
(conftest.c and similar) end up recorded alongside the real build. Run
configure and make as two separate Bear invocations instead, as
above; the second run’s output overwrites the first’s, so nothing from
the probe phase survives. See
Troubleshooting for
the mechanics, and for excluding probe files by pattern instead, if you
would rather keep one combined run.
Incremental builds
Make only rebuilds what is out of date, and Bear only records commands
the build actually executes, so a make with nothing to do records
nothing: running bear -- make a second time in a row against an
unchanged tree overwrites compile_commands.json with an empty [].
Build from clean when you want a complete database:
make clean
bear -- make
To fold in just the parts you rebuild, add --append. It places new
entries before the existing ones in the output file, so accumulate the
database as you touch different parts of the project instead of
rebuilding everything at once:
bear --append -- make main.o
bear --append -- make greet.o
After the first call the database holds one entry (main.c); after the
second it holds two, with the newest entry first. On the first call
there is nothing to append to, so Bear warns and ignores the option. If
a file is later rebuilt with different flags, its newest entry wins by
default; see Configure Bear
for the matching rule, and Bear produces an empty
compile_commands.json if entries still
seem to go missing.
Parallel builds
bear -- make -j4 needs nothing special: every compiler process, however
many run concurrently, reports back to Bear on its own. A parallel build
produces the same entries as the serial one.
Recursive Makefiles
A real build under Bear needs no extra flags for make -C or $(MAKE)
recursion: each intercepted command carries its own working directory,
so entries come out correctly scoped to the subdirectory they were
compiled in, however deep the recursion goes. In a project whose
top-level Makefile recurses into a libgreet/ subdirectory, the
database holds main.c with directory set to the project root and
greet.c with directory set to libgreet, with no
-w/--print-directory needed.
The -w flag matters only for the dry-run path below, not for a real
build.
Bear itself, not make, decides where compile_commands.json is
written: it always lands in the directory Bear was started from, even
when the Makefile it drives is elsewhere. Running bear -- make -C libgreet from inside libgreet/ writes the file there; running the
same command from the project root writes it at the root instead.
A build you cannot re-run
When you cannot run the build again, for example to recover a database
from a CI log after the fact, feed a dry run into bear parse-sh
instead of running anything:
make -n | bear parse-sh
For a recursive build add -w, so the top-level make also prints the
Entering directory markers that sub-makes already print by default;
parse-sh uses them to resolve each command to the right directory:
make -nw | bear parse-sh
Parsing a plain make -n for a recursive project from a directory other
than the build root puts the top-level source’s directory at the
parsing directory rather than the build root; make -nw from the same
place resolves it correctly. Without -w, the
top-level make prints no directory marker of its own, so parse-sh
falls back to its own working directory (or --directory, if given) for
commands issued before any marker appears.
This path is reconstruction from text, not observation, and is lower
fidelity than a real build: a dry run can omit commands entirely
(recursive make does not always propagate -n to sub-makes, and
commands behind not-yet-generated sources never print), the environment
and PATH used to resolve bare executable names is the parsing one, not
the real build’s, and only a documented subset of POSIX shell syntax is
understood (subshells, command substitution, and here-documents are not,
among others; an unparsed line is skipped and reported on standard error
with its line number). Prefer bear -- make whenever you can actually
run the build; reach for parse-sh only when you cannot. See bear parse-sh in the bear(1) man page for the full list of what
it understands.
Where the output goes, and how to change it
By default Bear writes compile_commands.json in the directory it was
started from. Use -o/--output (see Command-line
options) to write it
somewhere else:
bear -o build/compile_commands.json -- make
- (standard output) is not accepted for -o in combined mode, since
standard output belongs to the build; see the bear(1)
man page for the split intercept/semantic modes, which do
accept it.
Related pages
-
Bear produces an empty compile_commands.json when even a clean build gives you nothing.
-
Set up clangd for a project without CMake for pointing your editor at the file this page produces.
-
Use Bear with ccache, distcc, or icecc if your Makefile invokes the compiler through a launcher.
-
Run Bear inside a Docker container if the build itself runs in a container.
-
How Bear works for the interception mechanism behind everything on this page.
-
Command-line options and Configure Bear for the flags and keys named above.
-
Recipes for the rest of the task pages.