Build tests with dependency on executable

I am building an emulator executable which itself @embed()s an app binary produced by another executable target in build.zig

To do this, I have the following - which is working

const exe = b.addExecutable(.{ // the main emulator executable
...
};

const app_exe = b.addExecutable(.{ // the @embed() app binary executable
...
};

exe.step.dependOn(app_exe.step);

However, I also need to build app_exe before tests run. Which step do I need to make dependOn(app_exe.step)?

This is what I’ve tried, but the app_exe is not being built before the tests.

const exe_tests = b.addTest(.{
    .root_module = exe.root_module,
});

const run_exe_tests = b.addRunArtifact(exe_tests);

const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
test_step.dependOn(&run_exe_tests.step);

exe_tests.step.dependOn(app_exe.step);

Could you provide more details, how are you currently using @embedFile()? What does your @embedFile() statement look like in the code that uses it?

Apologies, I’m actually trying to readFilleAlloc()

const wasm_data = try std.fs.cwd().readFileAlloc(gpa, "zig-out/bin/app.wasm", 1024*1024);

Which works fine when the final main executable gets built, but perhaps it’s squirreled away somewhere else when built for tests?

I’ve just found it in ./.zig-cache/o/2eb7cc7b65b8f71e32de2206f19aa438/app.wasm, so I guess the question is how do I find that at test time?

@embedFile() works like @import() in that you can embed both by file path and module import name, so if you want to use @embedFile() you could do something like this in your build.zig:

// build.zig
const app_exe = b.addExecutable(...);
const exe = b.addExecutable(...);

exe.root_module.addAnonymousImport("app", .{
    .root_source_file = app_exe.getEmittedBin(),
});

// in your emulator code
const wasm_data = @embedFile("app");

However, note that this will quickly cause your .zig-cache to balloon in size because each new emulator binary you build will contain a whole copy of the wasm binary. So I think locating the wasm binary at runtime is the overall better approach.

It’s hard to know when you’ve only shared a snippet of your build script, but I suspect that the problem is that your test exe doesn’t depend on any step that installs the wasm binary to zig-out/bin/app.wasm. You need to have run_exe_tests depend on either b.getInstallStep() or &b.addInstallArtifact(app_exe, .{}).step.

I don’t think hard-coding the emulator to look for zig-out/bin/app.wasm is a good approach. In my opinion more sound solution would be to take the path to the wasm binary as a command-line argument. You can set up your run_exe step to pass app_exe.getEmittedBin() as an argument to the executable.

But, because zig test/b.addTest() uses a test runner which by default completely hijacks the command-line arguement handling (and rejects any arguments it doesn’t understand), in order to use that approach you need to compile the test binary specifying custom test runner which supports arbitrary command-line arguments. Refer to the default test_runner.zig for inspiration.

2 Likes

Don’t have time for the proper reply, but the short, cryptic one is that you should avoid using dependOn as much as possible outside of top-level steps, and instead leverage implicit dependency tracking provided by LazyPaths, e.g.,

1 Like

Check out this thread:

1 Like