Build.Step.Run: fix missing stdin buffer and flush

Writer.sendFileAll() asserts non-zero buffer capacity in the case that
the fallback is hit. It also requires the caller to flush. The buffer
may be bypassed as an optimization but this is not a guarantee.

Also improve the Writer documentation and add an earlier assert on
buffer capacity in sendFileAll().
This commit is contained in:
Isaac Freund 2025-08-16 11:32:10 +02:00 committed by Andrew Kelley
parent 399bace2f2
commit 551e009da7
2 changed files with 21 additions and 3 deletions

View File

@ -1780,9 +1780,10 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
}; };
defer file.close(); defer file.close();
// TODO https://github.com/ziglang/zig/issues/23955 // TODO https://github.com/ziglang/zig/issues/23955
var buffer: [1024]u8 = undefined; var read_buffer: [1024]u8 = undefined;
var file_reader = file.reader(&buffer); var file_reader = file.reader(&read_buffer);
var stdin_writer = child.stdin.?.writer(&.{}); var write_buffer: [1024]u8 = undefined;
var stdin_writer = child.stdin.?.writer(&write_buffer);
_ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{ error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{
path, file_reader.err.?, path, file_reader.err.?,
@ -1791,6 +1792,11 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
stdin_writer.err.?, stdin_writer.err.?,
}), }),
}; };
stdin_writer.interface.flush() catch |err| switch (err) {
error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{
stdin_writer.err.?,
}),
};
child.stdin.?.close(); child.stdin.?.close();
child.stdin = null; child.stdin = null;
}, },

View File

@ -882,6 +882,9 @@ pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
/// Unlike `writeSplat` and `writeVec`, this function will call into `VTable` /// Unlike `writeSplat` and `writeVec`, this function will call into `VTable`
/// even if there is enough buffer capacity for the file contents. /// even if there is enough buffer capacity for the file contents.
/// ///
/// The caller is responsible for flushing. Although the buffer may be bypassed
/// as an optimization, this is not a guarantee.
///
/// Although it would be possible to eliminate `error.Unimplemented` from the /// Although it would be possible to eliminate `error.Unimplemented` from the
/// error set by reading directly into the buffer in such case, this is not /// error set by reading directly into the buffer in such case, this is not
/// done because it is more efficient to do it higher up the call stack so that /// done because it is more efficient to do it higher up the call stack so that
@ -924,7 +927,16 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File
/// Number of bytes logically written is returned. This excludes bytes from /// Number of bytes logically written is returned. This excludes bytes from
/// `buffer` because they have already been logically written. /// `buffer` because they have already been logically written.
///
/// The caller is responsible for flushing. Although the buffer may be bypassed
/// as an optimization, this is not a guarantee.
///
/// Asserts nonzero buffer capacity.
pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize { pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
// The fallback sendFileReadingAll() path asserts non-zero buffer capacity.
// Explicitly assert it here as well to ensure the assert is hit even if
// the fallback path is not taken.
assert(w.buffer.len > 0);
var remaining = @intFromEnum(limit); var remaining = @intFromEnum(limit);
while (remaining > 0) { while (remaining > 0) {
const n = sendFile(w, file_reader, .limited(remaining)) catch |err| switch (err) { const n = sendFile(w, file_reader, .limited(remaining)) catch |err| switch (err) {