From 38efc69dc98f8548d473eeee24ea0717d07af11e Mon Sep 17 00:00:00 2001 From: Techatrix Date: Tue, 21 Jul 2026 16:29:09 +0200 Subject: [PATCH] Maker: serve build status over protocol --- lib/compiler/Maker.zig | 104 +++++++++++++++++++++++++++++++++-------- lib/std/zig/Server.zig | 36 ++++++++++++++ tools/bsp.zig | 21 +++++++++ 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 61e815370fd9952163dfd9f2f0f29afb3380791d..12cf637975aa43832d01c3e84486f157606d43b4 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -897,19 +897,8 @@ pub fn main(init: process.Init.Minimal) !void { error.WriteFailed => return stderr.file_writer.err.?, }; }) { - if (web_server) |ws| ws.startBuild(); - try maker.makeSteps(main_progress_node, fuzz); - if (web_server) |ws| { - if (fuzz) |mode| if (mode != .forever) fatal( - "error: limited fuzzing is not implemented yet for --webui", - .{}, - ); - - ws.finishBuild(.{ .fuzz = fuzz != null }); - } - if (web_server) |ws| { const c = &scanned_config.configuration; assert(!watch); // fatal error after CLI parsing @@ -2254,6 +2243,12 @@ fn makeSteps( const top_level_steps = &maker.scanned_config.top_level_steps; const c = &maker.scanned_config.configuration; + if (maker.web_server) |ws| ws.startBuild(); + + if (maker.protocol_server) |s| { + try s.serveBodylessMessage(.bsp_build_started); + } + { // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer, // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking @@ -2279,6 +2274,19 @@ fn makeSteps( try group.await(io); } + if (maker.web_server) |ws| { + if (fuzz) |mode| if (mode != .forever) fatal( + "error: limited fuzzing is not implemented yet for --webui", + .{}, + ); + + ws.finishBuild(.{ .fuzz = fuzz != null }); + } + + if (maker.protocol_server) |s| { + try s.serveBodylessMessage(.bsp_build_completed); + } + assert(maker.memory_blocked_steps.items.len == 0); var test_pass_count: usize = 0; @@ -2539,6 +2547,15 @@ fn makeStep( defer step_prog_node.end(); if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip); + if (maker.protocol_server) |s| { + maker.protocol_server_mutex.lockUncancelable(io); + defer maker.protocol_server_mutex.unlock(io); + + s.serveU32Message( + .bsp_step_started, + @backingInt(step_index), + ) catch @panic("TODO propagate error when failing to send protocol message"); + } const new_state: Step.State = for (deps) |dep_index| { const dep_make_step = maker.stepByIndex(dep_index); @@ -2564,7 +2581,7 @@ fn makeStep( @atomicStore(Step.State, &make_step.state, new_state, .monotonic); - switch (new_state) { + const success = switch (new_state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, .precheck_done => unreachable, @@ -2572,17 +2589,37 @@ fn makeStep( .failure, .dependency_failure, .skipped_oom, - => { - if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure); - std.Progress.setStatus(.failure_working); - }, + => false, .success, .skipped, - => { - if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success); - }, + => true, + }; + + if (maker.web_server) |ws| { + ws.updateStepStatus(step_index, if (success) .success else .failure); } + if (maker.protocol_server != null) { + maker.protocol_server_mutex.lockUncancelable(io); + defer maker.protocol_server_mutex.unlock(io); + + const status: Server.Message.BuildStepCompleted.Status = switch (new_state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .success => .success, + .failure, .dependency_failure => .failure, + .skipped => .skipped, + .skipped_oom => .skipped_oom, + }; + serveBuildStepCompleted( + maker, + step_index, + status, + ) catch |err| std.debug.panic("TODO propagate error when failing to send protocol message: {t}", .{err}); + } + + if (!success) std.Progress.setStatus(.failure_working); } // No matter the result, we want to display error/warning messages. @@ -3152,6 +3189,35 @@ fn serveBSPHandshake(s: *const std.zig.Server) !void { try s.out.flush(); } +fn serveBuildStepCompleted( + maker: *Maker, + step_index: Configuration.Step.Index, + status: Server.Message.BuildStepCompleted.Status, +) !void { + const s: *Server = maker.protocol_server.?; + const step = maker.stepByIndex(step_index); + const error_bundle = step.result_error_bundle; + + const body: Server.Message.BuildStepCompleted = .{ + .step_index = step_index, + .status = status, + .error_bundle = .{ + .extra_len = @intCast(error_bundle.extra.len), + .string_bytes_len = @intCast(error_bundle.string_bytes.len), + }, + }; + const eb_bytes_len = @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len; + const bytes_len = @sizeOf(Server.Message.BuildStepCompleted) + eb_bytes_len; + try s.serveMessageHeader(.{ + .tag = .bsp_step_completed, + .bytes_len = @intCast(bytes_len), + }); + try s.out.writeStruct(body, .little); + try s.out.writeSliceEndian(u32, error_bundle.extra, .little); + try s.out.writeAll(error_bundle.string_bytes); + try s.out.flush(); +} + fn initStdoutWriter(io: Io) *Writer { stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation); return &stdout_writer_allocation.interface; diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 1da63f95310b561f4264771a271fe70346c82fe7..1f6d208084abdcc9d93ef33929e36aa0239549af 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -82,6 +82,18 @@ 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, + /// Does not have a body. + /// This message only applies to the build system protocol. + bsp_build_started, + /// Does not have a body. + /// This message only applies to the build system protocol. + bsp_build_completed, + /// Body is a `Configuration.Step.Index`. + /// This message only applies to the build system protocol. + bsp_step_started, + /// Body is a `BuildStepCompleted`. + /// This message only applies to the build system protocol. + bsp_step_completed, _, }; @@ -99,6 +111,25 @@ pub const Message = struct { }; }; + /// Trailing: + /// * error_bundle: ErrorBundle, + pub const BuildStepCompleted = extern struct { + step_index: std.Build.Configuration.Step.Index, + status: Status, + error_bundle: ErrorBundle, + // TODO result_error_msgs + // TODO result_stderr + // TODO result_peak_rss + // TODO result_duration_ns + + pub const Status = enum(u32) { + success, + failure, + skipped, + skipped_oom, + }; + }; + pub const PathPrefix = enum(u8) { cwd, zig_lib, @@ -194,6 +225,11 @@ pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void { try s.out.writeStruct(header, .little); } +pub fn serveBodylessMessage(s: *const Server, tag: OutMessage.Tag) Writer.Error!void { + try s.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 }); + try s.out.flush(); +} + pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void { try serveMessageHeader(s, .{ .tag = tag, diff --git a/tools/bsp.zig b/tools/bsp.zig index afa16d58d0fd23f3eeefe3c6ddaf12849b1e1746..bab0bc2c70afb95e99eb51ccf29be357ce4a678f 100644 --- a/tools/bsp.zig +++ b/tools/bsp.zig @@ -168,6 +168,27 @@ pub fn main(init: std.process.Init) !void { try client.serveBuildSteps(steps.items, .{ .watch = watch }); + while (true) { + const header: Server.Message.Header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { + error.Canceled, error.ConcurrencyUnavailable => |e| return e, + error.Timeout => unreachable, + else => |e| { + log.err("failed to receive message: {t}", .{err}); + break :blk e; + }, + }; + const body = client_stdout.take(header.bytes_len) catch unreachable; + log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); + + switch (header.tag) { + .bsp_build_started => {}, + .bsp_build_completed => if (!watch) break, + .bsp_step_started => {}, + .bsp_step_completed => {}, + .bsp_configuration => @panic("TODO"), + else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}), + } + } continue; } else if (std.mem.eql(u8, command, "exit")) { try client.serveBodylessMessage(.exit); -- 2.54.0