Skip to content

Commit 5380e81

Browse files
committed
Support passing null to b.dependency()
Both null literals and optionals are supported.
1 parent 00bc72b commit 5380e81

File tree

2 files changed

+146
-79
lines changed

2 files changed

+146
-79
lines changed

lib/std/Build.zig

Lines changed: 101 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -408,104 +408,127 @@ fn createChildOnly(
408408
return child;
409409
}
410410

411-
fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOptionsMap {
412-
var user_input_options = UserInputOptionsMap.init(allocator);
411+
fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap {
412+
var map = UserInputOptionsMap.init(arena);
413413
inline for (@typeInfo(@TypeOf(args)).@"struct".fields) |field| {
414-
const v = @field(args, field.name);
415-
const T = @TypeOf(v);
416-
switch (T) {
417-
Target.Query => {
418-
user_input_options.put(field.name, .{
419-
.name = field.name,
420-
.value = .{ .scalar = v.zigTriple(allocator) catch @panic("OOM") },
421-
.used = false,
422-
}) catch @panic("OOM");
423-
user_input_options.put("cpu", .{
424-
.name = "cpu",
425-
.value = .{ .scalar = v.serializeCpuAlloc(allocator) catch @panic("OOM") },
426-
.used = false,
427-
}) catch @panic("OOM");
428-
},
429-
ResolvedTarget => {
430-
user_input_options.put(field.name, .{
431-
.name = field.name,
432-
.value = .{ .scalar = v.query.zigTriple(allocator) catch @panic("OOM") },
433-
.used = false,
434-
}) catch @panic("OOM");
435-
user_input_options.put("cpu", .{
436-
.name = "cpu",
437-
.value = .{ .scalar = v.query.serializeCpuAlloc(allocator) catch @panic("OOM") },
438-
.used = false,
439-
}) catch @panic("OOM");
440-
},
441-
LazyPath => {
442-
user_input_options.put(field.name, .{
414+
if (field.type == @Type(.null)) continue;
415+
addUserInputOptionFromArg(arena, &map, field, field.type, @field(args, field.name));
416+
}
417+
return map;
418+
}
419+
420+
fn addUserInputOptionFromArg(
421+
arena: Allocator,
422+
map: *UserInputOptionsMap,
423+
field: std.builtin.Type.StructField,
424+
comptime T: type,
425+
/// If null, the value won't be added, but `T` will still be type-checked.
426+
maybe_value: ?T,
427+
) void {
428+
switch (T) {
429+
Target.Query => return if (maybe_value) |v| {
430+
map.put(field.name, .{
431+
.name = field.name,
432+
.value = .{ .scalar = v.zigTriple(arena) catch @panic("OOM") },
433+
.used = false,
434+
}) catch @panic("OOM");
435+
map.put("cpu", .{
436+
.name = "cpu",
437+
.value = .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") },
438+
.used = false,
439+
}) catch @panic("OOM");
440+
},
441+
ResolvedTarget => return if (maybe_value) |v| {
442+
map.put(field.name, .{
443+
.name = field.name,
444+
.value = .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") },
445+
.used = false,
446+
}) catch @panic("OOM");
447+
map.put("cpu", .{
448+
.name = "cpu",
449+
.value = .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") },
450+
.used = false,
451+
}) catch @panic("OOM");
452+
},
453+
LazyPath => return if (maybe_value) |v| {
454+
map.put(field.name, .{
455+
.name = field.name,
456+
.value = .{ .lazy_path = v.dupeInner(arena) },
457+
.used = false,
458+
}) catch @panic("OOM");
459+
},
460+
[]const LazyPath => return if (maybe_value) |v| {
461+
var list = ArrayList(LazyPath).initCapacity(arena, v.len) catch @panic("OOM");
462+
for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena));
463+
map.put(field.name, .{
464+
.name = field.name,
465+
.value = .{ .lazy_path_list = list },
466+
.used = false,
467+
}) catch @panic("OOM");
468+
},
469+
[]const u8 => return if (maybe_value) |v| {
470+
map.put(field.name, .{
471+
.name = field.name,
472+
.value = .{ .scalar = v },
473+
.used = false,
474+
}) catch @panic("OOM");
475+
},
476+
[]const []const u8 => return if (maybe_value) |v| {
477+
var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
478+
list.appendSliceAssumeCapacity(v);
479+
map.put(field.name, .{
480+
.name = field.name,
481+
.value = .{ .list = list },
482+
.used = false,
483+
}) catch @panic("OOM");
484+
},
485+
else => switch (@typeInfo(T)) {
486+
.bool => return if (maybe_value) |v| {
487+
map.put(field.name, .{
443488
.name = field.name,
444-
.value = .{ .lazy_path = v.dupeInner(allocator) },
489+
.value = .{ .scalar = if (v) "true" else "false" },
445490
.used = false,
446491
}) catch @panic("OOM");
447492
},
448-
[]const LazyPath => {
449-
var list = ArrayList(LazyPath).initCapacity(allocator, v.len) catch @panic("OOM");
450-
for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(allocator));
451-
user_input_options.put(field.name, .{
493+
.@"enum", .enum_literal => return if (maybe_value) |v| {
494+
map.put(field.name, .{
452495
.name = field.name,
453-
.value = .{ .lazy_path_list = list },
496+
.value = .{ .scalar = @tagName(v) },
454497
.used = false,
455498
}) catch @panic("OOM");
456499
},
457-
[]const u8 => {
458-
user_input_options.put(field.name, .{
500+
.comptime_int, .int => return if (maybe_value) |v| {
501+
map.put(field.name, .{
459502
.name = field.name,
460-
.value = .{ .scalar = v },
503+
.value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") },
461504
.used = false,
462505
}) catch @panic("OOM");
463506
},
464-
[]const []const u8 => {
465-
var list = ArrayList([]const u8).initCapacity(allocator, v.len) catch @panic("OOM");
466-
list.appendSliceAssumeCapacity(v);
467-
468-
user_input_options.put(field.name, .{
507+
.comptime_float, .float => return if (maybe_value) |v| {
508+
map.put(field.name, .{
469509
.name = field.name,
470-
.value = .{ .list = list },
510+
.value = .{ .scalar = std.fmt.allocPrint(arena, "{e}", .{v}) catch @panic("OOM") },
471511
.used = false,
472512
}) catch @panic("OOM");
473513
},
474-
else => switch (@typeInfo(T)) {
475-
.bool => {
476-
user_input_options.put(field.name, .{
477-
.name = field.name,
478-
.value = .{ .scalar = if (v) "true" else "false" },
479-
.used = false,
480-
}) catch @panic("OOM");
481-
},
482-
.@"enum", .enum_literal => {
483-
user_input_options.put(field.name, .{
484-
.name = field.name,
485-
.value = .{ .scalar = @tagName(v) },
486-
.used = false,
487-
}) catch @panic("OOM");
488-
},
489-
.comptime_int, .int => {
490-
user_input_options.put(field.name, .{
491-
.name = field.name,
492-
.value = .{ .scalar = std.fmt.allocPrint(allocator, "{d}", .{v}) catch @panic("OOM") },
493-
.used = false,
494-
}) catch @panic("OOM");
514+
.null => unreachable,
515+
.optional => |info| switch (@typeInfo(info.child)) {
516+
.optional => {},
517+
else => {
518+
addUserInputOptionFromArg(
519+
arena,
520+
map,
521+
field,
522+
info.child,
523+
maybe_value orelse null,
524+
);
525+
return;
495526
},
496-
.comptime_float, .float => {
497-
user_input_options.put(field.name, .{
498-
.name = field.name,
499-
.value = .{ .scalar = std.fmt.allocPrint(allocator, "{e}", .{v}) catch @panic("OOM") },
500-
.used = false,
501-
}) catch @panic("OOM");
502-
},
503-
else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
504527
},
505-
}
528+
else => {},
529+
},
506530
}
507-
508-
return user_input_options;
531+
@compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(field.type));
509532
}
510533

511534
const OrderedUserValue = union(enum) {

test/standalone/dependency_options/build.zig

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@ pub fn build(b: *std.Build) !void {
1212
if (!none_specified_mod.resolved_target.?.query.eql(b.graph.host.query)) return error.TestFailed;
1313
if (none_specified_mod.optimize.? != .Debug) return error.TestFailed;
1414

15+
// Passing null is the same as not specifying the option,
16+
// so this should resolve to the same cached dependency instance.
17+
const null_specified = b.dependency("other", .{
18+
// Null literals
19+
.target = null,
20+
.optimize = null,
21+
.bool = null,
22+
23+
// Optionals
24+
.int = @as(?i64, null),
25+
.float = @as(?f64, null),
26+
27+
// Optionals of the wrong type
28+
.string = @as(?usize, null),
29+
.@"enum" = @as(?bool, null),
30+
31+
// Non-defined option names
32+
.this_option_does_not_exist = null,
33+
.neither_does_this_one = @as(?[]const u8, null),
34+
});
35+
36+
if (null_specified != none_specified) return error.TestFailed;
37+
1538
const all_specified = b.dependency("other", .{
1639
.target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
1740
.optimize = @as(std.builtin.OptimizeMode, .ReleaseSafe),
@@ -37,6 +60,27 @@ pub fn build(b: *std.Build) !void {
3760
if (all_specified_mod.resolved_target.?.result.abi != .gnu) return error.TestFailed;
3861
if (all_specified_mod.optimize.? != .ReleaseSafe) return error.TestFailed;
3962

63+
const all_specified_optional = b.dependency("other", .{
64+
.target = @as(?std.Build.ResolvedTarget, b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu })),
65+
.optimize = @as(?std.builtin.OptimizeMode, .ReleaseSafe),
66+
.bool = @as(?bool, true),
67+
.int = @as(?i64, 123),
68+
.float = @as(?f64, 0.5),
69+
.string = @as(?[]const u8, "abc"),
70+
.string_list = @as(?[]const []const u8, &.{ "a", "b", "c" }),
71+
.lazy_path = @as(?std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
72+
.lazy_path_list = @as(?[]const std.Build.LazyPath, &.{
73+
.{ .cwd_relative = "a.txt" },
74+
.{ .cwd_relative = "b.txt" },
75+
.{ .cwd_relative = "c.txt" },
76+
}),
77+
.@"enum" = @as(?Enum, .alfa),
78+
//.enum_list = @as(?[]const Enum, &.{ .alfa, .bravo, .charlie }),
79+
//.build_id = @as(?std.zig.BuildId, .uuid),
80+
});
81+
82+
if (all_specified_optional != all_specified) return error.TestFailed;
83+
4084
// Most supported option types are serialized to a string representation,
4185
// so alternative representations of the same option value should resolve
4286
// to the same cached dependency instance.
@@ -59,5 +103,5 @@ pub fn build(b: *std.Build) !void {
59103
//.build_id = @as(std.zig.BuildId, .uuid),
60104
});
61105

62-
if (all_specified != all_specified_alt) return error.TestFailed;
106+
if (all_specified_alt != all_specified) return error.TestFailed;
63107
}

0 commit comments

Comments
 (0)