| 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,6 +718,7 @@ pub const VTable = struct { |
| 718 | lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, | 718 | lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, |
| 719 | tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, | 719 | tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, |
| 720 | unlockStderr: *const fn (?*anyopaque) void, | 720 | unlockStderr: *const fn (?*anyopaque) void, |
| 721 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, | ||
| 721 | 722 | ||
| 722 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | 723 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 723 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | 724 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
lib/std/Io/Threaded.zig+71-3| ... | @@ -648,7 +648,7 @@ pub fn init( | ... | @@ -648,7 +648,7 @@ pub fn init( |
| 648 | .main_thread = .{ | 648 | .main_thread = .{ |
| 649 | .signal_id = Thread.currentSignalId(), | 649 | .signal_id = Thread.currentSignalId(), |
| 650 | .current_closure = null, | 650 | .current_closure = null, |
| 651 | .cancel_protection = undefined, | 651 | .cancel_protection = .unblocked, |
| 652 | }, | 652 | }, |
| 653 | .argv0 = options.argv0, | 653 | .argv0 = options.argv0, |
| 654 | .environ = options.environ, | 654 | .environ = options.environ, |
| ... | @@ -689,7 +689,7 @@ pub const init_single_threaded: Threaded = .{ | ... | @@ -689,7 +689,7 @@ pub const init_single_threaded: Threaded = .{ |
| 689 | .main_thread = .{ | 689 | .main_thread = .{ |
| 690 | .signal_id = undefined, | 690 | .signal_id = undefined, |
| 691 | .current_closure = null, | 691 | .current_closure = null, |
| 692 | .cancel_protection = undefined, | 692 | .cancel_protection = .unblocked, |
| 693 | }, | 693 | }, |
| 694 | .robust_cancel = .disabled, | 694 | .robust_cancel = .disabled, |
| 695 | .argv0 = .{}, | 695 | .argv0 = .{}, |
| ... | @@ -742,7 +742,7 @@ fn worker(t: *Threaded) void { | ... | @@ -742,7 +742,7 @@ fn worker(t: *Threaded) void { |
| 742 | var thread: Thread = .{ | 742 | var thread: Thread = .{ |
| 743 | .signal_id = Thread.currentSignalId(), | 743 | .signal_id = Thread.currentSignalId(), |
| 744 | .current_closure = null, | 744 | .current_closure = null, |
| 745 | .cancel_protection = undefined, | 745 | .cancel_protection = .unblocked, |
| 746 | }; | 746 | }; |
| 747 | Thread.current = &thread; | 747 | Thread.current = &thread; |
| 748 | 748 | ||
| ... | @@ -844,6 +844,7 @@ pub fn io(t: *Threaded) Io { | ... | @@ -844,6 +844,7 @@ pub fn io(t: *Threaded) Io { |
| 844 | .lockStderr = lockStderr, | 844 | .lockStderr = lockStderr, |
| 845 | .tryLockStderr = tryLockStderr, | 845 | .tryLockStderr = tryLockStderr, |
| 846 | .unlockStderr = unlockStderr, | 846 | .unlockStderr = unlockStderr, |
| 847 | .processSetCurrentDir = processSetCurrentDir, | ||
| 847 | 848 | ||
| 848 | .now = now, | 849 | .now = now, |
| 849 | .sleep = sleep, | 850 | .sleep = sleep, |
| ... | @@ -979,6 +980,7 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -979,6 +980,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 979 | .lockStderr = lockStderr, | 980 | .lockStderr = lockStderr, |
| 980 | .tryLockStderr = tryLockStderr, | 981 | .tryLockStderr = tryLockStderr, |
| 981 | .unlockStderr = unlockStderr, | 982 | .unlockStderr = unlockStderr, |
| 983 | .processSetCurrentDir = processSetCurrentDir, | ||
| 982 | 984 | ||
| 983 | .now = now, | 985 | .now = now, |
| 984 | .sleep = sleep, | 986 | .sleep = sleep, |
| ... | @@ -7370,6 +7372,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex | ... | @@ -7370,6 +7372,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7370 | }; | 7372 | }; |
| 7371 | defer w.CloseHandle(h_file); | 7373 | defer w.CloseHandle(h_file); |
| 7372 | 7374 | ||
| 7375 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | ||
| 7373 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); | 7376 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); |
| 7374 | 7377 | ||
| 7375 | const len = std.unicode.calcWtf8Len(wide_slice); | 7378 | const len = std.unicode.calcWtf8Len(wide_slice); |
| ... | @@ -10796,6 +10799,71 @@ fn unlockStderr(userdata: ?*anyopaque) void { | ... | @@ -10796,6 +10799,71 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 10796 | std.process.stderr_thread_mutex.unlock(); | 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 | pub const PosixAddress = extern union { | 10867 | pub const PosixAddress = extern union { |
| 10800 | any: posix.sockaddr, | 10868 | any: posix.sockaddr, |
| 10801 | in: posix.sockaddr.in, | 10869 | in: posix.sockaddr.in, |
lib/std/os/windows.zig-34| ... | @@ -2939,40 +2939,6 @@ pub fn WriteFile( | ... | @@ -2939,40 +2939,6 @@ pub fn WriteFile( |
| 2939 | return bytes_written; | 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 | pub const GetCurrentDirectoryError = error{ | 2942 | pub const GetCurrentDirectoryError = error{ |
| 2977 | NameTooLong, | 2943 | NameTooLong, |
| 2978 | Unexpected, | 2944 | Unexpected, |
lib/std/posix.zig+4-17| ... | @@ -1171,11 +1171,9 @@ pub const ChangeCurDirError = error{ | ... | @@ -1171,11 +1171,9 @@ pub const ChangeCurDirError = error{ |
| 1171 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | 1171 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. |
| 1172 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | 1172 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 1173 | if (native_os == .wasi and !builtin.link_libc) { | 1173 | if (native_os == .wasi and !builtin.link_libc) { |
| 1174 | @compileError("WASI does not support os.chdir"); | 1174 | @compileError("unsupported OS"); |
| 1175 | } else if (native_os == .windows) { | 1175 | } else if (native_os == .windows) { |
| 1176 | var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; | 1176 | @compileError("unsupported OS"); |
| 1177 | const len = try windows.wtf8ToWtf16Le(&wtf16_dir_path, dir_path); | ||
| 1178 | return chdirW(wtf16_dir_path[0..len]); | ||
| 1179 | } else { | 1177 | } else { |
| 1180 | const dir_path_c = try toPosixPath(dir_path); | 1178 | const dir_path_c = try toPosixPath(dir_path); |
| 1181 | return chdirZ(&dir_path_c); | 1179 | return chdirZ(&dir_path_c); |
| ... | @@ -1188,12 +1186,9 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | ... | @@ -1188,12 +1186,9 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 1188 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | 1186 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. |
| 1189 | pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { | 1187 | pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { |
| 1190 | if (native_os == .windows) { | 1188 | if (native_os == .windows) { |
| 1191 | const dir_path_span = mem.span(dir_path); | 1189 | @compileError("unsupported OS"); |
| 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]); | ||
| 1195 | } else if (native_os == .wasi and !builtin.link_libc) { | 1190 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 1196 | return chdir(mem.span(dir_path)); | 1191 | @compileError("unsupported OS"); |
| 1197 | } | 1192 | } |
| 1198 | switch (errno(system.chdir(dir_path))) { | 1193 | switch (errno(system.chdir(dir_path))) { |
| 1199 | .SUCCESS => return, | 1194 | .SUCCESS => return, |
| ... | @@ -1210,14 +1205,6 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { | ... | @@ -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 | pub const FchdirError = error{ | 1208 | pub const FchdirError = error{ |
| 1222 | AccessDenied, | 1209 | AccessDenied, |
| 1223 | NotDir, | 1210 | NotDir, |
lib/std/process.zig+25| ... | @@ -2304,3 +2304,28 @@ pub fn exit(status: u8) noreturn { | ... | @@ -2304,3 +2304,28 @@ pub fn exit(status: u8) noreturn { |
| 2304 | else => posix.system.exit(status), | 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,6 +629,7 @@ pub const TmpDir = struct { |
| 629 | }; | 629 | }; |
| 630 | 630 | ||
| 631 | pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { | 631 | pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { |
| 632 | comptime assert(builtin.is_test); | ||
| 632 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | 633 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; |
| 633 | std.crypto.random.bytes(&random_bytes); | 634 | std.crypto.random.bytes(&random_bytes); |
| 634 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | 635 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; |
test/standalone/posix/cwd.zig+59-13| ... | @@ -1,6 +1,10 @@ | ... | @@ -1,6 +1,10 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | 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 | const path_max = std.fs.max_path_bytes; | 8 | const path_max = std.fs.max_path_bytes; |
| 5 | 9 | ||
| 6 | pub fn main() !void { | 10 | pub fn main() !void { |
| ... | @@ -9,13 +13,17 @@ pub fn main() !void { | ... | @@ -9,13 +13,17 @@ pub fn main() !void { |
| 9 | return; | 13 | return; |
| 10 | } | 14 | } |
| 11 | 15 | ||
| 12 | var Allocator = std.heap.DebugAllocator(.{}){}; | 16 | var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; |
| 13 | const a = Allocator.allocator(); | 17 | defer assert(debug_allocator.deinit() == .ok); |
| 14 | defer std.debug.assert(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 | try test_chdir_self(); | 24 | try test_chdir_self(); |
| 17 | try test_chdir_absolute(); | 25 | try test_chdir_absolute(); |
| 18 | try test_chdir_relative(a); | 26 | try test_chdir_relative(gpa, io); |
| 19 | } | 27 | } |
| 20 | 28 | ||
| 21 | // get current working directory and expect it to match given path | 29 | // get current working directory and expect it to match given path |
| ... | @@ -46,20 +54,20 @@ fn test_chdir_absolute() !void { | ... | @@ -46,20 +54,20 @@ fn test_chdir_absolute() !void { |
| 46 | try expect_cwd(parent); | 54 | try expect_cwd(parent); |
| 47 | } | 55 | } |
| 48 | 56 | ||
| 49 | fn test_chdir_relative(a: std.mem.Allocator) !void { | 57 | fn test_chdir_relative(gpa: Allocator, io: Io) !void { |
| 50 | var tmp = std.testing.tmpDir(.{}); | 58 | var tmp = tmpDir(io, .{}); |
| 51 | defer tmp.cleanup(); | 59 | defer tmp.cleanup(io); |
| 52 | 60 | ||
| 53 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child | 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 | // Capture base working directory path, to build expected full path | 64 | // Capture base working directory path, to build expected full path |
| 57 | var base_cwd_buf: [path_max]u8 = undefined; | 65 | var base_cwd_buf: [path_max]u8 = undefined; |
| 58 | const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); | 66 | const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); |
| 59 | 67 | ||
| 60 | const relative_dir_name = &tmp.sub_path; | 68 | const relative_dir_name = &tmp.sub_path; |
| 61 | const expected_path = try std.fs.path.resolve(a, &.{ base_cwd, relative_dir_name }); | 69 | const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, relative_dir_name }); |
| 62 | defer a.free(expected_path); | 70 | defer gpa.free(expected_path); |
| 63 | 71 | ||
| 64 | // change current working directory to new test directory | 72 | // change current working directory to new test directory |
| 65 | try std.posix.chdir(relative_dir_name); | 73 | try std.posix.chdir(relative_dir_name); |
| ... | @@ -68,8 +76,46 @@ fn test_chdir_relative(a: std.mem.Allocator) !void { | ... | @@ -68,8 +76,46 @@ fn test_chdir_relative(a: std.mem.Allocator) !void { |
| 68 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); | 76 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); |
| 69 | 77 | ||
| 70 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase | 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}); | 79 | const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd}); |
| 72 | defer a.free(resolved_cwd); | 80 | defer gpa.free(resolved_cwd); |
| 73 | 81 | ||
| 74 | try std.testing.expectEqualStrings(expected_path, resolved_cwd); | 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,21 +14,21 @@ pub fn main() !void { |
| 14 | const gpa = debug_allocator.allocator(); | 14 | const gpa = debug_allocator.allocator(); |
| 15 | defer std.debug.assert(debug_allocator.deinit() == .ok); | 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 | 21 | var tmp = tmpDir(io, .{}); |
| 20 | // outside of unit testing. | 22 | defer tmp.cleanup(io); |
| 21 | var tmp = std.testing.tmpDir(.{}); | ||
| 22 | defer tmp.cleanup(); | ||
| 23 | 23 | ||
| 24 | // Want to test relative paths, so cd into the tmpdir for these tests | 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 | try test_symlink(gpa, io, tmp); | 27 | try test_symlink(gpa, io, tmp); |
| 28 | try test_link(io, tmp); | 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 | const target_name = "symlink-target"; | 32 | const target_name = "symlink-target"; |
| 33 | const symlink_name = "symlinker"; | 33 | const symlink_name = "symlinker"; |
| 34 | 34 | ||
| ... | @@ -47,32 +47,15 @@ fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { | ... | @@ -47,32 +47,15 @@ fn test_symlink(gpa: Allocator, io: Io, tmp: std.testing.TmpDir) !void { |
| 47 | else => return err, | 47 | else => return err, |
| 48 | }; | 48 | }; |
| 49 | } else { | 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 | var buffer: [std.fs.max_path_bytes]u8 = undefined; | 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 | try std.testing.expectEqualStrings(target_name, given); | 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 } { | 58 | fn test_link(io: Io, tmp: TmpDir) !void { |
| 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 { | ||
| 76 | switch (builtin.target.os.tag) { | 59 | switch (builtin.target.os.tag) { |
| 77 | .linux, .illumos => {}, | 60 | .linux, .illumos => {}, |
| 78 | else => return, | 61 | else => return, |
| ... | @@ -84,7 +67,7 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { | ... | @@ -84,7 +67,7 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { |
| 84 | try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); | 67 | try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); |
| 85 | 68 | ||
| 86 | // Test 1: create the relative link from inside tmp | 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 | // Verify | 72 | // Verify |
| 90 | const efd = try tmp.dir.openFile(io, target_name, .{}); | 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,16 +77,54 @@ fn test_link(io: Io, tmp: std.testing.TmpDir) !void { |
| 94 | defer nfd.close(io); | 77 | defer nfd.close(io); |
| 95 | 78 | ||
| 96 | { | 79 | { |
| 97 | const eino, _ = try getLinkInfo(efd.handle); | 80 | const e_stat = try efd.stat(io); |
| 98 | const nino, const nlink = try getLinkInfo(nfd.handle); | 81 | const n_stat = try nfd.stat(io); |
| 99 | try std.testing.expectEqual(eino, nino); | 82 | try std.testing.expectEqual(e_stat.inode, n_stat.inode); |
| 100 | try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); | 83 | try std.testing.expectEqual(2, n_stat.nlink); |
| 101 | } | 84 | } |
| 102 | 85 | ||
| 103 | // Test 2: Remove the link and see the stats update | 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); | 89 | const e_stat = try efd.stat(io); |
| 107 | try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); | 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,8 +44,8 @@ pub fn main() anyerror!void { |
| 44 | var tmp = std.testing.tmpDir(.{}); | 44 | var tmp = std.testing.tmpDir(.{}); |
| 45 | defer tmp.cleanup(); | 45 | defer tmp.cleanup(); |
| 46 | 46 | ||
| 47 | try tmp.dir.setAsCwd(); | 47 | try std.process.setCurrentDir(io, tmp.dir); |
| 48 | defer tmp.parent_dir.setAsCwd() catch {}; | 48 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; |
| 49 | 49 | ||
| 50 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. | 50 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. |
| 51 | const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); | 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,8 +18,8 @@ pub fn main() anyerror!void { |
| 18 | var tmp = std.testing.tmpDir(.{}); | 18 | var tmp = std.testing.tmpDir(.{}); |
| 19 | defer tmp.cleanup(); | 19 | defer tmp.cleanup(); |
| 20 | 20 | ||
| 21 | try tmp.dir.setAsCwd(); | 21 | try std.process.setCurrentDir(io, tmp.dir); |
| 22 | defer tmp.parent_dir.setAsCwd() catch {}; | 22 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; |
| 23 | 23 | ||
| 24 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. | 24 | // `child_exe_path_orig` might be relative; make it relative to our new cwd. |
| 25 | const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); | 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,8 +127,8 @@ pub fn main() anyerror!void { |
| 127 | try testExecError(error.FileNotFound, gpa, "goodbye"); | 127 | try testExecError(error.FileNotFound, gpa, "goodbye"); |
| 128 | 128 | ||
| 129 | // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir | 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(); | 130 | try std.process.setCurrentDir(io, tmp.dir); |
| 131 | defer tmp.parent_dir.setAsCwd() catch {}; | 131 | defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; |
| 132 | const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); | 132 | const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); |
| 133 | defer gpa.free(something_subdir_abs_path); | 133 | defer gpa.free(something_subdir_abs_path); |
| 134 | 134 | ||
| ... | @@ -191,7 +191,7 @@ pub fn main() anyerror!void { | ... | @@ -191,7 +191,7 @@ pub fn main() anyerror!void { |
| 191 | defer subdir_cwd.close(io); | 191 | defer subdir_cwd.close(io); |
| 192 | 192 | ||
| 193 | try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); | 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 | // clear the PATH again | 196 | // clear the PATH again |
| 197 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | 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,7 +4,6 @@ const Dir = std.Io.Dir; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const process = std.process; | 5 | const process = std.process; |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const tmpDir = std.testing.tmpDir; | ||
| 8 | const fatal = std.process.fatal; | 7 | const fatal = std.process.fatal; |
| 9 | const info = std.log.info; | 8 | const info = std.log.info; |
| 10 | 9 | ||
| ... | @@ -111,15 +110,14 @@ pub fn main() anyerror!void { | ... | @@ -111,15 +110,14 @@ pub fn main() anyerror!void { |
| 111 | const os_ver: OsVer = @enumFromInt(version.major); | 110 | const os_ver: OsVer = @enumFromInt(version.major); |
| 112 | info("found SDK deployment target macOS {f} aka '{t}'", .{ version, os_ver }); | 111 | info("found SDK deployment target macOS {f} aka '{t}'", .{ version, os_ver }); |
| 113 | 112 | ||
| 114 | var tmp = tmpDir(.{}); | 113 | const tmp_dir: Io.Dir = .cwd(); |
| 115 | defer tmp.cleanup(); | ||
| 116 | 114 | ||
| 117 | for (&[_]Arch{ .aarch64, .x86_64 }) |arch| { | 115 | for (&[_]Arch{ .aarch64, .x86_64 }) |arch| { |
| 118 | const target: Target = .{ | 116 | const target: Target = .{ |
| 119 | .arch = arch, | 117 | .arch = arch, |
| 120 | .os_ver = os_ver, | 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,11 +128,11 @@ fn fetchTarget( |
| 130 | sysroot: []const u8, | 128 | sysroot: []const u8, |
| 131 | target: Target, | 129 | target: Target, |
| 132 | ver: Version, | 130 | ver: Version, |
| 133 | tmp: std.testing.TmpDir, | 131 | tmp_dir: Io.Dir, |
| 134 | ) !void { | 132 | ) !void { |
| 135 | const tmp_filename = "macos-headers"; | 133 | const tmp_filename = "macos-headers"; |
| 136 | const headers_list_filename = "macos-headers.o.d"; | 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 | const tmp_file_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, tmp_filename }); | 136 | const tmp_file_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, tmp_filename }); |
| 139 | const headers_list_path = try Dir.path.join(arena, &[_][]const u8{ tmp_path, headers_list_filename }); | 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,7 +171,7 @@ fn fetchTarget( |
| 173 | } | 171 | } |
| 174 | 172 | ||
| 175 | // Read in the contents of `macos-headers.o.d` | 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 | defer headers_list_file.close(io); | 175 | defer headers_list_file.close(io); |
| 178 | 176 | ||
| 179 | var headers_dir = Dir.cwd().openDir(io, headers_source_prefix, .{}) catch |err| switch (err) { | 177 | var headers_dir = Dir.cwd().openDir(io, headers_source_prefix, .{}) catch |err| switch (err) { |