Hello.
I have been banging my head at this for the better part of three hours. And I cannot figure out how this is solved.
As a background, I am building a toy C compiler in Zig based on Nora Sandler’s Writing a C Compiler book. And I am trying to make some testing tasks go through build.zig
rather than relying on shell scripts.
The project itself (and build.zig)
lives in a directory different from. The test suite lives in another directory that I want to pass to the command. What I am working towards is calling the command this way zig build eye -- path/to/tests/ --flag
and it would traverse the folder calling bat
on each file, then calling my executable (with --flag
) so I can visually compare on the terminal the C file and its output.
This is the current build.zig
:
const std = @import("std");
pub fn build(b: *std.Build) !void {
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
// this is the target for the Book and my machine.
.target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .macos,
}),
.optimize = switch (b.release_mode) {
.off => .Debug,
.safe => .ReleaseSafe,
else => .ReleaseFast,
},
});
const fmt_step = b.addFmt(.{ .paths = &.{"./src/"} });
const exe = b.addExecutable(.{
.name = "paella",
.root_module = exe_mod,
});
exe.step.dependOn(&fmt_step.step);
b.installArtifact(exe);
{ // `zig build run` command
const run_step = b.step("run", "Run the app");
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| // pass aruments into `zig build run --`
run_cmd.addArgs(args);
run_cmd.step.dependOn(b.getInstallStep());
run_step.dependOn(&run_cmd.step);
}
// two steps omitted here because they work fine.
{ // `zig build eye` command
const eye_step = b.step("eye", "eye test all the files in a given directory");
var closure = b.allocator.create(Closure) catch unreachable;
closure.* = .{ .exe = exe, .step = std.Build.Step.init(.{
.id = .custom,
.name = "inner_eye",
.makeFn = make_eye_step,
.owner = b,
}) };
eye_step.dependOn(&closure.step);
}
}
const Closure = struct {
exe: *std.Build.Step.Compile,
step: std.Build.Step,
};
fn make_eye_step(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const b = step.owner;
const closure: *const Closure = @fieldParentPtr("step", step);
const exe = closure.exe;
const args: []const []const u8 = b.args orelse
&.{ "./c_files/", "--lex" }; // least harmful default;
const lazy = b.path(args[0]);
const path = lazy.getPath3(b, null);
const dir = try path.openDir("", .{ .access_sub_paths = false });
var walker = try dir.walk(b.allocator);
defer walker.deinit();
var prev_run_cmd: ?*std.Build.Step.Run = null;
while (try walker.next()) |entry| {
if (entry.kind == .file and std.mem.endsWith(u8, entry.basename, ".c")) {
const file = entry.path;
const bat = b.addSystemCommand(&.{ "bat", file });
bat.setCwd(lazy);
bat.stdio = .inherit;
if (prev_run_cmd) |c|
bat.step.dependOn(&c.step);
const run_cmd = b.addRunArtifact(exe);
run_cmd.setCwd(lazy);
run_cmd.addArg(file);
run_cmd.addArgs(args[1..]);
run_cmd.stdio = .inherit;
run_cmd.step.dependOn(b.getInstallStep());
run_cmd.step.dependOn(&bat.step);
prev_run_cmd = run_cmd;
}
}
step.dependOn(&prev_run_cmd.?.step);
}
When the loop is in the main function, it runs every time I run zig build
which is … unwanted behaviour. Someone on the Zig discord suggested putting it on a separate function and calling a custom step like that. But now while everything compiles , the commands don’t seem to even run.
What am I missing? I can’t make head or tails of anything in the standard library or the online documentation.
I can just as well create a shell script and run it from build.zig, but I want to make this work within Zig itself.