authorgravatar for 98696261+Reokodoku@users.noreply.github.comReokodoku <98696261+Reokodoku@users.noreply.github.com> 2024-03-18 03:46:54+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-03-18 02:46:54+00:00
log12066042621ca7920d67d339b54c0a2220d40696
tree96542c96c8053bc37e21102b15c7324c7b57ad49
parent64173dadcacdcad9b7f5898fee2b00f2cf177ead
signaturebadge-check Signed by PGP key B5690EEEBB952194

Implementing structs and better enums in build options


1 files changed, 322 insertions(+), 73 deletions(-)

lib/std/Build/Step/Options.zig+322-73
...@@ -41,123 +41,180 @@ pub fn addOption(self: *Options, comptime T: type, name: []const u8, value: T) v...@@ -41,123 +41,180 @@ pub fn addOption(self: *Options, comptime T: type, name: []const u8, value: T) v
4141
42fn addOptionFallible(self: *Options, comptime T: type, name: []const u8, value: T) !void {42fn addOptionFallible(self: *Options, comptime T: type, name: []const u8, value: T) !void {
43 const out = self.contents.writer();43 const out = self.contents.writer();
44 try printType(self, out, T, value, 0, name);
45}
46
47fn printType(self: *Options, out: anytype, comptime T: type, value: T, indent: u8, name: ?[]const u8) !void {
44 switch (T) {48 switch (T) {
45 []const []const u8 => {49 []const []const u8 => {
46 try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)});50 if (name) |payload| {
51 try out.print("pub const {}: []const []const u8 = ", .{std.zig.fmtId(payload)});
52 }
53
54 try out.writeAll("&[_][]const u8{\n");
55
47 for (value) |slice| {56 for (value) |slice| {
57 try out.writeByteNTimes(' ', indent);
48 try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});58 try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});
49 }59 }
50 try out.writeAll("};\n");60
51 return;61 if (name != null) {
52 },62 try out.writeAll("};\n");
53 [:0]const u8 => {63 } else {
54 try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });64 try out.writeAll("},\n");
65 }
66
55 return;67 return;
56 },68 },
57 []const u8 => {69 []const u8 => {
58 try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });70 if (name) |some| {
59 return;71 try out.print("pub const {}: []const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) });
72 } else {
73 try out.print("\"{}\",", .{std.zig.fmtEscapes(value)});
74 }
75 return out.writeAll("\n");
60 },76 },
61 ?[:0]const u8 => {77 [:0]const u8 => {
62 try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)});78 if (name) |some| {
79 try out.print("pub const {}: [:0]const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) });
80 } else {
81 try out.print("\"{}\",", .{std.zig.fmtEscapes(value)});
82 }
83 return out.writeAll("\n");
84 },
85 ?[]const u8 => {
86 if (name) |some| {
87 try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(some)});
88 }
89
63 if (value) |payload| {90 if (value) |payload| {
64 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});91 try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)});
92 } else {
93 try out.writeAll("null");
94 }
95
96 if (name != null) {
97 try out.writeAll(";\n");
65 } else {98 } else {
66 try out.writeAll("null;\n");99 try out.writeAll(",\n");
67 }100 }
68 return;101 return;
69 },102 },
70 ?[]const u8 => {103 ?[:0]const u8 => {
71 try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)});104 if (name) |some| {
105 try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(some)});
106 }
107
72 if (value) |payload| {108 if (value) |payload| {
73 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});109 try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)});
74 } else {110 } else {
75 try out.writeAll("null;\n");111 try out.writeAll("null");
112 }
113
114 if (name != null) {
115 try out.writeAll(";\n");
116 } else {
117 try out.writeAll(",\n");
76 }118 }
77 return;119 return;
78 },120 },
79 std.SemanticVersion => {121 std.SemanticVersion => {
80 try out.print(122 if (name) |some| {
81 \\pub const {}: @import("std").SemanticVersion = .{{123 try out.print("pub const {}: @import(\"std\").SemanticVersion = ", .{std.zig.fmtId(some)});
82 \\ .major = {d},124 }
83 \\ .minor = {d},125
84 \\ .patch = {d},126 try out.writeAll(".{\n");
85 \\127 try out.writeByteNTimes(' ', indent);
86 , .{128 try out.print(" .major = {d},\n", .{value.major});
87 std.zig.fmtId(name),129 try out.writeByteNTimes(' ', indent);
88130 try out.print(" .minor = {d},\n", .{value.minor});
89 value.major,131 try out.writeByteNTimes(' ', indent);
90 value.minor,132 try out.print(" .patch = {d},\n", .{value.patch});
91 value.patch,133
92 });
93 if (value.pre) |some| {134 if (value.pre) |some| {
135 try out.writeByteNTimes(' ', indent);
94 try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});136 try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});
95 }137 }
96 if (value.build) |some| {138 if (value.build) |some| {
139 try out.writeByteNTimes(' ', indent);
97 try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});140 try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});
98 }141 }
99 try out.writeAll("};\n");142
100 return;143 if (name != null) {
101 },
102 else => {},
103 }
104 switch (@typeInfo(T)) {
105 .Enum => |enum_info| {
106 const gop = try self.encountered_types.getOrPut(@typeName(T));
107 if (!gop.found_existing) {
108 try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))});
109 inline for (enum_info.fields) |field| {
110 try out.print(" {},\n", .{std.zig.fmtId(field.name)});
111 }
112 try out.writeAll("};\n");144 try out.writeAll("};\n");
145 } else {
146 try out.writeAll("},\n");
113 }147 }
114 try out.print("pub const {}: {s} = .{s};\n", .{
115 std.zig.fmtId(name),
116 std.zig.fmtId(@typeName(T)),
117 std.zig.fmtId(@tagName(value)),
118 });
119 return;148 return;
120 },149 },
121 else => {},150 else => {},
122 }151 }
123 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) });
124 try printLiteral(out, value, 0);
125 try out.writeAll(";\n");
126}
127152
128// TODO: non-recursive?
129fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
130 const T = @TypeOf(val);
131 switch (@typeInfo(T)) {153 switch (@typeInfo(T)) {
132 .Array => {154 .Array => {
155 if (name) |some| {
156 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
157 }
158
133 try out.print("{s} {{\n", .{@typeName(T)});159 try out.print("{s} {{\n", .{@typeName(T)});
134 for (val) |item| {160 for (value) |item| {
135 try out.writeByteNTimes(' ', indent + 4);161 try out.writeByteNTimes(' ', indent + 4);
136 try printLiteral(out, item, indent + 4);162 try printType(self, out, @TypeOf(item), item, indent + 4, null);
137 try out.writeAll(",\n");
138 }163 }
139 try out.writeByteNTimes(' ', indent);164 try out.writeByteNTimes(' ', indent);
140 try out.writeAll("}");165 try out.writeAll("}");
166
167 if (name != null) {
168 try out.writeAll(";\n");
169 } else {
170 try out.writeAll(",\n");
171 }
172 return;
141 },173 },
142 .Pointer => |p| {174 .Pointer => |p| {
143 if (p.size != .Slice) {175 if (p.size != .Slice) {
144 @compileError("Non-slice pointers are not yet supported in build options");176 @compileError("Non-slice pointers are not yet supported in build options");
145 }177 }
178
179 if (name) |some| {
180 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
181 }
182
146 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});183 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
147 for (val) |item| {184 for (value) |item| {
148 try out.writeByteNTimes(' ', indent + 4);185 try out.writeByteNTimes(' ', indent + 4);
149 try printLiteral(out, item, indent + 4);186 try printType(self, out, @TypeOf(item), item, indent + 4, null);
150 try out.writeAll(",\n");
151 }187 }
152 try out.writeByteNTimes(' ', indent);188 try out.writeByteNTimes(' ', indent);
153 try out.writeAll("}");189 try out.writeAll("}");
190
191 if (name != null) {
192 try out.writeAll(";\n");
193 } else {
194 try out.writeAll(",\n");
195 }
196 return;
154 },197 },
155 .Optional => {198 .Optional => {
156 if (val) |inner| {199 if (name) |some| {
157 return printLiteral(out, inner, indent);200 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
201 }
202
203 if (value) |inner| {
204 try printType(self, out, @TypeOf(inner), inner, indent + 4, null);
205 // Pop the '\n' and ',' chars
206 _ = self.contents.pop();
207 _ = self.contents.pop();
208 } else {
209 try out.writeAll("null");
210 }
211
212 if (name != null) {
213 try out.writeAll(";\n");
158 } else {214 } else {
159 return out.writeAll("null");215 try out.writeAll(",\n");
160 }216 }
217 return;
161 },218 },
162 .Void,219 .Void,
163 .Bool,220 .Bool,
...@@ -165,11 +222,160 @@ fn printLiteral(out: anytype, val: anytype, indent: u8) !void {...@@ -165,11 +222,160 @@ fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
165 .ComptimeInt,222 .ComptimeInt,
166 .Float,223 .Float,
167 .Null,224 .Null,
168 => try out.print("{any}", .{val}),225 => {
226 if (name) |some| {
227 try out.print("pub const {}: {s} = {any};\n", .{ std.zig.fmtId(some), @typeName(T), value });
228 } else {
229 try out.print("{any},\n", .{value});
230 }
231 return;
232 },
233 .Enum => |info| {
234 try printEnum(self, out, T, info, indent);
235
236 if (name) |some| {
237 try out.print("pub const {}: {s} = .{s};\n", .{
238 std.zig.fmtId(some),
239 std.zig.fmtId(@typeName(T)),
240 std.zig.fmtId(@tagName(value)),
241 });
242 }
243 return;
244 },
245 .Struct => |info| {
246 try printStruct(self, out, T, info, indent);
247
248 if (name) |some| {
249 try out.print("pub const {}: {s} = ", .{
250 std.zig.fmtId(some),
251 std.zig.fmtId(@typeName(T)),
252 });
253 try printStructValue(self, out, info, value, indent);
254 }
255 return;
256 },
169 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),257 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
170 }258 }
171}259}
172260
261fn printUserDefinedType(self: *Options, out: anytype, comptime T: type, indent: u8) !void {
262 switch (@typeInfo(T)) {
263 .Enum => |info| {
264 return try printEnum(self, out, T, info, indent);
265 },
266 .Struct => |info| {
267 return try printStruct(self, out, T, info, indent);
268 },
269 else => {},
270 }
271}
272
273fn printEnum(self: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Enum, indent: u8) !void {
274 const gop = try self.encountered_types.getOrPut(@typeName(T));
275 if (gop.found_existing) return;
276
277 try out.writeByteNTimes(' ', indent);
278 try out.print("pub const {} = enum ({s}) {{\n", .{ std.zig.fmtId(@typeName(T)), @typeName(val.tag_type) });
279
280 inline for (val.fields) |field| {
281 try out.writeByteNTimes(' ', indent);
282 try out.print(" {} = {d},\n", .{ std.zig.fmtId(field.name), field.value });
283 }
284
285 if (!val.is_exhaustive) {
286 try out.writeByteNTimes(' ', indent);
287 try out.writeAll(" _,\n");
288 }
289
290 try out.writeByteNTimes(' ', indent);
291 try out.writeAll("};\n");
292}
293
294fn printStruct(self: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Struct, indent: u8) !void {
295 const gop = try self.encountered_types.getOrPut(@typeName(T));
296 if (gop.found_existing) return;
297
298 try out.writeByteNTimes(' ', indent);
299 try out.print("pub const {} = ", .{std.zig.fmtId(@typeName(T))});
300
301 switch (val.layout) {
302 .@"extern" => try out.writeAll("extern struct"),
303 .@"packed" => try out.writeAll("packed struct"),
304 else => try out.writeAll("struct"),
305 }
306
307 try out.writeAll(" {\n");
308
309 inline for (val.fields) |field| {
310 try out.writeByteNTimes(' ', indent);
311
312 const type_name = @typeName(field.type);
313
314 // If the type name doesn't contains a '.' the type is from zig builtins.
315 if (std.mem.containsAtLeast(u8, type_name, 1, ".")) {
316 try out.print(" {}: {}", .{ std.zig.fmtId(field.name), std.zig.fmtId(type_name) });
317 } else {
318 try out.print(" {}: {s}", .{ std.zig.fmtId(field.name), type_name });
319 }
320
321 if (field.default_value != null) {
322 const default_value = @as(*field.type, @ptrCast(@alignCast(@constCast(field.default_value.?)))).*;
323
324 try out.writeAll(" = ");
325 switch (@typeInfo(@TypeOf(default_value))) {
326 .Enum => try out.print(".{s},\n", .{@tagName(default_value)}),
327 .Struct => |info| {
328 try printStructValue(self, out, info, default_value, indent + 4);
329 },
330 else => try printType(self, out, @TypeOf(default_value), default_value, indent, null),
331 }
332 } else {
333 try out.writeAll(",\n");
334 }
335 }
336
337 // TODO: write declarations
338
339 try out.writeByteNTimes(' ', indent);
340 try out.writeAll("};\n");
341
342 inline for (val.fields) |field| {
343 try printUserDefinedType(self, out, field.type, 0);
344 }
345}
346
347fn printStructValue(self: *Options, out: anytype, comptime struct_val: std.builtin.Type.Struct, val: anytype, indent: u8) !void {
348 try out.writeAll(".{\n");
349
350 if (struct_val.is_tuple) {
351 inline for (struct_val.fields) |field| {
352 try out.writeByteNTimes(' ', indent);
353 try printType(self, out, @TypeOf(@field(val, field.name)), @field(val, field.name), indent, null);
354 }
355 } else {
356 inline for (struct_val.fields) |field| {
357 try out.writeByteNTimes(' ', indent);
358 try out.print(" .{} = ", .{std.zig.fmtId(field.name)});
359
360 const field_name = @field(val, field.name);
361 switch (@typeInfo(@TypeOf(field_name))) {
362 .Enum => try out.print(".{s},\n", .{@tagName(field_name)}),
363 .Struct => |struct_info| {
364 try printStructValue(self, out, struct_info, field_name, indent + 4);
365 },
366 else => try printType(self, out, @TypeOf(field_name), field_name, indent, null),
367 }
368 }
369 }
370
371 if (indent == 0) {
372 try out.writeAll("};\n");
373 } else {
374 try out.writeByteNTimes(' ', indent);
375 try out.writeAll("},\n");
376 }
377}
378
173/// The value is the path in the cache dir.379/// The value is the path in the cache dir.
174/// Adds a dependency automatically.380/// Adds a dependency automatically.
175pub fn addOptionPath(381pub fn addOptionPath(
...@@ -341,6 +547,16 @@ test Options {...@@ -341,6 +547,16 @@ test Options {
341 };547 };
342 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };548 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
343549
550 const NormalStruct = struct {
551 hello: ?[]const u8,
552 world: bool = true,
553 };
554
555 const NestedStruct = struct {
556 normal_struct: NormalStruct,
557 normal_enum: NormalEnum = .foo,
558 };
559
344 options.addOption(usize, "option1", 1);560 options.addOption(usize, "option1", 1);
345 options.addOption(?usize, "option2", null);561 options.addOption(?usize, "option2", null);
346 options.addOption(?usize, "option3", 3);562 options.addOption(?usize, "option3", 3);
...@@ -351,8 +567,18 @@ test Options {...@@ -351,8 +567,18 @@ test Options {
351 options.addOption([]const []const u16, "nested_slice", nested_slice);567 options.addOption([]const []const u16, "nested_slice", nested_slice);
352 options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");568 options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
353 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));569 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
354 options.addOption(NormalEnum, "normal1", NormalEnum.foo);570 options.addOption(NormalEnum, "normal1_enum", NormalEnum.foo);
355 options.addOption(NormalEnum, "normal2", NormalEnum.bar);571 options.addOption(NormalEnum, "normal2_enum", NormalEnum.bar);
572 options.addOption(NormalStruct, "normal1_struct", NormalStruct{
573 .hello = "foo",
574 });
575 options.addOption(NormalStruct, "normal2_struct", NormalStruct{
576 .hello = null,
577 .world = false,
578 });
579 options.addOption(NestedStruct, "nested_struct", NestedStruct{
580 .normal_struct = .{ .hello = "bar" },
581 });
356582
357 try std.testing.expectEqualStrings(583 try std.testing.expectEqualStrings(
358 \\pub const option1: usize = 1;584 \\pub const option1: usize = 1;
...@@ -381,8 +607,8 @@ test Options {...@@ -381,8 +607,8 @@ test Options {
381 \\ 200,607 \\ 200,
382 \\ },608 \\ },
383 \\};609 \\};
384 \\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum {610 \\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum (u0) {
385 \\ @"0.8.1",611 \\ @"0.8.1" = 0,
386 \\};612 \\};
387 \\pub const keyword_enum: @"Build.Step.Options.decltest.Options.KeywordEnum" = .@"0.8.1";613 \\pub const keyword_enum: @"Build.Step.Options.decltest.Options.KeywordEnum" = .@"0.8.1";
388 \\pub const semantic_version: @import("std").SemanticVersion = .{614 \\pub const semantic_version: @import("std").SemanticVersion = .{
...@@ -392,12 +618,35 @@ test Options {...@@ -392,12 +618,35 @@ test Options {
392 \\ .pre = "foo",618 \\ .pre = "foo",
393 \\ .build = "bar",619 \\ .build = "bar",
394 \\};620 \\};
395 \\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum {621 \\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum (u1) {
396 \\ foo,622 \\ foo = 0,
397 \\ bar,623 \\ bar = 1,
624 \\};
625 \\pub const normal1_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo;
626 \\pub const normal2_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar;
627 \\pub const @"Build.Step.Options.decltest.Options.NormalStruct" = struct {
628 \\ hello: ?[]const u8,
629 \\ world: bool = true,
630 \\};
631 \\pub const normal1_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
632 \\ .hello = "foo",
633 \\ .world = true,
634 \\};
635 \\pub const normal2_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
636 \\ .hello = null,
637 \\ .world = false,
638 \\};
639 \\pub const @"Build.Step.Options.decltest.Options.NestedStruct" = struct {
640 \\ normal_struct: @"Build.Step.Options.decltest.Options.NormalStruct",
641 \\ normal_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo,
642 \\};
643 \\pub const nested_struct: @"Build.Step.Options.decltest.Options.NestedStruct" = .{
644 \\ .normal_struct = .{
645 \\ .hello = "bar",
646 \\ .world = true,
647 \\ },
648 \\ .normal_enum = .foo,
398 \\};649 \\};
399 \\pub const normal1: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo;
400 \\pub const normal2: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar;
401 \\650 \\
402 , options.contents.items);651 , options.contents.items);
403652