Skip to content

Multi file Projects

Derek Snider edited this page Jul 23, 2026 · 3 revisions

Multi-file Projects

madc builds real multi-file projects the same way clang tooling does: from a compile_commands.json compilation database. Each translation unit is compiled separately, the modules are linked, and the entry point runs — in-process, with no intermediate files.

The project driver: --project

myproject/
  main.c
  add.c
  compile_commands.json
// add.c
int add(int a, int b) { return a + b; }
// main.c
#include <stdio.h>
extern int add(int a, int b);
int main(void) { printf("2 + 3 = %d\n", add(2, 3)); return 0; }
[
  { "directory": "/path/to/myproject", "file": "main.c", "command": "cc -c main.c" },
  { "directory": "/path/to/myproject", "file": "add.c",  "command": "cc -c add.c" }
]
$ madc --project compile_commands.json
2 + 3 = 5

A .json argument is treated as a project manifest automatically, so this works too:

madc compile_commands.json

compile_commands.json is the standard compilation-database format — CMake emits one with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON, and bear generates one from any make build (bear -- make). Point madc at an existing C project's database and run it.

Linking libraries

-l<name> resolves symbols from shared libraries at link time, with or without --project:

madc --project compile_commands.json -lcrypt -lm

Native executables from projects

--project composes with the AOT flags — the whole multi-TU project becomes a single native ELF binary:

madc --project compile_commands.json -o myproject
./myproject

This is how madc's flagship test case runs: SMAUG 1.8, a real ~158k-line C89 MUD codebase of 51 translation units, boots both as a multi-TU JIT run and as a single ~5 MB native executable built this way.

The lightweight alternative: #include composition

For small projects a single top-level file that #includes the rest still works fine:

// myapp.mad
#include "config.mad"
#include "types.mad"
#include "utils.mad"
#include "main.mad"   // contains int main()
madc myapp.mad
  • #include "file.mad" resolves relative to the including file
  • Repeated includes of the same file are skipped automatically
  • Everything shares one global scope (it is one translation unit)

Prefer --project once a codebase has real separate translation units — file-scope statics, per-file globals, and existing build metadata all behave like they do under a conventional C toolchain.

What's next?

Clone this wiki locally