Building Golem Components in Zig
Building Golem components having an application manifest is straightforward, just use the golem
command line interface:
golem app build
If the project was created using golem app new
as recommended, the golem app build
command will always work as expected.
The result of the golem app build
command is a WebAssembly component file ready to be uploaded to Golem. It does not have to be specified explicitly, as the golem
tool will automatically find the correct file when doing for example:
golem component add
Under the hood
Building Golem components written in Zig involves a few steps.
If the project was created with golem new
, it already has a build.zig
file that incorporates all the necessary steps to build the component, so it is enough to run:
$ zig build
In details, building the component requires the following steps:
Generate the C bindings from the WIT files
const bindgen = b.addSystemCommand(&.{ "wit-bindgen", "c", "--autodrop-borrows", "yes", "./wit", "--out-dir", "src/bindings" });
Use the WASM32/WASI target
const wasm = b.addExecutable(.{ .name = "main", .root_source_file = b.path("src/main.zig"), .target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .wasi,
}), .optimize = optimize });
Include the generated C bindings
const binding_root = "src/bindings";
var binding_root_dir = try std.fs.cwd().openDir(binding_root, .{ .iterate = true});
defer binding_root_dir.close();
var it = try binding_root_dir.walk(b.allocator);
while (try it.next()) |entry| {
switch (entry.kind) {
.file => {
const path = b.pathJoin(&.{ binding_root, entry.path });
if (std.mem.endsWith(u8, entry.basename, ".c")) {
wasm.addCSourceFile(.{ .file = b.path(path), .flags = &.{} });
} else if (std.mem.endsWith(u8, entry.basename, ".o")) {
wasm.addObjectFile(b.path(path));
}
},
else => continue,
}
}
wasm.addIncludePath(b.path(binding_root));
wasm.linkLibC();
wasm.step.dependOn(&bindgen.step);
Package it into a WASM component
The resulting WASM file is a WebAssembly module, not a component. To be able to use it as a Golem component, use wasm-tools
to package the module as a component:
const adapter = b.option([]const u8, "adapter", "Path to the Golem Tier1 WASI adapter") orelse "adapters/tier1/wasi_snapshot_preview1.wasm";
const out = try std.fmt.allocPrint(b.allocator, "zig-out/bin/{s}", .{wasm.out_filename});
const component = b.addSystemCommand(&.{ "wasm-tools", "component", "new", out, "-o", "zig-out/bin/component.wasm", "--adapt", adapter });
component.step.dependOn(&wasm.step);
b.installArtifact(wasm);
b.getInstallStep().dependOn(&component.step);
Note that the adapters/tier1/wasi_snapshot_preview1.wasm
file is placed in the project's directory when using golem new
to create the new project.
If needed, it can be manually downloaded from https://github.com/golemcloud/golem-wit/blob/main/adapters/tier1/wasi_snapshot_preview1.wasm (opens in a new tab)