Capturing build options in a string

I’m looking for a way to record which options were used to build an executable so I can print it out in debug logs. That way it’ll be easier to replicate builds from bug reports. The reporter can attach a log, and it would document how the build was done. I’m also going to capture the git hash, but I think that’s fairly easy to do from within build.zig by calling out to git if available.

What I’m thinking is something which would record zig build’s command line. I tried to read argv, but the process whilst performing the build isn’t the zig build process I started from the command line. It’s a child of it, and so doesn’t have the relevant information.

Is there something else I could use to get this information?

My current build.zig is simple enough. It’s below, with a lot of options removed just to cut down on size.

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const threads = b.option(u16, "THREADS", "How many threads to use") orelse 1;
    const debug = b.option(bool, "DEBUG", "Enable debug output") orelse false;

    const options = b.addOptions();
    options.addOption(u16, "threads", threads);
    options.addOption(bool, "debug", debug);
    options.addOption([] const u8, "buildString", ???????); // What to pass in here.

    const exe = b.addExecutable(.{
        .name = "blah",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
        .use_llvm = true,
    });
    exe.root_module.addOptions("config", options);

    b.installArtifact(exe);
}

Instead of trying to do it in the build script, you can just look at what config has in your code

inline for (std.meta.declarations(config) |decl| {
    try writer.print("{any}", .{@field(config, decl.name)});
}
3 Likes

You also can use @embedFile if you want to embed the source code of the (generated) config module:

const std = @import("std");

const config = @import("config");
const config_string = @embedFile("config");

pub fn main() !void {
    inline for (comptime std.meta.declarations(config)) |decl| {
        std.debug.print("{s: <10}{any}\n", .{ decl.name ++ ":", @field(config, decl.name) });
    }

    std.debug.print("\nor the config source:\n{s}", .{config_string});
}

Options are just implemented as generated normal modules and when you use embed file you end up embedding the root source file of the corresponding module.

5 Likes

Ok, that certainly gets anything which is driving comptime configuration. It wouldn’t get any of Zig’s own flags though. Probably enough, but I was hoping to get everything.

If I had that source, I guess I could add a build option so that build.zig used it as the source for the options module instead of generating it?

there is @import("builtin") which has a bunch of compilation metadata, eg the target and optimisation.

Most flags are just not useful for you to know, there are a couple that theoretically could be used to drastically change things, but anyone doing that should know they will probably break things. For most cases there is just no reason to, for example, override the build script path or the build runner, or the std lib location.

That is a benefit of @Sze 's suggestion

1 Like