(written on zig version 0.7.1+d96f5b38a)
zig build <step> runs a specified step from the build.zig file. build.zig is a file written in zig that describes all the available things that can be built
An empty build file:
const std = @import("std");
const Builder = std.build.Builder;
pub fn build(b: *Builder) void {
}Running zig build on this file will do nothing. zig build --help will show two available steps, install (default) and uninstall that both do nothing.
Make a step to build the code and then a step to run the built code, then give commands to these steps
const std = @import("std");
const Builder = std.build.Builder;
pub fn build(b: *Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("exe_name", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
}A build-exe step is created (b.addExecutable), and then .install() adds it to the default zig build install step.
b.standardReleaseOptions() adds project specific options -Drelease-safe, -Drelease-fast, and -Drelease-small. These choose what optimization mode to build with, with the default being debug. exe.setBuildMode(mode) sets the -O flag on the build. -O Debug or -O ReleaseFast or … depending on what the mode is.
b.standardTargetOptions(.{}) adds the project specific option -Dtarget= so you can specify a cross-compiling target with zig build. zig build install -Dtarget=x86_64-linux-musl.
You can add your own options with b.option() but that is later.
Running the exe:
const run_cmd = exe.run();
run_cmd.step.dependOn(&exe.install_step.?.step);
const run_step = b.step("run", "Run the project");
run_step.dependOn(&run_cmd.step);runs. The default build file made with zig init-exe uses run_step.dependOn(b.getInstallStep()), but if your build file has multiple executables this will require building all of them even if the run step only depends on one executable.
system libraries with
b.linkSystemLibrary("SDL2");
b.linkSystemLibrary("fontconfig");this searches your system sometimg. it might use pkg-config or some other tool on linux and similar tools on windows and mac.
c and c++ files can be added to the build with .addCSourceFile
exe.addIncludeDir("src/c/");
exe.addCSourceFile("src/c/code.c", &[_][]const u8{});currently, all c files use the same include directories. there is no way to build c with one include directory and use a seperate set of include directories for zig code's @cInclude()
zig debug builds use the llvm undefined behaviour sanitizer, which some c code might crash with because it unknowingly uses undefined behaviour. This should be fixed, but the undefined behaviour sanitizer can be disabled in the meantime.
exe.addCSourceFile("deps/some_broken_library.c", &[_][]const u8{"-fno-sanitize=undefined"});const value = b.option([]const u8, "some-option", "option description")
this will add an option -Dsome-option=value, and whatever the user types for value is returned
it returns an optional in case the user doesn't specify an option, in which case you can have a default value or error
const renderer = b.option(enum{sdl, raylib}, "renderer", "renderer") orelse .raylib;
const value = b.option([]const u8, "value", "value") orelse {
@panic("expected -Dvalue=…");
};steps should not be added based on the result of options because that will break --help.
… = b.addStaticLibrary("name", "src/main.zig")
… = b.addTest("src/main.zig");
… = b.addFmt(&[_][]const u8{"src", "build.zig"}); … = b.addSystemCommand(&[_][]const u8{"touch", "hi.zig"}); exe.setTarget(std.zig.CrossTarget.parse(.{ .arch_os_abi = "wasm32-freestanding" }) catch @panic("err"));There might be a better way to do this