authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-12 22:42:34-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:33-07:00
loge10cbf08eeed53561b5efe87a8cae0d7827f7301
tree066a94f8082b1152a2b1c9aba1ee5fc6b75b17a3
parent4001724b4d5a694188481b144efe5e40c646194a

configure/make phase process separation sketch

`zig build` CLI kicks off async task to compile optimized make runner executable, does fetch, compiles configure process in debug mode, then checks cache for the CLI options that affect configuration only. On hit, skips building/running the configure script. On miss, runs it, saves result in cache. The cached artifact is a "configuration" file - a serialized build step graph, which also includes unlazy package dependencies and additional file system dependencies. Next, awaits task for compiling optimized make runner executable, passes configuration file to it. Make runner is responsible for the CLI after that point. For the use case of detecting when `git describe` needs to be rerun, we can allow the configure process to manually add a file system mtime dependencies, in this case it would be on `.git/index` and `.git/HEAD`. This will enable two optimizations: 1. The bulk of the build system will not be rebuilt when user changes their configure script. 2. The user logic can be completely bypassed when the CLI options provided do not affect the configure phase - even if they affect the make phase. Remaining tasks in the branch: * some stuff in `zig build` CLI is `@panic("TODO")`. * configure runner needs to implement serialization of build graph using std.zig.Configuration * build runner needs to be transformed into make runner, consuming configuration file as input and deserializing the step graph. * introduce depending only on a file's metadata and *not* its contents into the cache system, and add a std.Build API for using it.

8 files changed, 715 insertions(+), 137 deletions(-)

