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 {
957957 error.ProcessTerminated => error.PkgConfigCrashed,
958958 error.ExitCodeFailure => error.PkgConfigFailed,
959959 error.FileNotFound => error.PkgConfigNotInstalled,
960 error.InvalidName => error.PkgConfigNotInstalled,
960961 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
961962 else => return err,
962963 };
lib/std/child_process.zig+77-18
......@@ -50,8 +50,20 @@ pub const ChildProcess = struct {
5050 err_pipe: if (builtin.os == .windows) void else [2]os.fd_t,
5151 llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node,
5252
53 pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||
54 os.ChangeCurDirError || windows.CreateProcessError;
53 pub const SpawnError = error{
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
5668 pub const Term = union(enum) {
5769 Exited: u32,
......@@ -102,7 +114,7 @@ pub const ChildProcess = struct {
102114 }
103115
104116 /// On success must call `kill` or `wait`.
105 pub fn spawn(self: *ChildProcess) !void {
117 pub fn spawn(self: *ChildProcess) SpawnError!void {
106118 if (builtin.os == .windows) {
107119 return self.spawnWindows();
108120 } else {
......@@ -110,7 +122,7 @@ pub const ChildProcess = struct {
110122 }
111123 }
112124
113 pub fn spawnAndWait(self: *ChildProcess) !Term {
125 pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term {
114126 try self.spawn();
115127 return self.wait();
116128 }
......@@ -162,7 +174,13 @@ pub const ChildProcess = struct {
162174
163175 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
164176 /// 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 {
166184 const child = try ChildProcess.init(argv, allocator);
167185 defer child.deinit();
168186
......@@ -292,7 +310,7 @@ pub const ChildProcess = struct {
292310 Term{ .Unknown = status };
293311 }
294312
295 fn spawnPosix(self: *ChildProcess) !void {
313 fn spawnPosix(self: *ChildProcess) SpawnError!void {
296314 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined;
297315 errdefer if (self.stdin_behavior == StdIo.Pipe) {
298316 destroyPipe(stdin_pipe);
......@@ -309,7 +327,16 @@ pub const ChildProcess = struct {
309327 };
310328
311329 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;
313340 defer {
314341 if (any_ignore) os.close(dev_null_fd);
315342 }
......@@ -403,7 +430,7 @@ pub const ChildProcess = struct {
403430 }
404431 }
405432
406 fn spawnWindows(self: *ChildProcess) !void {
433 fn spawnWindows(self: *ChildProcess) SpawnError!void {
407434 const saAttr = windows.SECURITY_ATTRIBUTES{
408435 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
409436 .bInheritHandle = windows.TRUE,
......@@ -412,11 +439,28 @@ pub const ChildProcess = struct {
412439
413440 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: {
416 break :blk try windows.CreateFile("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null);
417 } else blk: {
418 break :blk undefined;
419 };
442 const nul_handle = if (any_ignore)
443 windows.CreateFile(
444 "NUL",
445 windows.GENERIC_READ,
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;
420464 defer {
421465 if (any_ignore) os.close(nul_handle);
422466 }
......@@ -542,10 +586,25 @@ pub const ChildProcess = struct {
542586 windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
543587 if (no_path_err != error.FileNotFound) return no_path_err;
544588
545 const PATH = try process.getEnvVarOwned(self.allocator, "PATH");
546 defer self.allocator.free(PATH);
547 const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT");
548 defer self.allocator.free(PATHEXT);
589 var free_path = true;
590 const PATH = process.getEnvVarOwned(self.allocator, "PATH") catch |err| switch (err) {
591 error.EnvironmentVariableNotFound => blk: {
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
550609 var it = mem.tokenize(PATH, ";");
551610 retry: while (it.next()) |search_path| {
......@@ -717,7 +776,7 @@ fn destroyPipe(pipe: [2]os.fd_t) void {
717776// Child of fork calls this to report an error to the fork parent.
718777// Then the child exits.
719778fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
720 writeIntFd(fd, @as(ErrInt,@errorToInt(err))) catch {};
779 writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {};
721780 os.exit(1);
722781}
723782