authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-15 18:47:38-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:33-07:00
log83a34758874dbe98d3566e297b56cd3be0e66900
treed67da5d8e8d2695f7a87329dfef9ad954cfb272d
parent0b3ca115206c0332284c80f7f7d42358d96379c1

configure runner: implement serialization of InstallArtifact


6 files changed, 219 insertions(+), 47 deletions(-)

lib/compiler/configure_runner.zig+79-28
......@@ -140,16 +140,13 @@ pub fn main(init: process.Init.Minimal) !void {
140140 if (try builder.addUserInputFlag(option_contents))
141141 fatal(" access the help menu with 'zig build -h'", .{});
142142 }
143 } else if (mem.startsWith(u8, arg, "-fsys=")) {
144 const name = arg["-fsys=".len..];
143 } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {
145144 graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM");
146 } else if (mem.startsWith(u8, arg, "-fno-sys=")) {
147 const name = arg["-fno-sys=".len..];
145 } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| {
148146 graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM");
149147 } else if (mem.eql(u8, arg, "--release")) {
150148 graph.release_mode = .any;
151 } else if (mem.startsWith(u8, arg, "--release=")) {
152 const text = arg["--release=".len..];
149 } else if (mem.cutPrefix(u8, arg, "--release=")) |text| {
153150 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {
154151 fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{
155152 arg, text,
......@@ -175,23 +172,11 @@ pub fn main(init: process.Init.Minimal) !void {
175172 multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse {
176173 fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
177174 };
178 } else if (mem.eql(u8, arg, "--seed")) {
179 const next_arg = nextArg(args, &arg_idx) orelse
180 fatalWithHint("expected u32 after '{s}'", .{arg});
181 graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
182 fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{
183 next_arg, @errorName(err),
184 });
185 };
186175 } else if (mem.eql(u8, arg, "--build-id")) {
187176 builder.build_id = .fast;
188 } else if (mem.startsWith(u8, arg, "--build-id=")) {
189 const style = arg["--build-id=".len..];
190 builder.build_id = std.zig.BuildId.parse(style) catch |err| {
191 fatal("unable to parse --build-id style '{s}': {s}", .{
192 style, @errorName(err),
193 });
194 };
177 } else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| {
178 builder.build_id = std.zig.BuildId.parse(style) catch |err|
179 fatal("unable to parse --build-id style '{s}': {t}", .{ style, err });
195180 } else if (mem.eql(u8, arg, "--debug-rt")) {
196181 graph.debug_compiler_runtime_libs = true;
197182 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
......@@ -203,11 +188,6 @@ pub fn main(init: process.Init.Minimal) !void {
203188 // but it is handled by the parent process. The build runner
204189 // only sees this flag.
205190 graph.system_package_mode = true;
206 } else if (mem.cutPrefix(u8, arg, "-j")) |text| {
207 const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
208 fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
209 if (n < 1) fatal("number of jobs must be at least 1", .{});
210 threaded.setAsyncLimit(.limited(n));
211191 } else {
212192 fatalWithHint("unrecognized argument: '{s}'", .{arg});
213193 }
......@@ -281,6 +261,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
281261 .name = try wc.addString(step.name),
282262 .flags = .{ .tag = step.tag },
283263 .deps = deps,
264 .max_rss = .fromBytes(step.max_rss),
284265 .extra_index = switch (step.tag) {
285266 .top_level => e: {
286267 const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
......@@ -289,7 +270,21 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
289270 }));
290271 },
291272 .compile => @panic("TODO"),
292 .install_artifact => @panic("TODO"),
273 .install_artifact => e: {
274 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
275 break :e try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{
276 .dest_dir = try addInstallDir(wc, ia.dest_dir),
277 .dest_sub_path = try wc.addString(ia.dest_sub_path),
278 .emitted_bin = try addOptionalLazyPath(wc, ia.emitted_bin),
279 .implib_dir = try addInstallDir(wc, ia.implib_dir),
280 .emitted_implib = try addOptionalLazyPath(wc, ia.emitted_implib),
281 .pdb_dir = try addInstallDir(wc, ia.pdb_dir),
282 .emitted_pdb = try addOptionalLazyPath(wc, ia.emitted_pdb),
283 .h_dir = try addInstallDir(wc, ia.h_dir),
284 .emitted_h = try addOptionalLazyPath(wc, ia.emitted_h),
285 .artifact = stepIndex(&step_map, &ia.artifact.step),
286 }));
287 },
293288 .install_file => @panic("TODO"),
294289 .install_dir => @panic("TODO"),
295290 .remove_dir => @panic("TODO"),
......@@ -315,10 +310,66 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
315310 }
316311
317312 try wc.write(writer, .{
318 .default_step = @intCast(step_map.getIndex(b.default_step).?),
313 .default_step = stepIndex(&step_map, b.default_step),
319314 });
320315}
321316
317fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
318 return @enumFromInt(switch (lp orelse return .none) {
319 .src_path => |src_path| i: {
320 const owner = builderToPackage(src_path.owner);
321 const sub_path = try wc.addString(src_path.sub_path);
322 break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.SourcePath, .{
323 .flags = .{},
324 .owner = owner,
325 .sub_path = sub_path,
326 }));
327 },
328 .generated => |generated| i: {
329 const sub_path = try wc.addString(generated.sub_path);
330 break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.Generated, .{
331 .flags = .{ .up = @intCast(generated.up) },
332 .sub_path = sub_path,
333 }));
334 },
335 .cwd_relative => |cwd_relative_sub_path| i: {
336 const sub_path = try wc.addString(cwd_relative_sub_path);
337 break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.Relative, .{
338 .flags = .{ .base = .cwd },
339 .sub_path = sub_path,
340 }));
341 },
342 .dependency => |dependency| i: {
343 const owner = builderToPackage(dependency.dependency.builder);
344 const sub_path = try wc.addString(dependency.sub_path);
345 break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.SourcePath, .{
346 .flags = .{},
347 .owner = owner,
348 .sub_path = sub_path,
349 }));
350 },
351 });
352}
353
354fn builderToPackage(b: *std.Build) Configuration.Package {
355 _ = b;
356 @panic("TODO");
357}
358
359fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
360 switch (install_dir orelse return .none) {
361 .prefix => return .prefix,
362 .lib => return .lib,
363 .bin => return .bin,
364 .header => return .header,
365 .custom => |sub_path| return .initCustom(try wc.addString(sub_path)),
366 }
367}
368
369fn stepIndex(step_map: *const std.AutoArrayHashMapUnmanaged(*Step, void), step: *Step) Configuration.Step.Index {
370 return @enumFromInt(step_map.getIndex(step).?);
371}
372
322373/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
323374/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
324375fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
lib/std/Build.zig-1
......@@ -103,7 +103,6 @@ pub const Graph = struct {
103103 needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty,
104104 /// Information about the native target. Computed before build() is invoked.
105105 host: ResolvedTarget,
106 random_seed: u32 = 0,
107106 dependency_cache: InitializedDepMap = .empty,
108107 allow_so_scripts: ?bool = null,
109108 /// Steps should use `io` to limit the number of jobs, however in the case of
lib/std/Build/Step/InstallArtifact.zig+2-2
......@@ -1,8 +1,8 @@
1const InstallArtifact = @This();
2
13const std = @import("std");
24const Step = std.Build.Step;
35const InstallDir = std.Build.InstallDir;
4const InstallArtifact = @This();
5const fs = std.fs;
66const LazyPath = std.Build.LazyPath;
77
88step: Step,
lib/std/Build/Step/Run.zig+3-4
......@@ -87,6 +87,7 @@ captured_stderr: ?*CapturedStdIo,
8787dep_output_file: ?*Output,
8888
8989has_side_effects: bool,
90test_runner_mode: bool = false,
9091
9192/// Populated during the fuzz phase if this run step corresponds to a unit test
9293/// executable that contains fuzz tests.
......@@ -234,13 +235,11 @@ pub fn setName(run: *Run, name: []const u8) void {
234235}
235236
236237pub fn enableTestRunnerMode(run: *Run) void {
238 if (run.test_runner_mode) return;
237239 const b = run.step.owner;
238240 run.stdio = .zig_test;
239241 run.addPrefixedDirectoryArg("--cache-dir=", .{ .cwd_relative = b.cache_root.path orelse "." });
240 run.addArgs(&.{
241 b.fmt("--seed=0x{x}", .{b.graph.random_seed}),
242 "--listen=-",
243 });
242 run.test_runner_mode = true;
244243}
245244
246245pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
lib/std/zig/Configuration.zig+117-7
......@@ -4,6 +4,7 @@ const std = @import("../std.zig");
44const Io = std.Io;
55const Allocator = std.mem.Allocator;
66const assert = std.debug.assert;
7const maxInt = std.math.maxInt;
78
89string_bytes: []u8,
910steps: []Step,
......@@ -21,8 +22,7 @@ pub const Header = extern struct {
2122 unlazy_deps_len: u32,
2223 extra_len: u32,
2324
24 /// Index into `steps`.
25 default_step: u32,
25 default_step: Step.Index,
2626};
2727
2828pub const Wip = struct {
......@@ -97,7 +97,7 @@ pub const Wip = struct {
9797 }
9898
9999 pub const Static = struct {
100 default_step: u32,
100 default_step: Step.Index,
101101 };
102102
103103 pub fn write(wip: *Wip, w: *Io.Writer, static: Static) Io.Writer.Error!void {
......@@ -182,10 +182,12 @@ pub const Wip = struct {
182182 const fields = @typeInfo(@TypeOf(extra)).@"struct".fields;
183183 var i = index;
184184 inline for (fields) |field| {
185 wip.extra.items[i] = switch (field.type) {
186 u32 => @field(extra, field.name),
187 String, Deps => @intFromEnum(@field(extra, field.name)),
188 else => @compileError("bad field type"),
185 comptime assert(@sizeOf(field.type) == @sizeOf(u32));
186 wip.extra.items[i] = switch (@typeInfo(field.type)) {
187 .int => @field(extra, field.name),
188 .@"enum" => @intFromEnum(@field(extra, field.name)),
189 .@"struct" => @bitCast(@field(extra, field.name)),
190 else => @compileError("bad field type: " ++ @typeName(field.type)),
189191 };
190192 i += 1;
191193 }
......@@ -196,6 +198,7 @@ pub const Step = extern struct {
196198 name: String,
197199 flags: Flags,
198200 deps: Deps,
201 max_rss: MaxRss,
199202 /// Points into `extra` for step-specific data.
200203 extra_index: u32,
201204
......@@ -231,6 +234,98 @@ pub const Step = extern struct {
231234 pub const TopLevel = struct {
232235 description: String,
233236 };
237
238 pub const InstallArtifact = struct {
239 dest_dir: InstallDir,
240 dest_sub_path: String,
241 emitted_bin: OptionalLazyPath,
242
243 implib_dir: InstallDir,
244 emitted_implib: OptionalLazyPath,
245
246 pdb_dir: InstallDir,
247 emitted_pdb: OptionalLazyPath,
248
249 h_dir: InstallDir,
250 emitted_h: OptionalLazyPath,
251
252 /// Always a compile step.
253 artifact: Step.Index,
254
255 const Flags = packed struct(u32) {
256 tag: Tag = .install_artifact,
257 dylib_symlinks: bool,
258 _: u23 = 0,
259 };
260 };
261};
262
263pub const MaxRss = enum(u32) {
264 none = 0,
265 _,
266
267 pub fn toBytes(mr: MaxRss) usize {
268 const x: usize = @intFromEnum(mr);
269 return x << 8;
270 }
271
272 pub fn fromBytes(bytes: usize) MaxRss {
273 return @enumFromInt(bytes >> 8);
274 }
275};
276
277/// An index into `extra`.
278pub const OptionalLazyPath = enum(u32) {
279 none = maxInt(u32),
280 _,
281
282 pub const Tag = enum(u8) {
283 /// A source file path relative to build root.
284 source_path,
285 generated,
286 relative,
287 };
288
289 pub const SourcePath = struct {
290 flags: Flags,
291 owner: Package,
292 sub_path: String,
293
294 pub const Flags = packed struct(u32) {
295 tag: Tag = .source_path,
296 _: u24 = 0,
297 };
298 };
299
300 pub const Generated = struct {
301 flags: Flags,
302 /// Applied after `up`.
303 sub_path: String,
304
305 pub const Flags = packed struct(u32) {
306 tag: Tag = .generated,
307 /// The number of parent directories to go up.
308 /// 0 means the generated file itself.
309 /// 1 means the directory of the generated file.
310 /// 2 means the parent of that directory, and so on.
311 up: u24,
312 };
313 };
314
315 pub const Relative = struct {
316 flags: Flags,
317 sub_path: String,
318
319 pub const Flags = packed struct(u32) {
320 tag: Tag = .relative,
321 base: Path.Base,
322 _: u16 = 0,
323 };
324 };
325};
326
327pub const Package = enum(u32) {
328 _,
234329};
235330
236331/// Points into `extra`, where the first element is number of deps,
......@@ -258,6 +353,21 @@ pub const Path = extern struct {
258353 }
259354};
260355
356pub const InstallDir = enum(u32) {
357 none = maxInt(u32) - 4,
358 prefix = maxInt(u32) - 3,
359 lib = maxInt(u32) - 2,
360 bin = maxInt(u32) - 1,
361 header = maxInt(u32),
362 /// A `String` path relative to the prefix.
363 _,
364
365 pub fn initCustom(sub_path: String) InstallDir {
366 assert(@intFromEnum(sub_path) < @intFromEnum(InstallDir.none));
367 return @enumFromInt(@intFromEnum(sub_path));
368 }
369};
370
261371/// Points into `string_bytes`, null-terminated.
262372pub const String = enum(u32) {
263373 _,
src/main.zig+18-5
......@@ -4982,34 +4982,47 @@ fn cmdBuild(
49824982 const default_seed = try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)});
49834983
49844984 try configure_argv.ensureUnusedCapacity(arena, 16);
4985 try make_argv.ensureUnusedCapacity(arena, 16);
49854986
49864987 const argv_index_exe = configure_argv.items.len;
49874988 _ = configure_argv.addOneAssumeCapacity();
4989 _ = make_argv.addOneAssumeCapacity();
49884990
49894991 configure_argv.appendAssumeCapacity("--zig");
49904992 configure_argv.appendAssumeCapacity(self_exe_path);
49914993
4994 make_argv.appendAssumeCapacity("--zig");
4995 make_argv.appendAssumeCapacity(self_exe_path);
4996
49924997 configure_argv.appendAssumeCapacity("--zig-lib-dir");
4998 make_argv.appendAssumeCapacity("--zig-lib-dir");
49934999 const argv_index_zig_lib_dir = configure_argv.items.len;
49945000 _ = configure_argv.addOneAssumeCapacity();
5001 _ = make_argv.addOneAssumeCapacity();
49955002
49965003 configure_argv.appendAssumeCapacity("--build-root");
5004 make_argv.appendAssumeCapacity("--build-root");
49975005 const argv_index_build_file = configure_argv.items.len;
49985006 _ = configure_argv.addOneAssumeCapacity();
5007 _ = make_argv.addOneAssumeCapacity();
49995008
50005009 configure_argv.appendAssumeCapacity("--local-cache");
5010 make_argv.appendAssumeCapacity("--local-cache");
50015011 const argv_index_cache_dir = configure_argv.items.len;
50025012 _ = configure_argv.addOneAssumeCapacity();
5013 _ = make_argv.addOneAssumeCapacity();
50035014
50045015 configure_argv.appendAssumeCapacity("--global-cache");
5016 make_argv.appendAssumeCapacity("--global-cache");
50055017 const argv_index_global_cache_dir = configure_argv.items.len;
50065018 _ = configure_argv.addOneAssumeCapacity();
5019 _ = make_argv.addOneAssumeCapacity();
50075020
5008 configure_argv.appendSliceAssumeCapacity(&.{ "--seed", default_seed });
5009 const argv_index_seed = configure_argv.items.len - 1;
5021 make_argv.appendSliceAssumeCapacity(&.{ "--configuration", undefined });
5022 const argv_index_configuration_file = make_argv.items.len - 1;
50105023
5011 const argv_index_configuration_file = make_argv.items.len;
5012 _ = try make_argv.addOne(arena);
5024 make_argv.appendSliceAssumeCapacity(&.{ "--seed", default_seed });
5025 const argv_index_seed = make_argv.items.len - 1;
50135026
50145027 var color: Color = .auto;
50155028 var n_jobs: ?u32 = null;
......@@ -5151,7 +5164,7 @@ fn cmdBuild(
51515164 } else if (mem.eql(u8, arg, "--seed")) {
51525165 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
51535166 i += 1;
5154 configure_argv.items[argv_index_seed] = args[i];
5167 make_argv.items[argv_index_seed] = args[i];
51555168 continue;
51565169 } else if (mem.eql(u8, arg, "--")) {
51575170 // The rest of the args are supposed to get passed onto