Welcome to the new Golem Cloud Docs! 👋

Zig

Install the latest version (0.12) of Zig (for example brew install zig).

You also have to install everything as described in Common tooling.

The easiest way to get started once the tooling is installed is to use the golem new command as described in the Quickstart.

If you prefer to do it from scratch, first write a Zig program that has an empty main function src/main.zig, and also the implementations of the exported interfaces coming from the WIT file:

package golem:test;
 
interface api {
  add: func(value: u64);
  get: func() -> u64;
}
 
world test {
  export api;
}
const std = @import("std");
 
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
 
const CommandTag = enum { add, get };
 
const Command = union(CommandTag) { add: i32, get: void };
 
var state: u64 = 0;
 
export fn exports_golem_test_api_add(value: u64) void {
    const stdout = std.io.getStdOut().writer();
    stdout.print("Adding {} to state\n", .{value}) catch unreachable;
    state += value;
}
 
export fn exports_golem_test_api_get() u64 {
    return state;
}
 
pub fn main() anyerror!void {
}

Create a Zig build file that incorporates generating C bindings and composing the final Golem component:

const std = @import("std");
const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
 
pub fn build(b: *Builder) !void {
    const optimize = b.standardOptimizeOption(.{
        .preferred_optimize_mode = .ReleaseSmall,
    });
 
    const bindgen = b.addSystemCommand(&.{ "wit-bindgen", "c", "--autodrop-borrows", "yes", "./wit", "--out-dir", "src/bindings" });
 
    const wasm = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "src/main.zig" }, .target = .{
        .cpu_arch = .wasm32,
        .os_tag = .wasi,
    }, .optimize = optimize });
 
    const binding_root = b.pathFromRoot("src/bindings");
    var binding_root_dir = try std.fs.cwd().openIterableDir(binding_root, .{});
    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 = .{ .path = path }, .flags = &.{} });
                } else if (std.mem.endsWith(u8, entry.basename, ".o")) {
                    wasm.addObjectFile(.{ .path = path });
                }
            },
            else => continue,
        }
    }
 
    wasm.addIncludePath(.{ .path = binding_root });
    wasm.linkLibC();
 
    wasm.step.dependOn(&bindgen.step);
 
    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);
}

Make sure there is a src/bindings directory created. Then build the WASM module with:

zig build

The resulting WASM file in zig-out/bin/component.wasm, ready to be used with Golem.

The wasi_snapshot_preview1.wasm describes the mapping from WASI Preview1 to Preview2. You must use the version of this file packaged together with golem-cli.

💡
Make sure you use the tier1 adapter from the golem package!

The resulting component.wasm is ready to be used with Golem.