Put .version field of build.zig.zon in my executable

Because I want my executable to feel professional AF, I would like to print “my-executable v0.1.2” at the beginning of execution, where “0.1.2” comes from the .version field of my build.zig.zon.

How can I accomplish this?

ATM, importing ZON files requires providing the result type.

It’ll be possible to simply import them after this PR is merged (the example code their demonstrates accomplishing exactly what you’re asking):

1 Like

Even today you can embedFile and find the version substring.

4 Likes

while we wait for the next release, here is one way of doing it that can give you at least major, minor, and patch:

fn getVersionFromZon() std.SemanticVersion {
    const build_zig_zon = @embedFile("build.zig.zon");
    var buffer: [10 * build_zig_zon.len]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const version = std.zon.parse.fromSlice(
        struct { version: []const u8 },
        fba.allocator(),
        build_zig_zon,
        null,
        .{ .ignore_unknown_fields = true },
    ) catch @panic("Invalid build.zig.zon!");
    const semantic_version = std.SemanticVersion.parse(version.version) catch @panic("Invalid version!");
    return std.SemanticVersion{
        .major = semantic_version.major,
        .minor = semantic_version.minor,
        .patch = semantic_version.patch,
        .build = null, // dont return pointers to stack memory
        .pre = null, // dont return pointers to stack memory
    };
}

4 Likes

Just as an example, here’s what the result type of @import("build.zig.zon") can look like:

4 Likes

Just to add on to the previous comments (until the next release when this becomes easier):

Put this in your build.zig:

    const zon_module = b.createModule(.{
        .root_source_file = b.path("build.zig.zon"),
    });
    exe_mod.addImport("zon_mod", zon_module);

Then the import in your code will look like this:

const zon: struct {
    name: enum { <your project name> },
    version: []const u8,
    fingerprint: u64,
    minimum_zig_version: []const u8,
    dependencies: struct {
//   zq: Dependency,
//   list all your dependencies from your build.zig.zon
    },
    paths: []const []const u8,
    const Dependency = struct { url: []const u8, hash: []const u8, lazy: bool = false };
} = @import("zon_mod");

Then you can use zon.version in your code.

A bit late but I saw this post and thought I could share here the full example for newcomers with this issue how I solved this in my little CLI tool here.

In your build.zig create a module referencing your build.zig.zon as root source file, and import it.

// Create and import a module for build.zig.zon, allows using its .version field in the codebase
exe.root_module.addImport("build_zig_zon", b.createModule(.{
    .root_source_file = b.path("build.zig.zon"),
    .target = target,
    .optimize = optimize,
}));

Then e.g. in your main.zig you can simply import it and access the fields. Moreover below you can see how to print the semantic version with the current zig master version (0.16.0-dev.2535+b5bd49460): parse comptime the semver and format it to be able to print to stdout using the new IO (p.s.: unsure if this is the proper/best way of doing this, but gets the job done).

const build_zig_zon = @import("build_zig_zon");

pub fn main(init: process.Init) !void {
    const version = comptime try std.SemanticVersion.parse(build_zig_zon.version);
    const version_string = std.fmt.comptimePrint("{f}\n", .{version});
    try Io.File.stdout().writeStreamingAll(io, version_string);
}
3 Likes

Also joining in on this old topic, but for yet another option, you can read the version in the build.zig and pass it into the program using options

In your build.zig add

const versionString = @import("./build.zig.zon").version;
const version = std.SemanticVersion.parse(versionString) catch @panic("Bad version");

const options = b.addOptions();
options.addOption(std.SemanticVersion, "version", version);

// mod can also be xxx.root_module if you did not save it to its own variable
mod.addImport("options", options.createModule());

Then you can use it from inside your program by doing something like

const std = @import("std");
const options = @import("options");

fn printVersion() void {
    std.debug.print("version: {f}\n", .{options.version});
}

The advantage of this is that you dont have to parse it in your program (even in comptime), there is just a const variable you can use however you want.

3 Likes