Pass build option to a dependency?

If I have a dependency that can be built in different ways via a b.addOptions entry in its build.zig file, is there a way I can pass that option down from the package that’s importing the dependency?

2 Likes

had exactly the same question, but didn’t manage to tackle it yet… to emphasize, here’s a concrete example where this would be important:

  • my timezone lib allows you to set a custom path to a database, e.g. to use the one on your system (/usr/share/zoneinfo for instance, instead of the one that ships with zdt). Here’s the build option. However, this doesn’t make much sense if an application that uses the library has no way to set that option.
1 Like

try b.addUserInputOption("name", "value");
is the same as invoking the build runner with -Dname=value.

see: addUserInputOption
called from: zig/lib/compiler/build_runner.zig at a685ab1499d6560c523f0dbce2890dc140671e43 · ziglang/zig · GitHub

In the client package build.zig, I do this:

const dep = b.dependency("dep", .{});
_ = dep.builder.addUserInputOption("opt", "val") catch unreachable;

In the dependency package, I have a debug.print to print the value of the option and it remains at its default, so it’s like this isn’t actually doing anything?

Do you mean b.option or b.addOptions? Your opening post mentions the latter but your replies suggest you’re asking about the former.

based on what I tried so far, it seems that

fails to find the option specified in the dependency and does nothing, whereas

fails with thread 16836 panic: Option 'prefix-tzdb' declared twice, i.e. finds the option but tries to create it (?) instead of using/configuring it.


edit: build option in dependency, attempt to set that option in toy app

Ok, I tried to reduce this by makind two small zig projects, mylib which has in its build.zig:

    const sub = b.option(bool, "sub", "Use sub, not add.") orelse false;
    const options = b.addOptions();
    options.addOption(bool, "sub", sub);

    const op_mod = b.addModule("op", .{
        .root_source_file = .{ .path = "src/op.zig" },
    });
    op_mod.addOptions("options", options);

This makes mylib either use add or sub as an operation.

Then myapp has in its build.zig:

    const mylib = b.dependency("mylib", .{});
    _ = mylib.builder.addUserInputOption("sub", "true") catch unreachable;

With or without the addUserInputOption line, the result is the same, mylib is built as if -Dsub is never passed. I also tried addUserInputFlag given this is just a bool flag, but same result.

Edit: I have a test in mylib that proves the option is working when building mylib directly.

test "Test the option" {
    if (options.sub)
        try std.testing.expect(op(3, 7) == -4)
    else
        try std.testing.expect(op(3, 7) == 10);
}

Here’s the sample:
depopt.tgz (2.3 KB)

I’ve seen an example of this in the mach project. You can pass user options of the dependency as part of the args struct:

const mylib = b.dependency("mylib", .{ .sub = true });
3 Likes

myapp should set the option in it’s dependency call:

const mylib = b.dependency("mylib", .{
    .sub = true,
});

Took me ages to figure this out because it’s sort of implicit and there is no explicit function you call.

2 Likes

that seems to work for true/false flags, but not strings: error: option 'prefix_tzdb' has unsupported type: *const [19:0]u8

In fact, this allows me to pass a boolean to an option that takes a []const u8, which makes no sense?

Maybe that’s because of the default string litereal type in Zig being a pointer to an array and not a slice. What if you try .{ .prefix_tzdb = @as([]const u8, "foo") }?

1 Like

@sea-grass , @neurocyte : It works! I’ll mark a solution once @FObersteiner 's issue is solved too.

std.Build.addUserInputOption is an internal API that is called by build_runner.zig when parsing -Dfoo=bar options, so it’s not meant to be called by user code and will not work with dependencies because their build function has already been run and their option values resolved.

Like you’ve already discovered, you pass build options to dependencies via the anonymous args struct instance passed to b.dependency. The arguments struct is converted to an internal map of option names and values via the private function userInputOptionsFromArgs, which as you can see from the source code currently only accepts strings typed as []const u8 and does not accept the default pointer-to-array type that string literals are typed as (*const [n:0]u8). (Note also that this internal map of options is just a std.StringHashMap, so this is why you can pass booleans to options declared as strings.)

If anyone new would like to contribute to the Zig code base, extending this function to accept strings that are not explicitly typed as []const u8 would be an excellent first contribution.

2 Likes

jup, that’s it, nice :slight_smile:

1 Like