Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting started with Bear

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

Install Bear with your platform’s package manager; see Install Bear for the commands. Then confirm it is on your PATH:

bear --version

Get a real project

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

Build it under Bear

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.

Look at the result

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.

Use the database: clangd

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.

Use the database: clang-tidy

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.

Next steps