Configuration: complete serialization of Compile steps

This commit is contained in:
Andrew Kelley 2026-02-27 18:48:27 -08:00
parent 8f74cf3222
commit ec009cd68e
4 changed files with 147 additions and 47 deletions

View file

@ -85,6 +85,8 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
.flag_length_prefixed_list => comptime unreachable,
.enum_optional => comptime unreachable,
.union_list => comptime unreachable,
.length_prefixed_list => comptime unreachable,
.flag_union => comptime unreachable,
} else if (std.enums.tagName(Field, field_value)) |name| {
try s.ident(name);
} else {
@ -103,14 +105,29 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
try s.value(null, .{});
}
},
.flag_length_prefixed_list => {
.length_prefixed_list, .flag_length_prefixed_list => {
try printValue(sc, s, @TypeOf(field_value.slice), field_value.slice);
},
.extended => @compileError("TODO"),
.union_list => @compileError("TODO"),
.flag_union => try printValue(sc, s, Field.Union, field_value.u),
},
else => @compileError("not implemented: " ++ @typeName(Field)),
},
.@"union" => {
switch (field_value) {
inline else => |u, tag| {
if (@TypeOf(u) == void) {
try s.ident(@tagName(tag));
} else {
var sub_struct = try s.beginStruct(.{});
try sub_struct.fieldPrefix(@tagName(tag));
try printValue(sc, s, @TypeOf(u), u);
try sub_struct.end();
}
},
}
},
else => @compileError("not implemented: " ++ @typeName(Field)),
},
}

View file

