I am trying to override the log level (std.log) settings normally induced by options like -Doptimize=ReleaseSafe
so that I can, for example, enforce “info” level logging for debug- or release-builds. I tried the approach shown below, but std.options.log_level
is constant. Is there a different way to achieve my goal?
pub fn build(b: *std.Build) void {
if (b.option([]const u8, "loglv", "The log level to use")) |str| {
if (std.meta.stringToEnum(std.log.Level, str)) |lv| {
std.log.info("log level is {any}", .{lv});
// The following line causes a "cannot assign to constant"
// compiler error:
std.options.log_level = lv;
} else {
std.log.err("unsupported log level '{s}'", .{str});
}
}
// ... more build stuff here...
You cannot edit std.options
directly, you have to declare your own for Zig std to find.
First you have to make your desired log_level
visible to your root module (the module you use as .root_module
in your final executable/library) by exposing it as an option like this:
// Create empty list of options
const options = b.addOptions();
// Add your log_level to list
options.addOption(std.log.Level, "log_level", lv);
[...]
// Add list of options to your root module
root_module.addOptions("options", options);
This will expose a module called "options"
that contains your chosen log_level
. However the type of log_level
will not be std.log.Level
, but rather a local copy of the type unique to the "options"
module, so casting by raw value will be necessary.
Then you have to declare your std options in the .root_source_file
of your root module (most likely main.zig
or root.zig
) like this:
// Import options from build.zig
const options = @import("options");
// Declare custom std options
pub const std_options: std.Options = .{
// Cast from options.log.Level to std.log.Level
.log_level = @enumFromInt(@intFromEnum(options.log_level)),
};
Zig std will automatically check your root module for this exact declaration (source code).
Note that the declaration has to be called std_options
for this to work, you can name everything else in this example however you like.
EDIT: added casting (see reply)
2 Likes
I have tried to incorporate your example code, which leads me to the following compiler complaint:
src/root.zig:8:16: error: expected type 'log.Level', found 'options.log.Level'
.log_level = options.log_level,
~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
.zig-cache/c/225d5f6d2da3ea4ecdbb663eec984759/options.zig:1:26: note: enum declared here
pub const @"log.Level" = enum (u2) {
^~~~
/Users/ralph/zig-macos-x86_64-0.15.0-dev.386+2e35fdd03/lib/std/log.zig:78:19: note: enum declared here
pub const Level = enum {
^~~~
error: the following command failed with 1 compilation errors:
[...]
Is there some form of type casting required?
1 Like
Yeah I’m getting the same error (didn’t check whether my code compiles sorry).
It seems like Build.Step.Options
generates options.zig
by serializing complex types to new declarations under the hood and just printing those to the file, so everything that’s more sophisticated than an integer becomes a new type (source code). This is not a problem if you’re using your own types anyways, but for a type from std
it’s a little akward.
You can indeed solve this by casting:
const options = @import("options");
pub const std_options: std.Options = .{
.log_level = @enumFromInt(@intFromEnum(options.log_level)),
};
(I even tested my own code this time :))
2 Likes
Yup, the enum type conversion by way of integers works. I have marked your original response as the solution, but perhaps you could amend the content a bit, to make it a suitable standalone solution?
In any case, thank you for your help!
1 Like
Sure, I’ve included the type casting in my original response.
No problem 
1 Like