authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-14 00:56:00-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 12:10:01-08:00
logd67c8f5d23ae02bf2dcc63049f39c71b35e80a50
treeb345ffdb3f1e289f997d95350276e6fbf7e62bb7
parentd579375a7af91320bd932878d2a5b124e2572b11

compiler: update for std.Io.File.MultiReader API


6 files changed, 59 insertions(+), 22 deletions(-)

lib/std/Build/Step.zig+10-1
...@@ -539,6 +539,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build....@@ -539,6 +539,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
539 if (!watch) try sendMessage(io, zp.child.stdin.?, .exit);539 if (!watch) try sendMessage(io, zp.child.stdin.?, .exit);
540540
541 var result: ?Path = null;541 var result: ?Path = null;
542 var eos_err: error{EndOfStream}!void = {};
542543
543 const stdout = zp.multi_reader.fileReader(0);544 const stdout = zp.multi_reader.fileReader(0);
544545
...@@ -549,7 +550,13 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build....@@ -549,7 +550,13 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
549 error.ReadFailed => return stdout.err.?,550 error.ReadFailed => return stdout.err.?,
550 };551 };
551 const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) {552 const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) {
552 error.EndOfStream => |e| return e,553 error.EndOfStream => |e| {
554 // Better to report the crash with stderr below, but we set
555 // this in case the child exits successfully while violating
556 // this protocol.
557 eos_err = e;
558 break;
559 },
553 error.ReadFailed => return stdout.err.?,560 error.ReadFailed => return stdout.err.?,
554 };561 };
555 switch (header.tag) {562 switch (header.tag) {
...@@ -647,6 +654,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build....@@ -647,6 +654,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
647 try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents));654 try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents));
648 }655 }
649656
657 try eos_err;
658
650 return result;659 return result;
651}660}
652661
lib/std/Io/File/MultiReader.zig+8
...@@ -246,6 +246,14 @@ pub fn fill(mr: *MultiReader, unused_capacity: usize, timeout: Io.Timeout) FillE...@@ -246,6 +246,14 @@ pub fn fill(mr: *MultiReader, unused_capacity: usize, timeout: Io.Timeout) FillE
246 if (!any_completed) return error.EndOfStream;246 if (!any_completed) return error.EndOfStream;
247}247}
248248
249/// Wait until all streams fail or reach the end.
250pub fn fillRemaining(mr: *MultiReader, timeout: Io.Timeout) Io.Batch.WaitError!void {
251 while (fill(mr, 1, timeout)) |_| {} else |err| switch (err) {
252 error.EndOfStream => return,
253 else => |e| return e,
254 }
255}
256
249fn rebaseGrowing(mr: *MultiReader, context: *Context, capacity: usize) Allocator.Error!void {257fn rebaseGrowing(mr: *MultiReader, context: *Context, capacity: usize) Allocator.Error!void {
250 const gpa = mr.gpa;258 const gpa = mr.gpa;
251 const r = &context.fr.interface;259 const r = &context.fr.interface;
lib/std/process.zig+2
...@@ -488,6 +488,7 @@ pub const RunOptions = struct {...@@ -488,6 +488,7 @@ pub const RunOptions = struct {
488 create_no_window: bool = true,488 create_no_window: bool = true,
489 /// Darwin-only. Disable ASLR for the child process.489 /// Darwin-only. Disable ASLR for the child process.
490 disable_aslr: bool = false,490 disable_aslr: bool = false,
491 timeout: Io.Timeout = .none,
491};492};
492493
493pub const RunResult = struct {494pub const RunResult = struct {
...@@ -529,6 +530,7 @@ pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {...@@ -529,6 +530,7 @@ pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {
529 .stderr = &stderr,530 .stderr = &stderr,
530 .stdout_limit = options.stdout_limit,531 .stdout_limit = options.stdout_limit,
531 .stderr_limit = options.stderr_limit,532 .stderr_limit = options.stderr_limit,
533 .timeout = options.timeout,
532 });534 });
533535
534 const term = try child.wait(io);536 const term = try child.wait(io);
lib/std/process/Child.zig+2-1
...@@ -137,6 +137,7 @@ pub const CollectOutputOptions = struct {...@@ -137,6 +137,7 @@ pub const CollectOutputOptions = struct {
137 allocator: ?Allocator = null,137 allocator: ?Allocator = null,
138 stdout_limit: Io.Limit = .unlimited,138 stdout_limit: Io.Limit = .unlimited,
139 stderr_limit: Io.Limit = .unlimited,139 stderr_limit: Io.Limit = .unlimited,
140 timeout: Io.Timeout = .none,
140};141};
141142
142/// Collect the output from the process's stdout and stderr. Will return once143/// Collect the output from the process's stdout and stderr. Will return once
...@@ -173,7 +174,7 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)...@@ -173,7 +174,7 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)
173 remaining += 1;174 remaining += 1;
174 }175 }
175 while (remaining > 0) {176 while (remaining > 0) {
176 try batch.wait(io, .none);177 try batch.wait(io, options.timeout);
177 while (batch.next()) |op| {178 while (batch.next()) |op| {
178 const n = try reads[op].file_read_streaming.status.result;179 const n = try reads[op].file_read_streaming.status.result;
179 if (n == 0) {180 if (n == 0) {
lib/std/zig/LibCInstallation.zig+4-2
...@@ -268,7 +268,8 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar...@@ -268,7 +268,8 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
268 });268 });
269269
270 const run_res = std.process.run(gpa, io, .{270 const run_res = std.process.run(gpa, io, .{
271 .max_output_bytes = 1024 * 1024,271 .stdout_limit = .limited(1024 * 1024),
272 .stderr_limit = .limited(1024 * 1024),
272 .argv = argv.items,273 .argv = argv.items,
273 .environ_map = &environ_map,274 .environ_map = &environ_map,
274 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path275 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
...@@ -584,7 +585,8 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8 {...@@ -584,7 +585,8 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8 {
584 try argv.append(arg1);585 try argv.append(arg1);
585586
586 const run_res = std.process.run(gpa, io, .{587 const run_res = std.process.run(gpa, io, .{
587 .max_output_bytes = 1024 * 1024,588 .stdout_limit = .limited(1024 * 1024),
589 .stderr_limit = .limited(1024 * 1024),
588 .argv = argv.items,590 .argv = argv.items,
589 .environ_map = &environ_map,591 .environ_map = &environ_map,
590 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path592 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
src/Compilation.zig+33-18
...@@ -6873,6 +6873,7 @@ fn spawnZigRc(...@@ -6873,6 +6873,7 @@ fn spawnZigRc(
6873 child_progress_node: std.Progress.Node,6873 child_progress_node: std.Progress.Node,
6874) !void {6874) !void {
6875 const io = comp.io;6875 const io = comp.io;
6876 const gpa = comp.gpa;
6876 var node_name: std.ArrayList(u8) = .empty;6877 var node_name: std.ArrayList(u8) = .empty;
6877 defer node_name.deinit(arena);6878 defer node_name.deinit(arena);
68786879
...@@ -6887,55 +6888,69 @@ fn spawnZigRc(...@@ -6887,55 +6888,69 @@ fn spawnZigRc(
6887 });6888 });
6888 defer child.kill(io);6889 defer child.kill(io);
68896890
6890 var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{6891 var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
6891 .stdout = child.stdout.?,6892 var multi_reader: Io.File.MultiReader = undefined;
6892 .stderr = child.stderr.?,6893 multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
6893 });6894 defer multi_reader.deinit();
6894 defer poller.deinit();
68956895
6896 const stdout = poller.reader(.stdout);6896 const stdout = multi_reader.fileReader(0);
6897 const MessageHeader = std.zig.Server.Message.Header;
68976898
6898 poll: while (true) {6899 var eos_err: error{EndOfStream}!void = {};
6899 const MessageHeader = std.zig.Server.Message.Header;
6900 while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll;
6901 const header = stdout.takeStruct(MessageHeader, .little) catch unreachable;
6902 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
6903 const body = stdout.take(header.bytes_len) catch unreachable;
69046900
6901 while (true) {
6902 const header = stdout.interface.takeStruct(MessageHeader, .little) catch |err| switch (err) {
6903 error.EndOfStream => break,
6904 error.ReadFailed => return stdout.err.?,
6905 };
6906 const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) {
6907 error.EndOfStream => |e| {
6908 // Better to report the crash with stderr below, but we set
6909 // this in case the child exits successfully while violating
6910 // this protocol.
6911 eos_err = e;
6912 break;
6913 },
6914 error.ReadFailed => return stdout.err.?,
6915 };
6905 switch (header.tag) {6916 switch (header.tag) {
6906 // We expect exactly one ErrorBundle, and if any error_bundle header is6917 // We expect exactly one ErrorBundle, and if any error_bundle header is
6907 // sent then it's a fatal error.6918 // sent then it's a fatal error.
6908 .error_bundle => {6919 .error_bundle => {
6909 const error_bundle = try std.zig.Server.allocErrorBundle(comp.gpa, body);6920 const error_bundle = try std.zig.Server.allocErrorBundle(gpa, body);
6910 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);6921 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
6911 },6922 },
6912 else => {}, // ignore other messages6923 else => {}, // ignore other messages
6913 }6924 }
6914 }6925 }
69156926
6916 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)6927 try multi_reader.fillRemaining(.none);
6917 const stderr = poller.reader(.stderr);
69186928
6929 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
6919 const term = child.wait(io) catch |err| {6930 const term = child.wait(io) catch |err| {
6920 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err });6931 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err });
6921 };6932 };
69226933
6934 const stderr = multi_reader.reader(1).buffered();
6935
6923 switch (term) {6936 switch (term) {
6924 .exited => |code| {6937 .exited => |code| {
6925 if (code != 0) {6938 if (code != 0) {
6926 log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()});6939 log.err("zig rc failed with stderr:\n{s}", .{stderr});
6927 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});6940 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
6928 }6941 }
6929 },6942 },
6930 .signal => |sig| {6943 .signal => |sig| {
6931 log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr.buffered() });6944 log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr });
6932 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});6945 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6933 },6946 },
6934 else => {6947 else => {
6935 log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()});6948 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
6936 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});6949 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6937 },6950 },
6938 }6951 }
6952
6953 try eos_err;
6939}6954}
69406955
6941pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {6956pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {