diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-02-29 01:57:06 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-29 01:57:06 -0500 |
| commit | 76176104001420ea04840f9b31e706289e4ebf11 (patch) | |
| tree | 4ba46044adc883630a3452b33f9d70541581d7ae /lib | |
| parent | 1b41f2d77ea14de27b0655a35a727d664cfc4ca4 (diff) | |
| parent | 3cba603eae1a1c8b0338f5584041c73d55682c0a (diff) | |
| download | zig-76176104001420ea04840f9b31e706289e4ebf11.tar.gz zig-76176104001420ea04840f9b31e706289e4ebf11.zip | |
Merge pull request #4550 from ziglang/os-version-ranges
introduce operating system version ranges as part of the target; self-host native dynamic linker detection and native glibc version detection
Diffstat (limited to 'lib')
63 files changed, 2591 insertions, 1076 deletions
diff --git a/lib/std/build.zig b/lib/std/build.zig index 29837d56d9..3ca59ae4cb 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1,5 +1,5 @@ const std = @import("std.zig"); -const builtin = @import("builtin"); +const builtin = std.builtin; const io = std.io; const fs = std.fs; const mem = std.mem; @@ -15,6 +15,7 @@ const BufSet = std.BufSet; const BufMap = std.BufMap; const fmt_lib = std.fmt; const File = std.fs.File; +const CrossTarget = std.zig.CrossTarget; pub const FmtStep = @import("build/fmt.zig").FmtStep; pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; @@ -521,24 +522,91 @@ pub const Builder = struct { return mode; } - /// Exposes standard `zig build` options for choosing a target. Pass `null` to support all targets. - pub fn standardTargetOptions(self: *Builder, supported_targets: ?[]const Target) Target { - if (supported_targets) |target_list| { - // TODO detect multiple args and emit an error message - // there's probably a better way to collect the target - for (target_list) |targ| { - const targ_str = targ.zigTriple(self.allocator) catch unreachable; - const targ_desc = targ.allocDescription(self.allocator) catch unreachable; - const this_targ_opt = self.option(bool, targ_str, targ_desc) orelse false; - if (this_targ_opt) { - return targ; + pub const StandardTargetOptionsArgs = struct { + whitelist: ?[]const CrossTarget = null, + + default_target: CrossTarget = CrossTarget{}, + }; + + /// Exposes standard `zig build` options for choosing a target. + pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { + const triple = self.option( + []const u8, + "target", + "The CPU architecture, OS, and ABI to build for.", + ) orelse return args.default_target; + + // TODO add cpu and features as part of the target triple + + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; + const selected_target = CrossTarget.parse(.{ + .arch_os_abi = triple, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + diags.cpu_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allCpuModels()) |cpu| { + std.debug.warn(" {}\n", .{cpu.name}); + } + 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); + }, + error.UnknownOperatingSystem => { + std.debug.warn( + \\Unknown OS: '{}' + \\Available operating systems: + \\ + , .{diags.os_name}); + inline for (std.meta.fields(std.Target.Os.Tag)) |field| { + std.debug.warn(" {}\n", .{field.name}); + } + process.exit(1); + }, + else => |e| { + std.debug.warn("Unable to parse target '{}': {}\n", .{ triple, @errorName(e) }); + process.exit(1); + }, + }; + + const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; + + if (args.whitelist) |list| whitelist_check: { + // Make sure it's a match of one of the list. + for (list) |t| { + const t_triple = t.zigTriple(self.allocator) catch unreachable; + if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { + break :whitelist_check; } } - return Target.Native; - } else { - const target_str = self.option([]const u8, "target", "the target to build for") orelse return Target.Native; - return Target.parse(.{ .arch_os_abi = target_str }) catch unreachable; // TODO better error message for bad target + std.debug.warn("Chosen target '{}' does not match one of the supported targets:\n", .{ + selected_canonicalized_triple, + }); + for (list) |t| { + const t_triple = t.zigTriple(self.allocator) catch unreachable; + std.debug.warn(" {}\n", .{t_triple}); + } + // TODO instead of process exit, return error and have a zig build flag implemented by + // the build runner that turns process exits into error return traces + process.exit(1); } + + return selected_target; } pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { @@ -796,7 +864,7 @@ pub const Builder = struct { pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations - const exe_extension = (Target{ .Native = {} }).exeFileExt(); + const exe_extension = @as(CrossTarget, .{}).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { for (names) |name| { if (fs.path.isAbsolute(name)) { @@ -971,21 +1039,19 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - // TODO: uncomment and fix the leak - // const builder = try Builder.create(std.testing.allocator, "zig", "zig-cache", "zig-cache"); - const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache"); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const builder = try Builder.create(&arena.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } -/// Deprecated. Use `builtin.Version`. +/// Deprecated. Use `std.builtin.Version`. pub const Version = builtin.Version; -/// Deprecated. Use `std.Target.Cross`. -pub const CrossTarget = std.Target.Cross; - -/// Deprecated. Use `std.Target`. -pub const Target = std.Target; +/// Deprecated. Use `std.zig.CrossTarget`. +pub const Target = std.zig.CrossTarget; pub const Pkg = struct { name: []const u8, @@ -1038,7 +1104,7 @@ pub const LibExeObjStep = struct { step: Step, builder: *Builder, name: []const u8, - target: Target, + target: CrossTarget = CrossTarget{}, linker_script: ?[]const u8 = null, version_script: ?[]const u8 = null, out_filename: []const u8, @@ -1076,7 +1142,7 @@ pub const LibExeObjStep = struct { out_pdb_filename: []const u8, packages: ArrayList(Pkg), build_options_contents: std.Buffer, - system_linker_hack: bool, + system_linker_hack: bool = false, object_src: []const u8, @@ -1091,7 +1157,6 @@ pub const LibExeObjStep = struct { install_step: ?*InstallArtifactStep, libc_file: ?[]const u8 = null, - target_glibc: ?Version = null, valgrind_support: ?bool = null, @@ -1112,8 +1177,6 @@ pub const LibExeObjStep = struct { /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. glibc_multi_install_dir: ?[]const u8 = null, - dynamic_linker: ?[]const u8 = null, - /// Position Independent Code force_pic: ?bool = null, @@ -1191,7 +1254,6 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .target = Target.Native, .frameworks = BufSet.init(builder.allocator), .step = Step.init(name, builder.allocator, make), .version = ver, @@ -1210,7 +1272,6 @@ pub const LibExeObjStep = struct { .object_src = undefined, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, .c_std = Builder.CStd.C99, - .system_linker_hack = false, .override_lib_dir = null, .main_pkg_path = null, .exec_cmd_args = null, @@ -1282,36 +1343,11 @@ pub const LibExeObjStep = struct { } } - /// Deprecated. Use `setTheTarget`. - pub fn setTarget( - self: *LibExeObjStep, - target_arch: builtin.Arch, - target_os: builtin.Os, - target_abi: builtin.Abi, - ) void { - return self.setTheTarget(Target{ - .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .abi = target_abi, - .cpu_features = target_arch.getBaselineCpuFeatures(), - }, - }); - } - - pub fn setTheTarget(self: *LibExeObjStep, target: Target) void { + pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { self.target = target; self.computeOutFileNames(); } - pub fn setTargetGLibC(self: *LibExeObjStep, major: u32, minor: u32, patch: u32) void { - self.target_glibc = Version{ - .major = major, - .minor = minor, - .patch = patch, - }; - } - pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { self.output_dir = self.builder.dupePath(dir); } @@ -1692,7 +1728,7 @@ pub const LibExeObjStep = struct { .NotFound => return error.VcpkgNotFound, .Found => |root| { const allocator = self.builder.allocator; - const triplet = try Target.vcpkgTriplet(allocator, self.target, linkage); + const triplet = try self.target.vcpkgTriplet(allocator, linkage); defer self.builder.allocator.free(triplet); const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" }); @@ -1905,47 +1941,46 @@ pub const LibExeObjStep = struct { try zig_args.append(@tagName(self.code_model)); } - switch (self.target) { - .Native => {}, - .Cross => |cross| { - try zig_args.append("-target"); - try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable); + if (!self.target.isNative()) { + try zig_args.append("-target"); + try zig_args.append(try self.target.zigTriple(builder.allocator)); - const all_features = self.target.getArch().allFeaturesList(); - var populated_cpu_features = cross.cpu.model.features; - populated_cpu_features.populateDependencies(all_features); + // TODO this logic can disappear if cpu model + features becomes part of the target triple + const cross = self.target.toTarget(); + const all_features = cross.cpu.arch.allFeaturesList(); + var populated_cpu_features = cross.cpu.model.features; + populated_cpu_features.populateDependencies(all_features); - if (populated_cpu_features.eql(cross.cpu.features)) { - // The CPU name alone is sufficient. - // If it is the baseline CPU, no command line args are required. - if (cross.cpu.model != Target.Cpu.baseline(self.target.getArch()).model) { - try zig_args.append("-mcpu"); - try zig_args.append(cross.cpu.model.name); - } - } else { - var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu="); - try mcpu_buffer.append(cross.cpu.model.name); - - for (all_features) |feature, i_usize| { - const i = @intCast(Target.Cpu.Feature.Set.Index, i_usize); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = cross.cpu.features.isEnabled(i); - if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.appendByte('-'); - try mcpu_buffer.append(feature.name); - } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.appendByte('+'); - try mcpu_buffer.append(feature.name); - } + if (populated_cpu_features.eql(cross.cpu.features)) { + // The CPU name alone is sufficient. + // If it is the baseline CPU, no command line args are required. + if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) { + try zig_args.append("-mcpu"); + try zig_args.append(cross.cpu.model.name); + } + } else { + var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu="); + try mcpu_buffer.append(cross.cpu.model.name); + + for (all_features) |feature, i_usize| { + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = cross.cpu.features.isEnabled(i); + if (in_cpu_set and !in_actual_set) { + try mcpu_buffer.appendByte('-'); + try mcpu_buffer.append(feature.name); + } else if (!in_cpu_set and in_actual_set) { + try mcpu_buffer.appendByte('+'); + try mcpu_buffer.append(feature.name); } - try zig_args.append(mcpu_buffer.toSliceConst()); } - }, - } + try zig_args.append(mcpu_buffer.toSliceConst()); + } - if (self.target_glibc) |ver| { - try zig_args.append("-target-glibc"); - try zig_args.append(builder.fmt("{}.{}.{}", .{ ver.major, ver.minor, ver.patch })); + if (self.target.dynamic_linker.get()) |dynamic_linker| { + try zig_args.append("--dynamic-linker"); + try zig_args.append(dynamic_linker); + } } if (self.linker_script) |linker_script| { @@ -1953,11 +1988,6 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable; } - if (self.dynamic_linker) |dynamic_linker| { - try zig_args.append("--dynamic-linker"); - try zig_args.append(dynamic_linker); - } - if (self.version_script) |version_script| { try zig_args.append("--version-script"); try zig_args.append(builder.pathFromRoot(version_script)); @@ -1975,7 +2005,7 @@ pub const LibExeObjStep = struct { } else switch (self.target.getExternalExecutor()) { .native, .unavailable => {}, .qemu => |bin_name| if (self.enable_qemu) qemu: { - const need_cross_glibc = self.target.isGnu() and self.target.isLinux() and self.is_linking_libc; + const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; const glibc_dir_arg = if (need_cross_glibc) self.glibc_multi_install_dir orelse break :qemu else @@ -2420,10 +2450,7 @@ const VcpkgRootStatus = enum { Found, }; -pub const VcpkgLinkage = enum { - Static, - Dynamic, -}; +pub const VcpkgLinkage = std.builtin.LinkMode; pub const InstallDir = enum { Prefix, diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 75809bde03..eacf408ba9 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -82,7 +82,7 @@ pub const RunStep = struct { var key: []const u8 = undefined; var prev_path: ?[]const u8 = undefined; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { key = "Path"; prev_path = env_map.get(key); if (prev_path == null) { diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig index 3d1bdad677..e9e61b190f 100644 --- a/lib/std/build/translate_c.zig +++ b/lib/std/build/translate_c.zig @@ -7,6 +7,7 @@ const LibExeObjStep = build.LibExeObjStep; const CheckFileStep = build.CheckFileStep; const fs = std.fs; const mem = std.mem; +const CrossTarget = std.zig.CrossTarget; pub const TranslateCStep = struct { step: Step, @@ -14,7 +15,7 @@ pub const TranslateCStep = struct { source: build.FileSource, output_dir: ?[]const u8, out_basename: []const u8, - target: std.Target = .Native, + target: CrossTarget = CrossTarget{}, pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { const self = builder.allocator.create(TranslateCStep) catch unreachable; @@ -39,7 +40,7 @@ pub const TranslateCStep = struct { ) catch unreachable; } - pub fn setTarget(self: *TranslateCStep, target: std.Target) void { + pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { self.target = target; } @@ -63,12 +64,9 @@ pub const TranslateCStep = struct { try argv_list.append("--cache"); try argv_list.append("on"); - switch (self.target) { - .Native => {}, - .Cross => { - try argv_list.append("-target"); - try argv_list.append(try self.target.zigTriple(self.builder.allocator)); - }, + if (!self.target.isNative()) { + try argv_list.append("-target"); + try argv_list.append(try self.target.zigTriple(self.builder.allocator)); } try argv_list.append(self.source.getPath(self.builder)); diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 5440de4d3b..de37fda903 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -398,7 +398,60 @@ pub const LinkMode = enum { pub const Version = struct { major: u32, minor: u32, - patch: u32, + patch: u32 = 0, + + pub const Range = struct { + min: Version, + max: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + if (self.min.compare(ver) == .gt) return false; + if (self.max.compare(ver) == .lt) return false; + return true; + } + }; + + pub fn order(lhs: Version, rhs: Version) std.math.Order { + if (lhs.major < rhs.major) return .lt; + if (lhs.major > rhs.major) return .gt; + if (lhs.minor < rhs.minor) return .lt; + if (lhs.minor > rhs.minor) return .gt; + if (lhs.patch < rhs.patch) return .lt; + if (lhs.patch > rhs.patch) return .gt; + return .eq; + } + + pub fn parse(text: []const u8) !Version { + var it = std.mem.separate(text, "."); + return Version{ + .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), + .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + }; + } + + pub fn format( + self: Version, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + context: var, + comptime Error: type, + comptime output: fn (@TypeOf(context), []const u8) Error!void, + ) Error!void { + if (fmt.len == 0) { + if (self.patch == 0) { + if (self.minor == 0) { + return std.fmt.format(context, Error, output, "{}", .{self.major}); + } else { + return std.fmt.format(context, Error, output, "{}.{}", .{ self.major, self.minor }); + } + } else { + return std.fmt.format(context, Error, output, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + } + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } + } }; /// This data structure is used by the Zig language code generation and @@ -474,7 +527,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn root.os.panic(msg, error_return_trace); unreachable; } - switch (os) { + switch (os.tag) { .freestanding => { while (true) { @breakpoint(); diff --git a/lib/std/c.zig b/lib/std/c.zig index f6c0e07dbd..48a3039f51 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = std.builtin; const page_size = std.mem.page_size; 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 switch (builtin.os) { +pub usingnamespace switch (std.Target.current.os.tag) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), @@ -46,17 +46,16 @@ pub fn versionCheck(glibc_version: builtin.Version) type { return struct { pub const ok = blk: { if (!builtin.link_libc) break :blk false; - switch (builtin.abi) { - .musl, .musleabi, .musleabihf => break :blk true, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => { - const ver = builtin.glibc_version orelse break :blk false; - if (ver.major < glibc_version.major) break :blk false; - if (ver.major > glibc_version.major) break :blk true; - if (ver.minor < glibc_version.minor) break :blk false; - if (ver.minor > glibc_version.minor) break :blk true; - break :blk ver.patch >= glibc_version.patch; - }, - else => break :blk false, + if (std.Target.current.abi.isMusl()) break :blk true; + if (std.Target.current.isGnuLibC()) { + const ver = std.Target.current.os.version_range.linux.glibc; + const order = ver.order(glibc_version); + break :blk switch (order) { + .gt, .eq => true, + .lt => false, + }; + } else { + break :blk false; } }; }; @@ -109,6 +108,7 @@ pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8 pub extern "c" fn dup(fd: fd_t) c_int; pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; @@ -125,6 +125,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; +pub extern "c" fn uname(buf: *utsname) c_int; pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 0f7abaaaa0..be32536d6f 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -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, }; 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, .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { .aarch64 => 48, diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index bb8ed2e8a0..d5e914b286 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -17,9 +17,9 @@ const TailQueue = std.TailQueue; const maxInt = std.math.maxInt; pub const ChildProcess = struct { - pid: if (builtin.os == .windows) void else i32, - handle: if (builtin.os == .windows) windows.HANDLE else void, - thread_handle: if (builtin.os == .windows) windows.HANDLE else void, + pid: if (builtin.os.tag == .windows) void else i32, + handle: if (builtin.os.tag == .windows) windows.HANDLE else void, + thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, allocator: *mem.Allocator, @@ -39,15 +39,15 @@ pub const ChildProcess = struct { stderr_behavior: StdIo, /// 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. - 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. 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, @@ -96,8 +96,8 @@ pub const ChildProcess = struct { .term = null, .env_map = null, .cwd = null, - .uid = if (builtin.os == .windows) {} else null, - .gid = if (builtin.os == .windows) {} else null, + .uid = if (builtin.os.tag == .windows) {} else null, + .gid = if (builtin.os.tag == .windows) {} else null, .stdin = null, .stdout = null, .stderr = null, @@ -118,7 +118,7 @@ pub const ChildProcess = struct { /// On success must call `kill` or `wait`. pub fn spawn(self: *ChildProcess) SpawnError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.spawnWindows(); } else { return self.spawnPosix(); @@ -132,7 +132,7 @@ pub const ChildProcess = struct { /// Forcibly terminates child process and then cleans up all resources. pub fn kill(self: *ChildProcess) !Term { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.killWindows(1); } else { return self.killPosix(); @@ -162,7 +162,7 @@ pub const ChildProcess = struct { /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: *ChildProcess) !Term { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.waitWindows(); } else { return self.waitPosix(); @@ -307,7 +307,7 @@ pub const ChildProcess = struct { fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { defer destroyPipe(self.err_pipe); - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var fd = [1]std.os.pollfd{std.os.pollfd{ .fd = self.err_pipe[0], .events = std.os.POLLIN, @@ -402,7 +402,7 @@ pub const ChildProcess = struct { // This pipe is used to communicate errors between the time of fork // and execve from the child process to the parent process. const err_pipe = blk: { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const fd = try os.eventfd(0, 0); // There's no distinction between the readable and the writeable // end with eventfd diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 765e0a45cf..4057d4b62b 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -4,8 +4,8 @@ const debug = std.debug; const mem = std.mem; const testing = std.testing; -pub const line_sep = switch (builtin.os) { - builtin.Os.windows => "\r\n", +pub const line_sep = switch (builtin.os.tag) { + .windows => "\r\n", else => "\n", }; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 343df9bde0..558b7e0513 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1,4 +1,5 @@ const std = @import("std.zig"); +const builtin = std.builtin; const math = std.math; const mem = std.mem; const io = std.io; @@ -11,7 +12,6 @@ const macho = std.macho; const coff = std.coff; const pdb = std.pdb; const ArrayList = std.ArrayList; -const builtin = @import("builtin"); const root = @import("root"); const maxInt = std.math.maxInt; const File = std.fs.File; @@ -101,7 +101,7 @@ pub fn detectTTYConfig() TTY.Config { } else |_| { if (stderr_file.supportsAnsiEscapeCodes()) { 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; } else { 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 /// equals the passed in addresses pointer. 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 u32_addrs_len = @intCast(u32, addrs.len); 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 { @setCold(true); // 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); } @@ -361,7 +361,7 @@ pub fn writeCurrentStackTrace( tty_config: TTY.Config, start_addr: ?usize, ) !void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); } var it = StackIterator.init(start_addr, null); @@ -418,7 +418,7 @@ pub const TTY = struct { .Dim => noasync out_stream.write(DIM) 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 { var attrs: windows.WORD = undefined; 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")) { return noasync root.os.debug.openSelfDebugInfo(allocator); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .freebsd, .macosx, @@ -1019,7 +1019,7 @@ pub const DebugInfo = struct { pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo { if (comptime std.Target.current.isDarwin()) return self.lookupModuleDyld(address) - else if (builtin.os == .windows) + else if (builtin.os.tag == .windows) return self.lookupModuleWin32(address) else 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 { base_address: usize, 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. -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")) root.enable_segfault_handler else @@ -1621,7 +1621,7 @@ pub fn attachSegfaultHandler() void { if (!have_segfault_handling_support) { @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); return; } @@ -1637,7 +1637,7 @@ pub fn attachSegfaultHandler() void { } fn resetSegfaultHandler() void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (windows_segfault_handle) |handle| { assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0); windows_segfault_handle = null; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 409dace20f..0d14f8d032 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -11,7 +11,7 @@ const system = std.os.system; const maxInt = std.math.maxInt; 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, .windows => WindowsDynLib, .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, @@ -82,12 +82,12 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { for (dyn_table) |*dyn| { switch (dyn.d_tag) { elf.DT_DEBUG => { - const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr); + const r_debug = @intToPtr(*RDebug, dyn.d_val); if (r_debug.r_version != 1) return error.InvalidExe; break :init r_debug.r_map; }, elf.DT_PLTGOT => { - const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr); + const got_table = @intToPtr([*]usize, dyn.d_val); // The address to the link_map structure is stored in the // second slot break :init @intToPtr(?*LinkMap, got_table[1]); @@ -390,7 +390,7 @@ pub const DlDynlib = struct { }; test "dynamic_library" { - const libname = switch (builtin.os) { + const libname = switch (builtin.os.tag) { .linux, .freebsd => "invalid_so.so", .windows => "invalid_dll.dll", .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib", diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 9e4c1ac5f6..99084f1897 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -349,16 +349,6 @@ pub const Elf = struct { program_headers: []ProgramHeader, 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( allocator: *mem.Allocator, seekable_stream: *io.SeekableStream(anyerror, anyerror), @@ -554,6 +544,21 @@ pub const Elf = struct { }; 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 Elf64_Half = u16; pub const Elf32_Word = u32; @@ -703,17 +708,11 @@ pub const Elf64_Rela = extern struct { }; pub const Elf32_Dyn = extern struct { d_tag: Elf32_Sword, - d_un: extern union { - d_val: Elf32_Word, - d_ptr: Elf32_Addr, - }, + d_val: Elf32_Addr, }; pub const Elf64_Dyn = extern struct { d_tag: Elf64_Sxword, - d_un: extern union { - d_val: Elf64_Xword, - d_ptr: Elf64_Addr, - }, + d_val: Elf64_Addr, }; pub const Elf32_Verdef = extern struct { vd_version: Elf32_Half, diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig index fd70f73aab..3c5b48d047 100644 --- a/lib/std/event/channel.zig +++ b/lib/std/event/channel.zig @@ -273,7 +273,7 @@ test "std.event.Channel" { if (builtin.single_threaded) return error.SkipZigTest; // 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; channel.init(&[0]i32{}); diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig index 492582da75..51a63e90ee 100644 --- a/lib/std/event/future.zig +++ b/lib/std/event/future.zig @@ -86,7 +86,7 @@ test "std.event.Future" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; // 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 if (!std.io.is_async) return error.SkipZigTest; diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig index e1b3495e5c..b9cbb5d95f 100644 --- a/lib/std/event/lock.zig +++ b/lib/std/event/lock.zig @@ -123,7 +123,7 @@ test "std.event.Lock" { if (builtin.single_threaded) return error.SkipZigTest; // 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(); defer lock.deinit(); diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 085e56fc15..80ba5a79b5 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -34,7 +34,7 @@ pub const Loop = struct { handle: anyframe, overlapped: Overlapped, - pub const overlapped_init = switch (builtin.os) { + pub const overlapped_init = switch (builtin.os.tag) { .windows => windows.OVERLAPPED{ .Internal = 0, .InternalHigh = 0, @@ -52,7 +52,7 @@ pub const Loop = struct { EventFd, }; - pub const EventFd = switch (builtin.os) { + pub const EventFd = switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => KEventFd, .linux => struct { base: ResumeNode, @@ -71,7 +71,7 @@ pub const Loop = struct { kevent: os.Kevent, }; - pub const Basic = switch (builtin.os) { + pub const Basic = switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic, .linux => struct { base: ResumeNode, @@ -173,7 +173,7 @@ pub const Loop = struct { const wakeup_bytes = [_]u8{0x1} ** 8; fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { self.os_data.fs_queue = std.atomic.Queue(Request).init(); self.os_data.fs_queue_item = 0; @@ -404,7 +404,7 @@ pub const Loop = struct { } fn deinitOsData(self: *Loop) void { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { noasync os.close(self.os_data.final_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; eventfd_node.base.handle = next_tick_node.data; - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent); const empty_kevs = &[0]os.Kevent{}; @@ -628,7 +628,7 @@ pub const Loop = struct { self.workerRun(); - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .freebsd, @@ -678,7 +678,7 @@ pub const Loop = struct { const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); if (prev == 1) { // cause all the threads to stop - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { self.posixFsRequest(&self.os_data.fs_end_request); // writing 8 bytes to an eventfd cannot fail @@ -902,7 +902,7 @@ pub const Loop = struct { self.finishOneEvent(); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { // only process 1 event so we don't steal from other threads 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 { self.beginOneEvent(); // finished in posixFsRun after processing the msg self.os_data.fs_queue.put(request_node); - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake); const empty_kevs = &[0]os.Kevent{}; @@ -1018,7 +1018,7 @@ pub const Loop = struct { // https://github.com/ziglang/zig/issues/3157 fn posixFsRun(self: *Loop) void { while (true) { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); } while (self.os_data.fs_queue.get()) |node| { @@ -1053,7 +1053,7 @@ pub const Loop = struct { } self.finishOneEvent(); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); 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, .macosx, .freebsd, .netbsd, .dragonfly => KEventData, .windows => struct { diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index aa6e414336..c62d614a6f 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -382,7 +382,7 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T { } test "fmt.parseFloat" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/fs.zig b/lib/std/fs.zig index ecd2c2b750..5077c52cd9 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -29,7 +29,7 @@ pub const Watch = @import("fs/watch.zig").Watch; /// All file system operations which return a path are guaranteed to /// fit into a UTF-8 encoded array of this length. /// 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, // 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 @@ -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 /// 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, else => true, }; @@ -270,7 +270,7 @@ pub const AtomicFile = struct { assert(!self.finished); self.file.close(); self.finished = true; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { 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)); return os.renameW(&tmp_path_w, &dest_path_w); @@ -394,7 +394,7 @@ pub const Dir = struct { 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 { dir: Dir, seek: i64, @@ -409,7 +409,7 @@ pub const Dir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .ios => return self.nextDarwin(), .freebsd, .netbsd, .dragonfly => return self.nextBsd(), else => @compileError("unimplemented"), @@ -644,7 +644,7 @@ pub const Dir = struct { }; pub fn iterate(self: Dir) Iterator { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ .dir = self, .seek = 0, @@ -710,7 +710,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. 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 (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openFileW(&path_w, flags); } @@ -720,7 +720,7 @@ pub const Dir = struct { /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.cStrToPrefixedFileW(sub_path); return self.openFileW(&path_w, flags); } @@ -760,7 +760,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. 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 (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.createFileW(&path_w, flags); } @@ -770,7 +770,7 @@ pub const Dir = struct { /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); return self.createFileW(&path_w, flags); } @@ -901,7 +901,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { 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); return self.openDirTraverseW(&sub_path_w); } @@ -919,7 +919,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { 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); return self.openDirListW(&sub_path_w); } @@ -930,7 +930,7 @@ pub const Dir = struct { /// Same as `openDirTraverse` except the parameter is null-terminated. 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); return self.openDirTraverseW(&sub_path_w); } else { @@ -941,7 +941,7 @@ pub const Dir = struct { /// Same as `openDirList` except the parameter is null-terminated. 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); return self.openDirListW(&sub_path_w); } else { @@ -1083,7 +1083,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { 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); 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 /// open it and handle the error for file not found. 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); 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. 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); 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. /// On POSIX targets, this function is comptime-callable. pub fn cwd() Dir { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; } else { 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 fn openSelfExe() OpenSelfExeError!File { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { return openFileAbsoluteC("/proc/self/exe", .{}); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const wide_slice = selfExePathW(); const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); return cwd().openReadW(&prefixed_path_w); @@ -1575,7 +1575,7 @@ pub fn openSelfExe() OpenSelfExeError!File { } test "openSelfExe" { - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(), 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; 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), .freebsd, .dragonfly => { 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. /// Returned value is a slice of out_buffer. 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, // the file path looks something like `/a/b/c/exe (deleted)` // This path cannot be opened, but it's valid for determining the directory diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 9d0ffcfd4a..c243eeb62c 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -29,7 +29,7 @@ pub const File = struct { pub const Mode = os.mode_t; - pub const default_mode = switch (builtin.os) { + pub const default_mode = switch (builtin.os.tag) { .windows => 0, else => 0o666, }; @@ -83,7 +83,7 @@ pub const File = struct { /// Test whether ANSI escape codes will be treated as such. pub fn supportsAnsiEscapeCodes(self: File) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.isCygwinPty(self.handle); } if (self.isTty()) { @@ -128,7 +128,7 @@ pub const File = struct { /// TODO: integrate with async I/O pub fn getEndPos(self: File) GetPosError!u64 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.GetFileSizeEx(self.handle); } return (try self.stat()).size; @@ -138,7 +138,7 @@ pub const File = struct { /// TODO: integrate with async I/O pub fn mode(self: File) ModeError!Mode { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return {}; } return (try self.stat()).mode; @@ -162,7 +162,7 @@ pub const File = struct { /// TODO: integrate with async I/O 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 info: windows.FILE_ALL_INFORMATION = undefined; 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 mtime: i64, ) UpdateTimesError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const atime_ft = windows.nanoSecondsToFileTime(atime); const mtime_ft = windows.nanoSecondsToFileTime(mtime); return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft); diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index 35c0265435..31aab590d8 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -13,7 +13,7 @@ pub const GetAppDataDirError = error{ /// Caller owns returned memory. /// TODO determine if we can remove the allocator requirement pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { - switch (builtin.os) { + switch (builtin.os.tag) { .windows => { var dir_path_ptr: [*:0]u16 = undefined; switch (os.windows.shell32.SHGetKnownFolderPath( diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 5d1c775629..35bc9b53b0 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -13,18 +13,18 @@ const process = std.process; pub const sep_windows = '\\'; 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_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_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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return byte == '/' or byte == '\\'; } else { return byte == '/'; @@ -74,7 +74,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u 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. /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return isAbsoluteWindowsC(path_c); } else { return isAbsolutePosixC(path_c); @@ -137,7 +137,7 @@ pub fn isAbsoluteC(path_c: [*:0]const u8) bool { } pub fn isAbsolute(path: []const u8) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return isAbsoluteWindows(path); } else { return isAbsolutePosix(path); @@ -318,7 +318,7 @@ test "windowsParsePath" { } pub fn diskDesignator(path: []const u8) []const u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return diskDesignatorWindows(path); } else { 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`. pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return resolveWindows(allocator, paths); } else { 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. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { 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); } @@ -495,7 +495,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { result_disk_designator = result[0..result_index]; }, 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); defer allocator.free(cwd); const parsed_cwd = windowsParsePath(cwd); @@ -510,7 +510,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, } } 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 const cwd = try process.getCwdAlloc(allocator); 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. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { 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); } @@ -603,7 +603,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (have_abs) { result = try allocator.alloc(u8, max_size); } 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); defer allocator.free(cwd); 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" { const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(cwd); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { cwd[0] = asciiUpper(cwd[0]); } @@ -661,7 +661,7 @@ test "resolveWindows" { // TODO https://github.com/ziglang/zig/issues/3288 return error.SkipZigTest; } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(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) /// then returns null pub fn dirname(path: []const u8) ?[]const u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return dirnameWindows(path); } else { 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return basenameWindows(path); } else { return basenamePosix(path); @@ -980,7 +980,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// string is returned. /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return relativeWindows(allocator, from, to); } else { return relativePosix(allocator, from, to); diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig index 0ff8c47ecf..1eb5a97ff1 100644 --- a/lib/std/fs/watch.zig +++ b/lib/std/fs/watch.zig @@ -42,7 +42,7 @@ pub fn Watch(comptime V: type) type { os_data: OsData, allocator: *Allocator, - const OsData = switch (builtin.os) { + const OsData = switch (builtin.os.tag) { // TODO https://github.com/ziglang/zig/issues/3778 .macosx, .freebsd, .netbsd, .dragonfly => KqOsData, .linux => LinuxOsData, @@ -121,7 +121,7 @@ pub fn Watch(comptime V: type) type { const self = try allocator.create(Self); errdefer allocator.destroy(self); - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); 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. pub fn deinit(self: *Self) void { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { // TODO we need to cancel the frames before destroying the lock 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 { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value), .linux => return addFileLinux(self, file_path, value), .windows => return addFileWindows(self, file_path, value), diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 4295f1393d..65809e97b4 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -36,7 +36,7 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new /// Thread-safe and lock-free. pub const page_allocator = if (std.Target.current.isWasm()) &wasm_page_allocator_state -else if (std.Target.current.getOs() == .freestanding) +else if (std.Target.current.os.tag == .freestanding) root.os.heap.page_allocator else &page_allocator_state; @@ -57,7 +57,7 @@ const PageAllocator = struct { fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { if (n == 0) return &[0]u8{}; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const w = os.windows; // 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 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const w = os.windows; if (new_size == 0) { // 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 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (old_mem.len == 0) { 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 { allocator: Allocator, heap_handle: ?HeapHandle, @@ -855,7 +855,7 @@ test "PageAllocator" { try testAllocatorAlignedShrink(allocator); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // Trying really large alignment. As mentionned in the implementation, // VirtualAlloc returns 64K aligned addresses. We want to make sure // PageAllocator works beyond that, as it's not tested by @@ -868,7 +868,7 @@ test "PageAllocator" { } test "HeapAllocator" { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { var heap_allocator = HeapAllocator.init(); defer heap_allocator.deinit(); diff --git a/lib/std/io.zig b/lib/std/io.zig index 548f119b4f..6a2a080ef5 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -35,7 +35,7 @@ else pub const is_async = mode != .blocking; fn getStdOutHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdOutput; } @@ -54,7 +54,7 @@ pub fn getStdOut() File { } fn getStdErrHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdError; } @@ -74,7 +74,7 @@ pub fn getStdErr() File { } fn getStdInHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdInput; } diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index dae8940016..f1840b49e3 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -544,7 +544,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: } test "Serializer/Deserializer generic" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index 61692283e6..2635962fcc 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -95,7 +95,7 @@ test "math.fabs64.special" { } test "math.fabs128.special" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index eeac61915c..c51747fd12 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -74,7 +74,7 @@ pub fn isNegativeInf(x: var) bool { } test "math.isInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -97,7 +97,7 @@ test "math.isInf" { } test "math.isPositiveInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -120,7 +120,7 @@ test "math.isPositiveInf" { } test "math.isNegativeInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/isnan.zig b/lib/std/math/isnan.zig index 4b7e69490a..2879c44502 100644 --- a/lib/std/math/isnan.zig +++ b/lib/std/math/isnan.zig @@ -16,7 +16,7 @@ pub fn isSignalNan(x: var) bool { } test "math.isNan" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 6954b2fb17..a57519cd14 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -73,7 +73,7 @@ pub const Mutex = if (builtin.single_threaded) return self.tryAcquire() orelse @panic("deadlock detected"); } } -else if (builtin.os == .windows) +else if (builtin.os.tag == .windows) // https://locklessinc.com/articles/keyed_events/ extern union { 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 struct { state: usize, diff --git a/lib/std/net.zig b/lib/std/net.zig index 898ba086be..b54803cd39 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -501,7 +501,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* return result; } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const flags = std.c.AI_NUMERICSERV; const family = os.AF_UNSPEC; var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 45d8b1cffd..4f3d955f30 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -63,7 +63,7 @@ test "parse and render IPv4 addresses" { } test "resolve DNS" { - if (std.builtin.os == .windows) { + if (std.builtin.os.tag == .windows) { // DNS resolution not implemented on Windows yet. return error.SkipZigTest; } @@ -81,7 +81,7 @@ test "resolve DNS" { test "listen on a port, send bytes, receive bytes" { 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 return error.SkipZigTest; } diff --git a/lib/std/os.zig b/lib/std/os.zig index 3b60a08cef..49e88bf9c7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -56,7 +56,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This()) root.os.system else if (builtin.link_libc) std.c -else switch (builtin.os) { +else switch (builtin.os.tag) { .macosx, .ios, .watchos, .tvos => darwin, .freebsd => freebsd, .linux => linux, @@ -93,10 +93,10 @@ pub const errno = system.getErrno; /// must call `fsync` before `close`. /// Note: The Zig standard library does not support POSIX thread cancellation. pub fn close(fd: fd_t) void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.CloseHandle(fd); } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { _ = wasi.fd_close(fd); } 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 /// library implementation. pub fn getrandom(buffer: []u8) GetRandomError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { 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; - 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; while (buf.len != 0) { @@ -153,7 +153,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { } return; } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { switch (wasi.random_get(buffer.ptr, buffer.len)) { 0 => return, else => |err| return unexpectedErrno(err), @@ -188,13 +188,13 @@ pub fn abort() noreturn { // MSVCRT abort() sometimes opens a popup window which is undesirable, so // even when linking libc on Windows we use our own abort implementation. // See https://github.com/ziglang/zig/issues/2071 for more details. - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (builtin.mode == .Debug) { @breakpoint(); } windows.kernel32.ExitProcess(3); } - if (!builtin.link_libc and builtin.os == .linux) { + if (!builtin.link_libc and builtin.os.tag == .linux) { raise(SIGABRT) catch {}; // TODO the rest of the implementation of abort() from musl libc here @@ -202,10 +202,10 @@ pub fn abort() noreturn { raise(SIGKILL) catch {}; exit(127); } - if (builtin.os == .uefi) { + if (builtin.os.tag == .uefi) { exit(0); // TODO choose appropriate exit code } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { @breakpoint(); 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; // block application signals _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set); @@ -260,16 +260,16 @@ pub fn exit(status: u8) noreturn { if (builtin.link_libc) { system.exit(status); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { windows.kernel32.ExitProcess(status); } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { 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); } - if (builtin.os == .uefi) { + if (builtin.os.tag == .uefi) { // 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. 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 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 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); } - if (builtin.os == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const iovs = [1]iovec{iovec{ .iov_base = buf.ptr, .iov_len = buf.len, @@ -352,7 +352,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// * Windows /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // TODO batch these into parallel requests var off: 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 /// 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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { 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 var off: 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 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 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); } - if (builtin.os == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const ciovs = [1]iovec_const{iovec_const{ .iov_base = bytes.ptr, .iov_len = bytes.len, @@ -650,7 +650,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { /// 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. pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) WriteError!void { - if (comptime std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { return windows.WriteFile(fd, bytes, offset); } @@ -739,7 +739,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void } } - if (comptime std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { var off = offset; for (iov) |item| { try pwrite(fd, item.iov_base[0..item.iov_len], off); @@ -1129,7 +1129,7 @@ pub fn getenv(key: []const u8) ?[]const u8 { } 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."); } // 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; 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."); } 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. /// See also `getenv`. 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"); } 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. pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.GetCurrentDirectory(out_buffer); } @@ -1240,7 +1240,7 @@ pub const SymLinkError = error{ /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkC` and `symlinkW`. 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 sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); 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. /// See also `symlink`. 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 sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); 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. /// See also `unlinkC`. 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); return windows.DeleteFileW(&file_path_w); } 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. 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); return windows.DeleteFileW(&file_path_w); } @@ -1372,7 +1372,7 @@ pub const UnlinkatError = UnlinkError || error{ /// Asserts that the path parameter has no null bytes. 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 (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); 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. 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); return unlinkatW(dirfd, &file_path_w, flags); } @@ -1493,7 +1493,7 @@ const RenameError = error{ /// Change the name or location of a file. 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 new_path_w = try windows.sliceToPrefixedFileW(new_path); 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. 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 new_path_w = try windows.cStrToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); @@ -1561,7 +1561,7 @@ pub const MakeDirError = error{ /// Create a directory. /// `mode` is ignored on Windows. 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); return windows.CreateDirectoryW(&dir_path_w, null); } 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. 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); return windows.CreateDirectoryW(&dir_path_w, null); } @@ -1611,7 +1611,7 @@ pub const DeleteDirError = error{ /// Deletes an empty directory. 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); return windows.RemoveDirectoryW(&dir_path_w); } else { @@ -1622,7 +1622,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void { /// Same as `rmdir` except the parameter is null-terminated. 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); return windows.RemoveDirectoryW(&dir_path_w); } @@ -1658,7 +1658,7 @@ pub const ChangeCurDirError = error{ /// Changes the current working directory of the calling process. /// `dir_path` is recommended to be a UTF-8 encoded string. 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); @compileError("TODO implement chdir for Windows"); } else { @@ -1669,7 +1669,7 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { /// Same as `chdir` except the parameter is null-terminated. 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); @compileError("TODO implement chdir for Windows"); } @@ -1700,7 +1700,7 @@ pub const ReadLinkError = error{ /// Read value of a symbolic link. /// The return value is a slice of `out_buffer` from index 0. 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); @compileError("TODO implement readlink for Windows"); } 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. 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); @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 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); @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. pub fn isatty(handle: fd_t) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (isCygwinPty(handle)) return true; @@ -1810,7 +1810,7 @@ pub fn isatty(handle: fd_t) bool { if (builtin.link_libc) { return system.isatty(handle) != 0; } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { var statbuf: fdstat_t = undefined; const err = system.fd_fdstat_get(handle, &statbuf); if (err != 0) { @@ -1828,7 +1828,7 @@ pub fn isatty(handle: fd_t) bool { return true; } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var wsz: linux.winsize = undefined; 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 { - if (builtin.os != .windows) return false; + if (builtin.os.tag != .windows) return false; const size = @sizeOf(windows.FILE_NAME_INFO); 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 /// TODO currently this assumes `mode` is `F_OK` on Windows. 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); _ = try windows.GetFileAttributesW(&path_w); return; @@ -2603,7 +2603,7 @@ pub const accessC = accessZ; /// Same as `access` except `path` is null-terminated. 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); _ = try windows.GetFileAttributesW(&path_w); 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. /// TODO currently this ignores `mode` and `flags` on Windows. 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); 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. 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); 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. 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; switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { 0 => return, @@ -2823,7 +2823,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_BEGIN(fd, offset); } 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. 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; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { 0 => return, @@ -2852,7 +2852,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT(fd, offset); } 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. 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; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { 0 => return, @@ -2880,7 +2880,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_END(fd, offset); } 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. 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; switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { 0 => return result, @@ -2908,7 +2908,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT_get(fd); } 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. /// See also `realpathC` and `realpathW`. 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); 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. 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); 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); defer close(fd); @@ -3121,7 +3121,7 @@ pub fn dl_iterate_phdr( pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; 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; switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { 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 { - if (comptime std.Target.current.getOs() == .wasi) { + if (std.Target.current.os.tag == .wasi) { var ts: timestamp_t = undefined; switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { 0 => res.* = .{ @@ -3222,7 +3222,7 @@ pub const SigaltstackError = error{ } || UnexpectedError; 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"); switch (errno(system.sigaltstack(ss, old_ss))) { @@ -3294,23 +3294,25 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .linux) { - var uts: utsname = undefined; - switch (errno(system.uname(&uts))) { - 0 => { - const hostname = mem.toSlice(u8, @ptrCast([*:0]u8, &uts.nodename)); - mem.copy(u8, name_buffer, hostname); - return name_buffer[0..hostname.len]; - }, - EFAULT => unreachable, - EPERM => return error.PermissionDenied, - else => |err| return unexpectedErrno(err), - } + if (builtin.os.tag == .linux) { + const uts = uname(); + const hostname = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.nodename)); + mem.copy(u8, name_buffer, hostname); + return name_buffer[0..hostname.len]; } @compileError("TODO implement gethostname for this OS"); } +pub fn uname() utsname { + var uts: utsname = undefined; + switch (errno(system.uname(&uts))) { + 0 => return uts, + EFAULT => unreachable, + else => unreachable, + } +} + pub fn res_mkquery( op: u4, dname: []const u8, @@ -3611,7 +3613,7 @@ pub const SchedYieldError = error{ }; 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 // an error condition on Windows. _ = windows.kernel32.SwitchToThread(); diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig index bab9ad0ae5..38f019d775 100644 --- a/lib/std/os/bits.zig +++ b/lib/std/os/bits.zig @@ -3,10 +3,10 @@ //! Root source files can define `os.bits` and these will additionally be added //! to the namespace. -const builtin = @import("builtin"); +const std = @import("std"); 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"), .dragonfly => @import("bits/dragonfly.zig"), .freebsd => @import("bits/freebsd.zig"), diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index d11f206482..30dba85e51 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1070,7 +1070,7 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi } test "" { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); } } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 488a557ed6..197edd82c1 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -53,7 +53,7 @@ test "std.Thread.getCurrentId" { thread.wait(); if (Thread.use_pthreads) { expect(thread_current_id == thread_id); - } else if (builtin.os == .windows) { + } else if (builtin.os.tag == .windows) { expect(Thread.getCurrentId() != thread_current_id); } else { // If the thread completes very quickly, then thread_id can be 0. See the @@ -151,7 +151,7 @@ test "realpath" { } 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; 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" { - 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; var counter: usize = 0; @@ -213,7 +213,7 @@ test "dl_iterate_phdr" { } test "gethostname" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; var buf: [os.HOST_NAME_MAX]u8 = undefined; @@ -222,7 +222,7 @@ test "gethostname" { } test "pipe" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; var fds = try os.pipe(); @@ -241,7 +241,7 @@ test "argsAlloc" { test "memfd_create" { // 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) { // Related: https://github.com/ziglang/zig/issues/4019 error.SystemOutdated => return error.SkipZigTest, @@ -258,7 +258,7 @@ test "memfd_create" { } test "mmap" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; // Simple mmap() call with non page-aligned size @@ -353,7 +353,7 @@ test "mmap" { } 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); } else { expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 63b6fffa73..51be01315e 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -593,7 +593,7 @@ test "PackedInt(Array/Slice)Endian" { // after this one is not mapped and will cause a segfault if we // don't account for the bounds. test "PackedIntArray at end of available memory" { - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, else => return, } @@ -612,7 +612,7 @@ test "PackedIntArray 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 => {}, else => return, } diff --git a/lib/std/process.zig b/lib/std/process.zig index 0dab8bb64b..01b9947518 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -36,7 +36,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const ptr = os.windows.peb().ProcessParameters.Environment; var i: usize = 0; @@ -61,7 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } return result; - } else if (builtin.os == .wasi) { + } else if (builtin.os.tag == .wasi) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; @@ -137,7 +137,7 @@ pub const GetEnvVarOwnedError = error{ /// Caller must free returned memory. 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 key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); defer allocator.free(key_w); @@ -338,12 +338,12 @@ pub const ArgIteratorWindows = 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, pub fn init() ArgIterator { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { // TODO: Figure out a compatible interface accomodating WASI @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. pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.inner.next(allocator); } else { 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. pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { var count: 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 { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { const last_item = args_alloc[args_alloc.len - 1]; const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated const first_item_ptr = args_alloc[0].ptr; @@ -498,7 +498,7 @@ pub const UserInfo = struct { /// POSIX function which gets a uid from username. 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), else => @compileError("Unsupported OS"), }; @@ -591,7 +591,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { } pub fn getBaseAddress() usize { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const base = os.system.getauxval(std.elf.AT_BASE); if (base != 0) { @@ -609,13 +609,17 @@ pub fn getBaseAddress() usize { } /// Caller owns the result value and each inner slice. +/// TODO Remove the `Allocator` requirement from this API, which will remove the `Allocator` +/// requirement from `std.zig.system.NativeTargetInfo.detect`. Most likely this will require +/// introducing a new, lower-level function which takes a callback function, and then this +/// function which takes an allocator can exist on top of it. pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]u8 { switch (builtin.link_mode) { .Static => return &[_][:0]u8{}, .Dynamic => {}, } const List = std.ArrayList([:0]u8); - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .freebsd, .netbsd, diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index b31906c5f8..c28db809ca 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -16,7 +16,7 @@ pub const ResetEvent = struct { pub const OsEvent = if (builtin.single_threaded) 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 else AtomicEvent; @@ -106,7 +106,7 @@ const PosixEvent = struct { fn deinit(self: *PosixEvent) void { // on dragonfly, *destroy() functions can return EINVAL // 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); 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, .linux => LinuxFutex, else => SpinFutex, diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 56ae3d0d8f..1f11fabca0 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -17,7 +17,7 @@ const is_msvc = switch (builtin.abi) { .msvc => true, else => false, }; -const is_freestanding = switch (builtin.os) { +const is_freestanding = switch (builtin.os.tag) { .freestanding => true, else => false, }; @@ -81,7 +81,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn @setCold(true); 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(); } while (true) {} @@ -178,11 +178,11 @@ test "test_bcmp" { comptime { if (builtin.mode != builtin.Mode.ReleaseFast and builtin.mode != builtin.Mode.ReleaseSmall and - builtin.os != builtin.Os.windows) + builtin.os.tag != .windows) { @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" }); } - if (builtin.os == builtin.Os.linux) { + if (builtin.os.tag == .linux) { @export(clone, .{ .name = "clone" }); } } diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 8d49fdbd2a..9ed866f62d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -1,11 +1,9 @@ -const builtin = @import("builtin"); +const std = @import("std"); +const builtin = std.builtin; const is_test = builtin.is_test; -const is_gnu = switch (builtin.abi) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, -}; -const is_mingw = builtin.os == .windows and is_gnu; +const is_gnu = std.Target.current.abi.isGnu(); +const is_mingw = builtin.os.tag == .windows and is_gnu; comptime { const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; @@ -180,7 +178,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_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 }); } @@ -250,7 +248,7 @@ comptime { @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 if (is_mingw) { @export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); @@ -288,7 +286,7 @@ comptime { else => {}, } } else { - if (builtin.glibc_version != null) { + if (std.Target.current.isGnuLibC() and builtin.link_libc) { @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); } @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @@ -307,7 +305,7 @@ comptime { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - @import("std").debug.panic("{}", .{msg}); + std.debug.panic("{}", .{msg}); } else { unreachable; } diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig index 402bb5a43c..d7e175045c 100644 --- a/lib/std/special/compiler_rt/addXf3_test.zig +++ b/lib/std/special/compiler_rt/addXf3_test.zig @@ -31,7 +31,7 @@ fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { } test "addtf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -75,7 +75,7 @@ fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { } test "subtf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index aa2faae901..e2664f6bae 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -90,7 +90,7 @@ test "extendhfsf2" { test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN // On x86 the NaN becomes quiet because the return is pushed on the x87 // 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(0, 0); // 0 diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig index 4c43c90550..cb4b94c5cd 100644 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ b/lib/std/special/compiler_rt/fixtfdi_test.zig @@ -11,7 +11,7 @@ fn test__fixtfdi(a: f128, expected: i64) void { } test "fixtfdi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig index 4eabd0c594..96bb151e80 100644 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ b/lib/std/special/compiler_rt/fixtfsi_test.zig @@ -11,7 +11,7 @@ fn test__fixtfsi(a: f128, expected: i32) void { } test "fixtfsi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig index acda2f162b..73cc0596e7 100644 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ b/lib/std/special/compiler_rt/fixtfti_test.zig @@ -11,7 +11,7 @@ fn test__fixtfti(a: f128, expected: i128) void { } test "fixtfti" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfdi_test.zig b/lib/std/special/compiler_rt/fixunstfdi_test.zig index 154fffe18a..02cef2f700 100644 --- a/lib/std/special/compiler_rt/fixunstfdi_test.zig +++ b/lib/std/special/compiler_rt/fixunstfdi_test.zig @@ -7,7 +7,7 @@ fn test__fixunstfdi(a: f128, expected: u64) void { } test "fixunstfdi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig index af312ddc46..734efff4de 100644 --- a/lib/std/special/compiler_rt/fixunstfsi_test.zig +++ b/lib/std/special/compiler_rt/fixunstfsi_test.zig @@ -9,7 +9,7 @@ fn test__fixunstfsi(a: f128, expected: u32) void { const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfsi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig index 84dbf991e2..649fcdf1e3 100644 --- a/lib/std/special/compiler_rt/fixunstfti_test.zig +++ b/lib/std/special/compiler_rt/fixunstfti_test.zig @@ -9,7 +9,7 @@ fn test__fixunstfti(a: f128, expected: u128) void { const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfti" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/floattitf_test.zig b/lib/std/special/compiler_rt/floattitf_test.zig index 0b2b5b958a..4601b90107 100644 --- a/lib/std/special/compiler_rt/floattitf_test.zig +++ b/lib/std/special/compiler_rt/floattitf_test.zig @@ -7,7 +7,7 @@ fn test__floattitf(a: i128, expected: f128) void { } test "floattitf" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/floatuntitf_test.zig b/lib/std/special/compiler_rt/floatuntitf_test.zig index 8b99bbef5d..34c7407c98 100644 --- a/lib/std/special/compiler_rt/floatuntitf_test.zig +++ b/lib/std/special/compiler_rt/floatuntitf_test.zig @@ -7,7 +7,7 @@ fn test__floatuntitf(a: u128, expected: f128) void { } test "floatuntitf" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index 00db984a89..45baa62a17 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -44,7 +44,7 @@ fn makeNaN128(rand: u64) f128 { return float_result; } test "multf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 7c83c66ec0..cba5b85264 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -1,23 +1,23 @@ const std = @import("std"); pub fn __truncsfhf2(a: f32) callconv(.C) u16 { - return @bitCast(u16, truncXfYf2(f16, f32, a)); + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f32, a })); } pub fn __truncdfhf2(a: f64) callconv(.C) u16 { - return @bitCast(u16, truncXfYf2(f16, f64, a)); + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); } pub fn __trunctfsf2(a: f128) callconv(.C) f32 { - return truncXfYf2(f32, f128, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); } pub fn __trunctfdf2(a: f128) callconv(.C) f64 { - return truncXfYf2(f64, f128, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f64, f128, a }); } pub fn __truncdfsf2(a: f64) callconv(.C) f32 { - return truncXfYf2(f32, f64, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f64, a }); } pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { @@ -35,7 +35,7 @@ pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); } -inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = std.meta.IntType(false, @typeInfo(src_t).Float.bits); const dst_rep_t = std.meta.IntType(false, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig index f14dbe6b43..bd05c8652c 100644 --- a/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -151,7 +151,7 @@ fn test__trunctfsf2(a: f128, expected: u32) void { } test "trunctfsf2" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -190,7 +190,7 @@ fn test__trunctfdf2(a: f128, expected: u64) void { } test "trunctfdf2" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/init-exe/build.zig b/lib/std/special/init-exe/build.zig index 0b7410f2ad..fd71588c5f 100644 --- a/lib/std/special/init-exe/build.zig +++ b/lib/std/special/init-exe/build.zig @@ -1,8 +1,18 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); + const exe = b.addExecutable("$", "src/main.zig"); + exe.setTarget(target); exe.setBuildMode(mode); exe.install(); diff --git a/lib/std/special/init-exe/src/main.zig b/lib/std/special/init-exe/src/main.zig index 5f35540dc0..c6a70af56d 100644 --- a/lib/std/special/init-exe/src/main.zig +++ b/lib/std/special/init-exe/src/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); pub fn main() anyerror!void { - std.debug.warn("All your base are belong to us.\n", .{}); + std.debug.warn("All your codebase are belong to us.\n", .{}); } diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index 1a3239a95c..0af08e9a84 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -46,7 +46,7 @@ pub const SpinLock = struct { // and yielding for 380-410 iterations was found to be // a nice sweet spot. Posix systems on the other hand, // especially linux, perform better by yielding the thread. - switch (builtin.os) { + switch (builtin.os.tag) { .windows => loopHint(400), else => std.os.sched_yield() catch loopHint(1), } diff --git a/lib/std/start.zig b/lib/std/start.zig index b58b6e8144..b8e3e97f94 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -12,7 +12,7 @@ const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start"; comptime { 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" }); } } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { @@ -20,17 +20,17 @@ comptime { if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { @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 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) { @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" }); } - } else if (builtin.os == .uefi) { + } else if (builtin.os.tag == .uefi) { 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 }); - } 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 }); } } @@ -78,7 +78,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv } 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, // and we want fewer call frames in stack traces. 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 fn posixCallMainAndExit() noreturn { - if (builtin.os == builtin.Os.freebsd) { + if (builtin.os.tag == .freebsd) { @setAlignStack(16); } const argc = starting_stack_ptr[0]; @@ -144,7 +144,7 @@ fn posixCallMainAndExit() noreturn { while (envp_optional[envp_count]) |_| : (envp_count += 1) {} 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 const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); std.os.linux.elf_aux_maybe = auxv; diff --git a/lib/std/target.zig b/lib/std/target.zig index cf83bb1f7a..5807ba65ef 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1,61 +1,291 @@ const std = @import("std.zig"); const mem = std.mem; const builtin = std.builtin; +const Version = std.builtin.Version; /// TODO Nearly all the functions in this namespace would be /// better off if https://github.com/ziglang/zig/issues/425 /// was solved. -pub const Target = union(enum) { - Native: void, - Cross: Cross, - - pub const Os = enum { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd, - fuchsia, - ios, - kfreebsd, - linux, - lv2, - macosx, - netbsd, - openbsd, - solaris, - windows, - haiku, - minix, - rtems, - nacl, - cnk, - aix, - cuda, - nvcl, - amdhsa, - ps4, - elfiamcu, - tvos, - watchos, - mesa3d, - contiki, - amdpal, - hermit, - hurd, - wasi, - emscripten, - uefi, - other, - - pub fn parse(text: []const u8) !Os { - const info = @typeInfo(Os); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(Os, field.name); +pub const Target = struct { + cpu: Cpu, + os: Os, + abi: Abi, + + pub const Os = struct { + tag: Tag, + version_range: VersionRange, + + pub const Tag = enum { + freestanding, + ananas, + cloudabi, + dragonfly, + freebsd, + fuchsia, + ios, + kfreebsd, + linux, + lv2, + macosx, + netbsd, + openbsd, + solaris, + windows, + haiku, + minix, + rtems, + nacl, + cnk, + aix, + cuda, + nvcl, + amdhsa, + ps4, + elfiamcu, + tvos, + watchos, + mesa3d, + contiki, + amdpal, + hermit, + hurd, + wasi, + emscripten, + uefi, + other, + + pub fn isDarwin(tag: Tag) bool { + return switch (tag) { + .ios, .macosx, .watchos, .tvos => true, + else => false, + }; + } + + pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 { + if (tag.isDarwin()) { + return ".dylib"; + } + switch (tag) { + .windows => return ".dll", + else => return ".so", + } + } + }; + + /// Based on NTDDI version constants from + /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt + pub const WindowsVersion = enum(u32) { + nt4 = 0x04000000, + win2k = 0x05000000, + xp = 0x05010000, + ws2003 = 0x05020000, + vista = 0x06000000, + win7 = 0x06010000, + win8 = 0x06020000, + win8_1 = 0x06030000, + win10 = 0x0A000000, + win10_th2 = 0x0A000001, + win10_rs1 = 0x0A000002, + win10_rs2 = 0x0A000003, + win10_rs3 = 0x0A000004, + win10_rs4 = 0x0A000005, + win10_rs5 = 0x0A000006, + win10_19h1 = 0x0A000007, + _, + + pub const Range = struct { + min: WindowsVersion, + max: WindowsVersion, + + pub fn includesVersion(self: Range, ver: WindowsVersion) bool { + return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max); } + }; + }; + + pub const LinuxVersionRange = struct { + range: Version.Range, + glibc: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + return self.range.includesVersion(ver); } - return error.UnknownOperatingSystem; + }; + + /// 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, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .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 }, + }, + }, + .ios => return .{ + .semver = .{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 13, .minor = 4, .patch = 0 }, + }, + }, + .watchos => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 0 }, + .max = .{ .major = 6, .minor = 2, .patch = 0 }, + }, + }, + .tvos => return .{ + .semver = .{ + .min = .{ .major = 13, .minor = 0 }, + .max = .{ .major = 13, .minor = 4, .patch = 0 }, + }, + }, + .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, + }, + }, + } + } + }; + + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), + }; + } + + pub fn requiresLibC(os: Os) bool { + return switch (os.tag) { + .freebsd, + .netbsd, + .macosx, + .ios, + .tvos, + .watchos, + .dragonfly, + .openbsd, + => true, + + .linux, + .windows, + .freestanding, + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => false, + }; } }; @@ -100,11 +330,10 @@ pub const Target = union(enum) { macabi, pub fn default(arch: Cpu.Arch, target_os: Os) Abi { - switch (arch) { - .wasm32, .wasm64 => return .musl, - else => {}, + if (arch.isWasm()) { + return .musl; } - switch (target_os) { + switch (target_os.tag) { .freestanding, .ananas, .cloudabi, @@ -149,14 +378,25 @@ pub const Target = union(enum) { } } - pub fn parse(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 error.UnknownApplicationBinaryInterface; + pub fn isGnu(abi: Abi) bool { + return switch (abi) { + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + else => false, + }; + } + + pub fn isMusl(abi: Abi) bool { + return switch (abi) { + .musl, .musleabi, .musleabihf => true, + else => false, + }; + } + + pub fn oFileExt(abi: Abi) [:0]const u8 { + return switch (abi) { + .msvc => ".obj", + else => ".o", + }; } }; @@ -179,12 +419,6 @@ pub const Target = union(enum) { EfiRuntimeDriver, }; - pub const Cross = struct { - cpu: Cpu, - os: Os, - abi: Abi, - }; - pub const Cpu = struct { /// Architecture arch: Arch, @@ -230,6 +464,12 @@ pub const Target = union(enum) { return Set{ .ints = [1]usize{0} ** usize_count }; } + pub fn isEmpty(set: Set) bool { + return for (set.ints) |x| { + if (x != 0) break false; + } else true; + } + pub fn isEnabled(set: Set, arch_feature_index: Index) bool { const usize_index = arch_feature_index / @bitSizeOf(usize); const bit_index = @intCast(ShiftInt, arch_feature_index % @bitSizeOf(usize)); @@ -256,6 +496,15 @@ pub const Target = union(enum) { set.ints[usize_index] &= ~(@as(usize, 1) << bit_index); } + /// Removes the specified feature but not its dependents. + pub fn removeFeatureSet(set: *Set, other_set: Set) void { + // TODO should be able to use binary not on @Vector type. + // https://github.com/ziglang/zig/issues/903 + for (set.ints) |*int, i| { + int.* &= ~other_set.ints[i]; + } + } + pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { @setEvalBranchQuota(1000000); @@ -393,7 +642,7 @@ pub const Target = union(enum) { return cpu; } } - return error.UnknownCpu; + return error.UnknownCpuModel; } pub fn toElfMachine(arch: Arch) std.elf.EM { @@ -509,6 +758,66 @@ pub const Target = union(enum) { }; } + pub fn ptrBitWidth(arch: Arch) u32 { + switch (arch) { + .avr, + .msp430, + => return 16, + + .arc, + .arm, + .armeb, + .hexagon, + .le32, + .mips, + .mipsel, + .powerpc, + .r600, + .riscv32, + .sparc, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .i386, + .xcore, + .nvptx, + .amdil, + .hsail, + .spir, + .kalimba, + .shave, + .lanai, + .wasm32, + .renderscript32, + .aarch64_32, + => return 32, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .x86_64, + .nvptx64, + .le64, + .amdil64, + .hsail64, + .spir64, + .wasm64, + .renderscript64, + .amdgcn, + .bpfel, + .bpfeb, + .sparcv9, + .s390x, + => return 64, + } + } + /// Returns a name that matches the lib/std/target/* directory name. pub fn genericName(arch: Arch) []const u8 { return switch (arch) { @@ -576,16 +885,6 @@ pub const Target = union(enum) { else => &[0]*const Model{}, }; } - - pub fn parse(text: []const u8) !Arch { - const info = @typeInfo(Arch); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @as(Arch, @field(Arch, field.name)); - } - } - return error.UnknownArchitecture; - } }; pub const Model = struct { @@ -602,524 +901,168 @@ pub const Target = union(enum) { .features = features, }; } + + pub fn baseline(arch: Arch) *const Model { + const S = struct { + const generic_model = Model{ + .name = "generic", + .llvm_name = null, + .features = Cpu.Feature.Set.empty, + }; + }; + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, + .avr => &avr.cpu.avr1, + .bpfel, .bpfeb => &bpf.cpu.generic, + .hexagon => &hexagon.cpu.generic, + .mips, .mipsel => &mips.cpu.mips32, + .mips64, .mips64el => &mips.cpu.mips64, + .msp430 => &msp430.cpu.generic, + .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, + .amdgcn => &amdgpu.cpu.generic, + .riscv32 => &riscv.cpu.baseline_rv32, + .riscv64 => &riscv.cpu.baseline_rv64, + .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, + .s390x => &systemz.cpu.generic, + .i386 => &x86.cpu.pentium4, + .x86_64 => &x86.cpu.x86_64, + .nvptx, .nvptx64 => &nvptx.cpu.sm_20, + .wasm32, .wasm64 => &wasm.cpu.generic, + + else => &S.generic_model, + }; + } }; /// The "default" set of CPU features for cross-compiling. A conservative set /// of features that is expected to be supported on most available hardware. pub fn baseline(arch: Arch) Cpu { - const S = struct { - const generic_model = Model{ - .name = "generic", - .llvm_name = null, - .features = Cpu.Feature.Set.empty, - }; - }; - const model = switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, - .avr => &avr.cpu.avr1, - .bpfel, .bpfeb => &bpf.cpu.generic, - .hexagon => &hexagon.cpu.generic, - .mips, .mipsel => &mips.cpu.mips32, - .mips64, .mips64el => &mips.cpu.mips64, - .msp430 => &msp430.cpu.generic, - .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, - .amdgcn => &amdgpu.cpu.generic, - .riscv32 => &riscv.cpu.baseline_rv32, - .riscv64 => &riscv.cpu.baseline_rv64, - .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, - .s390x => &systemz.cpu.generic, - .i386 => &x86.cpu.pentium4, - .x86_64 => &x86.cpu.x86_64, - .nvptx, .nvptx64 => &nvptx.cpu.sm_20, - .wasm32, .wasm64 => &wasm.cpu.generic, - - else => &S.generic_model, - }; - return model.toCpu(arch); + return Model.baseline(arch).toCpu(arch); } }; pub const current = Target{ - .Cross = Cross{ - .cpu = builtin.cpu, - .os = builtin.os, - .abi = builtin.abi, - }, + .cpu = builtin.cpu, + .os = builtin.os, + .abi = builtin.abi, }; pub const stack_align = 16; - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - }); - } - - /// Returned slice must be freed by the caller. - pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { - const arch = switch (target.getArch()) { - .i386 => "x86", - .x86_64 => "x64", - - .arm, - .armeb, - .thumb, - .thumbeb, - .aarch64_32, - => "arm", - - .aarch64, - .aarch64_be, - => "arm64", - - else => return error.VcpkgNoSuchArchitecture, - }; - - const os = switch (target.getOs()) { - .windows => "windows", - .linux => "linux", - .macosx => "macos", - else => return error.VcpkgNoSuchOs, - }; - - if (linkage == .Static) { - return try mem.join(allocator, "-", &[_][]const u8{ arch, os, "static" }); - } else { - return try mem.join(allocator, "-", &[_][]const u8{ arch, os }); - } + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator); } - pub fn allocDescription(self: Target, allocator: *mem.Allocator) ![]u8 { - // TODO is there anything else worthy of the description that is not - // already captured in the triple? - return self.zigTriple(allocator); + pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![:0]u8 { + return std.fmt.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); } - pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - }); + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); } - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - }); + pub fn oFileExt(self: Target) [:0]const u8 { + return self.abi.oFileExt(); } - pub const ParseOptions = struct { - /// This is sometimes called a "triple". It looks roughly like this: - /// riscv64-linux-gnu - /// The fields are, respectively: - /// * CPU Architecture - /// * Operating System - /// * C ABI (optional) - arch_os_abi: []const u8, - - /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" - /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features - /// to remove from the set. - cpu_features: []const u8 = "baseline", - - /// If this is provided, the function will populate some information about parsing failures, - /// so that user-friendly error messages can be delivered. - diagnostics: ?*Diagnostics = null, - - pub const Diagnostics = struct { - /// If the architecture was determined, this will be populated. - arch: ?Cpu.Arch = null, - - /// If the OS was determined, this will be populated. - os: ?Os = null, - - /// If the ABI was determined, this will be populated. - abi: ?Abi = null, - - /// If the CPU name was determined, this will be populated. - cpu_name: ?[]const u8 = null, - - /// If error.UnknownCpuFeature is returned, this will be populated. - unknown_feature_name: ?[]const u8 = null, - }; - }; - - pub fn parse(args: ParseOptions) !Target { - var dummy_diags: ParseOptions.Diagnostics = undefined; - var diags = args.diagnostics orelse &dummy_diags; - - var it = mem.separate(args.arch_os_abi, "-"); - const arch_name = it.next() orelse return error.MissingArchitecture; - const arch = try Cpu.Arch.parse(arch_name); - diags.arch = arch; - - const os_name = it.next() orelse return error.MissingOperatingSystem; - const os = try Os.parse(os_name); - diags.os = os; - - const abi_name = it.next(); - const abi = if (abi_name) |n| try Abi.parse(n) else Abi.default(arch, os); - diags.abi = abi; - - if (it.next() != null) return error.UnexpectedExtraField; - - const all_features = arch.allFeaturesList(); - var index: usize = 0; - while (index < args.cpu_features.len and - args.cpu_features[index] != '+' and - args.cpu_features[index] != '-') - { - index += 1; + pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { + switch (os_tag) { + .windows => return ".exe", + .uefi => return ".efi", + else => if (cpu_arch.isWasm()) { + return ".wasm"; + } else { + return ""; + }, } - const cpu_name = args.cpu_features[0..index]; - diags.cpu_name = cpu_name; - - const cpu: Cpu = if (mem.eql(u8, cpu_name, "baseline")) Cpu.baseline(arch) else blk: { - const cpu_model = try arch.parseCpuModel(cpu_name); - - var set = cpu_model.features; - while (index < args.cpu_features.len) { - const op = args.cpu_features[index]; - index += 1; - const start = index; - while (index < args.cpu_features.len and - args.cpu_features[index] != '+' and - args.cpu_features[index] != '-') - { - index += 1; - } - const feature_name = args.cpu_features[start..index]; - for (all_features) |feature, feat_index_usize| { - const feat_index = @intCast(Cpu.Feature.Set.Index, feat_index_usize); - if (mem.eql(u8, feature_name, feature.name)) { - switch (op) { - '+' => set.addFeature(feat_index), - '-' => set.removeFeature(feat_index), - else => unreachable, - } - break; - } - } else { - diags.unknown_feature_name = feature_name; - return error.UnknownCpuFeature; - } - } - set.populateDependencies(all_features); - break :blk .{ - .arch = arch, - .model = cpu_model, - .features = set, - }; - }; - var cross = Cross{ - .cpu = cpu, - .os = os, - .abi = abi, - }; - return Target{ .Cross = cross }; } - pub fn oFileExt(self: Target) []const u8 { - return switch (self.getAbi()) { - .msvc => ".obj", - else => ".o", - }; + pub fn exeFileExt(self: Target) [:0]const u8 { + return exeFileExtSimple(self.cpu.arch, self.os.tag); } - pub fn exeFileExt(self: Target) []const u8 { - if (self.isWindows()) { - return ".exe"; - } else if (self.isUefi()) { - return ".efi"; - } else if (self.isWasm()) { + pub fn staticLibSuffix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ".wasm"; - } else { - return ""; } - } - - pub fn staticLibSuffix(self: Target) []const u8 { - if (self.isWasm()) { - return ".wasm"; - } - switch (self.getAbi()) { + switch (abi) { .msvc => return ".lib", else => return ".a", } } - pub fn dynamicLibSuffix(self: Target) []const u8 { - if (self.isDarwin()) { - return ".dylib"; - } - switch (self.getOs()) { - .windows => return ".dll", - else => return ".so", - } + pub fn staticLibSuffix(self: Target) [:0]const u8 { + return staticLibSuffix_cpu_arch_abi(self.cpu.arch, self.abi); + } + + pub fn dynamicLibSuffix(self: Target) [:0]const u8 { + return self.os.tag.dynamicLibSuffix(); } - pub fn libPrefix(self: Target) []const u8 { - if (self.isWasm()) { + pub fn libPrefix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ""; } - switch (self.getAbi()) { + switch (abi) { .msvc => return "", else => return "lib", } } - pub fn getOs(self: Target) Os { - return switch (self) { - .Native => builtin.os, - .Cross => |t| t.os, - }; - } - - pub fn getCpu(self: Target) Cpu { - return switch (self) { - .Native => builtin.cpu, - .Cross => |cross| cross.cpu, - }; - } - - pub fn getArch(self: Target) Cpu.Arch { - return self.getCpu().arch; - } - - pub fn getAbi(self: Target) Abi { - switch (self) { - .Native => return builtin.abi, - .Cross => |t| return t.abi, - } + pub fn libPrefix(self: Target) [:0]const u8 { + return libPrefix_cpu_arch_abi(self.cpu.arch, self.abi); } pub fn getObjectFormat(self: Target) ObjectFormat { - switch (self) { - .Native => return @import("builtin").object_format, - .Cross => blk: { - if (self.isWindows() or self.isUefi()) { - return .coff; - } else if (self.isDarwin()) { - return .macho; - } - if (self.isWasm()) { - return .wasm; - } - return .elf; - }, + if (self.os.tag == .windows or self.os.tag == .uefi) { + return .coff; + } else if (self.isDarwin()) { + return .macho; + } + if (self.cpu.arch.isWasm()) { + return .wasm; } + return .elf; } pub fn isMinGW(self: Target) bool { - return self.isWindows() and self.isGnu(); + return self.os.tag == .windows and self.isGnu(); } pub fn isGnu(self: Target) bool { - return switch (self.getAbi()) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, - }; + return self.abi.isGnu(); } pub fn isMusl(self: Target) bool { - return switch (self.getAbi()) { - .musl, .musleabi, .musleabihf => true, - else => false, - }; - } - - pub fn isDarwin(self: Target) bool { - return switch (self.getOs()) { - .ios, .macosx, .watchos, .tvos => true, - else => false, - }; - } - - pub fn isWindows(self: Target) bool { - return switch (self.getOs()) { - .windows => true, - else => false, - }; - } - - pub fn isLinux(self: Target) bool { - return switch (self.getOs()) { - .linux => true, - else => false, - }; + return self.abi.isMusl(); } pub fn isAndroid(self: Target) bool { - return switch (self.getAbi()) { + return switch (self.abi) { .android => true, else => false, }; } - pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.getOs()) { - .dragonfly => true, - else => false, - }; - } - - pub fn isUefi(self: Target) bool { - return switch (self.getOs()) { - .uefi => true, - else => false, - }; - } - pub fn isWasm(self: Target) bool { - return switch (self.getArch()) { - .wasm32, .wasm64 => true, - else => false, - }; - } - - pub fn isFreeBSD(self: Target) bool { - return switch (self.getOs()) { - .freebsd => true, - else => false, - }; + return self.cpu.arch.isWasm(); } - pub fn isNetBSD(self: Target) bool { - return switch (self.getOs()) { - .netbsd => true, - else => false, - }; + pub fn isDarwin(self: Target) bool { + return self.os.tag.isDarwin(); } - pub fn wantSharedLibSymLinks(self: Target) bool { - return !self.isWindows(); + pub fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool { + return os_tag == .linux and abi.isGnu(); } - pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.isFreeBSD() or self.isNetBSD(); - } - - pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.getArch()) { - .avr, - .msp430, - => return 16, - - .arc, - .arm, - .armeb, - .hexagon, - .le32, - .mips, - .mipsel, - .powerpc, - .r600, - .riscv32, - .sparc, - .sparcel, - .tce, - .tcele, - .thumb, - .thumbeb, - .i386, - .xcore, - .nvptx, - .amdil, - .hsail, - .spir, - .kalimba, - .shave, - .lanai, - .wasm32, - .renderscript32, - .aarch64_32, - => return 32, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc64, - .powerpc64le, - .riscv64, - .x86_64, - .nvptx64, - .le64, - .amdil64, - .hsail64, - .spir64, - .wasm64, - .renderscript64, - .amdgcn, - .bpfel, - .bpfeb, - .sparcv9, - .s390x, - => return 64, - } + pub fn isGnuLibC(self: Target) bool { + return isGnuLibC_os_tag_abi(self.os.tag, self.abi); } pub fn supportsNewStackCall(self: Target) bool { - return !self.isWasm(); - } - - pub const Executor = union(enum) { - native, - qemu: []const u8, - wine: []const u8, - wasmtime: []const u8, - unavailable, - }; - - pub fn getExternalExecutor(self: Target) Executor { - 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 (self.getOs() == builtin.os) { - return switch (self.getArch()) { - .aarch64 => Executor{ .qemu = "qemu-aarch64" }, - .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, - .arm => Executor{ .qemu = "qemu-arm" }, - .armeb => Executor{ .qemu = "qemu-armeb" }, - .i386 => Executor{ .qemu = "qemu-i386" }, - .mips => Executor{ .qemu = "qemu-mips" }, - .mipsel => Executor{ .qemu = "qemu-mipsel" }, - .mips64 => Executor{ .qemu = "qemu-mips64" }, - .mips64el => Executor{ .qemu = "qemu-mips64el" }, - .powerpc => Executor{ .qemu = "qemu-ppc" }, - .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, - .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, - .riscv32 => Executor{ .qemu = "qemu-riscv32" }, - .riscv64 => Executor{ .qemu = "qemu-riscv64" }, - .s390x => Executor{ .qemu = "qemu-s390x" }, - .sparc => Executor{ .qemu = "qemu-sparc" }, - .x86_64 => Executor{ .qemu = "qemu-x86_64" }, - else => return .unavailable, - }; - } - - if (self.isWindows()) { - switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wine = "wine" }, - 64 => return Executor{ .wine = "wine64" }, - else => return .unavailable, - } - } - - if (self.getOs() == .wasi) { - switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wasmtime = "wasmtime" }, - else => return .unavailable, - } - } - - return .unavailable; + return !self.cpu.arch.isWasm(); } pub const FloatAbi = enum { @@ -1129,7 +1072,7 @@ pub const Target = union(enum) { }; pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getAbi()) { + return switch (self.abi) { .gnueabihf, .eabihf, .musleabihf, @@ -1139,13 +1082,10 @@ pub const Target = union(enum) { } pub fn hasDynamicLinker(self: Target) bool { - switch (self.getArch()) { - .wasm32, - .wasm64, - => return false, - else => {}, + if (self.cpu.arch.isWasm()) { + return false; } - switch (self.getOs()) { + switch (self.os.tag) { .freestanding, .ios, .tvos, @@ -1160,65 +1100,95 @@ pub const Target = union(enum) { } } - /// Caller owns returned memory. - pub fn getStandardDynamicLinkerPath( - self: Target, - allocator: *mem.Allocator, - ) error{ - OutOfMemory, - UnknownDynamicLinkerPath, - TargetHasNoDynamicLinker, - }![:0]u8 { - const a = allocator; - if (self.isAndroid()) { - return mem.dupeZ(a, u8, if (self.getArchPtrBitWidth() == 64) - "/system/bin/linker64" - else - "/system/bin/linker"); + pub const DynamicLinker = struct { + /// Contains the memory used to store the dynamic linker path. This field should + /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator. + buffer: [255]u8 = undefined, + + /// Used to construct the dynamic linker path. This field should not be used + /// directly. See `get` and `set`. + max_byte: ?u8 = null, + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn init(dl_or_null: ?[]const u8) DynamicLinker { + var result: DynamicLinker = undefined; + result.set(dl_or_null); + return result; } - if (self.isMusl()) { - var result = try std.Buffer.init(allocator, "/lib/ld-musl-"); - defer result.deinit(); - - var is_arm = false; - switch (self.getArch()) { - .arm, .thumb => { - try result.append("arm"); - is_arm = true; - }, - .armeb, .thumbeb => { - try result.append("armeb"); - is_arm = true; - }, - else => |arch| try result.append(@tagName(arch)), + /// The returned memory has the same lifetime as the `DynamicLinker`. + pub fn get(self: *const DynamicLinker) ?[]const u8 { + const m: usize = self.max_byte orelse return null; + return self.buffer[0 .. m + 1]; + } + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void { + if (dl_or_null) |dl| { + mem.copy(u8, &self.buffer, dl); + self.max_byte = @intCast(u8, dl.len - 1); + } else { + self.max_byte = null; + } + } + }; + + /// The result will be a byte index *pointing at the final byte*. In other words, length minus one. + /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. + pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { + var result: DynamicLinker = .{}; + const S = struct { + fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker { + r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); + return r.*; } - if (is_arm and self.getFloatAbi() == .hard) { - try result.append("hf"); + fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { + mem.copy(u8, &r.buffer, s); + r.max_byte = @intCast(u8, s.len - 1); + return r.*; } - try result.append(".so.1"); - return result.toOwnedSlice(); + }; + const print = S.print; + const copy = S.copy; + + if (self.isAndroid()) { + const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; + return print(&result, "/system/bin/linker{}", .{suffix}); } - switch (self.getOs()) { - .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), - .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), - .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), - .linux => switch (self.getArch()) { + if (self.isMusl()) { + const is_arm = switch (self.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => true, + else => false, + }; + const arch_part = switch (self.cpu.arch) { + .arm, .thumb => "arm", + .armeb, .thumbeb => "armeb", + else => |arch| @tagName(arch), + }; + const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else ""; + return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); + } + + switch (self.os.tag) { + .freebsd => return copy(&result, "/libexec/ld-elf.so.1"), + .netbsd => return copy(&result, "/libexec/ld.elf_so"), + .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"), + .linux => switch (self.cpu.arch) { .i386, .sparc, .sparcel, - => return mem.dupeZ(a, u8, "/lib/ld-linux.so.2"), + => return copy(&result, "/lib/ld-linux.so.2"), - .aarch64 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64.so.1"), - .aarch64_be => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_be.so.1"), - .aarch64_32 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_32.so.1"), + .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"), .arm, .armeb, .thumb, .thumbeb, - => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) { + => return copy(&result, switch (self.getFloatAbi()) { .hard => "/lib/ld-linux-armhf.so.3", else => "/lib/ld-linux.so.3", }), @@ -1227,28 +1197,43 @@ pub const Target = union(enum) { .mipsel, .mips64, .mips64el, - => return error.UnknownDynamicLinkerPath, + => { + const lib_suffix = switch (self.abi) { + .gnuabin32, .gnux32 => "32", + .gnuabi64 => "64", + else => "", + }; + const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); + const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; + return print(&result, "/lib{}/{}", .{ lib_suffix, loader }); + }, - .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"), - .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"), - .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"), - .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"), - .x86_64 => return mem.dupeZ(a, u8, switch (self.getAbi()) { + .powerpc => return copy(&result, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"), + .s390x => return copy(&result, "/lib64/ld64.so.1"), + .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"), + .x86_64 => return copy(&result, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", }), - .riscv32 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv32-ilp32.so.1"), - .riscv64 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv64-lp64.so.1"), + .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"), + // Architectures in this list have been verified as not having a standard + // dynamic linker path. .wasm32, .wasm64, - => return error.TargetHasNoDynamicLinker, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + => return result, + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. .arc, .avr, - .bpfel, - .bpfeb, .hexagon, .msp430, .r600, @@ -1256,8 +1241,6 @@ pub const Target = union(enum) { .tce, .tcele, .xcore, - .nvptx, - .nvptx64, .le32, .le64, .amdil, @@ -1271,9 +1254,11 @@ pub const Target = union(enum) { .lanai, .renderscript32, .renderscript64, - => return error.UnknownDynamicLinkerPath, + => return result, }, + // Operating systems in this list have been verified as not having a standard + // dynamic linker path. .freestanding, .ios, .tvos, @@ -1282,40 +1267,36 @@ pub const Target = union(enum) { .uefi, .windows, .emscripten, + .wasi, .other, - => return error.TargetHasNoDynamicLinker, - - else => return error.UnknownDynamicLinkerPath, + => return result, + + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .openbsd, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + => return result, } } }; - -test "Target.parse" { - { - const target = (try Target.parse(.{ - .arch_os_abi = "x86_64-linux-gnu", - .cpu_features = "x86_64-sse-sse2-avx-cx8", - })).Cross; - - std.testing.expect(target.os == .linux); - std.testing.expect(target.abi == .gnu); - 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, .avx)); - std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); - std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); - std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); - } - { - const target = (try Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - })).Cross; - - std.testing.expect(target.os == .linux); - std.testing.expect(target.abi == .musleabihf); - std.testing.expect(target.cpu.arch == .arm); - std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); - std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); - } -} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 348f651a88..398a71ff37 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -1,5 +1,3 @@ -const builtin = @import("builtin"); -const TypeId = builtin.TypeId; const std = @import("std.zig"); pub const LeakCountAllocator = @import("testing/leak_count_allocator.zig").LeakCountAllocator; @@ -65,16 +63,16 @@ pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void { .Pointer => |pointer| { switch (pointer.size) { - builtin.TypeInfo.Pointer.Size.One, - builtin.TypeInfo.Pointer.Size.Many, - builtin.TypeInfo.Pointer.Size.C, + .One, + .Many, + .C, => { if (actual != expected) { std.debug.panic("expected {*}, found {*}", .{ expected, actual }); } }, - builtin.TypeInfo.Pointer.Size.Slice => { + .Slice => { if (actual.ptr != expected.ptr) { std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); } diff --git a/lib/std/thread.zig b/lib/std/thread.zig index fcc71ae5a5..55db9d1733 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -9,14 +9,14 @@ const assert = std.debug.assert; pub const Thread = struct { 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. /// May be an integer or a pointer depending on the platform. /// On Linux and POSIX, this is the same as Id. pub const Handle = if (use_pthreads) c.pthread_t - else switch (builtin.os) { + else switch (builtin.os.tag) { .linux => i32, .windows => windows.HANDLE, else => void, @@ -25,7 +25,7 @@ pub const Thread = struct { /// Represents a unique ID per thread. /// May be an integer or pointer depending on the platform. /// 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, else => Handle, }; @@ -35,7 +35,7 @@ pub const Thread = struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, } - else switch (builtin.os) { + else switch (builtin.os.tag) { .linux => struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, @@ -55,7 +55,7 @@ pub const Thread = struct { if (use_pthreads) { return c.pthread_self(); } else - return switch (builtin.os) { + return switch (builtin.os.tag) { .linux => os.linux.gettid(), .windows => windows.kernel32.GetCurrentThreadId(), else => @compileError("Unsupported OS"), @@ -83,7 +83,7 @@ pub const Thread = struct { else => unreachable, } os.munmap(self.data.memory); - } else switch (builtin.os) { + } else switch (builtin.os.tag) { .linux => { while (true) { const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); @@ -150,7 +150,7 @@ pub const Thread = struct { const Context = @TypeOf(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 OuterContext = struct { thread: Thread, @@ -309,7 +309,7 @@ pub const Thread = struct { os.EINVAL => unreachable, 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 | os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_DETACHED; @@ -369,11 +369,11 @@ pub const Thread = struct { }; pub fn cpuCount() CpuCountError!usize { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const cpu_set = try os.sched_getaffinity(0); 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; windows.kernel32.GetSystemInfo(&system_info); return @intCast(usize, system_info.dwNumberOfProcessors); diff --git a/lib/std/time.zig b/lib/std/time.zig index 63d3ecce18..4112fb7bda 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std.zig"); +const builtin = std.builtin; const assert = std.debug.assert; const testing = std.testing; const os = std.os; @@ -7,10 +7,12 @@ const math = std.math; 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. /// TODO integrate with evented I/O pub fn sleep(nanoseconds: u64) void { - if (builtin.os == .windows) { + if (is_windows) { const ns_per_ms = ns_per_s / ms_per_s; 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); @@ -31,7 +33,7 @@ pub fn timestamp() u64 { /// Get the posix timestamp, UTC, in milliseconds /// TODO audit this function. is it possible to return an error? pub fn milliTimestamp() u64 { - if (builtin.os == .windows) { + if (is_windows) { //FileTime has a granularity of 100 nanoseconds // and uses the NTFS/Windows epoch var ft: os.windows.FILETIME = undefined; @@ -42,7 +44,7 @@ pub fn milliTimestamp() u64 { const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 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; // TODO: Verify that precision is ignored @@ -102,7 +104,7 @@ pub const Timer = struct { ///if we used resolution's value when performing the /// performance counter calc on windows/darwin, it would /// be less precise - frequency: switch (builtin.os) { + frequency: switch (builtin.os.tag) { .windows => u64, .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data, else => void, @@ -127,7 +129,7 @@ pub const Timer = struct { pub fn start() Error!Timer { var self: Timer = undefined; - if (builtin.os == .windows) { + if (is_windows) { self.frequency = os.windows.QueryPerformanceFrequency(); self.resolution = @divFloor(ns_per_s, self.frequency); self.start_time = os.windows.QueryPerformanceCounter(); @@ -172,7 +174,7 @@ pub const Timer = struct { } fn clockNative() u64 { - if (builtin.os == .windows) { + if (is_windows) { return os.windows.QueryPerformanceCounter(); } if (comptime std.Target.current.isDarwin()) { @@ -184,7 +186,7 @@ pub const Timer = struct { } fn nativeDurationToNanos(self: Timer, duration: u64) u64 { - if (builtin.os == .windows) { + if (is_windows) { return @divFloor(duration * ns_per_s, self.frequency); } if (comptime std.Target.current.isDarwin()) { diff --git a/lib/std/zig.zig b/lib/std/zig.zig index d76ed9dfd2..81f34b09c9 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -6,11 +6,8 @@ pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStri pub const render = @import("zig/render.zig").render; pub const ast = @import("zig/ast.zig"); pub const system = @import("zig/system.zig"); +pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; -test "std.zig tests" { - _ = @import("zig/ast.zig"); - _ = @import("zig/parse.zig"); - _ = @import("zig/render.zig"); - _ = @import("zig/tokenizer.zig"); - _ = @import("zig/parse_string_literal.zig"); +test "" { + @import("std").meta.refAllDecls(@This()); } diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig new file mode 100644 index 0000000000..08a8d21fad --- /dev/null +++ b/lib/std/zig/cross_target.zig @@ -0,0 +1,835 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const Target = std.Target; +const mem = std.mem; + +/// Contains all the same data as `Target`, additionally introducing the concept of "the native target". +/// The purpose of this abstraction is to provide meaningful and unsurprising defaults. +/// This struct does reference any resources and it is copyable. +pub const CrossTarget = struct { + /// `null` means native. + cpu_arch: ?Target.Cpu.Arch = null, + + cpu_model: CpuModel = CpuModel.determined_by_cpu_arch, + + /// Sparse set of CPU features to add to the set from `cpu_model`. + cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, + + /// Sparse set of CPU features to remove from the set from `cpu_model`. + cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, + + /// `null` means native. + os_tag: ?Target.Os.Tag = null, + + /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native) + /// then `null` for this field means native. + os_version_min: ?OsVersion = null, + + /// When cross compiling, `null` means default (latest known OS version). + /// When `os_tag` is native, `null` means equal to the native OS version. + os_version_max: ?OsVersion = null, + + /// `null` means default when cross compiling, or native when os_tag is native. + /// If `isGnuLibC()` is `false`, this must be `null` and is ignored. + glibc_version: ?SemVer = null, + + /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. + abi: ?Target.Abi = null, + + /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path + /// based on the `os_tag`. + dynamic_linker: DynamicLinker = DynamicLinker{}, + + pub const CpuModel = union(enum) { + /// Always native + native, + + /// Always baseline + baseline, + + /// If CPU Architecture is native, then the CPU model will be native. Otherwise, + /// it will be baseline. + determined_by_cpu_arch, + + explicit: *const Target.Cpu.Model, + }; + + pub const OsVersion = union(enum) { + none: void, + semver: SemVer, + windows: Target.Os.WindowsVersion, + }; + + pub const SemVer = std.builtin.Version; + + pub const DynamicLinker = Target.DynamicLinker; + + pub fn fromTarget(target: Target) CrossTarget { + var result: CrossTarget = .{ + .cpu_arch = target.cpu.arch, + .cpu_model = .{ .explicit = target.cpu.model }, + .os_tag = target.os.tag, + .os_version_min = undefined, + .os_version_max = undefined, + .abi = target.abi, + .glibc_version = if (target.isGnuLibC()) + target.os.version_range.linux.glibc + else + null, + }; + result.updateOsVersionRange(target.os); + + const all_features = target.cpu.arch.allFeaturesList(); + var cpu_model_set = target.cpu.model.features; + cpu_model_set.populateDependencies(all_features); + { + // The "add" set is the full set with the CPU Model set removed. + const add_set = &result.cpu_features_add; + add_set.* = target.cpu.features; + add_set.removeFeatureSet(cpu_model_set); + } + { + // The "sub" set is the features that are on in CPU Model set and off in the full set. + const sub_set = &result.cpu_features_sub; + sub_set.* = cpu_model_set; + sub_set.removeFeatureSet(target.cpu.features); + } + return result; + } + + fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void { + switch (os.tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => { + self.os_version_min = .{ .none = {} }; + self.os_version_max = .{ .none = {} }; + }, + + .freebsd, + .macosx, + .ios, + .netbsd, + .openbsd, + .tvos, + .watchos, + => { + self.os_version_min = .{ .semver = os.version_range.semver.min }; + self.os_version_max = .{ .semver = os.version_range.semver.max }; + }, + + .linux => { + self.os_version_min = .{ .semver = os.version_range.linux.range.min }; + self.os_version_max = .{ .semver = os.version_range.linux.range.max }; + }, + + .windows => { + self.os_version_min = .{ .windows = os.version_range.windows.min }; + self.os_version_max = .{ .windows = os.version_range.windows.max }; + }, + } + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn toTarget(self: CrossTarget) Target { + return .{ + .cpu = self.getCpu(), + .os = self.getOs(), + .abi = self.getAbi(), + }; + } + + pub const ParseOptions = struct { + /// This is sometimes called a "triple". It looks roughly like this: + /// riscv64-linux-musl + /// The fields are, respectively: + /// * CPU Architecture + /// * Operating System (and optional version range) + /// * C ABI (optional, with optional glibc version) + /// The string "native" can be used for CPU architecture as well as Operating System. + /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted. + arch_os_abi: []const u8 = "native", + + /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" + /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features + /// to remove from the set. + /// The following special strings are recognized for CPU Model name: + /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set + /// of features that is expected to be supported on most available hardware. + /// * "native" - The native CPU model is to be detected when compiling. + /// If this field is not provided (`null`), then the value will depend on the + /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline". + cpu_features: ?[]const u8 = null, + + /// Absolute path to dynamic linker, to override the default, which is either a natively + /// detected path, or a standard path. + dynamic_linker: ?[]const u8 = null, + + /// If this is provided, the function will populate some information about parsing failures, + /// so that user-friendly error messages can be delivered. + diagnostics: ?*Diagnostics = null, + + pub const Diagnostics = struct { + /// If the architecture was determined, this will be populated. + arch: ?Target.Cpu.Arch = null, + + /// If the OS name was determined, this will be populated. + os_name: ?[]const u8 = null, + + /// If the OS tag was determined, this will be populated. + os_tag: ?Target.Os.Tag = null, + + /// If the ABI was determined, this will be populated. + abi: ?Target.Abi = null, + + /// If the CPU name was determined, this will be populated. + cpu_name: ?[]const u8 = null, + + /// If error.UnknownCpuFeature is returned, this will be populated. + unknown_feature_name: ?[]const u8 = null, + }; + }; + + pub fn parse(args: ParseOptions) !CrossTarget { + var dummy_diags: ParseOptions.Diagnostics = undefined; + const diags = args.diagnostics orelse &dummy_diags; + + var result: CrossTarget = .{ + .dynamic_linker = DynamicLinker.init(args.dynamic_linker), + }; + + var it = mem.separate(args.arch_os_abi, "-"); + const arch_name = it.next().?; + const arch_is_native = mem.eql(u8, arch_name, "native"); + if (!arch_is_native) { + result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse + return error.UnknownArchitecture; + } + const arch = result.getCpuArch(); + diags.arch = arch; + + if (it.next()) |os_text| { + try parseOs(&result, diags, os_text); + } else if (!arch_is_native) { + return error.MissingOperatingSystem; + } + + const opt_abi_text = it.next(); + if (opt_abi_text) |abi_text| { + var abi_it = mem.separate(abi_text, "."); + const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse + return error.UnknownApplicationBinaryInterface; + result.abi = abi; + diags.abi = abi; + + const abi_ver_text = abi_it.rest(); + if (abi_it.next() != null) { + if (result.isGnuLibC()) { + result.glibc_version = SemVer.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; + } + } + } + + if (it.next() != null) return error.UnexpectedExtraField; + + if (args.cpu_features) |cpu_features| { + const all_features = arch.allFeaturesList(); + var index: usize = 0; + while (index < cpu_features.len and + cpu_features[index] != '+' and + cpu_features[index] != '-') + { + index += 1; + } + const cpu_name = cpu_features[0..index]; + diags.cpu_name = cpu_name; + + const add_set = &result.cpu_features_add; + const sub_set = &result.cpu_features_sub; + if (mem.eql(u8, cpu_name, "native")) { + result.cpu_model = .native; + } else if (mem.eql(u8, cpu_name, "baseline")) { + result.cpu_model = .baseline; + } else { + result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) }; + } + + while (index < cpu_features.len) { + const op = cpu_features[index]; + const set = switch (op) { + '+' => add_set, + '-' => sub_set, + else => unreachable, + }; + index += 1; + const start = index; + while (index < cpu_features.len and + cpu_features[index] != '+' and + cpu_features[index] != '-') + { + index += 1; + } + const feature_name = cpu_features[start..index]; + for (all_features) |feature, feat_index_usize| { + const feat_index = @intCast(Target.Cpu.Feature.Set.Index, feat_index_usize); + if (mem.eql(u8, feature_name, feature.name)) { + set.addFeature(feat_index); + break; + } + } else { + diags.unknown_feature_name = feature_name; + return error.UnknownCpuFeature; + } + } + } + + return result; + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn getCpu(self: CrossTarget) Target.Cpu { + switch (self.cpu_model) { + .native => { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.cpu; + }, + .baseline => { + var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); + self.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + }, + .determined_by_cpu_arch => if (self.cpu_arch == null) { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.cpu; + } else { + var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); + self.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + }, + .explicit => |model| { + var adjusted_model = model.toCpu(self.getCpuArch()); + self.updateCpuFeatures(&adjusted_model.features); + return adjusted_model; + }, + } + } + + pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch { + return self.cpu_arch orelse Target.current.cpu.arch; + } + + pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model { + if (self.cpu_model) |cpu_model| return cpu_model; + return self.getCpu().model; + } + + pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set { + return self.getCpu().features; + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn getOs(self: CrossTarget) Target.Os { + // `Target.current.os` works when doing `zig build` because Zig generates a build executable using + // native OS version range. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os; + + if (self.os_version_min) |min| switch (min) { + .none => {}, + .semver => |semver| switch (self.getOsTag()) { + .linux => adjusted_os.version_range.linux.range.min = semver, + else => adjusted_os.version_range.semver.min = semver, + }, + .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver, + }; + + if (self.os_version_max) |max| switch (max) { + .none => {}, + .semver => |semver| switch (self.getOsTag()) { + .linux => adjusted_os.version_range.linux.range.max = semver, + else => adjusted_os.version_range.semver.max = semver, + }, + .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver, + }; + + if (self.glibc_version) |glibc| { + assert(self.isGnuLibC()); + adjusted_os.version_range.linux.glibc = glibc; + } + + return adjusted_os; + } + + pub fn getOsTag(self: CrossTarget) Target.Os.Tag { + return self.os_tag orelse Target.current.os.tag; + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn getOsVersionMin(self: CrossTarget) OsVersion { + if (self.os_version_min) |version_min| return version_min; + var tmp: CrossTarget = undefined; + tmp.updateOsVersionRange(self.getOs()); + return tmp.os_version_min.?; + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn getOsVersionMax(self: CrossTarget) OsVersion { + if (self.os_version_max) |version_max| return version_max; + var tmp: CrossTarget = undefined; + tmp.updateOsVersionRange(self.getOs()); + return tmp.os_version_max.?; + } + + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. + pub fn getAbi(self: CrossTarget) Target.Abi { + if (self.abi) |abi| return abi; + + if (self.os_tag == null) { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.abi; + } + + return Target.Abi.default(self.getCpuArch(), self.getOs()); + } + + pub fn isFreeBSD(self: CrossTarget) bool { + return self.getOsTag() == .freebsd; + } + + pub fn isDarwin(self: CrossTarget) bool { + return self.getOsTag().isDarwin(); + } + + pub fn isNetBSD(self: CrossTarget) bool { + return self.getOsTag() == .netbsd; + } + + pub fn isUefi(self: CrossTarget) bool { + return self.getOsTag() == .uefi; + } + + pub fn isDragonFlyBSD(self: CrossTarget) bool { + return self.getOsTag() == .dragonfly; + } + + pub fn isLinux(self: CrossTarget) bool { + return self.getOsTag() == .linux; + } + + pub fn isWindows(self: CrossTarget) bool { + return self.getOsTag() == .windows; + } + + pub fn oFileExt(self: CrossTarget) [:0]const u8 { + return self.getAbi().oFileExt(); + } + + pub fn exeFileExt(self: CrossTarget) [:0]const u8 { + return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); + } + + pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 { + return Target.staticLibSuffix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); + } + + pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 { + return self.getOsTag().dynamicLibSuffix(); + } + + pub fn libPrefix(self: CrossTarget) [:0]const u8 { + return Target.libPrefix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); + } + + pub fn isNative(self: CrossTarget) bool { + return self.cpu_arch == null and + (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and + self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and + self.os_tag == null and self.os_version_min == null and self.os_version_max == null and + self.abi == null and self.dynamic_linker.get() == null and self.glibc_version == null; + } + + pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { + if (self.isNative()) { + return mem.dupeZ(allocator, u8, "native"); + } + + const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native"; + const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; + + var result = try std.Buffer.allocPrint(allocator, "{}-{}", .{ arch_name, os_name }); + defer result.deinit(); + + // The zig target syntax does not allow specifying a max os version with no min, so + // if either are present, we need the min. + if (self.os_version_min != null or self.os_version_max != null) { + switch (self.getOsVersionMin()) { + .none => {}, + .semver => |v| try result.print(".{}", .{v}), + .windows => |v| try result.print(".{}", .{@tagName(v)}), + } + } + if (self.os_version_max) |max| { + switch (max) { + .none => {}, + .semver => |v| try result.print("...{}", .{v}), + .windows => |v| try result.print("...{}", .{@tagName(v)}), + } + } + + if (self.glibc_version) |v| { + try result.print("-{}.{}", .{ @tagName(self.getAbi()), v }); + } else if (self.abi) |abi| { + try result.print("-{}", .{@tagName(abi)}); + } + + return result.toOwnedSlice(); + } + + pub fn allocDescription(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 { + // TODO is there anything else worthy of the description that is not + // already captured in the triple? + return self.zigTriple(allocator); + } + + pub fn linuxTriple(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 { + return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi()); + } + + pub fn wantSharedLibSymLinks(self: CrossTarget) bool { + return self.getOsTag() != .windows; + } + + pub const VcpkgLinkage = std.builtin.LinkMode; + + /// Returned slice must be freed by the caller. + pub fn vcpkgTriplet(self: CrossTarget, allocator: *mem.Allocator, linkage: VcpkgLinkage) ![:0]u8 { + const arch = switch (self.getCpuArch()) { + .i386 => "x86", + .x86_64 => "x64", + + .arm, + .armeb, + .thumb, + .thumbeb, + .aarch64_32, + => "arm", + + .aarch64, + .aarch64_be, + => "arm64", + + else => return error.UnsupportedVcpkgArchitecture, + }; + + const os = switch (self.getOsTag()) { + .windows => "windows", + .linux => "linux", + .macosx => "macos", + else => return error.UnsupportedVcpkgOperatingSystem, + }; + + const static_suffix = switch (linkage) { + .Static => "-static", + .Dynamic => "", + }; + + return std.fmt.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + } + + pub const Executor = union(enum) { + native, + qemu: []const u8, + wine: []const u8, + wasmtime: []const u8, + unavailable, + }; + + /// Note that even a `CrossTarget` which returns `false` for `isNative` could still be natively executed. + /// For example `-target arm-native` running on an aarch64 host. + pub fn getExternalExecutor(self: CrossTarget) Executor { + const cpu_arch = self.getCpuArch(); + const os_tag = self.getOsTag(); + const os_match = os_tag == Target.current.os.tag; + + // If the OS and CPU arch match, the binary can be considered native. + if (os_match and cpu_arch == Target.current.cpu.arch) { + // However, we also need to verify that the dynamic linker path is valid. + // TODO Until that is implemented, we prevent returning `.native` when the OS is non-native. + if (self.os_tag == null) { + return .native; + } + } + + // If the OS matches, we can use QEMU to emulate a foreign architecture. + if (os_match) { + return switch (cpu_arch) { + .aarch64 => Executor{ .qemu = "qemu-aarch64" }, + .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, + .arm => Executor{ .qemu = "qemu-arm" }, + .armeb => Executor{ .qemu = "qemu-armeb" }, + .i386 => Executor{ .qemu = "qemu-i386" }, + .mips => Executor{ .qemu = "qemu-mips" }, + .mipsel => Executor{ .qemu = "qemu-mipsel" }, + .mips64 => Executor{ .qemu = "qemu-mips64" }, + .mips64el => Executor{ .qemu = "qemu-mips64el" }, + .powerpc => Executor{ .qemu = "qemu-ppc" }, + .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, + .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, + .riscv32 => Executor{ .qemu = "qemu-riscv32" }, + .riscv64 => Executor{ .qemu = "qemu-riscv64" }, + .s390x => Executor{ .qemu = "qemu-s390x" }, + .sparc => Executor{ .qemu = "qemu-sparc" }, + .x86_64 => Executor{ .qemu = "qemu-x86_64" }, + else => return .unavailable, + }; + } + + switch (os_tag) { + .windows => switch (cpu_arch.ptrBitWidth()) { + 32 => return Executor{ .wine = "wine" }, + 64 => return Executor{ .wine = "wine64" }, + else => return .unavailable, + }, + .wasi => switch (cpu_arch.ptrBitWidth()) { + 32 => return Executor{ .wasmtime = "wasmtime" }, + else => return .unavailable, + }, + else => return .unavailable, + } + } + + pub fn isGnuLibC(self: CrossTarget) bool { + return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); + } + + pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void { + assert(self.isGnuLibC()); + self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch }; + } + + fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { + set.removeFeatureSet(self.cpu_features_sub); + set.addFeatureSet(self.cpu_features_add); + set.populateDependencies(self.getCpuArch().allFeaturesList()); + set.removeFeatureSet(self.cpu_features_sub); + } + + fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { + var it = mem.separate(text, "."); + const os_name = it.next().?; + diags.os_name = os_name; + const os_is_native = mem.eql(u8, os_name, "native"); + if (!os_is_native) { + result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse + return error.UnknownOperatingSystem; + } + const tag = result.getOsTag(); + diags.os_tag = tag; + + const version_text = it.rest(); + if (it.next() == null) return; + + 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 error.InvalidOperatingSystemVersion, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + .linux, + => { + var range_it = mem.separate(version_text, "..."); + + const min_text = range_it.next().?; + const min_ver = SemVer.parse(min_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + result.os_version_min = .{ .semver = min_ver }; + + const max_text = range_it.next() orelse return; + const max_ver = SemVer.parse(max_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + result.os_version_max = .{ .semver = max_ver }; + }, + + .windows => { + var range_it = mem.separate(version_text, "..."); + + const min_text = range_it.next().?; + const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse + return error.InvalidOperatingSystemVersion; + result.os_version_min = .{ .windows = min_ver }; + + const max_text = range_it.next() orelse return; + const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse + return error.InvalidOperatingSystemVersion; + result.os_version_max = .{ .windows = max_ver }; + }, + } + } +}; + +test "CrossTarget.parse" { + if (Target.current.isGnuLibC()) { + var cross_target = try CrossTarget.parse(.{}); + cross_target.setGnuLibCVersion(2, 1, 1); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux", + .cpu_features = "native", + }); + + std.testing.expect(cross_target.cpu_arch.? == .aarch64); + std.testing.expect(cross_target.cpu_model == .native); + } + { + const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); + + std.testing.expect(cross_target.cpu_arch == null); + std.testing.expect(cross_target.isNative()); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "native", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "x86_64-linux-gnu", + .cpu_features = "x86_64-sse-sse2-avx-cx8", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.abi == .gnu); + 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, .avx)); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.abi == .musleabihf); + std.testing.expect(target.cpu.arch == .arm); + std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); + std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", + .cpu_features = "generic+v8a", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.cpu.arch == .aarch64); + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.os.version_range.linux.range.min.major == 3); + std.testing.expect(target.os.version_range.linux.range.min.minor == 10); + std.testing.expect(target.os.version_range.linux.range.min.patch == 0); + std.testing.expect(target.os.version_range.linux.range.max.major == 4); + std.testing.expect(target.os.version_range.linux.range.max.minor == 4); + std.testing.expect(target.os.version_range.linux.range.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); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text); + } +} diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 3931e362c6..aa8def32a9 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -1,11 +1,15 @@ const std = @import("../std.zig"); +const elf = std.elf; const mem = std.mem; +const fs = std.fs; const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const assert = std.debug.assert; const process = std.process; +const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; -const is_windows = std.Target.current.isWindows(); +const is_windows = Target.current.os.tag == .windows; pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), @@ -77,7 +81,7 @@ pub const NativePaths = struct { } if (!is_windows) { - const triple = try std.Target.current.linuxTriple(allocator); + const triple = try Target.current.linuxTriple(allocator); // TODO: $ ld --verbose | grep SEARCH_DIR // the output contains some paths that end with lib64, maybe include them too? @@ -161,3 +165,609 @@ pub const NativePaths = struct { try array.append(item); } }; + +pub const NativeTargetInfo = struct { + target: Target, + + dynamic_linker: DynamicLinker = DynamicLinker{}, + + pub const DynamicLinker = Target.DynamicLinker; + + pub const DetectError = error{ + OutOfMemory, + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + DeviceBusy, + }; + + /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected + /// natively, which should be standard or default, and which are provided explicitly, this function + /// resolves the native components by detecting the native system, and then resolves standard/default parts + /// relative to that. + /// Any resources this function allocates are released before returning, and so there is no + /// deinitialization method. + /// TODO Remove the Allocator requirement from this function. + pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { + const cpu = switch (cross_target.cpu_model) { + .native => detectNativeCpuAndFeatures(cross_target), + .baseline => baselineCpuAndFeatures(cross_target), + .determined_by_cpu_arch => if (cross_target.cpu_arch == null) + detectNativeCpuAndFeatures(cross_target) + else + baselineCpuAndFeatures(cross_target), + .explicit => |model| blk: { + var adjusted_model = model.toCpu(cross_target.getCpuArch()); + cross_target.updateCpuFeatures(&adjusted_model.features); + break :blk adjusted_model; + }, + }; + + var os = Target.Os.defaultVersionRange(cross_target.getOsTag()); + if (cross_target.os_tag == null) { + switch (Target.current.os.tag) { + .linux => { + const uts = std.os.uname(); + const release = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release)); + if (std.builtin.Version.parse(release)) |ver| { + os.version_range.linux.range.min = ver; + os.version_range.linux.range.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidCharacter => {}, + error.InvalidVersion => {}, + } + }, + .windows => { + // TODO Detect native operating system version. + }, + .macosx => { + // TODO Detect native operating system version. + }, + .freebsd => { + // TODO Detect native operating system version. + }, + else => {}, + } + } + + if (cross_target.os_version_min) |min| switch (min) { + .none => {}, + .semver => |semver| switch (cross_target.getOsTag()) { + .linux => os.version_range.linux.range.min = semver, + else => os.version_range.semver.min = semver, + }, + .windows => |win_ver| os.version_range.windows.min = win_ver, + }; + + if (cross_target.os_version_max) |max| switch (max) { + .none => {}, + .semver => |semver| switch (cross_target.getOsTag()) { + .linux => os.version_range.linux.range.max = semver, + else => os.version_range.semver.max = semver, + }, + .windows => |win_ver| os.version_range.windows.max = win_ver, + }; + + if (cross_target.glibc_version) |glibc| { + assert(cross_target.isGnuLibC()); + os.version_range.linux.glibc = glibc; + } + + return detectAbiAndDynamicLinker(allocator, cpu, os, cross_target); + } + + /// 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. + /// TODO Remove the Allocator requirement from this function. + fn detectAbiAndDynamicLinker( + allocator: *Allocator, + cpu: Target.Cpu, + os: Target.Os, + cross_target: CrossTarget, + ) DetectError!NativeTargetInfo { + const native_target_has_ld = comptime Target.current.hasDynamicLinker(); + const is_linux = Target.current.os.tag == .linux; + const have_all_info = cross_target.dynamic_linker.get() != null and + cross_target.abi != null and (!is_linux or cross_target.abi.?.isGnu()); + const os_is_non_native = cross_target.os_tag != null; + if (!native_target_has_ld or have_all_info or os_is_non_native) { + return defaultAbiAndDynamicLinker(cpu, os, cross_target); + } + // 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 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; + }; + var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; + var ld_info_list_len: usize = 0; + + 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 ld = target.standardDynamicLinkerPath(); + if (ld.get() == null) continue; + + ld_info_list_buffer[ld_info_list_len] = .{ + .ld = ld, + .abi = abi, + }; + ld_info_list_len += 1; + } + const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; + + if (cross_target.dynamic_linker.get()) |explicit_ld| { + const explicit_ld_basename = fs.path.basename(explicit_ld); + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); + } + } + + // 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) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); + 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() and + cross_target.glibc_version == null) + { + 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; + } + } + } + + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os_adjusted, + .abi = cross_target.abi orelse found_ld_info.abi, + }, + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + DynamicLinker.init(found_ld_path) + else + cross_target.dynamic_linker, + }; + return result; + } + + // 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(cpu, os, ld_info_list, cross_target) catch |err| switch (err) { + error.FileSystem, + error.SystemResources, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.DeviceBusy, + => |e| return e, + + error.UnableToReadElfFile, + error.InvalidElfClass, + error.InvalidElfVersion, + error.InvalidElfEndian, + error.InvalidElfFile, + error.InvalidElfMagic, + error.UsrBinEnvNotAvailable, + error.Unexpected, + error.UnexpectedEndOfFile, + error.NameTooLong, + // Finally, we fall back on the standard path. + => defaultAbiAndDynamicLinker(cpu, os, cross_target), + }; + } + + 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, + }; + return glibcVerFromLinkName(link_name); + } + + fn glibcVerFromLinkName(link_name: []const u8) !std.builtin.Version { + // 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( + cpu: Target.Cpu, + os: Target.Os, + ld_info_list: []const LdInfo, + cross_target: CrossTarget, + ) !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; + _ = try preadFull(env_file, &hdr_buf, 0, hdr_buf.len); + 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); + + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os, + .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), + }, + .dynamic_linker = cross_target.dynamic_linker, + }; + var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC + const look_for_ld = cross_target.dynamic_linker.get() == null; + + var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; + + 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 ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); + const ph_read_byte_len = try preadFull(env_file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); + var ph_buf_i: usize = 0; + while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({ + ph_i += 1; + phoff += phentsize; + ph_buf_i += phentsize; + }) { + const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[ph_buf_i])); + const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[ph_buf_i])); + const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); + switch (p_type) { + elf.PT_INTERP => if (look_for_ld) { + const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; + _ = try preadFull(env_file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz); + // PT_INTERP includes a null byte in p_filesz. + const len = p_filesz - 1; + // dynamic_linker.max_byte is "max", not "len". + // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. + result.dynamic_linker.max_byte = @intCast(u8, len - 1); + + // Use it to determine ABI. + const full_ld_path = result.dynamic_linker.buffer[0..len]; + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); + if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { + result.target.abi = ld_info.abi; + break; + } + } + }, + // We only need this for detecting glibc version. + elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC() and + cross_target.glibc_version == null) + { + var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + const dyn_size: u64 = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); + const dyn_num = p_filesz / dyn_size; + var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined; + var dyn_i: usize = 0; + dyn: while (dyn_i < dyn_num) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); + const dyn_read_byte_len = try preadFull( + env_file, + dyn_buf[0 .. dyn_buf.len - dyn_reserve], + dyn_off, + dyn_size, + ); + var dyn_buf_i: usize = 0; + while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({ + dyn_i += 1; + dyn_off += dyn_size; + dyn_buf_i += dyn_size; + }) { + const dyn32 = @ptrCast( + *elf.Elf32_Dyn, + @alignCast(@alignOf(elf.Elf32_Dyn), &dyn_buf[dyn_buf_i]), + ); + const dyn64 = @ptrCast( + *elf.Elf64_Dyn, + @alignCast(@alignOf(elf.Elf64_Dyn), &dyn_buf[dyn_buf_i]), + ); + const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag); + const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val); + if (tag == elf.DT_RUNPATH) { + rpath_offset = val; + break :dyn; + } + } + } + }, + else => continue, + } + } + } + + if (Target.current.os.tag == .linux and result.target.isGnuLibC() and cross_target.glibc_version == null) { + if (rpath_offset) |rpoff| { + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + + var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); + const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); + const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); + + var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; + if (sh_buf.len < shentsize) return error.InvalidElfFile; + + _ = try preadFull(env_file, &sh_buf, str_section_off, shentsize); + const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); + const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); + const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); + const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); + var strtab_buf: [4096:0]u8 = undefined; + const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); + const shstrtab_read_len = try preadFull(env_file, &strtab_buf, shstrtab_off, shstrtab_len); + const shstrtab = strtab_buf[0..shstrtab_read_len]; + + const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); + var sh_i: u16 = 0; + const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); + const sh_read_byte_len = try preadFull( + env_file, + sh_buf[0 .. sh_buf.len - sh_reserve], + shoff, + shentsize, + ); + var sh_buf_i: usize = 0; + while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ + sh_i += 1; + shoff += shentsize; + sh_buf_i += shentsize; + }) { + const sh32 = @ptrCast( + *elf.Elf32_Shdr, + @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), + ); + const sh64 = @ptrCast( + *elf.Elf64_Shdr, + @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), + ); + const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); + // TODO this pointer cast should not be necessary + const sh_name = mem.toSliceConst(u8, @ptrCast([*:0]u8, shstrtab[sh_name_off..].ptr)); + if (mem.eql(u8, sh_name, ".dynstr")) { + break :find_dyn_str .{ + .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), + .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), + }; + } + } + } else null; + + if (dynstr) |ds| { + const strtab_len = std.math.min(ds.size, strtab_buf.len); + const strtab_read_len = try preadFull(env_file, &strtab_buf, ds.offset, shstrtab_len); + const strtab = strtab_buf[0..strtab_read_len]; + // TODO this pointer cast should not be necessary + const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr)); + var it = mem.tokenize(rpath_list, ":"); + while (it.next()) |rpath| { + var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.DeviceBusy => unreachable, + + error.FileNotFound, + error.NotDir, + error.AccessDenied, + error.NoDevice, + => continue, + + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.SystemResources, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + defer dir.close(); + + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlinkatC( + dir.fd, + glibc_so_basename, + &link_buf, + ) catch |err| switch (err) { + error.NameTooLong => unreachable, + + error.AccessDenied, + error.FileNotFound, + error.NotDir, + => continue, + + error.SystemResources, + error.FileSystem, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + result.target.os.version_range.linux.glibc = glibcVerFromLinkName( + link_name, + ) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName, + error.InvalidGnuLibCVersion, + => continue, + }; + break; + } + } + } + } + + return result; + } + + fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize { + var i: u64 = 0; + while (i < min_read_len) { + const len = file.pread(buf[i .. buf.len - i], offset + i) 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, + }; + if (len == 0) return error.UnexpectedEndOfFile; + i += len; + } + return i; + } + + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, cross_target: CrossTarget) !NativeTargetInfo { + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), + }; + return NativeTargetInfo{ + .target = target, + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + target.standardDynamicLinkerPath() + else + cross_target.dynamic_linker, + }; + } + + const LdInfo = struct { + ld: DynamicLinker, + abi: Target.Abi, + }; + + 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; + } + } + } + + fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu { + // TODO Detect native CPU model & features. Until that is implemented we use baseline. + return baselineCpuAndFeatures(cross_target); + } + + fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu { + var adjusted_baseline = Target.Cpu.baseline(cross_target.getCpuArch()); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + } +}; |
