WASI,libc: enable tests.

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda 2021-07-27 08:59:34 +09:00
parent fc105f2681
commit 1e20a62126
16 changed files with 221 additions and 86 deletions

View file

@ -24,7 +24,7 @@ pub const Condition = @import("Thread/Condition.zig");
pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint"); pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint");
pub const use_pthreads = target.os.tag != .windows and std.builtin.link_libc; pub const use_pthreads = target.os.tag != .windows and std.Target.current.os.tag != .wasi and std.builtin.link_libc;
const Thread = @This(); const Thread = @This();
const Impl = if (target.os.tag == .windows) const Impl = if (target.os.tag == .windows)

View file

@ -31,6 +31,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {
.fuchsia => @import("c/fuchsia.zig"), .fuchsia => @import("c/fuchsia.zig"),
.minix => @import("c/minix.zig"), .minix => @import("c/minix.zig"),
.emscripten => @import("c/emscripten.zig"), .emscripten => @import("c/emscripten.zig"),
.wasi => @import("c/wasi.zig"),
else => struct {}, else => struct {},
}; };
@ -77,6 +78,7 @@ pub extern "c" fn fread(noalias ptr: [*]u8, size_of_type: usize, item_count: usi
pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
pub extern "c" fn abort() noreturn; pub extern "c" fn abort() noreturn;
pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn exit(code: c_int) noreturn;
pub extern "c" fn _exit(code: c_int) noreturn; pub extern "c" fn _exit(code: c_int) noreturn;
pub extern "c" fn isatty(fd: fd_t) c_int; pub extern "c" fn isatty(fd: fd_t) c_int;

60
lib/std/c/wasi.zig Normal file
View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
usingnamespace @import("../os/bits.zig");
extern threadlocal var errno: c_int;
pub fn _errno() *c_int {
return &errno;
}
pub const pid_t = c_int;
pub const uid_t = u32;
pub const gid_t = u32;
pub const off_t = i64;
pub const libc_stat = extern struct {
dev: i32,
ino: ino_t,
nlink: u64,
mode: mode_t,
uid: uid_t,
gid: gid_t,
__pad0: isize,
rdev: i32,
size: off_t,
blksize: i32,
blocks: i64,
atimesec: time_t,
atimensec: isize,
mtimesec: time_t,
mtimensec: isize,
ctimesec: time_t,
ctimensec: isize,
pub fn atime(self: @This()) timespec {
return timespec{
.tv_sec = self.atimesec,
.tv_nsec = self.atimensec,
};
}
pub fn mtime(self: @This()) timespec {
return timespec{
.tv_sec = self.mtimesec,
.tv_nsec = self.mtimensec,
};
}
pub fn ctime(self: @This()) timespec {
return timespec{
.tv_sec = self.ctimesec,
.tv_nsec = self.ctimensec,
};
}
};

View file

@ -647,6 +647,9 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid /// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
pub fn next(self: *Self) Error!?Entry { pub fn next(self: *Self) Error!?Entry {
// We intentinally use fd_readdir even when linked with libc,
// since its implementation is exactly the same as below,
// and we avoid the code complexity here.
const w = os.wasi; const w = os.wasi;
start_over: while (true) { start_over: while (true) {
if (self.index >= self.end_index) { if (self.index >= self.end_index) {
@ -770,7 +773,7 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openFileW(path_w.span(), flags); return self.openFileW(path_w.span(), flags);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.openFileWasi(sub_path, flags); return self.openFileWasi(sub_path, flags);
} }
const path_c = try os.toPosixPath(sub_path); const path_c = try os.toPosixPath(sub_path);
@ -846,14 +849,16 @@ pub const Dir = struct {
try os.openatZ(self.fd, sub_path, os_flags, 0); try os.openatZ(self.fd, sub_path, os_flags, 0);
errdefer os.close(fd); errdefer os.close(fd);
if (!has_flock_open_flags and flags.lock != .None) { if (builtin.target.os.tag != .wasi) {
// TODO: integrate async I/O if (!has_flock_open_flags and flags.lock != .None) {
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); // TODO: integrate async I/O
try os.flock(fd, switch (flags.lock) { const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
.None => unreachable, try os.flock(fd, switch (flags.lock) {
.Shared => os.LOCK_SH | lock_nonblocking, .None => unreachable,
.Exclusive => os.LOCK_EX | lock_nonblocking, .Shared => os.LOCK_SH | lock_nonblocking,
}); .Exclusive => os.LOCK_EX | lock_nonblocking,
});
}
} }
if (has_flock_open_flags and flags.lock_nonblocking) { if (has_flock_open_flags and flags.lock_nonblocking) {
@ -926,7 +931,7 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.createFileW(path_w.span(), flags); return self.createFileW(path_w.span(), flags);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.createFileWasi(sub_path, flags); return self.createFileWasi(sub_path, flags);
} }
const path_c = try os.toPosixPath(sub_path); const path_c = try os.toPosixPath(sub_path);
@ -996,14 +1001,16 @@ pub const Dir = struct {
try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode); try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
errdefer os.close(fd); errdefer os.close(fd);
if (!has_flock_open_flags and flags.lock != .None) { if (builtin.target.os.tag != .wasi) {
// TODO: integrate async I/O if (!has_flock_open_flags and flags.lock != .None and builtin.target.os.tag != .wasi) {
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); // TODO: integrate async I/O
try os.flock(fd, switch (flags.lock) { const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
.None => unreachable, try os.flock(fd, switch (flags.lock) {
.Shared => os.LOCK_SH | lock_nonblocking, .None => unreachable,
.Exclusive => os.LOCK_EX | lock_nonblocking, .Shared => os.LOCK_SH | lock_nonblocking,
}); .Exclusive => os.LOCK_EX | lock_nonblocking,
});
}
} }
if (has_flock_open_flags and flags.lock_nonblocking) { if (has_flock_open_flags and flags.lock_nonblocking) {
@ -1284,7 +1291,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirW(sub_path_w.span().ptr, args); return self.openDirW(sub_path_w.span().ptr, args);
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.openDirWasi(sub_path, args); return self.openDirWasi(sub_path, args);
} else { } else {
const sub_path_c = try os.toPosixPath(sub_path); const sub_path_c = try os.toPosixPath(sub_path);
@ -1431,7 +1438,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteFileW(sub_path_w.span()); return self.deleteFileW(sub_path_w.span());
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) { os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
else => |e| return e, else => |e| return e,
@ -1494,7 +1501,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteDirW(sub_path_w.span()); return self.deleteDirW(sub_path_w.span());
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) { os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
else => |e| return e, else => |e| return e,
@ -1553,7 +1560,7 @@ pub const Dir = struct {
sym_link_path: []const u8, sym_link_path: []const u8,
flags: SymLinkFlags, flags: SymLinkFlags,
) !void { ) !void {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.symLinkWasi(target_path, sym_link_path, flags); return self.symLinkWasi(target_path, sym_link_path, flags);
} }
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
@ -1606,7 +1613,7 @@ pub const Dir = struct {
/// The return value is a slice of `buffer`, from index `0`. /// The return value is a slice of `buffer`, from index `0`.
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 { pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.readLinkWasi(sub_path, buffer); return self.readLinkWasi(sub_path, buffer);
} }
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
@ -2025,7 +2032,7 @@ pub const Dir = struct {
pub fn cwd() Dir { pub fn cwd() Dir {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
@compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead"); @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead");
} else { } else {
return Dir{ .fd = os.AT_FDCWD }; return Dir{ .fd = os.AT_FDCWD };

View file

@ -326,26 +326,23 @@ pub const File = struct {
.inode = st.ino, .inode = st.ino,
.size = @bitCast(u64, st.size), .size = @bitCast(u64, st.size),
.mode = st.mode, .mode = st.mode,
.kind = switch (builtin.os.tag) { .kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
.wasi => switch (st.filetype) { os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice, os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice, os.FILETYPE_DIRECTORY => Kind.Directory,
os.FILETYPE_DIRECTORY => Kind.Directory, os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink, os.FILETYPE_REGULAR_FILE => Kind.File,
os.FILETYPE_REGULAR_FILE => Kind.File, os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket, else => Kind.Unknown,
else => Kind.Unknown, } else switch (st.mode & os.S_IFMT) {
}, os.S_IFBLK => Kind.BlockDevice,
else => switch (st.mode & os.S_IFMT) { os.S_IFCHR => Kind.CharacterDevice,
os.S_IFBLK => Kind.BlockDevice, os.S_IFDIR => Kind.Directory,
os.S_IFCHR => Kind.CharacterDevice, os.S_IFIFO => Kind.NamedPipe,
os.S_IFDIR => Kind.Directory, os.S_IFLNK => Kind.SymLink,
os.S_IFIFO => Kind.NamedPipe, os.S_IFREG => Kind.File,
os.S_IFLNK => Kind.SymLink, os.S_IFSOCK => Kind.UnixDomainSocket,
os.S_IFREG => Kind.File, else => Kind.Unknown,
os.S_IFSOCK => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
}, },
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,

View file

@ -169,7 +169,7 @@ pub const PreopenList = struct {
}; };
test "extracting WASI preopens" { test "extracting WASI preopens" {
if (std.builtin.os.tag != .wasi) return error.SkipZigTest; if (std.builtin.os.tag != .wasi or @import("builtin").link_libc) return error.SkipZigTest;
var preopens = PreopenList.init(std.testing.allocator); var preopens = PreopenList.init(std.testing.allocator);
defer preopens.deinit(); defer preopens.deinit();

View file

@ -35,7 +35,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
} }
test { test {
if (!builtin.link_libc) return error.SkipZigTest; if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest;
const filename = "tmp_io_test_file.txt"; const filename = "tmp_io_test_file.txt";
const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;

View file

@ -404,7 +404,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
const first = iov[0]; const first = iov[0];
return read(fd, first.iov_base[0..first.iov_len]); return read(fd, first.iov_base[0..first.iov_len]);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var nread: usize = undefined; var nread: usize = undefined;
switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) { switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
wasi.ESUCCESS => return nread, wasi.ESUCCESS => return nread,
@ -461,7 +461,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, offset, std.io.default_mode); return windows.ReadFile(fd, buf, offset, std.io.default_mode);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
const iovs = [1]iovec{iovec{ const iovs = [1]iovec{iovec{
.iov_base = buf.ptr, .iov_base = buf.ptr,
.iov_len = buf.len, .iov_len = buf.len,
@ -556,7 +556,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
else => return windows.unexpectedStatus(rc), else => return windows.unexpectedStatus(rc),
} }
} }
if (std.Target.current.os.tag == .wasi) { if (std.Target.current.os.tag == .wasi and !builtin.link_libc) {
switch (wasi.fd_filestat_set_size(fd, length)) { switch (wasi.fd_filestat_set_size(fd, length)) {
wasi.ESUCCESS => return, wasi.ESUCCESS => return,
wasi.EINTR => unreachable, wasi.EINTR => unreachable,
@ -617,7 +617,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
const first = iov[0]; const first = iov[0];
return pread(fd, first.iov_base[0..first.iov_len], offset); return pread(fd, first.iov_base[0..first.iov_len], offset);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var nread: usize = undefined; var nread: usize = undefined;
switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) { switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
wasi.ESUCCESS => return nread, wasi.ESUCCESS => return nread,
@ -795,7 +795,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
const first = iov[0]; const first = iov[0];
return write(fd, first.iov_base[0..first.iov_len]); return write(fd, first.iov_base[0..first.iov_len]);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var nwritten: usize = undefined; var nwritten: usize = undefined;
switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) { switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
wasi.ESUCCESS => return nwritten, wasi.ESUCCESS => return nwritten,
@ -867,7 +867,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
if (std.Target.current.os.tag == .windows) { if (std.Target.current.os.tag == .windows) {
return windows.WriteFile(fd, bytes, offset, std.io.default_mode); return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
const ciovs = [1]iovec_const{iovec_const{ const ciovs = [1]iovec_const{iovec_const{
.iov_base = bytes.ptr, .iov_base = bytes.ptr,
.iov_len = bytes.len, .iov_len = bytes.len,
@ -968,7 +968,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
const first = iov[0]; const first = iov[0];
return pwrite(fd, first.iov_base[0..first.iov_len], offset); return pwrite(fd, first.iov_base[0..first.iov_len], offset);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var nwritten: usize = undefined; var nwritten: usize = undefined;
switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) { switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
wasi.ESUCCESS => return nwritten, wasi.ESUCCESS => return nwritten,
@ -1627,7 +1627,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
/// If `sym_link_path` exists, it will not be overwritten. /// If `sym_link_path` exists, it will not be overwritten.
/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return symlinkatWasi(target_path, newdirfd, sym_link_path); return symlinkatWasi(target_path, newdirfd, sym_link_path);
} }
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
@ -1856,7 +1856,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path); const file_path_w = try windows.sliceToPrefixedFileW(file_path);
return unlinkatW(dirfd, file_path_w.span(), flags); return unlinkatW(dirfd, file_path_w.span(), flags);
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
return unlinkatWasi(dirfd, file_path, flags); return unlinkatWasi(dirfd, file_path, flags);
} else { } else {
const file_path_c = try toPosixPath(file_path); const file_path_c = try toPosixPath(file_path);
@ -2023,7 +2023,7 @@ pub fn renameat(
const old_path_w = try windows.sliceToPrefixedFileW(old_path); const old_path_w = try windows.sliceToPrefixedFileW(old_path);
const new_path_w = try windows.sliceToPrefixedFileW(new_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path);
return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path); return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path);
} else { } else {
const old_path_c = try toPosixPath(old_path); const old_path_c = try toPosixPath(old_path);
@ -2159,7 +2159,7 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
return mkdiratWasi(dir_fd, sub_dir_path, mode); return mkdiratWasi(dir_fd, sub_dir_path, mode);
} else { } else {
const sub_dir_path_c = try toPosixPath(sub_dir_path); const sub_dir_path_c = try toPosixPath(sub_dir_path);
@ -2519,7 +2519,7 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
/// The return value is a slice of `out_buffer` from index 0. /// The return value is a slice of `out_buffer` from index 0.
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return readlinkatWasi(dirfd, file_path, out_buffer); return readlinkatWasi(dirfd, file_path, out_buffer);
} }
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
@ -3481,7 +3481,7 @@ pub const FStatError = error{
/// Return information about a file descriptor. /// Return information about a file descriptor.
pub fn fstat(fd: fd_t) FStatError!Stat { pub fn fstat(fd: fd_t) FStatError!Stat {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var stat: wasi.filestat_t = undefined; var stat: wasi.filestat_t = undefined;
switch (wasi.fd_filestat_get(fd, &stat)) { switch (wasi.fd_filestat_get(fd, &stat)) {
wasi.ESUCCESS => return Stat.fromFilestat(stat), wasi.ESUCCESS => return Stat.fromFilestat(stat),
@ -3519,7 +3519,7 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink
/// which is relative to `dirfd` handle. /// which is relative to `dirfd` handle.
/// See also `fstatatZ` and `fstatatWasi`. /// See also `fstatatZ` and `fstatatWasi`.
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return fstatatWasi(dirfd, pathname, flags); return fstatatWasi(dirfd, pathname, flags);
} else if (builtin.os.tag == .windows) { } else if (builtin.os.tag == .windows) {
@compileError("fstatat is not yet implemented on Windows"); @compileError("fstatat is not yet implemented on Windows");
@ -4123,7 +4123,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_BEGIN(fd, offset); return windows.SetFilePointerEx_BEGIN(fd, offset);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var new_offset: wasi.filesize_t = undefined; var new_offset: wasi.filesize_t = undefined;
switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) { switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) {
wasi.ESUCCESS => return, wasi.ESUCCESS => return,
@ -4171,7 +4171,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT(fd, offset); return windows.SetFilePointerEx_CURRENT(fd, offset);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var new_offset: wasi.filesize_t = undefined; var new_offset: wasi.filesize_t = undefined;
switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) { switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) {
wasi.ESUCCESS => return, wasi.ESUCCESS => return,
@ -4218,7 +4218,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_END(fd, offset); return windows.SetFilePointerEx_END(fd, offset);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var new_offset: wasi.filesize_t = undefined; var new_offset: wasi.filesize_t = undefined;
switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) { switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) {
wasi.ESUCCESS => return, wasi.ESUCCESS => return,
@ -4265,7 +4265,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT_get(fd); return windows.SetFilePointerEx_CURRENT_get(fd);
} }
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var new_offset: wasi.filesize_t = undefined; var new_offset: wasi.filesize_t = undefined;
switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) { switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) {
wasi.ESUCCESS => return new_offset, wasi.ESUCCESS => return new_offset,
@ -4665,7 +4665,7 @@ pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
/// TODO: change this to return the timespec as a return value /// TODO: change this to return the timespec as a return value
/// TODO: look into making clk_id an enum /// TODO: look into making clk_id an enum
pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
if (std.Target.current.os.tag == .wasi) { if (std.Target.current.os.tag == .wasi and !builtin.link_libc) {
var ts: timestamp_t = undefined; var ts: timestamp_t = undefined;
switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) {
0 => { 0 => {
@ -4706,7 +4706,7 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
} }
pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
if (std.Target.current.os.tag == .wasi) { if (std.Target.current.os.tag == .wasi and !builtin.link_libc) {
var ts: timestamp_t = undefined; var ts: timestamp_t = undefined;
switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) {
0 => res.* = .{ 0 => res.* = .{
@ -4834,7 +4834,7 @@ pub const FutimensError = error{
} || UnexpectedError; } || UnexpectedError;
pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
// TODO WASI encodes `wasi.fstflags` to signify magic values // TODO WASI encodes `wasi.fstflags` to signify magic values
// similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
// this here, but we should really handle it somehow. // this here, but we should really handle it somehow.
@ -5581,7 +5581,10 @@ var has_copy_file_range_syscall = std.atomic.Atomic(bool).init(true);
/// ///
/// Maximum offsets on Linux are `math.maxInt(i64)`. /// Maximum offsets on Linux are `math.maxInt(i64)`.
pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize { pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
const call_cfr = comptime if (builtin.link_libc) const call_cfr = comptime if (std.Target.current.os.tag == .wasi)
// WASI-libc doesn't have copy_file_range.
false
else if (builtin.link_libc)
std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok
else else
std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true; std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true;

View file

@ -4,11 +4,13 @@
// The MIT license requires this copyright notice to be included in all copies // The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software. // and substantial portions of the software.
// Convenience types and consts used by std.os module // Convenience types and consts used by std.os module
const builtin = @import("builtin");
pub const STDIN_FILENO = 0; pub const STDIN_FILENO = 0;
pub const STDOUT_FILENO = 1; pub const STDOUT_FILENO = 1;
pub const STDERR_FILENO = 2; pub const STDERR_FILENO = 2;
pub const mode_t = u0; pub const mode_t = u32;
pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc
@ -71,7 +73,8 @@ pub const kernel_stat = struct {
} }
}; };
pub const AT_REMOVEDIR: u32 = 1; // there's no AT_REMOVEDIR in WASI, but we simulate here to match other OSes pub const AT_REMOVEDIR: u32 = 0x4;
pub const AT_FDCWD: fd_t = -2;
// As defined in the wasi_snapshot_preview1 spec file: // As defined in the wasi_snapshot_preview1 spec file:
// https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx // https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx
@ -111,6 +114,7 @@ pub const EADDRINUSE: errno_t = 3;
pub const EADDRNOTAVAIL: errno_t = 4; pub const EADDRNOTAVAIL: errno_t = 4;
pub const EAFNOSUPPORT: errno_t = 5; pub const EAFNOSUPPORT: errno_t = 5;
pub const EAGAIN: errno_t = 6; pub const EAGAIN: errno_t = 6;
pub const EWOULDBLOCK = EAGAIN;
pub const EALREADY: errno_t = 7; pub const EALREADY: errno_t = 7;
pub const EBADF: errno_t = 8; pub const EBADF: errno_t = 8;
pub const EBADMSG: errno_t = 9; pub const EBADMSG: errno_t = 9;
@ -163,6 +167,7 @@ pub const ENOTEMPTY: errno_t = 55;
pub const ENOTRECOVERABLE: errno_t = 56; pub const ENOTRECOVERABLE: errno_t = 56;
pub const ENOTSOCK: errno_t = 57; pub const ENOTSOCK: errno_t = 57;
pub const ENOTSUP: errno_t = 58; pub const ENOTSUP: errno_t = 58;
pub const EOPNOTSUPP = ENOTSUP;
pub const ENOTTY: errno_t = 59; pub const ENOTTY: errno_t = 59;
pub const ENXIO: errno_t = 60; pub const ENXIO: errno_t = 60;
pub const EOVERFLOW: errno_t = 61; pub const EOVERFLOW: errno_t = 61;
@ -204,7 +209,7 @@ pub const EVENTTYPE_FD_WRITE: eventtype_t = 2;
pub const exitcode_t = u32; pub const exitcode_t = u32;
pub const fd_t = u32; pub const fd_t = if (builtin.link_libc) c_int else u32;
pub const fdflags_t = u16; pub const fdflags_t = u16;
pub const FDFLAG_APPEND: fdflags_t = 0x0001; pub const FDFLAG_APPEND: fdflags_t = 0x0001;
@ -271,11 +276,33 @@ pub const linkcount_t = u64;
pub const lookupflags_t = u32; pub const lookupflags_t = u32;
pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001;
pub const oflags_t = u16; pub usingnamespace if (builtin.link_libc) struct {
pub const O_CREAT: oflags_t = 0x0001; pub const O_ACCMODE = (O_EXEC | O_RDWR | O_SEARCH);
pub const O_DIRECTORY: oflags_t = 0x0002; pub const O_APPEND = 1 << 0; // = __WASI_FDFLAGS_APPEND
pub const O_EXCL: oflags_t = 0x0004; pub const O_CLOEXEC = (0);
pub const O_TRUNC: oflags_t = 0x0008; pub const O_CREAT = ((1 << 0) << 12); // = __WASI_OFLAGS_CREAT << 12
pub const O_DIRECTORY = ((1 << 1) << 12); // = __WASI_OFLAGS_DIRECTORY << 12
pub const O_DSYNC = (1 << 1); // = __WASI_FDFLAGS_DSYNC
pub const O_EXCL = ((1 << 2) << 12); // = __WASI_OFLAGS_EXCL << 12
pub const O_EXEC = (0x02000000);
pub const O_NOCTTY = (0);
pub const O_NOFOLLOW = (0x01000000);
pub const O_NONBLOCK = (1 << 2); // = __WASI_FDFLAGS_NONBLOCK
pub const O_RDONLY = (0x04000000);
pub const O_RDWR = (O_RDONLY | O_WRONLY);
pub const O_RSYNC = (1 << 3); // = __WASI_FDFLAGS_RSYNC
pub const O_SEARCH = (0x08000000);
pub const O_SYNC = (1 << 4); // = __WASI_FDFLAGS_SYNC
pub const O_TRUNC = ((1 << 3) << 12); // = __WASI_OFLAGS_TRUNC << 12
pub const O_TTY_INIT = (0);
pub const O_WRONLY = (0x10000000);
} else struct {
pub const oflags_t = u16;
pub const O_CREAT: oflags_t = 0x0001;
pub const O_DIRECTORY: oflags_t = 0x0002;
pub const O_EXCL: oflags_t = 0x0004;
pub const O_TRUNC: oflags_t = 0x0008;
};
pub const preopentype_t = u8; pub const preopentype_t = u8;
pub const PREOPENTYPE_DIR: preopentype_t = 0; pub const PREOPENTYPE_DIR: preopentype_t = 0;
@ -437,3 +464,23 @@ pub const whence_t = u8;
pub const WHENCE_SET: whence_t = 0; pub const WHENCE_SET: whence_t = 0;
pub const WHENCE_CUR: whence_t = 1; pub const WHENCE_CUR: whence_t = 1;
pub const WHENCE_END: whence_t = 2; pub const WHENCE_END: whence_t = 2;
pub const S_IEXEC = S_IXUSR;
pub const S_IFBLK = 0x6000;
pub const S_IFCHR = 0x2000;
pub const S_IFDIR = 0x4000;
pub const S_IFIFO = 0xc000;
pub const S_IFLNK = 0xa000;
pub const S_IFMT = S_IFBLK | S_IFCHR | S_IFDIR | S_IFIFO | S_IFLNK | S_IFREG | S_IFSOCK;
pub const S_IFREG = 0x8000;
// There's no concept of UNIX domain socket but we define this value here in order to line with other OSes.
pub const S_IFSOCK = 0x1;
pub const SEEK_SET = 0x0; // = __WASI_WHENCE_SET
pub const SEEK_CUR = 0x1; // = __WASI_WHENCE_CUR
pub const SEEK_END = 0x2; // = __WASI_WHENCE_END
pub const LOCK_SH = 0x1;
pub const LOCK_EX = 0x2;
pub const LOCK_NB = 0x4;
pub const LOCK_UN = 0x8;

View file

@ -88,7 +88,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.putMove(key, value); try result.putMove(key, value);
} }
return result; return result;
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
var environ_count: usize = undefined; var environ_count: usize = undefined;
var environ_buf_size: usize = undefined; var environ_buf_size: usize = undefined;
@ -450,7 +450,7 @@ pub const ArgIteratorWindows = struct {
pub const ArgIterator = struct { pub const ArgIterator = struct {
const InnerType = switch (builtin.os.tag) { const InnerType = switch (builtin.os.tag) {
.windows => ArgIteratorWindows, .windows => ArgIteratorWindows,
.wasi => ArgIteratorWasi, .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi,
else => ArgIteratorPosix, else => ArgIteratorPosix,
}; };
@ -469,7 +469,7 @@ pub const ArgIterator = struct {
/// You must deinitialize iterator's internal buffers by calling `deinit` when done. /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator { pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator {
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
return ArgIterator{ .inner = try InnerType.init(allocator) }; return ArgIterator{ .inner = try InnerType.init(allocator) };
} }
@ -507,7 +507,7 @@ pub const ArgIterator = struct {
/// was created with `initWithAllocator` function. /// was created with `initWithAllocator` function.
pub fn deinit(self: *ArgIterator) void { pub fn deinit(self: *ArgIterator) void {
// Unless we're targeting WASI, this is a no-op. // Unless we're targeting WASI, this is a no-op.
if (builtin.os.tag == .wasi) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
self.inner.deinit(); self.inner.deinit();
} }
} }

View file

@ -46,7 +46,9 @@ comptime {
} }
} else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
if (builtin.link_libc and @hasDecl(root, "main")) { if (builtin.link_libc and @hasDecl(root, "main")) {
if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { if (native_arch.isWasm()) {
@export(mainWithoutEnv, .{ .name = "main" });
} else if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
@export(main, .{ .name = "main" }); @export(main, .{ .name = "main" });
} }
} else if (native_os == .windows) { } else if (native_os == .windows) {
@ -420,6 +422,11 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
} }
fn mainWithoutEnv(c_argc: i32, c_argv: [*][*:0]u8) callconv(.C) usize {
std.os.argv = c_argv[0..@intCast(usize, c_argc)];
return @call(.{ .modifier = .always_inline }, callMain, .{});
}
// General error message for a malformed return type // General error message for a malformed return type
const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";

View file

@ -4,6 +4,8 @@
// The MIT license requires this copyright notice to be included in all copies // The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software. // and substantial portions of the software.
const std = @import("std.zig"); const std = @import("std.zig");
const builtin = @import("builtin");
const math = std.math; const math = std.math;
const print = std.debug.print; const print = std.debug.print;
@ -326,7 +328,7 @@ pub const TmpDir = struct {
}; };
fn getCwdOrWasiPreopen() std.fs.Dir { fn getCwdOrWasiPreopen() std.fs.Dir {
if (std.builtin.os.tag == .wasi) { if (std.builtin.os.tag == .wasi and !builtin.link_libc) {
var preopens = std.fs.wasi.PreopenList.init(allocator); var preopens = std.fs.wasi.PreopenList.init(allocator);
defer preopens.deinit(); defer preopens.deinit();
preopens.populate() catch preopens.populate() catch

View file

@ -582,7 +582,9 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
bool want_ssp_attrs = g->build_mode != BuildModeFastRelease && bool want_ssp_attrs = g->build_mode != BuildModeFastRelease &&
g->build_mode != BuildModeSmallRelease && g->build_mode != BuildModeSmallRelease &&
g->link_libc; g->link_libc &&
// WASI-libc does not support stack-protector yet.
!target_is_wasm(g->zig_target);
if (want_ssp_attrs) { if (want_ssp_attrs) {
addLLVMFnAttr(llvm_fn, "sspstrong"); addLLVMFnAttr(llvm_fn, "sspstrong");
addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4"); addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4");

View file

@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
var case = ctx.exe("hello world with updates", linux_x64); var case = ctx.exe("hello world with updates", linux_x64);
case.addError("", &[_][]const u8{ case.addError("", &[_][]const u8{
":93:9: error: struct 'tmp.tmp' has no member named 'main'", ":95:9: error: struct 'tmp.tmp' has no member named 'main'",
}); });
// Incorrect return type // Incorrect return type

View file

@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void {
{ {
var case = ctx.exe("hello world with updates", target); var case = ctx.exe("hello world with updates", target);
case.addError("", &[_][]const u8{ case.addError("", &[_][]const u8{
":93:9: error: struct 'tmp.tmp' has no member named 'main'", ":95:9: error: struct 'tmp.tmp' has no member named 'main'",
}); });
// Incorrect return type // Incorrect return type

View file

@ -57,6 +57,14 @@ const test_targets = blk: {
.link_libc = false, .link_libc = false,
.single_threaded = true, .single_threaded = true,
}, },
TestTarget{
.target = .{
.cpu_arch = .wasm32,
.os_tag = .wasi,
},
.link_libc = true,
.single_threaded = true,
},
TestTarget{ TestTarget{
.target = .{ .target = .{