diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 70b6c5bcc3..162fedca5f 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -718,6 +718,7 @@ pub const VTable = struct { lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, unlockStderr: *const fn (?*anyopaque) void, + processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, sleep: *const fn (?*anyopaque, Timeout) SleepError!void, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index ddabccf00d..8d383d1c01 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -648,7 +648,7 @@ pub fn init( .main_thread = .{ .signal_id = Thread.currentSignalId(), .current_closure = null, - .cancel_protection = undefined, + .cancel_protection = .unblocked, }, .argv0 = options.argv0, .environ = options.environ, @@ -689,7 +689,7 @@ pub const init_single_threaded: Threaded = .{ .main_thread = .{ .signal_id = undefined, .current_closure = null, - .cancel_protection = undefined, + .cancel_protection = .unblocked, }, .robust_cancel = .disabled, .argv0 = .{}, @@ -742,7 +742,7 @@ fn worker(t: *Threaded) void { var thread: Thread = .{ .signal_id = Thread.currentSignalId(), .current_closure = null, - .cancel_protection = undefined, + .cancel_protection = .unblocked, }; Thread.current = &thread; @@ -844,6 +844,7 @@ pub fn io(t: *Threaded) Io { .lockStderr = lockStderr, .tryLockStderr = tryLockStderr, .unlockStderr = unlockStderr, + .processSetCurrentDir = processSetCurrentDir, .now = now, .sleep = sleep, @@ -979,6 +980,7 @@ pub fn ioBasic(t: *Threaded) Io { .lockStderr = lockStderr, .tryLockStderr = tryLockStderr, .unlockStderr = unlockStderr, + .processSetCurrentDir = processSetCurrentDir, .now = now, .sleep = sleep, @@ -7370,6 +7372,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex }; defer w.CloseHandle(h_file); + // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); const len = std.unicode.calcWtf8Len(wide_slice); @@ -10796,6 +10799,71 @@ fn unlockStderr(userdata: ?*anyopaque) void { std.process.stderr_thread_mutex.unlock(); } +fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void { + if (native_os == .wasi) return error.OperationUnsupported; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + if (is_windows) { + try current_thread.checkCancel(); + var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; + // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks + const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer); + const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong; + try current_thread.checkCancel(); + var nt_name: windows.UNICODE_STRING = .{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + .Buffer = @constCast(dir_path.ptr), + }; + switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) { + .SUCCESS => return, + .OBJECT_NAME_INVALID => return error.BadPathName, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + .NO_MEDIA_IN_DEVICE => return error.NoDevice, + .INVALID_PARAMETER => |err| return windows.statusBug(err), + .ACCESS_DENIED => return error.AccessDenied, + .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err), + .NOT_A_DIRECTORY => return error.NotDir, + else => |status| return windows.unexpectedStatus(status), + } + } + + if (dir.handle == posix.AT.FDCWD) return; + + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.fchdir(dir.handle))) { + .SUCCESS => return current_thread.endSyscall(), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .ACCES => { + current_thread.endSyscall(); + return error.AccessDenied; + }, + .BADF => |err| { + current_thread.endSyscall(); + return errnoBug(err); + }, + .NOTDIR => { + current_thread.endSyscall(); + return error.NotDir; + }, + .IO => { + current_thread.endSyscall(); + return error.FileSystem; + }, + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, + } + } +} + pub const PosixAddress = extern union { any: posix.sockaddr, in: posix.sockaddr.in, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 3ea48585ff..12ec0427a9 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2939,40 +2939,6 @@ pub fn WriteFile( return bytes_written; } -pub const SetCurrentDirectoryError = error{ - NameTooLong, - FileNotFound, - NotDir, - AccessDenied, - NoDevice, - BadPathName, - Unexpected, -}; - -pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void { - const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong; - - var nt_name: UNICODE_STRING = .{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(path_name.ptr), - }; - - const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); - switch (rc) { - .SUCCESS => {}, - .OBJECT_NAME_INVALID => return error.BadPathName, - .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, - .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, - .NO_MEDIA_IN_DEVICE => return error.NoDevice, - .INVALID_PARAMETER => unreachable, - .ACCESS_DENIED => return error.AccessDenied, - .OBJECT_PATH_SYNTAX_BAD => unreachable, - .NOT_A_DIRECTORY => return error.NotDir, - else => return unexpectedStatus(rc), - } -} - pub const GetCurrentDirectoryError = error{ NameTooLong, Unexpected, diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 133dfc0293..4bd19acb1a 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -1171,11 +1171,9 @@ pub const ChangeCurDirError = error{ /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { if (native_os == .wasi and !builtin.link_libc) { - @compileError("WASI does not support os.chdir"); + @compileError("unsupported OS"); } else if (native_os == .windows) { - var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; - const len = try windows.wtf8ToWtf16Le(&wtf16_dir_path, dir_path); - return chdirW(wtf16_dir_path[0..len]); + @compileError("unsupported OS"); } else { const dir_path_c = try toPosixPath(dir_path); return chdirZ(&dir_path_c); @@ -1188,12 +1186,9 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { if (native_os == .windows) { - const dir_path_span = mem.span(dir_path); - var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; - const len = try windows.wtf8ToWtf16Le(&wtf16_dir_path, dir_path_span); - return chdirW(wtf16_dir_path[0..len]); + @compileError("unsupported OS"); } else if (native_os == .wasi and !builtin.link_libc) { - return chdir(mem.span(dir_path)); + @compileError("unsupported OS"); } switch (errno(system.chdir(dir_path))) { .SUCCESS => return, @@ -1210,14 +1205,6 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { } } -/// Windows-only. Same as `chdir` except the parameter is WTF16 LE encoded. -pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void { - windows.SetCurrentDirectory(dir_path) catch |err| switch (err) { - error.NoDevice => return error.FileSystem, - else => |e| return e, - }; -} - pub const FchdirError = error{ AccessDenied, NotDir, diff --git a/lib/std/process.zig b/lib/std/process.zig index cf751f7a28..865376d907 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -2304,3 +2304,28 @@ pub fn exit(status: u8) noreturn { else => posix.system.exit(status), } } + +pub const SetCurrentDirError = error{ + AccessDenied, + BadPathName, + FileNotFound, + FileSystem, + NameTooLong, + NoDevice, + NotDir, + OperationUnsupported, + UnrecognizedVolume, +} || Io.Cancelable || Io.UnexpectedError; + +/// Changes the current working directory to the open directory handle. +/// Corresponds to "fchdir" in libc. +/// +/// This modifies global process state and can have surprising effects in +/// multithreaded applications. Most applications and especially libraries +/// should not call this function as a general rule, however it can have use +/// cases in, for example, implementing a shell, or child process execution. +/// +/// Calling this function makes code less portable and less reusable. +pub fn setCurrentDir(io: Io, dir: Io.Dir) !void { + return io.vtable.processSetCurrentDir(io.userdata, dir); +} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 5b5ec852e9..9519eeb427 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -629,6 +629,7 @@ pub const TmpDir = struct { }; pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { + comptime assert(builtin.is_test); var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; std.crypto.random.bytes(&random_bytes); var sub_path: [TmpDir.sub_path_len]u8 = undefined; diff --git a/test/standalone/posix/cwd.zig b/test/standalone/posix/cwd.zig index 43dcc63bfe..1bafdfebac 100644 --- a/test/standalone/posix/cwd.zig +++ b/test/standalone/posix/cwd.zig @@ -1,6 +1,10 @@ -const std = @import("std"); const builtin = @import("builtin"); +const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + const path_max = std.fs.max_path_bytes; pub fn main() !void { @@ -9,13 +13,17 @@ pub fn main() !void { return; } - var Allocator = std.heap.DebugAllocator(.{}){}; - const a = Allocator.allocator(); - defer std.debug.assert(Allocator.deinit() == .ok); + var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; + defer assert(debug_allocator.deinit() == .ok); + const gpa = debug_allocator.allocator(); + + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); try test_chdir_self(); try test_chdir_absolute(); - try test_chdir_relative(a); + try test_chdir_relative(gpa, io); } // get current working directory and expect it to match given path @@ -46,20 +54,20 @@ fn test_chdir_absolute() !void { try expect_cwd(parent); } -fn test_chdir_relative(a: std.mem.Allocator) !void { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); +fn test_chdir_relative(gpa: Allocator, io: Io) !void { + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child - try tmp.parent_dir.setAsCwd(); + try std.process.setCurrentDir(io, tmp.parent_dir); // Capture base working directory path, to build expected full path var base_cwd_buf: [path_max]u8 = undefined; const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); const relative_dir_name = &tmp.sub_path; - const expected_path = try std.fs.path.resolve(a, &.{ base_cwd, relative_dir_name }); - defer a.free(expected_path); + const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, relative_dir_name }); + defer gpa.free(expected_path); // change current working directory to new test directory try std.posix.chdir(relative_dir_name); @@ -68,8 +76,46 @@ fn test_chdir_relative(a: std.mem.Allocator) !void { const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase - const resolved_cwd = try std.fs.path.resolve(a, &.{new_cwd}); - defer a.free(resolved_cwd); + const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd}); + defer gpa.free(resolved_cwd); try std.testing.expectEqualStrings(expected_path, resolved_cwd); } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig index 447285c444..5a8b438242 100644 --- a/test/standalone/posix/relpaths.zig +++ b/test/standalone/posix/relpaths.zig @@ -14,21 +14,21 @@ pub fn main() !void { const gpa = debug_allocator.allocator(); defer std.debug.assert(debug_allocator.deinit() == .ok); - const io = std.Io.Threaded.global_single_threaded.ioBasic(); + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); - // TODO this API isn't supposed to be used outside of unit testing. make it compilation error if used - // outside of unit testing. - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); // Want to test relative paths, so cd into the tmpdir for these tests - try tmp.dir.setAsCwd(); + try std.process.setCurrentDir(io, tmp.dir); try test_symlink(gpa, io, tmp); try test_link(io, tmp); } -fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { +fn test_symlink(gpa: Allocator, io: Io, tmp: TmpDir) !void { const target_name = "symlink-target"; const symlink_name = "symlinker"; @@ -47,32 +47,15 @@ fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { else => return err, }; } else { - try std.posix.symlink(target_name, symlink_name); + try Io.Dir.cwd().symLink(io, target_name, symlink_name, .{}); } var buffer: [std.fs.max_path_bytes]u8 = undefined; - const given = try std.posix.readlink(symlink_name, buffer[0..]); + const given = buffer[0..try Io.Dir.cwd().readLink(io, symlink_name, &buffer)]; try std.testing.expectEqualStrings(target_name, given); } -fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t } { - if (builtin.target.os.tag == .linux) { - const stx = try std.os.linux.wrapped.statx( - fd, - "", - std.posix.AT.EMPTY_PATH, - .{ .INO = true, .NLINK = true }, - ); - std.debug.assert(stx.mask.INO); - std.debug.assert(stx.mask.NLINK); - return .{ stx.ino, stx.nlink }; - } - - const st = try std.posix.fstat(fd); - return .{ st.ino, st.nlink }; -} - -fn test_link(io: Io, tmp: std.testing.TmpDir) !void { +fn test_link(io: Io, tmp: TmpDir) !void { switch (builtin.target.os.tag) { .linux, .illumos => {}, else => return, @@ -84,7 +67,7 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); // Test 1: create the relative link from inside tmp - try std.posix.link(target_name, link_name); + try Io.Dir.hardLink(.cwd(), target_name, .cwd(), link_name, io, .{}); // Verify const efd = try tmp.dir.openFile(io, target_name, .{}); @@ -94,16 +77,54 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { defer nfd.close(io); { - const eino, _ = try getLinkInfo(efd.handle); - const nino, const nlink = try getLinkInfo(nfd.handle); - try std.testing.expectEqual(eino, nino); - try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); + const e_stat = try efd.stat(io); + const n_stat = try nfd.stat(io); + try std.testing.expectEqual(e_stat.inode, n_stat.inode); + try std.testing.expectEqual(2, n_stat.nlink); } // Test 2: Remove the link and see the stats update - try std.posix.unlink(link_name); + try Io.Dir.cwd().deleteFile(io, link_name); { - _, const elink = try getLinkInfo(efd.handle); - try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); + const e_stat = try efd.stat(io); + try std.testing.expectEqual(1, e_stat.nlink); } } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/windows_bat_args/fuzz.zig b/test/standalone/windows_bat_args/fuzz.zig index db6043d3e9..b68639dda4 100644 --- a/test/standalone/windows_bat_args/fuzz.zig +++ b/test/standalone/windows_bat_args/fuzz.zig @@ -44,8 +44,8 @@ pub fn main() anyerror!void { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; // `child_exe_path_orig` might be relative; make it relative to our new cwd. const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); diff --git a/test/standalone/windows_bat_args/test.zig b/test/standalone/windows_bat_args/test.zig index 48c32826e5..73f0ee9453 100644 --- a/test/standalone/windows_bat_args/test.zig +++ b/test/standalone/windows_bat_args/test.zig @@ -18,8 +18,8 @@ pub fn main() anyerror!void { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; // `child_exe_path_orig` might be relative; make it relative to our new cwd. const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig index db771c88eb..f5df4dee25 100644 --- a/test/standalone/windows_spawn/main.zig +++ b/test/standalone/windows_spawn/main.zig @@ -127,8 +127,8 @@ pub fn main() anyerror!void { try testExecError(error.FileNotFound, gpa, "goodbye"); // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); defer gpa.free(something_subdir_abs_path); @@ -191,7 +191,7 @@ pub fn main() anyerror!void { defer subdir_cwd.close(io); try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); - try subdir_cwd.setAsCwd(); + try std.process.setCurrentDir(io, subdir_cwd); // clear the PATH again std.debug.assert(windows.kernel32.SetEnvironmentVariableW( diff --git a/tools/fetch_them_macos_headers.zig b/tools/fetch_them_macos_headers.zig index ed9b362452..c55a569e9f 100644 --- a/tools/fetch_them_macos_headers.zig +++ b/tools/fetch_them_macos_headers.zig @@ -4,7 +4,6 @@ const Dir = std.Io.Dir; const mem = std.mem; const process = std.process; const assert = std.debug.assert; -const tmpDir = std.testing.tmpDir; const fatal = std.process.fatal; const info = std.log.info; @@ -111,15 +110,14 @@ pub fn main() anyerror!void { const os_ver: OsVer = @enumFromInt(version.major); info("found SDK deployment target macOS {f} aka '{t}'", .{ version, os_ver }); - var tmp = tmpDir(.{}); - defer tmp.cleanup(); + const tmp_dir: Io.Dir = .cwd(); for (&[_]Arch{ .aarch64, .x86_64 }) |arch| { const target: Target = .{ .arch = arch, .os_ver = os_ver, }; - try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp); + try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp_dir); } } @@ -130,11 +128,11 @@ fn fetchTarget( sysroot: []const u8, target: Target, ver: Version, - tmp: std.testing.TmpDir, + tmp_dir: Io.Dir, ) !void { const tmp_filename = "macos-headers"; const headers_list_filename = "macos-headers.o.d"; - const tmp_path = try tmp.dir.realPathFileAlloc(io, ".", arena); + const tmp_path = try tmp_dir.realPathFileAlloc(io, ".", arena); const tmp_file_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, tmp_filename }); const headers_list_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, headers_list_filename }); @@ -173,7 +171,7 @@ fn fetchTarget( } // Read in the contents of `macos-headers.o.d` - const headers_list_file = try tmp.dir.openFile(io, headers_list_filename, .{}); + const headers_list_file = try tmp_dir.openFile(io, headers_list_filename, .{}); defer headers_list_file.close(io); var headers_dir = Dir.cwd().openDir(io, headers_source_prefix, .{}) catch |err| switch (err) {