stage2: properly model miscellaneous failed tasks

with error messages that go away after updates
This commit is contained in:
Andrew Kelley 2021-04-15 19:01:55 -07:00
parent 2b2920f599
commit ccdf55310b
2 changed files with 253 additions and 43 deletions

View File

@ -53,6 +53,9 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
/// This data is accessed by multiple threads and is protected by `mutex`.
failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{},
/// Miscellaneous things that can fail.
misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{},
keep_source_files_loaded: bool,
use_clang: bool,
sanitize_c: bool,
@ -256,6 +259,36 @@ pub const CObject = struct {
}
};
pub const MiscTask = enum {
write_builtin_zig,
glibc_crt_file,
glibc_shared_objects,
musl_crt_file,
mingw_crt_file,
windows_import_lib,
libunwind,
libcxx,
libcxxabi,
libtsan,
compiler_rt,
libssp,
zig_libc,
};
pub const MiscError = struct {
/// Allocated with gpa.
msg: []u8,
children: ?AllErrors = null,
pub fn deinit(misc_err: *MiscError, gpa: *Allocator) void {
gpa.free(misc_err.msg);
if (misc_err.children) |*children| {
children.deinit(gpa);
}
misc_err.* = undefined;
}
};
/// To support incremental compilation, errors are stored in various places
/// so that they can be created and destroyed appropriately. This structure
/// is used to collect all the errors from the various places into one
@ -278,6 +311,7 @@ pub const AllErrors = struct {
},
plain: struct {
msg: []const u8,
notes: []Message = &.{},
},
pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void {
@ -285,7 +319,7 @@ pub const AllErrors = struct {
const held = std.debug.getStderrMutex().acquire();
defer held.release();
const stderr = std.io.getStdErr();
return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red) catch return;
return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red, 0) catch return;
}
fn renderToStdErrInner(
@ -294,6 +328,7 @@ pub const AllErrors = struct {
stderr_file: std.fs.File,
kind: []const u8,
color: std.debug.TTY.Color,
indent: usize,
) anyerror!void {
const stderr = stderr_file.writer();
switch (msg) {
@ -305,6 +340,7 @@ pub const AllErrors = struct {
src.column + 1,
});
ttyconf.setColor(stderr, color);
try stderr.writeByteNTimes(' ', indent);
try stderr.writeAll(kind);
ttyconf.setColor(stderr, .Bold);
try stderr.print(" {s}\n", .{src.msg});
@ -318,11 +354,19 @@ pub const AllErrors = struct {
ttyconf.setColor(stderr, .Reset);
}
for (src.notes) |note| {
try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan);
try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan, indent);
}
},
.plain => |plain| {
try stderr.print("{s}: {s}\n", .{ kind, plain.msg });
ttyconf.setColor(stderr, color);
try stderr.writeByteNTimes(' ', indent);
try stderr.writeAll(kind);
ttyconf.setColor(stderr, .Reset);
try stderr.print(" {s}\n", .{plain.msg});
ttyconf.setColor(stderr, .Reset);
for (plain.notes) |note| {
try note.renderToStdErrInner(ttyconf, stderr_file, "error:", .Red, indent + 4);
}
},
}
}
@ -380,6 +424,45 @@ pub const AllErrors = struct {
) !void {
try errors.append(.{ .plain = .{ .msg = msg } });
}
fn addPlainWithChildren(
arena: *std.heap.ArenaAllocator,
errors: *std.ArrayList(Message),
msg: []const u8,
optional_children: ?AllErrors,
) !void {
const duped_msg = try arena.allocator.dupe(u8, msg);
if (optional_children) |*children| {
try errors.append(.{ .plain = .{
.msg = duped_msg,
.notes = try dupeList(children.list, &arena.allocator),
} });
} else {
try errors.append(.{ .plain = .{ .msg = duped_msg } });
}
}
fn dupeList(list: []const Message, arena: *Allocator) Allocator.Error![]Message {
const duped_list = try arena.alloc(Message, list.len);
for (list) |item, i| {
duped_list[i] = switch (item) {
.src => |src| .{ .src = .{
.msg = try arena.dupe(u8, src.msg),
.src_path = try arena.dupe(u8, src.src_path),
.line = src.line,
.column = src.column,
.byte_offset = src.byte_offset,
.source_line = if (src.source_line) |s| try arena.dupe(u8, s) else null,
.notes = try dupeList(src.notes, arena),
} },
.plain => |plain| .{ .plain = .{
.msg = try arena.dupe(u8, plain.msg),
.notes = try dupeList(plain.notes, arena),
} },
};
}
return duped_list;
}
};
pub const Directory = struct {
@ -1357,6 +1440,8 @@ pub fn destroy(self: *Compilation) void {
}
self.failed_c_objects.deinit(gpa);
self.clearMiscFailures();
self.cache_parent.manifest_dir.close();
if (self.owned_link_dir) |*dir| dir.close();
@ -1366,6 +1451,14 @@ pub fn destroy(self: *Compilation) void {
self.arena_state.promote(gpa).deinit();
}
pub fn clearMiscFailures(comp: *Compilation) void {
for (comp.misc_failures.items()) |*entry| {
entry.value.deinit(comp.gpa);
}
comp.misc_failures.deinit(comp.gpa);
comp.misc_failures = .{};
}
pub fn getTarget(self: Compilation) Target {
return self.bin_file.options.target;
}
@ -1375,6 +1468,7 @@ pub fn update(self: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
self.clearMiscFailures();
self.c_object_cache_digest_set.clearRetainingCapacity();
// For compiling C objects, we rely on the cache hash system to avoid duplicating work.
@ -1475,7 +1569,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void {
}
pub fn totalErrorCount(self: *Compilation) usize {
var total: usize = self.failed_c_objects.items().len;
var total: usize = self.failed_c_objects.count() + self.misc_failures.count();
if (self.bin_file.options.module) |module| {
total += module.failed_exports.items().len +
@ -1539,6 +1633,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
},
});
}
for (self.misc_failures.items()) |entry| {
try AllErrors.addPlainWithChildren(&arena, &errors, entry.value.msg, entry.value.children);
}
if (self.bin_file.options.module) |module| {
for (module.failed_files.items()) |entry| {
try AllErrors.add(module, &arena, &errors, entry.value.*);
@ -1775,89 +1872,160 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
},
.glibc_crt_file => |crt_file| {
glibc.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build glibc CRT file: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
@errorName(err),
});
};
},
.glibc_shared_objects => {
glibc.buildSharedObjects(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build glibc shared objects: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.glibc_shared_objects,
"unable to build glibc shared objects: {s}",
.{@errorName(err)},
);
};
},
.musl_crt_file => |crt_file| {
musl.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build musl CRT file: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.musl_crt_file,
"unable to build musl CRT file: {s}",
.{@errorName(err)},
);
};
},
.mingw_crt_file => |crt_file| {
mingw.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build mingw-w64 CRT file: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.mingw_crt_file,
"unable to build mingw-w64 CRT file: {s}",
.{@errorName(err)},
);
};
},
.windows_import_lib => |index| {
const link_lib = self.bin_file.options.system_libs.items()[index].key;
mingw.buildImportLib(self, link_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to generate DLL import .lib file: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.windows_import_lib,
"unable to generate DLL import .lib file: {s}",
.{@errorName(err)},
);
};
},
.libunwind => {
libunwind.buildStaticLib(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build libunwind: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.libunwind,
"unable to build libunwind: {s}",
.{@errorName(err)},
);
};
},
.libcxx => {
libcxx.buildLibCXX(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build libcxx: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.libcxx,
"unable to build libcxx: {s}",
.{@errorName(err)},
);
};
},
.libcxxabi => {
libcxx.buildLibCXXABI(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build libcxxabi: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.libcxxabi,
"unable to build libcxxabi: {s}",
.{@errorName(err)},
);
};
},
.libtsan => {
libtsan.buildTsan(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build TSAN library: {s}", .{@errorName(err)});
// TODO Surface more error details.
try self.setMiscFailure(
.libtsan,
"unable to build TSAN library: {s}",
.{@errorName(err)},
);
};
},
.compiler_rt_lib => {
self.buildOutputFromZig("compiler_rt.zig", .Lib, &self.compiler_rt_static_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build compiler_rt: {s}", .{@errorName(err)});
self.buildOutputFromZig(
"compiler_rt.zig",
.Lib,
&self.compiler_rt_static_lib,
.compiler_rt,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.SubCompilationFailed => continue, // error reported already
else => try self.setMiscFailure(
.compiler_rt,
"unable to build compiler_rt: {s}",
.{@errorName(err)},
),
};
},
.compiler_rt_obj => {
self.buildOutputFromZig("compiler_rt.zig", .Obj, &self.compiler_rt_obj) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build compiler_rt: {s}", .{@errorName(err)});
self.buildOutputFromZig(
"compiler_rt.zig",
.Obj,
&self.compiler_rt_obj,
.compiler_rt,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.SubCompilationFailed => continue, // error reported already
else => try self.setMiscFailure(
.compiler_rt,
"unable to build compiler_rt: {s}",
.{@errorName(err)},
),
};
},
.libssp => {
self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build libssp: {s}", .{@errorName(err)});
self.buildOutputFromZig(
"ssp.zig",
.Lib,
&self.libssp_static_lib,
.libssp,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.SubCompilationFailed => continue, // error reported already
else => try self.setMiscFailure(
.libssp,
"unable to build libssp: {s}",
.{@errorName(err)},
),
};
},
.zig_libc => {
self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to build zig's multitarget libc: {s}", .{@errorName(err)});
self.buildOutputFromZig(
"c.zig",
.Lib,
&self.libc_static_lib,
.zig_libc,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.SubCompilationFailed => continue, // error reported already
else => try self.setMiscFailure(
.zig_libc,
"unable to build zig's multitarget libc: {s}",
.{@errorName(err)},
),
};
},
.generate_builtin_zig => {
// This Job is only queued up if there is a zig module.
self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to update builtin.zig file: {s}", .{@errorName(err)});
};
try self.updateBuiltinZigFile(self.bin_file.options.module.?);
},
.stage1_module => {
if (!build_options.is_stage1)
@ -2858,13 +3026,31 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
target_util.libcNeedsLibUnwind(comp.getTarget());
}
fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void {
fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) Allocator.Error!void {
const tracy = trace(@src());
defer tracy.end();
const source = try comp.generateBuiltinZigSource(comp.gpa);
defer comp.gpa.free(source);
try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source);
mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source) catch |err| {
const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse ".";
try comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{
dir_path,
@errorName(err),
});
};
}
fn setMiscFailure(
comp: *Compilation,
tag: MiscTask,
comptime format: []const u8,
args: anytype,
) Allocator.Error!void {
try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1);
const msg = try std.fmt.allocPrint(comp.gpa, format, args);
comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg });
}
pub fn dump_argv(argv: []const []const u8) void {
@ -2874,7 +3060,7 @@ pub fn dump_argv(argv: []const []const u8) void {
std.debug.print("{s}\n", .{argv[argv.len - 1]});
}
pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Allocator.Error![]u8 {
const tracy = trace(@src());
defer tracy.end();
@ -3071,6 +3257,10 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
try sub_compilation.update();
// Look for compilation errors in this sub_compilation
// TODO instead of logging these errors, handle them in the callsites
// of updateSubCompilation and attach them as sub-errors, properly
// surfacing the errors. You can see an example of this already
// done inside buildOutputFromZig.
var errors = try sub_compilation.getAllErrorsAlloc();
defer errors.deinit(sub_compilation.gpa);
@ -3099,6 +3289,7 @@ fn buildOutputFromZig(
src_basename: []const u8,
output_mode: std.builtin.OutputMode,
out: *?CRTFile,
misc_task_tag: MiscTask,
) !void {
const tracy = trace(@src());
defer tracy.end();
@ -3173,7 +3364,23 @@ fn buildOutputFromZig(
});
defer sub_compilation.destroy();
try sub_compilation.updateSubCompilation();
try sub_compilation.update();
// Look for compilation errors in this sub_compilation.
var keep_errors = false;
var errors = try sub_compilation.getAllErrorsAlloc();
defer if (!keep_errors) errors.deinit(sub_compilation.gpa);
if (errors.list.len != 0) {
try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1);
comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{
.msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{
@tagName(misc_task_tag),
}),
.children = errors,
});
keep_errors = true;
return error.SubCompilationFailed;
}
assert(out.* == null);
out.* = Compilation.CRTFile{

View File

@ -2623,7 +2623,10 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
};
defer comp.destroy();
try updateModule(gpa, comp, .none);
updateModule(gpa, comp, .none) catch |err| switch (err) {
error.SemanticAnalyzeFail => process.exit(1),
else => |e| return e,
};
try comp.makeBinFileExecutable();
child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join(