(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