Dan Prince
Dan Prince

Reputation: 30009

zig externs when using zig test for wasm

I have a Zig codebase that I'm compiling to WebAssembly with a build.zig like this:

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
  const mode = b.standardReleaseOptions();
  const lib = b.addSharedLibrary("main", "src/main.zig", b.version(0, 0, 0));
  lib.setBuildMode(mode);
  lib.setTarget(.{.cpu_arch = .wasm32, .os_tag = .freestanding});
  lib.setOutputDir("web/dist");
  b.default_step.dependOn(&lib.step);
}

Inside src/main.zig there's an extern that marks a function provided through the WebAssembly imports.

extern fn printString(message: [*]u8, length: usize) void;

This works fine when running in the browser, but I'm trying to add tests to the project, but whenever I attempt to run zig test on main.zig (or any file with extern) I get the following linker error.

MachO Flush... error(link): undefined reference to symbol '_printString'

It might be worth pointing out that nothing in my test "..." {} blocks actually touches any of these extern functions.

Is there a sane way to link to alternate implementations of these externs during tests? Or any other workarounds that would allow me to write tests?

Upvotes: 0

Views: 689

Answers (1)

Dan Prince
Dan Prince

Reputation: 30009

You can use combine @import("builtin").is_test and usingnamespace to create stubs for extern functions during tests.

const builtin = @import("builtin");

pub usingnamespace if (!builtin.is_test) struct {
  pub extern fn printString(message: [*]u8, length: usize) void;
} else struct {
  pub fn printString(message: [*]u8, length: usize) void {
    _ = message;
    _ = length;
  }
}

pub usingnamespace makes all the members of the struct part of the module's public struct, and during tests we just use a different struct that doesn't rely on any external functions.

Credit to mitchellh/zig-js.

Upvotes: 0

Related Questions