How to have the filewatcher watch non-code files?

Hello, so this is a bit of a strange use case.
I am trying to make a build.zig that compiles a .tex file for me using pdflatex. I chose to use build.zig to just learn a bit about it and make use of its file system watching. Now my problem is that I dont know how to make it watch files that arent exactly source files.

In case anyone knows a project where a build.zig is used for non-zig languages id be really interested in looking at how its done too.

const std = @import("std");

pub fn build(b: *std.Build) !void {
    const build_cmd = b.addSystemCommand(&.{ "pdflatex", "-halt-on-error", "main.tex" });
    b.getInstallStep().dependOn(&build_cmd.step);

    build_cmd.addFileInput(b.path("main.tex"));

    const show_cmd = b.addSystemCommand(&.{ "firefox", "--new-window", "main.pdf" });
    show_cmd.step.dependOn(&build_cmd.step);

    show_cmd.addFileInput(b.path("main.tex"));

    const show = b.step("show", "Open the pdf");
    show.dependOn(&show_cmd.step);
}

Is my current code, but it sadly does not watch any files or directories and i dont know why.
I have found addFileInput here

I believe this is intended to work

I think a better way would be to use addOutputFileArg on the first system command and then use the resulting lazy path in the second system command.

Somewhat like this, but I don’t know if the pdflatex -o command flag is correct:

pub fn build(b: *std.Build) !void {
    const build_cmd = b.addSystemCommand(&.{ "pdflatex", "-halt-on-error", "-o" });
    const pdf = build_cmd.addOutputFileArg("main.pdf");
    
    build_cmd.addFileInput(b.path("main.tex"));

    const show_cmd = b.addSystemCommand(&.{ "firefox", "--new-window"});
    show_cmd.addFileInput(pdf);

    const show = b.step("show", "Open the pdf");
    show.dependOn(&show_cmd.step);
}

I think the lazy paths used as inputs and outputs should be enough to establish dependencies between the steps.

2 Likes

As @Sze said, this works if you let the build system know about both the input(s) and output(s). Here’s an example using addFileArg and addOutputFileArg:

const build_cmd = b.addSystemCommand(&.{ "resinator", "--" });
build_cmd.addFileArg(b.path("test.rc"));
_ = build_cmd.addOutputFileArg("test.res");

From my quick glance at what I think are the pdflatex docs, you might be interested in addOutputDirectoryArg.

Unsure if the input-only version not being watched is a bug. Setting build_cmd.has_side_effects = true didn’t have any effect either.

2 Likes

Can confirm and can confirm!

setting has_side_effects truly did not change anything and giving the build system output file information did the trick. Thank you so much!

1 Like

The addFileArg/addInputFile-only version does seem like it should work, too, though. Not sure what’s up with that.

1 Like

This exact snippet seems to work. Anything else I couldnt manage to get running so far.

const std = @import("std");

pub fn build(b: *std.Build) !void {
    const build_cmd = b.addSystemCommand(&.{ "pdflatex", "-halt-on-error", "main.tex" });
    b.getInstallStep().dependOn(&build_cmd.step);

    build_cmd.addFileInput(b.path("main.tex"));
    _ = build_cmd.addOutputFileArg("main.pdf");

    const show_cmd = b.addSystemCommand(&.{ "firefox", "--new-window", "main.pdf" });
    show_cmd.step.dependOn(&build_cmd.step);

    show_cmd.addFileInput(b.path("main.pdf"));

    const show = b.step("show", "Open the pdf");
    show.dependOn(&show_cmd.step);
}

Judging from here, I think this would be the most “correct” way to go (untested):

const std = @import("std");

pub fn build(b: *std.Build) !void {
    // -jobname=main is just to ensure the output filename is consistent
    const build_cmd = b.addSystemCommand(&.{ "pdflatex", "-halt-on-error", "-jobname=main", "-output-directory" });
    const output_dir = build_cmd.addOutputDirectoryArg("output");
    build_cmd.addFileArg(b.path("main.tex"));

    b.getInstallStep().dependOn(&build_cmd.step);

    const generated_pdf = output_dir.path("main.pdf");
    const show_cmd = b.addSystemCommand(&.{ "firefox", "--new-window" });
    // This will implicitly add a dependency on build_cmd
    show_cmd.addFileArg(generated_pdf);

    const show = b.step("show", "Open the pdf");
    show.dependOn(&show_cmd.step);
}

If you want the pdf to end up installed in some particular location, then you’d want to use b.addInstallFile or b.addInstallFileWithDir and pass it generated_pdf

1 Like