authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-02 10:25:02+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-02 10:25:02+02:00
logefd6f190fd829d89c71e2592b48945a1bacae107
tree13d56074b38a46e1e8e950360240c6c1d2ce844c
parent2a235aac2be87c2535f0ae2826b53b43bc541297
parent71ee8ceed9dd045aa7a4bb50291fceb05257314b

Merge pull request 'build system fixes' (#36014) from fix-poison into master

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

7 files changed, 59 insertions(+), 38 deletions(-)

CMakeLists.txt+3-1
......@@ -729,6 +729,8 @@ endif()
729729
730730
731731set(ZIG_BUILD_ARGS
732 "--zig-lib=${PROJECT_SOURCE_DIR}/lib"
733
732734 "-Dversion-string=${RESOLVED_ZIG_VERSION}"
733735 "-Dtarget=${ZIG_TARGET_TRIPLE}"
734736 "-Dcpu=${ZIG_TARGET_MCPU}"
......@@ -791,7 +793,7 @@ set(ZIG2_WORKING_DIR "${PROJECT_SOURCE_DIR}")
791793
792794add_custom_command(
793795 OUTPUT "${PROJECT_BINARY_DIR}/stage3/bin/zig"
794 COMMAND zig2 build --prefix "${PROJECT_BINARY_DIR}/stage3" ${ZIG_BUILD_ARGS}
796 COMMAND zig2 build ${ZIG_BUILD_ARGS} --prefix "${PROJECT_BINARY_DIR}/stage3"
795797 COMMENT "Building stage3"
796798 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"
797799)
cmake/install.cmake+1-1
......@@ -1,4 +1,4 @@
1set(ZIG_INSTALL_ARGS build --prefix "${CMAKE_INSTALL_PREFIX}" ${ZIG_BUILD_ARGS})
1set(ZIG_INSTALL_ARGS build ${ZIG_BUILD_ARGS} --prefix "${CMAKE_INSTALL_PREFIX}")
22execute_process(
33 COMMAND "${ZIG_EXECUTABLE}" ${ZIG_INSTALL_ARGS}
44 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"
lib/compiler/Maker.zig+39-30
......@@ -283,11 +283,11 @@ pub fn main(init: process.Init.Minimal) !void {
283283 cache_poison = .poisoned;
284284 configure_argv.appendAssumeCapacity("--cache-poison=poisoned");
285285 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
286 // Allow the configurer process to report parse failure.
287 if (stringToEnum(std.Build.Graph.CachePoison, rest)) |poison| {
288 cache_poison = poison;
289 }
290 configure_argv.appendAssumeCapacity(arg);
286 // We have to report parse failure here otherwise we would
287 // potentially get false positive cache hits for misspellings.
288 cache_poison = stringToEnum(std.Build.Graph.CachePoison, rest) orelse
289 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});
290 if (cache_poison != .pure) configure_argv.appendAssumeCapacity(arg);
291291 } else if (mem.eql(u8, arg, "--verbose")) {
292292 // Intentionally is added both to make and configure but
293293 // does not go into the cache hash.
......@@ -316,6 +316,8 @@ pub fn main(init: process.Init.Minimal) !void {
316316 try forks.append(arena, .init(rest));
317317 } else if (mem.eql(u8, arg, "--fork")) {
318318 try forks.append(arena, .init(nextArgOrFatal(args, &arg_i)));
319 } else if (mem.startsWith(u8, arg, "--zig-lib=")) {
320 fatal("--zig-lib= argument is special and must be first", .{});
319321 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
320322 help_menu = true;
321323 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
......@@ -981,27 +983,35 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
981983 // This loop is re-evaluated when the build script exits with an indication that it
982984 // could not continue due to missing lazy dependencies.
983985 const configuration_path: Path, var configuration_lock: ?Cache.Lock = cp: while (true) {
986 build_mod.deps.clearRetainingCapacity();
987 deps_mod.deps.clearRetainingCapacity();
988
984989 // Cache lookup for configure options. If we get a match, we can skip
985990 // execution of the configure script. If not, we get the file path to pass
986991 // to the configure process.
987992 //
988993 // In the hot path, we only check this cache, which means that also
989994 // configure source files need to go in here.
990 var config_man = graph.cache.obtain();
991 defer config_man.deinit();
995 var config_man_allocation: Cache.Manifest = undefined;
996 const config_man: ?*Cache.Manifest = switch (options.cache_poison) {
997 .pure, .disallowed, .ignored => m: {
998 config_man_allocation = graph.cache.obtain();
992999
993 for (options.cached_passthru_configure) |i|
994 config_man.hash.addBytes(configure_argv[i]);
1000 for (options.cached_passthru_configure) |i|
1001 config_man_allocation.hash.addBytes(configure_argv[i]);
9951002
996 if (target_arch_os_abi) |triple|
997 config_man.hash.addBytes(triple);
1003 if (target_arch_os_abi) |triple|
1004 config_man_allocation.hash.addBytes(triple);
9981005
999 // Prevents a `zig build` from getting a false positive cache hit following
1000 // a `zig build --cache-poison=ignored`.
1001 config_man.hash.add(options.cache_poison == .ignored);
1006 // Prevents a `zig build` from getting a false positive cache hit following
1007 // a `zig build --cache-poison=ignored`.
1008 config_man_allocation.hash.add(options.cache_poison == .ignored);
10021009
1003 build_mod.deps.clearRetainingCapacity();
1004 deps_mod.deps.clearRetainingCapacity();
1010 break :m &config_man_allocation;
1011 },
1012 .poisoned => null,
1013 };
1014 defer if (config_man) |man| man.deinit();
10051015
10061016 // We want to release all the locks before executing the child process, so we make a nice
10071017 // big block here to ensure the cleanup gets run when we extract out our argv.
......@@ -1217,26 +1227,24 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
12171227 const compile_prog_node = options.parent_progress_node.start("Compile Configure Script", 0);
12181228 defer compile_prog_node.end();
12191229
1220 switch (options.cache_poison) {
1221 .pure, .disallowed, .ignored => if (try config_man.hit(compile_prog_node)) {
1222 const digest = config_man.final();
1230 if (config_man) |man| {
1231 if (try man.hit(compile_prog_node)) {
1232 const digest = man.final();
12231233 break :cp .{
12241234 .{
12251235 .root_dir = graph.local_cache_root,
12261236 .sub_path = try arena.print("c/{s}", .{&digest}),
12271237 },
1228 config_man.toOwnedLock(),
1238 man.toOwnedLock(),
12291239 };
1230 },
1231 .poisoned => {}, // Don't bother checking for cache hit.
1240 }
12321241 }
1233
12341242 const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{
12351243 .argv = build_configurer_argv.items,
12361244 .cache_root = graph.local_cache_root,
12371245 .root_name = configurer_exe_name,
12381246 .environ_map = &graph.environ_map,
1239 .cache_manifest = &config_man,
1247 .cache_manifest = config_man,
12401248 .arch_os_abi = target_arch_os_abi,
12411249 .progress_node = compile_prog_node,
12421250 .skip_log_cmdline_on_compile_errors = !graph.verbose,
......@@ -1318,20 +1326,21 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13181326 continue :cp;
13191327 }
13201328
1321 for (configuration.path_deps) |path_dep| {
1329 if (config_man) |man| for (configuration.path_deps) |path_dep| {
13221330 switch (path_dep.flags.mode) {
13231331 .directory => {}, // TODO
1324 .contents => try config_man.addPathPost(confPathDepToCachePath(graph, &configuration, path_dep)),
1332 .contents => try man.addPathPost(confPathDepToCachePath(graph, &configuration, path_dep)),
13251333 .metadata => {}, // TODO
13261334 }
1327 }
1335 };
13281336
13291337 // If it is poisoned, there is no point in moving it to cached
13301338 // location. Just leave it in the tmp directory.
13311339 if (configuration.poisoned) {
13321340 break :cp .{ config_tmp_path, null };
13331341 } else {
1334 const digest = config_man.final();
1342 const man = config_man.?;
1343 const digest = man.final();
13351344 const final_path: Path = .{
13361345 .root_dir = graph.local_cache_root,
13371346 .sub_path = try arena.print("c/{s}", .{&digest}),
......@@ -1362,8 +1371,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13621371 config_tmp_path, final_path, e,
13631372 });
13641373 };
1365 config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});
1366 break :cp .{ final_path, config_man.toOwnedLock() };
1374 man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});
1375 break :cp .{ final_path, man.toOwnedLock() };
13671376 }
13681377 };
13691378 // Hang on to the configuration file lock until we finish loading the configuration file.
lib/compiler/Maker/ScannedConfig.zig+1
......@@ -342,6 +342,7 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
342342 \\ --build-file [file] Override path to build.zig
343343 \\ --cache-dir [path] Override path to local Zig cache directory
344344 \\ --global-cache-dir [path] Override path to global Zig cache directory
345 \\ --zig-lib=[arg] Override path to Zig lib directory
345346 \\ --seed [integer] For shuffling dependency traversal order (default: random)
346347 \\ --cache-poison[=mode] Override configuration caching behavior
347348 \\ pure (default) Avoid false positive cache hits
lib/compiler/configurer.zig+2-2
......@@ -113,8 +113,8 @@ pub fn main(init: process.Init.Minimal) !void {
113113 } else if (mem.eql(u8, arg, "--verbose")) {
114114 graph.verbose = true;
115115 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
116 graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest) orelse
117 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});
116 // Already parsed and validated by Maker.
117 graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest).?;
118118 } else if (mem.eql(u8, arg, "--search-prefix")) {
119119 try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i));
120120 } else {
lib/std/zig.zig+1-1
......@@ -1351,7 +1351,7 @@ pub const Directories = struct {
13511351 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");
13521352 if (wasi) break :d getPreopen(preopens, "/lib");
13531353 break :d findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| {
1354 fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err });
1354 fatal("unable to find zig installation directory from executable path {q}: {t}", .{ self_exe_path, err });
13551355 };
13561356 };
13571357
src/main.zig+12-3
......@@ -4932,9 +4932,18 @@ fn jitCmdInner(
49324932 else
49334933 options.release_mode;
49344934 const strip = optimize_mode != .Debug;
4935 const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
4935 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
49364936 const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
49374937
4938 // Special case: if first arg starts with --zig-lib= then it is handled here.
4939 var args_i: usize = 0;
4940 if (args.len - args_i != 0) {
4941 if (mem.cutPrefix(u8, args[args_i], "--zig-lib=")) |rest| {
4942 override_lib_dir = rest;
4943 args_i += 1;
4944 }
4945 }
4946
49384947 const cwd_path = try std.zig.getResolvedCwd(io, arena);
49394948
49404949 // This `init` calls `fatal` on error.
......@@ -4952,7 +4961,7 @@ fn jitCmdInner(
49524961 defer dirs.deinit(io);
49534962
49544963 var child_argv: std.ArrayList([]const u8) = .empty;
4955 try child_argv.ensureUnusedCapacity(arena, args.len + 6);
4964 try child_argv.ensureUnusedCapacity(arena, (args.len - args_i) + 6);
49564965
49574966 // We want to release all the locks before executing the child process, so we make a nice
49584967 // big block here to ensure the cleanup gets run when we extract out our argv.
......@@ -5064,7 +5073,7 @@ fn jitCmdInner(
50645073 if (options.prepend_seed)
50655074 child_argv.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{randInt(io, u32)}));
50665075
5067 child_argv.appendSliceAssumeCapacity(args);
5076 child_argv.appendSliceAssumeCapacity(args[args_i..]);
50685077
50695078 if (EnvVar.ZIG_VERBOSE_CMD.isSet(environ_map)) {
50705079 const cmd: std.zig.SubprocessCommand = .{