Introduction
Fabricate is a Lua-configured build system that produces Ninja build files. The
fabricate CLI evaluates your fab.lua configuration, downloads declared git
dependencies, records build graph information, and writes an output/build.ninja
file (and optionally a compile_commands.json). After fabricate setup runs you
invoke Ninja to actually build your project.
This guide documents how to use Fabricate: the CLI options, how to structure a
fab.lua, and the helper functions and object types that Fabricate makes
available to Lua. Development internals, contributing guidelines, and other
non-user topics are intentionally omitted.
Getting Started
- Install the
fabricatebinary (Cargocargo install fabricate2if you are building from source, or copy the compiled binary into your$PATH). - Create a working directory that contains a
fab.luaconfiguration file. All paths referenced by the configuration are interpreted relative to this directory. - Run
fabricate setupto generate the build directory. This will create the Ninja build file, compile commands, a fabricate cache file, ... - Build either with Ninja directly or using Fabricates wrapper. The default build directory is
build.
4.1. with Ninja:ninja -C <build dir>.
4.2. with Fabricate:fabricate --build-dir <build dir>. - Finally install artifacts with
fabricate install.
Command-Line Interface
The fabricate binary exposes three subcommands: setup, build, and
install. All commands share the --build-dir (-b) flag that chooses which
build directory to operate on. If omitted, the build directory defaults to
build.
fabricate [GLOBAL OPTIONS] <SUBCOMMAND> [OPTIONS]
| Global flag | Default | Description |
|---|---|---|
-b, --build-dir <path> | build | Directory that stores build.ninja, cached metadata, and intermediate outputs. |
-h, --help | – | Show help for the selected command. |
-V, --version | – | Show the Fabricate version. |
setup
Evaluates the Lua configuration and writes/updates build.ninja.
| Flag | Default (per code) | Description |
|---|---|---|
--config <path> | fab.lua | Lua configuration file to execute. |
--prefix <path> | fab.lua | Installation prefix recorded in fabricate_cache.toml. (The help text mentions /usr, but the current build sets the default to fab.lua.) |
-o, --option key=value | – | Collects user-defined options that Lua can read via fab.option. Repeat the flag for each key/value pair. |
--dependency-override name=path | – | Overrides the git dependency declared via fab.git(name, …) to use an existing checkout at path instead of cloning into the build directory. Repeat as needed. |
Example:
fabricate setup \
--config fab.lua \
--prefix /usr \
--build-dir build \
--option toolchain=clang \
--option enable-tests=yes
Dependency overrides let you substitute local checkouts for remote git dependencies during setup. Each override uses the dependency name (the first argument passed to fab.git) and either an absolute path or a path relative to the directory that contains fab.lua. When present, Fabricate records the dependency metadata but returns the provided path to Lua, so rules can consume your locally modified sources without triggering network fetches.
If Ninja is installed, setup also invokes ninja -t cleandead inside the
existing build directory before rewriting the graph.
build
Runs Ninja in the selected build directory. This is identical to running ninja -C <build-dir>.
install
Copies all artifacts listed in the install map.
This subcommand fails if the cache is missing, or if any artifact is absent.
Note that Fabricate does not allow for installation of directory artifacts.
| Flag | Default | Description |
|---|---|---|
--dest-dir <path> | (empty string) | Optional DESTDIR-style prefix prepended to each install destination. |
Example:
fabricate --build-dir build install --dest-dir /tmp/sysroot
Install computes each destination as DESTDIR + prefix + dest path where prefix
comes from the last setup invocation and dest path is the install map key (such as
bin/foo). Before copying a file Fabricate creates the parent directories.
Configuration File
Fabricate evaluates a single Lua file (by default fab.lua). The configuration is written in Lua (Lua 5.4).
Many of the standard lua functions and libraries are available.
Note that the lua environment is NOT sandboxed meaning a build script can run arbitrary commands even at setup time.
Install Artifacts
Anywhere in the root scope of fab.lua return a table. Fabricate currently reads the
optional install field to discover which artifacts should be copied during an
installation step. Destination paths are interpreted relative to the prefix.
The field must be a table mapping destination paths to the
Artifact objects produced earlier:
Example:
return {
install = {
["bin/fabricate-example"] = app_artifact,
["lib/libexample.a"] = static_lib,
}
}
If you do not want Fabricate to manage installation simply return a table without the install key or omit the return entirely.
Fab Library
The global fab table exposes functions implemented by Fabricate. These
functions are the authorative way of interacting with Fabricate but are
also very crude. This is why Fabricate provides many helpers written in
Lua for a more user friendly interface.
fab.glob(..., opts?)
Runs a set of globs relative to a given directory (project root by default) and returns a list of matches. All globs given must match. Options can be given by passing a table as the last argument, valid options are:
| Field | Type | Description |
|---|---|---|
case_sensitive | boolean | Override the default case-sensitive behavior. |
require_literal_separator | boolean | If true, * and ? will never match s/. This is false by default. |
relative_to | string | The directory relative to which globs will run. Project root by default. |
-- collect all C sources outside the tests directory
local c_files = fab.glob("src/**/*.c", {
case_sensitive = false,
excludes = { "src/tests/**" }
})
fab.project_dir()
Returns an absolute path to the project root.
fab.build_dir()
Returns an absolute path to the build directory.
fab.path_join(...)
Joins the provided path fragments using the host platform’s separator and returns the joined string. If a component is absolute, it replaces the entire path.
fab.path_rel(path)
Resolves a build directory relative path from an absolute path or a project root relative path.
fab.which(name)
Find an executable binary’s path by name. Returns an absolute path when the binary is found or nil otherwise.
- If given an absolute path, returns it if the file exists and is executable.
- If given a relative path, returns an absolute path to the file if it exists and is executable.
- If given a string without path separators, looks for a file named binary_name at each directory in
$PATHand if it finds an executable file there, returns it.
fab.option(name, type, required)
Declares a user option that can be provided on the CLI via
--option name=value. The type argument controls validation:
"string","number", or"boolean"expect the corresponding type and transform the CLI string automatically.- A table of allowed strings works as an enum (Fabricate checks that the CLI value matches one of the table entries and returns the matching value).
If required is false or omitted the option may be omitted and nil is returned. Otherwise
Fabricate raises a setup-time error if the option is missing.
local selected_cc = fab.option("toolchain", { "gcc", "clang" })
fab.git(name, url, revision)
Clones (or reuses) a git repository into the build directory and returns an
Artifact pointing at the repository directory. The clone is skipped when the
cache already contains a matching URL and revision.
fab.def_source(path)
Declares a source file relative to the project root, returning a Source.
Fabricate validates that the path stays inside the source tree. Note that
Sources must exist at setup-time whereas Artifacts might not.
fab.def_rule(name, command, description?, depstyle?, build_compdb?)
Creates a rule object. Arguments:
name: must be unique, contain only alphanumeric characters plus_or-, and must not start withfab_.command: shell command template. Allows for "embed variables", the embeds take the following form: @EMBED@. The names of the embeds are case-insensitive. They are replaced by values passed at each invocation of a rule build. Fabricate supports a few special embeds:Name Description @IN@Source file path(s) @OUT@Output file path @DEPFILE@Dependency file path description: optional description displayed by Ninja at build time.depstyle: one of"normal","gcc","clang", or"msvc"and controls how dependency files are interpreted. If unsure, set to"normal".build_compdb: set totrueto include builds using this rule when generatingcompile_commands.json.
The returned Rule object exposes the rule:build(...) method documented in the
Rules chapter.
fab.typeof(userdata)
A helper that inspects an arbitrary userdata value and returns "source", "rule", "artifact", or "unknown".
Object Reference
Fabricate injects several userdata types into Lua. These behave like opaque objects with fields/methods. Fabricate uses them to collect build graph information.
Source
| Field | Type | Description |
|---|---|---|
path | string | Path relative to the build directory. |
Represents an input file inside the project root. Must exist at setup time.
Artifact
| Field | Type | Description |
|---|---|---|
path | string | Path relative to the build directory. |
Represents a build artifact produced during at build time.
Rule
| Field | Type | Description |
|---|---|---|
name | string | Name of the rule. |
A rule defines how to run a command. Besides the name field, the important API
is rule:build(output, inputs, variables, implicit_inputs?):
output: Unique name of the artifact output by this build rule. This ends up as the special@OUT@variable.inputs: Array ofSourceorArtifactobjects that the build depends on. These also end up in the special@IN@variable.variables: Table containing custom@VAR@values declared when the rule was defined.implicit_inputs: Optional additional sources/artifacts that should be wired as implicit dependencies (dependend on but not directly used).
The method returns an Artifact describing the produced file.