| ... | @@ -19,8 +19,15 @@ const maxInt = std.math.maxInt; | ... | @@ -19,8 +19,15 @@ const maxInt = std.math.maxInt; |
| 19 | const assert = std.debug.assert; | 19 | const assert = std.debug.assert; |
| 20 | | 20 | |
| 21 | pub const ChildProcess = struct { | 21 | pub const ChildProcess = struct { |
| 22 | pid: if (builtin.os.tag == .windows) void else i32, | 22 | pub const Id = switch (builtin.os.tag) { |
| 23 | handle: if (builtin.os.tag == .windows) windows.HANDLE else void, | 23 | .windows => windows.HANDLE, |
| | 24 | else => os.pid_t, |
| | 25 | }; |
| | 26 | |
| | 27 | /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`. |
| | 28 | /// On Windows this is the hProcess. |
| | 29 | /// On POSIX this is the pid. |
| | 30 | id: Id, |
| 24 | thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, | 31 | thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, |
| 25 | | 32 | |
| 26 | allocator: mem.Allocator, | 33 | allocator: mem.Allocator, |
| ... | @@ -105,8 +112,7 @@ pub const ChildProcess = struct { | ... | @@ -105,8 +112,7 @@ pub const ChildProcess = struct { |
| 105 | return .{ | 112 | return .{ |
| 106 | .allocator = allocator, | 113 | .allocator = allocator, |
| 107 | .argv = argv, | 114 | .argv = argv, |
| 108 | .pid = undefined, | 115 | .id = undefined, |
| 109 | .handle = undefined, | | |
| 110 | .thread_handle = undefined, | 116 | .thread_handle = undefined, |
| 111 | .err_pipe = null, | 117 | .err_pipe = null, |
| 112 | .term = null, | 118 | .term = null, |
| ... | @@ -131,6 +137,7 @@ pub const ChildProcess = struct { | ... | @@ -131,6 +137,7 @@ pub const ChildProcess = struct { |
| 131 | } | 137 | } |
| 132 | | 138 | |
| 133 | /// On success must call `kill` or `wait`. | 139 | /// On success must call `kill` or `wait`. |
| | 140 | /// After spawning the `id` is available. |
| 134 | pub fn spawn(self: *ChildProcess) SpawnError!void { | 141 | pub fn spawn(self: *ChildProcess) SpawnError!void { |
| 135 | if (!std.process.can_spawn) { | 142 | if (!std.process.can_spawn) { |
| 136 | @compileError("the target operating system cannot spawn processes"); | 143 | @compileError("the target operating system cannot spawn processes"); |
| ... | @@ -167,7 +174,7 @@ pub const ChildProcess = struct { | ... | @@ -167,7 +174,7 @@ pub const ChildProcess = struct { |
| 167 | return term; | 174 | return term; |
| 168 | } | 175 | } |
| 169 | | 176 | |
| 170 | try windows.TerminateProcess(self.handle, exit_code); | 177 | try windows.TerminateProcess(self.id, exit_code); |
| 171 | try self.waitUnwrappedWindows(); | 178 | try self.waitUnwrappedWindows(); |
| 172 | return self.term.?; | 179 | return self.term.?; |
| 173 | } | 180 | } |
| ... | @@ -177,18 +184,21 @@ pub const ChildProcess = struct { | ... | @@ -177,18 +184,21 @@ pub const ChildProcess = struct { |
| 177 | self.cleanupStreams(); | 184 | self.cleanupStreams(); |
| 178 | return term; | 185 | return term; |
| 179 | } | 186 | } |
| 180 | try os.kill(self.pid, os.SIG.TERM); | 187 | try os.kill(self.id, os.SIG.TERM); |
| 181 | try self.waitUnwrapped(); | 188 | try self.waitUnwrapped(); |
| 182 | return self.term.?; | 189 | return self.term.?; |
| 183 | } | 190 | } |
| 184 | | 191 | |
| 185 | /// Blocks until child process terminates and then cleans up all resources. | 192 | /// Blocks until child process terminates and then cleans up all resources. |
| 186 | pub fn wait(self: *ChildProcess) !Term { | 193 | pub fn wait(self: *ChildProcess) !Term { |
| 187 | if (builtin.os.tag == .windows) { | 194 | const term = if (builtin.os.tag == .windows) |
| 188 | return self.waitWindows(); | 195 | try self.waitWindows() |
| 189 | } else { | 196 | else |
| 190 | return self.waitPosix(); | 197 | try self.waitPosix(); |
| 191 | } | 198 | |
| | 199 | self.id = undefined; |
| | 200 | |
| | 201 | return term; |
| 192 | } | 202 | } |
| 193 | | 203 | |
| 194 | pub const ExecResult = struct { | 204 | pub const ExecResult = struct { |
| ... | @@ -246,6 +256,11 @@ pub const ChildProcess = struct { | ... | @@ -246,6 +256,11 @@ pub const ChildProcess = struct { |
| 246 | stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr)); | 256 | stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr)); |
| 247 | } | 257 | } |
| 248 | | 258 | |
| | 259 | pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{ |
| | 260 | StdoutStreamTooLong, |
| | 261 | StderrStreamTooLong, |
| | 262 | }; |
| | 263 | |
| 249 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. | 264 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. |
| 250 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | 265 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 251 | pub fn exec(args: struct { | 266 | pub fn exec(args: struct { |
| ... | @@ -256,7 +271,7 @@ pub const ChildProcess = struct { | ... | @@ -256,7 +271,7 @@ pub const ChildProcess = struct { |
| 256 | env_map: ?*const EnvMap = null, | 271 | env_map: ?*const EnvMap = null, |
| 257 | max_output_bytes: usize = 50 * 1024, | 272 | max_output_bytes: usize = 50 * 1024, |
| 258 | expand_arg0: Arg0Expand = .no_expand, | 273 | expand_arg0: Arg0Expand = .no_expand, |
| 259 | }) !ExecResult { | 274 | }) ExecError!ExecResult { |
| 260 | var child = ChildProcess.init(args.argv, args.allocator); | 275 | var child = ChildProcess.init(args.argv, args.allocator); |
| 261 | child.stdin_behavior = .Ignore; | 276 | child.stdin_behavior = .Ignore; |
| 262 | child.stdout_behavior = .Pipe; | 277 | child.stdout_behavior = .Pipe; |
| ... | @@ -304,18 +319,18 @@ pub const ChildProcess = struct { | ... | @@ -304,18 +319,18 @@ pub const ChildProcess = struct { |
| 304 | } | 319 | } |
| 305 | | 320 | |
| 306 | fn waitUnwrappedWindows(self: *ChildProcess) !void { | 321 | fn waitUnwrappedWindows(self: *ChildProcess) !void { |
| 307 | const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false); | 322 | const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); |
| 308 | | 323 | |
| 309 | self.term = @as(SpawnError!Term, x: { | 324 | self.term = @as(SpawnError!Term, x: { |
| 310 | var exit_code: windows.DWORD = undefined; | 325 | var exit_code: windows.DWORD = undefined; |
| 311 | if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) { | 326 | if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) { |
| 312 | break :x Term{ .Unknown = 0 }; | 327 | break :x Term{ .Unknown = 0 }; |
| 313 | } else { | 328 | } else { |
| 314 | break :x Term{ .Exited = @truncate(u8, exit_code) }; | 329 | break :x Term{ .Exited = @truncate(u8, exit_code) }; |
| 315 | } | 330 | } |
| 316 | }); | 331 | }); |
| 317 | | 332 | |
| 318 | os.close(self.handle); | 333 | os.close(self.id); |
| 319 | os.close(self.thread_handle); | 334 | os.close(self.thread_handle); |
| 320 | self.cleanupStreams(); | 335 | self.cleanupStreams(); |
| 321 | return result; | 336 | return result; |
| ... | @@ -323,9 +338,9 @@ pub const ChildProcess = struct { | ... | @@ -323,9 +338,9 @@ pub const ChildProcess = struct { |
| 323 | | 338 | |
| 324 | fn waitUnwrapped(self: *ChildProcess) !void { | 339 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 325 | const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) | 340 | const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) |
| 326 | try os.posix_spawn.waitpid(self.pid, 0) | 341 | try os.posix_spawn.waitpid(self.id, 0) |
| 327 | else | 342 | else |
| 328 | os.waitpid(self.pid, 0); | 343 | os.waitpid(self.id, 0); |
| 329 | const status = res.status; | 344 | const status = res.status; |
| 330 | self.cleanupStreams(); | 345 | self.cleanupStreams(); |
| 331 | self.handleWaitResult(status); | 346 | self.handleWaitResult(status); |
| ... | @@ -483,7 +498,7 @@ pub const ChildProcess = struct { | ... | @@ -483,7 +498,7 @@ pub const ChildProcess = struct { |
| 483 | self.stderr = null; | 498 | self.stderr = null; |
| 484 | } | 499 | } |
| 485 | | 500 | |
| 486 | self.pid = pid; | 501 | self.id = pid; |
| 487 | self.term = null; | 502 | self.term = null; |
| 488 | | 503 | |
| 489 | if (self.stdin_behavior == StdIo.Pipe) { | 504 | if (self.stdin_behavior == StdIo.Pipe) { |
| ... | @@ -657,7 +672,7 @@ pub const ChildProcess = struct { | ... | @@ -657,7 +672,7 @@ pub const ChildProcess = struct { |
| 657 | self.stderr = null; | 672 | self.stderr = null; |
| 658 | } | 673 | } |
| 659 | | 674 | |
| 660 | self.pid = pid; | 675 | self.id = pid; |
| 661 | self.err_pipe = err_pipe; | 676 | self.err_pipe = err_pipe; |
| 662 | self.term = null; | 677 | self.term = null; |
| 663 | | 678 | |
| ... | @@ -923,7 +938,7 @@ pub const ChildProcess = struct { | ... | @@ -923,7 +938,7 @@ pub const ChildProcess = struct { |
| 923 | self.stderr = null; | 938 | self.stderr = null; |
| 924 | } | 939 | } |
| 925 | | 940 | |
| 926 | self.handle = piProcInfo.hProcess; | 941 | self.id = piProcInfo.hProcess; |
| 927 | self.thread_handle = piProcInfo.hThread; | 942 | self.thread_handle = piProcInfo.hThread; |
| 928 | self.term = null; | 943 | self.term = null; |
| 929 | | 944 | |