diff options
| author | Ryan Liptak <squeek502@hotmail.com> | 2024-03-08 11:54:56 -0800 |
|---|---|---|
| committer | Ryan Liptak <squeek502@hotmail.com> | 2024-03-11 05:06:17 -0700 |
| commit | 8799f7466d68405d715bf567c3de9935b619fcf6 (patch) | |
| tree | 1fb42c48809c524286a2bc1824c6d5291e419f6e | |
| parent | dc4b05894da63889d895482ef01e77ef0bf6e88c (diff) | |
| download | zig-8799f7466d68405d715bf567c3de9935b619fcf6.tar.gz zig-8799f7466d68405d715bf567c3de9935b619fcf6.zip | |
Report the progress of lazily building zig rc
jitCmd now takes a `server` option that will emit progress/errors via std.zig.Server when enabled.
| -rw-r--r-- | lib/compiler/resinator/main.zig | 6 | ||||
| -rw-r--r-- | src/Compilation.zig | 195 | ||||
| -rw-r--r-- | src/main.zig | 54 |
3 files changed, 153 insertions, 102 deletions
diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 08a3ea3fc3..8e6859978e 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -46,6 +46,12 @@ pub fn main() !void { }, }; + if (zig_integration) { + // Send progress with an empty string to indicate that the building of the + // resinator binary is finished and we've moved on to actually compiling the .rc file + try error_handler.server.serveStringMessage(.progress, ""); + } + var options = options: { var cli_diagnostics = cli.Diagnostics.init(allocator); defer cli_diagnostics.deinit(); diff --git a/src/Compilation.zig b/src/Compilation.zig index d978dc5280..3f312e9956 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4841,6 +4841,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 try argv.appendSlice(&.{ self_exe_path, "rc", + "--zig-integration", "/:no-preprocess", "/x", // ignore INCLUDE environment variable "/c65001", // UTF-8 codepage @@ -4849,31 +4850,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 }); try argv.appendSlice(&.{ "--", in_rc_path, out_res_path }); - var child = std.ChildProcess.init(argv.items, arena); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr_reader = child.stderr.?.reader(); - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - const term = child.wait() catch |err| { - return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - log.err("zig rc failed with stderr:\n{s}", .{stderr}); - return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); - } - }, - else => { - log.err("zig rc terminated with stderr:\n{s}", .{stderr}); - return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); - }, - } + try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node); break :blk digest; }; @@ -4941,79 +4918,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 try argv.appendSlice(rc_src.extra_flags); try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path }); - { - var child = std.ChildProcess.init(argv.items, arena); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; - - child.spawn() catch |err| { - return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv.items[0], @errorName(err) }); - }; - - var poller = std.io.poll(comp.gpa, enum { stdout }, .{ - .stdout = child.stdout.?, - }); - defer poller.deinit(); - - const stdout = poller.fifo(.stdout); - - poll: while (true) { - while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) { - if (!(try poller.poll())) break :poll; - } - const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable; - while (stdout.readableLength() < header.bytes_len) { - if (!(try poller.poll())) break :poll; - } - const body = stdout.readableSliceOfLen(header.bytes_len); - - switch (header.tag) { - // We expect exactly one ErrorBundle, and if any error_bundle header is - // sent then it's a fatal error. - .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len); - @memcpy(extra_array, unaligned_extra); - const error_bundle = .{ - .string_bytes = try comp.gpa.dupe(u8, string_bytes), - .extra = extra_array, - }; - return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle); - }, - else => {}, // ignore other messages - } - - stdout.discard(body.len); - } - - // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) - const stderr_reader = child.stderr.?.reader(); - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv.items[0], @errorName(err) }); - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - log.err("zig rc failed with stderr:\n{s}", .{stderr}); - return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); - } - }, - else => { - log.err("zig rc terminated with stderr:\n{s}", .{stderr}); - return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); - }, - } - } + try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node); // Read depfile and update cache manifest { @@ -5079,6 +4984,100 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 }; } +fn spawnZigRc( + comp: *Compilation, + win32_resource: *Win32Resource, + src_basename: []const u8, + arena: Allocator, + argv: []const []const u8, + child_progress_node: *std.Progress.Node, +) !void { + var node_name: std.ArrayListUnmanaged(u8) = .{}; + defer node_name.deinit(arena); + + var child = std.ChildProcess.init(argv, arena); + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + child.spawn() catch |err| { + return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); + }; + + var poller = std.io.poll(comp.gpa, enum { stdout }, .{ + .stdout = child.stdout.?, + }); + defer poller.deinit(); + + const stdout = poller.fifo(.stdout); + + poll: while (true) { + while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) { + if (!(try poller.poll())) break :poll; + } + const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable; + while (stdout.readableLength() < header.bytes_len) { + if (!(try poller.poll())) break :poll; + } + const body = stdout.readableSliceOfLen(header.bytes_len); + + switch (header.tag) { + // We expect exactly one ErrorBundle, and if any error_bundle header is + // sent then it's a fatal error. + .error_bundle => { + const EbHdr = std.zig.Server.Message.ErrorBundle; + const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); + const extra_bytes = + body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); + const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len); + @memcpy(extra_array, unaligned_extra); + const error_bundle = std.zig.ErrorBundle{ + .string_bytes = try comp.gpa.dupe(u8, string_bytes), + .extra = extra_array, + }; + return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle); + }, + .progress => { + node_name.clearRetainingCapacity(); + if (body.len > 0) { + try node_name.appendSlice(arena, "build 'zig rc'... "); + try node_name.appendSlice(arena, body); + child_progress_node.setName(node_name.items); + } else { + child_progress_node.setName(src_basename); + } + }, + else => {}, // ignore other messages + } + + stdout.discard(body.len); + } + + // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) + const stderr_reader = child.stderr.?.reader(); + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + log.err("zig rc failed with stderr:\n{s}", .{stderr}); + return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); + } + }, + else => { + log.err("zig rc terminated with stderr:\n{s}", .{stderr}); + return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); + }, + } +} + pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { const s = std.fs.path.sep_str; const rand_int = std.crypto.random.int(u64); diff --git a/src/main.zig b/src/main.zig index a11bf4b808..7fe08698f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -291,11 +291,13 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } else if (mem.eql(u8, cmd, "translate-c")) { return buildOutputType(gpa, arena, args, .translate_c); } else if (mem.eql(u8, cmd, "rc")) { + const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration"); return jitCmd(gpa, arena, cmd_args, .{ .cmd_name = "resinator", .root_src_path = "resinator/main.zig", .depend_on_aro = true, .prepend_zig_lib_dir_path = true, + .server = use_server, }); } else if (mem.eql(u8, cmd, "fmt")) { return jitCmd(gpa, arena, cmd_args, .{ @@ -5304,6 +5306,8 @@ const JitCmdOptions = struct { prepend_zig_exe_path: bool = false, depend_on_aro: bool = false, capture: ?*[]u8 = null, + /// Send progress and error bundles via std.zig.Server over stdout + server: bool = false, }; fn jitCmd( @@ -5449,10 +5453,52 @@ fn jitCmd( }; defer comp.destroy(); - updateModule(comp, color) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(2), - else => |e| return e, - }; + if (options.server and !builtin.single_threaded) { + var reset: std.Thread.ResetEvent = .{}; + var progress: std.Progress = .{ + .terminal = null, + .root = .{ + .context = undefined, + .parent = null, + .name = "", + .unprotected_estimated_total_items = 0, + .unprotected_completed_items = 0, + }, + .columns_written = 0, + .prev_refresh_timestamp = 0, + .timer = null, + .done = false, + }; + const main_progress_node = &progress.root; + main_progress_node.context = &progress; + var server = std.zig.Server{ + .out = std.io.getStdOut(), + .in = undefined, // won't be receiving messages + .receive_fifo = undefined, // won't be receiving messages + }; + + var progress_thread = try std.Thread.spawn(.{}, progressThread, .{ + &progress, &server, &reset, + }); + defer { + reset.set(); + progress_thread.join(); + } + + try comp.update(main_progress_node); + + var error_bundle = try comp.getAllErrorsAlloc(); + defer error_bundle.deinit(comp.gpa); + if (error_bundle.errorMessageCount() > 0) { + try server.serveErrorBundle(error_bundle); + process.exit(2); + } + } else { + updateModule(comp, color) catch |err| switch (err) { + error.SemanticAnalyzeFail => process.exit(2), + else => |e| return e, + }; + } const exe_path = try global_cache_directory.join(arena, &.{comp.cache_use.whole.bin_sub_path.?}); child_argv.appendAssumeCapacity(exe_path); |
