diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 0a7ea5cb1f5520d4c606f58049c7f1185f43ae09..739ddd0bb8183c8d9040f73fc11c1341054ff828 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -740,6 +740,9 @@ pub fn main(init: process.Init.Minimal) !void { var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty; defer configure_source_files.deinit(gpa); + var configure_error_bundle: std.zig.ErrorBundle = .empty; + defer configure_error_bundle.deinit(gpa); + // If this fails, we can still start the server and wait for user // to request a rebuild. If it returns error.FailedButCacheIntact // we can even still do file system watching and automatically @@ -762,6 +765,7 @@ pub fn main(init: process.Init.Minimal) !void { .print_configuration = print_configuration, .forks = forks.items, .src_files = &configure_source_files, + .error_bundle = if (protocol_server != null) &configure_error_bundle else null, })) |scanned_config| { if (help_menu) { scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) { @@ -1048,8 +1052,8 @@ pub fn main(init: process.Init.Minimal) !void { _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; process.exit(1); } - if (protocol_server != null) { - fatal("(zig build system) TODO send error messages to client when build.zig compilation fails", .{}); + if (protocol_server) |s| { + try s.serveErrorBundle(.bsp_configuration_failed, configure_error_bundle); } if (watch_flag and can_fs_watch) { fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); @@ -1087,6 +1091,7 @@ const ConfigureOptions = struct { print_configuration: PrintConfiguration, forks: []Fork, src_files: *Cache.Manifest.SelfContainedFiles, + error_bundle: ?*std.zig.ErrorBundle = null, }; fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { @@ -1471,6 +1476,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { .arch_os_abi = target_arch_os_abi, .progress_node = compile_prog_node, .skip_log_cmdline_on_compile_errors = !graph.verbose, + .error_bundle = options.error_bundle, })) |r| r.path else |err| return err; defer gpa.free(configure_exe_path.sub_path); diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index b2b0f270bde26aa0f9e4016e5fd8a63f9d5af4bd..02573432905f61cbbdd3756db86c6e4b9a2aa3f6 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -234,9 +234,9 @@ fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void { // The build system already knows what the output is at this point, we // only need to communicate that the process has finished. // Use the empty error bundle to indicate that the update is done. - try server.serveErrorBundle(std.zig.ErrorBundle.empty); + try server.serveErrorBundle(.error_bundle, std.zig.ErrorBundle.empty); }, - else => fatal("unsupported message: {s}", .{@tagName(hdr.tag)}), + else => fatal("unsupported message: {t}", .{hdr.tag}), } } } diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index f4e03fbe305f3b86cc7a9063ffd42c1089009df2..7ba7f7445cdb930783a3aa2bac9307b0367c4b6d 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -704,7 +704,7 @@ const ErrorHandler = union(enum) { var error_bundle = try cliDiagnosticsToErrorBundle(allocator, diagnostics); defer error_bundle.deinit(allocator); - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); }, .stderr => return diagnostics.renderToStderr(io, args), } @@ -726,7 +726,7 @@ const ErrorHandler = union(enum) { ); defer error_bundle.deinit(allocator); - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); }, .stderr => { // aro errors have already been emitted @@ -751,7 +751,7 @@ const ErrorHandler = union(enum) { var error_bundle = try diagnosticsToErrorBundle(allocator, source, diagnostics, mappings); defer error_bundle.deinit(allocator); - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); }, .stderr => return diagnostics.renderToStderr(io, cwd, source, mappings), } @@ -773,7 +773,7 @@ const ErrorHandler = union(enum) { var error_bundle = try errorStringToErrorBundle(allocator, format, args); defer error_bundle.deinit(allocator); - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); }, .stderr => { const stderr = try io.lockStderr(&.{}, null); diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index e5c3961076a2daa2ec4a119c5903f175db96857c..9c99a2c214ec876a50db38ef16531f81ebde997e 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -105,7 +105,7 @@ fn serveErrorBundle(arena: std.mem.Allocator, io: Io, diagnostics: *const aro.Di .out = &stdout_writer.interface, .in = undefined, }; - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); } pub const usage = diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 57bda0bbfd85ad057dd45dae7b482f226012b2ae..286654f1bd123b8fc6732cc70a5017e970d00f68 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1713,6 +1713,9 @@ pub const BuildExeSubprocessOptions = struct { cpu_features: ?[]const u8 = null, progress_node: std.Progress.Node = .none, skip_log_cmdline_on_compile_errors: bool = false, + /// If this is provided, compilation errors are sent here. Otherwise, they are printed to stderr. + /// Must be an initialized `ErrorBundle`; if it is updated then it is cleared first. + error_bundle: ?*ErrorBundle = null, }; pub const BuildExeSubprocessError = error{ @@ -1781,8 +1784,10 @@ pub fn buildExeSubprocess( var result: ?Cache.Path = null; defer if (result) |r| gpa.free(r.sub_path); - var result_error_bundle: ErrorBundle = .empty; - defer result_error_bundle.deinit(gpa); + var default_error_bundle: ErrorBundle = .empty; + defer default_error_bundle.deinit(gpa); + + const error_bundle = options.error_bundle orelse &default_error_bundle; var received_fs_inputs = false; var cache_hit = false; @@ -1816,8 +1821,8 @@ pub fn buildExeSubprocess( } }, .error_bundle => { - result_error_bundle.deinit(gpa); - result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) { + error_bundle.deinit(gpa); + error_bundle.* = Server.allocErrorBundle(gpa, body) catch |err| switch (err) { error.EndOfStream => break, else => |e| return e, }; @@ -1884,8 +1889,8 @@ pub fn buildExeSubprocess( return error.AlreadyReported; } - if (result_error_bundle.errorMessageCount() > 0) { - result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) { + if (default_error_bundle.errorMessageCount() > 0) { + default_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| { log.err("failed rendering error bundle: {t}", .{e}); @@ -1893,12 +1898,17 @@ pub fn buildExeSubprocess( }, }; if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{ - result_error_bundle.errorMessageCount(), cmd, + default_error_bundle.errorMessageCount(), cmd, }); if (received_fs_inputs) return error.FailedButCacheIntact; return error.AlreadyReported; } + if (error_bundle.errorMessageCount() > 0) { + if (received_fs_inputs) return error.FailedButCacheIntact; + return error.AlreadyReported; + } + const base_path = result orelse { log.err("command failed to report result: {f}", .{cmd}); return error.AlreadyReported; diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index a96f186f4460c585bffb9fc8cd42f030675d172c..4c7e3a85441554dba1a6fe6d92b530d90edcbf39 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -83,6 +83,10 @@ pub const Message = struct { /// Body is a cwd relative path to the configuration file. /// This message only applies to the build system protocol. bsp_configuration, + /// build.zig itself failed to build from source. + /// Body is an `ErrorBundle` + /// This message only applies to the build system protocol. + bsp_configuration_failed, /// Does not have a body. /// This message only applies to the build system protocol. bsp_build_started, @@ -307,7 +311,7 @@ pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void { try s.out.flush(); } -pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { +pub fn serveErrorBundle(s: *Server, tag: Message.Tag, error_bundle: std.zig.ErrorBundle) !void { const eb_hdr: OutMessage.ErrorBundle = .{ .extra_len = @intCast(error_bundle.extra.len), .string_bytes_len = @intCast(error_bundle.string_bytes.len), @@ -315,7 +319,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { const bytes_len = @sizeOf(OutMessage.ErrorBundle) + 4 * error_bundle.extra.len + error_bundle.string_bytes.len; try s.serveMessageHeader(.{ - .tag = .error_bundle, + .tag = tag, .bytes_len = @intCast(bytes_len), }); try s.out.writeStruct(eb_hdr, .little); @@ -327,31 +331,13 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle { var r: Reader = .fixed(body); const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) { - error.EndOfStream => |e| return e, error.ReadFailed => unreachable, - }; - - var eb: std.zig.ErrorBundle = .{ - .string_bytes = &.{}, - .extra = &.{}, - }; - errdefer eb.deinit(gpa); - - const extra = try gpa.alloc(u32, hdr.extra_len); - eb.extra = extra; - const string_bytes = try gpa.alloc(u8, hdr.string_bytes_len); - eb.string_bytes = string_bytes; - - r.readSliceEndian(u32, extra, .little) catch |err| switch (err) { error.EndOfStream => |e| return e, - error.ReadFailed => unreachable, }; - r.readSliceAll(string_bytes) catch |err| switch (err) { - error.EndOfStream => |e| return e, + return std.zig.ErrorBundle.readAlloc(&r, gpa, hdr.extra_len, hdr.string_bytes_len) catch |err| switch (err) { error.ReadFailed => unreachable, + else => |e| return e, }; - - return eb; } pub const TestMetadata = struct { diff --git a/src/main.zig b/src/main.zig index 1bd12d4a4f7464034824f4a14360360e00a39527..5ad21b548e12b1453aa4e1345c74fa7c17af5036 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4453,7 +4453,7 @@ fn serve( } if (output.errors.errorMessageCount() != 0) { - try server.serveErrorBundle(output.errors); + try server.serveErrorBundle(.error_bundle, output.errors); } else { try server.serveEmitDigest(&output.digest, .{ .flags = .{ .cache_hit = output.cache_hit }, @@ -4611,7 +4611,7 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { } if (error_bundle.errorMessageCount() > 0) { - try s.serveErrorBundle(error_bundle); + try s.serveErrorBundle(.error_bundle, error_bundle); return; } @@ -4622,7 +4622,7 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { } // Serve empty error bundle to indicate the update is done. - try s.serveErrorBundle(std.zig.ErrorBundle.empty); + try s.serveErrorBundle(.error_bundle, std.zig.ErrorBundle.empty); } fn runOrTest( @@ -5157,7 +5157,7 @@ fn jitCmdInner( var error_bundle = try comp.getAllErrorsAlloc(); defer error_bundle.deinit(comp.gpa); if (error_bundle.errorMessageCount() > 0) { - try server.serveErrorBundle(error_bundle); + try server.serveErrorBundle(.error_bundle, error_bundle); process.exit(2); } } else { diff --git a/tools/bsp.zig b/tools/bsp.zig index e58205fd958ad569ec76fbf4703671546d0062d5..3db201ea51b499f4246f65d80ed88e9cb188bc43 100644 --- a/tools/bsp.zig +++ b/tools/bsp.zig @@ -7,6 +7,7 @@ const Configuration = std.Build.Configuration; const Client = std.zig.Client; const Server = std.zig.Server; const log = std.log.scoped(.bsp); +const panic = std.debug.panic; pub fn main(init: std.process.Init) !void { const io = init.io; @@ -32,7 +33,7 @@ pub fn main(init: std.process.Init) !void { .stdin = .pipe, .stdout = .pipe, .stderr = .pipe, - }) catch |err| std.debug.panic("failed to spawn process: {}", .{err}); + }) catch |err| panic("failed to spawn process: {}", .{err}); errdefer child_process.kill(io); var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; @@ -66,10 +67,10 @@ pub fn main(init: std.process.Init) !void { }, }; const body = client_stdout.take(header.bytes_len) catch unreachable; - log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); + log.debug("received {} ({d} bytes)", .{ header.tag, body.len }); if (header.tag != .bsp_handshake) { - log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); + log.err("received unexpected message: {}", .{header.tag}); return error.UnexpectedMessage; } @@ -94,17 +95,26 @@ pub fn main(init: std.process.Init) !void { const body = client_stdout.take(header.bytes_len) catch unreachable; log.debug("received {t} ({d} bytes)", .{ header.tag, body.len }); - if (header.tag != .bsp_configuration) { - log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); - return error.UnexpectedMessage; + switch (header.tag) { + .bsp_configuration => {}, + .bsp_configuration_failed => { + var eb = try Server.allocErrorBundle(gpa, body); + defer eb.deinit(gpa); + try eb.renderToStderr(io, .{}, .auto); + panic("configuration failed", .{}); + }, + else => { + log.err("received unexpected message: {}", .{header.tag}); + return error.UnexpectedMessage; + }, } const configuration_path = body; var file = Io.Dir.cwd().openFile(io, configuration_path, .{}) catch |err| - std.debug.panic("failed to open configuration file {q}: {t}", .{ configuration_path, err }); + panic("failed to open configuration file {q}: {t}", .{ configuration_path, err }); defer file.close(io); break :configuration Configuration.loadFile(conf_arena, io, file) catch |err| - std.debug.panic("failed to load configuration file {q}: {t}", .{ configuration_path, err }); + panic("failed to load configuration file {q}: {t}", .{ configuration_path, err }); }; const c = &configuration; @@ -157,7 +167,7 @@ pub fn main(init: std.process.Init) !void { if (std.fmt.parseInt(u32, arg, 10)) |i| @fromBackingInt(i) else |_| - top_level_steps.get(arg) orelse std.debug.panic("unexpected step name or index", .{}); + top_level_steps.get(arg) orelse panic("unexpected step name or index", .{}); try steps.append(gpa, step); } } @@ -178,7 +188,7 @@ pub fn main(init: std.process.Init) !void { }, }; const body = client_stdout.take(header.bytes_len) catch unreachable; - log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); + log.debug("received {} ({d} bytes)", .{ header.tag, body.len }); switch (header.tag) { .bsp_build_started => {}, @@ -214,7 +224,7 @@ pub fn main(init: std.process.Init) !void { } }, .bsp_configuration => @panic("TODO"), - else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}), + else => panic("received unexpected message: {}", .{header.tag}), } } continue; @@ -242,29 +252,3 @@ pub fn main(init: std.process.Init) !void { log.err("maker {f}", .{term}); } } - -const FormatEnum = union(enum) { - named: []const u8, - unnamed: usize, - - pub fn format( - e: FormatEnum, - writer: *std.Io.Writer, - ) std.Io.Writer.Error!void { - switch (e) { - .named => |name| { - try writer.writeByte('.'); - try writer.writeAll(name); - }, - .unnamed => |number| try writer.print("0x{x}", .{number}), - } - } -}; - -fn fmtEnum(e: anytype) FormatEnum { - if (std.enums.tagName(@TypeOf(e), e)) |name| { - return .{ .named = name }; - } else { - return .{ .unnamed = @backingInt(e) }; - } -}