A C based build system for when it's 2AM and you just want things to compile.
Built originally to compile my own game engine. Somehow can handle complex directory structures now.
There is some limitation, maximum .c files per build only 256. And only work for
UNIX-based system (although i haven't tested it on macOS & BSD).
2am-builder.h - A single-header C build system that builds your project by compiling it first.
- Weekend C projects
- Learning C builds
- Simple tools & utilities
- School assignments
- When other build tools feels like overkill (or confusing to setup)
- Single-header (one dependency: libc)
- Incremental compilation (only recompiles changed files)
- Dependency tracking (.d files, Make-style)
- System library & custom library linking
- Mirror source directory structure
- Can handle complex builds (I think)
- Generate compilation database (.json) for clangd
- Deploy prebuilt library to install on system (or local project directory)
Note: By default, 2am-builder mirrors your source structure. Call
AM_SET_OBJECT_LAYOUT(AM_FLAT)beforeAM_BUILDif you prefer all object files in a single directory.
2am-builder.h must live in the project root directory.
DO NOT put it in
src/,include/,tools/, or anywhere “cleaner”. The build system assumes all paths are relative to the project root.
If you move it:
- files won’t be found
- outputs will appear in strange places
- reality will feel unstable
typedef enum {
AM_MIRROR,
AM_FLAT,
} object_layout_t;
typedef enum {
BUILD_EXE,
BUILD_SHARED,
BUILD_STATIC
} build_type_t;
void AM_INIT(void);
void AM_RESET(void);
// Compiler setup
void AM_SET_COMPILER(const char *compiler, const char *c_version);
void AM_SET_COMPILER_WARN(const char *warning);
// Source target setup
void AM_SET_TARGET_DIRS(const char *binary_path, const char *object_path);
void AM_SET_TARGET_NAME(const char *target_name);
// Flag & linker setup
void AM_SET_FLAGS(const char *flags);
void AM_ADD_LINKER_FLAGS(const char *linker_flags);
// Directory include
void AM_ADD_INCLUDE(const char *include_dir);
// Source selection
void AM_SET_SOURCE_ALL(const char *path);
void AM_SET_SOURCE_SPECIFIC(const char *path, const char **files);
void AM_SET_SOURCE_WITH_EXCLUDE(const char *base_path,
const char *exclude_path);
// Library linking
void AM_USE_PREBUILT_LIB(const char *lib_name, const char *lib_path);
void AM_USE_LIB(const char *lib_name);
// Build & compile
void AM_BUILD(build_type_t type, bool use_dependency);
// Additonal Features
void AM_GEN_DATABASE(void);
void AM_PREBUILT_TO_SYSTEM(bool system_mode, const char *library_path,
const char *header_path, bool has_headers);
void AM_SET_OBJECT_LAYOUT(object_layout_t);- Copy
2am-builder.hto your project. - Create a
build.cor whatever naming you like.
// build game engine with a complex setup and dependencies
#define TWO_AM_BUILD_IMPL
#include "2am-builder.h"
int main(void) {
AM_INIT(); // --> initialize builder state
// ======== BUILD GLFW (static library) ==========
AM_SET_COMPILER("clang", "c99");
AM_SET_TARGET_DIRS("build", "build/vendor/glfw");
AM_SET_FLAGS("-fPIC, -D_GLFW_X11, -D_GLFW_GLX");
AM_ADD_INCLUDE("engine/src/vendor/glfw/include");
const char *glfw_linux_file[] = {
"context.c", "init.c", "input.c", "monitor.c", "vulkan.c", "window.c",
"egl_context.c", "osmesa_context.c",
// Linux-specific files
"x11_init.c", "x11_monitor.c", "x11_window.c", "xkb_unicode.c",
"glx_context.c", "linux_joystick.c", "posix_time.c", "posix_thread.c",
NULL};
AM_SET_SOURCE_SPECIFIC("engine/src/vendor/glfw/src", glfw_linux_file);
AM_SET_TARGET_NAME("glfw3");
AM_BUILD(BUILD_STATIC, false);
AM_RESET(); // --> reset builder state
// ======== BUILD ENGINE (shared library) ==========
AM_SET_COMPILER("clang", "c99");
AM_SET_COMPILER_WARN("-g, -Wall, -Wextra");
AM_SET_FLAGS("-fPIC, -DENGINE_DEBUG);
AM_SET_TARGET_DIRS("bin", "build/engine");
AM_SET_TARGET_NAME("engine");
AM_SET_SOURCE_WITH_EXCLUDE("engine/src", "vendor/glfw");
AM_USE_PREBUILT_LIB("glfw3", "build"); // --> if using GLFW compile from source
AM_USE_LIB("GL, m, dl, rt, X11");
AM_BUILD(BUILD_SHARED, true);
AM_GEN_DATABASE(); // --> generate compilation database (.json)
AM_RESET(); // --> reset builder state
// ======== BUILD APPLICATION ==========
AM_SET_COMPILER("clang", "c99");
AM_SET_COMPILER_WARN("-g, -Wall, -Wextra");
AM_SET_TARGET_DIRS("bin", "build/myapp");
AM_SET_TARGET_NAME("myapp");
AM_ADD_INCLUDE("engine/src");
AM_SET_SOURCE_ALL("myapp/src");
AM_USE_PREBUILT_LIB("engine", "bin");
AM_ADD_LINKER_FLAGS("-Wl, -rpath, .");
AM_BUILD(BUILD_EXE, true);
AM_RESET(); // --> reset builder state
return 0;
}