What is Catter?
Catter is a build process interception tool. It hooks into child process creation during a build, captures compilation and linker commands, and lets you process them with JavaScript scripts powered by an embedded QuickJS runtime.
Motivation
Catter was created for clice, a new C++ language server. Language servers depend on a Compilation Database (CDB) to understand how each source file is compiled, but obtaining one is often painful:
- Limited build system support -- CMake only generates CDB when using Makefile or Ninja backends. Many other popular C++ build systems have no native CDB support at all.
- Inaccessible build directories -- When C++ code is built as part of a Python package (or similar), the build directory is managed externally and the CDB file is out of reach.
- Code generators require a full build -- Projects like LLVM use tools such as
tablegenthat generate header files at build time. These files are referenced by-Iflags in the compilation commands, so the language server cannot resolve includes until a full build has run -- even if you only want to read the code.
No existing cross-platform tool solves all of these problems. That is why we created catter.
How It Works
Catter's approach is similar to Bear and scan-build. It intercepts child process creation using platform-specific hooks (LD_PRELOAD on Linux, DYLD_INSERT_LIBRARIES on macOS, DLL injection on Windows). Every intercepted command is sent to a decision-making daemon that runs user-provided JavaScript scripts via QuickJS, determining what to do with each command.
Core Capabilities
Compilation Database Generation
The primary use case. Catter captures compiler invocations across any build system and generates a standard compile_commands.json. No build system plugins or special configuration needed.
Linker Analysis
By capturing linker commands, catter can reconstruct the dependency graph between build targets. This is essential for C++20 modules support: the C++ standard says a program may have at most one module with a given name, and "program" is defined by targets (libraries, executables). This target information is missing from the CDB standard, but catter can infer it from linker commands.
Fake Compilation (Planned)
Instead of forwarding compilation to the real compiler, catter will be able to generate placeholder object files. This lets the build system run to completion without actually compiling, producing a complete CDB in a fraction of the time. Code generators (like tablegen) are still built normally -- catter analyzes dependencies to determine the minimal set of tools that must be genuinely compiled.
Build Profiling (Planned)
Capture process timing, durations, and parent-child relationships during a build. The data can be rendered visually in a browser, letting you analyze parallelism and identify bottlenecks.
Custom Script Patching
With the embedded QuickJS engine, users can write JavaScript scripts to dynamically modify, redirect, or inject logic into build commands -- without changing the original build files.
Architecture at a Glance
Catter is organized around runtimes: a runtime is the mechanism that actually captures build process creation. The default inject runtime intercepts child process creation through platform hooks (Linux LD_PRELOAD, macOS DYLD_INSERT_LIBRARIES, Windows DLL injection); every captured command is handed to a daemon running your JavaScript script.
Scripts participate through four callbacks: onStart before the build, onCommand before each command, onExecution after each command, and onFinish after the whole build. A script can run a command as-is, run it modified, skip it, or abort; it can use the capability information in config.runtime to adapt to different runtimes.
For details on the architecture concepts, runtimes, and the IPC flow, see the design documentation and Runtime.