Architecture Overview
Catter probably can be divided into 3 parts:
- HOOK: Intercept compilation commands from any build system.
- PROXY: Act as a proxy compiler to capture commands.
- DECISION: Communicate with the PROXY to determine how to handle commands.
General process follows like this:
- User runs
catter <build command>. catterstarts the Decision-making Server and delegates execution by spawning<catter-proxy> <build command>.- PROXY starts. It queries DECISION, which instructs it to execute the build command with HOOK attached (e.g., via
LD_PRELOADor DLL Injection etc.). - The build system runs. When it attempts to spawn a compiler (e.g.,
g++ ...), the HOOK intercepts the call. - HOOK rewrites the execution call to redirect to
<catter-proxy> <compiler command>. - PROXY starts. It sends the captured arguments to DECISION.
- DECISION analyzes the arguments (using the user's JS script) and replies with an action (e.g., "Execute", "Hook and Execute", "Skip", etc).
- PROXY performs the action requested by DECISION.
Components
catteracts as DECISION: The user entry point. It runs as a daemon/server that holds the JS runtime. It instructs the PROXY on what to do but never interacts with the OS processes directly.catter-proxyacts as PROXY: The platform-specific tool, which private to users. It handles process creation, hook injection, and IPC communication. It acts in two modes:- Injector Mode: Launches the build system (e.g.,
make) with hooks attached. - Wrapper Mode: Masquerades as the compiler (e.g.,
g++) to capture arguments.
- Injector Mode: Launches the build system (e.g.,
catter-proxyincludes processing logic of HOOK, which has different implementations on different platforms and we can't give a specific name here. For example, on Windows, it uses DLL injection with a specific DLL name likecatter-hook.dll.
We use -- to separate commands for catter and commands for the actual build system. For example:
bash
catter [options] -- <build system command>
catter-proxy [options] -- <compiler command>- The first parameter of
catteris always used to specify the script. - Then, you can provide specific options for scripts.
- Finally, after
--, you provide the actual build system command that you want to run.
To specify a built-in script:
bash
catter script::<script-name> [options] -- <build system command>or custom script path:
bash
catter /path/to/custom/script.js [options] -- <build system command>A Simple Example
- User runs:bash
catter script::cdb -o path/to/compile_commands.json -- make catterstarts the Decision-making Server and spawns:bashcatter-proxy -- makecatter-proxy(PID: 100) starts.- Connects to
catterServer. catterinstructs: This is a build command, execute it with hooks.catter-proxyinjects hooks and startsmake(PID: 101).
- Connects to
make(PID: 101) runs with hooks. It parses the Makefile and prepares to executeg++.- HOOK (inside
make): Intercepts the spawn call forg++. It rewrites the arguments tocatter-proxy -- g++and spawns a new process. catter-proxy(PID: 102) starts.- Connects to
catterServer. catterinstructs: This is a compile command, record it in the database and then execute the original command.catter-proxyexecutesg++(PID: 103).
- Connects to
g++(PID: 103) compiles normally.cattercollects all compilation commands in its database and can export them as acompile_commands.jsonfile whenmakefinishes.