srctree

Ryan Liptak parent dc4b0589 8799f746
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.

inlinesplit
lib/compiler/resinator/main.zig added: 156, removed: 105, total 51
@@ -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();
 
src/Compilation.zig added: 156, removed: 105, total 51
@@ -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);
 
src/main.zig added: 156, removed: 105, total 51
@@ -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);