authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-03-06 21:28:49-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-03-11 05:06:16-07:00
log7c05330287b453a0095bc179c7541ab6ea53d146
tree92552fb937671d22991460d9c8e3917ce766fa61
parent52de2802c457140f3d9923cf014b51bb8c16689f

Integrate resinator errors with Zig using std.zig.Server and ErrorBundle

This takes the code that was previously in src/Compilation.zig to turn resinator diagnostics into Zig error bundles and puts it in resinator/main.zig, and then makes resinator emit the resulting error bundles via std.zig.Server (which is used by the build runner, etc). Also adds support for turning Aro diagnostics into ErrorBundles.

3 files changed, 507 insertions(+), 46 deletions(-)

lib/compiler/resinator/main.zig+435-25
......@@ -25,26 +25,48 @@ pub fn main() !void {
2525 std.os.exit(1);
2626 }
2727 const zig_lib_dir = args[1];
28 var cli_args = args[2..];
29
30 var zig_integration = false;
31 if (cli_args.len > 0 and std.mem.eql(u8, cli_args[0], "--zig-integration")) {
32 zig_integration = true;
33 cli_args = args[3..];
34 }
35
36 var error_handler: ErrorHandler = switch (zig_integration) {
37 true => .{
38 .server = .{
39 .out = std.io.getStdOut(),
40 .in = undefined, // won't be receiving messages
41 .receive_fifo = undefined, // won't be receiving messages
42 },
43 },
44 false => .{
45 .tty = stderr_config,
46 },
47 };
2848
2949 var options = options: {
3050 var cli_diagnostics = cli.Diagnostics.init(allocator);
3151 defer cli_diagnostics.deinit();
32 var options = cli.parse(allocator, args[2..], &cli_diagnostics) catch |err| switch (err) {
52 var options = cli.parse(allocator, cli_args, &cli_diagnostics) catch |err| switch (err) {
3353 error.ParseError => {
34 cli_diagnostics.renderToStdErr(args, stderr_config);
54 try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics);
3555 std.os.exit(1);
3656 },
3757 else => |e| return e,
3858 };
3959 try options.maybeAppendRC(std.fs.cwd());
4060
41 // print any warnings/notes
42 cli_diagnostics.renderToStdErr(args, stderr_config);
43 // If there was something printed, then add an extra newline separator
44 // so that there is a clear separation between the cli diagnostics and whatever
45 // gets printed after
46 if (cli_diagnostics.errors.items.len > 0) {
47 try stderr.writeAll("\n");
61 if (!zig_integration) {
62 // print any warnings/notes
63 cli_diagnostics.renderToStdErr(args, stderr_config);
64 // If there was something printed, then add an extra newline separator
65 // so that there is a clear separation between the cli diagnostics and whatever
66 // gets printed after
67 if (cli_diagnostics.errors.items.len > 0) {
68 try stderr.writeAll("\n");
69 }
4870 }
4971 break :options options;
5072 };
......@@ -55,6 +77,9 @@ pub fn main() !void {
5577 return;
5678 }
5779
80 // Don't allow verbose when integrating with Zig via stdout
81 options.verbose = false;
82
5883 const stdout_writer = std.io.getStdOut().writer();
5984 if (options.verbose) {
6085 try options.dumpVerbose(stdout_writer);
......@@ -86,13 +111,13 @@ pub fn main() !void {
86111 else => |e| {
87112 switch (e) {
88113 error.MsvcIncludesNotFound => {
89 try renderErrorMessage(stderr.writer(), stderr_config, .err, "MSVC include paths could not be automatically detected", .{});
114 try error_handler.emitMessage(allocator, .err, "MSVC include paths could not be automatically detected", .{});
90115 },
91116 error.MingwIncludesNotFound => {
92 try renderErrorMessage(stderr.writer(), stderr_config, .err, "MinGW include paths could not be automatically detected", .{});
117 try error_handler.emitMessage(allocator, .err, "MinGW include paths could not be automatically detected", .{});
93118 },
94119 }
95 try renderErrorMessage(stderr.writer(), stderr_config, .note, "to disable auto includes, use the option /:auto-includes none", .{});
120 try error_handler.emitMessage(allocator, .note, "to disable auto includes, use the option /:auto-includes none", .{});
96121 std.os.exit(1);
97122 },
98123 };
......@@ -117,20 +142,16 @@ pub fn main() !void {
117142
118143 preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {
119144 error.GeneratedSourceError => {
120 // extra newline to separate this line from the aro errors
121 try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessor setup (this is always a bug):\n", .{});
122 aro.Diagnostics.render(&comp, stderr_config);
145 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);
123146 std.os.exit(1);
124147 },
125148 // ArgError can occur if e.g. the .rc file is not found
126149 error.ArgError, error.PreprocessError => {
127 // extra newline to separate this line from the aro errors
128 try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessing:\n", .{});
129 aro.Diagnostics.render(&comp, stderr_config);
150 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp);
130151 std.os.exit(1);
131152 },
132153 error.StreamTooLong => {
133 try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessing: maximum file size exceeded", .{});
154 try error_handler.emitMessage(allocator, .err, "failed during preprocessing: maximum file size exceeded", .{});
134155 std.os.exit(1);
135156 },
136157 error.OutOfMemory => |e| return e,
......@@ -139,7 +160,7 @@ pub fn main() !void {
139160 break :full_input try preprocessed_buf.toOwnedSlice();
140161 } else {
141162 break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| {
142 try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });
163 try error_handler.emitMessage(allocator, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });
143164 std.os.exit(1);
144165 };
145166 }
......@@ -159,14 +180,14 @@ pub fn main() !void {
159180
160181 const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {
161182 error.InvalidSourceMappingCollapse => {
162 try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during comment removal; this is a known bug", .{});
183 try error_handler.emitMessage(allocator, .err, "failed during comment removal; this is a known bug", .{});
163184 std.os.exit(1);
164185 },
165186 else => |e| return e,
166187 };
167188
168189 var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| {
169 try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });
190 try error_handler.emitMessage(allocator, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });
170191 std.os.exit(1);
171192 };
172193 var output_file_closed = false;
......@@ -193,7 +214,7 @@ pub fn main() !void {
193214 .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,
194215 }) catch |err| switch (err) {
195216 error.ParseError, error.CompileError => {
196 diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
217 try error_handler.emitDiagnostics(allocator, std.fs.cwd(), final_input, &diagnostics, mapping_results.mappings);
197218 // Delete the output file on error
198219 output_file.close();
199220 output_file_closed = true;
......@@ -207,12 +228,14 @@ pub fn main() !void {
207228 try output_buffered_stream.flush();
208229
209230 // print any warnings/notes
210 diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
231 if (!zig_integration) {
232 diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
233 }
211234
212235 // write the depfile
213236 if (options.depfile_path) |depfile_path| {
214237 var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| {
215 try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });
238 try error_handler.emitMessage(allocator, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });
216239 std.os.exit(1);
217240 };
218241 defer depfile.close();
......@@ -296,3 +319,390 @@ fn getIncludePaths(arena: std.mem.Allocator, auto_includes_option: cli.Options.A
296319 }
297320 }
298321}
322
323const ErrorBundle = std.zig.ErrorBundle;
324const SourceMappings = @import("source_mapping.zig").SourceMappings;
325
326const ErrorHandler = union(enum) {
327 server: std.zig.Server,
328 tty: std.io.tty.Config,
329
330 pub fn emitCliDiagnostics(
331 self: *ErrorHandler,
332 allocator: std.mem.Allocator,
333 args: []const []const u8,
334 diagnostics: *cli.Diagnostics,
335 ) !void {
336 switch (self.*) {
337 .server => |*server| {
338 var error_bundle = try cliDiagnosticsToErrorBundle(allocator, diagnostics);
339 defer error_bundle.deinit(allocator);
340
341 try server.serveErrorBundle(error_bundle);
342 },
343 .tty => {
344 diagnostics.renderToStdErr(args, self.tty);
345 },
346 }
347 }
348
349 pub fn emitAroDiagnostics(
350 self: *ErrorHandler,
351 allocator: std.mem.Allocator,
352 fail_msg: []const u8,
353 comp: *aro.Compilation,
354 ) !void {
355 switch (self.*) {
356 .server => |*server| {
357 var error_bundle = try aroDiagnosticsToErrorBundle(allocator, fail_msg, comp);
358 defer error_bundle.deinit(allocator);
359
360 try server.serveErrorBundle(error_bundle);
361 },
362 .tty => {
363 // extra newline to separate this line from the aro errors
364 try renderErrorMessage(std.io.getStdErr().writer(), self.tty, .err, "{s}\n", .{fail_msg});
365 aro.Diagnostics.render(comp, self.tty);
366 },
367 }
368 }
369
370 pub fn emitDiagnostics(
371 self: *ErrorHandler,
372 allocator: std.mem.Allocator,
373 cwd: std.fs.Dir,
374 source: []const u8,
375 diagnostics: *Diagnostics,
376 mappings: SourceMappings,
377 ) !void {
378 switch (self.*) {
379 .server => |*server| {
380 var error_bundle = try diagnosticsToErrorBundle(allocator, source, diagnostics, mappings);
381 defer error_bundle.deinit(allocator);
382
383 try server.serveErrorBundle(error_bundle);
384 },
385 .tty => {
386 diagnostics.renderToStdErr(cwd, source, self.tty, mappings);
387 },
388 }
389 }
390
391 pub fn emitMessage(
392 self: *ErrorHandler,
393 allocator: std.mem.Allocator,
394 msg_type: @import("utils.zig").ErrorMessageType,
395 comptime format: []const u8,
396 args: anytype,
397 ) !void {
398 switch (self.*) {
399 .server => |*server| {
400 // only emit errors
401 if (msg_type != .err) return;
402
403 var error_bundle = try errorStringToErrorBundle(allocator, format, args);
404 defer error_bundle.deinit(allocator);
405
406 try server.serveErrorBundle(error_bundle);
407 },
408 .tty => {
409 try renderErrorMessage(std.io.getStdErr().writer(), self.tty, msg_type, format, args);
410 },
411 }
412 }
413};
414
415fn cliDiagnosticsToErrorBundle(
416 gpa: std.mem.Allocator,
417 diagnostics: *cli.Diagnostics,
418) !ErrorBundle {
419 @setCold(true);
420
421 var bundle: ErrorBundle.Wip = undefined;
422 try bundle.init(gpa);
423 errdefer bundle.deinit();
424
425 try bundle.addRootErrorMessage(.{
426 .msg = try bundle.addString("invalid command line option(s)"),
427 });
428
429 var cur_err: ?ErrorBundle.ErrorMessage = null;
430 var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
431 defer cur_notes.deinit(gpa);
432 for (diagnostics.errors.items) |err_details| {
433 switch (err_details.type) {
434 .err => {
435 if (cur_err) |err| {
436 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
437 }
438 cur_err = .{
439 .msg = try bundle.addString(err_details.msg.items),
440 };
441 cur_notes.clearRetainingCapacity();
442 },
443 .warning => cur_err = null,
444 .note => {
445 if (cur_err == null) continue;
446 cur_err.?.notes_len += 1;
447 try cur_notes.append(gpa, .{
448 .msg = try bundle.addString(err_details.msg.items),
449 });
450 },
451 }
452 }
453 if (cur_err) |err| {
454 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
455 }
456
457 return try bundle.toOwnedBundle("");
458}
459
460fn diagnosticsToErrorBundle(
461 gpa: std.mem.Allocator,
462 source: []const u8,
463 diagnostics: *Diagnostics,
464 mappings: SourceMappings,
465) !ErrorBundle {
466 @setCold(true);
467
468 var bundle: ErrorBundle.Wip = undefined;
469 try bundle.init(gpa);
470 errdefer bundle.deinit();
471
472 var msg_buf: std.ArrayListUnmanaged(u8) = .{};
473 defer msg_buf.deinit(gpa);
474 var cur_err: ?ErrorBundle.ErrorMessage = null;
475 var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
476 defer cur_notes.deinit(gpa);
477 for (diagnostics.errors.items) |err_details| {
478 switch (err_details.type) {
479 .hint => continue,
480 // Clear the current error so that notes don't bleed into unassociated errors
481 .warning => {
482 cur_err = null;
483 continue;
484 },
485 .note => if (cur_err == null) continue,
486 .err => {},
487 }
488 const corresponding_span = mappings.getCorrespondingSpan(err_details.token.line_number).?;
489 const err_line = corresponding_span.start_line;
490 const err_filename = mappings.files.get(corresponding_span.filename_offset);
491
492 const source_line_start = err_details.token.getLineStartForErrorDisplay(source);
493 // Treat tab stops as 1 column wide for error display purposes,
494 // and add one to get a 1-based column
495 const column = err_details.token.calculateColumn(source, 1, source_line_start) + 1;
496
497 msg_buf.clearRetainingCapacity();
498 try err_details.render(msg_buf.writer(gpa), source, diagnostics.strings.items);
499
500 const src_loc = src_loc: {
501 var src_loc: ErrorBundle.SourceLocation = .{
502 .src_path = try bundle.addString(err_filename),
503 .line = @intCast(err_line - 1), // 1-based -> 0-based
504 .column = @intCast(column - 1), // 1-based -> 0-based
505 .span_start = 0,
506 .span_main = 0,
507 .span_end = 0,
508 };
509 if (err_details.print_source_line) {
510 const source_line = err_details.token.getLineForErrorDisplay(source, source_line_start);
511 const visual_info = err_details.visualTokenInfo(source_line_start, source_line_start + source_line.len);
512 src_loc.span_start = @intCast(visual_info.point_offset - visual_info.before_len);
513 src_loc.span_main = @intCast(visual_info.point_offset);
514 src_loc.span_end = @intCast(visual_info.point_offset + 1 + visual_info.after_len);
515 src_loc.source_line = try bundle.addString(source_line);
516 }
517 break :src_loc try bundle.addSourceLocation(src_loc);
518 };
519
520 switch (err_details.type) {
521 .err => {
522 if (cur_err) |err| {
523 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
524 }
525 cur_err = .{
526 .msg = try bundle.addString(msg_buf.items),
527 .src_loc = src_loc,
528 };
529 cur_notes.clearRetainingCapacity();
530 },
531 .note => {
532 cur_err.?.notes_len += 1;
533 try cur_notes.append(gpa, .{
534 .msg = try bundle.addString(msg_buf.items),
535 .src_loc = src_loc,
536 });
537 },
538 .warning, .hint => unreachable,
539 }
540 }
541 if (cur_err) |err| {
542 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
543 }
544
545 return try bundle.toOwnedBundle("");
546}
547
548fn flushErrorMessageIntoBundle(wip: *ErrorBundle.Wip, msg: ErrorBundle.ErrorMessage, notes: []const ErrorBundle.ErrorMessage) !void {
549 try wip.addRootErrorMessage(msg);
550 const notes_start = try wip.reserveNotes(@intCast(notes.len));
551 for (notes_start.., notes) |i, note| {
552 wip.extra.items[i] = @intFromEnum(wip.addErrorMessageAssumeCapacity(note));
553 }
554}
555
556fn errorStringToErrorBundle(allocator: std.mem.Allocator, comptime format: []const u8, args: anytype) !ErrorBundle {
557 @setCold(true);
558 var bundle: ErrorBundle.Wip = undefined;
559 try bundle.init(allocator);
560 errdefer bundle.deinit();
561 try bundle.addRootErrorMessage(.{
562 .msg = try bundle.printString(format, args),
563 });
564 return try bundle.toOwnedBundle("");
565}
566
567fn aroDiagnosticsToErrorBundle(
568 gpa: std.mem.Allocator,
569 fail_msg: []const u8,
570 comp: *aro.Compilation,
571) !ErrorBundle {
572 @setCold(true);
573
574 var bundle: ErrorBundle.Wip = undefined;
575 try bundle.init(gpa);
576 errdefer bundle.deinit();
577
578 try bundle.addRootErrorMessage(.{
579 .msg = try bundle.addString(fail_msg),
580 });
581
582 var msg_writer = MsgWriter.init(gpa);
583 defer msg_writer.deinit();
584 var cur_err: ?ErrorBundle.ErrorMessage = null;
585 var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
586 defer cur_notes.deinit(gpa);
587 for (comp.diagnostics.list.items) |msg| {
588 switch (msg.kind) {
589 // Clear the current error so that notes don't bleed into unassociated errors
590 .off, .warning => {
591 cur_err = null;
592 continue;
593 },
594 .note => if (cur_err == null) continue,
595 .@"fatal error", .@"error" => {},
596 .default => unreachable,
597 }
598 msg_writer.resetRetainingCapacity();
599 aro.Diagnostics.renderMessage(comp, &msg_writer, msg);
600
601 const src_loc = src_loc: {
602 if (msg_writer.path) |src_path| {
603 var src_loc: ErrorBundle.SourceLocation = .{
604 .src_path = try bundle.addString(src_path),
605 .line = msg_writer.line - 1, // 1-based -> 0-based
606 .column = msg_writer.col - 1, // 1-based -> 0-based
607 .span_start = 0,
608 .span_main = 0,
609 .span_end = 0,
610 };
611 if (msg_writer.source_line) |source_line| {
612 src_loc.span_start = msg_writer.span_main;
613 src_loc.span_main = msg_writer.span_main;
614 src_loc.span_end = msg_writer.span_main;
615 src_loc.source_line = try bundle.addString(source_line);
616 }
617 break :src_loc try bundle.addSourceLocation(src_loc);
618 }
619 break :src_loc ErrorBundle.SourceLocationIndex.none;
620 };
621
622 switch (msg.kind) {
623 .@"fatal error", .@"error" => {
624 if (cur_err) |err| {
625 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
626 }
627 cur_err = .{
628 .msg = try bundle.addString(msg_writer.buf.items),
629 .src_loc = src_loc,
630 };
631 cur_notes.clearRetainingCapacity();
632 },
633 .note => {
634 cur_err.?.notes_len += 1;
635 try cur_notes.append(gpa, .{
636 .msg = try bundle.addString(msg_writer.buf.items),
637 .src_loc = src_loc,
638 });
639 },
640 .off, .warning, .default => unreachable,
641 }
642 }
643 if (cur_err) |err| {
644 try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
645 }
646
647 return try bundle.toOwnedBundle("");
648}
649
650// Similar to aro.Diagnostics.MsgWriter but:
651// - Writers to an ArrayList
652// - Only prints the message itself (no location, source line, error: prefix, etc)
653// - Keeps track of source path/line/col instead
654const MsgWriter = struct {
655 buf: std.ArrayList(u8),
656 path: ?[]const u8 = null,
657 // 1-indexed
658 line: u32 = undefined,
659 col: u32 = undefined,
660 source_line: ?[]const u8 = null,
661 span_main: u32 = undefined,
662
663 fn init(allocator: std.mem.Allocator) MsgWriter {
664 return .{
665 .buf = std.ArrayList(u8).init(allocator),
666 };
667 }
668
669 fn deinit(m: *MsgWriter) void {
670 m.buf.deinit();
671 }
672
673 fn resetRetainingCapacity(m: *MsgWriter) void {
674 m.buf.clearRetainingCapacity();
675 m.path = null;
676 m.source_line = null;
677 }
678
679 pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {
680 m.buf.writer().print(fmt, args) catch {};
681 }
682
683 pub fn write(m: *MsgWriter, msg: []const u8) void {
684 m.buf.writer().writeAll(msg) catch {};
685 }
686
687 pub fn setColor(m: *MsgWriter, color: std.io.tty.Color) void {
688 _ = m;
689 _ = color;
690 }
691
692 pub fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void {
693 m.path = path;
694 m.line = line;
695 m.col = col;
696 }
697
698 pub fn start(m: *MsgWriter, kind: aro.Diagnostics.Kind) void {
699 _ = m;
700 _ = kind;
701 }
702
703 pub fn end(m: *MsgWriter, maybe_line: ?[]const u8, col: u32, end_with_splice: bool) void {
704 _ = end_with_splice;
705 m.source_line = maybe_line;
706 m.span_main = col;
707 }
708};
lib/compiler/resinator/utils.zig+3-1
......@@ -82,9 +82,11 @@ pub fn isNonAsciiDigit(c: u21) bool {
8282 };
8383}
8484
85pub const ErrorMessageType = enum { err, warning, note };
86
8587/// Used for generic colored errors/warnings/notes, more context-specific error messages
8688/// are handled elsewhere.
87pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: enum { err, warning, note }, comptime format: []const u8, args: anytype) !void {
89pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {
8890 switch (msg_type) {
8991 .err => {
9092 try config.setColor(writer, .bold);
src/Compilation.zig+69-20
......@@ -4921,6 +4921,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
49214921 try argv.appendSlice(&.{
49224922 self_exe_path,
49234923 "rc",
4924 "--zig-integration",
49244925 "/:depfile",
49254926 out_dep_path,
49264927 "/:depfile-fmt",
......@@ -4940,30 +4941,78 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
49404941 try argv.appendSlice(rc_src.extra_flags);
49414942 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
49424943
4943 var child = std.ChildProcess.init(argv.items, arena);
4944 child.stdin_behavior = .Ignore;
4945 child.stdout_behavior = .Ignore;
4946 child.stderr_behavior = .Pipe;
4944 {
4945 var child = std.ChildProcess.init(argv.items, arena);
4946 child.stdin_behavior = .Ignore;
4947 child.stdout_behavior = .Pipe;
4948 child.stderr_behavior = .Pipe;
49474949
4948 try child.spawn();
4950 child.spawn() catch |err| {
4951 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv.items[0], @errorName(err) });
4952 };
49494953
4950 const stderr_reader = child.stderr.?.reader();
4951 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
4952 const term = child.wait() catch |err| {
4953 return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
4954 };
4954 var poller = std.io.poll(comp.gpa, enum { stdout }, .{
4955 .stdout = child.stdout.?,
4956 });
4957 defer poller.deinit();
49554958
4956 switch (term) {
4957 .Exited => |code| {
4958 if (code != 0) {
4959 log.err("zig rc failed with stderr:\n{s}", .{stderr});
4960 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
4959 const stdout = poller.fifo(.stdout);
4960
4961 poll: while (true) {
4962 while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
4963 if (!(try poller.poll())) break :poll;
49614964 }
4962 },
4963 else => {
4964 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
4965 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
4966 },
4965 const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
4966 while (stdout.readableLength() < header.bytes_len) {
4967 if (!(try poller.poll())) break :poll;
4968 }
4969 const body = stdout.readableSliceOfLen(header.bytes_len);
4970
4971 switch (header.tag) {
4972 // We expect exactly one ErrorBundle, and if any error_bundle header is
4973 // sent then it's a fatal error.
4974 .error_bundle => {
4975 const EbHdr = std.zig.Server.Message.ErrorBundle;
4976 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
4977 const extra_bytes =
4978 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
4979 const string_bytes =
4980 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
4981 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
4982 const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len);
4983 @memcpy(extra_array, unaligned_extra);
4984 const error_bundle = .{
4985 .string_bytes = try comp.gpa.dupe(u8, string_bytes),
4986 .extra = extra_array,
4987 };
4988 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
4989 },
4990 else => {}, // ignore other messages
4991 }
4992
4993 stdout.discard(body.len);
4994 }
4995
4996 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
4997 const stderr_reader = child.stderr.?.reader();
4998 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
4999
5000 const term = child.wait() catch |err| {
5001 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv.items[0], @errorName(err) });
5002 };
5003
5004 switch (term) {
5005 .Exited => |code| {
5006 if (code != 0) {
5007 log.err("zig rc failed with stderr:\n{s}", .{stderr});
5008 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
5009 }
5010 },
5011 else => {
5012 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
5013 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
5014 },
5015 }
49675016 }
49685017
49695018 // Read depfile and update cache manifest