@ -132,21 +132,20 @@ fn lowerZigArgs(
},
}
for (conf_comp.force_undefined_symbols.slice) |symbol_name| {
try zig_args.appendSlice(gpa, &.{ "--force_undefined", symbol_name.slice(conf) });
}
if (conf_comp.stack_size.value) |stack_size| {
try zig_args.appendSlice(gpa, &.{ "--stack", try allocPrint(arena, "{d}", .{stack_size}) });
}
try addBool(gpa, zig_args, "-ffuzz", fuzz);
if (true) @panic("TODO");
{
for (compile.force_undefined_symbols.keys()) |symbol_name| {
try zig_args.append(gpa, "--force_undefined");
try zig_args.append(gpa, symbol_name.*);
}
}
if (compile.stack_size) |stack_size| {
try zig_args.append(gpa, "--stack");
try zig_args.append(gpa, try allocPrint(arena, "{}", .{stack_size}));
}
try addBool(gpa, zig_args, fuzz, "-ffuzz");
var is_linking_libc = conf_comp.flags3.is_linking_libc;
var is_linking_libcpp = conf_comp.flags3.is_linking_libcpp;
{
// Stores system libraries that have already been seen for at least one
@ -161,14 +160,14 @@ fn lowerZigArgs(
var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
// Track the number of positional arguments so that a nice error can be
// emitted if there is nothing to link.
var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);
var total_linker_objects: usize = @intFromBool(root_module.root_source_file != .none);
// Fully recursive iteration including dynamic libraries to detect
// libc and libc++ linkage.
for (getCompileDependencies(true)) |some_compile| {
for (some_compile.root_module.getGraph().modules) |mod| {
if (mod.link_libc == true) compile.is_linking_libc = true;
if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
if (mod.link_libc == true) is_linking_libc = true;
if (mod.link_libcpp == true) is_linking_libcpp = true;
}
}
@ -463,11 +462,11 @@ fn lowerZigArgs(
try zig_args.append(gpa, name);
}
if (compile.is_linking_libcpp) {
if (is_linking_libcpp) {
try zig_args.append(gpa, "-lc++");
}
if (compile.is_linking_libc) {
if (is_linking_libc) {
try zig_args.append(gpa, "-lc");
}
}
@ -498,15 +497,15 @@ fn lowerZigArgs(
try zig_args.appendSlice(gpa, &.{ "--debug-log", log_scope });
}
try addBool(gpa, zig_args, graph.debug_compile_errors, "--debug-compile-errors");
try addBool(gpa, zig_args, graph.debug_incremental, "--debug-incremental");
try addBool(gpa, zig_args, graph.verbose_cimport, "--verbose-cimport");
try addBool(gpa, zig_args, graph.verbose_air, "--verbose-air");
try addBool(gpa, zig_args, graph.verbose_llvm_ir, "--verbose-llvm-ir");
try addBool(gpa, zig_args, graph.verbose_link or compile.verbose_link, "--verbose-link");
try addBool(gpa, zig_args, graph.verbose_cc or compile.verbose_cc, "--verbose-cc");
try addBool(gpa, zig_args, graph.verbose_llvm_cpu_features, "--verbose-llvm-cpu-features");
try addBool(gpa, zig_args, graph.time_report, "--time-report");
try addBool(gpa, zig_args, "--debug-compile-errors", graph.debug_compile_errors);
try addBool(gpa, zig_args, "--debug-incremental", graph.debug_incremental);
try addBool(gpa, zig_args, "--verbose-cimport", graph.verbose_cimport);
try addBool(gpa, zig_args, "--verbose-air", graph.verbose_air);
try addBool(gpa, zig_args, "--verbose-llvm-ir", graph.verbose_llvm_ir);
try addBool(gpa, zig_args, "--verbose-link", graph.verbose_link or compile.verbose_link);
try addBool(gpa, zig_args, "--verbose-cc", graph.verbose_cc or compile.verbose_cc);
try addBool(gpa, zig_args, "--verbose-llvm-cpu-features", graph.verbose_llvm_cpu_features);
try addBool(gpa, zig_args, "--time-report", graph.time_report);
if (compile.generated_asm != null) try zig_args.append(gpa, "-femit-asm");
if (compile.generated_bin == null) try zig_args.append(gpa, "-fno-emit-bin");

View file

@ -606,7 +606,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.max_memory = c.max_memory != null,
.kind = c.kind,
.global_base = c.global_base != null,
.test_runner_mode = if (c.test_runner) |tr| switch (tr.mode) {
.test_runner = if (c.test_runner) |tr| switch (tr.mode) {
.simple => .simple,
.server => .server,
} else .default,
@ -678,10 +678,18 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.exec_cmd_args = .{ .slice = try s.initOptionalStringList(exec_cmd_args) },
.installed_headers = .initErased(installed_headers),
.force_undefined_symbols = .{ .slice = try s.initStringList(c.force_undefined_symbols.keys()) },
.expect_errors = .{ .u = if (c.expect_errors) |x| switch (x) {
.contains => |slice| .{ .contains = try wc.addString(slice) },
.exact => |exact| .{ .exact = .{ .slice = try s.initStringList(exact) } },
.starts_with => |slice| .{ .starts_with = try wc.addString(slice) },
.stderr_contains => |slice| .{ .stderr_contains = try wc.addString(slice) },
} else .none },
.test_runner = .{ .u = if (c.test_runner) |tr| switch (tr.mode) {
.simple => .{ .simple = try s.addLazyPath(tr.path) },
.server => .{ .server = try s.addLazyPath(tr.path) },
} else .default },
}));
log.err("TODO serialize the trailing Compile step data", .{});
break :e @enumFromInt(extra_index);
},
.install_artifact => e: {

View file

@ -584,9 +584,6 @@ pub const Step = extern struct {
};
};
/// Trailing:
/// * exact_match: String, // if expect_errors is contains, starts_with, or stderr_contains
/// * test_runner: LazyPath, // if test_runner_mode is not default
pub const Compile = struct {
flags: @This().Flags,
flags2: Flags2,
@ -600,7 +597,7 @@ pub const Step = extern struct {
exec_cmd_args: Storage.FlagLengthPrefixedList(.flags, .exec_cmd_args_len, OptionalString),
installed_headers: Storage.FlagLengthPrefixedList(.flags, .installed_headers_len, Storage.Extended(InstalledHeader.Flags, InstalledHeader)),
force_undefined_symbols: Storage.FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String),
//exacts: EnumConditionalPrefixedList(.flags4, .expect_errors, .exact, String),
expect_errors: Storage.FlagUnion(.flags4, .expect_errors, ExpectErrors),
linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath),
version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath),
zig_lib_dir: Storage.FlagOptional(.flags3, .zig_lib_dir, LazyPath),
@ -622,6 +619,7 @@ pub const Step = extern struct {
headerpad_size: Storage.FlagOptional(.flags4, .headerpad_size, u32),
error_limit: Storage.FlagOptional(.flags4, .error_limit, u32),
build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String),
test_runner: Storage.FlagUnion(.flags3, .test_runner, TestRunner),
pub const InstalledHeader = union(@This().Tag) {
file: File,
@ -663,8 +661,22 @@ pub const Step = extern struct {
};
};
};
pub const ExpectErrors = enum(u3) { contains, exact, starts_with, stderr_contains, none };
pub const TestRunnerMode = enum(u2) { default, simple, server };
pub const ExpectErrors = union(@This().Tag) {
pub const Tag = enum(u3) { contains, exact, starts_with, stderr_contains, none };
contains: String,
exact: Storage.LengthPrefixedList(String),
starts_with: String,
stderr_contains: String,
none: void,
};
pub const TestRunner = union(@This().Tag) {
pub const Tag = enum(u2) { default, simple, server };
default: void,
simple: LazyPath,
server: LazyPath,
};
pub const Entry = enum(u2) { default, disabled, enabled, symbol_name };
pub const Lto = enum(u2) {
@ -826,7 +838,7 @@ pub const Step = extern struct {
kind: Kind,
compress_debug_sections: std.zig.CompressDebugSections,
global_base: bool,
test_runner_mode: TestRunnerMode,
test_runner: TestRunner.Tag,
wasi_exec_model: WasiExecModel,
win32_manifest: bool,
win32_module_definition: bool,
@ -849,7 +861,7 @@ pub const Step = extern struct {
error_limit: bool,
install_name: bool,
entitlements: bool,
expect_errors: ExpectErrors,
expect_errors: ExpectErrors.Tag,
linker_script: bool,
version_script: bool,
_: u18 = 0,
@ -1756,8 +1768,10 @@ pub const Storage = enum {
flag_optional,
enum_optional,
extended,
length_prefixed_list,
flag_length_prefixed_list,
union_list,
flag_union,
/// The presence of the field is determined by a boolean within a packed
/// struct.
@ -1776,6 +1790,24 @@ pub const Storage = enum {
};
}
/// The type of the field is determined by an enum within a packed struct.
pub fn FlagUnion(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime UnionArg: type,
) type {
return struct {
u: Union,
pub const storage: Storage = .flag_union;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Union = UnionArg;
pub const Tag = @typeInfo(Union).@"union".tag_type.?;
};
}
/// The field is present if an enum tag from flags matches a specific value.
pub fn EnumOptional(
comptime flags_arg: @EnumLiteral(),
@ -1814,21 +1846,32 @@ pub const Storage = enum {
/// A field in flags determines whether the length is zero or nonzero. If the length is
/// nonzero, then there is a length field followed by the list.
///
/// When deserializing, the slice field is set. When serializing, the index
/// field must be set.
pub fn FlagLengthPrefixedList(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
comptime ValueArg: type,
comptime ElemArg: type,
) type {
return struct {
slice: []const Value,
slice: []const Elem,
pub const storage: Storage = .flag_length_prefixed_list;
pub const flags = flags_arg;
pub const flag = flag_arg;
pub const Value = ValueArg;
pub const Elem = ElemArg;
pub fn initErased(s: []const u32) @This() {
return .{ .slice = @ptrCast(s) };
}
};
}
/// The field contains a u32 length followed by that many items.
pub fn LengthPrefixedList(comptime ElemArg: type) type {
return struct {
slice: []const Elem,
pub const storage: Storage = .length_prefixed_list;
pub const Elem = ElemArg;
pub fn initErased(s: []const u32) @This() {
return .{ .slice = @ptrCast(s) };
@ -1910,6 +1953,7 @@ pub const Storage = enum {
fn dataField(buffer: []const u32, i: *usize, container: anytype, comptime Field: type) Field {
switch (@typeInfo(Field)) {
.void => return {},
.int => |info| switch (info.bits) {
32 => {
defer i.* += 1;
@ -1954,6 +1998,24 @@ pub const Storage = enum {
.value = if (flag) dataField(buffer, i, container, Field.Value) else null,
};
},
.flag_union => {
const flags = @field(container, @tagName(Field.flags));
const tag: Field.Tag = @field(flags, @tagName(Field.flag));
return .{
.u = switch (tag) {
inline else => |comptime_tag| @unionInit(
Field.Union,
@tagName(comptime_tag),
dataField(
buffer,
i,
container,
@typeInfo(Field.Union).@"union".fields[@intFromEnum(comptime_tag)].type,
),
),
},
};
},
.enum_optional => {
const flags = @field(container, @tagName(Field.flags));
const tag = @field(flags, @tagName(Field.flag));
@ -1963,6 +2025,12 @@ pub const Storage = enum {
};
},
.extended => @compileError("TODO"),
.length_prefixed_list => {
const data_start = i.* + 1;
const len = buffer[data_start - 1];
defer i.* = data_start + len;
return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };
},
.flag_length_prefixed_list => {
const flags = @field(container, @tagName(Field.flags));
const flag = @field(flags, @tagName(Field.flag));
@ -2010,6 +2078,7 @@ pub const Storage = enum {
fn extraFieldLen(field: anytype) usize {
const Field = @TypeOf(field);
return switch (@typeInfo(Field)) {
.void => 0,
.int => |info| switch (info.bits) {
32 => 1,
64 => 2,
@ -2024,8 +2093,11 @@ pub const Storage = enum {
},
.auto => switch (Field.storage) {
.flag_optional, .enum_optional, .extended => 1,
.flag_length_prefixed_list => field.slice.len + 1,
.length_prefixed_list, .flag_length_prefixed_list => field.slice.len + 1,
.union_list => Field.extraLen(field.len),
.flag_union => switch (field.u) {
inline else => |v| extraFieldLen(v),
},
},
.@"extern" => comptime unreachable,
},
@ -2044,6 +2116,7 @@ pub const Storage = enum {
inline fn setExtraField(buffer: []u32, i: usize, comptime Field: type, value: anytype) usize {
switch (@typeInfo(Field)) {
.void => return 0,
.int => |info| switch (info.bits) {
32 => {
buffer[i] = value;
@ -2081,8 +2154,11 @@ pub const Storage = enum {
.flag_optional, .enum_optional => {
return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;
},
.flag_union => return switch (value.u) {
inline else => |x| setExtraField(buffer, i, @TypeOf(x), x),
},
.extended => @compileError("TODO"),
.flag_length_prefixed_list => {
.flag_length_prefixed_list, .length_prefixed_list => {
const len: u32 = @intCast(value.slice.len);
if (len == 0) return 0;
buffer[i] = len;