diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index b372f8999270f53a0ba15e916f7ecb8200ec662a..90c6965a6d3fe1bdd9519b9b25861a084d3faabd 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -63,9 +63,9 @@ protocol_server_mutex: Io.Mutex, web_server: ?*AvoidableWebServer, /// Allocated into `gpa`. memory_blocked_steps: std.ArrayList(Configuration.Step.Index), -/// Allocated into `gpa`. +/// Allocated into `gpa` during `prepare`. initial_steps: std.array_hash_map.Auto(Configuration.Step.Index, void), -/// Allocated into `gpa`. +/// Allocated into `gpa` during `prepare`. step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void), pkg_config: PkgConfig, @@ -163,7 +163,6 @@ pub fn main(init: process.Init.Minimal) !void { var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator); defer arena_instance.deinit(); - defer if (debugMakerLeaks()) log.debug("used {Bi} of arena", .{arena_instance.queryCapacity()}); const arena = arena_instance.allocator(); const args = try init.args.toSlice(arena); @@ -445,8 +444,6 @@ pub fn main(init: process.Init.Minimal) !void { } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| { graph.debug_compiler_runtime_libs = stringToEnum(std.lang.Optimize, rest) orelse fatal("unrecognized optimization mode: {s}", .{rest}); - } else if (is_debug_mode and mem.eql(u8, arg, "--debug-maker-leaks")) { - debug_maker_leaks = true; } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) { // --glibc-runtimes was the old name of the flag; kept for compatibility for now. graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i); @@ -682,9 +679,8 @@ pub fn main(init: process.Init.Minimal) !void { } else null; var stdin_buffer: [256]u8 = undefined; - var stdout_buffer: [256]u8 = undefined; var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); - var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); + const stdout_writer = initStdoutWriter(io); var protocol_server_allocation: AvoidableServer = undefined; const protocol_server: ?*AvoidableServer = if (listen) s: { @@ -694,9 +690,9 @@ pub fn main(init: process.Init.Minimal) !void { if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{}); protocol_server_allocation = .{ .in = &stdin_reader.interface, - .out = &stdout_writer.interface, + .out = stdout_writer, }; - try serveBSPHandshake(&protocol_server_allocation); + try serveBspHandshake(&protocol_server_allocation); break :s &protocol_server_allocation; } else null; @@ -764,10 +760,8 @@ pub fn main(init: process.Init.Minimal) !void { .include = install_include_path, }, - // Extra step at the end which is the autogenerated placeholder - // step which indicates that we need to reconfigure. - .steps = try arena.alloc(Step, scanned_config.configuration.steps.len + 1), - .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len), + .steps = &.{}, + .generated_files = &.{}, .run_args = run_args, .available_rss = max_rss, @@ -794,11 +788,7 @@ pub fn main(init: process.Init.Minimal) !void { else .failures, }; - defer { - maker.memory_blocked_steps.deinit(gpa); - maker.initial_steps.deinit(gpa); - maker.step_stack.deinit(gpa); - } + defer maker.deinit(); if (maker.available_rss == 0) { maker.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64); @@ -820,6 +810,11 @@ pub fn main(init: process.Init.Minimal) !void { var select: Io.Select(Event) = .init(io, &select_buffer); defer select.cancelDiscard(); + // File watching cannot be canceled without blocking + // https://codeberg.org/ziglang/zig/issues/31693 + var is_fs_watching = false; + defer if (is_fs_watching) @panic("(zig build system) TODO file watching cannot be canceled without blocking"); + try select.concurrent(.message, Server.receiveMessage, .{s}); var in_debounce = false; @@ -828,10 +823,13 @@ pub fn main(init: process.Init.Minimal) !void { const header: Client.Message.Header = try payload; switch (header.tag) { .exit => { - cleanExit(io, &scanned_config); - process.exit(0); + // exit early until file watching supports cancelation without blocking + if (is_fs_watching) std.process.exit(0); + return cleanExit(io, &scanned_config); }, .bsp_build_steps => { + if (is_fs_watching) @panic("(zig build system) TODO file watching cannot be canceled without blocking"); + // Cancel existing file watching select.cancelDiscard(); in_debounce = false; @@ -865,6 +863,10 @@ pub fn main(init: process.Init.Minimal) !void { &watch.?, if (in_debounce) .{ .ms = debounce_interval_ms } else .none, }); + is_fs_watching = true; + } else if (watch) |*w| { + w.deinit(); + watch = null; } continue :loop try select.await(); @@ -873,6 +875,7 @@ pub fn main(init: process.Init.Minimal) !void { } }, .fs_event => |payload| { + is_fs_watching = false; if (!Watch.have_impl) unreachable; switch (payload catch |err| switch (err) { error.MustReconfigure => { @@ -894,6 +897,7 @@ pub fn main(init: process.Init.Minimal) !void { &watch.?, if (in_debounce) .{ .ms = debounce_interval_ms } else .none, }); + is_fs_watching = true; continue :loop try select.await(); }, } @@ -917,7 +921,7 @@ pub fn main(init: process.Init.Minimal) !void { if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os}); break :w try .init(&maker); }; - defer w.deinit(); + defer if (watch_flag) w.deinit(); if (web_server) |ws| try ws.updateConfiguration(&maker); @@ -2333,6 +2337,11 @@ fn prepare( const step_stack = &maker.step_stack; const c = &maker.scanned_config.configuration; + // Extra step at the end which is the autogenerated placeholder + // step which indicates that we need to reconfigure. + maker.steps = try arena.alloc(Step, c.steps.len + 1); + maker.generated_files = try arena.alloc(Path, c.generated_files_len); + // The last element is a reserved special pseudostep which contains the // watch inputs for the configurer executable. for (maker.steps[0 .. maker.steps.len - 1], 0..) |*step, step_index_usize| { @@ -2652,8 +2661,7 @@ fn makeSteps( break :code 2; // failure; do not print build command }; if (code == 0) { - removePoisonedConfiguration(io, maker.scanned_config); - if (debugMakerLeaks()) return deinit(maker); + return cleanExit(io, maker.scanned_config); } cleanup_task.await(io); // There is a defer above but an exit below. _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; @@ -2662,12 +2670,10 @@ fn makeSteps( fn deinit(maker: *Maker) void { const gpa = maker.gpa; - for (maker.steps) |*step| { - step.clearResultStderr(gpa); - step.clearFailedCommand(gpa); - step.clearErrorBundle(gpa); - step.inputs.deinit(gpa); - } + for (maker.steps) |*step| step.deinit(gpa); + maker.memory_blocked_steps.deinit(gpa); + maker.initial_steps.deinit(gpa); + maker.step_stack.deinit(gpa); } fn stepReady( @@ -2800,9 +2806,10 @@ fn makeStep( } // No matter the result, we want to display error/warning messages. - if (make_step.result_error_bundle.errorMessageCount() > 0 or - make_step.result_error_msgs.items.len > 0 or - make_step.result_stderr.len > 0) + if (maker.protocol_server == null and + (make_step.result_error_bundle.errorMessageCount() > 0 or + make_step.result_error_msgs.items.len > 0 or + make_step.result_stderr.len > 0)) { const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); defer io.unlockStderr(); @@ -3360,7 +3367,7 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void { } } -fn serveBSPHandshake(s: *const std.zig.Server) !void { +fn serveBspHandshake(s: *const std.zig.Server) !void { const handshake_header: Server.Message.Handshake = .{ .version = Server.build_system_version, .flags = .{ @@ -3744,11 +3751,6 @@ fn removePoisonedConfiguration(io: Io, scanned_config: *const ScannedConfig) voi } } -inline fn debugMakerLeaks() bool { - if (!is_debug_mode) return false; - return debug_maker_leaks; -} - const BuildRoot = struct { directory: Cache.Directory, close_directory: bool, diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 4e9eb614efcea395d8f763e1210de56070fb2743..f87b1dd94adda17c420fe93d243e32390bf48122 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -309,6 +309,15 @@ pub fn make( } } +pub fn deinit(step: *Step, gpa: Allocator) void { + step.clearResultStderr(gpa); + step.clearFailedCommand(gpa); + step.clearErrorBundle(gpa); + step.inputs.deinit(gpa); + if (step.getZigProcess()) |zp| gpa.destroy(zp); + step.* = undefined; +} + /// Prepares the step for being re-evaluated. pub fn reset(step: *Step, maker: *Maker) void { assert(step.state == .precheck_done);