Threaded: implement process spawning for macOS

This commit is contained in:
Jacob Young committed 2026-09-26 19:08:50 -04:00
1 parent 00b59eb18a
commit 9fe0ebe108
6 files changed
+492 -30

No files matched your search

+441 -6
View File
@@ -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,
+2
View File
@@ -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;
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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),
+2
View File
@@ -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);
}