| author | |
| committer | |
| log | fa79d346744308250c165519f842fedfc26a1c14 |
| tree | 21a007127f3199bca0334c0e959480f5cb7dd0eb |
| parent | 98e9716c082aa7e134e11ed14db8c2f631fedc8a |
There's a good argument to not have this in the std lib but it's more
work to remove it than to leave it in, and this branch is already
20,000+ lines changed.12 files changed, 229 insertions(+), 116 deletions(-)
lib/std/Io.zig+1| ... | ... | @@ -718,6 +718,7 @@ pub const VTable = struct { |
| 718 | 718 | lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, |
| 719 | 719 | tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, |
| 720 | 720 | unlockStderr: *const fn (?*anyopaque) void, |
| 721 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, | |
| 721 | 722 | |
| 722 | 723 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 723 | 724 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
lib/std/Io/Threaded.zig+71-3| ... | ... | @@ -648,7 +648,7 @@ pub fn init( |
| 648 | 648 | .main_thread = .{ |
| 649 | 649 | .signal_id = Thread.currentSignalId(), |
| 650 | 650 | .current_closure = null, |
| 651 | .cancel_protection = undefined, | |
| 651 | .cancel_protection = .unblocked, | |
| 652 | 652 | }, |
| 653 | 653 | .argv0 = options.argv0, |
| 654 | 654 | .environ = options.environ, |
| ... | ... | @@ -689,7 +689,7 @@ pub const init_single_threaded: Threaded = .{ |
| 689 | 689 | .main_thread = .{ |
| 690 | 690 | .signal_id = undefined, |
| 691 | 691 | .current_closure = null, |
| 692 | .cancel_protection = undefined, | |
| 692 | .cancel_protection = .unblocked, | |
| 693 | 693 | }, |
| 694 | 694 | .robust_cancel = .disabled, |
| 695 | 695 | .argv0 = .{}, |
| ... | ... | @@ -742,7 +742,7 @@ fn worker(t: *Threaded) void { |
| 742 | 742 | var thread: Thread = .{ |
| 743 | 743 | .signal_id = Thread.currentSignalId(), |
| 744 | 744 | .current_closure = null, |
| 745 | .cancel_protection = undefined, | |
| 745 | .cancel_protection = .unblocked, | |
| 746 | 746 | }; |
| 747 | 747 | Thread.current = &thread; |
| 748 | 748 | |
| ... | ... | @@ -844,6 +844,7 @@ pub fn io(t: *Threaded) Io { |
| 844 | 844 | .lockStderr = lockStderr, |
| 845 | 845 | .tryLockStderr = tryLockStderr, |
| 846 | 846 | .unlockStderr = unlockStderr, |
| 847 | .processSetCurrentDir = processSetCurrentDir, | |
| 847 | 848 | |
| 848 | 849 | .now = now, |
| 849 | 850 | .sleep = sleep, |
| ... | ... | @@ -979,6 +980,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 979 | 980 | .lockStderr = lockStderr, |
| 980 | 981 | .tryLockStderr = tryLockStderr, |
| 981 | 982 | .unlockStderr = unlockStderr, |
| 983 | .processSetCurrentDir = processSetCurrentDir, | |
| 982 | 984 | |
| 983 | 985 | .now = now, |
| 984 | 986 | .sleep = sleep, |
| ... | ... | @@ -7370,6 +7372,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7370 | 7372 | }; |
| 7371 | 7373 | defer w.CloseHandle(h_file); |
| 7372 | 7374 | |
| 7375 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | |
| 7373 | 7376 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); |
| 7374 | 7377 | |
| 7375 | 7378 | const len = std.unicode.calcWtf8Len(wide_slice); |
| ... | ... | @@ -10796,6 +10799,71 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 10796 | 10799 | std.process.stderr_thread_mutex.unlock(); |
| 10797 | 10800 | } |
| 10798 | 10801 | |
| 10802 | fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void { | |
| 10803 | if (native_os == .wasi) return error.OperationUnsupported; | |
| 10804 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | |
| 10805 | const current_thread = Thread.getCurrent(t); | |
| 10806 | ||
| 10807 | if (is_windows) { | |
| 10808 | try current_thread.checkCancel(); | |
| 10809 | var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; | |
| 10810 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | |
| 10811 | const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer); | |
| 10812 | const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong; | |
| 10813 | try current_thread.checkCancel(); | |
| 10814 | var nt_name: windows.UNICODE_STRING = .{ | |
| 10815 | .Length = path_len_bytes, | |
| 10816 | .MaximumLength = path_len_bytes, | |
| 10817 | .Buffer = @constCast(dir_path.ptr), | |
| 10818 | }; | |
| 10819 | switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) { | |
| 10820 | .SUCCESS => return, | |
| 10821 | .OBJECT_NAME_INVALID => return error.BadPathName, | |
| 10822 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | |
| 10823 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | |
| 10824 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | |
| 10825 | .INVALID_PARAMETER => |err| return windows.statusBug(err), | |
| 10826 | .ACCESS_DENIED => return error.AccessDenied, | |
| 10827 | .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err), | |
| 10828 | .NOT_A_DIRECTORY => return error.NotDir, | |
| 10829 | else => |status| return windows.unexpectedStatus(status), | |
| 10830 | } | |
| 10831 | } | |
| 10832 | ||
| 10833 | if (dir.handle == posix.AT.FDCWD) return; | |
| 10834 | ||
| 10835 | try current_thread.beginSyscall(); | |
| 10836 | while (true) { | |
| 10837 | switch (posix.errno(posix.system.fchdir(dir.handle))) { | |
| 10838 | .SUCCESS => return current_thread.endSyscall(), | |
| 10839 | .INTR => { | |
| 10840 | try current_thread.checkCancel(); | |
| 10841 | continue; | |
| 10842 | }, | |
| 10843 | .ACCES => { | |
| 10844 | current_thread.endSyscall(); | |
| 10845 | return error.AccessDenied; | |
| 10846 | }, | |
| 10847 | .BADF => |err| { | |
| 10848 | current_thread.endSyscall(); | |
| 10849 | return errnoBug(err); | |
| 10850 | }, | |
| 10851 | .NOTDIR => { | |
| 10852 | current_thread.endSyscall(); | |
| 10853 | return error.NotDir; | |
| 10854 | }, | |
| 10855 | .IO => { | |
| 10856 | current_thread.endSyscall(); | |
| 10857 | return error.FileSystem; | |
| 10858 | }, | |
| 10859 | else => |err| { | |
| 10860 | current_thread.endSyscall(); | |
| 10861 | return posix.unexpectedErrno(err); | |
| 10862 | }, | |
| 10863 | } | |
| 10864 | } | |
| 10865 | } | |
| 10866 | ||
| 10799 | 10867 | pub const PosixAddress = extern union { |
| 10800 | 10868 | any: posix.sockaddr, |
| 10801 | 10869 | in: posix.sockaddr.in, |
lib/std/os/windows.zig-34| ... | ... | @@ -2939,40 +2939,6 @@ pub fn WriteFile( |
| 2939 | 2939 | return bytes_written; |
| 2940 | 2940 | } |
| 2941 | 2941 | |
| 2942 | pub const SetCurrentDirectoryError = error{ | |
| 2943 | NameTooLong, | |
| 2944 | FileNotFound, | |
| 2945 | NotDir, | |
| 2946 | AccessDenied, | |
| 2947 | NoDevice, | |
| 2948 | BadPathName, | |
| 2949 | Unexpected, | |
| 2950 | }; | |
| 2951 | ||
| 2952 | pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void { | |
| 2953 | const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong; | |
| 2954 | ||
| 2955 | var nt_name: UNICODE_STRING = .{ | |
| 2956 | .Length = path_len_bytes, | |
| 2957 | .MaximumLength = path_len_bytes, | |
| 2958 | .Buffer = @constCast(path_name.ptr), | |
| 2959 | }; | |
| 2960 | ||
| 2961 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); | |
| 2962 | switch (rc) { | |
| 2963 | .SUCCESS => {}, | |
| 2964 | .OBJECT_NAME_INVALID => return error.BadPathName, | |
| 2965 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | |
| 2966 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | |
| 2967 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | |
| 2968 | .INVALID_PARAMETER => unreachable, | |
| 2969 | .ACCESS_DENIED => return error.AccessDenied, | |
| 2970 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | |
| 2971 | .NOT_A_DIRECTORY => return error.NotDir, | |
| 2972 | else => return unexpectedStatus(rc), | |
| 2973 | } | |
| 2974 | } | |
| 2975 | ||
| 2976 | 2942 | pub const GetCurrentDirectoryError = error{ |
| 2977 | 2943 | NameTooLong, |
| 2978 | 2944 | Unexpected, |
lib/std/posix.zig+4-17| ... | ... | @@ -1171,11 +1171,9 @@ pub const ChangeCurDirError = error{ |
| 1171 | 1171 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. |
| 1172 | 1172 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 1173 | 1173 | if (native_os == .wasi and !builtin.link_libc) { |
| 1174 | @compileError("WASI does not support os.chdir"); | |
| 1174 | @compileError("unsupported OS"); | |
| 1175 | 1175 | } else if (native_os == .windows) { |
| 1176 | var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; | |
| 1177 | const len = try windows.wtf8ToWtf16Le(&wtf16_dir_path, dir_path); | |
| 1178 | return chdirW(wtf16_dir_path[0..len]); | |
| 1176 | @compileError("unsupported OS"); | |
| 1179 | 1177 | } else { |
| 1180 | 1178 | const dir_path_c = try toPosixPath(dir_path); |
| 1181 | 1179 | return chdirZ(&dir_path_c); |
| ... | ... | @@ -1188,12 +1186,9 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 1188 | 1186 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. |
| 1189 | 1187 | pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { |
| 1190 | 1188 | if (native_os == .windows) { |
| 1191 | const dir_path_span = mem.span(dir_path); | |
| 1192 | var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; | |
| 1193 | const len = try windows.wtf8ToWtf16Le(&wtf16_dir_path, dir_path_span); | |
| 1194 | return chdirW(wtf16_dir_path[0..len]); | |
| 1189 | @compileError("unsupported OS"); | |
| 1195 | 1190 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 1196 | return chdir(mem.span(dir_path)); | |
| 1191 | @compileError("unsupported OS"); | |
| 1197 | 1192 | } |
| 1198 | 1193 | switch (errno(system.chdir(dir_path))) { |
| 1199 | 1194 | .SUCCESS => return, |
| ... | ... | @@ -1210,14 +1205,6 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { |
| 1210 | 1205 | } |
| 1211 | 1206 | } |
| 1212 | 1207 | |
| 1213 | /// Windows-only. Same as `chdir` except the parameter is WTF16 LE encoded. | |
| 1214 | pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void { | |
| 1215 | windows.SetCurrentDirectory(dir_path) catch |err| switch (err) { | |
| 1216 | error.NoDevice => return error.FileSystem, | |
| 1217 | else => |e| return e, | |
| 1218 | }; | |
| 1219 | } | |
| 1220 | ||
| 1221 | 1208 | pub const FchdirError = error{ |
| 1222 | 1209 | AccessDenied, |
| 1223 | 1210 | NotDir, |
lib/std/process.zig+25| ... | ... | @@ -2304,3 +2304,28 @@ pub fn exit(status: u8) noreturn { |
| 2304 | 2304 | else => posix.system.exit(status), |
| 2305 | 2305 | } |
| 2306 | 2306 | } |
| 2307 | ||
| 2308 | pub const SetCurrentDirError = error{ | |
| 2309 | AccessDenied, | |
| 2310 | BadPathName, | |
| 2311 | FileNotFound, | |
| 2312 | FileSystem, | |
| 2313 | NameTooLong, | |
| 2314 | NoDevice, | |
| 2315 | NotDir, | |
| 2316 | OperationUnsupported, | |
| 2317 | UnrecognizedVolume, | |
| 2318 | } || Io.Cancelable || Io.UnexpectedError; | |
| 2319 | ||
| 2320 | /// Changes the current working directory to the open directory handle. | |
| 2321 | /// Corresponds to "fchdir" in libc. | |
| 2322 | /// | |
| 2323 | /// This modifies global process state and can have surprising effects in | |
| 2324 | /// multithreaded applications. Most applications and especially libraries | |
| 2325 | /// should not call this function as a general rule, however it can have use | |
| 2326 | /// cases in, for example, implementing a shell, or child process execution. | |
| 2327 | /// | |
| 2328 | /// Calling this function makes code less portable and less reusable. | |
| 2329 | pub fn setCurrentDir(io: Io, dir: Io.Dir) !void { | |
| 2330 | return io.vtable.processSetCurrentDir(io.userdata, dir); | |
| 2331 | } |
lib/std/testing.zig+1| ... | ... | @@ -629,6 +629,7 @@ pub const TmpDir = struct { |
| 629 | 629 | }; |
| 630 | 630 | |
| 631 | 631 | pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { |
| 632 | comptime assert(builtin.is_test); | |
| 632 | 633 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; |
| 633 | 634 | std.crypto.random.bytes(&random_bytes); |
| 634 | 635 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; |
test/standalone/posix/cwd.zig+59-13| ... | ... | @@ -1,6 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | 1 | const builtin = @import("builtin"); |
| 3 | 2 | |
| 3 | const std = @import("std"); | |
| 4 | const Io = std.Io; | |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | const assert = std.debug.assert; | |
| 7 | ||
| 4 | 8 | const path_max = std.fs.max_path_bytes; |
| 5 | 9 | |
| 6 | 10 | pub fn main() !void { |
| ... | ... | @@ -9,13 +13,17 @@ pub fn main() !void { |
| 9 | 13 | return; |
| 10 | 14 | } |
| 11 | 15 | |
| 12 | var Allocator = std.heap.DebugAllocator(.{}){}; | |
| 13 | const a = Allocator.allocator(); | |
| 14 | defer std.debug.assert(Allocator.deinit() == .ok); | |
| 16 | var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; | |
| 17 | defer assert(debug_allocator.deinit() == .ok); | |
| 18 | const gpa = debug_allocator.allocator(); | |
| 19 | ||
| 20 | var threaded: std.Io.Threaded = .init(gpa, .{}); | |
| 21 | defer threaded.deinit(); | |
| 22 | const io = threaded.io(); | |
| 15 | 23 | |
| 16 | 24 | try test_chdir_self(); |
| 17 | 25 | try test_chdir_absolute(); |
| 18 | try test_chdir_relative(a); | |
| 26 | try test_chdir_relative(gpa, io); | |
| 19 | 27 | } |
| 20 | 28 | |
| 21 | 29 | // get current working directory and expect it to match given path |
| ... | ... | @@ -46,20 +54,20 @@ fn test_chdir_absolute() !void { |
| 46 | 54 | try expect_cwd(parent); |
| 47 | 55 | } |
| 48 | 56 | |
| 49 | fn test_chdir_relative(a: std.mem.Allocator) !void { | |
| 50 | var tmp = std.testing.tmpDir(.{}); | |
| 51 | defer tmp.cleanup(); | |
| 57 | fn test_chdir_relative(gpa: Allocator, io: Io) !void { | |
| 58 | var tmp = tmpDir(io, .{}); | |
| 59 | defer tmp.cleanup(io); | |
| 52 | 60 | |
| 53 | 61 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child |
| 54 | try tmp.parent_dir.setAsCwd(); | |
| 62 | try std.process.setCurrentDir(io, tmp.parent_dir); | |
| 55 | 63 | |
| 56 | 64 | // Capture base working directory path, to build expected full path |
| 57 | 65 | var base_cwd_buf: [path_max]u8 = undefined; |
| 58 | 66 | const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); |
| 59 | 67 | |
| 60 | 68 | const relative_dir_name = &tmp.sub_path; |
| 61 | const expected_path = try std.fs.path.resolve(a, &.{ base_cwd, relative_dir_name }); | |
| 62 | defer a.free(expected_path); | |
| 69 | const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, relative_dir_name }); | |
| 70 | defer gpa.free(expected_path); | |
| 63 | 71 | |
| 64 | 72 | // change current working directory to new test directory |
| 65 | 73 | try std.posix.chdir(relative_dir_name); |
| ... | ... | @@ -68,8 +76,46 @@ fn test_chdir_relative(a: std.mem.Allocator) !void { |
| 68 | 76 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); |
| 69 | 77 | |
| 70 | 78 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase |
| 71 | const resolved_cwd = try std.fs.path.resolve(a, &.{new_cwd}); | |
| 72 | defer a.free(resolved_cwd); | |
| 79 | const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd}); | |
| 80 | defer gpa.free(resolved_cwd); | |
| 73 | 81 | |
| 74 | 82 | try std.testing.expectEqualStrings(expected_path, resolved_cwd); |
| 75 | 83 | } |
| 84 | ||
| 85 | pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { | |
| 86 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | |
| 87 | std.crypto.random.bytes(&random_bytes); | |
| 88 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | |
| 89 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); | |
| 90 | ||
| 91 | const cwd = Io.Dir.cwd(); | |
| 92 | var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch | |
| 93 | @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); | |
| 94 | defer cache_dir.close(io); | |
| 95 | const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch | |
| 96 | @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); | |
| 97 | const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch | |
| 98 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); | |
| 99 | ||
| 100 | return .{ | |
| 101 | .dir = dir, | |
| 102 | .parent_dir = parent_dir, | |
| 103 | .sub_path = sub_path, | |
| 104 | }; | |
| 105 | } | |
| 106 | ||
| 107 | pub const TmpDir = struct { | |
| 108 | dir: Io.Dir, | |
| 109 | parent_dir: Io.Dir, | |
| 110 | sub_path: [sub_path_len]u8, | |
| 111 | ||
| 112 | const random_bytes_count = 12; | |
| 113 | const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); | |
| 114 | ||
| 115 | pub fn cleanup(self: *TmpDir, io: Io) void { | |
| 116 | self.dir.close(io); | |
| 117 | self.parent_dir.deleteTree(io, &self.sub_path) catch {}; | |
| 118 | self.parent_dir.close(io); | |
| 119 | self.* = undefined; | |
| 120 | } | |
| 121 | }; |
test/standalone/posix/relpaths.zig+56-35| ... | ... | @@ -14,21 +14,21 @@ pub fn main() !void { |
| 14 | 14 | const gpa = debug_allocator.allocator(); |
| 15 | 15 | defer std.debug.assert(debug_allocator.deinit() == .ok); |
| 16 | 16 | |
| 17 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | |
| 17 | var threaded: std.Io.Threaded = .init(gpa, .{}); | |
| 18 | defer threaded.deinit(); | |
| 19 | const io = threaded.io(); | |
| 18 | 20 | |
| 19 | // TODO this API isn't supposed to be used outside of unit testing. make it compilation error if used | |
| 20 | // outside of unit testing. | |
| 21 | var tmp = std.testing.tmpDir(.{}); | |
| 22 | defer tmp.cleanup(); | |
| 21 | var tmp = tmpDir(io, .{}); | |
| 22 | defer tmp.cleanup(io); | |
| 23 | 23 | |
| 24 | 24 | // Want to test relative paths, so cd into the tmpdir for these tests |
| 25 | try tmp.dir.setAsCwd(); | |
| 25 | try std.process.setCurrentDir(io, tmp.dir); | |
| 26 | 26 | |
| 27 | 27 | try test_symlink(gpa, io, tmp); |
| 28 | 28 | try test_link(io, tmp); |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { | |
| 31 | fn test_symlink(gpa: Allocator, io: Io, tmp: TmpDir) !void { | |
| 32 | 32 | const target_name = "symlink-target"; |
| 33 | 33 | const symlink_name = "symlinker"; |
| 34 | 34 | |
| ... | ... | @@ -47,32 +47,15 @@ fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { |
| 47 | 47 | else => return err, |
| 48 | 48 | }; |
| 49 | 49 | } else { |
| 50 | try std.posix.symlink(target_name, symlink_name); | |
| 50 | try Io.Dir.cwd().symLink(io, target_name, symlink_name, .{}); | |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | var buffer: [std.fs.max_path_bytes]u8 = undefined; |
| 54 | const given = try std.posix.readlink(symlink_name, buffer[0..]); | |
| 54 | const given = buffer[0..try Io.Dir.cwd().readLink(io, symlink_name, &buffer)]; | |
| 55 | 55 | try std.testing.expectEqualStrings(target_name, given); |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t } { | |
| 59 | if (builtin.target.os.tag == .linux) { | |
| 60 | const stx = try std.os.linux.wrapped.statx( | |
| 61 | fd, | |
| 62 | "", | |
| 63 | std.posix.AT.EMPTY_PATH, | |
| 64 | .{ .INO = true, .NLINK = true }, | |
| 65 | ); | |
| 66 | std.debug.assert(stx.mask.INO); | |
| 67 | std.debug.assert(stx.mask.NLINK); | |
| 68 | return .{ stx.ino, stx.nlink }; | |
| 69 | } | |
| 70 | ||
| 71 | const st = try std.posix.fstat(fd); | |
| 72 | return .{ st.ino, st.nlink }; | |
| 73 | } | |
| 74 | ||
| 75 | fn test_link(io: Io, tmp: std.testing.TmpDir) !void { | |
| 58 | fn test_link(io: Io, tmp: TmpDir) !void { | |
| 76 | 59 | switch (builtin.target.os.tag) { |
| 77 | 60 | .linux, .illumos => {}, |
| 78 | 61 | else => return, |
| ... | ... | @@ -84,7 +67,7 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { |
| 84 | 67 | try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); |
| 85 | 68 | |
| 86 | 69 | // Test 1: create the relative link from inside tmp |
| 87 | try std.posix.link(target_name, link_name); | |
| 70 | try Io.Dir.hardLink(.cwd(), target_name, .cwd(), link_name, io, .{}); | |
| 88 | 71 | |
| 89 | 72 | // Verify |
| 90 | 73 | const efd = try tmp.dir.openFile(io, target_name, .{}); |
| ... | ... | @@ -94,16 +77,54 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { |
| 94 | 77 | defer nfd.close(io); |
| 95 | 78 | |
| 96 | 79 | { |
| 97 | const eino, _ = try getLinkInfo(efd.handle); | |
| 98 | const nino, const nlink = try getLinkInfo(nfd.handle); | |
| 99 | try std.testing.expectEqual(eino, nino); | |
| 100 | try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); | |
| 80 | const e_stat = try efd.stat(io); | |
| 81 | const n_stat = try nfd.stat(io); | |
| 82 | try std.testing.expectEqual(e_stat.inode, n_stat.inode); | |
| 83 | try std.testing.expectEqual(2, n_stat.nlink); | |
| 101 | 84 | } |
| 102 | 85 | |
| 103 | 86 | // Test 2: Remove the link and see the stats update |
| 104 | try std.posix.unlink(link_name); | |
| 87 | try Io.Dir.cwd().deleteFile(io, link_name); | |
| 105 | 88 | { |
| 106 | _, const elink = try getLinkInfo(efd.handle); | |
| 107 | try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); | |
| 89 | const e_stat = try efd.stat(io); | |
| 90 | try std.testing.expectEqual(1, e_stat.nlink); | |
| 108 | 91 | } |
| 109 | 92 | } |
| 93 | ||
| 94 | pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { | |
| 95 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | |
| 96 | std.crypto.random.bytes(&random_bytes); | |
| 97 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | |
| 98 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); | |
| 99 | ||
| 100 | const cwd = Io.Dir.cwd(); | |
| 101 | var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch | |
| 102 | @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); | |
| 103 | defer cache_dir.close(io); | |
| 104 | const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch | |
| 105 | @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); | |
| 106 | const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch | |
| 107 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); | |
| 108 | ||
| 109 | return .{ | |
| 110 | .dir = dir, | |
| 111 | .parent_dir = parent_dir, | |
| 112 | .sub_path = sub_path, | |
| 113 | }; | |
| 114 | } | |
| 115 | ||
| 116 | pub const TmpDir = struct { | |
| 117 | dir: Io.Dir, | |
| 118 | parent_dir: Io.Dir, | |
| 119 | sub_path: [sub_path_len]u8, | |
| 120 | ||
| 121 | const random_bytes_count = 12; | |
| 122 | const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); | |
| 123 | ||
| 124 | pub fn cleanup(self: *TmpDir, io: Io) void { | |
| 125 | self.dir.close(io); | |
| 126 | self.parent_dir.deleteTree(io, &self.sub_path) catch {}; | |
| 127 | self.parent_dir.close(io); | |
| 128 | self.* = undefined; | |
| 129 | } | |
| 130 | }; |
test/standalone/windows_bat_args/fuzz.zig+2-2| ... | ... | @@ -44,8 +44,8 @@ pub fn main() anyerror!void { |
| 44 | 44 | var tmp = std.testing.tmpDir(.{}); |
| 45 | 45 | defer tmp.cleanup(); |
| 46 | 46 | |
| 47 | try tmp.dir.setAsCwd(); | |
| 48 | defer tmp.parent_dir.setAsCwd() catch {}; | |
| 47 | try std.process.setCurrentDir(io, tmp.dir); | |
| 48 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; | |
| 49 | 49 | |
| 50 | 50 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. |
| 51 | 51 | const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); |
test/standalone/windows_bat_args/test.zig+2-2| ... | ... | @@ -18,8 +18,8 @@ pub fn main() anyerror!void { |
| 18 | 18 | var tmp = std.testing.tmpDir(.{}); |
| 19 | 19 | defer tmp.cleanup(); |
| 20 | 20 | |
| 21 | try tmp.dir.setAsCwd(); | |
| 22 | defer tmp.parent_dir.setAsCwd() catch {}; | |
| 21 | try std.process.setCurrentDir(io, tmp.dir); | |
| 22 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; | |
| 23 | 23 | |
| 24 | 24 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. |
| 25 | 25 | const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); |
test/standalone/windows_spawn/main.zig+3-3| ... | ... | @@ -127,8 +127,8 @@ pub fn main() anyerror!void { |
| 127 | 127 | try testExecError(error.FileNotFound, gpa, "goodbye"); |
| 128 | 128 | |
| 129 | 129 | // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir |
| 130 | try tmp.dir.setAsCwd(); | |
| 131 | defer tmp.parent_dir.setAsCwd() catch {}; | |
| 130 | try std.process.setCurrentDir(io, tmp.dir); | |
| 131 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; | |
| 132 | 132 | const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); |
| 133 | 133 | defer gpa.free(something_subdir_abs_path); |
| 134 | 134 | |
| ... | ... | @@ -191,7 +191,7 @@ pub fn main() anyerror!void { |
| 191 | 191 | defer subdir_cwd.close(io); |
| 192 | 192 | |
| 193 | 193 | try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); |
| 194 | try subdir_cwd.setAsCwd(); | |
| 194 | try std.process.setCurrentDir(io, subdir_cwd); | |
| 195 | 195 | |
| 196 | 196 | // clear the PATH again |
| 197 | 197 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( |
tools/fetch_them_macos_headers.zig+5-7| ... | ... | @@ -4,7 +4,6 @@ const Dir = std.Io.Dir; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const process = std.process; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | const tmpDir = std.testing.tmpDir; | |
| 8 | 7 | const fatal = std.process.fatal; |
| 9 | 8 | const info = std.log.info; |
| 10 | 9 | |
| ... | ... | @@ -111,15 +110,14 @@ pub fn main() anyerror!void { |
| 111 | 110 | const os_ver: OsVer = @enumFromInt(version.major); |
| 112 | 111 | info("found SDK deployment target macOS {f} aka '{t}'", .{ version, os_ver }); |
| 113 | 112 | |
| 114 | var tmp = tmpDir(.{}); | |
| 115 | defer tmp.cleanup(); | |
| 113 | const tmp_dir: Io.Dir = .cwd(); | |
| 116 | 114 | |
| 117 | 115 | for (&[_]Arch{ .aarch64, .x86_64 }) |arch| { |
| 118 | 116 | const target: Target = .{ |
| 119 | 117 | .arch = arch, |
| 120 | 118 | .os_ver = os_ver, |
| 121 | 119 | }; |
| 122 | try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp); | |
| 120 | try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp_dir); | |
| 123 | 121 | } |
| 124 | 122 | } |
| 125 | 123 | |
| ... | ... | @@ -130,11 +128,11 @@ fn fetchTarget( |
| 130 | 128 | sysroot: []const u8, |
| 131 | 129 | target: Target, |
| 132 | 130 | ver: Version, |
| 133 | tmp: std.testing.TmpDir, | |
| 131 | tmp_dir: Io.Dir, | |
| 134 | 132 | ) !void { |
| 135 | 133 | const tmp_filename = "macos-headers"; |
| 136 | 134 | const headers_list_filename = "macos-headers.o.d"; |
| 137 | const tmp_path = try tmp.dir.realPathFileAlloc(io, ".", arena); | |
| 135 | const tmp_path = try tmp_dir.realPathFileAlloc(io, ".", arena); | |
| 138 | 136 | const tmp_file_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, tmp_filename }); |
| 139 | 137 | const headers_list_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, headers_list_filename }); |
| 140 | 138 | |
| ... | ... | @@ -173,7 +171,7 @@ fn fetchTarget( |
| 173 | 171 | } |
| 174 | 172 | |
| 175 | 173 | // Read in the contents of `macos-headers.o.d` |
| 176 | const headers_list_file = try tmp.dir.openFile(io, headers_list_filename, .{}); | |
| 174 | const headers_list_file = try tmp_dir.openFile(io, headers_list_filename, .{}); | |
| 177 | 175 | defer headers_list_file.close(io); |
| 178 | 176 | |
| 179 | 177 | var headers_dir = Dir.cwd().openDir(io, headers_source_prefix, .{}) catch |err| switch (err) { |