site/src/guides/recipes/empty-compilation-database.md
The most common cause is that the build compiled nothing: an incremental build with everything already up to date runs no compiler at all, and Bear only records commands the build actually executes. Rebuild from clean:
make clean
bear -- make
If a clean rebuild still leaves [], work through the other causes
below, most common first. Each one shows the command that confirms it
and the command that fixes it.
An incremental build that finds every target up to date runs no compiler, so there is nothing for Bear to intercept. This is not a bug: Bear observes execution, it does not read your build files to guess what "should" happen.
Confirm: check whether the build itself said it did nothing, for example make's
make: Nothing to be done for 'all'.
Fix: clean first, or build into an empty directory, as shown above.
When you only rebuilt part of a larger project and want to keep the rest
of a previous run's entries instead of doing a full clean, use
--append (see Command-line options
for its exact semantics, and Troubleshooting
for the duplicate-handling rules that apply when you do).
Bear decides whether a command is a compilation by matching the executable's filename against a table of known names; a name outside that table produces no entry, even though the compiler ran and built your code successfully.
Confirm: check whether your compiler's name is one Bear knows, on Supported compilers, or ask the installed build directly:
bear semantic --print-compilers
A custom or renamed executable will not appear here. The Diagnostics section below shows a more precise way to confirm the exact command that went unrecognized.
Fix: add a compilers: hint mapping the executable's path to the
family it behaves like:
schema: "4.2"
compilers:
- path: /usr/local/bin/my-custom-cc
as: gcc
A Makefile rule invoking ./my-totally-custom-cc, itself a plain copy
of gcc, produces [] on its own, and a working entry once the hint
above names its path. The
accepted as: values are listed on Supported
compilers.
Bear has two interception methods, chosen once per invocation: preload
injects a library into every process the build starts; wrapper
substitutes the known compilers on PATH instead. See Configure
Bear for which one is the
default on your platform and how to force the other, and How Bear
works for the mechanism. A few
situations keep preload from seeing a compilation at all:
A statically linked build tool. Preload works by hooking library calls inside the process that makes them; a statically linked executable never loads the dynamic linker, so no hook is ever installed in it, and any compiler it execs directly is invisible to Bear. This is a fundamental limit of the approach, not a bug.
Confirm with:
file "$(command -v your-build-tool)"
A report of "statically linked" (rather than "dynamically linked")
means preload cannot see what that tool executes. A statically linked
launcher that directly execs a compiler produces [] under the
default (preload) mode on Linux, and a correct entry once wrapper mode
is forced:
schema: "4.2"
intercept:
mode: wrapper
macOS with System Integrity Protection enabled. SIP strips the
preload environment variable from protected executables before it can
take effect, so preload cannot run there; it is wrapper mode's reason
for being the macOS default in the first place. See Bear on
macOS for the detail. Forcing preload mode
where it is unavailable (SIP-protected macOS, or Windows, which has no
preload library at all) is a startup error naming wrapper mode as the
alternative - Bear does not silently fall back, and the build does not
run, so this case shows up as an explicit error rather than an empty
compile_commands.json.
Running against a container from the outside. bear -- docker exec ... on the host does not work: the daemon runs the command inside the
container's own process tree, which the host-side Bear process never
observes, so the database comes out empty with no other symptom. Bear
must run inside the container as part of the build. See Run Bear
inside a Docker container for the correct invocation.
In every one of these cases, wrapper mode is the general fallback, with one condition of its own: the build has to actually pick the wrapper up, covered below.
A missing or broken libexec.so install is not a failure the dynamic
linker treats as fatal: it drops a preload target it cannot open, prints
a warning on standard error, and lets the build run anyway. Only the
top-level command Bear launched is observed directly; everything that
command itself spawns runs unwatched, so a build whose compiler is
invoked through make or a shell script comes back with an empty
database, and Bear still exits 0 because the build succeeded. See
Exit status for why success and an
empty database are not mutually exclusive.
Confirm: look for this line in the build's own standard error, not Bear's own log output:
ERROR: ld.so: object '.../libexec.so' from LD_PRELOAD cannot be preloaded: ignored.
Fix: see Troubleshooting for the install-path check.
Wrapper mode works by putting reporting executables ahead of the real
compilers on PATH. A build step that discovers the compiler on its
own, before the wrapper directory is in place, bakes in the real
compiler's path directly, and every later invocation goes straight to
the real compiler with no wrapper in between: a ./configure script run
outside Bear, or a CMake configure step that resolves cc to an
absolute path, are the common versions of this.
Confirm: look for the real compiler's absolute path already baked
into a generated build file (config.mk, CMakeCache.txt, or similar)
from before the build ran under Bear.
Fix: run the configure step under Bear too, discarding its output, then run the real build under Bear:
bear --config bear.yml -- ./configure
bear --config bear.yml -- make
A configure.sh that resolves cc to an absolute path and writes it
into a makefile fragment produces [] when only the subsequent make
runs under Bear in wrapper mode. Running configure.sh under Bear as
well, with the same config and discarding that run's own output, makes
the following build's database come out populated.
This matches the two-step CMake example in the bear(1) man
page's EXAMPLES section.
Bear records the compiler invocations that actually happen; if the build fails before starting any of them - a missing include in a Makefile, a dependency that does not exist, a configure step that aborts - there is nothing to record.
Note the distinction: a compile that fails after the compiler was invoked (a syntax error in the source) still produces an entry, because Bear records the attempted command, not its outcome. An empty database from a failed build specifically means the failure happened before any compiler ran at all.
Confirm: check the build's own error output and Bear's exit code (see Exit status); a non-zero exit with no compiler messages at all points here.
Fix: fix whatever stops the build before it reaches a compile step, then rebuild under Bear.
A sources: rule in the configuration file that is broader than
intended can exclude every compiled file, leaving a [] even though the
build compiled normally. (A duplicates: rule cannot do this by
itself: it always keeps the first occurrence of a group of duplicates,
so it can shrink a database but never empty it out on its own; see
Troubleshooting
for that case.)
Confirm: temporarily remove the sources: section from your
configuration and rebuild; if entries appear, a rule there is the cause.
Fix: narrow the rule. For example, excluding every .c file:
schema: "4.2"
sources:
files:
- pattern: "*.c"
action: exclude
produces [] for a project with only .c sources, by design. See
Configure Bear for the full
sources: syntax.
Before anything else, re-run with logging turned on:
RUST_LOG=info bear -- <build command>
Near the end of the output is a summary that tells you which side of the pipeline lost your entries:
Output pipeline:
semantic events: 5
current entries: 2
previous entries: 0
filtered entries by duplicate: 0
filtered entries by source: 0
synthesized header entries: 0
dropped entries (invalid): 0
total entries written: 1
semantic events near the minimum (as low as 1, just the build
command itself) means the build never executed a compiler - the
"nothing to compile" or "build failed early" cases above.semantic events clearly greater than zero but current entries: 0
means commands ran but none were recognized as a compiler - the
"compiler not recognized" case.filtered entries by source or filtered entries by duplicate
greater than zero, with total entries written at zero, means
recognized entries were filtered out by your configuration.Set RUST_LOG=debug instead for more detail: every intercepted command
is tagged Recognized(...) or NotRecognized(...), which pins down
exactly which invocation Bear saw and how it classified it. Without
RUST_LOG set, Bear prints only warnings and errors; see the
bear(1) man page's ENVIRONMENT section for the exact levels.
Related: Troubleshooting for a database that is short or wrong rather than empty, Supported compilers for the recognized names, How Bear works for the interception and analysis mechanism, the Recipes index, and the platform notes for Linux, macOS, Windows, and BSD.