authorgravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-07-21 16:29:09+02:00
committergravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-07-21 16:31:30+02:00
log38efc69dc98f8548d473eeee24ea0717d07af11e
tree1045e32e2d7ee7b67f6c4c22cb629272823eb917
parent54162b1b9d1df526a145d6c825de3640dee50ede
signaturebadge-check Signed by SSH key SHA256:HYC3SjXQcAt6uwv9pu/6OoVQ2rUH8rb5zKiUHSe9uxk

Maker: serve build status over protocol


3 files changed, 142 insertions(+), 19 deletions(-)

lib/compiler/Maker.zig+85-19
...@@ -897,19 +897,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -897,19 +897,8 @@ pub fn main(init: process.Init.Minimal) !void {
897 error.WriteFailed => return stderr.file_writer.err.?,897 error.WriteFailed => return stderr.file_writer.err.?,
898 };898 };
899 }) {899 }) {
900 if (web_server) |ws| ws.startBuild();
901
902 try maker.makeSteps(main_progress_node, fuzz);900 try maker.makeSteps(main_progress_node, fuzz);
903901
904 if (web_server) |ws| {
905 if (fuzz) |mode| if (mode != .forever) fatal(
906 "error: limited fuzzing is not implemented yet for --webui",
907 .{},
908 );
909
910 ws.finishBuild(.{ .fuzz = fuzz != null });
911 }
912
913 if (web_server) |ws| {902 if (web_server) |ws| {
914 const c = &scanned_config.configuration;903 const c = &scanned_config.configuration;
915 assert(!watch); // fatal error after CLI parsing904 assert(!watch); // fatal error after CLI parsing
...@@ -2254,6 +2243,12 @@ fn makeSteps(...@@ -2254,6 +2243,12 @@ fn makeSteps(
2254 const top_level_steps = &maker.scanned_config.top_level_steps;2243 const top_level_steps = &maker.scanned_config.top_level_steps;
2255 const c = &maker.scanned_config.configuration;2244 const c = &maker.scanned_config.configuration;
22562245
2246 if (maker.web_server) |ws| ws.startBuild();
2247
2248 if (maker.protocol_server) |s| {
2249 try s.serveBodylessMessage(.bsp_build_started);
2250 }
2251
2257 {2252 {
2258 // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,2253 // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
2259 // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking2254 // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
...@@ -2279,6 +2274,19 @@ fn makeSteps(...@@ -2279,6 +2274,19 @@ fn makeSteps(
2279 try group.await(io);2274 try group.await(io);
2280 }2275 }
22812276
2277 if (maker.web_server) |ws| {
2278 if (fuzz) |mode| if (mode != .forever) fatal(
2279 "error: limited fuzzing is not implemented yet for --webui",
2280 .{},
2281 );
2282
2283 ws.finishBuild(.{ .fuzz = fuzz != null });
2284 }
2285
2286 if (maker.protocol_server) |s| {
2287 try s.serveBodylessMessage(.bsp_build_completed);
2288 }
2289
2282 assert(maker.memory_blocked_steps.items.len == 0);2290 assert(maker.memory_blocked_steps.items.len == 0);
22832291
2284 var test_pass_count: usize = 0;2292 var test_pass_count: usize = 0;
...@@ -2539,6 +2547,15 @@ fn makeStep(...@@ -2539,6 +2547,15 @@ fn makeStep(
2539 defer step_prog_node.end();2547 defer step_prog_node.end();
25402548
2541 if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip);2549 if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip);
2550 if (maker.protocol_server) |s| {
2551 maker.protocol_server_mutex.lockUncancelable(io);
2552 defer maker.protocol_server_mutex.unlock(io);
2553
2554 s.serveU32Message(
2555 .bsp_step_started,
2556 @backingInt(step_index),
2557 ) catch @panic("TODO propagate error when failing to send protocol message");
2558 }
25422559
2543 const new_state: Step.State = for (deps) |dep_index| {2560 const new_state: Step.State = for (deps) |dep_index| {
2544 const dep_make_step = maker.stepByIndex(dep_index);2561 const dep_make_step = maker.stepByIndex(dep_index);
...@@ -2564,7 +2581,7 @@ fn makeStep(...@@ -2564,7 +2581,7 @@ fn makeStep(
25642581
2565 @atomicStore(Step.State, &make_step.state, new_state, .monotonic);2582 @atomicStore(Step.State, &make_step.state, new_state, .monotonic);
25662583
2567 switch (new_state) {2584 const success = switch (new_state) {
2568 .precheck_unstarted => unreachable,2585 .precheck_unstarted => unreachable,
2569 .precheck_started => unreachable,2586 .precheck_started => unreachable,
2570 .precheck_done => unreachable,2587 .precheck_done => unreachable,
...@@ -2572,17 +2589,37 @@ fn makeStep(...@@ -2572,17 +2589,37 @@ fn makeStep(
2572 .failure,2589 .failure,
2573 .dependency_failure,2590 .dependency_failure,
2574 .skipped_oom,2591 .skipped_oom,
2575 => {2592 => false,
2576 if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure);
2577 std.Progress.setStatus(.failure_working);
2578 },
25792593
2580 .success,2594 .success,
2581 .skipped,2595 .skipped,
2582 => {2596 => true,
2583 if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success);2597 };
2584 },2598
2599 if (maker.web_server) |ws| {
2600 ws.updateStepStatus(step_index, if (success) .success else .failure);
2601 }
2602 if (maker.protocol_server != null) {
2603 maker.protocol_server_mutex.lockUncancelable(io);
2604 defer maker.protocol_server_mutex.unlock(io);
2605
2606 const status: Server.Message.BuildStepCompleted.Status = switch (new_state) {
2607 .precheck_unstarted => unreachable,
2608 .precheck_started => unreachable,
2609 .precheck_done => unreachable,
2610 .success => .success,
2611 .failure, .dependency_failure => .failure,
2612 .skipped => .skipped,
2613 .skipped_oom => .skipped_oom,
2614 };
2615 serveBuildStepCompleted(
2616 maker,
2617 step_index,
2618 status,
2619 ) catch |err| std.debug.panic("TODO propagate error when failing to send protocol message: {t}", .{err});
2585 }2620 }
2621
2622 if (!success) std.Progress.setStatus(.failure_working);
2586 }2623 }
25872624
2588 // No matter the result, we want to display error/warning messages.2625 // No matter the result, we want to display error/warning messages.
...@@ -3152,6 +3189,35 @@ fn serveBSPHandshake(s: *const std.zig.Server) !void {...@@ -3152,6 +3189,35 @@ fn serveBSPHandshake(s: *const std.zig.Server) !void {
3152 try s.out.flush();3189 try s.out.flush();
3153}3190}
31543191
3192fn serveBuildStepCompleted(
3193 maker: *Maker,
3194 step_index: Configuration.Step.Index,
3195 status: Server.Message.BuildStepCompleted.Status,
3196) !void {
3197 const s: *Server = maker.protocol_server.?;
3198 const step = maker.stepByIndex(step_index);
3199 const error_bundle = step.result_error_bundle;
3200
3201 const body: Server.Message.BuildStepCompleted = .{
3202 .step_index = step_index,
3203 .status = status,
3204 .error_bundle = .{
3205 .extra_len = @intCast(error_bundle.extra.len),
3206 .string_bytes_len = @intCast(error_bundle.string_bytes.len),
3207 },
3208 };
3209 const eb_bytes_len = @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len;
3210 const bytes_len = @sizeOf(Server.Message.BuildStepCompleted) + eb_bytes_len;
3211 try s.serveMessageHeader(.{
3212 .tag = .bsp_step_completed,
3213 .bytes_len = @intCast(bytes_len),
3214 });
3215 try s.out.writeStruct(body, .little);
3216 try s.out.writeSliceEndian(u32, error_bundle.extra, .little);
3217 try s.out.writeAll(error_bundle.string_bytes);
3218 try s.out.flush();
3219}
3220
3155fn initStdoutWriter(io: Io) *Writer {3221fn initStdoutWriter(io: Io) *Writer {
3156 stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);3222 stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);
3157 return &stdout_writer_allocation.interface;3223 return &stdout_writer_allocation.interface;
lib/std/zig/Server.zig+36
...@@ -82,6 +82,18 @@ pub const Message = struct {...@@ -82,6 +82,18 @@ pub const Message = struct {
82 /// Body is a cwd relative path to the configuration file.82 /// Body is a cwd relative path to the configuration file.
83 /// This message only applies to the build system protocol.83 /// This message only applies to the build system protocol.
84 bsp_configuration,84 bsp_configuration,
85 /// Does not have a body.
86 /// This message only applies to the build system protocol.
87 bsp_build_started,
88 /// Does not have a body.
89 /// This message only applies to the build system protocol.
90 bsp_build_completed,
91 /// Body is a `Configuration.Step.Index`.
92 /// This message only applies to the build system protocol.
93 bsp_step_started,
94 /// Body is a `BuildStepCompleted`.
95 /// This message only applies to the build system protocol.
96 bsp_step_completed,
8597
86 _,98 _,
87 };99 };
...@@ -99,6 +111,25 @@ pub const Message = struct {...@@ -99,6 +111,25 @@ pub const Message = struct {
99 };111 };
100 };112 };
101113
114 /// Trailing:
115 /// * error_bundle: ErrorBundle,
116 pub const BuildStepCompleted = extern struct {
117 step_index: std.Build.Configuration.Step.Index,
118 status: Status,
119 error_bundle: ErrorBundle,
120 // TODO result_error_msgs
121 // TODO result_stderr
122 // TODO result_peak_rss
123 // TODO result_duration_ns
124
125 pub const Status = enum(u32) {
126 success,
127 failure,
128 skipped,
129 skipped_oom,
130 };
131 };
132
102 pub const PathPrefix = enum(u8) {133 pub const PathPrefix = enum(u8) {
103 cwd,134 cwd,
104 zig_lib,135 zig_lib,
...@@ -194,6 +225,11 @@ pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void {...@@ -194,6 +225,11 @@ pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void {
194 try s.out.writeStruct(header, .little);225 try s.out.writeStruct(header, .little);
195}226}
196227
228pub fn serveBodylessMessage(s: *const Server, tag: OutMessage.Tag) Writer.Error!void {
229 try s.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 });
230 try s.out.flush();
231}
232
197pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void {233pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void {
198 try serveMessageHeader(s, .{234 try serveMessageHeader(s, .{
199 .tag = tag,235 .tag = tag,
tools/bsp.zig+21
...@@ -168,6 +168,27 @@ pub fn main(init: std.process.Init) !void {...@@ -168,6 +168,27 @@ pub fn main(init: std.process.Init) !void {
168168
169 try client.serveBuildSteps(steps.items, .{ .watch = watch });169 try client.serveBuildSteps(steps.items, .{ .watch = watch });
170170
171 while (true) {
172 const header: Server.Message.Header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) {
173 error.Canceled, error.ConcurrencyUnavailable => |e| return e,
174 error.Timeout => unreachable,
175 else => |e| {
176 log.err("failed to receive message: {t}", .{err});
177 break :blk e;
178 },
179 };
180 const body = client_stdout.take(header.bytes_len) catch unreachable;
181 log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len });
182
183 switch (header.tag) {
184 .bsp_build_started => {},
185 .bsp_build_completed => if (!watch) break,
186 .bsp_step_started => {},
187 .bsp_step_completed => {},
188 .bsp_configuration => @panic("TODO"),
189 else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}),
190 }
191 }
171 continue;192 continue;
172 } else if (std.mem.eql(u8, command, "exit")) {193 } else if (std.mem.eql(u8, command, "exit")) {
173 try client.serveBodylessMessage(.exit);194 try client.serveBodylessMessage(.exit);