introduce operating system version ranges as part of the target

* re-introduce `std.build.Target` which is distinct from `std.Target`.
   `std.build.Target` wraps `std.Target` so that it can be annotated as
   "the native target" or an explicitly specified target.
 * `std.Target.Os` is moved to `std.Target.Os.Tag`. The former is now a
   struct which has the tag as well as version range information.
 * `std.elf` gains some more ELF header constants.
 * `std.Target.parse` gains the ability to parse operating system
   version ranges as well as glibc version.
 * Added `std.Target.isGnuLibC()`.
 * self-hosted dynamic linker detection and glibc version detection.
   This also adds the improved logic using `/usr/bin/env` rather than
   invoking the system C compiler to find the dynamic linker when zig
   is statically linked. Related: #2084
   Note: this `/usr/bin/env` code is work-in-progress.
 * `-target-glibc` CLI option is removed in favor of the new `-target`
   syntax. Example: `-target x86_64-linux-gnu.2.27`

closes #1907
This commit is contained in:
Andrew Kelley 2020-02-25 01:52:27 -05:00
parent fba39ff331
commit 4616af0ca4
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
59 changed files with 1274 additions and 932 deletions

View File

@ -971,9 +971,9 @@ pub const Builder = struct {
}; };
test "builder.findProgram compiles" { test "builder.findProgram compiles" {
// TODO: uncomment and fix the leak var buf: [1000]u8 = undefined;
// const builder = try Builder.create(std.testing.allocator, "zig", "zig-cache", "zig-cache"); var fba = std.heap.FixedBufferAllocator.init(&buf);
const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache"); const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache");
defer builder.destroy(); defer builder.destroy();
_ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
} }
@ -981,11 +981,37 @@ test "builder.findProgram compiles" {
/// Deprecated. Use `builtin.Version`. /// Deprecated. Use `builtin.Version`.
pub const Version = builtin.Version; pub const Version = builtin.Version;
/// Deprecated. Use `std.Target.Cross`.
pub const CrossTarget = std.Target.Cross;
/// Deprecated. Use `std.Target`. /// Deprecated. Use `std.Target`.
pub const Target = std.Target; pub const CrossTarget = std.Target;
/// Wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target.
pub const Target = union(enum) {
Native,
Cross: std.Target,
pub fn getTarget(self: Target) std.Target {
return switch (self) {
.Native => std.Target.current,
.Cross => |t| t,
};
}
pub fn getOs(self: Target) std.Target.Os.Tag {
return self.getTarget().os.tag;
}
pub fn getCpu(self: Target) std.Target.Cpu {
return self.getTarget().cpu;
}
pub fn getAbi(self: Target) std.Target.Abi {
return self.getTarget().abi;
}
pub fn getArch(self: Target) std.Target.Cpu.Arch {
return self.getCpu().arch;
}
};
pub const Pkg = struct { pub const Pkg = struct {
name: []const u8, name: []const u8,

View File

@ -82,7 +82,7 @@ pub const RunStep = struct {
var key: []const u8 = undefined; var key: []const u8 = undefined;
var prev_path: ?[]const u8 = undefined; var prev_path: ?[]const u8 = undefined;
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
key = "Path"; key = "Path";
prev_path = env_map.get(key); prev_path = env_map.get(key);
if (prev_path == null) { if (prev_path == null) {

View File

@ -411,7 +411,7 @@ pub const Version = struct {
} }
}; };
pub fn order(lhs: Version, rhs: version) std.math.Order { pub fn order(lhs: Version, rhs: Version) std.math.Order {
if (lhs.major < rhs.major) return .lt; if (lhs.major < rhs.major) return .lt;
if (lhs.major > rhs.major) return .gt; if (lhs.major > rhs.major) return .gt;
if (lhs.minor < rhs.minor) return .lt; if (lhs.minor < rhs.minor) return .lt;
@ -504,7 +504,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
root.os.panic(msg, error_return_trace); root.os.panic(msg, error_return_trace);
unreachable; unreachable;
} }
switch (os) { switch (os.tag) {
.freestanding => { .freestanding => {
while (true) { while (true) {
@breakpoint(); @breakpoint();

View File

@ -1,5 +1,5 @@
const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const builtin = std.builtin;
const page_size = std.mem.page_size; const page_size = std.mem.page_size;
pub const tokenizer = @import("c/tokenizer.zig"); pub const tokenizer = @import("c/tokenizer.zig");
@ -10,7 +10,7 @@ pub const ast = @import("c/ast.zig");
pub usingnamespace @import("os/bits.zig"); pub usingnamespace @import("os/bits.zig");
pub usingnamespace switch (builtin.os) { pub usingnamespace switch (std.Target.current.os.tag) {
.linux => @import("c/linux.zig"), .linux => @import("c/linux.zig"),
.windows => @import("c/windows.zig"), .windows => @import("c/windows.zig"),
.macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"),
@ -46,17 +46,16 @@ pub fn versionCheck(glibc_version: builtin.Version) type {
return struct { return struct {
pub const ok = blk: { pub const ok = blk: {
if (!builtin.link_libc) break :blk false; if (!builtin.link_libc) break :blk false;
switch (builtin.abi) { if (std.Target.current.abi.isMusl()) break :blk true;
.musl, .musleabi, .musleabihf => break :blk true, if (std.Target.current.isGnuLibC()) {
.gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => { const ver = std.Target.current.os.version_range.linux.glibc;
const ver = builtin.glibc_version orelse break :blk false; const order = ver.order(glibc_version);
if (ver.major < glibc_version.major) break :blk false; break :blk switch (order) {
if (ver.major > glibc_version.major) break :blk true; .gt, .eq => true,
if (ver.minor < glibc_version.minor) break :blk false; .lt => false,
if (ver.minor > glibc_version.minor) break :blk true; };
break :blk ver.patch >= glibc_version.patch; } else {
}, break :blk false;
else => break :blk false,
} }
}; };
}; };

View File

@ -94,7 +94,7 @@ pub const pthread_cond_t = extern struct {
size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T,
}; };
const __SIZEOF_PTHREAD_COND_T = 48; const __SIZEOF_PTHREAD_COND_T = 48;
const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) { const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch (builtin.abi) {
.musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24,
.gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) {
.aarch64 => 48, .aarch64 => 48,

View File

@ -17,9 +17,9 @@ const TailQueue = std.TailQueue;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
pub const ChildProcess = struct { pub const ChildProcess = struct {
pid: if (builtin.os == .windows) void else i32, pid: if (builtin.os.tag == .windows) void else i32,
handle: if (builtin.os == .windows) windows.HANDLE else void, handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
thread_handle: if (builtin.os == .windows) windows.HANDLE else void, thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
allocator: *mem.Allocator, allocator: *mem.Allocator,
@ -39,15 +39,15 @@ pub const ChildProcess = struct {
stderr_behavior: StdIo, stderr_behavior: StdIo,
/// Set to change the user id when spawning the child process. /// Set to change the user id when spawning the child process.
uid: if (builtin.os == .windows) void else ?u32, uid: if (builtin.os.tag == .windows) void else ?u32,
/// Set to change the group id when spawning the child process. /// Set to change the group id when spawning the child process.
gid: if (builtin.os == .windows) void else ?u32, gid: if (builtin.os.tag == .windows) void else ?u32,
/// Set to change the current working directory when spawning the child process. /// Set to change the current working directory when spawning the child process.
cwd: ?[]const u8, cwd: ?[]const u8,
err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
expand_arg0: Arg0Expand, expand_arg0: Arg0Expand,
@ -96,8 +96,8 @@ pub const ChildProcess = struct {
.term = null, .term = null,
.env_map = null, .env_map = null,
.cwd = null, .cwd = null,
.uid = if (builtin.os == .windows) {} else null, .uid = if (builtin.os.tag == .windows) {} else null,
.gid = if (builtin.os == .windows) {} else null, .gid = if (builtin.os.tag == .windows) {} else null,
.stdin = null, .stdin = null,
.stdout = null, .stdout = null,
.stderr = null, .stderr = null,
@ -118,7 +118,7 @@ pub const ChildProcess = struct {
/// On success must call `kill` or `wait`. /// On success must call `kill` or `wait`.
pub fn spawn(self: *ChildProcess) SpawnError!void { pub fn spawn(self: *ChildProcess) SpawnError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return self.spawnWindows(); return self.spawnWindows();
} else { } else {
return self.spawnPosix(); return self.spawnPosix();
@ -132,7 +132,7 @@ pub const ChildProcess = struct {
/// Forcibly terminates child process and then cleans up all resources. /// Forcibly terminates child process and then cleans up all resources.
pub fn kill(self: *ChildProcess) !Term { pub fn kill(self: *ChildProcess) !Term {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return self.killWindows(1); return self.killWindows(1);
} else { } else {
return self.killPosix(); return self.killPosix();
@ -162,7 +162,7 @@ pub const ChildProcess = struct {
/// Blocks until child process terminates and then cleans up all resources. /// Blocks until child process terminates and then cleans up all resources.
pub fn wait(self: *ChildProcess) !Term { pub fn wait(self: *ChildProcess) !Term {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return self.waitWindows(); return self.waitWindows();
} else { } else {
return self.waitPosix(); return self.waitPosix();
@ -307,7 +307,7 @@ pub const ChildProcess = struct {
fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term {
defer destroyPipe(self.err_pipe); defer destroyPipe(self.err_pipe);
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
var fd = [1]std.os.pollfd{std.os.pollfd{ var fd = [1]std.os.pollfd{std.os.pollfd{
.fd = self.err_pipe[0], .fd = self.err_pipe[0],
.events = std.os.POLLIN, .events = std.os.POLLIN,
@ -402,7 +402,7 @@ pub const ChildProcess = struct {
// This pipe is used to communicate errors between the time of fork // This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process. // and execve from the child process to the parent process.
const err_pipe = blk: { const err_pipe = blk: {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
const fd = try os.eventfd(0, 0); const fd = try os.eventfd(0, 0);
// There's no distinction between the readable and the writeable // There's no distinction between the readable and the writeable
// end with eventfd // end with eventfd

View File

@ -4,8 +4,8 @@ const debug = std.debug;
const mem = std.mem; const mem = std.mem;
const testing = std.testing; const testing = std.testing;
pub const line_sep = switch (builtin.os) { pub const line_sep = switch (builtin.os.tag) {
builtin.Os.windows => "\r\n", .windows => "\r\n",
else => "\n", else => "\n",
}; };

View File

@ -1,4 +1,5 @@
const std = @import("std.zig"); const std = @import("std.zig");
const builtin = std.builtin;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const io = std.io; const io = std.io;
@ -11,7 +12,6 @@ const macho = std.macho;
const coff = std.coff; const coff = std.coff;
const pdb = std.pdb; const pdb = std.pdb;
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
const builtin = @import("builtin");
const root = @import("root"); const root = @import("root");
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
const File = std.fs.File; const File = std.fs.File;
@ -101,7 +101,7 @@ pub fn detectTTYConfig() TTY.Config {
} else |_| { } else |_| {
if (stderr_file.supportsAnsiEscapeCodes()) { if (stderr_file.supportsAnsiEscapeCodes()) {
return .escape_codes; return .escape_codes;
} else if (builtin.os == .windows and stderr_file.isTty()) { } else if (builtin.os.tag == .windows and stderr_file.isTty()) {
return .windows_api; return .windows_api;
} else { } else {
return .no_color; return .no_color;
@ -155,7 +155,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
/// chopping off the irrelevant frames and shifting so that the returned addresses pointer /// chopping off the irrelevant frames and shifting so that the returned addresses pointer
/// equals the passed in addresses pointer. /// equals the passed in addresses pointer.
pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const addrs = stack_trace.instruction_addresses; const addrs = stack_trace.instruction_addresses;
const u32_addrs_len = @intCast(u32, addrs.len); const u32_addrs_len = @intCast(u32, addrs.len);
const first_addr = first_address orelse { const first_addr = first_address orelse {
@ -231,7 +231,7 @@ pub fn assert(ok: bool) void {
pub fn panic(comptime format: []const u8, args: var) noreturn { pub fn panic(comptime format: []const u8, args: var) noreturn {
@setCold(true); @setCold(true);
// TODO: remove conditional once wasi / LLVM defines __builtin_return_address // TODO: remove conditional once wasi / LLVM defines __builtin_return_address
const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress(); const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress();
panicExtra(null, first_trace_addr, format, args); panicExtra(null, first_trace_addr, format, args);
} }
@ -361,7 +361,7 @@ pub fn writeCurrentStackTrace(
tty_config: TTY.Config, tty_config: TTY.Config,
start_addr: ?usize, start_addr: ?usize,
) !void { ) !void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);
} }
var it = StackIterator.init(start_addr, null); var it = StackIterator.init(start_addr, null);
@ -418,7 +418,7 @@ pub const TTY = struct {
.Dim => noasync out_stream.write(DIM) catch return, .Dim => noasync out_stream.write(DIM) catch return,
.Reset => noasync out_stream.write(RESET) catch return, .Reset => noasync out_stream.write(RESET) catch return,
}, },
.windows_api => if (builtin.os == .windows) { .windows_api => if (builtin.os.tag == .windows) {
const S = struct { const S = struct {
var attrs: windows.WORD = undefined; var attrs: windows.WORD = undefined;
var init_attrs = false; var init_attrs = false;
@ -617,7 +617,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
return noasync root.os.debug.openSelfDebugInfo(allocator); return noasync root.os.debug.openSelfDebugInfo(allocator);
} }
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .linux,
.freebsd, .freebsd,
.macosx, .macosx,
@ -1019,7 +1019,7 @@ pub const DebugInfo = struct {
pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo { pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
if (comptime std.Target.current.isDarwin()) if (comptime std.Target.current.isDarwin())
return self.lookupModuleDyld(address) return self.lookupModuleDyld(address)
else if (builtin.os == .windows) else if (builtin.os.tag == .windows)
return self.lookupModuleWin32(address) return self.lookupModuleWin32(address)
else else
return self.lookupModuleDl(address); return self.lookupModuleDl(address);
@ -1242,7 +1242,7 @@ const SymbolInfo = struct {
} }
}; };
pub const ModuleDebugInfo = switch (builtin.os) { pub const ModuleDebugInfo = switch (builtin.os.tag) {
.macosx, .ios, .watchos, .tvos => struct { .macosx, .ios, .watchos, .tvos => struct {
base_address: usize, base_address: usize,
mapped_memory: []const u8, mapped_memory: []const u8,
@ -1602,7 +1602,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
} }
/// Whether or not the current target can print useful debug information when a segfault occurs. /// Whether or not the current target can print useful debug information when a segfault occurs.
pub const have_segfault_handling_support = builtin.os == .linux or builtin.os == .windows; pub const have_segfault_handling_support = builtin.os.tag == .linux or builtin.os.tag == .windows;
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler")) pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
root.enable_segfault_handler root.enable_segfault_handler
else else
@ -1621,7 +1621,7 @@ pub fn attachSegfaultHandler() void {
if (!have_segfault_handling_support) { if (!have_segfault_handling_support) {
@compileError("segfault handler not supported for this target"); @compileError("segfault handler not supported for this target");
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return; return;
} }
@ -1637,7 +1637,7 @@ pub fn attachSegfaultHandler() void {
} }
fn resetSegfaultHandler() void { fn resetSegfaultHandler() void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
if (windows_segfault_handle) |handle| { if (windows_segfault_handle) |handle| {
assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0); assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0);
windows_segfault_handle = null; windows_segfault_handle = null;

View File

@ -11,7 +11,7 @@ const system = std.os.system;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
const max = std.math.max; const max = std.math.max;
pub const DynLib = switch (builtin.os) { pub const DynLib = switch (builtin.os.tag) {
.linux => if (builtin.link_libc) DlDynlib else ElfDynLib, .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
.windows => WindowsDynLib, .windows => WindowsDynLib,
.macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib,
@ -390,7 +390,7 @@ pub const DlDynlib = struct {
}; };
test "dynamic_library" { test "dynamic_library" {
const libname = switch (builtin.os) { const libname = switch (builtin.os.tag) {
.linux, .freebsd => "invalid_so.so", .linux, .freebsd => "invalid_so.so",
.windows => "invalid_dll.dll", .windows => "invalid_dll.dll",
.macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib", .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",

View File

@ -349,16 +349,6 @@ pub const Elf = struct {
program_headers: []ProgramHeader, program_headers: []ProgramHeader,
allocator: *mem.Allocator, allocator: *mem.Allocator,
/// Call close when done.
pub fn openPath(allocator: *mem.Allocator, path: []const u8) !Elf {
@compileError("TODO implement");
}
/// Call close when done.
pub fn openFile(allocator: *mem.Allocator, file: File) !Elf {
@compileError("TODO implement");
}
pub fn openStream( pub fn openStream(
allocator: *mem.Allocator, allocator: *mem.Allocator,
seekable_stream: *io.SeekableStream(anyerror, anyerror), seekable_stream: *io.SeekableStream(anyerror, anyerror),
@ -554,6 +544,21 @@ pub const Elf = struct {
}; };
pub const EI_NIDENT = 16; pub const EI_NIDENT = 16;
pub const EI_CLASS = 4;
pub const ELFCLASSNONE = 0;
pub const ELFCLASS32 = 1;
pub const ELFCLASS64 = 2;
pub const ELFCLASSNUM = 3;
pub const EI_DATA = 5;
pub const ELFDATANONE = 0;
pub const ELFDATA2LSB = 1;
pub const ELFDATA2MSB = 2;
pub const ELFDATANUM = 3;
pub const EI_VERSION = 6;
pub const Elf32_Half = u16; pub const Elf32_Half = u16;
pub const Elf64_Half = u16; pub const Elf64_Half = u16;
pub const Elf32_Word = u32; pub const Elf32_Word = u32;

View File

@ -273,7 +273,7 @@ test "std.event.Channel" {
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3251 // https://github.com/ziglang/zig/issues/3251
if (builtin.os == .freebsd) return error.SkipZigTest; if (builtin.os.tag == .freebsd) return error.SkipZigTest;
var channel: Channel(i32) = undefined; var channel: Channel(i32) = undefined;
channel.init(&[0]i32{}); channel.init(&[0]i32{});

View File

@ -86,7 +86,7 @@ test "std.event.Future" {
// https://github.com/ziglang/zig/issues/1908 // https://github.com/ziglang/zig/issues/1908
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3251 // https://github.com/ziglang/zig/issues/3251
if (builtin.os == .freebsd) return error.SkipZigTest; if (builtin.os.tag == .freebsd) return error.SkipZigTest;
// TODO provide a way to run tests in evented I/O mode // TODO provide a way to run tests in evented I/O mode
if (!std.io.is_async) return error.SkipZigTest; if (!std.io.is_async) return error.SkipZigTest;

View File

@ -123,7 +123,7 @@ test "std.event.Lock" {
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
// TODO https://github.com/ziglang/zig/issues/3251 // TODO https://github.com/ziglang/zig/issues/3251
if (builtin.os == .freebsd) return error.SkipZigTest; if (builtin.os.tag == .freebsd) return error.SkipZigTest;
var lock = Lock.init(); var lock = Lock.init();
defer lock.deinit(); defer lock.deinit();

View File

@ -34,7 +34,7 @@ pub const Loop = struct {
handle: anyframe, handle: anyframe,
overlapped: Overlapped, overlapped: Overlapped,
pub const overlapped_init = switch (builtin.os) { pub const overlapped_init = switch (builtin.os.tag) {
.windows => windows.OVERLAPPED{ .windows => windows.OVERLAPPED{
.Internal = 0, .Internal = 0,
.InternalHigh = 0, .InternalHigh = 0,
@ -52,7 +52,7 @@ pub const Loop = struct {
EventFd, EventFd,
}; };
pub const EventFd = switch (builtin.os) { pub const EventFd = switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => KEventFd, .macosx, .freebsd, .netbsd, .dragonfly => KEventFd,
.linux => struct { .linux => struct {
base: ResumeNode, base: ResumeNode,
@ -71,7 +71,7 @@ pub const Loop = struct {
kevent: os.Kevent, kevent: os.Kevent,
}; };
pub const Basic = switch (builtin.os) { pub const Basic = switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => KEventBasic, .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic,
.linux => struct { .linux => struct {
base: ResumeNode, base: ResumeNode,
@ -173,7 +173,7 @@ pub const Loop = struct {
const wakeup_bytes = [_]u8{0x1} ** 8; const wakeup_bytes = [_]u8{0x1} ** 8;
fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
self.os_data.fs_queue = std.atomic.Queue(Request).init(); self.os_data.fs_queue = std.atomic.Queue(Request).init();
self.os_data.fs_queue_item = 0; self.os_data.fs_queue_item = 0;
@ -404,7 +404,7 @@ pub const Loop = struct {
} }
fn deinitOsData(self: *Loop) void { fn deinitOsData(self: *Loop) void {
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
noasync os.close(self.os_data.final_eventfd); noasync os.close(self.os_data.final_eventfd);
while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd); while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd);
@ -568,7 +568,7 @@ pub const Loop = struct {
}; };
const eventfd_node = &resume_stack_node.data; const eventfd_node = &resume_stack_node.data;
eventfd_node.base.handle = next_tick_node.data; eventfd_node.base.handle = next_tick_node.data;
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => { .macosx, .freebsd, .netbsd, .dragonfly => {
const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent); const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent);
const empty_kevs = &[0]os.Kevent{}; const empty_kevs = &[0]os.Kevent{};
@ -628,7 +628,7 @@ pub const Loop = struct {
self.workerRun(); self.workerRun();
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .linux,
.macosx, .macosx,
.freebsd, .freebsd,
@ -678,7 +678,7 @@ pub const Loop = struct {
const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
if (prev == 1) { if (prev == 1) {
// cause all the threads to stop // cause all the threads to stop
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
self.posixFsRequest(&self.os_data.fs_end_request); self.posixFsRequest(&self.os_data.fs_end_request);
// writing 8 bytes to an eventfd cannot fail // writing 8 bytes to an eventfd cannot fail
@ -902,7 +902,7 @@ pub const Loop = struct {
self.finishOneEvent(); self.finishOneEvent();
} }
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
// only process 1 event so we don't steal from other threads // only process 1 event so we don't steal from other threads
var events: [1]os.linux.epoll_event = undefined; var events: [1]os.linux.epoll_event = undefined;
@ -989,7 +989,7 @@ pub const Loop = struct {
fn posixFsRequest(self: *Loop, request_node: *Request.Node) void { fn posixFsRequest(self: *Loop, request_node: *Request.Node) void {
self.beginOneEvent(); // finished in posixFsRun after processing the msg self.beginOneEvent(); // finished in posixFsRun after processing the msg
self.os_data.fs_queue.put(request_node); self.os_data.fs_queue.put(request_node);
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => { .macosx, .freebsd, .netbsd, .dragonfly => {
const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake); const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake);
const empty_kevs = &[0]os.Kevent{}; const empty_kevs = &[0]os.Kevent{};
@ -1018,7 +1018,7 @@ pub const Loop = struct {
// https://github.com/ziglang/zig/issues/3157 // https://github.com/ziglang/zig/issues/3157
fn posixFsRun(self: *Loop) void { fn posixFsRun(self: *Loop) void {
while (true) { while (true) {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
@atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst);
} }
while (self.os_data.fs_queue.get()) |node| { while (self.os_data.fs_queue.get()) |node| {
@ -1053,7 +1053,7 @@ pub const Loop = struct {
} }
self.finishOneEvent(); self.finishOneEvent();
} }
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
switch (os.linux.getErrno(rc)) { switch (os.linux.getErrno(rc)) {
@ -1071,7 +1071,7 @@ pub const Loop = struct {
} }
} }
const OsData = switch (builtin.os) { const OsData = switch (builtin.os.tag) {
.linux => LinuxOsData, .linux => LinuxOsData,
.macosx, .freebsd, .netbsd, .dragonfly => KEventData, .macosx, .freebsd, .netbsd, .dragonfly => KEventData,
.windows => struct { .windows => struct {

View File

@ -29,7 +29,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
/// All file system operations which return a path are guaranteed to /// All file system operations which return a path are guaranteed to
/// fit into a UTF-8 encoded array of this length. /// fit into a UTF-8 encoded array of this length.
/// The byte count includes room for a null sentinel byte. /// The byte count includes room for a null sentinel byte.
pub const MAX_PATH_BYTES = switch (builtin.os) { pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX, .linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX,
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes. // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
// If it would require 4 UTF-8 bytes, then there would be a surrogate // If it would require 4 UTF-8 bytes, then there would be a surrogate
@ -47,7 +47,7 @@ pub const base64_encoder = base64.Base64Encoder.init(
/// Whether or not async file system syscalls need a dedicated thread because the operating /// Whether or not async file system syscalls need a dedicated thread because the operating
/// system does not support non-blocking I/O on the file system. /// system does not support non-blocking I/O on the file system.
pub const need_async_thread = std.io.is_async and switch (builtin.os) { pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) {
.windows, .other => false, .windows, .other => false,
else => true, else => true,
}; };
@ -270,7 +270,7 @@ pub const AtomicFile = struct {
assert(!self.finished); assert(!self.finished);
self.file.close(); self.file.close();
self.finished = true; self.finished = true;
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
return os.renameW(&tmp_path_w, &dest_path_w); return os.renameW(&tmp_path_w, &dest_path_w);
@ -394,7 +394,7 @@ pub const Dir = struct {
const IteratorError = error{AccessDenied} || os.UnexpectedError; const IteratorError = error{AccessDenied} || os.UnexpectedError;
pub const Iterator = switch (builtin.os) { pub const Iterator = switch (builtin.os.tag) {
.macosx, .ios, .freebsd, .netbsd, .dragonfly => struct { .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct {
dir: Dir, dir: Dir,
seek: i64, seek: i64,
@ -409,7 +409,7 @@ 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 {
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .ios => return self.nextDarwin(), .macosx, .ios => return self.nextDarwin(),
.freebsd, .netbsd, .dragonfly => return self.nextBsd(), .freebsd, .netbsd, .dragonfly => return self.nextBsd(),
else => @compileError("unimplemented"), else => @compileError("unimplemented"),
@ -644,7 +644,7 @@ pub const Dir = struct {
}; };
pub fn iterate(self: Dir) Iterator { pub fn iterate(self: Dir) Iterator {
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{
.dir = self, .dir = self,
.seek = 0, .seek = 0,
@ -710,7 +710,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openFileW(&path_w, flags); return self.openFileW(&path_w, flags);
} }
@ -720,7 +720,7 @@ pub const Dir = struct {
/// Same as `openFile` but the path parameter is null-terminated. /// Same as `openFile` but the path parameter is null-terminated.
pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try os.windows.cStrToPrefixedFileW(sub_path); const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
return self.openFileW(&path_w, flags); return self.openFileW(&path_w, flags);
} }
@ -760,7 +760,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.createFileW(&path_w, flags); return self.createFileW(&path_w, flags);
} }
@ -770,7 +770,7 @@ pub const Dir = struct {
/// Same as `createFile` but the path parameter is null-terminated. /// Same as `createFile` but the path parameter is null-terminated.
pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.createFileW(&path_w, flags); return self.createFileW(&path_w, flags);
} }
@ -901,7 +901,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .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.openDirTraverseW(&sub_path_w); return self.openDirTraverseW(&sub_path_w);
} }
@ -919,7 +919,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .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.openDirListW(&sub_path_w); return self.openDirListW(&sub_path_w);
} }
@ -930,7 +930,7 @@ pub const Dir = struct {
/// Same as `openDirTraverse` except the parameter is null-terminated. /// Same as `openDirTraverse` except the parameter is null-terminated.
pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.openDirTraverseW(&sub_path_w); return self.openDirTraverseW(&sub_path_w);
} else { } else {
@ -941,7 +941,7 @@ pub const Dir = struct {
/// Same as `openDirList` except the parameter is null-terminated. /// Same as `openDirList` except the parameter is null-terminated.
pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.openDirListW(&sub_path_w); return self.openDirListW(&sub_path_w);
} else { } else {
@ -1083,7 +1083,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .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); return self.deleteDirW(&sub_path_w);
} }
@ -1340,7 +1340,7 @@ pub const Dir = struct {
/// For example, instead of testing if a file exists and then opening it, just /// For example, instead of testing if a file exists and then opening it, just
/// open it and handle the error for file not found. /// open it and handle the error for file not found.
pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
if (builtin.os == .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.accessW(&sub_path_w, flags); return self.accessW(&sub_path_w, flags);
} }
@ -1350,7 +1350,7 @@ pub const Dir = struct {
/// Same as `access` except the path parameter is null-terminated. /// Same as `access` except the path parameter is null-terminated.
pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path); const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path);
return self.accessW(&sub_path_w, flags); return self.accessW(&sub_path_w, flags);
} }
@ -1381,7 +1381,7 @@ pub const Dir = struct {
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
/// On POSIX targets, this function is comptime-callable. /// On POSIX targets, this function is comptime-callable.
pub fn cwd() Dir { pub fn cwd() Dir {
if (builtin.os == .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 { } else {
return Dir{ .fd = os.AT_FDCWD }; return Dir{ .fd = os.AT_FDCWD };
@ -1560,10 +1560,10 @@ pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
pub fn openSelfExe() OpenSelfExeError!File { pub fn openSelfExe() OpenSelfExeError!File {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
return openFileAbsoluteC("/proc/self/exe", .{}); return openFileAbsoluteC("/proc/self/exe", .{});
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const wide_slice = selfExePathW(); const wide_slice = selfExePathW();
const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
return cwd().openReadW(&prefixed_path_w); return cwd().openReadW(&prefixed_path_w);
@ -1575,7 +1575,7 @@ pub fn openSelfExe() OpenSelfExeError!File {
} }
test "openSelfExe" { test "openSelfExe" {
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(), .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(),
else => return error.SkipZigTest, // Unsupported OS. else => return error.SkipZigTest, // Unsupported OS.
} }
@ -1600,7 +1600,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
if (rc != 0) return error.NameTooLong; if (rc != 0) return error.NameTooLong;
return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer)); return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer));
} }
switch (builtin.os) { switch (builtin.os.tag) {
.linux => return os.readlinkC("/proc/self/exe", out_buffer), .linux => return os.readlinkC("/proc/self/exe", out_buffer),
.freebsd, .dragonfly => { .freebsd, .dragonfly => {
var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };
@ -1642,7 +1642,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
/// Get the directory path that contains the current executable. /// Get the directory path that contains the current executable.
/// Returned value is a slice of out_buffer. /// Returned value is a slice of out_buffer.
pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
// If the currently executing binary has been deleted, // If the currently executing binary has been deleted,
// the file path looks something like `/a/b/c/exe (deleted)` // the file path looks something like `/a/b/c/exe (deleted)`
// This path cannot be opened, but it's valid for determining the directory // This path cannot be opened, but it's valid for determining the directory

View File

@ -29,7 +29,7 @@ pub const File = struct {
pub const Mode = os.mode_t; pub const Mode = os.mode_t;
pub const default_mode = switch (builtin.os) { pub const default_mode = switch (builtin.os.tag) {
.windows => 0, .windows => 0,
else => 0o666, else => 0o666,
}; };
@ -83,7 +83,7 @@ pub const File = struct {
/// Test whether ANSI escape codes will be treated as such. /// Test whether ANSI escape codes will be treated as such.
pub fn supportsAnsiEscapeCodes(self: File) bool { pub fn supportsAnsiEscapeCodes(self: File) bool {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return os.isCygwinPty(self.handle); return os.isCygwinPty(self.handle);
} }
if (self.isTty()) { if (self.isTty()) {
@ -128,7 +128,7 @@ pub const File = struct {
/// TODO: integrate with async I/O /// TODO: integrate with async I/O
pub fn getEndPos(self: File) GetPosError!u64 { pub fn getEndPos(self: File) GetPosError!u64 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.GetFileSizeEx(self.handle); return windows.GetFileSizeEx(self.handle);
} }
return (try self.stat()).size; return (try self.stat()).size;
@ -138,7 +138,7 @@ pub const File = struct {
/// TODO: integrate with async I/O /// TODO: integrate with async I/O
pub fn mode(self: File) ModeError!Mode { pub fn mode(self: File) ModeError!Mode {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return {}; return {};
} }
return (try self.stat()).mode; return (try self.stat()).mode;
@ -162,7 +162,7 @@ pub const File = struct {
/// TODO: integrate with async I/O /// TODO: integrate with async I/O
pub fn stat(self: File) StatError!Stat { pub fn stat(self: File) StatError!Stat {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
var io_status_block: windows.IO_STATUS_BLOCK = undefined; var io_status_block: windows.IO_STATUS_BLOCK = undefined;
var info: windows.FILE_ALL_INFORMATION = undefined; var info: windows.FILE_ALL_INFORMATION = undefined;
const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation); const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
@ -209,7 +209,7 @@ pub const File = struct {
/// last modification timestamp in nanoseconds /// last modification timestamp in nanoseconds
mtime: i64, mtime: i64,
) UpdateTimesError!void { ) UpdateTimesError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const atime_ft = windows.nanoSecondsToFileTime(atime); const atime_ft = windows.nanoSecondsToFileTime(atime);
const mtime_ft = windows.nanoSecondsToFileTime(mtime); const mtime_ft = windows.nanoSecondsToFileTime(mtime);
return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft); return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft);

View File

@ -13,7 +13,7 @@ pub const GetAppDataDirError = error{
/// Caller owns returned memory. /// Caller owns returned memory.
/// TODO determine if we can remove the allocator requirement /// TODO determine if we can remove the allocator requirement
pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
switch (builtin.os) { switch (builtin.os.tag) {
.windows => { .windows => {
var dir_path_ptr: [*:0]u16 = undefined; var dir_path_ptr: [*:0]u16 = undefined;
switch (os.windows.shell32.SHGetKnownFolderPath( switch (os.windows.shell32.SHGetKnownFolderPath(

View File

@ -13,18 +13,18 @@ const process = std.process;
pub const sep_windows = '\\'; pub const sep_windows = '\\';
pub const sep_posix = '/'; pub const sep_posix = '/';
pub const sep = if (builtin.os == .windows) sep_windows else sep_posix; pub const sep = if (builtin.os.tag == .windows) sep_windows else sep_posix;
pub const sep_str_windows = "\\"; pub const sep_str_windows = "\\";
pub const sep_str_posix = "/"; pub const sep_str_posix = "/";
pub const sep_str = if (builtin.os == .windows) sep_str_windows else sep_str_posix; pub const sep_str = if (builtin.os.tag == .windows) sep_str_windows else sep_str_posix;
pub const delimiter_windows = ';'; pub const delimiter_windows = ';';
pub const delimiter_posix = ':'; pub const delimiter_posix = ':';
pub const delimiter = if (builtin.os == .windows) delimiter_windows else delimiter_posix; pub const delimiter = if (builtin.os.tag == .windows) delimiter_windows else delimiter_posix;
pub fn isSep(byte: u8) bool { pub fn isSep(byte: u8) bool {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return byte == '/' or byte == '\\'; return byte == '/' or byte == '\\';
} else { } else {
return byte == '/'; return byte == '/';
@ -74,7 +74,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u
return buf; return buf;
} }
pub const join = if (builtin.os == .windows) joinWindows else joinPosix; pub const join = if (builtin.os.tag == .windows) joinWindows else joinPosix;
/// Naively combines a series of paths with the native path seperator. /// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller. /// Allocates memory for the result, which must be freed by the caller.
@ -129,7 +129,7 @@ test "join" {
} }
pub fn isAbsoluteC(path_c: [*:0]const u8) bool { pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return isAbsoluteWindowsC(path_c); return isAbsoluteWindowsC(path_c);
} else { } else {
return isAbsolutePosixC(path_c); return isAbsolutePosixC(path_c);
@ -137,7 +137,7 @@ pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
} }
pub fn isAbsolute(path: []const u8) bool { pub fn isAbsolute(path: []const u8) bool {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return isAbsoluteWindows(path); return isAbsoluteWindows(path);
} else { } else {
return isAbsolutePosix(path); return isAbsolutePosix(path);
@ -318,7 +318,7 @@ test "windowsParsePath" {
} }
pub fn diskDesignator(path: []const u8) []const u8 { pub fn diskDesignator(path: []const u8) []const u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return diskDesignatorWindows(path); return diskDesignatorWindows(path);
} else { } else {
return ""; return "";
@ -383,7 +383,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return resolveWindows(allocator, paths); return resolveWindows(allocator, paths);
} else { } else {
return resolvePosix(allocator, paths); return resolvePosix(allocator, paths);
@ -400,7 +400,7 @@ pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
/// Without performing actual syscalls, resolving `..` could be incorrect. /// Without performing actual syscalls, resolving `..` could be incorrect.
pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) { if (paths.len == 0) {
assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
return process.getCwdAlloc(allocator); return process.getCwdAlloc(allocator);
} }
@ -495,7 +495,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
result_disk_designator = result[0..result_index]; result_disk_designator = result[0..result_index];
}, },
WindowsPath.Kind.None => { WindowsPath.Kind.None => {
assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
const cwd = try process.getCwdAlloc(allocator); const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd); defer allocator.free(cwd);
const parsed_cwd = windowsParsePath(cwd); const parsed_cwd = windowsParsePath(cwd);
@ -510,7 +510,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
}, },
} }
} else { } else {
assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
// TODO call get cwd for the result_disk_designator instead of the global one // TODO call get cwd for the result_disk_designator instead of the global one
const cwd = try process.getCwdAlloc(allocator); const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd); defer allocator.free(cwd);
@ -581,7 +581,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
/// Without performing actual syscalls, resolving `..` could be incorrect. /// Without performing actual syscalls, resolving `..` could be incorrect.
pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) { if (paths.len == 0) {
assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd
return process.getCwdAlloc(allocator); return process.getCwdAlloc(allocator);
} }
@ -603,7 +603,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (have_abs) { if (have_abs) {
result = try allocator.alloc(u8, max_size); result = try allocator.alloc(u8, max_size);
} else { } else {
assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd
const cwd = try process.getCwdAlloc(allocator); const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd); defer allocator.free(cwd);
result = try allocator.alloc(u8, max_size + cwd.len + 1); result = try allocator.alloc(u8, max_size + cwd.len + 1);
@ -645,7 +645,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
test "resolve" { test "resolve" {
const cwd = try process.getCwdAlloc(testing.allocator); const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd); defer testing.allocator.free(cwd);
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
cwd[0] = asciiUpper(cwd[0]); cwd[0] = asciiUpper(cwd[0]);
} }
@ -661,7 +661,7 @@ test "resolveWindows" {
// TODO https://github.com/ziglang/zig/issues/3288 // TODO https://github.com/ziglang/zig/issues/3288
return error.SkipZigTest; return error.SkipZigTest;
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const cwd = try process.getCwdAlloc(testing.allocator); const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd); defer testing.allocator.free(cwd);
const parsed_cwd = windowsParsePath(cwd); const parsed_cwd = windowsParsePath(cwd);
@ -732,7 +732,7 @@ fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {
/// If the path is a file in the current directory (no directory component) /// If the path is a file in the current directory (no directory component)
/// then returns null /// then returns null
pub fn dirname(path: []const u8) ?[]const u8 { pub fn dirname(path: []const u8) ?[]const u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return dirnameWindows(path); return dirnameWindows(path);
} else { } else {
return dirnamePosix(path); return dirnamePosix(path);
@ -864,7 +864,7 @@ fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void {
} }
pub fn basename(path: []const u8) []const u8 { pub fn basename(path: []const u8) []const u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return basenameWindows(path); return basenameWindows(path);
} else { } else {
return basenamePosix(path); return basenamePosix(path);
@ -980,7 +980,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {
/// string is returned. /// string is returned.
/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.
pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return relativeWindows(allocator, from, to); return relativeWindows(allocator, from, to);
} else { } else {
return relativePosix(allocator, from, to); return relativePosix(allocator, from, to);

View File

@ -42,7 +42,7 @@ pub fn Watch(comptime V: type) type {
os_data: OsData, os_data: OsData,
allocator: *Allocator, allocator: *Allocator,
const OsData = switch (builtin.os) { const OsData = switch (builtin.os.tag) {
// TODO https://github.com/ziglang/zig/issues/3778 // TODO https://github.com/ziglang/zig/issues/3778
.macosx, .freebsd, .netbsd, .dragonfly => KqOsData, .macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
.linux => LinuxOsData, .linux => LinuxOsData,
@ -121,7 +121,7 @@ pub fn Watch(comptime V: type) type {
const self = try allocator.create(Self); const self = try allocator.create(Self);
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
errdefer os.close(inotify_fd); errdefer os.close(inotify_fd);
@ -172,7 +172,7 @@ pub fn Watch(comptime V: type) type {
/// All addFile calls and removeFile calls must have completed. /// All addFile calls and removeFile calls must have completed.
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => { .macosx, .freebsd, .netbsd, .dragonfly => {
// TODO we need to cancel the frames before destroying the lock // TODO we need to cancel the frames before destroying the lock
self.os_data.table_lock.deinit(); self.os_data.table_lock.deinit();
@ -223,7 +223,7 @@ pub fn Watch(comptime V: type) type {
} }
pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V { pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
switch (builtin.os) { switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value), .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
.linux => return addFileLinux(self, file_path, value), .linux => return addFileLinux(self, file_path, value),
.windows => return addFileWindows(self, file_path, value), .windows => return addFileWindows(self, file_path, value),

View File

@ -36,7 +36,7 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new
/// Thread-safe and lock-free. /// Thread-safe and lock-free.
pub const page_allocator = if (std.Target.current.isWasm()) pub const page_allocator = if (std.Target.current.isWasm())
&wasm_page_allocator_state &wasm_page_allocator_state
else if (std.Target.current.getOs() == .freestanding) else if (std.Target.current.os.tag == .freestanding)
root.os.heap.page_allocator root.os.heap.page_allocator
else else
&page_allocator_state; &page_allocator_state;
@ -57,7 +57,7 @@ const PageAllocator = struct {
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
if (n == 0) return &[0]u8{}; if (n == 0) return &[0]u8{};
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const w = os.windows; const w = os.windows;
// Although officially it's at least aligned to page boundary, // Although officially it's at least aligned to page boundary,
@ -143,7 +143,7 @@ const PageAllocator = struct {
fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
const old_mem = @alignCast(mem.page_size, old_mem_unaligned); const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const w = os.windows; const w = os.windows;
if (new_size == 0) { if (new_size == 0) {
// From the docs: // From the docs:
@ -183,7 +183,7 @@ const PageAllocator = struct {
fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
const old_mem = @alignCast(mem.page_size, old_mem_unaligned); const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
if (old_mem.len == 0) { if (old_mem.len == 0) {
return alloc(allocator, new_size, new_align); return alloc(allocator, new_size, new_align);
} }
@ -412,7 +412,7 @@ const WasmPageAllocator = struct {
} }
}; };
pub const HeapAllocator = switch (builtin.os) { pub const HeapAllocator = switch (builtin.os.tag) {
.windows => struct { .windows => struct {
allocator: Allocator, allocator: Allocator,
heap_handle: ?HeapHandle, heap_handle: ?HeapHandle,
@ -855,7 +855,7 @@ test "PageAllocator" {
try testAllocatorAlignedShrink(allocator); try testAllocatorAlignedShrink(allocator);
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
// Trying really large alignment. As mentionned in the implementation, // Trying really large alignment. As mentionned in the implementation,
// VirtualAlloc returns 64K aligned addresses. We want to make sure // VirtualAlloc returns 64K aligned addresses. We want to make sure
// PageAllocator works beyond that, as it's not tested by // PageAllocator works beyond that, as it's not tested by
@ -868,7 +868,7 @@ test "PageAllocator" {
} }
test "HeapAllocator" { test "HeapAllocator" {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
var heap_allocator = HeapAllocator.init(); var heap_allocator = HeapAllocator.init();
defer heap_allocator.deinit(); defer heap_allocator.deinit();

View File

@ -35,7 +35,7 @@ else
pub const is_async = mode != .blocking; pub const is_async = mode != .blocking;
fn getStdOutHandle() os.fd_t { fn getStdOutHandle() os.fd_t {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdOutput; return os.windows.peb().ProcessParameters.hStdOutput;
} }
@ -54,7 +54,7 @@ pub fn getStdOut() File {
} }
fn getStdErrHandle() os.fd_t { fn getStdErrHandle() os.fd_t {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdError; return os.windows.peb().ProcessParameters.hStdError;
} }
@ -74,7 +74,7 @@ pub fn getStdErr() File {
} }
fn getStdInHandle() os.fd_t { fn getStdInHandle() os.fd_t {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdInput; return os.windows.peb().ProcessParameters.hStdInput;
} }

View File

@ -73,7 +73,7 @@ pub const Mutex = if (builtin.single_threaded)
return self.tryAcquire() orelse @panic("deadlock detected"); return self.tryAcquire() orelse @panic("deadlock detected");
} }
} }
else if (builtin.os == .windows) else if (builtin.os.tag == .windows)
// https://locklessinc.com/articles/keyed_events/ // https://locklessinc.com/articles/keyed_events/
extern union { extern union {
locked: u8, locked: u8,
@ -161,7 +161,7 @@ else if (builtin.os == .windows)
} }
}; };
} }
else if (builtin.link_libc or builtin.os == .linux) else if (builtin.link_libc or builtin.os.tag == .linux)
// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs // stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
struct { struct {
state: usize, state: usize,

View File

@ -501,7 +501,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
return result; return result;
} }
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
const flags = std.c.AI_NUMERICSERV; const flags = std.c.AI_NUMERICSERV;
const family = os.AF_UNSPEC; const family = os.AF_UNSPEC;
var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);

View File

@ -63,7 +63,7 @@ test "parse and render IPv4 addresses" {
} }
test "resolve DNS" { test "resolve DNS" {
if (std.builtin.os == .windows) { if (std.builtin.os.tag == .windows) {
// DNS resolution not implemented on Windows yet. // DNS resolution not implemented on Windows yet.
return error.SkipZigTest; return error.SkipZigTest;
} }
@ -81,7 +81,7 @@ test "resolve DNS" {
test "listen on a port, send bytes, receive bytes" { test "listen on a port, send bytes, receive bytes" {
if (!std.io.is_async) return error.SkipZigTest; if (!std.io.is_async) return error.SkipZigTest;
if (std.builtin.os != .linux) { if (std.builtin.os.tag != .linux) {
// TODO build abstractions for other operating systems // TODO build abstractions for other operating systems
return error.SkipZigTest; return error.SkipZigTest;
} }

View File

@ -56,7 +56,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This())
root.os.system root.os.system
else if (builtin.link_libc) else if (builtin.link_libc)
std.c std.c
else switch (builtin.os) { else switch (builtin.os.tag) {
.macosx, .ios, .watchos, .tvos => darwin, .macosx, .ios, .watchos, .tvos => darwin,
.freebsd => freebsd, .freebsd => freebsd,
.linux => linux, .linux => linux,
@ -93,10 +93,10 @@ pub const errno = system.getErrno;
/// must call `fsync` before `close`. /// must call `fsync` before `close`.
/// Note: The Zig standard library does not support POSIX thread cancellation. /// Note: The Zig standard library does not support POSIX thread cancellation.
pub fn close(fd: fd_t) void { pub fn close(fd: fd_t) void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.CloseHandle(fd); return windows.CloseHandle(fd);
} }
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
_ = wasi.fd_close(fd); _ = wasi.fd_close(fd);
} }
if (comptime std.Target.current.isDarwin()) { if (comptime std.Target.current.isDarwin()) {
@ -121,12 +121,12 @@ pub const GetRandomError = OpenError;
/// appropriate OS-specific library call. Otherwise it uses the zig standard /// appropriate OS-specific library call. Otherwise it uses the zig standard
/// library implementation. /// library implementation.
pub fn getrandom(buffer: []u8) GetRandomError!void { pub fn getrandom(buffer: []u8) GetRandomError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.RtlGenRandom(buffer); return windows.RtlGenRandom(buffer);
} }
if (builtin.os == .linux or builtin.os == .freebsd) { if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
var buf = buffer; var buf = buffer;
const use_c = builtin.os != .linux or const use_c = builtin.os.tag != .linux or
std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
while (buf.len != 0) { while (buf.len != 0) {
@ -153,7 +153,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void {
} }
return; return;
} }
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
switch (wasi.random_get(buffer.ptr, buffer.len)) { switch (wasi.random_get(buffer.ptr, buffer.len)) {
0 => return, 0 => return,
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
@ -188,13 +188,13 @@ pub fn abort() noreturn {
// MSVCRT abort() sometimes opens a popup window which is undesirable, so // MSVCRT abort() sometimes opens a popup window which is undesirable, so
// even when linking libc on Windows we use our own abort implementation. // even when linking libc on Windows we use our own abort implementation.
// See https://github.com/ziglang/zig/issues/2071 for more details. // See https://github.com/ziglang/zig/issues/2071 for more details.
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
if (builtin.mode == .Debug) { if (builtin.mode == .Debug) {
@breakpoint(); @breakpoint();
} }
windows.kernel32.ExitProcess(3); windows.kernel32.ExitProcess(3);
} }
if (!builtin.link_libc and builtin.os == .linux) { if (!builtin.link_libc and builtin.os.tag == .linux) {
raise(SIGABRT) catch {}; raise(SIGABRT) catch {};
// TODO the rest of the implementation of abort() from musl libc here // TODO the rest of the implementation of abort() from musl libc here
@ -202,10 +202,10 @@ pub fn abort() noreturn {
raise(SIGKILL) catch {}; raise(SIGKILL) catch {};
exit(127); exit(127);
} }
if (builtin.os == .uefi) { if (builtin.os.tag == .uefi) {
exit(0); // TODO choose appropriate exit code exit(0); // TODO choose appropriate exit code
} }
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
@breakpoint(); @breakpoint();
exit(1); exit(1);
} }
@ -223,7 +223,7 @@ pub fn raise(sig: u8) RaiseError!void {
} }
} }
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
var set: linux.sigset_t = undefined; var set: linux.sigset_t = undefined;
// block application signals // block application signals
_ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set); _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set);
@ -260,16 +260,16 @@ pub fn exit(status: u8) noreturn {
if (builtin.link_libc) { if (builtin.link_libc) {
system.exit(status); system.exit(status);
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
windows.kernel32.ExitProcess(status); windows.kernel32.ExitProcess(status);
} }
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
wasi.proc_exit(status); wasi.proc_exit(status);
} }
if (builtin.os == .linux and !builtin.single_threaded) { if (builtin.os.tag == .linux and !builtin.single_threaded) {
linux.exit_group(status); linux.exit_group(status);
} }
if (builtin.os == .uefi) { if (builtin.os.tag == .uefi) {
// exit() is only avaliable if exitBootServices() has not been called yet. // exit() is only avaliable if exitBootServices() has not been called yet.
// This call to exit should not fail, so we don't care about its return value. // This call to exit should not fail, so we don't care about its return value.
if (uefi.system_table.boot_services) |bs| { if (uefi.system_table.boot_services) |bs| {
@ -299,11 +299,11 @@ pub const ReadError = error{
/// If the application has a global event loop enabled, EAGAIN is handled /// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize { pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, null); return windows.ReadFile(fd, buf, null);
} }
if (builtin.os == .wasi and !builtin.link_libc) { 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,
@ -352,7 +352,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
/// * Windows /// * Windows
/// On these systems, the read races with concurrent writes to the same file descriptor. /// On these systems, the read races with concurrent writes to the same file descriptor.
pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
// TODO batch these into parallel requests // TODO batch these into parallel requests
var off: usize = 0; var off: usize = 0;
var iov_i: usize = 0; var iov_i: usize = 0;
@ -406,7 +406,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
pub fn pread(fd: fd_t, buf: []u8, offset: u64) ReadError!usize { pub fn pread(fd: fd_t, buf: []u8, offset: u64) ReadError!usize {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, offset); return windows.ReadFile(fd, buf, offset);
} }
@ -493,7 +493,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
} }
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
// TODO batch these into parallel requests // TODO batch these into parallel requests
var off: usize = 0; var off: usize = 0;
var iov_i: usize = 0; var iov_i: usize = 0;
@ -557,11 +557,11 @@ pub const WriteError = error{
/// If the application has a global event loop enabled, EAGAIN is handled /// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.WriteFile(fd, bytes, null); return windows.WriteFile(fd, bytes, null);
} }
if (builtin.os == .wasi and !builtin.link_libc) { 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,
@ -1129,7 +1129,7 @@ pub fn getenv(key: []const u8) ?[]const u8 {
} }
return null; return null;
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
@compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); @compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
} }
// TODO see https://github.com/ziglang/zig/issues/4524 // TODO see https://github.com/ziglang/zig/issues/4524
@ -1158,7 +1158,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
const value = system.getenv(key) orelse return null; const value = system.getenv(key) orelse return null;
return mem.toSliceConst(u8, value); return mem.toSliceConst(u8, value);
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
@compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
} }
return getenv(mem.toSliceConst(u8, key)); return getenv(mem.toSliceConst(u8, key));
@ -1167,7 +1167,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
/// See also `getenv`. /// See also `getenv`.
pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
if (builtin.os != .windows) { if (builtin.os.tag != .windows) {
@compileError("std.os.getenvW is a Windows-only API"); @compileError("std.os.getenvW is a Windows-only API");
} }
const key_slice = mem.toSliceConst(u16, key); const key_slice = mem.toSliceConst(u16, key);
@ -1199,7 +1199,7 @@ pub const GetCwdError = error{
/// The result is a slice of out_buffer, indexed from 0. /// The result is a slice of out_buffer, indexed from 0.
pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.GetCurrentDirectory(out_buffer); return windows.GetCurrentDirectory(out_buffer);
} }
@ -1240,7 +1240,7 @@ pub const SymLinkError = error{
/// If `sym_link_path` exists, it will not be overwritten. /// If `sym_link_path` exists, it will not be overwritten.
/// See also `symlinkC` and `symlinkW`. /// See also `symlinkC` and `symlinkW`.
pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(target_path); const target_path_w = try windows.sliceToPrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
@ -1254,7 +1254,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!
/// This is the same as `symlink` except the parameters are null-terminated pointers. /// This is the same as `symlink` except the parameters are null-terminated pointers.
/// See also `symlink`. /// See also `symlink`.
pub fn symlinkC(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { pub fn symlinkC(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(target_path); const target_path_w = try windows.cStrToPrefixedFileW(target_path);
const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
@ -1329,7 +1329,7 @@ pub const UnlinkError = error{
/// Delete a name and possibly the file it refers to. /// Delete a name and possibly the file it refers to.
/// See also `unlinkC`. /// See also `unlinkC`.
pub fn unlink(file_path: []const u8) UnlinkError!void { pub fn unlink(file_path: []const u8) UnlinkError!void {
if (builtin.os == .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 windows.DeleteFileW(&file_path_w); return windows.DeleteFileW(&file_path_w);
} else { } else {
@ -1340,7 +1340,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
pub fn unlinkC(file_path: [*:0]const u8) UnlinkError!void { pub fn unlinkC(file_path: [*:0]const u8) UnlinkError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path); const file_path_w = try windows.cStrToPrefixedFileW(file_path);
return windows.DeleteFileW(&file_path_w); return windows.DeleteFileW(&file_path_w);
} }
@ -1372,7 +1372,7 @@ pub const UnlinkatError = UnlinkError || error{
/// Asserts that the path parameter has no null bytes. /// Asserts that the path parameter has no null bytes.
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
if (builtin.os == .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, flags); return unlinkatW(dirfd, &file_path_w, flags);
} }
@ -1382,7 +1382,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
/// Same as `unlinkat` but `file_path` is a null-terminated string. /// Same as `unlinkat` but `file_path` is a null-terminated string.
pub fn unlinkatC(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { pub fn unlinkatC(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
return unlinkatW(dirfd, &file_path_w, flags); return unlinkatW(dirfd, &file_path_w, flags);
} }
@ -1493,7 +1493,7 @@ const RenameError = error{
/// Change the name or location of a file. /// Change the name or location of a file.
pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
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 renameW(&old_path_w, &new_path_w); return renameW(&old_path_w, &new_path_w);
@ -1506,7 +1506,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
/// Same as `rename` except the parameters are null-terminated byte arrays. /// Same as `rename` except the parameters are null-terminated byte arrays.
pub fn renameC(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void { pub fn renameC(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const old_path_w = try windows.cStrToPrefixedFileW(old_path); const old_path_w = try windows.cStrToPrefixedFileW(old_path);
const new_path_w = try windows.cStrToPrefixedFileW(new_path); const new_path_w = try windows.cStrToPrefixedFileW(new_path);
return renameW(&old_path_w, &new_path_w); return renameW(&old_path_w, &new_path_w);
@ -1561,7 +1561,7 @@ pub const MakeDirError = error{
/// Create a directory. /// Create a directory.
/// `mode` is ignored on Windows. /// `mode` is ignored on Windows.
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
return windows.CreateDirectoryW(&dir_path_w, null); return windows.CreateDirectoryW(&dir_path_w, null);
} else { } else {
@ -1572,7 +1572,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
return windows.CreateDirectoryW(&dir_path_w, null); return windows.CreateDirectoryW(&dir_path_w, null);
} }
@ -1611,7 +1611,7 @@ pub const DeleteDirError = error{
/// Deletes an empty directory. /// Deletes an empty directory.
pub fn rmdir(dir_path: []const u8) DeleteDirError!void { pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
return windows.RemoveDirectoryW(&dir_path_w); return windows.RemoveDirectoryW(&dir_path_w);
} else { } else {
@ -1622,7 +1622,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
/// Same as `rmdir` except the parameter is null-terminated. /// Same as `rmdir` except the parameter is null-terminated.
pub fn rmdirC(dir_path: [*:0]const u8) DeleteDirError!void { pub fn rmdirC(dir_path: [*:0]const u8) DeleteDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
return windows.RemoveDirectoryW(&dir_path_w); return windows.RemoveDirectoryW(&dir_path_w);
} }
@ -1658,7 +1658,7 @@ pub const ChangeCurDirError = error{
/// Changes the current working directory of the calling process. /// Changes the current working directory of the calling process.
/// `dir_path` is recommended to be a UTF-8 encoded string. /// `dir_path` is recommended to be a UTF-8 encoded string.
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
@compileError("TODO implement chdir for Windows"); @compileError("TODO implement chdir for Windows");
} else { } else {
@ -1669,7 +1669,7 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
/// Same as `chdir` except the parameter is null-terminated. /// Same as `chdir` except the parameter is null-terminated.
pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void { pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
@compileError("TODO implement chdir for Windows"); @compileError("TODO implement chdir for Windows");
} }
@ -1700,7 +1700,7 @@ pub const ReadLinkError = error{
/// Read value of a symbolic link. /// Read value of a symbolic link.
/// The return value is a slice of `out_buffer` from index 0. /// The return value is a slice of `out_buffer` from index 0.
pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path); const file_path_w = try windows.sliceToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows"); @compileError("TODO implement readlink for Windows");
} else { } else {
@ -1711,7 +1711,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
/// Same as `readlink` except `file_path` is null-terminated. /// Same as `readlink` except `file_path` is null-terminated.
pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path); const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows"); @compileError("TODO implement readlink for Windows");
} }
@ -1732,7 +1732,7 @@ pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
} }
pub fn readlinkatC(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { pub fn readlinkatC(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path); const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows"); @compileError("TODO implement readlink for Windows");
} }
@ -1800,7 +1800,7 @@ pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
/// Test whether a file descriptor refers to a terminal. /// Test whether a file descriptor refers to a terminal.
pub fn isatty(handle: fd_t) bool { pub fn isatty(handle: fd_t) bool {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
if (isCygwinPty(handle)) if (isCygwinPty(handle))
return true; return true;
@ -1810,7 +1810,7 @@ pub fn isatty(handle: fd_t) bool {
if (builtin.link_libc) { if (builtin.link_libc) {
return system.isatty(handle) != 0; return system.isatty(handle) != 0;
} }
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
var statbuf: fdstat_t = undefined; var statbuf: fdstat_t = undefined;
const err = system.fd_fdstat_get(handle, &statbuf); const err = system.fd_fdstat_get(handle, &statbuf);
if (err != 0) { if (err != 0) {
@ -1828,7 +1828,7 @@ pub fn isatty(handle: fd_t) bool {
return true; return true;
} }
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
var wsz: linux.winsize = undefined; var wsz: linux.winsize = undefined;
return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
} }
@ -1836,7 +1836,7 @@ pub fn isatty(handle: fd_t) bool {
} }
pub fn isCygwinPty(handle: fd_t) bool { pub fn isCygwinPty(handle: fd_t) bool {
if (builtin.os != .windows) return false; if (builtin.os.tag != .windows) return false;
const size = @sizeOf(windows.FILE_NAME_INFO); const size = @sizeOf(windows.FILE_NAME_INFO);
var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
@ -2589,7 +2589,7 @@ pub const AccessError = error{
/// check user's permissions for a file /// check user's permissions for a file
/// TODO currently this assumes `mode` is `F_OK` on Windows. /// TODO currently this assumes `mode` is `F_OK` on Windows.
pub fn access(path: []const u8, mode: u32) AccessError!void { pub fn access(path: []const u8, mode: u32) AccessError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try windows.sliceToPrefixedFileW(path); const path_w = try windows.sliceToPrefixedFileW(path);
_ = try windows.GetFileAttributesW(&path_w); _ = try windows.GetFileAttributesW(&path_w);
return; return;
@ -2603,7 +2603,7 @@ pub const accessC = accessZ;
/// Same as `access` except `path` is null-terminated. /// Same as `access` except `path` is null-terminated.
pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try windows.cStrToPrefixedFileW(path); const path_w = try windows.cStrToPrefixedFileW(path);
_ = try windows.GetFileAttributesW(&path_w); _ = try windows.GetFileAttributesW(&path_w);
return; return;
@ -2644,7 +2644,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v
/// Check user's permissions for a file, based on an open directory handle. /// Check user's permissions for a file, based on an open directory handle.
/// TODO currently this ignores `mode` and `flags` on Windows. /// TODO currently this ignores `mode` and `flags` on Windows.
pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try windows.sliceToPrefixedFileW(path); const path_w = try windows.sliceToPrefixedFileW(path);
return faccessatW(dirfd, &path_w, mode, flags); return faccessatW(dirfd, &path_w, mode, flags);
} }
@ -2654,7 +2654,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr
/// Same as `faccessat` except the path parameter is null-terminated. /// Same as `faccessat` except the path parameter is null-terminated.
pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const path_w = try windows.cStrToPrefixedFileW(path); const path_w = try windows.cStrToPrefixedFileW(path);
return faccessatW(dirfd, &path_w, mode, flags); return faccessatW(dirfd, &path_w, mode, flags);
} }
@ -2811,7 +2811,7 @@ pub const SeekError = error{Unseekable} || UnexpectedError;
/// Repositions read/write file offset relative to the beginning. /// Repositions read/write file offset relative to the beginning.
pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined; var result: u64 = undefined;
switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) {
0 => return, 0 => return,
@ -2823,7 +2823,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_BEGIN(fd, offset); return windows.SetFilePointerEx_BEGIN(fd, offset);
} }
const ipos = @bitCast(i64, offset); // the OS treats this as unsigned const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
@ -2840,7 +2840,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
/// Repositions read/write file offset relative to the current offset. /// Repositions read/write file offset relative to the current offset.
pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined; var result: u64 = undefined;
switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) {
0 => return, 0 => return,
@ -2852,7 +2852,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT(fd, offset); return windows.SetFilePointerEx_CURRENT(fd, offset);
} }
switch (errno(system.lseek(fd, offset, SEEK_CUR))) { switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
@ -2868,7 +2868,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
/// Repositions read/write file offset relative to the end. /// Repositions read/write file offset relative to the end.
pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined; var result: u64 = undefined;
switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) {
0 => return, 0 => return,
@ -2880,7 +2880,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_END(fd, offset); return windows.SetFilePointerEx_END(fd, offset);
} }
switch (errno(system.lseek(fd, offset, SEEK_END))) { switch (errno(system.lseek(fd, offset, SEEK_END))) {
@ -2896,7 +2896,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
/// Returns the read/write file offset relative to the beginning. /// Returns the read/write file offset relative to the beginning.
pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined; var result: u64 = undefined;
switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) {
0 => return result, 0 => return result,
@ -2908,7 +2908,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT_get(fd); return windows.SetFilePointerEx_CURRENT_get(fd);
} }
const rc = system.lseek(fd, 0, SEEK_CUR); const rc = system.lseek(fd, 0, SEEK_CUR);
@ -2957,7 +2957,7 @@ pub const RealPathError = error{
/// The return value is a slice of `out_buffer`, but not necessarily from the beginning. /// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
/// See also `realpathC` and `realpathW`. /// See also `realpathC` and `realpathW`.
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const pathname_w = try windows.sliceToPrefixedFileW(pathname); const pathname_w = try windows.sliceToPrefixedFileW(pathname);
return realpathW(&pathname_w, out_buffer); return realpathW(&pathname_w, out_buffer);
} }
@ -2967,11 +2967,11 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
/// Same as `realpath` except `pathname` is null-terminated. /// Same as `realpath` except `pathname` is null-terminated.
pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const pathname_w = try windows.cStrToPrefixedFileW(pathname); const pathname_w = try windows.cStrToPrefixedFileW(pathname);
return realpathW(&pathname_w, out_buffer); return realpathW(&pathname_w, out_buffer);
} }
if (builtin.os == .linux and !builtin.link_libc) { if (builtin.os.tag == .linux and !builtin.link_libc) {
const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0); const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0);
defer close(fd); defer close(fd);
@ -3121,7 +3121,7 @@ pub fn dl_iterate_phdr(
pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
if (comptime std.Target.current.getOs() == .wasi) { if (std.Target.current.os.tag == .wasi) {
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 => {
@ -3144,7 +3144,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 (comptime std.Target.current.getOs() == .wasi) { if (std.Target.current.os.tag == .wasi) {
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.* = .{
@ -3222,7 +3222,7 @@ pub const SigaltstackError = error{
} || UnexpectedError; } || UnexpectedError;
pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi) if (builtin.os.tag == .windows or builtin.os.tag == .uefi or builtin.os.tag == .wasi)
@compileError("std.os.sigaltstack not available for this target"); @compileError("std.os.sigaltstack not available for this target");
switch (errno(system.sigaltstack(ss, old_ss))) { switch (errno(system.sigaltstack(ss, old_ss))) {
@ -3294,7 +3294,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
var uts: utsname = undefined; var uts: utsname = undefined;
switch (errno(system.uname(&uts))) { switch (errno(system.uname(&uts))) {
0 => { 0 => {
@ -3611,7 +3611,7 @@ pub const SchedYieldError = error{
}; };
pub fn sched_yield() SchedYieldError!void { pub fn sched_yield() SchedYieldError!void {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
// The return value has to do with how many other threads there are; it is not // The return value has to do with how many other threads there are; it is not
// an error condition on Windows. // an error condition on Windows.
_ = windows.kernel32.SwitchToThread(); _ = windows.kernel32.SwitchToThread();

View File

@ -3,10 +3,10 @@
//! Root source files can define `os.bits` and these will additionally be added //! Root source files can define `os.bits` and these will additionally be added
//! to the namespace. //! to the namespace.
const builtin = @import("builtin"); const std = @import("std");
const root = @import("root"); const root = @import("root");
pub usingnamespace switch (builtin.os) { pub usingnamespace switch (std.Target.current.os.tag) {
.macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"), .macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"),
.dragonfly => @import("bits/dragonfly.zig"), .dragonfly => @import("bits/dragonfly.zig"),
.freebsd => @import("bits/freebsd.zig"), .freebsd => @import("bits/freebsd.zig"),

View File

@ -1070,7 +1070,7 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi
} }
test "" { test "" {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
_ = @import("linux/test.zig"); _ = @import("linux/test.zig");
} }
} }

View File

@ -53,7 +53,7 @@ test "std.Thread.getCurrentId" {
thread.wait(); thread.wait();
if (Thread.use_pthreads) { if (Thread.use_pthreads) {
expect(thread_current_id == thread_id); expect(thread_current_id == thread_id);
} else if (builtin.os == .windows) { } else if (builtin.os.tag == .windows) {
expect(Thread.getCurrentId() != thread_current_id); expect(Thread.getCurrentId() != thread_current_id);
} else { } else {
// If the thread completes very quickly, then thread_id can be 0. See the // If the thread completes very quickly, then thread_id can be 0. See the
@ -151,7 +151,7 @@ test "realpath" {
} }
test "sigaltstack" { test "sigaltstack" {
if (builtin.os == .windows or builtin.os == .wasi) return error.SkipZigTest; if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;
var st: os.stack_t = undefined; var st: os.stack_t = undefined;
try os.sigaltstack(null, &st); try os.sigaltstack(null, &st);
@ -204,7 +204,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
} }
test "dl_iterate_phdr" { test "dl_iterate_phdr" {
if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx) if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macosx)
return error.SkipZigTest; return error.SkipZigTest;
var counter: usize = 0; var counter: usize = 0;
@ -213,7 +213,7 @@ test "dl_iterate_phdr" {
} }
test "gethostname" { test "gethostname" {
if (builtin.os == .windows) if (builtin.os.tag == .windows)
return error.SkipZigTest; return error.SkipZigTest;
var buf: [os.HOST_NAME_MAX]u8 = undefined; var buf: [os.HOST_NAME_MAX]u8 = undefined;
@ -222,7 +222,7 @@ test "gethostname" {
} }
test "pipe" { test "pipe" {
if (builtin.os == .windows) if (builtin.os.tag == .windows)
return error.SkipZigTest; return error.SkipZigTest;
var fds = try os.pipe(); var fds = try os.pipe();
@ -241,7 +241,7 @@ test "argsAlloc" {
test "memfd_create" { test "memfd_create" {
// memfd_create is linux specific. // memfd_create is linux specific.
if (builtin.os != .linux) return error.SkipZigTest; if (builtin.os.tag != .linux) return error.SkipZigTest;
const fd = std.os.memfd_create("test", 0) catch |err| switch (err) { const fd = std.os.memfd_create("test", 0) catch |err| switch (err) {
// Related: https://github.com/ziglang/zig/issues/4019 // Related: https://github.com/ziglang/zig/issues/4019
error.SystemOutdated => return error.SkipZigTest, error.SystemOutdated => return error.SkipZigTest,
@ -258,7 +258,7 @@ test "memfd_create" {
} }
test "mmap" { test "mmap" {
if (builtin.os == .windows) if (builtin.os.tag == .windows)
return error.SkipZigTest; return error.SkipZigTest;
// Simple mmap() call with non page-aligned size // Simple mmap() call with non page-aligned size
@ -353,7 +353,7 @@ test "mmap" {
} }
test "getenv" { test "getenv" {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null); expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null);
} else { } else {
expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);

View File

@ -593,7 +593,7 @@ test "PackedInt(Array/Slice)Endian" {
// after this one is not mapped and will cause a segfault if we // after this one is not mapped and will cause a segfault if we
// don't account for the bounds. // don't account for the bounds.
test "PackedIntArray at end of available memory" { test "PackedIntArray at end of available memory" {
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return, else => return,
} }
@ -612,7 +612,7 @@ test "PackedIntArray at end of available memory" {
} }
test "PackedIntSlice at end of available memory" { test "PackedIntSlice at end of available memory" {
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return, else => return,
} }

View File

@ -36,7 +36,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
var result = BufMap.init(allocator); var result = BufMap.init(allocator);
errdefer result.deinit(); errdefer result.deinit();
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const ptr = os.windows.peb().ProcessParameters.Environment; const ptr = os.windows.peb().ProcessParameters.Environment;
var i: usize = 0; var i: usize = 0;
@ -61,7 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value); try result.setMove(key, value);
} }
return result; return result;
} else if (builtin.os == .wasi) { } else if (builtin.os.tag == .wasi) {
var environ_count: usize = undefined; var environ_count: usize = undefined;
var environ_buf_size: usize = undefined; var environ_buf_size: usize = undefined;
@ -137,7 +137,7 @@ pub const GetEnvVarOwnedError = error{
/// Caller must free returned memory. /// Caller must free returned memory.
pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
const result_w = blk: { const result_w = blk: {
const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);
defer allocator.free(key_w); defer allocator.free(key_w);
@ -338,12 +338,12 @@ pub const ArgIteratorWindows = struct {
}; };
pub const ArgIterator = struct { pub const ArgIterator = struct {
const InnerType = if (builtin.os == .windows) ArgIteratorWindows else ArgIteratorPosix; const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix;
inner: InnerType, inner: InnerType,
pub fn init() ArgIterator { pub fn init() ArgIterator {
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
// TODO: Figure out a compatible interface accomodating WASI // TODO: Figure out a compatible interface accomodating WASI
@compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
} }
@ -355,7 +355,7 @@ pub const ArgIterator = struct {
/// You must free the returned memory when done. /// You must free the returned memory when done.
pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) {
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
return self.inner.next(allocator); return self.inner.next(allocator);
} else { } else {
return mem.dupe(allocator, u8, self.inner.next() orelse return null); return mem.dupe(allocator, u8, self.inner.next() orelse return null);
@ -380,7 +380,7 @@ pub fn args() ArgIterator {
/// Caller must call argsFree on result. /// Caller must call argsFree on result.
pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
var count: usize = undefined; var count: usize = undefined;
var buf_size: usize = undefined; var buf_size: usize = undefined;
@ -445,7 +445,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
} }
pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
if (builtin.os == .wasi) { if (builtin.os.tag == .wasi) {
const last_item = args_alloc[args_alloc.len - 1]; const last_item = args_alloc[args_alloc.len - 1];
const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
const first_item_ptr = args_alloc[0].ptr; const first_item_ptr = args_alloc[0].ptr;
@ -498,7 +498,7 @@ pub const UserInfo = struct {
/// POSIX function which gets a uid from username. /// POSIX function which gets a uid from username.
pub fn getUserInfo(name: []const u8) !UserInfo { pub fn getUserInfo(name: []const u8) !UserInfo {
return switch (builtin.os) { return switch (builtin.os.tag) {
.linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name), .linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name),
else => @compileError("Unsupported OS"), else => @compileError("Unsupported OS"),
}; };
@ -591,7 +591,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
} }
pub fn getBaseAddress() usize { pub fn getBaseAddress() usize {
switch (builtin.os) { switch (builtin.os.tag) {
.linux => { .linux => {
const base = os.system.getauxval(std.elf.AT_BASE); const base = os.system.getauxval(std.elf.AT_BASE);
if (base != 0) { if (base != 0) {
@ -615,7 +615,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
.Dynamic => {}, .Dynamic => {},
} }
const List = std.ArrayList([:0]u8); const List = std.ArrayList([:0]u8);
switch (builtin.os) { switch (builtin.os.tag) {
.linux, .linux,
.freebsd, .freebsd,
.netbsd, .netbsd,

View File

@ -16,7 +16,7 @@ pub const ResetEvent = struct {
pub const OsEvent = if (builtin.single_threaded) pub const OsEvent = if (builtin.single_threaded)
DebugEvent DebugEvent
else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux) else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux)
PosixEvent PosixEvent
else else
AtomicEvent; AtomicEvent;
@ -106,7 +106,7 @@ const PosixEvent = struct {
fn deinit(self: *PosixEvent) void { fn deinit(self: *PosixEvent) void {
// on dragonfly, *destroy() functions can return EINVAL // on dragonfly, *destroy() functions can return EINVAL
// for statically initialized pthread structures // for statically initialized pthread structures
const err = if (builtin.os == .dragonfly) os.EINVAL else 0; const err = if (builtin.os.tag == .dragonfly) os.EINVAL else 0;
const retm = c.pthread_mutex_destroy(&self.mutex); const retm = c.pthread_mutex_destroy(&self.mutex);
assert(retm == 0 or retm == err); assert(retm == 0 or retm == err);
@ -215,7 +215,7 @@ const AtomicEvent = struct {
} }
} }
pub const Futex = switch (builtin.os) { pub const Futex = switch (builtin.os.tag) {
.windows => WindowsFutex, .windows => WindowsFutex,
.linux => LinuxFutex, .linux => LinuxFutex,
else => SpinFutex, else => SpinFutex,

View File

@ -17,7 +17,7 @@ const is_msvc = switch (builtin.abi) {
.msvc => true, .msvc => true,
else => false, else => false,
}; };
const is_freestanding = switch (builtin.os) { const is_freestanding = switch (builtin.os.tag) {
.freestanding => true, .freestanding => true,
else => false, else => false,
}; };
@ -81,7 +81,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
@setCold(true); @setCold(true);
std.debug.panic("{}", .{msg}); std.debug.panic("{}", .{msg});
} }
if (builtin.os != .freestanding and builtin.os != .other) { if (builtin.os.tag != .freestanding and builtin.os.tag != .other) {
std.os.abort(); std.os.abort();
} }
while (true) {} while (true) {}
@ -178,11 +178,11 @@ test "test_bcmp" {
comptime { comptime {
if (builtin.mode != builtin.Mode.ReleaseFast and if (builtin.mode != builtin.Mode.ReleaseFast and
builtin.mode != builtin.Mode.ReleaseSmall and builtin.mode != builtin.Mode.ReleaseSmall and
builtin.os != builtin.Os.windows) builtin.os.tag != .windows)
{ {
@export(__stack_chk_fail, .{ .name = "__stack_chk_fail" }); @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" });
} }
if (builtin.os == builtin.Os.linux) { if (builtin.os.tag == .linux) {
@export(clone, .{ .name = "clone" }); @export(clone, .{ .name = "clone" });
} }
} }

View File

@ -1,11 +1,12 @@
const builtin = @import("builtin"); const std = @import("std");
const builtin = std.builtin;
const is_test = builtin.is_test; const is_test = builtin.is_test;
const is_gnu = switch (builtin.abi) { const is_gnu = switch (builtin.abi) {
.gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true,
else => false, else => false,
}; };
const is_mingw = builtin.os == .windows and is_gnu; const is_mingw = builtin.os.tag == .windows and is_gnu;
comptime { comptime {
const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
@ -180,7 +181,7 @@ comptime {
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage });
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage });
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
@export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
} }
@ -250,7 +251,7 @@ comptime {
@export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); @export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
// Default stack-probe functions emitted by LLVM // Default stack-probe functions emitted by LLVM
if (is_mingw) { if (is_mingw) {
@export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); @export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage });
@ -288,7 +289,7 @@ comptime {
else => {}, else => {},
} }
} else { } else {
if (builtin.glibc_version != null) { if (std.Target.current.isGnuLibC()) {
@export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage });
} }
@export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage });
@ -307,7 +308,7 @@ comptime {
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true); @setCold(true);
if (is_test) { if (is_test) {
@import("std").debug.panic("{}", .{msg}); std.debug.panic("{}", .{msg});
} else { } else {
unreachable; unreachable;
} }

View File

@ -90,7 +90,7 @@ test "extendhfsf2" {
test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
// On x86 the NaN becomes quiet because the return is pushed on the x87 // On x86 the NaN becomes quiet because the return is pushed on the x87
// stack due to ABI requirements // stack due to ABI requirements
if (builtin.arch != .i386 and builtin.os == .windows) if (builtin.arch != .i386 and builtin.os.tag == .windows)
test__extendhfsf2(0x7c01, 0x7f802000); // sNaN test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
test__extendhfsf2(0, 0); // 0 test__extendhfsf2(0, 0); // 0

View File

@ -46,7 +46,7 @@ pub const SpinLock = struct {
// and yielding for 380-410 iterations was found to be // and yielding for 380-410 iterations was found to be
// a nice sweet spot. Posix systems on the other hand, // a nice sweet spot. Posix systems on the other hand,
// especially linux, perform better by yielding the thread. // especially linux, perform better by yielding the thread.
switch (builtin.os) { switch (builtin.os.tag) {
.windows => loopHint(400), .windows => loopHint(400),
else => std.os.sched_yield() catch loopHint(1), else => std.os.sched_yield() catch loopHint(1),
} }

View File

@ -12,7 +12,7 @@ const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start";
comptime { comptime {
if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
@export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
} }
} else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
@ -20,17 +20,17 @@ comptime {
if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
@export(main, .{ .name = "main", .linkage = .Weak }); @export(main, .{ .name = "main", .linkage = .Weak });
} }
} else if (builtin.os == .windows) { } else if (builtin.os.tag == .windows) {
if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
!@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
{ {
@export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" }); @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" });
} }
} else if (builtin.os == .uefi) { } else if (builtin.os.tag == .uefi) {
if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
} else if (builtin.arch.isWasm() and builtin.os == .freestanding) { } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
} else if (builtin.os != .other and builtin.os != .freestanding) { } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
} }
} }
@ -78,7 +78,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
} }
fn _start() callconv(.Naked) noreturn { fn _start() callconv(.Naked) noreturn {
if (builtin.os == builtin.Os.wasi) { if (builtin.os.tag == .wasi) {
// This is marked inline because for some reason LLVM in release mode fails to inline it, // This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces. // and we want fewer call frames in stack traces.
std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{})); std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
@ -133,7 +133,7 @@ fn WinMainCRTStartup() callconv(.Stdcall) noreturn {
// TODO https://github.com/ziglang/zig/issues/265 // TODO https://github.com/ziglang/zig/issues/265
fn posixCallMainAndExit() noreturn { fn posixCallMainAndExit() noreturn {
if (builtin.os == builtin.Os.freebsd) { if (builtin.os.tag == .freebsd) {
@setAlignStack(16); @setAlignStack(16);
} }
const argc = starting_stack_ptr[0]; const argc = starting_stack_ptr[0];
@ -144,7 +144,7 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {} while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count]; const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count];
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
// Find the beginning of the auxiliary vector // Find the beginning of the auxiliary vector
const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1));
std.os.linux.elf_aux_maybe = auxv; std.os.linux.elf_aux_maybe = auxv;

View File

@ -11,143 +11,48 @@ pub const Target = struct {
os: Os, os: Os,
abi: Abi, abi: Abi,
/// The version ranges here represent the minimum OS version to be supported pub const Os = struct {
/// and the maximum OS version to be supported. The default values represent tag: Tag,
/// the range that the Zig Standard Library bases its abstractions on. version_range: VersionRange,
///
/// The minimum version of the range is the main setting to tweak for a target.
/// Usually, the maximum target OS version will remain the default, which is
/// the latest released version of the OS.
///
/// To test at compile time if the target is guaranteed to support a given OS feature,
/// one should check that the minimum version of the range is greater than or equal to
/// the version the feature was introduced in.
///
/// To test at compile time if the target certainly will not support a given OS feature,
/// one should check that the maximum version of the range is less than the version the
/// feature was introduced in.
///
/// If neither of these cases apply, a runtime check should be used to determine if the
/// target supports a given OS feature.
///
/// Binaries built with a given maximum version will continue to function on newer operating system
/// versions. However, such a binary may not take full advantage of the newer operating system APIs.
pub const Os = union(enum) {
freestanding,
ananas,
cloudabi,
dragonfly,
freebsd: Version.Range,
fuchsia,
ios,
kfreebsd,
linux: LinuxVersionRange,
lv2,
macosx: Version.Range,
netbsd: Version.Range,
openbsd: Version.Range,
solaris,
windows: WindowsVersion.Range,
haiku,
minix,
rtems,
nacl,
cnk,
aix,
cuda,
nvcl,
amdhsa,
ps4,
elfiamcu,
tvos,
watchos,
mesa3d,
contiki,
amdpal,
hermit,
hurd,
wasi,
emscripten,
uefi,
other,
/// See the documentation for `Os` for an explanation of the default version range. pub const Tag = enum {
pub fn defaultVersionRange(tag: @TagType(Os)) Os { freestanding,
switch (tag) { ananas,
.freestanding => return .freestanding, cloudabi,
.ananas => return .ananas, dragonfly,
.cloudabi => return .cloudabi, freebsd,
.dragonfly => return .dragonfly, fuchsia,
.freebsd => return .{ ios,
.freebsd = Version.Range{ kfreebsd,
.min = .{ .major = 12, .minor = 0 }, linux,
.max = .{ .major = 12, .minor = 1 }, lv2,
}, macosx,
}, netbsd,
.fuchsia => return .fuchsia, openbsd,
.ios => return .ios, solaris,
.kfreebsd => return .kfreebsd, windows,
.linux => return .{ haiku,
.linux = .{ minix,
.range = .{ rtems,
.min = .{ .major = 3, .minor = 16 }, nacl,
.max = .{ .major = 5, .minor = 5, .patch = 5 }, cnk,
}, aix,
.glibc = .{ .major = 2, .minor = 17 }, cuda,
}, nvcl,
}, amdhsa,
.lv2 => return .lv2, ps4,
.macosx => return .{ elfiamcu,
.min = .{ .major = 10, .minor = 13 }, tvos,
.max = .{ .major = 10, .minor = 15, .patch = 3 }, watchos,
}, mesa3d,
.netbsd => return .{ contiki,
.min = .{ .major = 8, .minor = 0 }, amdpal,
.max = .{ .major = 9, .minor = 0 }, hermit,
}, hurd,
.openbsd => return .{ wasi,
.min = .{ .major = 6, .minor = 6 }, emscripten,
.max = .{ .major = 6, .minor = 6 }, uefi,
}, other,
solaris => return .solaris,
windows => return .{
.windows = .{
.min = .win8_1,
.max = .win10_19h1,
},
},
haiku => return .haiku,
minix => return .minix,
rtems => return .rtems,
nacl => return .nacl,
cnk => return .cnk,
aix => return .aix,
cuda => return .cuda,
nvcl => return .nvcl,
amdhsa => return .amdhsa,
ps4 => return .ps4,
elfiamcu => return .elfiamcu,
tvos => return .tvos,
watchos => return .watchos,
mesa3d => return .mesa3d,
contiki => return .contiki,
amdpal => return .amdpal,
hermit => return .hermit,
hurd => return .hurd,
wasi => return .wasi,
emscripten => return .emscripten,
uefi => return .uefi,
other => return .other,
}
}
pub const LinuxVersionRange = struct {
range: Version.Range,
glibc: Version,
pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
return self.range.includesVersion(ver);
}
}; };
/// Based on NTDDI version constants from /// Based on NTDDI version constants from
@ -178,29 +83,137 @@ pub const Target = struct {
return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max); return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
} }
}; };
};
pub fn nameToTag(name: []const u8) ?WindowsVersion { pub const LinuxVersionRange = struct {
const info = @typeInfo(WindowsVersion); range: Version.Range,
inline for (info.Enum.fields) |field| { glibc: Version,
if (mem.eql(u8, name, field.name)) {
return @field(WindowsVersion, field.name); pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
} return self.range.includesVersion(ver);
}
};
/// The version ranges here represent the minimum OS version to be supported
/// and the maximum OS version to be supported. The default values represent
/// the range that the Zig Standard Library bases its abstractions on.
///
/// The minimum version of the range is the main setting to tweak for a target.
/// Usually, the maximum target OS version will remain the default, which is
/// the latest released version of the OS.
///
/// To test at compile time if the target is guaranteed to support a given OS feature,
/// one should check that the minimum version of the range is greater than or equal to
/// the version the feature was introduced in.
///
/// To test at compile time if the target certainly will not support a given OS feature,
/// one should check that the maximum version of the range is less than the version the
/// feature was introduced in.
///
/// If neither of these cases apply, a runtime check should be used to determine if the
/// target supports a given OS feature.
///
/// Binaries built with a given maximum version will continue to function on newer operating system
/// versions. However, such a binary may not take full advantage of the newer operating system APIs.
pub const VersionRange = union {
none: void,
semver: Version.Range,
linux: LinuxVersionRange,
windows: WindowsVersion.Range,
/// The default `VersionRange` represents the range that the Zig Standard Library
/// bases its abstractions on.
pub fn default(tag: Tag) VersionRange {
switch (tag) {
.freestanding,
.ananas,
.cloudabi,
.dragonfly,
.fuchsia,
.ios,
.kfreebsd,
.lv2,
.solaris,
.haiku,
.minix,
.rtems,
.nacl,
.cnk,
.aix,
.cuda,
.nvcl,
.amdhsa,
.ps4,
.elfiamcu,
.tvos,
.watchos,
.mesa3d,
.contiki,
.amdpal,
.hermit,
.hurd,
.wasi,
.emscripten,
.uefi,
.other,
=> return .{ .none = {} },
.freebsd => return .{
.semver = Version.Range{
.min = .{ .major = 12, .minor = 0 },
.max = .{ .major = 12, .minor = 1 },
},
},
.macosx => return .{
.semver = .{
.min = .{ .major = 10, .minor = 13 },
.max = .{ .major = 10, .minor = 15, .patch = 3 },
},
},
.netbsd => return .{
.semver = .{
.min = .{ .major = 8, .minor = 0 },
.max = .{ .major = 9, .minor = 0 },
},
},
.openbsd => return .{
.semver = .{
.min = .{ .major = 6, .minor = 6 },
.max = .{ .major = 6, .minor = 6 },
},
},
.linux => return .{
.linux = .{
.range = .{
.min = .{ .major = 3, .minor = 16 },
.max = .{ .major = 5, .minor = 5, .patch = 5 },
},
.glibc = .{ .major = 2, .minor = 17 },
},
},
.windows => return .{
.windows = .{
.min = .win8_1,
.max = .win10_19h1,
},
},
} }
return null;
} }
}; };
pub fn parse(text: []const u8) !Os { pub fn parse(text: []const u8) !Os {
var it = mem.separate(text, "."); var it = mem.separate(text, ".");
const os_name = it.next().?; const os_name = it.next().?;
const tag = nameToTag(os_name) orelse return error.UnknownOperatingSystem; const tag = std.meta.stringToEnum(Tag, os_name) orelse return error.UnknownOperatingSystem;
const version_text = it.rest(); const version_text = it.rest();
const S = struct { const S = struct {
fn parseNone(s: []const u8) !void { fn parseNone(s: []const u8) !void {
if (s.len != 0) return error.InvalidOperatingSystemVersion; if (s.len != 0) return error.InvalidOperatingSystemVersion;
} }
fn parseSemVer(s: []const u8, default: Version.Range) !Version.Range { fn parseSemVer(s: []const u8, d_range: Version.Range) !Version.Range {
if (s.len == 0) return default; if (s.len == 0) return d_range;
var range_it = mem.separate(s, "..."); var range_it = mem.separate(s, "...");
const min_text = range_it.next().?; const min_text = range_it.next().?;
@ -212,7 +225,7 @@ pub const Target = struct {
const max_text = range_it.next() orelse return Version.Range{ const max_text = range_it.next() orelse return Version.Range{
.min = min_ver, .min = min_ver,
.max = default.max, .max = d_range.max,
}; };
const max_ver = Version.parse(max_text) catch |err| switch (err) { const max_ver = Version.parse(max_text) catch |err| switch (err) {
error.Overflow => return error.InvalidOperatingSystemVersion, error.Overflow => return error.InvalidOperatingSystemVersion,
@ -222,79 +235,93 @@ pub const Target = struct {
return Version.Range{ .min = min_ver, .max = max_ver }; return Version.Range{ .min = min_ver, .max = max_ver };
} }
fn parseWindows(s: []const u8, default: WindowsVersion.Range) !WindowsVersion.Range { fn parseWindows(s: []const u8, d_range: WindowsVersion.Range) !WindowsVersion.Range {
if (s.len == 0) return default; if (s.len == 0) return d_range;
var range_it = mem.separate(s, "..."); var range_it = mem.separate(s, "...");
const min_text = range_it.next().?; const min_text = range_it.next().?;
const min_ver = WindowsVersion.nameToTag(min_text) orelse const min_ver = std.meta.stringToEnum(WindowsVersion, min_text) orelse
return error.InvalidOperatingSystemVersion; return error.InvalidOperatingSystemVersion;
const max_text = range_it.next() orelse return WindowsVersion.Range{ const max_text = range_it.next() orelse return WindowsVersion.Range{
.min = min_ver, .min = min_ver,
.max = default.max, .max = d_range.max,
}; };
const max_ver = WindowsVersion.nameToTag(max_text) orelse const max_ver = std.meta.stringToEnum(WindowsVersion, max_text) orelse
return error.InvalidOperatingSystemVersion; return error.InvalidOperatingSystemVersion;
return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; return WindowsVersion.Range{ .min = min_ver, .max = max_ver };
} }
}; };
const default = defaultVersionRange(tag); const d_range = VersionRange.default(tag);
switch (tag) { switch (tag) {
.freestanding => return Os{ .freestanding = try S.parseNone(version_text) }, .freestanding,
.ananas => return Os{ .ananas = try S.parseNone(version_text) }, .ananas,
.cloudabi => return Os{ .cloudabi = try S.parseNone(version_text) }, .cloudabi,
.dragonfly => return Os{ .dragonfly = try S.parseNone(version_text) }, .dragonfly,
.freebsd => return Os{ .freebsd = try S.parseSemVer(version_text, default.freebsd) }, .fuchsia,
.fuchsia => return Os{ .fuchsia = try S.parseNone(version_text) }, .ios,
.ios => return Os{ .ios = try S.parseNone(version_text) }, .kfreebsd,
.kfreebsd => return Os{ .kfreebsd = try S.parseNone(version_text) }, .lv2,
.solaris,
.haiku,
.minix,
.rtems,
.nacl,
.cnk,
.aix,
.cuda,
.nvcl,
.amdhsa,
.ps4,
.elfiamcu,
.tvos,
.watchos,
.mesa3d,
.contiki,
.amdpal,
.hermit,
.hurd,
.wasi,
.emscripten,
.uefi,
.other,
=> return Os{
.tag = tag,
.version_range = .{ .none = try S.parseNone(version_text) },
},
.freebsd,
.macosx,
.netbsd,
.openbsd,
=> return Os{
.tag = tag,
.version_range = .{ .semver = try S.parseSemVer(version_text, d_range.semver) },
},
.linux => return Os{ .linux => return Os{
.linux = .{ .tag = tag,
.range = try S.parseSemVer(version_text, default.linux.range), .version_range = .{
.glibc = default.linux.glibc, .linux = .{
.range = try S.parseSemVer(version_text, d_range.linux.range),
.glibc = d_range.linux.glibc,
},
}, },
}, },
.lv2 => return Os{ .lv2 = try S.parseNone(version_text) },
.macosx => return Os{ .macosx = try S.parseSemVer(version_text, default.macosx) }, .windows => return Os{
.netbsd => return Os{ .netbsd = try S.parseSemVer(version_text, default.netbsd) }, .tag = tag,
.openbsd => return Os{ .openbsd = try S.parseSemVer(version_text, default.openbsd) }, .version_range = .{ .windows = try S.parseWindows(version_text, d_range.windows) },
.solaris => return Os{ .solaris = try S.parseNone(version_text) }, },
.windows => return Os{ .windows = try S.parseWindows(version_text, default.windows) },
.haiku => return Os{ .haiku = try S.parseNone(version_text) },
.minix => return Os{ .minix = try S.parseNone(version_text) },
.rtems => return Os{ .rtems = try S.parseNone(version_text) },
.nacl => return Os{ .nacl = try S.parseNone(version_text) },
.cnk => return Os{ .cnk = try S.parseNone(version_text) },
.aix => return Os{ .aix = try S.parseNone(version_text) },
.cuda => return Os{ .cuda = try S.parseNone(version_text) },
.nvcl => return Os{ .nvcl = try S.parseNone(version_text) },
.amdhsa => return Os{ .amdhsa = try S.parseNone(version_text) },
.ps4 => return Os{ .ps4 = try S.parseNone(version_text) },
.elfiamcu => return Os{ .elfiamcu = try S.parseNone(version_text) },
.tvos => return Os{ .tvos = try S.parseNone(version_text) },
.watchos => return Os{ .watchos = try S.parseNone(version_text) },
.mesa3d => return Os{ .mesa3d = try S.parseNone(version_text) },
.contiki => return Os{ .contiki = try S.parseNone(version_text) },
.amdpal => return Os{ .amdpal = try S.parseNone(version_text) },
.hermit => return Os{ .hermit = try S.parseNone(version_text) },
.hurd => return Os{ .hurd = try S.parseNone(version_text) },
.wasi => return Os{ .wasi = try S.parseNone(version_text) },
.emscripten => return Os{ .emscripten = try S.parseNone(version_text) },
.uefi => return Os{ .uefi = try S.parseNone(version_text) },
.other => return Os{ .other = try S.parseNone(version_text) },
} }
} }
pub fn nameToTag(name: []const u8) ?@TagType(Os) { pub fn defaultVersionRange(tag: Tag) Os {
const info = @typeInfo(Os); return .{
inline for (info.Union.fields) |field| { .tag = tag,
if (mem.eql(u8, name, field.name)) { .version_range = VersionRange.default(tag),
return @field(Os, field.name); };
}
}
return null;
} }
}; };
@ -339,11 +366,10 @@ pub const Target = struct {
macabi, macabi,
pub fn default(arch: Cpu.Arch, target_os: Os) Abi { pub fn default(arch: Cpu.Arch, target_os: Os) Abi {
switch (arch) { if (arch.isWasm()) {
.wasm32, .wasm64 => return .musl, return .musl;
else => {},
} }
switch (target_os) { switch (target_os.tag) {
.freestanding, .freestanding,
.ananas, .ananas,
.cloudabi, .cloudabi,
@ -388,40 +414,19 @@ pub const Target = struct {
} }
} }
pub fn nameToTag(text: []const u8) ?Abi {
const info = @typeInfo(Abi);
inline for (info.Enum.fields) |field| {
if (mem.eql(u8, text, field.name)) {
return @field(Abi, field.name);
}
}
return null;
}
pub fn parse(text: []const u8, os: *Os) !Abi {
var it = mem.separate(text, ".");
const tag = nameToTag(it.next().?) orelse return error.UnknownApplicationBinaryInterface;
const version_text = it.rest();
if (version_text.len != 0) {
if (@as(@TagType(Os), os.*) == .linux and tag.isGnu()) {
os.linux.glibc = Version.parse(version_text) catch |err| switch (err) {
error.Overflow => return error.InvalidGlibcVersion,
error.InvalidCharacter => return error.InvalidGlibcVersion,
error.InvalidVersion => return error.InvalidGlibcVersion,
};
} else {
return error.InvalidAbiVersion;
}
}
return tag;
}
pub fn isGnu(abi: Abi) bool { pub fn isGnu(abi: Abi) bool {
return switch (abi) { return switch (abi) {
.gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true,
else => false, else => false,
}; };
} }
pub fn isMusl(abi: Abi) bool {
return switch (abi) {
.musl, .musleabi, .musleabihf => true,
else => false,
};
}
}; };
pub const ObjectFormat = enum { pub const ObjectFormat = enum {
@ -909,15 +914,15 @@ pub const Target = struct {
/// TODO add OS version ranges and glibc version /// TODO add OS version ranges and glibc version
pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
@tagName(self.getArch()), @tagName(self.cpu.arch),
@tagName(self.os), @tagName(self.os.tag),
@tagName(self.abi), @tagName(self.abi),
}); });
} }
/// Returned slice must be freed by the caller. /// Returned slice must be freed by the caller.
pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 {
const arch = switch (target.getArch()) { const arch = switch (target.cpu.arch) {
.i386 => "x86", .i386 => "x86",
.x86_64 => "x64", .x86_64 => "x64",
@ -957,16 +962,16 @@ pub const Target = struct {
pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 {
return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
@tagName(self.getArch()), @tagName(self.cpu.arch),
@tagName(self.os), @tagName(self.os.tag),
@tagName(self.abi), @tagName(self.abi),
}); });
} }
pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
@tagName(self.getArch()), @tagName(self.cpu.arch),
@tagName(self.os), @tagName(self.os.tag),
@tagName(self.abi), @tagName(self.abi),
}); });
} }
@ -1017,11 +1022,28 @@ pub const Target = struct {
diags.arch = arch; diags.arch = arch;
const os_name = it.next() orelse return error.MissingOperatingSystem; const os_name = it.next() orelse return error.MissingOperatingSystem;
var os = try Os.parse(os_name); // var because Abi.parse can update linux.glibc version var os = try Os.parse(os_name);
diags.os = os; diags.os = os;
const abi_name = it.next(); const opt_abi_text = it.next();
const abi = if (abi_name) |n| try Abi.parse(n, &os) else Abi.default(arch, os); const abi = if (opt_abi_text) |abi_text| blk: {
var abi_it = mem.separate(abi_text, ".");
const abi = std.meta.stringToEnum(Abi, abi_it.next().?) orelse
return error.UnknownApplicationBinaryInterface;
const abi_ver_text = abi_it.rest();
if (abi_ver_text.len != 0) {
if (os.tag == .linux and abi.isGnu()) {
os.version_range.linux.glibc = Version.parse(abi_ver_text) catch |err| switch (err) {
error.Overflow => return error.InvalidAbiVersion,
error.InvalidCharacter => return error.InvalidAbiVersion,
error.InvalidVersion => return error.InvalidAbiVersion,
};
} else {
return error.InvalidAbiVersion;
}
}
break :blk abi;
} else Abi.default(arch, os);
diags.abi = abi; diags.abi = abi;
if (it.next() != null) return error.UnexpectedExtraField; if (it.next() != null) return error.UnexpectedExtraField;
@ -1130,25 +1152,6 @@ pub const Target = struct {
} }
} }
/// Deprecated; access the `os` field directly.
pub fn getOs(self: Target) @TagType(Os) {
return self.os;
}
/// Deprecated; access the `cpu` field directly.
pub fn getCpu(self: Target) Cpu {
return self.cpu;
}
/// Deprecated; access the `abi` field directly.
pub fn getAbi(self: Target) Abi {
return self.abi;
}
pub fn getArch(self: Target) Cpu.Arch {
return self.cpu.arch;
}
pub fn getObjectFormat(self: Target) ObjectFormat { pub fn getObjectFormat(self: Target) ObjectFormat {
if (self.isWindows() or self.isUefi()) { if (self.isWindows() or self.isUefi()) {
return .coff; return .coff;
@ -1170,28 +1173,25 @@ pub const Target = struct {
} }
pub fn isMusl(self: Target) bool { pub fn isMusl(self: Target) bool {
return switch (self.abi) { return self.abi.isMusl();
.musl, .musleabi, .musleabihf => true,
else => false,
};
} }
pub fn isDarwin(self: Target) bool { pub fn isDarwin(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.ios, .macosx, .watchos, .tvos => true, .ios, .macosx, .watchos, .tvos => true,
else => false, else => false,
}; };
} }
pub fn isWindows(self: Target) bool { pub fn isWindows(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.windows => true, .windows => true,
else => false, else => false,
}; };
} }
pub fn isLinux(self: Target) bool { pub fn isLinux(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.linux => true, .linux => true,
else => false, else => false,
}; };
@ -1205,40 +1205,41 @@ pub const Target = struct {
} }
pub fn isDragonFlyBSD(self: Target) bool { pub fn isDragonFlyBSD(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.dragonfly => true, .dragonfly => true,
else => false, else => false,
}; };
} }
pub fn isUefi(self: Target) bool { pub fn isUefi(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.uefi => true, .uefi => true,
else => false, else => false,
}; };
} }
pub fn isWasm(self: Target) bool { pub fn isWasm(self: Target) bool {
return switch (self.getArch()) { return self.cpu.arch.isWasm();
.wasm32, .wasm64 => true,
else => false,
};
} }
pub fn isFreeBSD(self: Target) bool { pub fn isFreeBSD(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.freebsd => true, .freebsd => true,
else => false, else => false,
}; };
} }
pub fn isNetBSD(self: Target) bool { pub fn isNetBSD(self: Target) bool {
return switch (self.os) { return switch (self.os.tag) {
.netbsd => true, .netbsd => true,
else => false, else => false,
}; };
} }
pub fn isGnuLibC(self: Target) bool {
return self.os.tag == .linux and self.abi.isGnu();
}
pub fn wantSharedLibSymLinks(self: Target) bool { pub fn wantSharedLibSymLinks(self: Target) bool {
return !self.isWindows(); return !self.isWindows();
} }
@ -1248,7 +1249,7 @@ pub const Target = struct {
} }
pub fn getArchPtrBitWidth(self: Target) u32 { pub fn getArchPtrBitWidth(self: Target) u32 {
switch (self.getArch()) { switch (self.cpu.arch) {
.avr, .avr,
.msp430, .msp430,
=> return 16, => return 16,
@ -1323,8 +1324,8 @@ pub const Target = struct {
if (@as(@TagType(Target), self) == .Native) return .native; if (@as(@TagType(Target), self) == .Native) return .native;
// If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture.
if (self.os == builtin.os) { if (self.os.tag == builtin.os.tag) {
return switch (self.getArch()) { return switch (self.cpu.arch) {
.aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64 => Executor{ .qemu = "qemu-aarch64" },
.aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
.arm => Executor{ .qemu = "qemu-arm" }, .arm => Executor{ .qemu = "qemu-arm" },
@ -1381,13 +1382,10 @@ pub const Target = struct {
} }
pub fn hasDynamicLinker(self: Target) bool { pub fn hasDynamicLinker(self: Target) bool {
switch (self.getArch()) { if (self.cpu.arch.isWasm()) {
.wasm32, return false;
.wasm64,
=> return false,
else => {},
} }
switch (self.os) { switch (self.os.tag) {
.freestanding, .freestanding,
.ios, .ios,
.tvos, .tvos,
@ -1424,7 +1422,7 @@ pub const Target = struct {
defer result.deinit(); defer result.deinit();
var is_arm = false; var is_arm = false;
switch (self.getArch()) { switch (self.cpu.arch) {
.arm, .thumb => { .arm, .thumb => {
try result.append("arm"); try result.append("arm");
is_arm = true; is_arm = true;
@ -1442,11 +1440,11 @@ pub const Target = struct {
return result.toOwnedSlice(); return result.toOwnedSlice();
} }
switch (self.os) { switch (self.os.tag) {
.freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"),
.netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"),
.dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"),
.linux => switch (self.getArch()) { .linux => switch (self.cpu.arch) {
.i386, .i386,
.sparc, .sparc,
.sparcel, .sparcel,
@ -1539,7 +1537,7 @@ test "Target.parse" {
.cpu_features = "x86_64-sse-sse2-avx-cx8", .cpu_features = "x86_64-sse-sse2-avx-cx8",
}); });
std.testing.expect(target.os == .linux); std.testing.expect(target.os.tag == .linux);
std.testing.expect(target.abi == .gnu); std.testing.expect(target.abi == .gnu);
std.testing.expect(target.cpu.arch == .x86_64); std.testing.expect(target.cpu.arch == .x86_64);
std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
@ -1554,10 +1552,29 @@ test "Target.parse" {
.cpu_features = "generic+v8a", .cpu_features = "generic+v8a",
}); });
std.testing.expect(target.os == .linux); std.testing.expect(target.os.tag == .linux);
std.testing.expect(target.abi == .musleabihf); std.testing.expect(target.abi == .musleabihf);
std.testing.expect(target.cpu.arch == .arm); std.testing.expect(target.cpu.arch == .arm);
std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
} }
{
const target = try Target.parse(.{
.arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
.cpu_features = "generic+v8a",
});
std.testing.expect(target.cpu.arch == .aarch64);
std.testing.expect(target.os.tag == .linux);
std.testing.expect(target.os.version_range.linux.min.major == 3);
std.testing.expect(target.os.version_range.linux.min.minor == 10);
std.testing.expect(target.os.version_range.linux.min.patch == 0);
std.testing.expect(target.os.version_range.linux.max.major == 4);
std.testing.expect(target.os.version_range.linux.max.minor == 4);
std.testing.expect(target.os.version_range.linux.max.patch == 1);
std.testing.expect(target.os.version_range.linux.glibc.major == 2);
std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
std.testing.expect(target.abi == .gnu);
}
} }

View File

@ -9,14 +9,14 @@ const assert = std.debug.assert;
pub const Thread = struct { pub const Thread = struct {
data: Data, data: Data,
pub const use_pthreads = builtin.os != .windows and builtin.link_libc; pub const use_pthreads = builtin.os.tag != .windows and builtin.link_libc;
/// Represents a kernel thread handle. /// Represents a kernel thread handle.
/// May be an integer or a pointer depending on the platform. /// May be an integer or a pointer depending on the platform.
/// On Linux and POSIX, this is the same as Id. /// On Linux and POSIX, this is the same as Id.
pub const Handle = if (use_pthreads) pub const Handle = if (use_pthreads)
c.pthread_t c.pthread_t
else switch (builtin.os) { else switch (builtin.os.tag) {
.linux => i32, .linux => i32,
.windows => windows.HANDLE, .windows => windows.HANDLE,
else => void, else => void,
@ -25,7 +25,7 @@ pub const Thread = struct {
/// Represents a unique ID per thread. /// Represents a unique ID per thread.
/// May be an integer or pointer depending on the platform. /// May be an integer or pointer depending on the platform.
/// On Linux and POSIX, this is the same as Handle. /// On Linux and POSIX, this is the same as Handle.
pub const Id = switch (builtin.os) { pub const Id = switch (builtin.os.tag) {
.windows => windows.DWORD, .windows => windows.DWORD,
else => Handle, else => Handle,
}; };
@ -35,7 +35,7 @@ pub const Thread = struct {
handle: Thread.Handle, handle: Thread.Handle,
memory: []align(mem.page_size) u8, memory: []align(mem.page_size) u8,
} }
else switch (builtin.os) { else switch (builtin.os.tag) {
.linux => struct { .linux => struct {
handle: Thread.Handle, handle: Thread.Handle,
memory: []align(mem.page_size) u8, memory: []align(mem.page_size) u8,
@ -55,7 +55,7 @@ pub const Thread = struct {
if (use_pthreads) { if (use_pthreads) {
return c.pthread_self(); return c.pthread_self();
} else } else
return switch (builtin.os) { return switch (builtin.os.tag) {
.linux => os.linux.gettid(), .linux => os.linux.gettid(),
.windows => windows.kernel32.GetCurrentThreadId(), .windows => windows.kernel32.GetCurrentThreadId(),
else => @compileError("Unsupported OS"), else => @compileError("Unsupported OS"),
@ -83,7 +83,7 @@ pub const Thread = struct {
else => unreachable, else => unreachable,
} }
os.munmap(self.data.memory); os.munmap(self.data.memory);
} else switch (builtin.os) { } else switch (builtin.os.tag) {
.linux => { .linux => {
while (true) { while (true) {
const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
@ -150,7 +150,7 @@ pub const Thread = struct {
const Context = @TypeOf(context); const Context = @TypeOf(context);
comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context); comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context);
if (builtin.os == builtin.Os.windows) { if (builtin.os.tag == .windows) {
const WinThread = struct { const WinThread = struct {
const OuterContext = struct { const OuterContext = struct {
thread: Thread, thread: Thread,
@ -309,7 +309,7 @@ pub const Thread = struct {
os.EINVAL => unreachable, os.EINVAL => unreachable,
else => return os.unexpectedErrno(@intCast(usize, err)), else => return os.unexpectedErrno(@intCast(usize, err)),
} }
} else if (builtin.os == .linux) { } else if (builtin.os.tag == .linux) {
var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND | var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND |
os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
os.CLONE_DETACHED; os.CLONE_DETACHED;
@ -369,11 +369,11 @@ pub const Thread = struct {
}; };
pub fn cpuCount() CpuCountError!usize { pub fn cpuCount() CpuCountError!usize {
if (builtin.os == .linux) { if (builtin.os.tag == .linux) {
const cpu_set = try os.sched_getaffinity(0); const cpu_set = try os.sched_getaffinity(0);
return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast
} }
if (builtin.os == .windows) { if (builtin.os.tag == .windows) {
var system_info: windows.SYSTEM_INFO = undefined; var system_info: windows.SYSTEM_INFO = undefined;
windows.kernel32.GetSystemInfo(&system_info); windows.kernel32.GetSystemInfo(&system_info);
return @intCast(usize, system_info.dwNumberOfProcessors); return @intCast(usize, system_info.dwNumberOfProcessors);

View File

@ -1,5 +1,5 @@
const builtin = @import("builtin");
const std = @import("std.zig"); const std = @import("std.zig");
const builtin = std.builtin;
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const os = std.os; const os = std.os;
@ -7,10 +7,12 @@ const math = std.math;
pub const epoch = @import("time/epoch.zig"); pub const epoch = @import("time/epoch.zig");
const is_windows = std.Target.current.os.tag == .windows;
/// Spurious wakeups are possible and no precision of timing is guaranteed. /// Spurious wakeups are possible and no precision of timing is guaranteed.
/// TODO integrate with evented I/O /// TODO integrate with evented I/O
pub fn sleep(nanoseconds: u64) void { pub fn sleep(nanoseconds: u64) void {
if (builtin.os == .windows) { if (is_windows) {
const ns_per_ms = ns_per_s / ms_per_s; const ns_per_ms = ns_per_s / ms_per_s;
const big_ms_from_ns = nanoseconds / ns_per_ms; const big_ms_from_ns = nanoseconds / ns_per_ms;
const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD); const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD);
@ -31,7 +33,7 @@ pub fn timestamp() u64 {
/// Get the posix timestamp, UTC, in milliseconds /// Get the posix timestamp, UTC, in milliseconds
/// TODO audit this function. is it possible to return an error? /// TODO audit this function. is it possible to return an error?
pub fn milliTimestamp() u64 { pub fn milliTimestamp() u64 {
if (builtin.os == .windows) { if (is_windows) {
//FileTime has a granularity of 100 nanoseconds //FileTime has a granularity of 100 nanoseconds
// and uses the NTFS/Windows epoch // and uses the NTFS/Windows epoch
var ft: os.windows.FILETIME = undefined; var ft: os.windows.FILETIME = undefined;
@ -42,7 +44,7 @@ pub fn milliTimestamp() u64 {
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return @divFloor(ft64, hns_per_ms) - -epoch_adj; return @divFloor(ft64, hns_per_ms) - -epoch_adj;
} }
if (builtin.os == .wasi and !builtin.link_libc) { if (builtin.os.tag == .wasi and !builtin.link_libc) {
var ns: os.wasi.timestamp_t = undefined; var ns: os.wasi.timestamp_t = undefined;
// TODO: Verify that precision is ignored // TODO: Verify that precision is ignored
@ -102,7 +104,7 @@ pub const Timer = struct {
///if we used resolution's value when performing the ///if we used resolution's value when performing the
/// performance counter calc on windows/darwin, it would /// performance counter calc on windows/darwin, it would
/// be less precise /// be less precise
frequency: switch (builtin.os) { frequency: switch (builtin.os.tag) {
.windows => u64, .windows => u64,
.macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data, .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,
else => void, else => void,
@ -127,7 +129,7 @@ pub const Timer = struct {
pub fn start() Error!Timer { pub fn start() Error!Timer {
var self: Timer = undefined; var self: Timer = undefined;
if (builtin.os == .windows) { if (is_windows) {
self.frequency = os.windows.QueryPerformanceFrequency(); self.frequency = os.windows.QueryPerformanceFrequency();
self.resolution = @divFloor(ns_per_s, self.frequency); self.resolution = @divFloor(ns_per_s, self.frequency);
self.start_time = os.windows.QueryPerformanceCounter(); self.start_time = os.windows.QueryPerformanceCounter();
@ -172,7 +174,7 @@ pub const Timer = struct {
} }
fn clockNative() u64 { fn clockNative() u64 {
if (builtin.os == .windows) { if (is_windows) {
return os.windows.QueryPerformanceCounter(); return os.windows.QueryPerformanceCounter();
} }
if (comptime std.Target.current.isDarwin()) { if (comptime std.Target.current.isDarwin()) {
@ -184,7 +186,7 @@ pub const Timer = struct {
} }
fn nativeDurationToNanos(self: Timer, duration: u64) u64 { fn nativeDurationToNanos(self: Timer, duration: u64) u64 {
if (builtin.os == .windows) { if (is_windows) {
return @divFloor(duration * ns_per_s, self.frequency); return @divFloor(duration * ns_per_s, self.frequency);
} }
if (comptime std.Target.current.isDarwin()) { if (comptime std.Target.current.isDarwin()) {

View File

@ -1,11 +1,14 @@
const std = @import("../std.zig"); const std = @import("../std.zig");
const elf = std.elf;
const mem = std.mem; const mem = std.mem;
const fs = std.fs;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
const assert = std.debug.assert; const assert = std.debug.assert;
const process = std.process; const process = std.process;
const Target = std.Target;
const is_windows = std.Target.current.isWindows(); const is_windows = Target.current.os.tag == .windows;
pub const NativePaths = struct { pub const NativePaths = struct {
include_dirs: ArrayList([:0]u8), include_dirs: ArrayList([:0]u8),
@ -77,7 +80,7 @@ pub const NativePaths = struct {
} }
if (!is_windows) { if (!is_windows) {
const triple = try std.Target.current.linuxTriple(allocator); const triple = try Target.current.linuxTriple(allocator);
// TODO: $ ld --verbose | grep SEARCH_DIR // TODO: $ ld --verbose | grep SEARCH_DIR
// the output contains some paths that end with lib64, maybe include them too? // the output contains some paths that end with lib64, maybe include them too?
@ -161,3 +164,326 @@ pub const NativePaths = struct {
try array.append(item); try array.append(item);
} }
}; };
pub const NativeTargetInfo = struct {
target: Target,
dynamic_linker: ?[:0]u8,
pub const DetectError = error{
OutOfMemory,
FileSystem,
SystemResources,
SymLinkLoop,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
DeviceBusy,
};
/// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker.
/// On Linux, this is additionally responsible for detecting the native glibc version when applicable.
pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo {
const arch = Target.current.cpu.arch;
const os_tag = Target.current.os.tag;
// TODO Detect native CPU model & features. Until that is implemented we hard code baseline.
const cpu = Target.Cpu.baseline(arch);
// TODO Detect native operating system version. Until that is implemented we use the minimum version
// of the default range.
const os = Target.Os.defaultVersionRange(os_tag);
return detectAbiAndDynamicLinker(allocator, cpu, os);
}
/// Must be the same `Allocator` passed to `detect`.
pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void {
if (self.dynamic_linker) |dl| allocator.free(dl);
self.* = undefined;
}
/// First we attempt to use the executable's own binary. If it is dynamically
/// linked, then it should answer both the C ABI question and the dynamic linker question.
/// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then
/// we fall back to the defaults.
fn detectAbiAndDynamicLinker(
allocator: *Allocator,
cpu: Target.Cpu,
os: Target.Os,
) DetectError!NativeTargetInfo {
if (!comptime Target.current.hasDynamicLinker()) {
return defaultAbiAndDynamicLinker(allocator, cpu, os);
}
// The current target's ABI cannot be relied on for this. For example, we may build the zig
// compiler for target riscv64-linux-musl and provide a tarball for users to download.
// A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
// and supported by Zig. But that means that we must detect the system ABI here rather than
// relying on `Target.current`.
const LdInfo = struct {
ld_path: []u8,
abi: Target.Abi,
};
var ld_info_list = std.ArrayList(LdInfo).init(allocator);
defer {
for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path);
ld_info_list.deinit();
}
const all_abis = comptime blk: {
assert(@enumToInt(Target.Abi.none) == 0);
const fields = std.meta.fields(Target.Abi)[1..];
var array: [fields.len]Target.Abi = undefined;
inline for (fields) |field, i| {
array[i] = @field(Target.Abi, field.name);
}
break :blk array;
};
for (all_abis) |abi| {
// This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
// skip adding it to `ld_info_list`.
const target: Target = .{
.cpu = cpu,
.os = os,
.abi = abi,
};
const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
};
errdefer allocator.free(standard_ld_path);
try ld_info_list.append(.{
.ld_path = standard_ld_path,
.abi = abi,
});
}
// Best case scenario: the executable is dynamically linked, and we can iterate
// over our own shared objects and find a dynamic linker.
self_exe: {
const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
defer allocator.free(lib_paths);
var found_ld_info: LdInfo = undefined;
var found_ld_path: [:0]const u8 = undefined;
// Look for dynamic linker.
// This is O(N^M) but typical case here is N=2 and M=10.
find_ld: for (lib_paths) |lib_path| {
for (ld_info_list.toSlice()) |ld_info| {
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
found_ld_info = ld_info;
found_ld_path = lib_path;
break :find_ld;
}
}
} else break :self_exe;
// Look for glibc version.
var os_adjusted = os;
if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu()) {
for (lib_paths) |lib_path| {
if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) {
os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) {
error.UnrecognizedGnuLibCFileName => continue,
error.InvalidGnuLibCVersion => continue,
error.GnuLibCVersionUnavailable => continue,
else => |e| return e,
};
break;
}
}
}
return NativeTargetInfo{
.target = .{
.cpu = cpu,
.os = os_adjusted,
.abi = found_ld_info.abi,
},
.dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path),
};
}
// If Zig is statically linked, such as via distributed binary static builds, the above
// trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env.
// Since that path is hard-coded into the shebang line of many portable scripts, it's a
// reasonably reliable path to check for.
return abiAndDynamicLinkerFromUsrBinEnv(allocator, cpu, os) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.FileSystem => return error.FileSystem,
error.SystemResources => return error.SystemResources,
error.SymLinkLoop => return error.SymLinkLoop,
error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded,
error.DeviceBusy => return error.DeviceBusy,
error.UnableToReadElfFile,
error.ElfNotADynamicExecutable,
error.InvalidElfProgramHeaders,
error.InvalidElfClass,
error.InvalidElfVersion,
error.InvalidElfEndian,
error.InvalidElfFile,
error.InvalidElfMagic,
error.UsrBinEnvNotAvailable,
error.Unexpected,
// Finally, we fall back on the standard path.
=> defaultAbiAndDynamicLinker(allocator, cpu, os),
};
}
const glibc_so_basename = "libc.so.6";
fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version {
var link_buf: [std.os.PATH_MAX]u8 = undefined;
const link_name = std.os.readlinkC(so_path.ptr, &link_buf) catch |err| switch (err) {
error.AccessDenied => return error.GnuLibCVersionUnavailable,
error.FileSystem => return error.FileSystem,
error.SymLinkLoop => return error.SymLinkLoop,
error.NameTooLong => unreachable,
error.FileNotFound => return error.GnuLibCVersionUnavailable,
error.SystemResources => return error.SystemResources,
error.NotDir => return error.GnuLibCVersionUnavailable,
error.Unexpected => return error.GnuLibCVersionUnavailable,
};
// example: "libc-2.3.4.so"
// example: "libc-2.27.so"
const prefix = "libc-";
const suffix = ".so";
if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
return error.UnrecognizedGnuLibCFileName;
}
// chop off "libc-" and ".so"
const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) {
error.Overflow => return error.InvalidGnuLibCVersion,
error.InvalidCharacter => return error.InvalidGnuLibCVersion,
error.InvalidVersion => return error.InvalidGnuLibCVersion,
};
}
fn abiAndDynamicLinkerFromUsrBinEnv(
allocator: *Allocator,
cpu: Target.Cpu,
os: Target.Os,
) !NativeTargetInfo {
const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) {
error.NoSpaceLeft => unreachable,
error.NameTooLong => unreachable,
error.PathAlreadyExists => unreachable,
error.SharingViolation => unreachable,
error.InvalidUtf8 => unreachable,
error.BadPathName => unreachable,
error.PipeBusy => unreachable,
error.IsDir => return error.UsrBinEnvNotAvailable,
error.NotDir => return error.UsrBinEnvNotAvailable,
error.AccessDenied => return error.UsrBinEnvNotAvailable,
error.NoDevice => return error.UsrBinEnvNotAvailable,
error.FileNotFound => return error.UsrBinEnvNotAvailable,
error.FileTooBig => return error.UsrBinEnvNotAvailable,
else => |e| return e,
};
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
const hdr_bytes_len = try wrapRead(env_file.pread(&hdr_buf, 0));
if (hdr_bytes_len < @sizeOf(elf.Elf32_Ehdr)) return error.InvalidElfFile;
const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
elf.ELFDATA2LSB => .Little,
elf.ELFDATA2MSB => .Big,
else => return error.InvalidElfEndian,
};
const need_bswap = elf_endian != std.builtin.endian;
if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
elf.ELFCLASS32 => false,
elf.ELFCLASS64 => true,
else => return error.InvalidElfClass,
};
var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) {
error.Overflow => return error.InvalidElfProgramHeaders,
};
var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
var ph_i: u16 = 0;
while (ph_i < phnum) {
// Reserve some bytes so that we can deref the 64-bit struct fields even when the ELF file is 32-bits.
const reserve = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
const read_byte_len = try wrapRead(env_file.pread(ph_buf[0 .. ph_buf.len - reserve], phoff));
if (read_byte_len < phentsize) return error.ElfNotADynamicExecutable;
var buf_i: usize = 0;
while (buf_i < read_byte_len and ph_i < phnum) : ({
ph_i += 1;
phoff += phentsize;
buf_i += phentsize;
}) {
const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[buf_i]));
const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[buf_i]));
const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
switch (p_type) {
elf.PT_INTERP => {
std.debug.warn("found PT_INTERP\n", .{});
},
elf.PT_DYNAMIC => {
std.debug.warn("found PT_DYNAMIC\n", .{});
},
else => continue,
}
}
}
return error.OutOfMemory; // TODO
}
fn wrapRead(res: std.os.ReadError!usize) !usize {
return res catch |err| switch (err) {
error.OperationAborted => unreachable, // Windows-only
error.WouldBlock => unreachable, // Did not request blocking mode
error.SystemResources => return error.SystemResources,
error.IsDir => return error.UnableToReadElfFile,
error.BrokenPipe => return error.UnableToReadElfFile,
error.ConnectionResetByPeer => return error.UnableToReadElfFile,
error.Unexpected => return error.Unexpected,
error.InputOutput => return error.FileSystem,
};
}
fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo {
const target: Target = .{
.cpu = cpu,
.os = os,
.abi = Target.Abi.default(cpu.arch, os),
};
return @as(NativeTargetInfo, .{
.target = target,
.dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null,
},
});
}
};
fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
if (is_64) {
if (need_bswap) {
return @byteSwap(@TypeOf(int_64), int_64);
} else {
return int_64;
}
} else {
if (need_bswap) {
return @byteSwap(@TypeOf(int_32), int_32);
} else {
return int_32;
}
}
}

View File

@ -70,7 +70,7 @@ pub const CInt = struct {
pub fn sizeInBits(cint: CInt, self: Target) u32 { pub fn sizeInBits(cint: CInt, self: Target) u32 {
const arch = self.getArch(); const arch = self.getArch();
switch (self.getOs()) { switch (self.os.tag) {
.freestanding, .other => switch (self.getArch()) { .freestanding, .other => switch (self.getArch()) {
.msp430 => switch (cint.id) { .msp430 => switch (cint.id) {
.Short, .Short,

View File

@ -1050,7 +1050,7 @@ pub const struct_ZigClangExprEvalResult = extern struct {
pub const struct_ZigClangAPValue = extern struct { pub const struct_ZigClangAPValue = extern struct {
Kind: ZigClangAPValueKind, Kind: ZigClangAPValueKind,
Data: if (builtin.os == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8,
}; };
pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType; pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType;

View File

@ -1,4 +1,4 @@
// Introspection and determination of system libraries needed by zig. //! Introspection and determination of system libraries needed by zig.
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
@ -6,14 +6,6 @@ const fs = std.fs;
const warn = std.debug.warn; const warn = std.debug.warn;
pub fn detectDynamicLinker(allocator: *mem.Allocator, target: std.Target) ![:0]u8 {
if (target == .Native) {
return @import("libc_installation.zig").detectNativeDynamicLinker(allocator);
} else {
return target.getStandardDynamicLinkerPath(allocator);
}
}
/// Caller must free result /// Caller must free result
pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" });

View File

@ -99,27 +99,27 @@ pub const LibCInstallation = struct {
return error.ParseError; return error.ParseError;
} }
if (self.crt_dir == null and !is_darwin) { if (self.crt_dir == null and !is_darwin) {
try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())}); try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
return error.ParseError; return error.ParseError;
} }
if (self.static_crt_dir == null and is_windows and is_gnu) { if (self.static_crt_dir == null and is_windows and is_gnu) {
try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{ try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{
@tagName(Target.current.getOs()), @tagName(Target.current.os.tag),
@tagName(Target.current.getAbi()), @tagName(Target.current.abi),
}); });
return error.ParseError; return error.ParseError;
} }
if (self.msvc_lib_dir == null and is_windows and !is_gnu) { if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{ try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{
@tagName(Target.current.getOs()), @tagName(Target.current.os.tag),
@tagName(Target.current.getAbi()), @tagName(Target.current.abi),
}); });
return error.ParseError; return error.ParseError;
} }
if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{ try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{
@tagName(Target.current.getOs()), @tagName(Target.current.os.tag),
@tagName(Target.current.getAbi()), @tagName(Target.current.abi),
}); });
return error.ParseError; return error.ParseError;
} }
@ -616,104 +616,6 @@ fn printVerboseInvocation(
} }
} }
/// Caller owns returned memory.
pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
OutOfMemory,
TargetHasNoDynamicLinker,
UnknownDynamicLinkerPath,
}![:0]u8 {
if (!comptime Target.current.hasDynamicLinker()) {
return error.TargetHasNoDynamicLinker;
}
// The current target's ABI cannot be relied on for this. For example, we may build the zig
// compiler for target riscv64-linux-musl and provide a tarball for users to download.
// A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
// and supported by Zig. But that means that we must detect the system ABI here rather than
// relying on `std.Target.current`.
const LdInfo = struct {
ld_path: []u8,
abi: Target.Abi,
};
var ld_info_list = std.ArrayList(LdInfo).init(allocator);
defer {
for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path);
ld_info_list.deinit();
}
const all_abis = comptime blk: {
const fields = std.meta.fields(Target.Abi);
var array: [fields.len]Target.Abi = undefined;
inline for (fields) |field, i| {
array[i] = @field(Target.Abi, field.name);
}
break :blk array;
};
for (all_abis) |abi| {
// This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
// skip adding it to `ld_info_list`.
const target: Target = .{
.Cross = .{
.cpu = Target.Cpu.baseline(Target.current.getArch()),
.os = Target.current.getOs(),
.abi = abi,
},
};
const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
};
errdefer allocator.free(standard_ld_path);
try ld_info_list.append(.{
.ld_path = standard_ld_path,
.abi = abi,
});
}
// Best case scenario: the zig compiler is dynamically linked, and we can iterate
// over our own shared objects and find a dynamic linker.
{
const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
defer allocator.free(lib_paths);
// This is O(N^M) but typical case here is N=2 and M=10.
for (lib_paths) |lib_path| {
for (ld_info_list.toSlice()) |ld_info| {
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
return std.mem.dupeZ(allocator, u8, lib_path);
}
}
}
}
// If Zig is statically linked, such as via distributed binary static builds, the above
// trick won't work. What are we left with? Try to run the system C compiler and get
// it to tell us the dynamic linker path.
// TODO: instead of this, look at the shared libs of /usr/bin/env.
for (ld_info_list.toSlice()) |ld_info| {
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
const full_ld_path = ccPrintFileName(.{
.allocator = allocator,
.search_basename = standard_ld_basename,
.want_dirname = .full_path,
}) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.LibCRuntimeNotFound,
error.CCompilerExitCode,
error.CCompilerCrashed,
error.UnableToSpawnCCompiler,
=> continue,
};
return full_ld_path;
}
// Finally, we fall back on the standard path.
return Target.current.getStandardDynamicLinkerPath(allocator);
}
const Search = struct { const Search = struct {
path: []const u8, path: []const u8,
version: []const u8, version: []const u8,

View File

@ -515,7 +515,7 @@ const DarwinPlatform = struct {
break :blk ver; break :blk ver;
}, },
.None => blk: { .None => blk: {
assert(comp.target.getOs() == .macosx); assert(comp.target.os.tag == .macosx);
result.kind = .MacOS; result.kind = .MacOS;
break :blk "10.14"; break :blk "10.14";
}, },
@ -534,7 +534,7 @@ const DarwinPlatform = struct {
} }
if (result.kind == .IPhoneOS) { if (result.kind == .IPhoneOS) {
switch (comp.target.getArch()) { switch (comp.target.cpu.arch) {
.i386, .i386,
.x86_64, .x86_64,
=> result.kind = .IPhoneOSSimulator, => result.kind = .IPhoneOSSimulator,

View File

@ -79,9 +79,9 @@ pub fn main() !void {
} else if (mem.eql(u8, cmd, "libc")) { } else if (mem.eql(u8, cmd, "libc")) {
return cmdLibC(allocator, cmd_args); return cmdLibC(allocator, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) { } else if (mem.eql(u8, cmd, "targets")) {
// TODO figure out the current target rather than using the target that was specified when const info = try std.zig.system.NativeTargetInfo.detect(allocator);
// compiling the compiler defer info.deinit(allocator);
return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, Target.current); return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
} else if (mem.eql(u8, cmd, "version")) { } else if (mem.eql(u8, cmd, "version")) {
return cmdVersion(allocator, cmd_args); return cmdVersion(allocator, cmd_args);
} else if (mem.eql(u8, cmd, "zen")) { } else if (mem.eql(u8, cmd, "zen")) {

View File

@ -124,7 +124,7 @@ pub fn cmdTargets(
try jws.objectField("os"); try jws.objectField("os");
try jws.beginArray(); try jws.beginArray();
inline for (@typeInfo(Target.Os).Enum.fields) |field| { inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| {
try jws.arrayElem(); try jws.arrayElem();
try jws.emitString(field.name); try jws.emitString(field.name);
} }
@ -201,16 +201,16 @@ pub fn cmdTargets(
try jws.objectField("cpu"); try jws.objectField("cpu");
try jws.beginObject(); try jws.beginObject();
try jws.objectField("arch"); try jws.objectField("arch");
try jws.emitString(@tagName(native_target.getArch())); try jws.emitString(@tagName(native_target.cpu.arch));
try jws.objectField("name"); try jws.objectField("name");
const cpu = native_target.getCpu(); const cpu = native_target.cpu;
try jws.emitString(cpu.model.name); try jws.emitString(cpu.model.name);
{ {
try jws.objectField("features"); try jws.objectField("features");
try jws.beginArray(); try jws.beginArray();
for (native_target.getArch().allFeaturesList()) |feature, i_usize| { for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| {
const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize); const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
if (cpu.features.isEnabled(index)) { if (cpu.features.isEnabled(index)) {
try jws.arrayElem(); try jws.arrayElem();
@ -222,9 +222,9 @@ pub fn cmdTargets(
try jws.endObject(); try jws.endObject();
} }
try jws.objectField("os"); try jws.objectField("os");
try jws.emitString(@tagName(native_target.getOs())); try jws.emitString(@tagName(native_target.os.tag));
try jws.objectField("abi"); try jws.objectField("abi");
try jws.emitString(@tagName(native_target.getAbi())); try jws.emitString(@tagName(native_target.abi));
// TODO implement native glibc version detection in self-hosted // TODO implement native glibc version detection in self-hosted
try jws.endObject(); try jws.endObject();

View File

@ -110,6 +110,8 @@ const Error = extern enum {
WindowsSdkNotFound, WindowsSdkNotFound,
UnknownDynamicLinkerPath, UnknownDynamicLinkerPath,
TargetHasNoDynamicLinker, TargetHasNoDynamicLinker,
InvalidAbiVersion,
InvalidOperatingSystemVersion,
}; };
const FILE = std.c.FILE; const FILE = std.c.FILE;
@ -633,11 +635,11 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int {
fn cmdTargets(zig_triple: [*:0]const u8) !void { fn cmdTargets(zig_triple: [*:0]const u8) !void {
var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) });
target.Cross.cpu = blk: { target.cpu = blk: {
const llvm = @import("llvm.zig"); const llvm = @import("llvm.zig");
const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_name = llvm.GetHostCPUName();
const llvm_cpu_features = llvm.GetNativeFeatures(); const llvm_cpu_features = llvm.GetNativeFeatures();
break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); break :blk try detectNativeCpuWithLLVM(target.cpu.arch, llvm_cpu_name, llvm_cpu_features);
}; };
return @import("print_targets.zig").cmdTargets( return @import("print_targets.zig").cmdTargets(
std.heap.c_allocator, std.heap.c_allocator,
@ -662,6 +664,14 @@ export fn stage2_target_parse(
error.MissingArchitecture => return .MissingArchitecture, error.MissingArchitecture => return .MissingArchitecture,
error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat,
error.UnexpectedExtraField => return .SemanticAnalyzeFail, error.UnexpectedExtraField => return .SemanticAnalyzeFail,
error.InvalidAbiVersion => return .InvalidAbiVersion,
error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion,
error.FileSystem => return .FileSystem,
error.SymLinkLoop => return .SymLinkLoop,
error.SystemResources => return .SystemResources,
error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
error.DeviceBusy => return .DeviceBusy,
}; };
return .None; return .None;
} }
@ -671,108 +681,48 @@ fn stage2TargetParse(
zig_triple_oz: ?[*:0]const u8, zig_triple_oz: ?[*:0]const u8,
mcpu_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8,
) !void { ) !void {
const target: Target = if (zig_triple_oz) |zig_triple_z| blk: { const target: std.build.Target = if (zig_triple_oz) |zig_triple_z| blk: {
const zig_triple = mem.toSliceConst(u8, zig_triple_z); const zig_triple = mem.toSliceConst(u8, zig_triple_z);
const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline";
var diags: std.Target.ParseOptions.Diagnostics = .{}; var diags: std.Target.ParseOptions.Diagnostics = .{};
break :blk Target.parse(.{ break :blk std.build.Target{
.arch_os_abi = zig_triple, .Cross = Target.parse(.{
.cpu_features = mcpu, .arch_os_abi = zig_triple,
.diagnostics = &diags, .cpu_features = mcpu,
}) catch |err| switch (err) { .diagnostics = &diags,
error.UnknownCpu => { }) catch |err| switch (err) {
std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ error.UnknownCpu => {
diags.cpu_name.?, std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
@tagName(diags.arch.?), diags.cpu_name.?,
}); @tagName(diags.arch.?),
for (diags.arch.?.allCpuModels()) |cpu| { });
std.debug.warn(" {}\n", .{cpu.name}); for (diags.arch.?.allCpuModels()) |cpu| {
} std.debug.warn(" {}\n", .{cpu.name});
process.exit(1); }
process.exit(1);
},
error.UnknownCpuFeature => {
std.debug.warn(
\\Unknown CPU feature: '{}'
\\Available CPU features for architecture '{}':
\\
, .{
diags.unknown_feature_name,
@tagName(diags.arch.?),
});
for (diags.arch.?.allFeaturesList()) |feature| {
std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
}
process.exit(1);
},
else => |e| return e,
}, },
error.UnknownCpuFeature => {
std.debug.warn(
\\Unknown CPU feature: '{}'
\\Available CPU features for architecture '{}':
\\
, .{
diags.unknown_feature_name,
@tagName(diags.arch.?),
});
for (diags.arch.?.allFeaturesList()) |feature| {
std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
}
process.exit(1);
},
else => |e| return e,
}; };
} else Target.Native; } else std.build.Target.Native;
try stage1_target.fromTarget(target); try stage1_target.fromTarget(target);
} }
fn initStage1TargetCpuFeatures(stage1_target: *Stage2Target, cpu: Target.Cpu) !void {
const allocator = std.heap.c_allocator;
const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{
cpu.model.name,
cpu.features.asBytes(),
});
errdefer allocator.free(cache_hash);
const generic_arch_name = cpu.arch.genericName();
var builtin_str_buffer = try std.Buffer.allocPrint(allocator,
\\Cpu{{
\\ .arch = .{},
\\ .model = &Target.{}.cpu.{},
\\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
\\
, .{
@tagName(cpu.arch),
generic_arch_name,
cpu.model.name,
generic_arch_name,
generic_arch_name,
});
defer builtin_str_buffer.deinit();
var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
defer llvm_features_buffer.deinit();
for (cpu.arch.allFeaturesList()) |feature, index_usize| {
const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
const is_enabled = cpu.features.isEnabled(index);
if (feature.llvm_name) |llvm_name| {
const plus_or_minus = "-+"[@boolToInt(is_enabled)];
try llvm_features_buffer.appendByte(plus_or_minus);
try llvm_features_buffer.append(llvm_name);
try llvm_features_buffer.append(",");
}
if (is_enabled) {
// TODO some kind of "zig identifier escape" function rather than
// unconditionally using @"" syntax
try builtin_str_buffer.append(" .@\"");
try builtin_str_buffer.append(feature.name);
try builtin_str_buffer.append("\",\n");
}
}
try builtin_str_buffer.append(
\\ }),
\\};
\\
);
assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
stage1_target.llvm_cpu_name = if (cpu.model.llvm_name) |s| s.ptr else null;
stage1_target.llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr;
stage1_target.builtin_str = builtin_str_buffer.toOwnedSlice().ptr;
stage1_target.cache_hash = cache_hash.ptr;
}
// ABI warning // ABI warning
const Stage2LibCInstallation = extern struct { const Stage2LibCInstallation = extern struct {
include_dir: [*:0]const u8, include_dir: [*:0]const u8,
@ -952,10 +902,13 @@ const Stage2Target = extern struct {
llvm_cpu_name: ?[*:0]const u8, llvm_cpu_name: ?[*:0]const u8,
llvm_cpu_features: ?[*:0]const u8, llvm_cpu_features: ?[*:0]const u8,
builtin_str: ?[*:0]const u8, cpu_builtin_str: ?[*:0]const u8,
cache_hash: ?[*:0]const u8, cache_hash: ?[*:0]const u8,
os_builtin_str: ?[*:0]const u8,
fn toTarget(in_target: Stage2Target) Target { dynamic_linker: ?[*:0]const u8,
fn toTarget(in_target: Stage2Target) std.build.Target {
if (in_target.is_native) return .Native; if (in_target.is_native) return .Native;
const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch
@ -965,39 +918,244 @@ const Stage2Target = extern struct {
return .{ return .{
.Cross = .{ .Cross = .{
.cpu = Target.Cpu.baseline(enumInt(Target.Cpu.Arch, in_arch)), .cpu = Target.Cpu.baseline(enumInt(Target.Cpu.Arch, in_arch)),
.os = enumInt(Target.Os, in_os), .os = Target.Os.defaultVersionRange(enumInt(Target.Os.Tag, in_os)),
.abi = enumInt(Target.Abi, in_abi), .abi = enumInt(Target.Abi, in_abi),
}, },
}; };
} }
fn fromTarget(self: *Stage2Target, target: Target) !void { fn fromTarget(self: *Stage2Target, build_target: std.build.Target) !void {
const cpu = switch (target) { const allocator = std.heap.c_allocator;
var dynamic_linker: ?[*:0]u8 = null;
const target = switch (build_target) {
.Native => blk: { .Native => blk: {
// TODO self-host CPU model and feature detection instead of relying on LLVM const info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator);
if (info.dynamic_linker) |dl| {
dynamic_linker = dl.ptr;
}
// TODO we want to just use info.target but implementing CPU model & feature detection is todo
// so here we rely on LLVM
const llvm = @import("llvm.zig"); const llvm = @import("llvm.zig");
const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_name = llvm.GetHostCPUName();
const llvm_cpu_features = llvm.GetNativeFeatures(); const llvm_cpu_features = llvm.GetNativeFeatures();
break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); const arch = std.Target.current.cpu.arch;
var t = info.target;
t.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
break :blk t;
}, },
.Cross => target.getCpu(), .Cross => |t| t,
}; };
var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{
target.cpu.model.name,
target.cpu.features.asBytes(),
});
defer cache_hash.deinit();
const generic_arch_name = target.cpu.arch.genericName();
var cpu_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
\\Cpu{{
\\ .arch = .{},
\\ .model = &Target.{}.cpu.{},
\\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
\\
, .{
@tagName(target.cpu.arch),
generic_arch_name,
target.cpu.model.name,
generic_arch_name,
generic_arch_name,
});
defer cpu_builtin_str_buffer.deinit();
var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
defer llvm_features_buffer.deinit();
for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
const is_enabled = target.cpu.features.isEnabled(index);
if (feature.llvm_name) |llvm_name| {
const plus_or_minus = "-+"[@boolToInt(is_enabled)];
try llvm_features_buffer.appendByte(plus_or_minus);
try llvm_features_buffer.append(llvm_name);
try llvm_features_buffer.append(",");
}
if (is_enabled) {
// TODO some kind of "zig identifier escape" function rather than
// unconditionally using @"" syntax
try cpu_builtin_str_buffer.append(" .@\"");
try cpu_builtin_str_buffer.append(feature.name);
try cpu_builtin_str_buffer.append("\",\n");
}
}
try cpu_builtin_str_buffer.append(
\\ }),
\\};
\\
);
assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
var os_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
\\Os{{
\\ .tag = .{},
\\ .version_range = .{{
, .{@tagName(target.os.tag)});
defer os_builtin_str_buffer.deinit();
// We'll re-use the OS version range builtin string for the cache hash.
const os_builtin_str_ver_start_index = os_builtin_str_buffer.len();
@setEvalBranchQuota(2000);
switch (target.os.tag) {
.freestanding,
.ananas,
.cloudabi,
.dragonfly,
.fuchsia,
.ios,
.kfreebsd,
.lv2,
.solaris,
.haiku,
.minix,
.rtems,
.nacl,
.cnk,
.aix,
.cuda,
.nvcl,
.amdhsa,
.ps4,
.elfiamcu,
.tvos,
.watchos,
.mesa3d,
.contiki,
.amdpal,
.hermit,
.hurd,
.wasi,
.emscripten,
.uefi,
.other,
=> try os_builtin_str_buffer.append(" .none = {} }\n"),
.freebsd,
.macosx,
.netbsd,
.openbsd,
=> try os_builtin_str_buffer.print(
\\.semver = .{{
\\ .min = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ .max = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }}}},
, .{
target.os.version_range.semver.min.major,
target.os.version_range.semver.min.minor,
target.os.version_range.semver.min.patch,
target.os.version_range.semver.max.major,
target.os.version_range.semver.max.minor,
target.os.version_range.semver.max.patch,
}),
.linux => try os_builtin_str_buffer.print(
\\.linux = .{{
\\ .range = .{{
\\ .min = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ .max = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }},
\\ .glibc = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }}}},
\\
, .{
target.os.version_range.linux.range.min.major,
target.os.version_range.linux.range.min.minor,
target.os.version_range.linux.range.min.patch,
target.os.version_range.linux.range.max.major,
target.os.version_range.linux.range.max.minor,
target.os.version_range.linux.range.max.patch,
target.os.version_range.linux.glibc.major,
target.os.version_range.linux.glibc.minor,
target.os.version_range.linux.glibc.patch,
}),
.windows => try os_builtin_str_buffer.print(
\\.semver = .{{
\\ .min = .{},
\\ .max = .{},
\\ }}}},
, .{
@tagName(target.os.version_range.windows.min),
@tagName(target.os.version_range.windows.max),
}),
}
try os_builtin_str_buffer.append("};\n");
try cache_hash.append(
os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()],
);
const glibc_version = if (target.isGnuLibC()) blk: {
const stage1_glibc = try std.heap.c_allocator.create(Stage2GLibCVersion);
const stage2_glibc = target.os.version_range.linux.glibc;
stage1_glibc.* = .{
.major = stage2_glibc.major,
.minor = stage2_glibc.minor,
.patch = stage2_glibc.patch,
};
break :blk stage1_glibc;
} else null;
self.* = .{ self.* = .{
.arch = @enumToInt(target.getArch()) + 1, // skip over ZigLLVM_UnknownArch .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
.vendor = 0, .vendor = 0,
.os = @enumToInt(target.getOs()), .os = @enumToInt(target.os.tag),
.abi = @enumToInt(target.getAbi()), .abi = @enumToInt(target.abi),
.llvm_cpu_name = null, .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
.llvm_cpu_features = null, .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr,
.builtin_str = null, .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr,
.cache_hash = null, .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr,
.is_native = target == .Native, .cache_hash = cache_hash.toOwnedSlice().ptr,
.glibc_version = null, .is_native = build_target == .Native,
.glibc_version = glibc_version,
.dynamic_linker = dynamic_linker,
}; };
try initStage1TargetCpuFeatures(self, cpu);
} }
}; };
fn enumInt(comptime Enum: type, int: c_int) Enum {
return @intToEnum(Enum, @intCast(@TagType(Enum), int));
}
// ABI warning // ABI warning
const Stage2GLibCVersion = extern struct { const Stage2GLibCVersion = extern struct {
major: u32, major: u32,
@ -1005,26 +1163,6 @@ const Stage2GLibCVersion = extern struct {
patch: u32, patch: u32,
}; };
// ABI warning
export fn stage2_detect_dynamic_linker(in_target: *const Stage2Target, out_ptr: *[*:0]u8, out_len: *usize) Error {
const target = in_target.toTarget();
const result = @import("introspect.zig").detectDynamicLinker(
std.heap.c_allocator,
target,
) catch |err| switch (err) {
error.OutOfMemory => return .OutOfMemory,
error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath,
error.TargetHasNoDynamicLinker => return .TargetHasNoDynamicLinker,
};
out_ptr.* = result.ptr;
out_len.* = result.len;
return .None;
}
fn enumInt(comptime Enum: type, int: c_int) Enum {
return @intToEnum(Enum, @intCast(@TagType(Enum), int));
}
// ABI warning // ABI warning
const Stage2NativePaths = extern struct { const Stage2NativePaths = extern struct {
include_dirs_ptr: [*][*:0]u8, include_dirs_ptr: [*][*:0]u8,

View File

@ -34,25 +34,3 @@ pub fn initializeAllTargets() void {
llvm.InitializeAllAsmPrinters(); llvm.InitializeAllAsmPrinters();
llvm.InitializeAllAsmParsers(); llvm.InitializeAllAsmParsers();
} }
pub fn getTriple(allocator: *std.mem.Allocator, self: std.Target) !std.Buffer {
var result = try std.Buffer.initSize(allocator, 0);
errdefer result.deinit();
// LLVM WebAssembly output support requires the target to be activated at
// build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
//
// LLVM determines the output format based on the abi suffix,
// defaulting to an object based on the architecture. The default format in
// LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
// explicitly set this ourself in order for it to work.
//
// This is fixed in LLVM 7 and you will be able to get wasm output by
// using the target triple `wasm32-unknown-unknown-unknown`.
const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi());
var out = &std.io.BufferOutStream.init(&result).stream;
try out.print("{}-unknown-{}-{}", .{ @tagName(self.getArch()), @tagName(self.getOs()), env_name });
return result;
}

View File

@ -4483,7 +4483,7 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *execu
if (!type_has_bits(field->type_entry)) { if (!type_has_bits(field->type_entry)) {
ZigType *tag_type = union_type->data.unionation.tag_type; ZigType *tag_type = union_type->data.unionation.tag_type;
if (!instruction->initializing || !type_has_bits(tag_type)) if (!instruction->initializing || tag_type == nullptr || !type_has_bits(tag_type))
return nullptr; return nullptr;
// The field has no bits but we still have to change the discriminant // The field has no bits but we still have to change the discriminant
@ -8543,25 +8543,24 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type);
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n");
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi);
{ {
buf_append_str(contents, "pub const cpu: Cpu = "); buf_append_str(contents, "pub const cpu: Cpu = ");
if (g->zig_target->builtin_str != nullptr) { if (g->zig_target->cpu_builtin_str != nullptr) {
buf_append_str(contents, g->zig_target->builtin_str); buf_append_str(contents, g->zig_target->cpu_builtin_str);
} else { } else {
buf_append_str(contents, "Target.Cpu.baseline(arch);\n"); buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch);
} }
} }
if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) { {
buf_appendf(contents, buf_append_str(contents, "pub const os = ");
"pub const glibc_version: ?Version = Version{.major = %d, .minor = %d, .patch = %d};\n", if (g->zig_target->os_builtin_str != nullptr) {
g->zig_target->glibc_version->major, buf_append_str(contents, g->zig_target->os_builtin_str);
g->zig_target->glibc_version->minor, } else {
g->zig_target->glibc_version->patch); buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os);
} else { }
buf_appendf(contents, "pub const glibc_version: ?Version = null;\n");
} }
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
@ -8867,8 +8866,6 @@ static void init(CodeGen *g) {
} }
static void detect_dynamic_linker(CodeGen *g) { static void detect_dynamic_linker(CodeGen *g) {
Error err;
if (g->dynamic_linker_path != nullptr) if (g->dynamic_linker_path != nullptr)
return; return;
if (!g->have_dynamic_link) if (!g->have_dynamic_link)
@ -8876,16 +8873,9 @@ static void detect_dynamic_linker(CodeGen *g) {
if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))
return; return;
char *dynamic_linker_ptr; if (g->zig_target->dynamic_linker != nullptr) {
size_t dynamic_linker_len; g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker);
if ((err = stage2_detect_dynamic_linker(g->zig_target, &dynamic_linker_ptr, &dynamic_linker_len))) {
if (err == ErrorTargetHasNoDynamicLinker) return;
fprintf(stderr, "Unable to detect dynamic linker: %s\n", err_str(err));
exit(1);
} }
g->dynamic_linker_path = buf_create_from_mem(dynamic_linker_ptr, dynamic_linker_len);
// Skips heap::c_allocator because the memory is allocated by stage2 library.
free(dynamic_linker_ptr);
} }
static void detect_libc(CodeGen *g) { static void detect_libc(CodeGen *g) {

View File

@ -81,6 +81,8 @@ const char *err_str(Error err) {
case ErrorWindowsSdkNotFound: return "Windows SDK not found"; case ErrorWindowsSdkNotFound: return "Windows SDK not found";
case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path";
case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker";
case ErrorInvalidAbiVersion: return "invalid C ABI version";
case ErrorInvalidOperatingSystemVersion: return "invalid operating system version";
} }
return "(invalid error)"; return "(invalid error)";
} }

View File

@ -89,8 +89,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --single-threaded source may assume it is only used single-threaded\n" " --single-threaded source may assume it is only used single-threaded\n"
" -dynamic create a shared library (.so; .dll; .dylib)\n" " -dynamic create a shared library (.so; .dll; .dylib)\n"
" --strip exclude debug symbols\n" " --strip exclude debug symbols\n"
" -target [name] <arch><sub>-<os>-<abi> see the targets command\n" " -target [name] <arch>-<os>-<abi> see the targets command\n"
" -target-glibc [version] target a specific glibc version (default: 2.17)\n"
" --verbose-tokenize enable compiler debug output for tokenization\n" " --verbose-tokenize enable compiler debug output for tokenization\n"
" --verbose-ast enable compiler debug output for AST parsing\n" " --verbose-ast enable compiler debug output for AST parsing\n"
" --verbose-link enable compiler debug output for linking\n" " --verbose-link enable compiler debug output for linking\n"
@ -419,7 +418,6 @@ static int main0(int argc, char **argv) {
const char *mios_version_min = nullptr; const char *mios_version_min = nullptr;
const char *linker_script = nullptr; const char *linker_script = nullptr;
Buf *version_script = nullptr; Buf *version_script = nullptr;
const char *target_glibc = nullptr;
ZigList<const char *> rpath_list = {0}; ZigList<const char *> rpath_list = {0};
bool each_lib_rpath = false; bool each_lib_rpath = false;
ZigList<const char *> objects = {0}; ZigList<const char *> objects = {0};
@ -853,8 +851,6 @@ static int main0(int argc, char **argv) {
linker_script = argv[i]; linker_script = argv[i];
} else if (strcmp(arg, "--version-script") == 0) { } else if (strcmp(arg, "--version-script") == 0) {
version_script = buf_create_from_str(argv[i]); version_script = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "-target-glibc") == 0) {
target_glibc = argv[i];
} else if (strcmp(arg, "-rpath") == 0) { } else if (strcmp(arg, "-rpath") == 0) {
rpath_list.append(argv[i]); rpath_list.append(argv[i]);
} else if (strcmp(arg, "--test-filter") == 0) { } else if (strcmp(arg, "--test-filter") == 0) {
@ -982,29 +978,6 @@ static int main0(int argc, char **argv) {
"See `%s targets` to display valid targets.\n", err_str(err), arg0); "See `%s targets` to display valid targets.\n", err_str(err), arg0);
return print_error_usage(arg0); return print_error_usage(arg0);
} }
if (target_is_glibc(&target)) {
target.glibc_version = heap::c_allocator.create<ZigGLibCVersion>();
if (target_glibc != nullptr) {
if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) {
fprintf(stderr, "invalid glibc version '%s': %s\n", target_glibc, err_str(err));
return print_error_usage(arg0);
}
} else {
target_init_default_glibc_version(&target);
#if defined(ZIG_OS_LINUX)
if (target.is_native) {
// TODO self-host glibc version detection, and then this logic can go away
if ((err = glibc_detect_native_version(target.glibc_version))) {
// Fall back to the default version.
}
}
#endif
}
} else if (target_glibc != nullptr) {
fprintf(stderr, "'%s' is not a glibc-compatible target", target_string);
return print_error_usage(arg0);
}
Buf zig_triple_buf = BUF_INIT; Buf zig_triple_buf = BUF_INIT;
target_triple_zig(&zig_triple_buf, &target); target_triple_zig(&zig_triple_buf, &target);

View File

@ -100,13 +100,11 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons
if (mcpu == nullptr) { if (mcpu == nullptr) {
target->llvm_cpu_name = ZigLLVMGetHostCPUName(); target->llvm_cpu_name = ZigLLVMGetHostCPUName();
target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); target->llvm_cpu_features = ZigLLVMGetNativeFeatures();
target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "native\n\n"; target->cache_hash = "native\n\n";
} else if (strcmp(mcpu, "baseline") == 0) { } else if (strcmp(mcpu, "baseline") == 0) {
target->is_native = false; target->is_native = false;
target->llvm_cpu_name = ""; target->llvm_cpu_name = "";
target->llvm_cpu_features = ""; target->llvm_cpu_features = "";
target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "baseline\n\n"; target->cache_hash = "baseline\n\n";
} else { } else {
const char *msg = "stage0 can't handle CPU/features in the target"; const char *msg = "stage0 can't handle CPU/features in the target";
@ -148,7 +146,6 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons
const char *msg = "stage0 can't handle CPU/features in the target"; const char *msg = "stage0 can't handle CPU/features in the target";
stage2_panic(msg, strlen(msg)); stage2_panic(msg, strlen(msg));
} }
target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "\n\n"; target->cache_hash = "\n\n";
} }
@ -186,11 +183,6 @@ enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) {
stage2_panic(msg, strlen(msg)); stage2_panic(msg, strlen(msg));
} }
enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, char **out_ptr, size_t *out_len) {
const char *msg = "stage0 called stage2_detect_dynamic_linker";
stage2_panic(msg, strlen(msg));
}
enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) { enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
native_paths->include_dirs_ptr = nullptr; native_paths->include_dirs_ptr = nullptr;
native_paths->include_dirs_len = 0; native_paths->include_dirs_len = 0;

View File

@ -103,6 +103,8 @@ enum Error {
ErrorWindowsSdkNotFound, ErrorWindowsSdkNotFound,
ErrorUnknownDynamicLinkerPath, ErrorUnknownDynamicLinkerPath,
ErrorTargetHasNoDynamicLinker, ErrorTargetHasNoDynamicLinker,
ErrorInvalidAbiVersion,
ErrorInvalidOperatingSystemVersion,
}; };
// ABI warning // ABI warning
@ -290,14 +292,12 @@ struct ZigTarget {
const char *llvm_cpu_name; const char *llvm_cpu_name;
const char *llvm_cpu_features; const char *llvm_cpu_features;
const char *builtin_str; const char *cpu_builtin_str;
const char *cache_hash; const char *cache_hash;
const char *os_builtin_str;
const char *dynamic_linker;
}; };
// ABI warning
ZIG_EXTERN_C enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target,
char **out_ptr, size_t *out_len);
// ABI warning // ABI warning
ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu); ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu);

View File

@ -1,9 +1,10 @@
const std = @import("std"); const std = @import("std");
const config = @import("builtin");
const expect = std.testing.expect; const expect = std.testing.expect;
const is_x86_64_linux = std.Target.current.cpu.arch == .x86_64 and std.Target.current.os.tag == .linux;
comptime { comptime {
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { if (is_x86_64_linux) {
asm ( asm (
\\.globl this_is_my_alias; \\.globl this_is_my_alias;
\\.type this_is_my_alias, @function; \\.type this_is_my_alias, @function;
@ -13,7 +14,7 @@ comptime {
} }
test "module level assembly" { test "module level assembly" {
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { if (is_x86_64_linux) {
expect(this_is_my_alias() == 1234); expect(this_is_my_alias() == 1234);
} }
} }

View File

@ -1,6 +1,5 @@
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
const builtin = @import("builtin");
test "@byteSwap integers" { test "@byteSwap integers" {
const ByteSwapIntTest = struct { const ByteSwapIntTest = struct {
@ -41,10 +40,10 @@ test "@byteSwap integers" {
test "@byteSwap vectors" { test "@byteSwap vectors" {
// https://github.com/ziglang/zig/issues/3563 // https://github.com/ziglang/zig/issues/3563
if (builtin.os == .dragonfly) return error.SkipZigTest; if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3317 // https://github.com/ziglang/zig/issues/3317
if (builtin.arch == .mipsel) return error.SkipZigTest; if (std.Target.current.cpu.arch == .mipsel) return error.SkipZigTest;
const ByteSwapVectorTest = struct { const ByteSwapVectorTest = struct {
fn run() void { fn run() void {

View File

@ -1,5 +1,5 @@
const builtin = @import("builtin"); const std = @import("std");
const expect = @import("std").testing.expect; const expect = std.testing.expect;
test "namespace depends on compile var" { test "namespace depends on compile var" {
if (some_namespace.a_bool) { if (some_namespace.a_bool) {
@ -8,7 +8,7 @@ test "namespace depends on compile var" {
expect(!some_namespace.a_bool); expect(!some_namespace.a_bool);
} }
} }
const some_namespace = switch (builtin.os) { const some_namespace = switch (std.builtin.os.tag) {
builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"), .linux => @import("namespace_depends_on_compile_var/a.zig"),
else => @import("namespace_depends_on_compile_var/b.zig"), else => @import("namespace_depends_on_compile_var/b.zig"),
}; };

View File

@ -2,7 +2,6 @@ const std = @import("std");
const mem = std.mem; const mem = std.mem;
const expect = std.testing.expect; const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const builtin = @import("builtin");
test "implicit cast vector to array - bool" { test "implicit cast vector to array - bool" {
const S = struct { const S = struct {
@ -114,7 +113,7 @@ test "array to vector" {
test "vector casts of sizes not divisable by 8" { test "vector casts of sizes not divisable by 8" {
// https://github.com/ziglang/zig/issues/3563 // https://github.com/ziglang/zig/issues/3563
if (builtin.os == .dragonfly) return error.SkipZigTest; if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
const S = struct { const S = struct {
fn doTheTest() void { fn doTheTest() void {

View File

@ -31,7 +31,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla
pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext;
const TestTarget = struct { const TestTarget = struct {
target: Target = .Native, target: build.Target = .Native,
mode: builtin.Mode = .Debug, mode: builtin.Mode = .Debug,
link_libc: bool = false, link_libc: bool = false,
single_threaded: bool = false, single_threaded: bool = false,