authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-03-08 11:54:56-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-03-11 05:06:17-07:00
log8799f7466d68405d715bf567c3de9935b619fcf6
tree1fb42c48809c524286a2bc1824c6d5291e419f6e
parentdc4b05894da63889d895482ef01e77ef0bf6e88c

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.

3 files changed, 153 insertions(+), 102 deletions(-)

lib/compiler/resinator/main.zig+6
......@@ -46,6 +46,12 @@ pub fn main() !void {
4646 },
4747 };
4848
49 if (zig_integration) {
50 // Send progress with an empty string to indicate that the building of the
51 // resinator binary is finished and we've moved on to actually compiling the .rc file
52 try error_handler.server.serveStringMessage(.progress, "");
53 }
54
4955 var options = options: {
5056 var cli_diagnostics = cli.Diagnostics.init(allocator);
5157 defer cli_diagnostics.deinit();
src/Compilation.zig+97-98
......@@ -4841,6 +4841,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
48414841 try argv.appendSlice(&.{
48424842 self_exe_path,
48434843 "rc",
4844 "--zig-integration",
48444845 "/:no-preprocess",
48454846 "/x", // ignore INCLUDE environment variable
48464847 "/c65001", // UTF-8 codepage
......@@ -4849,31 +4850,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
48494850 });
48504851 try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });
48514852
4852 var child = std.ChildProcess.init(argv.items, arena);
4853 child.stdin_behavior = .Ignore;
4854 child.stdout_behavior = .Ignore;
4855 child.stderr_behavior = .Pipe;
4856
4857 try child.spawn();
4858
4859 const stderr_reader = child.stderr.?.reader();
4860 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
4861 const term = child.wait() catch |err| {
4862 return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
4863 };
4864
4865 switch (term) {
4866 .Exited => |code| {
4867 if (code != 0) {
4868 log.err("zig rc failed with stderr:\n{s}", .{stderr});
4869 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
4870 }
4871 },
4872 else => {
4873 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
4874 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
4875 },
4876 }
4853 try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node);
48774854
48784855 break :blk digest;
48794856 };
......@@ -4941,79 +4918,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
49414918 try argv.appendSlice(rc_src.extra_flags);
49424919 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
49434920
4944 {
4945 var child = std.ChildProcess.init(argv.items, arena);
4946 child.stdin_behavior = .Ignore;
4947 child.stdout_behavior = .Pipe;
4948 child.stderr_behavior = .Pipe;
4949
4950 child.spawn() catch |err| {
4951 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv.items[0], @errorName(err) });
4952 };
4953
4954 var poller = std.io.poll(comp.gpa, enum { stdout }, .{
4955 .stdout = child.stdout.?,
4956 });
4957 defer poller.deinit();
4958
4959 const stdout = poller.fifo(.stdout);
4960
4961 poll: while (true) {
4962 while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
4963 if (!(try poller.poll())) break :poll;
4964 }
4965 const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
4966 while (stdout.readableLength() < header.bytes_len) {
4967 if (!(try poller.poll())) break :poll;
4968 }
4969 const body = stdout.readableSliceOfLen(header.bytes_len);
4970
4971 switch (header.tag) {
4972 // We expect exactly one ErrorBundle, and if any error_bundle header is
4973 // sent then it's a fatal error.
4974 .error_bundle => {
4975 const EbHdr = std.zig.Server.Message.ErrorBundle;
4976 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
4977 const extra_bytes =
4978 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
4979 const string_bytes =
4980 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
4981 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
4982 const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len);
4983 @memcpy(extra_array, unaligned_extra);
4984 const error_bundle = .{
4985 .string_bytes = try comp.gpa.dupe(u8, string_bytes),
4986 .extra = extra_array,
4987 };
4988 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
4989 },
4990 else => {}, // ignore other messages
4991 }
4992
4993 stdout.discard(body.len);
4994 }
4995
4996 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
4997 const stderr_reader = child.stderr.?.reader();
4998 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
4999
5000 const term = child.wait() catch |err| {
5001 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv.items[0], @errorName(err) });
5002 };
5003
5004 switch (term) {
5005 .Exited => |code| {
5006 if (code != 0) {
5007 log.err("zig rc failed with stderr:\n{s}", .{stderr});
5008 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
5009 }
5010 },
5011 else => {
5012 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
5013 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
5014 },
5015 }
5016 }
4921 try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node);
50174922
50184923 // Read depfile and update cache manifest
50194924 {
......@@ -5079,6 +4984,100 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
50794984 };
50804985}
50814986
4987fn spawnZigRc(
4988 comp: *Compilation,
4989 win32_resource: *Win32Resource,
4990 src_basename: []const u8,
4991 arena: Allocator,
4992 argv: []const []const u8,
4993 child_progress_node: *std.Progress.Node,
4994) !void {
4995 var node_name: std.ArrayListUnmanaged(u8) = .{};
4996 defer node_name.deinit(arena);
4997
4998 var child = std.ChildProcess.init(argv, arena);
4999 child.stdin_behavior = .Ignore;
5000 child.stdout_behavior = .Pipe;
5001 child.stderr_behavior = .Pipe;
5002
5003 child.spawn() catch |err| {
5004 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
5005 };
5006
5007 var poller = std.io.poll(comp.gpa, enum { stdout }, .{
5008 .stdout = child.stdout.?,
5009 });
5010 defer poller.deinit();
5011
5012 const stdout = poller.fifo(.stdout);
5013
5014 poll: while (true) {
5015 while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
5016 if (!(try poller.poll())) break :poll;
5017 }
5018 const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
5019 while (stdout.readableLength() < header.bytes_len) {
5020 if (!(try poller.poll())) break :poll;
5021 }
5022 const body = stdout.readableSliceOfLen(header.bytes_len);
5023
5024 switch (header.tag) {
5025 // We expect exactly one ErrorBundle, and if any error_bundle header is
5026 // sent then it's a fatal error.
5027 .error_bundle => {
5028 const EbHdr = std.zig.Server.Message.ErrorBundle;
5029 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
5030 const extra_bytes =
5031 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
5032 const string_bytes =
5033 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
5034 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
5035 const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len);
5036 @memcpy(extra_array, unaligned_extra);
5037 const error_bundle = std.zig.ErrorBundle{
5038 .string_bytes = try comp.gpa.dupe(u8, string_bytes),
5039 .extra = extra_array,
5040 };
5041 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
5042 },
5043 .progress => {
5044 node_name.clearRetainingCapacity();
5045 if (body.len > 0) {
5046 try node_name.appendSlice(arena, "build 'zig rc'... ");
5047 try node_name.appendSlice(arena, body);
5048 child_progress_node.setName(node_name.items);
5049 } else {
5050 child_progress_node.setName(src_basename);
5051 }
5052 },
5053 else => {}, // ignore other messages
5054 }
5055
5056 stdout.discard(body.len);
5057 }
5058
5059 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
5060 const stderr_reader = child.stderr.?.reader();
5061 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
5062
5063 const term = child.wait() catch |err| {
5064 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) });
5065 };
5066
5067 switch (term) {
5068 .Exited => |code| {
5069 if (code != 0) {
5070 log.err("zig rc failed with stderr:\n{s}", .{stderr});
5071 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
5072 }
5073 },
5074 else => {
5075 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
5076 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
5077 },
5078 }
5079}
5080
50825081pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
50835082 const s = std.fs.path.sep_str;
50845083 const rand_int = std.crypto.random.int(u64);
src/main.zig+50-4
......@@ -291,11 +291,13 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
291291 } else if (mem.eql(u8, cmd, "translate-c")) {
292292 return buildOutputType(gpa, arena, args, .translate_c);
293293 } else if (mem.eql(u8, cmd, "rc")) {
294 const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
294295 return jitCmd(gpa, arena, cmd_args, .{
295296 .cmd_name = "resinator",
296297 .root_src_path = "resinator/main.zig",
297298 .depend_on_aro = true,
298299 .prepend_zig_lib_dir_path = true,
300 .server = use_server,
299301 });
300302 } else if (mem.eql(u8, cmd, "fmt")) {
301303 return jitCmd(gpa, arena, cmd_args, .{
......@@ -5304,6 +5306,8 @@ const JitCmdOptions = struct {
53045306 prepend_zig_exe_path: bool = false,
53055307 depend_on_aro: bool = false,
53065308 capture: ?*[]u8 = null,
5309 /// Send progress and error bundles via std.zig.Server over stdout
5310 server: bool = false,
53075311};
53085312
53095313fn jitCmd(
......@@ -5449,10 +5453,52 @@ fn jitCmd(
54495453 };
54505454 defer comp.destroy();
54515455
5452 updateModule(comp, color) catch |err| switch (err) {
5453 error.SemanticAnalyzeFail => process.exit(2),
5454 else => |e| return e,
5455 };
5456 if (options.server and !builtin.single_threaded) {
5457 var reset: std.Thread.ResetEvent = .{};
5458 var progress: std.Progress = .{
5459 .terminal = null,
5460 .root = .{
5461 .context = undefined,
5462 .parent = null,
5463 .name = "",
5464 .unprotected_estimated_total_items = 0,
5465 .unprotected_completed_items = 0,
5466 },
5467 .columns_written = 0,
5468 .prev_refresh_timestamp = 0,
5469 .timer = null,
5470 .done = false,
5471 };
5472 const main_progress_node = &progress.root;
5473 main_progress_node.context = &progress;
5474 var server = std.zig.Server{
5475 .out = std.io.getStdOut(),
5476 .in = undefined, // won't be receiving messages
5477 .receive_fifo = undefined, // won't be receiving messages
5478 };
5479
5480 var progress_thread = try std.Thread.spawn(.{}, progressThread, .{
5481 &progress, &server, &reset,
5482 });
5483 defer {
5484 reset.set();
5485 progress_thread.join();
5486 }
5487
5488 try comp.update(main_progress_node);
5489
5490 var error_bundle = try comp.getAllErrorsAlloc();
5491 defer error_bundle.deinit(comp.gpa);
5492 if (error_bundle.errorMessageCount() > 0) {
5493 try server.serveErrorBundle(error_bundle);
5494 process.exit(2);
5495 }
5496 } else {
5497 updateModule(comp, color) catch |err| switch (err) {
5498 error.SemanticAnalyzeFail => process.exit(2),
5499 else => |e| return e,
5500 };
5501 }
54565502
54575503 const exe_path = try global_cache_directory.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});
54585504 child_argv.appendAssumeCapacity(exe_path);