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 {...@@ -1024,6 +1024,12 @@ pub const Manifest = struct {
1024 try self.populateFileHash(gop.key_ptr);1024 try self.populateFileHash(gop.key_ptr);
1025 }1025 }
10261026
1027 pub fn addPathPost(man: *Manifest, path: Path) !void {
1028 _ = man;
1029 _ = path;
1030 @panic("TODO");
1031 }
1032
1027 /// Like `addFilePost` but when the file contents have already been loaded from disk.1033 /// Like `addFilePost` but when the file contents have already been loaded from disk.
1028 pub fn addFilePostContents(1034 pub fn addFilePostContents(
1029 self: *Manifest,1035 self: *Manifest,
lib/std/zig.zig+2
...@@ -11,6 +11,8 @@ const Writer = std.Io.Writer;...@@ -11,6 +11,8 @@ const Writer = std.Io.Writer;
1111
12const tokenizer = @import("zig/tokenizer.zig");12const tokenizer = @import("zig/tokenizer.zig");
1313
14/// The serialized output of configure phase ingested by make phase.
15pub const Configuration = @import("zig/Configuration.zig");
14pub const ErrorBundle = @import("zig/ErrorBundle.zig");16pub const ErrorBundle = @import("zig/ErrorBundle.zig");
15pub const Server = @import("zig/Server.zig");17pub const Server = @import("zig/Server.zig");
16pub const Client = @import("zig/Client.zig");18pub 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;...@@ -13,6 +13,7 @@ const Target = std.Target;
13const fs = std.fs;13const fs = std.fs;
14const Allocator = std.mem.Allocator;14const Allocator = std.mem.Allocator;
15const Path = std.Build.Cache.Path;15const Path = std.Build.Cache.Path;
16const Cache = std.Build.Cache;
16const log = std.log.scoped(.libc_installation);17const log = std.log.scoped(.libc_installation);
17const Environ = std.process.Environ;18const Environ = std.process.Environ;
1819
...@@ -990,7 +991,7 @@ pub fn resolveCrtPaths(...@@ -990,7 +991,7 @@ pub fn resolveCrtPaths(
990 target: *const std.Target,991 target: *const std.Target,
991) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths {992) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths {
992 const crt_dir_path: Path = .{993 const crt_dir_path: Path = .{
993 .root_dir = std.Build.Cache.Directory.cwd(),994 .root_dir = Cache.Directory.cwd(),
994 .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir,995 .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir,
995 };996 };
996 switch (target.os.tag) {997 switch (target.os.tag) {
...@@ -1016,7 +1017,7 @@ pub fn resolveCrtPaths(...@@ -1016,7 +1017,7 @@ pub fn resolveCrtPaths(
1016 },1017 },
1017 .haiku, .serenity => {1018 .haiku, .serenity => {
1018 const gcc_dir_path: Path = .{1019 const gcc_dir_path: Path = .{
1019 .root_dir = std.Build.Cache.Directory.cwd(),1020 .root_dir = Cache.Directory.cwd(),
1020 .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir,1021 .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir,
1021 };1022 };
1022 return .{1023 return .{
...@@ -1038,3 +1039,16 @@ pub fn resolveCrtPaths(...@@ -1038,3 +1039,16 @@ pub fn resolveCrtPaths(
1038 },1039 },
1039 }1040 }
1040}1041}
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 {...@@ -752,13 +752,10 @@ pub const Directories = struct {
752 else => []const u8,752 else => []const u8,
753 },753 },
754 environ_map: *const std.process.Environ.Map,754 environ_map: *const std.process.Environ.Map,
755 cwd: []const u8,
755 ) Directories {756 ) Directories {
756 const wasi = builtin.target.os.tag == .wasi;757 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
762 const zig_lib: Cache.Directory = d: {759 const zig_lib: Cache.Directory = d: {
763 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");760 if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");
764 if (wasi) break :d getPreopen(preopens, "/lib");761 if (wasi) break :d getPreopen(preopens, "/lib");
...@@ -3528,14 +3525,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3528,14 +3525,7 @@ fn addNonIncrementalStuffToCacheManifest(
3528 man.hash.addListOfBytes(opts.rpath_list);3525 man.hash.addListOfBytes(opts.rpath_list);
3529 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());3526 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
3530 if (comp.config.link_libc) {3527 if (comp.config.link_libc) {
3531 man.hash.add(comp.libc_installation != null);3528 LibCInstallation.addToHash(comp.libc_installation, &man.hash, target.abi);
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 }
3539 man.hash.addOptionalBytes(target.dynamic_linker.get());3529 man.hash.addOptionalBytes(target.dynamic_linker.get());
3540 }3530 }
3541 man.hash.add(opts.repro);3531 man.hash.add(opts.repro);
src/main.zig+266-123
...@@ -3166,6 +3166,8 @@ fn buildOutputType(...@@ -3166,6 +3166,8 @@ fn buildOutputType(
3166 else => process.executablePathAlloc(io, arena) catch |err| fatal("unable to find zig self exe path: {t}", .{err}),3166 else => process.executablePathAlloc(io, arena) catch |err| fatal("unable to find zig self exe path: {t}", .{err}),
3167 };3167 };
31683168
3169 const cwd_path = try introspect.getResolvedCwd(io, arena);
3170
3169 // This `init` calls `fatal` on error.3171 // This `init` calls `fatal` on error.
3170 var dirs: Compilation.Directories = .init(3172 var dirs: Compilation.Directories = .init(
3171 arena,3173 arena,
...@@ -3182,6 +3184,7 @@ fn buildOutputType(...@@ -3182,6 +3184,7 @@ fn buildOutputType(
3182 preopens,3184 preopens,
3183 self_exe_path,3185 self_exe_path,
3184 environ_map,3186 environ_map,
3187 cwd_path,
3185 );3188 );
3186 defer dirs.deinit(io);3189 defer dirs.deinit(io);
31873190
...@@ -4936,16 +4939,21 @@ test sanitizeExampleName {...@@ -4936,16 +4939,21 @@ test sanitizeExampleName {
4936 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));4939 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
4937}4940}
49384941
4939fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, environ_map: *process.Environ.Map) !void {4942fn cmdBuild(
4940 dev.check(.build_command);4943 gpa: Allocator,
49414944 arena: Allocator,
4945 io: Io,
4946 args: []const []const u8,
4947 environ_map: *process.Environ.Map,
4948) !void {
4942 var build_file: ?[]const u8 = null;4949 var build_file: ?[]const u8 = null;
4943 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);4950 var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
4944 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);4951 var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
4945 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);4952 var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
4946 var override_pkg_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_PKG_DIR.get(environ_map);4953 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);4954 var override_make_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);
4948 var child_argv: std.ArrayList([]const u8) = .empty;4955 var configure_argv: std.ArrayList([]const u8) = .empty;
4956 var make_argv: std.ArrayList([]const u8) = .empty;
4949 var forks: std.ArrayList(Fork) = .empty;4957 var forks: std.ArrayList(Fork) = .empty;
4950 var reference_trace: ?u32 = null;4958 var reference_trace: ?u32 = null;
4951 var debug_compile_errors = false;4959 var debug_compile_errors = false;
...@@ -4965,46 +4973,32 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4965,46 +4973,32 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
4965 var debug_target: ?[]const u8 = null;4973 var debug_target: ?[]const u8 = null;
4966 var debug_libc_paths_file: ?[]const u8 = null;4974 var debug_libc_paths_file: ?[]const u8 = null;
49674975
4968 const argv_index_exe = child_argv.items.len;4976 const argv_index_exe = configure_argv.items.len;
4969 _ = try child_argv.addOne(arena);4977 _ = try configure_argv.addOne(arena);
49704978
4971 const self_exe_path = try process.executablePathAlloc(io, arena);4979 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;4982 const argv_index_zig_lib_dir = configure_argv.items.len;
4975 _ = try child_argv.addOne(arena);4983 _ = try configure_argv.addOne(arena);
49764984
4977 const argv_index_build_file = child_argv.items.len;4985 const argv_index_build_file = configure_argv.items.len;
4978 _ = try child_argv.addOne(arena);4986 _ = try configure_argv.addOne(arena);
49794987
4980 const argv_index_cache_dir = child_argv.items.len;4988 const argv_index_cache_dir = configure_argv.items.len;
4981 _ = try child_argv.addOne(arena);4989 _ = try configure_argv.addOne(arena);
49824990
4983 const argv_index_global_cache_dir = child_argv.items.len;4991 const argv_index_global_cache_dir = configure_argv.items.len;
4984 _ = try child_argv.addOne(arena);4992 _ = try configure_argv.addOne(arena);
49854993
4986 try child_argv.appendSlice(arena, &.{4994 try configure_argv.appendSlice(arena, &.{
4987 "--seed",4995 "--seed",
4988 try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),4996 try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),
4989 });4997 });
4990 const argv_index_seed = child_argv.items.len - 1;4998 const argv_index_seed = configure_argv.items.len - 1;
49914999
4992 // This parent process needs a way to obtain results from the configuration5000 const argv_index_configuration_file = make_argv.items.len;
4993 // phase of the child process. In the future, the make phase will be5001 _ = try make_argv.addOne(arena);
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);
50085002
5009 var color: Color = .auto;5003 var color: Color = .auto;
5010 var n_jobs: ?u32 = null;5004 var n_jobs: ?u32 = null;
...@@ -5027,7 +5021,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5027,7 +5021,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5027 } else if (mem.eql(u8, arg, "--build-runner")) {5021 } else if (mem.eql(u8, arg, "--build-runner")) {
5028 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5022 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5029 i += 1;5023 i += 1;
5030 override_build_runner = args[i];5024 override_make_runner = args[i];
5031 continue;5025 continue;
5032 } else if (mem.eql(u8, arg, "--cache-dir")) {5026 } else if (mem.eql(u8, arg, "--cache-dir")) {
5033 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5027 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,...@@ -5071,7 +5065,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5071 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5065 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5072 i += 1;5066 i += 1;
5073 system_pkg_dir_path = args[i];5067 system_pkg_dir_path = args[i];
5074 try child_argv.append(arena, "--system");5068 try configure_argv.append(arena, "--system");
5075 continue;5069 continue;
5076 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {5070 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
5077 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {5071 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,...@@ -5081,7 +5075,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5081 reference_trace = null;5075 reference_trace = null;
5082 } else if (mem.eql(u8, arg, "--debug-log")) {5076 } else if (mem.eql(u8, arg, "--debug-log")) {
5083 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5077 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]);
5085 i += 1;5079 i += 1;
5086 try addDebugLog(arena, args[i]);5080 try addDebugLog(arena, args[i]);
5087 continue;5081 continue;
...@@ -5131,7 +5125,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5131,7 +5125,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5131 color = std.meta.stringToEnum(Color, args[i]) orelse {5125 color = std.meta.stringToEnum(Color, args[i]) orelse {
5132 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });5126 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
5133 };5127 };
5134 try child_argv.appendSlice(arena, &.{ arg, args[i] });5128 try configure_argv.appendSlice(arena, &.{ arg, args[i] });
5135 continue;5129 continue;
5136 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {5130 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
5137 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {5131 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,...@@ -5146,25 +5140,85 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5146 } else if (mem.eql(u8, arg, "--seed")) {5140 } else if (mem.eql(u8, arg, "--seed")) {
5147 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5141 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5148 i += 1;5142 i += 1;
5149 child_argv.items[argv_index_seed] = args[i];5143 configure_argv.items[argv_index_seed] = args[i];
5150 continue;5144 continue;
5151 } else if (mem.eql(u8, arg, "--")) {5145 } else if (mem.eql(u8, arg, "--")) {
5152 // The rest of the args are supposed to get passed onto5146 // The rest of the args are supposed to get passed onto
5153 // build runner's `build.args`5147 // build runner's `build.args`
5154 try child_argv.appendSlice(arena, args[i..]);5148 try configure_argv.appendSlice(arena, args[i..]);
5155 break;5149 break;
5156 }5150 }
5157 }5151 }
5158 try child_argv.append(arena, arg);5152 try make_argv.append(arena, arg);
5159 }5153 }
5160 }5154 }
51615155
5162 const root_prog_node = std.Progress.start(io, .{5156 const root_prog_node = std.Progress.start(io, .{
5163 .disable_printing = (color == .off),5157 .disable_printing = (color == .off),
5164 .root_name = "Compile Build Script",5158 .root_name = "",
5165 });5159 });
5166 defer root_prog_node.end();5160 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
5168 // Normally the build runner is compiled for the host target but here is5222 // Normally the build runner is compiled for the host target but here is
5169 // some code to help when debugging edits to the build runner so that you5223 // some code to help when debugging edits to the build runner so that you
5170 // can make sure it compiles successfully on other targets.5224 // 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,...@@ -5174,6 +5228,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5174 const target_query = try std.Target.Query.parse(.{5228 const target_query = try std.Target.Query.parse(.{
5175 .arch_os_abi = triple,5229 .arch_os_abi = triple,
5176 });5230 });
5231 config_man.hash.addBytes(triple);
5177 break :t .{5232 break :t .{
5178 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),5233 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5179 .is_native_os = false,5234 .is_native_os = false,
...@@ -5189,49 +5244,21 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5189,49 +5244,21 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5189 .is_explicit_dynamic_linker = false,5244 .is_explicit_dynamic_linker = false,
5190 };5245 };
5191 };5246 };
5247
5192 // Likewise, `--debug-libc` allows overriding the libc installation.5248 // Likewise, `--debug-libc` allows overriding the libc installation.
5193 const libc_installation: ?*const LibCInstallation = lci: {5249 const libc_installation: ?*const LibCInstallation = lci: {
5194 const paths_file = debug_libc_paths_file orelse break :lci null;5250 const paths_file = debug_libc_paths_file orelse break :lci null;
5195 if (!build_options.enable_debug_extensions) unreachable;5251 if (!build_options.enable_debug_extensions) unreachable;
5196 const lci = try arena.create(LibCInstallation);5252 const lci = try arena.create(LibCInstallation);
5197 lci.* = try .parse(arena, io, paths_file, &resolved_target.result);5253 lci.* = try .parse(arena, io, paths_file, &resolved_target.result);
5254 LibCInstallation.addToHash(lci, &config_man.hash, resolved_target.result.abi);
5198 break :lci lci;5255 break :lci lci;
5199 };5256 };
52005257
5201 process.raiseFileDescriptorLimit();5258 configure_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
52025259 configure_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5203 const cwd_path = try introspect.getResolvedCwd(io, arena);5260 configure_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5204 const build_root = try findBuildRoot(arena, io, .{5261 configure_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
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);
52355262
5236 // Dummy http client that is not actually used when fetch_command is unsupported.5263 // Dummy http client that is not actually used when fetch_command is unsupported.
5237 // Prevents bootstrap from depending on a bunch of unnecessary stuff.5264 // 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,...@@ -5269,11 +5296,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
52695296
5270 // This loop is re-evaluated when the build script exits with an indication that it5297 // This loop is re-evaluated when the build script exits with an indication that it
5271 // could not continue due to missing lazy dependencies.5298 // could not continue due to missing lazy dependencies.
5272 while (true) {5299 const configuration_path: Path = cp: while (true) {
5273 // We want to release all the locks before executing the child process, so we make a nice5300 // We want to release all the locks before executing the child process, so we make a nice
5274 // big block here to ensure the cleanup gets run when we extract out our argv.5301 // big block here to ensure the cleanup gets run when we extract out our argv.
5275 {5302 {
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| .{
5277 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),5304 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
5278 .root_src_path = fs.path.basename(runner),5305 .root_src_path = fs.path.basename(runner),
5279 } else .{5306 } else .{
...@@ -5497,6 +5524,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5497,6 +5524,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5497 config,5524 config,
5498 );5525 );
54995526
5527 const compile_prog_node = root_prog_node.start("Compile Configure Script", 0);
5528 defer compile_prog_node.end();
5529
5500 try root_mod.deps.put(arena, "@build", build_mod);5530 try root_mod.deps.put(arena, "@build", build_mod);
55015531
5502 var create_diag: Compilation.CreateDiagnostic = undefined;5532 var create_diag: Compilation.CreateDiagnostic = undefined;
...@@ -5528,7 +5558,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5528,7 +5558,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5528 };5558 };
5529 defer comp.destroy();5559 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) {
5532 error.CompileErrorsReported => process.exit(2),5562 error.CompileErrorsReported => process.exit(2),
5533 else => |e| return e,5563 else => |e| return e,
5534 };5564 };
...@@ -5536,52 +5566,74 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5536,52 +5566,74 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5536 // Since incremental compilation isn't done yet, we use cache_mode = whole5566 // Since incremental compilation isn't done yet, we use cache_mode = whole
5537 // above, and thus the output file is already closed.5567 // above, and thus the output file is already closed.
5538 //try comp.makeBinFileExecutable();5568 //try comp.makeBinFileExecutable();
5539 child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{5569 const hex_digest: []const u8 = &Cache.binToHex(comp.digest.?);
5540 "o",5570 const exe_path: Path = .{
5541 &Cache.binToHex(comp.digest.?),5571 .root_dir = dirs.local_cache,
5542 comp.emit_bin.?,5572 .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }),
5543 });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 }
5544 }5584 }
55455585
5546 if (!process.can_spawn) {5586 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);
5548 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });5588 fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
5549 }5589 }
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
5550 switch (term: {5604 switch (term: {
5551 _ = try io.lockStderr(&.{}, .no_color);5605 const child_node = root_prog_node.start("Run Configure Script", 0);
5552 defer io.unlockStderr();5606 defer child_node.end();
5553 var child = std.process.spawn(io, .{5607 var child = std.process.spawn(io, .{
5554 .argv = child_argv.items,5608 .argv = configure_argv.items,
5555 }) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err });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 });
5556 defer child.kill(io);5612 defer child.kill(io);
5557 break :term child.wait(io) catch |err|5613 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 });
5559 }) {5615 }) {
5560 .exited => |code| {5616 .exited => |code| {
5561 if (code == 0) return cleanExit(io);5617 if (code != 0) {
5562 // Indicates that the build runner has reported compile errors5618 // Failure to produce the configuration file.
5563 // and this parent process does not need to report any further5619 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5564 // diagnostics.5620 fatal("the following configure command failed with exit code {d}:\n{s}", .{ code, cmd });
5565 if (code == 2) process.exit(2);5621 }
55665622 // Even though the file is designed to be sent directly to make
5567 if (code == 3) {5623 // runner, we must load it now because:
5568 if (!dev.env.supports(.fetch_command)) process.exit(3);5624 // * If it contains additional file dependencies, we need to
5569 // Indicates the configure phase failed due to missing lazy5625 // add them to `config_man` before obtaining the final digest.
5570 // dependencies and stdout contains the hashes of the ones5626 // * If it contains a set of lazy packages that need to be
5571 // that are missing.5627 // fetched, we need to fetch those now and re-run configure.
5572 const s = fs.path.sep_str;5628 var configuration = std.zig.Configuration.load(arena, io, config_tmp_file) catch |err|
5573 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;5629 fatal("failed to load configuration file {f}: {t}", .{ config_tmp_path, err });
5574 const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {5630
5575 fatal("unable to read results of configure phase from '{f}{s}': {t}", .{5631 if (configuration.unlazy_deps.len != 0) {
5576 dirs.local_cache, tmp_sub_path, err,5632 if (!dev.env.supports(.fetch_command)) process.exit(1);
5577 });
5578 };
5579 dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {};
5580
5581 var it = mem.splitScalar(u8, stdout, '\n');
5582 var any_errors = false;5633 var any_errors = false;
5583 while (it.next()) |hash| {5634 for (configuration.unlazy_deps) |hash_string| {
5584 if (hash.len == 0) continue;5635 const hash = hash_string.slice(&configuration);
5636 assert(hash.len != 0);
5585 if (hash.len > Package.Hash.max_len) {5637 if (hash.len > Package.Hash.max_len) {
5586 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{5638 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
5587 hash.len, hash,5639 hash.len, hash,
...@@ -5591,10 +5643,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5591,10 +5643,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5591 }5643 }
5592 try unlazy_set.put(arena, .fromSlice(hash), {});5644 try unlazy_set.put(arena, .fromSlice(hash), {});
5593 }5645 }
5594 if (any_errors) process.exit(3);5646 if (any_errors) process.exit(1);
5595 if (system_pkg_dir_path) |p| {5647 if (system_pkg_dir_path) |p| {
5596 // In this mode, the system needs to provide these packages; they5648 // In this mode, the system needs to provide these packages; they
5597 // cannot be fetched by Zig.5649 // cannot be fetched by Zig.
5650 const s = fs.path.sep_str;
5598 for (unlazy_set.keys()) |*hash| {5651 for (unlazy_set.keys()) |*hash| {
5599 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{5652 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
5600 p, hash.toSlice(),5653 p, hash.toSlice(),
...@@ -5602,28 +5655,115 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5602,28 +5655,115 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5602 }5655 }
5603 std.log.info("remote package fetching disabled due to --system mode", .{});5656 std.log.info("remote package fetching disabled due to --system mode", .{});
5604 std.log.info("dependencies might be avoidable depending on build configuration", .{});5657 std.log.info("dependencies might be avoidable depending on build configuration", .{});
5605 process.exit(3);5658 process.exit(1);
5606 }5659 }
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));
5608 }5666 }
56095667
5610 const cmd = try std.mem.join(arena, " ", child_argv.items);5668 const digest = config_man.final();
5611 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });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;
5612 },5687 },
5613 .signal => |sig| {5688 .signal => |sig| {
5614 const cmd = try std.mem.join(arena, " ", child_argv.items);5689 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5615 fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });5690 fatal("the following configure command terminated with signal {t}:\n{s}", .{ sig, cmd });
5616 },5691 },
5617 .stopped => |sig| {5692 .stopped => |sig| {
5618 const cmd = try std.mem.join(arena, " ", child_argv.items);5693 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5619 fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd });5694 fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd });
5620 },5695 },
5621 .unknown => {5696 .unknown => {
5622 const cmd = try std.mem.join(arena, " ", child_argv.items);5697 const cmd = try std.mem.join(arena, " ", configure_argv.items);
5623 fatal("the following build command crashed:\n{s}", .{cmd});5698 fatal("the following build command crashed:\n{s}", .{cmd});
5624 },5699 },
5625 }5700 }
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");
5626 }5760 }
5761};
5762
5763fn compileMakeRunner(io: Io, options: MakeRunner.Options) !MakeRunner {
5764 _ = io;
5765 _ = options;
5766 @panic("TODO");
5627}5767}
56285768
5629const Fork = struct {5769const Fork = struct {
...@@ -5749,6 +5889,8 @@ fn jitCmdInner(...@@ -5749,6 +5889,8 @@ fn jitCmdInner(
5749 const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);5889 const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
5750 const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);5890 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
5752 // This `init` calls `fatal` on error.5894 // This `init` calls `fatal` on error.
5753 var dirs: Compilation.Directories = .init(5895 var dirs: Compilation.Directories = .init(
5754 arena,5896 arena,
...@@ -5759,6 +5901,7 @@ fn jitCmdInner(...@@ -5759,6 +5901,7 @@ fn jitCmdInner(
5759 preopens,5901 preopens,
5760 self_exe_path,5902 self_exe_path,
5761 environ_map,5903 environ_map,
5904 cwd_path,
5762 );5905 );
5763 defer dirs.deinit(io);5906 defer dirs.deinit(io);
57645907
src/print_env.zig+4
...@@ -8,6 +8,7 @@ const fatal = std.process.fatal;...@@ -8,6 +8,7 @@ const fatal = std.process.fatal;
88
9const build_options = @import("build_options");9const build_options = @import("build_options");
10const Compilation = @import("Compilation.zig");10const Compilation = @import("Compilation.zig");
11const introspect = @import("introspect.zig");
1112
12pub fn cmdEnv(13pub fn cmdEnv(
13 arena: Allocator,14 arena: Allocator,
...@@ -28,6 +29,8 @@ pub fn cmdEnv(...@@ -28,6 +29,8 @@ pub fn cmdEnv(
28 },29 },
29 };30 };
3031
32 const cwd_path = try introspect.getResolvedCwd(io, arena);
33
31 var dirs: Compilation.Directories = .init(34 var dirs: Compilation.Directories = .init(
32 arena,35 arena,
33 io,36 io,
...@@ -37,6 +40,7 @@ pub fn cmdEnv(...@@ -37,6 +40,7 @@ pub fn cmdEnv(
37 preopens,40 preopens,
38 if (builtin.target.os.tag != .wasi) self_exe_path,41 if (builtin.target.os.tag != .wasi) self_exe_path,
39 environ_map,42 environ_map,
43 cwd_path,
40 );44 );
41 defer dirs.deinit(io);45 defer dirs.deinit(io);
4246