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()...@@ -729,6 +729,8 @@ endif()
729729
730730
731set(ZIG_BUILD_ARGS731set(ZIG_BUILD_ARGS
732 "--zig-lib=${PROJECT_SOURCE_DIR}/lib"
733
732 "-Dversion-string=${RESOLVED_ZIG_VERSION}"734 "-Dversion-string=${RESOLVED_ZIG_VERSION}"
733 "-Dtarget=${ZIG_TARGET_TRIPLE}"735 "-Dtarget=${ZIG_TARGET_TRIPLE}"
734 "-Dcpu=${ZIG_TARGET_MCPU}"736 "-Dcpu=${ZIG_TARGET_MCPU}"
...@@ -791,7 +793,7 @@ set(ZIG2_WORKING_DIR "${PROJECT_SOURCE_DIR}")...@@ -791,7 +793,7 @@ set(ZIG2_WORKING_DIR "${PROJECT_SOURCE_DIR}")
791793
792add_custom_command(794add_custom_command(
793 OUTPUT "${PROJECT_BINARY_DIR}/stage3/bin/zig"795 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"
795 COMMENT "Building stage3"797 COMMENT "Building stage3"
796 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"798 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"
797)799)
cmake/install.cmake+1-1
...@@ -1,4 +1,4 @@...@@ -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}")
2execute_process(2execute_process(
3 COMMAND "${ZIG_EXECUTABLE}" ${ZIG_INSTALL_ARGS}3 COMMAND "${ZIG_EXECUTABLE}" ${ZIG_INSTALL_ARGS}
4 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"4 WORKING_DIRECTORY "${ZIG2_WORKING_DIR}"
lib/compiler/Maker.zig+39-30
...@@ -283,11 +283,11 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -283,11 +283,11 @@ pub fn main(init: process.Init.Minimal) !void {
283 cache_poison = .poisoned;283 cache_poison = .poisoned;
284 configure_argv.appendAssumeCapacity("--cache-poison=poisoned");284 configure_argv.appendAssumeCapacity("--cache-poison=poisoned");
285 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {285 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
286 // Allow the configurer process to report parse failure.286 // We have to report parse failure here otherwise we would
287 if (stringToEnum(std.Build.Graph.CachePoison, rest)) |poison| {287 // potentially get false positive cache hits for misspellings.
288 cache_poison = poison;288 cache_poison = stringToEnum(std.Build.Graph.CachePoison, rest) orelse
289 }289 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});
290 configure_argv.appendAssumeCapacity(arg);290 if (cache_poison != .pure) configure_argv.appendAssumeCapacity(arg);
291 } else if (mem.eql(u8, arg, "--verbose")) {291 } else if (mem.eql(u8, arg, "--verbose")) {
292 // Intentionally is added both to make and configure but292 // Intentionally is added both to make and configure but
293 // does not go into the cache hash.293 // does not go into the cache hash.
...@@ -316,6 +316,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -316,6 +316,8 @@ pub fn main(init: process.Init.Minimal) !void {
316 try forks.append(arena, .init(rest));316 try forks.append(arena, .init(rest));
317 } else if (mem.eql(u8, arg, "--fork")) {317 } else if (mem.eql(u8, arg, "--fork")) {
318 try forks.append(arena, .init(nextArgOrFatal(args, &arg_i)));318 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", .{});
319 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {321 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
320 help_menu = true;322 help_menu = true;
321 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {323 } 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 {...@@ -981,27 +983,35 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
981 // This loop is re-evaluated when the build script exits with an indication that it983 // This loop is re-evaluated when the build script exits with an indication that it
982 // could not continue due to missing lazy dependencies.984 // could not continue due to missing lazy dependencies.
983 const configuration_path: Path, var configuration_lock: ?Cache.Lock = cp: while (true) {985 const configuration_path: Path, var configuration_lock: ?Cache.Lock = cp: while (true) {
986 build_mod.deps.clearRetainingCapacity();
987 deps_mod.deps.clearRetainingCapacity();
988
984 // Cache lookup for configure options. If we get a match, we can skip989 // Cache lookup for configure options. If we get a match, we can skip
985 // execution of the configure script. If not, we get the file path to pass990 // execution of the configure script. If not, we get the file path to pass
986 // to the configure process.991 // to the configure process.
987 //992 //
988 // In the hot path, we only check this cache, which means that also993 // In the hot path, we only check this cache, which means that also
989 // configure source files need to go in here.994 // configure source files need to go in here.
990 var config_man = graph.cache.obtain();995 var config_man_allocation: Cache.Manifest = undefined;
991 defer config_man.deinit();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|1000 for (options.cached_passthru_configure) |i|
994 config_man.hash.addBytes(configure_argv[i]);1001 config_man_allocation.hash.addBytes(configure_argv[i]);
9951002
996 if (target_arch_os_abi) |triple|1003 if (target_arch_os_abi) |triple|
997 config_man.hash.addBytes(triple);1004 config_man_allocation.hash.addBytes(triple);
9981005
999 // Prevents a `zig build` from getting a false positive cache hit following1006 // Prevents a `zig build` from getting a false positive cache hit following
1000 // a `zig build --cache-poison=ignored`.1007 // a `zig build --cache-poison=ignored`.
1001 config_man.hash.add(options.cache_poison == .ignored);1008 config_man_allocation.hash.add(options.cache_poison == .ignored);
10021009
1003 build_mod.deps.clearRetainingCapacity();1010 break :m &config_man_allocation;
1004 deps_mod.deps.clearRetainingCapacity();1011 },
1012 .poisoned => null,
1013 };
1014 defer if (config_man) |man| man.deinit();
10051015
1006 // We want to release all the locks before executing the child process, so we make a nice1016 // We want to release all the locks before executing the child process, so we make a nice
1007 // big block here to ensure the cleanup gets run when we extract out our argv.1017 // 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 {...@@ -1217,26 +1227,24 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1217 const compile_prog_node = options.parent_progress_node.start("Compile Configure Script", 0);1227 const compile_prog_node = options.parent_progress_node.start("Compile Configure Script", 0);
1218 defer compile_prog_node.end();1228 defer compile_prog_node.end();
12191229
1220 switch (options.cache_poison) {1230 if (config_man) |man| {
1221 .pure, .disallowed, .ignored => if (try config_man.hit(compile_prog_node)) {1231 if (try man.hit(compile_prog_node)) {
1222 const digest = config_man.final();1232 const digest = man.final();
1223 break :cp .{1233 break :cp .{
1224 .{1234 .{
1225 .root_dir = graph.local_cache_root,1235 .root_dir = graph.local_cache_root,
1226 .sub_path = try arena.print("c/{s}", .{&digest}),1236 .sub_path = try arena.print("c/{s}", .{&digest}),
1227 },1237 },
1228 config_man.toOwnedLock(),1238 man.toOwnedLock(),
1229 };1239 };
1230 },1240 }
1231 .poisoned => {}, // Don't bother checking for cache hit.
1232 }1241 }
1233
1234 const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{1242 const configure_exe_path: Path = if (std.zig.buildExeSubprocess(gpa, io, .{
1235 .argv = build_configurer_argv.items,1243 .argv = build_configurer_argv.items,
1236 .cache_root = graph.local_cache_root,1244 .cache_root = graph.local_cache_root,
1237 .root_name = configurer_exe_name,1245 .root_name = configurer_exe_name,
1238 .environ_map = &graph.environ_map,1246 .environ_map = &graph.environ_map,
1239 .cache_manifest = &config_man,1247 .cache_manifest = config_man,
1240 .arch_os_abi = target_arch_os_abi,1248 .arch_os_abi = target_arch_os_abi,
1241 .progress_node = compile_prog_node,1249 .progress_node = compile_prog_node,
1242 .skip_log_cmdline_on_compile_errors = !graph.verbose,1250 .skip_log_cmdline_on_compile_errors = !graph.verbose,
...@@ -1318,20 +1326,21 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1318,20 +1326,21 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1318 continue :cp;1326 continue :cp;
1319 }1327 }
13201328
1321 for (configuration.path_deps) |path_dep| {1329 if (config_man) |man| for (configuration.path_deps) |path_dep| {
1322 switch (path_dep.flags.mode) {1330 switch (path_dep.flags.mode) {
1323 .directory => {}, // TODO1331 .directory => {}, // TODO
1324 .contents => try config_man.addPathPost(confPathDepToCachePath(graph, &configuration, path_dep)),1332 .contents => try man.addPathPost(confPathDepToCachePath(graph, &configuration, path_dep)),
1325 .metadata => {}, // TODO1333 .metadata => {}, // TODO
1326 }1334 }
1327 }1335 };
13281336
1329 // If it is poisoned, there is no point in moving it to cached1337 // If it is poisoned, there is no point in moving it to cached
1330 // location. Just leave it in the tmp directory.1338 // location. Just leave it in the tmp directory.
1331 if (configuration.poisoned) {1339 if (configuration.poisoned) {
1332 break :cp .{ config_tmp_path, null };1340 break :cp .{ config_tmp_path, null };
1333 } else {1341 } else {
1334 const digest = config_man.final();1342 const man = config_man.?;
1343 const digest = man.final();
1335 const final_path: Path = .{1344 const final_path: Path = .{
1336 .root_dir = graph.local_cache_root,1345 .root_dir = graph.local_cache_root,
1337 .sub_path = try arena.print("c/{s}", .{&digest}),1346 .sub_path = try arena.print("c/{s}", .{&digest}),
...@@ -1362,8 +1371,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1362,8 +1371,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1362 config_tmp_path, final_path, e,1371 config_tmp_path, final_path, e,
1363 });1372 });
1364 };1373 };
1365 config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});1374 man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});
1366 break :cp .{ final_path, config_man.toOwnedLock() };1375 break :cp .{ final_path, man.toOwnedLock() };
1367 }1376 }
1368 };1377 };
1369 // Hang on to the configuration file lock until we finish loading the configuration file.1378 // 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 {...@@ -342,6 +342,7 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
342 \\ --build-file [file] Override path to build.zig342 \\ --build-file [file] Override path to build.zig
343 \\ --cache-dir [path] Override path to local Zig cache directory343 \\ --cache-dir [path] Override path to local Zig cache directory
344 \\ --global-cache-dir [path] Override path to global Zig cache directory344 \\ --global-cache-dir [path] Override path to global Zig cache directory
345 \\ --zig-lib=[arg] Override path to Zig lib directory
345 \\ --seed [integer] For shuffling dependency traversal order (default: random)346 \\ --seed [integer] For shuffling dependency traversal order (default: random)
346 \\ --cache-poison[=mode] Override configuration caching behavior347 \\ --cache-poison[=mode] Override configuration caching behavior
347 \\ pure (default) Avoid false positive cache hits348 \\ 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 {...@@ -113,8 +113,8 @@ pub fn main(init: process.Init.Minimal) !void {
113 } else if (mem.eql(u8, arg, "--verbose")) {113 } else if (mem.eql(u8, arg, "--verbose")) {
114 graph.verbose = true;114 graph.verbose = true;
115 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {115 } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
116 graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest) orelse116 // Already parsed and validated by Maker.
117 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});117 graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest).?;
118 } else if (mem.eql(u8, arg, "--search-prefix")) {118 } else if (mem.eql(u8, arg, "--search-prefix")) {
119 try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i));119 try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i));
120 } else {120 } else {
lib/std/zig.zig+1-1
...@@ -1351,7 +1351,7 @@ pub const Directories = struct {...@@ -1351,7 +1351,7 @@ pub const Directories = struct {
1351 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");1351 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");
1352 if (wasi) break :d getPreopen(preopens, "/lib");1352 if (wasi) break :d getPreopen(preopens, "/lib");
1353 break :d findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| {1353 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 });
1355 };1355 };
1356 };1356 };
13571357
src/main.zig+12-3
...@@ -4932,9 +4932,18 @@ fn jitCmdInner(...@@ -4932,9 +4932,18 @@ fn jitCmdInner(
4932 else4932 else
4933 options.release_mode;4933 options.release_mode;
4934 const strip = optimize_mode != .Debug;4934 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);
4936 const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);4936 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
4938 const cwd_path = try std.zig.getResolvedCwd(io, arena);4947 const cwd_path = try std.zig.getResolvedCwd(io, arena);
49394948
4940 // This `init` calls `fatal` on error.4949 // This `init` calls `fatal` on error.
...@@ -4952,7 +4961,7 @@ fn jitCmdInner(...@@ -4952,7 +4961,7 @@ fn jitCmdInner(
4952 defer dirs.deinit(io);4961 defer dirs.deinit(io);
49534962
4954 var child_argv: std.ArrayList([]const u8) = .empty;4963 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
4957 // We want to release all the locks before executing the child process, so we make a nice4966 // We want to release all the locks before executing the child process, so we make a nice
4958 // big block here to ensure the cleanup gets run when we extract out our argv.4967 // big block here to ensure the cleanup gets run when we extract out our argv.
...@@ -5064,7 +5073,7 @@ fn jitCmdInner(...@@ -5064,7 +5073,7 @@ fn jitCmdInner(
5064 if (options.prepend_seed)5073 if (options.prepend_seed)
5065 child_argv.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{randInt(io, u32)}));5074 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
5069 if (EnvVar.ZIG_VERBOSE_CMD.isSet(environ_map)) {5078 if (EnvVar.ZIG_VERBOSE_CMD.isSet(environ_map)) {
5070 const cmd: std.zig.SubprocessCommand = .{5079 const cmd: std.zig.SubprocessCommand = .{