Use Bear with ccache, distcc, or icecc
In the common case Bear needs no configuration for a launcher in front of a compiler. Run the build exactly as you already do, launcher and all:
bear -- ccache gcc -c main.c -o main.o
produces this entry in compile_commands.json:
[
{
"file": "main.c",
"arguments": ["gcc", "-c", "main.c", "-o", "main.o"],
"directory": "/path/to/project",
"output": "main.o"
}
]
The ccache token is gone: arguments holds the real compiler’s argv,
taken verbatim from whatever followed the launcher’s name, not a
resolved or canonicalized path. distcc and icecc drop out the same way;
the database never contains an entry whose command is the launcher
itself:
bear -- ccache gcc -c main.c -o main.o # -> "gcc", ...
bear -- ccache cc -c main.c -o main.o # -> "cc", ...
bear -- ccache /usr/bin/gcc -c main.c -o main.o # -> "/usr/bin/gcc", ...
In each case arguments starts with exactly the token shown in the
comment. Bear does not chase the name further; if the build wrote a bare
cc, that is what ends up in the database.
The ccache masquerade case
Some distributions (Fedora, Arch, Gentoo) put a directory of symlinks
ahead of the real compilers on PATH, so a plain cc or gcc resolves
to ccache without the build ever typing the word ccache. This is
different from the case above: there is no launcher token for Bear to
drop, because the build’s command line never named one. On a host where
/usr/lib64/ccache/cc is a symlink to ccache:
bear -- cc -c main.c -o main.o
records "cc" as the compiler, exactly as invoked, not resolved past the
symlink and not rewritten to gcc. Bear classifies the ambiguous cc
name by probing --version (ccache passes that probe through to the real
compiler), so the entry parses with the right flag family, but the
recorded name stays what the build actually ran. This matches the
contract in Supported compilers for
ambiguous names.
Preload mode versus wrapper mode
Both interception modes handle an explicit launcher the same way: a
build command of ccache gcc -c main.c -o main.o produces the identical
single entry (compiler gcc, ccache dropped) whether
intercept.mode is
preload or wrapper.
Wrapper mode has one behavior preload mode does not need: when a build
discovers its compiler through PATH or a CC variable and that lookup
lands on a ccache masquerade directory (not an explicit ccache token),
Bear resolves past the masquerade at discovery time and records the real
compiler’s absolute path. With CC=gcc and a masquerade directory first
on PATH, Bear logs
resolve: masquerade wrapper at /usr/lib64/ccache/gcc; re-resolving 'gcc' past /usr/lib64/ccache
and the entry records the real compiler the masquerade pointed at, never
the masquerade symlink and never Bear’s own .bear/ wrapper. See
“The recursion hazard” below for why this step exists.
distcc specifics
distcc’s own options that appear before the compiler name (-j,
--jobs, -v, --verbose, -i, --show-hosts, --scan-avail,
--show-principal) are recognized and skipped while Bear looks for the
compiler, so they never get mistaken for it and never leak into the
recorded arguments. distcc -j4 clang -c main.c therefore records
clang -c main.c, not -j4 clang -c main.c.
icecc specifics
icecc follows the same contract as ccache, distcc, and sccache: the
icecc token is dropped and the real compiler’s arguments are recorded
as invoked. icecream’s icerun is a different tool (it runs arbitrary
commands on the cluster, not compilations) and is not recognized as a
launcher.
The recursion hazard, and how Bear avoids it
Wrapper mode works by prepending its own directory of compiler-named
executables to PATH. A masquerade wrapper does the same thing for its
own purpose. If Bear recorded the masquerade wrapper’s path as “the
compiler” instead of resolving past it, the two directories would look
each other up indefinitely: Bear’s wrapper would call the masquerade
symlink, which would search PATH for the real compiler, find Bear’s
own wrapper first, and call back into it. Bear avoids this by resolving
its lookup PATH past any masquerade directory before recording what a
bare compiler name points to, so the real compiler is what gets wrapped
and recorded, never the masquerade link or Bear’s own wrapper. Detection
is a filesystem check (does the resolved path’s target look like a known
launcher binary), not a subprocess probe, and it only changes what Bear
resolves, not the build’s own child environment: a build that actually
wants ccache’s caching behavior for other invocations still gets it.
Setting CCACHE_COMPILER or CCACHE_PATH, or stripping the masquerade
directory from the build’s own PATH, were all considered and rejected,
because each changes the build’s behaviour rather than only Bear’s
lookup; the reasoning is recorded with the source, under
docs/rationale/.
What does not work
- A launcher wrapping another launcher (
ccache distcc gcc -c main.c) produces no entry. Bear does not chase a chain of launchers; the first launcher’s argument has to be a real compiler. - A launcher whose argument is not a recognized compiler (
ccache make all), or a bare launcher invocation with no argument at all, produces no entry. - A launcher under a name Bear does not recognize is not treated as a launcher at all: its own invocation produces no entry (it is not a compiler), though a real compiler it execs directly may still be recorded on its own. Add an override (below) to fix the launcher’s own entry instead of relying on that side effect.
A launcher at a nonstandard name or path
If the build wraps the compiler with a launcher under a custom path or a renamed binary, teach Bear the mapping in the configuration file, the same way you would hint an unrecognized compiler:
schema: "4.2"
compilers:
- path: /opt/build-tools/my-ccache
as: ccache
With this override, my-ccache gcc -c main.c -o main.o records the same
single entry, compiler gcc, as a plain ccache invocation would. as
accepts each launcher’s own name for the equivalent case with those
tools, and wrapper as the generic spelling for any of them; see
Configure Bear for the
compilers: key in full, and Supported
compilers for the accepted
names, generated from Bear’s own definitions.
Related: Supported compilers for the recognized launcher and compiler names, Troubleshooting for a database that comes out wrong, and the Recipes index for other tasks.