Welcome to the new Golem Cloud Docs! 👋
Experimental Languages
Zig
Building Components

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)