# Introduction to `build.zig`

### Introduction to `build.zig`

Zig has a built-in build system.

That means you do not need a separate tool like Make, CMake, Ninja, or a shell script for ordinary Zig projects. You can describe your build directly in Zig code.

The main file is usually named:

```text
build.zig
```

This file tells Zig how to build your program, how to run it, how to test it, what dependencies it needs, and what options should be passed to the compiler.

A small Zig project often looks like this:

```text
hello/
  build.zig
  src/
    main.zig
```

The source code lives in `src/main.zig`. The build instructions live in `build.zig`.

You build the project with:

```bash
zig build
```

That command reads `build.zig`, creates the build graph, compiles the program, and places the output in Zig’s build cache or install directory.

#### Why Zig Has Its Own Build System

Many languages separate the programming language from the build language.

For example, a C project might use C for the program, but Make, CMake, Meson, or another language for the build.

Zig takes a different approach: the build file itself is Zig code.

That gives you one language for both your program and your build logic. You can use variables, functions, conditionals, loops, structs, and standard library APIs inside the build file.

A build file can stay simple for small projects, but it can also grow into a precise build description for larger systems.

#### A Minimal `build.zig`

Here is a simple `build.zig` file:

```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 = "hello",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    b.installArtifact(exe);
}
```

This looks more complex than a single compiler command, but each part has a clear job.

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

This imports the standard library.

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

Every `build.zig` file defines a public function named `build`. Zig calls this function when you run:

```bash
zig build
```

The parameter `b` is a pointer to a `std.Build` object. You use it to describe what the project should build.

#### Target and Optimization Options

These two lines are common in Zig build files:

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

The first line lets the user choose the target platform.

For example, the same project can be built for your current machine, Linux, Windows, macOS, WebAssembly, ARM, or another supported target.

The second line lets the user choose the optimization mode.

Common modes include debug builds and release builds. A debug build is better while developing. A release build is better when you care about speed or small binary size.

This means your build file supports commands such as:

```bash
zig build
zig build -Doptimize=ReleaseFast
zig build -Dtarget=x86_64-linux
```

You do not need to hard-code everything.

#### Adding an Executable

This part creates an executable build step:

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

The name of the output program is:

```zig
.name = "hello"
```

The main source file is:

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

The target and optimization settings come from the options we defined earlier.

So this code says: build an executable named `hello` from `src/main.zig`, using the selected target and optimization mode.

#### Installing the Artifact

This line tells Zig to install the executable:

```zig
b.installArtifact(exe);
```

An artifact is something produced by the build, such as an executable, static library, dynamic library, or generated file.

When you run:

```bash
zig build
```

Zig builds the executable and installs it into the project’s build output directory.

You can usually find installed outputs under:

```text
zig-out/
```

For example:

```text
zig-out/
  bin/
    hello
```

On Windows, the file may be:

```text
zig-out/bin/hello.exe
```

#### `zig build` vs `zig build-exe`

Earlier, we used:

```bash
zig build-exe src/main.zig
```

That command directly compiles one source file into an executable.

For very small experiments, this is fine.

But for real projects, `zig build` is usually better.

With `zig build`, you can describe the whole project in one place: executables, libraries, tests, dependencies, build options, generated files, install rules, and custom commands.

A simple rule is:

Use `zig build-exe` when learning or testing one file.

Use `zig build` when making a project.

#### Build Files Are Programs

A `build.zig` file is not just configuration. It is actual Zig code.

That means you can write helper functions:

```zig
fn addCommonOptions(module: *std.Build.Module) void {
    _ = module;
}
```

You can use conditionals:

```zig
if (optimize == .Debug) {
    // debug-only build setup
}
```

You can create several artifacts:

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

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

This makes the build system flexible without introducing a separate build language.

#### Build Steps

The Zig build system is based on steps.

A step is an action in the build process. Examples include:

```text
compile this executable
run this executable
run these tests
install this artifact
generate this file
```

When you run `zig build`, Zig executes the default build steps.

You can also define named steps. For example, many projects define a `run` step:

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

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

Then you can run:

```bash
zig build run
```

This builds the executable and runs it.

That is useful during development because you do not need to manually find the compiled binary.

#### A Build File with a Run Step

Here is a slightly larger example:

```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 = "hello",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);

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

Now the project supports:

```bash
zig build
zig build run
```

The first command builds the program.

The second command builds and runs the program.

#### Adding Tests

A project often needs a test step too.

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

const run_exe_tests = b.addRunArtifact(exe_tests);

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

Now you can run:

```bash
zig build test
```

This gives your project a standard test command.

The build file controls how tests are compiled and executed.

#### The Important Idea

The purpose of `build.zig` is to describe the shape of your project.

It answers questions like:

Where is the main source file?

What should the output binary be called?

Which target should this build use?

Which optimization mode should this build use?

What command should run the program?

What command should run the tests?

What files should be installed?

What dependencies should be included?

At first, `build.zig` may look like extra work. But as a project grows, it becomes one of the most important files in the repository.

It is the place where the project becomes reproducible. A new developer can clone the project and run:

```bash
zig build
```

The build file tells Zig the rest.

