authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-18 19:00:25-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-05-18 19:00:25-04:00
logfe28d732710b916695c6926a06aad0cc93156516
treec3e674556afe22d31536748d6211639333e0b7d5
parenta80ad0782d5e7ce065dd47f21bcb81f69c1d1670
parentcd8daa533ad7e53a3d071dcb782bc14c6fc72711
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5303 from kubkon/dir-fns

Add/fix missing WASI functionality to pass libstd tests

25 files changed, 886 insertions(+), 147 deletions(-)

build.zig+6-4
...@@ -86,6 +86,7 @@ pub fn build(b: *Builder) !void {...@@ -86,6 +86,7 @@ pub fn build(b: *Builder) !void {
8686
87 const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false;87 const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false;
88 const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false;88 const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false;
89 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
89 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");90 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
9091
91 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");92 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
...@@ -115,11 +116,12 @@ pub fn build(b: *Builder) !void {...@@ -115,11 +116,12 @@ pub fn build(b: *Builder) !void {
115 const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");116 const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");
116 fmt_step.dependOn(&fmt_build_zig.step);117 fmt_step.dependOn(&fmt_build_zig.step);
117118
118 test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, glibc_multi_dir));119 // TODO for the moment, skip wasm32-wasi until bugs are sorted out.
120 test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir));
119121
120 test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, glibc_multi_dir));122 test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir));
121123
122 test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, glibc_multi_dir));124 test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir));
123125
124 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));126 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
125 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));127 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
...@@ -130,7 +132,7 @@ pub fn build(b: *Builder) !void {...@@ -130,7 +132,7 @@ pub fn build(b: *Builder) !void {
130 test_step.dependOn(tests.addTranslateCTests(b, test_filter));132 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
131 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));133 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));
132 // tests for this feature are disabled until we have the self-hosted compiler available134 // tests for this feature are disabled until we have the self-hosted compiler available
133 //test_step.dependOn(tests.addGenHTests(b, test_filter));135 // test_step.dependOn(tests.addGenHTests(b, test_filter));
134 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));136 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
135 test_step.dependOn(docs_step);137 test_step.dependOn(docs_step);
136}138}
ci/azure/linux_script+6-1
...@@ -19,6 +19,11 @@ wget https://ziglang.org/deps/$QEMUBASE.tar.xz...@@ -19,6 +19,11 @@ wget https://ziglang.org/deps/$QEMUBASE.tar.xz
19tar xf $QEMUBASE.tar.xz19tar xf $QEMUBASE.tar.xz
20PATH=$PWD/$QEMUBASE/bin:$PATH20PATH=$PWD/$QEMUBASE/bin:$PATH
2121
22WASMTIME="wasmtime-v0.15.0-x86_64-linux"
23wget https://github.com/bytecodealliance/wasmtime/releases/download/v0.15.0/$WASMTIME.tar.xz
24tar xf $WASMTIME.tar.xz
25PATH=$PWD/$WASMTIME:$PATH
26
22# Make the `zig version` number consistent.27# Make the `zig version` number consistent.
23# This will affect the cmake command below.28# This will affect the cmake command below.
24git config core.abbrev 929git config core.abbrev 9
...@@ -45,7 +50,7 @@ mkdir build...@@ -45,7 +50,7 @@ mkdir build
45cd build50cd build
46cmake .. -DCMAKE_BUILD_TYPE=Release51cmake .. -DCMAKE_BUILD_TYPE=Release
47make -j$(nproc) install52make -j$(nproc) install
48./zig build test -Denable-qemu53./zig build test -Denable-qemu -Denable-wasmtime
49VERSION="$(./zig version)"54VERSION="$(./zig version)"
5055
51if [ "${BUILD_REASON}" != "PullRequest" ]; then56if [ "${BUILD_REASON}" != "PullRequest" ]; then
lib/std/build.zig+5-1
...@@ -1061,6 +1061,8 @@ pub const Builder = struct {...@@ -1061,6 +1061,8 @@ pub const Builder = struct {
1061};1061};
10621062
1063test "builder.findProgram compiles" {1063test "builder.findProgram compiles" {
1064 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1065
1064 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);1066 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1065 defer arena.deinit();1067 defer arena.deinit();
10661068
...@@ -1706,7 +1708,7 @@ pub const LibExeObjStep = struct {...@@ -1706,7 +1708,7 @@ pub const LibExeObjStep = struct {
1706 .Enum => |enum_info| {1708 .Enum => |enum_info| {
1707 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;1709 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
1708 inline for (enum_info.fields) |field| {1710 inline for (enum_info.fields) |field| {
1709 out.print(" {},\n", .{ field.name }) catch unreachable;1711 out.print(" {},\n", .{field.name}) catch unreachable;
1710 }1712 }
1711 out.print("}};\n", .{}) catch unreachable;1713 out.print("}};\n", .{}) catch unreachable;
1712 },1714 },
...@@ -2089,6 +2091,8 @@ pub const LibExeObjStep = struct {...@@ -2089,6 +2091,8 @@ pub const LibExeObjStep = struct {
2089 .wasmtime => |bin_name| if (self.enable_wasmtime) {2091 .wasmtime => |bin_name| if (self.enable_wasmtime) {
2090 try zig_args.append("--test-cmd");2092 try zig_args.append("--test-cmd");
2091 try zig_args.append(bin_name);2093 try zig_args.append(bin_name);
2094 try zig_args.append("--test-cmd");
2095 try zig_args.append("--dir=.");
2092 try zig_args.append("--test-cmd-bin");2096 try zig_args.append("--test-cmd-bin");
2093 },2097 },
2094 }2098 }
lib/std/fmt.zig+5-2
...@@ -1684,12 +1684,15 @@ test "vector" {...@@ -1684,12 +1684,15 @@ test "vector" {
1684 // https://github.com/ziglang/zig/issues/44861684 // https://github.com/ziglang/zig/issues/4486
1685 return error.SkipZigTest;1685 return error.SkipZigTest;
1686 }1686 }
1687 if (builtin.arch != .wasm32) {
1688 // TODO investigate why this fails on wasm32
1689 const vbool: std.meta.Vector(4, bool) = [_]bool{ true, false, true, false };
1690 try testFmt("{ true, false, true, false }", "{}", .{vbool});
1691 }
16871692
1688 const vbool: std.meta.Vector(4, bool) = [_]bool{ true, false, true, false };
1689 const vi64: std.meta.Vector(4, i64) = [_]i64{ -2, -1, 0, 1 };1693 const vi64: std.meta.Vector(4, i64) = [_]i64{ -2, -1, 0, 1 };
1690 const vu64: std.meta.Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 };1694 const vu64: std.meta.Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 };
16911695
1692 try testFmt("{ true, false, true, false }", "{}", .{vbool});
1693 try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});1696 try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
1694 try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64});1697 try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64});
1695 try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});1698 try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
lib/std/fs.zig+151-36
...@@ -44,6 +44,8 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {...@@ -44,6 +44,8 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
44 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.44 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
45 // +1 for the null byte at the end, which can be encoded in 1 byte.45 // +1 for the null byte at the end, which can be encoded in 1 byte.
46 .windows => os.windows.PATH_MAX_WIDE * 3 + 1,46 .windows => os.windows.PATH_MAX_WIDE * 3 + 1,
47 // TODO work out what a reasonable value we should use here
48 .wasi => 4096,
47 else => @compileError("Unsupported OS"),49 else => @compileError("Unsupported OS"),
48};50};
4951
...@@ -155,7 +157,7 @@ pub const AtomicFile = struct {...@@ -155,7 +157,7 @@ pub const AtomicFile = struct {
155 try crypto.randomBytes(rand_buf[0..]);157 try crypto.randomBytes(rand_buf[0..]);
156 base64_encoder.encode(&tmp_path_buf, &rand_buf);158 base64_encoder.encode(&tmp_path_buf, &rand_buf);
157159
158 const file = dir.createFileZ(160 const file = dir.createFile(
159 &tmp_path_buf,161 &tmp_path_buf,
160 .{ .mode = mode, .exclusive = true },162 .{ .mode = mode, .exclusive = true },
161 ) catch |err| switch (err) {163 ) catch |err| switch (err) {
...@@ -182,7 +184,7 @@ pub const AtomicFile = struct {...@@ -182,7 +184,7 @@ pub const AtomicFile = struct {
182 self.file_open = false;184 self.file_open = false;
183 }185 }
184 if (self.file_exists) {186 if (self.file_exists) {
185 self.dir.deleteFileZ(&self.tmp_path_buf) catch {};187 self.dir.deleteFile(&self.tmp_path_buf) catch {};
186 self.file_exists = false;188 self.file_exists = false;
187 }189 }
188 if (self.close_dir_on_deinit) {190 if (self.close_dir_on_deinit) {
...@@ -197,16 +199,8 @@ pub const AtomicFile = struct {...@@ -197,16 +199,8 @@ pub const AtomicFile = struct {
197 self.file.close();199 self.file.close();
198 self.file_open = false;200 self.file_open = false;
199 }201 }
200 if (std.Target.current.os.tag == .windows) {202 try os.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);
201 const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_basename);203 self.file_exists = false;
202 const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf);
203 try os.renameatW(self.dir.fd, tmp_path_w.span(), self.dir.fd, dest_path_w.span(), os.windows.TRUE);
204 self.file_exists = false;
205 } else {
206 const dest_path_c = try os.toPosixPath(self.dest_basename);
207 try os.renameatZ(self.dir.fd, &self.tmp_path_buf, self.dir.fd, &dest_path_c);
208 self.file_exists = false;
209 }
210 }204 }
211};205};
212206
...@@ -522,6 +516,66 @@ pub const Dir = struct {...@@ -522,6 +516,66 @@ pub const Dir = struct {
522 }516 }
523 }517 }
524 },518 },
519 .wasi => struct {
520 dir: Dir,
521 buf: [8192]u8, // TODO align(@alignOf(os.wasi.dirent_t)),
522 cookie: u64,
523 index: usize,
524 end_index: usize,
525
526 const Self = @This();
527
528 pub const Error = IteratorError;
529
530 /// Memory such as file names referenced in this returned entry becomes invalid
531 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
532 pub fn next(self: *Self) Error!?Entry {
533 const w = os.wasi;
534 start_over: while (true) {
535 if (self.index >= self.end_index) {
536 var bufused: usize = undefined;
537 switch (w.fd_readdir(self.dir.fd, &self.buf, self.buf.len, self.cookie, &bufused)) {
538 w.ESUCCESS => {},
539 w.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
540 w.EFAULT => unreachable,
541 w.ENOTDIR => unreachable,
542 w.EINVAL => unreachable,
543 else => |err| return os.unexpectedErrno(err),
544 }
545 if (bufused == 0) return null;
546 self.index = 0;
547 self.end_index = bufused;
548 }
549 const entry = @ptrCast(*align(1) os.wasi.dirent_t, &self.buf[self.index]);
550 const entry_size = @sizeOf(os.wasi.dirent_t);
551 const name_index = self.index + entry_size;
552 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);
553
554 const next_index = name_index + entry.d_namlen;
555 self.index = next_index;
556 self.cookie = entry.d_next;
557
558 // skip . and .. entries
559 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
560 continue :start_over;
561 }
562
563 const entry_kind = switch (entry.d_type) {
564 wasi.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice,
565 wasi.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice,
566 wasi.FILETYPE_DIRECTORY => Entry.Kind.Directory,
567 wasi.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink,
568 wasi.FILETYPE_REGULAR_FILE => Entry.Kind.File,
569 wasi.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,
570 else => Entry.Kind.Unknown,
571 };
572 return Entry{
573 .name = name,
574 .kind = entry_kind,
575 };
576 }
577 }
578 },
525 else => @compileError("unimplemented"),579 else => @compileError("unimplemented"),
526 };580 };
527581
...@@ -548,6 +602,13 @@ pub const Dir = struct {...@@ -548,6 +602,13 @@ pub const Dir = struct {
548 .buf = undefined,602 .buf = undefined,
549 .name_data = undefined,603 .name_data = undefined,
550 },604 },
605 .wasi => return Iterator{
606 .dir = self,
607 .cookie = os.wasi.DIRCOOKIE_START,
608 .index = 0,
609 .end_index = 0,
610 .buf = undefined,
611 },
551 else => @compileError("unimplemented"),612 else => @compileError("unimplemented"),
552 }613 }
553 }614 }
...@@ -595,24 +656,25 @@ pub const Dir = struct {...@@ -595,24 +656,25 @@ pub const Dir = struct {
595 /// Save as `openFile` but WASI only.656 /// Save as `openFile` but WASI only.
596 pub fn openFileWasi(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {657 pub fn openFileWasi(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
597 const w = os.wasi;658 const w = os.wasi;
598 var fdflags: w.fdflag_t = 0x0;659 var fdflags: w.fdflags_t = 0x0;
599 var rights: w.rights_t = 0x0;660 var base: w.rights_t = 0x0;
600 if (flags.read) {661 if (flags.read) {
601 rights |= w.FD_READ | w.FD_TELL | w.FD_FILESTAT_GET;662 base |= w.RIGHT_FD_READ | w.RIGHT_FD_TELL | w.RIGHT_FD_SEEK | w.RIGHT_FD_FILESTAT_GET;
602 }663 }
603 if (flags.write) {664 if (flags.write) {
604 fdflags |= w.FDFLAG_APPEND;665 fdflags |= w.FDFLAG_APPEND;
605 rights |= w.FD_WRITE |666 base |= w.RIGHT_FD_WRITE |
606 w.FD_DATASYNC |667 w.RIGHT_FD_TELL |
607 w.FD_SEEK |668 w.RIGHT_FD_SEEK |
608 w.FD_FDSTAT_SET_FLAGS |669 w.RIGHT_FD_DATASYNC |
609 w.FD_SYNC |670 w.RIGHT_FD_FDSTAT_SET_FLAGS |
610 w.FD_ALLOCATE |671 w.RIGHT_FD_SYNC |
611 w.FD_ADVISE |672 w.RIGHT_FD_ALLOCATE |
612 w.FD_FILESTAT_SET_TIMES |673 w.RIGHT_FD_ADVISE |
613 w.FD_FILESTAT_SET_SIZE;674 w.RIGHT_FD_FILESTAT_SET_TIMES |
675 w.RIGHT_FD_FILESTAT_SET_SIZE;
614 }676 }
615 const fd = try wasi.openat(self.fd, sub_path, 0x0, fdflags, rights);677 const fd = try os.openatWasi(self.fd, sub_path, 0x0, fdflags, base, 0x0);
616 return File{ .handle = fd };678 return File{ .handle = fd };
617 }679 }
618680
...@@ -712,17 +774,19 @@ pub const Dir = struct {...@@ -712,17 +774,19 @@ pub const Dir = struct {
712 pub fn createFileWasi(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {774 pub fn createFileWasi(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
713 const w = os.wasi;775 const w = os.wasi;
714 var oflags = w.O_CREAT;776 var oflags = w.O_CREAT;
715 var rights = w.RIGHT_FD_WRITE |777 var base: w.rights_t = w.RIGHT_FD_WRITE |
716 w.RIGHT_FD_DATASYNC |778 w.RIGHT_FD_DATASYNC |
717 w.RIGHT_FD_SEEK |779 w.RIGHT_FD_SEEK |
780 w.RIGHT_FD_TELL |
718 w.RIGHT_FD_FDSTAT_SET_FLAGS |781 w.RIGHT_FD_FDSTAT_SET_FLAGS |
719 w.RIGHT_FD_SYNC |782 w.RIGHT_FD_SYNC |
720 w.RIGHT_FD_ALLOCATE |783 w.RIGHT_FD_ALLOCATE |
721 w.RIGHT_FD_ADVISE |784 w.RIGHT_FD_ADVISE |
722 w.RIGHT_FD_FILESTAT_SET_TIMES |785 w.RIGHT_FD_FILESTAT_SET_TIMES |
723 w.RIGHT_FD_FILESTAT_SET_SIZE;786 w.RIGHT_FD_FILESTAT_SET_SIZE |
787 w.RIGHT_FD_FILESTAT_GET;
724 if (flags.read) {788 if (flags.read) {
725 rights |= w.RIGHT_FD_READ | w.RIGHT_FD_TELL | w.RIGHT_FD_FILESTAT_GET;789 base |= w.RIGHT_FD_READ;
726 }790 }
727 if (flags.truncate) {791 if (flags.truncate) {
728 oflags |= w.O_TRUNC;792 oflags |= w.O_TRUNC;
...@@ -730,7 +794,7 @@ pub const Dir = struct {...@@ -730,7 +794,7 @@ pub const Dir = struct {
730 if (flags.exclusive) {794 if (flags.exclusive) {
731 oflags |= w.O_EXCL;795 oflags |= w.O_EXCL;
732 }796 }
733 const fd = try wasi.openat(self.fd, sub_path, oflags, 0x0, rights);797 const fd = try os.openatWasi(self.fd, sub_path, oflags, 0x0, base, 0x0);
734 return File{ .handle = fd };798 return File{ .handle = fd };
735 }799 }
736800
...@@ -877,6 +941,9 @@ pub const Dir = struct {...@@ -877,6 +941,9 @@ pub const Dir = struct {
877 /// Not all targets support this. For example, WASI does not have the concept941 /// Not all targets support this. For example, WASI does not have the concept
878 /// of a current working directory.942 /// of a current working directory.
879 pub fn setAsCwd(self: Dir) !void {943 pub fn setAsCwd(self: Dir) !void {
944 if (builtin.os.tag == .wasi) {
945 @compileError("changing cwd is not currently possible in WASI");
946 }
880 try os.fchdir(self.fd);947 try os.fchdir(self.fd);
881 }948 }
882949
...@@ -899,6 +966,8 @@ pub const Dir = struct {...@@ -899,6 +966,8 @@ pub const Dir = struct {
899 if (builtin.os.tag == .windows) {966 if (builtin.os.tag == .windows) {
900 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);967 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
901 return self.openDirW(sub_path_w.span().ptr, args);968 return self.openDirW(sub_path_w.span().ptr, args);
969 } else if (builtin.os.tag == .wasi) {
970 return self.openDirWasi(sub_path, args);
902 } else {971 } else {
903 const sub_path_c = try os.toPosixPath(sub_path);972 const sub_path_c = try os.toPosixPath(sub_path);
904 return self.openDirZ(&sub_path_c, args);973 return self.openDirZ(&sub_path_c, args);
...@@ -907,6 +976,44 @@ pub const Dir = struct {...@@ -907,6 +976,44 @@ pub const Dir = struct {
907976
908 pub const openDirC = @compileError("deprecated: renamed to openDirZ");977 pub const openDirC = @compileError("deprecated: renamed to openDirZ");
909978
979 /// Same as `openDir` except only WASI.
980 pub fn openDirWasi(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
981 const w = os.wasi;
982 var base: w.rights_t = w.RIGHT_FD_FILESTAT_GET | w.RIGHT_FD_FDSTAT_SET_FLAGS | w.RIGHT_FD_FILESTAT_SET_TIMES;
983 if (args.iterate) {
984 base |= w.RIGHT_FD_READDIR;
985 }
986 if (args.access_sub_paths) {
987 base |= w.RIGHT_PATH_CREATE_DIRECTORY |
988 w.RIGHT_PATH_CREATE_FILE |
989 w.RIGHT_PATH_LINK_SOURCE |
990 w.RIGHT_PATH_LINK_TARGET |
991 w.RIGHT_PATH_OPEN |
992 w.RIGHT_PATH_READLINK |
993 w.RIGHT_PATH_RENAME_SOURCE |
994 w.RIGHT_PATH_RENAME_TARGET |
995 w.RIGHT_PATH_FILESTAT_GET |
996 w.RIGHT_PATH_FILESTAT_SET_SIZE |
997 w.RIGHT_PATH_FILESTAT_SET_TIMES |
998 w.RIGHT_PATH_SYMLINK |
999 w.RIGHT_PATH_REMOVE_DIRECTORY |
1000 w.RIGHT_PATH_UNLINK_FILE;
1001 }
1002 // TODO do we really need all the rights here?
1003 const inheriting: w.rights_t = w.RIGHT_ALL ^ w.RIGHT_SOCK_SHUTDOWN;
1004
1005 const result = os.openatWasi(self.fd, sub_path, w.O_DIRECTORY, 0x0, base, inheriting);
1006 const fd = result catch |err| switch (err) {
1007 error.FileTooBig => unreachable, // can't happen for directories
1008 error.IsDir => unreachable, // we're providing O_DIRECTORY
1009 error.NoSpaceLeft => unreachable, // not providing O_CREAT
1010 error.PathAlreadyExists => unreachable, // not providing O_CREAT
1011 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1012 else => |e| return e,
1013 };
1014 return Dir{ .fd = fd };
1015 }
1016
910 /// Same as `openDir` except the parameter is null-terminated.1017 /// Same as `openDir` except the parameter is null-terminated.
911 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {1018 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
912 if (builtin.os.tag == .windows) {1019 if (builtin.os.tag == .windows) {
...@@ -1054,9 +1161,15 @@ pub const Dir = struct {...@@ -1054,9 +1161,15 @@ pub const Dir = struct {
1054 if (builtin.os.tag == .windows) {1161 if (builtin.os.tag == .windows) {
1055 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);1162 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1056 return self.deleteDirW(sub_path_w.span().ptr);1163 return self.deleteDirW(sub_path_w.span().ptr);
1164 } else if (builtin.os.tag == .wasi) {
1165 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
1166 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
1167 else => |e| return e,
1168 };
1169 } else {
1170 const sub_path_c = try os.toPosixPath(sub_path);
1171 return self.deleteDirZ(&sub_path_c);
1057 }1172 }
1058 const sub_path_c = try os.toPosixPath(sub_path);
1059 return self.deleteDirZ(&sub_path_c);
1060 }1173 }
10611174
1062 /// Same as `deleteDir` except the parameter is null-terminated.1175 /// Same as `deleteDir` except the parameter is null-terminated.
...@@ -1448,7 +1561,7 @@ pub fn cwd() Dir {...@@ -1448,7 +1561,7 @@ pub fn cwd() Dir {
1448 if (builtin.os.tag == .windows) {1561 if (builtin.os.tag == .windows) {
1449 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };1562 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
1450 } else if (builtin.os.tag == .wasi) {1563 } else if (builtin.os.tag == .wasi) {
1451 @compileError("WASI doesn't have a concept of cwd; use TODO instead");1564 @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead");
1452 } else {1565 } else {
1453 return Dir{ .fd = os.AT_FDCWD };1566 return Dir{ .fd = os.AT_FDCWD };
1454 }1567 }
...@@ -1754,10 +1867,12 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {...@@ -1754,10 +1867,12 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1754}1867}
17551868
1756test "" {1869test "" {
1757 _ = makeDirAbsolute;1870 if (builtin.os.tag != .wasi) {
1758 _ = makeDirAbsoluteZ;1871 _ = makeDirAbsolute;
1759 _ = copyFileAbsolute;1872 _ = makeDirAbsoluteZ;
1760 _ = updateFileAbsolute;1873 _ = copyFileAbsolute;
1874 _ = updateFileAbsolute;
1875 }
1761 _ = Dir.copyFile;1876 _ = Dir.copyFile;
1762 _ = @import("fs/test.zig");1877 _ = @import("fs/test.zig");
1763 _ = @import("fs/path.zig");1878 _ = @import("fs/path.zig");
lib/std/fs/file.zig+7
...@@ -30,6 +30,7 @@ pub const File = struct {...@@ -30,6 +30,7 @@ pub const File = struct {
3030
31 pub const default_mode = switch (builtin.os.tag) {31 pub const default_mode = switch (builtin.os.tag) {
32 .windows => 0,32 .windows => 0,
33 .wasi => 0,
33 else => 0o666,34 else => 0o666,
34 };35 };
3536
...@@ -140,6 +141,12 @@ pub const File = struct {...@@ -140,6 +141,12 @@ pub const File = struct {
140 if (builtin.os.tag == .windows) {141 if (builtin.os.tag == .windows) {
141 return os.isCygwinPty(self.handle);142 return os.isCygwinPty(self.handle);
142 }143 }
144 if (builtin.os.tag == .wasi) {
145 // WASI sanitizes stdout when fd is a tty so ANSI escape codes
146 // will not be interpreted as actual cursor commands, and
147 // stderr is always sanitized.
148 return false;
149 }
143 if (self.isTty()) {150 if (self.isTty()) {
144 if (self.handle == os.STDOUT_FILENO or self.handle == os.STDERR_FILENO) {151 if (self.handle == os.STDOUT_FILENO or self.handle == os.STDERR_FILENO) {
145 // Use getenvC to workaround https://github.com/ziglang/zig/issues/3511152 // Use getenvC to workaround https://github.com/ziglang/zig/issues/3511
lib/std/fs/get_app_data_dir.zig+2
...@@ -56,6 +56,8 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD...@@ -56,6 +56,8 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
56}56}
5757
58test "getAppDataDir" {58test "getAppDataDir" {
59 if (builtin.os.tag == .wasi) return error.SkipZigTest;
60
59 // We can't actually validate the result61 // We can't actually validate the result
60 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;62 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;
61 defer std.testing.allocator.free(dir);63 defer std.testing.allocator.free(dir);
lib/std/fs/path.zig+9-2
...@@ -653,6 +653,8 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -653,6 +653,8 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
653}653}
654654
655test "resolve" {655test "resolve" {
656 if (builtin.os.tag == .wasi) return error.SkipZigTest;
657
656 const cwd = try process.getCwdAlloc(testing.allocator);658 const cwd = try process.getCwdAlloc(testing.allocator);
657 defer testing.allocator.free(cwd);659 defer testing.allocator.free(cwd);
658 if (builtin.os.tag == .windows) {660 if (builtin.os.tag == .windows) {
...@@ -667,10 +669,11 @@ test "resolve" {...@@ -667,10 +669,11 @@ test "resolve" {
667}669}
668670
669test "resolveWindows" {671test "resolveWindows" {
670 if (@import("builtin").arch == .aarch64) {672 if (builtin.arch == .aarch64) {
671 // TODO https://github.com/ziglang/zig/issues/3288673 // TODO https://github.com/ziglang/zig/issues/3288
672 return error.SkipZigTest;674 return error.SkipZigTest;
673 }675 }
676 if (builtin.os.tag == .wasi) return error.SkipZigTest;
674 if (builtin.os.tag == .windows) {677 if (builtin.os.tag == .windows) {
675 const cwd = try process.getCwdAlloc(testing.allocator);678 const cwd = try process.getCwdAlloc(testing.allocator);
676 defer testing.allocator.free(cwd);679 defer testing.allocator.free(cwd);
...@@ -715,6 +718,8 @@ test "resolveWindows" {...@@ -715,6 +718,8 @@ test "resolveWindows" {
715}718}
716719
717test "resolvePosix" {720test "resolvePosix" {
721 if (builtin.os.tag == .wasi) return error.SkipZigTest;
722
718 try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");723 try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
719 try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");724 try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
720 try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a");725 try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a");
...@@ -1116,10 +1121,12 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![...@@ -1116,10 +1121,12 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
1116}1121}
11171122
1118test "relative" {1123test "relative" {
1119 if (@import("builtin").arch == .aarch64) {1124 if (builtin.arch == .aarch64) {
1120 // TODO https://github.com/ziglang/zig/issues/32881125 // TODO https://github.com/ziglang/zig/issues/3288
1121 return error.SkipZigTest;1126 return error.SkipZigTest;
1122 }1127 }
1128 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1129
1123 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");1130 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
1124 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");1131 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
1125 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");1132 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
lib/std/fs/test.zig+6-1
...@@ -4,6 +4,8 @@ const fs = std.fs;...@@ -4,6 +4,8 @@ const fs = std.fs;
4const File = std.fs.File;4const File = std.fs.File;
55
6test "openSelfExe" {6test "openSelfExe" {
7 if (builtin.os.tag == .wasi) return error.SkipZigTest;
8
7 const self_exe_file = try std.fs.openSelfExe();9 const self_exe_file = try std.fs.openSelfExe();
8 self_exe_file.close();10 self_exe_file.close();
9}11}
...@@ -11,6 +13,8 @@ test "openSelfExe" {...@@ -11,6 +13,8 @@ test "openSelfExe" {
11const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;13const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
1214
13test "open file with exclusive nonblocking lock twice" {15test "open file with exclusive nonblocking lock twice" {
16 if (builtin.os.tag == .wasi) return error.SkipZigTest;
17
14 const dir = fs.cwd();18 const dir = fs.cwd();
15 const filename = "file_nonblocking_lock_test.txt";19 const filename = "file_nonblocking_lock_test.txt";
1620
...@@ -35,7 +39,6 @@ test "open file with lock twice, make sure it wasn't open at the same time" {...@@ -35,7 +39,6 @@ test "open file with lock twice, make sure it wasn't open at the same time" {
35 }39 }
3640
37 const filename = "file_lock_test.txt";41 const filename = "file_lock_test.txt";
38
39 var contexts = [_]FileLockTestContext{42 var contexts = [_]FileLockTestContext{
40 .{ .filename = filename, .create = true, .lock = .Exclusive },43 .{ .filename = filename, .create = true, .lock = .Exclusive },
41 .{ .filename = filename, .create = true, .lock = .Exclusive },44 .{ .filename = filename, .create = true, .lock = .Exclusive },
...@@ -111,6 +114,8 @@ test "create file, lock and read from multiple process at once" {...@@ -111,6 +114,8 @@ test "create file, lock and read from multiple process at once" {
111}114}
112115
113test "open file with exclusive nonblocking lock twice (absolute paths)" {116test "open file with exclusive nonblocking lock twice (absolute paths)" {
117 if (builtin.os.tag == .wasi) return error.SkipZigTest;
118
114 const allocator = std.testing.allocator;119 const allocator = std.testing.allocator;
115120
116 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};121 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
lib/std/fs/wasi.zig+2-13
...@@ -117,7 +117,7 @@ pub const PreopenList = struct {...@@ -117,7 +117,7 @@ pub const PreopenList = struct {
117 /// TODO make the function more generic by searching by `PreopenType` union. This will117 /// TODO make the function more generic by searching by `PreopenType` union. This will
118 /// be needed in the future when WASI extends its capabilities to resources118 /// be needed in the future when WASI extends its capabilities to resources
119 /// other than preopened directories.119 /// other than preopened directories.
120 pub fn find(self: *const Self, path: []const u8) ?*const Preopen {120 pub fn find(self: Self, path: []const u8) ?*const Preopen {
121 for (self.buffer.items) |preopen| {121 for (self.buffer.items) |preopen| {
122 switch (preopen.@"type") {122 switch (preopen.@"type") {
123 PreopenType.Dir => |preopen_path| {123 PreopenType.Dir => |preopen_path| {
...@@ -129,7 +129,7 @@ pub const PreopenList = struct {...@@ -129,7 +129,7 @@ pub const PreopenList = struct {
129 }129 }
130130
131 /// Return the inner buffer as read-only slice.131 /// Return the inner buffer as read-only slice.
132 pub fn asSlice(self: *const Self) []const Preopen {132 pub fn asSlice(self: Self) []const Preopen {
133 return self.buffer.items;133 return self.buffer.items;
134 }134 }
135135
...@@ -138,14 +138,3 @@ pub const PreopenList = struct {...@@ -138,14 +138,3 @@ pub const PreopenList = struct {
138 return self.buffer.toOwnedSlice();138 return self.buffer.toOwnedSlice();
139 }139 }
140};140};
141
142/// Convenience wrapper for `std.os.wasi.path_open` syscall.
143pub fn openat(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags: fdflags_t, rights: rights_t) os.OpenError!fd_t {
144 var fd: fd_t = undefined;
145 switch (path_open(dir_fd, 0x0, file_path.ptr, file_path.len, oflags, rights, 0x0, fdflags, &fd)) {
146 0 => {},
147 // TODO map errors
148 else => |err| return std.os.unexpectedErrno(err),
149 }
150 return fd;
151}
lib/std/io/test.zig+29-14
...@@ -11,15 +11,18 @@ const mem = std.mem;...@@ -11,15 +11,18 @@ const mem = std.mem;
11const fs = std.fs;11const fs = std.fs;
12const File = std.fs.File;12const File = std.fs.File;
1313
14const tmpDir = std.testing.tmpDir;
15
14test "write a file, read it, then delete it" {16test "write a file, read it, then delete it" {
15 const cwd = fs.cwd();17 var tmp = tmpDir(.{});
18 defer tmp.cleanup();
1619
17 var data: [1024]u8 = undefined;20 var data: [1024]u8 = undefined;
18 var prng = DefaultPrng.init(1234);21 var prng = DefaultPrng.init(1234);
19 prng.random.bytes(data[0..]);22 prng.random.bytes(data[0..]);
20 const tmp_file_name = "temp_test_file.txt";23 const tmp_file_name = "temp_test_file.txt";
21 {24 {
22 var file = try cwd.createFile(tmp_file_name, .{});25 var file = try tmp.dir.createFile(tmp_file_name, .{});
23 defer file.close();26 defer file.close();
2427
25 var buf_stream = io.bufferedOutStream(file.outStream());28 var buf_stream = io.bufferedOutStream(file.outStream());
...@@ -32,7 +35,7 @@ test "write a file, read it, then delete it" {...@@ -32,7 +35,7 @@ test "write a file, read it, then delete it" {
3235
33 {36 {
34 // Make sure the exclusive flag is honored.37 // Make sure the exclusive flag is honored.
35 if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| {38 if (tmp.dir.createFile(tmp_file_name, .{ .exclusive = true })) |file| {
36 unreachable;39 unreachable;
37 } else |err| {40 } else |err| {
38 std.debug.assert(err == File.OpenError.PathAlreadyExists);41 std.debug.assert(err == File.OpenError.PathAlreadyExists);
...@@ -40,7 +43,7 @@ test "write a file, read it, then delete it" {...@@ -40,7 +43,7 @@ test "write a file, read it, then delete it" {
40 }43 }
4144
42 {45 {
43 var file = try cwd.openFile(tmp_file_name, .{});46 var file = try tmp.dir.openFile(tmp_file_name, .{});
44 defer file.close();47 defer file.close();
4548
46 const file_size = try file.getEndPos();49 const file_size = try file.getEndPos();
...@@ -56,13 +59,16 @@ test "write a file, read it, then delete it" {...@@ -56,13 +59,16 @@ test "write a file, read it, then delete it" {
56 expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));59 expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));
57 expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));60 expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
58 }61 }
59 try cwd.deleteFile(tmp_file_name);62 try tmp.dir.deleteFile(tmp_file_name);
60}63}
6164
62test "BitStreams with File Stream" {65test "BitStreams with File Stream" {
66 var tmp = tmpDir(.{});
67 defer tmp.cleanup();
68
63 const tmp_file_name = "temp_test_file.txt";69 const tmp_file_name = "temp_test_file.txt";
64 {70 {
65 var file = try fs.cwd().createFile(tmp_file_name, .{});71 var file = try tmp.dir.createFile(tmp_file_name, .{});
66 defer file.close();72 defer file.close();
6773
68 var bit_stream = io.bitOutStream(builtin.endian, file.outStream());74 var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
...@@ -76,7 +82,7 @@ test "BitStreams with File Stream" {...@@ -76,7 +82,7 @@ test "BitStreams with File Stream" {
76 try bit_stream.flushBits();82 try bit_stream.flushBits();
77 }83 }
78 {84 {
79 var file = try fs.cwd().openFile(tmp_file_name, .{});85 var file = try tmp.dir.openFile(tmp_file_name, .{});
80 defer file.close();86 defer file.close();
8187
82 var bit_stream = io.bitInStream(builtin.endian, file.inStream());88 var bit_stream = io.bitInStream(builtin.endian, file.inStream());
...@@ -98,15 +104,18 @@ test "BitStreams with File Stream" {...@@ -98,15 +104,18 @@ test "BitStreams with File Stream" {
98104
99 expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));105 expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));
100 }106 }
101 try fs.cwd().deleteFile(tmp_file_name);107 try tmp.dir.deleteFile(tmp_file_name);
102}108}
103109
104test "File seek ops" {110test "File seek ops" {
111 var tmp = tmpDir(.{});
112 defer tmp.cleanup();
113
105 const tmp_file_name = "temp_test_file.txt";114 const tmp_file_name = "temp_test_file.txt";
106 var file = try fs.cwd().createFile(tmp_file_name, .{});115 var file = try tmp.dir.createFile(tmp_file_name, .{});
107 defer {116 defer {
108 file.close();117 file.close();
109 fs.cwd().deleteFile(tmp_file_name) catch {};118 tmp.dir.deleteFile(tmp_file_name) catch {};
110 }119 }
111120
112 try file.writeAll(&([_]u8{0x55} ** 8192));121 try file.writeAll(&([_]u8{0x55} ** 8192));
...@@ -129,11 +138,14 @@ test "setEndPos" {...@@ -129,11 +138,14 @@ test "setEndPos" {
129 // https://github.com/ziglang/zig/issues/5127138 // https://github.com/ziglang/zig/issues/5127
130 if (std.Target.current.cpu.arch == .mips) return error.SkipZigTest;139 if (std.Target.current.cpu.arch == .mips) return error.SkipZigTest;
131140
141 var tmp = tmpDir(.{});
142 defer tmp.cleanup();
143
132 const tmp_file_name = "temp_test_file.txt";144 const tmp_file_name = "temp_test_file.txt";
133 var file = try fs.cwd().createFile(tmp_file_name, .{});145 var file = try tmp.dir.createFile(tmp_file_name, .{});
134 defer {146 defer {
135 file.close();147 file.close();
136 fs.cwd().deleteFile(tmp_file_name) catch {};148 tmp.dir.deleteFile(tmp_file_name) catch {};
137 }149 }
138150
139 // Verify that the file size changes and the file offset is not moved151 // Verify that the file size changes and the file offset is not moved
...@@ -152,11 +164,14 @@ test "setEndPos" {...@@ -152,11 +164,14 @@ test "setEndPos" {
152}164}
153165
154test "updateTimes" {166test "updateTimes" {
167 var tmp = tmpDir(.{});
168 defer tmp.cleanup();
169
155 const tmp_file_name = "just_a_temporary_file.txt";170 const tmp_file_name = "just_a_temporary_file.txt";
156 var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true });171 var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true });
157 defer {172 defer {
158 file.close();173 file.close();
159 std.fs.cwd().deleteFile(tmp_file_name) catch {};174 tmp.dir.deleteFile(tmp_file_name) catch {};
160 }175 }
161 var stat_old = try file.stat();176 var stat_old = try file.stat();
162 // Set atime and mtime to 5s before177 // Set atime and mtime to 5s before
lib/std/net/test.zig+10-1
...@@ -1,9 +1,12 @@...@@ -1,9 +1,12 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = std.builtin;
2const net = std.net;3const net = std.net;
3const mem = std.mem;4const mem = std.mem;
4const testing = std.testing;5const testing = std.testing;
56
6test "parse and render IPv6 addresses" {7test "parse and render IPv6 addresses" {
8 if (builtin.os.tag == .wasi) return error.SkipZigTest;
9
7 var buffer: [100]u8 = undefined;10 var buffer: [100]u8 = undefined;
8 const ips = [_][]const u8{11 const ips = [_][]const u8{
9 "FF01:0:0:0:0:0:0:FB",12 "FF01:0:0:0:0:0:0:FB",
...@@ -42,6 +45,8 @@ test "parse and render IPv6 addresses" {...@@ -42,6 +45,8 @@ test "parse and render IPv6 addresses" {
42}45}
4346
44test "parse and render IPv4 addresses" {47test "parse and render IPv4 addresses" {
48 if (builtin.os.tag == .wasi) return error.SkipZigTest;
49
45 var buffer: [18]u8 = undefined;50 var buffer: [18]u8 = undefined;
46 for ([_][]const u8{51 for ([_][]const u8{
47 "0.0.0.0",52 "0.0.0.0",
...@@ -63,7 +68,7 @@ test "parse and render IPv4 addresses" {...@@ -63,7 +68,7 @@ test "parse and render IPv4 addresses" {
63}68}
6469
65test "resolve DNS" {70test "resolve DNS" {
66 if (std.builtin.os.tag == .windows) {71 if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {
67 // DNS resolution not implemented on Windows yet.72 // DNS resolution not implemented on Windows yet.
68 return error.SkipZigTest;73 return error.SkipZigTest;
69 }74 }
...@@ -101,6 +106,8 @@ test "listen on a port, send bytes, receive bytes" {...@@ -101,6 +106,8 @@ test "listen on a port, send bytes, receive bytes" {
101}106}
102107
103fn testClient(addr: net.Address) anyerror!void {108fn testClient(addr: net.Address) anyerror!void {
109 if (builtin.os.tag == .wasi) return error.SkipZigTest;
110
104 const socket_file = try net.tcpConnectToAddress(addr);111 const socket_file = try net.tcpConnectToAddress(addr);
105 defer socket_file.close();112 defer socket_file.close();
106113
...@@ -111,6 +118,8 @@ fn testClient(addr: net.Address) anyerror!void {...@@ -111,6 +118,8 @@ fn testClient(addr: net.Address) anyerror!void {
111}118}
112119
113fn testServer(server: *net.StreamServer) anyerror!void {120fn testServer(server: *net.StreamServer) anyerror!void {
121 if (builtin.os.tag == .wasi) return error.SkipZigTest;
122
114 var client = try server.accept();123 var client = try server.accept();
115124
116 const stream = client.file.outStream();125 const stream = client.file.outStream();
lib/std/os.zig+376-8
...@@ -98,6 +98,7 @@ pub fn close(fd: fd_t) void {...@@ -98,6 +98,7 @@ pub fn close(fd: fd_t) void {
98 }98 }
99 if (builtin.os.tag == .wasi) {99 if (builtin.os.tag == .wasi) {
100 _ = wasi.fd_close(fd);100 _ = wasi.fd_close(fd);
101 return;
101 }102 }
102 if (comptime std.Target.current.isDarwin()) {103 if (comptime std.Target.current.isDarwin()) {
103 // This avoids the EINTR problem.104 // This avoids the EINTR problem.
...@@ -312,7 +313,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -312,7 +313,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
312 if (builtin.os.tag == .windows) {313 if (builtin.os.tag == .windows) {
313 return windows.ReadFile(fd, buf, null, std.io.default_mode);314 return windows.ReadFile(fd, buf, null, std.io.default_mode);
314 }315 }
315
316 if (builtin.os.tag == .wasi and !builtin.link_libc) {316 if (builtin.os.tag == .wasi and !builtin.link_libc) {
317 const iovs = [1]iovec{iovec{317 const iovs = [1]iovec{iovec{
318 .iov_base = buf.ptr,318 .iov_base = buf.ptr,
...@@ -321,7 +321,18 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -321,7 +321,18 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
321321
322 var nread: usize = undefined;322 var nread: usize = undefined;
323 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {323 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
324 0 => return nread,324 wasi.ESUCCESS => return nread,
325 wasi.EINTR => unreachable,
326 wasi.EINVAL => unreachable,
327 wasi.EFAULT => unreachable,
328 wasi.EAGAIN => unreachable,
329 wasi.EBADF => unreachable, // Always a race condition.
330 wasi.EIO => return error.InputOutput,
331 wasi.EISDIR => return error.IsDir,
332 wasi.ENOBUFS => return error.SystemResources,
333 wasi.ENOMEM => return error.SystemResources,
334 wasi.ECONNRESET => return error.ConnectionResetByPeer,
335 wasi.ETIMEDOUT => return error.ConnectionTimedOut,
325 else => |err| return unexpectedErrno(err),336 else => |err| return unexpectedErrno(err),
326 }337 }
327 }338 }
...@@ -376,6 +387,22 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {...@@ -376,6 +387,22 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
376 const first = iov[0];387 const first = iov[0];
377 return read(fd, first.iov_base[0..first.iov_len]);388 return read(fd, first.iov_base[0..first.iov_len]);
378 }389 }
390 if (builtin.os.tag == .wasi) {
391 var nread: usize = undefined;
392 switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
393 wasi.ESUCCESS => return nread,
394 wasi.EINTR => unreachable,
395 wasi.EINVAL => unreachable,
396 wasi.EFAULT => unreachable,
397 wasi.EAGAIN => unreachable, // currently not support in WASI
398 wasi.EBADF => unreachable, // always a race condition
399 wasi.EIO => return error.InputOutput,
400 wasi.EISDIR => return error.IsDir,
401 wasi.ENOBUFS => return error.SystemResources,
402 wasi.ENOMEM => return error.SystemResources,
403 else => |err| return unexpectedErrno(err),
404 }
405 }
379406
380 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);407 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
381 while (true) {408 while (true) {
...@@ -416,6 +443,31 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {...@@ -416,6 +443,31 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
416 if (builtin.os.tag == .windows) {443 if (builtin.os.tag == .windows) {
417 return windows.ReadFile(fd, buf, offset, std.io.default_mode);444 return windows.ReadFile(fd, buf, offset, std.io.default_mode);
418 }445 }
446 if (builtin.os.tag == .wasi) {
447 const iovs = [1]iovec{iovec{
448 .iov_base = buf.ptr,
449 .iov_len = buf.len,
450 }};
451
452 var nread: usize = undefined;
453 switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
454 wasi.ESUCCESS => return nread,
455 wasi.EINTR => unreachable,
456 wasi.EINVAL => unreachable,
457 wasi.EFAULT => unreachable,
458 wasi.EAGAIN => unreachable,
459 wasi.EBADF => unreachable, // Always a race condition.
460 wasi.EIO => return error.InputOutput,
461 wasi.EISDIR => return error.IsDir,
462 wasi.ENOBUFS => return error.SystemResources,
463 wasi.ENOMEM => return error.SystemResources,
464 wasi.ECONNRESET => return error.ConnectionResetByPeer,
465 wasi.ENXIO => return error.Unseekable,
466 wasi.ESPIPE => return error.Unseekable,
467 wasi.EOVERFLOW => return error.Unseekable,
468 else => |err| return unexpectedErrno(err),
469 }
470 }
419471
420 while (true) {472 while (true) {
421 const rc = system.pread(fd, buf.ptr, buf.len, offset);473 const rc = system.pread(fd, buf.ptr, buf.len, offset);
...@@ -442,7 +494,6 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {...@@ -442,7 +494,6 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
442 else => |err| return unexpectedErrno(err),494 else => |err| return unexpectedErrno(err),
443 }495 }
444 }496 }
445 return index;
446}497}
447498
448pub const TruncateError = error{499pub const TruncateError = error{
...@@ -474,6 +525,19 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -474,6 +525,19 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
474 else => return windows.unexpectedStatus(rc),525 else => return windows.unexpectedStatus(rc),
475 }526 }
476 }527 }
528 if (std.Target.current.os.tag == .wasi) {
529 switch (wasi.fd_filestat_set_size(fd, length)) {
530 wasi.ESUCCESS => return,
531 wasi.EINTR => unreachable,
532 wasi.EFBIG => return error.FileTooBig,
533 wasi.EIO => return error.InputOutput,
534 wasi.EPERM => return error.CannotTruncate,
535 wasi.ETXTBSY => return error.FileBusy,
536 wasi.EBADF => unreachable, // Handle not open for writing
537 wasi.EINVAL => unreachable, // Handle not open for writing
538 else => |err| return unexpectedErrno(err),
539 }
540 }
477541
478 while (true) {542 while (true) {
479 const rc = if (builtin.link_libc)543 const rc = if (builtin.link_libc)
...@@ -523,6 +587,25 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {...@@ -523,6 +587,25 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
523 const first = iov[0];587 const first = iov[0];
524 return pread(fd, first.iov_base[0..first.iov_len], offset);588 return pread(fd, first.iov_base[0..first.iov_len], offset);
525 }589 }
590 if (builtin.os.tag == .wasi) {
591 var nread: usize = undefined;
592 switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
593 wasi.ESUCCESS => return nread,
594 wasi.EINTR => unreachable,
595 wasi.EINVAL => unreachable,
596 wasi.EFAULT => unreachable,
597 wasi.EAGAIN => unreachable,
598 wasi.EBADF => unreachable, // always a race condition
599 wasi.EIO => return error.InputOutput,
600 wasi.EISDIR => return error.IsDir,
601 wasi.ENOBUFS => return error.SystemResources,
602 wasi.ENOMEM => return error.SystemResources,
603 wasi.ENXIO => return error.Unseekable,
604 wasi.ESPIPE => return error.Unseekable,
605 wasi.EOVERFLOW => return error.Unseekable,
606 else => |err| return unexpectedErrno(err),
607 }
608 }
526609
527 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);610 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
528611
...@@ -594,13 +677,25 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -594,13 +677,25 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
594 }677 }
595678
596 if (builtin.os.tag == .wasi and !builtin.link_libc) {679 if (builtin.os.tag == .wasi and !builtin.link_libc) {
597 const ciovs = [1]iovec_const{iovec_const{680 const ciovs = [_]iovec_const{iovec_const{
598 .iov_base = bytes.ptr,681 .iov_base = bytes.ptr,
599 .iov_len = bytes.len,682 .iov_len = bytes.len,
600 }};683 }};
601 var nwritten: usize = undefined;684 var nwritten: usize = undefined;
602 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {685 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
603 0 => return nwritten,686 wasi.ESUCCESS => return nwritten,
687 wasi.EINTR => unreachable,
688 wasi.EINVAL => unreachable,
689 wasi.EFAULT => unreachable,
690 wasi.EAGAIN => unreachable,
691 wasi.EBADF => unreachable, // Always a race condition.
692 wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
693 wasi.EDQUOT => return error.DiskQuota,
694 wasi.EFBIG => return error.FileTooBig,
695 wasi.EIO => return error.InputOutput,
696 wasi.ENOSPC => return error.NoSpaceLeft,
697 wasi.EPERM => return error.AccessDenied,
698 wasi.EPIPE => return error.BrokenPipe,
604 else => |err| return unexpectedErrno(err),699 else => |err| return unexpectedErrno(err),
605 }700 }
606 }701 }
...@@ -662,6 +757,25 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {...@@ -662,6 +757,25 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
662 const first = iov[0];757 const first = iov[0];
663 return write(fd, first.iov_base[0..first.iov_len]);758 return write(fd, first.iov_base[0..first.iov_len]);
664 }759 }
760 if (builtin.os.tag == .wasi) {
761 var nwritten: usize = undefined;
762 switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
763 wasi.ESUCCESS => return nwritten,
764 wasi.EINTR => unreachable,
765 wasi.EINVAL => unreachable,
766 wasi.EFAULT => unreachable,
767 wasi.EAGAIN => unreachable,
768 wasi.EBADF => unreachable, // Always a race condition.
769 wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
770 wasi.EDQUOT => return error.DiskQuota,
771 wasi.EFBIG => return error.FileTooBig,
772 wasi.EIO => return error.InputOutput,
773 wasi.ENOSPC => return error.NoSpaceLeft,
774 wasi.EPERM => return error.AccessDenied,
775 wasi.EPIPE => return error.BrokenPipe,
776 else => |err| return unexpectedErrno(err),
777 }
778 }
665779
666 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);780 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
667 while (true) {781 while (true) {
...@@ -717,6 +831,33 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -717,6 +831,33 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
717 if (std.Target.current.os.tag == .windows) {831 if (std.Target.current.os.tag == .windows) {
718 return windows.WriteFile(fd, bytes, offset, std.io.default_mode);832 return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
719 }833 }
834 if (builtin.os.tag == .wasi) {
835 const ciovs = [1]iovec_const{iovec_const{
836 .iov_base = bytes.ptr,
837 .iov_len = bytes.len,
838 }};
839
840 var nwritten: usize = undefined;
841 switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
842 wasi.ESUCCESS => return nwritten,
843 wasi.EINTR => unreachable,
844 wasi.EINVAL => unreachable,
845 wasi.EFAULT => unreachable,
846 wasi.EAGAIN => unreachable,
847 wasi.EBADF => unreachable, // Always a race condition.
848 wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
849 wasi.EDQUOT => return error.DiskQuota,
850 wasi.EFBIG => return error.FileTooBig,
851 wasi.EIO => return error.InputOutput,
852 wasi.ENOSPC => return error.NoSpaceLeft,
853 wasi.EPERM => return error.AccessDenied,
854 wasi.EPIPE => return error.BrokenPipe,
855 wasi.ENXIO => return error.Unseekable,
856 wasi.ESPIPE => return error.Unseekable,
857 wasi.EOVERFLOW => return error.Unseekable,
858 else => |err| return unexpectedErrno(err),
859 }
860 }
720861
721 // Prevent EINVAL.862 // Prevent EINVAL.
722 const max_count = switch (std.Target.current.os.tag) {863 const max_count = switch (std.Target.current.os.tag) {
...@@ -788,6 +929,28 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -788,6 +929,28 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
788 const first = iov[0];929 const first = iov[0];
789 return pwrite(fd, first.iov_base[0..first.iov_len], offset);930 return pwrite(fd, first.iov_base[0..first.iov_len], offset);
790 }931 }
932 if (builtin.os.tag == .wasi) {
933 var nwritten: usize = undefined;
934 switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
935 wasi.ESUCCESS => return nwritten,
936 wasi.EINTR => unreachable,
937 wasi.EINVAL => unreachable,
938 wasi.EFAULT => unreachable,
939 wasi.EAGAIN => unreachable,
940 wasi.EBADF => unreachable, // Always a race condition.
941 wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
942 wasi.EDQUOT => return error.DiskQuota,
943 wasi.EFBIG => return error.FileTooBig,
944 wasi.EIO => return error.InputOutput,
945 wasi.ENOSPC => return error.NoSpaceLeft,
946 wasi.EPERM => return error.AccessDenied,
947 wasi.EPIPE => return error.BrokenPipe,
948 wasi.ENXIO => return error.Unseekable,
949 wasi.ESPIPE => return error.Unseekable,
950 wasi.EOVERFLOW => return error.Unseekable,
951 else => |err| return unexpectedErrno(err),
952 }
953 }
791954
792 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);955 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
793 while (true) {956 while (true) {
...@@ -923,6 +1086,37 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope...@@ -923,6 +1086,37 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope
923 return openatZ(dir_fd, &file_path_c, flags, mode);1086 return openatZ(dir_fd, &file_path_c, flags, mode);
924}1087}
9251088
1089/// Open and possibly create a file in WASI.
1090pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t {
1091 while (true) {
1092 var fd: fd_t = undefined;
1093 switch (wasi.path_open(dir_fd, 0x0, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
1094 wasi.ESUCCESS => return fd,
1095 wasi.EINTR => continue,
1096
1097 wasi.EFAULT => unreachable,
1098 wasi.EINVAL => unreachable,
1099 wasi.EACCES => return error.AccessDenied,
1100 wasi.EFBIG => return error.FileTooBig,
1101 wasi.EOVERFLOW => return error.FileTooBig,
1102 wasi.EISDIR => return error.IsDir,
1103 wasi.ELOOP => return error.SymLinkLoop,
1104 wasi.EMFILE => return error.ProcessFdQuotaExceeded,
1105 wasi.ENAMETOOLONG => return error.NameTooLong,
1106 wasi.ENFILE => return error.SystemFdQuotaExceeded,
1107 wasi.ENODEV => return error.NoDevice,
1108 wasi.ENOENT => return error.FileNotFound,
1109 wasi.ENOMEM => return error.SystemResources,
1110 wasi.ENOSPC => return error.NoSpaceLeft,
1111 wasi.ENOTDIR => return error.NotDir,
1112 wasi.EPERM => return error.AccessDenied,
1113 wasi.EEXIST => return error.PathAlreadyExists,
1114 wasi.EBUSY => return error.DeviceBusy,
1115 else => |err| return unexpectedErrno(err),
1116 }
1117 }
1118}
1119
926pub const openatC = @compileError("deprecated: renamed to openatZ");1120pub const openatC = @compileError("deprecated: renamed to openatZ");
9271121
928/// Open and possibly create a file. Keeps trying if it gets interrupted.1122/// Open and possibly create a file. Keeps trying if it gets interrupted.
...@@ -1282,6 +1476,9 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -1282,6 +1476,9 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1282 if (builtin.os.tag == .windows) {1476 if (builtin.os.tag == .windows) {
1283 return windows.GetCurrentDirectory(out_buffer);1477 return windows.GetCurrentDirectory(out_buffer);
1284 }1478 }
1479 if (builtin.os.tag == .wasi) {
1480 @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead");
1481 }
12851482
1286 const err = if (builtin.link_libc) blk: {1483 const err = if (builtin.link_libc) blk: {
1287 break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;1484 break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
...@@ -1460,13 +1657,45 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo...@@ -1460,13 +1657,45 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
1460 if (builtin.os.tag == .windows) {1657 if (builtin.os.tag == .windows) {
1461 const file_path_w = try windows.sliceToPrefixedFileW(file_path);1658 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1462 return unlinkatW(dirfd, file_path_w.span().ptr, flags);1659 return unlinkatW(dirfd, file_path_w.span().ptr, flags);
1660 } else if (builtin.os.tag == .wasi) {
1661 return unlinkatWasi(dirfd, file_path, flags);
1662 } else {
1663 const file_path_c = try toPosixPath(file_path);
1664 return unlinkatZ(dirfd, &file_path_c, flags);
1463 }1665 }
1464 const file_path_c = try toPosixPath(file_path);
1465 return unlinkatZ(dirfd, &file_path_c, flags);
1466}1666}
14671667
1468pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");1668pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");
14691669
1670pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1671 const remove_dir = (flags & AT_REMOVEDIR) != 0;
1672 const res = if (remove_dir)
1673 wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
1674 else
1675 wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
1676 switch (res) {
1677 wasi.ESUCCESS => return,
1678 wasi.EACCES => return error.AccessDenied,
1679 wasi.EPERM => return error.AccessDenied,
1680 wasi.EBUSY => return error.FileBusy,
1681 wasi.EFAULT => unreachable,
1682 wasi.EIO => return error.FileSystem,
1683 wasi.EISDIR => return error.IsDir,
1684 wasi.ELOOP => return error.SymLinkLoop,
1685 wasi.ENAMETOOLONG => return error.NameTooLong,
1686 wasi.ENOENT => return error.FileNotFound,
1687 wasi.ENOTDIR => return error.NotDir,
1688 wasi.ENOMEM => return error.SystemResources,
1689 wasi.EROFS => return error.ReadOnlyFileSystem,
1690 wasi.ENOTEMPTY => return error.DirNotEmpty,
1691
1692 wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component
1693 wasi.EBADF => unreachable, // always a race condition
1694
1695 else => |err| return unexpectedErrno(err),
1696 }
1697}
1698
1470/// Same as `unlinkat` but `file_path` is a null-terminated string.1699/// Same as `unlinkat` but `file_path` is a null-terminated string.
1471pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {1700pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
1472 if (builtin.os.tag == .windows) {1701 if (builtin.os.tag == .windows) {
...@@ -1645,6 +1874,8 @@ pub fn renameat(...@@ -1645,6 +1874,8 @@ pub fn renameat(
1645 const old_path_w = try windows.sliceToPrefixedFileW(old_path);1874 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
1646 const new_path_w = try windows.sliceToPrefixedFileW(new_path);1875 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
1647 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);1876 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
1877 } else if (builtin.os.tag == .wasi) {
1878 return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path);
1648 } else {1879 } else {
1649 const old_path_c = try toPosixPath(old_path);1880 const old_path_c = try toPosixPath(old_path);
1650 const new_path_c = try toPosixPath(new_path);1881 const new_path_c = try toPosixPath(new_path);
...@@ -1652,6 +1883,32 @@ pub fn renameat(...@@ -1652,6 +1883,32 @@ pub fn renameat(
1652 }1883 }
1653}1884}
16541885
1886/// Same as `renameat` expect only WASI.
1887pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
1888 switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
1889 wasi.ESUCCESS => return,
1890 wasi.EACCES => return error.AccessDenied,
1891 wasi.EPERM => return error.AccessDenied,
1892 wasi.EBUSY => return error.FileBusy,
1893 wasi.EDQUOT => return error.DiskQuota,
1894 wasi.EFAULT => unreachable,
1895 wasi.EINVAL => unreachable,
1896 wasi.EISDIR => return error.IsDir,
1897 wasi.ELOOP => return error.SymLinkLoop,
1898 wasi.EMLINK => return error.LinkQuotaExceeded,
1899 wasi.ENAMETOOLONG => return error.NameTooLong,
1900 wasi.ENOENT => return error.FileNotFound,
1901 wasi.ENOTDIR => return error.NotDir,
1902 wasi.ENOMEM => return error.SystemResources,
1903 wasi.ENOSPC => return error.NoSpaceLeft,
1904 wasi.EEXIST => return error.PathAlreadyExists,
1905 wasi.ENOTEMPTY => return error.PathAlreadyExists,
1906 wasi.EROFS => return error.ReadOnlyFileSystem,
1907 wasi.EXDEV => return error.RenameAcrossMountPoints,
1908 else => |err| return unexpectedErrno(err),
1909 }
1910}
1911
1655/// Same as `renameat` except the parameters are null-terminated byte arrays.1912/// Same as `renameat` except the parameters are null-terminated byte arrays.
1656pub fn renameatZ(1913pub fn renameatZ(
1657 old_dir_fd: fd_t,1914 old_dir_fd: fd_t,
...@@ -1767,6 +2024,8 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v...@@ -1767,6 +2024,8 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v
1767 if (builtin.os.tag == .windows) {2024 if (builtin.os.tag == .windows) {
1768 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);2025 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
1769 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);2026 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
2027 } else if (builtin.os.tag == .wasi) {
2028 return mkdiratWasi(dir_fd, sub_dir_path, mode);
1770 } else {2029 } else {
1771 const sub_dir_path_c = try toPosixPath(sub_dir_path);2030 const sub_dir_path_c = try toPosixPath(sub_dir_path);
1772 return mkdiratZ(dir_fd, &sub_dir_path_c, mode);2031 return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
...@@ -1775,6 +2034,27 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v...@@ -1775,6 +2034,27 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v
17752034
1776pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ");2035pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ");
17772036
2037pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2038 switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
2039 wasi.ESUCCESS => return,
2040 wasi.EACCES => return error.AccessDenied,
2041 wasi.EBADF => unreachable,
2042 wasi.EPERM => return error.AccessDenied,
2043 wasi.EDQUOT => return error.DiskQuota,
2044 wasi.EEXIST => return error.PathAlreadyExists,
2045 wasi.EFAULT => unreachable,
2046 wasi.ELOOP => return error.SymLinkLoop,
2047 wasi.EMLINK => return error.LinkQuotaExceeded,
2048 wasi.ENAMETOOLONG => return error.NameTooLong,
2049 wasi.ENOENT => return error.FileNotFound,
2050 wasi.ENOMEM => return error.SystemResources,
2051 wasi.ENOSPC => return error.NoSpaceLeft,
2052 wasi.ENOTDIR => return error.NotDir,
2053 wasi.EROFS => return error.ReadOnlyFileSystem,
2054 else => |err| return unexpectedErrno(err),
2055 }
2056}
2057
1778pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {2058pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
1779 if (builtin.os.tag == .windows) {2059 if (builtin.os.tag == .windows) {
1780 const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);2060 const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
...@@ -2623,8 +2903,19 @@ pub const FStatError = error{...@@ -2623,8 +2903,19 @@ pub const FStatError = error{
2623} || UnexpectedError;2903} || UnexpectedError;
26242904
2625pub fn fstat(fd: fd_t) FStatError!Stat {2905pub fn fstat(fd: fd_t) FStatError!Stat {
2626 var stat: Stat = undefined;2906 if (builtin.os.tag == .wasi) {
2907 var stat: wasi.filestat_t = undefined;
2908 switch (wasi.fd_filestat_get(fd, &stat)) {
2909 wasi.ESUCCESS => return Stat.fromFilestat(stat),
2910 wasi.EINVAL => unreachable,
2911 wasi.EBADF => unreachable, // Always a race condition.
2912 wasi.ENOMEM => return error.SystemResources,
2913 wasi.EACCES => return error.AccessDenied,
2914 else => |err| return unexpectedErrno(err),
2915 }
2916 }
26272917
2918 var stat: Stat = undefined;
2628 switch (errno(system.fstat(fd, &stat))) {2919 switch (errno(system.fstat(fd, &stat))) {
2629 0 => return stat,2920 0 => return stat,
2630 EINVAL => unreachable,2921 EINVAL => unreachable,
...@@ -3097,6 +3388,10 @@ pub fn sysctl(...@@ -3097,6 +3388,10 @@ pub fn sysctl(
3097 newp: ?*c_void,3388 newp: ?*c_void,
3098 newlen: usize,3389 newlen: usize,
3099) SysCtlError!void {3390) SysCtlError!void {
3391 if (builtin.os.tag == .wasi) {
3392 @panic("unsupported");
3393 }
3394
3100 const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong;3395 const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong;
3101 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {3396 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
3102 0 => return,3397 0 => return,
...@@ -3117,6 +3412,10 @@ pub fn sysctlbynameZ(...@@ -3117,6 +3412,10 @@ pub fn sysctlbynameZ(
3117 newp: ?*c_void,3412 newp: ?*c_void,
3118 newlen: usize,3413 newlen: usize,
3119) SysCtlError!void {3414) SysCtlError!void {
3415 if (builtin.os.tag == .wasi) {
3416 @panic("unsupported");
3417 }
3418
3120 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {3419 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
3121 0 => return,3420 0 => return,
3122 EFAULT => unreachable,3421 EFAULT => unreachable,
...@@ -3154,6 +3453,18 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {...@@ -3154,6 +3453,18 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
3154 if (builtin.os.tag == .windows) {3453 if (builtin.os.tag == .windows) {
3155 return windows.SetFilePointerEx_BEGIN(fd, offset);3454 return windows.SetFilePointerEx_BEGIN(fd, offset);
3156 }3455 }
3456 if (builtin.os.tag == .wasi) {
3457 var new_offset: wasi.filesize_t = undefined;
3458 switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) {
3459 wasi.ESUCCESS => return,
3460 wasi.EBADF => unreachable, // always a race condition
3461 wasi.EINVAL => return error.Unseekable,
3462 wasi.EOVERFLOW => return error.Unseekable,
3463 wasi.ESPIPE => return error.Unseekable,
3464 wasi.ENXIO => return error.Unseekable,
3465 else => |err| return unexpectedErrno(err),
3466 }
3467 }
3157 const ipos = @bitCast(i64, offset); // the OS treats this as unsigned3468 const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
3158 switch (errno(system.lseek(fd, ipos, SEEK_SET))) {3469 switch (errno(system.lseek(fd, ipos, SEEK_SET))) {
3159 0 => return,3470 0 => return,
...@@ -3183,6 +3494,18 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {...@@ -3183,6 +3494,18 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
3183 if (builtin.os.tag == .windows) {3494 if (builtin.os.tag == .windows) {
3184 return windows.SetFilePointerEx_CURRENT(fd, offset);3495 return windows.SetFilePointerEx_CURRENT(fd, offset);
3185 }3496 }
3497 if (builtin.os.tag == .wasi) {
3498 var new_offset: wasi.filesize_t = undefined;
3499 switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) {
3500 wasi.ESUCCESS => return,
3501 wasi.EBADF => unreachable, // always a race condition
3502 wasi.EINVAL => return error.Unseekable,
3503 wasi.EOVERFLOW => return error.Unseekable,
3504 wasi.ESPIPE => return error.Unseekable,
3505 wasi.ENXIO => return error.Unseekable,
3506 else => |err| return unexpectedErrno(err),
3507 }
3508 }
3186 switch (errno(system.lseek(fd, offset, SEEK_CUR))) {3509 switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
3187 0 => return,3510 0 => return,
3188 EBADF => unreachable, // always a race condition3511 EBADF => unreachable, // always a race condition
...@@ -3211,6 +3534,18 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {...@@ -3211,6 +3534,18 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
3211 if (builtin.os.tag == .windows) {3534 if (builtin.os.tag == .windows) {
3212 return windows.SetFilePointerEx_END(fd, offset);3535 return windows.SetFilePointerEx_END(fd, offset);
3213 }3536 }
3537 if (builtin.os.tag == .wasi) {
3538 var new_offset: wasi.filesize_t = undefined;
3539 switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) {
3540 wasi.ESUCCESS => return,
3541 wasi.EBADF => unreachable, // always a race condition
3542 wasi.EINVAL => return error.Unseekable,
3543 wasi.EOVERFLOW => return error.Unseekable,
3544 wasi.ESPIPE => return error.Unseekable,
3545 wasi.ENXIO => return error.Unseekable,
3546 else => |err| return unexpectedErrno(err),
3547 }
3548 }
3214 switch (errno(system.lseek(fd, offset, SEEK_END))) {3549 switch (errno(system.lseek(fd, offset, SEEK_END))) {
3215 0 => return,3550 0 => return,
3216 EBADF => unreachable, // always a race condition3551 EBADF => unreachable, // always a race condition
...@@ -3239,6 +3574,18 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {...@@ -3239,6 +3574,18 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
3239 if (builtin.os.tag == .windows) {3574 if (builtin.os.tag == .windows) {
3240 return windows.SetFilePointerEx_CURRENT_get(fd);3575 return windows.SetFilePointerEx_CURRENT_get(fd);
3241 }3576 }
3577 if (builtin.os.tag == .wasi) {
3578 var new_offset: wasi.filesize_t = undefined;
3579 switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) {
3580 wasi.ESUCCESS => return new_offset,
3581 wasi.EBADF => unreachable, // always a race condition
3582 wasi.EINVAL => return error.Unseekable,
3583 wasi.EOVERFLOW => return error.Unseekable,
3584 wasi.ESPIPE => return error.Unseekable,
3585 wasi.ENXIO => return error.Unseekable,
3586 else => |err| return unexpectedErrno(err),
3587 }
3588 }
3242 const rc = system.lseek(fd, 0, SEEK_CUR);3589 const rc = system.lseek(fd, 0, SEEK_CUR);
3243 switch (errno(rc)) {3590 switch (errno(rc)) {
3244 0 => return @bitCast(u64, rc),3591 0 => return @bitCast(u64, rc),
...@@ -3365,6 +3712,9 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE...@@ -3365,6 +3712,9 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
3365 const pathname_w = try windows.sliceToPrefixedFileW(pathname);3712 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
3366 return realpathW(pathname_w.span().ptr, out_buffer);3713 return realpathW(pathname_w.span().ptr, out_buffer);
3367 }3714 }
3715 if (builtin.os.tag == .wasi) {
3716 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
3717 }
3368 const pathname_c = try toPosixPath(pathname);3718 const pathname_c = try toPosixPath(pathname);
3369 return realpathZ(&pathname_c, out_buffer);3719 return realpathZ(&pathname_c, out_buffer);
3370}3720}
...@@ -3679,6 +4029,24 @@ pub const FutimensError = error{...@@ -3679,6 +4029,24 @@ pub const FutimensError = error{
3679} || UnexpectedError;4029} || UnexpectedError;
36804030
3681pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {4031pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
4032 if (builtin.os.tag == .wasi) {
4033 // TODO WASI encodes `wasi.fstflags` to signify magic values
4034 // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
4035 // this here, but we should really handle it somehow.
4036 const atim = times[0].toTimestamp();
4037 const mtim = times[1].toTimestamp();
4038 switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) {
4039 wasi.ESUCCESS => return,
4040 wasi.EACCES => return error.AccessDenied,
4041 wasi.EPERM => return error.PermissionDenied,
4042 wasi.EBADF => unreachable, // always a race condition
4043 wasi.EFAULT => unreachable,
4044 wasi.EINVAL => unreachable,
4045 wasi.EROFS => return error.ReadOnlyFileSystem,
4046 else => |err| return unexpectedErrno(err),
4047 }
4048 }
4049
3682 switch (errno(system.futimens(fd, times))) {4050 switch (errno(system.futimens(fd, times))) {
3683 0 => return,4051 0 => return,
3684 EACCES => return error.AccessDenied,4052 EACCES => return error.AccessDenied,
lib/std/os/bits/wasi.zig+108-11
...@@ -3,15 +3,71 @@ pub const STDIN_FILENO = 0;...@@ -3,15 +3,71 @@ pub const STDIN_FILENO = 0;
3pub const STDOUT_FILENO = 1;3pub const STDOUT_FILENO = 1;
4pub const STDERR_FILENO = 2;4pub const STDERR_FILENO = 2;
55
6pub const mode_t = u32;6pub const mode_t = u0;
77
8pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc8pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc
99
10pub const timespec = extern struct {10pub const timespec = struct {
11 tv_sec: time_t,11 tv_sec: time_t,
12 tv_nsec: isize,12 tv_nsec: isize,
13
14 pub fn fromTimestamp(tm: timestamp_t) timespec {
15 const tv_sec: timestamp_t = tm / 1_000_000_000;
16 const tv_nsec = tm - tv_sec * 1_000_000_000;
17 return timespec{
18 .tv_sec = @intCast(time_t, tv_sec),
19 .tv_nsec = @intCast(isize, tv_nsec),
20 };
21 }
22
23 pub fn toTimestamp(ts: timespec) timestamp_t {
24 const tm = @intCast(timestamp_t, ts.tv_sec * 1_000_000_000) + @intCast(timestamp_t, ts.tv_nsec);
25 return tm;
26 }
27};
28
29pub const Stat = struct {
30 dev: device_t,
31 ino: inode_t,
32 mode: mode_t,
33 filetype: filetype_t,
34 nlink: linkcount_t,
35 size: filesize_t,
36 atim: timespec,
37 mtim: timespec,
38 ctim: timespec,
39
40 const Self = @This();
41
42 pub fn fromFilestat(stat: filestat_t) Self {
43 return Self{
44 .dev = stat.dev,
45 .ino = stat.ino,
46 .mode = 0,
47 .filetype = stat.filetype,
48 .nlink = stat.nlink,
49 .size = stat.size,
50 .atim = stat.atime(),
51 .mtim = stat.mtime(),
52 .ctim = stat.ctime(),
53 };
54 }
55
56 pub fn atime(self: Self) timespec {
57 return self.atim;
58 }
59
60 pub fn mtime(self: Self) timespec {
61 return self.mtim;
62 }
63
64 pub fn ctime(self: Self) timespec {
65 return self.ctim;
66 }
13};67};
1468
69pub const AT_REMOVEDIR: u32 = 1; // there's no AT_REMOVEDIR in WASI, but we simulate here to match other OSes
70
15// As defined in the wasi_snapshot_preview1 spec file:71// As defined in the wasi_snapshot_preview1 spec file:
16// https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx72// https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx
17pub const advice_t = u8;73pub const advice_t = u8;
...@@ -164,14 +220,26 @@ pub const filedelta_t = i64;...@@ -164,14 +220,26 @@ pub const filedelta_t = i64;
164pub const filesize_t = u64;220pub const filesize_t = u64;
165221
166pub const filestat_t = extern struct {222pub const filestat_t = extern struct {
167 st_dev: device_t,223 dev: device_t,
168 st_ino: inode_t,224 ino: inode_t,
169 st_filetype: filetype_t,225 filetype: filetype_t,
170 st_nlink: linkcount_t,226 nlink: linkcount_t,
171 st_size: filesize_t,227 size: filesize_t,
172 st_atim: timestamp_t,228 atim: timestamp_t,
173 st_mtim: timestamp_t,229 mtim: timestamp_t,
174 st_ctim: timestamp_t,230 ctim: timestamp_t,
231
232 pub fn atime(self: filestat_t) timespec {
233 return timespec.fromTimestamp(self.atim);
234 }
235
236 pub fn mtime(self: filestat_t) timespec {
237 return timespec.fromTimestamp(self.mtim);
238 }
239
240 pub fn ctime(self: filestat_t) timespec {
241 return timespec.fromTimestamp(self.ctim);
242 }
175};243};
176244
177pub const filetype_t = u8;245pub const filetype_t = u8;
...@@ -254,6 +322,35 @@ pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000;...@@ -254,6 +322,35 @@ pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000;
254pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000;322pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000;
255pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000;323pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000;
256pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000;324pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000;
325pub const RIGHT_ALL: rights_t = RIGHT_FD_DATASYNC |
326 RIGHT_FD_READ |
327 RIGHT_FD_SEEK |
328 RIGHT_FD_FDSTAT_SET_FLAGS |
329 RIGHT_FD_SYNC |
330 RIGHT_FD_TELL |
331 RIGHT_FD_WRITE |
332 RIGHT_FD_ADVISE |
333 RIGHT_FD_ALLOCATE |
334 RIGHT_PATH_CREATE_DIRECTORY |
335 RIGHT_PATH_CREATE_FILE |
336 RIGHT_PATH_LINK_SOURCE |
337 RIGHT_PATH_LINK_TARGET |
338 RIGHT_PATH_OPEN |
339 RIGHT_FD_READDIR |
340 RIGHT_PATH_READLINK |
341 RIGHT_PATH_RENAME_SOURCE |
342 RIGHT_PATH_RENAME_TARGET |
343 RIGHT_PATH_FILESTAT_GET |
344 RIGHT_PATH_FILESTAT_SET_SIZE |
345 RIGHT_PATH_FILESTAT_SET_TIMES |
346 RIGHT_FD_FILESTAT_GET |
347 RIGHT_FD_FILESTAT_SET_SIZE |
348 RIGHT_FD_FILESTAT_SET_TIMES |
349 RIGHT_PATH_SYMLINK |
350 RIGHT_PATH_REMOVE_DIRECTORY |
351 RIGHT_PATH_UNLINK_FILE |
352 RIGHT_POLL_FD_READWRITE |
353 RIGHT_SOCK_SHUTDOWN;
257354
258pub const roflags_t = u16;355pub const roflags_t = u16;
259pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001;356pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001;
...@@ -306,7 +403,7 @@ pub const subscription_t = extern struct {...@@ -306,7 +403,7 @@ pub const subscription_t = extern struct {
306};403};
307404
308pub const subscription_clock_t = extern struct {405pub const subscription_clock_t = extern struct {
309 id: clock_id_t,406 id: clockid_t,
310 timeout: timestamp_t,407 timeout: timestamp_t,
311 precision: timestamp_t,408 precision: timestamp_t,
312 flags: subclockflags_t,409 flags: subclockflags_t,
lib/std/os/test.zig+69-39
...@@ -15,13 +15,18 @@ const a = std.testing.allocator;...@@ -15,13 +15,18 @@ const a = std.testing.allocator;
15const builtin = @import("builtin");15const builtin = @import("builtin");
16const AtomicRmwOp = builtin.AtomicRmwOp;16const AtomicRmwOp = builtin.AtomicRmwOp;
17const AtomicOrder = builtin.AtomicOrder;17const AtomicOrder = builtin.AtomicOrder;
18const tmpDir = std.testing.tmpDir;
19const Dir = std.fs.Dir;
1820
19test "makePath, put some files in it, deleteTree" {21test "makePath, put some files in it, deleteTree" {
20 try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");22 var tmp = tmpDir(.{});
21 try fs.cwd().writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");23 defer tmp.cleanup();
22 try fs.cwd().writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");24
23 try fs.cwd().deleteTree("os_test_tmp");25 try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
24 if (fs.cwd().openDir("os_test_tmp", .{})) |dir| {26 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
27 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
28 try tmp.dir.deleteTree("os_test_tmp");
29 if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
25 @panic("expected error");30 @panic("expected error");
26 } else |err| {31 } else |err| {
27 expect(err == error.FileNotFound);32 expect(err == error.FileNotFound);
...@@ -29,16 +34,21 @@ test "makePath, put some files in it, deleteTree" {...@@ -29,16 +34,21 @@ test "makePath, put some files in it, deleteTree" {
29}34}
3035
31test "access file" {36test "access file" {
32 try fs.cwd().makePath("os_test_tmp");37 if (builtin.os.tag == .wasi) return error.SkipZigTest;
33 if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {38
39 var tmp = tmpDir(.{});
40 defer tmp.cleanup();
41
42 try tmp.dir.makePath("os_test_tmp");
43 if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
34 @panic("expected error");44 @panic("expected error");
35 } else |err| {45 } else |err| {
36 expect(err == error.FileNotFound);46 expect(err == error.FileNotFound);
37 }47 }
3848
39 try fs.cwd().writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");49 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
40 try fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});50 try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
41 try fs.cwd().deleteTree("os_test_tmp");51 try tmp.dir.deleteTree("os_test_tmp");
42}52}
4353
44fn testThreadIdFn(thread_id: *Thread.Id) void {54fn testThreadIdFn(thread_id: *Thread.Id) void {
...@@ -46,10 +56,13 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {...@@ -46,10 +56,13 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
46}56}
4757
48test "sendfile" {58test "sendfile" {
49 try fs.cwd().makePath("os_test_tmp");59 var tmp = tmpDir(.{});
50 defer fs.cwd().deleteTree("os_test_tmp") catch {};60 defer tmp.cleanup();
61
62 try tmp.dir.makePath("os_test_tmp");
63 defer tmp.dir.deleteTree("os_test_tmp") catch {};
5164
52 var dir = try fs.cwd().openDir("os_test_tmp", .{});65 var dir = try tmp.dir.openDir("os_test_tmp", .{});
53 defer dir.close();66 defer dir.close();
5467
55 const line1 = "line1\n";68 const line1 = "line1\n";
...@@ -65,12 +78,12 @@ test "sendfile" {...@@ -65,12 +78,12 @@ test "sendfile" {
65 },78 },
66 };79 };
6780
68 var src_file = try dir.createFileZ("sendfile1.txt", .{ .read = true });81 var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });
69 defer src_file.close();82 defer src_file.close();
7083
71 try src_file.writevAll(&vecs);84 try src_file.writevAll(&vecs);
7285
73 var dest_file = try dir.createFileZ("sendfile2.txt", .{ .read = true });86 var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
74 defer dest_file.close();87 defer dest_file.close();
7588
76 const header1 = "header1\n";89 const header1 = "header1\n";
...@@ -113,23 +126,24 @@ test "fs.copyFile" {...@@ -113,23 +126,24 @@ test "fs.copyFile" {
113 const dest_file = "tmp_test_copy_file2.txt";126 const dest_file = "tmp_test_copy_file2.txt";
114 const dest_file2 = "tmp_test_copy_file3.txt";127 const dest_file2 = "tmp_test_copy_file3.txt";
115128
116 const cwd = fs.cwd();129 var tmp = tmpDir(.{});
130 defer tmp.cleanup();
117131
118 try cwd.writeFile(src_file, data);132 try tmp.dir.writeFile(src_file, data);
119 defer cwd.deleteFile(src_file) catch {};133 defer tmp.dir.deleteFile(src_file) catch {};
120134
121 try cwd.copyFile(src_file, cwd, dest_file, .{});135 try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});
122 defer cwd.deleteFile(dest_file) catch {};136 defer tmp.dir.deleteFile(dest_file) catch {};
123137
124 try cwd.copyFile(src_file, cwd, dest_file2, .{ .override_mode = File.default_mode });138 try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });
125 defer cwd.deleteFile(dest_file2) catch {};139 defer tmp.dir.deleteFile(dest_file2) catch {};
126140
127 try expectFileContents(dest_file, data);141 try expectFileContents(tmp.dir, dest_file, data);
128 try expectFileContents(dest_file2, data);142 try expectFileContents(tmp.dir, dest_file2, data);
129}143}
130144
131fn expectFileContents(file_path: []const u8, data: []const u8) !void {145fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
132 const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);146 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
133 defer testing.allocator.free(contents);147 defer testing.allocator.free(contents);
134148
135 testing.expectEqualSlices(u8, data, contents);149 testing.expectEqualSlices(u8, data, contents);
...@@ -181,6 +195,8 @@ fn start2(ctx: *i32) u8 {...@@ -181,6 +195,8 @@ fn start2(ctx: *i32) u8 {
181}195}
182196
183test "cpu count" {197test "cpu count" {
198 if (builtin.os.tag == .wasi) return error.SkipZigTest;
199
184 const cpu_count = try Thread.cpuCount();200 const cpu_count = try Thread.cpuCount();
185 expect(cpu_count >= 1);201 expect(cpu_count >= 1);
186}202}
...@@ -191,17 +207,21 @@ test "AtomicFile" {...@@ -191,17 +207,21 @@ test "AtomicFile" {
191 \\ hello!207 \\ hello!
192 \\ this is a test file208 \\ this is a test file
193 ;209 ;
210
211 var tmp = tmpDir(.{});
212 defer tmp.cleanup();
213
194 {214 {
195 var af = try fs.cwd().atomicFile(test_out_file, .{});215 var af = try tmp.dir.atomicFile(test_out_file, .{});
196 defer af.deinit();216 defer af.deinit();
197 try af.file.writeAll(test_content);217 try af.file.writeAll(test_content);
198 try af.finish();218 try af.finish();
199 }219 }
200 const content = try fs.cwd().readFileAlloc(testing.allocator, test_out_file, 9999);220 const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
201 defer testing.allocator.free(content);221 defer testing.allocator.free(content);
202 expect(mem.eql(u8, content, test_content));222 expect(mem.eql(u8, content, test_content));
203223
204 try fs.cwd().deleteFile(test_out_file);224 try tmp.dir.deleteFile(test_out_file);
205}225}
206226
207test "thread local storage" {227test "thread local storage" {
...@@ -231,12 +251,16 @@ test "getrandom" {...@@ -231,12 +251,16 @@ test "getrandom" {
231}251}
232252
233test "getcwd" {253test "getcwd" {
254 if (builtin.os.tag == .wasi) return error.SkipZigTest;
255
234 // at least call it so it gets compiled256 // at least call it so it gets compiled
235 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;257 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
236 _ = os.getcwd(&buf) catch undefined;258 _ = os.getcwd(&buf) catch undefined;
237}259}
238260
239test "realpath" {261test "realpath" {
262 if (builtin.os.tag == .wasi) return error.SkipZigTest;
263
240 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;264 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
241 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));265 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
242}266}
...@@ -304,7 +328,7 @@ test "dl_iterate_phdr" {...@@ -304,7 +328,7 @@ test "dl_iterate_phdr" {
304}328}
305329
306test "gethostname" {330test "gethostname" {
307 if (builtin.os.tag == .windows)331 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
308 return error.SkipZigTest;332 return error.SkipZigTest;
309333
310 var buf: [os.HOST_NAME_MAX]u8 = undefined;334 var buf: [os.HOST_NAME_MAX]u8 = undefined;
...@@ -313,7 +337,7 @@ test "gethostname" {...@@ -313,7 +337,7 @@ test "gethostname" {
313}337}
314338
315test "pipe" {339test "pipe" {
316 if (builtin.os.tag == .windows)340 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
317 return error.SkipZigTest;341 return error.SkipZigTest;
318342
319 var fds = try os.pipe();343 var fds = try os.pipe();
...@@ -349,9 +373,12 @@ test "memfd_create" {...@@ -349,9 +373,12 @@ test "memfd_create" {
349}373}
350374
351test "mmap" {375test "mmap" {
352 if (builtin.os.tag == .windows)376 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
353 return error.SkipZigTest;377 return error.SkipZigTest;
354378
379 var tmp = tmpDir(.{});
380 defer tmp.cleanup();
381
355 // Simple mmap() call with non page-aligned size382 // Simple mmap() call with non page-aligned size
356 {383 {
357 const data = try os.mmap(384 const data = try os.mmap(
...@@ -380,7 +407,7 @@ test "mmap" {...@@ -380,7 +407,7 @@ test "mmap" {
380407
381 // Create a file used for testing mmap() calls with a file descriptor408 // Create a file used for testing mmap() calls with a file descriptor
382 {409 {
383 const file = try fs.cwd().createFile(test_out_file, .{});410 const file = try tmp.dir.createFile(test_out_file, .{});
384 defer file.close();411 defer file.close();
385412
386 const stream = file.outStream();413 const stream = file.outStream();
...@@ -393,7 +420,7 @@ test "mmap" {...@@ -393,7 +420,7 @@ test "mmap" {
393420
394 // Map the whole file421 // Map the whole file
395 {422 {
396 const file = try fs.cwd().openFile(test_out_file, .{});423 const file = try tmp.dir.openFile(test_out_file, .{});
397 defer file.close();424 defer file.close();
398425
399 const data = try os.mmap(426 const data = try os.mmap(
...@@ -417,7 +444,7 @@ test "mmap" {...@@ -417,7 +444,7 @@ test "mmap" {
417444
418 // Map the upper half of the file445 // Map the upper half of the file
419 {446 {
420 const file = try fs.cwd().openFile(test_out_file, .{});447 const file = try tmp.dir.openFile(test_out_file, .{});
421 defer file.close();448 defer file.close();
422449
423 const data = try os.mmap(450 const data = try os.mmap(
...@@ -439,7 +466,7 @@ test "mmap" {...@@ -439,7 +466,7 @@ test "mmap" {
439 }466 }
440 }467 }
441468
442 try fs.cwd().deleteFile(test_out_file);469 try tmp.dir.deleteFile(test_out_file);
443}470}
444471
445test "getenv" {472test "getenv" {
...@@ -451,15 +478,18 @@ test "getenv" {...@@ -451,15 +478,18 @@ test "getenv" {
451}478}
452479
453test "fcntl" {480test "fcntl" {
454 if (builtin.os.tag == .windows)481 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
455 return error.SkipZigTest;482 return error.SkipZigTest;
456483
484 var tmp = tmpDir(.{});
485 defer tmp.cleanup();
486
457 const test_out_file = "os_tmp_test";487 const test_out_file = "os_tmp_test";
458488
459 const file = try fs.cwd().createFile(test_out_file, .{});489 const file = try tmp.dir.createFile(test_out_file, .{});
460 defer {490 defer {
461 file.close();491 file.close();
462 fs.cwd().deleteFile(test_out_file) catch {};492 tmp.dir.deleteFile(test_out_file) catch {};
463 }493 }
464494
465 // Note: The test assumes createFile opens the file with O_CLOEXEC495 // Note: The test assumes createFile opens the file with O_CLOEXEC
lib/std/packed_int_array.zig+6
...@@ -314,6 +314,9 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian)...@@ -314,6 +314,9 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian)
314}314}
315315
316test "PackedIntArray" {316test "PackedIntArray" {
317 // TODO @setEvalBranchQuota generates panics in wasm32. Investigate.
318 if (builtin.arch == .wasm32) return error.SkipZigTest;
319
317 @setEvalBranchQuota(10000);320 @setEvalBranchQuota(10000);
318 const max_bits = 256;321 const max_bits = 256;
319 const int_count = 19;322 const int_count = 19;
...@@ -357,6 +360,9 @@ test "PackedIntArray init" {...@@ -357,6 +360,9 @@ test "PackedIntArray init" {
357}360}
358361
359test "PackedIntSlice" {362test "PackedIntSlice" {
363 // TODO @setEvalBranchQuota generates panics in wasm32. Investigate.
364 if (builtin.arch == .wasm32) return error.SkipZigTest;
365
360 @setEvalBranchQuota(10000);366 @setEvalBranchQuota(10000);
361 const max_bits = 256;367 const max_bits = 256;
362 const int_count = 19;368 const int_count = 19;
lib/std/process.zig+8-10
...@@ -26,6 +26,8 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {...@@ -26,6 +26,8 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
26}26}
2727
28test "getCwdAlloc" {28test "getCwdAlloc" {
29 if (builtin.os.tag == .wasi) return error.SkipZigTest;
30
29 const cwd = try getCwdAlloc(testing.allocator);31 const cwd = try getCwdAlloc(testing.allocator);
30 testing.allocator.free(cwd);32 testing.allocator.free(cwd);
31}33}
...@@ -69,9 +71,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -69,9 +71,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
69 return os.unexpectedErrno(environ_sizes_get_ret);71 return os.unexpectedErrno(environ_sizes_get_ret);
70 }72 }
7173
72 // TODO: Verify that the documentation is incorrect74 var environ = try allocator.alloc([*:0]u8, environ_count);
73 // https://github.com/WebAssembly/WASI/issues/27
74 var environ = try allocator.alloc(?[*:0]u8, environ_count + 1);
75 defer allocator.free(environ);75 defer allocator.free(environ);
76 var environ_buf = try allocator.alloc(u8, environ_buf_size);76 var environ_buf = try allocator.alloc(u8, environ_buf_size);
77 defer allocator.free(environ_buf);77 defer allocator.free(environ_buf);
...@@ -82,13 +82,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -82,13 +82,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
82 }82 }
8383
84 for (environ) |env| {84 for (environ) |env| {
85 if (env) |ptr| {85 const pair = mem.spanZ(env);
86 const pair = mem.spanZ(ptr);86 var parts = mem.split(pair, "=");
87 var parts = mem.split(pair, "=");87 const key = parts.next().?;
88 const key = parts.next().?;88 const value = parts.next().?;
89 const value = parts.next().?;89 try result.set(key, value);
90 try result.set(key, value);
91 }
92 }90 }
93 return result;91 return result;
94 } else if (builtin.link_libc) {92 } else if (builtin.link_libc) {
lib/std/testing.zig+17-1
...@@ -209,6 +209,21 @@ pub const TmpDir = struct {...@@ -209,6 +209,21 @@ pub const TmpDir = struct {
209 }209 }
210};210};
211211
212fn getCwdOrWasiPreopen() std.fs.Dir {
213 if (@import("builtin").os.tag == .wasi) {
214 var preopens = std.fs.wasi.PreopenList.init(allocator);
215 defer preopens.deinit();
216 preopens.populate() catch
217 @panic("unable to make tmp dir for testing: unable to populate preopens");
218 const preopen = preopens.find(".") orelse
219 @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
220
221 return std.fs.Dir{ .fd = preopen.fd };
222 } else {
223 return std.fs.cwd();
224 }
225}
226
212pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {227pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
213 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;228 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
214 std.crypto.randomBytes(&random_bytes) catch229 std.crypto.randomBytes(&random_bytes) catch
...@@ -216,7 +231,8 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {...@@ -216,7 +231,8 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
216 var sub_path: [TmpDir.sub_path_len]u8 = undefined;231 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
217 std.fs.base64_encoder.encode(&sub_path, &random_bytes);232 std.fs.base64_encoder.encode(&sub_path, &random_bytes);
218233
219 var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch234 var cwd = getCwdOrWasiPreopen();
235 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
220 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");236 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
221 defer cache_dir.close();237 defer cache_dir.close();
222 var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch238 var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
lib/std/time.zig+26-1
...@@ -19,6 +19,31 @@ pub fn sleep(nanoseconds: u64) void {...@@ -19,6 +19,31 @@ pub fn sleep(nanoseconds: u64) void {
19 os.windows.kernel32.Sleep(ms);19 os.windows.kernel32.Sleep(ms);
20 return;20 return;
21 }21 }
22 if (builtin.os.tag == .wasi) {
23 const w = std.os.wasi;
24 const userdata: w.userdata_t = 0x0123_45678;
25 const clock = w.subscription_clock_t{
26 .id = w.CLOCK_MONOTONIC,
27 .timeout = nanoseconds,
28 .precision = 0,
29 .flags = 0,
30 };
31 const in = w.subscription_t{
32 .userdata = userdata,
33 .u = w.subscription_u_t{
34 .tag = w.EVENTTYPE_CLOCK,
35 .u = w.subscription_u_u_t{
36 .clock = clock,
37 },
38 },
39 };
40
41 var event: w.event_t = undefined;
42 var nevents: usize = undefined;
43 _ = w.poll_oneoff(&in, &event, 1, &nevents);
44 return;
45 }
46
22 const s = nanoseconds / ns_per_s;47 const s = nanoseconds / ns_per_s;
23 const ns = nanoseconds % ns_per_s;48 const ns = nanoseconds % ns_per_s;
24 std.os.nanosleep(s, ns);49 std.os.nanosleep(s, ns);
...@@ -202,7 +227,7 @@ test "sleep" {...@@ -202,7 +227,7 @@ test "sleep" {
202227
203test "timestamp" {228test "timestamp" {
204 const ns_per_ms = (ns_per_s / ms_per_s);229 const ns_per_ms = (ns_per_s / ms_per_s);
205 const margin = 50;230 const margin = ns_per_ms * 50;
206231
207 const time_0 = milliTimestamp();232 const time_0 = milliTimestamp();
208 sleep(ns_per_ms);233 sleep(ns_per_ms);
lib/std/zig/parser_test.zig+7
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1const builtin = @import("builtin");
2
1test "recovery: top level" {3test "recovery: top level" {
2 try testError(4 try testError(
3 \\test "" {inline}5 \\test "" {inline}
...@@ -941,6 +943,9 @@ test "zig fmt: same-line doc comment on variable declaration" {...@@ -941,6 +943,9 @@ test "zig fmt: same-line doc comment on variable declaration" {
941}943}
942944
943test "zig fmt: if-else with comment before else" {945test "zig fmt: if-else with comment before else" {
946 // TODO investigate why this fails in wasm.
947 if (builtin.cpu.arch == .wasm32) return error.SkipZigTest;
948
944 try testCanonical(949 try testCanonical(
945 \\comptime {950 \\comptime {
946 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan951 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan
...@@ -1555,6 +1560,8 @@ test "zig fmt: comment after if before another if" {...@@ -1555,6 +1560,8 @@ test "zig fmt: comment after if before another if" {
1555}1560}
15561561
1557test "zig fmt: line comment between if block and else keyword" {1562test "zig fmt: line comment between if block and else keyword" {
1563 // TODO investigate why this fails in wasm.
1564 if (builtin.cpu.arch == .wasm32) return error.SkipZigTest;
1558 try testCanonical(1565 try testCanonical(
1559 \\test "aoeu" {1566 \\test "aoeu" {
1560 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan1567 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan
test/stage1/behavior.zig+6-2
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1const builtin = @import("builtin");
2
1comptime {3comptime {
2 _ = @import("behavior/align.zig");4 _ = @import("behavior/align.zig");
3 _ = @import("behavior/alignof.zig");5 _ = @import("behavior/alignof.zig");
4 _ = @import("behavior/array.zig");6 _ = @import("behavior/array.zig");
5 _ = @import("behavior/asm.zig");7 if (builtin.os.tag != .wasi) {
6 _ = @import("behavior/async_fn.zig");8 _ = @import("behavior/asm.zig");
9 _ = @import("behavior/async_fn.zig");
10 }
7 _ = @import("behavior/atomics.zig");11 _ = @import("behavior/atomics.zig");
8 _ = @import("behavior/await_struct.zig");12 _ = @import("behavior/await_struct.zig");
9 _ = @import("behavior/bit_shifting.zig");13 _ = @import("behavior/bit_shifting.zig");
test/stage1/behavior/align.zig+6
...@@ -133,6 +133,9 @@ fn alignedBig() align(16) i32 {...@@ -133,6 +133,9 @@ fn alignedBig() align(16) i32 {
133}133}
134134
135test "@alignCast functions" {135test "@alignCast functions" {
136 // TODO investigate why this fails when cross-compiled to wasm.
137 if (builtin.os.tag == .wasi) return error.SkipZigTest;
138
136 expect(fnExpectsOnly1(simple4) == 0x19);139 expect(fnExpectsOnly1(simple4) == 0x19);
137}140}
138fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {141fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
...@@ -324,6 +327,9 @@ test "align(@alignOf(T)) T does not force resolution of T" {...@@ -324,6 +327,9 @@ test "align(@alignOf(T)) T does not force resolution of T" {
324}327}
325328
326test "align(N) on functions" {329test "align(N) on functions" {
330 // TODO investigate why this fails when cross-compiled to wasm.
331 if (builtin.os.tag == .wasi) return error.SkipZigTest;
332
327 expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);333 expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
328}334}
329fn overaligned_fn() align(0x1000) i32 {335fn overaligned_fn() align(0x1000) i32 {
test/stage1/behavior/shuffle.zig+4
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const mem = std.mem;3const mem = std.mem;
3const expect = std.testing.expect;4const expect = std.testing.expect;
4const Vector = std.meta.Vector;5const Vector = std.meta.Vector;
56
6test "@shuffle" {7test "@shuffle" {
8 // TODO investigate why this fails when cross-compiling to wasm.
9 if (builtin.os.tag == .wasi) return error.SkipZigTest;
10
7 const S = struct {11 const S = struct {
8 fn doTheTest() void {12 fn doTheTest() void {
9 var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };13 var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
test/stage1/behavior/vector.zig+4
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const mem = std.mem;3const mem = std.mem;
3const math = std.math;4const math = std.math;
4const expect = std.testing.expect;5const expect = std.testing.expect;
...@@ -387,6 +388,9 @@ test "vector bitwise not operator" {...@@ -387,6 +388,9 @@ test "vector bitwise not operator" {
387}388}
388389
389test "vector shift operators" {390test "vector shift operators" {
391 // TODO investigate why this fails when cross-compiled to wasm.
392 if (builtin.os.tag == .wasi) return error.SkipZigTest;
393
390 const S = struct {394 const S = struct {
391 fn doTheTestShift(x: var, y: var) void {395 fn doTheTestShift(x: var, y: var) void {
392 const N = @typeInfo(@TypeOf(x)).Array.len;396 const N = @typeInfo(@TypeOf(x)).Array.len;
test/tests.zig+11
...@@ -50,6 +50,15 @@ const test_targets = blk: {...@@ -50,6 +50,15 @@ const test_targets = blk: {
50 .single_threaded = true,50 .single_threaded = true,
51 },51 },
5252
53 TestTarget{
54 .target = .{
55 .cpu_arch = .wasm32,
56 .os_tag = .wasi,
57 },
58 .link_libc = false,
59 .single_threaded = true,
60 },
61
53 TestTarget{62 TestTarget{
54 .target = .{63 .target = .{
55 .cpu_arch = .x86_64,64 .cpu_arch = .x86_64,
...@@ -465,6 +474,7 @@ pub fn addPkgTests(...@@ -465,6 +474,7 @@ pub fn addPkgTests(
465 skip_libc: bool,474 skip_libc: bool,
466 is_wine_enabled: bool,475 is_wine_enabled: bool,
467 is_qemu_enabled: bool,476 is_qemu_enabled: bool,
477 is_wasmtime_enabled: bool,
468 glibc_dir: ?[]const u8,478 glibc_dir: ?[]const u8,
469) *build.Step {479) *build.Step {
470 const step = b.step(b.fmt("test-{}", .{name}), desc);480 const step = b.step(b.fmt("test-{}", .{name}), desc);
...@@ -525,6 +535,7 @@ pub fn addPkgTests(...@@ -525,6 +535,7 @@ pub fn addPkgTests(
525 these_tests.overrideZigLibDir("lib");535 these_tests.overrideZigLibDir("lib");
526 these_tests.enable_wine = is_wine_enabled;536 these_tests.enable_wine = is_wine_enabled;
527 these_tests.enable_qemu = is_qemu_enabled;537 these_tests.enable_qemu = is_qemu_enabled;
538 these_tests.enable_wasmtime = is_wasmtime_enabled;
528 these_tests.glibc_multi_install_dir = glibc_dir;539 these_tests.glibc_multi_install_dir = glibc_dir;
529540
530 step.dependOn(&these_tests.step);541 step.dependOn(&these_tests.step);