1
+ //! Use `zig init --strip` next time to generate a project without comments.
1
2
const std = @import ("std" );
2
3
3
- // Although this function looks imperative, note that its job is to
4
- // declaratively construct a build graph that will be executed by an external
5
- // runner.
4
+ // Although this function looks imperative, it does not perform the build
5
+ // directly and instead it mutates the build graph (`b`) that will be then
6
+ // executed by an external runner. The functions in `std.Build` implement a DSL
7
+ // for defining build steps and express dependencies between them, allowing the
8
+ // build runner to parallelize the build automatically (and the cache system to
9
+ // know when a step doesn't need to be re-run).
6
10
pub fn build (b : * std.Build ) void {
7
- // Standard target options allows the person running `zig build` to choose
11
+ // Standard target options allow the person running `zig build` to choose
8
12
// what target to build for. Here we do not override the defaults, which
9
13
// means any target is allowed, and the default is native. Other options
10
14
// for restricting supported target set are available.
11
15
const target = b .standardTargetOptions (.{});
12
-
13
16
// Standard optimization options allow the person running `zig build` to select
14
17
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15
18
// set a preferred release mode, allowing the user to decide how to optimize.
16
19
const optimize = b .standardOptimizeOption (.{});
20
+ // It's also possible to define more custom flags to toggle optional features
21
+ // of this build script using `b.option()`. All defined flags (including
22
+ // target and optimize options) will be listed when running `zig build --help`
23
+ // in this directory.
17
24
18
- // This creates a " module" , which represents a collection of source files alongside
25
+ // This creates a module, which represents a collection of source files alongside
19
26
// some compilation options, such as optimization mode and linked system libraries.
20
- // Every executable or library we compile will be based on one or more modules.
21
- const lib_mod = b .createModule (.{
22
- // `root_source_file` is the Zig "entry point" of the module. If a module
23
- // only contains e.g. external object files, you can make this `null`.
24
- // In this case the main source file is merely a path, however, in more
25
- // complicated build scripts, this could be a generated file.
27
+ // Zig modules are the preferred way of making Zig code available to consumers.
28
+ // addModule defines a module that we intend to make available for importing
29
+ // to our consumers. We must give it a name because a Zig package can expose
30
+ // multiple modules and consumers will need to be able to specify which
31
+ // module they want to access.
32
+ const mod = b .addModule (".NAME" , .{
33
+ // The root source file is the "entry point" of this module. Users of
34
+ // this module will only be able to access public declarations contained
35
+ // in this file, which means that if you have declarations that you
36
+ // intend to expose to consumers that were defined in other files part
37
+ // of this module, you will have to make sure to re-export them from
38
+ // the root file.
26
39
.root_source_file = b .path ("src/root.zig" ),
40
+ // Later on we'll use this module as the root module of a test executable
41
+ // which requires us to specify a target.
27
42
.target = target ,
28
- .optimize = optimize ,
29
- });
30
-
31
- // We will also create a module for our other entry point, 'main.zig'.
32
- const exe_mod = b .createModule (.{
33
- // `root_source_file` is the Zig "entry point" of the module. If a module
34
- // only contains e.g. external object files, you can make this `null`.
35
- // In this case the main source file is merely a path, however, in more
36
- // complicated build scripts, this could be a generated file.
37
- .root_source_file = b .path ("src/main.zig" ),
38
- .target = target ,
39
- .optimize = optimize ,
40
- });
41
-
42
- // Modules can depend on one another using the `std.Build.Module.addImport` function.
43
- // This is what allows Zig source code to use `@import("foo")` where 'foo' is not a
44
- // file path. In this case, we set up `exe_mod` to import `lib_mod`.
45
- exe_mod .addImport (".NAME_lib" , lib_mod );
46
-
47
- // Now, we will create a static library based on the module we created above.
48
- // This creates a `std.Build.Step.Compile`, which is the build step responsible
49
- // for actually invoking the compiler.
50
- const lib = b .addLibrary (.{
51
- .linkage = .static ,
52
- .name = ".NAME" ,
53
- .root_module = lib_mod ,
54
43
});
55
44
56
- // This declares intent for the library to be installed into the standard
57
- // location when the user invokes the "install" step (the default step when
58
- // running `zig build`).
59
- b .installArtifact (lib );
60
-
61
- // This creates another `std.Build.Step.Compile`, but this one builds an executable
62
- // rather than a static library.
45
+ // Here we define an executable. An executable needs to have a root module
46
+ // which needs to expose a `main` function. While we could add a main function
47
+ // to the module defined above, it's sometimes preferable to split business
48
+ // business logic and the CLI into two separate modules.
49
+ //
50
+ // If your goal is to create a Zig library for others to use, consider if
51
+ // it might benefit from also exposing a CLI tool. A parser library for a
52
+ // data serialization format could also bundle a CLI syntax checker, for example.
53
+ //
54
+ // If instead your goal is to create an executable, consider if users might
55
+ // be interested in also being able to embed the core functionality of your
56
+ // program in their own executable in order to avoid the overhead involved in
57
+ // subprocessing your CLI tool.
58
+ //
59
+ // If neither case applies to you, feel free to delete the declaration you
60
+ // don't need and to put everything under a single module.
63
61
const exe = b .addExecutable (.{
64
62
.name = ".NAME" ,
65
- .root_module = exe_mod ,
63
+ .root_module = b .createModule (.{
64
+ // b.createModule defines a new module just like b.addModule but,
65
+ // unlike b.addModule, it does not expose the module to consumers of
66
+ // this package, which is why in this case we don't have to give it a name.
67
+ .root_source_file = b .path ("src/main.zig" ),
68
+ // Target and optimization levels must be explicitly wired in when
69
+ // defining an executable or library (in the root module), and you
70
+ // can also hardcode a specific target for an executable or library
71
+ // definition if desireable (e.g. firmware for embedded devices).
72
+ .target = target ,
73
+ .optimize = optimize ,
74
+ // List of modules available for import in source files part of the
75
+ // root module.
76
+ .imports = &.{
77
+ // Here ".NAME" is the name you will use in your source code to
78
+ // import this module (e.g. `@import(".NAME")`). The name is
79
+ // repeated because you are allowed to rename your imports, which
80
+ // can be extremely useful in case of collisions (which can happen
81
+ // importing modules from different packages).
82
+ .{ .name = ".NAME" , .module = mod },
83
+ },
84
+ }),
66
85
});
67
86
68
87
// This declares intent for the executable to be installed into the
69
- // standard location when the user invokes the "install" step (the default
70
- // step when running `zig build`).
88
+ // install prefix when running `zig build` (i.e. when executing the default
89
+ // step). By default the install prefix is `zig-out/` but can be overridden
90
+ // by passing `--prefix` or `-p`.
71
91
b .installArtifact (exe );
72
92
73
- // This *creates* a Run step in the build graph, to be executed when another
74
- // step is evaluated that depends on it. The next line below will establish
75
- // such a dependency.
93
+ // This creates a top level step. Top level steps have a name and can be
94
+ // invoked by name when running `zig build` (e.g. `zig build run`).
95
+ // This will evaluate the `run` step rather than the default step.
96
+ // For a top level step to actually do something, it must depend on other
97
+ // steps (e.g. a Run step, as we will see in a moment).
98
+ const run_step = b .step ("run" , "Run the app" );
99
+
100
+ // This creates a RunArtifact step in the build graph. A RunArtifact step
101
+ // invokes an executable compiled by Zig. Steps will only be executed by the
102
+ // runner if invoked directly by the user (in the case of top level steps)
103
+ // or if another step depends on it, so it's up to you to define when and
104
+ // how this Run step will be executed. In our case we want to run it when
105
+ // the user runs `zig build run`, so we create a dependency link.
76
106
const run_cmd = b .addRunArtifact (exe );
107
+ run_step .dependOn (& run_cmd .step );
77
108
78
- // By making the run step depend on the install step, it will be run from the
109
+ // By making the run step depend on the default step, it will be run from the
79
110
// installation directory rather than directly from within the cache directory.
80
- // This is not necessary, however, if the application depends on other installed
81
- // files, this ensures they will be present and in the expected location.
82
111
run_cmd .step .dependOn (b .getInstallStep ());
83
112
84
113
// This allows the user to pass arguments to the application in the build
@@ -87,30 +116,42 @@ pub fn build(b: *std.Build) void {
87
116
run_cmd .addArgs (args );
88
117
}
89
118
90
- // This creates a build step. It will be visible in the `zig build --help` menu,
91
- // and can be selected like this: `zig build run`
92
- // This will evaluate the `run` step rather than the default, which is "install".
93
- const run_step = b .step ("run" , "Run the app" );
94
- run_step .dependOn (& run_cmd .step );
95
-
96
- // Creates a step for unit testing. This only builds the test executable
97
- // but does not run it.
98
- const lib_unit_tests = b .addTest (.{
99
- .root_module = lib_mod ,
119
+ // Creates an executable that will run `test` blocks from the provided module.
120
+ // Here `mod` needs to define a target, which is why earlier we made sure to
121
+ // set the releative field.
122
+ const mod_tests = b .addTest (.{
123
+ .root_module = mod ,
100
124
});
101
125
102
- const run_lib_unit_tests = b .addRunArtifact (lib_unit_tests );
126
+ // A run step that will run the test executable.
127
+ const run_mod_tests = b .addRunArtifact (mod_tests );
103
128
104
- const exe_unit_tests = b .addTest (.{
105
- .root_module = exe_mod ,
129
+ // Creates an executable that will run `test` blocks from the executable's
130
+ // root module. Note that test executables only test one module at a time,
131
+ // hence why we have to create two separate ones.
132
+ const exe_tests = b .addTest (.{
133
+ .root_module = exe .root_module ,
106
134
});
107
135
108
- const run_exe_unit_tests = b .addRunArtifact (exe_unit_tests );
109
-
110
- // Similar to creating the run step earlier, this exposes a `test` step to
111
- // the `zig build --help` menu, providing a way for the user to request
112
- // running the unit tests.
113
- const test_step = b .step ("test" , "Run unit tests" );
114
- test_step .dependOn (& run_lib_unit_tests .step );
115
- test_step .dependOn (& run_exe_unit_tests .step );
136
+ // A run step that will run the second test executable.
137
+ const run_exe_tests = b .addRunArtifact (exe_tests );
138
+
139
+ // A top level step for running all tests. dependOn can be called multiple
140
+ // times and since the two run steps do not depend on one another, this will
141
+ // make the two of them run in parallel.
142
+ const test_step = b .step ("test" , "Run tests" );
143
+ test_step .dependOn (& run_mod_tests .step );
144
+ test_step .dependOn (& run_exe_tests .step );
145
+
146
+ // Just like flags, top level steps are also listed in the `--help` menu.
147
+ //
148
+ // The Zig build system is entirely implemented in userland, which means
149
+ // that it cannot hook into private compiler APIs. All compilation work
150
+ // orchestrated by the build system will result in other Zig compiler
151
+ // subcommands being invoked with the right flags defined. You can observe
152
+ // these invocations when one fails (or you pass a flag to increase
153
+ // verbosity) to validate assumptions and diagnose problems.
154
+ //
155
+ // Lastly, the Zig build system is relatively simple and self-contained,
156
+ // and reading its source code will allow you to master it.
116
157
}
0 commit comments