I am building a WASM based web app that does text processing. In it, I use Zig to do all my processing and this also involves using the C PCRE2 library for Regex. While I was able to build a command line based PoC for arm64 darwin, I have not had any luck in building my app for WASM using the Zig build system. Since PCRE2 has a build.zig
in it, it is fairly trivial to add to my project. Here is a very basic example of my build file:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "pcre2_test",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const pcre2_dep = b.dependency("pcre2", .{
.target = target,
.optimize = optimize,
});
exe.linkLibrary(pcre2_dep.artifact("pcre2-8"));
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
But when it comes time to build a wasm32-freestanding
file, the build system is unable to find some C functions that PCRE2 uses. Functions like malloc
, free
, and memchr
. I have not been able to figure out how to fix this.
So I changed my approach to:
- Build PCRE2 for WASM using Emcscripten instead of Zig.
- Use Zig to only build my code to an Object.
- Use
emcc
to complete (link?) the last stage.
This ended up working for me and for now I will grudgingly accept that this is my workflow for the time being.
I am now in the process of trying to unify step 2 and 3 in my build.zig
and my question is this, if I am building and copying objects to an output directory, how do I reference them in a separate step. Please see my new build.zig
file below:
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addObject(.{
.name = "wasm-test",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.addSystemIncludePath(b.path("../emsdk/upstream/emscripten/cache/sysroot/include/"));
exe.linkSystemLibrary("pcre2-8");
exe.addIncludePath(b.path("../pcre2/wasm/include"));
exe.addLibraryPath(b.path("../pcre2/wasm/lib"));
b.getInstallStep().dependOn(&exe.step);
const instDir = b.addInstallDirectory(.{
.source_dir = exe.getEmittedBinDirectory(),
.install_dir = .prefix,
.install_subdir = "wasm-obj",
});
b.getInstallStep().dependOn(&instDir.step);
}
The instDir
step will copy two files wasm-test.o
and wasm-test.o.o
to my zig-out directory and both these files are needed when I use emcc
. I would like to reference them and run the emcc
command from within the Zig build process.
So far from what I have tried by using getPath()
I see a: getPath() was called on a GeneratedFile that wasn't built yet.
error. I am assuming that is because the addObject
is just an intermediate step and so nothing is fully finalized yet? Is this even doable or am I missing some steps in between?