How to use zig build --watch to rerun a system command

The aim here is to use shadercross to compile shaders into the various formats needed by the library being built. I have a set of addSystemCommand calls that compile each shader to each format and installs the compiled files in the right place. The only thing that is missing is that it only runs the shadercross step once when zig build --watch is first run, it never runs it again when files change (neither zig files or the shader source files).

The revelvant code is here: gamedev-playground/examples/cube/build.zig at 172ca1bb018c32f421eeeb8a2c7fc1f18fd38033 · zoeesilcock/gamedev-playground · GitHub

I am using addFileArg and addOutputFileArg to make sure that zig is aware of both the source file and the compiled result. I also added has_side_effects = true just in case. I have tried various dependsOn structures to try and force it to rerun the shadercross step every time watch mode sees a change but nothing I have found makes that work.

Does anyone know of a way to force it to rerun the addSystemCommand calls on each rebuild?

It’s also unclear if it will see changes to the source hlsl files as cause to rebuild. Does watch mode only consider zig files included in the module when looking for changes?

1 Like

I found that I can use b.getInstallStep().addWatchInput(...); to add the source shader files to the watcher. This causes some kind of rerun, but not of the addSystemCommand. The output when changing the shader source file is then:

Build Summary: 26/26 steps succeeded

You can use --summary all to get more detailed output that should tell you if it reruns something or not.

Have you checked the outputted files to see if they are being updated?

1 Like

Thanks, the --summary all arg helped me debug this much faster than manually checking the resulting files each time. It turns out I was very close, I just had to change which step I used the addWatchInput on. When I used it on the addSystemCommand step rather than the step that depends on the addSystemCommand it worked:

var compile_shader = b.addSystemCommand(&.{"shadercross"});
// ...
try compile_shader.step.addWatchInput(b.path("src/shaders/" ++ shader ++ ".hlsl"));

Now the shaders are re-compiled every time any change is made in the source files, and since I already had my own watch which recreates the pipeline when the compiled shaders change I also get instant visual feedback! I’m very pleased with the result :smile:

3 Likes

Kinda seems like we should be adding that watch input for you since it’s communicated to the build system as an input for the command. Maybe open an issue on ziglang/zig for discussion at least.

2 Likes

Yeah, that was the impression I got from reading about addFileArg. I’ll put together an issue about.

2 Likes

the add[File/Dir]Arg even document that they cause it to be watched, but only generated paths are in the implementation, conversely addWatchInput doesnt implement for generated paths, though thats intentional as they work differently, though it is a weird decision.

1 Like

As I was preparing the issue I realized that after removing the has_side_effects = true; from the addSystemCommand I no longer need the explicit addWatchInput. I must have done both changes at once and assumed it was the addWatchInput that solved it, my bad.

1 Like

Reminds me that I need to check if I can get watch mode to work with sokol-zig’s shader pipeline. The difference there is that the shader compiler (sokol-shdc) outputs .zig files which then are imported as file or module. E.g. this would need to work:

.glsl input file is changed => kicks of sokol-shdc which produces a .zig output file => which in turn needs to recompile some other zig file(s) which import the shader compiler output file either as a module or as a .zig file.

But maybe this scenario is even simpler than cuasing a re-generated .hlsl source file to trigger a rebuild of zig sources…

1 Like