The Zig build system is pretty good. Here are conventions to make your experience with the build system even better.
First thing, at the top of your fn build, declare a variable called build_steps like so:
pub fn build(b: *std.Build) void {
const build_steps = .{
.run = b.step("run", "Run the executable"),
.@"test" = b.step("test", "Run the tests"),
};
...
}
This serves as a quick, at-a-glance reference for all of the build steps defined in your build.zig. Of course, you could also run zig build --help and inspect the output to determine which build steps exist.
After you declare build_steps, add a defer statement to ensure that your steps actually have dependencies - otherwise it's likely best to prune them, just as Zig encourages you to get rid of unused variables.
pub fn build(b: *std.Build) void {
const build_steps = .{ ... };
defer inline for (@typeInfo(@TypeOf(build_steps)).@"struct".fields) |f| {
if (f.type == *std.Build.Step) {
debug.assert(@field(build_steps, f.name).dependencies.items.len > 0);
}
};
...
}
This can save you time and headaches when adding new steps or modifying your build.zig by ensuring you've properly registered step dependencies for every step defined in build_steps. If your build complains, you know either a step has been misconfigured or isn't needed anymore.
If you don't define build_steps and instead define each of your steps as separate variables, the Zig compiler will already error out if they aren't referenced anywhere, so this almost restores that kind of safety rail for the build_steps struct.