mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
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.
This commit is contained in:
parent
52de2802c4
commit
7c05330287
@ -25,26 +25,48 @@ pub fn main() !void {
|
||||
std.os.exit(1);
|
||||
}
|
||||
const zig_lib_dir = args[1];
|
||||
var cli_args = args[2..];
|
||||
|
||||
var zig_integration = false;
|
||||
if (cli_args.len > 0 and std.mem.eql(u8, cli_args[0], "--zig-integration")) {
|
||||
zig_integration = true;
|
||||
cli_args = args[3..];
|
||||
}
|
||||
|
||||
var error_handler: ErrorHandler = switch (zig_integration) {
|
||||
true => .{
|
||||
.server = .{
|
||||
.out = std.io.getStdOut(),
|
||||
.in = undefined, // won't be receiving messages
|
||||
.receive_fifo = undefined, // won't be receiving messages
|
||||
},
|
||||
},
|
||||
false => .{
|
||||
.tty = stderr_config,
|
||||
},
|
||||
};
|
||||
|
||||
var options = options: {
|
||||
var cli_diagnostics = cli.Diagnostics.init(allocator);
|
||||
defer cli_diagnostics.deinit();
|
||||
var options = cli.parse(allocator, args[2..], &cli_diagnostics) catch |err| switch (err) {
|
||||
var options = cli.parse(allocator, cli_args, &cli_diagnostics) catch |err| switch (err) {
|
||||
error.ParseError => {
|
||||
cli_diagnostics.renderToStdErr(args, stderr_config);
|
||||
try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics);
|
||||
std.os.exit(1);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
try options.maybeAppendRC(std.fs.cwd());
|
||||
|
||||
// print any warnings/notes
|
||||
cli_diagnostics.renderToStdErr(args, stderr_config);
|
||||
// If there was something printed, then add an extra newline separator
|
||||
// so that there is a clear separation between the cli diagnostics and whatever
|
||||
// gets printed after
|
||||
if (cli_diagnostics.errors.items.len > 0) {
|
||||
try stderr.writeAll("\n");
|
||||
if (!zig_integration) {
|
||||
// print any warnings/notes
|
||||
cli_diagnostics.renderToStdErr(args, stderr_config);
|
||||
// If there was something printed, then add an extra newline separator
|
||||
// so that there is a clear separation between the cli diagnostics and whatever
|
||||
// gets printed after
|
||||
if (cli_diagnostics.errors.items.len > 0) {
|
||||
try stderr.writeAll("\n");
|
||||
}
|
||||
}
|
||||
break :options options;
|
||||
};
|
||||
@ -55,6 +77,9 @@ pub fn main() !void {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow verbose when integrating with Zig via stdout
|
||||
options.verbose = false;
|
||||
|
||||
const stdout_writer = std.io.getStdOut().writer();
|
||||
if (options.verbose) {
|
||||
try options.dumpVerbose(stdout_writer);
|
||||
@ -86,13 +111,13 @@ pub fn main() !void {
|
||||
else => |e| {
|
||||
switch (e) {
|
||||
error.MsvcIncludesNotFound => {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "MSVC include paths could not be automatically detected", .{});
|
||||
try error_handler.emitMessage(allocator, .err, "MSVC include paths could not be automatically detected", .{});
|
||||
},
|
||||
error.MingwIncludesNotFound => {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "MinGW include paths could not be automatically detected", .{});
|
||||
try error_handler.emitMessage(allocator, .err, "MinGW include paths could not be automatically detected", .{});
|
||||
},
|
||||
}
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .note, "to disable auto includes, use the option /:auto-includes none", .{});
|
||||
try error_handler.emitMessage(allocator, .note, "to disable auto includes, use the option /:auto-includes none", .{});
|
||||
std.os.exit(1);
|
||||
},
|
||||
};
|
||||
@ -117,20 +142,16 @@ pub fn main() !void {
|
||||
|
||||
preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {
|
||||
error.GeneratedSourceError => {
|
||||
// extra newline to separate this line from the aro errors
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessor setup (this is always a bug):\n", .{});
|
||||
aro.Diagnostics.render(&comp, stderr_config);
|
||||
try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);
|
||||
std.os.exit(1);
|
||||
},
|
||||
// ArgError can occur if e.g. the .rc file is not found
|
||||
error.ArgError, error.PreprocessError => {
|
||||
// extra newline to separate this line from the aro errors
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessing:\n", .{});
|
||||
aro.Diagnostics.render(&comp, stderr_config);
|
||||
try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp);
|
||||
std.os.exit(1);
|
||||
},
|
||||
error.StreamTooLong => {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during preprocessing: maximum file size exceeded", .{});
|
||||
try error_handler.emitMessage(allocator, .err, "failed during preprocessing: maximum file size exceeded", .{});
|
||||
std.os.exit(1);
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
@ -139,7 +160,7 @@ pub fn main() !void {
|
||||
break :full_input try preprocessed_buf.toOwnedSlice();
|
||||
} else {
|
||||
break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });
|
||||
try error_handler.emitMessage(allocator, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });
|
||||
std.os.exit(1);
|
||||
};
|
||||
}
|
||||
@ -159,14 +180,14 @@ pub fn main() !void {
|
||||
|
||||
const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {
|
||||
error.InvalidSourceMappingCollapse => {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "failed during comment removal; this is a known bug", .{});
|
||||
try error_handler.emitMessage(allocator, .err, "failed during comment removal; this is a known bug", .{});
|
||||
std.os.exit(1);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });
|
||||
try error_handler.emitMessage(allocator, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });
|
||||
std.os.exit(1);
|
||||
};
|
||||
var output_file_closed = false;
|
||||
@ -193,7 +214,7 @@ pub fn main() !void {
|
||||
.warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,
|
||||
}) catch |err| switch (err) {
|
||||
error.ParseError, error.CompileError => {
|
||||
diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
|
||||
try error_handler.emitDiagnostics(allocator, std.fs.cwd(), final_input, &diagnostics, mapping_results.mappings);
|
||||
// Delete the output file on error
|
||||
output_file.close();
|
||||
output_file_closed = true;
|
||||
@ -207,12 +228,14 @@ pub fn main() !void {
|
||||
try output_buffered_stream.flush();
|
||||
|
||||
// print any warnings/notes
|
||||
diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
|
||||
if (!zig_integration) {
|
||||
diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
|
||||
}
|
||||
|
||||
// write the depfile
|
||||
if (options.depfile_path) |depfile_path| {
|
||||
var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| {
|
||||
try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });
|
||||
try error_handler.emitMessage(allocator, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });
|
||||
std.os.exit(1);
|
||||
};
|
||||
defer depfile.close();
|
||||
@ -296,3 +319,390 @@ fn getIncludePaths(arena: std.mem.Allocator, auto_includes_option: cli.Options.A
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ErrorBundle = std.zig.ErrorBundle;
|
||||
const SourceMappings = @import("source_mapping.zig").SourceMappings;
|
||||
|
||||
const ErrorHandler = union(enum) {
|
||||
server: std.zig.Server,
|
||||
tty: std.io.tty.Config,
|
||||
|
||||
pub fn emitCliDiagnostics(
|
||||
self: *ErrorHandler,
|
||||
allocator: std.mem.Allocator,
|
||||
args: []const []const u8,
|
||||
diagnostics: *cli.Diagnostics,
|
||||
) !void {
|
||||
switch (self.*) {
|
||||
.server => |*server| {
|
||||
var error_bundle = try cliDiagnosticsToErrorBundle(allocator, diagnostics);
|
||||
defer error_bundle.deinit(allocator);
|
||||
|
||||
try server.serveErrorBundle(error_bundle);
|
||||
},
|
||||
.tty => {
|
||||
diagnostics.renderToStdErr(args, self.tty);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitAroDiagnostics(
|
||||
self: *ErrorHandler,
|
||||
allocator: std.mem.Allocator,
|
||||
fail_msg: []const u8,
|
||||
comp: *aro.Compilation,
|
||||
) !void {
|
||||
switch (self.*) {
|
||||
.server => |*server| {
|
||||
var error_bundle = try aroDiagnosticsToErrorBundle(allocator, fail_msg, comp);
|
||||
defer error_bundle.deinit(allocator);
|
||||
|
||||
try server.serveErrorBundle(error_bundle);
|
||||
},
|
||||
.tty => {
|
||||
// extra newline to separate this line from the aro errors
|
||||
try renderErrorMessage(std.io.getStdErr().writer(), self.tty, .err, "{s}\n", .{fail_msg});
|
||||
aro.Diagnostics.render(comp, self.tty);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitDiagnostics(
|
||||
self: *ErrorHandler,
|
||||
allocator: std.mem.Allocator,
|
||||
cwd: std.fs.Dir,
|
||||
source: []const u8,
|
||||
diagnostics: *Diagnostics,
|
||||
mappings: SourceMappings,
|
||||
) !void {
|
||||
switch (self.*) {
|
||||
.server => |*server| {
|
||||
var error_bundle = try diagnosticsToErrorBundle(allocator, source, diagnostics, mappings);
|
||||
defer error_bundle.deinit(allocator);
|
||||
|
||||
try server.serveErrorBundle(error_bundle);
|
||||
},
|
||||
.tty => {
|
||||
diagnostics.renderToStdErr(cwd, source, self.tty, mappings);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitMessage(
|
||||
self: *ErrorHandler,
|
||||
allocator: std.mem.Allocator,
|
||||
msg_type: @import("utils.zig").ErrorMessageType,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
switch (self.*) {
|
||||
.server => |*server| {
|
||||
// only emit errors
|
||||
if (msg_type != .err) return;
|
||||
|
||||
var error_bundle = try errorStringToErrorBundle(allocator, format, args);
|
||||
defer error_bundle.deinit(allocator);
|
||||
|
||||
try server.serveErrorBundle(error_bundle);
|
||||
},
|
||||
.tty => {
|
||||
try renderErrorMessage(std.io.getStdErr().writer(), self.tty, msg_type, format, args);
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn cliDiagnosticsToErrorBundle(
|
||||
gpa: std.mem.Allocator,
|
||||
diagnostics: *cli.Diagnostics,
|
||||
) !ErrorBundle {
|
||||
@setCold(true);
|
||||
|
||||
var bundle: ErrorBundle.Wip = undefined;
|
||||
try bundle.init(gpa);
|
||||
errdefer bundle.deinit();
|
||||
|
||||
try bundle.addRootErrorMessage(.{
|
||||
.msg = try bundle.addString("invalid command line option(s)"),
|
||||
});
|
||||
|
||||
var cur_err: ?ErrorBundle.ErrorMessage = null;
|
||||
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
|
||||
defer cur_notes.deinit(gpa);
|
||||
for (diagnostics.errors.items) |err_details| {
|
||||
switch (err_details.type) {
|
||||
.err => {
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
cur_err = .{
|
||||
.msg = try bundle.addString(err_details.msg.items),
|
||||
};
|
||||
cur_notes.clearRetainingCapacity();
|
||||
},
|
||||
.warning => cur_err = null,
|
||||
.note => {
|
||||
if (cur_err == null) continue;
|
||||
cur_err.?.notes_len += 1;
|
||||
try cur_notes.append(gpa, .{
|
||||
.msg = try bundle.addString(err_details.msg.items),
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
|
||||
return try bundle.toOwnedBundle("");
|
||||
}
|
||||
|
||||
fn diagnosticsToErrorBundle(
|
||||
gpa: std.mem.Allocator,
|
||||
source: []const u8,
|
||||
diagnostics: *Diagnostics,
|
||||
mappings: SourceMappings,
|
||||
) !ErrorBundle {
|
||||
@setCold(true);
|
||||
|
||||
var bundle: ErrorBundle.Wip = undefined;
|
||||
try bundle.init(gpa);
|
||||
errdefer bundle.deinit();
|
||||
|
||||
var msg_buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
defer msg_buf.deinit(gpa);
|
||||
var cur_err: ?ErrorBundle.ErrorMessage = null;
|
||||
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
|
||||
defer cur_notes.deinit(gpa);
|
||||
for (diagnostics.errors.items) |err_details| {
|
||||
switch (err_details.type) {
|
||||
.hint => continue,
|
||||
// Clear the current error so that notes don't bleed into unassociated errors
|
||||
.warning => {
|
||||
cur_err = null;
|
||||
continue;
|
||||
},
|
||||
.note => if (cur_err == null) continue,
|
||||
.err => {},
|
||||
}
|
||||
const corresponding_span = mappings.getCorrespondingSpan(err_details.token.line_number).?;
|
||||
const err_line = corresponding_span.start_line;
|
||||
const err_filename = mappings.files.get(corresponding_span.filename_offset);
|
||||
|
||||
const source_line_start = err_details.token.getLineStartForErrorDisplay(source);
|
||||
// Treat tab stops as 1 column wide for error display purposes,
|
||||
// and add one to get a 1-based column
|
||||
const column = err_details.token.calculateColumn(source, 1, source_line_start) + 1;
|
||||
|
||||
msg_buf.clearRetainingCapacity();
|
||||
try err_details.render(msg_buf.writer(gpa), source, diagnostics.strings.items);
|
||||
|
||||
const src_loc = src_loc: {
|
||||
var src_loc: ErrorBundle.SourceLocation = .{
|
||||
.src_path = try bundle.addString(err_filename),
|
||||
.line = @intCast(err_line - 1), // 1-based -> 0-based
|
||||
.column = @intCast(column - 1), // 1-based -> 0-based
|
||||
.span_start = 0,
|
||||
.span_main = 0,
|
||||
.span_end = 0,
|
||||
};
|
||||
if (err_details.print_source_line) {
|
||||
const source_line = err_details.token.getLineForErrorDisplay(source, source_line_start);
|
||||
const visual_info = err_details.visualTokenInfo(source_line_start, source_line_start + source_line.len);
|
||||
src_loc.span_start = @intCast(visual_info.point_offset - visual_info.before_len);
|
||||
src_loc.span_main = @intCast(visual_info.point_offset);
|
||||
src_loc.span_end = @intCast(visual_info.point_offset + 1 + visual_info.after_len);
|
||||
src_loc.source_line = try bundle.addString(source_line);
|
||||
}
|
||||
break :src_loc try bundle.addSourceLocation(src_loc);
|
||||
};
|
||||
|
||||
switch (err_details.type) {
|
||||
.err => {
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
cur_err = .{
|
||||
.msg = try bundle.addString(msg_buf.items),
|
||||
.src_loc = src_loc,
|
||||
};
|
||||
cur_notes.clearRetainingCapacity();
|
||||
},
|
||||
.note => {
|
||||
cur_err.?.notes_len += 1;
|
||||
try cur_notes.append(gpa, .{
|
||||
.msg = try bundle.addString(msg_buf.items),
|
||||
.src_loc = src_loc,
|
||||
});
|
||||
},
|
||||
.warning, .hint => unreachable,
|
||||
}
|
||||
}
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
|
||||
return try bundle.toOwnedBundle("");
|
||||
}
|
||||
|
||||
fn flushErrorMessageIntoBundle(wip: *ErrorBundle.Wip, msg: ErrorBundle.ErrorMessage, notes: []const ErrorBundle.ErrorMessage) !void {
|
||||
try wip.addRootErrorMessage(msg);
|
||||
const notes_start = try wip.reserveNotes(@intCast(notes.len));
|
||||
for (notes_start.., notes) |i, note| {
|
||||
wip.extra.items[i] = @intFromEnum(wip.addErrorMessageAssumeCapacity(note));
|
||||
}
|
||||
}
|
||||
|
||||
fn errorStringToErrorBundle(allocator: std.mem.Allocator, comptime format: []const u8, args: anytype) !ErrorBundle {
|
||||
@setCold(true);
|
||||
var bundle: ErrorBundle.Wip = undefined;
|
||||
try bundle.init(allocator);
|
||||
errdefer bundle.deinit();
|
||||
try bundle.addRootErrorMessage(.{
|
||||
.msg = try bundle.printString(format, args),
|
||||
});
|
||||
return try bundle.toOwnedBundle("");
|
||||
}
|
||||
|
||||
fn aroDiagnosticsToErrorBundle(
|
||||
gpa: std.mem.Allocator,
|
||||
fail_msg: []const u8,
|
||||
comp: *aro.Compilation,
|
||||
) !ErrorBundle {
|
||||
@setCold(true);
|
||||
|
||||
var bundle: ErrorBundle.Wip = undefined;
|
||||
try bundle.init(gpa);
|
||||
errdefer bundle.deinit();
|
||||
|
||||
try bundle.addRootErrorMessage(.{
|
||||
.msg = try bundle.addString(fail_msg),
|
||||
});
|
||||
|
||||
var msg_writer = MsgWriter.init(gpa);
|
||||
defer msg_writer.deinit();
|
||||
var cur_err: ?ErrorBundle.ErrorMessage = null;
|
||||
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{};
|
||||
defer cur_notes.deinit(gpa);
|
||||
for (comp.diagnostics.list.items) |msg| {
|
||||
switch (msg.kind) {
|
||||
// Clear the current error so that notes don't bleed into unassociated errors
|
||||
.off, .warning => {
|
||||
cur_err = null;
|
||||
continue;
|
||||
},
|
||||
.note => if (cur_err == null) continue,
|
||||
.@"fatal error", .@"error" => {},
|
||||
.default => unreachable,
|
||||
}
|
||||
msg_writer.resetRetainingCapacity();
|
||||
aro.Diagnostics.renderMessage(comp, &msg_writer, msg);
|
||||
|
||||
const src_loc = src_loc: {
|
||||
if (msg_writer.path) |src_path| {
|
||||
var src_loc: ErrorBundle.SourceLocation = .{
|
||||
.src_path = try bundle.addString(src_path),
|
||||
.line = msg_writer.line - 1, // 1-based -> 0-based
|
||||
.column = msg_writer.col - 1, // 1-based -> 0-based
|
||||
.span_start = 0,
|
||||
.span_main = 0,
|
||||
.span_end = 0,
|
||||
};
|
||||
if (msg_writer.source_line) |source_line| {
|
||||
src_loc.span_start = msg_writer.span_main;
|
||||
src_loc.span_main = msg_writer.span_main;
|
||||
src_loc.span_end = msg_writer.span_main;
|
||||
src_loc.source_line = try bundle.addString(source_line);
|
||||
}
|
||||
break :src_loc try bundle.addSourceLocation(src_loc);
|
||||
}
|
||||
break :src_loc ErrorBundle.SourceLocationIndex.none;
|
||||
};
|
||||
|
||||
switch (msg.kind) {
|
||||
.@"fatal error", .@"error" => {
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
cur_err = .{
|
||||
.msg = try bundle.addString(msg_writer.buf.items),
|
||||
.src_loc = src_loc,
|
||||
};
|
||||
cur_notes.clearRetainingCapacity();
|
||||
},
|
||||
.note => {
|
||||
cur_err.?.notes_len += 1;
|
||||
try cur_notes.append(gpa, .{
|
||||
.msg = try bundle.addString(msg_writer.buf.items),
|
||||
.src_loc = src_loc,
|
||||
});
|
||||
},
|
||||
.off, .warning, .default => unreachable,
|
||||
}
|
||||
}
|
||||
if (cur_err) |err| {
|
||||
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
|
||||
}
|
||||
|
||||
return try bundle.toOwnedBundle("");
|
||||
}
|
||||
|
||||
// Similar to aro.Diagnostics.MsgWriter but:
|
||||
// - Writers to an ArrayList
|
||||
// - Only prints the message itself (no location, source line, error: prefix, etc)
|
||||
// - Keeps track of source path/line/col instead
|
||||
const MsgWriter = struct {
|
||||
buf: std.ArrayList(u8),
|
||||
path: ?[]const u8 = null,
|
||||
// 1-indexed
|
||||
line: u32 = undefined,
|
||||
col: u32 = undefined,
|
||||
source_line: ?[]const u8 = null,
|
||||
span_main: u32 = undefined,
|
||||
|
||||
fn init(allocator: std.mem.Allocator) MsgWriter {
|
||||
return .{
|
||||
.buf = std.ArrayList(u8).init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(m: *MsgWriter) void {
|
||||
m.buf.deinit();
|
||||
}
|
||||
|
||||
fn resetRetainingCapacity(m: *MsgWriter) void {
|
||||
m.buf.clearRetainingCapacity();
|
||||
m.path = null;
|
||||
m.source_line = null;
|
||||
}
|
||||
|
||||
pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {
|
||||
m.buf.writer().print(fmt, args) catch {};
|
||||
}
|
||||
|
||||
pub fn write(m: *MsgWriter, msg: []const u8) void {
|
||||
m.buf.writer().writeAll(msg) catch {};
|
||||
}
|
||||
|
||||
pub fn setColor(m: *MsgWriter, color: std.io.tty.Color) void {
|
||||
_ = m;
|
||||
_ = color;
|
||||
}
|
||||
|
||||
pub fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void {
|
||||
m.path = path;
|
||||
m.line = line;
|
||||
m.col = col;
|
||||
}
|
||||
|
||||
pub fn start(m: *MsgWriter, kind: aro.Diagnostics.Kind) void {
|
||||
_ = m;
|
||||
_ = kind;
|
||||
}
|
||||
|
||||
pub fn end(m: *MsgWriter, maybe_line: ?[]const u8, col: u32, end_with_splice: bool) void {
|
||||
_ = end_with_splice;
|
||||
m.source_line = maybe_line;
|
||||
m.span_main = col;
|
||||
}
|
||||
};
|
||||
|
||||
@ -82,9 +82,11 @@ pub fn isNonAsciiDigit(c: u21) bool {
|
||||
};
|
||||
}
|
||||
|
||||
pub const ErrorMessageType = enum { err, warning, note };
|
||||
|
||||
/// Used for generic colored errors/warnings/notes, more context-specific error messages
|
||||
/// are handled elsewhere.
|
||||
pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: enum { err, warning, note }, comptime format: []const u8, args: anytype) !void {
|
||||
pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {
|
||||
switch (msg_type) {
|
||||
.err => {
|
||||
try config.setColor(writer, .bold);
|
||||
|
||||
@ -4921,6 +4921,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
|
||||
try argv.appendSlice(&.{
|
||||
self_exe_path,
|
||||
"rc",
|
||||
"--zig-integration",
|
||||
"/:depfile",
|
||||
out_dep_path,
|
||||
"/:depfile-fmt",
|
||||
@ -4940,30 +4941,78 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
|
||||
try argv.appendSlice(rc_src.extra_flags);
|
||||
try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
|
||||
|
||||
var child = std.ChildProcess.init(argv.items, arena);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
{
|
||||
var child = std.ChildProcess.init(argv.items, arena);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Pipe;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
child.spawn() catch |err| {
|
||||
return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv.items[0], @errorName(err) });
|
||||
};
|
||||
|
||||
const stderr_reader = child.stderr.?.reader();
|
||||
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
|
||||
const term = child.wait() catch |err| {
|
||||
return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
|
||||
};
|
||||
var poller = std.io.poll(comp.gpa, enum { stdout }, .{
|
||||
.stdout = child.stdout.?,
|
||||
});
|
||||
defer poller.deinit();
|
||||
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
log.err("zig rc failed with stderr:\n{s}", .{stderr});
|
||||
return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
|
||||
const stdout = poller.fifo(.stdout);
|
||||
|
||||
poll: while (true) {
|
||||
while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
|
||||
if (!(try poller.poll())) break :poll;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.err("zig rc terminated with stderr:\n{s}", .{stderr});
|
||||
return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
|
||||
},
|
||||
const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
|
||||
while (stdout.readableLength() < header.bytes_len) {
|
||||
if (!(try poller.poll())) break :poll;
|
||||
}
|
||||
const body = stdout.readableSliceOfLen(header.bytes_len);
|
||||
|
||||
switch (header.tag) {
|
||||
// We expect exactly one ErrorBundle, and if any error_bundle header is
|
||||
// sent then it's a fatal error.
|
||||
.error_bundle => {
|
||||
const EbHdr = std.zig.Server.Message.ErrorBundle;
|
||||
const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
|
||||
const extra_bytes =
|
||||
body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
|
||||
const string_bytes =
|
||||
body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
|
||||
const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
|
||||
const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len);
|
||||
@memcpy(extra_array, unaligned_extra);
|
||||
const error_bundle = .{
|
||||
.string_bytes = try comp.gpa.dupe(u8, string_bytes),
|
||||
.extra = extra_array,
|
||||
};
|
||||
return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
|
||||
},
|
||||
else => {}, // ignore other messages
|
||||
}
|
||||
|
||||
stdout.discard(body.len);
|
||||
}
|
||||
|
||||
// Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
|
||||
const stderr_reader = child.stderr.?.reader();
|
||||
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv.items[0], @errorName(err) });
|
||||
};
|
||||
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
log.err("zig rc failed with stderr:\n{s}", .{stderr});
|
||||
return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.err("zig rc terminated with stderr:\n{s}", .{stderr});
|
||||
return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read depfile and update cache manifest
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user