authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-24 13:57:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:36-07:00
logf79ae3214c41183c90ca4d7acb0ac0ded3d99f61
tree8a0bf37f31f1ede135bec7a3801e154d85f14923
parentbc031bedaa470d14348a6be1da09a8ba5fd31c5d

zig build: close open file handles before spawning child

ever so slightly reduces rlimit pressure in the child. plus it avoids accounting problems in libc-test

1 files changed, 505 insertions(+), 503 deletions(-)

src/main.zig+505-503
......@@ -5202,562 +5202,564 @@ fn cmdBuild(
52025202 .build_file = build_file,
52035203 });
52045204
5205 // This `init` calls `fatal` on error.
5206 var dirs: Compilation.Directories = .init(
5207 arena,
5208 io,
5209 override_lib_dir,
5210 override_global_cache_dir,
5211 .{ .override = path: {
5212 if (override_local_cache_dir) |d| break :path d;
5213 break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5214 } },
5215 .empty,
5216 self_exe_path,
5217 environ_map,
5218 cwd_path,
5219 );
5220 defer dirs.deinit(io);
5221
5222 const thread_limit = @min(
5223 @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
5224 std.math.maxInt(Zcu.PerThread.IdBacking),
5225 );
5226 try setThreadLimit(arena, thread_limit);
5205 {
5206 // This `init` calls `fatal` on error.
5207 var dirs: Compilation.Directories = .init(
5208 arena,
5209 io,
5210 override_lib_dir,
5211 override_global_cache_dir,
5212 .{ .override = path: {
5213 if (override_local_cache_dir) |d| break :path d;
5214 break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5215 } },
5216 .empty,
5217 self_exe_path,
5218 environ_map,
5219 cwd_path,
5220 );
5221 defer dirs.deinit(io);
52275222
5228 // Cache lookup for configure options. If we get a match, we can skip
5229 // execution of the configure script. If not, we get the file path to pass
5230 // to the configure process.
5231 var local_cache: Cache = .{
5232 .gpa = gpa,
5233 .io = io,
5234 .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}),
5235 .cwd = cwd_path,
5236 };
5237 local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
5238 local_cache.addPrefix(dirs.zig_lib);
5239 local_cache.addPrefix(dirs.local_cache);
5240 local_cache.addPrefix(dirs.global_cache);
5241 defer local_cache.manifest_dir.close(io);
5242
5243 var config_man = local_cache.obtain();
5244 defer config_man.deinit();
5245 config_man.hash.addBytes(build_options.version);
5246
5247 for (cached_passthru_configure.items) |i|
5248 config_man.hash.addBytes(configure_argv.items[i]);
5249
5250 // Prevents a `zig build` from getting a false positive cache hit following
5251 // a `zig build --cache-poison=ignored`.
5252 config_man.hash.add(cache_poison == .ignored);
5253
5254 // Normally the build runner is compiled for the host target but here is
5255 // some code to help when debugging edits to the build runner so that you
5256 // can make sure it compiles successfully on other targets.
5257 const resolved_target: Package.Module.ResolvedTarget = t: {
5258 if (build_options.enable_debug_extensions) {
5259 if (debug_target) |triple| {
5260 const target_query = try std.Target.Query.parse(.{
5261 .arch_os_abi = triple,
5262 });
5263 config_man.hash.addBytes(triple);
5264 break :t .{
5265 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5266 .is_native_os = false,
5267 .is_native_abi = false,
5268 .is_explicit_dynamic_linker = false,
5269 };
5223 const thread_limit = @min(
5224 @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
5225 std.math.maxInt(Zcu.PerThread.IdBacking),
5226 );
5227 try setThreadLimit(arena, thread_limit);
5228
5229 // Cache lookup for configure options. If we get a match, we can skip
5230 // execution of the configure script. If not, we get the file path to pass
5231 // to the configure process.
5232 var local_cache: Cache = .{
5233 .gpa = gpa,
5234 .io = io,
5235 .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}),
5236 .cwd = cwd_path,
5237 };
5238 local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
5239 local_cache.addPrefix(dirs.zig_lib);
5240 local_cache.addPrefix(dirs.local_cache);
5241 local_cache.addPrefix(dirs.global_cache);
5242 defer local_cache.manifest_dir.close(io);
5243
5244 var config_man = local_cache.obtain();
5245 defer config_man.deinit();
5246 config_man.hash.addBytes(build_options.version);
5247
5248 for (cached_passthru_configure.items) |i|
5249 config_man.hash.addBytes(configure_argv.items[i]);
5250
5251 // Prevents a `zig build` from getting a false positive cache hit following
5252 // a `zig build --cache-poison=ignored`.
5253 config_man.hash.add(cache_poison == .ignored);
5254
5255 // Normally the build runner is compiled for the host target but here is
5256 // some code to help when debugging edits to the build runner so that you
5257 // can make sure it compiles successfully on other targets.
5258 const resolved_target: Package.Module.ResolvedTarget = t: {
5259 if (build_options.enable_debug_extensions) {
5260 if (debug_target) |triple| {
5261 const target_query = try std.Target.Query.parse(.{
5262 .arch_os_abi = triple,
5263 });
5264 config_man.hash.addBytes(triple);
5265 break :t .{
5266 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5267 .is_native_os = false,
5268 .is_native_abi = false,
5269 .is_explicit_dynamic_linker = false,
5270 };
5271 }
52705272 }
5271 }
5272 break :t .{
5273 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
5274 .is_native_os = true,
5275 .is_native_abi = true,
5276 .is_explicit_dynamic_linker = false,
5273 break :t .{
5274 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
5275 .is_native_os = true,
5276 .is_native_abi = true,
5277 .is_explicit_dynamic_linker = false,
5278 };
52775279 };
5278 };
52795280
5280 // Likewise, `--debug-libc` allows overriding the libc installation.
5281 const libc_installation: ?*const LibCInstallation = lci: {
5282 const paths_file = debug_libc_paths_file orelse break :lci null;
5283 if (!build_options.enable_debug_extensions) unreachable;
5284 const lci = try arena.create(LibCInstallation);
5285 lci.* = try .parse(arena, io, paths_file, &resolved_target.result);
5286 LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi);
5287 break :lci lci;
5288 };
5289
5290 // Kick off an optimized compilation of the make runner.
5291 var make_runner_task = io.async(compileMakeRunner, .{ gpa, arena, io, .{
5292 .dirs = .{
5293 .cwd = dirs.cwd,
5294 .zig_lib = dirs.zig_lib,
5295 .global_cache = dirs.global_cache,
5296 .local_cache = dirs.global_cache,
5297 },
5298 .environ_map = environ_map,
5299 .parent_prog_node = root_prog_node,
5300 .resolved_target = resolved_target,
5301 .libc_installation = libc_installation,
5302 .thread_limit = thread_limit,
5303 .self_exe_path = self_exe_path,
5304 .color = color,
5305 .reference_trace = reference_trace,
5306 .optimize_mode = maker_optimize_mode,
5307 } });
5308 defer _ = make_runner_task.cancel(io) catch {};
5309
5310 const pkg_root: Path = if (override_pkg_dir) |p|
5311 .initCwd(p)
5312 else if (system_pkg_dir_path) |p|
5313 .initCwd(p)
5314 else
5315 .{
5316 .root_dir = build_root.directory,
5317 .sub_path = "zig-pkg",
5281 // Likewise, `--debug-libc` allows overriding the libc installation.
5282 const libc_installation: ?*const LibCInstallation = lci: {
5283 const paths_file = debug_libc_paths_file orelse break :lci null;
5284 if (!build_options.enable_debug_extensions) unreachable;
5285 const lci = try arena.create(LibCInstallation);
5286 lci.* = try .parse(arena, io, paths_file, &resolved_target.result);
5287 LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi);
5288 break :lci lci;
53185289 };
53195290
5320 make_argv.items[make_argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5321 make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path;
5322 make_argv.items[make_argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5323 make_argv.items[make_argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
5291 // Kick off an optimized compilation of the make runner.
5292 var make_runner_task = io.async(compileMakeRunner, .{ gpa, arena, io, .{
5293 .dirs = .{
5294 .cwd = dirs.cwd,
5295 .zig_lib = dirs.zig_lib,
5296 .global_cache = dirs.global_cache,
5297 .local_cache = dirs.global_cache,
5298 },
5299 .environ_map = environ_map,
5300 .parent_prog_node = root_prog_node,
5301 .resolved_target = resolved_target,
5302 .libc_installation = libc_installation,
5303 .thread_limit = thread_limit,
5304 .self_exe_path = self_exe_path,
5305 .color = color,
5306 .reference_trace = reference_trace,
5307 .optimize_mode = maker_optimize_mode,
5308 } });
5309 defer _ = make_runner_task.cancel(io) catch {};
5310
5311 const pkg_root: Path = if (override_pkg_dir) |p|
5312 .initCwd(p)
5313 else if (system_pkg_dir_path) |p|
5314 .initCwd(p)
5315 else
5316 .{
5317 .root_dir = build_root.directory,
5318 .sub_path = "zig-pkg",
5319 };
53245320
5325 configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path;
5321 make_argv.items[make_argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5322 make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path;
5323 make_argv.items[make_argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5324 make_argv.items[make_argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
53265325
5327 // Dummy http client that is not actually used when fetch_command is unsupported.
5328 // Prevents bootstrap from depending on a bunch of unnecessary stuff.
5329 var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct {
5330 allocator: Allocator,
5331 io: Io,
5332 fn deinit(_: @This()) void {}
5333 } = .{ .allocator = gpa, .io = io };
5334 defer http_client.deinit();
5326 configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path;
53355327
5336 var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{};
5337 var fork_set: Package.Fetch.JobQueue.ForkSet = .{};
5328 // Dummy http client that is not actually used when fetch_command is unsupported.
5329 // Prevents bootstrap from depending on a bunch of unnecessary stuff.
5330 var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct {
5331 allocator: Allocator,
5332 io: Io,
5333 fn deinit(_: @This()) void {}
5334 } = .{ .allocator = gpa, .io = io };
5335 defer http_client.deinit();
53385336
5339 {
5340 // Populate fork_set.
5341 var group: Io.Group = .init;
5342 defer group.cancel(io);
5343
5344 for (forks.items) |*fork|
5345 group.async(io, Fork.load, .{ io, gpa, fork, color });
5346
5347 try group.await(io);
5348
5349 for (forks.items) |*fork| {
5350 if (fork.failed) process.exit(1);
5351 try fork_set.put(arena, .{
5352 .path = fork.path,
5353 .manifest_ast = fork.manifest_ast,
5354 .manifest = fork.manifest,
5355 .uses = 0,
5356 }, {});
5357 }
5358 }
5359 defer Fork.deinitList(forks.items);
5337 var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{};
5338 var fork_set: Package.Fetch.JobQueue.ForkSet = .{};
53605339
5361 // This loop is re-evaluated when the build script exits with an indication that it
5362 // could not continue due to missing lazy dependencies.
5363 const configuration_path: Path, const poisoned: bool = cp: while (true) {
5364 // We want to release all the locks before executing the child process, so we make a nice
5365 // big block here to ensure the cleanup gets run when we extract out our argv.
53665340 {
5367 const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_make_runner) |runner| .{
5368 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
5369 .root_src_path = fs.path.basename(runner),
5370 } else .{
5371 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5372 .root_src_path = "configurer.zig",
5373 };
5341 // Populate fork_set.
5342 var group: Io.Group = .init;
5343 defer group.cancel(io);
5344
5345 for (forks.items) |*fork|
5346 group.async(io, Fork.load, .{ io, gpa, fork, color });
5347
5348 try group.await(io);
5349
5350 for (forks.items) |*fork| {
5351 if (fork.failed) process.exit(1);
5352 try fork_set.put(arena, .{
5353 .path = fork.path,
5354 .manifest_ast = fork.manifest_ast,
5355 .manifest = fork.manifest,
5356 .uses = 0,
5357 }, {});
5358 }
5359 }
5360 defer Fork.deinitList(forks.items);
53745361
5375 const config = try Compilation.Config.resolve(.{
5376 .output_mode = .Exe,
5377 .resolved_target = resolved_target,
5378 .have_zcu = true,
5379 .emit_bin = true,
5380 .is_test = false,
5381 });
5362 // This loop is re-evaluated when the build script exits with an indication that it
5363 // could not continue due to missing lazy dependencies.
5364 const configuration_path: Path, const poisoned: bool = cp: while (true) {
5365 // We want to release all the locks before executing the child process, so we make a nice
5366 // big block here to ensure the cleanup gets run when we extract out our argv.
5367 {
5368 const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_make_runner) |runner| .{
5369 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
5370 .root_src_path = fs.path.basename(runner),
5371 } else .{
5372 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5373 .root_src_path = "configurer.zig",
5374 };
53825375
5383 const root_mod = try Package.Module.create(arena, .{
5384 .paths = main_mod_paths,
5385 .fully_qualified_name = "root",
5386 .cc_argv = &.{},
5387 .inherited = .{
5376 const config = try Compilation.Config.resolve(.{
5377 .output_mode = .Exe,
53885378 .resolved_target = resolved_target,
5389 .single_threaded = true,
5390 },
5391 .global = config,
5392 .parent = null,
5393 });
5379 .have_zcu = true,
5380 .emit_bin = true,
5381 .is_test = false,
5382 });
53945383
5395 const build_mod = try Package.Module.create(arena, .{
5396 .paths = .{
5397 .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}),
5398 .root_src_path = build_root.build_zig_basename,
5399 },
5400 .fully_qualified_name = "root.@build",
5401 .cc_argv = &.{},
5402 .inherited = .{},
5403 .global = config,
5404 .parent = root_mod,
5405 });
5384 const root_mod = try Package.Module.create(arena, .{
5385 .paths = main_mod_paths,
5386 .fully_qualified_name = "root",
5387 .cc_argv = &.{},
5388 .inherited = .{
5389 .resolved_target = resolved_target,
5390 .single_threaded = true,
5391 },
5392 .global = config,
5393 .parent = null,
5394 });
54065395
5407 if (dev.env.supports(.fetch_command)) {
5408 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5409 defer fetch_prog_node.end();
5396 const build_mod = try Package.Module.create(arena, .{
5397 .paths = .{
5398 .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}),
5399 .root_src_path = build_root.build_zig_basename,
5400 },
5401 .fully_qualified_name = "root.@build",
5402 .cc_argv = &.{},
5403 .inherited = .{},
5404 .global = config,
5405 .parent = root_mod,
5406 });
54105407
5411 // Reset fork match counts.
5412 for (fork_set.keys()) |*fork| fork.uses = 0;
5408 if (dev.env.supports(.fetch_command)) {
5409 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5410 defer fetch_prog_node.end();
54135411
5414 var job_queue: Package.Fetch.JobQueue = .{
5415 .io = io,
5416 .http_client = &http_client,
5417 .global_cache = dirs.global_cache,
5418 .local_storage = &.{
5419 .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" },
5420 .pkg_root = pkg_root,
5421 },
5422 .recursive = true,
5423 .debug_hash = false,
5424 .unlazy_set = unlazy_set,
5425 .fork_set = fork_set,
5426 .mode = fetch_mode,
5427 .prog_node = fetch_prog_node,
5428 .read_only = system_pkg_dir_path != null,
5429 };
5430 defer job_queue.deinit();
5412 // Reset fork match counts.
5413 for (fork_set.keys()) |*fork| fork.uses = 0;
54315414
5432 if (system_pkg_dir_path == null) {
5433 try http_client.initDefaultProxies(arena, environ_map);
5434 }
5415 var job_queue: Package.Fetch.JobQueue = .{
5416 .io = io,
5417 .http_client = &http_client,
5418 .global_cache = dirs.global_cache,
5419 .local_storage = &.{
5420 .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" },
5421 .pkg_root = pkg_root,
5422 },
5423 .recursive = true,
5424 .debug_hash = false,
5425 .unlazy_set = unlazy_set,
5426 .fork_set = fork_set,
5427 .mode = fetch_mode,
5428 .prog_node = fetch_prog_node,
5429 .read_only = system_pkg_dir_path != null,
5430 };
5431 defer job_queue.deinit();
54355432
5436 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
5437 try job_queue.table.ensureUnusedCapacity(gpa, 1);
5438
5439 const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory };
5440
5441 var fetch: Package.Fetch = .{
5442 .arena = std.heap.ArenaAllocator.init(gpa),
5443 .location = .{ .relative_path = phantom_package_root },
5444 .location_tok = 0,
5445 .hash_tok = .none,
5446 .name_tok = 0,
5447 .lazy_status = .eager,
5448 .remote_package_root = phantom_package_root,
5449 .parent_package_root = phantom_package_root,
5450 .parent_manifest_ast = null,
5451 .prog_node = fetch_prog_node,
5452 .job_queue = &job_queue,
5453 .omit_missing_hash_error = true,
5454 .allow_missing_paths_field = false,
5455 .use_latest_commit = false,
5456
5457 .package_root = undefined,
5458 .error_bundle = undefined,
5459 .manifest = undefined,
5460 .manifest_ast = undefined,
5461 .have_manifest = false,
5462 .computed_hash = undefined,
5463 .has_build_zig = true,
5464 .oom_flag = false,
5465 .latest_commit = null,
5466
5467 .module = build_mod,
5468 };
5433 if (system_pkg_dir_path == null) {
5434 try http_client.initDefaultProxies(arena, environ_map);
5435 }
54695436
5470 job_queue.all_fetches.appendAssumeCapacity(&fetch);
5437 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
5438 try job_queue.table.ensureUnusedCapacity(gpa, 1);
5439
5440 const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory };
5441
5442 var fetch: Package.Fetch = .{
5443 .arena = std.heap.ArenaAllocator.init(gpa),
5444 .location = .{ .relative_path = phantom_package_root },
5445 .location_tok = 0,
5446 .hash_tok = .none,
5447 .name_tok = 0,
5448 .lazy_status = .eager,
5449 .remote_package_root = phantom_package_root,
5450 .parent_package_root = phantom_package_root,
5451 .parent_manifest_ast = null,
5452 .prog_node = fetch_prog_node,
5453 .job_queue = &job_queue,
5454 .omit_missing_hash_error = true,
5455 .allow_missing_paths_field = false,
5456 .use_latest_commit = false,
5457
5458 .package_root = undefined,
5459 .error_bundle = undefined,
5460 .manifest = undefined,
5461 .manifest_ast = undefined,
5462 .have_manifest = false,
5463 .computed_hash = undefined,
5464 .has_build_zig = true,
5465 .oom_flag = false,
5466 .latest_commit = null,
5467
5468 .module = build_mod,
5469 };
54715470
5472 job_queue.table.putAssumeCapacityNoClobber(
5473 Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache),
5474 &fetch,
5475 );
5471 job_queue.all_fetches.appendAssumeCapacity(&fetch);
54765472
5477 job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" });
5478 try job_queue.group.await(io);
5473 job_queue.table.putAssumeCapacityNoClobber(
5474 Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache),
5475 &fetch,
5476 );
54795477
5480 {
5481 // Ensure that forks were actually used. This is done
5482 // before printing manifest errors because using a fork can
5483 // prevent them.
5484 var any_unused = false;
5485 for (fork_set.keys()) |*fork| {
5486 if (fork.uses == 0) {
5487 std.log.err("fork {f} matched no {s} packages", .{
5488 fork.path, fork.manifest.name,
5489 });
5490 any_unused = true;
5491 } else {
5492 std.log.info("fork {f} matched {d} {s} packages", .{
5493 fork.path, fork.uses, fork.manifest.name,
5494 });
5478 job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" });
5479 try job_queue.group.await(io);
5480
5481 {
5482 // Ensure that forks were actually used. This is done
5483 // before printing manifest errors because using a fork can
5484 // prevent them.
5485 var any_unused = false;
5486 for (fork_set.keys()) |*fork| {
5487 if (fork.uses == 0) {
5488 std.log.err("fork {f} matched no {s} packages", .{
5489 fork.path, fork.manifest.name,
5490 });
5491 any_unused = true;
5492 } else {
5493 std.log.info("fork {f} matched {d} {s} packages", .{
5494 fork.path, fork.uses, fork.manifest.name,
5495 });
5496 }
54955497 }
5498 if (any_unused) process.exit(1);
54965499 }
5497 if (any_unused) process.exit(1);
5498 }
54995500
5500 try job_queue.consolidateErrors();
5501 try job_queue.consolidateErrors();
55015502
5502 if (fetch.error_bundle.root_list.items.len > 0) {
5503 var errors = try fetch.error_bundle.toOwnedBundle("");
5504 errors.renderToStderr(io, .{}, color) catch {};
5505 process.exit(1);
5506 }
5503 if (fetch.error_bundle.root_list.items.len > 0) {
5504 var errors = try fetch.error_bundle.toOwnedBundle("");
5505 errors.renderToStderr(io, .{}, color) catch {};
5506 process.exit(1);
5507 }
55075508
5508 if (fetch_only) return cleanExit(io);
5509 if (fetch_only) return cleanExit(io);
5510
5511 var source_buf = std.array_list.Managed(u8).init(gpa);
5512 defer source_buf.deinit();
5513 try job_queue.createDependenciesSource(&source_buf);
5514 const deps_mod = try createDependenciesModule(
5515 arena,
5516 io,
5517 source_buf.items,
5518 root_mod,
5519 dirs,
5520 config,
5521 );
5522
5523 {
5524 // We need a Module for each package's build.zig.
5525 const hashes = job_queue.table.keys();
5526 const fetches = job_queue.table.values();
5527 try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len));
5528 for (hashes, fetches) |*hash, f| {
5529 if (f == &fetch) {
5530 // The first one is a dummy package for the current project.
5531 continue;
5532 }
5533 if (!f.has_build_zig)
5534 continue;
5535 const hash_slice = hash.toSlice();
5536 const mod_root_path = try f.package_root.toString(arena);
5537 const m = try Package.Module.create(arena, .{
5538 .paths = .{
5539 .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}),
5540 .root_src_path = Package.build_zig_basename,
5541 },
5542 .fully_qualified_name = try std.fmt.allocPrint(
5543 arena,
5544 "root.@dependencies.{s}",
5545 .{hash_slice},
5546 ),
5547 .cc_argv = &.{},
5548 .inherited = .{},
5549 .global = config,
5550 .parent = root_mod,
5551 });
5552 const hash_cloned = try arena.dupe(u8, hash_slice);
5553 deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m);
5554 f.module = m;
5555 }
55095556
5510 var source_buf = std.array_list.Managed(u8).init(gpa);
5511 defer source_buf.deinit();
5512 try job_queue.createDependenciesSource(&source_buf);
5513 const deps_mod = try createDependenciesModule(
5557 // Each build.zig module needs access to each of its
5558 // dependencies' build.zig modules by name.
5559 for (fetches) |f| {
5560 const mod = f.module orelse continue;
5561 if (!f.have_manifest) continue;
5562 const man = &f.manifest;
5563 const dep_names = man.dependencies.keys();
5564 try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));
5565 for (dep_names, man.dependencies.values()) |name, dep| {
5566 const dep_digest = Package.Fetch.depDigest(
5567 f.package_root,
5568 dirs.global_cache,
5569 dep,
5570 ) orelse continue;
5571 const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue;
5572 const name_cloned = try arena.dupe(u8, name);
5573 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
5574 }
5575 }
5576 }
5577 } else try createEmptyDependenciesModule(
55145578 arena,
55155579 io,
5516 source_buf.items,
55175580 root_mod,
55185581 dirs,
55195582 config,
55205583 );
55215584
5522 {
5523 // We need a Module for each package's build.zig.
5524 const hashes = job_queue.table.keys();
5525 const fetches = job_queue.table.values();
5526 try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len));
5527 for (hashes, fetches) |*hash, f| {
5528 if (f == &fetch) {
5529 // The first one is a dummy package for the current project.
5530 continue;
5531 }
5532 if (!f.has_build_zig)
5533 continue;
5534 const hash_slice = hash.toSlice();
5535 const mod_root_path = try f.package_root.toString(arena);
5536 const m = try Package.Module.create(arena, .{
5537 .paths = .{
5538 .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}),
5539 .root_src_path = Package.build_zig_basename,
5540 },
5541 .fully_qualified_name = try std.fmt.allocPrint(
5542 arena,
5543 "root.@dependencies.{s}",
5544 .{hash_slice},
5545 ),
5546 .cc_argv = &.{},
5547 .inherited = .{},
5548 .global = config,
5549 .parent = root_mod,
5550 });
5551 const hash_cloned = try arena.dupe(u8, hash_slice);
5552 deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m);
5553 f.module = m;
5554 }
5585 const compile_prog_node = root_prog_node.start("Compile Configure Script", 0);
5586 defer compile_prog_node.end();
5587
5588 try root_mod.deps.put(arena, "@build", build_mod);
5589
5590 var create_diag: Compilation.CreateDiagnostic = undefined;
5591 const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5592 .libc_installation = libc_installation,
5593 .dirs = dirs,
5594 .root_name = "configure",
5595 .config = config,
5596 .root_mod = root_mod,
5597 .main_mod = build_mod,
5598 .emit_bin = .yes_cache,
5599 .self_exe_path = self_exe_path,
5600 .thread_limit = thread_limit,
5601 .verbose_cc = verbose_cc,
5602 .verbose_link = verbose_link,
5603 .verbose_air = verbose_air,
5604 .verbose_intern_pool = verbose_intern_pool,
5605 .verbose_generic_instances = verbose_generic_instances,
5606 .verbose_llvm_ir = verbose_llvm_ir,
5607 .verbose_llvm_bc = verbose_llvm_bc,
5608 .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
5609 .cache_mode = .whole,
5610 .reference_trace = reference_trace,
5611 .debug_compile_errors = debug_compile_errors,
5612 .environ_map = environ_map,
5613 }) catch |err| switch (err) {
5614 error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5615 else => |e| fatal("failed to create compilation: {t}", .{e}),
5616 };
5617 defer comp.destroy();
55555618
5556 // Each build.zig module needs access to each of its
5557 // dependencies' build.zig modules by name.
5558 for (fetches) |f| {
5559 const mod = f.module orelse continue;
5560 if (!f.have_manifest) continue;
5561 const man = &f.manifest;
5562 const dep_names = man.dependencies.keys();
5563 try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));
5564 for (dep_names, man.dependencies.values()) |name, dep| {
5565 const dep_digest = Package.Fetch.depDigest(
5566 f.package_root,
5567 dirs.global_cache,
5568 dep,
5569 ) orelse continue;
5570 const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue;
5571 const name_cloned = try arena.dupe(u8, name);
5572 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
5573 }
5574 }
5575 }
5576 } else try createEmptyDependenciesModule(
5577 arena,
5578 io,
5579 root_mod,
5580 dirs,
5581 config,
5582 );
5619 updateModule(comp, color, compile_prog_node) catch |err| switch (err) {
5620 error.CompileErrorsReported => process.exit(2),
5621 else => |e| return e,
5622 };
55835623
5584 const compile_prog_node = root_prog_node.start("Compile Configure Script", 0);
5585 defer compile_prog_node.end();
5586
5587 try root_mod.deps.put(arena, "@build", build_mod);
5588
5589 var create_diag: Compilation.CreateDiagnostic = undefined;
5590 const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5591 .libc_installation = libc_installation,
5592 .dirs = dirs,
5593 .root_name = "configure",
5594 .config = config,
5595 .root_mod = root_mod,
5596 .main_mod = build_mod,
5597 .emit_bin = .yes_cache,
5598 .self_exe_path = self_exe_path,
5599 .thread_limit = thread_limit,
5600 .verbose_cc = verbose_cc,
5601 .verbose_link = verbose_link,
5602 .verbose_air = verbose_air,
5603 .verbose_intern_pool = verbose_intern_pool,
5604 .verbose_generic_instances = verbose_generic_instances,
5605 .verbose_llvm_ir = verbose_llvm_ir,
5606 .verbose_llvm_bc = verbose_llvm_bc,
5607 .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
5608 .cache_mode = .whole,
5609 .reference_trace = reference_trace,
5610 .debug_compile_errors = debug_compile_errors,
5611 .environ_map = environ_map,
5612 }) catch |err| switch (err) {
5613 error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5614 else => |e| fatal("failed to create compilation: {t}", .{e}),
5615 };
5616 defer comp.destroy();
5624 // Since incremental compilation isn't done yet, we use cache_mode = whole
5625 // above, and thus the output file is already closed.
5626 //try comp.makeBinFileExecutable();
5627 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);
5628 const exe_path: Path = .{
5629 .root_dir = dirs.local_cache,
5630 .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),
5631 };
5632 _ = try config_man.addFilePath(exe_path, null);
5633 configure_argv.items[0] = try exe_path.toString(arena);
5634
5635 switch (cache_poison) {
5636 .pure, .disallowed, .ignored => if (try config_man.hit()) {
5637 const digest = config_man.final();
5638 break :cp .{
5639 .{
5640 .root_dir = dirs.local_cache,
5641 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5642 },
5643 false,
5644 };
5645 },
5646 .poisoned => {}, // Don't bother checking for cache hit.
5647 }
5648 }
56175649
5618 updateModule(comp, color, compile_prog_node) catch |err| switch (err) {
5619 error.CompileErrorsReported => process.exit(2),
5620 else => |e| return e,
5621 };
5650 if (!process.can_spawn) {
5651 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5652 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
5653 }
56225654
5623 // Since incremental compilation isn't done yet, we use cache_mode = whole
5624 // above, and thus the output file is already closed.
5625 //try comp.makeBinFileExecutable();
5626 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);
5627 const exe_path: Path = .{
5655 const rand_int = randInt(io, u64);
5656 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
5657 const config_tmp_path: Path = .{
56285658 .root_dir = dirs.local_cache,
5629 .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),
5659 .sub_path = tmp_dir_sub_path,
56305660 };
5631 _ = try config_man.addFilePath(exe_path, null);
5632 configure_argv.items[0] = try exe_path.toString(arena);
5633
5634 switch (cache_poison) {
5635 .pure, .disallowed, .ignored => if (try config_man.hit()) {
5636 const digest = config_man.final();
5637 break :cp .{
5638 .{
5639 .root_dir = dirs.local_cache,
5640 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5641 },
5642 false,
5643 };
5644 },
5645 .poisoned => {}, // Don't bother checking for cache hit.
5661 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
5662 io,
5663 config_tmp_path.sub_path,
5664 .{ .read = true, .exclusive = true },
5665 );
5666 defer config_tmp_file.close(io);
5667
5668 const term = term: {
5669 const child_node = root_prog_node.start("Run Configure Script", 0);
5670 defer child_node.end();
5671 var child = std.process.spawn(io, .{
5672 .argv = configure_argv.items,
5673 .stdout = .{ .file = config_tmp_file },
5674 .progress_node = child_node,
5675 }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err });
5676 defer child.kill(io);
5677 break :term child.wait(io) catch |err|
5678 fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err });
5679 };
5680 if (!term.success()) {
5681 // Failure to produce the configuration file.
5682 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5683 fatal("the following configure command {f}:\n{s}", .{ term, cmd });
56465684 }
5647 }
5648
5649 if (!process.can_spawn) {
5650 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5651 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
5652 }
5653
5654 const rand_int = randInt(io, u64);
5655 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
5656 const config_tmp_path: Path = .{
5657 .root_dir = dirs.local_cache,
5658 .sub_path = tmp_dir_sub_path,
5659 };
5660 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
5661 io,
5662 config_tmp_path.sub_path,
5663 .{ .read = true, .exclusive = true },
5664 );
5665 defer config_tmp_file.close(io);
5666
5667 const term = term: {
5668 const child_node = root_prog_node.start("Run Configure Script", 0);
5669 defer child_node.end();
5670 var child = std.process.spawn(io, .{
5671 .argv = configure_argv.items,
5672 .stdout = .{ .file = config_tmp_file },
5673 .progress_node = child_node,
5674 }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err });
5675 defer child.kill(io);
5676 break :term child.wait(io) catch |err|
5677 fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err });
5678 };
5679 if (!term.success()) {
5680 // Failure to produce the configuration file.
5681 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5682 fatal("the following configure command {f}:\n{s}", .{ term, cmd });
5683 }
5684 // Even though the file is designed to be sent directly to make
5685 // runner, we must load it now because:
5686 // * If it contains additional file dependencies, we need to
5687 // add them to `config_man` before obtaining the final digest.
5688 // * If it contains a set of lazy packages that need to be
5689 // fetched, we need to fetch those now and re-run configure.
5690 var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err|
5691 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
5692
5693 if (configuration.unlazy_deps.len != 0) {
5694 if (!dev.env.supports(.fetch_command)) process.exit(1);
5695 var any_errors = false;
5696 for (configuration.unlazy_deps) |hash_string| {
5697 const hash = hash_string.slice(&configuration);
5698 assert(hash.len != 0);
5699 if (hash.len > Package.Hash.max_len) {
5700 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ hash.len, hash });
5701 any_errors = true;
5702 continue;
5685 // Even though the file is designed to be sent directly to make
5686 // runner, we must load it now because:
5687 // * If it contains additional file dependencies, we need to
5688 // add them to `config_man` before obtaining the final digest.
5689 // * If it contains a set of lazy packages that need to be
5690 // fetched, we need to fetch those now and re-run configure.
5691 var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err|
5692 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
5693
5694 if (configuration.unlazy_deps.len != 0) {
5695 if (!dev.env.supports(.fetch_command)) process.exit(1);
5696 var any_errors = false;
5697 for (configuration.unlazy_deps) |hash_string| {
5698 const hash = hash_string.slice(&configuration);
5699 assert(hash.len != 0);
5700 if (hash.len > Package.Hash.max_len) {
5701 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ hash.len, hash });
5702 any_errors = true;
5703 continue;
5704 }
5705 try unlazy_set.put(arena, .fromSlice(hash), {});
57035706 }
5704 try unlazy_set.put(arena, .fromSlice(hash), {});
5705 }
5706 if (any_errors) process.exit(1);
5707 if (system_pkg_dir_path) |p| {
5708 // In this mode, the system needs to provide these packages; they
5709 // cannot be fetched by Zig.
5710 const s = fs.path.sep_str;
5711 for (unlazy_set.keys()) |*hash| {
5712 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() });
5707 if (any_errors) process.exit(1);
5708 if (system_pkg_dir_path) |p| {
5709 // In this mode, the system needs to provide these packages; they
5710 // cannot be fetched by Zig.
5711 const s = fs.path.sep_str;
5712 for (unlazy_set.keys()) |*hash| {
5713 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() });
5714 }
5715 std.log.info("remote package fetching disabled due to --system mode", .{});
5716 std.log.info("dependencies might be avoidable depending on build configuration", .{});
5717 process.exit(1);
57135718 }
5714 std.log.info("remote package fetching disabled due to --system mode", .{});
5715 std.log.info("dependencies might be avoidable depending on build configuration", .{});
5716 process.exit(1);
5719 continue :cp;
57175720 }
5718 continue :cp;
5719 }
57205721
5721 for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| {
5722 const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub };
5723 try config_man.addPathPost(conf_path.toCachePath(&configuration, arena));
5724 }
5722 for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| {
5723 const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub };
5724 try config_man.addPathPost(conf_path.toCachePath(&configuration, arena));
5725 }
57255726
5726 // If it is poisoned, there is no point in moving it to cached
5727 // location. Just leave it in the tmp directory.
5728 if (configuration.poisoned) {
5729 break :cp .{ config_tmp_path, true };
5730 } else {
5731 const digest = config_man.final();
5732 const final_path: Path = .{
5733 .root_dir = dirs.local_cache,
5734 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5735 };
5736 Io.Dir.rename(
5737 config_tmp_path.root_dir.handle,
5738 config_tmp_path.sub_path,
5739 final_path.root_dir.handle,
5740 final_path.sub_path,
5741 io,
5742 ) catch |err| {
5743 fatal("failed to rename configuration file from {f} into {f}: {t}", .{
5744 config_tmp_path, final_path, err,
5745 });
5746 };
5747 config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err});
5748 break :cp .{ final_path, false };
5749 }
5750 };
5727 // If it is poisoned, there is no point in moving it to cached
5728 // location. Just leave it in the tmp directory.
5729 if (configuration.poisoned) {
5730 break :cp .{ config_tmp_path, true };
5731 } else {
5732 const digest = config_man.final();
5733 const final_path: Path = .{
5734 .root_dir = dirs.local_cache,
5735 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5736 };
5737 Io.Dir.rename(
5738 config_tmp_path.root_dir.handle,
5739 config_tmp_path.sub_path,
5740 final_path.root_dir.handle,
5741 final_path.sub_path,
5742 io,
5743 ) catch |err| {
5744 fatal("failed to rename configuration file from {f} into {f}: {t}", .{
5745 config_tmp_path, final_path, err,
5746 });
5747 };
5748 config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err});
5749 break :cp .{ final_path, false };
5750 }
5751 };
57515752
5752 {
5753 // Release all file system locks just before running the maker process.
5754 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
5755 defer if (configuration_lock) |*l| l.release(io);
5753 {
5754 // Release all file system locks just before running the maker process.
5755 var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null;
5756 defer if (configuration_lock) |*l| l.release(io);
57565757
5757 const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err});
5758 const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err});
57585759
5759 make_argv.items[0] = try make_runner.exe_path.toString(arena);
5760 make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena);
5760 make_argv.items[0] = try make_runner.exe_path.toString(arena);
5761 make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena);
5762 }
57615763 }
57625764
57635765 if (!process.can_spawn) {