zig build
Large programs are rarely built with a single compiler command.
A program may contain many source files. It may need tests, examples, generated files, libraries, or platform-specific options. Typing long command lines by hand quickly becomes tedious.
Zig solves this with a build system written in Zig itself.
Most Zig projects contain a file named build.zig. The zig build command reads this file and executes the build graph described inside it.
A small project might look like this:
project/
├── build.zig
├── build.zig.zon
└── src/
└── main.zigThe simplest useful build.zig file is short:
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "hello",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = b.standardTargetOptions(.{}),
.optimize = b.standardOptimizeOption(.{}),
}),
});
b.installArtifact(exe);
}Build the program:
zig buildRun the executable:
./zig-out/bin/helloThe command zig build does not directly compile source files itself. Instead, it executes the build description in build.zig.
The function:
pub fn build(b: *std.Build) voidis the entry point of the build system.
The parameter b is the build context. It provides functions for creating executables, libraries, tests, install steps, and custom commands.
This line creates an executable build step:
const exe = b.addExecutable(...)The executable is named:
.name = "hello",The source file is:
.root_source_file = b.path("src/main.zig"),The compiler target comes from:
.target = b.standardTargetOptions(.{}),This allows the user to select targets from the command line:
zig build -Dtarget=x86_64-linuxOptimization mode comes from:
.optimize = b.standardOptimizeOption(.{}),This enables commands such as:
zig build -Doptimize=ReleaseFastThe final line:
b.installArtifact(exe);adds an install step. By convention, installed files are written into zig-out/.
The build system is incremental. If a source file has not changed, Zig avoids rebuilding unnecessary parts of the project.
A build script is ordinary Zig code. Loops, conditions, helper functions, and compile-time computation can all be used naturally.
For example:
if (some_condition) {
// add extra library
}or:
for (targets) |target| {
// build multiple executables
}This is one of the major design choices of Zig’s tooling. There is no separate build language. The language used to build Zig programs is Zig itself.
A project often defines additional build steps. For example:
const run_cmd = b.addRunArtifact(exe);
const run_step = b.step("run", "Run the program");
run_step.dependOn(&run_cmd.step);Now the program can be run with:
zig build runTests are commonly integrated the same way:
const tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
}),
});and executed with:
zig build testThe build system is still evolving in Zig 0.16, but its overall direction is stable:
- Build descriptions are code.
- Cross compilation is built in.
- Dependencies are explicit.
- Build graphs are incremental.
- The standard toolchain handles compilation, linking, testing, formatting, and packaging together.
Exercise 15-1. Create a project with build.zig and one source file.
Exercise 15-2. Add a run step.
Exercise 15-3. Build the same program for another operating system.
Exercise 15-4. Add a test step and run it with zig build test.