blob c8aa77ed (73582B) - Raw
1 const builtin = @import("builtin"); 2 3 const std = @import("std"); 4 const Allocator = std.mem.Allocator; 5 const Color = std.zig.Color; 6 const Configuration = std.Build.Configuration; 7 const File = std.Io.File; 8 const Io = std.Io; 9 const Step = std.Build.Step; 10 const Writer = std.Io.Writer; 11 const assert = std.debug.assert; 12 const fatal = std.process.fatal; 13 const fmt = std.fmt; 14 const log = std.log; 15 const mem = std.mem; 16 const process = std.process; 17 18 pub const root = @import("@build"); 19 pub const dependencies = @import("@dependencies"); 20 21 pub const std_options: std.Options = .{ 22 .side_channels_mitigations = .none, 23 .http_disable_tls = true, 24 }; 25 26 pub fn main(init: process.Init.Minimal) !void { 27 var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); 28 defer arena_allocator.deinit(); 29 const arena = arena_allocator.allocator(); 30 31 // The configurer is always short-lived because all it does is serialize 32 // the configuration, which is picked up by a separate maker process. 33 var threaded: std.Io.Threaded = .init(arena, .{ 34 .environ = init.environ, 35 .argv0 = .init(init.args), 36 }); 37 defer threaded.deinit(); 38 const io = threaded.io(); 39 40 const args = try init.args.toSlice(arena); 41 42 var arg_i: usize = 1; // Skip own executable name. 43 44 const zig_exe = expectArgOrFatal(args, &arg_i, "--zig"); 45 const build_root_sub_path = expectArgOrFatal(args, &arg_i, "--build-root"); 46 47 var graph: std.Build.Graph = .{ 48 .io = io, 49 .arena = arena, 50 .environ_map = try init.environ.createMap(arena), 51 // TODO get this from parent process instead 52 .host = .{ 53 .query = .{}, 54 .result = try std.zig.system.resolveTargetQuery(io, .{}), 55 }, 56 .generated_files = .empty, 57 .zig_exe = zig_exe, 58 59 // Created before running the user's configure script so that some things 60 // can be added during script execution such as strings. 61 // 62 // Use of arena here is load-bearing because `std.Build.dupe` is 63 // implemented by string internment, and then returning the interned 64 // slice. When the string bytes array is reallocated, that reference 65 // must stay alive. 66 .wip_configuration = .init(arena), 67 }; 68 assert(try graph.wip_configuration.addString("") == .empty); 69 assert(try graph.wip_configuration.addString("root") == .root); 70 71 const cwd: Io.Dir = .cwd(); 72 73 const build_root: std.Build.Cache.Path = .{ 74 .root_dir = .{ 75 .handle = try cwd.openDir(io, build_root_sub_path, .{}), 76 .path = build_root_sub_path, 77 }, 78 }; 79 80 const builder = try std.Build.create(&graph, build_root, dependencies.root_deps); 81 82 var color: Color = .auto; 83 84 while (nextArg(args, &arg_i)) |arg| { 85 if (mem.cutPrefix(u8, arg, "-D")) |option_contents| { 86 if (option_contents.len == 0) 87 fatalWithHint("expected option name after '-D'", .{}); 88 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { 89 const option_name = option_contents[0..name_end]; 90 const option_value = option_contents[name_end + 1 ..]; 91 if (try builder.addUserInputOption(option_name, option_value)) 92 fatal(" access the help menu with 'zig build -h'", .{}); 93 } else { 94 if (try builder.addUserInputFlag(option_contents)) 95 fatal(" access the help menu with 'zig build -h'", .{}); 96 } 97 } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| { 98 try graph.system_integration_options.put(arena, name, .user_enabled); 99 } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| { 100 try graph.system_integration_options.put(arena, name, .user_disabled); 101 } else if (mem.eql(u8, arg, "--release")) { 102 graph.release_mode = .any; 103 } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| { 104 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse { 105 fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg}); 106 }; 107 } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { 108 color = std.meta.stringToEnum(Color, rest) orelse 109 fatalWithHint("expected --color=[auto|on|off]; found: {s}", .{arg}); 110 } else if (mem.eql(u8, arg, "--system")) { 111 // The usage text shows another argument after this parameter 112 // but it is handled by the parent process. The build runner 113 // only sees this flag. 114 graph.system_package_mode = true; 115 } else if (mem.eql(u8, arg, "--verbose")) { 116 graph.verbose = true; 117 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| { 118 graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest) orelse 119 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg}); 120 } else { 121 fatalWithHint("unrecognized argument: {s}", .{arg}); 122 } 123 } 124 125 const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map); 126 const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map); 127 128 graph.stderr_mode = switch (color) { 129 .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), 130 .on => .escape_codes, 131 .off => .no_color, 132 }; 133 134 try builder.runBuild(root); 135 136 if (builder.validateUserInputDidItFail()) { 137 fatal(" access the help menu with 'zig build -h'", .{}); 138 } 139 140 try serializePackageOptions(builder, &graph.wip_configuration); 141 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration); 142 143 var stdout_buffer: [1024]u8 = undefined; 144 var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); 145 serialize(builder, &graph.wip_configuration, &file_writer.interface) catch |err| switch (err) { 146 error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}), 147 error.OutOfMemory => |e| return e, 148 }; 149 file_writer.flush() catch |err| fatal("failed to write configuration output: {t}", .{err}); 150 151 // This executable is short-lived and run in Debug mode, so we'd rather 152 // have `zig build` run faster than catch resource leaks in the user's 153 // build.zig script (or, frankly, this configure runner), therefore we call 154 // exit directly here rather than cleanExit. 155 process.exit(0); 156 } 157 158 const Serialize = struct { 159 arena: Allocator, 160 wc: *Configuration.Wip, 161 module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty, 162 package_map: std.AutoArrayHashMapUnmanaged(*std.Build, Configuration.Package.Index) = .empty, 163 /// Index corresponds to `Configuration.steps` index. 164 step_map: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty, 165 166 fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index { 167 if (b.pkg_hash.len == 0) return .root; 168 const arena = s.arena; 169 const wc = s.wc; 170 const gop = try s.package_map.getOrPut(arena, b); 171 if (!gop.found_existing) { 172 gop.value_ptr.* = try wc.addExtra(Configuration.Package, .{ 173 .hash = try wc.addString(b.pkg_hash), 174 .dep_prefix = try wc.addString(b.dep_prefix), 175 .root_path = try wc.addString(try b.root.toString(arena)), 176 }); 177 } 178 return gop.value_ptr.*; 179 } 180 181 fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.LazyPath.OptionalIndex { 182 const wc = s.wc; 183 return @enumFromInt(switch (lp orelse return .none) { 184 .src_path => |src_path| i: { 185 const sub_path = try wc.addString(src_path.sub_path); 186 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{ 187 .owner = try s.builderToPackage(src_path.owner), 188 .sub_path = sub_path, 189 }); 190 }, 191 .generated => |generated| i: { 192 const sub_path = try wc.addString(generated.sub_path); 193 break :i try wc.addExtraErased(Configuration.LazyPath.Generated, .{ 194 .flags = .{ .up = @intCast(generated.up) }, 195 .index = generated.index, 196 .sub_path = sub_path, 197 }); 198 }, 199 .cwd_relative => |cwd_relative_sub_path| i: { 200 const sub_path = try wc.addString(cwd_relative_sub_path); 201 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{ 202 .flags = .{ .base = .cwd }, 203 .sub_path = sub_path, 204 }); 205 }, 206 .relative => |relative| i: { 207 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{ 208 .flags = .{ .base = relative.base }, 209 .sub_path = relative.sub_path, 210 }); 211 }, 212 .dependency => |dependency| i: { 213 const sub_path = try wc.addString(dependency.sub_path); 214 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{ 215 .owner = try s.builderToPackage(dependency.dependency.builder), 216 .sub_path = sub_path, 217 }); 218 }, 219 }); 220 } 221 222 fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !?Configuration.LazyPath.Index { 223 return (try addOptionalLazyPathEnum(s, lp)).unwrap(); 224 } 225 226 fn addLazyPath(s: *Serialize, lp: std.Build.LazyPath) !Configuration.LazyPath.Index { 227 return @enumFromInt(@intFromEnum(try addOptionalLazyPathEnum(s, lp))); 228 } 229 230 fn addOptionalSemVer(s: *Serialize, sem_ver: ?std.SemanticVersion) !?Configuration.String { 231 return if (sem_ver) |sv| try s.wc.addSemVer(sv) else null; 232 } 233 234 fn addOptionalString(s: *Serialize, opt_slice: ?[]const u8) !?Configuration.String { 235 return if (opt_slice) |slice| try s.wc.addString(slice) else null; 236 } 237 238 fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index { 239 const wc = s.wc; 240 return try wc.addDeduped(Configuration.SystemLib, .{ 241 .flags = .{ 242 .needed = sl.needed, 243 .weak = sl.weak, 244 .use_pkg_config = sl.use_pkg_config, 245 .preferred_link_mode = sl.preferred_link_mode, 246 .search_strategy = sl.search_strategy, 247 }, 248 .name = try wc.addString(sl.name), 249 }); 250 } 251 252 fn addCSourceFile(s: *Serialize, csf: *const std.Build.Module.CSourceFile) !Configuration.CSourceFile.Index { 253 const wc = s.wc; 254 const args = try initStringList(s, csf.flags); 255 return try wc.addExtra(Configuration.CSourceFile, .{ 256 .flags = .{ 257 .args_len = @intCast(args.len), 258 .lang = .init(csf.language), 259 }, 260 .file = try addLazyPath(s, csf.file), 261 .args = .{ .slice = args }, 262 }); 263 } 264 265 fn addCSourceFiles(s: *Serialize, csf: *const std.Build.Module.CSourceFiles) !Configuration.CSourceFiles.Index { 266 const wc = s.wc; 267 const sub_paths = try initStringList(s, csf.files); 268 const args = try initStringList(s, csf.flags); 269 return try wc.addExtra(Configuration.CSourceFiles, .{ 270 .flags = .{ 271 .args_len = @intCast(args.len), 272 .lang = .init(csf.language), 273 }, 274 .root = try addLazyPath(s, csf.root), 275 .sub_paths = .{ .slice = sub_paths }, 276 .args = .{ .slice = args }, 277 }); 278 } 279 280 fn addRcSourceFile(s: *Serialize, rsf: *const std.Build.Module.RcSourceFile) !Configuration.RcSourceFile.Index { 281 const wc = s.wc; 282 const include_paths = try initLazyPathList(s, rsf.include_paths); 283 const args = try initStringList(s, rsf.flags); 284 return try wc.addExtra(Configuration.RcSourceFile, .{ 285 .flags = .{ 286 .args_len = @intCast(args.len), 287 .include_paths = include_paths.len != 0, 288 }, 289 .file = try addLazyPath(s, rsf.file), 290 .include_paths = .{ .slice = include_paths }, 291 .args = .{ .slice = args }, 292 }); 293 } 294 295 fn addEnvironMap(s: *Serialize, opt_map: ?*std.process.Environ.Map) !?Configuration.EnvironMap.Index { 296 const wc = s.wc; 297 const map = opt_map orelse return null; 298 return try wc.addDeduped(Configuration.EnvironMap, .{ 299 .keys = try wc.addStringList(map.array_hash_map.keys()), 300 .values = try wc.addStringList(map.array_hash_map.values()), 301 }); 302 } 303 304 fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuration.Step.Run.Arg.Index { 305 const wc = s.wc; 306 const result = try s.arena.alloc(Configuration.Step.Run.Arg.Index, args.len); 307 for (result, args) |*dest, src| { 308 dest.* = try wc.addExtra(Configuration.Step.Run.Arg, switch (src) { 309 .artifact => |a| .{ 310 .flags = .{ 311 .tag = .artifact, 312 .prefix = a.prefix.len != 0, 313 .suffix = false, 314 .basename = false, 315 .path = false, 316 .producer = true, 317 .generated = false, 318 .dep_file = false, 319 }, 320 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 321 .suffix = .{ .value = null }, 322 .basename = .{ .value = null }, 323 .path = .{ .value = null }, 324 .producer = .{ .value = stepIndex(s, &a.artifact.step) }, 325 .generated = .{ .value = null }, 326 }, 327 .lazy_path => |a| .{ 328 .flags = .{ 329 .tag = .path_file, 330 .prefix = a.prefix.len != 0, 331 .suffix = false, 332 .basename = false, 333 .path = true, 334 .producer = false, 335 .generated = false, 336 .dep_file = false, 337 }, 338 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 339 .suffix = .{ .value = null }, 340 .basename = .{ .value = null }, 341 .path = .{ .value = try addLazyPath(s, a.lazy_path) }, 342 .producer = .{ .value = null }, 343 .generated = .{ .value = null }, 344 }, 345 .decorated_directory => |a| .{ 346 .flags = .{ 347 .tag = .path_directory, 348 .prefix = a.prefix.len != 0, 349 .suffix = a.suffix.len != 0, 350 .basename = false, 351 .path = true, 352 .producer = false, 353 .generated = false, 354 .dep_file = false, 355 }, 356 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 357 .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null }, 358 .basename = .{ .value = null }, 359 .path = .{ .value = try addLazyPath(s, a.lazy_path) }, 360 .producer = .{ .value = null }, 361 .generated = .{ .value = null }, 362 }, 363 .file_content => |a| .{ 364 .flags = .{ 365 .tag = .file_content, 366 .prefix = a.prefix.len != 0, 367 .suffix = false, 368 .basename = false, 369 .path = true, 370 .producer = false, 371 .generated = false, 372 .dep_file = false, 373 }, 374 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 375 .suffix = .{ .value = null }, 376 .basename = .{ .value = null }, 377 .path = .{ .value = try addLazyPath(s, a.lazy_path) }, 378 .producer = .{ .value = null }, 379 .generated = .{ .value = null }, 380 }, 381 .bytes => |a| .{ 382 .flags = .{ 383 .tag = .string, 384 .prefix = true, 385 .suffix = false, 386 .basename = false, 387 .path = false, 388 .producer = false, 389 .generated = false, 390 .dep_file = false, 391 }, 392 .prefix = .{ .value = try wc.addString(a) }, 393 .suffix = .{ .value = null }, 394 .basename = .{ .value = null }, 395 .path = .{ .value = null }, 396 .producer = .{ .value = null }, 397 .generated = .{ .value = null }, 398 }, 399 .output_file, .output_file_dep => |a, tag| .{ 400 .flags = .{ 401 .tag = .output_file, 402 .prefix = a.prefix.len != 0, 403 .suffix = false, 404 .basename = a.basename.len != 0, 405 .path = false, 406 .producer = false, 407 .generated = true, 408 .dep_file = tag == .output_file_dep, 409 }, 410 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 411 .suffix = .{ .value = null }, 412 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null }, 413 .path = .{ .value = null }, 414 .producer = .{ .value = null }, 415 .generated = .{ .value = a.generated_file }, 416 }, 417 .output_directory => |a| .{ 418 .flags = .{ 419 .tag = .output_directory, 420 .prefix = a.prefix.len != 0, 421 .suffix = false, 422 .basename = a.basename.len != 0, 423 .path = false, 424 .producer = false, 425 .generated = true, 426 .dep_file = false, 427 }, 428 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null }, 429 .suffix = .{ .value = null }, 430 .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null }, 431 .path = .{ .value = null }, 432 .producer = .{ .value = null }, 433 .generated = .{ .value = a.generated_file }, 434 }, 435 .passthru => .{ 436 .flags = .{ 437 .tag = .passthru, 438 .prefix = false, 439 .suffix = false, 440 .basename = false, 441 .path = false, 442 .producer = false, 443 .generated = false, 444 .dep_file = false, 445 }, 446 .prefix = .{ .value = null }, 447 .suffix = .{ .value = null }, 448 .basename = .{ .value = null }, 449 .path = .{ .value = null }, 450 .producer = .{ .value = null }, 451 .generated = .{ .value = null }, 452 }, 453 }); 454 } 455 return result; 456 } 457 458 fn initIncludeDirList( 459 s: *Serialize, 460 list: []const std.Build.Module.IncludeDir, 461 ) ![]const Configuration.Module.IncludeDir { 462 const result = try s.arena.alloc(Configuration.Module.IncludeDir, list.len); 463 for (result, list) |*dest, src| dest.* = switch (src) { 464 .path => |lp| .{ .path = try addLazyPath(s, lp) }, 465 .path_system => |lp| .{ .path_system = try addLazyPath(s, lp) }, 466 .path_after => |lp| .{ .path_after = try addLazyPath(s, lp) }, 467 .framework_path => |lp| .{ .framework_path = try addLazyPath(s, lp) }, 468 .framework_path_system => |lp| .{ .framework_path_system = try addLazyPath(s, lp) }, 469 .embed_path => |lp| .{ .embed_path = try addLazyPath(s, lp) }, 470 .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) }, 471 .config_header_step => |chs| .{ .config_header_step = stepIndex(s, &chs.step) }, 472 }; 473 return result; 474 } 475 476 fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index { 477 const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len); 478 for (result, list) |*dest, src| dest.* = try addLazyPath(s, src); 479 return result; 480 } 481 482 fn initStringList(s: *Serialize, list: []const []const u8) ![]const Configuration.String { 483 const wc = s.wc; 484 const result = try s.arena.alloc(Configuration.String, list.len); 485 for (result, list) |*dest, src| dest.* = try wc.addString(src); 486 return result; 487 } 488 489 fn initCopyList(s: *Serialize, list: []const Step.WriteFile.Copy) ![]const Configuration.Step.WriteFile.Copy { 490 const result = try s.arena.alloc(Configuration.Step.WriteFile.Copy, list.len); 491 for (result, list) |*dest, src| dest.* = .{ 492 .sub_path = src.sub_path, 493 .src_file = try s.addLazyPath(src.src_file), 494 }; 495 return result; 496 } 497 498 fn initOptionalStringList(s: *Serialize, list: []const ?[]const u8) ![]const Configuration.OptionalString { 499 const wc = s.wc; 500 const result = try s.arena.alloc(Configuration.OptionalString, list.len); 501 for (result, list) |*dest, src| dest.* = try wc.addOptionalString(src); 502 return result; 503 } 504 505 fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index { 506 if (s.module_map.get(m)) |index| return index; 507 508 const wc = s.wc; 509 const arena = s.arena; 510 511 const rpaths = try arena.alloc(Configuration.Module.RPath, m.rpaths.items.len); 512 for (rpaths, m.rpaths.items) |*dest, src| dest.* = switch (src) { 513 .lazy_path => |lp| .{ .lazy_path = try addLazyPath(s, lp) }, 514 .special => |slice| .{ .special = try wc.addString(slice) }, 515 }; 516 517 const link_objects = try arena.alloc(Configuration.Module.LinkObject, m.link_objects.items.len); 518 for (link_objects, m.link_objects.items) |*dest, *src| dest.* = switch (src.*) { 519 .static_path => |lp| .{ .static_path = try addLazyPath(s, lp) }, 520 .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) }, 521 .system_lib => |*sl| .{ .system_lib = try addSystemLib(s, sl) }, 522 .assembly_file => |lp| .{ .assembly_file = try addLazyPath(s, lp) }, 523 .c_source_file => |csf| .{ .c_source_file = try addCSourceFile(s, csf) }, 524 .c_source_files => |csf| .{ .c_source_files = try addCSourceFiles(s, csf) }, 525 .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) }, 526 }; 527 528 const frameworks = try arena.alloc(Configuration.Module.Framework, m.frameworks.entries.len); 529 for (frameworks, m.frameworks.keys(), m.frameworks.values()) |*dest, name, options| dest.* = .{ 530 .flags = .{ 531 .needed = options.needed, 532 .weak = options.weak, 533 }, 534 .name = try wc.addString(name), 535 }; 536 537 const lib_paths = try initLazyPathList(s, m.lib_paths.items); 538 const c_macros = try initStringList(s, m.c_macros.items); 539 const export_symbol_names = try initStringList(s, m.export_symbol_names); 540 541 const module_index: Configuration.Module.Index = try wc.addExtra(Configuration.Module, .{ 542 .flags = .{ 543 .optimize = .init(m.optimize), 544 .strip = .init(m.strip), 545 .unwind_tables = .init(m.unwind_tables), 546 .dwarf_format = .init(m.dwarf_format), 547 .single_threaded = .init(m.single_threaded), 548 .stack_protector = .init(m.stack_protector), 549 .stack_check = .init(m.stack_check), 550 .sanitize_c = .init(m.sanitize_c), 551 .sanitize_thread = .init(m.sanitize_thread), 552 .fuzz = .init(m.fuzz), 553 .code_model = m.code_model, 554 .c_macros = c_macros.len != 0, 555 .include_dirs = m.include_dirs.items.len != 0, 556 .lib_paths = lib_paths.len != 0, 557 .rpaths = rpaths.len != 0, 558 .frameworks = frameworks.len != 0, 559 .link_objects = link_objects.len != 0, 560 .export_symbol_names = export_symbol_names.len != 0, 561 }, 562 .flags2 = .{ 563 .valgrind = .init(m.valgrind), 564 .pic = .init(m.pic), 565 .red_zone = .init(m.red_zone), 566 .omit_frame_pointer = .init(m.omit_frame_pointer), 567 .error_tracing = .init(m.error_tracing), 568 .link_libc = .init(m.link_libc), 569 .link_libcpp = .init(m.link_libcpp), 570 .no_builtin = .init(m.no_builtin), 571 }, 572 .owner = try s.builderToPackage(m.owner), 573 .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file), 574 .import_table = .invalid, 575 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target), 576 .c_macros = .{ .slice = c_macros }, 577 .lib_paths = .{ .slice = lib_paths }, 578 .export_symbol_names = .{ .slice = export_symbol_names }, 579 .include_dirs = .init(try s.initIncludeDirList(m.include_dirs.items)), 580 .rpaths = .init(rpaths), 581 .link_objects = .init(link_objects), 582 .frameworks = .{ .slice = frameworks }, 583 }); 584 585 // The import table is the only place that modules can form dependency 586 // loops. Therefore, we populate the module indexes only after adding 587 // the module to module_map. 588 try s.module_map.putNoClobber(arena, m, module_index); 589 590 var imports = try std.MultiArrayList(Configuration.ImportTable.Import).initCapacity(arena, m.import_table.entries.len); 591 imports.len = m.import_table.entries.len; 592 for ( 593 imports.items(.name), 594 imports.items(.module), 595 m.import_table.keys(), 596 m.import_table.values(), 597 ) |*dest_name, *dest_module, src_name, src_module| { 598 dest_name.* = try wc.addString(src_name); 599 dest_module.* = try addModule(s, src_module); 600 } 601 602 comptime assert(std.mem.eql(u8, @typeInfo(Configuration.Module).@"struct".fields[2].name, "import_table")); 603 comptime assert(@typeInfo(Configuration.Module).@"struct".fields[2].type == Configuration.ImportTable.Index); 604 assert(wc.extra.items[@intFromEnum(module_index) + 2] == @intFromEnum(Configuration.ImportTable.Index.invalid)); 605 const import_table_index = try wc.addDeduped(Configuration.ImportTable, .{ 606 .imports = .{ .mal = imports }, 607 }); 608 wc.extra.items[@intFromEnum(module_index) + 2] = @intFromEnum(import_table_index); 609 610 return module_index; 611 } 612 613 fn stepIndex(s: *const Serialize, step: *Step) Configuration.Step.Index { 614 return @enumFromInt(s.step_map.getIndex(step).?); 615 } 616 }; 617 618 fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { 619 const graph = b.graph; 620 const arena = graph.arena; 621 const gpa = wc.gpa; 622 623 var s: Serialize = .{ .wc = wc, .arena = arena }; 624 625 // Starting from all top-level steps in `b`, traverse the entire step graph 626 // and add all step dependencies implied by module graphs. 627 const top_level_steps = b.top_level_steps.values(); 628 try s.step_map.ensureUnusedCapacity(arena, top_level_steps.len); 629 for (top_level_steps) |tls| { 630 s.step_map.putAssumeCapacityNoClobber(&tls.step, {}); 631 } 632 { 633 while (wc.steps.items.len < s.step_map.count()) { 634 const step = s.step_map.keys()[wc.steps.items.len]; 635 636 // Set up any implied dependencies for this step. It's important that we do this first, so 637 // that the loop below discovers steps implied by the module graph. 638 try createModuleDependenciesForStep(step); 639 640 try s.step_map.ensureUnusedCapacity(arena, step.dependencies.items.len); 641 for (step.dependencies.items) |other_step| { 642 s.step_map.putAssumeCapacity(other_step, {}); 643 } 644 645 // Add and then de-duplicate dependencies. 646 const dep_steps = try arena.alloc(Configuration.Step.Index, step.dependencies.items.len); 647 for (dep_steps, step.dependencies.items) |*dest, src| 648 dest.* = @enumFromInt(s.step_map.getIndex(src).?); 649 650 const deps: Configuration.Deps.Index = try wc.addDeduped(Configuration.Deps, .{ 651 .steps = .{ .slice = dep_steps }, 652 }); 653 654 try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity); 655 wc.steps.appendAssumeCapacity(.{ 656 .name = try wc.addString(step.name), 657 .owner = try s.builderToPackage(step.owner), 658 .deps = deps, 659 .max_rss = .fromBytes(step.max_rss), 660 .extended = @enumFromInt(switch (step.tag) { 661 .top_level => e: { 662 const top_level: *Step.TopLevel = @fieldParentPtr("step", step); 663 break :e try wc.addExtraErased(Configuration.Step.TopLevel, .{ 664 .description = try wc.addString(top_level.description), 665 }); 666 }, 667 .compile => e: { 668 const c: *Step.Compile = @fieldParentPtr("step", step); 669 const exec_cmd_args: []const ?[]const u8 = c.exec_cmd_args orelse &.{}; 670 const installed_headers: []u32 = try arena.alloc(u32, c.installed_headers.items.len); 671 for (installed_headers, c.installed_headers.items) |*dst, src| switch (src) { 672 .file => |file| { 673 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.File, .{ 674 .source = try s.addLazyPath(file.source), 675 .dest_sub_path = try wc.addString(file.dest_rel_path), 676 }); 677 }, 678 .directory => |directory| { 679 const include_extensions = directory.options.include_extensions orelse &.{}; 680 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.Directory, .{ 681 .flags = .{ 682 .include_extensions = include_extensions.len != 0, 683 .exclude_extensions = directory.options.exclude_extensions.len != 0, 684 }, 685 .source = try s.addLazyPath(directory.source), 686 .dest_sub_path = try wc.addString(directory.dest_rel_path), 687 .exclude_extensions = .{ .slice = try s.initStringList(directory.options.exclude_extensions) }, 688 .include_extensions = .{ .slice = try s.initStringList(include_extensions) }, 689 }); 690 }, 691 }; 692 693 break :e try wc.addExtraErased(Configuration.Step.Compile, .{ 694 .flags = .{ 695 .filters_len = c.filters.len != 0, 696 .exec_cmd_args_len = exec_cmd_args.len != 0, 697 .installed_headers_len = installed_headers.len != 0, 698 .force_undefined_symbols_len = c.force_undefined_symbols.entries.len != 0, 699 700 .verbose_link = c.verbose_link, 701 .verbose_cc = c.verbose_cc, 702 .rdynamic = c.rdynamic, 703 .import_memory = c.import_memory, 704 .export_memory = c.export_memory, 705 .import_symbols = c.import_symbols, 706 .import_table = c.import_table, 707 .export_table = c.export_table, 708 .shared_memory = c.shared_memory, 709 .link_eh_frame_hdr = c.link_eh_frame_hdr, 710 .link_emit_relocs = c.link_emit_relocs, 711 .link_function_sections = c.link_function_sections, 712 .link_data_sections = c.link_data_sections, 713 .linker_dynamicbase = c.linker_dynamicbase, 714 .link_z_notext = c.link_z_notext, 715 .link_z_relro = c.link_z_relro, 716 .link_z_lazy = c.link_z_lazy, 717 .link_z_defs = c.link_z_defs, 718 .headerpad_max_install_names = c.headerpad_max_install_names, 719 .dead_strip_dylibs = c.dead_strip_dylibs, 720 .force_load_objc = c.force_load_objc, 721 .discard_local_symbols = c.discard_local_symbols, 722 .mingw_unicode_entry_point = c.mingw_unicode_entry_point, 723 }, 724 .flags2 = .{ 725 .pie = .init(c.pie), 726 .formatted_panics = .init(c.formatted_panics), 727 .bundle_compiler_rt = .init(c.bundle_compiler_rt), 728 .bundle_ubsan_rt = .init(c.bundle_ubsan_rt), 729 .each_lib_rpath = .init(c.each_lib_rpath), 730 .link_gc_sections = .init(c.link_gc_sections), 731 .linker_allow_shlib_undefined = .init(c.linker_allow_shlib_undefined), 732 .linker_allow_undefined_version = .init(c.linker_allow_undefined_version), 733 .linker_enable_new_dtags = .init(c.linker_enable_new_dtags), 734 .dll_export_fns = .init(c.dll_export_fns), 735 .use_llvm = .init(c.use_llvm), 736 .use_lld = .init(c.use_lld), 737 .use_new_linker = .init(c.use_new_linker), 738 .allow_so_scripts = .init(c.allow_so_scripts), 739 .sanitize_coverage_trace_pc_guard = .init(c.sanitize_coverage_trace_pc_guard), 740 .linkage = .init(c.linkage), 741 }, 742 .flags3 = .{ 743 .is_linking_libc = c.is_linking_libc, 744 .is_linking_libcpp = c.is_linking_libcpp, 745 .version = c.version != null, 746 .compress_debug_sections = c.compress_debug_sections, 747 .initial_memory = c.initial_memory != null, 748 .max_memory = c.max_memory != null, 749 .kind = c.kind, 750 .global_base = c.global_base != null, 751 .test_runner = if (c.test_runner) |tr| switch (tr.mode) { 752 .simple => .simple, 753 .server => .server, 754 } else .default, 755 .wasi_exec_model = .init(c.wasi_exec_model), 756 .win32_manifest = c.win32_manifest != null, 757 .win32_module_definition = c.win32_module_definition != null, 758 .zig_lib_dir = c.zig_lib_dir != null, 759 .rc_includes = c.rc_includes, 760 .image_base = c.image_base != null, 761 .build_id = .init(c.build_id), 762 .entry = switch (c.entry) { 763 .default => .default, 764 .disabled => .disabled, 765 .enabled => .enabled, 766 .symbol_name => .symbol_name, 767 }, 768 .lto = .init(c.lto), 769 .subsystem = .init(c.subsystem), 770 }, 771 .flags4 = .{ 772 .libc_file = c.libc_file != null, 773 .link_z_common_page_size = c.link_z_common_page_size != null, 774 .link_z_max_page_size = c.link_z_max_page_size != null, 775 .pagezero_size = c.pagezero_size != null, 776 .stack_size = c.stack_size != null, 777 .headerpad_size = c.headerpad_size != null, 778 .error_limit = c.error_limit != null, 779 .install_name = c.install_name != null, 780 .entitlements = c.entitlements != null, 781 .expect_errors = if (c.expect_errors) |x| switch (x) { 782 .contains => .contains, 783 .exact => .exact, 784 .starts_with => .starts_with, 785 .stderr_contains => .stderr_contains, 786 } else .none, 787 .linker_script = c.linker_script != null, 788 .version_script = c.version_script != null, 789 .emit_directory = c.emit_directory != .none, 790 .generated_docs = c.generated_docs != .none, 791 .generated_asm = c.generated_asm != .none, 792 .generated_bin = c.generated_bin != .none, 793 .generated_pdb = c.generated_pdb != .none, 794 .generated_implib = c.generated_implib != .none, 795 .generated_llvm_bc = c.generated_llvm_bc != .none, 796 .generated_llvm_ir = c.generated_llvm_ir != .none, 797 .generated_h = c.generated_h != .none, 798 }, 799 .root_module = try s.addModule(c.root_module), 800 .root_name = try wc.addString(c.name), 801 .linker_script = .{ .value = try s.addOptionalLazyPath(c.linker_script) }, 802 .version_script = .{ .value = try s.addOptionalLazyPath(c.version_script) }, 803 .zig_lib_dir = .{ .value = try s.addOptionalLazyPath(c.zig_lib_dir) }, 804 .libc_file = .{ .value = try s.addOptionalLazyPath(c.libc_file) }, 805 .win32_manifest = .{ .value = try s.addOptionalLazyPath(c.win32_manifest) }, 806 .win32_module_definition = .{ .value = try s.addOptionalLazyPath(c.win32_module_definition) }, 807 .entitlements = .{ .value = try s.addOptionalLazyPath(c.entitlements) }, 808 .version = .{ .value = try s.addOptionalSemVer(c.version) }, 809 .install_name = .{ .value = try s.addOptionalString(c.install_name) }, 810 .initial_memory = .{ .value = c.initial_memory }, 811 .max_memory = .{ .value = c.max_memory }, 812 .global_base = .{ .value = c.global_base }, 813 .image_base = .{ .value = c.image_base }, 814 .link_z_common_page_size = .{ .value = c.link_z_common_page_size }, 815 .link_z_max_page_size = .{ .value = c.link_z_max_page_size }, 816 .pagezero_size = .{ .value = c.pagezero_size }, 817 .stack_size = .{ .value = c.stack_size }, 818 .headerpad_size = .{ .value = c.headerpad_size }, 819 .error_limit = .{ .value = c.error_limit }, 820 .entry = .{ .value = switch (c.entry) { 821 .symbol_name => |name| try wc.addString(name), 822 .default, .disabled, .enabled => null, 823 } }, 824 .build_id = .{ .value = if (c.build_id) |id| switch (id) { 825 .hexstring => |*hexstring| try wc.addString(hexstring.toSlice()), 826 .none, .fast, .uuid, .sha1, .md5 => null, 827 } else null }, 828 .filters = .{ .slice = try s.initStringList(c.filters) }, 829 .exec_cmd_args = .{ .slice = try s.initOptionalStringList(exec_cmd_args) }, 830 .installed_headers = .initErased(installed_headers), 831 .force_undefined_symbols = .{ .slice = try s.initStringList(c.force_undefined_symbols.keys()) }, 832 .expect_errors = .{ .u = if (c.expect_errors) |x| switch (x) { 833 .contains => |slice| .{ .contains = try wc.addString(slice) }, 834 .exact => |exact| .{ .exact = .{ .slice = try s.initStringList(exact) } }, 835 .starts_with => |slice| .{ .starts_with = try wc.addString(slice) }, 836 .stderr_contains => |slice| .{ .stderr_contains = try wc.addString(slice) }, 837 } else .none }, 838 .test_runner = .{ .u = if (c.test_runner) |tr| switch (tr.mode) { 839 .simple => .{ .simple = try s.addLazyPath(tr.path) }, 840 .server => .{ .server = try s.addLazyPath(tr.path) }, 841 } else .default }, 842 843 .emit_directory = .{ .value = c.emit_directory.unwrap() }, 844 .generated_docs = .{ .value = c.generated_docs.unwrap() }, 845 .generated_asm = .{ .value = c.generated_asm.unwrap() }, 846 .generated_bin = .{ .value = c.generated_bin.unwrap() }, 847 .generated_pdb = .{ .value = c.generated_pdb.unwrap() }, 848 .generated_implib = .{ .value = c.generated_implib.unwrap() }, 849 .generated_llvm_bc = .{ .value = c.generated_llvm_bc.unwrap() }, 850 .generated_llvm_ir = .{ .value = c.generated_llvm_ir.unwrap() }, 851 .generated_h = .{ .value = c.generated_h.unwrap() }, 852 }); 853 }, 854 .install_artifact => e: { 855 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step); 856 break :e try wc.addExtraErased(Configuration.Step.InstallArtifact, .{ 857 .flags = .{ 858 .dylib_symlinks = ia.dylib_symlinks, 859 .bin_dir = ia.dest_dir != null, 860 .implib_dir = ia.implib_dir != null, 861 .pdb_dir = ia.pdb_dir != null, 862 .h_dir = ia.h_dir != null, 863 .bin_sub_path = ia.dest_sub_path != null, 864 }, 865 .bin_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.dest_dir) }, 866 .implib_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.implib_dir) }, 867 .pdb_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.pdb_dir) }, 868 .h_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.h_dir) }, 869 .bin_sub_path = .{ .value = try s.addOptionalString(ia.dest_sub_path) }, 870 }); 871 }, 872 .install_file => e: { 873 const sif: *Step.InstallFile = @fieldParentPtr("step", step); 874 break :e try wc.addExtraErased(Configuration.Step.InstallFile, .{ 875 .source = try s.addLazyPath(sif.source), 876 .dest_dir = try addInstallDir(wc, sif.dir), 877 .dest_sub_path = try wc.addString(sif.dest_rel_path), 878 }); 879 }, 880 .install_dir => e: { 881 const sid: *Step.InstallDir = @fieldParentPtr("step", step); 882 const dest_sub_path: ?[]const u8 = if (sid.options.install_subdir.len != 0) 883 sid.options.install_subdir 884 else 885 null; 886 const include_extensions = sid.options.include_extensions orelse &.{}; 887 break :e try wc.addExtraErased(Configuration.Step.InstallDir, .{ 888 .flags = .{ 889 .dest_sub_path = dest_sub_path != null, 890 .exclude_extensions = sid.options.exclude_extensions.len != 0, 891 .include_extensions = include_extensions.len != 0, 892 .include_extensions_active = sid.options.include_extensions != null, 893 .blank_extensions = sid.options.blank_extensions.len != 0, 894 }, 895 .source_dir = try s.addLazyPath(sid.options.source_dir), 896 .dest_dir = try addInstallDir(wc, sid.options.install_dir), 897 .dest_sub_path = .{ .value = try s.addOptionalString(dest_sub_path) }, 898 .exclude_extensions = .{ .slice = try s.initStringList(sid.options.exclude_extensions) }, 899 .include_extensions = .{ .slice = try s.initStringList(include_extensions) }, 900 .blank_extensions = .{ .slice = try s.initStringList(sid.options.blank_extensions) }, 901 }); 902 }, 903 .fail => e: { 904 const sf: *Step.Fail = @fieldParentPtr("step", step); 905 break :e try wc.addExtraErased(Configuration.Step.Fail, .{ 906 .msg = sf.error_msg, 907 }); 908 }, 909 .find_program => @panic("TODO"), 910 .fmt => e: { 911 const sf: *Step.Fmt = @fieldParentPtr("step", step); 912 break :e try wc.addExtraErased(Configuration.Step.Fmt, .{ 913 .flags = .{ 914 .paths = sf.paths.len != 0, 915 .exclude_paths = sf.exclude_paths.len != 0, 916 .check = sf.check, 917 }, 918 .paths = .{ .slice = try s.initLazyPathList(sf.paths) }, 919 .exclude_paths = .{ .slice = try s.initLazyPathList(sf.exclude_paths) }, 920 }); 921 }, 922 .translate_c => e: { 923 const tc: *Step.TranslateC = @fieldParentPtr("step", step); 924 925 const system_libs = try arena.alloc(Configuration.SystemLib.Index, tc.system_libs.items.len); 926 for (system_libs, tc.system_libs.items) |*dest, *src| dest.* = try s.addSystemLib(src); 927 928 break :e try wc.addExtraErased(Configuration.Step.TranslateC, .{ 929 .flags = .{ 930 .include_dirs = tc.include_dirs.items.len != 0, 931 .system_libs = system_libs.len != 0, 932 .c_macros = tc.c_macros.items.len != 0, 933 .link_libc = tc.link_libc, 934 .optimize = .init(tc.optimize), 935 }, 936 .src_path = try s.addLazyPath(tc.source), 937 .output_file = tc.output_file, 938 .include_dirs = .init(try s.initIncludeDirList(tc.include_dirs.items)), 939 .system_libs = .{ .slice = system_libs }, 940 .c_macros = .{ .slice = tc.c_macros.items }, 941 .target = try addOptionalResolvedTarget(wc, tc.target), 942 }); 943 }, 944 .write_file => e: { 945 const wf: *Step.WriteFile = @fieldParentPtr("step", step); 946 947 const directories = try arena.alloc( 948 Configuration.Step.WriteFile.Directory, 949 wf.directories.items.len, 950 ); 951 for (directories, wf.directories.items) |*dest, src| dest.* = .{ 952 .sub_path = src.sub_path, 953 .src_path = try s.addLazyPath(src.src_path), 954 .exclude_extensions = src.exclude_extensions, 955 .include_extensions = src.include_extensions, 956 }; 957 958 break :e try wc.addExtraErased(Configuration.Step.WriteFile, .{ 959 .flags = .{ 960 .embeds = wf.embeds.items.len != 0, 961 .copies = wf.copies.items.len != 0, 962 .directories = directories.len != 0, 963 .mode = switch (wf.mode) { 964 .whole_cached => .whole_cached, 965 .tmp => .tmp, 966 .mutate => .mutate, 967 }, 968 }, 969 .generated_directory = wf.generated_directory, 970 .embeds = .{ .slice = wf.embeds.items }, 971 .copies = .{ .slice = try s.initCopyList(wf.copies.items) }, 972 .directories = .{ .slice = directories }, 973 .mutate_path = .{ .value = switch (wf.mode) { 974 .mutate => |lp| try s.addLazyPath(lp), 975 .whole_cached, .tmp => null, 976 } }, 977 }); 978 }, 979 .update_source_files => e: { 980 const usf: *Step.UpdateSourceFiles = @fieldParentPtr("step", step); 981 break :e try wc.addExtraErased(Configuration.Step.UpdateSourceFiles, .{ 982 .flags = .{ 983 .embeds = usf.embeds.items.len != 0, 984 .copies = usf.copies.items.len != 0, 985 }, 986 .embeds = .{ .slice = usf.embeds.items }, 987 .copies = .{ .slice = try s.initCopyList(usf.copies.items) }, 988 }); 989 }, 990 .run => e: { 991 const run: *Step.Run = @fieldParentPtr("step", step); 992 var expect_stderr_exact: ?Configuration.Bytes = null; 993 var expect_stdout_exact: ?Configuration.Bytes = null; 994 var expect_stderr_match: std.ArrayList(Configuration.Bytes) = .empty; 995 var expect_stdout_match: std.ArrayList(Configuration.Bytes) = .empty; 996 var expect_term: ?struct { 997 status: Configuration.Step.Run.ExpectTermStatus, 998 value: u32, 999 } = null; 1000 switch (run.stdio) { 1001 .check => |checks| for (checks.items) |check| switch (check) { 1002 .expect_stderr_exact => |bytes| expect_stderr_exact = try wc.addBytes(bytes), 1003 .expect_stdout_exact => |bytes| expect_stdout_exact = try wc.addBytes(bytes), 1004 .expect_stderr_match => |bytes| { 1005 try expect_stderr_match.append(arena, try wc.addBytes(bytes)); 1006 }, 1007 .expect_stdout_match => |bytes| { 1008 try expect_stdout_match.append(arena, try wc.addBytes(bytes)); 1009 }, 1010 .expect_term => |t| expect_term = switch (t) { 1011 .exited => |x| .{ .status = .exited, .value = x }, 1012 .signal => |x| .{ .status = .signal, .value = @intFromEnum(x) }, 1013 .stopped => |x| .{ .status = .stopped, .value = @intFromEnum(x) }, 1014 .unknown => |x| .{ .status = .unknown, .value = x }, 1015 }, 1016 }, 1017 else => {}, 1018 } 1019 1020 break :e try wc.addExtraErased(Configuration.Step.Run, .{ 1021 .flags = .{ 1022 .disable_zig_progress = run.disable_zig_progress, 1023 .skip_foreign_checks = run.skip_foreign_checks, 1024 .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error, 1025 .has_side_effects = run.has_side_effects, 1026 .test_runner_mode = run.test_runner_mode, 1027 .color = run.color, 1028 .stdio = switch (run.stdio) { 1029 .infer_from_args => .infer_from_args, 1030 .inherit => .inherit, 1031 .check => .check, 1032 .zig_test => .zig_test, 1033 }, 1034 .stdin = switch (run.stdin) { 1035 .none => .none, 1036 .bytes => .bytes, 1037 .lazy_path => .lazy_path, 1038 }, 1039 .stdout_trim_whitespace = if (run.captured_stdout) |cs| cs.trim_whitespace else .none, 1040 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none, 1041 .stdio_limit = run.stdio_limit != .unlimited, 1042 .producer = run.producer != null, 1043 .cwd = run.cwd != null, 1044 .captured_stdout = run.captured_stdout != null, 1045 .captured_stderr = run.captured_stderr != null, 1046 .environ_map = run.environ_map != null, 1047 }, 1048 .flags2 = .{ 1049 .expect_stderr_exact = expect_stderr_exact != null, 1050 .expect_stdout_exact = expect_stdout_exact != null, 1051 .expect_stderr_match = expect_stderr_match.items.len != 0, 1052 .expect_stdout_match = expect_stdout_match.items.len != 0, 1053 .expect_term = expect_term != null, 1054 .expect_term_status = if (expect_term) |t| t.status else .exited, 1055 }, 1056 .file_inputs = .{ .slice = try s.initLazyPathList(run.file_inputs.items) }, 1057 .args = .{ .slice = try s.initArgsList(run.argv.items) }, 1058 .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) }, 1059 .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{ 1060 .basename = try wc.addString(cs.output.basename), 1061 .generated_file = cs.output.generated_file, 1062 } else null }, 1063 .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{ 1064 .basename = try wc.addString(cs.output.basename), 1065 .generated_file = cs.output.generated_file, 1066 } else null }, 1067 .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) }, 1068 .expect_term_value = .{ .value = if (expect_term) |t| t.value else null }, 1069 .stdio_limit = .{ .value = run.stdio_limit.toInt() }, 1070 .producer = .{ .value = if (run.producer) |cs| s.stepIndex(&cs.step) else null }, 1071 .expect_stderr_exact = .{ .value = if (expect_stderr_exact) |bytes| bytes else null }, 1072 .expect_stdout_exact = .{ .value = if (expect_stdout_exact) |bytes| bytes else null }, 1073 .expect_stderr_match = .{ .slice = expect_stderr_match.items }, 1074 .expect_stdout_match = .{ .slice = expect_stdout_match.items }, 1075 .stdin = .{ .u = switch (run.stdin) { 1076 .none => .none, 1077 .bytes => |bytes| .{ .bytes = try wc.addBytes(bytes) }, 1078 .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) }, 1079 } }, 1080 }); 1081 }, 1082 .check_file => e: { 1083 const cf: *Step.CheckFile = @fieldParentPtr("step", step); 1084 break :e try wc.addExtraErased(Configuration.Step.CheckFile, .{ 1085 .flags = .{ 1086 .expected_exact = cf.expected_exact != null, 1087 .expected_matches = cf.expected_matches.len != 0, 1088 .max_bytes = cf.max_bytes != null, 1089 }, 1090 .file = try s.addLazyPath(cf.file), 1091 .expected_exact = .{ .value = cf.expected_exact }, 1092 .expected_matches = .{ .slice = cf.expected_matches }, 1093 .max_bytes = .{ .value = cf.max_bytes }, 1094 }); 1095 }, 1096 .config_header => e: { 1097 const ch: *Step.ConfigHeader = @fieldParentPtr("step", step); 1098 const lazy_path: ?std.Build.LazyPath = ch.style.getPath(); 1099 const pairs = try arena.alloc(Configuration.Step.ConfigHeader.Value.Pair, ch.values.count()); 1100 for (pairs, ch.values.keys(), ch.values.values()) |*pair, key, value| pair.* = .{ 1101 .key = try wc.addString(key), 1102 .index = switch (value) { 1103 .undef => .undef, 1104 .defined => .defined, 1105 .boolean => |x| switch (x) { 1106 false => .bool_false, 1107 true => .bool_true, 1108 }, 1109 .int => |x| switch (x) { 1110 0 => .int_0, 1111 1 => .int_1, 1112 else => try wc.addExtra(Configuration.Step.ConfigHeader.Value, .initSigned(x)), 1113 }, 1114 .ident => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{ 1115 .flags = .{ 1116 .tag = .ident, 1117 .small = 0, 1118 }, 1119 .i64 = .{ .value = null }, 1120 .u64 = .{ .value = null }, 1121 .ident = .{ .value = try wc.addString(x) }, 1122 .string = .{ .value = null }, 1123 }), 1124 .string => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{ 1125 .flags = .{ 1126 .tag = .string, 1127 .small = 0, 1128 }, 1129 .i64 = .{ .value = null }, 1130 .u64 = .{ .value = null }, 1131 .ident = .{ .value = null }, 1132 .string = .{ .value = try wc.addString(x) }, 1133 }), 1134 }, 1135 }; 1136 break :e try wc.addExtraErased(Configuration.Step.ConfigHeader, .{ 1137 .flags = .{ 1138 .template_file = lazy_path != null, 1139 .style = .init(ch.style), 1140 .input_size_limit = ch.input_size_limit != null, 1141 .include_guard = ch.include_guard != .none, 1142 }, 1143 .template_file = .{ .value = try s.addOptionalLazyPath(lazy_path) }, 1144 .generated_dir = ch.generated_dir, 1145 .input_size_limit = .{ .value = ch.input_size_limit }, 1146 .include_path = try wc.addString(ch.include_path), 1147 .include_guard = .{ .value = ch.include_guard.unwrap() }, 1148 .values = .{ .slice = pairs }, 1149 }); 1150 }, 1151 .obj_copy => e: { 1152 const oc: *Step.ObjCopy = @fieldParentPtr("step", step); 1153 1154 const debug_basename: ?Configuration.String = if (oc.debug_file) |df| 1155 df.basename.unwrap() 1156 else 1157 null; 1158 1159 const debug_file: ?Configuration.GeneratedFileIndex = if (oc.debug_file) |df| 1160 df.output_file 1161 else 1162 null; 1163 1164 const add_sections = try arena.alloc( 1165 Configuration.Step.ObjCopy.AddSection, 1166 oc.add_sections.items.len, 1167 ); 1168 for (add_sections, oc.add_sections.items) |*dest, src| dest.* = .{ 1169 .section_name = src.section_name, 1170 .file_path = try s.addLazyPath(src.file_path), 1171 }; 1172 1173 break :e try wc.addExtraErased(Configuration.Step.ObjCopy, .{ 1174 .flags = .{ 1175 .basename = oc.basename != .none, 1176 .debug_file = debug_file != null, 1177 .debug_basename = debug_basename != null, 1178 .format = .init(oc.format), 1179 .strip = oc.strip, 1180 .compress_debug = oc.compress_debug, 1181 .only_section = oc.only_section != .none, 1182 .pad_to = oc.pad_to != null, 1183 .add_section = add_sections.len != 0, 1184 .update_section = oc.update_sections.items.len != 0, 1185 }, 1186 .input_file = try s.addLazyPath(oc.input_file), 1187 .output_file = oc.output_file, 1188 .basename = .{ .value = oc.basename.unwrap() }, 1189 .debug_file = .{ .value = debug_file }, 1190 .debug_basename = .{ .value = debug_basename }, 1191 .only_section = .{ .value = oc.only_section.unwrap() }, 1192 .pad_to = .{ .value = oc.pad_to }, 1193 .add_section = .{ .slice = add_sections }, 1194 .update_section = .{ .slice = oc.update_sections.items }, 1195 }); 1196 }, 1197 .options => e: { 1198 const so: *Step.Options = @fieldParentPtr("step", step); 1199 1200 const args = try arena.alloc(Configuration.Step.Options.Arg, so.args.items.len); 1201 for (args, so.args.items) |*dest, src| dest.* = .{ 1202 .name = src.name, 1203 .path = try s.addLazyPath(src.path), 1204 }; 1205 1206 break :e try wc.addExtraErased(Configuration.Step.Options, .{ 1207 .flags = .{ 1208 .args = so.args.items.len != 0, 1209 }, 1210 .generated_file = so.generated_file, 1211 .contents = try wc.addBytes(so.contents.items), 1212 .args = .{ .slice = args }, 1213 }); 1214 }, 1215 }), 1216 }); 1217 } 1218 } 1219 1220 try wc.unlazy_deps.ensureUnusedCapacity(gpa, graph.needed_lazy_dependencies.keys().len); 1221 for (graph.needed_lazy_dependencies.keys()) |k| { 1222 wc.unlazy_deps.appendAssumeCapacity(try wc.addString(k)); 1223 } 1224 1225 try wc.write(writer, .{ 1226 .default_step = s.stepIndex(b.default_step), 1227 .generated_files_len = @intCast(graph.generated_files.items.len), 1228 .poisoned = switch (graph.cache_poison) { 1229 .pure, .disallowed, .ignored => false, 1230 .poisoned => true, 1231 }, 1232 }); 1233 } 1234 1235 fn addOptionalResolvedTarget( 1236 wc: *Configuration.Wip, 1237 optional_resolved_target: ?std.Build.ResolvedTarget, 1238 ) !Configuration.ResolvedTarget.OptionalIndex { 1239 const resolved_target = optional_resolved_target orelse return .none; 1240 return .init(try wc.addDeduped(Configuration.ResolvedTarget, .{ 1241 .query = try wc.addTargetQuery(&resolved_target.query), 1242 .result = try wc.addTarget(resolved_target.result), 1243 })); 1244 } 1245 1246 fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir { 1247 switch (install_dir orelse return .none) { 1248 .prefix => return .prefix, 1249 .lib => return .lib, 1250 .bin => return .bin, 1251 .header => return .header, 1252 .custom => |sub_path| return .initCustom(try wc.addString(sub_path)), 1253 } 1254 } 1255 1256 fn addInstallDirDefaultNull(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !?Configuration.InstallDestDir { 1257 return try addInstallDir(wc, install_dir orelse return null); 1258 } 1259 1260 /// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which 1261 /// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`. 1262 fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void { 1263 const root_module = if (step.cast(Step.Compile)) |cs| root: { 1264 break :root cs.root_module; 1265 } else return; // not a compile step so no module dependencies 1266 1267 // Starting from `root_module`, discover all modules in this graph. 1268 const modules = root_module.getGraph().modules; 1269 1270 // For each of those modules, set up the implied step dependencies. 1271 for (modules) |mod| { 1272 if (mod.root_source_file) |lp| lp.addStepDependencies(step); 1273 for (mod.include_dirs.items) |include_dir| switch (include_dir) { 1274 .path, 1275 .path_system, 1276 .path_after, 1277 .framework_path, 1278 .framework_path_system, 1279 .embed_path, 1280 => |lp| lp.addStepDependencies(step), 1281 1282 .other_step => |other| { 1283 other.getEmittedIncludeTree().addStepDependencies(step); 1284 step.dependOn(&other.step); 1285 }, 1286 1287 .config_header_step => |other| step.dependOn(&other.step), 1288 }; 1289 for (mod.lib_paths.items) |lp| lp.addStepDependencies(step); 1290 for (mod.rpaths.items) |rpath| switch (rpath) { 1291 .lazy_path => |lp| lp.addStepDependencies(step), 1292 .special => {}, 1293 }; 1294 for (mod.link_objects.items) |link_object| switch (link_object) { 1295 .static_path, 1296 .assembly_file, 1297 => |lp| lp.addStepDependencies(step), 1298 .other_step => |other| step.dependOn(&other.step), 1299 .system_lib => {}, 1300 .c_source_file => |source| source.file.addStepDependencies(step), 1301 .c_source_files => |source_files| source_files.root.addStepDependencies(step), 1302 .win32_resource_file => |rc_source| { 1303 rc_source.file.addStepDependencies(step); 1304 for (rc_source.include_paths) |lp| lp.addStepDependencies(step); 1305 }, 1306 }; 1307 } 1308 } 1309 1310 fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { 1311 if (idx.* >= args.len) return null; 1312 defer idx.* += 1; 1313 return args[idx.*]; 1314 } 1315 1316 fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 { 1317 const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first}); 1318 if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg }); 1319 const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first}); 1320 return arg; 1321 } 1322 1323 const ErrorStyle = enum { 1324 verbose, 1325 minimal, 1326 verbose_clear, 1327 minimal_clear, 1328 fn verboseContext(s: ErrorStyle) bool { 1329 return switch (s) { 1330 .verbose, .verbose_clear => true, 1331 .minimal, .minimal_clear => false, 1332 }; 1333 } 1334 fn clearOnUpdate(s: ErrorStyle) bool { 1335 return switch (s) { 1336 .verbose, .minimal => false, 1337 .verbose_clear, .minimal_clear => true, 1338 }; 1339 } 1340 }; 1341 const MultilineErrors = enum { indent, newline, none }; 1342 const Summary = enum { all, new, failures, line, none }; 1343 1344 fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { 1345 log.info("to access the help menu: zig build -h", .{}); 1346 fatal(f, args); 1347 } 1348 1349 fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void { 1350 const gpa = wc.gpa; 1351 1352 var bad = false; 1353 try wc.system_integrations.ensureTotalCapacityPrecise(gpa, graph.system_integration_options.entries.len); 1354 for (graph.system_integration_options.keys(), graph.system_integration_options.values()) |k, v| { 1355 wc.system_integrations.appendAssumeCapacity(.{ 1356 .name = try wc.addString(k), 1357 .status = switch (v) { 1358 .user_disabled, .user_enabled => x: { 1359 // The user tried to enable or disable a system library integration, but 1360 // the configure script did not recognize that option. 1361 log.err("system integration name not recognized by configure script: {s}", .{k}); 1362 bad = true; 1363 break :x .disabled; 1364 }, 1365 .declared_disabled => .disabled, 1366 .declared_enabled => .enabled, 1367 }, 1368 }); 1369 } 1370 if (bad) { 1371 log.info("help menu contains available options: zig build -h", .{}); 1372 process.exit(1); 1373 } 1374 } 1375 1376 fn serializePackageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void { 1377 const gpa = wc.gpa; 1378 1379 try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count()); 1380 for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| { 1381 wc.available_options.appendAssumeCapacity(.{ 1382 .name = try wc.addString(name), 1383 .description = try wc.addString(opt.description), 1384 .type = opt.type_id, 1385 .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none, 1386 }); 1387 } 1388 }