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 {...@@ -25,26 +25,48 @@ pub fn main() !void {
25 std.os.exit(1);25 std.os.exit(1);
26 }26 }
27 const zig_lib_dir = args[1];27 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
29 var options = options: {49 var options = options: {
30 var cli_diagnostics = cli.Diagnostics.init(allocator);50 var cli_diagnostics = cli.Diagnostics.init(allocator);
31 defer cli_diagnostics.deinit();51 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) {
33 error.ParseError => {53 error.ParseError => {
34 cli_diagnostics.renderToStdErr(args, stderr_config);54 try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics);
35 std.os.exit(1);55 std.os.exit(1);
36 },56 },
37 else => |e| return e,57 else => |e| return e,
38 };58 };
39 try options.maybeAppendRC(std.fs.cwd());59 try options.maybeAppendRC(std.fs.cwd());
4060
41 // print any warnings/notes61 if (!zig_integration) {
42 cli_diagnostics.renderToStdErr(args, stderr_config);62 // print any warnings/notes
43 // If there was something printed, then add an extra newline separator63 cli_diagnostics.renderToStdErr(args, stderr_config);
44 // so that there is a clear separation between the cli diagnostics and whatever64 // If there was something printed, then add an extra newline separator
45 // gets printed after65 // so that there is a clear separation between the cli diagnostics and whatever
46 if (cli_diagnostics.errors.items.len > 0) {66 // gets printed after
47 try stderr.writeAll("\n");67 if (cli_diagnostics.errors.items.len > 0) {
68 try stderr.writeAll("\n");
69 }
48 }70 }
49 break :options options;71 break :options options;
50 };72 };
...@@ -55,6 +77,9 @@ pub fn main() !void {...@@ -55,6 +77,9 @@ pub fn main() !void {
55 return;77 return;
56 }78 }
5779
80 // Don't allow verbose when integrating with Zig via stdout
81 options.verbose = false;
82
58 const stdout_writer = std.io.getStdOut().writer();83 const stdout_writer = std.io.getStdOut().writer();
59 if (options.verbose) {84 if (options.verbose) {
60 try options.dumpVerbose(stdout_writer);85 try options.dumpVerbose(stdout_writer);
...@@ -86,13 +111,13 @@ pub fn main() !void {...@@ -86,13 +111,13 @@ pub fn main() !void {
86 else => |e| {111 else => |e| {
87 switch (e) {112 switch (e) {
88 error.MsvcIncludesNotFound => {113 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", .{});
90 },115 },
91 error.MingwIncludesNotFound => {116 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", .{});
93 },118 },
94 }119 }
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", .{});
96 std.os.exit(1);121 std.os.exit(1);
97 },122 },
98 };123 };
...@@ -117,20 +142,16 @@ pub fn main() !void {...@@ -117,20 +142,16 @@ pub fn main() !void {
117142
118 preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {143 preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {
119 error.GeneratedSourceError => {144 error.GeneratedSourceError => {
120 // extra newline to separate this line from the aro errors145 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);
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);
123 std.os.exit(1);146 std.os.exit(1);
124 },147 },
125 // ArgError can occur if e.g. the .rc file is not found148 // ArgError can occur if e.g. the .rc file is not found
126 error.ArgError, error.PreprocessError => {149 error.ArgError, error.PreprocessError => {
127 // extra newline to separate this line from the aro errors150 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp);
128 try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessing:\n", .{});
129 aro.Diagnostics.render(&comp, stderr_config);
130 std.os.exit(1);151 std.os.exit(1);
131 },152 },
132 error.StreamTooLong => {153 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", .{});
134 std.os.exit(1);155 std.os.exit(1);
135 },156 },
136 error.OutOfMemory => |e| return e,157 error.OutOfMemory => |e| return e,
...@@ -139,7 +160,7 @@ pub fn main() !void {...@@ -139,7 +160,7 @@ pub fn main() !void {
139 break :full_input try preprocessed_buf.toOwnedSlice();160 break :full_input try preprocessed_buf.toOwnedSlice();
140 } else {161 } else {
141 break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| {162 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) });
143 std.os.exit(1);164 std.os.exit(1);
144 };165 };
145 }166 }
...@@ -159,14 +180,14 @@ pub fn main() !void {...@@ -159,14 +180,14 @@ pub fn main() !void {
159180
160 const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {181 const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {
161 error.InvalidSourceMappingCollapse => {182 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", .{});
163 std.os.exit(1);184 std.os.exit(1);
164 },185 },
165 else => |e| return e,186 else => |e| return e,
166 };187 };
167188
168 var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| {189 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) });
170 std.os.exit(1);191 std.os.exit(1);
171 };192 };
172 var output_file_closed = false;193 var output_file_closed = false;
...@@ -193,7 +214,7 @@ pub fn main() !void {...@@ -193,7 +214,7 @@ pub fn main() !void {
193 .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,214 .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,
194 }) catch |err| switch (err) {215 }) catch |err| switch (err) {
195 error.ParseError, error.CompileError => {216 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);
197 // Delete the output file on error218 // Delete the output file on error
198 output_file.close();219 output_file.close();
199 output_file_closed = true;220 output_file_closed = true;
...@@ -207,12 +228,14 @@ pub fn main() !void {...@@ -207,12 +228,14 @@ pub fn main() !void {
207 try output_buffered_stream.flush();228 try output_buffered_stream.flush();
208229
209 // print any warnings/notes230 // 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
212 // write the depfile235 // write the depfile
213 if (options.depfile_path) |depfile_path| {236 if (options.depfile_path) |depfile_path| {
214 var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| {237 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) });
216 std.os.exit(1);239 std.os.exit(1);
217 };240 };
218 defer depfile.close();241 defer depfile.close();
...@@ -296,3 +319,390 @@ fn getIncludePaths(arena: std.mem.Allocator, auto_includes_option: cli.Options.A...@@ -296,3 +319,390 @@ fn getIncludePaths(arena: std.mem.Allocator, auto_includes_option: cli.Options.A
296 }319 }
297 }320 }
298}321}
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 {...@@ -82,9 +82,11 @@ pub fn isNonAsciiDigit(c: u21) bool {
82 };82 };
83}83}
8484
85pub const ErrorMessageType = enum { err, warning, note };
86
85/// Used for generic colored errors/warnings/notes, more context-specific error messages87/// Used for generic colored errors/warnings/notes, more context-specific error messages
86/// are handled elsewhere.88/// 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 {
88 switch (msg_type) {90 switch (msg_type) {
89 .err => {91 .err => {
90 try config.setColor(writer, .bold);92 try config.setColor(writer, .bold);
src/Compilation.zig+69-20
...@@ -4921,6 +4921,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4921,6 +4921,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4921 try argv.appendSlice(&.{4921 try argv.appendSlice(&.{
4922 self_exe_path,4922 self_exe_path,
4923 "rc",4923 "rc",
4924 "--zig-integration",
4924 "/:depfile",4925 "/:depfile",
4925 out_dep_path,4926 out_dep_path,
4926 "/:depfile-fmt",4927 "/:depfile-fmt",
...@@ -4940,30 +4941,78 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4940,30 +4941,78 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4940 try argv.appendSlice(rc_src.extra_flags);4941 try argv.appendSlice(rc_src.extra_flags);
4941 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });4942 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
49424943
4943 var child = std.ChildProcess.init(argv.items, arena);4944 {
4944 child.stdin_behavior = .Ignore;4945 var child = std.ChildProcess.init(argv.items, arena);
4945 child.stdout_behavior = .Ignore;4946 child.stdin_behavior = .Ignore;
4946 child.stderr_behavior = .Pipe;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();4954 var poller = std.io.poll(comp.gpa, enum { stdout }, .{
4951 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);4955 .stdout = child.stdout.?,
4952 const term = child.wait() catch |err| {4956 });
4953 return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });4957 defer poller.deinit();
4954 };
49554958
4956 switch (term) {4959 const stdout = poller.fifo(.stdout);
4957 .Exited => |code| {4960
4958 if (code != 0) {4961 poll: while (true) {
4959 log.err("zig rc failed with stderr:\n{s}", .{stderr});4962 while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
4960 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});4963 if (!(try poller.poll())) break :poll;
4961 }4964 }
4962 },4965 const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
4963 else => {4966 while (stdout.readableLength() < header.bytes_len) {
4964 log.err("zig rc terminated with stderr:\n{s}", .{stderr});4967 if (!(try poller.poll())) break :poll;
4965 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});4968 }
4966 },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 }
4967 }5016 }
49685017
4969 // Read depfile and update cache manifest5018 // Read depfile and update cache manifest