/ learning / zig / zitadel / build.zig
build.zig
  1  const std = @import("std");
  2  
  3  // Although this function looks imperative, it does not perform the build
  4  // directly and instead it mutates the build graph (`b`) that will be then
  5  // executed by an external runner. The functions in `std.Build` implement a DSL
  6  // for defining build steps and express dependencies between them, allowing the
  7  // build runner to parallelize the build automatically (and the cache system to
  8  // know when a step doesn't need to be re-run).
  9  pub fn build(b: *std.Build) void {
 10      // Standard target options allow the person running `zig build` to choose
 11      // what target to build for. Here we do not override the defaults, which
 12      // means any target is allowed, and the default is native. Other options
 13      // for restricting supported target set are available.
 14      const target = b.standardTargetOptions(.{});
 15  
 16      // Standard optimization options allow the person running `zig build` to select
 17      // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
 18      // set a preferred release mode, allowing the user to decide how to optimize.
 19      const optimize = b.standardOptimizeOption(.{});
 20  
 21      // This creates a module, which represents a collection of source files alongside
 22      // some compilation options, such as optimization mode and linked system libraries.
 23      // Zig modules are the preferred way of making Zig code available to consumers.
 24      // addModule defines a module that we intend to make available for importing
 25      // to our consumers. We must give it a name because a Zig package can expose
 26      // multiple modules and consumers will need to be able to specify which
 27      // module they want to access.
 28      const mod = b.addModule("zitadel", .{
 29          // The root source file is the "entry point" of this module. Users of
 30          // this module will only be able to access public declarations contained
 31          // in this file, which means that if you have declarations that you
 32          // intend to expose to consumers that were defined in other files part
 33          // of this module, you will have to make sure to re-export them from
 34          // the root file.
 35          .root_source_file = b.path("src/root.zig"),
 36  
 37          // Having a target here is useful because we will also use this module for tests.
 38          .target = target,
 39          .optimize = optimize,
 40      });
 41  
 42      mod.addIncludePath(b.path("include"));
 43      mod.link_libc = true;
 44  
 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      // 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.
 61      const exe_root = b.createModule(.{
 62          // b.createModule defines a new module just like b.addModule but,
 63          // unlike b.addModule, it does not expose the module to consumers of
 64          // this package, which is why in this case we don't have to give it a name.
 65          .root_source_file = b.path("src/main.zig"),
 66  
 67          // Target and optimization levels must be explicitly wired in when
 68          // defining an executable or library (in the root module), and you
 69          // can also hardcode a specific target for an executable or library
 70          // definition if desireable (e.g. firmware for embedded devices).
 71          .target = target,
 72          .optimize = optimize,
 73  
 74          // List of modules available for import in source files part of the
 75          // root module.
 76          .imports = &.{
 77              // Here "zitadel" is the name you will use in your source code to
 78              // import this module (e.g. `@import("zitadel")`). 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 = "zitadel", .module = mod },
 83          },
 84      });
 85  
 86      const exe = b.addExecutable(.{
 87          .name = "zitadel",
 88          .root_module = exe_root,
 89      });
 90  
 91      // Zig 0.16: include paths are configured on modules, not on the compile step.
 92      // Use include/ if you have C-style headers there; use src/ only if you truly include from src/.
 93      exe.root_module.addIncludePath(b.path("include"));
 94      exe_root.addCSourceFiles(.{
 95          .files = &.{"modules/arithmetic/arithmetic.c"},
 96          .flags = &.{ "-Wall", "-Wextra" },
 97      });
 98      exe.root_module.link_libc = true;
 99  
100      // This declares intent for the executable to be installed into the
101      // install prefix when running `zig build` (i.e. when executing the default
102      // step). By default the install prefix is `zig-out/` but can be overridden
103      // by passing `--prefix` or `-p`.
104      b.installArtifact(exe);
105  
106      // This creates a top level step. Top level steps have a name and can be
107      // invoked by name when running `zig build` (e.g. `zig build run`).
108      // This will evaluate the `run` step rather than the default step.
109      // For a top level step to actually do something, it must depend on other
110      // steps (e.g. a Run step, as we will see in a moment).
111      const run_step = b.step("run", "Run the app");
112  
113      // This creates a RunArtifact step in the build graph. A RunArtifact step
114      // invokes an executable compiled by Zig. Steps will only be executed by the
115      // runner if invoked directly by the user (in the case of top level steps)
116      // or if another step depends on it, so it's up to you to define when and
117      // how this Run step will be executed. In our case we want to run it when
118      // the user runs `zig build run`, so we create a dependency link.
119      const run_cmd = b.addRunArtifact(exe);
120      run_step.dependOn(&run_cmd.step);
121  
122      // By making the run step depend on the default step, it will be run from the
123      // installation directory rather than directly from within the cache directory.
124      run_cmd.step.dependOn(b.getInstallStep());
125  
126      // This allows the user to pass arguments to the application in the build
127      // command itself, like this: `zig build run -- arg1 arg2 etc`
128      if (b.args) |args| {
129          run_cmd.addArgs(args);
130      }
131  
132      // Creates an executable that will run `test` blocks from the provided module.
133      const mod_tests = b.addTest(.{
134          .root_module = mod,
135      });
136  
137      // A run step that will run the test executable.
138      const run_mod_tests = b.addRunArtifact(mod_tests);
139  
140      // Creates an executable that will run `test` blocks from the executable's
141      // root module. Note that test executables only test one module at a time,
142      // hence why we have to create two separate ones.
143      const exe_tests = b.addTest(.{
144          .root_module = exe.root_module,
145      });
146  
147      // exe_tests.root_module.addIncludePath(b.path("include"));
148      // exe_tests.root_module.addCSourceFiles(.{
149      //     .files = &.{"modules/arithmetic/arithmetic.c"},
150      //     .flags = &.{},
151      // });
152  
153      // A run step that will run the second test executable.
154      const run_exe_tests = b.addRunArtifact(exe_tests);
155  
156      // A top level step for running all tests. dependOn can be called multiple
157      // times and since the two run steps do not depend on one another, this will
158      // make the two of them run in parallel.
159      const test_step = b.step("test", "Run tests");
160      test_step.dependOn(&run_mod_tests.step);
161      test_step.dependOn(&run_exe_tests.step);
162  
163      // Just like flags, top level steps are also listed in the `--help` menu.
164      //
165      // The Zig build system is entirely implemented in userland, which means
166      // that it cannot hook into private compiler APIs. All compilation work
167      // orchestrated by the build system will result in other Zig compiler
168      // subcommands being invoked with the right flags defined. You can observe
169      // these invocations when one fails (or you pass a flag to increase
170      // verbosity) to validate assumptions and diagnose problems.
171      //
172      // Lastly, the Zig build system is relatively simple and self-contained,
173      // and reading its source code will allow you to master it.
174  }