|
| 1 | +--- |
| 2 | +author: "Stephen Gutekanst" |
| 3 | +title: "Zig tips: v0.11 std.build API / package manager changes" |
| 4 | +date: "2023-02-13" |
| 5 | +draft: false |
| 6 | +categories: |
| 7 | +- zig |
| 8 | +- zigtips |
| 9 | +description: "With Zig v0.11 will come several changes to the std.build API. We've just updated Mach to the latest nightly Zig version, and wanted to provide some tips on how to update your own Zig code." |
| 10 | +images: ["https://user-images.githubusercontent.com/3173176/218508235-43e18733-d18d-428a-8475-737f804f590c.png"] |
| 11 | +--- |
| 12 | + |
| 13 | +We've just updated [Mach engine](https://machengine.org/) to use the latest Zig nightly version, which includes a fair amount of improvements and breaking changes to the `std.build` API used in `build.zig` files, and figured now would be a good time to share the general changes you may need to make if you want to update your own code. |
| 14 | + |
| 15 | +## Package manager: incoming! |
| 16 | + |
| 17 | +Zig is finally starting to see its package manager and build system shape up, some notable mentions: |
| 18 | + |
| 19 | +* `std.http.Client` and `std.crypto.tls` were added ([#13980](https://github.com/ziglang/zig/pull/13980)) |
| 20 | +* The package manager MVP landed almost a month ago and has seen steady improvements since ([#14265](https://github.com/ziglang/zig/pull/14265)) |
| 21 | +* Zig packages can now expose C headers are part of their public API ([#14449](https://github.com/ziglang/zig/pull/14449)) |
| 22 | +* Transitive dependencies are now handled better ([#14392](https://github.com/ziglang/zig/pull/14392)) |
| 23 | +* "zig build: The breakings will continue until morale improves." ([#14498](https://github.com/ziglang/zig/pull/14498)) |
| 24 | +* Zig Object Notation (ZON, an alternative to JSON) was introduced ([#14523](https://github.com/ziglang/zig/pull/14523)) |
| 25 | +* The caching system is being moved from the compiler to the std lib to start using it in the bulid system ([#14571](https://github.com/ziglang/zig/pull/14571)) |
| 26 | +* Zig plans to run the build system in a sandboxed WASM environment ([#14286](https://github.com/ziglang/zig/issues/14286)) |
| 27 | + |
| 28 | +You can get an overview of progress on the package manager on this [GitHub project board](https://github.com/ziglang/zig/projects/4) |
| 29 | + |
| 30 | +Mach isn't yet using the new package manager: it's improving rapidly, and we plan to make use of it soon, but things are still changing so we've held off for now. What we have done, though, is updated to the latest API and want to share those changes with you. |
| 31 | + |
| 32 | +## Release options have been renamed to optimization |
| 33 | + |
| 34 | +Previously you would've used `b.standardReleaseOptions()` which would provide your `zig build` command with multiple options like `zig build -Drelease-fast=true`, `zig build -Drelease-safe=true`, etc. |
| 35 | + |
| 36 | +It's been renamed to `b.standardOptimizeOption(.{})` and now exposes a single build option `zig build -Doptimize=ReleaseFast`, `zig build -Doptimize=ReleaseSafe`, etc. instead. |
| 37 | + |
| 38 | +```diff |
| 39 | +-pub fn build(b: *std.build.Builder) void { |
| 40 | +- const mode = b.standardReleaseOptions(); |
| 41 | +- const target = b.standardTargetOptions(.{}); |
| 42 | ++pub fn build(b: *std.Build) void { |
| 43 | ++ const optimize = b.standardOptimizeOption(.{}); |
| 44 | ++ const target = b.standardTargetOptions(.{}); |
| 45 | +``` |
| 46 | + |
| 47 | +```diff |
| 48 | +-mode: std.builtin.Mode |
| 49 | ++optimize: std.builtin.OptimizeMode |
| 50 | +``` |
| 51 | + |
| 52 | +```diff |
| 53 | +-step.build_mode |
| 54 | ++step.optimize |
| 55 | +``` |
| 56 | + |
| 57 | +## Creating tests, libraries, and executables |
| 58 | + |
| 59 | +Creating tests, libraries, and executables now takes a struct with options as the parameter instead of using a setter API: |
| 60 | + |
| 61 | +```diff |
| 62 | +-const exe = b.addExecutable("example", "src/main.zig"); |
| 63 | +-exe.setBuildMode(mode); |
| 64 | +-exe.setTarget(target); |
| 65 | ++const exe = b.addExecutable(.{ |
| 66 | ++ .name = "example", |
| 67 | ++ .root_source_file = "src/main.zig", |
| 68 | ++ .target = target, |
| 69 | ++ .optimize = optimize, |
| 70 | ++}); |
| 71 | +``` |
| 72 | + |
| 73 | +<details> |
| 74 | +<summary>See more examples</summary> |
| 75 | + |
| 76 | +Tests: |
| 77 | + |
| 78 | +```diff |
| 79 | +-const main_tests = b.addTestExe("glfw-tests", sdkPath("/src/main.zig")); |
| 80 | +-main_tests.setBuildMode(mode); |
| 81 | +-main_tests.setTarget(target); |
| 82 | ++const main_tests = b.addTest(.{ |
| 83 | ++ .name = "glfw-tests", |
| 84 | ++ .kind = .test_exe, |
| 85 | ++ .root_source_file = .{ .path = sdkPath("/src/main.zig") }, |
| 86 | ++ .target = target, |
| 87 | ++ .optimize = optimize, |
| 88 | ++}); |
| 89 | +``` |
| 90 | + |
| 91 | +Shared libraries: |
| 92 | + |
| 93 | +```diff |
| 94 | +-const lib = b.addSharedLibrary("glfw", null, .unversioned) |
| 95 | +-lib.setTarget(target); |
| 96 | +-lib.setBuildMode(mode); |
| 97 | ++b.addSharedLibrary(.{ .name = "glfw", .target = target, .optimize = optimize }) |
| 98 | +``` |
| 99 | + |
| 100 | +```diff |
| 101 | +-const lib = b.addSharedLibrary("machcore", "src/platform/libmachcore.zig", .unversioned); |
| 102 | +-lib.setTarget(target); |
| 103 | +-lib.setBuildMode(mode); |
| 104 | ++const lib = b.addSharedLibrary(.{ |
| 105 | ++ .name = "machcore", |
| 106 | ++ .root_source_file = "src/platform/libmachcore.zig", |
| 107 | ++ .target = target, |
| 108 | ++ .optimize = optimize |
| 109 | ++}); |
| 110 | +``` |
| 111 | + |
| 112 | +Static libraries: |
| 113 | + |
| 114 | +```diff |
| 115 | +-const lib = b.addStaticLibrary("basisu-transcoder", null); |
| 116 | +-lib.setTarget(target); |
| 117 | +-lib.setMode(mode); |
| 118 | ++const lib = b.addStaticLibrary(.{ |
| 119 | ++ .name = "basisu-transcoder", |
| 120 | ++ .target = target, |
| 121 | ++ .optimize = optimize, |
| 122 | ++}); |
| 123 | +``` |
| 124 | + |
| 125 | +</details> |
| 126 | + |
| 127 | +## Renamings |
| 128 | + |
| 129 | +* `std.build.LibExeObjStep` has been renamed to just `std.build.CompileStep` (beautiful!) |
| 130 | +* `*std.build.Builder` has been renamed to just `*std.Build` (nice, this is used extensively everywhere!) |
| 131 | + |
| 132 | +## Modules |
| 133 | + |
| 134 | +Units of code you `@import("foo")` (previously known as _packages_) are now known as _modules_, and _packages_ now refers to a piece of code you download/depend on using the Zig package manager. _Libraries_ is reserved for referring to C-style libraries, `.dll`s, etc. |
| 135 | + |
| 136 | +These units of code used to be declared as a `std.build.Pkg` struct: |
| 137 | + |
| 138 | +```zig |
| 139 | +pub const my_pkg = std.build.Pkg{ |
| 140 | + .name = "earcut", |
| 141 | + .source = .{ .path = "src/main.zig" }, |
| 142 | +}; |
| 143 | +``` |
| 144 | + |
| 145 | +And added as a dependency using e.g. `exe.addPackage(my_pkg);` |
| 146 | + |
| 147 | +Now, these are called _modules_ and can be created in a few ways. One is using `b.createModule`: |
| 148 | + |
| 149 | +```zig |
| 150 | +const my_module = b.createModule(.{ |
| 151 | + .source_file = .{ .path = "src/main.zig" }, |
| 152 | + .dependencies = &.{ |
| 153 | + .{ .name = "core", .module = core.module(b) }, |
| 154 | + .{ .name = "ecs", .module = ecs.module(b) }, |
| 155 | + .{ .name = "sysaudio", .module = sysaudio.module(b) }, |
| 156 | + }, |
| 157 | +}); |
| 158 | +``` |
| 159 | + |
| 160 | +And then depend on that module using e.g. `exe.addModule("earcut", my_module);` |
| 161 | + |
| 162 | +Notably, modules are created at _runtime_ via the `*std.Build` now - so you may have some reworking to do if you previously depended on `std.build.Pkg` being a global constant you could rely on at comptime. |
| 163 | + |
| 164 | +Another option, which may be preferred, is via [`addModule`](https://github.com/ziglang/zig/blob/fc48467a97021cb872ff2a947f96e882274c39c1/lib/std/Build.zig#L547-L558). It will make the module available to other packages which depend on this package. |
| 165 | + |
| 166 | +You may also like to know that a _pair of dependency name + the module_ can be represented as [`std.Build.ModuleDependency`](https://github.com/ziglang/zig/blob/fc48467a97021cb872ff2a947f96e882274c39c1/lib/std/Build.zig#L560-L563) now. |
| 167 | + |
| 168 | +We've just gone for an initial 1:1 translation in our code, but adoption of the package manager will likely mean structuring your code a bit differently than the above, and the package manager is still a work-in-progress. |
| 169 | + |
| 170 | +## Thanks for reading |
| 171 | + |
| 172 | +As we work towards Mach v0.2, we're getting more serious about what _stability_ means for us. Our intent is to enable us to move quickly, while also helping you to update your code. We will be achieving this through articles like this which help you understand & update your code to the latest APIs. Hopefully this has helped you! You can find other _zig: Tips_ [here](/categories/zigtips/). |
| 173 | + |
| 174 | +<img align="left" style="max-height: 150px;" src="https://user-images.githubusercontent.com/3173176/187348488-0b52e87d-3a48-421c-9402-be78e32b5a20.png"></img> |
| 175 | +Be sure to join the [Mach engine Discord](https://discord.gg/XNG3NZgCqp) where we're building the future of Zig game development. |
| 176 | +<br><br> |
| 177 | +You can also [sponsor my work](https://github.com/sponsors/slimsag) if you like what I'm doing! :) |
0 commit comments