#include "build.h"

User Guide

A build script calls ib_init, declares targets, and returns ib_build. IncludeBuild provides the command line, parallel execution, incremental builds, and self-rebuild behavior.

1. Bootstrap

Copy build.h into your project, write build.c, compile it once:

#define IB_IMPLEMENTATION
#include "build.h"

int main(int argc, char** argv) {
    ib_init(argc, argv);
    ib_sources(ib_executable("app"), "**.c");
    return ib_build();
}
$ cc -o build build.c
$ ./build

On later runs, changes to build.c or build.h cause ./build to recompile and restart.

2. The Command Line

./build [debug|release] [verb] [options]

verbs:    build (default) · run [target] · clean · compdb · help · version
options:  -jN · -v/--verbose · -q/--quiet · --color/--no-color

All projects use the same interface. ./build release selects release mode, ./build run builds and runs an executable, and ./build help lists targets. Arguments after -- are passed to the executable. Each mode has its own object tree.

3. Configuration Is a Struct

Set configuration fields between ib_init and ib_build. Unset fields use their defaults:

ib.cc             = "clang";      // else $CC, else cc (gcc on Windows)
ib.cxx            = "clang++";    // used for C++ sources and links
ib.cflags         = "-Wall";      // every compile, both languages
ib.cflags_debug   = "-g -O0";     // ./build
ib.cflags_release = "-O3 -flto";  // ./build release
ib.ldflags        = "-lm";        // every executable/shared link
ib.out_dir        = "bin";        // artifacts (default: project root)
ib.state_dir      = ".ibuild";    // objects, depfiles, records
ib.jobs           = 0;            // 0 = CPU count; CLI -jN wins

Command-line options override struct fields. ib.mode is available after ib_init for mode-specific logic.

4. Targets and Sources

ib_target* app = ib_executable("app");
ib_target* core = ib_static_lib("core");     // libcore.a
ib_target* plug = ib_shared_lib("plug");     // libplug.so / .dylib / .dll

ib_sources(app, "src/**.c");      // ** crosses directories
ib_sources(app, "extra/tool.c");  // plain paths work too
ib_include(app, "include");       // -Iinclude
ib_cflags(app, "-DAPP=1");        // this target only
ib_ldflags(app, "-lSDL2");

Globs use * and ? within a path segment and ** across directories. Matches are sorted and deduplicated. build.c and dot-directories are skipped. Empty matches are errors.

5. Library Dependencies

ib_use(app, core);   // link core into app

ib_use puts library archives on the link line in dependency order, propagates include directories, and relinks dependent targets when a library changes. Static code linked into a shared library is compiled with -fPIC.

6. Incremental Builds

Compiles run in parallel. An object rebuilds when it is missing, a dependency changed, or its compile command changed. Links use the same checks.

$ ./build -v
[1/2] cc  src/main.c
  $ cc -g -O0 -DDEBUG -MMD -MF .ibuild/... -c src/main.c -o .ibuild/...
  because: include/app.h changed

7. Editor Support

Run ./build compdb to create compile_commands.json. Successful builds update it.

8. Custom Arguments

Remove custom arguments from argv before calling ib_init:

int keep = 1;
for (int i = 1; i < argc; ++i) {
    if (strncmp(argv[i], "value=", 6) == 0) value = argv[i] + 6;
    else argv[keep++] = argv[i];
}
argc = keep;
ib_init(argc, argv);

9. Errors

Configuration errors print a message and exit nonzero. Compiler and linker failures show the command and its output. ib_build() returns the process exit code.