zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit f79ae3214c41183c90ca4d7acb0ac0ded3d99f61 (tree)
parent bc031bedaa470d14348a6be1da09a8ba5fd31c5d
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Sun, 24 May 2026 13:57:35 -0700

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

Diffstat:
Msrc/main.zig | 1008++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 505 insertions(+), 503 deletions(-)

diff --git a/src/main.zig b/src/main.zig @@ -5202,562 +5202,564 @@ fn cmdBuild( .build_file = build_file, }); - // This `init` calls `fatal` on error. - var dirs: Compilation.Directories = .init( - arena, - io, - override_lib_dir, - override_global_cache_dir, - .{ .override = path: { - if (override_local_cache_dir) |d| break :path d; - break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); - } }, - .empty, - self_exe_path, - environ_map, - cwd_path, - ); - defer dirs.deinit(io); - - const thread_limit = @min( - @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), - std.math.maxInt(Zcu.PerThread.IdBacking), - ); - try setThreadLimit(arena, thread_limit); + { + // This `init` calls `fatal` on error. + var dirs: Compilation.Directories = .init( + arena, + io, + override_lib_dir, + override_global_cache_dir, + .{ .override = path: { + if (override_local_cache_dir) |d| break :path d; + break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); + } }, + .empty, + self_exe_path, + environ_map, + cwd_path, + ); + defer dirs.deinit(io); - // Cache lookup for configure options. If we get a match, we can skip - // execution of the configure script. If not, we get the file path to pass - // to the configure process. - var local_cache: Cache = .{ - .gpa = gpa, - .io = io, - .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}), - .cwd = cwd_path, - }; - local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); - local_cache.addPrefix(dirs.zig_lib); - local_cache.addPrefix(dirs.local_cache); - local_cache.addPrefix(dirs.global_cache); - defer local_cache.manifest_dir.close(io); - - var config_man = local_cache.obtain(); - defer config_man.deinit(); - config_man.hash.addBytes(build_options.version); - - for (cached_passthru_configure.items) |i| - config_man.hash.addBytes(configure_argv.items[i]); - - // Prevents a `zig build` from getting a false positive cache hit following - // a `zig build --cache-poison=ignored`. - config_man.hash.add(cache_poison == .ignored); - - // Normally the build runner is compiled for the host target but here is - // some code to help when debugging edits to the build runner so that you - // can make sure it compiles successfully on other targets. - const resolved_target: Package.Module.ResolvedTarget = t: { - if (build_options.enable_debug_extensions) { - if (debug_target) |triple| { - const target_query = try std.Target.Query.parse(.{ - .arch_os_abi = triple, - }); - config_man.hash.addBytes(triple); - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, target_query), - .is_native_os = false, - .is_native_abi = false, - .is_explicit_dynamic_linker = false, - }; + const thread_limit = @min( + @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), + std.math.maxInt(Zcu.PerThread.IdBacking), + ); + try setThreadLimit(arena, thread_limit); + + // Cache lookup for configure options. If we get a match, we can skip + // execution of the configure script. If not, we get the file path to pass + // to the configure process. + var local_cache: Cache = .{ + .gpa = gpa, + .io = io, + .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}), + .cwd = cwd_path, + }; + local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); + local_cache.addPrefix(dirs.zig_lib); + local_cache.addPrefix(dirs.local_cache); + local_cache.addPrefix(dirs.global_cache); + defer local_cache.manifest_dir.close(io); + + var config_man = local_cache.obtain(); + defer config_man.deinit(); + config_man.hash.addBytes(build_options.version); + + for (cached_passthru_configure.items) |i| + config_man.hash.addBytes(configure_argv.items[i]); + + // Prevents a `zig build` from getting a false positive cache hit following + // a `zig build --cache-poison=ignored`. + config_man.hash.add(cache_poison == .ignored); + + // Normally the build runner is compiled for the host target but here is + // some code to help when debugging edits to the build runner so that you + // can make sure it compiles successfully on other targets. + const resolved_target: Package.Module.ResolvedTarget = t: { + if (build_options.enable_debug_extensions) { + if (debug_target) |triple| { + const target_query = try std.Target.Query.parse(.{ + .arch_os_abi = triple, + }); + config_man.hash.addBytes(triple); + break :t .{ + .result = std.zig.resolveTargetQueryOrFatal(io, target_query), + .is_native_os = false, + .is_native_abi = false, + .is_explicit_dynamic_linker = false, + }; + } } - } - break :t .{ - .result = std.zig.resolveTargetQueryOrFatal(io, .{}), - .is_native_os = true, - .is_native_abi = true, - .is_explicit_dynamic_linker = false, + break :t .{ + .result = std.zig.resolveTargetQueryOrFatal(io, .{}), + .is_native_os = true, + .is_native_abi = true, + .is_explicit_dynamic_linker = false, + }; }; - }; - // Likewise, `--debug-libc` allows overriding the libc installation. - const libc_installation: ?*const LibCInstallation = lci: { - const paths_file = debug_libc_paths_file orelse break :lci null; - if (!build_options.enable_debug_extensions) unreachable; - const lci = try arena.create(LibCInstallation); - lci.* = try .parse(arena, io, paths_file, &resolved_target.result); - LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi); - break :lci lci; - }; - - // Kick off an optimized compilation of the make runner. - var make_runner_task = io.async(compileMakeRunner, .{ gpa, arena, io, .{ - .dirs = .{ - .cwd = dirs.cwd, - .zig_lib = dirs.zig_lib, - .global_cache = dirs.global_cache, - .local_cache = dirs.global_cache, - }, - .environ_map = environ_map, - .parent_prog_node = root_prog_node, - .resolved_target = resolved_target, - .libc_installation = libc_installation, - .thread_limit = thread_limit, - .self_exe_path = self_exe_path, - .color = color, - .reference_trace = reference_trace, - .optimize_mode = maker_optimize_mode, - } }); - defer _ = make_runner_task.cancel(io) catch {}; - - const pkg_root: Path = if (override_pkg_dir) |p| - .initCwd(p) - else if (system_pkg_dir_path) |p| - .initCwd(p) - else - .{ - .root_dir = build_root.directory, - .sub_path = "zig-pkg", + // Likewise, `--debug-libc` allows overriding the libc installation. + const libc_installation: ?*const LibCInstallation = lci: { + const paths_file = debug_libc_paths_file orelse break :lci null; + if (!build_options.enable_debug_extensions) unreachable; + const lci = try arena.create(LibCInstallation); + lci.* = try .parse(arena, io, paths_file, &resolved_target.result); + LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi); + break :lci lci; }; - make_argv.items[make_argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path; - make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path; - make_argv.items[make_argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; - make_argv.items[make_argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; + // Kick off an optimized compilation of the make runner. + var make_runner_task = io.async(compileMakeRunner, .{ gpa, arena, io, .{ + .dirs = .{ + .cwd = dirs.cwd, + .zig_lib = dirs.zig_lib, + .global_cache = dirs.global_cache, + .local_cache = dirs.global_cache, + }, + .environ_map = environ_map, + .parent_prog_node = root_prog_node, + .resolved_target = resolved_target, + .libc_installation = libc_installation, + .thread_limit = thread_limit, + .self_exe_path = self_exe_path, + .color = color, + .reference_trace = reference_trace, + .optimize_mode = maker_optimize_mode, + } }); + defer _ = make_runner_task.cancel(io) catch {}; + + const pkg_root: Path = if (override_pkg_dir) |p| + .initCwd(p) + else if (system_pkg_dir_path) |p| + .initCwd(p) + else + .{ + .root_dir = build_root.directory, + .sub_path = "zig-pkg", + }; - configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; + make_argv.items[make_argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path; + make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path; + make_argv.items[make_argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; + make_argv.items[make_argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; - // Dummy http client that is not actually used when fetch_command is unsupported. - // Prevents bootstrap from depending on a bunch of unnecessary stuff. - var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct { - allocator: Allocator, - io: Io, - fn deinit(_: @This()) void {} - } = .{ .allocator = gpa, .io = io }; - defer http_client.deinit(); + configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; - var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; - var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; + // Dummy http client that is not actually used when fetch_command is unsupported. + // Prevents bootstrap from depending on a bunch of unnecessary stuff. + var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct { + allocator: Allocator, + io: Io, + fn deinit(_: @This()) void {} + } = .{ .allocator = gpa, .io = io }; + defer http_client.deinit(); - { - // Populate fork_set. - var group: Io.Group = .init; - defer group.cancel(io); - - for (forks.items) |*fork| - group.async(io, Fork.load, .{ io, gpa, fork, color }); - - try group.await(io); - - for (forks.items) |*fork| { - if (fork.failed) process.exit(1); - try fork_set.put(arena, .{ - .path = fork.path, - .manifest_ast = fork.manifest_ast, - .manifest = fork.manifest, - .uses = 0, - }, {}); - } - } - defer Fork.deinitList(forks.items); + var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; + var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; - // This loop is re-evaluated when the build script exits with an indication that it - // could not continue due to missing lazy dependencies. - const configuration_path: Path, const poisoned: bool = cp: while (true) { - // We want to release all the locks before executing the child process, so we make a nice - // big block here to ensure the cleanup gets run when we extract out our argv. { - const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_make_runner) |runner| .{ - .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}), - .root_src_path = fs.path.basename(runner), - } else .{ - .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"), - .root_src_path = "configurer.zig", - }; + // Populate fork_set. + var group: Io.Group = .init; + defer group.cancel(io); + + for (forks.items) |*fork| + group.async(io, Fork.load, .{ io, gpa, fork, color }); + + try group.await(io); + + for (forks.items) |*fork| { + if (fork.failed) process.exit(1); + try fork_set.put(arena, .{ + .path = fork.path, + .manifest_ast = fork.manifest_ast, + .manifest = fork.manifest, + .uses = 0, + }, {}); + } + } + defer Fork.deinitList(forks.items); - const config = try Compilation.Config.resolve(.{ - .output_mode = .Exe, - .resolved_target = resolved_target, - .have_zcu = true, - .emit_bin = true, - .is_test = false, - }); + // This loop is re-evaluated when the build script exits with an indication that it + // could not continue due to missing lazy dependencies. + const configuration_path: Path, const poisoned: bool = cp: while (true) { + // We want to release all the locks before executing the child process, so we make a nice + // big block here to ensure the cleanup gets run when we extract out our argv. + { + const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_make_runner) |runner| .{ + .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}), + .root_src_path = fs.path.basename(runner), + } else .{ + .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"), + .root_src_path = "configurer.zig", + }; - const root_mod = try Package.Module.create(arena, .{ - .paths = main_mod_paths, - .fully_qualified_name = "root", - .cc_argv = &.{}, - .inherited = .{ + const config = try Compilation.Config.resolve(.{ + .output_mode = .Exe, .resolved_target = resolved_target, - .single_threaded = true, - }, - .global = config, - .parent = null, - }); + .have_zcu = true, + .emit_bin = true, + .is_test = false, + }); - const build_mod = try Package.Module.create(arena, .{ - .paths = .{ - .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}), - .root_src_path = build_root.build_zig_basename, - }, - .fully_qualified_name = "root.@build", - .cc_argv = &.{}, - .inherited = .{}, - .global = config, - .parent = root_mod, - }); + const root_mod = try Package.Module.create(arena, .{ + .paths = main_mod_paths, + .fully_qualified_name = "root", + .cc_argv = &.{}, + .inherited = .{ + .resolved_target = resolved_target, + .single_threaded = true, + }, + .global = config, + .parent = null, + }); - if (dev.env.supports(.fetch_command)) { - const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); - defer fetch_prog_node.end(); + const build_mod = try Package.Module.create(arena, .{ + .paths = .{ + .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}), + .root_src_path = build_root.build_zig_basename, + }, + .fully_qualified_name = "root.@build", + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = root_mod, + }); - // Reset fork match counts. - for (fork_set.keys()) |*fork| fork.uses = 0; + if (dev.env.supports(.fetch_command)) { + const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); + defer fetch_prog_node.end(); - var job_queue: Package.Fetch.JobQueue = .{ - .io = io, - .http_client = &http_client, - .global_cache = dirs.global_cache, - .local_storage = &.{ - .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" }, - .pkg_root = pkg_root, - }, - .recursive = true, - .debug_hash = false, - .unlazy_set = unlazy_set, - .fork_set = fork_set, - .mode = fetch_mode, - .prog_node = fetch_prog_node, - .read_only = system_pkg_dir_path != null, - }; - defer job_queue.deinit(); + // Reset fork match counts. + for (fork_set.keys()) |*fork| fork.uses = 0; - if (system_pkg_dir_path == null) { - try http_client.initDefaultProxies(arena, environ_map); - } + var job_queue: Package.Fetch.JobQueue = .{ + .io = io, + .http_client = &http_client, + .global_cache = dirs.global_cache, + .local_storage = &.{ + .cache_root = .{ .root_dir = dirs.local_cache, .sub_path = "" }, + .pkg_root = pkg_root, + }, + .recursive = true, + .debug_hash = false, + .unlazy_set = unlazy_set, + .fork_set = fork_set, + .mode = fetch_mode, + .prog_node = fetch_prog_node, + .read_only = system_pkg_dir_path != null, + }; + defer job_queue.deinit(); - try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); - try job_queue.table.ensureUnusedCapacity(gpa, 1); - - const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory }; - - var fetch: Package.Fetch = .{ - .arena = std.heap.ArenaAllocator.init(gpa), - .location = .{ .relative_path = phantom_package_root }, - .location_tok = 0, - .hash_tok = .none, - .name_tok = 0, - .lazy_status = .eager, - .remote_package_root = phantom_package_root, - .parent_package_root = phantom_package_root, - .parent_manifest_ast = null, - .prog_node = fetch_prog_node, - .job_queue = &job_queue, - .omit_missing_hash_error = true, - .allow_missing_paths_field = false, - .use_latest_commit = false, - - .package_root = undefined, - .error_bundle = undefined, - .manifest = undefined, - .manifest_ast = undefined, - .have_manifest = false, - .computed_hash = undefined, - .has_build_zig = true, - .oom_flag = false, - .latest_commit = null, - - .module = build_mod, - }; + if (system_pkg_dir_path == null) { + try http_client.initDefaultProxies(arena, environ_map); + } - job_queue.all_fetches.appendAssumeCapacity(&fetch); + try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); + try job_queue.table.ensureUnusedCapacity(gpa, 1); + + const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory }; + + var fetch: Package.Fetch = .{ + .arena = std.heap.ArenaAllocator.init(gpa), + .location = .{ .relative_path = phantom_package_root }, + .location_tok = 0, + .hash_tok = .none, + .name_tok = 0, + .lazy_status = .eager, + .remote_package_root = phantom_package_root, + .parent_package_root = phantom_package_root, + .parent_manifest_ast = null, + .prog_node = fetch_prog_node, + .job_queue = &job_queue, + .omit_missing_hash_error = true, + .allow_missing_paths_field = false, + .use_latest_commit = false, + + .package_root = undefined, + .error_bundle = undefined, + .manifest = undefined, + .manifest_ast = undefined, + .have_manifest = false, + .computed_hash = undefined, + .has_build_zig = true, + .oom_flag = false, + .latest_commit = null, + + .module = build_mod, + }; - job_queue.table.putAssumeCapacityNoClobber( - Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache), - &fetch, - ); + job_queue.all_fetches.appendAssumeCapacity(&fetch); - job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); - try job_queue.group.await(io); + job_queue.table.putAssumeCapacityNoClobber( + Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache), + &fetch, + ); - { - // Ensure that forks were actually used. This is done - // before printing manifest errors because using a fork can - // prevent them. - var any_unused = false; - for (fork_set.keys()) |*fork| { - if (fork.uses == 0) { - std.log.err("fork {f} matched no {s} packages", .{ - fork.path, fork.manifest.name, - }); - any_unused = true; - } else { - std.log.info("fork {f} matched {d} {s} packages", .{ - fork.path, fork.uses, fork.manifest.name, - }); + job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); + try job_queue.group.await(io); + + { + // Ensure that forks were actually used. This is done + // before printing manifest errors because using a fork can + // prevent them. + var any_unused = false; + for (fork_set.keys()) |*fork| { + if (fork.uses == 0) { + std.log.err("fork {f} matched no {s} packages", .{ + fork.path, fork.manifest.name, + }); + any_unused = true; + } else { + std.log.info("fork {f} matched {d} {s} packages", .{ + fork.path, fork.uses, fork.manifest.name, + }); + } } + if (any_unused) process.exit(1); } - if (any_unused) process.exit(1); - } - try job_queue.consolidateErrors(); + try job_queue.consolidateErrors(); - if (fetch.error_bundle.root_list.items.len > 0) { - var errors = try fetch.error_bundle.toOwnedBundle(""); - errors.renderToStderr(io, .{}, color) catch {}; - process.exit(1); - } + if (fetch.error_bundle.root_list.items.len > 0) { + var errors = try fetch.error_bundle.toOwnedBundle(""); + errors.renderToStderr(io, .{}, color) catch {}; + process.exit(1); + } - if (fetch_only) return cleanExit(io); + if (fetch_only) return cleanExit(io); + + var source_buf = std.array_list.Managed(u8).init(gpa); + defer source_buf.deinit(); + try job_queue.createDependenciesSource(&source_buf); + const deps_mod = try createDependenciesModule( + arena, + io, + source_buf.items, + root_mod, + dirs, + config, + ); + + { + // We need a Module for each package's build.zig. + const hashes = job_queue.table.keys(); + const fetches = job_queue.table.values(); + try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); + for (hashes, fetches) |*hash, f| { + if (f == &fetch) { + // The first one is a dummy package for the current project. + continue; + } + if (!f.has_build_zig) + continue; + const hash_slice = hash.toSlice(); + const mod_root_path = try f.package_root.toString(arena); + const m = try Package.Module.create(arena, .{ + .paths = .{ + .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}), + .root_src_path = Package.build_zig_basename, + }, + .fully_qualified_name = try std.fmt.allocPrint( + arena, + "root.@dependencies.{s}", + .{hash_slice}, + ), + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = root_mod, + }); + const hash_cloned = try arena.dupe(u8, hash_slice); + deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m); + f.module = m; + } - var source_buf = std.array_list.Managed(u8).init(gpa); - defer source_buf.deinit(); - try job_queue.createDependenciesSource(&source_buf); - const deps_mod = try createDependenciesModule( + // Each build.zig module needs access to each of its + // dependencies' build.zig modules by name. + for (fetches) |f| { + const mod = f.module orelse continue; + if (!f.have_manifest) continue; + const man = &f.manifest; + const dep_names = man.dependencies.keys(); + try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); + for (dep_names, man.dependencies.values()) |name, dep| { + const dep_digest = Package.Fetch.depDigest( + f.package_root, + dirs.global_cache, + dep, + ) orelse continue; + const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue; + const name_cloned = try arena.dupe(u8, name); + mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); + } + } + } + } else try createEmptyDependenciesModule( arena, io, - source_buf.items, root_mod, dirs, config, ); - { - // We need a Module for each package's build.zig. - const hashes = job_queue.table.keys(); - const fetches = job_queue.table.values(); - try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len)); - for (hashes, fetches) |*hash, f| { - if (f == &fetch) { - // The first one is a dummy package for the current project. - continue; - } - if (!f.has_build_zig) - continue; - const hash_slice = hash.toSlice(); - const mod_root_path = try f.package_root.toString(arena); - const m = try Package.Module.create(arena, .{ - .paths = .{ - .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}), - .root_src_path = Package.build_zig_basename, - }, - .fully_qualified_name = try std.fmt.allocPrint( - arena, - "root.@dependencies.{s}", - .{hash_slice}, - ), - .cc_argv = &.{}, - .inherited = .{}, - .global = config, - .parent = root_mod, - }); - const hash_cloned = try arena.dupe(u8, hash_slice); - deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m); - f.module = m; - } + const compile_prog_node = root_prog_node.start("Compile Configure Script", 0); + defer compile_prog_node.end(); + + try root_mod.deps.put(arena, "@build", build_mod); + + var create_diag: Compilation.CreateDiagnostic = undefined; + const comp = Compilation.create(gpa, arena, io, &create_diag, .{ + .libc_installation = libc_installation, + .dirs = dirs, + .root_name = "configure", + .config = config, + .root_mod = root_mod, + .main_mod = build_mod, + .emit_bin = .yes_cache, + .self_exe_path = self_exe_path, + .thread_limit = thread_limit, + .verbose_cc = verbose_cc, + .verbose_link = verbose_link, + .verbose_air = verbose_air, + .verbose_intern_pool = verbose_intern_pool, + .verbose_generic_instances = verbose_generic_instances, + .verbose_llvm_ir = verbose_llvm_ir, + .verbose_llvm_bc = verbose_llvm_bc, + .verbose_llvm_cpu_features = verbose_llvm_cpu_features, + .cache_mode = .whole, + .reference_trace = reference_trace, + .debug_compile_errors = debug_compile_errors, + .environ_map = environ_map, + }) catch |err| switch (err) { + error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), + else => |e| fatal("failed to create compilation: {t}", .{e}), + }; + defer comp.destroy(); - // Each build.zig module needs access to each of its - // dependencies' build.zig modules by name. - for (fetches) |f| { - const mod = f.module orelse continue; - if (!f.have_manifest) continue; - const man = &f.manifest; - const dep_names = man.dependencies.keys(); - try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); - for (dep_names, man.dependencies.values()) |name, dep| { - const dep_digest = Package.Fetch.depDigest( - f.package_root, - dirs.global_cache, - dep, - ) orelse continue; - const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue; - const name_cloned = try arena.dupe(u8, name); - mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod); - } - } - } - } else try createEmptyDependenciesModule( - arena, - io, - root_mod, - dirs, - config, - ); + updateModule(comp, color, compile_prog_node) catch |err| switch (err) { + error.CompileErrorsReported => process.exit(2), + else => |e| return e, + }; - const compile_prog_node = root_prog_node.start("Compile Configure Script", 0); - defer compile_prog_node.end(); - - try root_mod.deps.put(arena, "@build", build_mod); - - var create_diag: Compilation.CreateDiagnostic = undefined; - const comp = Compilation.create(gpa, arena, io, &create_diag, .{ - .libc_installation = libc_installation, - .dirs = dirs, - .root_name = "configure", - .config = config, - .root_mod = root_mod, - .main_mod = build_mod, - .emit_bin = .yes_cache, - .self_exe_path = self_exe_path, - .thread_limit = thread_limit, - .verbose_cc = verbose_cc, - .verbose_link = verbose_link, - .verbose_air = verbose_air, - .verbose_intern_pool = verbose_intern_pool, - .verbose_generic_instances = verbose_generic_instances, - .verbose_llvm_ir = verbose_llvm_ir, - .verbose_llvm_bc = verbose_llvm_bc, - .verbose_llvm_cpu_features = verbose_llvm_cpu_features, - .cache_mode = .whole, - .reference_trace = reference_trace, - .debug_compile_errors = debug_compile_errors, - .environ_map = environ_map, - }) catch |err| switch (err) { - error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), - else => |e| fatal("failed to create compilation: {t}", .{e}), - }; - defer comp.destroy(); + // Since incremental compilation isn't done yet, we use cache_mode = whole + // above, and thus the output file is already closed. + //try comp.makeBinFileExecutable(); + const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?); + const exe_path: Path = .{ + .root_dir = dirs.local_cache, + .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), + }; + _ = try config_man.addFilePath(exe_path, null); + configure_argv.items[0] = try exe_path.toString(arena); + + switch (cache_poison) { + .pure, .disallowed, .ignored => if (try config_man.hit()) { + const digest = config_man.final(); + break :cp .{ + .{ + .root_dir = dirs.local_cache, + .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}), + }, + false, + }; + }, + .poisoned => {}, // Don't bother checking for cache hit. + } + } - updateModule(comp, color, compile_prog_node) catch |err| switch (err) { - error.CompileErrorsReported => process.exit(2), - else => |e| return e, - }; + if (!process.can_spawn) { + const cmd = try std.mem.join(arena, " ", configure_argv.items); + fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); + } - // Since incremental compilation isn't done yet, we use cache_mode = whole - // above, and thus the output file is already closed. - //try comp.makeBinFileExecutable(); - const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?); - const exe_path: Path = .{ + const rand_int = randInt(io, u64); + const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const config_tmp_path: Path = .{ .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), + .sub_path = tmp_dir_sub_path, }; - _ = try config_man.addFilePath(exe_path, null); - configure_argv.items[0] = try exe_path.toString(arena); - - switch (cache_poison) { - .pure, .disallowed, .ignored => if (try config_man.hit()) { - const digest = config_man.final(); - break :cp .{ - .{ - .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}), - }, - false, - }; - }, - .poisoned => {}, // Don't bother checking for cache hit. + const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( + io, + config_tmp_path.sub_path, + .{ .read = true, .exclusive = true }, + ); + defer config_tmp_file.close(io); + + const term = term: { + const child_node = root_prog_node.start("Run Configure Script", 0); + defer child_node.end(); + var child = std.process.spawn(io, .{ + .argv = configure_argv.items, + .stdout = .{ .file = config_tmp_file }, + .progress_node = child_node, + }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err }); + defer child.kill(io); + break :term child.wait(io) catch |err| + fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err }); + }; + if (!term.success()) { + // Failure to produce the configuration file. + const cmd = try std.mem.join(arena, " ", configure_argv.items); + fatal("the following configure command {f}:\n{s}", .{ term, cmd }); } - } - - if (!process.can_spawn) { - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); - } - - const rand_int = randInt(io, u64); - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); - const config_tmp_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = tmp_dir_sub_path, - }; - const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile( - io, - config_tmp_path.sub_path, - .{ .read = true, .exclusive = true }, - ); - defer config_tmp_file.close(io); - - const term = term: { - const child_node = root_prog_node.start("Run Configure Script", 0); - defer child_node.end(); - var child = std.process.spawn(io, .{ - .argv = configure_argv.items, - .stdout = .{ .file = config_tmp_file }, - .progress_node = child_node, - }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err }); - defer child.kill(io); - break :term child.wait(io) catch |err| - fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err }); - }; - if (!term.success()) { - // Failure to produce the configuration file. - const cmd = try std.mem.join(arena, " ", configure_argv.items); - fatal("the following configure command {f}:\n{s}", .{ term, cmd }); - } - // Even though the file is designed to be sent directly to make - // runner, we must load it now because: - // * If it contains additional file dependencies, we need to - // add them to `config_man` before obtaining the final digest. - // * If it contains a set of lazy packages that need to be - // fetched, we need to fetch those now and re-run configure. - var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err| - fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); - - if (configuration.unlazy_deps.len != 0) { - if (!dev.env.supports(.fetch_command)) process.exit(1); - var any_errors = false; - for (configuration.unlazy_deps) |hash_string| { - const hash = hash_string.slice(&configuration); - assert(hash.len != 0); - if (hash.len > Package.Hash.max_len) { - std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ hash.len, hash }); - any_errors = true; - continue; + // Even though the file is designed to be sent directly to make + // runner, we must load it now because: + // * If it contains additional file dependencies, we need to + // add them to `config_man` before obtaining the final digest. + // * If it contains a set of lazy packages that need to be + // fetched, we need to fetch those now and re-run configure. + var configuration = std.Build.Configuration.loadFile(arena, io, config_tmp_file) catch |err| + fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err }); + + if (configuration.unlazy_deps.len != 0) { + if (!dev.env.supports(.fetch_command)) process.exit(1); + var any_errors = false; + for (configuration.unlazy_deps) |hash_string| { + const hash = hash_string.slice(&configuration); + assert(hash.len != 0); + if (hash.len > Package.Hash.max_len) { + std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ hash.len, hash }); + any_errors = true; + continue; + } + try unlazy_set.put(arena, .fromSlice(hash), {}); } - try unlazy_set.put(arena, .fromSlice(hash), {}); - } - if (any_errors) process.exit(1); - if (system_pkg_dir_path) |p| { - // In this mode, the system needs to provide these packages; they - // cannot be fetched by Zig. - const s = fs.path.sep_str; - for (unlazy_set.keys()) |*hash| { - std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); + if (any_errors) process.exit(1); + if (system_pkg_dir_path) |p| { + // In this mode, the system needs to provide these packages; they + // cannot be fetched by Zig. + const s = fs.path.sep_str; + for (unlazy_set.keys()) |*hash| { + std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); + } + std.log.info("remote package fetching disabled due to --system mode", .{}); + std.log.info("dependencies might be avoidable depending on build configuration", .{}); + process.exit(1); } - std.log.info("remote package fetching disabled due to --system mode", .{}); - std.log.info("dependencies might be avoidable depending on build configuration", .{}); - process.exit(1); + continue :cp; } - continue :cp; - } - for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| { - const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub }; - try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); - } + for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| { + const conf_path: std.Build.Configuration.Path = .{ .base = base, .sub = sub }; + try config_man.addPathPost(conf_path.toCachePath(&configuration, arena)); + } - // If it is poisoned, there is no point in moving it to cached - // location. Just leave it in the tmp directory. - if (configuration.poisoned) { - break :cp .{ config_tmp_path, true }; - } else { - const digest = config_man.final(); - const final_path: Path = .{ - .root_dir = dirs.local_cache, - .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}), - }; - Io.Dir.rename( - config_tmp_path.root_dir.handle, - config_tmp_path.sub_path, - final_path.root_dir.handle, - final_path.sub_path, - io, - ) catch |err| { - fatal("failed to rename configuration file from {f} into {f}: {t}", .{ - config_tmp_path, final_path, err, - }); - }; - config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); - break :cp .{ final_path, false }; - } - }; + // If it is poisoned, there is no point in moving it to cached + // location. Just leave it in the tmp directory. + if (configuration.poisoned) { + break :cp .{ config_tmp_path, true }; + } else { + const digest = config_man.final(); + const final_path: Path = .{ + .root_dir = dirs.local_cache, + .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}), + }; + Io.Dir.rename( + config_tmp_path.root_dir.handle, + config_tmp_path.sub_path, + final_path.root_dir.handle, + final_path.sub_path, + io, + ) catch |err| { + fatal("failed to rename configuration file from {f} into {f}: {t}", .{ + config_tmp_path, final_path, err, + }); + }; + config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); + break :cp .{ final_path, false }; + } + }; - { - // Release all file system locks just before running the maker process. - var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; - defer if (configuration_lock) |*l| l.release(io); + { + // Release all file system locks just before running the maker process. + var configuration_lock = if (!poisoned) config_man.toOwnedLock() else null; + defer if (configuration_lock) |*l| l.release(io); - const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err}); + const make_runner = make_runner_task.await(io) catch |err| fatal("failed compiling maker: {t}", .{err}); - make_argv.items[0] = try make_runner.exe_path.toString(arena); - make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena); + make_argv.items[0] = try make_runner.exe_path.toString(arena); + make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena); + } } if (!process.can_spawn) {