# `build.zig`

### `build.zig`

The file `build.zig` is a Zig program.

It is not a configuration file. It is not a list of compiler flags. It is code that constructs a build graph.

A minimal file has one public function:

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    _ = b;
}
```

The build system looks for this function:

```zig
pub fn build(b: *std.Build) void
```

The name must be `build`. The parameter is a pointer to `std.Build`.

The build context `b` owns the objects used during the build. It allocates build steps, stores options, resolves paths, and connects dependencies between steps.

A useful `build.zig` usually starts like this:

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "demo",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    b.installArtifact(exe);
}
```

The two standard options are important.

```zig
const target = b.standardTargetOptions(.{});
```

This adds the standard `-Dtarget` option to the build.

```zig
const optimize = b.standardOptimizeOption(.{});
```

This adds the standard `-Doptimize` option.

Together they allow ordinary command lines such as:

```sh
zig build
zig build -Doptimize=ReleaseSafe
zig build -Dtarget=x86_64-linux
zig build -Dtarget=aarch64-macos -Doptimize=ReleaseFast
```

The call to `b.addExecutable` creates a compile step. It does not immediately compile the program. It adds a node to the build graph.

The call to `b.installArtifact(exe)` adds another step. This step depends on the executable step. If the executable has not been built, it is built first.

The default `zig build` command runs the install step.

A build file can define named steps. A named step is a command the user can run.

```zig
const run_cmd = b.addRunArtifact(exe);

const run_step = b.step("run", "Run the program");
run_step.dependOn(&run_cmd.step);
```

Now this command exists:

```sh
zig build run
```

The build graph is explicit. The `run` step depends on the run command. The run command depends on the executable. The executable depends on its source files and options.

Tests are added in the same style:

```zig
const tests = b.addTest(.{
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

const test_cmd = b.addRunArtifact(tests);

const test_step = b.step("test", "Run tests");
test_step.dependOn(&test_cmd.step);
```

Now:

```sh
zig build test
```

builds and runs the tests.

The build file can also read project options.

```zig
const enable_logs = b.option(bool, "logs", "Enable log output") orelse false;
```

The user passes the option like this:

```sh
zig build -Dlogs=true
```

The value can be passed into the program as a compile-time option.

```zig
const options = b.addOptions();
options.addOption(bool, "enable_logs", enable_logs);

exe.root_module.addOptions("config", options);
```

Then source code can import it:

```zig
const config = @import("config");

pub fn main() void {
    if (config.enable_logs) {
        // logging code
    }
}
```

This keeps build-time decisions explicit. The program does not inspect hidden environment state. The build script decides what to expose.

Paths should normally be written through the build context:

```zig
b.path("src/main.zig")
```

This makes the path relative to the package root. It avoids depending on the current working directory.

A `build.zig` file should stay boring. It should describe how to build the project, not become a second application. Use helper functions when the project grows, but keep the graph visible.

A good build file answers these questions plainly:

What is being built?

Where is the root source file?

What target is used?

What optimization mode is used?

What steps can the user run?

What options can the user pass?

Exercise 15-5. Write a `build.zig` file for an executable named `count`.

Exercise 15-6. Add a `run` step.

Exercise 15-7. Add a boolean option named `trace`.

Exercise 15-8. Import the option from source code and print a message only when it is enabled.

