Zig Build Documentation: understanding "Generating Files" section

Hi,

As Zig newby, I am studying the guide in:

Zig Build System ⚡ Zig Programming Language

In the section " Generating Files: Running System Tools" I can see 2 steps. One is a standard compilation step that generates the exe and the install step depends on it. The other forks the “jq” command line tool, the install step (via the output) also depends on it. So far, so good.

What I don’t get is how the the exe depends on the jq fork command. I think this is only possible if the build system can infer that the exe needs the jq command’s output. But I do not see where this happens.

Can anyone explain this?

TIA

1 Like

Hello @hmf, welcome to Ziggit!

In the Generating Files section, and the Running System Tools subsection, the build.zig example does a few things related to the jq command and it’s output. I’ll do my best to break it down, line by line:

const tool_run = b.addSystemCommand(&.{"jq"});

This tells the build system to create a new Run step, and assigns the step to tool_run. When this step is depended on, it will run the jq command.

tool_run.addArgs(&.{
        b.fmt(
            \\.["{s}"]
        , .{lang}),
        "-r", // raw output to omit quotes around the selected string
    });

This tells the build system to append the arguments to the Run step’s argv. For example, if -Dlang is not passed in the build command, this will be .["en"] -r.

tool_run.addFileArg(b.path("words.json"));

This line is similar to the previous in that it adds an argument to the Run step’s argv, but it’s not just passing a string; instead, the build system will pass a path to the Run step’s argv. Additionally, the file passed to addFileArg will be depended on, and will be tracked by the build system, triggering this step to re-run if there’s a cache miss (i.e. the file changes).

const output = tool_run.captureStdOut();

This line tells the build system that you want to capture the stdout of the Run step after it runs (i.e. jq’s stdout). Note that output holds a LazyPath: this is a path to a file somewhere within the build system’s cache that contains the contents of the Run step’s stdout.

b.getInstallStep().dependOn(&b.addInstallFileWithDir(output, .prefix, "word.txt").step);

This is likely the line that answers your question. However, it could be broken up into two steps for more clarity:

const install_word = b.addInstallFileWithDir(output, .prefix, "word.txt");
b.getInstallStep().dependOn(&install_word.step);

The first line will create an InstallFile. Note that the path of the InstallFile (the first argument) is the output variable, which holds the path of the Run command’s stdout. This “installs” the file into the build directory; the name of the file is specified as the last argument: word.txt.

The second line makes the install step dependent on word.txt, meaning that when the install step runs, it will also run the InstallFile step (and so on, for all dependencies of the DAG).

The rest of the build.zig file is just boilerplate code that builds the exe and installs it. Let me know if you need more clarification on that process.

8 Likes

@weskoerber Thank you for the clear explanation. I cannot help but think that this explanation should be part of the documentation.

Thanks again.

2 Likes