| ... | ... | @@ -186,6 +186,56 @@ pub const ChildProcess = struct { |
| 186 | 186 | |
| 187 | 187 | pub const exec2 = @compileError("deprecated: exec2 is renamed to exec"); |
| 188 | 188 | |
| 189 | fn collectOutputPosix( |
| 190 | child: *const ChildProcess, |
| 191 | stdout: *std.ArrayList(u8), |
| 192 | stderr: *std.ArrayList(u8), |
| 193 | max_output_bytes: usize, |
| 194 | ) !void { |
| 195 | var poll_fds = [_]os.pollfd{ |
| 196 | .{ .fd = child.stdout.?.handle, .events = os.POLLIN, .revents = undefined }, |
| 197 | .{ .fd = child.stderr.?.handle, .events = os.POLLIN, .revents = undefined }, |
| 198 | }; |
| 199 | |
| 200 | var dead_fds: usize = 0; |
| 201 | // We ask for ensureCapacity with this much extra space. This has more of an |
| 202 | // effect on small reads because once the reads start to get larger the amount |
| 203 | // of space an ArrayList will allocate grows exponentially. |
| 204 | const bump_amt = 512; |
| 205 | |
| 206 | while (dead_fds < poll_fds.len) { |
| 207 | const events = try os.poll(&poll_fds, std.math.maxInt(i32)); |
| 208 | if (events == 0) continue; |
| 209 | |
| 210 | // Try reading whatever is available before checking the error |
| 211 | // conditions. |
| 212 | if (poll_fds[0].revents & os.POLLIN != 0) { |
| 213 | // stdout is ready. |
| 214 | const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes); |
| 215 | if (new_capacity == stdout.capacity) return error.StdoutStreamTooLong; |
| 216 | try stdout.ensureCapacity(new_capacity); |
| 217 | stdout.items.len += try os.read(poll_fds[0].fd, stdout.unusedCapacitySlice()); |
| 218 | } |
| 219 | if (poll_fds[1].revents & os.POLLIN != 0) { |
| 220 | // stderr is ready. |
| 221 | const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes); |
| 222 | if (new_capacity == stderr.capacity) return error.StderrStreamTooLong; |
| 223 | try stderr.ensureCapacity(new_capacity); |
| 224 | stderr.items.len += try os.read(poll_fds[1].fd, stderr.unusedCapacitySlice()); |
| 225 | } |
| 226 | |
| 227 | // Exclude the fds that signaled an error. |
| 228 | if (poll_fds[0].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) { |
| 229 | poll_fds[0].fd = -1; |
| 230 | dead_fds += 1; |
| 231 | } |
| 232 | if (poll_fds[1].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) { |
| 233 | poll_fds[1].fd = -1; |
| 234 | dead_fds += 1; |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
| 189 | 239 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. |
| 190 | 240 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 191 | 241 | pub fn exec(args: struct { |
| ... | ... | @@ -210,19 +260,33 @@ pub const ChildProcess = struct { |
| 210 | 260 | |
| 211 | 261 | try child.spawn(); |
| 212 | 262 | |
| 213 | | const stdout_in = child.stdout.?.reader(); |
| 214 | | const stderr_in = child.stderr.?.reader(); |
| 263 | // TODO collect output in a deadlock-avoiding way on Windows. |
| 264 | // https://github.com/ziglang/zig/issues/6343 |
| 265 | if (builtin.os.tag == .windows) { |
| 266 | const stdout_in = child.stdout.?.reader(); |
| 267 | const stderr_in = child.stderr.?.reader(); |
| 268 | |
| 269 | const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes); |
| 270 | errdefer args.allocator.free(stdout); |
| 271 | const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes); |
| 272 | errdefer args.allocator.free(stderr); |
| 273 | |
| 274 | return ExecResult{ |
| 275 | .term = try child.wait(), |
| 276 | .stdout = stdout, |
| 277 | .stderr = stderr, |
| 278 | }; |
| 279 | } |
| 280 | |
| 281 | var stdout = std.ArrayList(u8).init(args.allocator); |
| 282 | var stderr = std.ArrayList(u8).init(args.allocator); |
| 215 | 283 | |
| 216 | | // TODO https://github.com/ziglang/zig/issues/6343 |
| 217 | | const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes); |
| 218 | | errdefer args.allocator.free(stdout); |
| 219 | | const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes); |
| 220 | | errdefer args.allocator.free(stderr); |
| 284 | try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); |
| 221 | 285 | |
| 222 | 286 | return ExecResult{ |
| 223 | 287 | .term = try child.wait(), |
| 224 | | .stdout = stdout, |
| 225 | | .stderr = stderr, |
| 288 | .stdout = stdout.toOwnedSlice(), |
| 289 | .stderr = stderr.toOwnedSlice(), |
| 226 | 290 | }; |
| 227 | 291 | } |
| 228 | 292 | |