commit 4a53e5b0b4131c6b8e18bb551e8215e425f8ac71 (tree)
parent ebdbbd20ace6e93b581b90075f52946b3832da93
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 8 Dec 2025 20:03:50 -0800
fix a handful of compilation errors related to std.fs migration
Diffstat:
24 files changed, 148 insertions(+), 133 deletions(-)
diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig
@@ -2010,7 +2010,7 @@ test "maybeAppendRC" {
// Now delete the file and try again. But this time change the input format
// to non-rc.
- try tmp.dir.deleteFile("foo");
+ try tmp.dir.deleteFile(io, "foo");
options.input_format = .res;
try options.maybeAppendRC(io, tmp.dir);
try std.testing.expectEqualStrings("foo", options.input_source.filename);
diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig
@@ -440,7 +440,7 @@ const IoStream = struct {
// Delete the output file on error
file.close(io);
// Failing to delete is not really a big deal, so swallow any errors
- Io.Dir.cwd().deleteFile(self.name) catch {};
+ Io.Dir.cwd().deleteFile(io, self.name) catch {};
},
.stdio, .memory, .closed => return,
}
diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig
@@ -72,8 +72,8 @@ pub fn main() !void {
const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port});
std.Io.File.stdout().writeAll(url_with_newline) catch {};
if (should_open_browser) {
- openBrowserTab(gpa, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| {
- std.log.err("unable to open browser: {s}", .{@errorName(err)});
+ openBrowserTab(gpa, io, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| {
+ std.log.err("unable to open browser: {t}", .{err});
};
}
@@ -89,7 +89,7 @@ pub fn main() !void {
while (true) {
const connection = try http_server.accept();
_ = std.Thread.spawn(.{}, accept, .{ &context, connection }) catch |err| {
- std.log.err("unable to accept connection: {s}", .{@errorName(err)});
+ std.log.err("unable to accept connection: {t}", .{err});
connection.stream.close(io);
continue;
};
@@ -328,7 +328,7 @@ fn buildWasmBinary(
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
- try child.spawn();
+ try child.spawn(io);
var poller = std.Io.poll(gpa, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
@@ -434,13 +434,13 @@ fn sendMessage(io: Io, file: std.Io.File, tag: std.zig.Client.Message.Tag) !void
};
}
-fn openBrowserTab(gpa: Allocator, url: []const u8) !void {
+fn openBrowserTab(gpa: Allocator, io: Io, url: []const u8) !void {
// Until https://github.com/ziglang/zig/issues/19205 is implemented, we
// spawn a thread for this child process.
- _ = try std.Thread.spawn(.{}, openBrowserTabThread, .{ gpa, url });
+ _ = try std.Thread.spawn(.{}, openBrowserTabThread, .{ gpa, io, url });
}
-fn openBrowserTabThread(gpa: Allocator, url: []const u8) !void {
+fn openBrowserTabThread(gpa: Allocator, io: Io, url: []const u8) !void {
const main_exe = switch (builtin.os.tag) {
.windows => "explorer",
.macos => "open",
@@ -450,6 +450,6 @@ fn openBrowserTabThread(gpa: Allocator, url: []const u8) !void {
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Ignore;
child.stderr_behavior = .Ignore;
- try child.spawn();
- _ = try child.wait();
+ try child.spawn(io);
+ _ = try child.wait(io);
}
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
@@ -1838,7 +1838,7 @@ pub fn runAllowFail(
child.env_map = &b.graph.env_map;
try Step.handleVerbose2(b, null, child.env_map, argv);
- try child.spawn();
+ try child.spawn(io);
var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch {
diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig
@@ -1300,7 +1300,7 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp {
});
defer {
file.close(io);
- dir.deleteFile(test_out_file) catch {};
+ dir.deleteFile(io, test_out_file) catch {};
}
return (try file.stat(io)).mtime;
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
@@ -455,7 +455,7 @@ pub fn evalZigProcess(
child.request_resource_usage_statistics = true;
child.progress_node = prog_node;
- child.spawn() catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
+ child.spawn(io) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
const zp = try gpa.create(ZigProcess);
zp.* = .{
diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig
@@ -1689,7 +1689,7 @@ fn evalZigTest(
};
while (true) {
- try child.spawn();
+ try child.spawn(io);
var poller = std.Io.poll(gpa, StdioPollEnum, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
@@ -2168,7 +2168,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
const io = b.graph.io;
const arena = b.allocator;
- try child.spawn();
+ try child.spawn(io);
errdefer _ = child.kill(io) catch {};
try child.waitForSpawn();
diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig
@@ -580,7 +580,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
- try child.spawn();
+ try child.spawn(io);
var poller = Io.poll(gpa, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig
@@ -1366,7 +1366,7 @@ pub fn deleteTree(dir: Dir, io: Io, sub_path: []const u8) DeleteTreeError!void {
=> |e| return e,
};
} else {
- if (parent_dir.deleteFile(name)) {
+ if (parent_dir.deleteFile(io, name)) {
continue :process_stack;
} else |err| switch (err) {
error.FileNotFound => continue :process_stack,
@@ -1477,7 +1477,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8,
dir_name = result;
continue :scan_dir;
} else {
- if (dir.deleteFile(entry.name)) {
+ if (dir.deleteFile(io, entry.name)) {
continue :dir_it;
} else |err| switch (err) {
error.FileNotFound => continue :dir_it,
@@ -1567,7 +1567,7 @@ fn deleteTreeOpenInitialSubpath(dir: Dir, io: Io, sub_path: []const u8, kind_hin
=> |e| return e,
};
} else {
- if (dir.deleteFile(sub_path)) {
+ if (dir.deleteFile(io, sub_path)) {
return null;
} else |err| switch (err) {
error.FileNotFound => return null,
@@ -1588,6 +1588,7 @@ fn deleteTreeOpenInitialSubpath(dir: Dir, io: Io, sub_path: []const u8, kind_hin
error.FileBusy,
error.BadPathName,
error.NetworkNotFound,
+ error.Canceled,
error.Unexpected,
=> |e| return e,
}
diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig
@@ -407,6 +407,10 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
return @intFromEnum(self);
}
+ pub fn fromMode(mode: std.posix.mode_t) @This() {
+ return @enumFromInt(mode);
+ }
+
/// Returns `true` if and only if no class has write permissions.
pub fn readOnly(self: @This()) bool {
const mode = toMode(self);
diff --git a/lib/std/Io/File/Atomic.zig b/lib/std/Io/File/Atomic.zig
@@ -20,7 +20,7 @@ pub const InitError = File.OpenError;
pub fn init(
io: Io,
dest_basename: []const u8,
- mode: File.Mode,
+ permissions: File.Permissions,
dir: Dir,
close_dir_on_deinit: bool,
write_buffer: []u8,
@@ -28,7 +28,10 @@ pub fn init(
while (true) {
const random_integer = std.crypto.random.int(u64);
const tmp_sub_path = std.fmt.hex(random_integer);
- const file = dir.createFile(io, &tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
+ const file = dir.createFile(io, &tmp_sub_path, .{
+ .permissions = permissions,
+ .exclusive = true,
+ }) catch |err| switch (err) {
error.PathAlreadyExists => continue,
else => |e| return e,
};
diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig
@@ -278,7 +278,7 @@ test "listen on a unix socket, send bytes, receive bytes" {
defer testing.allocator.free(socket_path);
const socket_addr = try net.UnixAddress.init(socket_path);
- defer Io.Dir.cwd().deleteFile(socket_path) catch {};
+ defer Io.Dir.cwd().deleteFile(io, socket_path) catch {};
var server = try socket_addr.listen(io, .{});
defer server.socket.close(io);
diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig
@@ -60,7 +60,7 @@ test "write a file, read it, then delete it" {
try expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));
try expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
}
- try tmp.dir.deleteFile(tmp_file_name);
+ try tmp.dir.deleteFile(io, tmp_file_name);
}
test "File seek ops" {
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
@@ -873,7 +873,7 @@ test "file operations on directories" {
try ctx.dir.makeDir(io, test_dir_name, .default_dir);
try testing.expectError(error.IsDir, ctx.dir.createFile(io, test_dir_name, .{}));
- try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name));
+ try testing.expectError(error.IsDir, ctx.dir.deleteFile(io, test_dir_name));
switch (native_os) {
.dragonfly, .netbsd => {
// no error when reading a directory. See https://github.com/ziglang/zig/issues/5732
@@ -942,7 +942,7 @@ test "deleteDir" {
try testing.expectError(error.DirNotEmpty, ctx.dir.deleteDir(test_dir_path));
// deleting an empty directory
- try ctx.dir.deleteFile(test_file_path);
+ try ctx.dir.deleteFile(io, test_file_path);
try ctx.dir.deleteDir(test_dir_path);
}
}.impl);
@@ -1671,13 +1671,13 @@ test "copyFile" {
const dest_file2 = try ctx.transformPath("tmp_test_copy_file3.txt");
try ctx.dir.writeFile(io, .{ .sub_path = src_file, .data = data });
- defer ctx.dir.deleteFile(src_file) catch {};
+ defer ctx.dir.deleteFile(io, src_file) catch {};
try ctx.dir.copyFile(src_file, ctx.dir, dest_file, .{});
- defer ctx.dir.deleteFile(dest_file) catch {};
+ defer ctx.dir.deleteFile(io, dest_file) catch {};
try ctx.dir.copyFile(src_file, ctx.dir, dest_file2, .{ .override_mode = File.default_mode });
- defer ctx.dir.deleteFile(dest_file2) catch {};
+ defer ctx.dir.deleteFile(io, dest_file2) catch {};
try expectFileContents(io, ctx.dir, dest_file, data);
try expectFileContents(io, ctx.dir, dest_file2, data);
@@ -1713,7 +1713,7 @@ test "AtomicFile" {
const content = try ctx.dir.readFileAlloc(io, test_out_file, allocator, .limited(9999));
try testing.expectEqualStrings(test_content, content);
- try ctx.dir.deleteFile(test_out_file);
+ try ctx.dir.deleteFile(io, test_out_file);
}
}.impl);
}
@@ -2055,7 +2055,7 @@ test "'.' and '..' in Io.Dir functions" {
try ctx.dir.rename(copy_path, ctx.dir, rename_path, io);
const renamed_file = try ctx.dir.openFile(io, rename_path, .{});
renamed_file.close(io);
- try ctx.dir.deleteFile(rename_path);
+ try ctx.dir.deleteFile(io, rename_path);
try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });
var dir = ctx.dir;
@@ -2113,19 +2113,19 @@ test "chmod" {
var tmp = tmpDir(.{});
defer tmp.cleanup();
- const file = try tmp.dir.createFile(io, "test_file", .{ .mode = 0o600 });
+ const file = try tmp.dir.createFile(io, "test_file", .{ .permissions = .fromMode(0o600) });
defer file.close(io);
- try testing.expectEqual(@as(File.Mode, 0o600), (try file.stat(io)).mode & 0o7777);
+ try testing.expectEqual(@as(posix.mode_t, 0o600), (try file.stat(io)).permissions.toMode() & 0o7777);
- try file.chmod(0o644);
- try testing.expectEqual(@as(File.Mode, 0o644), (try file.stat(io)).mode & 0o7777);
+ try file.setPermissions(io, .fromMode(0o644));
+ try testing.expectEqual(@as(posix.mode_t, 0o644), (try file.stat(io)).permissions.toMode() & 0o7777);
try tmp.dir.makeDir(io, "test_dir", .default_dir);
var dir = try tmp.dir.openDir(io, "test_dir", .{ .iterate = true });
defer dir.close(io);
- try dir.chmod(0o700);
- try testing.expectEqual(@as(File.Mode, 0o700), (try dir.stat(io)).mode & 0o7777);
+ try dir.setPermissions(io, .fromMode(0o700));
+ try testing.expectEqual(@as(posix.mode_t, 0o700), (try dir.stat(io)).permissions.toMode() & 0o7777);
}
test "chown" {
diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig
@@ -144,7 +144,7 @@ test "linkat with different directories" {
const subdir = try tmp.dir.makeOpenPath("subdir", .{});
- defer tmp.dir.deleteFile(target_name) catch {};
+ defer tmp.dir.deleteFile(io, target_name) catch {};
try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });
// Test 1: link from file in subdir back up to target in parent directory
diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig
@@ -1,4 +1,4 @@
-const ChildProcess = @This();
+const Child = @This();
const builtin = @import("builtin");
const native_os = builtin.os.tag;
@@ -31,7 +31,7 @@ pub const Id = switch (native_os) {
id: Id,
thread_handle: if (native_os == .windows) windows.HANDLE else void,
-allocator: mem.Allocator,
+allocator: Allocator,
/// The writing end of the child process's standard input pipe.
/// Usage requires `stdin_behavior == StdIo.Pipe`.
@@ -229,7 +229,7 @@ pub const StdIo = enum {
};
/// First argument in argv is the executable.
-pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
+pub fn init(argv: []const []const u8, allocator: Allocator) Child {
return .{
.allocator = allocator,
.argv = argv,
@@ -252,7 +252,7 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
};
}
-pub fn setUserName(self: *ChildProcess, name: []const u8) !void {
+pub fn setUserName(self: *Child, name: []const u8) !void {
const user_info = try process.getUserInfo(name);
self.uid = user_info.uid;
self.gid = user_info.gid;
@@ -260,7 +260,7 @@ pub fn setUserName(self: *ChildProcess, name: []const u8) !void {
/// On success must call `kill` or `wait`.
/// After spawning the `id` is available.
-pub fn spawn(self: *ChildProcess) SpawnError!void {
+pub fn spawn(self: *Child, io: Io) SpawnError!void {
if (!process.can_spawn) {
@compileError("the target operating system cannot spawn processes");
}
@@ -268,17 +268,17 @@ pub fn spawn(self: *ChildProcess) SpawnError!void {
if (native_os == .windows) {
return self.spawnWindows();
} else {
- return self.spawnPosix();
+ return self.spawnPosix(io);
}
}
-pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term {
- try self.spawn();
- return self.wait();
+pub fn spawnAndWait(child: *Child, io: Io) SpawnError!Term {
+ try child.spawn(io);
+ return child.wait(io);
}
/// Forcibly terminates child process and then cleans up all resources.
-pub fn kill(self: *ChildProcess, io: Io) !Term {
+pub fn kill(self: *Child, io: Io) !Term {
if (native_os == .windows) {
return self.killWindows(io, 1);
} else {
@@ -286,7 +286,7 @@ pub fn kill(self: *ChildProcess, io: Io) !Term {
}
}
-pub fn killWindows(self: *ChildProcess, io: Io, exit_code: windows.UINT) !Term {
+pub fn killWindows(self: *Child, io: Io, exit_code: windows.UINT) !Term {
if (self.term) |term| {
self.cleanupStreams(io);
return term;
@@ -308,7 +308,7 @@ pub fn killWindows(self: *ChildProcess, io: Io, exit_code: windows.UINT) !Term {
return self.term.?;
}
-pub fn killPosix(self: *ChildProcess, io: Io) !Term {
+pub fn killPosix(self: *Child, io: Io) !Term {
if (self.term) |term| {
self.cleanupStreams(io);
return term;
@@ -325,7 +325,7 @@ pub const WaitError = SpawnError || std.os.windows.GetProcessMemoryInfoError;
/// On some targets, `spawn` may not report all spawn errors, such as `error.InvalidExe`.
/// This function will block until any spawn errors can be reported, and return them.
-pub fn waitForSpawn(self: *ChildProcess) SpawnError!void {
+pub fn waitForSpawn(self: *Child) SpawnError!void {
if (native_os == .windows) return; // `spawn` reports everything
if (self.term) |term| {
_ = term catch |spawn_err| return spawn_err;
@@ -355,7 +355,7 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void {
}
/// Blocks until child process terminates and then cleans up all resources.
-pub fn wait(self: *ChildProcess, io: Io) WaitError!Term {
+pub fn wait(self: *Child, io: Io) WaitError!Term {
try self.waitForSpawn(); // report spawn errors
if (self.term) |term| {
self.cleanupStreams(io);
@@ -381,7 +381,7 @@ pub const RunResult = struct {
///
/// The process must be started with stdout_behavior and stderr_behavior == .Pipe
pub fn collectOutput(
- child: ChildProcess,
+ child: Child,
/// Used for `stdout` and `stderr`.
allocator: Allocator,
stdout: *ArrayList(u8),
@@ -446,7 +446,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct {
expand_arg0: Arg0Expand = .no_expand,
progress_node: std.Progress.Node = std.Progress.Node.none,
}) RunError!RunResult {
- var child = ChildProcess.init(args.argv, allocator);
+ var child = Child.init(args.argv, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
@@ -461,7 +461,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct {
var stderr: ArrayList(u8) = .empty;
defer stderr.deinit(allocator);
- try child.spawn();
+ try child.spawn(io);
errdefer {
_ = child.kill(io) catch {};
}
@@ -474,7 +474,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct {
};
}
-fn waitUnwrappedWindows(self: *ChildProcess, io: Io) WaitError!void {
+fn waitUnwrappedWindows(self: *Child, io: Io) WaitError!void {
const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false);
self.term = @as(SpawnError!Term, x: {
@@ -496,7 +496,7 @@ fn waitUnwrappedWindows(self: *ChildProcess, io: Io) WaitError!void {
return result;
}
-fn waitUnwrappedPosix(self: *ChildProcess, io: Io) void {
+fn waitUnwrappedPosix(self: *Child, io: Io) void {
const res: posix.WaitPidResult = res: {
if (self.request_resource_usage_statistics) {
switch (native_os) {
@@ -531,11 +531,11 @@ fn waitUnwrappedPosix(self: *ChildProcess, io: Io) void {
self.handleWaitResult(status);
}
-fn handleWaitResult(self: *ChildProcess, status: u32) void {
+fn handleWaitResult(self: *Child, status: u32) void {
self.term = statusToTerm(status);
}
-fn cleanupStreams(self: *ChildProcess, io: Io) void {
+fn cleanupStreams(self: *Child, io: Io) void {
if (self.stdin) |*stdin| {
stdin.close(io);
self.stdin = null;
@@ -561,7 +561,7 @@ fn statusToTerm(status: u32) Term {
Term{ .Unknown = status };
}
-fn spawnPosix(self: *ChildProcess) SpawnError!void {
+fn spawnPosix(self: *Child, io: Io) SpawnError!void {
// 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
@@ -659,7 +659,7 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
})).ptr;
} else {
// TODO come up with a solution for this.
- @panic("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");
+ @panic("missing std lib enhancement: std.process.Child implementation has no way to collect the environment variables to forward to the child process");
}
};
@@ -671,41 +671,41 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
const pid_result = try posix.fork();
if (pid_result == 0) {
// we are the child
- setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
- setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
- setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
+ setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
+ setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
+ setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
if (self.cwd_dir) |cwd| {
- posix.fchdir(cwd.handle) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.fchdir(cwd.handle) catch |err| forkChildErrReport(io, err_pipe[1], err);
} else if (self.cwd) |cwd| {
- posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.chdir(cwd) catch |err| forkChildErrReport(io, err_pipe[1], err);
}
// Must happen after fchdir above, the cwd file descriptor might be
// equal to prog_fileno and be clobbered by this dup2 call.
- if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(err_pipe[1], err);
+ if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(io, err_pipe[1], err);
if (self.gid) |gid| {
- posix.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.setregid(gid, gid) catch |err| forkChildErrReport(io, err_pipe[1], err);
}
if (self.uid) |uid| {
- posix.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.setreuid(uid, uid) catch |err| forkChildErrReport(io, err_pipe[1], err);
}
if (self.pgid) |pid| {
- posix.setpgid(0, pid) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.setpgid(0, pid) catch |err| forkChildErrReport(io, err_pipe[1], err);
}
if (self.start_suspended) {
- posix.kill(posix.getpid(), .STOP) catch |err| forkChildErrReport(err_pipe[1], err);
+ posix.kill(posix.getpid(), .STOP) catch |err| forkChildErrReport(io, err_pipe[1], err);
}
const err = switch (self.expand_arg0) {
.expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
.no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
};
- forkChildErrReport(err_pipe[1], err);
+ forkChildErrReport(io, err_pipe[1], err);
}
// we are the parent
@@ -750,7 +750,7 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
self.progress_node.setIpcFd(prog_pipe[0]);
}
-fn spawnWindows(self: *ChildProcess) SpawnError!void {
+fn spawnWindows(self: *Child) SpawnError!void {
var saAttr = windows.SECURITY_ATTRIBUTES{
.nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
.bInheritHandle = windows.TRUE,
@@ -880,7 +880,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
const app_name_wtf8 = self.argv[0];
const app_name_is_absolute = fs.path.isAbsolute(app_name_wtf8);
- // the cwd set in ChildProcess is in effect when choosing the executable path
+ // the cwd set in Child is in effect when choosing the executable path
// to match posix semantics
var cwd_path_w_needs_free = false;
const cwd_path_w = x: {
@@ -965,7 +965,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
// If the app name had path separators, that disallows PATH searching,
// and there's no need to search the PATH if the app name is absolute.
// We still search the path if the cwd is absolute because of the
- // "cwd set in ChildProcess is in effect when choosing the executable path
+ // "cwd set in Child is in effect when choosing the executable path
// to match posix semantics" behavior--we don't want to skip searching
// the PATH just because we were trying to set the cwd of the child process.
if (app_dirname_w != null or app_name_is_absolute) {
@@ -1039,8 +1039,8 @@ fn destroyPipe(pipe: [2]posix.fd_t) void {
// Child of fork calls this to report an error to the fork parent.
// Then the child exits.
-fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
- writeIntFd(fd, @as(ErrInt, @intFromError(err))) catch {};
+fn forkChildErrReport(io: Io, fd: i32, err: Child.SpawnError) noreturn {
+ writeIntFd(io, fd, @as(ErrInt, @intFromError(err))) catch {};
// If we're linking libc, some naughty applications may have registered atexit handlers
// which we really do not want to run in the fork child. I caught LLVM doing this and
// it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
@@ -1052,9 +1052,9 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
posix.system.exit(1);
}
-fn writeIntFd(fd: i32, value: ErrInt) !void {
+fn writeIntFd(io: Io, fd: i32, value: ErrInt) !void {
var buffer: [8]u8 = undefined;
- var fw: File.Writer = .initStreaming(.{ .handle = fd }, &buffer);
+ var fw: File.Writer = .initStreaming(.{ .handle = fd }, io, &buffer);
fw.interface.writeInt(u64, value, .little) catch unreachable;
fw.interface.flush() catch return error.SystemResources;
}
@@ -1078,7 +1078,7 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
/// Note: `app_buf` should not contain any leading path separators.
/// Note: If the dir is the cwd, dir_buf should be empty (len = 0).
fn windowsCreateProcessPathExt(
- allocator: mem.Allocator,
+ allocator: Allocator,
dir_buf: *ArrayList(u16),
app_buf: *ArrayList(u16),
pathext: [:0]const u16,
@@ -1525,9 +1525,9 @@ const WindowsCommandLineCache = struct {
script_cmd_line: ?[:0]u16 = null,
cmd_exe_path: ?[:0]u16 = null,
argv: []const []const u8,
- allocator: mem.Allocator,
+ allocator: Allocator,
- fn init(allocator: mem.Allocator, argv: []const []const u8) WindowsCommandLineCache {
+ fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache {
return .{
.allocator = allocator,
.argv = argv,
@@ -1571,7 +1571,7 @@ const WindowsCommandLineCache = struct {
/// Returns the absolute path of `cmd.exe` within the Windows system directory.
/// The caller owns the returned slice.
-fn windowsCmdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
+fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
var buf = try ArrayList(u16).initCapacity(allocator, 128);
errdefer buf.deinit(allocator);
while (true) {
@@ -1608,7 +1608,7 @@ const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 };
///
/// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead.
fn argvToCommandLineWindows(
- allocator: mem.Allocator,
+ allocator: Allocator,
argv: []const []const u8,
) ArgvToCommandLineError![:0]u16 {
var buf = std.array_list.Managed(u8).init(allocator);
@@ -1784,7 +1784,7 @@ const ArgvToScriptCommandLineError = error{
/// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.
/// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.
fn argvToScriptCommandLineWindows(
- allocator: mem.Allocator,
+ allocator: Allocator,
/// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD.
/// The script must have been verified to exist at this path before calling this function.
script_path: []const u16,
diff --git a/lib/std/tar.zig b/lib/std/tar.zig
@@ -610,7 +610,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp
}
},
.file => {
- if (createDirAndFile(io, dir, file_name, fileMode(file.mode, options))) |fs_file| {
+ if (createDirAndFile(io, dir, file_name, filePermissions(file.mode, options))) |fs_file| {
defer fs_file.close(io);
var file_writer = fs_file.writer(io, &file_contents_buffer);
try it.streamRemaining(file, &file_writer.interface);
@@ -638,12 +638,12 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp
}
}
-fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mode) !Io.File {
- const fs_file = dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }) catch |err| {
+fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, permissions: Io.File.Permissions) !Io.File {
+ const fs_file = dir.createFile(io, file_name, .{ .exclusive = true, .permissions = permissions }) catch |err| {
if (err == error.FileNotFound) {
if (std.fs.path.dirname(file_name)) |dir_name| {
try dir.makePath(io, dir_name);
- return try dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode });
+ return try dir.createFile(io, file_name, .{ .exclusive = true, .permissions = permissions });
}
}
return err;
@@ -880,9 +880,9 @@ test "create file and symlink" {
var root = testing.tmpDir(.{});
defer root.cleanup();
- var file = try createDirAndFile(io, root.dir, "file1", default_mode);
+ var file = try createDirAndFile(io, root.dir, "file1", .default_file);
file.close(io);
- file = try createDirAndFile(io, root.dir, "a/b/c/file2", default_mode);
+ file = try createDirAndFile(io, root.dir, "a/b/c/file2", .default_file);
file.close(io);
createDirAndSymlink(io, root.dir, "a/b/c/file2", "symlink1") catch |err| {
@@ -894,7 +894,7 @@ test "create file and symlink" {
// Danglink symlnik, file created later
try createDirAndSymlink(io, root.dir, "../../../g/h/i/file4", "j/k/l/symlink3");
- file = try createDirAndFile(io, root.dir, "g/h/i/file4", default_mode);
+ file = try createDirAndFile(io, root.dir, "g/h/i/file4", .default_file);
file.close(io);
}
@@ -1118,30 +1118,30 @@ fn normalizePath(bytes: []u8) []u8 {
return bytes;
}
-const default_mode = Io.File.default_mode;
-
// File system mode based on tar header mode and mode_mode options.
-fn fileMode(mode: u32, options: PipeOptions) Io.File.Mode {
+fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions {
+ const default_mode = 0o666;
+
if (!std.fs.has_executable_bit or options.mode_mode == .ignore)
- return default_mode;
+ return .fromMode(default_mode);
const S = std.posix.S;
// The mode from the tar file is inspected for the owner executable bit.
if (mode & S.IXUSR == 0)
- return default_mode;
+ return .fromMode(default_mode);
// This bit is copied to the group and other executable bits.
// Other bits of the mode are left as the default when creating files.
- return default_mode | S.IXUSR | S.IXGRP | S.IXOTH;
+ return .fromMode(default_mode | S.IXUSR | S.IXGRP | S.IXOTH);
}
-test fileMode {
+test filePermissions {
if (!std.fs.has_executable_bit) return error.SkipZigTest;
- try testing.expectEqual(default_mode, fileMode(0o744, PipeOptions{ .mode_mode = .ignore }));
- try testing.expectEqual(0o777, fileMode(0o744, PipeOptions{}));
- try testing.expectEqual(0o666, fileMode(0o644, PipeOptions{}));
- try testing.expectEqual(0o666, fileMode(0o655, PipeOptions{}));
+ try testing.expectEqual(0o666, filePermissions(0o744, PipeOptions{ .mode_mode = .ignore }));
+ try testing.expectEqual(0o777, filePermissions(0o744, PipeOptions{}));
+ try testing.expectEqual(0o666, filePermissions(0o644, PipeOptions{}));
+ try testing.expectEqual(0o666, filePermissions(0o655, PipeOptions{}));
}
test "executable bit" {
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -5782,7 +5782,7 @@ pub fn translateC(
}
// Just to save disk space, we delete the file because it is never needed again.
- cache_tmp_dir.deleteFile(dep_basename) catch |err| {
+ cache_tmp_dir.deleteFile(io, dep_basename) catch |err| {
log.warn("failed to delete '{s}': {t}", .{ dep_file_path, err });
};
}
@@ -6314,11 +6314,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
}
// Just to save disk space, we delete the files that are never needed again.
- defer if (out_diag_path) |diag_file_path| zig_cache_tmp_dir.deleteFile(fs.path.basename(diag_file_path)) catch |err| switch (err) {
+ defer if (out_diag_path) |diag_file_path| zig_cache_tmp_dir.deleteFile(io, fs.path.basename(diag_file_path)) catch |err| switch (err) {
error.FileNotFound => {}, // the file wasn't created due to an error we reported
else => log.warn("failed to delete '{s}': {s}", .{ diag_file_path, @errorName(err) }),
};
- defer if (out_dep_path) |dep_file_path| zig_cache_tmp_dir.deleteFile(fs.path.basename(dep_file_path)) catch |err| switch (err) {
+ defer if (out_dep_path) |dep_file_path| zig_cache_tmp_dir.deleteFile(io, fs.path.basename(dep_file_path)) catch |err| switch (err) {
error.FileNotFound => {}, // the file wasn't created due to an error we reported
else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }),
};
@@ -6329,7 +6329,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
- const term = child.spawnAndWait() catch |err| {
+ const term = child.spawnAndWait(io) catch |err| {
return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) });
};
switch (term) {
@@ -6347,7 +6347,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
child.stdout_behavior = .Ignore;
child.stderr_behavior = .Pipe;
- try child.spawn();
+ try child.spawn(io);
var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
@@ -6723,6 +6723,7 @@ fn spawnZigRc(
argv: []const []const u8,
child_progress_node: std.Progress.Node,
) !void {
+ const io = comp.io;
var node_name: std.ArrayList(u8) = .empty;
defer node_name.deinit(arena);
@@ -6732,8 +6733,8 @@ fn spawnZigRc(
child.stderr_behavior = .Pipe;
child.progress_node = child_progress_node;
- child.spawn() catch |err| {
- return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
+ child.spawn(io) catch |err| {
+ return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ argv[0], err });
};
var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{
diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig
@@ -1347,7 +1347,7 @@ fn unzip(
.diagnostics = &diagnostics,
}) catch |err| return f.fail(f.location_tok, try eb.printString("zip extract failed: {t}", .{err}));
- cache_root.handle.deleteFile(&zip_path) catch |err|
+ cache_root.handle.deleteFile(io, &zip_path) catch |err|
return f.fail(f.location_tok, try eb.printString("delete temporary zip failed: {t}", .{err}));
return .{ .root_dir = diagnostics.root_dir };
@@ -1547,7 +1547,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute
.fs_path = fs_path,
.failure = undefined, // to be populated by the worker
};
- group.async(io, workerDeleteFile, .{ root_dir, deleted_file });
+ group.async(io, workerDeleteFile, .{ io, root_dir, deleted_file });
try deleted_files.append(deleted_file);
continue;
}
@@ -1669,8 +1669,8 @@ fn workerHashFile(dir: Io.Dir, hashed_file: *HashedFile) void {
hashed_file.failure = hashFileFallible(dir, hashed_file);
}
-fn workerDeleteFile(dir: Io.Dir, deleted_file: *DeletedFile) void {
- deleted_file.failure = deleteFileFallible(dir, deleted_file);
+fn workerDeleteFile(io: Io, dir: Io.Dir, deleted_file: *DeletedFile) void {
+ deleted_file.failure = deleteFileFallible(io, dir, deleted_file);
}
fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Error!void {
@@ -1712,8 +1712,8 @@ fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Er
hashed_file.size = file_size;
}
-fn deleteFileFallible(dir: Io.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void {
- try dir.deleteFile(deleted_file.fs_path);
+fn deleteFileFallible(io: Io, dir: Io.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void {
+ try dir.deleteFile(io, deleted_file.fs_path);
}
fn setExecutable(file: Io.File) !void {
diff --git a/src/link.zig b/src/link.zig
@@ -1235,22 +1235,26 @@ pub const File = struct {
ty: InternPool.Index,
};
- pub fn determineMode(
+ pub fn determinePermissions(
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
- ) Io.File.Mode {
+ ) Io.File.Permissions {
// On common systems with a 0o022 umask, 0o777 will still result in a file created
// with 0o755 permissions, but it works appropriately if the system is configured
// more leniently. As another data point, C's fopen seems to open files with the
// 666 mode.
- const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
+ const executable_mode: Io.FilePermissions = if (builtin.target.os.tag == .windows)
+ .default_file
+ else
+ .fromMode(0o777);
+
switch (output_mode) {
.Lib => return switch (link_mode) {
.dynamic => executable_mode,
- .static => Io.File.default_mode,
+ .static => .default_file,
},
.Exe => return executable_mode,
- .Obj => return Io.File.default_mode,
+ .Obj => return .default_file,
}
}
diff --git a/src/link/Lld.zig b/src/link/Lld.zig
@@ -1608,13 +1608,13 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
- break :term child.spawnAndWait();
+ break :term child.spawnAndWait(io);
} else term: {
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Ignore;
child.stderr_behavior = .Pipe;
- child.spawn() catch |err| break :term err;
+ child.spawn(io) catch |err| break :term err;
var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(comp.gpa, .unlimited);
break :term child.wait();
@@ -1658,13 +1658,13 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
rsp_child.stdout_behavior = .Inherit;
rsp_child.stderr_behavior = .Inherit;
- break :term rsp_child.spawnAndWait() catch |err| break :err err;
+ break :term rsp_child.spawnAndWait(io) catch |err| break :err err;
} else {
rsp_child.stdin_behavior = .Ignore;
rsp_child.stdout_behavior = .Ignore;
rsp_child.stderr_behavior = .Pipe;
- rsp_child.spawn() catch |err| break :err err;
+ rsp_child.spawn(io) catch |err| break :err err;
var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(comp.gpa, .unlimited);
break :term rsp_child.wait() catch |err| break :err err;
diff --git a/src/main.zig b/src/main.zig
@@ -4457,7 +4457,7 @@ fn runOrTest(
const term_result = t: {
std.debug.lockStdErr();
defer std.debug.unlockStdErr();
- break :t child.spawnAndWait();
+ break :t child.spawnAndWait(io);
};
const term = term_result catch |err| {
try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
@@ -4512,6 +4512,7 @@ fn runOrTestHotSwap(
all_args: []const []const u8,
runtime_args_start: ?usize,
) !std.process.Child.Id {
+ const io = comp.io;
const lf = comp.bin_file.?;
const exe_path = switch (builtin.target.os.tag) {
@@ -4593,7 +4594,7 @@ fn runOrTestHotSwap(
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
- try child.spawn();
+ try child.spawn(io);
return child.id;
},
@@ -5419,8 +5420,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
const term = t: {
std.debug.lockStdErr();
defer std.debug.unlockStdErr();
- break :t child.spawnAndWait() catch |err| {
- fatal("failed to spawn build runner {s}: {s}", .{ child_argv.items[0], @errorName(err) });
+ break :t child.spawnAndWait(io) catch |err| {
+ fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err });
};
};
@@ -5444,7 +5445,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
dirs.local_cache, tmp_sub_path, @errorName(err),
});
};
- dirs.local_cache.handle.deleteFile(tmp_sub_path) catch {};
+ dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {};
var it = mem.splitScalar(u8, stdout, '\n');
var any_errors = false;
@@ -5685,7 +5686,7 @@ fn jitCmd(
child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
child.stderr_behavior = .Inherit;
- try child.spawn();
+ try child.spawn(io);
if (options.capture) |ptr| {
var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
diff --git a/test/src/Cases.zig b/test/src/Cases.zig
@@ -461,6 +461,7 @@ pub fn lowerToBuildSteps(
parent_step: *std.Build.Step,
options: CaseTestOptions,
) void {
+ const io = self.io;
const host = b.resolveTargetQuery(.{});
const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
@@ -595,7 +596,7 @@ pub fn lowerToBuildSteps(
},
.Execution => |expected_stdout| no_exec: {
const run = if (case.target.result.ofmt == .c) run_step: {
- if (getExternalExecutor(&host.result, &case.target.result, .{ .link_libc = true }) != .native) {
+ if (getExternalExecutor(io, &host.result, &case.target.result, .{ .link_libc = true }) != .native) {
// We wouldn't be able to run the compiled C code.
break :no_exec;
}
diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig
@@ -29,7 +29,7 @@ pub fn main() !void {
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Inherit;
- try child.spawn();
+ try child.spawn(io);
const child_stdin = child.stdin.?;
try child_stdin.writeAll("hello from stdin"); // verified in child
child_stdin.close(io);