docs/requirements/output-compilation-entries.md
Build systems often compile several sources in one compiler
invocation, or combine compiling and linking in a single command.
Tools that consume compile_commands.json (clangd, clang-tidy, and
similar) expect one entry per translation unit -- each entry
describing how to compile exactly one source file on its own, without
the noise of linking or of siblings compiled in the same invocation.
Bear's job is to turn each intercepted compiler invocation into zero, one, or many entries, so that downstream tools see a clean, per-source compile command regardless of how the build system phrased the invocation.
The rules below describe the user-visible transformation. The JSON
shape of an individual entry is covered separately by
output-json-compilation-database. Per-compiler details (which flags
each compiler recognizes, which extensions identify its sources, how
MSVC-style flags differ from GCC/Clang-style) are defined per
compiler family; which executables belong to which family is owned by
the recognition requirements (recognition-compiler-names and its
siblings).
For compilers whose sources are separable translation units -- each source compiles on its own and contributes an independent object (GCC, Clang, the Fortran and CUDA families, MSVC, and the like):
file field is one of those sourcesSome compilers treat all of an invocation's sources as a single
translation unit: they parse the sources together and produce one
combined output rather than one object per source. The Vala compiler
is the motivating example -- it compiles every .vala/.gs source of
a target together and produces one library/binding. For such a
compiler:
file field is the first source in argument orderBecause the entire invocation collapses to one entry keyed on its
first source, duplicate detection (output-duplicate-detection) sees
one producer per invocation rather than N near-identical producers;
this is the intended behaviour for a single-translation-unit compiler
and avoids emitting several entries that all claim to produce the same
combined output.
Some compilers analyze every source of an invocation together as a
single "whole module" but still need one entry per source file,
because per-file consuming tooling looks up a compile command by file
path. Swift's swiftc in whole-module mode is the motivating example:
a single invocation lists several .swift sources, and each file's
semantics depend on the whole module, so no entry can be reduced to
"this file only" the way a separable-sources entry is. For such a
compiler:
file field is a distinct source from the invocationThis is a third, distinct shape: unlike the separable case, no source is stripped from any entry's arguments; unlike the single-invocation case, N entries are produced (one per source, not one for the whole invocation).
Regardless of entry shape, an invocation produces an entry only when a recognized source is compiled -- never a best-effort guess. In particular, an invocation produces no entries when any of the following holds:
.o, .obj, .a, .so, .lib, .dylib, ...)
-- this is a pure link step--version, --help,
-###, -dumpversion, ...)-E) or dependency
generation only (-M or -MM without a compile step)-fsyntax-only, -MD, and -MMD do compile the source (the last
two emit dependency files as a side effect) and therefore produce an
entry.
When a single invocation both compiles and links
(cc -o a.out -lsomething src.c), the resulting entry describes only
the compile step. Flags whose effect is limited to the link stage are
removed; each compiler family defines its link-stage flag set, with
GCC-style -l<name> as the representative example. Stripping applies
to all three entry shapes.
Flags affecting preprocessing, compiling, or assembling are kept
(-O2 as the representative example), together with driver-level
options that affect compilation.
Within a given entry, the remaining arguments appear in the same
relative order as in the original command. Downstream consumers are
order-sensitive (search paths, macro definitions, and warning options
all depend on their relative order), and -x <lang> overrides must
keep their position relative to the source files they apply to. The
compiler executable stays at index 0 of arguments.
output fieldThe per-entry output field (see output-json-compilation-database)
is optional and off by default. When enabled via configuration, each
entry's output is the value of the invocation's output flag, copied
verbatim into every entry produced from that invocation (see Known
limitations); when the invocation has no output flag, the field is
absent.
foo.C is treated as a C++ source
because the extension is .C, while on Windows and typical macOS
configurations the same file also matches extension lists written
in lowercase@file) tokens pass through untouched by default;
the default and the opt-in inlining are owned by
output-response-file-inliningsrc1.o, src2.o) are not inferred.Given a build that runs cc -c src1.c src2.c src3.c in one
invocation:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains three entries, withfileset tosrc1.c,src2.c, andsrc3.crespectively, each entry'sargumentscontain-cand only its own source file, and the other two source files do not appear in that entry'sarguments.
Given a build that compiles a single source file:
When the user runs
bear -- cc -c src.c, thencompile_commands.jsoncontains exactly one entry, withfileset tosrc.cand the compiler atarguments[0].
Given a build that runs a single-translation-unit compiler over two or
more sources in one invocation (for example valac src1.vala src2.vala):
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains exactly one entry for that invocation, withfileset to the first source (src1.vala), and all of the sources appearing in that entry'sarguments.
Given a build that runs a whole-module compiler over two or more
sources in one invocation (for example
swiftc -module-name App a.swift b.swift):
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains two entries, one fora.swiftand one forb.swift, and both entries'argumentscontain botha.swiftandb.swift(the whole invocation, not stripped to one source each).
Given a build that runs cc -o a.out src1.c src2.c src3.c:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains three entries, one per source file, each entry describes a pure compile step (no link-only flags), and no entry'sfileisa.out.
Given a build that runs cc -o a.out obj1.o obj2.o obj3.o:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains no entries for this invocation.
Given a build that runs cc -o a.out -lsomething -O2 src.c:
When the user runs Bear wrapping that build, then the resulting entry contains
-O2, and it does not contain-lsomething.
Given a build that runs cc -I first -I second -DFOO -DBAR -c src.c:
When the user runs Bear wrapping that build, then the entry lists
-I firstbefore-I second, and-DFOObefore-DBAR, matching the original order.
Given a build that runs cc --version or cc -###:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains no entries for this invocation.
Given a build that runs cc -E src.c and cc -M src.c:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains no entries for either invocation (preprocess-only and dependency-generation-only steps compile nothing).
Given a build that runs cc -MD -c src.c:
When the user runs Bear wrapping that build, then
compile_commands.jsoncontains exactly one entry, withfileset tosrc.c(the dependency file is a side effect of a real compile step).
Given a build that runs cc -o a.out src1.c src2.c with the
output field enabled via configuration:
When the user runs Bear wrapping that build, then every entry's
outputisa.out(see Known limitations).
Given a build that runs cc -c src.c (no output flag) with the
output field enabled via configuration:
When the user runs Bear wrapping that build, then the resulting entry has no
outputkey.
output-json-compilation-database -- per-entry JSON
shape.output-env-derived-flags -- environment variables the
compiler reads (CPATH, CL, ...) folded into entry arguments.output-append, output-duplicate-detection,
output-source-directory-filter -- stages that run on the entries
produced by this step.