Skip to content

Support all operating systems, even hobby OSes, with a "bring your own OS layer" in the std lib #3784

Description

@andrewrk

The std.Target.Os enum recognizes the following operating systems:

 freestanding
ananas
cloudabi
dragonfly
freebsd
fuchsia
ios
kfreebsd
linux
lv2
macosx
netbsd
openbsd
solaris
windows
haiku
minix
rtems
nacl
cnk
aix
cuda
nvcl
amdhsa
ps4
elfiamcu
tvos
watchos
mesa3d
contiki
amdpal
hermit
hurd
wasi
emscripten
zen
uefi

This list is the list of targets that LLVM supports + zen (a hobby OS not maintained for over 1 year) + UEFI.

It doesn't make sense to put every hobby OS into this list, but it does make sense to support them! It should be possible for people to take advantage of Zig's cross platform abstractions without having to get support for their hobby OS upstreamed into Zig.

I propose to do this with 2 things:

  • Add other tag to std.Target.Os
  • Support an OS layer struct exposed in the root source file (next to pub fn main())

This allows hobby OS developers to maintain a zig package that makes the zig standard library support their OS. Application developers could use it like this:

pubconstos=@import("my_hobby_os_package");
pubfnmain() void {
// ...
}

Next, standard library abstractions will detect when they should utilize this. If the operating system is POSIX compliant, then many things will Just Work. For example, std.os.read is currently defined like this:

/// Returns the number of bytes that were read, which can be less than/// buf.len. If 0 bytes were read, that means EOF./// If the application has a global event loop enabled, EAGAIN is handled/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.pubfnread(fd: fd_t, buf: []u8) ReadError!usize {
if (builtin.os==.windows) {
returnwindows.ReadFile(fd, buf);
}
if (builtin.os==.wasiand!builtin.link_libc) {
constiovs= [1]iovec{iovec{
.iov_base=buf.ptr,
.iov_len=buf.len,
}};
varnread: usize=undefined;
switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
0=>returnnread,
else=>|err|returnunexpectedErrno(err),
}
}
while (true) {
constrc=system.read(fd, buf.ptr, buf.len);
switch (errno(rc)) {
0=>return@intCast(usize, rc),
EINTR=>continue,
EINVAL=>unreachable,
EFAULT=>unreachable,
EAGAIN=>if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(fd);
continue;
} else {
returnerror.WouldBlock;
},
EBADF=>unreachable, // Always a race condition.EIO=>returnerror.InputOutput,
EISDIR=>returnerror.IsDir,
ENOBUFS=>returnerror.SystemResources,
ENOMEM=>returnerror.SystemResources,
ECONNRESET=>returnerror.ConnectionResetByPeer,
else=>|err|returnunexpectedErrno(err),
}
}
returnindex;
}

system in this refers to:

/// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface.pubconstsystem=if (builtin.link_libc) std.celseswitch (builtin.os) {
.macosx, .ios, .watchos, .tvos=>darwin,
.freebsd=>freebsd,
.linux=>linux,
.netbsd=>netbsd,
.dragonfly=>dragonfly,
.wasi=>wasi,
.windows=>windows,
.zen=>zen,
else=>struct {},
};

Here we would change else => struct{}, to look for @import("root").os if it was provided. With this modification, as long as the OS package defined all the constants (such as fd_t and EISDIR), then std.os.write would end up calling the write function from the hobby OS package, and everything Just Works.

Some abstractions may not work so smoothly; in this case there could be code that looks something like this:

fndoTheOsThing() void {
if (std.builtin.os==.otherand@hasDecl(root, "os") and@hasDecl(root.os, "doTheOsThing")) {
return@import("root").os.doTheOsThing();
}
}

Really, the check for the "other" OS isn't even necessary. Allowing applications to override fundamental OS functions could be useful on any operating system.

With this proposal implemented, Zig would have very near first class support for all operating systems. The main difference between upstream-recognized OSes and "other" OSes would be where the support is maintained - upstream zig std lib, or in a third party "OS layer" package.

cc @pixelherodev

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedThis proposal is planned.proposalThis issue suggests language modifications. If it also has the "accepted" label then it is planned.standard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions