authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-24 23:08:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-24 23:10:27-07:00
log3d2207fc7917d5a2e05cbce22ba4d94cf2cbdac4
treed6ffabd11cb265dc1209179c03361c9e495be063
parent01eff30056ffef1e3c4f5987d2908acb35397caf

build server protocol: send generated file paths

closes #36403

4 files changed, 216 insertions(+), 3 deletions(-)

lib/compiler/Maker.zig+156-1
...@@ -3434,6 +3434,8 @@ fn serveBuildStepCompleted(...@@ -3434,6 +3434,8 @@ fn serveBuildStepCompleted(
3434 const step = maker.stepByIndex(step_index);3434 const step = maker.stepByIndex(step_index);
3435 const error_bundle = step.result_error_bundle;3435 const error_bundle = step.result_error_bundle;
34363436
3437 const gf_size = countGeneratedFiles(maker.generated_files, step_index, &maker.scanned_config.configuration);
3438
3437 const body: Server.Message.BuildStepCompleted = .{3439 const body: Server.Message.BuildStepCompleted = .{
3438 .step_index = step_index,3440 .step_index = step_index,
3439 .status = status,3441 .status = status,
...@@ -3441,19 +3443,172 @@ fn serveBuildStepCompleted(...@@ -3441,19 +3443,172 @@ fn serveBuildStepCompleted(
3441 .extra_len = @intCast(error_bundle.extra.len),3443 .extra_len = @intCast(error_bundle.extra.len),
3442 .string_bytes_len = @intCast(error_bundle.string_bytes.len),3444 .string_bytes_len = @intCast(error_bundle.string_bytes.len),
3443 },3445 },
3446 .generated_files_len = gf_size.count,
3444 };3447 };
3445 const eb_bytes_len = @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len;3448 const eb_bytes_len = @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len;
3446 const bytes_len = @sizeOf(Server.Message.BuildStepCompleted) + eb_bytes_len;3449 const bytes_len = @sizeOf(Server.Message.BuildStepCompleted) + eb_bytes_len +
3450 (gf_size.count * @sizeOf(Server.Message.GeneratedFile)) + gf_size.size;
3447 try s.serveMessageHeader(.{3451 try s.serveMessageHeader(.{
3448 .tag = .bsp_step_completed,3452 .tag = .bsp_step_completed,
3449 .bytes_len = @intCast(bytes_len),3453 .bytes_len = @intCast(bytes_len),
3450 });3454 });
3451 try s.out.writeStruct(body, .little);3455 try s.out.writeStruct(body, .little);
3456
3452 try s.out.writeSliceEndian(u32, error_bundle.extra, .little);3457 try s.out.writeSliceEndian(u32, error_bundle.extra, .little);
3453 try s.out.writeAll(error_bundle.string_bytes);3458 try s.out.writeAll(error_bundle.string_bytes);
3459
3460 try writeGeneratedFiles(maker.generated_files, step_index, &maker.scanned_config.configuration, s.out);
3461 try writeGeneratedFilesData(maker.generated_files, step_index, &maker.scanned_config.configuration, s.out);
3462
3454 try s.out.flush();3463 try s.out.flush();
3455}3464}
34563465
3466fn walkGeneratedFiles(
3467 step_index: Configuration.Step.Index,
3468 conf: *const Configuration,
3469 comptime Context: type,
3470 context: Context,
3471) void {
3472 const conf_step = step_index.ptr(conf);
3473 switch (conf_step.extended.get(conf.extra)) {
3474 .check_file,
3475 .fail,
3476 .fmt,
3477 .install_artifact,
3478 .install_dir,
3479 .install_file,
3480 .top_level,
3481 .update_source_files,
3482 => {},
3483
3484 .compile => |compile| {
3485 walkGeneratedFilesOne(context, compile.emit_directory.value);
3486 walkGeneratedFilesOne(context, compile.generated_docs.value);
3487 walkGeneratedFilesOne(context, compile.generated_asm.value);
3488 walkGeneratedFilesOne(context, compile.generated_bin.value);
3489 walkGeneratedFilesOne(context, compile.generated_pdb.value);
3490 walkGeneratedFilesOne(context, compile.generated_implib.value);
3491 walkGeneratedFilesOne(context, compile.generated_llvm_bc.value);
3492 walkGeneratedFilesOne(context, compile.generated_llvm_ir.value);
3493 walkGeneratedFilesOne(context, compile.generated_h.value);
3494 },
3495 .config_header => |config_header| {
3496 walkGeneratedFilesOne(context, config_header.generated_dir);
3497 },
3498 .find_program => |find_program| {
3499 walkGeneratedFilesOne(context, find_program.found_path);
3500 },
3501 .obj_copy => |obj_copy| {
3502 walkGeneratedFilesOne(context, obj_copy.output_file);
3503 walkGeneratedFilesOne(context, obj_copy.debug_file.value);
3504 },
3505 .options => |options| {
3506 walkGeneratedFilesOne(context, options.generated_file);
3507 },
3508 .run => |run| {
3509 if (run.captured_stdout.value) |v| walkGeneratedFilesOne(context, v.generated_file);
3510 if (run.captured_stderr.value) |v| walkGeneratedFilesOne(context, v.generated_file);
3511 for (run.args.slice) |arg|
3512 walkGeneratedFilesOne(context, arg.get(conf).generated.value);
3513 },
3514 .translate_c => |translate_c| {
3515 walkGeneratedFilesOne(context, translate_c.output_file);
3516 },
3517 .write_file => |write_file| {
3518 walkGeneratedFilesOne(context, write_file.generated_directory);
3519 },
3520 }
3521}
3522
3523fn walkGeneratedFilesOne(
3524 context: anytype,
3525 opt: ?Configuration.GeneratedFileIndex,
3526) void {
3527 context.visit(opt orelse return);
3528}
3529
3530fn countGeneratedFiles(
3531 generated_files: []const GeneratedFile,
3532 step_index: Configuration.Step.Index,
3533 conf: *const Configuration,
3534) struct { count: u32, size: u32 } {
3535 const Visitor = struct {
3536 generated_files: []const GeneratedFile,
3537 count: u32,
3538 size: u32,
3539
3540 fn visit(this: *@This(), i: Configuration.GeneratedFileIndex) void {
3541 const data = this.generated_files[@backingInt(i)].data orelse return;
3542 this.count += 1;
3543 this.size = @intCast(this.size + data.len);
3544 }
3545 };
3546 var v: Visitor = .{
3547 .generated_files = generated_files,
3548 .count = 0,
3549 .size = 0,
3550 };
3551 walkGeneratedFiles(step_index, conf, *Visitor, &v);
3552 return .{
3553 .count = v.count,
3554 .size = v.size,
3555 };
3556}
3557
3558fn writeGeneratedFiles(
3559 generated_files: []const GeneratedFile,
3560 step_index: Configuration.Step.Index,
3561 conf: *const Configuration,
3562 writer: *Io.Writer,
3563) Io.Writer.Error!void {
3564 const Visitor = struct {
3565 generated_files: []const GeneratedFile,
3566 writer: *Io.Writer,
3567 result: Io.Writer.Error!void,
3568
3569 fn visit(this: *@This(), i: Configuration.GeneratedFileIndex) void {
3570 const unpacked = this.generated_files[@backingInt(i)].unpack() orelse return;
3571 const payload: Server.Message.GeneratedFile = .{
3572 .index = i,
3573 .path_len = @intCast(unpacked.sub_path.len),
3574 };
3575 this.result = this.writer.writeAll(@ptrCast((&payload)[0..1]));
3576 }
3577 };
3578 var v: Visitor = .{
3579 .generated_files = generated_files,
3580 .writer = writer,
3581 .result = {},
3582 };
3583 walkGeneratedFiles(step_index, conf, *Visitor, &v);
3584 return v.result;
3585}
3586
3587fn writeGeneratedFilesData(
3588 generated_files: []const GeneratedFile,
3589 step_index: Configuration.Step.Index,
3590 conf: *const Configuration,
3591 writer: *Io.Writer,
3592) Io.Writer.Error!void {
3593 const Visitor = struct {
3594 generated_files: []const GeneratedFile,
3595 writer: *Io.Writer,
3596 result: Io.Writer.Error!void,
3597
3598 fn visit(this: *@This(), i: Configuration.GeneratedFileIndex) void {
3599 const data = this.generated_files[@backingInt(i)].data orelse return;
3600 this.result = this.writer.writeAll(data);
3601 }
3602 };
3603 var v: Visitor = .{
3604 .generated_files = generated_files,
3605 .writer = writer,
3606 .result = {},
3607 };
3608 walkGeneratedFiles(step_index, conf, *Visitor, &v);
3609 return v.result;
3610}
3611
3457fn initStdoutWriter(io: Io) *Writer {3612fn initStdoutWriter(io: Io) *Writer {
3458 stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);3613 stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);
3459 return &stdout_writer_allocation.interface;3614 return &stdout_writer_allocation.interface;
lib/std/zig/ErrorBundle.zig+18
...@@ -197,6 +197,24 @@ pub fn renderToTerminal(eb: ErrorBundle, options: RenderOptions, t: Io.Terminal)...@@ -197,6 +197,24 @@ pub fn renderToTerminal(eb: ErrorBundle, options: RenderOptions, t: Io.Terminal)
197 }197 }
198}198}
199199
200pub fn readAlloc(
201 r: *Io.Reader,
202 gpa: Allocator,
203 extra_len: u32,
204 string_bytes_len: u32,
205) Io.Reader.ReadAllocError!ErrorBundle {
206 const extra = try r.readSliceEndianAlloc(gpa, u32, extra_len, .little);
207 errdefer gpa.free(extra);
208
209 const string_bytes = try r.readAlloc(gpa, string_bytes_len);
210 errdefer gpa.free(string_bytes);
211
212 return .{
213 .extra = extra,
214 .string_bytes = string_bytes,
215 };
216}
217
200fn renderErrorMessage(218fn renderErrorMessage(
201 eb: ErrorBundle,219 eb: ErrorBundle,
202 options: RenderOptions,220 options: RenderOptions,
lib/std/zig/Server.zig+13-1
...@@ -8,6 +8,7 @@ const OutMessage = std.zig.Server.Message;...@@ -8,6 +8,7 @@ const OutMessage = std.zig.Server.Message;
8const InMessage = std.zig.Client.Message;8const InMessage = std.zig.Client.Message;
9const Reader = std.Io.Reader;9const Reader = std.Io.Reader;
10const Writer = std.Io.Writer;10const Writer = std.Io.Writer;
11const Configuration = std.Build.Configuration;
1112
12in: *Reader,13in: *Reader,
13out: *Writer,14out: *Writer,
...@@ -113,10 +114,15 @@ pub const Message = struct {...@@ -113,10 +114,15 @@ pub const Message = struct {
113114
114 /// Trailing:115 /// Trailing:
115 /// * error_bundle: ErrorBundle,116 /// * error_bundle: ErrorBundle,
117 /// * generated_file: [generated_files_len]GeneratedFile,
118 /// * path_bytes: [_]u8, // for each GeneratedFile
119 /// - PathPrefix
120 /// - sub_path: [_]u8,
116 pub const BuildStepCompleted = extern struct {121 pub const BuildStepCompleted = extern struct {
117 step_index: std.Build.Configuration.Step.Index,122 step_index: Configuration.Step.Index,
118 status: Status,123 status: Status,
119 error_bundle: ErrorBundle,124 error_bundle: ErrorBundle,
125 generated_files_len: u32,
120 // TODO result_error_msgs126 // TODO result_error_msgs
121 // TODO result_stderr127 // TODO result_stderr
122 // TODO result_peak_rss128 // TODO result_peak_rss
...@@ -130,6 +136,12 @@ pub const Message = struct {...@@ -130,6 +136,12 @@ pub const Message = struct {
130 };136 };
131 };137 };
132138
139 pub const GeneratedFile = extern struct {
140 index: Configuration.GeneratedFileIndex,
141 /// Includes only the path bytes, not the prefix or null byte.
142 path_len: u32,
143 };
144
133 pub const PathPrefix = enum(u8) {145 pub const PathPrefix = enum(u8) {
134 cwd,146 cwd,
135 zig_lib,147 zig_lib,
tools/bsp.zig+29-1
...@@ -184,7 +184,35 @@ pub fn main(init: std.process.Init) !void {...@@ -184,7 +184,35 @@ pub fn main(init: std.process.Init) !void {
184 .bsp_build_started => {},184 .bsp_build_started => {},
185 .bsp_build_completed => if (!watch) break,185 .bsp_build_completed => if (!watch) break,
186 .bsp_step_started => {},186 .bsp_step_started => {},
187 .bsp_step_completed => {},187 .bsp_step_completed => {
188 var reader: Io.Reader = .fixed(body);
189 const bsc = try reader.takeStruct(Server.Message.BuildStepCompleted, .little);
190 log.info("step {q} {t}: ({d} generated files)", .{
191 bsc.step_index.ptr(c).name.slice(c),
192 bsc.status,
193 bsc.generated_files_len,
194 });
195
196 var eb = try std.zig.ErrorBundle.readAlloc(
197 &reader,
198 gpa,
199 bsc.error_bundle.extra_len,
200 bsc.error_bundle.string_bytes_len,
201 );
202 defer eb.deinit(gpa);
203 try eb.renderToStderr(io, .{}, .auto);
204
205 const modified_files: []align(1) Server.Message.GeneratedFile = @ptrCast(
206 try reader.take(bsc.generated_files_len * @sizeOf(Server.Message.GeneratedFile)),
207 );
208 for (modified_files) |modified_file| {
209 const prefix = try reader.takeEnum(Server.Message.PathPrefix, .little);
210 const sub_path = try reader.take(modified_file.path_len);
211 log.info("generated file: {d} {t} {q}", .{
212 modified_file.index, prefix, sub_path,
213 });
214 }
215 },
188 .bsp_configuration => @panic("TODO"),216 .bsp_configuration => @panic("TODO"),
189 else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}),217 else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}),
190 }218 }