lib/compiler/configure_runner.zig created+336
......@@ -0,0 +1,336 @@
1const builtin = @import("builtin");
2
3const std = @import("std");
4const Io = std.Io;
5const assert = std.debug.assert;
6const fmt = std.fmt;
7const mem = std.mem;
8const process = std.process;
9const File = std.Io.File;
10const Step = std.Build.Step;
11const Watch = std.Build.Watch;
12const WebServer = std.Build.WebServer;
13const Allocator = std.mem.Allocator;
14const fatal = std.process.fatal;
15const Writer = std.Io.Writer;
16const Color = std.zig.Color;
17
18pub const root = @import("@build");
19pub const dependencies = @import("@dependencies");
20
21pub const std_options: std.Options = .{
22 .side_channels_mitigations = .none,
23 .http_disable_tls = true,
24};
25
26pub fn main(init: process.Init.Minimal) !void {
27 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
28 // always the case. So, we do need a true gpa for some things.
29 var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;
30 defer _ = debug_gpa_state.deinit();
31 const gpa = debug_gpa_state.allocator();
32
33 var threaded: std.Io.Threaded = .init(gpa, .{
34 .environ = init.environ,
35 .argv0 = .init(init.args),
36 });
37 defer threaded.deinit();
38 const io = threaded.io();
39
40 // ...but we'll back our arena by `std.heap.page_allocator` for efficiency.
41 var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
42 defer arena_allocator.deinit();
43 const arena = arena_allocator.allocator();
44
45 const args = try init.args.toSlice(arena);
46
47 // skip my own exe name
48 var arg_idx: usize = 1;
49
50 const zig_exe = nextArg(args, &arg_idx) orelse fatal("missing zig compiler path", .{});
51 const zig_lib_dir = nextArg(args, &arg_idx) orelse fatal("missing zig lib directory path", .{});
52 const build_root = nextArg(args, &arg_idx) orelse fatal("missing build root directory path", .{});
53 const cache_root = nextArg(args, &arg_idx) orelse fatal("missing cache root directory path", .{});
54 const global_cache_root = nextArg(args, &arg_idx) orelse fatal("missing global cache root directory path", .{});
55
56 const cwd: Io.Dir = .cwd();
57
58 const zig_lib_directory: std.Build.Cache.Directory = .{
59 .path = zig_lib_dir,
60 .handle = try cwd.openDir(io, zig_lib_dir, .{}),
61 };
62
63 const build_root_directory: std.Build.Cache.Directory = .{
64 .path = build_root,
65 .handle = try cwd.openDir(io, build_root, .{}),
66 };
67
68 const local_cache_directory: std.Build.Cache.Directory = .{
69 .path = cache_root,
70 .handle = try cwd.createDirPathOpen(io, cache_root, .{}),
71 };
72
73 const global_cache_directory: std.Build.Cache.Directory = .{
74 .path = global_cache_root,
75 .handle = try cwd.createDirPathOpen(io, global_cache_root, .{}),
76 };
77
78 var graph: std.Build.Graph = .{
79 .io = io,
80 .arena = arena,
81 .cache = .{
82 .io = io,
83 .gpa = gpa,
84 .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
85 .cwd = try process.currentPathAlloc(io, arena),
86 },
87 .zig_exe = zig_exe,
88 .environ_map = try init.environ.createMap(arena),
89 .global_cache_root = global_cache_directory,
90 .zig_lib_directory = zig_lib_directory,
91 .host = .{
92 .query = .{},
93 .result = try std.zig.system.resolveTargetQuery(io, .{}),
94 },
95 .time_report = false,
96 };
97
98 graph.cache.addPrefix(.{ .path = null, .handle = cwd });
99 graph.cache.addPrefix(build_root_directory);
100 graph.cache.addPrefix(local_cache_directory);
101 graph.cache.addPrefix(global_cache_directory);
102 graph.cache.hash.addBytes(builtin.zig_version_string);
103
104 const builder = try std.Build.create(
105 &graph,
106 build_root_directory,
107 local_cache_directory,
108 dependencies.root_deps,
109 );
110
111 var error_style: ErrorStyle = .verbose;
112 var multiline_errors: MultilineErrors = .indent;
113 var color: Color = .auto;
114
115 if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
116 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
117 error_style = style;
118 }
119 }
120
121 if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| {
122 if (std.meta.stringToEnum(MultilineErrors, str)) |style| {
123 multiline_errors = style;
124 }
125 }
126
127 while (nextArg(args, &arg_idx)) |arg| {
128 if (mem.cutPrefix(u8, arg, "-D")) |option_contents| {
129 if (option_contents.len == 0)
130 fatalWithHint("expected option name after '-D'", .{});
131 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
132 const option_name = option_contents[0..name_end];
133 const option_value = option_contents[name_end + 1 ..];
134 if (try builder.addUserInputOption(option_name, option_value))
135 fatal(" access the help menu with 'zig build -h'", .{});
136 } else {
137 if (try builder.addUserInputFlag(option_contents))
138 fatal(" access the help menu with 'zig build -h'", .{});
139 }
140 } else if (mem.eql(u8, arg, "--verbose")) {
141 builder.verbose = true;
142 } else if (mem.startsWith(u8, arg, "-fsys=")) {
143 const name = arg["-fsys=".len..];
144 graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM");
145 } else if (mem.startsWith(u8, arg, "-fno-sys=")) {
146 const name = arg["-fno-sys=".len..];
147 graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM");
148 } else if (mem.eql(u8, arg, "--release")) {
149 builder.release_mode = .any;
150 } else if (mem.startsWith(u8, arg, "--release=")) {
151 const text = arg["--release=".len..];
152 builder.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {
153 fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{
154 arg, text,
155 });
156 };
157 } else if (mem.eql(u8, arg, "--search-prefix")) {
158 const search_prefix = nextArgOrFatal(args, &arg_idx);
159 builder.addSearchPrefix(search_prefix);
160 } else if (mem.eql(u8, arg, "--libc")) {
161 builder.libc_file = nextArgOrFatal(args, &arg_idx);
162 } else if (mem.eql(u8, arg, "--color")) {
163 const next_arg = nextArg(args, &arg_idx) orelse
164 fatalWithHint("expected [auto|on|off] after '{s}'", .{arg});
165 color = std.meta.stringToEnum(Color, next_arg) orelse {
166 fatalWithHint("expected [auto|on|off] after '{s}', found '{s}'", .{
167 arg, next_arg,
168 });
169 };
170 } else if (mem.eql(u8, arg, "--error-style")) {
171 const next_arg = nextArg(args, &arg_idx) orelse
172 fatalWithHint("expected style after '{s}'", .{arg});
173 error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse {
174 fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
175 };
176 } else if (mem.eql(u8, arg, "--multiline-errors")) {
177 const next_arg = nextArg(args, &arg_idx) orelse
178 fatalWithHint("expected style after '{s}'", .{arg});
179 multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse {
180 fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
181 };
182 } else if (mem.eql(u8, arg, "--seed")) {
183 const next_arg = nextArg(args, &arg_idx) orelse
184 fatalWithHint("expected u32 after '{s}'", .{arg});
185 graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
186 fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{
187 next_arg, @errorName(err),
188 });
189 };
190 } else if (mem.eql(u8, arg, "--build-id")) {
191 builder.build_id = .fast;
192 } else if (mem.startsWith(u8, arg, "--build-id=")) {
193 const style = arg["--build-id=".len..];
194 builder.build_id = std.zig.BuildId.parse(style) catch |err| {
195 fatal("unable to parse --build-id style '{s}': {s}", .{
196 style, @errorName(err),
197 });
198 };
199 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
200 builder.debug_pkg_config = true;
201 } else if (mem.eql(u8, arg, "--debug-rt")) {
202 graph.debug_compiler_runtime_libs = true;
203 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
204 builder.debug_compile_errors = true;
205 } else if (mem.eql(u8, arg, "--debug-incremental")) {
206 builder.debug_incremental = true;
207 } else if (mem.eql(u8, arg, "--system")) {
208 // The usage text shows another argument after this parameter
209 // but it is handled by the parent process. The build runner
210 // only sees this flag.
211 graph.system_package_mode = true;
212 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
213 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
214 builder.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
215 } else if (mem.eql(u8, arg, "--verbose-link")) {
216 builder.verbose_link = true;
217 } else if (mem.eql(u8, arg, "--verbose-air")) {
218 builder.verbose_air = true;
219 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
220 builder.verbose_llvm_ir = "-";
221 } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
222 builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];
223 } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) {
224 builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
225 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
226 builder.verbose_cimport = true;
227 } else if (mem.eql(u8, arg, "--verbose-cc")) {
228 builder.verbose_cc = true;
229 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
230 builder.verbose_llvm_cpu_features = true;
231 } else if (mem.eql(u8, arg, "-fincremental")) {
232 graph.incremental = true;
233 } else if (mem.eql(u8, arg, "-fno-incremental")) {
234 graph.incremental = false;
235 } else if (mem.eql(u8, arg, "-fwine")) {
236 builder.enable_wine = true;
237 } else if (mem.eql(u8, arg, "-fno-wine")) {
238 builder.enable_wine = false;
239 } else if (mem.eql(u8, arg, "-fqemu")) {
240 builder.enable_qemu = true;
241 } else if (mem.eql(u8, arg, "-fno-qemu")) {
242 builder.enable_qemu = false;
243 } else if (mem.eql(u8, arg, "-fwasmtime")) {
244 builder.enable_wasmtime = true;
245 } else if (mem.eql(u8, arg, "-fno-wasmtime")) {
246 builder.enable_wasmtime = false;
247 } else if (mem.eql(u8, arg, "-frosetta")) {
248 builder.enable_rosetta = true;
249 } else if (mem.eql(u8, arg, "-fno-rosetta")) {
250 builder.enable_rosetta = false;
251 } else if (mem.eql(u8, arg, "-fdarling")) {
252 builder.enable_darling = true;
253 } else if (mem.eql(u8, arg, "-fno-darling")) {
254 builder.enable_darling = false;
255 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
256 graph.allow_so_scripts = true;
257 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
258 graph.allow_so_scripts = false;
259 } else if (mem.eql(u8, arg, "-freference-trace")) {
260 builder.reference_trace = 256;
261 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
262 const num = arg["-freference-trace=".len..];
263 builder.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
264 std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
265 process.exit(1);
266 };
267 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
268 builder.reference_trace = null;
269 } else if (mem.cutPrefix(u8, arg, "-j")) |text| {
270 const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
271 fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
272 if (n < 1) fatal("number of jobs must be at least 1", .{});
273 threaded.setAsyncLimit(.limited(n));
274 } else if (mem.eql(u8, arg, "--")) {
275 builder.args = argsRest(args, arg_idx);
276 break;
277 } else {
278 fatalWithHint("unrecognized argument: '{s}'", .{arg});
279 }
280 }
281
282 const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map);
283 const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map);
284
285 graph.stderr_mode = switch (color) {
286 .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE),
287 .on => .escape_codes,
288 .off => .no_color,
289 };
290
291 try builder.runBuild(root);
292}
293
294fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
295 if (idx.* >= args.len) return null;
296 defer idx.* += 1;
297 return args[idx.*];
298}
299
300fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
301 return nextArg(args, idx) orelse {
302 std.debug.print("expected argument after '{s}'\n access the help menu with 'zig build -h'\n", .{args[idx.* - 1]});
303 process.exit(1);
304 };
305}
306
307fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 {
308 if (idx >= args.len) return null;
309 return args[idx..];
310}
311
312const ErrorStyle = enum {
313 verbose,
314 minimal,
315 verbose_clear,
316 minimal_clear,
317 fn verboseContext(s: ErrorStyle) bool {
318 return switch (s) {
319 .verbose, .verbose_clear => true,
320 .minimal, .minimal_clear => false,
321 };
322 }
323 fn clearOnUpdate(s: ErrorStyle) bool {
324 return switch (s) {
325 .verbose, .minimal => false,
326 .verbose_clear, .minimal_clear => true,
327 };
328 }
329};
330const MultilineErrors = enum { indent, newline, none };
331const Summary = enum { all, new, failures, line, none };
332
333fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
334 std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args);
335 process.exit(1);
336}
lib/std/Build/Cache.zig+6
......@@ -1024,6 +1024,12 @@ pub const Manifest = struct {
10241024 try self.populateFileHash(gop.key_ptr);
10251025 }
10261026
1027 pub fn addPathPost(man: *Manifest, path: Path) !void {
1028 _ = man;
1029 _ = path;
1030 @panic("TODO");
1031 }
1032
10271033 /// Like `addFilePost` but when the file contents have already been loaded from disk.
10281034 pub fn addFilePostContents(
10291035 self: *Manifest,
lib/std/zig.zig+2
......@@ -11,6 +11,8 @@ const Writer = std.Io.Writer;
1111
1212const tokenizer = @import("zig/tokenizer.zig");
1313
14/// The serialized output of configure phase ingested by make phase.
15pub const Configuration = @import("zig/Configuration.zig");
1416pub const ErrorBundle = @import("zig/ErrorBundle.zig");
1517pub const Server = @import("zig/Server.zig");
1618pub const Client = @import("zig/Client.zig");
lib/std/zig/Configuration.zig created+83
......@@ -0,0 +1,83 @@
1const Configuration = @This();
2
3const std = @import("../std.zig");
4const Io = std.Io;
5const Allocator = std.mem.Allocator;
6
7string_bytes: []u8,
8steps: []Step,
9path_deps_base: []Path.Base,
10path_deps_sub: []String,
11unlazy_deps: []String,
12
13pub const Header = extern struct {
14 string_bytes_len: u32,
15 steps_len: u32,
16 path_deps_len: u32,
17 unlazy_deps_len: u32,
18};
19
20pub const Step = extern struct {
21 name: String,
22};
23
24pub const Path = extern struct {
25 base: Base,
26 sub: String,
27
28 pub const Base = enum(u8) {
29 cwd,
30 global_cache,
31 local_cache,
32 build_root,
33 };
34
35 pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path {
36 _ = c;
37 _ = arena;
38 _ = path;
39 @panic("TODO");
40 }
41};
42
43pub const String = enum(u32) {
44 _,
45
46 pub fn slice(index: String, c: *const Configuration) [:0]const u8 {
47 const start_slice = c.string_bytes[@intFromEnum(index)..];
48 return start_slice[0..std.mem.indexOfScalar(u8, start_slice, 0).? :0];
49 }
50};
51
52pub const LoadError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream};
53
54pub fn load(arena: Allocator, io: Io, file: Io.File) LoadError!Configuration {
55 var buffer: [2000]u8 = undefined;
56 var fr = file.reader(io, &buffer);
57 const header = fr.interface.takeStruct(Header, .little) catch |err| switch (err) {
58 error.ReadFailed => return fr.err.?,
59 else => |e| return e,
60 };
61
62 var result: Configuration = .{
63 .string_bytes = try arena.alloc(u8, header.string_bytes_len),
64 .steps = try arena.alloc(Step, header.steps_len),
65 .path_deps_sub = try arena.alloc(String, header.path_deps_len),
66 .path_deps_base = try arena.alloc(Path.Base, header.path_deps_len),
67 .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len),
68 };
69
70 var vecs = [_][]u8{
71 result.string_bytes,
72 @ptrCast(result.steps),
73 @ptrCast(result.path_deps_base),
74 @ptrCast(result.path_deps_sub),
75 @ptrCast(result.unlazy_deps),
76 };
77 fr.interface.readVecAll(&vecs) catch |err| switch (err) {
78 error.ReadFailed => return fr.err.?,
79 else => |e| return e,
80 };
81
82 return result;
83}
lib/std/zig/LibCInstallation.zig+16-2
......@@ -13,6 +13,7 @@ const Target = std.Target;
1313const fs = std.fs;
1414const Allocator = std.mem.Allocator;
1515const Path = std.Build.Cache.Path;
16const Cache = std.Build.Cache;
1617const log = std.log.scoped(.libc_installation);
1718const Environ = std.process.Environ;
1819
......@@ -990,7 +991,7 @@ pub fn resolveCrtPaths(
990991 target: *const std.Target,
991992) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths {
992993 const crt_dir_path: Path = .{
993 .root_dir = std.Build.Cache.Directory.cwd(),
994 .root_dir = Cache.Directory.cwd(),
994995 .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir,
995996 };
996997 switch (target.os.tag) {
......@@ -1016,7 +1017,7 @@ pub fn resolveCrtPaths(
10161017 },
10171018 .haiku, .serenity => {
10181019 const gcc_dir_path: Path = .{
1019 .root_dir = std.Build.Cache.Directory.cwd(),
1020 .root_dir = Cache.Directory.cwd(),
10201021 .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir,
10211022 };
10221023 return .{
......@@ -1038,3 +1039,16 @@ pub fn resolveCrtPaths(
10381039 },
10391040 }
10401041}
1042
1043pub fn addToHash(opt_lci: ?*const LibCInstallation, hh: *Cache.HashHelper, abi: std.Target.Abi) void {
1044 const lci = opt_lci orelse return hh.add(false);
1045 hh.add(true);
1046 hh.addOptionalBytes(lci.crt_dir);
1047 switch (abi) {
1048 .msvc, .itanium => {
1049 hh.addOptionalBytes(lci.msvc_lib_dir);
1050 hh.addOptionalBytes(lci.kernel32_lib_dir);
1051 },
1052 else => {},
1053 }
1054}
src/Compilation.zig+2-12
......@@ -752,13 +752,10 @@ pub const Directories = struct {
752752 else => []const u8,
753753 },
754754 environ_map: *const std.process.Environ.Map,
755 cwd: []const u8,
755756 ) Directories {
756757 const wasi = builtin.target.os.tag == .wasi;
757758
758 const cwd = introspect.getResolvedCwd(io, arena) catch |err| {
759 fatal("unable to get cwd: {t}", .{err});
760 };
761
762759 const zig_lib: Cache.Directory = d: {
763760 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");
764761 if (wasi) break :d getPreopen(preopens, "/lib");
......@@ -3528,14 +3525,7 @@ fn addNonIncrementalStuffToCacheManifest(
35283525 man.hash.addListOfBytes(opts.rpath_list);
35293526 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
35303527 if (comp.config.link_libc) {
3531 man.hash.add(comp.libc_installation != null);
3532 if (comp.libc_installation) |libc_installation| {
3533 man.hash.addOptionalBytes(libc_installation.crt_dir);
3534 if (target.abi == .msvc or target.abi == .itanium) {
3535 man.hash.addOptionalBytes(libc_installation.msvc_lib_dir);
3536 man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir);
3537 }
3538 }
3528 LibCInstallation.addToHash(comp.libc_installation, &man.hash, target.abi);
35393529 man.hash.addOptionalBytes(target.dynamic_linker.get());
35403530 }
35413531 man.hash.add(opts.repro);
src/main.zig+266-123
......@@ -3166,6 +3166,8 @@ fn buildOutputType(
31663166 else => process.executablePathAlloc(io, arena) catch |err| fatal("unable to find zig self exe path: {t}", .{err}),
31673167 };
31683168
3169 const cwd_path = try introspect.getResolvedCwd(io, arena);
3170
31693171 // This `init` calls `fatal` on error.
31703172 var dirs: Compilation.Directories = .init(
31713173 arena,
......@@ -3182,6 +3184,7 @@ fn buildOutputType(
31823184 preopens,
31833185 self_exe_path,
31843186 environ_map,
3187 cwd_path,
31853188 );
31863189 defer dirs.deinit(io);
31873190
......@@ -4936,16 +4939,21 @@ test sanitizeExampleName {
49364939 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
49374940}
49384941
4939fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, environ_map: *process.Environ.Map) !void {
4940 dev.check(.build_command);
4941
4942fn cmdBuild(
4943 gpa: Allocator,
4944 arena: Allocator,
4945 io: Io,
4946 args: []const []const u8,
4947 environ_map: *process.Environ.Map,
4948) !void {
49424949 var build_file: ?[]const u8 = null;
49434950 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
49444951 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
49454952 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
49464953 var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map);
4947 var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);
4948 var child_argv: std.ArrayList([]const u8) = .empty;
4954 var override_make_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);
4955 var configure_argv: std.ArrayList([]const u8) = .empty;
4956 var make_argv: std.ArrayList([]const u8) = .empty;
49494957 var forks: std.ArrayList(Fork) = .empty;
49504958 var reference_trace: ?u32 = null;
49514959 var debug_compile_errors = false;
......@@ -4965,46 +4973,32 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
49654973 var debug_target: ?[]const u8 = null;
49664974 var debug_libc_paths_file: ?[]const u8 = null;
49674975
4968 const argv_index_exe = child_argv.items.len;
4969 _ = try child_argv.addOne(arena);
4976 const argv_index_exe = configure_argv.items.len;
4977 _ = try configure_argv.addOne(arena);
49704978
49714979 const self_exe_path = try process.executablePathAlloc(io, arena);
4972 try child_argv.append(arena, self_exe_path);
4980 try configure_argv.append(arena, self_exe_path);
49734981
4974 const argv_index_zig_lib_dir = child_argv.items.len;
4975 _ = try child_argv.addOne(arena);
4982 const argv_index_zig_lib_dir = configure_argv.items.len;
4983 _ = try configure_argv.addOne(arena);
49764984
4977 const argv_index_build_file = child_argv.items.len;
4978 _ = try child_argv.addOne(arena);
4985 const argv_index_build_file = configure_argv.items.len;
4986 _ = try configure_argv.addOne(arena);
49794987
4980 const argv_index_cache_dir = child_argv.items.len;
4981 _ = try child_argv.addOne(arena);
4988 const argv_index_cache_dir = configure_argv.items.len;
4989 _ = try configure_argv.addOne(arena);
49824990
4983 const argv_index_global_cache_dir = child_argv.items.len;
4984 _ = try child_argv.addOne(arena);
4991 const argv_index_global_cache_dir = configure_argv.items.len;
4992 _ = try configure_argv.addOne(arena);
49854993
4986 try child_argv.appendSlice(arena, &.{
4994 try configure_argv.appendSlice(arena, &.{
49874995 "--seed",
49884996 try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),
49894997 });
4990 const argv_index_seed = child_argv.items.len - 1;
4991
4992 // This parent process needs a way to obtain results from the configuration
4993 // phase of the child process. In the future, the make phase will be
4994 // executed in a separate process than the configure phase, and we can then
4995 // use stdout from the configuration phase for this purpose.
4996 //
4997 // However, currently, both phases are in the same process, and Run Step
4998 // provides API for making the runned subprocesses inherit stdout and stderr
4999 // which means these streams are not available for passing metadata back
5000 // to the parent.
5001 //
5002 // Until make and configure phases are separated into different processes,
5003 // the strategy is to choose a temporary file name ahead of time, and then
5004 // read this file in the parent to obtain the results, in the case the child
5005 // exits with code 3.
5006 const results_tmp_file_nonce = std.fmt.hex(randInt(io, u64));
5007 try child_argv.append(arena, "-Z" ++ results_tmp_file_nonce);
4998 const argv_index_seed = configure_argv.items.len - 1;
4999
5000 const argv_index_configuration_file = make_argv.items.len;
5001 _ = try make_argv.addOne(arena);
50085002
50095003 var color: Color = .auto;
50105004 var n_jobs: ?u32 = null;
......@@ -5027,7 +5021,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
50275021 } else if (mem.eql(u8, arg, "--build-runner")) {
50285022 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
50295023 i += 1;
5030 override_build_runner = args[i];
5024 override_make_runner = args[i];
50315025 continue;
50325026 } else if (mem.eql(u8, arg, "--cache-dir")) {
50335027 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
......@@ -5071,7 +5065,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
50715065 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
50725066 i += 1;
50735067 system_pkg_dir_path = args[i];
5074 try child_argv.append(arena, "--system");
5068 try configure_argv.append(arena, "--system");
50755069 continue;
50765070 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
50775071 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
......@@ -5081,7 +5075,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
50815075 reference_trace = null;
50825076 } else if (mem.eql(u8, arg, "--debug-log")) {
50835077 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5084 try child_argv.appendSlice(arena, args[i .. i + 2]);
5078 try make_argv.appendSlice(arena, args[i .. i + 2]);
50855079 i += 1;
50865080 try addDebugLog(arena, args[i]);
50875081 continue;
......@@ -5131,7 +5125,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
51315125 color = std.meta.stringToEnum(Color, args[i]) orelse {
51325126 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
51335127 };
5134 try child_argv.appendSlice(arena, &.{ arg, args[i] });
5128 try configure_argv.appendSlice(arena, &.{ arg, args[i] });
51355129 continue;
51365130 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
51375131 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
......@@ -5146,25 +5140,85 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
51465140 } else if (mem.eql(u8, arg, "--seed")) {
51475141 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
51485142 i += 1;
5149 child_argv.items[argv_index_seed] = args[i];
5143 configure_argv.items[argv_index_seed] = args[i];
51505144 continue;
51515145 } else if (mem.eql(u8, arg, "--")) {
51525146 // The rest of the args are supposed to get passed onto
51535147 // build runner's `build.args`
5154 try child_argv.appendSlice(arena, args[i..]);
5148 try configure_argv.appendSlice(arena, args[i..]);
51555149 break;
51565150 }
51575151 }
5158 try child_argv.append(arena, arg);
5152 try make_argv.append(arena, arg);
51595153 }
51605154 }
51615155
51625156 const root_prog_node = std.Progress.start(io, .{
51635157 .disable_printing = (color == .off),
5164 .root_name = "Compile Build Script",
5158 .root_name = "",
51655159 });
51665160 defer root_prog_node.end();
51675161
5162 process.raiseFileDescriptorLimit();
5163
5164 const cwd_path = introspect.getResolvedCwd(io, arena) catch |err|
5165 fatal("failed to get current directory path: {t}", .{err});
5166
5167 const build_root = try findBuildRoot(arena, io, .{
5168 .cwd_path = cwd_path,
5169 .build_file = build_file,
5170 });
5171
5172 // This `init` calls `fatal` on error.
5173 var dirs: Compilation.Directories = .init(
5174 arena,
5175 io,
5176 override_lib_dir,
5177 override_global_cache_dir,
5178 .{ .override = path: {
5179 if (override_local_cache_dir) |d| break :path d;
5180 break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5181 } },
5182 .empty,
5183 self_exe_path,
5184 environ_map,
5185 cwd_path,
5186 );
5187 defer dirs.deinit(io);
5188
5189 const thread_limit = @min(
5190 @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
5191 std.math.maxInt(Zcu.PerThread.IdBacking),
5192 );
5193 try setThreadLimit(arena, thread_limit);
5194
5195 // Kick off an optimized compilation of the make runner.
5196 var make_runner_task = io.async(compileMakeRunner, .{ io, .{
5197 .dirs = &dirs,
5198 .optimize = .ReleaseSafe,
5199 .parent_prog_node = root_prog_node,
5200 } });
5201 defer if (make_runner_task.cancel(io)) |mr| mr.deinit(io) else |_| {};
5202
5203 // Cache lookup for configure options. If we get a match, we can skip
5204 // execution of the configure script. If not, we get the file path to pass
5205 // to the configure process.
5206 var local_cache: Cache = .{
5207 .gpa = gpa,
5208 .io = io,
5209 .manifest_dir = try dirs.local_cache.handle.createDirPathOpen(io, "h", .{}),
5210 .cwd = cwd_path,
5211 };
5212 local_cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
5213 local_cache.addPrefix(dirs.zig_lib);
5214 local_cache.addPrefix(dirs.local_cache);
5215 local_cache.addPrefix(dirs.global_cache);
5216 defer local_cache.manifest_dir.close(io);
5217
5218 var config_man = local_cache.obtain();
5219 defer config_man.deinit();
5220 config_man.hash.addBytes(build_options.version);
5221
51685222 // Normally the build runner is compiled for the host target but here is
51695223 // some code to help when debugging edits to the build runner so that you
51705224 // can make sure it compiles successfully on other targets.
......@@ -5174,6 +5228,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
51745228 const target_query = try std.Target.Query.parse(.{
51755229 .arch_os_abi = triple,
51765230 });
5231 config_man.hash.addBytes(triple);
51775232 break :t .{
51785233 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
51795234 .is_native_os = false,
......@@ -5189,49 +5244,21 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
51895244 .is_explicit_dynamic_linker = false,
51905245 };
51915246 };
5247
51925248 // Likewise, `--debug-libc` allows overriding the libc installation.
51935249 const libc_installation: ?*const LibCInstallation = lci: {
51945250 const paths_file = debug_libc_paths_file orelse break :lci null;
51955251 if (!build_options.enable_debug_extensions) unreachable;
51965252 const lci = try arena.create(LibCInstallation);
51975253 lci.* = try .parse(arena, io, paths_file, &resolved_target.result);
5254 LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi);
51985255 break :lci lci;
51995256 };
52005257
5201 process.raiseFileDescriptorLimit();
5202
5203 const cwd_path = try introspect.getResolvedCwd(io, arena);
5204 const build_root = try findBuildRoot(arena, io, .{
5205 .cwd_path = cwd_path,
5206 .build_file = build_file,
5207 });
5208
5209 // This `init` calls `fatal` on error.
5210 var dirs: Compilation.Directories = .init(
5211 arena,
5212 io,
5213 override_lib_dir,
5214 override_global_cache_dir,
5215 .{ .override = path: {
5216 if (override_local_cache_dir) |d| break :path d;
5217 break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5218 } },
5219 .empty,
5220 self_exe_path,
5221 environ_map,
5222 );
5223 defer dirs.deinit(io);
5224
5225 child_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5226 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5227 child_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5228 child_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
5229
5230 const thread_limit = @min(
5231 @max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1),
5232 std.math.maxInt(Zcu.PerThread.IdBacking),
5233 );
5234 try setThreadLimit(arena, thread_limit);
5258 configure_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5259 configure_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5260 configure_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5261 configure_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
52355262
52365263 // Dummy http client that is not actually used when fetch_command is unsupported.
52375264 // Prevents bootstrap from depending on a bunch of unnecessary stuff.
......@@ -5269,11 +5296,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
52695296
52705297 // This loop is re-evaluated when the build script exits with an indication that it
52715298 // could not continue due to missing lazy dependencies.
5272 while (true) {
5299 const configuration_path: Path = cp: while (true) {
52735300 // We want to release all the locks before executing the child process, so we make a nice
52745301 // big block here to ensure the cleanup gets run when we extract out our argv.
52755302 {
5276 const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_build_runner) |runner| .{
5303 const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_make_runner) |runner| .{
52775304 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
52785305 .root_src_path = fs.path.basename(runner),
52795306 } else .{
......@@ -5497,6 +5524,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
54975524 config,
54985525 );
54995526
5527 const compile_prog_node = root_prog_node.start("Compile Configure Script", 0);
5528 defer compile_prog_node.end();
5529
55005530 try root_mod.deps.put(arena, "@build", build_mod);
55015531
55025532 var create_diag: Compilation.CreateDiagnostic = undefined;
......@@ -5528,7 +5558,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
55285558 };
55295559 defer comp.destroy();
55305560
5531 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5561 updateModule(comp, color, compile_prog_node) catch |err| switch (err) {
55325562 error.CompileErrorsReported => process.exit(2),
55335563 else => |e| return e,
55345564 };
......@@ -5536,52 +5566,74 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
55365566 // Since incremental compilation isn't done yet, we use cache_mode = whole
55375567 // above, and thus the output file is already closed.
55385568 //try comp.makeBinFileExecutable();
5539 child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{
5540 "o",
5541 &Cache.binToHex(comp.digest.?),
5542 comp.emit_bin.?,
5543 });
5569 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);
5570 const exe_path: Path = .{
5571 .root_dir = dirs.local_cache,
5572 .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),
5573 };
5574 _ = try config_man.addFilePath(exe_path, null);
5575 configure_argv.items[argv_index_exe] = try exe_path.toString(arena);
5576
5577 if (try config_man.hit()) {
5578 const digest = config_man.final();
5579 break :cp .{
5580 .root_dir = dirs.local_cache,
5581 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5582 };
5583 }
55445584 }
55455585
55465586 if (!process.can_spawn) {
5547 const cmd = try std.mem.join(arena, " ", child_argv.items);
5587 const cmd = try std.mem.join(arena, " ", configure_argv.items);
55485588 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
55495589 }
5590
5591 const rand_int = randInt(io, u64);
5592 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
5593 const config_tmp_path: Path = .{
5594 .root_dir = dirs.local_cache,
5595 .sub_path = tmp_dir_sub_path,
5596 };
5597 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
5598 io,
5599 config_tmp_path.sub_path,
5600 .{ .read = true, .exclusive = true },
5601 );
5602 defer config_tmp_file.close(io);
5603
55505604 switch (term: {
5551 _ = try io.lockStderr(&.{}, .no_color);
5552 defer io.unlockStderr();
5605 const child_node = root_prog_node.start("Run Configure Script", 0);
5606 defer child_node.end();
55535607 var child = std.process.spawn(io, .{
5554 .argv = child_argv.items,
5555 }) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err });
5608 .argv = configure_argv.items,
5609 .stdout = .{ .file = config_tmp_file },
5610 .progress_node = child_node,
5611 }) catch |err| fatal("failed to spawn configure script {s}: {t}", .{ configure_argv.items[0], err });
55565612 defer child.kill(io);
55575613 break :term child.wait(io) catch |err|
5558 fatal("failed to wait build runner {s}: {t}", .{ child_argv.items[0], err });
5614 fatal("failed to wait configure script {s}: {t}", .{ configure_argv.items[0], err });
55595615 }) {
55605616 .exited => |code| {
5561 if (code == 0) return cleanExit(io);
5562 // Indicates that the build runner has reported compile errors
5563 // and this parent process does not need to report any further
5564 // diagnostics.
5565 if (code == 2) process.exit(2);
5566
5567 if (code == 3) {
5568 if (!dev.env.supports(.fetch_command)) process.exit(3);
5569 // Indicates the configure phase failed due to missing lazy
5570 // dependencies and stdout contains the hashes of the ones
5571 // that are missing.
5572 const s = fs.path.sep_str;
5573 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
5574 const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {
5575 fatal("unable to read results of configure phase from '{f}{s}': {t}", .{
5576 dirs.local_cache, tmp_sub_path, err,
5577 });
5578 };
5579 dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {};
5580
5581 var it = mem.splitScalar(u8, stdout, '\n');
5617 if (code != 0) {
5618 // Failure to produce the configuration file.
5619 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5620 fatal("the following configure command failed with exit code {d}:\n{s}", .{ code, cmd });
5621 }
5622 // Even though the file is designed to be sent directly to make
5623 // runner, we must load it now because:
5624 // * If it contains additional file dependencies, we need to
5625 // add them to `config_man` before obtaining the final digest.
5626 // * If it contains a set of lazy packages that need to be
5627 // fetched, we need to fetch those now and re-run configure.
5628 var configuration = std.zig.Configuration.load(arena, io, config_tmp_file) catch |err|
5629 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
5630
5631 if (configuration.unlazy_deps.len != 0) {
5632 if (!dev.env.supports(.fetch_command)) process.exit(1);
55825633 var any_errors = false;
5583 while (it.next()) |hash| {
5584 if (hash.len == 0) continue;
5634 for (configuration.unlazy_deps) |hash_string| {
5635 const hash = hash_string.slice(&configuration);
5636 assert(hash.len != 0);
55855637 if (hash.len > Package.Hash.max_len) {
55865638 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
55875639 hash.len, hash,
......@@ -5591,10 +5643,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
55915643 }
55925644 try unlazy_set.put(arena, .fromSlice(hash), {});
55935645 }
5594 if (any_errors) process.exit(3);
5646 if (any_errors) process.exit(1);
55955647 if (system_pkg_dir_path) |p| {
55965648 // In this mode, the system needs to provide these packages; they
55975649 // cannot be fetched by Zig.
5650 const s = fs.path.sep_str;
55985651 for (unlazy_set.keys()) |*hash| {
55995652 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
56005653 p, hash.toSlice(),
......@@ -5602,28 +5655,115 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
56025655 }
56035656 std.log.info("remote package fetching disabled due to --system mode", .{});
56045657 std.log.info("dependencies might be avoidable depending on build configuration", .{});
5605 process.exit(3);
5658 process.exit(1);
56065659 }
5607 continue;
5660 continue :cp;
5661 }
5662
5663 for (configuration.path_deps_base, configuration.path_deps_sub) |base, sub| {
5664 const conf_path: std.zig.Configuration.Path = .{ .base = base, .sub = sub };
5665 try config_man.addPathPost(conf_path.toCachePath(&configuration, arena));
56085666 }
56095667
5610 const cmd = try std.mem.join(arena, " ", child_argv.items);
5611 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
5668 const digest = config_man.final();
5669 const final_path: Path = .{
5670 .root_dir = dirs.local_cache,
5671 .sub_path = try std.fmt.allocPrint(arena, "o/{s}", .{&digest}),
5672 };
5673 Io.Dir.rename(
5674 config_tmp_path.root_dir.handle,
5675 config_tmp_path.sub_path,
5676 final_path.root_dir.handle,
5677 final_path.sub_path,
5678 io,
5679 ) catch |err| {
5680 fatal("failed to rename configuration file from {f} into {f}: {t}", .{
5681 config_tmp_path, final_path, err,
5682 });
5683 };
5684 config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err});
5685
5686 break :cp final_path;
56125687 },
56135688 .signal => |sig| {
5614 const cmd = try std.mem.join(arena, " ", child_argv.items);
5615 fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });
5689 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5690 fatal("the following configure command terminated with signal {t}:\n{s}", .{ sig, cmd });
56165691 },
56175692 .stopped => |sig| {
5618 const cmd = try std.mem.join(arena, " ", child_argv.items);
5693 const cmd = try std.mem.join(arena, " ", configure_argv.items);
56195694 fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd });
56205695 },
56215696 .unknown => {
5622 const cmd = try std.mem.join(arena, " ", child_argv.items);
5697 const cmd = try std.mem.join(arena, " ", configure_argv.items);
56235698 fatal("the following build command crashed:\n{s}", .{cmd});
56245699 },
56255700 }
5701 };
5702
5703 {
5704 // Release all file system locks just before running the maker process.
5705 var configuration_lock = config_man.toOwnedLock();
5706 defer configuration_lock.release(io);
5707
5708 const make_runner = make_runner_task.await(io) catch |err|
5709 fatal("failed to compile maker: {t}", .{err});
5710 defer make_runner.deinit(io);
5711
5712 make_argv.items[0] = try make_runner.exe_path.toString(arena);
5713 make_argv.items[argv_index_configuration_file] = try configuration_path.toString(arena);
5714 }
5715
5716 if (!process.can_spawn) {
5717 const cmd = try std.mem.join(arena, " ", make_argv.items);
5718 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
5719 }
5720
5721 switch (term: {
5722 _ = try io.lockStderr(&.{}, .no_color);
5723 defer io.unlockStderr();
5724 var child = std.process.spawn(io, .{
5725 .argv = make_argv.items,
5726 }) catch |err| fatal("failed to spawn maker {s}: {t}", .{ make_argv.items[0], err });
5727 defer child.kill(io);
5728 break :term child.wait(io) catch |err|
5729 fatal("failed to wait maker {s}: {t}", .{ make_argv.items[0], err });
5730 }) {
5731 .exited => |code| {
5732 if (code == 0) return cleanExit(io);
5733 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5734 fatal("the following maker command failed with exit code {d}:\n{s}", .{ code, cmd });
5735 },
5736 .signal => |sig| {
5737 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5738 fatal("the following maker command terminated with signal {t}:\n{s}", .{ sig, cmd });
5739 },
5740 else => {
5741 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5742 fatal("the following maker command crashed:\n{s}", .{cmd});
5743 },
5744 }
5745}
5746
5747const MakeRunner = struct {
5748 exe_path: Path,
5749
5750 const Options = struct {
5751 dirs: *Compilation.Directories,
5752 optimize: std.builtin.OptimizeMode,
5753 parent_prog_node: std.Progress.Node,
5754 };
5755
5756 fn deinit(mr: MakeRunner, io: Io) void {
5757 _ = mr;
5758 _ = io;
5759 @panic("TODO");
56265760 }
5761};
5762
5763fn compileMakeRunner(io: Io, options: MakeRunner.Options) !MakeRunner {
5764 _ = io;
5765 _ = options;
5766 @panic("TODO");
56275767}
56285768
56295769const Fork = struct {
......@@ -5749,6 +5889,8 @@ fn jitCmdInner(
57495889 const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
57505890 const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
57515891
5892 const cwd_path = try introspect.getResolvedCwd(io, arena);
5893
57525894 // This `init` calls `fatal` on error.
57535895 var dirs: Compilation.Directories = .init(
57545896 arena,
......@@ -5759,6 +5901,7 @@ fn jitCmdInner(
57595901 preopens,
57605902 self_exe_path,
57615903 environ_map,
5904 cwd_path,
57625905 );
57635906 defer dirs.deinit(io);
57645907
src/print_env.zig+4
......@@ -8,6 +8,7 @@ const fatal = std.process.fatal;
88
99const build_options = @import("build_options");
1010const Compilation = @import("Compilation.zig");
11const introspect = @import("introspect.zig");
1112
1213pub fn cmdEnv(
1314 arena: Allocator,
......@@ -28,6 +29,8 @@ pub fn cmdEnv(
2829 },
2930 };
3031
32 const cwd_path = try introspect.getResolvedCwd(io, arena);
33
3134 var dirs: Compilation.Directories = .init(
3235 arena,
3336 io,
......@@ -37,6 +40,7 @@ pub fn cmdEnv(
3740 preopens,
3841 if (builtin.target.os.tag != .wasi) self_exe_path,
3942 environ_map,
43 cwd_path,
4044 );
4145 defer dirs.deinit(io);
4246