authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-02 05:02:49+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-02 05:02:49+02:00
log04b6f33e5eb46bc3070b1e8d95e364db65e0cd7a
treeec378b7321410d1d04283574ea59987aa1ca2210
parentf1531406979b7c196ec535008a159b5f411cf78f
parent1fce802929a3db8971d1127e6b81c3799b00021c

Merge pull request 'lazy dependency ergonomic enhancements' (#36009) from lazy-dependency-ergo into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36009

4 files changed, 1369 insertions(+), 1354 deletions(-)

lib/compiler/Maker.zig+25-26
......@@ -881,22 +881,6 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
881881 const io = graph.io;
882882 const arena = graph.arena;
883883
884 // Cache lookup for configure options. If we get a match, we can skip
885 // execution of the configure script. If not, we get the file path to pass
886 // to the configure process.
887 //
888 // In the hot path, we only check this cache, which means that also
889 // configure source files need to go in here.
890 var config_man = graph.cache.obtain();
891 defer config_man.deinit();
892
893 for (options.cached_passthru_configure) |i|
894 config_man.hash.addBytes(configure_argv[i]);
895
896 // Prevents a `zig build` from getting a false positive cache hit following
897 // a `zig build --cache-poison=ignored`.
898 config_man.hash.add(options.cache_poison == .ignored);
899
900884 configure_argv[options.conf_argv_index_build_root] = options.build_root.directory.path orelse options.cwd_path;
901885
902886 var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
......@@ -958,7 +942,6 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
958942 // some code to help when debugging edits to the build runner so that you
959943 // can make sure it compiles successfully on other targets.
960944 const target_arch_os_abi: ?[]const u8 = if (options.debug_target) |triple| t: {
961 config_man.hash.addBytes(triple);
962945 try build_configurer_argv.appendSlice(gpa, &.{ "-target", triple });
963946 break :t triple;
964947 } else null;
......@@ -997,7 +980,26 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
997980
998981 // This loop is re-evaluated when the build script exits with an indication that it
999982 // could not continue due to missing lazy dependencies.
1000 const configuration_path: Path, const poisoned: bool = cp: while (true) {
983 const configuration_path: Path, var configuration_lock: ?Cache.Lock = cp: while (true) {
984 // Cache lookup for configure options. If we get a match, we can skip
985 // execution of the configure script. If not, we get the file path to pass
986 // to the configure process.
987 //
988 // In the hot path, we only check this cache, which means that also
989 // configure source files need to go in here.
990 var config_man = graph.cache.obtain();
991 defer config_man.deinit();
992
993 for (options.cached_passthru_configure) |i|
994 config_man.hash.addBytes(configure_argv[i]);
995
996 if (target_arch_os_abi) |triple|
997 config_man.hash.addBytes(triple);
998
999 // Prevents a `zig build` from getting a false positive cache hit following
1000 // a `zig build --cache-poison=ignored`.
1001 config_man.hash.add(options.cache_poison == .ignored);
1002
10011003 build_mod.deps.clearRetainingCapacity();
10021004 deps_mod.deps.clearRetainingCapacity();
10031005
......@@ -1223,7 +1225,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
12231225 .root_dir = graph.local_cache_root,
12241226 .sub_path = try arena.print("c/{s}", .{&digest}),
12251227 },
1226 false,
1228 config_man.toOwnedLock(),
12271229 };
12281230 },
12291231 .poisoned => {}, // Don't bother checking for cache hit.
......@@ -1250,11 +1252,9 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
12501252 }) });
12511253 }
12521254
1253 const rand_int = randInt(io, u64);
1254 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
12551255 const config_tmp_path: Path = .{
12561256 .root_dir = graph.local_cache_root,
1257 .sub_path = try arena.dupe(u8, tmp_dir_sub_path),
1257 .sub_path = try arena.print("tmp" ++ Dir.path.sep_str ++ "{x}", .{randInt(io, u64)}),
12581258 };
12591259 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
12601260 io,
......@@ -1300,6 +1300,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13001300 any_errors = true;
13011301 continue;
13021302 }
1303 log.info("fetching lazy dependency {s}", .{hash});
13031304 try unlazy_set.put(arena, .fromSlice(hash), {});
13041305 }
13051306 if (any_errors) return error.FailedButCacheIntact;
......@@ -1328,7 +1329,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13281329 // If it is poisoned, there is no point in moving it to cached
13291330 // location. Just leave it in the tmp directory.
13301331 if (configuration.poisoned) {
1331 break :cp .{ config_tmp_path, true };
1332 break :cp .{ config_tmp_path, null };
13321333 } else {
13331334 const digest = config_man.final();
13341335 const final_path: Path = .{
......@@ -1362,12 +1363,10 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13621363 });
13631364 };
13641365 config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});
1365 break :cp .{ final_path, false };
1366 break :cp .{ final_path, config_man.toOwnedLock() };
13661367 }
13671368 };
1368
13691369 // Hang on to the configuration file lock until we finish loading the configuration file.
1370 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
13711370 defer if (configuration_lock) |*l| l.release(io);
13721371
13731372 switch (options.print_configuration) {
lib/compiler/configurer.zig+5-1286
......@@ -4,16 +4,14 @@ const std = @import("std");
44const Allocator = std.mem.Allocator;
55const Color = std.zig.Color;
66const Configuration = std.Build.Configuration;
7const File = std.Io.File;
87const Io = std.Io;
98const Step = std.Build.Step;
10const Writer = std.Io.Writer;
119const assert = std.debug.assert;
1210const fatal = std.process.fatal;
13const fmt = std.fmt;
1411const log = std.log;
1512const mem = std.mem;
1613const process = std.process;
14const Serialize = std.Build.Serialize;
1715
1816pub const root = @import("@build");
1917pub const dependencies = @import("@dependencies");
......@@ -133,1233 +131,16 @@ pub fn main(init: process.Init.Minimal) !void {
133131 .off => .no_color,
134132 };
135133
136 builder.runBuild(root);
134 builder.runPackageScript(root);
137135
138136 if (builder.validateUserInputDidItFail()) {
139137 fatal(" access the help menu with 'zig build -h'", .{});
140138 }
141139
142 try serializePackageOptions(builder, &graph.wip_configuration);
143 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);
140 try Serialize.packageOptions(builder, &graph.wip_configuration);
141 try Serialize.systemIntegrationOptions(&graph, &graph.wip_configuration);
144142
145 var stdout_buffer: [1024]u8 = undefined;
146 var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
147 serialize(builder, &graph.wip_configuration, &file_writer.interface) catch |err| switch (err) {
148 error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}),
149 error.OutOfMemory => |e| return e,
150 };
151 file_writer.flush() catch |err| fatal("failed to write configuration output: {t}", .{err});
152
153 // This executable is short-lived and run in Debug mode, so we'd rather
154 // have `zig build` run faster than catch resource leaks in the user's
155 // build.zig script (or, frankly, this configure runner), therefore we call
156 // exit directly here rather than cleanExit.
157 process.exit(0);
158}
159
160const Serialize = struct {
161 arena: Allocator,
162 wc: *Configuration.Wip,
163 module_map: std.array_hash_map.Auto(*std.Build.Module, Configuration.Module.Index) = .empty,
164 package_map: std.array_hash_map.Auto(*std.Build, Configuration.Package.Index) = .empty,
165 /// Index corresponds to `Configuration.steps` index.
166 step_map: std.array_hash_map.Auto(*Step, void) = .empty,
167
168 fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
169 if (b.pkg_hash.len == 0) return .root;
170 const arena = s.arena;
171 const wc = s.wc;
172 const gop = try s.package_map.getOrPut(arena, b);
173 if (!gop.found_existing) {
174 gop.value_ptr.* = try wc.addExtra(Configuration.Package, .{
175 .hash = try wc.addString(b.pkg_hash),
176 .dep_prefix = try wc.addString(b.dep_prefix),
177 .root_path = try wc.addString(try b.root.toString(arena)),
178 });
179 }
180 return gop.value_ptr.*;
181 }
182
183 fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.LazyPath.OptionalIndex {
184 const wc = s.wc;
185 return @enumFromInt(switch (lp orelse return .none) {
186 .src_path => |src_path| i: {
187 const sub_path = try wc.addString(src_path.sub_path);
188 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
189 .owner = try s.builderToPackage(src_path.owner),
190 .sub_path = sub_path,
191 });
192 },
193 .generated => |generated| i: {
194 const sub_path = try wc.addString(generated.sub_path);
195 break :i try wc.addExtraErased(Configuration.LazyPath.Generated, .{
196 .flags = .{ .up = @intCast(generated.up) },
197 .index = generated.index,
198 .sub_path = sub_path,
199 });
200 },
201 .cwd_relative => |cwd_relative_sub_path| i: {
202 const sub_path = try wc.addString(cwd_relative_sub_path);
203 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
204 .flags = .{ .base = .cwd },
205 .sub_path = sub_path,
206 });
207 },
208 .relative => |relative| i: {
209 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
210 .flags = .{ .base = relative.base },
211 .sub_path = try wc.addString(relative.sub_path),
212 });
213 },
214 .dependency => |dependency| i: {
215 const sub_path = try wc.addString(dependency.sub_path);
216 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
217 .owner = try s.builderToPackage(dependency.dependency.builder),
218 .sub_path = sub_path,
219 });
220 },
221 });
222 }
223
224 fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !?Configuration.LazyPath.Index {
225 return (try addOptionalLazyPathEnum(s, lp)).unwrap();
226 }
227
228 fn addLazyPath(s: *Serialize, lp: std.Build.LazyPath) !Configuration.LazyPath.Index {
229 return @enumFromInt(@intFromEnum(try addOptionalLazyPathEnum(s, lp)));
230 }
231
232 fn addOptionalSemVer(s: *Serialize, sem_ver: ?std.SemanticVersion) !?Configuration.String {
233 return if (sem_ver) |sv| try s.wc.addSemVer(sv) else null;
234 }
235
236 fn addOptionalString(s: *Serialize, opt_slice: ?[]const u8) !?Configuration.String {
237 return if (opt_slice) |slice| try s.wc.addString(slice) else null;
238 }
239
240 fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {
241 const wc = s.wc;
242 return try wc.addDeduped(Configuration.SystemLib, .{
243 .flags = .{
244 .needed = sl.needed,
245 .weak = sl.weak,
246 .use_pkg_config = sl.use_pkg_config,
247 .preferred_link_mode = sl.preferred_link_mode,
248 .search_strategy = sl.search_strategy,
249 },
250 .name = try wc.addString(sl.name),
251 });
252 }
253
254 fn addCSourceFile(s: *Serialize, csf: *const std.Build.Module.CSourceFile) !Configuration.CSourceFile.Index {
255 const wc = s.wc;
256 const args = try initStringList(s, csf.flags);
257 return try wc.addExtra(Configuration.CSourceFile, .{
258 .flags = .{
259 .args_len = @intCast(args.len),
260 .lang = .init(csf.language),
261 },
262 .file = try addLazyPath(s, csf.file),
263 .args = .{ .slice = args },
264 });
265 }
266
267 fn addCSourceFiles(s: *Serialize, csf: *const std.Build.Module.CSourceFiles) !Configuration.CSourceFiles.Index {
268 const wc = s.wc;
269 const sub_paths = try initStringList(s, csf.files);
270 const args = try initStringList(s, csf.flags);
271 return try wc.addExtra(Configuration.CSourceFiles, .{
272 .flags = .{
273 .args_len = @intCast(args.len),
274 .lang = .init(csf.language),
275 },
276 .root = try addLazyPath(s, csf.root),
277 .sub_paths = .{ .slice = sub_paths },
278 .args = .{ .slice = args },
279 });
280 }
281
282 fn addRcSourceFile(s: *Serialize, rsf: *const std.Build.Module.RcSourceFile) !Configuration.RcSourceFile.Index {
283 const wc = s.wc;
284 const include_paths = try initLazyPathList(s, rsf.include_paths);
285 const args = try initStringList(s, rsf.flags);
286 return try wc.addExtra(Configuration.RcSourceFile, .{
287 .flags = .{
288 .args_len = @intCast(args.len),
289 .include_paths = include_paths.len != 0,
290 },
291 .file = try addLazyPath(s, rsf.file),
292 .include_paths = .{ .slice = include_paths },
293 .args = .{ .slice = args },
294 });
295 }
296
297 fn addEnvironMap(s: *Serialize, opt_map: ?*std.process.Environ.Map) !?Configuration.EnvironMap.Index {
298 const wc = s.wc;
299 const map = opt_map orelse return null;
300 return try wc.addDeduped(Configuration.EnvironMap, .{
301 .keys = try wc.addStringList(map.array_hash_map.keys()),
302 .values = try wc.addStringList(map.array_hash_map.values()),
303 });
304 }
305
306 fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuration.Step.Run.Arg.Index {
307 const wc = s.wc;
308 const result = try s.arena.alloc(Configuration.Step.Run.Arg.Index, args.len);
309 for (result, args) |*dest, src| {
310 dest.* = try wc.addExtra(Configuration.Step.Run.Arg, switch (src) {
311 .artifact => |a| .{
312 .flags = .{
313 .tag = .artifact,
314 .prefix = a.prefix.len != 0,
315 .suffix = a.suffix.len != 0,
316 .basename = false,
317 .path = false,
318 .producer = true,
319 .generated = false,
320 .dep_file = false,
321 .make_absolute = a.make_absolute,
322 },
323 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
324 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
325 .basename = .{ .value = null },
326 .path = .{ .value = null },
327 .producer = .{ .value = stepIndex(s, &a.artifact.step) },
328 .generated = .{ .value = null },
329 },
330 .lazy_path => |a| .{
331 .flags = .{
332 .tag = .path_file,
333 .prefix = a.prefix.len != 0,
334 .suffix = a.suffix.len != 0,
335 .basename = false,
336 .path = true,
337 .producer = false,
338 .generated = false,
339 .dep_file = false,
340 .make_absolute = a.make_absolute,
341 },
342 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
343 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
344 .basename = .{ .value = null },
345 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
346 .producer = .{ .value = null },
347 .generated = .{ .value = null },
348 },
349 .decorated_directory => |a| .{
350 .flags = .{
351 .tag = .path_directory,
352 .prefix = a.prefix.len != 0,
353 .suffix = a.suffix.len != 0,
354 .basename = false,
355 .path = true,
356 .producer = false,
357 .generated = false,
358 .dep_file = false,
359 .make_absolute = a.make_absolute,
360 },
361 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
362 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
363 .basename = .{ .value = null },
364 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
365 .producer = .{ .value = null },
366 .generated = .{ .value = null },
367 },
368 .file_content => |a| .{
369 .flags = .{
370 .tag = .file_content,
371 .prefix = a.prefix.len != 0,
372 .suffix = a.suffix.len != 0,
373 .basename = false,
374 .path = true,
375 .producer = false,
376 .generated = false,
377 .dep_file = false,
378 .make_absolute = false,
379 },
380 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
381 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
382 .basename = .{ .value = null },
383 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
384 .producer = .{ .value = null },
385 .generated = .{ .value = null },
386 },
387 .bytes => |a| .{
388 .flags = .{
389 .tag = .string,
390 .prefix = true,
391 .suffix = false,
392 .basename = false,
393 .path = false,
394 .producer = false,
395 .generated = false,
396 .dep_file = false,
397 .make_absolute = false,
398 },
399 .prefix = .{ .value = try wc.addString(a) },
400 .suffix = .{ .value = null },
401 .basename = .{ .value = null },
402 .path = .{ .value = null },
403 .producer = .{ .value = null },
404 .generated = .{ .value = null },
405 },
406 .output_file, .output_file_dep => |a, tag| .{
407 .flags = .{
408 .tag = .output_file,
409 .prefix = a.prefix.len != 0,
410 .suffix = a.suffix.len != 0,
411 .basename = a.basename.len != 0,
412 .path = false,
413 .producer = false,
414 .generated = true,
415 .dep_file = tag == .output_file_dep,
416 .make_absolute = a.make_absolute,
417 },
418 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
419 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
420 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
421 .path = .{ .value = null },
422 .producer = .{ .value = null },
423 .generated = .{ .value = a.generated_file },
424 },
425 .output_directory => |a| .{
426 .flags = .{
427 .tag = .output_directory,
428 .prefix = a.prefix.len != 0,
429 .suffix = a.suffix.len != 0,
430 .basename = a.basename.len != 0,
431 .path = false,
432 .producer = false,
433 .generated = true,
434 .dep_file = false,
435 .make_absolute = a.make_absolute,
436 },
437 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
438 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
439 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
440 .path = .{ .value = null },
441 .producer = .{ .value = null },
442 .generated = .{ .value = a.generated_file },
443 },
444 .passthru => .{
445 .flags = .{
446 .tag = .passthru,
447 .prefix = false,
448 .suffix = false,
449 .basename = false,
450 .path = false,
451 .producer = false,
452 .generated = false,
453 .dep_file = false,
454 .make_absolute = false,
455 },
456 .prefix = .{ .value = null },
457 .suffix = .{ .value = null },
458 .basename = .{ .value = null },
459 .path = .{ .value = null },
460 .producer = .{ .value = null },
461 .generated = .{ .value = null },
462 },
463 });
464 }
465 return result;
466 }
467
468 fn initIncludeDirList(
469 s: *Serialize,
470 list: []const std.Build.Module.IncludeDir,
471 ) ![]const Configuration.Module.IncludeDir {
472 const result = try s.arena.alloc(Configuration.Module.IncludeDir, list.len);
473 for (result, list) |*dest, src| dest.* = switch (src) {
474 .path => |lp| .{ .path = try addLazyPath(s, lp) },
475 .path_system => |lp| .{ .path_system = try addLazyPath(s, lp) },
476 .path_after => |lp| .{ .path_after = try addLazyPath(s, lp) },
477 .framework_path => |lp| .{ .framework_path = try addLazyPath(s, lp) },
478 .framework_path_system => |lp| .{ .framework_path_system = try addLazyPath(s, lp) },
479 .embed_path => |lp| .{ .embed_path = try addLazyPath(s, lp) },
480 .other_step => |cs| .{ .path = try addLazyPath(s, cs.installed_headers_include_tree.?.getDirectory()) },
481 .config_header_step => |chs| .{ .config_header_step = stepIndex(s, &chs.step) },
482 };
483 return result;
484 }
485
486 fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index {
487 const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len);
488 for (result, list) |*dest, src| dest.* = try addLazyPath(s, src);
489 return result;
490 }
491
492 fn initStringList(s: *Serialize, list: []const []const u8) ![]const Configuration.String {
493 const wc = s.wc;
494 const result = try s.arena.alloc(Configuration.String, list.len);
495 for (result, list) |*dest, src| dest.* = try wc.addString(src);
496 return result;
497 }
498
499 fn initCopyList(s: *Serialize, list: []const Step.WriteFile.Copy) ![]const Configuration.Step.WriteFile.Copy {
500 const result = try s.arena.alloc(Configuration.Step.WriteFile.Copy, list.len);
501 for (result, list) |*dest, src| dest.* = .{
502 .sub_path = src.sub_path,
503 .src_file = try s.addLazyPath(src.src_file),
504 };
505 return result;
506 }
507
508 fn initOptionalStringList(s: *Serialize, list: []const ?[]const u8) ![]const Configuration.OptionalString {
509 const wc = s.wc;
510 const result = try s.arena.alloc(Configuration.OptionalString, list.len);
511 for (result, list) |*dest, src| dest.* = try wc.addOptionalString(src);
512 return result;
513 }
514
515 fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
516 if (s.module_map.get(m)) |index| return index;
517
518 const wc = s.wc;
519 const arena = s.arena;
520
521 const rpaths = try arena.alloc(Configuration.Module.RPath, m.rpaths.items.len);
522 for (rpaths, m.rpaths.items) |*dest, src| dest.* = switch (src) {
523 .lazy_path => |lp| .{ .lazy_path = try addLazyPath(s, lp) },
524 .special => |slice| .{ .special = try wc.addString(slice) },
525 };
526
527 const link_objects = try arena.alloc(Configuration.Module.LinkObject, m.link_objects.items.len);
528 for (link_objects, m.link_objects.items) |*dest, *src| dest.* = switch (src.*) {
529 .static_path => |lp| .{ .static_path = try addLazyPath(s, lp) },
530 .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
531 .system_lib => |*sl| .{ .system_lib = try addSystemLib(s, sl) },
532 .assembly_file => |lp| .{ .assembly_file = try addLazyPath(s, lp) },
533 .c_source_file => |csf| .{ .c_source_file = try addCSourceFile(s, csf) },
534 .c_source_files => |csf| .{ .c_source_files = try addCSourceFiles(s, csf) },
535 .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },
536 };
537
538 const frameworks = try arena.alloc(Configuration.Module.Framework, m.frameworks.entries.len);
539 for (frameworks, m.frameworks.keys(), m.frameworks.values()) |*dest, name, options| dest.* = .{
540 .flags = .{
541 .needed = options.needed,
542 .weak = options.weak,
543 },
544 .name = try wc.addString(name),
545 };
546
547 const lib_paths = try initLazyPathList(s, m.lib_paths.items);
548 const c_macros = try initStringList(s, m.c_macros.items);
549 const export_symbol_names = try initStringList(s, m.export_symbol_names);
550
551 const module_index: Configuration.Module.Index = try wc.addExtra(Configuration.Module, .{
552 .flags = .{
553 .optimize = .init(m.optimize),
554 .strip = .init(m.strip),
555 .unwind_tables = .init(m.unwind_tables),
556 .dwarf_format = .init(m.dwarf_format),
557 .single_threaded = .init(m.single_threaded),
558 .stack_protector = .init(m.stack_protector),
559 .stack_check = .init(m.stack_check),
560 .sanitize_c = .init(m.sanitize_c),
561 .sanitize_thread = .init(m.sanitize_thread),
562 .fuzz = .init(m.fuzz),
563 .code_model = m.code_model,
564 .c_macros = c_macros.len != 0,
565 .include_dirs = m.include_dirs.items.len != 0,
566 .lib_paths = lib_paths.len != 0,
567 .rpaths = rpaths.len != 0,
568 .frameworks = frameworks.len != 0,
569 .link_objects = link_objects.len != 0,
570 .export_symbol_names = export_symbol_names.len != 0,
571 },
572 .flags2 = .{
573 .valgrind = .init(m.valgrind),
574 .pic = .init(m.pic),
575 .red_zone = .init(m.red_zone),
576 .omit_frame_pointer = .init(m.omit_frame_pointer),
577 .error_tracing = .init(m.error_tracing),
578 .link_libc = .init(m.link_libc),
579 .link_libcpp = .init(m.link_libcpp),
580 .no_builtin = .init(m.no_builtin),
581 },
582 .owner = try s.builderToPackage(m.owner),
583 .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file),
584 .import_table = .invalid,
585 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
586 .c_macros = .{ .slice = c_macros },
587 .lib_paths = .{ .slice = lib_paths },
588 .export_symbol_names = .{ .slice = export_symbol_names },
589 .include_dirs = .init(try s.initIncludeDirList(m.include_dirs.items)),
590 .rpaths = .init(rpaths),
591 .link_objects = .init(link_objects),
592 .frameworks = .{ .slice = frameworks },
593 });
594
595 // The import table is the only place that modules can form dependency
596 // loops. Therefore, we populate the module indexes only after adding
597 // the module to module_map.
598 try s.module_map.putNoClobber(arena, m, module_index);
599
600 var imports = try std.MultiArrayList(Configuration.ImportTable.Import).initCapacity(arena, m.import_table.entries.len);
601 imports.len = m.import_table.entries.len;
602 for (
603 imports.items(.name),
604 imports.items(.module),
605 m.import_table.keys(),
606 m.import_table.values(),
607 ) |*dest_name, *dest_module, src_name, src_module| {
608 dest_name.* = try wc.addString(src_name);
609 dest_module.* = try addModule(s, src_module);
610 }
611
612 comptime assert(std.mem.eql(u8, @typeInfo(Configuration.Module).@"struct".field_names[2], "import_table"));
613 comptime assert(@typeInfo(Configuration.Module).@"struct".field_types[2] == Configuration.ImportTable.Index);
614 assert(wc.extra.items[@intFromEnum(module_index) + 2] == @intFromEnum(Configuration.ImportTable.Index.invalid));
615 const import_table_index = try wc.addDeduped(Configuration.ImportTable, .{
616 .imports = .{ .mal = imports },
617 });
618 wc.extra.items[@intFromEnum(module_index) + 2] = @intFromEnum(import_table_index);
619
620 return module_index;
621 }
622
623 fn stepIndex(s: *const Serialize, step: *Step) Configuration.Step.Index {
624 return @enumFromInt(s.step_map.getIndex(step).?);
625 }
626};
627
628fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
629 const graph = b.graph;
630 const arena = graph.arena;
631 const gpa = wc.gpa;
632
633 var s: Serialize = .{ .wc = wc, .arena = arena };
634
635 try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len);
636 for (
637 graph.configure_dependencies.items,
638 wc.path_deps.addManyAsSliceAssumeCapacity(graph.configure_dependencies.items.len),
639 ) |src, *dest| {
640 dest.* = .{
641 .flags = .{
642 .base = switch (src.lazy_path) {
643 .src_path, .dependency => .build_root,
644 .generated => unreachable,
645 .cwd_relative => .cwd,
646 .relative => |r| r.base,
647 },
648 .mode = src.mode,
649 },
650 .sub = switch (src.lazy_path) {
651 .src_path => |sp| try wc.addString(sp.sub_path),
652 .generated => unreachable,
653 .cwd_relative => |sub_path| try wc.addString(sub_path),
654 .dependency => |d| try wc.addString(d.sub_path),
655 .relative => |r| try wc.addString(r.sub_path),
656 },
657 .pkg = switch (src.lazy_path) {
658 .src_path => |sp| .init(try s.builderToPackage(sp.owner)),
659 .generated => unreachable,
660 .cwd_relative, .relative => .none,
661 .dependency => |d| .init(try s.builderToPackage(d.dependency.builder)),
662 },
663 };
664 }
665
666 // Starting from all top-level steps in `b`, traverse the entire step graph
667 // and add all step dependencies implied by module graphs.
668 const top_level_steps = b.top_level_steps.values();
669 try s.step_map.ensureUnusedCapacity(arena, top_level_steps.len);
670 for (top_level_steps) |tls| {
671 s.step_map.putAssumeCapacityNoClobber(&tls.step, {});
672 }
673 {
674 while (wc.steps.items.len < s.step_map.count()) {
675 const step = s.step_map.keys()[wc.steps.items.len];
676
677 // Set up any implied dependencies for this step. It's important that we do this first, so
678 // that the loop below discovers steps implied by the module graph.
679 try createModuleDependenciesForStep(step);
680
681 try s.step_map.ensureUnusedCapacity(arena, step.dependencies.items.len);
682 for (step.dependencies.items) |other_step| {
683 s.step_map.putAssumeCapacity(other_step, {});
684 }
685
686 // Add and then de-duplicate dependencies.
687 const dep_steps = try arena.alloc(Configuration.Step.Index, step.dependencies.items.len);
688 for (dep_steps, step.dependencies.items) |*dest, src|
689 dest.* = @enumFromInt(s.step_map.getIndex(src).?);
690
691 const deps: Configuration.Deps.Index = try wc.addDeduped(Configuration.Deps, .{
692 .steps = .{ .slice = dep_steps },
693 });
694
695 try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);
696 wc.steps.appendAssumeCapacity(.{
697 .name = try wc.addString(step.name),
698 .owner = try s.builderToPackage(step.owner),
699 .deps = deps,
700 .max_rss = .fromBytes(step.max_rss),
701 .extended = @enumFromInt(switch (step.tag) {
702 .top_level => e: {
703 const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
704 break :e try wc.addExtraErased(Configuration.Step.TopLevel, .{
705 .description = try wc.addString(top_level.description),
706 });
707 },
708 .compile => e: {
709 const c: *Step.Compile = @fieldParentPtr("step", step);
710 const installed_headers: []u32 = try arena.alloc(u32, c.installed_headers.items.len);
711 for (installed_headers, c.installed_headers.items) |*dst, src| switch (src) {
712 .file => |file| {
713 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.File, .{
714 .source = try s.addLazyPath(file.source),
715 .dest_sub_path = try wc.addString(file.dest_rel_path),
716 });
717 },
718 .directory => |directory| {
719 const include_extensions = directory.options.include_extensions orelse &.{};
720 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.Directory, .{
721 .flags = .{
722 .include_extensions = include_extensions.len != 0,
723 .exclude_extensions = directory.options.exclude_extensions.len != 0,
724 },
725 .source = try s.addLazyPath(directory.source),
726 .dest_sub_path = try wc.addString(directory.dest_rel_path),
727 .exclude_extensions = .{ .slice = try s.initStringList(directory.options.exclude_extensions) },
728 .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
729 });
730 },
731 };
732
733 break :e try wc.addExtraErased(Configuration.Step.Compile, .{
734 .flags = .{
735 .filters_len = c.filters.len != 0,
736 .installed_headers_len = installed_headers.len != 0,
737 .force_undefined_symbols_len = c.force_undefined_symbols.entries.len != 0,
738
739 .verbose_link = c.verbose_link,
740 .verbose_cc = c.verbose_cc,
741 .rdynamic = c.rdynamic,
742 .import_memory = c.import_memory,
743 .export_memory = c.export_memory,
744 .import_symbols = c.import_symbols,
745 .import_table = c.import_table,
746 .export_table = c.export_table,
747 .shared_memory = c.shared_memory,
748 .link_eh_frame_hdr = c.link_eh_frame_hdr,
749 .link_emit_relocs = c.link_emit_relocs,
750 .link_function_sections = c.link_function_sections,
751 .link_data_sections = c.link_data_sections,
752 .linker_dynamicbase = c.linker_dynamicbase,
753 .link_z_notext = c.link_z_notext,
754 .link_z_relro = c.link_z_relro,
755 .link_z_lazy = c.link_z_lazy,
756 .link_z_defs = c.link_z_defs,
757 .headerpad_max_install_names = c.headerpad_max_install_names,
758 .dead_strip_dylibs = c.dead_strip_dylibs,
759 .force_load_objc = c.force_load_objc,
760 .discard_local_symbols = c.discard_local_symbols,
761 .mingw_unicode_entry_point = c.mingw_unicode_entry_point,
762 },
763 .flags2 = .{
764 .pie = .init(c.pie),
765 .formatted_panics = .init(c.formatted_panics),
766 .bundle_compiler_rt = .init(c.bundle_compiler_rt),
767 .bundle_ubsan_rt = .init(c.bundle_ubsan_rt),
768 .each_lib_rpath = .init(c.each_lib_rpath),
769 .link_gc_sections = .init(c.link_gc_sections),
770 .linker_allow_shlib_undefined = .init(c.linker_allow_shlib_undefined),
771 .linker_allow_undefined_version = .init(c.linker_allow_undefined_version),
772 .linker_enable_new_dtags = .init(c.linker_enable_new_dtags),
773 .dll_export_fns = .init(c.dll_export_fns),
774 .use_llvm = .init(c.use_llvm),
775 .use_lld = .init(c.use_lld),
776 .use_new_linker = .init(c.use_new_linker),
777 .allow_so_scripts = .init(c.allow_so_scripts),
778 .sanitize_coverage_trace_pc_guard = .init(c.sanitize_coverage_trace_pc_guard),
779 .linkage = .init(c.linkage),
780 },
781 .flags3 = .{
782 .is_linking_libc = c.is_linking_libc,
783 .is_linking_libcpp = c.is_linking_libcpp,
784 .version = c.version != null,
785 .compress_debug_sections = c.compress_debug_sections,
786 .initial_memory = c.initial_memory != null,
787 .max_memory = c.max_memory != null,
788 .kind = c.kind,
789 .global_base = c.global_base != null,
790 .test_runner = if (c.test_runner) |tr| switch (tr.mode) {
791 .simple => .simple,
792 .server => .server,
793 } else .default,
794 .wasi_exec_model = .init(c.wasi_exec_model),
795 .win32_manifest = c.win32_manifest != null,
796 .win32_module_definition = c.win32_module_definition != null,
797 .zig_lib_dir = c.zig_lib_dir != null,
798 .rc_includes = c.rc_includes,
799 .image_base = c.image_base != null,
800 .build_id = .init(c.build_id),
801 .entry = switch (c.entry) {
802 .default => .default,
803 .disabled => .disabled,
804 .enabled => .enabled,
805 .symbol_name => .symbol_name,
806 },
807 .lto = .init(c.lto),
808 .subsystem = .init(c.subsystem),
809 },
810 .flags4 = .{
811 .libc_file = c.libc_file != null,
812 .link_z_common_page_size = c.link_z_common_page_size != null,
813 .link_z_max_page_size = c.link_z_max_page_size != null,
814 .pagezero_size = c.pagezero_size != null,
815 .stack_size = c.stack_size != null,
816 .headerpad_size = c.headerpad_size != null,
817 .error_limit = c.error_limit != null,
818 .install_name = c.install_name != null,
819 .entitlements = c.entitlements != null,
820 .expect_errors = if (c.expect_errors) |x| switch (x) {
821 .contains => .contains,
822 .exact => .exact,
823 .starts_with => .starts_with,
824 .stderr_contains => .stderr_contains,
825 } else .none,
826 .linker_script = c.linker_script != null,
827 .version_script = c.version_script != null,
828 .emit_directory = c.emit_directory != .none,
829 .generated_docs = c.generated_docs != .none,
830 .generated_asm = c.generated_asm != .none,
831 .generated_bin = c.generated_bin != .none,
832 .generated_pdb = c.generated_pdb != .none,
833 .generated_implib = c.generated_implib != .none,
834 .generated_llvm_bc = c.generated_llvm_bc != .none,
835 .generated_llvm_ir = c.generated_llvm_ir != .none,
836 .generated_h = c.generated_h != .none,
837 .incremental = .init(c.incremental),
838 },
839 .root_module = try s.addModule(c.root_module),
840 .root_name = try wc.addString(c.name),
841 .linker_script = .{ .value = try s.addOptionalLazyPath(c.linker_script) },
842 .version_script = .{ .value = try s.addOptionalLazyPath(c.version_script) },
843 .zig_lib_dir = .{ .value = try s.addOptionalLazyPath(c.zig_lib_dir) },
844 .libc_file = .{ .value = try s.addOptionalLazyPath(c.libc_file) },
845 .win32_manifest = .{ .value = try s.addOptionalLazyPath(c.win32_manifest) },
846 .win32_module_definition = .{ .value = try s.addOptionalLazyPath(c.win32_module_definition) },
847 .entitlements = .{ .value = try s.addOptionalLazyPath(c.entitlements) },
848 .version = .{ .value = try s.addOptionalSemVer(c.version) },
849 .install_name = .{ .value = try s.addOptionalString(c.install_name) },
850 .initial_memory = .{ .value = c.initial_memory },
851 .max_memory = .{ .value = c.max_memory },
852 .global_base = .{ .value = c.global_base },
853 .image_base = .{ .value = c.image_base },
854 .link_z_common_page_size = .{ .value = c.link_z_common_page_size },
855 .link_z_max_page_size = .{ .value = c.link_z_max_page_size },
856 .pagezero_size = .{ .value = c.pagezero_size },
857 .stack_size = .{ .value = c.stack_size },
858 .headerpad_size = .{ .value = c.headerpad_size },
859 .error_limit = .{ .value = c.error_limit },
860 .entry = .{ .value = switch (c.entry) {
861 .symbol_name => |name| try wc.addString(name),
862 .default, .disabled, .enabled => null,
863 } },
864 .build_id = .{ .value = if (c.build_id) |id| switch (id) {
865 .hexstring => |*hexstring| try wc.addString(hexstring.toSlice()),
866 .none, .fast, .uuid, .sha1, .md5 => null,
867 } else null },
868 .filters = .{ .slice = try s.initStringList(c.filters) },
869 .installed_headers = .initErased(installed_headers),
870 .force_undefined_symbols = .{ .slice = try s.initStringList(c.force_undefined_symbols.keys()) },
871 .expect_errors = .{ .u = if (c.expect_errors) |x| switch (x) {
872 .contains => |slice| .{ .contains = try wc.addString(slice) },
873 .exact => |exact| .{ .exact = .{ .slice = try s.initStringList(exact) } },
874 .starts_with => |slice| .{ .starts_with = try wc.addString(slice) },
875 .stderr_contains => |slice| .{ .stderr_contains = try wc.addString(slice) },
876 } else .none },
877 .test_runner = .{ .u = if (c.test_runner) |tr| switch (tr.mode) {
878 .simple => .{ .simple = try s.addLazyPath(tr.path) },
879 .server => .{ .server = try s.addLazyPath(tr.path) },
880 } else .default },
881
882 .emit_directory = .{ .value = c.emit_directory.unwrap() },
883 .generated_docs = .{ .value = c.generated_docs.unwrap() },
884 .generated_asm = .{ .value = c.generated_asm.unwrap() },
885 .generated_bin = .{ .value = c.generated_bin.unwrap() },
886 .generated_pdb = .{ .value = c.generated_pdb.unwrap() },
887 .generated_implib = .{ .value = c.generated_implib.unwrap() },
888 .generated_llvm_bc = .{ .value = c.generated_llvm_bc.unwrap() },
889 .generated_llvm_ir = .{ .value = c.generated_llvm_ir.unwrap() },
890 .generated_h = .{ .value = c.generated_h.unwrap() },
891 });
892 },
893 .install_artifact => e: {
894 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
895 break :e try wc.addExtraErased(Configuration.Step.InstallArtifact, .{
896 .flags = .{
897 .dylib_symlinks = ia.dylib_symlinks,
898 .bin_dir = ia.dest_dir != null,
899 .implib_dir = ia.implib_dir != null,
900 .pdb_dir = ia.pdb_dir != null,
901 .h_dir = ia.h_dir != null,
902 .bin_sub_path = ia.dest_sub_path != null,
903 },
904 .bin_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.dest_dir) },
905 .implib_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.implib_dir) },
906 .pdb_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.pdb_dir) },
907 .h_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.h_dir) },
908 .bin_sub_path = .{ .value = try s.addOptionalString(ia.dest_sub_path) },
909 });
910 },
911 .install_file => e: {
912 const sif: *Step.InstallFile = @fieldParentPtr("step", step);
913 break :e try wc.addExtraErased(Configuration.Step.InstallFile, .{
914 .source = try s.addLazyPath(sif.source),
915 .dest_dir = try addInstallDir(wc, sif.dir),
916 .dest_sub_path = try wc.addString(sif.dest_rel_path),
917 });
918 },
919 .install_dir => e: {
920 const sid: *Step.InstallDir = @fieldParentPtr("step", step);
921 const dest_sub_path: ?[]const u8 = if (sid.options.install_subdir.len != 0)
922 sid.options.install_subdir
923 else
924 null;
925 const include_extensions = sid.options.include_extensions orelse &.{};
926 break :e try wc.addExtraErased(Configuration.Step.InstallDir, .{
927 .flags = .{
928 .dest_sub_path = dest_sub_path != null,
929 .exclude_extensions = sid.options.exclude_extensions.len != 0,
930 .include_extensions = include_extensions.len != 0,
931 .include_extensions_active = sid.options.include_extensions != null,
932 .blank_extensions = sid.options.blank_extensions.len != 0,
933 },
934 .source_dir = try s.addLazyPath(sid.options.source_dir),
935 .dest_dir = try addInstallDir(wc, sid.options.install_dir),
936 .dest_sub_path = .{ .value = try s.addOptionalString(dest_sub_path) },
937 .exclude_extensions = .{ .slice = try s.initStringList(sid.options.exclude_extensions) },
938 .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
939 .blank_extensions = .{ .slice = try s.initStringList(sid.options.blank_extensions) },
940 });
941 },
942 .fail => e: {
943 const sf: *Step.Fail = @fieldParentPtr("step", step);
944 break :e try wc.addExtraErased(Configuration.Step.Fail, .{
945 .msg = sf.error_msg,
946 });
947 },
948 .find_program => e: {
949 const fp: *Step.FindProgram = @fieldParentPtr("step", step);
950 break :e try wc.addExtraErased(Configuration.Step.FindProgram, .{
951 .names = fp.names,
952 .found_path = fp.found_path,
953 });
954 },
955 .fmt => e: {
956 const sf: *Step.Fmt = @fieldParentPtr("step", step);
957 break :e try wc.addExtraErased(Configuration.Step.Fmt, .{
958 .flags = .{
959 .paths = sf.paths.len != 0,
960 .exclude_paths = sf.exclude_paths.len != 0,
961 .check = sf.check,
962 },
963 .paths = .{ .slice = try s.initLazyPathList(sf.paths) },
964 .exclude_paths = .{ .slice = try s.initLazyPathList(sf.exclude_paths) },
965 });
966 },
967 .translate_c => e: {
968 const tc: *Step.TranslateC = @fieldParentPtr("step", step);
969
970 const system_libs = try arena.alloc(Configuration.SystemLib.Index, tc.system_libs.items.len);
971 for (system_libs, tc.system_libs.items) |*dest, *src| dest.* = try s.addSystemLib(src);
972
973 break :e try wc.addExtraErased(Configuration.Step.TranslateC, .{
974 .flags = .{
975 .include_dirs = tc.include_dirs.items.len != 0,
976 .system_libs = system_libs.len != 0,
977 .c_macros = tc.c_macros.items.len != 0,
978 .link_libc = tc.link_libc,
979 .optimize = .init(tc.optimize),
980 },
981 .src_path = try s.addLazyPath(tc.source),
982 .output_file = tc.output_file,
983 .include_dirs = .init(try s.initIncludeDirList(tc.include_dirs.items)),
984 .system_libs = .{ .slice = system_libs },
985 .c_macros = .{ .slice = tc.c_macros.items },
986 .target = try addOptionalResolvedTarget(wc, tc.target),
987 });
988 },
989 .write_file => e: {
990 const wf: *Step.WriteFile = @fieldParentPtr("step", step);
991
992 const directories = try arena.alloc(
993 Configuration.Step.WriteFile.Directory,
994 wf.directories.items.len,
995 );
996 for (directories, wf.directories.items) |*dest, src| dest.* = .{
997 .sub_path = src.sub_path,
998 .src_path = try s.addLazyPath(src.src_path),
999 .exclude_extensions = src.exclude_extensions,
1000 .include_extensions = src.include_extensions,
1001 };
1002
1003 break :e try wc.addExtraErased(Configuration.Step.WriteFile, .{
1004 .flags = .{
1005 .embeds = wf.embeds.items.len != 0,
1006 .copies = wf.copies.items.len != 0,
1007 .directories = directories.len != 0,
1008 .mode = switch (wf.mode) {
1009 .whole_cached => .whole_cached,
1010 .tmp => .tmp,
1011 .mutate => .mutate,
1012 },
1013 },
1014 .generated_directory = wf.generated_directory,
1015 .embeds = .{ .slice = wf.embeds.items },
1016 .copies = .{ .slice = try s.initCopyList(wf.copies.items) },
1017 .directories = .{ .slice = directories },
1018 .mutate_path = .{ .value = switch (wf.mode) {
1019 .mutate => |lp| try s.addLazyPath(lp),
1020 .whole_cached, .tmp => null,
1021 } },
1022 });
1023 },
1024 .update_source_files => e: {
1025 const usf: *Step.UpdateSourceFiles = @fieldParentPtr("step", step);
1026 break :e try wc.addExtraErased(Configuration.Step.UpdateSourceFiles, .{
1027 .flags = .{
1028 .embeds = usf.embeds.items.len != 0,
1029 .copies = usf.copies.items.len != 0,
1030 },
1031 .embeds = .{ .slice = usf.embeds.items },
1032 .copies = .{ .slice = try s.initCopyList(usf.copies.items) },
1033 });
1034 },
1035 .run => e: {
1036 const run: *Step.Run = @fieldParentPtr("step", step);
1037 var expect_stderr_exact: ?Configuration.Bytes = null;
1038 var expect_stdout_exact: ?Configuration.Bytes = null;
1039 var expect_stderr_match: std.ArrayList(Configuration.Bytes) = .empty;
1040 var expect_stdout_match: std.ArrayList(Configuration.Bytes) = .empty;
1041 var expect_term: ?struct {
1042 status: Configuration.Step.Run.ExpectTermStatus,
1043 value: u32,
1044 } = null;
1045 var expect_stderr_snapshot: ?Configuration.LazyPath.Index = null;
1046 var expect_stdout_snapshot: ?Configuration.LazyPath.Index = null;
1047 switch (run.stdio) {
1048 .check => |checks| for (checks.items) |check| switch (check) {
1049 .expect_stderr_exact => |bytes| expect_stderr_exact = try wc.addBytes(bytes),
1050 .expect_stdout_exact => |bytes| expect_stdout_exact = try wc.addBytes(bytes),
1051 .expect_stderr_match => |bytes| {
1052 try expect_stderr_match.append(arena, try wc.addBytes(bytes));
1053 },
1054 .expect_stdout_match => |bytes| {
1055 try expect_stdout_match.append(arena, try wc.addBytes(bytes));
1056 },
1057 .expect_term => |t| expect_term = switch (t) {
1058 .exited => |x| .{ .status = .exited, .value = x },
1059 .signal => |x| .{ .status = .signal, .value = @intFromEnum(x) },
1060 .stopped => |x| .{ .status = .stopped, .value = @intFromEnum(x) },
1061 .unknown => |x| .{ .status = .unknown, .value = x },
1062 },
1063 .expect_stderr_snapshot => |path| expect_stderr_snapshot = try s.addLazyPath(path),
1064 .expect_stdout_snapshot => |path| expect_stdout_snapshot = try s.addLazyPath(path),
1065 },
1066 else => {},
1067 }
1068
1069 break :e try wc.addExtraErased(Configuration.Step.Run, .{
1070 .flags = .{
1071 .disable_zig_progress = run.disable_zig_progress,
1072 .skip_foreign_checks = run.skip_foreign_checks,
1073 .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error,
1074 .has_side_effects = run.has_side_effects,
1075 .test_runner_mode = run.test_runner_mode,
1076 .color = run.color,
1077 .stdio = switch (run.stdio) {
1078 .infer_from_args => .infer_from_args,
1079 .inherit => .inherit,
1080 .check => .check,
1081 .zig_test => .zig_test,
1082 },
1083 .stdin = switch (run.stdin) {
1084 .none => .none,
1085 .bytes => .bytes,
1086 .lazy_path => .lazy_path,
1087 },
1088 .stdout_trim_whitespace = if (run.captured_stdout) |cs| cs.trim_whitespace else .none,
1089 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none,
1090 .stdio_limit = run.stdio_limit != .unlimited,
1091 .producer = run.producer != null,
1092 .cwd = run.cwd != null,
1093 .captured_stdout = run.captured_stdout != null,
1094 .captured_stderr = run.captured_stderr != null,
1095 .environ_map = run.environ_map != null,
1096 },
1097 .flags2 = .{
1098 .expect_stderr_exact = expect_stderr_exact != null,
1099 .expect_stdout_exact = expect_stdout_exact != null,
1100 .expect_stderr_match = expect_stderr_match.items.len != 0,
1101 .expect_stdout_match = expect_stdout_match.items.len != 0,
1102 .expect_term = expect_term != null,
1103 .expect_term_status = if (expect_term) |t| t.status else .exited,
1104 .expect_stderr_snapshot = expect_stderr_snapshot != null,
1105 .expect_stdout_snapshot = expect_stdout_snapshot != null,
1106 },
1107 .file_inputs = .{ .slice = try s.initLazyPathList(run.file_inputs.items) },
1108 .args = .{ .slice = try s.initArgsList(run.argv.items) },
1109 .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
1110 .preopen_names = .{ .slice = try s.initStringList(run.preopens.keys()) },
1111 .preopen_paths = .{ .slice = try s.initLazyPathList(run.preopens.values()) },
1112 .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
1113 .basename = try wc.addString(cs.basename),
1114 .generated_file = cs.generated_file,
1115 } else null },
1116 .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
1117 .basename = try wc.addString(cs.basename),
1118 .generated_file = cs.generated_file,
1119 } else null },
1120 .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
1121 .expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
1122 .stdio_limit = .{ .value = run.stdio_limit.toInt64() },
1123 .producer = .{ .value = if (run.producer) |cs| s.stepIndex(&cs.step) else null },
1124 .expect_stderr_exact = .{ .value = if (expect_stderr_exact) |bytes| bytes else null },
1125 .expect_stdout_exact = .{ .value = if (expect_stdout_exact) |bytes| bytes else null },
1126 .expect_stderr_match = .{ .slice = expect_stderr_match.items },
1127 .expect_stdout_match = .{ .slice = expect_stdout_match.items },
1128 .expect_stderr_snapshot = .{ .value = expect_stderr_snapshot orelse null },
1129 .expect_stdout_snapshot = .{ .value = expect_stdout_snapshot orelse null },
1130 .stdin = .{ .u = switch (run.stdin) {
1131 .none => .none,
1132 .bytes => |bytes| .{ .bytes = try wc.addBytes(bytes) },
1133 .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) },
1134 } },
1135 });
1136 },
1137 .check_file => e: {
1138 const cf: *Step.CheckFile = @fieldParentPtr("step", step);
1139 break :e try wc.addExtraErased(Configuration.Step.CheckFile, .{
1140 .flags = .{
1141 .expected_exact = cf.expected_exact != null,
1142 .expected_matches = cf.expected_matches.len != 0,
1143 .max_bytes = cf.max_bytes != null,
1144 },
1145 .file = try s.addLazyPath(cf.file),
1146 .expected_exact = .{ .value = cf.expected_exact },
1147 .expected_matches = .{ .slice = cf.expected_matches },
1148 .max_bytes = .{ .value = cf.max_bytes },
1149 });
1150 },
1151 .config_header => e: {
1152 const ch: *Step.ConfigHeader = @fieldParentPtr("step", step);
1153 const lazy_path: ?std.Build.LazyPath = ch.style.getPath();
1154 const pairs = try arena.alloc(Configuration.Step.ConfigHeader.Value.Pair, ch.values.count());
1155 for (pairs, ch.values.keys(), ch.values.values()) |*pair, key, value| pair.* = .{
1156 .key = try wc.addString(key),
1157 .index = switch (value) {
1158 .undef => .undef,
1159 .defined => .defined,
1160 .boolean => |x| switch (x) {
1161 false => .bool_false,
1162 true => .bool_true,
1163 },
1164 .int => |x| switch (x) {
1165 0 => .int_0,
1166 1 => .int_1,
1167 else => try wc.addExtra(Configuration.Step.ConfigHeader.Value, .initSigned(x)),
1168 },
1169 .ident => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
1170 .flags = .{
1171 .tag = .ident,
1172 .small = 0,
1173 },
1174 .i64 = .{ .value = null },
1175 .u64 = .{ .value = null },
1176 .ident = .{ .value = try wc.addString(x) },
1177 .string = .{ .value = null },
1178 }),
1179 .string => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
1180 .flags = .{
1181 .tag = .string,
1182 .small = 0,
1183 },
1184 .i64 = .{ .value = null },
1185 .u64 = .{ .value = null },
1186 .ident = .{ .value = null },
1187 .string = .{ .value = try wc.addString(x) },
1188 }),
1189 },
1190 };
1191 break :e try wc.addExtraErased(Configuration.Step.ConfigHeader, .{
1192 .flags = .{
1193 .template_file = lazy_path != null,
1194 .style = .init(ch.style),
1195 .input_size_limit = ch.input_size_limit != null,
1196 .include_guard = ch.include_guard != .none,
1197 },
1198 .template_file = .{ .value = try s.addOptionalLazyPath(lazy_path) },
1199 .generated_dir = ch.generated_dir,
1200 .input_size_limit = .{ .value = ch.input_size_limit },
1201 .include_path = try wc.addString(ch.include_path),
1202 .include_guard = .{ .value = ch.include_guard.unwrap() },
1203 .values = .{ .slice = pairs },
1204 });
1205 },
1206 .obj_copy => e: {
1207 const oc: *Step.ObjCopy = @fieldParentPtr("step", step);
1208
1209 const debug_basename: ?Configuration.String = if (oc.debug_file) |df|
1210 df.basename.unwrap()
1211 else
1212 null;
1213
1214 const debug_file: ?Configuration.GeneratedFileIndex = if (oc.debug_file) |df|
1215 df.output_file
1216 else
1217 null;
1218
1219 const add_sections = try arena.alloc(
1220 Configuration.Step.ObjCopy.AddSection,
1221 oc.add_sections.items.len,
1222 );
1223 for (add_sections, oc.add_sections.items) |*dest, src| dest.* = .{
1224 .section_name = src.section_name,
1225 .file_path = try s.addLazyPath(src.file_path),
1226 };
1227
1228 break :e try wc.addExtraErased(Configuration.Step.ObjCopy, .{
1229 .flags = .{
1230 .basename = oc.basename != .none,
1231 .debug_file = debug_file != null,
1232 .debug_basename = debug_basename != null,
1233 .format = .init(oc.format),
1234 .strip = oc.strip,
1235 .compress_debug = oc.compress_debug,
1236 .only_section = oc.only_section != .none,
1237 .pad_to = oc.pad_to != null,
1238 .add_section = add_sections.len != 0,
1239 .update_section = oc.update_sections.items.len != 0,
1240 },
1241 .input_file = try s.addLazyPath(oc.input_file),
1242 .output_file = oc.output_file,
1243 .basename = .{ .value = oc.basename.unwrap() },
1244 .debug_file = .{ .value = debug_file },
1245 .debug_basename = .{ .value = debug_basename },
1246 .only_section = .{ .value = oc.only_section.unwrap() },
1247 .pad_to = .{ .value = oc.pad_to },
1248 .add_section = .{ .slice = add_sections },
1249 .update_section = .{ .slice = oc.update_sections.items },
1250 });
1251 },
1252 .options => e: {
1253 const so: *Step.Options = @fieldParentPtr("step", step);
1254
1255 const args = try arena.alloc(Configuration.Step.Options.Arg, so.args.items.len);
1256 for (args, so.args.items) |*dest, src| dest.* = .{
1257 .name = src.name,
1258 .path = try s.addLazyPath(src.path),
1259 };
1260
1261 break :e try wc.addExtraErased(Configuration.Step.Options, .{
1262 .flags = .{
1263 .args = so.args.items.len != 0,
1264 },
1265 .generated_file = so.generated_file,
1266 .contents = try wc.addBytes(so.contents.items),
1267 .args = .{ .slice = args },
1268 });
1269 },
1270 }),
1271 });
1272 }
1273 }
1274
1275 try wc.unlazy_deps.ensureUnusedCapacity(gpa, graph.needed_lazy_dependencies.keys().len);
1276 for (graph.needed_lazy_dependencies.keys()) |k| {
1277 wc.unlazy_deps.appendAssumeCapacity(try wc.addString(k));
1278 }
1279
1280 try wc.write(writer, .{
1281 .default_step = s.stepIndex(b.default_step),
1282 .generated_files_len = @intCast(graph.generated_files.items.len),
1283 .poisoned = switch (graph.cache_poison) {
1284 .pure, .disallowed, .ignored => false,
1285 .poisoned => true,
1286 },
1287 });
1288}
1289
1290fn addOptionalResolvedTarget(
1291 wc: *Configuration.Wip,
1292 optional_resolved_target: ?std.Build.ResolvedTarget,
1293) !Configuration.ResolvedTarget.OptionalIndex {
1294 const resolved_target = optional_resolved_target orelse return .none;
1295 return .init(try wc.addDeduped(Configuration.ResolvedTarget, .{
1296 .query = try wc.addTargetQuery(&resolved_target.query),
1297 .result = try wc.addTarget(resolved_target.result),
1298 }));
1299}
1300
1301fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir {
1302 switch (install_dir orelse return .none) {
1303 .prefix => return .prefix,
1304 .lib => return .lib,
1305 .bin => return .bin,
1306 .header => return .header,
1307 .custom => |sub_path| return .initCustom(try wc.addString(sub_path)),
1308 }
1309}
1310
1311fn addInstallDirDefaultNull(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !?Configuration.InstallDestDir {
1312 return try addInstallDir(wc, install_dir orelse return null);
1313}
1314
1315/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
1316/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
1317fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
1318 const root_module = if (step.cast(Step.Compile)) |cs| root: {
1319 break :root cs.root_module;
1320 } else return; // not a compile step so no module dependencies
1321
1322 // Starting from `root_module`, discover all modules in this graph.
1323 const modules = root_module.getGraph().modules;
1324
1325 // For each of those modules, set up the implied step dependencies.
1326 for (modules) |mod| {
1327 if (mod.root_source_file) |lp| lp.addStepDependencies(step);
1328 for (mod.include_dirs.items) |include_dir| switch (include_dir) {
1329 .path,
1330 .path_system,
1331 .path_after,
1332 .framework_path,
1333 .framework_path_system,
1334 .embed_path,
1335 => |lp| lp.addStepDependencies(step),
1336
1337 .other_step => |other| {
1338 other.getEmittedIncludeTree().addStepDependencies(step);
1339 step.dependOn(&other.step);
1340 },
1341
1342 .config_header_step => |other| step.dependOn(&other.step),
1343 };
1344 for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
1345 for (mod.rpaths.items) |rpath| switch (rpath) {
1346 .lazy_path => |lp| lp.addStepDependencies(step),
1347 .special => {},
1348 };
1349 for (mod.link_objects.items) |link_object| switch (link_object) {
1350 .static_path,
1351 .assembly_file,
1352 => |lp| lp.addStepDependencies(step),
1353 .other_step => |other| step.dependOn(&other.step),
1354 .system_lib => {},
1355 .c_source_file => |source| source.file.addStepDependencies(step),
1356 .c_source_files => |source_files| source_files.root.addStepDependencies(step),
1357 .win32_resource_file => |rc_source| {
1358 rc_source.file.addStepDependencies(step);
1359 for (rc_source.include_paths) |lp| lp.addStepDependencies(step);
1360 },
1361 };
1362 }
143 builder.serializeConfigurationExiting();
1363144}
1364145
1365146fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
......@@ -1381,69 +162,7 @@ fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []cons
1381162 return arg;
1382163}
1383164
1384const ErrorStyle = enum {
1385 verbose,
1386 minimal,
1387 verbose_clear,
1388 minimal_clear,
1389 fn verboseContext(s: ErrorStyle) bool {
1390 return switch (s) {
1391 .verbose, .verbose_clear => true,
1392 .minimal, .minimal_clear => false,
1393 };
1394 }
1395 fn clearOnUpdate(s: ErrorStyle) bool {
1396 return switch (s) {
1397 .verbose, .minimal => false,
1398 .verbose_clear, .minimal_clear => true,
1399 };
1400 }
1401};
1402const MultilineErrors = enum { indent, newline, none };
1403const Summary = enum { all, new, failures, line, none };
1404
1405165fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
1406166 log.info("to access the help menu: zig build -h", .{});
1407167 fatal(f, args);
1408168}
1409
1410fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
1411 const gpa = wc.gpa;
1412
1413 var bad = false;
1414 try wc.system_integrations.ensureTotalCapacityPrecise(gpa, graph.system_integration_options.entries.len);
1415 for (graph.system_integration_options.keys(), graph.system_integration_options.values()) |k, v| {
1416 wc.system_integrations.appendAssumeCapacity(.{
1417 .name = try wc.addString(k),
1418 .status = switch (v) {
1419 .user_disabled, .user_enabled => x: {
1420 // The user tried to enable or disable a system library integration, but
1421 // the configure script did not recognize that option.
1422 log.err("system integration name not recognized by configure script: {s}", .{k});
1423 bad = true;
1424 break :x .disabled;
1425 },
1426 .declared_disabled => .disabled,
1427 .declared_enabled => .enabled,
1428 },
1429 });
1430 }
1431 if (bad) {
1432 log.info("help menu contains available options: zig build -h", .{});
1433 process.exit(1);
1434 }
1435}
1436
1437fn serializePackageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void {
1438 const gpa = wc.gpa;
1439
1440 try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count());
1441 for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| {
1442 wc.available_options.appendAssumeCapacity(.{
1443 .name = try wc.addString(name),
1444 .description = try wc.addString(opt.description),
1445 .type = opt.type_id,
1446 .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none,
1447 });
1448 }
1449}
lib/std/Build.zig+87-42
......@@ -16,6 +16,7 @@ const process = std.process;
1616const File = std.Io.File;
1717const Sha256 = std.crypto.hash.sha2.Sha256;
1818const ArrayList = std.ArrayList;
19const fatal = std.process.fatal;
1920
2021pub const Cache = @import("Build/Cache.zig");
2122pub const Step = @import("Build/Step.zig");
......@@ -23,6 +24,8 @@ pub const Module = @import("Build/Module.zig");
2324pub const abi = @import("Build/abi.zig");
2425/// The serialized output of configure phase ingested by make phase.
2526pub const Configuration = @import("Build/Configuration.zig");
27/// Logic that transforms `Build` into `Configuration`.
28pub const Serialize = @import("Build/Serialize.zig");
2629
2730/// Shared state among all Build instances.
2831graph: *Graph,
......@@ -1987,13 +1990,13 @@ pub fn run(b: *Build, argv: []const []const u8) []u8 {
19871990 .stderr_behavior = .inherit,
19881991 })) {
19891992 .success => |stdout| return stdout,
1990 .spawn_failed => |err| process.fatal("the following command failed with {t}:\n{s}", .{
1993 .spawn_failed => |err| fatal("the following command failed with {t}:\n{s}", .{
19911994 err, std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"),
19921995 }),
1993 .bad_exit_code => |code| process.fatal("the following command exited with code {d}:\n{s}", .{
1996 .bad_exit_code => |code| fatal("the following command exited with code {d}:\n{s}", .{
19941997 code, std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"),
19951998 }),
1996 .crashed => process.fatal("the following command crashed:\n{s}", .{
1999 .crashed => fatal("the following command crashed:\n{s}", .{
19972000 std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"),
19982001 }),
19992002 }
......@@ -2075,7 +2078,7 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
20752078 for (b.available_deps) |dep| {
20762079 if (mem.eql(u8, dep[0], name)) return dep[1];
20772080 }
2078 std.log.info("all dependencies used by build.zig must be declared in corresponding build.zig.zon", .{});
2081 log.info("all dependencies used by build.zig must be declared in corresponding build.zig.zon", .{});
20792082 if (b.pkg_hash.len == 0) panic("no dependency named {s}", .{name});
20802083 panic("no dependency named {s} in {s} ({s})", .{ name, b.dep_prefix, b.pkg_hash });
20812084}
......@@ -2109,21 +2112,29 @@ fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
21092112 b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
21102113}
21112114
2112/// When this function is called, it means that the current build does, in
2113/// fact, require this dependency. If the dependency is already fetched, it
2114/// proceeds in the same manner as `dependency`. However if the dependency was
2115/// not fetched, then when the build script is finished running, the build will
2116/// not proceed to the make phase. Instead, the parent process will
2117/// additionally fetch all the lazy dependencies that were actually required by
2118/// running the build script, rebuild the build script, and then run it again.
2119/// In other words, if this function returns `null` it means that the only
2120/// purpose of completing the configure phase is to find out all the other lazy
2121/// dependencies that are also required.
2122///
2123/// It is allowed to use this function for non-lazy dependencies, in which case
2124/// it will never return `null`. This allows toggling laziness via
2125/// build.zig.zon without changing build.zig logic.
2115/// Deprecated in favor of `dependencyLazy`.
21262116pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
2117 return dependencyLazy(b, name, args) catch |err| switch (err) {
2118 error.LazyDependencyNeeded => null,
2119 };
2120}
2121
2122/// Declares that the current configuration does in fact require a potentially
2123/// lazy dependency.
2124///
2125/// If the dependency is already fetched, it is returned. However if the
2126/// dependency is not yet fetched, then when the build script is finished
2127/// running, the toolchain will not proceed to the make phase. Instead, the
2128/// parent process will additionally fetch all the lazy dependencies that were
2129/// actually required by running the build script, recompile the build script,
2130/// and then run it again. In other words, if this function returns
2131/// `error.LazyDependencyNeeded` it means that the only purpose of completing
2132/// the configure phase is to find out all the other lazy dependencies that are
2133/// also required. In this case, one must propagate the error all the way up
2134/// and return it from the main build function.
2135///
2136/// For non-lazy dependencies, this always succeeds.
2137pub fn dependencyLazy(b: *Build, name: []const u8, args: anytype) error{LazyDependencyNeeded}!*Dependency {
21272138 const build_runner = @import("root");
21282139 const deps = build_runner.dependencies;
21292140 const pkg_hash = findPkgHashOrFatal(b, name);
......@@ -2134,31 +2145,34 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
21342145 const available = !@hasDecl(pkg, "available") or pkg.available;
21352146 if (!available) {
21362147 markNeededLazyDep(b, pkg_hash);
2137 return null;
2148 return error.LazyDependencyNeeded;
21382149 }
21392150 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
21402151 }
21412152 }
21422153
2143 unreachable; // Bad @dependencies source
2154 unreachable; // bad @dependencies source
21442155}
21452156
2157/// Declares that the current configuration does in fact require a potentially
2158/// lazy dependency.
2159///
2160/// If the dependency is already fetched, it is returned. Otherwise, exits the
2161/// configuration phase with intent to fetch the lazy dependency and rerun the
2162/// configuration script.
2163///
2164/// If it is known to the caller at this point that additional lazy
2165/// dependencies are also required, it would save time to call `dependencyLazy`
2166/// instead, handling `error.LazyDependencyNeeded` in a way that marks multiple
2167/// potentially lazy dependencies as required before eventually returning
2168/// that error from the top level build function.
21462169pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
2147 const build_runner = @import("root");
2148 const deps = build_runner.dependencies;
2149 const pkg_hash = findPkgHashOrFatal(b, name);
2150
2151 inline for (@typeInfo(deps.packages).@"struct".decl_names) |decl_name| {
2152 if (mem.eql(u8, decl_name, pkg_hash)) {
2153 const pkg = @field(deps.packages, decl_name);
2154 if (@hasDecl(pkg, "available")) {
2155 panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name });
2156 }
2157 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
2158 }
2159 }
2160
2161 unreachable; // Bad @dependencies source
2170 return dependencyLazy(b, name, args) catch |err| switch (err) {
2171 error.LazyDependencyNeeded => {
2172 assert(b.graph.needed_lazy_dependencies.count() != 0);
2173 serializeConfigurationExiting(b);
2174 },
2175 };
21622176}
21632177
21642178/// In a build.zig file, this function is to `@import` what `lazyDependency` is to `dependency`.
......@@ -2329,13 +2343,13 @@ fn dependencyInner(
23292343 .root_dir = .{
23302344 .path = build_root_string,
23312345 .handle = Io.Dir.cwd().openDir(io, build_root_string, .{}) catch |err|
2332 process.fatal("failed to open {q}: {t}", .{ build_root_string, err }),
2346 fatal("failed to open {q}: {t}", .{ build_root_string, err }),
23332347 },
23342348 };
23352349
23362350 const sub_builder = b.createChild(name, dep_root, pkg_hash, pkg_deps, user_input_options) catch @panic("OOM");
23372351 if (build_zig) |bz| {
2338 sub_builder.runBuild(bz);
2352 sub_builder.runPackageScript(bz);
23392353
23402354 if (sub_builder.validateUserInputDidItFail()) {
23412355 std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() });
......@@ -2353,11 +2367,22 @@ fn dependencyInner(
23532367}
23542368
23552369/// Build system implementation detail.
2356pub fn runBuild(b: *Build, build_zig: anytype) void {
2357 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).@"fn".return_type.?)) {
2358 .error_union => return build_zig.build(b) catch unreachable,
2359 else => return build_zig.build(b),
2360 }
2370pub inline fn runPackageScript(b: *Build, comptime build_zig: anytype) void {
2371 const result: anyerror!void = build_zig.build(b);
2372 result catch |err| switch (err) {
2373 error.LazyDependencyNeeded => assert(b.graph.needed_lazy_dependencies.count() != 0),
2374 else => {
2375 if (b.dep_prefix.len == 0) {
2376 log.err("package {q} configuration failed: {t}", .{ b.dep_prefix, err });
2377 } else {
2378 log.err("configuration failed: {t}", .{err});
2379 }
2380 if (@errorReturnTrace()) |trace| std.debug.dumpErrorReturnTrace(trace);
2381 const lazy_count = b.graph.needed_lazy_dependencies.count();
2382 if (lazy_count == 0) process.exit(1);
2383 log.info("{d} lazy dependencies detected; fetching and retrying configuration", .{lazy_count});
2384 },
2385 };
23612386}
23622387
23632388// dirnameAllowEmpty is a variant of fs.path.dirname
......@@ -2815,6 +2840,26 @@ fn validateConfigureDependency(lazy_path: LazyPath) void {
28152840 }
28162841}
28172842
2843/// Build system implementation detail.
2844pub fn serializeConfigurationExiting(b: *Build) noreturn {
2845 const graph = b.graph;
2846 const io = graph.io;
2847
2848 var stdout_buffer: [1024]u8 = undefined;
2849 var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
2850 Serialize.write(b, &graph.wip_configuration, &file_writer.interface) catch |err| switch (err) {
2851 error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}),
2852 error.OutOfMemory => @panic("OOM"),
2853 };
2854 file_writer.flush() catch |err| fatal("failed to write configuration output: {t}", .{err});
2855
2856 // This executable is short-lived and run in Debug mode, so we'd rather
2857 // have `zig build` run faster than catch resource leaks in the user's
2858 // build.zig script (or, frankly, this configure runner), therefore we call
2859 // exit directly here rather than cleanExit.
2860 process.exit(0);
2861}
2862
28182863test {
28192864 _ = Cache;
28202865 _ = Configuration;
lib/std/Build/Serialize.zig created+1252
......@@ -0,0 +1,1252 @@
1const Serialize = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const Configuration = std.Build.Configuration;
6const Step = std.Build.Step;
7const assert = std.debug.assert;
8const log = std.log;
9
10arena: Allocator,
11wc: *Configuration.Wip,
12module_map: std.array_hash_map.Auto(*std.Build.Module, Configuration.Module.Index) = .empty,
13package_map: std.array_hash_map.Auto(*std.Build, Configuration.Package.Index) = .empty,
14/// Index corresponds to `Configuration.steps` index.
15step_map: std.array_hash_map.Auto(*Step, void) = .empty,
16
17pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !void {
18 const graph = b.graph;
19 const arena = graph.arena;
20 const gpa = wc.gpa;
21
22 var s: Serialize = .{ .wc = wc, .arena = arena };
23
24 try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len);
25 for (
26 graph.configure_dependencies.items,
27 wc.path_deps.addManyAsSliceAssumeCapacity(graph.configure_dependencies.items.len),
28 ) |src, *dest| {
29 dest.* = .{
30 .flags = .{
31 .base = switch (src.lazy_path) {
32 .src_path, .dependency => .build_root,
33 .generated => unreachable,
34 .cwd_relative => .cwd,
35 .relative => |r| r.base,
36 },
37 .mode = src.mode,
38 },
39 .sub = switch (src.lazy_path) {
40 .src_path => |sp| try wc.addString(sp.sub_path),
41 .generated => unreachable,
42 .cwd_relative => |sub_path| try wc.addString(sub_path),
43 .dependency => |d| try wc.addString(d.sub_path),
44 .relative => |r| try wc.addString(r.sub_path),
45 },
46 .pkg = switch (src.lazy_path) {
47 .src_path => |sp| .init(try s.builderToPackage(sp.owner)),
48 .generated => unreachable,
49 .cwd_relative, .relative => .none,
50 .dependency => |d| .init(try s.builderToPackage(d.dependency.builder)),
51 },
52 };
53 }
54
55 // Starting from all top-level steps in `b`, traverse the entire step graph
56 // and add all step dependencies implied by module graphs.
57 const top_level_steps = b.top_level_steps.values();
58 try s.step_map.ensureUnusedCapacity(arena, top_level_steps.len);
59 for (top_level_steps) |tls| {
60 s.step_map.putAssumeCapacityNoClobber(&tls.step, {});
61 }
62 {
63 while (wc.steps.items.len < s.step_map.count()) {
64 const step = s.step_map.keys()[wc.steps.items.len];
65
66 // Set up any implied dependencies for this step. It's important that we do this first, so
67 // that the loop below discovers steps implied by the module graph.
68 try createModuleDependenciesForStep(step);
69
70 try s.step_map.ensureUnusedCapacity(arena, step.dependencies.items.len);
71 for (step.dependencies.items) |other_step| {
72 s.step_map.putAssumeCapacity(other_step, {});
73 }
74
75 // Add and then de-duplicate dependencies.
76 const dep_steps = try arena.alloc(Configuration.Step.Index, step.dependencies.items.len);
77 for (dep_steps, step.dependencies.items) |*dest, src|
78 dest.* = @enumFromInt(s.step_map.getIndex(src).?);
79
80 const deps: Configuration.Deps.Index = try wc.addDeduped(Configuration.Deps, .{
81 .steps = .{ .slice = dep_steps },
82 });
83
84 try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);
85 wc.steps.appendAssumeCapacity(.{
86 .name = try wc.addString(step.name),
87 .owner = try s.builderToPackage(step.owner),
88 .deps = deps,
89 .max_rss = .fromBytes(step.max_rss),
90 .extended = @enumFromInt(switch (step.tag) {
91 .top_level => e: {
92 const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
93 break :e try wc.addExtraErased(Configuration.Step.TopLevel, .{
94 .description = try wc.addString(top_level.description),
95 });
96 },
97 .compile => e: {
98 const c: *Step.Compile = @fieldParentPtr("step", step);
99 const installed_headers: []u32 = try arena.alloc(u32, c.installed_headers.items.len);
100 for (installed_headers, c.installed_headers.items) |*dst, src| switch (src) {
101 .file => |file| {
102 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.File, .{
103 .source = try s.addLazyPath(file.source),
104 .dest_sub_path = try wc.addString(file.dest_rel_path),
105 });
106 },
107 .directory => |directory| {
108 const include_extensions = directory.options.include_extensions orelse &.{};
109 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.Directory, .{
110 .flags = .{
111 .include_extensions = include_extensions.len != 0,
112 .exclude_extensions = directory.options.exclude_extensions.len != 0,
113 },
114 .source = try s.addLazyPath(directory.source),
115 .dest_sub_path = try wc.addString(directory.dest_rel_path),
116 .exclude_extensions = .{ .slice = try s.initStringList(directory.options.exclude_extensions) },
117 .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
118 });
119 },
120 };
121
122 break :e try wc.addExtraErased(Configuration.Step.Compile, .{
123 .flags = .{
124 .filters_len = c.filters.len != 0,
125 .installed_headers_len = installed_headers.len != 0,
126 .force_undefined_symbols_len = c.force_undefined_symbols.entries.len != 0,
127
128 .verbose_link = c.verbose_link,
129 .verbose_cc = c.verbose_cc,
130 .rdynamic = c.rdynamic,
131 .import_memory = c.import_memory,
132 .export_memory = c.export_memory,
133 .import_symbols = c.import_symbols,
134 .import_table = c.import_table,
135 .export_table = c.export_table,
136 .shared_memory = c.shared_memory,
137 .link_eh_frame_hdr = c.link_eh_frame_hdr,
138 .link_emit_relocs = c.link_emit_relocs,
139 .link_function_sections = c.link_function_sections,
140 .link_data_sections = c.link_data_sections,
141 .linker_dynamicbase = c.linker_dynamicbase,
142 .link_z_notext = c.link_z_notext,
143 .link_z_relro = c.link_z_relro,
144 .link_z_lazy = c.link_z_lazy,
145 .link_z_defs = c.link_z_defs,
146 .headerpad_max_install_names = c.headerpad_max_install_names,
147 .dead_strip_dylibs = c.dead_strip_dylibs,
148 .force_load_objc = c.force_load_objc,
149 .discard_local_symbols = c.discard_local_symbols,
150 .mingw_unicode_entry_point = c.mingw_unicode_entry_point,
151 },
152 .flags2 = .{
153 .pie = .init(c.pie),
154 .formatted_panics = .init(c.formatted_panics),
155 .bundle_compiler_rt = .init(c.bundle_compiler_rt),
156 .bundle_ubsan_rt = .init(c.bundle_ubsan_rt),
157 .each_lib_rpath = .init(c.each_lib_rpath),
158 .link_gc_sections = .init(c.link_gc_sections),
159 .linker_allow_shlib_undefined = .init(c.linker_allow_shlib_undefined),
160 .linker_allow_undefined_version = .init(c.linker_allow_undefined_version),
161 .linker_enable_new_dtags = .init(c.linker_enable_new_dtags),
162 .dll_export_fns = .init(c.dll_export_fns),
163 .use_llvm = .init(c.use_llvm),
164 .use_lld = .init(c.use_lld),
165 .use_new_linker = .init(c.use_new_linker),
166 .allow_so_scripts = .init(c.allow_so_scripts),
167 .sanitize_coverage_trace_pc_guard = .init(c.sanitize_coverage_trace_pc_guard),
168 .linkage = .init(c.linkage),
169 },
170 .flags3 = .{
171 .is_linking_libc = c.is_linking_libc,
172 .is_linking_libcpp = c.is_linking_libcpp,
173 .version = c.version != null,
174 .compress_debug_sections = c.compress_debug_sections,
175 .initial_memory = c.initial_memory != null,
176 .max_memory = c.max_memory != null,
177 .kind = c.kind,
178 .global_base = c.global_base != null,
179 .test_runner = if (c.test_runner) |tr| switch (tr.mode) {
180 .simple => .simple,
181 .server => .server,
182 } else .default,
183 .wasi_exec_model = .init(c.wasi_exec_model),
184 .win32_manifest = c.win32_manifest != null,
185 .win32_module_definition = c.win32_module_definition != null,
186 .zig_lib_dir = c.zig_lib_dir != null,
187 .rc_includes = c.rc_includes,
188 .image_base = c.image_base != null,
189 .build_id = .init(c.build_id),
190 .entry = switch (c.entry) {
191 .default => .default,
192 .disabled => .disabled,
193 .enabled => .enabled,
194 .symbol_name => .symbol_name,
195 },
196 .lto = .init(c.lto),
197 .subsystem = .init(c.subsystem),
198 },
199 .flags4 = .{
200 .libc_file = c.libc_file != null,
201 .link_z_common_page_size = c.link_z_common_page_size != null,
202 .link_z_max_page_size = c.link_z_max_page_size != null,
203 .pagezero_size = c.pagezero_size != null,
204 .stack_size = c.stack_size != null,
205 .headerpad_size = c.headerpad_size != null,
206 .error_limit = c.error_limit != null,
207 .install_name = c.install_name != null,
208 .entitlements = c.entitlements != null,
209 .expect_errors = if (c.expect_errors) |x| switch (x) {
210 .contains => .contains,
211 .exact => .exact,
212 .starts_with => .starts_with,
213 .stderr_contains => .stderr_contains,
214 } else .none,
215 .linker_script = c.linker_script != null,
216 .version_script = c.version_script != null,
217 .emit_directory = c.emit_directory != .none,
218 .generated_docs = c.generated_docs != .none,
219 .generated_asm = c.generated_asm != .none,
220 .generated_bin = c.generated_bin != .none,
221 .generated_pdb = c.generated_pdb != .none,
222 .generated_implib = c.generated_implib != .none,
223 .generated_llvm_bc = c.generated_llvm_bc != .none,
224 .generated_llvm_ir = c.generated_llvm_ir != .none,
225 .generated_h = c.generated_h != .none,
226 .incremental = .init(c.incremental),
227 },
228 .root_module = try s.addModule(c.root_module),
229 .root_name = try wc.addString(c.name),
230 .linker_script = .{ .value = try s.addOptionalLazyPath(c.linker_script) },
231 .version_script = .{ .value = try s.addOptionalLazyPath(c.version_script) },
232 .zig_lib_dir = .{ .value = try s.addOptionalLazyPath(c.zig_lib_dir) },
233 .libc_file = .{ .value = try s.addOptionalLazyPath(c.libc_file) },
234 .win32_manifest = .{ .value = try s.addOptionalLazyPath(c.win32_manifest) },
235 .win32_module_definition = .{ .value = try s.addOptionalLazyPath(c.win32_module_definition) },
236 .entitlements = .{ .value = try s.addOptionalLazyPath(c.entitlements) },
237 .version = .{ .value = try s.addOptionalSemVer(c.version) },
238 .install_name = .{ .value = try s.addOptionalString(c.install_name) },
239 .initial_memory = .{ .value = c.initial_memory },
240 .max_memory = .{ .value = c.max_memory },
241 .global_base = .{ .value = c.global_base },
242 .image_base = .{ .value = c.image_base },
243 .link_z_common_page_size = .{ .value = c.link_z_common_page_size },
244 .link_z_max_page_size = .{ .value = c.link_z_max_page_size },
245 .pagezero_size = .{ .value = c.pagezero_size },
246 .stack_size = .{ .value = c.stack_size },
247 .headerpad_size = .{ .value = c.headerpad_size },
248 .error_limit = .{ .value = c.error_limit },
249 .entry = .{ .value = switch (c.entry) {
250 .symbol_name => |name| try wc.addString(name),
251 .default, .disabled, .enabled => null,
252 } },
253 .build_id = .{ .value = if (c.build_id) |id| switch (id) {
254 .hexstring => |*hexstring| try wc.addString(hexstring.toSlice()),
255 .none, .fast, .uuid, .sha1, .md5 => null,
256 } else null },
257 .filters = .{ .slice = try s.initStringList(c.filters) },
258 .installed_headers = .initErased(installed_headers),
259 .force_undefined_symbols = .{ .slice = try s.initStringList(c.force_undefined_symbols.keys()) },
260 .expect_errors = .{ .u = if (c.expect_errors) |x| switch (x) {
261 .contains => |slice| .{ .contains = try wc.addString(slice) },
262 .exact => |exact| .{ .exact = .{ .slice = try s.initStringList(exact) } },
263 .starts_with => |slice| .{ .starts_with = try wc.addString(slice) },
264 .stderr_contains => |slice| .{ .stderr_contains = try wc.addString(slice) },
265 } else .none },
266 .test_runner = .{ .u = if (c.test_runner) |tr| switch (tr.mode) {
267 .simple => .{ .simple = try s.addLazyPath(tr.path) },
268 .server => .{ .server = try s.addLazyPath(tr.path) },
269 } else .default },
270
271 .emit_directory = .{ .value = c.emit_directory.unwrap() },
272 .generated_docs = .{ .value = c.generated_docs.unwrap() },
273 .generated_asm = .{ .value = c.generated_asm.unwrap() },
274 .generated_bin = .{ .value = c.generated_bin.unwrap() },
275 .generated_pdb = .{ .value = c.generated_pdb.unwrap() },
276 .generated_implib = .{ .value = c.generated_implib.unwrap() },
277 .generated_llvm_bc = .{ .value = c.generated_llvm_bc.unwrap() },
278 .generated_llvm_ir = .{ .value = c.generated_llvm_ir.unwrap() },
279 .generated_h = .{ .value = c.generated_h.unwrap() },
280 });
281 },
282 .install_artifact => e: {
283 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
284 break :e try wc.addExtraErased(Configuration.Step.InstallArtifact, .{
285 .flags = .{
286 .dylib_symlinks = ia.dylib_symlinks,
287 .bin_dir = ia.dest_dir != null,
288 .implib_dir = ia.implib_dir != null,
289 .pdb_dir = ia.pdb_dir != null,
290 .h_dir = ia.h_dir != null,
291 .bin_sub_path = ia.dest_sub_path != null,
292 },
293 .bin_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.dest_dir) },
294 .implib_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.implib_dir) },
295 .pdb_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.pdb_dir) },
296 .h_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.h_dir) },
297 .bin_sub_path = .{ .value = try s.addOptionalString(ia.dest_sub_path) },
298 });
299 },
300 .install_file => e: {
301 const sif: *Step.InstallFile = @fieldParentPtr("step", step);
302 break :e try wc.addExtraErased(Configuration.Step.InstallFile, .{
303 .source = try s.addLazyPath(sif.source),
304 .dest_dir = try addInstallDir(wc, sif.dir),
305 .dest_sub_path = try wc.addString(sif.dest_rel_path),
306 });
307 },
308 .install_dir => e: {
309 const sid: *Step.InstallDir = @fieldParentPtr("step", step);
310 const dest_sub_path: ?[]const u8 = if (sid.options.install_subdir.len != 0)
311 sid.options.install_subdir
312 else
313 null;
314 const include_extensions = sid.options.include_extensions orelse &.{};
315 break :e try wc.addExtraErased(Configuration.Step.InstallDir, .{
316 .flags = .{
317 .dest_sub_path = dest_sub_path != null,
318 .exclude_extensions = sid.options.exclude_extensions.len != 0,
319 .include_extensions = include_extensions.len != 0,
320 .include_extensions_active = sid.options.include_extensions != null,
321 .blank_extensions = sid.options.blank_extensions.len != 0,
322 },
323 .source_dir = try s.addLazyPath(sid.options.source_dir),
324 .dest_dir = try addInstallDir(wc, sid.options.install_dir),
325 .dest_sub_path = .{ .value = try s.addOptionalString(dest_sub_path) },
326 .exclude_extensions = .{ .slice = try s.initStringList(sid.options.exclude_extensions) },
327 .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
328 .blank_extensions = .{ .slice = try s.initStringList(sid.options.blank_extensions) },
329 });
330 },
331 .fail => e: {
332 const sf: *Step.Fail = @fieldParentPtr("step", step);
333 break :e try wc.addExtraErased(Configuration.Step.Fail, .{
334 .msg = sf.error_msg,
335 });
336 },
337 .find_program => e: {
338 const fp: *Step.FindProgram = @fieldParentPtr("step", step);
339 break :e try wc.addExtraErased(Configuration.Step.FindProgram, .{
340 .names = fp.names,
341 .found_path = fp.found_path,
342 });
343 },
344 .fmt => e: {
345 const sf: *Step.Fmt = @fieldParentPtr("step", step);
346 break :e try wc.addExtraErased(Configuration.Step.Fmt, .{
347 .flags = .{
348 .paths = sf.paths.len != 0,
349 .exclude_paths = sf.exclude_paths.len != 0,
350 .check = sf.check,
351 },
352 .paths = .{ .slice = try s.initLazyPathList(sf.paths) },
353 .exclude_paths = .{ .slice = try s.initLazyPathList(sf.exclude_paths) },
354 });
355 },
356 .translate_c => e: {
357 const tc: *Step.TranslateC = @fieldParentPtr("step", step);
358
359 const system_libs = try arena.alloc(Configuration.SystemLib.Index, tc.system_libs.items.len);
360 for (system_libs, tc.system_libs.items) |*dest, *src| dest.* = try s.addSystemLib(src);
361
362 break :e try wc.addExtraErased(Configuration.Step.TranslateC, .{
363 .flags = .{
364 .include_dirs = tc.include_dirs.items.len != 0,
365 .system_libs = system_libs.len != 0,
366 .c_macros = tc.c_macros.items.len != 0,
367 .link_libc = tc.link_libc,
368 .optimize = .init(tc.optimize),
369 },
370 .src_path = try s.addLazyPath(tc.source),
371 .output_file = tc.output_file,
372 .include_dirs = .init(try s.initIncludeDirList(tc.include_dirs.items)),
373 .system_libs = .{ .slice = system_libs },
374 .c_macros = .{ .slice = tc.c_macros.items },
375 .target = try addOptionalResolvedTarget(wc, tc.target),
376 });
377 },
378 .write_file => e: {
379 const wf: *Step.WriteFile = @fieldParentPtr("step", step);
380
381 const directories = try arena.alloc(
382 Configuration.Step.WriteFile.Directory,
383 wf.directories.items.len,
384 );
385 for (directories, wf.directories.items) |*dest, src| dest.* = .{
386 .sub_path = src.sub_path,
387 .src_path = try s.addLazyPath(src.src_path),
388 .exclude_extensions = src.exclude_extensions,
389 .include_extensions = src.include_extensions,
390 };
391
392 break :e try wc.addExtraErased(Configuration.Step.WriteFile, .{
393 .flags = .{
394 .embeds = wf.embeds.items.len != 0,
395 .copies = wf.copies.items.len != 0,
396 .directories = directories.len != 0,
397 .mode = switch (wf.mode) {
398 .whole_cached => .whole_cached,
399 .tmp => .tmp,
400 .mutate => .mutate,
401 },
402 },
403 .generated_directory = wf.generated_directory,
404 .embeds = .{ .slice = wf.embeds.items },
405 .copies = .{ .slice = try s.initCopyList(wf.copies.items) },
406 .directories = .{ .slice = directories },
407 .mutate_path = .{ .value = switch (wf.mode) {
408 .mutate => |lp| try s.addLazyPath(lp),
409 .whole_cached, .tmp => null,
410 } },
411 });
412 },
413 .update_source_files => e: {
414 const usf: *Step.UpdateSourceFiles = @fieldParentPtr("step", step);
415 break :e try wc.addExtraErased(Configuration.Step.UpdateSourceFiles, .{
416 .flags = .{
417 .embeds = usf.embeds.items.len != 0,
418 .copies = usf.copies.items.len != 0,
419 },
420 .embeds = .{ .slice = usf.embeds.items },
421 .copies = .{ .slice = try s.initCopyList(usf.copies.items) },
422 });
423 },
424 .run => e: {
425 const run: *Step.Run = @fieldParentPtr("step", step);
426 var expect_stderr_exact: ?Configuration.Bytes = null;
427 var expect_stdout_exact: ?Configuration.Bytes = null;
428 var expect_stderr_match: std.ArrayList(Configuration.Bytes) = .empty;
429 var expect_stdout_match: std.ArrayList(Configuration.Bytes) = .empty;
430 var expect_term: ?struct {
431 status: Configuration.Step.Run.ExpectTermStatus,
432 value: u32,
433 } = null;
434 var expect_stderr_snapshot: ?Configuration.LazyPath.Index = null;
435 var expect_stdout_snapshot: ?Configuration.LazyPath.Index = null;
436 switch (run.stdio) {
437 .check => |checks| for (checks.items) |check| switch (check) {
438 .expect_stderr_exact => |bytes| expect_stderr_exact = try wc.addBytes(bytes),
439 .expect_stdout_exact => |bytes| expect_stdout_exact = try wc.addBytes(bytes),
440 .expect_stderr_match => |bytes| {
441 try expect_stderr_match.append(arena, try wc.addBytes(bytes));
442 },
443 .expect_stdout_match => |bytes| {
444 try expect_stdout_match.append(arena, try wc.addBytes(bytes));
445 },
446 .expect_term => |t| expect_term = switch (t) {
447 .exited => |x| .{ .status = .exited, .value = x },
448 .signal => |x| .{ .status = .signal, .value = @intFromEnum(x) },
449 .stopped => |x| .{ .status = .stopped, .value = @intFromEnum(x) },
450 .unknown => |x| .{ .status = .unknown, .value = x },
451 },
452 .expect_stderr_snapshot => |path| expect_stderr_snapshot = try s.addLazyPath(path),
453 .expect_stdout_snapshot => |path| expect_stdout_snapshot = try s.addLazyPath(path),
454 },
455 else => {},
456 }
457
458 break :e try wc.addExtraErased(Configuration.Step.Run, .{
459 .flags = .{
460 .disable_zig_progress = run.disable_zig_progress,
461 .skip_foreign_checks = run.skip_foreign_checks,
462 .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error,
463 .has_side_effects = run.has_side_effects,
464 .test_runner_mode = run.test_runner_mode,
465 .color = run.color,
466 .stdio = switch (run.stdio) {
467 .infer_from_args => .infer_from_args,
468 .inherit => .inherit,
469 .check => .check,
470 .zig_test => .zig_test,
471 },
472 .stdin = switch (run.stdin) {
473 .none => .none,
474 .bytes => .bytes,
475 .lazy_path => .lazy_path,
476 },
477 .stdout_trim_whitespace = if (run.captured_stdout) |cs| cs.trim_whitespace else .none,
478 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none,
479 .stdio_limit = run.stdio_limit != .unlimited,
480 .producer = run.producer != null,
481 .cwd = run.cwd != null,
482 .captured_stdout = run.captured_stdout != null,
483 .captured_stderr = run.captured_stderr != null,
484 .environ_map = run.environ_map != null,
485 },
486 .flags2 = .{
487 .expect_stderr_exact = expect_stderr_exact != null,
488 .expect_stdout_exact = expect_stdout_exact != null,
489 .expect_stderr_match = expect_stderr_match.items.len != 0,
490 .expect_stdout_match = expect_stdout_match.items.len != 0,
491 .expect_term = expect_term != null,
492 .expect_term_status = if (expect_term) |t| t.status else .exited,
493 .expect_stderr_snapshot = expect_stderr_snapshot != null,
494 .expect_stdout_snapshot = expect_stdout_snapshot != null,
495 },
496 .file_inputs = .{ .slice = try s.initLazyPathList(run.file_inputs.items) },
497 .args = .{ .slice = try s.initArgsList(run.argv.items) },
498 .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
499 .preopen_names = .{ .slice = try s.initStringList(run.preopens.keys()) },
500 .preopen_paths = .{ .slice = try s.initLazyPathList(run.preopens.values()) },
501 .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
502 .basename = try wc.addString(cs.basename),
503 .generated_file = cs.generated_file,
504 } else null },
505 .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
506 .basename = try wc.addString(cs.basename),
507 .generated_file = cs.generated_file,
508 } else null },
509 .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
510 .expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
511 .stdio_limit = .{ .value = run.stdio_limit.toInt64() },
512 .producer = .{ .value = if (run.producer) |cs| s.stepIndex(&cs.step) else null },
513 .expect_stderr_exact = .{ .value = if (expect_stderr_exact) |bytes| bytes else null },
514 .expect_stdout_exact = .{ .value = if (expect_stdout_exact) |bytes| bytes else null },
515 .expect_stderr_match = .{ .slice = expect_stderr_match.items },
516 .expect_stdout_match = .{ .slice = expect_stdout_match.items },
517 .expect_stderr_snapshot = .{ .value = expect_stderr_snapshot orelse null },
518 .expect_stdout_snapshot = .{ .value = expect_stdout_snapshot orelse null },
519 .stdin = .{ .u = switch (run.stdin) {
520 .none => .none,
521 .bytes => |bytes| .{ .bytes = try wc.addBytes(bytes) },
522 .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) },
523 } },
524 });
525 },
526 .check_file => e: {
527 const cf: *Step.CheckFile = @fieldParentPtr("step", step);
528 break :e try wc.addExtraErased(Configuration.Step.CheckFile, .{
529 .flags = .{
530 .expected_exact = cf.expected_exact != null,
531 .expected_matches = cf.expected_matches.len != 0,
532 .max_bytes = cf.max_bytes != null,
533 },
534 .file = try s.addLazyPath(cf.file),
535 .expected_exact = .{ .value = cf.expected_exact },
536 .expected_matches = .{ .slice = cf.expected_matches },
537 .max_bytes = .{ .value = cf.max_bytes },
538 });
539 },
540 .config_header => e: {
541 const ch: *Step.ConfigHeader = @fieldParentPtr("step", step);
542 const lazy_path: ?std.Build.LazyPath = ch.style.getPath();
543 const pairs = try arena.alloc(Configuration.Step.ConfigHeader.Value.Pair, ch.values.count());
544 for (pairs, ch.values.keys(), ch.values.values()) |*pair, key, value| pair.* = .{
545 .key = try wc.addString(key),
546 .index = switch (value) {
547 .undef => .undef,
548 .defined => .defined,
549 .boolean => |x| switch (x) {
550 false => .bool_false,
551 true => .bool_true,
552 },
553 .int => |x| switch (x) {
554 0 => .int_0,
555 1 => .int_1,
556 else => try wc.addExtra(Configuration.Step.ConfigHeader.Value, .initSigned(x)),
557 },
558 .ident => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
559 .flags = .{
560 .tag = .ident,
561 .small = 0,
562 },
563 .i64 = .{ .value = null },
564 .u64 = .{ .value = null },
565 .ident = .{ .value = try wc.addString(x) },
566 .string = .{ .value = null },
567 }),
568 .string => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
569 .flags = .{
570 .tag = .string,
571 .small = 0,
572 },
573 .i64 = .{ .value = null },
574 .u64 = .{ .value = null },
575 .ident = .{ .value = null },
576 .string = .{ .value = try wc.addString(x) },
577 }),
578 },
579 };
580 break :e try wc.addExtraErased(Configuration.Step.ConfigHeader, .{
581 .flags = .{
582 .template_file = lazy_path != null,
583 .style = .init(ch.style),
584 .input_size_limit = ch.input_size_limit != null,
585 .include_guard = ch.include_guard != .none,
586 },
587 .template_file = .{ .value = try s.addOptionalLazyPath(lazy_path) },
588 .generated_dir = ch.generated_dir,
589 .input_size_limit = .{ .value = ch.input_size_limit },
590 .include_path = try wc.addString(ch.include_path),
591 .include_guard = .{ .value = ch.include_guard.unwrap() },
592 .values = .{ .slice = pairs },
593 });
594 },
595 .obj_copy => e: {
596 const oc: *Step.ObjCopy = @fieldParentPtr("step", step);
597
598 const debug_basename: ?Configuration.String = if (oc.debug_file) |df|
599 df.basename.unwrap()
600 else
601 null;
602
603 const debug_file: ?Configuration.GeneratedFileIndex = if (oc.debug_file) |df|
604 df.output_file
605 else
606 null;
607
608 const add_sections = try arena.alloc(
609 Configuration.Step.ObjCopy.AddSection,
610 oc.add_sections.items.len,
611 );
612 for (add_sections, oc.add_sections.items) |*dest, src| dest.* = .{
613 .section_name = src.section_name,
614 .file_path = try s.addLazyPath(src.file_path),
615 };
616
617 break :e try wc.addExtraErased(Configuration.Step.ObjCopy, .{
618 .flags = .{
619 .basename = oc.basename != .none,
620 .debug_file = debug_file != null,
621 .debug_basename = debug_basename != null,
622 .format = .init(oc.format),
623 .strip = oc.strip,
624 .compress_debug = oc.compress_debug,
625 .only_section = oc.only_section != .none,
626 .pad_to = oc.pad_to != null,
627 .add_section = add_sections.len != 0,
628 .update_section = oc.update_sections.items.len != 0,
629 },
630 .input_file = try s.addLazyPath(oc.input_file),
631 .output_file = oc.output_file,
632 .basename = .{ .value = oc.basename.unwrap() },
633 .debug_file = .{ .value = debug_file },
634 .debug_basename = .{ .value = debug_basename },
635 .only_section = .{ .value = oc.only_section.unwrap() },
636 .pad_to = .{ .value = oc.pad_to },
637 .add_section = .{ .slice = add_sections },
638 .update_section = .{ .slice = oc.update_sections.items },
639 });
640 },
641 .options => e: {
642 const so: *Step.Options = @fieldParentPtr("step", step);
643
644 const args = try arena.alloc(Configuration.Step.Options.Arg, so.args.items.len);
645 for (args, so.args.items) |*dest, src| dest.* = .{
646 .name = src.name,
647 .path = try s.addLazyPath(src.path),
648 };
649
650 break :e try wc.addExtraErased(Configuration.Step.Options, .{
651 .flags = .{
652 .args = so.args.items.len != 0,
653 },
654 .generated_file = so.generated_file,
655 .contents = try wc.addBytes(so.contents.items),
656 .args = .{ .slice = args },
657 });
658 },
659 }),
660 });
661 }
662 }
663
664 try wc.unlazy_deps.ensureUnusedCapacity(gpa, graph.needed_lazy_dependencies.keys().len);
665 for (graph.needed_lazy_dependencies.keys()) |k| {
666 wc.unlazy_deps.appendAssumeCapacity(try wc.addString(k));
667 }
668
669 try wc.write(writer, .{
670 .default_step = s.stepIndex(b.default_step),
671 .generated_files_len = @intCast(graph.generated_files.items.len),
672 .poisoned = switch (graph.cache_poison) {
673 .pure, .disallowed, .ignored => false,
674 .poisoned => true,
675 },
676 });
677}
678
679pub fn systemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
680 const gpa = wc.gpa;
681
682 var bad = false;
683 try wc.system_integrations.ensureTotalCapacityPrecise(gpa, graph.system_integration_options.entries.len);
684 for (graph.system_integration_options.keys(), graph.system_integration_options.values()) |k, v| {
685 wc.system_integrations.appendAssumeCapacity(.{
686 .name = try wc.addString(k),
687 .status = switch (v) {
688 .user_disabled, .user_enabled => x: {
689 // The user tried to enable or disable a system library integration, but
690 // the configure script did not recognize that option.
691 log.err("system integration name not recognized by configure script: {s}", .{k});
692 bad = true;
693 break :x .disabled;
694 },
695 .declared_disabled => .disabled,
696 .declared_enabled => .enabled,
697 },
698 });
699 }
700 if (bad) {
701 log.info("help menu contains available options: zig build -h", .{});
702 std.process.exit(1);
703 }
704}
705
706pub fn packageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void {
707 const gpa = wc.gpa;
708
709 try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count());
710 for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| {
711 wc.available_options.appendAssumeCapacity(.{
712 .name = try wc.addString(name),
713 .description = try wc.addString(opt.description),
714 .type = opt.type_id,
715 .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none,
716 });
717 }
718}
719
720fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
721 if (b.pkg_hash.len == 0) return .root;
722 const arena = s.arena;
723 const wc = s.wc;
724 const gop = try s.package_map.getOrPut(arena, b);
725 if (!gop.found_existing) {
726 gop.value_ptr.* = try wc.addExtra(Configuration.Package, .{
727 .hash = try wc.addString(b.pkg_hash),
728 .dep_prefix = try wc.addString(b.dep_prefix),
729 .root_path = try wc.addString(try b.root.toString(arena)),
730 });
731 }
732 return gop.value_ptr.*;
733}
734
735fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.LazyPath.OptionalIndex {
736 const wc = s.wc;
737 return @enumFromInt(switch (lp orelse return .none) {
738 .src_path => |src_path| i: {
739 const sub_path = try wc.addString(src_path.sub_path);
740 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
741 .owner = try s.builderToPackage(src_path.owner),
742 .sub_path = sub_path,
743 });
744 },
745 .generated => |generated| i: {
746 const sub_path = try wc.addString(generated.sub_path);
747 break :i try wc.addExtraErased(Configuration.LazyPath.Generated, .{
748 .flags = .{ .up = @intCast(generated.up) },
749 .index = generated.index,
750 .sub_path = sub_path,
751 });
752 },
753 .cwd_relative => |cwd_relative_sub_path| i: {
754 const sub_path = try wc.addString(cwd_relative_sub_path);
755 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
756 .flags = .{ .base = .cwd },
757 .sub_path = sub_path,
758 });
759 },
760 .relative => |relative| i: {
761 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
762 .flags = .{ .base = relative.base },
763 .sub_path = try wc.addString(relative.sub_path),
764 });
765 },
766 .dependency => |dependency| i: {
767 const sub_path = try wc.addString(dependency.sub_path);
768 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
769 .owner = try s.builderToPackage(dependency.dependency.builder),
770 .sub_path = sub_path,
771 });
772 },
773 });
774}
775
776fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !?Configuration.LazyPath.Index {
777 return (try addOptionalLazyPathEnum(s, lp)).unwrap();
778}
779
780fn addLazyPath(s: *Serialize, lp: std.Build.LazyPath) !Configuration.LazyPath.Index {
781 return @enumFromInt(@intFromEnum(try addOptionalLazyPathEnum(s, lp)));
782}
783
784fn addOptionalSemVer(s: *Serialize, sem_ver: ?std.SemanticVersion) !?Configuration.String {
785 return if (sem_ver) |sv| try s.wc.addSemVer(sv) else null;
786}
787
788fn addOptionalString(s: *Serialize, opt_slice: ?[]const u8) !?Configuration.String {
789 return if (opt_slice) |slice| try s.wc.addString(slice) else null;
790}
791
792fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {
793 const wc = s.wc;
794 return try wc.addDeduped(Configuration.SystemLib, .{
795 .flags = .{
796 .needed = sl.needed,
797 .weak = sl.weak,
798 .use_pkg_config = sl.use_pkg_config,
799 .preferred_link_mode = sl.preferred_link_mode,
800 .search_strategy = sl.search_strategy,
801 },
802 .name = try wc.addString(sl.name),
803 });
804}
805
806fn addCSourceFile(s: *Serialize, csf: *const std.Build.Module.CSourceFile) !Configuration.CSourceFile.Index {
807 const wc = s.wc;
808 const args = try initStringList(s, csf.flags);
809 return try wc.addExtra(Configuration.CSourceFile, .{
810 .flags = .{
811 .args_len = @intCast(args.len),
812 .lang = .init(csf.language),
813 },
814 .file = try addLazyPath(s, csf.file),
815 .args = .{ .slice = args },
816 });
817}
818
819fn addCSourceFiles(s: *Serialize, csf: *const std.Build.Module.CSourceFiles) !Configuration.CSourceFiles.Index {
820 const wc = s.wc;
821 const sub_paths = try initStringList(s, csf.files);
822 const args = try initStringList(s, csf.flags);
823 return try wc.addExtra(Configuration.CSourceFiles, .{
824 .flags = .{
825 .args_len = @intCast(args.len),
826 .lang = .init(csf.language),
827 },
828 .root = try addLazyPath(s, csf.root),
829 .sub_paths = .{ .slice = sub_paths },
830 .args = .{ .slice = args },
831 });
832}
833
834fn addRcSourceFile(s: *Serialize, rsf: *const std.Build.Module.RcSourceFile) !Configuration.RcSourceFile.Index {
835 const wc = s.wc;
836 const include_paths = try initLazyPathList(s, rsf.include_paths);
837 const args = try initStringList(s, rsf.flags);
838 return try wc.addExtra(Configuration.RcSourceFile, .{
839 .flags = .{
840 .args_len = @intCast(args.len),
841 .include_paths = include_paths.len != 0,
842 },
843 .file = try addLazyPath(s, rsf.file),
844 .include_paths = .{ .slice = include_paths },
845 .args = .{ .slice = args },
846 });
847}
848
849fn addEnvironMap(s: *Serialize, opt_map: ?*std.process.Environ.Map) !?Configuration.EnvironMap.Index {
850 const wc = s.wc;
851 const map = opt_map orelse return null;
852 return try wc.addDeduped(Configuration.EnvironMap, .{
853 .keys = try wc.addStringList(map.array_hash_map.keys()),
854 .values = try wc.addStringList(map.array_hash_map.values()),
855 });
856}
857
858fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuration.Step.Run.Arg.Index {
859 const wc = s.wc;
860 const result = try s.arena.alloc(Configuration.Step.Run.Arg.Index, args.len);
861 for (result, args) |*dest, src| {
862 dest.* = try wc.addExtra(Configuration.Step.Run.Arg, switch (src) {
863 .artifact => |a| .{
864 .flags = .{
865 .tag = .artifact,
866 .prefix = a.prefix.len != 0,
867 .suffix = a.suffix.len != 0,
868 .basename = false,
869 .path = false,
870 .producer = true,
871 .generated = false,
872 .dep_file = false,
873 .make_absolute = a.make_absolute,
874 },
875 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
876 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
877 .basename = .{ .value = null },
878 .path = .{ .value = null },
879 .producer = .{ .value = stepIndex(s, &a.artifact.step) },
880 .generated = .{ .value = null },
881 },
882 .lazy_path => |a| .{
883 .flags = .{
884 .tag = .path_file,
885 .prefix = a.prefix.len != 0,
886 .suffix = a.suffix.len != 0,
887 .basename = false,
888 .path = true,
889 .producer = false,
890 .generated = false,
891 .dep_file = false,
892 .make_absolute = a.make_absolute,
893 },
894 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
895 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
896 .basename = .{ .value = null },
897 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
898 .producer = .{ .value = null },
899 .generated = .{ .value = null },
900 },
901 .decorated_directory => |a| .{
902 .flags = .{
903 .tag = .path_directory,
904 .prefix = a.prefix.len != 0,
905 .suffix = a.suffix.len != 0,
906 .basename = false,
907 .path = true,
908 .producer = false,
909 .generated = false,
910 .dep_file = false,
911 .make_absolute = a.make_absolute,
912 },
913 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
914 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
915 .basename = .{ .value = null },
916 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
917 .producer = .{ .value = null },
918 .generated = .{ .value = null },
919 },
920 .file_content => |a| .{
921 .flags = .{
922 .tag = .file_content,
923 .prefix = a.prefix.len != 0,
924 .suffix = a.suffix.len != 0,
925 .basename = false,
926 .path = true,
927 .producer = false,
928 .generated = false,
929 .dep_file = false,
930 .make_absolute = false,
931 },
932 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
933 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
934 .basename = .{ .value = null },
935 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
936 .producer = .{ .value = null },
937 .generated = .{ .value = null },
938 },
939 .bytes => |a| .{
940 .flags = .{
941 .tag = .string,
942 .prefix = true,
943 .suffix = false,
944 .basename = false,
945 .path = false,
946 .producer = false,
947 .generated = false,
948 .dep_file = false,
949 .make_absolute = false,
950 },
951 .prefix = .{ .value = try wc.addString(a) },
952 .suffix = .{ .value = null },
953 .basename = .{ .value = null },
954 .path = .{ .value = null },
955 .producer = .{ .value = null },
956 .generated = .{ .value = null },
957 },
958 .output_file, .output_file_dep => |a, tag| .{
959 .flags = .{
960 .tag = .output_file,
961 .prefix = a.prefix.len != 0,
962 .suffix = a.suffix.len != 0,
963 .basename = a.basename.len != 0,
964 .path = false,
965 .producer = false,
966 .generated = true,
967 .dep_file = tag == .output_file_dep,
968 .make_absolute = a.make_absolute,
969 },
970 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
971 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
972 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
973 .path = .{ .value = null },
974 .producer = .{ .value = null },
975 .generated = .{ .value = a.generated_file },
976 },
977 .output_directory => |a| .{
978 .flags = .{
979 .tag = .output_directory,
980 .prefix = a.prefix.len != 0,
981 .suffix = a.suffix.len != 0,
982 .basename = a.basename.len != 0,
983 .path = false,
984 .producer = false,
985 .generated = true,
986 .dep_file = false,
987 .make_absolute = a.make_absolute,
988 },
989 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
990 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
991 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
992 .path = .{ .value = null },
993 .producer = .{ .value = null },
994 .generated = .{ .value = a.generated_file },
995 },
996 .passthru => .{
997 .flags = .{
998 .tag = .passthru,
999 .prefix = false,
1000 .suffix = false,
1001 .basename = false,
1002 .path = false,
1003 .producer = false,
1004 .generated = false,
1005 .dep_file = false,
1006 .make_absolute = false,
1007 },
1008 .prefix = .{ .value = null },
1009 .suffix = .{ .value = null },
1010 .basename = .{ .value = null },
1011 .path = .{ .value = null },
1012 .producer = .{ .value = null },
1013 .generated = .{ .value = null },
1014 },
1015 });
1016 }
1017 return result;
1018}
1019
1020fn initIncludeDirList(
1021 s: *Serialize,
1022 list: []const std.Build.Module.IncludeDir,
1023) ![]const Configuration.Module.IncludeDir {
1024 const result = try s.arena.alloc(Configuration.Module.IncludeDir, list.len);
1025 for (result, list) |*dest, src| dest.* = switch (src) {
1026 .path => |lp| .{ .path = try addLazyPath(s, lp) },
1027 .path_system => |lp| .{ .path_system = try addLazyPath(s, lp) },
1028 .path_after => |lp| .{ .path_after = try addLazyPath(s, lp) },
1029 .framework_path => |lp| .{ .framework_path = try addLazyPath(s, lp) },
1030 .framework_path_system => |lp| .{ .framework_path_system = try addLazyPath(s, lp) },
1031 .embed_path => |lp| .{ .embed_path = try addLazyPath(s, lp) },
1032 .other_step => |cs| .{ .path = try addLazyPath(s, cs.installed_headers_include_tree.?.getDirectory()) },
1033 .config_header_step => |chs| .{ .config_header_step = stepIndex(s, &chs.step) },
1034 };
1035 return result;
1036}
1037
1038fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index {
1039 const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len);
1040 for (result, list) |*dest, src| dest.* = try addLazyPath(s, src);
1041 return result;
1042}
1043
1044fn initStringList(s: *Serialize, list: []const []const u8) ![]const Configuration.String {
1045 const wc = s.wc;
1046 const result = try s.arena.alloc(Configuration.String, list.len);
1047 for (result, list) |*dest, src| dest.* = try wc.addString(src);
1048 return result;
1049}
1050
1051fn initCopyList(s: *Serialize, list: []const Step.WriteFile.Copy) ![]const Configuration.Step.WriteFile.Copy {
1052 const result = try s.arena.alloc(Configuration.Step.WriteFile.Copy, list.len);
1053 for (result, list) |*dest, src| dest.* = .{
1054 .sub_path = src.sub_path,
1055 .src_file = try s.addLazyPath(src.src_file),
1056 };
1057 return result;
1058}
1059
1060fn initOptionalStringList(s: *Serialize, list: []const ?[]const u8) ![]const Configuration.OptionalString {
1061 const wc = s.wc;
1062 const result = try s.arena.alloc(Configuration.OptionalString, list.len);
1063 for (result, list) |*dest, src| dest.* = try wc.addOptionalString(src);
1064 return result;
1065}
1066
1067fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
1068 if (s.module_map.get(m)) |index| return index;
1069
1070 const wc = s.wc;
1071 const arena = s.arena;
1072
1073 const rpaths = try arena.alloc(Configuration.Module.RPath, m.rpaths.items.len);
1074 for (rpaths, m.rpaths.items) |*dest, src| dest.* = switch (src) {
1075 .lazy_path => |lp| .{ .lazy_path = try addLazyPath(s, lp) },
1076 .special => |slice| .{ .special = try wc.addString(slice) },
1077 };
1078
1079 const link_objects = try arena.alloc(Configuration.Module.LinkObject, m.link_objects.items.len);
1080 for (link_objects, m.link_objects.items) |*dest, *src| dest.* = switch (src.*) {
1081 .static_path => |lp| .{ .static_path = try addLazyPath(s, lp) },
1082 .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
1083 .system_lib => |*sl| .{ .system_lib = try addSystemLib(s, sl) },
1084 .assembly_file => |lp| .{ .assembly_file = try addLazyPath(s, lp) },
1085 .c_source_file => |csf| .{ .c_source_file = try addCSourceFile(s, csf) },
1086 .c_source_files => |csf| .{ .c_source_files = try addCSourceFiles(s, csf) },
1087 .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },
1088 };
1089
1090 const frameworks = try arena.alloc(Configuration.Module.Framework, m.frameworks.entries.len);
1091 for (frameworks, m.frameworks.keys(), m.frameworks.values()) |*dest, name, options| dest.* = .{
1092 .flags = .{
1093 .needed = options.needed,
1094 .weak = options.weak,
1095 },
1096 .name = try wc.addString(name),
1097 };
1098
1099 const lib_paths = try initLazyPathList(s, m.lib_paths.items);
1100 const c_macros = try initStringList(s, m.c_macros.items);
1101 const export_symbol_names = try initStringList(s, m.export_symbol_names);
1102
1103 const module_index: Configuration.Module.Index = try wc.addExtra(Configuration.Module, .{
1104 .flags = .{
1105 .optimize = .init(m.optimize),
1106 .strip = .init(m.strip),
1107 .unwind_tables = .init(m.unwind_tables),
1108 .dwarf_format = .init(m.dwarf_format),
1109 .single_threaded = .init(m.single_threaded),
1110 .stack_protector = .init(m.stack_protector),
1111 .stack_check = .init(m.stack_check),
1112 .sanitize_c = .init(m.sanitize_c),
1113 .sanitize_thread = .init(m.sanitize_thread),
1114 .fuzz = .init(m.fuzz),
1115 .code_model = m.code_model,
1116 .c_macros = c_macros.len != 0,
1117 .include_dirs = m.include_dirs.items.len != 0,
1118 .lib_paths = lib_paths.len != 0,
1119 .rpaths = rpaths.len != 0,
1120 .frameworks = frameworks.len != 0,
1121 .link_objects = link_objects.len != 0,
1122 .export_symbol_names = export_symbol_names.len != 0,
1123 },
1124 .flags2 = .{
1125 .valgrind = .init(m.valgrind),
1126 .pic = .init(m.pic),
1127 .red_zone = .init(m.red_zone),
1128 .omit_frame_pointer = .init(m.omit_frame_pointer),
1129 .error_tracing = .init(m.error_tracing),
1130 .link_libc = .init(m.link_libc),
1131 .link_libcpp = .init(m.link_libcpp),
1132 .no_builtin = .init(m.no_builtin),
1133 },
1134 .owner = try s.builderToPackage(m.owner),
1135 .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file),
1136 .import_table = .invalid,
1137 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
1138 .c_macros = .{ .slice = c_macros },
1139 .lib_paths = .{ .slice = lib_paths },
1140 .export_symbol_names = .{ .slice = export_symbol_names },
1141 .include_dirs = .init(try s.initIncludeDirList(m.include_dirs.items)),
1142 .rpaths = .init(rpaths),
1143 .link_objects = .init(link_objects),
1144 .frameworks = .{ .slice = frameworks },
1145 });
1146
1147 // The import table is the only place that modules can form dependency
1148 // loops. Therefore, we populate the module indexes only after adding
1149 // the module to module_map.
1150 try s.module_map.putNoClobber(arena, m, module_index);
1151
1152 var imports = try std.MultiArrayList(Configuration.ImportTable.Import).initCapacity(arena, m.import_table.entries.len);
1153 imports.len = m.import_table.entries.len;
1154 for (
1155 imports.items(.name),
1156 imports.items(.module),
1157 m.import_table.keys(),
1158 m.import_table.values(),
1159 ) |*dest_name, *dest_module, src_name, src_module| {
1160 dest_name.* = try wc.addString(src_name);
1161 dest_module.* = try addModule(s, src_module);
1162 }
1163
1164 comptime assert(std.mem.eql(u8, @typeInfo(Configuration.Module).@"struct".field_names[2], "import_table"));
1165 comptime assert(@typeInfo(Configuration.Module).@"struct".field_types[2] == Configuration.ImportTable.Index);
1166 assert(wc.extra.items[@intFromEnum(module_index) + 2] == @intFromEnum(Configuration.ImportTable.Index.invalid));
1167 const import_table_index = try wc.addDeduped(Configuration.ImportTable, .{
1168 .imports = .{ .mal = imports },
1169 });
1170 wc.extra.items[@intFromEnum(module_index) + 2] = @intFromEnum(import_table_index);
1171
1172 return module_index;
1173}
1174
1175fn stepIndex(s: *const Serialize, step: *Step) Configuration.Step.Index {
1176 return @enumFromInt(s.step_map.getIndex(step).?);
1177}
1178
1179fn addOptionalResolvedTarget(
1180 wc: *Configuration.Wip,
1181 optional_resolved_target: ?std.Build.ResolvedTarget,
1182) !Configuration.ResolvedTarget.OptionalIndex {
1183 const resolved_target = optional_resolved_target orelse return .none;
1184 return .init(try wc.addDeduped(Configuration.ResolvedTarget, .{
1185 .query = try wc.addTargetQuery(&resolved_target.query),
1186 .result = try wc.addTarget(resolved_target.result),
1187 }));
1188}
1189
1190/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
1191/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
1192fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
1193 const root_module = if (step.cast(Step.Compile)) |cs| root: {
1194 break :root cs.root_module;
1195 } else return; // not a compile step so no module dependencies
1196
1197 // Starting from `root_module`, discover all modules in this graph.
1198 const modules = root_module.getGraph().modules;
1199
1200 // For each of those modules, set up the implied step dependencies.
1201 for (modules) |mod| {
1202 if (mod.root_source_file) |lp| lp.addStepDependencies(step);
1203 for (mod.include_dirs.items) |include_dir| switch (include_dir) {
1204 .path,
1205 .path_system,
1206 .path_after,
1207 .framework_path,
1208 .framework_path_system,
1209 .embed_path,
1210 => |lp| lp.addStepDependencies(step),
1211
1212 .other_step => |other| {
1213 other.getEmittedIncludeTree().addStepDependencies(step);
1214 step.dependOn(&other.step);
1215 },
1216
1217 .config_header_step => |other| step.dependOn(&other.step),
1218 };
1219 for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
1220 for (mod.rpaths.items) |rpath| switch (rpath) {
1221 .lazy_path => |lp| lp.addStepDependencies(step),
1222 .special => {},
1223 };
1224 for (mod.link_objects.items) |link_object| switch (link_object) {
1225 .static_path,
1226 .assembly_file,
1227 => |lp| lp.addStepDependencies(step),
1228 .other_step => |other| step.dependOn(&other.step),
1229 .system_lib => {},
1230 .c_source_file => |source| source.file.addStepDependencies(step),
1231 .c_source_files => |source_files| source_files.root.addStepDependencies(step),
1232 .win32_resource_file => |rc_source| {
1233 rc_source.file.addStepDependencies(step);
1234 for (rc_source.include_paths) |lp| lp.addStepDependencies(step);
1235 },
1236 };
1237 }
1238}
1239
1240fn addInstallDirDefaultNull(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !?Configuration.InstallDestDir {
1241 return try addInstallDir(wc, install_dir orelse return null);
1242}
1243
1244fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir {
1245 switch (install_dir orelse return .none) {
1246 .prefix => return .prefix,
1247 .lib => return .lib,
1248 .bin => return .bin,
1249 .header => return .header,
1250 .custom => |sub_path| return .initCustom(try wc.addString(sub_path)),
1251 }
1252}