Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,13 @@ fn findOption(name: []const u8) ?spec.Option {
return null;
}

fn addBuildOptions(b: *std.Build, module_name: []const u8, option_names: []const []const u8) ?*std.Build.Step.Options {
if (option_names.len == 0) return null;
fn addBuildOptions(
b: *std.Build,
module_name: []const u8,
option_names: []const []const u8,
option_values: []const spec.OptionValue,
) ?*std.Build.Step.Options {
if (option_names.len == 0 and option_values.len == 0) return null;

const options = b.addOptions();
for (option_names) |name| {
Expand All @@ -135,6 +140,30 @@ fn addBuildOptions(b: *std.Build, module_name: []const u8, option_names: []const
}
}

// Injected literal values, for options modules a repo's build.zig would
// normally synthesize (e.g. tigerbeetle's vsr_options).
for (option_values) |ov| {
if (std.mem.eql(u8, ov.kind, "bool")) {
options.addOption(bool, ov.name, ov.bool_value);
} else if (std.mem.eql(u8, ov.kind, "string")) {
options.addOption([]const u8, ov.name, ov.string_value);
} else if (std.mem.eql(u8, ov.kind, "u32")) {
options.addOption(u32, ov.name, ov.u32_value);
} else if (std.mem.eql(u8, ov.kind, "opt_commit")) {
// A ?[40]u8 git commit hash: the 40-char value, or null.
if (ov.commit_value) |commit| {
var buf: [40]u8 = undefined;
if (commit.len != 40) @panic("opt_commit value must be exactly 40 characters");
@memcpy(&buf, commit[0..40]);
options.addOption(?[40]u8, ov.name, buf);
} else {
options.addOption(?[40]u8, ov.name, null);
}
} else {
@panic("unsupported option_value kind");
}
}

_ = module_name;
return options;
}
Expand Down Expand Up @@ -315,7 +344,7 @@ pub fn build(b: *std.Build) void {
.optimize = m.optimize,
});
applyNative(b, mod, m.native);
if (addBuildOptions(b, m.name, m.build_options)) |options| {
if (addBuildOptions(b, m.name, m.build_options, m.option_values)) |options| {
mod.addOptions(m.build_options_import, options);
}
modules.put(m.name, mod) catch unreachable;
Expand Down
12 changes: 12 additions & 0 deletions build_spec_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ test "package imports and artifacts name their package surfaces" {
}
try testing.expect(found);
}
for (m.option_values) |ov| {
try testing.expect(ov.name.len > 0);
// kind is one of the four supported tags; opt_commit is null or 40.
const ok = std.mem.eql(u8, ov.kind, "bool") or
std.mem.eql(u8, ov.kind, "string") or
std.mem.eql(u8, ov.kind, "u32") or
std.mem.eql(u8, ov.kind, "opt_commit");
try testing.expect(ok);
if (std.mem.eql(u8, ov.kind, "opt_commit")) {
if (ov.commit_value) |c| try testing.expect(c.len == 40);
}
}
}
}

Expand Down
31 changes: 31 additions & 0 deletions gen_build_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ pub const PackageArtifact = struct {
backend: ?[]const u8,
};

pub const OptionValue = struct {
name: []const u8,
kind: []const u8,
bool_value: bool,
string_value: []const u8,
u32_value: u32,
commit_value: ?[]const u8,
};

pub const Native = struct {
c_sources: []const []const u8,
include_dirs: []const []const u8,
Expand Down Expand Up @@ -97,6 +106,7 @@ pub const Module = struct {
pkg_imports: []const PackageImport,
pkg_artifacts: []const PackageArtifact,
build_options: []const []const u8,
option_values: []const OptionValue,
build_options_import: []const u8,
native: Native,
optimize: std.builtin.OptimizeMode,
Expand Down Expand Up @@ -272,6 +282,25 @@ def zig_pkg_artifacts(items):
def zig_bool(v):
return 'true' if v else 'false'

def zig_option_values(items):
if not items:
return '&.{}'
rendered = []
for item in items:
kind = item['kind']
commit = item.get('commit')
commit_field = 'null' if (kind != 'opt_commit' or not commit) else zig_string(commit)
rendered.append(
'.{ .name = ' + zig_string(item['name'])
+ ', .kind = ' + zig_string(kind)
+ ', .bool_value = ' + zig_bool(item.get('bool_value', False))
+ ', .string_value = ' + zig_string(item.get('string_value', ''))
+ ', .u32_value = ' + str(int(item.get('u32_value', 0)))
+ ', .commit_value = ' + commit_field
+ ' }'
)
return '&.{ ' + ', '.join(rendered) + ' }'

def zig_native(item):
item = item or {}
return '''.{{
Expand Down Expand Up @@ -315,6 +344,7 @@ for name, m in mods.items():
pkg_imports_str = zig_pkg_imports(m.get('pkg_imports', []))
pkg_artifacts_str = zig_pkg_artifacts(m.get('pkg_artifacts', []))
build_options_str = zig_string_list(m.get('build_options', []))
option_values_str = zig_option_values(m.get('option_values', []))
native_str = zig_native(m.get('native', {}))
print(f''' .{{
.name = {zig_string(name)},
Expand All @@ -330,6 +360,7 @@ for name, m in mods.items():
.pkg_imports = {pkg_imports_str},
.pkg_artifacts = {pkg_artifacts_str},
.build_options = {build_options_str},
.option_values = {option_values_str},
.build_options_import = {zig_string(m.get('build_options_import', 'build-options'))},
.native = {native_str},
.optimize = {opt},
Expand Down
17 changes: 17 additions & 0 deletions schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ package build
backend?: string
}

// A literal, typed value injected into a module's build-options module (the
// one imported under build_options_import). Unlike #Option, which surfaces a
// CLI flag, an #OptionValue is a fixed value the build config supplies so a
// module that does @import("<options>") compiles without the project's own
// build.zig. Used to reproduce generated options like tigerbeetle's
// vsr_options. `kind` selects which typed field is emitted.
#OptionValue: {
name: string
kind: "bool" | "string" | "u32" | "opt_commit"
bool_value: bool | *false
string_value: string | *""
u32_value: uint32 | *0
// opt_commit emits a ?[40]u8: a 40-char hex string, or "" for null.
commit: string | *""
}

#Package: {
url?: string
hash?: string
Expand Down Expand Up @@ -98,6 +114,7 @@ package build
pkg_imports: [...#PackageImport] | *[]
pkg_artifacts: [...#PackageArtifact] | *[]
build_options: [...string] | *[]
option_values: [...#OptionValue] | *[]
build_options_import: string | *"build-options"
native: #Native | *{}

Expand Down
Loading