mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-10-28 19:00:52 +08:00
Threaded: implement process spawning for macOS
This commit is contained in:
1 parent
00b59eb18a
commit
9fe0ebe108
6 files changed
+492
-30
No files matched your search
+441
-6
@@ -7028,6 +7028,11 @@ fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.R
|
||||
}
|
||||
|
||||
fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize {
|
||||
if (fd == posix.AT.FDCWD) return processCurrentPathPosix(out_buffer) catch |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.CurrentDirUnlinked => return error.FileNotFound,
|
||||
error.PathExceedsLimit => return error.NameTooLong,
|
||||
};
|
||||
switch (native_os) {
|
||||
.dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
||||
var sufficient_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
@@ -14143,6 +14148,7 @@ fn unlockStderr(userdata: ?*anyopaque) void {
|
||||
fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
if (is_windows) {
|
||||
var wtf16le_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined;
|
||||
const n = windows.ntdll.RtlGetCurrentDirectory_U(wtf16le_buf.len * 2 + 2, &wtf16le_buf) / 2;
|
||||
@@ -14158,12 +14164,18 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr
|
||||
end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
|
||||
}
|
||||
return end_index;
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
}
|
||||
|
||||
if (native_os == .wasi and !builtin.link_libc) {
|
||||
if (buffer.len == 0) return error.NameTooLong;
|
||||
buffer[0] = '.';
|
||||
return 1;
|
||||
}
|
||||
|
||||
return processCurrentPathPosix(buffer);
|
||||
}
|
||||
|
||||
fn processCurrentPathPosix(buffer: []u8) process.CurrentPathError!usize {
|
||||
const err: posix.E = if (builtin.link_libc) err: {
|
||||
const c_err = if (std.c.getcwd(buffer.ptr, buffer.len)) |_| 0 else std.c._errno().*;
|
||||
break :err @fromBackingInt(@intCast(c_err));
|
||||
@@ -15131,12 +15143,54 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces
|
||||
});
|
||||
};
|
||||
|
||||
if (is_darwin) if (t.spawnDarwin(
|
||||
.replace,
|
||||
options.exe,
|
||||
argv_buf.ptr,
|
||||
options.cwd,
|
||||
env_block,
|
||||
options.expand_arg0,
|
||||
.none,
|
||||
.inherit,
|
||||
.inherit,
|
||||
.inherit,
|
||||
options.inherit_dirs,
|
||||
options.inherit_files,
|
||||
null,
|
||||
options.start_suspended,
|
||||
PATH,
|
||||
)) |_| unreachable else |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.WouldBlock => return error.SystemResources,
|
||||
error.FileTooBig,
|
||||
error.NoSpaceLeft,
|
||||
error.PathAlreadyExists,
|
||||
error.SymLinkLoop,
|
||||
error.NetworkNotFound,
|
||||
error.PipeBusy,
|
||||
error.AntivirusInterference,
|
||||
=> return error.FileNotFound,
|
||||
error.DeviceBusy,
|
||||
error.NoDevice,
|
||||
error.UnrecognizedVolume,
|
||||
error.ReadOnlyFileSystem,
|
||||
=> return error.FileSystem,
|
||||
error.ResourceLimitReached => return error.SystemResources,
|
||||
error.FileLocksUnsupported => unreachable, // lock not requested
|
||||
error.InvalidWtf8 => unreachable, // windows only
|
||||
error.InvalidBatchScriptArg => unreachable, // windows only
|
||||
error.InvalidUserId => unreachable, // not supported
|
||||
error.InvalidProcessGroupId => unreachable, // passed null
|
||||
error.InvalidName => unreachable, // windows only
|
||||
error.ProcessAlreadyExec => unreachable, // linux only
|
||||
};
|
||||
return posixExec(options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH);
|
||||
}
|
||||
|
||||
const processSpawn = switch (native_os) {
|
||||
.wasi, .emscripten, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported,
|
||||
.windows => processSpawnWindows,
|
||||
.driverkit, .maccatalyst, .macos => processSpawnDarwin,
|
||||
else => processSpawnPosix,
|
||||
};
|
||||
|
||||
@@ -15743,6 +15797,56 @@ fn destroyPipe(pipe: [2]posix.fd_t) void {
|
||||
if (pipe[0] != pipe[1]) closeFd(pipe[1]);
|
||||
}
|
||||
|
||||
fn processSpawnDarwin(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
|
||||
t.scanEnviron(); // for PATH
|
||||
const PATH = t.environ.string.PATH orelse default_PATH;
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
|
||||
for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr;
|
||||
|
||||
const env_block = env_block: {
|
||||
const prog_fd: i32 = -1;
|
||||
if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{
|
||||
.zig_progress_fd = prog_fd,
|
||||
});
|
||||
break :env_block try t.environ.process_environ.createPosixBlock(arena, .{
|
||||
.zig_progress_fd = prog_fd,
|
||||
});
|
||||
};
|
||||
|
||||
const spawned = try t.spawnDarwin(
|
||||
.spawn,
|
||||
options.exe,
|
||||
argv_buf.ptr,
|
||||
options.cwd,
|
||||
env_block,
|
||||
options.expand_arg0,
|
||||
options.progress_node,
|
||||
options.stdin,
|
||||
options.stdout,
|
||||
options.stderr,
|
||||
options.inherit_dirs,
|
||||
options.inherit_files,
|
||||
options.pgid,
|
||||
options.start_suspended,
|
||||
PATH,
|
||||
);
|
||||
return .{
|
||||
.id = spawned.pid,
|
||||
.thread_handle = {},
|
||||
.stdin = spawned.stdin,
|
||||
.stdout = spawned.stdout,
|
||||
.stderr = spawned.stderr,
|
||||
.request_resource_usage_statistics = options.request_resource_usage_statistics,
|
||||
};
|
||||
}
|
||||
|
||||
fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
|
||||
@@ -17037,6 +17141,7 @@ fn posixExec(
|
||||
.path = .cwd(),
|
||||
} else .search,
|
||||
.search => {
|
||||
if (Dir.path.isAbsolute(arg0_slice)) continue :exe .{ .path = .cwd() };
|
||||
// Use of PATH_MAX here is valid as the path_buf will be passed
|
||||
// directly to the operating system in posixExecveat.
|
||||
var path_buf: [posix.PATH_MAX]u8 = undefined;
|
||||
@@ -17047,7 +17152,7 @@ fn posixExec(
|
||||
const path_len = search_path.len + arg0_slice.len + 1;
|
||||
if (path_buf.len < path_len + 1) return error.NameTooLong;
|
||||
@memcpy(path_buf[0..search_path.len], search_path);
|
||||
path_buf[search_path.len] = '/';
|
||||
path_buf[search_path.len] = Dir.path.sep;
|
||||
@memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice);
|
||||
path_buf[path_len] = 0;
|
||||
const full_path = path_buf[0..path_len :0].ptr;
|
||||
@@ -17065,7 +17170,7 @@ fn posixExec(
|
||||
}
|
||||
return err;
|
||||
},
|
||||
.path => |path| return posixExecveat(path.handle, argv[0], argv, env_block),
|
||||
.path => |path| return posixExecveat(path.handle, arg0_slice, argv, env_block),
|
||||
.file => |file| return posixExecveat(file.handle, null, argv, env_block),
|
||||
.explicit => |explicit| {
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
@@ -17100,7 +17205,7 @@ pub fn posixExecveat(
|
||||
.NOSYS => {},
|
||||
}
|
||||
}
|
||||
var path_buffer: [posix.PATH_MAX:0]u8 = undefined;
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
var path_len = realPathPosix(dir, &path_buffer) catch |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.InputOutput,
|
||||
@@ -17125,7 +17230,9 @@ pub fn posixExecveat(
|
||||
path_len += path_slice.len;
|
||||
}
|
||||
path_buffer[path_len] = 0;
|
||||
break :err posix.errno(posix.system.execve(&path_buffer, argv, env_block.slice.ptr));
|
||||
break :err posix.errno(
|
||||
posix.system.execve(path_buffer[0..path_len :0], argv, env_block.slice.ptr),
|
||||
);
|
||||
}) {
|
||||
.FAULT => |err| return errnoBug(err), // Bad pointer parameter.
|
||||
.@"2BIG" => return error.SystemResources,
|
||||
@@ -17158,6 +17265,334 @@ pub fn posixExecveat(
|
||||
}
|
||||
}
|
||||
|
||||
fn spawnDarwin(
|
||||
t: *Threaded,
|
||||
mode: enum { spawn, replace },
|
||||
exe: process.ReplaceOptions.Exe,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
cwd: process.Child.Cwd,
|
||||
env_block: process.Environ.PosixBlock,
|
||||
expand_arg0: process.ArgExpansion,
|
||||
progress_node: std.Progress.Node,
|
||||
stdin: process.SpawnOptions.StdIo,
|
||||
stdout: process.SpawnOptions.StdIo,
|
||||
stderr: process.SpawnOptions.StdIo,
|
||||
inherit_dirs: []const Dir,
|
||||
inherit_files: []const File,
|
||||
pgid: ?posix.pid_t,
|
||||
start_suspended: bool,
|
||||
PATH: []const u8,
|
||||
) (process.SpawnError || process.ReplaceError)!Spawned {
|
||||
var fa: std.c.posix_spawn_file_actions_t = undefined;
|
||||
switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawn_file_actions_init(&fa))))) {
|
||||
.SUCCESS => {},
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
defer switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_destroy(&fa),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.INVAL => recoverableOsBugDetected(),
|
||||
else => recoverableOsBugDetected(),
|
||||
};
|
||||
|
||||
var attr: std.c.posix_spawnattr_t = undefined;
|
||||
switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_init(&attr))))) {
|
||||
.SUCCESS => {},
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
defer switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_destroy(&attr))))) {
|
||||
.SUCCESS => {},
|
||||
.INVAL => recoverableOsBugDetected(),
|
||||
else => recoverableOsBugDetected(),
|
||||
};
|
||||
|
||||
// The child process does need to access (one end of) these pipes. However,
|
||||
// we must initially set CLOEXEC to avoid a race condition. If another thread
|
||||
// is racing to spawn a different child process, we don't want it to inherit
|
||||
// these FDs in any scenario; that would mean that, for instance, calls to
|
||||
// `poll` from the parent would not report the child's stdout as closing when
|
||||
// expected, since the other child may retain a reference to the write end of
|
||||
// the pipe. So, we create the pipes with CLOEXEC initially. After fork, we
|
||||
// need to do something in the new child to make sure we preserve the reference
|
||||
// we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it
|
||||
// turns out, we `dup2` everything anyway, so there's no need!
|
||||
const pipe_flags: posix.O = .{ .CLOEXEC = true };
|
||||
|
||||
const stdin_pipe = if (stdin == .pipe) try pipe2(pipe_flags) else undefined;
|
||||
errdefer if (stdin == .pipe) {
|
||||
destroyPipe(stdin_pipe);
|
||||
};
|
||||
|
||||
const stdout_pipe = if (stdout == .pipe) try pipe2(pipe_flags) else undefined;
|
||||
errdefer if (stdout == .pipe) {
|
||||
destroyPipe(stdout_pipe);
|
||||
};
|
||||
|
||||
const stderr_pipe = if (stderr == .pipe) try pipe2(pipe_flags) else undefined;
|
||||
errdefer if (stderr == .pipe) {
|
||||
destroyPipe(stderr_pipe);
|
||||
};
|
||||
|
||||
const any_ignore = (stdin == .ignore or stdout == .ignore or stderr == .ignore);
|
||||
const dev_null_fd = if (any_ignore) try t.getDevNullFd() else undefined;
|
||||
|
||||
const prog_pipe: [2]posix.fd_t = if (progress_node.index != .none) pipe: {
|
||||
// We use CLOEXEC for the same reason as in `pipe_flags`.
|
||||
break :pipe try pipe2(.{ .NONBLOCK = true, .CLOEXEC = true });
|
||||
} else .{ -1, -1 };
|
||||
errdefer destroyPipe(prog_pipe);
|
||||
|
||||
var cwd_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
switch (cwd) {
|
||||
.inherit => {},
|
||||
.dir => |dir| switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_addfchdir_np(&fa, dir.handle),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
},
|
||||
.path => |path| switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_addchdir_np(&fa, try pathToPosix(path, &cwd_buffer)),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
},
|
||||
}
|
||||
|
||||
for (inherit_dirs) |inherit_dir| switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_addinherit_np(&fa, inherit_dir.handle),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
};
|
||||
for (inherit_files) |inherit_file| switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_addinherit_np(&fa, inherit_file.handle),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
};
|
||||
if (prog_pipe[1] != -1) switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn_file_actions_addinherit_np(&fa, prog_pipe[1]),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
};
|
||||
for (
|
||||
[_]process.SpawnOptions.StdIo{ stdin, stdout, stderr },
|
||||
[_]posix.fd_t{ stdin_pipe[0], stdout_pipe[1], stderr_pipe[1] },
|
||||
[_]posix.fd_t{ posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO },
|
||||
) |stdio, pipe_fd, std_fd| switch (@as(std.c.E, @fromBackingInt(@intCast(switch (stdio) {
|
||||
.pipe => std.c.posix_spawn_file_actions_adddup2(&fa, pipe_fd, std_fd),
|
||||
.close => std.c.posix_spawn_file_actions_addclose(&fa, std_fd),
|
||||
.inherit => std.c.posix_spawn_file_actions_addinherit_np(&fa, std_fd),
|
||||
.ignore => std.c.posix_spawn_file_actions_adddup2(&fa, dev_null_fd, std_fd),
|
||||
.file => |file| std.c.posix_spawn_file_actions_adddup2(&fa, file.handle, std_fd),
|
||||
})))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
};
|
||||
|
||||
if (pgid) |pid| switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawnattr_setpgroup(&attr, pid),
|
||||
)))) {
|
||||
.SUCCESS => {},
|
||||
.INVAL => return error.InvalidProcessGroupId,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
};
|
||||
|
||||
switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_setflags(&attr, .{
|
||||
.SETPGROUP = pgid != null,
|
||||
.SETEXEC = switch (mode) {
|
||||
.spawn => false,
|
||||
.replace => true,
|
||||
},
|
||||
.START_SUSPENDED = start_suspended,
|
||||
}))))) {
|
||||
.SUCCESS => {},
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
var pid: posix.pid_t = undefined;
|
||||
const arg0_slice = std.mem.span(argv[0].?);
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
switch (err: {
|
||||
break :err @as(std.c.E, @fromBackingInt(@intCast(exe: switch (exe) {
|
||||
.detect => if (std.mem.findScalar(u8, arg0_slice, '/')) |_|
|
||||
std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr)
|
||||
else
|
||||
continue :exe .search,
|
||||
.search => if (!Dir.path.isAbsolute(arg0_slice)) {
|
||||
// Use of PATH_MAX here is valid as the path_buf will be passed
|
||||
// directly to the operating system in posixExecveat.
|
||||
var path_buf: [posix.PATH_MAX]u8 = undefined;
|
||||
var it = std.mem.tokenizeScalar(u8, PATH, ':');
|
||||
var err: error{ AccessDenied, FileNotFound, NotDir } = error.FileNotFound;
|
||||
|
||||
while (it.next()) |search_path| {
|
||||
const path_len = search_path.len + arg0_slice.len + 1;
|
||||
if (path_buf.len < path_len + 1) return error.NameTooLong;
|
||||
@memcpy(path_buf[0..search_path.len], search_path);
|
||||
path_buf[search_path.len] = Dir.path.sep;
|
||||
@memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice);
|
||||
path_buf[path_len] = 0;
|
||||
const full_path = path_buf[0..path_len :0].ptr;
|
||||
switch (expand_arg0) {
|
||||
.expand => argv[0] = full_path,
|
||||
.no_expand => {},
|
||||
}
|
||||
switch (@as(std.c.E, @fromBackingInt(@intCast(
|
||||
std.c.posix_spawn(&pid, full_path, &fa, &attr, argv, env_block.slice.ptr),
|
||||
)))) {
|
||||
else => |e| break :err e,
|
||||
.ACCES, .NOENT, .NOTDIR => |e| switch (err) {
|
||||
error.AccessDenied => {},
|
||||
error.FileNotFound, error.NotDir => err = switch (e) {
|
||||
else => unreachable,
|
||||
.ACCES => error.AccessDenied,
|
||||
.NOENT => error.FileNotFound,
|
||||
.NOTDIR => error.NotDir,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return err;
|
||||
} else std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr),
|
||||
.path => |path| if (path.handle != posix.AT.FDCWD and !Dir.path.isAbsolute(arg0_slice)) {
|
||||
var path_len = realPathPosix(path.handle, &path_buffer) catch |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.InputOutput => return error.FileNotFound,
|
||||
};
|
||||
path_buffer[path_len] = Dir.path.sep;
|
||||
path_len += 1;
|
||||
@memcpy(path_buffer[path_len..][0..arg0_slice.len], arg0_slice);
|
||||
path_len += arg0_slice.len;
|
||||
path_buffer[path_len] = 0;
|
||||
break :exe std.c.posix_spawn(
|
||||
&pid,
|
||||
path_buffer[0..path_len :0],
|
||||
&fa,
|
||||
&attr,
|
||||
argv,
|
||||
env_block.slice.ptr,
|
||||
);
|
||||
} else std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr),
|
||||
.file => |file| {
|
||||
const path_len = realPathPosix(file.handle, &path_buffer) catch |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.InputOutput => return error.FileNotFound,
|
||||
};
|
||||
path_buffer[path_len] = 0;
|
||||
break :exe std.c.posix_spawn(
|
||||
&pid,
|
||||
path_buffer[0..path_len :0],
|
||||
&fa,
|
||||
&attr,
|
||||
argv,
|
||||
env_block.slice.ptr,
|
||||
);
|
||||
},
|
||||
.explicit => |explicit| if (explicit.dir.handle != posix.AT.FDCWD and
|
||||
!Dir.path.isAbsolute(explicit.path))
|
||||
{
|
||||
var path_len = realPathPosix(
|
||||
explicit.dir.handle,
|
||||
&path_buffer,
|
||||
) catch |err| switch (err) {
|
||||
else => |e| return e,
|
||||
error.InputOutput => return error.FileNotFound,
|
||||
};
|
||||
path_buffer[path_len] = Dir.path.sep;
|
||||
path_len += 1;
|
||||
@memcpy(path_buffer[path_len..][0..explicit.path.len], explicit.path);
|
||||
path_len += explicit.path.len;
|
||||
path_buffer[path_len] = 0;
|
||||
break :exe std.c.posix_spawn(
|
||||
&pid,
|
||||
path_buffer[0..path_len :0],
|
||||
&fa,
|
||||
&attr,
|
||||
argv,
|
||||
env_block.slice.ptr,
|
||||
);
|
||||
} else std.c.posix_spawn(
|
||||
&pid,
|
||||
try pathToPosix(explicit.path, &path_buffer),
|
||||
&fa,
|
||||
&attr,
|
||||
argv,
|
||||
env_block.slice.ptr,
|
||||
),
|
||||
})));
|
||||
}) {
|
||||
.SUCCESS => {},
|
||||
.AGAIN => return error.SystemResources,
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.@"2BIG" => return error.SystemResources,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.IO => return error.FileSystem,
|
||||
.LOOP => return error.FileSystem,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOEXEC => return error.InvalidExe,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.TXTBSY => return error.FileBusy,
|
||||
.BADARCH => return error.InvalidExe,
|
||||
.BADF => |err| return errnoBug(err),
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
errdefer comptime unreachable; // The child is spawned; we must not error from now on
|
||||
|
||||
if (stdin == .pipe) closeFd(stdin_pipe[0]);
|
||||
if (stdout == .pipe) closeFd(stdout_pipe[1]);
|
||||
if (stderr == .pipe) closeFd(stderr_pipe[1]);
|
||||
|
||||
if (prog_pipe[1] != -1) closeFd(prog_pipe[1]);
|
||||
progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
|
||||
|
||||
return .{
|
||||
.pid = pid,
|
||||
.err_fd = -1,
|
||||
.stdin = switch (stdin) {
|
||||
.pipe => .{ .handle = stdin_pipe[1], .flags = .{ .nonblocking = false } },
|
||||
else => null,
|
||||
},
|
||||
.stdout = switch (stdout) {
|
||||
.pipe => .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = false } },
|
||||
else => null,
|
||||
},
|
||||
.stderr = switch (stderr) {
|
||||
.pipe => .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = false } },
|
||||
else => null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub const CreatePipeOptions = struct {
|
||||
server: End,
|
||||
client: End,
|
||||
@@ -18469,7 +18904,7 @@ const CreateFileMapError = error{
|
||||
|
||||
fn createFileMap(
|
||||
file: File,
|
||||
protection: std.process.MemoryProtection,
|
||||
protection: process.MemoryProtection,
|
||||
offset: u64,
|
||||
populate: bool,
|
||||
len: usize,
|
||||
|
||||
@@ -11785,8 +11785,10 @@ pub const posix_spawn_file_actions_init = darwin.posix_spawn_file_actions_init;
|
||||
pub const posix_spawn_file_actions_t = darwin.posix_spawn_file_actions_t;
|
||||
pub const posix_spawnattr_destroy = darwin.posix_spawnattr_destroy;
|
||||
pub const posix_spawnattr_getflags = darwin.posix_spawnattr_getflags;
|
||||
pub const posix_spawnattr_getpgroup = darwin.posix_spawnattr_getpgroup;
|
||||
pub const posix_spawnattr_init = darwin.posix_spawnattr_init;
|
||||
pub const posix_spawnattr_setflags = darwin.posix_spawnattr_setflags;
|
||||
pub const posix_spawnattr_setpgroup = darwin.posix_spawnattr_setpgroup;
|
||||
pub const posix_spawnattr_t = darwin.posix_spawnattr_t;
|
||||
pub const posix_spawnp = darwin.posix_spawnp;
|
||||
pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np;
|
||||
|
||||
@@ -1253,8 +1253,10 @@ pub const posix_spawnattr_t = *opaque {};
|
||||
pub const posix_spawn_file_actions_t = *opaque {};
|
||||
pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: POSIX_SPAWN) c_int;
|
||||
pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *POSIX_SPAWN) c_int;
|
||||
pub extern "c" fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t, pgroup: *pid_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: POSIX_SPAWN) c_int;
|
||||
pub extern "c" fn posix_spawnattr_setpgroup(attr: *posix_spawnattr_t, pgroup: pid_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
|
||||
+11
-1
@@ -284,10 +284,20 @@ pub const ReplaceError = error{
|
||||
pub const ReplaceOptions = struct {
|
||||
exe: Exe = .detect,
|
||||
argv: []const []const u8,
|
||||
expand_arg0: ArgExpansion = .no_expand,
|
||||
|
||||
/// Set to change the current working directory when spawning the child process.
|
||||
cwd: Child.Cwd = .inherit,
|
||||
/// Replaces the environment when provided. The PATH value from here is
|
||||
/// never used to resolve `argv[0]`.
|
||||
environ_map: ?*const Environ.Map = null,
|
||||
expand_arg0: ArgExpansion = .no_expand,
|
||||
|
||||
inherit_dirs: []const Dir = &.{},
|
||||
inherit_files: []const File = &.{},
|
||||
|
||||
/// Start child process in suspended state.
|
||||
/// For Posix systems it's started as if SIGSTOP was sent.
|
||||
start_suspended: bool = false,
|
||||
|
||||
pub const Exe = union(enum) {
|
||||
/// `argv[0]` is the name of the program to execute. If it is not already a
|
||||
|
||||
+33
-22
@@ -26,6 +26,7 @@ const Args = struct {
|
||||
enable_wine: bool,
|
||||
enable_wasmtime: bool,
|
||||
enable_darling: bool,
|
||||
quiet: bool,
|
||||
|
||||
fn deinit(args: *Args, gpa: std.mem.Allocator) void {
|
||||
args.targets.deinit(gpa);
|
||||
@@ -98,6 +99,8 @@ pub fn runServer(runner: *Runner) ProtocolError {
|
||||
runner.args.enable_wasmtime = true;
|
||||
} else if (std.mem.eql(u8, string, "-fdarling")) {
|
||||
runner.args.enable_darling = true;
|
||||
} else if (std.mem.eql(u8, string, "--quiet")) {
|
||||
runner.args.quiet = true;
|
||||
},
|
||||
.zig => unreachable,
|
||||
.lib => unreachable,
|
||||
@@ -463,10 +466,10 @@ fn testOne(runner: *Runner, test_index: u32) TestError!void {
|
||||
.target_query = target_query,
|
||||
.manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: {
|
||||
var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined;
|
||||
|
||||
const src_path = src_path: {
|
||||
break :src_path path_buffer[0 .. std.Io.Dir.readLinkAbsolute(
|
||||
break :src_path path_buffer[0 .. manifest_file.realPath(
|
||||
runner.io,
|
||||
try runner.arena.allocator().print("/proc/self/fd/{d}", .{manifest_file.handle}),
|
||||
&path_buffer,
|
||||
) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
@@ -839,12 +842,9 @@ fn spawnCompiler(
|
||||
.selfhosted => try argv.append(gpa, "-fno-llvm"),
|
||||
.llvm => try argv.append(gpa, "-fllvm"),
|
||||
.cbe => try argv.append(gpa, "-ofmt=c"),
|
||||
} else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. std.Io.Dir.readLinkAbsolute(
|
||||
runner.io,
|
||||
try arena.print("/proc/self/fd/{d}", .{(runner.args.lib_dir orelse
|
||||
return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})).handle}),
|
||||
&path_buffer,
|
||||
) catch |err| switch (err) {
|
||||
} else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse
|
||||
return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{}))
|
||||
.realPath(runner.io, &path_buffer) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
else => |e| return runner.fail("unable to get lib path: {t}", .{e}),
|
||||
}] });
|
||||
@@ -1034,8 +1034,8 @@ const Compiler = struct {
|
||||
)) {
|
||||
.bad_dl, .bad_os_or_cpu => {
|
||||
// This binary cannot be executed on this host.
|
||||
std.log.warn("skipping execution because host '{s}' cannot execute binaries for " ++
|
||||
"foreign target '{s}'", .{
|
||||
if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++
|
||||
"execute binaries for foreign target {q}", .{
|
||||
try runner.host.zigTriple(arena), comp.target.triple,
|
||||
});
|
||||
return;
|
||||
@@ -1080,12 +1080,12 @@ const Compiler = struct {
|
||||
.progress_node = run_prog_node,
|
||||
}) catch |err| if (executor) |_| {
|
||||
// Chances are the foreign executor isn't available. Skip this evaluation.
|
||||
std.log.warn(
|
||||
"skipping execution of '{s}' via executor for foreign target '{s}': {t}",
|
||||
if (!runner.args.quiet) std.log.warn(
|
||||
"skipping execution of {q} via executor for foreign target {q}: {t}",
|
||||
.{ bin_path, comp.target.triple, err },
|
||||
);
|
||||
return;
|
||||
} else return runner.fail("unable to run the generated executable '{s}': {t}", .{
|
||||
} else return runner.fail("unable to run the generated executable {q}: {t}", .{
|
||||
bin_path, err,
|
||||
});
|
||||
defer {
|
||||
@@ -1097,7 +1097,7 @@ const Compiler = struct {
|
||||
.errors, .lldb => unreachable,
|
||||
.stdout => {
|
||||
if (code != 0) return runner.fail(
|
||||
"generated executable '{s}' failed with code {d}",
|
||||
"generated executable {q} failed with code {d}",
|
||||
.{ bin_path, code },
|
||||
);
|
||||
std.testing.expectEqualStrings(
|
||||
@@ -1125,20 +1125,20 @@ const Compiler = struct {
|
||||
},
|
||||
},
|
||||
.signal => |sig| return runner.fail(
|
||||
"generated executable '{s}' terminated with signal {t}",
|
||||
"generated executable {q} terminated with signal {t}",
|
||||
.{ bin_path, sig },
|
||||
),
|
||||
.stopped => |sig| return runner.fail(
|
||||
"generated executable '{s}' stopped with signal {t}",
|
||||
"generated executable {q} stopped with signal {t}",
|
||||
.{ bin_path, sig },
|
||||
),
|
||||
.unknown => return runner.fail(
|
||||
"generated executable '{s}' terminated unexpectedly",
|
||||
"generated executable {q} terminated unexpectedly",
|
||||
.{bin_path},
|
||||
),
|
||||
}
|
||||
if (executor == null and result.stderr.len > 0) {
|
||||
std.log.err("generated executable '{s}' had unexpected stderr:\n{s}", .{
|
||||
std.log.err("generated executable {q} had unexpected stderr:\n{s}", .{
|
||||
bin_path, result.stderr,
|
||||
});
|
||||
}
|
||||
@@ -1247,6 +1247,7 @@ const ClientArgs = struct {
|
||||
enable_wine: bool,
|
||||
enable_wasmtime: bool,
|
||||
enable_darling: bool,
|
||||
quiet: bool,
|
||||
};
|
||||
pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 {
|
||||
const fatal = struct {
|
||||
@@ -1272,6 +1273,7 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
|
||||
.enable_wine = false,
|
||||
.enable_wasmtime = false,
|
||||
.enable_darling = false,
|
||||
.quiet = false,
|
||||
};
|
||||
defer args.targets.deinit(init.gpa);
|
||||
|
||||
@@ -1300,6 +1302,7 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
|
||||
.enable_wine = false,
|
||||
.enable_wasmtime = false,
|
||||
.enable_darling = false,
|
||||
.quiet = false,
|
||||
},
|
||||
.host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
@@ -1328,17 +1331,17 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
|
||||
},
|
||||
}
|
||||
} else if (std.mem.eql(u8, arg, "--zig")) {
|
||||
zig_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
|
||||
zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg});
|
||||
} else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| {
|
||||
zig_path_arg = zig_path;
|
||||
} else if (std.mem.eql(u8, arg, "--lib")) {
|
||||
lib_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
|
||||
lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg});
|
||||
} else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| {
|
||||
lib_path_arg = lib_path;
|
||||
} else if (std.mem.eql(u8, arg, "--target")) {
|
||||
try args.targets.append(
|
||||
init.gpa,
|
||||
try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after '{s}'", .{arg})),
|
||||
try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})),
|
||||
);
|
||||
} else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| {
|
||||
try args.targets.append(init.gpa, try arena.dupe(u8, target));
|
||||
@@ -1352,8 +1355,10 @@ pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelabl
|
||||
args.enable_wasmtime = true;
|
||||
} else if (std.mem.eql(u8, arg, "-fdarling")) {
|
||||
args.enable_darling = true;
|
||||
} else if (std.mem.eql(u8, arg, "--quiet")) {
|
||||
args.quiet = true;
|
||||
} else {
|
||||
if (test_path_arg) |_| fatal("unknown arg '{s}'", .{arg});
|
||||
if (test_path_arg) |_| fatal("unknown arg {q}", .{arg});
|
||||
test_path_arg = arg;
|
||||
}
|
||||
}
|
||||
@@ -1509,6 +1514,12 @@ fn runClient(
|
||||
try argv.append(gpa, 0);
|
||||
}
|
||||
|
||||
if (args.quiet) {
|
||||
try argv.append(gpa, @backingInt(Arg.string));
|
||||
try argv.appendSlice(gpa, "--quiet");
|
||||
try argv.append(gpa, 0);
|
||||
}
|
||||
|
||||
try client.serveMessageHeader(.{
|
||||
.tag = .args,
|
||||
.bytes_len = @intCast(argv.items.len),
|
||||
|
||||
@@ -3417,6 +3417,8 @@ pub fn addIncrementalTests(
|
||||
run.addThirdPartyEnabledArgWasmtime(.{ .enabled = "-fwasmtime" });
|
||||
run.addThirdPartyEnabledArgWine(.{ .enabled = "-fwine" });
|
||||
|
||||
run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc
|
||||
|
||||
tests_step.dependOn(&run.step);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user