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