Core Concepts
The build graph
A Zaza build file is a Zig program that adds steps to std.Build. Zig runs it,
resolves the dependency graph, and executes only the steps whose inputs changed.
There is no configure phase distinct from the build phase, and no generated
Makefiles or Ninja files in between.
Step names follow four conventions:
| Pattern | Meaning |
|---|---|
<name> | Build or stage the artifact |
<name>-run | Execute something real |
<name>-report | Inspect or validate an artifact the host cannot run |
<name>-serve | Start a long-running local server |
build_lib/build_graph.zig holds a small standalone graph structure with
topological sort, used by the tests to assert ordering behaviour independently
of std.Build.
CppExample
CppExample is the C and C++ target type. It is a plain struct, so a target is
data that can be inspected, serialised, and modified before it is built.
pub var example = cpp.CppExample.executable(.{
.name = "my_app",
.description = "demo",
.source_files = &.{"src/main.cpp"},
.public_include_dirs = &.{"include"},
.public_defines = &.{"MY_API=1"},
.public_link_libs = &.{"pthread"},
.cpp_std = "17",
});
Five constructors cover the target kinds:
cpp.CppExample.executable(...)
cpp.CppExample.staticLibrary(...)
cpp.CppExample.sharedLibrary(...)
cpp.CppExample.objectLibrary(...)
cpp.CppExample.interfaceLibrary(...)
All five call CppExample.make(kind, options).
Fields split along the usual visibility lines. public_include_dirs,
public_defines, and public_link_libs are meant to propagate to consumers.
private_include_dirs, private_defines, and private_link_libs are not.
Transitive propagation across dependency edges is partial today; see
CMAKE_PARITY.md.
Generated sources are declared alongside a command that produces them:
.generated_source_files = &.{"zig-out/gen/generated.cpp"},
.custom_commands = &.{
.{
.name = "generate_generated_cpp",
.argv = &.{"sh", "scripts/generate.sh", "zig-out/gen/generated.cpp"},
},
},
Install and export fields produce a stable layout:
zig-out/include/<export_name>/...
zig-out/lib/...
zig-out/cmake/<export_name>/<export_name>Config.cmake
zig-out/share/zaza/<export_name>.json
The full field list is in SYNTAX_REFERENCE.md.
Presets
ZAZA_PRESET selects a named BuildConfig list from build_lib/presets.zig.
Six are defined:
| Preset | Effect |
|---|---|
debug | mode = .Debug (the default) |
release | mode = .Release |
relwithdebinfo | mode = .RelWithDebInfo |
minsizerel | mode = .MinSizeRel |
asan | .Debug plus -fsanitize=address and ZAZA_ASAN=1 |
lto | .Release plus want_lto and ZAZA_LTO=1 |
An unrecognised name silently falls back to debug.
ZAZA_EXAMPLES=preset-profiles ZAZA_PRESET=release zig build preset-profiles-run
preset: release
preset runtime ready
Presets apply only to the examples that opt in: cmake_combo, cmake_net,
cmake_shim, and preset_profiles.
The package registry
registry/registry.json maps a short package name to a source URL and version.
zig run scripts/zaza.zig -- list
Available packages (7):
juce 7.0.9
fmt 10.2.1
spdlog 1.14.1
zlib 1.3.1
mbedtls 3.6.2
curl 8.6.0
nlohmann_json 3.11.3
Fetching a package resolves its hash with zig fetch and writes the entry into
build.zig.zon:
zig build zaza-fetch -- fmt
The rest of the CLI:
zaza fetch <name> Fetch a package from the registry into build.zig.zon (alias: add)
zaza add <name> Alias for fetch
zaza remove <name> Remove a dependency from build.zig.zon (alias: rm)
zaza list List all packages available in the registry (alias: ls)
zaza deps List dependencies with source, lock state, and on-disk presence
zaza clean-deps Remove deps/ and zig-out/deps (alias: clean)
zaza cache Show the Zig cache directories and whether they are writable
zaza search <query> Search packages by name
zaza init [name] Scaffold a new Zaza project in the current directory
zaza deps reads build.zig.zon for the declared names and zaza.lock for
what has been pinned, and checks deps/ for what is present on disk:
zig run scripts/zaza.zig -- deps
Dependencies (7):
name source hash on disk
mbedtls unlocked - no
fmt registry 1220abcdef0123 yes
...
fetch resolves the hash with zig fetch and records it in both
build.zig.zon and zaza.lock, so a fetched dependency shows its source and
hash here. unlocked means there is no zaza.lock entry pinning it yet.
zaza clean-deps removes the fetched sources in deps/ and their built outputs
in zig-out/deps, so the next build re-fetches from the pinned hashes. zaza
cache reports the global and local Zig cache directories and whether each is
writable, which is the first thing to check when a build fails with a cache
error:
zig run scripts/zaza.zig -- cache
Zig cache directories:
global /Users/you/.cache/zig (writable)
local .zig-cache (default) (writable)
The root build also resolves registry entries on your behalf. If an enabled
example needs a package that is missing from build.zig.zon, the build adds it
and stops with a message telling you to re-run. ZAZA_REGISTRY=0 disables that.
The CMake interop shim
Some libraries only build with CMake. Zaza models those as dependencies with
type = .CMake, and the graph clones, configures, builds, and installs them:
.deps = &.{
.{
.name = "fmt",
.url = "https://github.com/fmtlib/fmt.git",
.git_ref = "10.2.1",
.type = .CMake,
.cmake_config = .{
.install_prefix = "zig-out/deps",
.install = true,
.configure_args = &.{ "-DFMT_DOC=OFF", "-DFMT_TEST=OFF", "-DFMT_INSTALL=ON" },
},
},
},
.deps_build_system = .CMake,
.main_build_system = .Zig,
Sources land in deps/<name>/, build trees in deps/<name>/build/<Config>/,
and installed output in zig-out/deps/. The final artifact is built by Zig and
links against the installed archives.
Three examples of increasing difficulty:
ZAZA_EXAMPLES=cmake-shim ZAZA_SYSTEM_CMDS=1 zig build cmake-shim-run
{
"awesome": true,
"name": "C++ with Zig"
}
ZAZA_EXAMPLES=cmake-combo zig build cmake-combo-run
[2026-07-21 12:06:42.346] [info] hello from cmake_combo
ZAZA_EXAMPLES=cmake-net zig build cmake-net-run
cmake_net
curl: libcurl/8.6.0-DEV mbedTLS/3.6.2 zlib/1.3.1
zlib: 1.3.1
mbedtls: 3.6.2
The last one is the interesting case: curl is configured against the zlib and mbedtls that the same graph installed a moment earlier.
Interop runs the other way too. export_cmake = true writes a
<name>Config.cmake into zig-out/cmake/, so a CMake project can consume a
Zaza-built library.