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,...@@ -63,9 +63,9 @@ protocol_server_mutex: Io.Mutex,
63web_server: ?*AvoidableWebServer,63web_server: ?*AvoidableWebServer,
64/// Allocated into `gpa`.64/// Allocated into `gpa`.
65memory_blocked_steps: std.ArrayList(Configuration.Step.Index),65memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
66/// Allocated into `gpa`.66/// Allocated into `gpa` during `prepare`.
67initial_steps: std.array_hash_map.Auto(Configuration.Step.Index, void),67initial_steps: std.array_hash_map.Auto(Configuration.Step.Index, void),
68/// Allocated into `gpa`.68/// Allocated into `gpa` during `prepare`.
69step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void),69step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void),
70pkg_config: PkgConfig,70pkg_config: PkgConfig,
7171
...@@ -163,7 +163,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -163,7 +163,6 @@ pub fn main(init: process.Init.Minimal) !void {
163163
164 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);164 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
165 defer arena_instance.deinit();165 defer arena_instance.deinit();
166 defer if (debugMakerLeaks()) log.debug("used {Bi} of arena", .{arena_instance.queryCapacity()});
167 const arena = arena_instance.allocator();166 const arena = arena_instance.allocator();
168167
169 const args = try init.args.toSlice(arena);168 const args = try init.args.toSlice(arena);
...@@ -445,8 +444,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -445,8 +444,6 @@ pub fn main(init: process.Init.Minimal) !void {
445 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {444 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
446 graph.debug_compiler_runtime_libs = stringToEnum(std.lang.Optimize, rest) orelse445 graph.debug_compiler_runtime_libs = stringToEnum(std.lang.Optimize, rest) orelse
447 fatal("unrecognized optimization mode: {s}", .{rest});446 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;
450 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {447 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
451 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.448 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
452 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i);449 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i);
...@@ -682,9 +679,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -682,9 +679,8 @@ pub fn main(init: process.Init.Minimal) !void {
682 } else null;679 } else null;
683680
684 var stdin_buffer: [256]u8 = undefined;681 var stdin_buffer: [256]u8 = undefined;
685 var stdout_buffer: [256]u8 = undefined;
686 var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer);682 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
689 var protocol_server_allocation: AvoidableServer = undefined;685 var protocol_server_allocation: AvoidableServer = undefined;
690 const protocol_server: ?*AvoidableServer = if (listen) s: {686 const protocol_server: ?*AvoidableServer = if (listen) s: {
...@@ -694,9 +690,9 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -694,9 +690,9 @@ pub fn main(init: process.Init.Minimal) !void {
694 if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{});690 if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{});
695 protocol_server_allocation = .{691 protocol_server_allocation = .{
696 .in = &stdin_reader.interface,692 .in = &stdin_reader.interface,
697 .out = &stdout_writer.interface,693 .out = stdout_writer,
698 };694 };
699 try serveBSPHandshake(&protocol_server_allocation);695 try serveBspHandshake(&protocol_server_allocation);
700 break :s &protocol_server_allocation;696 break :s &protocol_server_allocation;
701 } else null;697 } else null;
702698
...@@ -764,10 +760,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -764,10 +760,8 @@ pub fn main(init: process.Init.Minimal) !void {
764 .include = install_include_path,760 .include = install_include_path,
765 },761 },
766762
767 // Extra step at the end which is the autogenerated placeholder763 .steps = &.{},
768 // step which indicates that we need to reconfigure.764 .generated_files = &.{},
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),
771 .run_args = run_args,765 .run_args = run_args,
772766
773 .available_rss = max_rss,767 .available_rss = max_rss,
...@@ -794,11 +788,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -794,11 +788,7 @@ pub fn main(init: process.Init.Minimal) !void {
794 else788 else
795 .failures,789 .failures,
796 };790 };
797 defer {791 defer maker.deinit();
798 maker.memory_blocked_steps.deinit(gpa);
799 maker.initial_steps.deinit(gpa);
800 maker.step_stack.deinit(gpa);
801 }
802792
803 if (maker.available_rss == 0) {793 if (maker.available_rss == 0) {
804 maker.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64);794 maker.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64);
...@@ -820,6 +810,11 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -820,6 +810,11 @@ pub fn main(init: process.Init.Minimal) !void {
820 var select: Io.Select(Event) = .init(io, &select_buffer);810 var select: Io.Select(Event) = .init(io, &select_buffer);
821 defer select.cancelDiscard();811 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
823 try select.concurrent(.message, Server.receiveMessage, .{s});818 try select.concurrent(.message, Server.receiveMessage, .{s});
824819
825 var in_debounce = false;820 var in_debounce = false;
...@@ -828,10 +823,13 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -828,10 +823,13 @@ pub fn main(init: process.Init.Minimal) !void {
828 const header: Client.Message.Header = try payload;823 const header: Client.Message.Header = try payload;
829 switch (header.tag) {824 switch (header.tag) {
830 .exit => {825 .exit => {
831 cleanExit(io, &scanned_config);826 // exit early until file watching supports cancelation without blocking
832 process.exit(0);827 if (is_fs_watching) std.process.exit(0);
828 return cleanExit(io, &scanned_config);
833 },829 },
834 .bsp_build_steps => {830 .bsp_build_steps => {
831 if (is_fs_watching) @panic("(zig build system) TODO file watching cannot be canceled without blocking");
832
835 // Cancel existing file watching833 // Cancel existing file watching
836 select.cancelDiscard();834 select.cancelDiscard();
837 in_debounce = false;835 in_debounce = false;
...@@ -865,6 +863,10 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -865,6 +863,10 @@ pub fn main(init: process.Init.Minimal) !void {
865 &watch.?,863 &watch.?,
866 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,864 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
867 });865 });
866 is_fs_watching = true;
867 } else if (watch) |*w| {
868 w.deinit();
869 watch = null;
868 }870 }
869871
870 continue :loop try select.await();872 continue :loop try select.await();
...@@ -873,6 +875,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -873,6 +875,7 @@ pub fn main(init: process.Init.Minimal) !void {
873 }875 }
874 },876 },
875 .fs_event => |payload| {877 .fs_event => |payload| {
878 is_fs_watching = false;
876 if (!Watch.have_impl) unreachable;879 if (!Watch.have_impl) unreachable;
877 switch (payload catch |err| switch (err) {880 switch (payload catch |err| switch (err) {
878 error.MustReconfigure => {881 error.MustReconfigure => {
...@@ -894,6 +897,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -894,6 +897,7 @@ pub fn main(init: process.Init.Minimal) !void {
894 &watch.?,897 &watch.?,
895 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,898 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
896 });899 });
900 is_fs_watching = true;
897 continue :loop try select.await();901 continue :loop try select.await();
898 },902 },
899 }903 }
...@@ -917,7 +921,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -917,7 +921,7 @@ pub fn main(init: process.Init.Minimal) !void {
917 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});921 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});
918 break :w try .init(&maker);922 break :w try .init(&maker);
919 };923 };
920 defer w.deinit();924 defer if (watch_flag) w.deinit();
921925
922 if (web_server) |ws| try ws.updateConfiguration(&maker);926 if (web_server) |ws| try ws.updateConfiguration(&maker);
923927
...@@ -2333,6 +2337,11 @@ fn prepare(...@@ -2333,6 +2337,11 @@ fn prepare(
2333 const step_stack = &maker.step_stack;2337 const step_stack = &maker.step_stack;
2334 const c = &maker.scanned_config.configuration;2338 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
2336 // The last element is a reserved special pseudostep which contains the2345 // The last element is a reserved special pseudostep which contains the
2337 // watch inputs for the configurer executable.2346 // watch inputs for the configurer executable.
2338 for (maker.steps[0 .. maker.steps.len - 1], 0..) |*step, step_index_usize| {2347 for (maker.steps[0 .. maker.steps.len - 1], 0..) |*step, step_index_usize| {
...@@ -2652,8 +2661,7 @@ fn makeSteps(...@@ -2652,8 +2661,7 @@ fn makeSteps(
2652 break :code 2; // failure; do not print build command2661 break :code 2; // failure; do not print build command
2653 };2662 };
2654 if (code == 0) {2663 if (code == 0) {
2655 removePoisonedConfiguration(io, maker.scanned_config);2664 return cleanExit(io, maker.scanned_config);
2656 if (debugMakerLeaks()) return deinit(maker);
2657 }2665 }
2658 cleanup_task.await(io); // There is a defer above but an exit below.2666 cleanup_task.await(io); // There is a defer above but an exit below.
2659 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};2667 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
...@@ -2662,12 +2670,10 @@ fn makeSteps(...@@ -2662,12 +2670,10 @@ fn makeSteps(
26622670
2663fn deinit(maker: *Maker) void {2671fn deinit(maker: *Maker) void {
2664 const gpa = maker.gpa;2672 const gpa = maker.gpa;
2665 for (maker.steps) |*step| {2673 for (maker.steps) |*step| step.deinit(gpa);
2666 step.clearResultStderr(gpa);2674 maker.memory_blocked_steps.deinit(gpa);
2667 step.clearFailedCommand(gpa);2675 maker.initial_steps.deinit(gpa);
2668 step.clearErrorBundle(gpa);2676 maker.step_stack.deinit(gpa);
2669 step.inputs.deinit(gpa);
2670 }
2671}2677}
26722678
2673fn stepReady(2679fn stepReady(
...@@ -2800,9 +2806,10 @@ fn makeStep(...@@ -2800,9 +2806,10 @@ fn makeStep(
2800 }2806 }
28012807
2802 // No matter the result, we want to display error/warning messages.2808 // No matter the result, we want to display error/warning messages.
2803 if (make_step.result_error_bundle.errorMessageCount() > 0 or2809 if (maker.protocol_server == null and
2804 make_step.result_error_msgs.items.len > 0 or2810 (make_step.result_error_bundle.errorMessageCount() > 0 or
2805 make_step.result_stderr.len > 0)2811 make_step.result_error_msgs.items.len > 0 or
2812 make_step.result_stderr.len > 0))
2806 {2813 {
2807 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);2814 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
2808 defer io.unlockStderr();2815 defer io.unlockStderr();
...@@ -3360,7 +3367,7 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void {...@@ -3360,7 +3367,7 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void {
3360 }3367 }
3361}3368}
33623369
3363fn serveBSPHandshake(s: *const std.zig.Server) !void {3370fn serveBspHandshake(s: *const std.zig.Server) !void {
3364 const handshake_header: Server.Message.Handshake = .{3371 const handshake_header: Server.Message.Handshake = .{
3365 .version = Server.build_system_version,3372 .version = Server.build_system_version,
3366 .flags = .{3373 .flags = .{
...@@ -3744,11 +3751,6 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi...@@ -3744,11 +3751,6 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi
3744 }3751 }
3745}3752}
37463753
3747inline fn debugMakerLeaks() bool {
3748 if (!is_debug_mode) return false;
3749 return debug_maker_leaks;
3750}
3751
3752const BuildRoot = struct {3754const BuildRoot = struct {
3753 directory: Cache.Directory,3755 directory: Cache.Directory,
3754 close_directory: bool,3756 close_directory: bool,
lib/compiler/Maker/Step.zig+9
...@@ -309,6 +309,15 @@ pub fn make(...@@ -309,6 +309,15 @@ pub fn make(
309 }309 }
310}310}
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
312/// Prepares the step for being re-evaluated.321/// Prepares the step for being re-evaluated.
313pub fn reset(step: *Step, maker: *Maker) void {322pub fn reset(step: *Step, maker: *Maker) void {
314 assert(step.state == .precheck_done);323 assert(step.state == .precheck_done);