Assumes that argv contains --listen=- and that the process being spawned
is the zig compiler - the same version that compiled the build runner.
Populates s.result_failed_command on failure.
pub fn evalZigProcess(
step_index: Configuration.Step.Index,
maker: *Maker,
argv: []const []const u8,
prog_node: std.Progress.Node,
watch: bool,
) (Step.ExtendedMakeError || error
pub fn evalZigProcess(
step_index: Configuration.Step.Index,
maker: *Maker,
argv: []const []const u8,
prog_node: std.Progress.Node,
watch: bool,
) (Step.ExtendedMakeError || error{NeedCompileErrorCheck})!?Path {
const s = maker.stepByIndex(step_index);
const gpa = maker.gpa;
const graph = maker.graph;
const io = graph.io;
// If an error occurs, it's happened in this command:
errdefer s.setFailedCommand(gpa, argv, .{});
if (s.getZigProcess()) |zp| update: {
assert(watch);
if (zp.progress_ipc_index) |ipc_index| prog_node.setIpcIndex(ipc_index);
zp.progress_ipc_index = null;
var exited = false;
defer if (exited) {
s.extended.compile.zig_process = null;
zp.deinit(io);
gpa.destroy(zp);
} else zp.saveState(prog_node);
const result = zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) {
error.BrokenPipe, error.EndOfStream => |reason| {
// Process restart required.
std.log.info("{s} restart required: {t}", .{ argv[0], reason });
_ = zp.child.wait(io) catch |e| return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e });
exited = true;
break :update;
},
error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e,
else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}),
};
if (s.result_error_bundle.errorMessageCount() > 0)
return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
if (s.result_error_msgs.items.len > 0 and result == null) {
// Crash detected.
const term = zp.child.wait(io) catch |e| {
return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e });
};
s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
exited = true;
try handleChildProcessTerm(s, maker, term);
return error.MakeFailed;
}
return result;
}
assert(argv.len != 0);
try handleChildProcUnsupported(s, maker);
try graph.handleVerbose(null, null, argv);
const zp = try gpa.create(ZigProcess);
defer if (!watch) gpa.destroy(zp);
zp.child = std.process.spawn(io, .{
.argv = argv,
.environ_map = &graph.environ_map,
.stdin = .pipe,
.stdout = .pipe,
.stderr = .pipe,
.request_resource_usage_statistics = true,
.progress_node = prog_node,
}) catch |err| return s.fail(maker, "failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
zp.multi_reader.init(gpa, io, zp.multi_reader_buffer.toStreams(), &.{
zp.child.stdout.?, zp.child.stderr.?,
});
if (watch) s.extended.compile.zig_process = zp;
defer if (!watch) zp.deinit(io);
const result = result: {
defer if (watch) zp.saveState(prog_node);
break :result zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) {
error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e,
else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}),
};
};
if (!watch) {
// Send EOF to stdin.
zp.child.stdin.?.close(io);
zp.child.stdin = null;
const term = zp.child.wait(io) catch |err| {
return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], err });
};
s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
// Special handling for compile step that is expecting compile errors.
const conf = &maker.scanned_config.configuration;
if (term == .exited) switch (step_index.ptr(conf).extended.get(conf.extra)) {
.compile => |compile| if (compile.flags4.expect_errors != .none) {
// Note that the exit code may be 0 in this case due to the
// compiler server protocol.
return error.NeedCompileErrorCheck;
},
else => {},
};
try handleChildProcessTerm(s, maker, term);
}
if (s.result_error_bundle.errorMessageCount() > 0) {
return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
}
return result;
}