# Creating Build Steps

### Creating Build Steps

A Zig build is made from steps.

A step is one action in the build process. It might compile an executable, run tests, copy a file, generate code, or run a command.

When you write:

```bash
zig build
```

Zig loads `build.zig`, creates a graph of steps, and runs the default step.

The build graph is important. A build step can depend on another build step. If step B depends on step A, Zig must finish A before B can run.

#### The Default Build Step

Every `build.zig` file receives a build object:

```zig
pub fn build(b: *std.Build) void {
    // build description goes here
}
```

The object `b` owns the build graph.

When you add an executable:

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

Zig creates a compile step internally. That step knows how to build the `hello` executable.

But creating the executable is not enough. You usually also want to install it:

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

This connects the executable to the default install step. Now `zig build` will build and install it.

#### Named Steps

You can create your own named step with:

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

The first string is the command name. The second string is the help text.

After this, the user can run:

```bash
zig build run
```

But the step does nothing yet. A named step needs dependencies.

#### Running an Executable

To run an executable from the build system, create a run command:

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

Then connect it to the named step:

```zig
run_step.dependOn(&run_cmd.step);
```

Full 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 and installs the program.

The second command builds the program and then runs it.

#### Passing Arguments to a Run Step

Sometimes you want to pass arguments to the program.

Suppose your program reads command-line arguments:

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

pub fn main() !void {
    var args = std.process.args();

    while (args.next()) |arg| {
        std.debug.print("{s}\n", .{arg});
    }
}
```

You can allow arguments after `--`:

```zig
if (b.args) |args| {
    run_cmd.addArgs(args);
}
```

Full build snippet:

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

if (b.args) |args| {
    run_cmd.addArgs(args);
}

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

Now you can run:

```bash
zig build run -- hello zig users
```

The arguments after `--` are passed to your program, not to the build system.

#### Test Steps

Tests are also build steps.

You create a test artifact with:

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

This compiles the tests, but does not yet connect them to a named command.

To run the tests:

```zig
const run_unit_tests = b.addRunArtifact(unit_tests);
```

Then create a named step:

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

Now this works:

```bash
zig build test
```

Full 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);
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

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

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

    const run_unit_tests = b.addRunArtifact(unit_tests);

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

#### Step Dependencies

A step dependency means one step must happen before another.

This line:

```zig
run_step.dependOn(&run_cmd.step);
```

means:

```text
Before the run step is complete, the run command step must be complete.
```

The run command itself depends on the executable being built. So Zig knows the order:

```text
compile executable
run executable
finish run step
```

You do not manually write that order as a shell script. You describe the relationships, and Zig executes the needed steps.

#### Multiple Dependencies

A step can depend on more than one thing.

For example, you can create a `check` step that runs several checks:

```zig
const check_step = b.step("check", "Run all checks");

check_step.dependOn(&run_unit_tests.step);
```

Later, you might add formatting checks, generated-code checks, or integration tests:

```zig
check_step.dependOn(&run_unit_tests.step);
check_step.dependOn(&run_integration_tests.step);
check_step.dependOn(&lint_generated_files.step);
```

Then:

```bash
zig build check
```

runs everything connected to that step.

This is a clean way to define project workflows.

#### Custom System Commands

You can also create a step that runs an external command.

For example:

```zig
const echo_cmd = b.addSystemCommand(&.{
    "echo",
    "Hello from the build system",
});

const hello_step = b.step("hello", "Print a message");
hello_step.dependOn(&echo_cmd.step);
```

Now this works:

```bash
zig build hello
```

This is useful when a project needs to call another tool, such as a code generator.

Use this carefully. External commands make the build less portable if they depend on tools that may not exist on every system.

#### Generated Files

Build steps are often used to generate files before compilation.

For example, a project might generate a source file from a schema, grammar, protocol definition, or asset list.

The general idea is:

```text
generate file
compile program that imports generated file
install program
```

The exact APIs depend on what kind of generation you need, but the dependency idea stays the same. The compile step must depend on the generation step, so the file exists before the compiler needs it.

This is the build system’s main job: make the order explicit.

#### Helpful Step Names

Good step names are short and predictable.

Common step names:

```text
run
test
check
docs
bench
install
```

A user should be able to guess them.

The help text should explain the step plainly:

```zig
const bench_step = b.step("bench", "Run benchmarks");
const docs_step = b.step("docs", "Build documentation");
const check_step = b.step("check", "Run all checks");
```

You can see available steps with:

```bash
zig build --help
```

Your named steps appear in that help output.

#### The Important Idea

A `build.zig` file describes a graph of work.

Each step is a node in that graph. Dependencies are edges between nodes.

You do not write a long script that says “do this, then this, then this.” Instead, you tell Zig what each step needs.

That gives you a build that is easier to extend.

For a beginner, remember this pattern:

```zig
const step = b.step("name", "Description");
step.dependOn(&some_other_step.step);
```

That is the basic shape of custom build logic in Zig.

