···11const std = @import("std");
2233-// Although this function looks imperative, note that its job is to
44-// declaratively construct a build graph that will be executed by an external
55-// runner.
33+// Although this function looks imperative, it does not perform the build
44+// directly and instead it mutates the build graph (`b`) that will be then
55+// executed by an external runner. The functions in `std.Build` implement a DSL
66+// for defining build steps and express dependencies between them, allowing the
77+// build runner to parallelize the build automatically (and the cache system to
88+// know when a step doesn't need to be re-run).
69pub fn build(b: *std.Build) void {
77- // Standard target options allows the person running `zig build` to choose
1010+ // Standard target options allow the person running `zig build` to choose
811 // what target to build for. Here we do not override the defaults, which
912 // means any target is allowed, and the default is native. Other options
1013 // for restricting supported target set are available.
1114 const target = b.standardTargetOptions(.{});
1212-1315 // Standard optimization options allow the person running `zig build` to select
1416 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
1517 // set a preferred release mode, allowing the user to decide how to optimize.
1618 const optimize = b.standardOptimizeOption(.{});
1919+ // It's also possible to define more custom flags to toggle optional features
2020+ // of this build script using `b.option()`. All defined flags (including
2121+ // target and optimize options) will be listed when running `zig build --help`
2222+ // in this directory.
17231818- const lib_mod = b.createModule(.{
2424+ // This creates a module, which represents a collection of source files alongside
2525+ // some compilation options, such as optimization mode and linked system libraries.
2626+ // Zig modules are the preferred way of making Zig code available to consumers.
2727+ // addModule defines a module that we intend to make available for importing
2828+ // to our consumers. We must give it a name because a Zig package can expose
2929+ // multiple modules and consumers will need to be able to specify which
3030+ // module they want to access.
3131+ const mod = b.addModule("zaprus", .{
3232+ // The root source file is the "entry point" of this module. Users of
3333+ // this module will only be able to access public declarations contained
3434+ // in this file, which means that if you have declarations that you
3535+ // intend to expose to consumers that were defined in other files part
3636+ // of this module, you will have to make sure to re-export them from
3737+ // the root file.
1938 .root_source_file = b.path("src/root.zig"),
2020- .target = target,
2121- .optimize = optimize,
2222- });
2323-2424- // We will also create a module for our other entry point, 'main.zig'.
2525- const exe_mod = b.createModule(.{
2626- // `root_source_file` is the Zig "entry point" of the module. If a module
2727- // only contains e.g. external object files, you can make this `null`.
2828- // In this case the main source file is merely a path, however, in more
2929- // complicated build scripts, this could be a generated file.
3030- .root_source_file = b.path("src/main.zig"),
3939+ // Later on we'll use this module as the root module of a test executable
4040+ // which requires us to specify a target.
3141 .target = target,
3232- .optimize = optimize,
3342 });
34433535- lib_mod.addImport("network", b.dependency("network", .{}).module("network"));
3636- lib_mod.addImport("gatorcat", b.dependency("gatorcat", .{}).module("gatorcat"));
4444+ mod.addImport("network", b.dependency("network", .{}).module("network"));
4545+ mod.addImport("gatorcat", b.dependency("gatorcat", .{}).module("gatorcat"));
37463838- exe_mod.addImport("zaprus", lib_mod);
3939- exe_mod.addImport("clap", b.dependency("clap", .{}).module("clap"));
4040-4141- const lib = b.addLibrary(.{
4242- .linkage = .static,
4343- .name = "zaprus",
4444- .root_module = lib_mod,
4545- });
4646-4747- b.installArtifact(lib);
4848-4949- // This creates another `std.Build.Step.Compile`, but this one builds an executable
5050- // rather than a static library.
4747+ // Here we define an executable. An executable needs to have a root module
4848+ // which needs to expose a `main` function. While we could add a main function
4949+ // to the module defined above, it's sometimes preferable to split business
5050+ // business logic and the CLI into two separate modules.
5151+ //
5252+ // If your goal is to create a Zig library for others to use, consider if
5353+ // it might benefit from also exposing a CLI tool. A parser library for a
5454+ // data serialization format could also bundle a CLI syntax checker, for example.
5555+ //
5656+ // If instead your goal is to create an executable, consider if users might
5757+ // be interested in also being able to embed the core functionality of your
5858+ // program in their own executable in order to avoid the overhead involved in
5959+ // subprocessing your CLI tool.
6060+ //
6161+ // If neither case applies to you, feel free to delete the declaration you
6262+ // don't need and to put everything under a single module.
5163 const exe = b.addExecutable(.{
5264 .name = "zaprus",
5353- .root_module = exe_mod,
6565+ .root_module = b.createModule(.{
6666+ // b.createModule defines a new module just like b.addModule but,
6767+ // unlike b.addModule, it does not expose the module to consumers of
6868+ // this package, which is why in this case we don't have to give it a name.
6969+ .root_source_file = b.path("src/main.zig"),
7070+ // Target and optimization levels must be explicitly wired in when
7171+ // defining an executable or library (in the root module), and you
7272+ // can also hardcode a specific target for an executable or library
7373+ // definition if desireable (e.g. firmware for embedded devices).
7474+ .target = target,
7575+ .optimize = optimize,
7676+ // List of modules available for import in source files part of the
7777+ // root module.
7878+ .imports = &.{
7979+ // Here "zaprus" is the name you will use in your source code to
8080+ // import this module (e.g. `@import("zaprus")`). The name is
8181+ // repeated because you are allowed to rename your imports, which
8282+ // can be extremely useful in case of collisions (which can happen
8383+ // importing modules from different packages).
8484+ .{ .name = "zaprus", .module = mod },
8585+ .{ .name = "clap", .module = b.dependency("clap", .{}).module("clap") },
8686+ },
8787+ }),
5488 });
55895690 // This declares intent for the executable to be installed into the
5757- // standard location when the user invokes the "install" step (the default
5858- // step when running `zig build`).
9191+ // install prefix when running `zig build` (i.e. when executing the default
9292+ // step). By default the install prefix is `zig-out/` but can be overridden
9393+ // by passing `--prefix` or `-p`.
5994 b.installArtifact(exe);
9595+ b.installArtifact(b.addLibrary(.{
9696+ .linkage = .static,
9797+ .name = "zaprus",
9898+ .root_module = mod,
9999+ }));
601006161- // This *creates* a Run step in the build graph, to be executed when another
6262- // step is evaluated that depends on it. The next line below will establish
6363- // such a dependency.
101101+ // This creates a top level step. Top level steps have a name and can be
102102+ // invoked by name when running `zig build` (e.g. `zig build run`).
103103+ // This will evaluate the `run` step rather than the default step.
104104+ // For a top level step to actually do something, it must depend on other
105105+ // steps (e.g. a Run step, as we will see in a moment).
106106+ const run_step = b.step("run", "Run the app");
107107+108108+ // This creates a RunArtifact step in the build graph. A RunArtifact step
109109+ // invokes an executable compiled by Zig. Steps will only be executed by the
110110+ // runner if invoked directly by the user (in the case of top level steps)
111111+ // or if another step depends on it, so it's up to you to define when and
112112+ // how this Run step will be executed. In our case we want to run it when
113113+ // the user runs `zig build run`, so we create a dependency link.
64114 const run_cmd = b.addRunArtifact(exe);
115115+ run_step.dependOn(&run_cmd.step);
651166666- // By making the run step depend on the install step, it will be run from the
117117+ // By making the run step depend on the default step, it will be run from the
67118 // installation directory rather than directly from within the cache directory.
6868- // This is not necessary, however, if the application depends on other installed
6969- // files, this ensures they will be present and in the expected location.
70119 run_cmd.step.dependOn(b.getInstallStep());
7112072121 // This allows the user to pass arguments to the application in the build
···75124 run_cmd.addArgs(args);
76125 }
771267878- // This creates a build step. It will be visible in the `zig build --help` menu,
7979- // and can be selected like this: `zig build run`
8080- // This will evaluate the `run` step rather than the default, which is "install".
8181- const run_step = b.step("run", "Run the app");
8282- run_step.dependOn(&run_cmd.step);
127127+ // Creates an executable that will run `test` blocks from the provided module.
128128+ // Here `mod` needs to define a target, which is why earlier we made sure to
129129+ // set the releative field.
130130+ const mod_tests = b.addTest(.{
131131+ .root_module = mod,
132132+ });
133133+134134+ // A run step that will run the test executable.
135135+ const run_mod_tests = b.addRunArtifact(mod_tests);
831368484- const exe_unit_tests = b.addTest(.{
8585- .root_module = exe_mod,
137137+ // Creates an executable that will run `test` blocks from the executable's
138138+ // root module. Note that test executables only test one module at a time,
139139+ // hence why we have to create two separate ones.
140140+ const exe_tests = b.addTest(.{
141141+ .root_module = exe.root_module,
86142 });
871438888- const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
144144+ // A run step that will run the second test executable.
145145+ const run_exe_tests = b.addRunArtifact(exe_tests);
146146+147147+ // A top level step for running all tests. dependOn can be called multiple
148148+ // times and since the two run steps do not depend on one another, this will
149149+ // make the two of them run in parallel.
150150+ const test_step = b.step("test", "Run tests");
151151+ test_step.dependOn(&run_mod_tests.step);
152152+ test_step.dependOn(&run_exe_tests.step);
891539090- // Similar to creating the run step earlier, this exposes a `test` step to
9191- // the `zig build --help` menu, providing a way for the user to request
9292- // running the unit tests.
9393- const test_step = b.step("test", "Run unit tests");
9494- test_step.dependOn(&run_exe_unit_tests.step);
154154+ // Just like flags, top level steps are also listed in the `--help` menu.
155155+ //
156156+ // The Zig build system is entirely implemented in userland, which means
157157+ // that it cannot hook into private compiler APIs. All compilation work
158158+ // orchestrated by the build system will result in other Zig compiler
159159+ // subcommands being invoked with the right flags defined. You can observe
160160+ // these invocations when one fails (or you pass a flag to increase
161161+ // verbosity) to validate assumptions and diagnose problems.
162162+ //
163163+ // Lastly, the Zig build system is relatively simple and self-contained,
164164+ // and reading its source code will allow you to master it.
95165}