authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-17 22:20:41+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-17 22:20:41+02:00
log6b3b6ec32c5c45095c77f4af99ec21f7d75b82ef
tree88912fa6138d2adc82f14569a956b2cb9bd10344
parent8f61a7d34a33c2ec912c3f4404d69586e721c75a
parent88d0dbe9355fb17d24cab9861c692e89f32bbc7c

Merge pull request 'follow-up fixes to the build system protocol' (#36427) from techatrix/zig:bsp-follow-ups into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36427 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

2 files changed, 49 insertions(+), 38 deletions(-)

lib/compiler/Maker.zig+40-38
......@@ -63,9 +63,9 @@ protocol_server_mutex: Io.Mutex,
6363web_server: ?*AvoidableWebServer,
6464/// Allocated into `gpa`.
6565memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
66/// Allocated into `gpa`.
66/// Allocated into `gpa` during `prepare`.
6767initial_steps: std.array_hash_map.Auto(Configuration.Step.Index, void),
68/// Allocated into `gpa`.
68/// Allocated into `gpa` during `prepare`.
6969step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void),
7070pkg_config: PkgConfig,
7171
......@@ -163,7 +163,6 @@ pub fn main(init: process.Init.Minimal) !void {
163163
164164 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
165165 defer arena_instance.deinit();
166 defer if (debugMakerLeaks()) log.debug("used {Bi} of arena", .{arena_instance.queryCapacity()});
167166 const arena = arena_instance.allocator();
168167
169168 const args = try init.args.toSlice(arena);
......@@ -445,8 +444,6 @@ pub fn main(init: process.Init.Minimal) !void {
445444 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
446445 graph.debug_compiler_runtime_libs = stringToEnum(std.lang.Optimize, rest) orelse
447446 fatal("unrecognized optimization mode: {s}", .{rest});
448 } else if (is_debug_mode and mem.eql(u8, arg, "--debug-maker-leaks")) {
449 debug_maker_leaks = true;
450447 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
451448 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
452449 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i);
......@@ -682,9 +679,8 @@ pub fn main(init: process.Init.Minimal) !void {
682679 } else null;
683680
684681 var stdin_buffer: [256]u8 = undefined;
685 var stdout_buffer: [256]u8 = undefined;
686682 var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer);
687 var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
683 const stdout_writer = initStdoutWriter(io);
688684
689685 var protocol_server_allocation: AvoidableServer = undefined;
690686 const protocol_server: ?*AvoidableServer = if (listen) s: {
......@@ -694,9 +690,9 @@ pub fn main(init: process.Init.Minimal) !void {
694690 if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{});
695691 protocol_server_allocation = .{
696692 .in = &stdin_reader.interface,
697 .out = &stdout_writer.interface,
693 .out = stdout_writer,
698694 };
699 try serveBSPHandshake(&protocol_server_allocation);
695 try serveBspHandshake(&protocol_server_allocation);
700696 break :s &protocol_server_allocation;
701697 } else null;
702698
......@@ -764,10 +760,8 @@ pub fn main(init: process.Init.Minimal) !void {
764760 .include = install_include_path,
765761 },
766762
767 // Extra step at the end which is the autogenerated placeholder
768 // step which indicates that we need to reconfigure.
769 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len + 1),
770 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),
763 .steps = &.{},
764 .generated_files = &.{},
771765 .run_args = run_args,
772766
773767 .available_rss = max_rss,
......@@ -794,11 +788,7 @@ pub fn main(init: process.Init.Minimal) !void {
794788 else
795789 .failures,
796790 };
797 defer {
798 maker.memory_blocked_steps.deinit(gpa);
799 maker.initial_steps.deinit(gpa);
800 maker.step_stack.deinit(gpa);
801 }
791 defer maker.deinit();
802792
803793 if (maker.available_rss == 0) {
804794 maker.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64);
......@@ -820,6 +810,11 @@ pub fn main(init: process.Init.Minimal) !void {
820810 var select: Io.Select(Event) = .init(io, &select_buffer);
821811 defer select.cancelDiscard();
822812
813 // File watching cannot be canceled without blocking
814 // https://codeberg.org/ziglang/zig/issues/31693
815 var is_fs_watching = false;
816 defer if (is_fs_watching) @panic("(zig build system) TODO file watching cannot be canceled without blocking");
817
823818 try select.concurrent(.message, Server.receiveMessage, .{s});
824819
825820 var in_debounce = false;
......@@ -828,10 +823,13 @@ pub fn main(init: process.Init.Minimal) !void {
828823 const header: Client.Message.Header = try payload;
829824 switch (header.tag) {
830825 .exit => {
831 cleanExit(io, &scanned_config);
832 process.exit(0);
826 // exit early until file watching supports cancelation without blocking
827 if (is_fs_watching) std.process.exit(0);
828 return cleanExit(io, &scanned_config);
833829 },
834830 .bsp_build_steps => {
831 if (is_fs_watching) @panic("(zig build system) TODO file watching cannot be canceled without blocking");
832
835833 // Cancel existing file watching
836834 select.cancelDiscard();
837835 in_debounce = false;
......@@ -865,6 +863,10 @@ pub fn main(init: process.Init.Minimal) !void {
865863 &watch.?,
866864 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
867865 });
866 is_fs_watching = true;
867 } else if (watch) |*w| {
868 w.deinit();
869 watch = null;
868870 }
869871
870872 continue :loop try select.await();
......@@ -873,6 +875,7 @@ pub fn main(init: process.Init.Minimal) !void {
873875 }
874876 },
875877 .fs_event => |payload| {
878 is_fs_watching = false;
876879 if (!Watch.have_impl) unreachable;
877880 switch (payload catch |err| switch (err) {
878881 error.MustReconfigure => {
......@@ -894,6 +897,7 @@ pub fn main(init: process.Init.Minimal) !void {
894897 &watch.?,
895898 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
896899 });
900 is_fs_watching = true;
897901 continue :loop try select.await();
898902 },
899903 }
......@@ -917,7 +921,7 @@ pub fn main(init: process.Init.Minimal) !void {
917921 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});
918922 break :w try .init(&maker);
919923 };
920 defer w.deinit();
924 defer if (watch_flag) w.deinit();
921925
922926 if (web_server) |ws| try ws.updateConfiguration(&maker);
923927
......@@ -2333,6 +2337,11 @@ fn prepare(
23332337 const step_stack = &maker.step_stack;
23342338 const c = &maker.scanned_config.configuration;
23352339
2340 // Extra step at the end which is the autogenerated placeholder
2341 // step which indicates that we need to reconfigure.
2342 maker.steps = try arena.alloc(Step, c.steps.len + 1);
2343 maker.generated_files = try arena.alloc(Path, c.generated_files_len);
2344
23362345 // The last element is a reserved special pseudostep which contains the
23372346 // watch inputs for the configurer executable.
23382347 for (maker.steps[0 .. maker.steps.len - 1], 0..) |*step, step_index_usize| {
......@@ -2652,8 +2661,7 @@ fn makeSteps(
26522661 break :code 2; // failure; do not print build command
26532662 };
26542663 if (code == 0) {
2655 removePoisonedConfiguration(io, maker.scanned_config);
2656 if (debugMakerLeaks()) return deinit(maker);
2664 return cleanExit(io, maker.scanned_config);
26572665 }
26582666 cleanup_task.await(io); // There is a defer above but an exit below.
26592667 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
......@@ -2662,12 +2670,10 @@ fn makeSteps(
26622670
26632671fn deinit(maker: *Maker) void {
26642672 const gpa = maker.gpa;
2665 for (maker.steps) |*step| {
2666 step.clearResultStderr(gpa);
2667 step.clearFailedCommand(gpa);
2668 step.clearErrorBundle(gpa);
2669 step.inputs.deinit(gpa);
2670 }
2673 for (maker.steps) |*step| step.deinit(gpa);
2674 maker.memory_blocked_steps.deinit(gpa);
2675 maker.initial_steps.deinit(gpa);
2676 maker.step_stack.deinit(gpa);
26712677}
26722678
26732679fn stepReady(
......@@ -2800,9 +2806,10 @@ fn makeStep(
28002806 }
28012807
28022808 // No matter the result, we want to display error/warning messages.
2803 if (make_step.result_error_bundle.errorMessageCount() > 0 or
2804 make_step.result_error_msgs.items.len > 0 or
2805 make_step.result_stderr.len > 0)
2809 if (maker.protocol_server == null and
2810 (make_step.result_error_bundle.errorMessageCount() > 0 or
2811 make_step.result_error_msgs.items.len > 0 or
2812 make_step.result_stderr.len > 0))
28062813 {
28072814 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
28082815 defer io.unlockStderr();
......@@ -3360,7 +3367,7 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void {
33603367 }
33613368}
33623369
3363fn serveBSPHandshake(s: *const std.zig.Server) !void {
3370fn serveBspHandshake(s: *const std.zig.Server) !void {
33643371 const handshake_header: Server.Message.Handshake = .{
33653372 .version = Server.build_system_version,
33663373 .flags = .{
......@@ -3744,11 +3751,6 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi
37443751 }
37453752}
37463753
3747inline fn debugMakerLeaks() bool {
3748 if (!is_debug_mode) return false;
3749 return debug_maker_leaks;
3750}
3751
37523754const BuildRoot = struct {
37533755 directory: Cache.Directory,
37543756 close_directory: bool,
lib/compiler/Maker/Step.zig+9
......@@ -309,6 +309,15 @@ pub fn make(
309309 }
310310}
311311
312pub fn deinit(step: *Step, gpa: Allocator) void {
313 step.clearResultStderr(gpa);
314 step.clearFailedCommand(gpa);
315 step.clearErrorBundle(gpa);
316 step.inputs.deinit(gpa);
317 if (step.getZigProcess()) |zp| gpa.destroy(zp);
318 step.* = undefined;
319}
320
312321/// Prepares the step for being re-evaluated.
313322pub fn reset(step: *Step, maker: *Maker) void {
314323 assert(step.state == .precheck_done);