authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 19:50:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
logdd51fc30f884aa1c3305793010a30d826689be89
tree9e6d744014ebe36593fa3874c5e9db2c83aa60c9
parentb998d71e939c304ba76860077b4483249f670b7d

maker: finish migrating compile step make logic


8 files changed, 308 insertions(+), 203 deletions(-)

lib/compiler/Maker.zig+42
...@@ -1084,6 +1084,7 @@ fn makeStep(...@@ -1084,6 +1084,7 @@ fn makeStep(
1084 } else |err| switch (err) {1084 } else |err| switch (err) {
1085 error.MakeFailed => .failure,1085 error.MakeFailed => .failure,
1086 error.MakeSkipped => .skipped,1086 error.MakeSkipped => .skipped,
1087 error.Canceled => |e| return e,
1087 };1088 };
10881089
1089 @atomicStore(Step.State, &make_step.state, new_state, .monotonic);1090 @atomicStore(Step.State, &make_step.state, new_state, .monotonic);
...@@ -1764,6 +1765,10 @@ pub fn resolveLazyPathIndexAbs(...@@ -1764,6 +1765,10 @@ pub fn resolveLazyPathIndexAbs(
1764 return resolveLazyPathAbs(maker, arena, lazy_path_index.get(c), asking_step_index);1765 return resolveLazyPathAbs(maker, arena, lazy_path_index.get(c), asking_step_index);
1765}1766}
17661767
1768pub fn generatedPath(maker: *Maker, index: Configuration.GeneratedFileIndex) *Path {
1769 return &maker.generated_files[@intFromEnum(index)];
1770}
1771
1767fn packagePath(1772fn packagePath(
1768 maker: *const Maker,1773 maker: *const Maker,
1769 arena: Allocator,1774 arena: Allocator,
...@@ -1783,3 +1788,40 @@ fn packagePath(...@@ -1783,3 +1788,40 @@ fn packagePath(
1783 .sub_path = try Io.Dir.path.join(arena, &.{ pkg_root.sub_path, hash, sub_path }),1788 .sub_path = try Io.Dir.path.join(arena, &.{ pkg_root.sub_path, hash, sub_path }),
1784 };1789 };
1785}1790}
1791
1792/// Wrapper around `Io.Dir.updateFile` that handles verbose and error output.
1793pub fn installFile(
1794 maker: *Maker,
1795 arena: Allocator,
1796 src_lazy_path: Configuration.LazyPath,
1797 dest_path: []const u8,
1798 asking_step_index: Configuration.Step.Index,
1799) !Io.Dir.PrevStatus {
1800 const graph = maker.graph;
1801 const io = graph.io;
1802 const src_path = try resolveLazyPath(maker, arena, src_lazy_path, asking_step_index);
1803 {
1804 const src_path_rendered = try src_path.toString(arena);
1805 defer arena.free(src_path_rendered);
1806 try graph.handleVerbose(.inherit, null, &.{ "install", "-C", src_path_rendered, dest_path });
1807 }
1808 return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| {
1809 const s = stepByIndex(maker, asking_step_index);
1810 return s.fail(maker, "unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
1811 };
1812}
1813
1814/// Wrapper around `Io.Dir.createDirPathStatus` that handles verbose and error output.
1815pub fn installDir(
1816 maker: *Maker,
1817 dest_path: []const u8,
1818 asking_step_index: Configuration.Step.Index,
1819) !Io.Dir.CreatePathStatus {
1820 const graph = maker.graph;
1821 const io = graph.io;
1822 try graph.handleVerbose(.inherit, null, &.{ "install", "-d", dest_path });
1823 return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err| {
1824 const s = stepByIndex(maker, asking_step_index);
1825 return s.fail(maker, "unable to create dir '{s}': {t}", .{ dest_path, err });
1826 };
1827}
lib/compiler/Maker/Graph.zig+18
...@@ -47,3 +47,21 @@ sysroot: ?[]const u8 = null,...@@ -47,3 +47,21 @@ sysroot: ?[]const u8 = null,
47search_prefixes: std.ArrayList([]const u8) = .empty,47search_prefixes: std.ArrayList([]const u8) = .empty,
48build_id: ?std.zig.BuildId = null,48build_id: ?std.zig.BuildId = null,
49error_limit: ?u32 = null,49error_limit: ?u32 = null,
50
51/// Intention of verbose is to print all sub-process command lines to stderr
52/// before spawning them.
53pub fn handleVerbose(
54 graph: *const Graph,
55 cwd: std.process.Child.Cwd,
56 opt_env: ?*const std.process.Environ.Map,
57 argv: []const []const u8,
58) error{OutOfMemory}!void {
59 if (!graph.verbose) return;
60 const arena = graph.arena;
61 const text = try std.zig.allocPrintCmd(arena, cwd, if (opt_env) |env| .{
62 .child = env,
63 .parent = &graph.environ_map,
64 } else null, argv);
65 defer arena.free(text);
66 std.log.scoped(.verbose).info("{s}", .{text});
67}
lib/compiler/Maker/Step.zig+134-154
...@@ -111,10 +111,9 @@ pub const Extended = union(enum) {...@@ -111,10 +111,9 @@ pub const Extended = union(enum) {
111 progress_node: std.Progress.Node,111 progress_node: std.Progress.Node,
112 ) Step.ExtendedMakeError!void {112 ) Step.ExtendedMakeError!void {
113 _ = todo;113 _ = todo;
114 _ = step_index;
115 _ = maker;114 _ = maker;
116 _ = progress_node;115 _ = progress_node;
117 @panic("TODO implement another step type");116 std.debug.panic("TODO implement another step type (index {d})", .{step_index});
118 }117 }
119 };118 };
120};119};
...@@ -146,7 +145,7 @@ pub const Inputs = struct {...@@ -146,7 +145,7 @@ pub const Inputs = struct {
146 .table = .{},145 .table = .{},
147 };146 };
148147
149 pub const Table = std.ArrayHashMapUnmanaged(Cache.Path, Files, Cache.Path.TableAdapter, false);148 pub const Table = std.ArrayHashMapUnmanaged(Path, Files, Path.TableAdapter, false);
150 /// The special file name "." means any changes inside the directory.149 /// The special file name "." means any changes inside the directory.
151 pub const Files = std.ArrayList([]const u8);150 pub const Files = std.ArrayList([]const u8);
152151
...@@ -205,7 +204,7 @@ pub const MakeError = error{...@@ -205,7 +204,7 @@ pub const MakeError = error{
205 /// Indicates the error is already reported.204 /// Indicates the error is already reported.
206 MakeFailed,205 MakeFailed,
207 MakeSkipped,206 MakeSkipped,
208};207} || Io.Cancelable;
209208
210pub const ExtendedMakeError = MakeError || Allocator.Error;209pub const ExtendedMakeError = MakeError || Allocator.Error;
211210
...@@ -215,7 +214,7 @@ pub fn make(...@@ -215,7 +214,7 @@ pub fn make(
215 progress_node: std.Progress.Node,214 progress_node: std.Progress.Node,
216) MakeError!void {215) MakeError!void {
217 const graph = maker.graph;216 const graph = maker.graph;
218 const process_arena = graph.arena; // TODO don't leak into the process arena217 const arena = graph.arena; // TODO don't leak into the process arena
219 const io = graph.io;218 const io = graph.io;
220 const c = &maker.scanned_config.configuration;219 const c = &maker.scanned_config.configuration;
221 const conf_step = step_index.ptr(c);220 const conf_step = step_index.ptr(c);
...@@ -248,6 +247,7 @@ pub fn make(...@@ -248,6 +247,7 @@ pub fn make(
248 s.result_oom = true;247 s.result_oom = true;
249 return error.MakeFailed;248 return error.MakeFailed;
250 },249 },
250 error.Canceled => |e| return e,
251 };251 };
252252
253 if (!s.test_results.isSuccess()) {253 if (!s.test_results.isSuccess()) {
...@@ -257,11 +257,11 @@ pub fn make(...@@ -257,11 +257,11 @@ pub fn make(
257 const max_rss = conf_step.max_rss.toBytes();257 const max_rss = conf_step.max_rss.toBytes();
258 if (max_rss != 0 and s.result_peak_rss > max_rss) {258 if (max_rss != 0 and s.result_peak_rss > max_rss) {
259 if (std.fmt.allocPrint(259 if (std.fmt.allocPrint(
260 process_arena,260 arena,
261 "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)",261 "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)",
262 .{ s.result_peak_rss, max_rss },262 .{ s.result_peak_rss, max_rss },
263 )) |msg| {263 )) |msg| {
264 s.oomWrap(s.result_error_msgs.append(process_arena, msg));264 s.oomWrap(s.result_error_msgs.append(arena, msg));
265 } else |_| s.result_oom = true;265 } else |_| s.result_oom = true;
266 }266 }
267}267}
...@@ -288,11 +288,12 @@ pub fn reset(step: *Step, gpa: Allocator) void {...@@ -288,11 +288,12 @@ pub fn reset(step: *Step, gpa: Allocator) void {
288/// Populates `s.result_failed_command`.288/// Populates `s.result_failed_command`.
289pub fn captureChildProcess(289pub fn captureChildProcess(
290 s: *Step,290 s: *Step,
291 gpa: Allocator,291 maker: *Maker,
292 progress_node: std.Progress.Node,292 progress_node: std.Progress.Node,
293 argv: []const []const u8,293 argv: []const []const u8,
294) !std.process.RunResult {294) !std.process.RunResult {
295 const graph = s.owner.graph;295 const gpa = maker.gpa;
296 const graph = maker.graph;
296 const arena = graph.arena;297 const arena = graph.arena;
297 const io = graph.io;298 const io = graph.io;
298299
...@@ -300,14 +301,14 @@ pub fn captureChildProcess(...@@ -300,14 +301,14 @@ pub fn captureChildProcess(
300 assert(s.result_failed_command == null);301 assert(s.result_failed_command == null);
301 s.result_failed_command = try std.zig.allocPrintCmd(gpa, .inherit, null, argv);302 s.result_failed_command = try std.zig.allocPrintCmd(gpa, .inherit, null, argv);
302303
303 try handleChildProcUnsupported(s);304 try handleChildProcUnsupported(s, maker);
304 try handleVerbose(s, .inherit, argv);305 try graph.handleVerbose(.inherit, null, argv);
305306
306 const result = std.process.run(arena, io, .{307 const result = std.process.run(arena, io, .{
307 .argv = argv,308 .argv = argv,
308 .environ_map = &graph.environ_map,309 .environ_map = &graph.environ_map,
309 .progress_node = progress_node,310 .progress_node = progress_node,
310 }) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err });311 }) catch |err| return s.fail(maker, "failed to run {s}: {t}", .{ argv[0], err });
311312
312 if (result.stderr.len > 0) {313 if (result.stderr.len > 0) {
313 try s.result_error_msgs.append(arena, result.stderr);314 try s.result_error_msgs.append(arena, result.stderr);
...@@ -316,7 +317,9 @@ pub fn captureChildProcess(...@@ -316,7 +317,9 @@ pub fn captureChildProcess(
316 return result;317 return result;
317}318}
318319
319pub fn fail(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } {320pub const FailError = error{ OutOfMemory, MakeFailed };
321
322pub fn fail(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) FailError {
320 try step.addError(maker, fmt, args);323 try step.addError(maker, fmt, args);
321 return error.MakeFailed;324 return error.MakeFailed;
322}325}
...@@ -351,15 +354,16 @@ pub const ZigProcess = struct {...@@ -351,15 +354,16 @@ pub const ZigProcess = struct {
351/// is the zig compiler - the same version that compiled the build runner.354/// is the zig compiler - the same version that compiled the build runner.
352/// Populates `s.result_failed_command`.355/// Populates `s.result_failed_command`.
353pub fn evalZigProcess(356pub fn evalZigProcess(
354 s: *Step,357 step_index: Configuration.Step.Index,
358 maker: *Maker,
355 argv: []const []const u8,359 argv: []const []const u8,
356 prog_node: std.Progress.Node,360 prog_node: std.Progress.Node,
357 watch: bool,361 watch: bool,
358 maker: *Maker,362) (Step.ExtendedMakeError || error{NeedCompileErrorCheck})!?Path {
359) !?Cache.Path {363 const s = maker.stepByIndex(step_index);
360 const gpa = maker.gpa;364 const gpa = maker.gpa;
361 const b = s.owner;365 const graph = maker.graph;
362 const io = b.graph.io;366 const io = graph.io;
363367
364 // If an error occurs, it's happened in this command:368 // If an error occurs, it's happened in this command:
365 assert(s.result_failed_command == null);369 assert(s.result_failed_command == null);
...@@ -371,36 +375,33 @@ pub fn evalZigProcess(...@@ -371,36 +375,33 @@ pub fn evalZigProcess(
371 zp.progress_ipc_index = null;375 zp.progress_ipc_index = null;
372 var exited = false;376 var exited = false;
373 defer if (exited) {377 defer if (exited) {
374 s.cast(Compile).?.zig_process = null;378 s.extended.compile.zig_process = null;
375 zp.deinit(io);379 zp.deinit(io);
376 gpa.destroy(zp);380 gpa.destroy(zp);
377 } else zp.saveState(prog_node);381 } else zp.saveState(prog_node);
378 const result = zigProcessUpdate(s, zp, watch, maker) catch |err| switch (err) {382 const result = zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) {
379 error.BrokenPipe, error.EndOfStream => |reason| {383 error.BrokenPipe, error.EndOfStream => |reason| {
380 std.log.info("{s} restart required: {t}", .{ argv[0], reason });
381 // Process restart required.384 // Process restart required.
382 const term = zp.child.wait(io) catch |e| {385 std.log.info("{s} restart required: {t}", .{ argv[0], reason });
383 return s.fail("unable to wait for {s}: {t}", .{ argv[0], e });386 _ = zp.child.wait(io) catch |e| return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e });
384 };
385 _ = term;
386 exited = true;387 exited = true;
387 break :update;388 break :update;
388 },389 },
389 else => |e| return e,390 error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e,
391 else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}),
390 };392 };
391393
392 if (s.result_error_bundle.errorMessageCount() > 0) {394 if (s.result_error_bundle.errorMessageCount() > 0)
393 return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});395 return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
394 }
395396
396 if (s.result_error_msgs.items.len > 0 and result == null) {397 if (s.result_error_msgs.items.len > 0 and result == null) {
397 // Crash detected.398 // Crash detected.
398 const term = zp.child.wait(io) catch |e| {399 const term = zp.child.wait(io) catch |e| {
399 return s.fail("unable to wait for {s}: {t}", .{ argv[0], e });400 return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e });
400 };401 };
401 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;402 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
402 exited = true;403 exited = true;
403 try handleChildProcessTerm(s, term);404 try handleChildProcessTerm(s, maker, term);
404 return error.MakeFailed;405 return error.MakeFailed;
405 }406 }
406407
...@@ -408,31 +409,34 @@ pub fn evalZigProcess(...@@ -408,31 +409,34 @@ pub fn evalZigProcess(
408 }409 }
409 assert(argv.len != 0);410 assert(argv.len != 0);
410411
411 try handleChildProcUnsupported(s);412 try handleChildProcUnsupported(s, maker);
412 try handleVerbose(s, .inherit, argv);413 try graph.handleVerbose(.inherit, null, argv);
413414
414 const zp = try gpa.create(ZigProcess);415 const zp = try gpa.create(ZigProcess);
415 defer if (!watch) gpa.destroy(zp);416 defer if (!watch) gpa.destroy(zp);
416417
417 zp.child = std.process.spawn(io, .{418 zp.child = std.process.spawn(io, .{
418 .argv = argv,419 .argv = argv,
419 .environ_map = &b.graph.environ_map,420 .environ_map = &graph.environ_map,
420 .stdin = .pipe,421 .stdin = .pipe,
421 .stdout = .pipe,422 .stdout = .pipe,
422 .stderr = .pipe,423 .stderr = .pipe,
423 .request_resource_usage_statistics = true,424 .request_resource_usage_statistics = true,
424 .progress_node = prog_node,425 .progress_node = prog_node,
425 }) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });426 }) catch |err| return s.fail(maker, "failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
426427
427 zp.multi_reader.init(gpa, io, zp.multi_reader_buffer.toStreams(), &.{428 zp.multi_reader.init(gpa, io, zp.multi_reader_buffer.toStreams(), &.{
428 zp.child.stdout.?, zp.child.stderr.?,429 zp.child.stdout.?, zp.child.stderr.?,
429 });430 });
430 if (watch) s.cast(Compile).?.zig_process = zp;431 if (watch) s.extended.compile.zig_process = zp;
431 defer if (!watch) zp.deinit(io);432 defer if (!watch) zp.deinit(io);
432433
433 const result = result: {434 const result = result: {
434 defer if (watch) zp.saveState(prog_node);435 defer if (watch) zp.saveState(prog_node);
435 break :result try zigProcessUpdate(s, zp, watch, maker);436 break :result zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) {
437 error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e,
438 else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}),
439 };
436 };440 };
437441
438 if (!watch) {442 if (!watch) {
...@@ -441,56 +445,36 @@ pub fn evalZigProcess(...@@ -441,56 +445,36 @@ pub fn evalZigProcess(
441 zp.child.stdin = null;445 zp.child.stdin = null;
442446
443 const term = zp.child.wait(io) catch |err| {447 const term = zp.child.wait(io) catch |err| {
444 return s.fail("unable to wait for {s}: {t}", .{ argv[0], err });448 return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], err });
445 };449 };
446 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;450 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
447451
448 // Special handling for Compile step that is expecting compile errors.452 // Special handling for compile step that is expecting compile errors.
449 if (s.cast(Compile)) |compile| switch (term) {453 const conf = &maker.scanned_config.configuration;
450 .exited => {454 if (term == .exited) switch (step_index.ptr(conf).extended.get(conf.extra)) {
455 .compile => |compile| if (compile.flags4.expect_errors != .none) {
451 // Note that the exit code may be 0 in this case due to the456 // Note that the exit code may be 0 in this case due to the
452 // compiler server protocol.457 // compiler server protocol.
453 if (compile.expect_errors != null) {458 return error.NeedCompileErrorCheck;
454 return error.NeedCompileErrorCheck;
455 }
456 },459 },
457 else => {},460 else => {},
458 };461 };
459462 try handleChildProcessTerm(s, maker, term);
460 try handleChildProcessTerm(s, term);
461 }463 }
462464
463 if (s.result_error_bundle.errorMessageCount() > 0) {465 if (s.result_error_bundle.errorMessageCount() > 0) {
464 return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});466 return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
465 }467 }
466468
467 return result;469 return result;
468}470}
469471
470/// Wrapper around `Io.Dir.updateFile` that handles verbose and error output.472fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *ZigProcess, watch: bool) !?Path {
471pub fn installFile(s: *Step, src_lazy_path: LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {473 const s = maker.stepByIndex(step_index);
472 const b = s.owner;
473 const io = b.graph.io;
474 const src_path = src_lazy_path.getPath3(b, s);
475 try handleVerbose(s, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
476 return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|
477 return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
478}
479
480/// Wrapper around `Io.Dir.createDirPathStatus` that handles verbose and error output.
481pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus {
482 const b = s.owner;
483 const io = b.graph.io;
484 try handleVerbose(s, .inherit, &.{ "install", "-d", dest_path });
485 return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err|
486 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
487}
488
489fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Path {
490 const gpa = maker.gpa;474 const gpa = maker.gpa;
491 const b = s.owner;475 const graph = maker.graph;
492 const arena = b.allocator;476 const arena = graph.arena; // TODO don't leak into the process arena
493 const io = b.graph.io;477 const io = graph.io;
494478
495 const start_ts = Io.Clock.awake.now(io);479 const start_ts = Io.Clock.awake.now(io);
496480
...@@ -522,6 +506,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat...@@ -522,6 +506,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat
522 .zig_version => {506 .zig_version => {
523 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {507 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
524 return s.fail(508 return s.fail(
509 maker,
525 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",510 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
526 .{ builtin.zig_version_string, body },511 .{ builtin.zig_version_string, body },
527 );512 );
...@@ -538,61 +523,65 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat...@@ -538,61 +523,65 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat
538 s.result_cached = emit_digest.flags.cache_hit;523 s.result_cached = emit_digest.flags.cache_hit;
539 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];524 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
540 result = .{525 result = .{
541 .root_dir = b.cache_root,526 .root_dir = graph.local_cache_root,
542 .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),527 .sub_path = try arena.dupe(u8, "o" ++ Io.Dir.path.sep_str ++ Cache.binToHex(digest.*)),
543 };528 };
544 },529 },
545 .file_system_inputs => {530 .file_system_inputs => {
546 s.clearWatchInputs();531 clearWatchInputs(s, maker);
532 const conf = &maker.scanned_config.configuration;
533 const conf_step = step_index.ptr(conf);
547 var it = std.mem.splitScalar(u8, body, 0);534 var it = std.mem.splitScalar(u8, body, 0);
548 while (it.next()) |prefixed_path| {535 while (it.next()) |prefixed_path| {
549 const prefix_index: std.zig.Server.Message.PathPrefix = @enumFromInt(prefixed_path[0] - 1);536 const prefix_index: std.zig.Server.Message.PathPrefix = @enumFromInt(prefixed_path[0] - 1);
550 const sub_path = try arena.dupe(u8, prefixed_path[1..]);537 const sub_path = try arena.dupe(u8, prefixed_path[1..]);
551 const sub_path_dirname = std.fs.path.dirname(sub_path) orelse "";538 const sub_path_dirname = Io.Dir.path.dirname(sub_path) orelse "";
552 switch (prefix_index) {539 switch (prefix_index) {
553 .cwd => {540 .cwd => {
554 const path: Cache.Path = .{541 const path: Path = .{
555 .root_dir = Cache.Directory.cwd(),542 .root_dir = .cwd(),
556 .sub_path = sub_path_dirname,543 .sub_path = sub_path_dirname,
557 };544 };
558 try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path));545 try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path));
559 },546 },
560 .zig_lib => zl: {547 .zig_lib => zl: {
561 if (s.cast(Step.Compile)) |compile| {548 switch (conf_step.extended.get(conf.extra)) {
562 if (compile.zig_lib_dir) |zig_lib_dir| {549 .compile => |compile| if (compile.zig_lib_dir.value) |zig_lib_dir| {
563 const lp = try zig_lib_dir.join(arena, sub_path);550 const resolved = try maker.resolveLazyPathIndex(arena, zig_lib_dir, step_index);
564 try addWatchInput(s, lp);551 const appended = try resolved.join(arena, sub_path);
552 try addWatchInputPath(s, maker, appended);
565 break :zl;553 break :zl;
566 }554 },
555 else => {},
567 }556 }
568 const path: Cache.Path = .{557 const path: Path = .{
569 .root_dir = s.owner.graph.zig_lib_directory,558 .root_dir = graph.zig_lib_directory,
570 .sub_path = sub_path_dirname,559 .sub_path = sub_path_dirname,
571 };560 };
572 try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path));561 try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path));
573 },562 },
574 .local_cache => {563 .local_cache => {
575 const path: Cache.Path = .{564 const path: Path = .{
576 .root_dir = b.cache_root,565 .root_dir = graph.local_cache_root,
577 .sub_path = sub_path_dirname,566 .sub_path = sub_path_dirname,
578 };567 };
579 try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path));568 try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path));
580 },569 },
581 .global_cache => {570 .global_cache => {
582 const path: Cache.Path = .{571 const path: Path = .{
583 .root_dir = s.owner.graph.global_cache_root,572 .root_dir = graph.global_cache_root,
584 .sub_path = sub_path_dirname,573 .sub_path = sub_path_dirname,
585 };574 };
586 try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path));575 try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path));
587 },576 },
588 }577 }
589 }578 }
590 },579 },
591 .time_report => if (maker.web_server) |ws| {580 .time_report => if (maker.web_server) |*ws| {
592 const TimeReport = std.zig.Server.Message.TimeReport;581 const TimeReport = std.zig.Server.Message.TimeReport;
593 const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]);582 const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]);
594 ws.updateTimeReportCompile(.{583 ws.updateTimeReportCompile(.{
595 .compile = s.cast(Step.Compile).?,584 .compile_step = step_index,
596 .use_llvm = tr.flags.use_llvm,585 .use_llvm = tr.flags.use_llvm,
597 .stats = tr.stats,586 .stats = tr.stats,
598 .ns_total = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()),587 .ns_total = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()),
...@@ -636,46 +625,29 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {...@@ -636,46 +625,29 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
636 };625 };
637}626}
638627
639pub fn handleVerbose(
640 s: *Step,
641 arena: Allocator,
642 cwd: std.process.Child.Cwd,
643 opt_env: ?*const std.process.Environ.Map,
644 argv: []const []const u8,
645) error{OutOfMemory}!void {
646 const graph = s.graph;
647 if (!graph.verbose) return;
648 // Intention of verbose is to print all sub-process command lines to
649 // stderr before spawning them.
650 const text = try std.zig.allocPrintCmd(arena, cwd, if (opt_env) |env| .{
651 .child = env,
652 .parent = &graph.environ_map,
653 } else null, argv);
654 std.log.scoped(.verbose).info("{s}", .{text});
655}
656
657/// Asserts that the caller has already populated `s.result_failed_command`.628/// Asserts that the caller has already populated `s.result_failed_command`.
658pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFailed }!void {629pub inline fn handleChildProcUnsupported(s: *Step, maker: *Maker) FailError!void {
630 assert(s.result_failed_command != null);
659 if (!std.process.can_spawn) {631 if (!std.process.can_spawn) {
660 return s.fail("unable to spawn process: host cannot spawn child processes", .{});632 return s.fail(maker, "unable to spawn process: host cannot spawn child processes", .{});
661 }633 }
662}634}
663635
664/// Asserts that the caller has already populated `s.result_failed_command`.636/// Asserts that the caller has already populated `s.result_failed_command`.
665pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void {637pub fn handleChildProcessTerm(s: *Step, maker: *Maker, term: std.process.Child.Term) FailError!void {
666 assert(s.result_failed_command != null);638 assert(s.result_failed_command != null);
667 return switch (term) {639 return switch (term) {
668 .exited => |code| if (code != 0) s.fail("process exited with error code {d}", .{code}),640 .exited => |code| if (code != 0) s.fail(maker, "process exited with error code {d}", .{code}),
669 .signal => |sig| s.fail("process terminated with signal {t}", .{sig}),641 .signal => |sig| s.fail(maker, "process terminated with signal {t}", .{sig}),
670 .stopped => |sig| s.fail("process stopped with signal {t}", .{sig}),642 .stopped => |sig| s.fail(maker, "process stopped with signal {t}", .{sig}),
671 .unknown => s.fail("process terminated unexpectedly", .{}),643 .unknown => s.fail(maker, "process terminated unexpectedly", .{}),
672 };644 };
673}645}
674646
675/// Prefer `cacheHitAndWatch` unless you already added watch inputs647/// Prefer `cacheHitAndWatch` unless you already added watch inputs
676/// separately from using the cache system.648/// separately from using the cache system.
677pub fn cacheHit(s: *Step, man: *Cache.Manifest) !bool {649pub fn cacheHit(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool {
678 s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err);650 s.result_cached = man.hit() catch |err| return failWithCacheError(s, maker, man, err);
679 return s.result_cached;651 return s.result_cached;
680}652}
681653
...@@ -683,36 +655,37 @@ pub fn cacheHit(s: *Step, man: *Cache.Manifest) !bool {...@@ -683,36 +655,37 @@ pub fn cacheHit(s: *Step, man: *Cache.Manifest) !bool {
683/// the full set of files picked up by the cache manifest.655/// the full set of files picked up by the cache manifest.
684///656///
685/// Must be accompanied with `writeManifestAndWatch`.657/// Must be accompanied with `writeManifestAndWatch`.
686pub fn cacheHitAndWatch(s: *Step, man: *Cache.Manifest) !bool {658pub fn cacheHitAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool {
687 const is_hit = man.hit() catch |err| return failWithCacheError(s, man, err);659 const is_hit = man.hit() catch |err| return failWithCacheError(s, maker, man, err);
688 s.result_cached = is_hit;660 s.result_cached = is_hit;
689 // The above call to hit() populates the manifest with files, so in case of661 // The above call to hit() populates the manifest with files, so in case of
690 // a hit, we need to populate watch inputs.662 // a hit, we need to populate watch inputs.
691 if (is_hit) try setWatchInputsFromManifest(s, man);663 if (is_hit) try setWatchInputsFromManifest(s, maker, man);
692 return is_hit;664 return is_hit;
693}665}
694666
695fn failWithCacheError(667fn failWithCacheError(
696 s: *Step,668 s: *Step,
669 maker: *Maker,
697 man: *const Cache.Manifest,670 man: *const Cache.Manifest,
698 err: Cache.Manifest.HitError,671 err: Cache.Manifest.HitError,
699) error{ OutOfMemory, Canceled, MakeFailed } {672) error{ OutOfMemory, Canceled, MakeFailed } {
700 switch (err) {673 switch (err) {
701 error.CacheCheckFailed => switch (man.diagnostic) {674 error.CacheCheckFailed => switch (man.diagnostic) {
702 .none => unreachable,675 .none => unreachable,
703 .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail("failed to check cache: {t} {t}", .{676 .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail(maker, "failed to check cache: {t} {t}", .{
704 man.diagnostic, e,677 man.diagnostic, e,
705 }),678 }),
706 .file_open, .file_stat, .file_read, .file_hash => |op| {679 .file_open, .file_stat, .file_read, .file_hash => |op| {
707 const pp = man.files.keys()[op.file_index].prefixed_path;680 const pp = man.files.keys()[op.file_index].prefixed_path;
708 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";681 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
709 return s.fail("failed to check cache: '{s}{c}{s}' {t} {t}", .{682 return s.fail(maker, "failed to check cache: '{s}{c}{s}' {t} {t}", .{
710 prefix, std.fs.path.sep, pp.sub_path, man.diagnostic, op.err,683 prefix, Io.Dir.path.sep, pp.sub_path, man.diagnostic, op.err,
711 });684 });
712 },685 },
713 },686 },
714 error.OutOfMemory, error.Canceled => |e| return e,687 error.OutOfMemory, error.Canceled => |e| return e,
715 error.InvalidFormat => return s.fail("failed to check cache: invalid manifest file format", .{}),688 error.InvalidFormat => return s.fail(maker, "failed to check cache: invalid manifest file format", .{}),
716 }689 }
717}690}
718691
...@@ -730,48 +703,48 @@ pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void {...@@ -730,48 +703,48 @@ pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void {
730/// the full set of files picked up by the cache manifest.703/// the full set of files picked up by the cache manifest.
731///704///
732/// Must be accompanied with `cacheHitAndWatch`.705/// Must be accompanied with `cacheHitAndWatch`.
733pub fn writeManifestAndWatch(s: *Step, man: *Cache.Manifest) !void {706pub fn writeManifestAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {
734 try writeManifest(s, man);707 try writeManifest(s, man);
735 try setWatchInputsFromManifest(s, man);708 try setWatchInputsFromManifest(s, maker, man);
736}709}
737710
738fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void {711fn setWatchInputsFromManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {
739 const arena = s.owner.allocator;712 const graph = maker.graph;
713 const arena = graph.arena; // TODO don't leak into process arena
740 const prefixes = man.cache.prefixes();714 const prefixes = man.cache.prefixes();
741 clearWatchInputs(s);715 clearWatchInputs(s, maker);
742 for (man.files.keys()) |file| {716 for (man.files.keys()) |file| {
743 // The file path data is freed when the cache manifest is cleaned up at the end of `make`.717 // The file path data is freed when the cache manifest is cleaned up at the end of `make`.
744 const sub_path = try arena.dupe(u8, file.prefixed_path.sub_path);718 const sub_path = try arena.dupe(u8, file.prefixed_path.sub_path);
745 try addWatchInputFromPath(s, .{719 try addWatchInputFromPath(s, maker, .{
746 .root_dir = prefixes[file.prefixed_path.prefix],720 .root_dir = prefixes[file.prefixed_path.prefix],
747 .sub_path = std.fs.path.dirname(sub_path) orelse "",721 .sub_path = Io.Dir.path.dirname(sub_path) orelse "",
748 }, std.fs.path.basename(sub_path));722 }, Io.Dir.path.basename(sub_path));
749 }723 }
750}724}
751725
752/// For steps that have a single input that never changes when re-running `make`.726/// For steps that have a single input that never changes when re-running `make`.
753pub fn singleUnchangingWatchInput(step: *Step, lazy_path: LazyPath) Allocator.Error!void {727pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, lazy_path: LazyPath) Allocator.Error!void {
754 if (!step.inputs.populated()) try step.addWatchInput(lazy_path);728 if (!step.inputs.populated()) try step.addWatchInput(maker, lazy_path);
755}729}
756730
757pub fn clearWatchInputs(step: *Step) void {731pub fn clearWatchInputs(step: *Step, maker: *Maker) void {
758 const gpa = step.owner.allocator;732 step.inputs.clear(maker.gpa);
759 step.inputs.clear(gpa);
760}733}
761734
762/// Places a *file* dependency on the path.735/// Places a *file* dependency on the path.
763pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void {736pub fn addWatchInput(step: *Step, maker: *Maker, lazy_file: LazyPath) Allocator.Error!void {
764 switch (lazy_file) {737 switch (lazy_file) {
765 .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),738 .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
766 .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),739 .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
767 .cwd_relative => |path_string| {740 .cwd_relative => |path_string| {
768 try addWatchInputFromPath(step, .{741 try addWatchInputFromPath(step, maker, .{
769 .root_dir = .{742 .root_dir = .{
770 .path = null,743 .path = null,
771 .handle = Io.Dir.cwd(),744 .handle = Io.Dir.cwd(),
772 },745 },
773 .sub_path = std.fs.path.dirname(path_string) orelse "",746 .sub_path = Io.Dir.path.dirname(path_string) orelse "",
774 }, std.fs.path.basename(path_string));747 }, Io.Dir.path.basename(path_string));
775 },748 },
776 // Nothing to watch because this dependency edge is modeled instead via `dependants`.749 // Nothing to watch because this dependency edge is modeled instead via `dependants`.
777 .generated => {},750 .generated => {},
...@@ -780,7 +753,7 @@ pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void {...@@ -780,7 +753,7 @@ pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void {
780753
781/// Any changes inside the directory will trigger invalidation.754/// Any changes inside the directory will trigger invalidation.
782///755///
783/// See also `addDirectoryWatchInputFromPath` which takes a `Cache.Path` instead.756/// See also `addDirectoryWatchInputFromPath` which takes a `Path` instead.
784///757///
785/// Paths derived from this directory should also be manually added via758/// Paths derived from this directory should also be manually added via
786/// `addDirectoryWatchInputFromPath` if and only if this function returns759/// `addDirectoryWatchInputFromPath` if and only if this function returns
...@@ -812,15 +785,15 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.E...@@ -812,15 +785,15 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.E
812/// dependency on `path` is not already accounted for by a `Step` dependency.785/// dependency on `path` is not already accounted for by a `Step` dependency.
813/// In other words, before calling this function, first check that the786/// In other words, before calling this function, first check that the
814/// `LazyPath` which this `path` is derived from is not `generated`.787/// `LazyPath` which this `path` is derived from is not `generated`.
815pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void {788pub fn addDirectoryWatchInputFromPath(step: *Step, maker: *Maker, path: Path) !void {
816 return addWatchInputFromPath(step, path, ".");789 return addWatchInputFromPath(step, maker, path, ".");
817}790}
818791
819fn addWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {792fn addWatchInputFromBuilder(step: *Step, maker: *Maker, package: Package, sub_path: []const u8) !void {
820 return addWatchInputFromPath(step, .{793 return addWatchInputFromPath(step, maker, .{
821 .root_dir = package.build_root,794 .root_dir = package.build_root,
822 .sub_path = std.fs.path.dirname(sub_path) orelse "",795 .sub_path = Io.Dir.path.dirname(sub_path) orelse "",
823 }, std.fs.path.basename(sub_path));796 }, Io.Dir.path.basename(sub_path));
824}797}
825798
826fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {799fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
...@@ -830,9 +803,16 @@ fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []...@@ -830,9 +803,16 @@ fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []
830 });803 });
831}804}
832805
833fn addWatchInputFromPath(step: *Step, path: Cache.Path, basename: []const u8) !void {806fn addWatchInputPath(step: *Step, maker: *Maker, path: Path) Allocator.Error!void {
834 const gpa = step.owner.allocator;807 return addWatchInputFromPath(step, maker, .{
835 const gop = try step.inputs.table.getOrPut(gpa, path);808 .root_dir = path.root_dir,
809 .sub_path = Io.Dir.path.dirname(path.sub_path) orelse "",
810 }, Io.Dir.path.basename(path.sub_path));
811}
812
813fn addWatchInputFromPath(step: *Step, maker: *Maker, directory: Path, basename: []const u8) Allocator.Error!void {
814 const gpa = maker.gpa;
815 const gop = try step.inputs.table.getOrPut(gpa, directory);
836 if (!gop.found_existing) gop.value_ptr.* = .empty;816 if (!gop.found_existing) gop.value_ptr.* = .empty;
837 try gop.value_ptr.append(gpa, basename);817 try gop.value_ptr.append(gpa, basename);
838}818}
lib/compiler/Maker/Step/Compile.zig+59-28
...@@ -29,61 +29,91 @@ pub fn make(...@@ -29,61 +29,91 @@ pub fn make(
29) Step.ExtendedMakeError!void {29) Step.ExtendedMakeError!void {
30 const graph = maker.graph;30 const graph = maker.graph;
31 const step = maker.stepByIndex(compile_index);31 const step = maker.stepByIndex(compile_index);
32 const conf = &maker.scanned_config.configuration;
33 const conf_step = compile_index.ptr(conf);
34 const conf_comp = conf_step.extended.get(conf.extra).compile;
3235
33 // Reset / repopulate persistent state.36 // Reset / repopulate persistent state.
34 compile.zig_args.clearRetainingCapacity();37 compile.zig_args.clearRetainingCapacity();
3538
36 try lowerZigArgs(compile, compile_index, maker, &compile.zig_args, false);39 try lowerZigArgs(compile, compile_index, maker, &compile.zig_args, false);
37 if (true) @panic("TODO implement compile.make()");
38 const process_arena = graph.arena; // TODO don't leak into the process_arena
3940
40 const maybe_output_dir = step.evalZigProcess(41 const maybe_output_dir = Step.evalZigProcess(
42 compile_index,
43 maker,
41 compile.zig_args.items,44 compile.zig_args.items,
42 progress_node,45 progress_node,
43 (graph.incremental == true) and (maker.watch or maker.web_server != null),46 (graph.incremental == true) and (maker.watch or maker.web_server != null),
44 maker,
45 ) catch |err| switch (err) {47 ) catch |err| switch (err) {
46 error.NeedCompileErrorCheck => {48 error.NeedCompileErrorCheck => {
47 assert(compile.expect_errors != null);
48 try checkCompileErrors(compile, maker);49 try checkCompileErrors(compile, maker);
49 return;50 return;
50 },51 },
51 else => |e| return e,52 else => |e| return e,
52 };53 };
5354
55 const root_module = conf_comp.root_module.get(conf);
56 const target = root_module.resolved_target.get(conf).?.result.get(conf);
57
54 // Update generated files58 // Update generated files
55 if (maybe_output_dir) |output_dir| {59 if (maybe_output_dir) |output_dir| {
56 if (compile.emit_directory) |lp| {60 if (conf_comp.emit_directory.value) |gf| maker.generatedPath(gf).* = output_dir;
57 lp.path = try allocPrint(process_arena, "{f}", .{output_dir});61 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_bin.value, .bin);
58 }62 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_pdb.value, .pdb);
5963 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_implib.value, .implib);
60 // zig fmt: off64 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_h.value, .h);
61 if (compile.generated_bin) |lp| lp.path = compile.outputPath(output_dir, .bin);65 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_docs.value, .docs);
62 if (compile.generated_pdb) |lp| lp.path = compile.outputPath(output_dir, .pdb);66 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_asm.value, .@"asm");
63 // hack for stage2_x86_64 + coff67 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_llvm_ir.value, .llvm_ir);
64 if (compile.generated_compiler_rt_dyn_lib) |lp| lp.path = compile.outputPath(output_dir, .compiler_rt_dyn_lib);68 try updateGeneratedFile(&conf_comp, maker, output_dir, &target, conf_comp.generated_llvm_bc.value, .llvm_bc);
65 if (compile.generated_implib) |lp| lp.path = compile.outputPath(output_dir, .implib);
66 if (compile.generated_h) |lp| lp.path = compile.outputPath(output_dir, .h);
67 if (compile.generated_docs) |lp| lp.path = compile.outputPath(output_dir, .docs);
68 if (compile.generated_asm) |lp| lp.path = compile.outputPath(output_dir, .@"asm");
69 if (compile.generated_llvm_ir) |lp| lp.path = compile.outputPath(output_dir, .llvm_ir);
70 if (compile.generated_llvm_bc) |lp| lp.path = compile.outputPath(output_dir, .llvm_bc);
71 // zig fmt: on
72 }69 }
7370
74 if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic and71 if (conf_comp.flags3.kind == .lib and conf_comp.flags2.linkage == .dynamic and
75 compile.version != null and compile.generated_bin != null and72 conf_comp.version.value != null and conf_comp.generated_bin.value != null and
76 std.Build.wantSharedLibSymLinks(compile.rootModuleTarget()))73 target.flags.os_tag != .windows)
77 {74 {
75 if (true) @panic("TODO");
78 try doAtomicSymLinks(76 try doAtomicSymLinks(
79 step,77 step,
80 compile.getEmittedBin().getPath2(step),78 conf_comp.getEmittedBin().getPath2(step),
81 compile.major_only_filename.?,79 conf_comp.major_only_filename.?,
82 compile.name_only_filename.?,80 conf_comp.name_only_filename.?,
83 );81 );
84 }82 }
85}83}
8684
85fn updateGeneratedFile(
86 conf_comp: *const Configuration.Step.Compile,
87 maker: *Maker,
88 out_path: std.Build.Cache.Path,
89 target: *const Configuration.TargetQuery,
90 opt_gf: ?Configuration.GeneratedFileIndex,
91 ea: std.zig.EmitArtifact,
92) Allocator.Error!void {
93 const gf = opt_gf orelse return;
94 const graph = maker.graph;
95 const conf = &maker.scanned_config.configuration;
96 const arena = graph.arena; // TODO don't leak into process arena
97 const name = try ea.cacheName(arena, .{
98 .root_name = conf_comp.root_name.slice(conf),
99 .cpu_arch = target.flags.cpu_arch.unwrap().?,
100 .os_tag = target.flags.os_tag.unwrap().?,
101 .ofmt = target.flags.object_format.unwrap().?,
102 .abi = target.flags.abi.unwrap().?,
103 .output_mode = switch (conf_comp.flags3.kind) {
104 .lib => .Lib,
105 .obj, .test_obj => .Obj,
106 .exe, .@"test" => .Exe,
107 },
108 .link_mode = conf_comp.flags2.linkage.unwrap(),
109 .version = if (conf_comp.version.value) |v|
110 std.SemanticVersion.parse(v.slice(conf)) catch unreachable
111 else
112 null,
113 });
114 maker.generatedPath(gf).* = try out_path.join(arena, name);
115}
116
87/// List of importable modules in a compilation's module graph, including117/// List of importable modules in a compilation's module graph, including
88/// the root module. The root module is guaranteed to be first.118/// the root module. The root module is guaranteed to be first.
89const ModuleList = std.AutoArrayHashMapUnmanaged(Configuration.Module.Index, Configuration.String);119const ModuleList = std.AutoArrayHashMapUnmanaged(Configuration.Module.Index, Configuration.String);
...@@ -154,7 +184,7 @@ fn lowerZigArgs(...@@ -154,7 +184,7 @@ fn lowerZigArgs(
154 const root_module = conf_comp.root_module.get(conf);184 const root_module = conf_comp.root_module.get(conf);
155185
156 if (root_module.resolved_target.get(conf).?.query.unwrap()) |query| {186 if (root_module.resolved_target.get(conf).?.query.unwrap()) |query| {
157 if (query.get(conf).flags.object_format.get()) |ofmt| {187 if (query.get(conf).flags.object_format.unwrap()) |ofmt| {
158 try zig_args.append(gpa, try allocPrint(arena, "-ofmt={t}", .{ofmt}));188 try zig_args.append(gpa, try allocPrint(arena, "-ofmt={t}", .{ofmt}));
159 }189 }
160 }190 }
...@@ -1146,6 +1176,7 @@ fn runPkgConfig(compile: *const Compile, maker: *const Maker, lib_name: []const...@@ -1146,6 +1176,7 @@ fn runPkgConfig(compile: *const Compile, maker: *const Maker, lib_name: []const
1146}1176}
11471177
1148fn checkCompileErrors(compile: *Compile, maker: *Maker) !void {1178fn checkCompileErrors(compile: *Compile, maker: *Maker) !void {
1179 if (true) @panic("TODO");
1149 // Clear this field so that it does not get printed by the build runner.1180 // Clear this field so that it does not get printed by the build runner.
1150 const actual_eb = compile.step.result_error_bundle;1181 const actual_eb = compile.step.result_error_bundle;
1151 compile.step.result_error_bundle = .empty;1182 compile.step.result_error_bundle = .empty;
lib/compiler/Maker/WebServer.zig+8-4
...@@ -757,12 +757,16 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -757,12 +757,16 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
757 });757 });
758 return error.WasmCompilationFailed;758 return error.WasmCompilationFailed;
759 };759 };
760 const target = std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{
761 .arch_os_abi = arch_os_abi,
762 .cpu_features = cpu_features,
763 }) catch unreachable) catch unreachable;
760 const bin_name = try std.zig.binNameAlloc(arena, .{764 const bin_name = try std.zig.binNameAlloc(arena, .{
761 .root_name = root_name,765 .root_name = root_name,
762 .target = &(std.zig.system.resolveTargetQuery(io, std.Build.parseTargetQuery(.{766 .cpu_arch = target.cpu.arch,
763 .arch_os_abi = arch_os_abi,767 .os_tag = target.os.tag,
764 .cpu_features = cpu_features,768 .ofmt = target.ofmt,
765 }) catch unreachable) catch unreachable),769 .abi = target.abi,
766 .output_mode = .Exe,770 .output_mode = .Exe,
767 });771 });
768 return base_path.join(arena, bin_name);772 return base_path.join(arena, bin_name);
lib/std/Build/Configuration.zig+21-2
...@@ -768,6 +768,14 @@ pub const Step = extern struct {...@@ -768,6 +768,14 @@ pub const Step = extern struct {
768 .dynamic => .dynamic,768 .dynamic => .dynamic,
769 };769 };
770 }770 }
771
772 pub fn unwrap(this: @This()) ?std.builtin.LinkMode {
773 return switch (this) {
774 .static => .static,
775 .dynamic => .dynamic,
776 .default => null,
777 };
778 }
771 };779 };
772 pub const Kind = enum(u3) {780 pub const Kind = enum(u3) {
773 exe,781 exe,
...@@ -1838,6 +1846,12 @@ pub const TargetQuery = struct {...@@ -1838,6 +1846,12 @@ pub const TargetQuery = struct {
1838 // TODO comptime assert the enums match1846 // TODO comptime assert the enums match
1839 return @enumFromInt(@intFromEnum(x orelse return .default));1847 return @enumFromInt(@intFromEnum(x orelse return .default));
1840 }1848 }
1849
1850 pub fn unwrap(this: @This()) ?std.Target.Cpu.Arch {
1851 // TODO comptime assert the enums match
1852 if (this == .default) return null;
1853 return @enumFromInt(@intFromEnum(this));
1854 }
1841 };1855 };
1842 pub const OsTag = enum(u6) {1856 pub const OsTag = enum(u6) {
1843 freestanding,1857 freestanding,
...@@ -1913,7 +1927,7 @@ pub const TargetQuery = struct {...@@ -1913,7 +1927,7 @@ pub const TargetQuery = struct {
1913 return @enumFromInt(@intFromEnum(x orelse return .default));1927 return @enumFromInt(@intFromEnum(x orelse return .default));
1914 }1928 }
19151929
1916 pub fn get(this: @This()) ?std.Target.ObjectFormat {1930 pub fn unwrap(this: @This()) ?std.Target.ObjectFormat {
1917 return switch (this) {1931 return switch (this) {
1918 .c => .c,1932 .c => .c,
1919 .coff => .coff,1933 .coff => .coff,
...@@ -2018,11 +2032,16 @@ pub const Storage = enum {...@@ -2018,11 +2032,16 @@ pub const Storage = enum {
20182032
2019 pub const storage: Storage = .extended;2033 pub const storage: Storage = .extended;
20202034
2035 pub fn tag(this: @This(), c: *const Configuration) @FieldType(BaseFlags, "tag") {
2036 const base_flags: BaseFlags = @bitCast(c.extra[@intFromEnum(this)]);
2037 return base_flags.tag;
2038 }
2039
2021 pub fn get(this: @This(), buffer: []const u32) U {2040 pub fn get(this: @This(), buffer: []const u32) U {
2022 var i: usize = @intFromEnum(this);2041 var i: usize = @intFromEnum(this);
2023 const base_flags: BaseFlags = @bitCast(buffer[i]);2042 const base_flags: BaseFlags = @bitCast(buffer[i]);
2024 return switch (base_flags.tag) {2043 return switch (base_flags.tag) {
2025 inline else => |tag| @unionInit(U, @tagName(tag), data(buffer, &i, @FieldType(U, @tagName(tag)))),2044 inline else => |t| @unionInit(U, @tagName(t), data(buffer, &i, @FieldType(U, @tagName(t)))),
2026 };2045 };
2027 }2046 }
2028 };2047 };
lib/std/Build/Step/Compile.zig+4-1
...@@ -384,7 +384,10 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -384,7 +384,10 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
384384
385 const out_filename = std.zig.binNameAlloc(arena, .{385 const out_filename = std.zig.binNameAlloc(arena, .{
386 .root_name = name,386 .root_name = name,
387 .target = target,387 .cpu_arch = target.cpu.arch,
388 .os_tag = target.os.tag,
389 .ofmt = target.ofmt,
390 .abi = target.abi,
388 .output_mode = switch (options.kind) {391 .output_mode = switch (options.kind) {
389 .lib => .Lib,392 .lib => .Lib,
390 .obj, .test_obj => .Obj,393 .obj, .test_obj => .Obj,
lib/std/zig.zig+22-14
...@@ -146,7 +146,10 @@ pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {...@@ -146,7 +146,10 @@ pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
146146
147pub const BinNameOptions = struct {147pub const BinNameOptions = struct {
148 root_name: []const u8,148 root_name: []const u8,
149 target: *const std.Target,149 cpu_arch: std.Target.Cpu.Arch,
150 os_tag: std.Target.Os.Tag,
151 ofmt: std.Target.ObjectFormat,
152 abi: std.Target.Abi,
150 output_mode: std.builtin.OutputMode,153 output_mode: std.builtin.OutputMode,
151 link_mode: ?std.builtin.LinkMode = null,154 link_mode: ?std.builtin.LinkMode = null,
152 version: ?std.SemanticVersion = null,155 version: ?std.SemanticVersion = null,
...@@ -155,10 +158,12 @@ pub const BinNameOptions = struct {...@@ -155,10 +158,12 @@ pub const BinNameOptions = struct {
155/// Returns the standard file system basename of a binary generated by the Zig compiler.158/// Returns the standard file system basename of a binary generated by the Zig compiler.
156pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {159pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
157 const root_name = options.root_name;160 const root_name = options.root_name;
158 const t = options.target;161 switch (options.ofmt) {
159 switch (t.ofmt) {
160 .coff => switch (options.output_mode) {162 .coff => switch (options.output_mode) {
161 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),163 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{
164 root_name,
165 options.os_tag.exeFileExt(options.cpu_arch),
166 }),
162 .Lib => {167 .Lib => {
163 const suffix = switch (options.link_mode orelse .static) {168 const suffix = switch (options.link_mode orelse .static) {
164 .static => ".lib",169 .static => ".lib",
...@@ -173,16 +178,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe...@@ -173,16 +178,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
173 .Lib => {178 .Lib => {
174 switch (options.link_mode orelse .static) {179 switch (options.link_mode orelse .static) {
175 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{180 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
176 t.libPrefix(), root_name,181 options.os_tag.libPrefix(options.abi), root_name,
177 }),182 }),
178 .dynamic => {183 .dynamic => {
179 if (options.version) |ver| {184 if (options.version) |ver| {
180 return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{185 return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
181 t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,186 options.os_tag.libPrefix(options.abi), root_name, ver.major, ver.minor, ver.patch,
182 });187 });
183 } else {188 } else {
184 return std.fmt.allocPrint(allocator, "{s}{s}.so", .{189 return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
185 t.libPrefix(), root_name,190 options.os_tag.libPrefix(options.abi), root_name,
186 });191 });
187 }192 }
188 },193 },
...@@ -195,16 +200,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe...@@ -195,16 +200,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
195 .Lib => {200 .Lib => {
196 switch (options.link_mode orelse .static) {201 switch (options.link_mode orelse .static) {
197 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{202 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
198 t.libPrefix(), root_name,203 options.os_tag.libPrefix(options.abi), root_name,
199 }),204 }),
200 .dynamic => {205 .dynamic => {
201 if (options.version) |ver| {206 if (options.version) |ver| {
202 return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{207 return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
203 t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,208 options.os_tag.libPrefix(options.abi), root_name, ver.major, ver.minor, ver.patch,
204 });209 });
205 } else {210 } else {
206 return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{211 return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
207 t.libPrefix(), root_name,212 options.os_tag.libPrefix(options.abi), root_name,
208 });213 });
209 }214 }
210 },215 },
...@@ -213,11 +218,14 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe...@@ -213,11 +218,14 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
213 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),218 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
214 },219 },
215 .wasm => switch (options.output_mode) {220 .wasm => switch (options.output_mode) {
216 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),221 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{
222 root_name,
223 options.os_tag.exeFileExt(options.cpu_arch),
224 }),
217 .Lib => {225 .Lib => {
218 switch (options.link_mode orelse .static) {226 switch (options.link_mode orelse .static) {
219 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{227 .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
220 t.libPrefix(), root_name,228 options.os_tag.libPrefix(options.abi), root_name,
221 }),229 }),
222 .dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),230 .dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
223 }231 }
...@@ -231,10 +239,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe...@@ -231,10 +239,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
231 .plan9 => switch (options.output_mode) {239 .plan9 => switch (options.output_mode) {
232 .Exe => return allocator.dupe(u8, root_name),240 .Exe => return allocator.dupe(u8, root_name),
233 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{241 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
234 root_name, t.ofmt.fileExt(t.cpu.arch),242 root_name, options.ofmt.fileExt(options.cpu_arch),
235 }),243 }),
236 .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{244 .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
237 t.libPrefix(), root_name,245 options.os_tag.libPrefix(options.abi), root_name,
238 }),246 }),
239 },247 },
240 }248 }