| author | |
| committer | |
| log | d67c8f5d23ae02bf2dcc63049f39c71b35e80a50 |
| tree | b345ffdb3f1e289f997d95350276e6fbf7e62bb7 |
| parent | d579375a7af91320bd932878d2a5b124e2572b11 |
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); |
| 540 | 540 | ||
| 541 | var result: ?Path = null; | 541 | var result: ?Path = null; |
| 542 | var eos_err: error{EndOfStream}!void = {}; | ||
| 542 | 543 | ||
| 543 | const stdout = zp.multi_reader.fileReader(0); | 544 | const stdout = zp.multi_reader.fileReader(0); |
| 544 | 545 | ||
| ... | @@ -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 | } |
| 649 | 656 | ||
| 657 | try eos_err; | ||
| 658 | |||
| 650 | return result; | 659 | return result; |
| 651 | } | 660 | } |
| 652 | 661 |
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 | } |
| 248 | 248 | ||
| 249 | /// Wait until all streams fail or reach the end. | ||
| 250 | pub 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 | |||
| 249 | fn rebaseGrowing(mr: *MultiReader, context: *Context, capacity: usize) Allocator.Error!void { | 257 | fn 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 | }; |
| 492 | 493 | ||
| 493 | pub const RunResult = struct { | 494 | pub 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 | }); |
| 533 | 535 | ||
| 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 | }; |
| 141 | 142 | ||
| 142 | /// Collect the output from the process's stdout and stderr. Will return once | 143 | /// 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 | }); |
| 269 | 269 | ||
| 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 path | 275 | // 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); |
| 585 | 586 | ||
| 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 path | 592 | // 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); |
| 6878 | 6879 | ||
| ... | @@ -6887,55 +6888,69 @@ fn spawnZigRc( | ... | @@ -6887,55 +6888,69 @@ fn spawnZigRc( |
| 6887 | }); | 6888 | }); |
| 6888 | defer child.kill(io); | 6889 | defer child.kill(io); |
| 6889 | 6890 | ||
| 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(); | ||
| 6895 | 6895 | ||
| 6896 | const stdout = poller.reader(.stdout); | 6896 | const stdout = multi_reader.fileReader(0); |
| 6897 | const MessageHeader = std.zig.Server.Message.Header; | ||
| 6897 | 6898 | ||
| 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; | ||
| 6904 | 6900 | ||
| 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 is | 6917 | // 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 messages | 6923 | else => {}, // ignore other messages |
| 6913 | } | 6924 | } |
| 6914 | } | 6925 | } |
| 6915 | 6926 | ||
| 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); | ||
| 6918 | 6928 | ||
| 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 | }; |
| 6922 | 6933 | ||
| 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 | } |
| 6940 | 6955 | ||
| 6941 | pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { | 6956 | pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { |