Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASI,libc: fix libstd with wasi-libc linkage, and enable tests. #9227

Merged
merged 5 commits into from Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/std/Thread.zig
Expand Up @@ -24,7 +24,7 @@ pub const Condition = @import("Thread/Condition.zig");

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 Impl = if (target.os.tag == .windows)
Expand Down
1 change: 1 addition & 0 deletions lib/std/c.zig
Expand Up @@ -31,6 +31,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {
.fuchsia => @import("c/fuchsia.zig"),
.minix => @import("c/minix.zig"),
.emscripten => @import("c/emscripten.zig"),
.wasi => @import("c/wasi.zig"),
else => struct {},
};

Expand Down
60 changes: 60 additions & 0 deletions lib/std/c/wasi.zig
@@ -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,
};
}
};
59 changes: 35 additions & 24 deletions lib/std/fs.zig
Expand Up @@ -647,6 +647,9 @@ pub const Dir = struct {
/// 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.
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;
start_over: while (true) {
if (self.index >= self.end_index) {
Expand Down Expand Up @@ -858,7 +861,7 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
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);
}
const path_c = try os.toPosixPath(sub_path);
Expand Down Expand Up @@ -934,14 +937,18 @@ pub const Dir = struct {
try os.openatZ(self.fd, sub_path, os_flags, 0);
errdefer os.close(fd);

if (!has_flock_open_flags and flags.lock != .None) {
// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
.None => unreachable,
.Shared => os.LOCK_SH | lock_nonblocking,
.Exclusive => os.LOCK_EX | lock_nonblocking,
});
// WASI doesn't have os.flock so we intetinally check OS prior to the inner if block
// since it is not compiltime-known and we need to avoid undefined symbol in Wasm.
if (builtin.target.os.tag != .wasi) {
if (!has_flock_open_flags and flags.lock != .None) {
Comment on lines +942 to +943
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (builtin.target.os.tag != .wasi) {
if (!has_flock_open_flags and flags.lock != .None) {
if (!has_flock_open_flags and flags.lock != .None and builtin.target.os.tag != .wasi) {

I think we can put all checks in one if.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is a little bit trick.. we need to remove out this if block since since the expression is not compiltime-known, and WASI doesn't have os.flock that's why I put the OS check prior to the if block. Added comment about this: 68617c9

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah gotcha, makes sense!

// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
.None => unreachable,
.Shared => os.LOCK_SH | lock_nonblocking,
.Exclusive => os.LOCK_EX | lock_nonblocking,
});
}
}

if (has_flock_open_flags and flags.lock_nonblocking) {
Expand Down Expand Up @@ -1014,7 +1021,7 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
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);
}
const path_c = try os.toPosixPath(sub_path);
Expand Down Expand Up @@ -1084,14 +1091,18 @@ pub const Dir = struct {
try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
errdefer os.close(fd);

if (!has_flock_open_flags and flags.lock != .None) {
// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
.None => unreachable,
.Shared => os.LOCK_SH | lock_nonblocking,
.Exclusive => os.LOCK_EX | lock_nonblocking,
});
// WASI doesn't have os.flock so we intetinally check OS prior to the inner if block
// since it is not compiltime-known and we need to avoid undefined symbol in Wasm.
if (builtin.target.os.tag != .wasi) {
if (!has_flock_open_flags and flags.lock != .None) {
// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
.None => unreachable,
.Shared => os.LOCK_SH | lock_nonblocking,
.Exclusive => os.LOCK_EX | lock_nonblocking,
});
}
}

if (has_flock_open_flags and flags.lock_nonblocking) {
Expand Down Expand Up @@ -1372,7 +1383,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
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);
} else {
const sub_path_c = try os.toPosixPath(sub_path);
Expand Down Expand Up @@ -1519,7 +1530,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
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) {
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
else => |e| return e,
Expand Down Expand Up @@ -1582,7 +1593,7 @@ pub const Dir = struct {
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
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) {
error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
else => |e| return e,
Expand Down Expand Up @@ -1641,7 +1652,7 @@ pub const Dir = struct {
sym_link_path: []const u8,
flags: SymLinkFlags,
) !void {
if (builtin.os.tag == .wasi) {
if (builtin.os.tag == .wasi and !builtin.link_libc) {
return self.symLinkWasi(target_path, sym_link_path, flags);
}
if (builtin.os.tag == .windows) {
Expand Down Expand Up @@ -1694,7 +1705,7 @@ pub const Dir = struct {
/// The return value is a slice of `buffer`, from index `0`.
/// Asserts that the path parameter has no null bytes.
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);
}
if (builtin.os.tag == .windows) {
Expand Down Expand Up @@ -2113,7 +2124,7 @@ pub const Dir = struct {
pub fn cwd() Dir {
if (builtin.os.tag == .windows) {
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");
} else {
return Dir{ .fd = os.AT_FDCWD };
Expand Down
37 changes: 17 additions & 20 deletions lib/std/fs/file.zig
Expand Up @@ -326,26 +326,23 @@ pub const File = struct {
.inode = st.ino,
.size = @bitCast(u64, st.size),
.mode = st.mode,
.kind = switch (builtin.os.tag) {
.wasi => switch (st.filetype) {
os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
os.FILETYPE_DIRECTORY => Kind.Directory,
os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
os.FILETYPE_REGULAR_FILE => Kind.File,
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
else => switch (st.mode & os.S_IFMT) {
os.S_IFBLK => Kind.BlockDevice,
os.S_IFCHR => Kind.CharacterDevice,
os.S_IFDIR => Kind.Directory,
os.S_IFIFO => Kind.NamedPipe,
os.S_IFLNK => Kind.SymLink,
os.S_IFREG => Kind.File,
os.S_IFSOCK => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
.kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
os.FILETYPE_DIRECTORY => Kind.Directory,
os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
os.FILETYPE_REGULAR_FILE => Kind.File,
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
else => Kind.Unknown,
} else switch (st.mode & os.S_IFMT) {
os.S_IFBLK => Kind.BlockDevice,
os.S_IFCHR => Kind.CharacterDevice,
os.S_IFDIR => Kind.Directory,
os.S_IFIFO => Kind.NamedPipe,
os.S_IFLNK => Kind.SymLink,
os.S_IFREG => Kind.File,
os.S_IFSOCK => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
.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,
Expand Down
2 changes: 1 addition & 1 deletion lib/std/fs/wasi.zig
Expand Up @@ -169,7 +169,7 @@ pub const PreopenList = struct {
};

test "extracting WASI preopens" {
if (std.builtin.os.tag != .wasi) return error.SkipZigTest;
if (std.builtin.os.tag != .wasi or std.builtin.link_libc) return error.SkipZigTest;

var preopens = PreopenList.init(std.testing.allocator);
defer preopens.deinit();
Expand Down
2 changes: 1 addition & 1 deletion lib/std/io/c_writer.zig
Expand Up @@ -35,7 +35,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
}

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 out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
Expand Down