Capturing build arguments (0.16 porting)

I was doing something a bit off-piste in one of my build.zig file, and I’m trying to work out how to do it in 0.16. Basically I want the program to be able to report how it was built (for bug reproduction reasons), and so I capture the command line arguments and pass them as a string to the program.

pub fn build(b: *std.Build) !void {

    var argv: [][]const u8 = try b.allocator.alloc([]const u8, std.os.argv.len);
    defer b.allocator.free(argv);

    // Convert each argument into a slice
    for (std.os.argv, 0..) |x, i| {
        argv[i] = std.mem.span(x);
    }

    // The interesting arguments seem to start at number 9.
    // Join them into a single string for simplicity.
    const build_cmd: []const u8 = try std.mem.join(b.allocator, " ", argv[9..]);

    const options = b.addOptions();
    options.addOption([]const u8, "COMPILER_FLAGS", build_cmd);
...
}

It’s hacky, but it did the job. I then can print them out at run-time:

...
    try stdout.print("Compiler version : {s}\n", .{builtin.zig_version_string});
    try stdout.print("Compiler flags   : {s}\n", .{options.COMPILER_FLAGS});
...

I’m suspecting that now arguments are part of std.Process.Init.Minimal,std.os.argv no longer exists, and std.Build doesn’t contain a std.Io or a std.Process.Init… this isn’t going to be easy.

Any suggestions on how I can embed how the executable was built inside the executable?

3 Likes

You probably can’t capture all build arguments exactly as passed to zig build.
But @import("builtin") gives you access to compile-time variables (target, optimize mode, etc.), which might be enough depending on what you need.

The b.graph.io field may be useful here, since it gives you access to I/O in the build context.

1 Like

Adding to @ktz_alias’s answer: any custom flags you want to include can be added to a build options import like you were already doing

The goal is to be able to reproduce a build as precisely as I can from the information logged. I’d have the compiler version, and the code version (Git hash + whether it was modified). Certainly the information in builtin need to be part of the set, but if (for example) somebody has specified --fork to override a dependency then I’d like to capture that. If I could get the command line then that is guaranteed to get everything.

There’s no supported way to detect how zig build was invoked (e.g. whether --fork was used).

If you need to detect that kind of thing, it’s usually better handled at the CI/logging layer rather than inside build.zig.