starting libuv bindings

This commit is contained in:
Mitchell Hashimoto
2022-04-21 09:33:32 -07:00
parent 55351487a9
commit 21ee510471
9 changed files with 391 additions and 5 deletions

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "vendor/cglm"]
path = vendor/cglm
url = https://github.com/recp/cglm.git
[submodule "vendor/libuv"]
path = vendor/libuv
url = https://github.com/libuv/libuv.git

View File

@ -3,6 +3,7 @@ const Builder = std.build.Builder;
const LibExeObjStep = std.build.LibExeObjStep;
const glfw = @import("vendor/mach/glfw/build.zig");
const ft = @import("src/freetype/build.zig");
const uv = @import("src/libuv/build.zig");
pub fn build(b: *std.build.Builder) !void {
const target = b.standardTargetOptions(.{});
@ -23,11 +24,8 @@ pub fn build(b: *std.build.Builder) !void {
const ftlib = try ft.create(b, target, mode, .{});
ftlib.link(exe);
// to link to system:
// exe.linkSystemLibrary("freetype2");
// exe.linkSystemLibrary("libpng");
// exe.linkSystemLibrary("bzip2");
// ftlib.addIncludeDirs(exe);
const libuv = try uv.create(b, target, mode);
// stb if we need it
// exe.addIncludeDir("vendor/stb");
@ -46,6 +44,7 @@ pub fn build(b: *std.build.Builder) !void {
const test_step = b.step("test", "Run all tests");
const lib_tests = b.addTest("src/main.zig");
ftlib.link(lib_tests);
libuv.link(lib_tests);
lib_tests.addIncludeDir("vendor/glad/include/");
lib_tests.addCSourceFile("vendor/glad/src/gl.c", &.{});
test_step.dependOn(&lib_tests.step);

53
src/libuv/Loop.zig Normal file
View File

@ -0,0 +1,53 @@
const Loop = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const testing = std.testing;
const c = @import("c.zig");
const errors = @import("error.zig");
loop: *c.uv_loop_t,
/// Initialize a new uv_loop.
pub fn init(alloc: Allocator) !Loop {
// The uv_loop_t type MUST be heap allocated and must not be copied.
// I can't find a definitive source on this, but the test suite starts
// hanging in weird places and doing bad things when it is copied.
const loop = try alloc.create(c.uv_loop_t);
try errors.convertError(c.uv_loop_init(loop));
return Loop{ .loop = loop };
}
/// Releases all internal loop resources. Call this function only when the
/// loop has finished executing and all open handles and requests have been
/// closed, or this will silently fail (in debug mode it will panic).
pub fn deinit(self: *Loop, alloc: Allocator) void {
// deinit functions idiomatically cannot fail in Zig, so we do the
// next best thing here and assert so that in debug mode you'll get
// a crash.
std.debug.assert(c.uv_loop_close(self.loop) >= 0);
alloc.destroy(self.loop);
self.* = undefined;
}
/// This function runs the event loop. See RunMode for mode documentation.
///
/// This is not reentrant. It must not be called from a callback.
pub fn run(self: *Loop, mode: RunMode) !u32 {
const res = c.uv_run(self.loop, @enumToInt(mode));
try errors.convertError(res);
return @intCast(u32, res);
}
/// Mode used to run the loop with uv_run().
pub const RunMode = enum(c.uv_run_mode) {
default = c.UV_RUN_DEFAULT,
once = c.UV_RUN_ONCE,
nowait = c.UV_RUN_NOWAIT,
};
test {
var loop = try init(testing.allocator);
defer loop.deinit(testing.allocator);
try testing.expectEqual(@as(u32, 0), try loop.run(.nowait));
}

132
src/libuv/build.zig Normal file
View File

@ -0,0 +1,132 @@
const std = @import("std");
/// This is the type returned by create.
pub const Library = struct {
step: *std.build.LibExeObjStep,
/// statically link this library into the given step
pub fn link(self: Library, other: *std.build.LibExeObjStep) void {
self.addIncludeDirs(other);
other.linkLibrary(self.step);
}
/// only add the include dirs to the given step. This is useful if building
/// a static library that you don't want to fully link in the code of this
/// library.
pub fn addIncludeDirs(self: Library, other: *std.build.LibExeObjStep) void {
_ = self;
other.addIncludeDir(include_dir);
}
};
/// Create this library. This is the primary API users of build.zig should
/// use to link this library to their application. On the resulting Library,
/// call the link function and given your own application step.
pub fn create(
b: *std.build.Builder,
target: std.zig.CrossTarget,
mode: std.builtin.Mode,
) !Library {
const ret = b.addStaticLibrary("uv", null);
ret.setTarget(target);
ret.setBuildMode(mode);
var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit();
// try flags.appendSlice(&.{});
if (!target.isWindows()) {
try flags.appendSlice(&.{
"-D_FILE_OFFSET_BITS=64",
"-D_LARGEFILE_SOURCE",
});
}
if (target.isLinux()) {
try flags.appendSlice(&.{
"-D_GNU_SOURCE",
"-D_POSIX_C_SOURCE=200112",
});
}
// C files common to all platforms
ret.addCSourceFiles(&.{
root() ++ "src/fs-poll.c",
root() ++ "src/idna.c",
root() ++ "src/inet.c",
root() ++ "src/random.c",
root() ++ "src/strscpy.c",
root() ++ "src/strtok.c",
root() ++ "src/threadpool.c",
root() ++ "src/timer.c",
root() ++ "src/uv-common.c",
root() ++ "src/uv-data-getter-setters.c",
root() ++ "src/version.c",
}, flags.items);
if (!target.isWindows()) {
ret.addCSourceFiles(&.{
root() ++ "src/unix/async.c",
root() ++ "src/unix/core.c",
root() ++ "src/unix/dl.c",
root() ++ "src/unix/fs.c",
root() ++ "src/unix/getaddrinfo.c",
root() ++ "src/unix/getnameinfo.c",
root() ++ "src/unix/loop-watcher.c",
root() ++ "src/unix/loop.c",
root() ++ "src/unix/pipe.c",
root() ++ "src/unix/poll.c",
root() ++ "src/unix/process.c",
root() ++ "src/unix/random-devurandom.c",
root() ++ "src/unix/signal.c",
root() ++ "src/unix/stream.c",
root() ++ "src/unix/tcp.c",
root() ++ "src/unix/thread.c",
root() ++ "src/unix/tty.c",
root() ++ "src/unix/udp.c",
}, flags.items);
}
if (target.isLinux() or target.isDarwin()) {
ret.addCSourceFiles(&.{
root() ++ "src/unix/proctitle.c",
}, flags.items);
}
if (target.isLinux()) {
ret.addCSourceFiles(&.{
root() ++ "src/unix/linux-core.c",
root() ++ "src/unix/linux-inotify.c",
root() ++ "src/unix/linux-syscalls.c",
root() ++ "src/unix/procfs-exepath.c",
root() ++ "src/unix/random-getrandom.c",
root() ++ "src/unix/random-sysctl-linux.c",
root() ++ "src/unix/epoll.c",
}, flags.items);
}
ret.addIncludeDir(include_dir);
ret.addIncludeDir(root() ++ "src");
if (target.isWindows()) {
ret.linkSystemLibrary("psapi");
ret.linkSystemLibrary("user32");
ret.linkSystemLibrary("advapi32");
ret.linkSystemLibrary("iphlpapi");
ret.linkSystemLibrary("userenv");
ret.linkSystemLibrary("ws2_32");
}
if (target.isLinux()) {
ret.linkSystemLibrary("pthread");
}
ret.linkLibC();
return Library{ .step = ret };
}
fn root() []const u8 {
return (std.fs.path.dirname(@src().file) orelse unreachable) ++ "/../../vendor/libuv/";
}
/// Directories with our includes.
const include_dir = root() ++ "include";

3
src/libuv/c.zig Normal file
View File

@ -0,0 +1,3 @@
pub usingnamespace @cImport({
@cInclude("uv.h");
});

185
src/libuv/error.zig Normal file
View File

@ -0,0 +1,185 @@
const std = @import("std");
const testing = std.testing;
const c = @import("c.zig");
/// Errors that libuv can produce
///
/// http://docs.libuv.org/en/v1.x/errors.html
pub const Error = error{
E2BIG,
EACCES,
EADDRINUSE,
EADDRNOTAVAIL,
EAFNOSUPPORT,
EAGAIN,
EAI_ADDRFAMILY,
EAI_AGAIN,
EAI_BADFLAGS,
EAI_BADHINTS,
EAI_CANCELED,
EAI_FAIL,
EAI_FAMILY,
EAI_MEMORY,
EAI_NODATA,
EAI_NONAME,
EAI_OVERFLOW,
EAI_PROTOCOL,
EAI_SERVICE,
EAI_SOCKTYPE,
EALREADY,
EBADF,
EBUSY,
ECANCELED,
ECHARSET,
ECONNABORTED,
ECONNREFUSED,
ECONNRESET,
EDESTADDRREQ,
EEXIST,
EFAULT,
EFBIG,
EHOSTUNREACH,
EINTR,
EINVAL,
EIO,
EISCONN,
EISDIR,
ELOOP,
EMFILE,
EMSGSIZE,
ENAMETOOLONG,
ENETDOWN,
ENETUNREACH,
ENFILE,
ENOBUFS,
ENODEV,
ENOENT,
ENOMEM,
ENONET,
ENOPROTOOPT,
ENOSPC,
ENOSYS,
ENOTCONN,
ENOTDIR,
ENOTEMPTY,
ENOTSOCK,
ENOTSUP,
EPERM,
EPIPE,
EPROTO,
EPROTONOSUPPORT,
EPROTOTYPE,
ERANGE,
EROFS,
ESHUTDOWN,
ESPIPE,
ESRCH,
ETIMEDOUT,
ETXTBSY,
EXDEV,
UNKNOWN,
EOF,
ENXIO,
EMLINK,
EHOSTDOWN,
EREMOTEIO,
ENOTTY,
EFTYPE,
EILSEQ,
ESOCKTNOSUPPORT,
};
/// Convert the result of a libuv API call to an error (or no error).
pub fn convertError(r: i32) !void {
if (r >= 0) return;
return switch (r) {
c.UV_E2BIG => Error.E2BIG,
c.UV_EACCES => Error.EACCES,
c.UV_EADDRINUSE => Error.EADDRINUSE,
c.UV_EADDRNOTAVAIL => Error.EADDRNOTAVAIL,
c.UV_EAFNOSUPPORT => Error.EAFNOSUPPORT,
c.UV_EAGAIN => Error.EAGAIN,
c.UV_EAI_ADDRFAMILY => Error.EAI_ADDRFAMILY,
c.UV_EAI_AGAIN => Error.EAI_AGAIN,
c.UV_EAI_BADFLAGS => Error.EAI_BADFLAGS,
c.UV_EAI_BADHINTS => Error.EAI_BADHINTS,
c.UV_EAI_CANCELED => Error.EAI_CANCELED,
c.UV_EAI_FAIL => Error.EAI_FAIL,
c.UV_EAI_FAMILY => Error.EAI_FAMILY,
c.UV_EAI_MEMORY => Error.EAI_MEMORY,
c.UV_EAI_NODATA => Error.EAI_NODATA,
c.UV_EAI_NONAME => Error.EAI_NONAME,
c.UV_EAI_OVERFLOW => Error.EAI_OVERFLOW,
c.UV_EAI_PROTOCOL => Error.EAI_PROTOCOL,
c.UV_EAI_SERVICE => Error.EAI_SERVICE,
c.UV_EAI_SOCKTYPE => Error.EAI_SOCKTYPE,
c.UV_EALREADY => Error.EALREADY,
c.UV_EBADF => Error.EBADF,
c.UV_EBUSY => Error.EBUSY,
c.UV_ECANCELED => Error.ECANCELED,
c.UV_ECHARSET => Error.ECHARSET,
c.UV_ECONNABORTED => Error.ECONNABORTED,
c.UV_ECONNREFUSED => Error.ECONNREFUSED,
c.UV_ECONNRESET => Error.ECONNRESET,
c.UV_EDESTADDRREQ => Error.EDESTADDRREQ,
c.UV_EEXIST => Error.EEXIST,
c.UV_EFAULT => Error.EFAULT,
c.UV_EFBIG => Error.EFBIG,
c.UV_EHOSTUNREACH => Error.EHOSTUNREACH,
c.UV_EINTR => Error.EINTR,
c.UV_EINVAL => Error.EINVAL,
c.UV_EIO => Error.EIO,
c.UV_EISCONN => Error.EISCONN,
c.UV_EISDIR => Error.EISDIR,
c.UV_ELOOP => Error.ELOOP,
c.UV_EMFILE => Error.EMFILE,
c.UV_EMSGSIZE => Error.EMSGSIZE,
c.UV_ENAMETOOLONG => Error.ENAMETOOLONG,
c.UV_ENETDOWN => Error.ENETDOWN,
c.UV_ENETUNREACH => Error.ENETUNREACH,
c.UV_ENFILE => Error.ENFILE,
c.UV_ENOBUFS => Error.ENOBUFS,
c.UV_ENODEV => Error.ENODEV,
c.UV_ENOENT => Error.ENOENT,
c.UV_ENOMEM => Error.ENOMEM,
c.UV_ENONET => Error.ENONET,
c.UV_ENOPROTOOPT => Error.ENOPROTOOPT,
c.UV_ENOSPC => Error.ENOSPC,
c.UV_ENOSYS => Error.ENOSYS,
c.UV_ENOTCONN => Error.ENOTCONN,
c.UV_ENOTDIR => Error.ENOTDIR,
c.UV_ENOTEMPTY => Error.ENOTEMPTY,
c.UV_ENOTSOCK => Error.ENOTSOCK,
c.UV_ENOTSUP => Error.ENOTSUP,
c.UV_EPERM => Error.EPERM,
c.UV_EPIPE => Error.EPIPE,
c.UV_EPROTO => Error.EPROTO,
c.UV_EPROTONOSUPPORT => Error.EPROTONOSUPPORT,
c.UV_EPROTOTYPE => Error.EPROTOTYPE,
c.UV_ERANGE => Error.ERANGE,
c.UV_EROFS => Error.EROFS,
c.UV_ESHUTDOWN => Error.ESHUTDOWN,
c.UV_ESPIPE => Error.ESPIPE,
c.UV_ESRCH => Error.ESRCH,
c.UV_ETIMEDOUT => Error.ETIMEDOUT,
c.UV_ETXTBSY => Error.ETXTBSY,
c.UV_EXDEV => Error.EXDEV,
c.UV_UNKNOWN => Error.UNKNOWN,
c.UV_EOF => Error.EOF,
c.UV_ENXIO => Error.ENXIO,
c.UV_EHOSTDOWN => Error.EHOSTDOWN,
c.UV_EREMOTEIO => Error.EREMOTEIO,
c.UV_ENOTTY => Error.ENOTTY,
c.UV_EFTYPE => Error.EFTYPE,
c.UV_EILSEQ => Error.EILSEQ,
c.UV_ESOCKTNOSUPPORT => Error.ESOCKTNOSUPPORT,
else => unreachable,
};
}
test {
// This is mostly just forcing our error type and function to be
// codegenned and run once to ensure we have all the types.
try testing.expectError(Error.EFTYPE, convertError(c.UV_EFTYPE));
}

7
src/libuv/main.zig Normal file
View File

@ -0,0 +1,7 @@
const Loop = @import("Loop.zig");
const Error = @import("error.zig").Error;
test {
_ = Loop;
_ = Error;
}

View File

@ -26,4 +26,7 @@ test {
_ = @import("Command.zig");
_ = @import("TempDir.zig");
_ = @import("terminal/Terminal.zig");
// TEMP
_ = @import("libuv/main.zig");
}

1
vendor/libuv vendored Submodule

@ -0,0 +1 @@
Subproject commit 9e59aa1bc8c4d215ea3e05eafec7181747206f67