authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-10 14:58:24-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-10 15:40:27-05:00
logd44a69689eb2579ceb290cd19b91f824b30e6acc
treec9cd60aa685e569fb0b93679494f463040d9e73a
parent891e2149b0d72a511e58a994c3f66dff500e6615
signaturelock-open Commit is signed but in an unrecognized format.

std.ChildProcess.spawn has a consistent error set

across targets. Also fix detection of pkg-config not installed on Windows when using zig build.

2 files changed, 78 insertions(+), 18 deletions(-)

lib/std/build.zig+1
...@@ -957,6 +957,7 @@ pub const Builder = struct {...@@ -957,6 +957,7 @@ pub const Builder = struct {
957 error.ProcessTerminated => error.PkgConfigCrashed,957 error.ProcessTerminated => error.PkgConfigCrashed,
958 error.ExitCodeFailure => error.PkgConfigFailed,958 error.ExitCodeFailure => error.PkgConfigFailed,
959 error.FileNotFound => error.PkgConfigNotInstalled,959 error.FileNotFound => error.PkgConfigNotInstalled,
960 error.InvalidName => error.PkgConfigNotInstalled,
960 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,961 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
961 else => return err,962 else => return err,
962 };963 };
lib/std/child_process.zig+77-18
...@@ -50,8 +50,20 @@ pub const ChildProcess = struct {...@@ -50,8 +50,20 @@ pub const ChildProcess = struct {
50 err_pipe: if (builtin.os == .windows) void else [2]os.fd_t,50 err_pipe: if (builtin.os == .windows) void else [2]os.fd_t,
51 llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node,51 llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node,
5252
53 pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||53 pub const SpawnError = error{
54 os.ChangeCurDirError || windows.CreateProcessError;54 OutOfMemory,
55
56 /// POSIX-only. `StdIo.Ignore` was selected and opening `/dev/null` returned ENODEV.
57 NoDevice,
58
59 /// Windows-only. One of:
60 /// * `cwd` was provided and it could not be re-encoded into UTF16LE, or
61 /// * The `PATH` or `PATHEXT` environment variable contained invalid UTF-8.
62 InvalidUtf8,
63
64 /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
65 CurrentWorkingDirectoryUnlinked,
66 } || os.ExecveError || os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError;
5567
56 pub const Term = union(enum) {68 pub const Term = union(enum) {
57 Exited: u32,69 Exited: u32,
...@@ -102,7 +114,7 @@ pub const ChildProcess = struct {...@@ -102,7 +114,7 @@ pub const ChildProcess = struct {
102 }114 }
103115
104 /// On success must call `kill` or `wait`.116 /// On success must call `kill` or `wait`.
105 pub fn spawn(self: *ChildProcess) !void {117 pub fn spawn(self: *ChildProcess) SpawnError!void {
106 if (builtin.os == .windows) {118 if (builtin.os == .windows) {
107 return self.spawnWindows();119 return self.spawnWindows();
108 } else {120 } else {
...@@ -110,7 +122,7 @@ pub const ChildProcess = struct {...@@ -110,7 +122,7 @@ pub const ChildProcess = struct {
110 }122 }
111 }123 }
112124
113 pub fn spawnAndWait(self: *ChildProcess) !Term {125 pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term {
114 try self.spawn();126 try self.spawn();
115 return self.wait();127 return self.wait();
116 }128 }
...@@ -162,7 +174,13 @@ pub const ChildProcess = struct {...@@ -162,7 +174,13 @@ pub const ChildProcess = struct {
162174
163 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.175 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
164 /// If it succeeds, the caller owns result.stdout and result.stderr memory.176 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
165 pub fn exec(allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, max_output_size: usize) !ExecResult {177 pub fn exec(
178 allocator: *mem.Allocator,
179 argv: []const []const u8,
180 cwd: ?[]const u8,
181 env_map: ?*const BufMap,
182 max_output_size: usize,
183 ) !ExecResult {
166 const child = try ChildProcess.init(argv, allocator);184 const child = try ChildProcess.init(argv, allocator);
167 defer child.deinit();185 defer child.deinit();
168186
...@@ -292,7 +310,7 @@ pub const ChildProcess = struct {...@@ -292,7 +310,7 @@ pub const ChildProcess = struct {
292 Term{ .Unknown = status };310 Term{ .Unknown = status };
293 }311 }
294312
295 fn spawnPosix(self: *ChildProcess) !void {313 fn spawnPosix(self: *ChildProcess) SpawnError!void {
296 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined;314 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined;
297 errdefer if (self.stdin_behavior == StdIo.Pipe) {315 errdefer if (self.stdin_behavior == StdIo.Pipe) {
298 destroyPipe(stdin_pipe);316 destroyPipe(stdin_pipe);
...@@ -309,7 +327,16 @@ pub const ChildProcess = struct {...@@ -309,7 +327,16 @@ pub const ChildProcess = struct {
309 };327 };
310328
311 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);329 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
312 const dev_null_fd = if (any_ignore) try os.openC(c"/dev/null", os.O_RDWR, 0) else undefined;330 const dev_null_fd = if (any_ignore)
331 os.openC(c"/dev/null", os.O_RDWR, 0) catch |err| switch (err) {
332 error.PathAlreadyExists => unreachable,
333 error.NoSpaceLeft => unreachable,
334 error.FileTooBig => unreachable,
335 error.DeviceBusy => unreachable,
336 else => |e| return e,
337 }
338 else
339 undefined;
313 defer {340 defer {
314 if (any_ignore) os.close(dev_null_fd);341 if (any_ignore) os.close(dev_null_fd);
315 }342 }
...@@ -403,7 +430,7 @@ pub const ChildProcess = struct {...@@ -403,7 +430,7 @@ pub const ChildProcess = struct {
403 }430 }
404 }431 }
405432
406 fn spawnWindows(self: *ChildProcess) !void {433 fn spawnWindows(self: *ChildProcess) SpawnError!void {
407 const saAttr = windows.SECURITY_ATTRIBUTES{434 const saAttr = windows.SECURITY_ATTRIBUTES{
408 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),435 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
409 .bInheritHandle = windows.TRUE,436 .bInheritHandle = windows.TRUE,
...@@ -412,11 +439,28 @@ pub const ChildProcess = struct {...@@ -412,11 +439,28 @@ pub const ChildProcess = struct {
412439
413 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);440 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
414441
415 const nul_handle = if (any_ignore) blk: {442 const nul_handle = if (any_ignore)
416 break :blk try windows.CreateFile("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null);443 windows.CreateFile(
417 } else blk: {444 "NUL",
418 break :blk undefined;445 windows.GENERIC_READ,
419 };446 windows.FILE_SHARE_READ,
447 null,
448 windows.OPEN_EXISTING,
449 windows.FILE_ATTRIBUTE_NORMAL,
450 null,
451 ) catch |err| switch (err) {
452 error.SharingViolation => unreachable, // not possible for "NUL"
453 error.PathAlreadyExists => unreachable, // not possible for "NUL"
454 error.PipeBusy => unreachable, // not possible for "NUL"
455 error.InvalidUtf8 => unreachable, // not possible for "NUL"
456 error.BadPathName => unreachable, // not possible for "NUL"
457 error.FileNotFound => unreachable, // not possible for "NUL"
458 error.AccessDenied => unreachable, // not possible for "NUL"
459 error.NameTooLong => unreachable, // not possible for "NUL"
460 else => |e| return e,
461 }
462 else
463 undefined;
420 defer {464 defer {
421 if (any_ignore) os.close(nul_handle);465 if (any_ignore) os.close(nul_handle);
422 }466 }
...@@ -542,10 +586,25 @@ pub const ChildProcess = struct {...@@ -542,10 +586,25 @@ pub const ChildProcess = struct {
542 windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {586 windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
543 if (no_path_err != error.FileNotFound) return no_path_err;587 if (no_path_err != error.FileNotFound) return no_path_err;
544588
545 const PATH = try process.getEnvVarOwned(self.allocator, "PATH");589 var free_path = true;
546 defer self.allocator.free(PATH);590 const PATH = process.getEnvVarOwned(self.allocator, "PATH") catch |err| switch (err) {
547 const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT");591 error.EnvironmentVariableNotFound => blk: {
548 defer self.allocator.free(PATHEXT);592 free_path = false;
593 break :blk "";
594 },
595 else => |e| return e,
596 };
597 defer if (free_path) self.allocator.free(PATH);
598
599 var free_path_ext = true;
600 const PATHEXT = process.getEnvVarOwned(self.allocator, "PATHEXT") catch |err| switch (err) {
601 error.EnvironmentVariableNotFound => blk: {
602 free_path_ext = false;
603 break :blk "";
604 },
605 else => |e| return e,
606 };
607 defer if (free_path_ext) self.allocator.free(PATHEXT);
549608
550 var it = mem.tokenize(PATH, ";");609 var it = mem.tokenize(PATH, ";");
551 retry: while (it.next()) |search_path| {610 retry: while (it.next()) |search_path| {
...@@ -717,7 +776,7 @@ fn destroyPipe(pipe: [2]os.fd_t) void {...@@ -717,7 +776,7 @@ fn destroyPipe(pipe: [2]os.fd_t) void {
717// Child of fork calls this to report an error to the fork parent.776// Child of fork calls this to report an error to the fork parent.
718// Then the child exits.777// Then the child exits.
719fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {778fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
720 writeIntFd(fd, @as(ErrInt,@errorToInt(err))) catch {};779 writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {};
721 os.exit(1);780 os.exit(1);
722}781}
723782