mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Implementing structs and better enums in build options
This commit is contained in:
parent
64173dadca
commit
1206604262
1 changed files with 330 additions and 81 deletions
|
|
@ -41,123 +41,180 @@ pub fn addOption(self: *Options, comptime T: type, name: []const u8, value: T) v
|
|||
|
||||
fn addOptionFallible(self: *Options, comptime T: type, name: []const u8, value: T) !void {
|
||||
const out = self.contents.writer();
|
||||
try printType(self, out, T, value, 0, name);
|
||||
}
|
||||
|
||||
fn printType(self: *Options, out: anytype, comptime T: type, value: T, indent: u8, name: ?[]const u8) !void {
|
||||
switch (T) {
|
||||
[]const []const u8 => {
|
||||
try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)});
|
||||
if (name) |payload| {
|
||||
try out.print("pub const {}: []const []const u8 = ", .{std.zig.fmtId(payload)});
|
||||
}
|
||||
|
||||
try out.writeAll("&[_][]const u8{\n");
|
||||
|
||||
for (value) |slice| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll("};\n");
|
||||
return;
|
||||
},
|
||||
[:0]const u8 => {
|
||||
try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
|
||||
} else {
|
||||
try out.writeAll("},\n");
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
[]const u8 => {
|
||||
try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
|
||||
return;
|
||||
},
|
||||
?[:0]const u8 => {
|
||||
try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)});
|
||||
if (value) |payload| {
|
||||
try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: []const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) });
|
||||
} else {
|
||||
try out.writeAll("null;\n");
|
||||
try out.print("\"{}\",", .{std.zig.fmtEscapes(value)});
|
||||
}
|
||||
return out.writeAll("\n");
|
||||
},
|
||||
[:0]const u8 => {
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: [:0]const u8 = \"{}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) });
|
||||
} else {
|
||||
try out.print("\"{}\",", .{std.zig.fmtEscapes(value)});
|
||||
}
|
||||
return out.writeAll("\n");
|
||||
},
|
||||
?[]const u8 => {
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(some)});
|
||||
}
|
||||
|
||||
if (value) |payload| {
|
||||
try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)});
|
||||
} else {
|
||||
try out.writeAll("null");
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll(";\n");
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
?[]const u8 => {
|
||||
try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)});
|
||||
?[:0]const u8 => {
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(some)});
|
||||
}
|
||||
|
||||
if (value) |payload| {
|
||||
try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
|
||||
try out.print("\"{}\"", .{std.zig.fmtEscapes(payload)});
|
||||
} else {
|
||||
try out.writeAll("null;\n");
|
||||
try out.writeAll("null");
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll(";\n");
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
std.SemanticVersion => {
|
||||
try out.print(
|
||||
\\pub const {}: @import("std").SemanticVersion = .{{
|
||||
\\ .major = {d},
|
||||
\\ .minor = {d},
|
||||
\\ .patch = {d},
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtId(name),
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: @import(\"std\").SemanticVersion = ", .{std.zig.fmtId(some)});
|
||||
}
|
||||
|
||||
try out.writeAll(".{\n");
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .major = {d},\n", .{value.major});
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .minor = {d},\n", .{value.minor});
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .patch = {d},\n", .{value.patch});
|
||||
|
||||
value.major,
|
||||
value.minor,
|
||||
value.patch,
|
||||
});
|
||||
if (value.pre) |some| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});
|
||||
}
|
||||
if (value.build) |some| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll("};\n");
|
||||
} else {
|
||||
try out.writeAll("},\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (@typeInfo(T)) {
|
||||
.Enum => |enum_info| {
|
||||
const gop = try self.encountered_types.getOrPut(@typeName(T));
|
||||
if (!gop.found_existing) {
|
||||
try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))});
|
||||
inline for (enum_info.fields) |field| {
|
||||
try out.print(" {},\n", .{std.zig.fmtId(field.name)});
|
||||
}
|
||||
try out.writeAll("};\n");
|
||||
}
|
||||
try out.print("pub const {}: {s} = .{s};\n", .{
|
||||
std.zig.fmtId(name),
|
||||
std.zig.fmtId(@typeName(T)),
|
||||
std.zig.fmtId(@tagName(value)),
|
||||
});
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) });
|
||||
try printLiteral(out, value, 0);
|
||||
try out.writeAll(";\n");
|
||||
}
|
||||
|
||||
// TODO: non-recursive?
|
||||
fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
|
||||
const T = @TypeOf(val);
|
||||
switch (@typeInfo(T)) {
|
||||
.Array => {
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
|
||||
}
|
||||
|
||||
try out.print("{s} {{\n", .{@typeName(T)});
|
||||
for (val) |item| {
|
||||
for (value) |item| {
|
||||
try out.writeByteNTimes(' ', indent + 4);
|
||||
try printLiteral(out, item, indent + 4);
|
||||
try out.writeAll(",\n");
|
||||
try printType(self, out, @TypeOf(item), item, indent + 4, null);
|
||||
}
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll("}");
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll(";\n");
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Pointer => |p| {
|
||||
if (p.size != .Slice) {
|
||||
@compileError("Non-slice pointers are not yet supported in build options");
|
||||
}
|
||||
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
|
||||
}
|
||||
|
||||
try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
|
||||
for (val) |item| {
|
||||
for (value) |item| {
|
||||
try out.writeByteNTimes(' ', indent + 4);
|
||||
try printLiteral(out, item, indent + 4);
|
||||
try out.writeAll(",\n");
|
||||
try printType(self, out, @TypeOf(item), item, indent + 4, null);
|
||||
}
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll("}");
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll(";\n");
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Optional => {
|
||||
if (val) |inner| {
|
||||
return printLiteral(out, inner, indent);
|
||||
} else {
|
||||
return out.writeAll("null");
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
|
||||
}
|
||||
|
||||
if (value) |inner| {
|
||||
try printType(self, out, @TypeOf(inner), inner, indent + 4, null);
|
||||
// Pop the '\n' and ',' chars
|
||||
_ = self.contents.pop();
|
||||
_ = self.contents.pop();
|
||||
} else {
|
||||
try out.writeAll("null");
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
try out.writeAll(";\n");
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Void,
|
||||
.Bool,
|
||||
|
|
@ -165,11 +222,160 @@ fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
|
|||
.ComptimeInt,
|
||||
.Float,
|
||||
.Null,
|
||||
=> try out.print("{any}", .{val}),
|
||||
=> {
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = {any};\n", .{ std.zig.fmtId(some), @typeName(T), value });
|
||||
} else {
|
||||
try out.print("{any},\n", .{value});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Enum => |info| {
|
||||
try printEnum(self, out, T, info, indent);
|
||||
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = .{s};\n", .{
|
||||
std.zig.fmtId(some),
|
||||
std.zig.fmtId(@typeName(T)),
|
||||
std.zig.fmtId(@tagName(value)),
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Struct => |info| {
|
||||
try printStruct(self, out, T, info, indent);
|
||||
|
||||
if (name) |some| {
|
||||
try out.print("pub const {}: {s} = ", .{
|
||||
std.zig.fmtId(some),
|
||||
std.zig.fmtId(@typeName(T)),
|
||||
});
|
||||
try printStructValue(self, out, info, value, indent);
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
|
||||
}
|
||||
}
|
||||
|
||||
fn printUserDefinedType(self: *Options, out: anytype, comptime T: type, indent: u8) !void {
|
||||
switch (@typeInfo(T)) {
|
||||
.Enum => |info| {
|
||||
return try printEnum(self, out, T, info, indent);
|
||||
},
|
||||
.Struct => |info| {
|
||||
return try printStruct(self, out, T, info, indent);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn printEnum(self: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Enum, indent: u8) !void {
|
||||
const gop = try self.encountered_types.getOrPut(@typeName(T));
|
||||
if (gop.found_existing) return;
|
||||
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print("pub const {} = enum ({s}) {{\n", .{ std.zig.fmtId(@typeName(T)), @typeName(val.tag_type) });
|
||||
|
||||
inline for (val.fields) |field| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" {} = {d},\n", .{ std.zig.fmtId(field.name), field.value });
|
||||
}
|
||||
|
||||
if (!val.is_exhaustive) {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll(" _,\n");
|
||||
}
|
||||
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll("};\n");
|
||||
}
|
||||
|
||||
fn printStruct(self: *Options, out: anytype, comptime T: type, comptime val: std.builtin.Type.Struct, indent: u8) !void {
|
||||
const gop = try self.encountered_types.getOrPut(@typeName(T));
|
||||
if (gop.found_existing) return;
|
||||
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print("pub const {} = ", .{std.zig.fmtId(@typeName(T))});
|
||||
|
||||
switch (val.layout) {
|
||||
.@"extern" => try out.writeAll("extern struct"),
|
||||
.@"packed" => try out.writeAll("packed struct"),
|
||||
else => try out.writeAll("struct"),
|
||||
}
|
||||
|
||||
try out.writeAll(" {\n");
|
||||
|
||||
inline for (val.fields) |field| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
|
||||
const type_name = @typeName(field.type);
|
||||
|
||||
// If the type name doesn't contains a '.' the type is from zig builtins.
|
||||
if (std.mem.containsAtLeast(u8, type_name, 1, ".")) {
|
||||
try out.print(" {}: {}", .{ std.zig.fmtId(field.name), std.zig.fmtId(type_name) });
|
||||
} else {
|
||||
try out.print(" {}: {s}", .{ std.zig.fmtId(field.name), type_name });
|
||||
}
|
||||
|
||||
if (field.default_value != null) {
|
||||
const default_value = @as(*field.type, @ptrCast(@alignCast(@constCast(field.default_value.?)))).*;
|
||||
|
||||
try out.writeAll(" = ");
|
||||
switch (@typeInfo(@TypeOf(default_value))) {
|
||||
.Enum => try out.print(".{s},\n", .{@tagName(default_value)}),
|
||||
.Struct => |info| {
|
||||
try printStructValue(self, out, info, default_value, indent + 4);
|
||||
},
|
||||
else => try printType(self, out, @TypeOf(default_value), default_value, indent, null),
|
||||
}
|
||||
} else {
|
||||
try out.writeAll(",\n");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: write declarations
|
||||
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll("};\n");
|
||||
|
||||
inline for (val.fields) |field| {
|
||||
try printUserDefinedType(self, out, field.type, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn printStructValue(self: *Options, out: anytype, comptime struct_val: std.builtin.Type.Struct, val: anytype, indent: u8) !void {
|
||||
try out.writeAll(".{\n");
|
||||
|
||||
if (struct_val.is_tuple) {
|
||||
inline for (struct_val.fields) |field| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try printType(self, out, @TypeOf(@field(val, field.name)), @field(val, field.name), indent, null);
|
||||
}
|
||||
} else {
|
||||
inline for (struct_val.fields) |field| {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.print(" .{} = ", .{std.zig.fmtId(field.name)});
|
||||
|
||||
const field_name = @field(val, field.name);
|
||||
switch (@typeInfo(@TypeOf(field_name))) {
|
||||
.Enum => try out.print(".{s},\n", .{@tagName(field_name)}),
|
||||
.Struct => |struct_info| {
|
||||
try printStructValue(self, out, struct_info, field_name, indent + 4);
|
||||
},
|
||||
else => try printType(self, out, @TypeOf(field_name), field_name, indent, null),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indent == 0) {
|
||||
try out.writeAll("};\n");
|
||||
} else {
|
||||
try out.writeByteNTimes(' ', indent);
|
||||
try out.writeAll("},\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// The value is the path in the cache dir.
|
||||
/// Adds a dependency automatically.
|
||||
pub fn addOptionPath(
|
||||
|
|
@ -341,6 +547,16 @@ test Options {
|
|||
};
|
||||
const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
|
||||
|
||||
const NormalStruct = struct {
|
||||
hello: ?[]const u8,
|
||||
world: bool = true,
|
||||
};
|
||||
|
||||
const NestedStruct = struct {
|
||||
normal_struct: NormalStruct,
|
||||
normal_enum: NormalEnum = .foo,
|
||||
};
|
||||
|
||||
options.addOption(usize, "option1", 1);
|
||||
options.addOption(?usize, "option2", null);
|
||||
options.addOption(?usize, "option3", 3);
|
||||
|
|
@ -351,8 +567,18 @@ test Options {
|
|||
options.addOption([]const []const u16, "nested_slice", nested_slice);
|
||||
options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
|
||||
options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
|
||||
options.addOption(NormalEnum, "normal1", NormalEnum.foo);
|
||||
options.addOption(NormalEnum, "normal2", NormalEnum.bar);
|
||||
options.addOption(NormalEnum, "normal1_enum", NormalEnum.foo);
|
||||
options.addOption(NormalEnum, "normal2_enum", NormalEnum.bar);
|
||||
options.addOption(NormalStruct, "normal1_struct", NormalStruct{
|
||||
.hello = "foo",
|
||||
});
|
||||
options.addOption(NormalStruct, "normal2_struct", NormalStruct{
|
||||
.hello = null,
|
||||
.world = false,
|
||||
});
|
||||
options.addOption(NestedStruct, "nested_struct", NestedStruct{
|
||||
.normal_struct = .{ .hello = "bar" },
|
||||
});
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\pub const option1: usize = 1;
|
||||
|
|
@ -381,8 +607,8 @@ test Options {
|
|||
\\ 200,
|
||||
\\ },
|
||||
\\};
|
||||
\\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum {
|
||||
\\ @"0.8.1",
|
||||
\\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum (u0) {
|
||||
\\ @"0.8.1" = 0,
|
||||
\\};
|
||||
\\pub const keyword_enum: @"Build.Step.Options.decltest.Options.KeywordEnum" = .@"0.8.1";
|
||||
\\pub const semantic_version: @import("std").SemanticVersion = .{
|
||||
|
|
@ -392,12 +618,35 @@ test Options {
|
|||
\\ .pre = "foo",
|
||||
\\ .build = "bar",
|
||||
\\};
|
||||
\\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum {
|
||||
\\ foo,
|
||||
\\ bar,
|
||||
\\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum (u1) {
|
||||
\\ foo = 0,
|
||||
\\ bar = 1,
|
||||
\\};
|
||||
\\pub const normal1_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo;
|
||||
\\pub const normal2_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar;
|
||||
\\pub const @"Build.Step.Options.decltest.Options.NormalStruct" = struct {
|
||||
\\ hello: ?[]const u8,
|
||||
\\ world: bool = true,
|
||||
\\};
|
||||
\\pub const normal1_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
|
||||
\\ .hello = "foo",
|
||||
\\ .world = true,
|
||||
\\};
|
||||
\\pub const normal2_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{
|
||||
\\ .hello = null,
|
||||
\\ .world = false,
|
||||
\\};
|
||||
\\pub const @"Build.Step.Options.decltest.Options.NestedStruct" = struct {
|
||||
\\ normal_struct: @"Build.Step.Options.decltest.Options.NormalStruct",
|
||||
\\ normal_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo,
|
||||
\\};
|
||||
\\pub const nested_struct: @"Build.Step.Options.decltest.Options.NestedStruct" = .{
|
||||
\\ .normal_struct = .{
|
||||
\\ .hello = "bar",
|
||||
\\ .world = true,
|
||||
\\ },
|
||||
\\ .normal_enum = .foo,
|
||||
\\};
|
||||
\\pub const normal1: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo;
|
||||
\\pub const normal2: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar;
|
||||
\\
|
||||
, options.contents.items);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue