site/src/tutorials/getting-started.md
By the end of this page you will have built Lua 5.4 under Bear, looked at
the compile_commands.json it wrote, and used that file to run clangd
and clang-tidy against the Lua source, the same way an editor or a CI
lint job would.
Install Bear with your platform's package manager; see
Install Bear for the commands. Then confirm it is on
your PATH:
bear --version
Lua 5.4 is a good first target: a plain Makefile, no configure step, no
dependencies beyond a C compiler and make. Download and extract it:
curl -fsSL -O https://www.lua.org/ftp/lua-5.4.8.tar.gz
tar xzf lua-5.4.8.tar.gz
cd lua-5.4.8
lua.org's ftp directory does not change existing releases, so this
download always matches this checksum:
$ sha256sum lua-5.4.8.tar.gz
4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae lua-5.4.8.tar.gz
bear -- make
Bear runs the build exactly as make would on its own, forwarding its
output, and adds one thing: a compile_commands.json file in the
current directory, one entry per compiled source file. Lua's Makefile
recurses into src, so this produces 34 entries, one for each .c file
that went into lua and luac.
cat compile_commands.json
Each entry looks like this:
{
"file": "lapi.c",
"arguments": [
"/usr/bin/gcc",
"-std=gnu99",
"-O2",
"-Wall",
"-Wextra",
"-DLUA_COMPAT_5_3",
"-DLUA_USE_LINUX",
"-c",
"-o",
"lapi.o",
"lapi.c"
],
"directory": "/home/you/lua-5.4.8/src",
"output": "lapi.o"
}
directory is where the compiler ran, file is the source it compiled
(relative to directory), arguments is the exact command as an array,
and output is the object file it produced. Your own output will show
your own resolved compiler path in arguments[0]; on a distribution
that routes compilers through ccache, that path may be a ccache
symlink instead of the compiler itself. There is one entry per compiled
source and none for the headers Lua's sources include, such as
lapi.h, because Bear only records commands the build actually ran,
and the build never invokes the compiler on a header directly. For what
Bear does and does not capture, and why, see
How Bear works.
compile_commands.json exists for tools like clangd to read, not to be
read by people. Point clangd at one Lua source file and it logs the
exact command it pulled from the database:
$ clangd --check=src/lapi.c
I[...] Compile command from CDB is: [/home/you/lua-5.4.8/src] /usr/bin/gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_USE_LINUX -c -o lapi.o -resource-dir=... -- /home/you/lua-5.4.8/src/lapi.c
That is the whole hand-off: an editor with clangd support opened on this directory gets working "go to definition", completion, and diagnostics with no other setup. See Set up clangd for a project without CMake for the editor side.
clang-tidy reads the same file to know how to parse each source it lints. Run it against one Lua source file, restricted to a single check so the output stays short:
$ clang-tidy -p . -checks='-*,clang-analyzer-deadcode.DeadStores' src/lfunc.c
1 warning generated.
lfunc.c:196:42: warning: Although the value stored to 'upl' is used in the enclosing expression, the value is never actually read from 'upl' [clang-analyzer-deadcode.DeadStores]
196 | while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
| ^
That is a real finding in Lua's own source, produced by clang-tidy, not
by Bear: Bear's job ends at writing compile_commands.json, and every
tool built on Clang's tooling API, from editors to linters to
refactoring tools, takes it from there.
compile_commands.json if a
real project's database comes out empty, and
Troubleshooting if it comes out wrong.