authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-26 02:34:25+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-26 02:34:25+02:00
log08e1138080b40027595798572be1aad2600019f2
treec7e34dc78236924a8ab54c75c28a968ddfdab278
parent5a263ea6752da8736d00f00af5d2a75e69970259
parent50cf87f8fc01be98ec6f12ed954fbbcef0156a0d

Merge pull request 'maker: serialize compilation errors of configurer over the build protocol' (#36959) from bsp-configure-ftbfs into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36959

8 files changed, 65 insertions(+), 79 deletions(-)

lib/compiler/Maker.zig+8-2
......@@ -740,6 +740,9 @@ pub fn main(init: process.Init.Minimal) !void {
740740 var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty;
741741 defer configure_source_files.deinit(gpa);
742742
743 var configure_error_bundle: std.zig.ErrorBundle = .empty;
744 defer configure_error_bundle.deinit(gpa);
745
743746 // If this fails, we can still start the server and wait for user
744747 // to request a rebuild. If it returns error.FailedButCacheIntact
745748 // we can even still do file system watching and automatically
......@@ -762,6 +765,7 @@ pub fn main(init: process.Init.Minimal) !void {
762765 .print_configuration = print_configuration,
763766 .forks = forks.items,
764767 .src_files = &configure_source_files,
768 .error_bundle = if (protocol_server != null) &configure_error_bundle else null,
765769 })) |scanned_config| {
766770 if (help_menu) {
767771 scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) {
......@@ -1048,8 +1052,8 @@ pub fn main(init: process.Init.Minimal) !void {
10481052 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
10491053 process.exit(1);
10501054 }
1051 if (protocol_server != null) {
1052 fatal("(zig build system) TODO send error messages to client when build.zig compilation fails", .{});
1055 if (protocol_server) |s| {
1056 try s.serveErrorBundle(.bsp_configuration_failed, configure_error_bundle);
10531057 }
10541058 if (watch_flag and can_fs_watch) {
10551059 fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{});
......@@ -1087,6 +1091,7 @@ const ConfigureOptions = struct {
10871091 print_configuration: PrintConfiguration,
10881092 forks: []Fork,
10891093 src_files: *Cache.Manifest.SelfContainedFiles,
1094 error_bundle: ?*std.zig.ErrorBundle = null,
10901095};
10911096
10921097fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
......@@ -1471,6 +1476,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
14711476 .arch_os_abi = target_arch_os_abi,
14721477 .progress_node = compile_prog_node,
14731478 .skip_log_cmdline_on_compile_errors = !graph.verbose,
1479 .error_bundle = options.error_bundle,
14741480 })) |r| r.path else |err| return err;
14751481 defer gpa.free(configure_exe_path.sub_path);
14761482
lib/compiler/objcopy.zig+2-2
......@@ -234,9 +234,9 @@ fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void {
234234 // The build system already knows what the output is at this point, we
235235 // only need to communicate that the process has finished.
236236 // Use the empty error bundle to indicate that the update is done.
237 try server.serveErrorBundle(std.zig.ErrorBundle.empty);
237 try server.serveErrorBundle(.error_bundle, std.zig.ErrorBundle.empty);
238238 },
239 else => fatal("unsupported message: {s}", .{@tagName(hdr.tag)}),
239 else => fatal("unsupported message: {t}", .{hdr.tag}),
240240 }
241241 }
242242 }
lib/compiler/resinator/main.zig+4-4
......@@ -704,7 +704,7 @@ const ErrorHandler = union(enum) {
704704 var error_bundle = try cliDiagnosticsToErrorBundle(allocator, diagnostics);
705705 defer error_bundle.deinit(allocator);
706706
707 try server.serveErrorBundle(error_bundle);
707 try server.serveErrorBundle(.error_bundle, error_bundle);
708708 },
709709 .stderr => return diagnostics.renderToStderr(io, args),
710710 }
......@@ -726,7 +726,7 @@ const ErrorHandler = union(enum) {
726726 );
727727 defer error_bundle.deinit(allocator);
728728
729 try server.serveErrorBundle(error_bundle);
729 try server.serveErrorBundle(.error_bundle, error_bundle);
730730 },
731731 .stderr => {
732732 // aro errors have already been emitted
......@@ -751,7 +751,7 @@ const ErrorHandler = union(enum) {
751751 var error_bundle = try diagnosticsToErrorBundle(allocator, source, diagnostics, mappings);
752752 defer error_bundle.deinit(allocator);
753753
754 try server.serveErrorBundle(error_bundle);
754 try server.serveErrorBundle(.error_bundle, error_bundle);
755755 },
756756 .stderr => return diagnostics.renderToStderr(io, cwd, source, mappings),
757757 }
......@@ -773,7 +773,7 @@ const ErrorHandler = union(enum) {
773773 var error_bundle = try errorStringToErrorBundle(allocator, format, args);
774774 defer error_bundle.deinit(allocator);
775775
776 try server.serveErrorBundle(error_bundle);
776 try server.serveErrorBundle(.error_bundle, error_bundle);
777777 },
778778 .stderr => {
779779 const stderr = try io.lockStderr(&.{}, null);
lib/compiler/translate-c/main.zig+1-1
......@@ -105,7 +105,7 @@ fn serveErrorBundle(arena: std.mem.Allocator, io: Io, diagnostics: *const aro.Di
105105 .out = &stdout_writer.interface,
106106 .in = undefined,
107107 };
108 try server.serveErrorBundle(error_bundle);
108 try server.serveErrorBundle(.error_bundle, error_bundle);
109109}
110110
111111pub const usage =
lib/std/zig.zig+17-7
......@@ -1713,6 +1713,9 @@ pub const BuildExeSubprocessOptions = struct {
17131713 cpu_features: ?[]const u8 = null,
17141714 progress_node: std.Progress.Node = .none,
17151715 skip_log_cmdline_on_compile_errors: bool = false,
1716 /// If this is provided, compilation errors are sent here. Otherwise, they are printed to stderr.
1717 /// Must be an initialized `ErrorBundle`; if it is updated then it is cleared first.
1718 error_bundle: ?*ErrorBundle = null,
17161719};
17171720
17181721pub const BuildExeSubprocessError = error{
......@@ -1781,8 +1784,10 @@ pub fn buildExeSubprocess(
17811784 var result: ?Cache.Path = null;
17821785 defer if (result) |r| gpa.free(r.sub_path);
17831786
1784 var result_error_bundle: ErrorBundle = .empty;
1785 defer result_error_bundle.deinit(gpa);
1787 var default_error_bundle: ErrorBundle = .empty;
1788 defer default_error_bundle.deinit(gpa);
1789
1790 const error_bundle = options.error_bundle orelse &default_error_bundle;
17861791
17871792 var received_fs_inputs = false;
17881793 var cache_hit = false;
......@@ -1816,8 +1821,8 @@ pub fn buildExeSubprocess(
18161821 }
18171822 },
18181823 .error_bundle => {
1819 result_error_bundle.deinit(gpa);
1820 result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) {
1824 error_bundle.deinit(gpa);
1825 error_bundle.* = Server.allocErrorBundle(gpa, body) catch |err| switch (err) {
18211826 error.EndOfStream => break,
18221827 else => |e| return e,
18231828 };
......@@ -1884,8 +1889,8 @@ pub fn buildExeSubprocess(
18841889 return error.AlreadyReported;
18851890 }
18861891
1887 if (result_error_bundle.errorMessageCount() > 0) {
1888 result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) {
1892 if (default_error_bundle.errorMessageCount() > 0) {
1893 default_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) {
18891894 error.Canceled => |e| return e,
18901895 else => |e| {
18911896 log.err("failed rendering error bundle: {t}", .{e});
......@@ -1893,12 +1898,17 @@ pub fn buildExeSubprocess(
18931898 },
18941899 };
18951900 if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{
1896 result_error_bundle.errorMessageCount(), cmd,
1901 default_error_bundle.errorMessageCount(), cmd,
18971902 });
18981903 if (received_fs_inputs) return error.FailedButCacheIntact;
18991904 return error.AlreadyReported;
19001905 }
19011906
1907 if (error_bundle.errorMessageCount() > 0) {
1908 if (received_fs_inputs) return error.FailedButCacheIntact;
1909 return error.AlreadyReported;
1910 }
1911
19021912 const base_path = result orelse {
19031913 log.err("command failed to report result: {f}", .{cmd});
19041914 return error.AlreadyReported;
lib/std/zig/Server.zig+8-22
......@@ -83,6 +83,10 @@ pub const Message = struct {
8383 /// Body is a cwd relative path to the configuration file.
8484 /// This message only applies to the build system protocol.
8585 bsp_configuration,
86 /// build.zig itself failed to build from source.
87 /// Body is an `ErrorBundle`
88 /// This message only applies to the build system protocol.
89 bsp_configuration_failed,
8690 /// Does not have a body.
8791 /// This message only applies to the build system protocol.
8892 bsp_build_started,
......@@ -307,7 +311,7 @@ pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void {
307311 try s.out.flush();
308312}
309313
310pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
314pub fn serveErrorBundle(s: *Server, tag: Message.Tag, error_bundle: std.zig.ErrorBundle) !void {
311315 const eb_hdr: OutMessage.ErrorBundle = .{
312316 .extra_len = @intCast(error_bundle.extra.len),
313317 .string_bytes_len = @intCast(error_bundle.string_bytes.len),
......@@ -315,7 +319,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
315319 const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
316320 4 * error_bundle.extra.len + error_bundle.string_bytes.len;
317321 try s.serveMessageHeader(.{
318 .tag = .error_bundle,
322 .tag = tag,
319323 .bytes_len = @intCast(bytes_len),
320324 });
321325 try s.out.writeStruct(eb_hdr, .little);
......@@ -327,31 +331,13 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
327331pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle {
328332 var r: Reader = .fixed(body);
329333 const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) {
330 error.EndOfStream => |e| return e,
331334 error.ReadFailed => unreachable,
332 };
333
334 var eb: std.zig.ErrorBundle = .{
335 .string_bytes = &.{},
336 .extra = &.{},
337 };
338 errdefer eb.deinit(gpa);
339
340 const extra = try gpa.alloc(u32, hdr.extra_len);
341 eb.extra = extra;
342 const string_bytes = try gpa.alloc(u8, hdr.string_bytes_len);
343 eb.string_bytes = string_bytes;
344
345 r.readSliceEndian(u32, extra, .little) catch |err| switch (err) {
346335 error.EndOfStream => |e| return e,
347 error.ReadFailed => unreachable,
348336 };
349 r.readSliceAll(string_bytes) catch |err| switch (err) {
350 error.EndOfStream => |e| return e,
337 return std.zig.ErrorBundle.readAlloc(&r, gpa, hdr.extra_len, hdr.string_bytes_len) catch |err| switch (err) {
351338 error.ReadFailed => unreachable,
339 else => |e| return e,
352340 };
353
354 return eb;
355341}
356342
357343pub const TestMetadata = struct {
src/main.zig+4-4
......@@ -4453,7 +4453,7 @@ fn serve(
44534453 }
44544454
44554455 if (output.errors.errorMessageCount() != 0) {
4456 try server.serveErrorBundle(output.errors);
4456 try server.serveErrorBundle(.error_bundle, output.errors);
44574457 } else {
44584458 try server.serveEmitDigest(&output.digest, .{
44594459 .flags = .{ .cache_hit = output.cache_hit },
......@@ -4611,7 +4611,7 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
46114611 }
46124612
46134613 if (error_bundle.errorMessageCount() > 0) {
4614 try s.serveErrorBundle(error_bundle);
4614 try s.serveErrorBundle(.error_bundle, error_bundle);
46154615 return;
46164616 }
46174617
......@@ -4622,7 +4622,7 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
46224622 }
46234623
46244624 // Serve empty error bundle to indicate the update is done.
4625 try s.serveErrorBundle(std.zig.ErrorBundle.empty);
4625 try s.serveErrorBundle(.error_bundle, std.zig.ErrorBundle.empty);
46264626}
46274627
46284628fn runOrTest(
......@@ -5157,7 +5157,7 @@ fn jitCmdInner(
51575157 var error_bundle = try comp.getAllErrorsAlloc();
51585158 defer error_bundle.deinit(comp.gpa);
51595159 if (error_bundle.errorMessageCount() > 0) {
5160 try server.serveErrorBundle(error_bundle);
5160 try server.serveErrorBundle(.error_bundle, error_bundle);
51615161 process.exit(2);
51625162 }
51635163 } else {
tools/bsp.zig+21-37
......@@ -7,6 +7,7 @@ const Configuration = std.Build.Configuration;
77const Client = std.zig.Client;
88const Server = std.zig.Server;
99const log = std.log.scoped(.bsp);
10const panic = std.debug.panic;
1011
1112pub fn main(init: std.process.Init) !void {
1213 const io = init.io;
......@@ -32,7 +33,7 @@ pub fn main(init: std.process.Init) !void {
3233 .stdin = .pipe,
3334 .stdout = .pipe,
3435 .stderr = .pipe,
35 }) catch |err| std.debug.panic("failed to spawn process: {}", .{err});
36 }) catch |err| panic("failed to spawn process: {}", .{err});
3637 errdefer child_process.kill(io);
3738
3839 var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
......@@ -66,10 +67,10 @@ pub fn main(init: std.process.Init) !void {
6667 },
6768 };
6869 const body = client_stdout.take(header.bytes_len) catch unreachable;
69 log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len });
70 log.debug("received {} ({d} bytes)", .{ header.tag, body.len });
7071
7172 if (header.tag != .bsp_handshake) {
72 log.err("received unexpected message: {f}", .{fmtEnum(header.tag)});
73 log.err("received unexpected message: {}", .{header.tag});
7374 return error.UnexpectedMessage;
7475 }
7576
......@@ -94,17 +95,26 @@ pub fn main(init: std.process.Init) !void {
9495 const body = client_stdout.take(header.bytes_len) catch unreachable;
9596 log.debug("received {t} ({d} bytes)", .{ header.tag, body.len });
9697
97 if (header.tag != .bsp_configuration) {
98 log.err("received unexpected message: {f}", .{fmtEnum(header.tag)});
99 return error.UnexpectedMessage;
98 switch (header.tag) {
99 .bsp_configuration => {},
100 .bsp_configuration_failed => {
101 var eb = try Server.allocErrorBundle(gpa, body);
102 defer eb.deinit(gpa);
103 try eb.renderToStderr(io, .{}, .auto);
104 panic("configuration failed", .{});
105 },
106 else => {
107 log.err("received unexpected message: {}", .{header.tag});
108 return error.UnexpectedMessage;
109 },
100110 }
101111
102112 const configuration_path = body;
103113 var file = Io.Dir.cwd().openFile(io, configuration_path, .{}) catch |err|
104 std.debug.panic("failed to open configuration file {q}: {t}", .{ configuration_path, err });
114 panic("failed to open configuration file {q}: {t}", .{ configuration_path, err });
105115 defer file.close(io);
106116 break :configuration Configuration.loadFile(conf_arena, io, file) catch |err|
107 std.debug.panic("failed to load configuration file {q}: {t}", .{ configuration_path, err });
117 panic("failed to load configuration file {q}: {t}", .{ configuration_path, err });
108118 };
109119 const c = &configuration;
110120
......@@ -157,7 +167,7 @@ pub fn main(init: std.process.Init) !void {
157167 if (std.fmt.parseInt(u32, arg, 10)) |i|
158168 @fromBackingInt(i)
159169 else |_|
160 top_level_steps.get(arg) orelse std.debug.panic("unexpected step name or index", .{});
170 top_level_steps.get(arg) orelse panic("unexpected step name or index", .{});
161171 try steps.append(gpa, step);
162172 }
163173 }
......@@ -178,7 +188,7 @@ pub fn main(init: std.process.Init) !void {
178188 },
179189 };
180190 const body = client_stdout.take(header.bytes_len) catch unreachable;
181 log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len });
191 log.debug("received {} ({d} bytes)", .{ header.tag, body.len });
182192
183193 switch (header.tag) {
184194 .bsp_build_started => {},
......@@ -214,7 +224,7 @@ pub fn main(init: std.process.Init) !void {
214224 }
215225 },
216226 .bsp_configuration => @panic("TODO"),
217 else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}),
227 else => panic("received unexpected message: {}", .{header.tag}),
218228 }
219229 }
220230 continue;
......@@ -242,29 +252,3 @@ pub fn main(init: std.process.Init) !void {
242252 log.err("maker {f}", .{term});
243253 }
244254}
245
246const FormatEnum = union(enum) {
247 named: []const u8,
248 unnamed: usize,
249
250 pub fn format(
251 e: FormatEnum,
252 writer: *std.Io.Writer,
253 ) std.Io.Writer.Error!void {
254 switch (e) {
255 .named => |name| {
256 try writer.writeByte('.');
257 try writer.writeAll(name);
258 },
259 .unnamed => |number| try writer.print("0x{x}", .{number}),
260 }
261 }
262};
263
264fn fmtEnum(e: anytype) FormatEnum {
265 if (std.enums.tagName(@TypeOf(e), e)) |name| {
266 return .{ .named = name };
267 } else {
268 return .{ .unnamed = @backingInt(e) };
269 }
270}