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.