From fba39ff331a84f1a32d076ccbb8b87cd02ea7121 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Feb 2020 01:38:21 -0500 Subject: [PATCH 01/37] restructuring std.Target for OS version ranges, pass 1 --- lib/std/builtin.zig | 32 +++- lib/std/target.zig | 430 ++++++++++++++++++++++++++++++++++---------- src/analyze.cpp | 2 +- test/tests.zig | 30 ++-- 4 files changed, 383 insertions(+), 111 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 5440de4d3b..1bb8fd85ae 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -398,7 +398,37 @@ 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), + }; + } }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/target.zig b/lib/std/target.zig index cf83bb1f7a..84056eae02 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1,30 +1,53 @@ 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 Target = struct { + cpu: Cpu, + os: Os, + abi: Abi, - pub const Os = enum { + /// 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 Os = union(enum) { freestanding, ananas, cloudabi, dragonfly, - freebsd, + freebsd: Version.Range, fuchsia, ios, kfreebsd, - linux, + linux: LinuxVersionRange, lv2, - macosx, - netbsd, - openbsd, + macosx: Version.Range, + netbsd: Version.Range, + openbsd: Version.Range, solaris, - windows, + windows: WindowsVersion.Range, haiku, minix, rtems, @@ -48,14 +71,230 @@ pub const Target = union(enum) { uefi, other, + /// See the documentation for `Os` for an explanation of the default version range. + pub fn defaultVersionRange(tag: @TagType(Os)) Os { + switch (tag) { + .freestanding => return .freestanding, + .ananas => return .ananas, + .cloudabi => return .cloudabi, + .dragonfly => return .dragonfly, + .freebsd => return .{ + .freebsd = Version.Range{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 12, .minor = 1 }, + }, + }, + .fuchsia => return .fuchsia, + .ios => return .ios, + .kfreebsd => return .kfreebsd, + .linux => return .{ + .linux = .{ + .range = .{ + .min = .{ .major = 3, .minor = 16 }, + .max = .{ .major = 5, .minor = 5, .patch = 5 }, + }, + .glibc = .{ .major = 2, .minor = 17 }, + }, + }, + .lv2 => return .lv2, + .macosx => return .{ + .min = .{ .major = 10, .minor = 13 }, + .max = .{ .major = 10, .minor = 15, .patch = 3 }, + }, + .netbsd => return .{ + .min = .{ .major = 8, .minor = 0 }, + .max = .{ .major = 9, .minor = 0 }, + }, + .openbsd => return .{ + .min = .{ .major = 6, .minor = 6 }, + .max = .{ .major = 6, .minor = 6 }, + }, + solaris => return .solaris, + windows => return .{ + .windows = .{ + .min = .win8_1, + .max = .win10_19h1, + }, + }, + haiku => return .haiku, + minix => return .minix, + rtems => return .rtems, + nacl => return .nacl, + cnk => return .cnk, + aix => return .aix, + cuda => return .cuda, + nvcl => return .nvcl, + amdhsa => return .amdhsa, + ps4 => return .ps4, + elfiamcu => return .elfiamcu, + tvos => return .tvos, + watchos => return .watchos, + mesa3d => return .mesa3d, + contiki => return .contiki, + amdpal => return .amdpal, + hermit => return .hermit, + hurd => return .hurd, + wasi => return .wasi, + emscripten => return .emscripten, + uefi => return .uefi, + other => return .other, + } + } + + pub const LinuxVersionRange = struct { + range: Version.Range, + glibc: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + return self.range.includesVersion(ver); + } + }; + + /// Based on NTDDI version constants from + /// 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 fn nameToTag(name: []const u8) ?WindowsVersion { + const info = @typeInfo(WindowsVersion); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, name, field.name)) { + return @field(WindowsVersion, field.name); + } + } + return null; + } + }; + pub fn parse(text: []const u8) !Os { + var it = mem.separate(text, "."); + const os_name = it.next().?; + const tag = nameToTag(os_name) orelse return error.UnknownOperatingSystem; + const version_text = it.rest(); + const S = struct { + fn parseNone(s: []const u8) !void { + if (s.len != 0) return error.InvalidOperatingSystemVersion; + } + fn parseSemVer(s: []const u8, default: Version.Range) !Version.Range { + if (s.len == 0) return default; + var range_it = mem.separate(s, "..."); + + const min_text = range_it.next().?; + const min_ver = Version.parse(min_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + + const max_text = range_it.next() orelse return Version.Range{ + .min = min_ver, + .max = default.max, + }; + const max_ver = Version.parse(max_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + + return Version.Range{ .min = min_ver, .max = max_ver }; + } + fn parseWindows(s: []const u8, default: WindowsVersion.Range) !WindowsVersion.Range { + if (s.len == 0) return default; + var range_it = mem.separate(s, "..."); + + const min_text = range_it.next().?; + const min_ver = WindowsVersion.nameToTag(min_text) orelse + return error.InvalidOperatingSystemVersion; + + const max_text = range_it.next() orelse return WindowsVersion.Range{ + .min = min_ver, + .max = default.max, + }; + const max_ver = WindowsVersion.nameToTag(max_text) orelse + return error.InvalidOperatingSystemVersion; + + return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; + } + }; + const default = defaultVersionRange(tag); + switch (tag) { + .freestanding => return Os{ .freestanding = try S.parseNone(version_text) }, + .ananas => return Os{ .ananas = try S.parseNone(version_text) }, + .cloudabi => return Os{ .cloudabi = try S.parseNone(version_text) }, + .dragonfly => return Os{ .dragonfly = try S.parseNone(version_text) }, + .freebsd => return Os{ .freebsd = try S.parseSemVer(version_text, default.freebsd) }, + .fuchsia => return Os{ .fuchsia = try S.parseNone(version_text) }, + .ios => return Os{ .ios = try S.parseNone(version_text) }, + .kfreebsd => return Os{ .kfreebsd = try S.parseNone(version_text) }, + .linux => return Os{ + .linux = .{ + .range = try S.parseSemVer(version_text, default.linux.range), + .glibc = default.linux.glibc, + }, + }, + .lv2 => return Os{ .lv2 = try S.parseNone(version_text) }, + .macosx => return Os{ .macosx = try S.parseSemVer(version_text, default.macosx) }, + .netbsd => return Os{ .netbsd = try S.parseSemVer(version_text, default.netbsd) }, + .openbsd => return Os{ .openbsd = try S.parseSemVer(version_text, default.openbsd) }, + .solaris => return Os{ .solaris = try S.parseNone(version_text) }, + .windows => return Os{ .windows = try S.parseWindows(version_text, default.windows) }, + .haiku => return Os{ .haiku = try S.parseNone(version_text) }, + .minix => return Os{ .minix = try S.parseNone(version_text) }, + .rtems => return Os{ .rtems = try S.parseNone(version_text) }, + .nacl => return Os{ .nacl = try S.parseNone(version_text) }, + .cnk => return Os{ .cnk = try S.parseNone(version_text) }, + .aix => return Os{ .aix = try S.parseNone(version_text) }, + .cuda => return Os{ .cuda = try S.parseNone(version_text) }, + .nvcl => return Os{ .nvcl = try S.parseNone(version_text) }, + .amdhsa => return Os{ .amdhsa = try S.parseNone(version_text) }, + .ps4 => return Os{ .ps4 = try S.parseNone(version_text) }, + .elfiamcu => return Os{ .elfiamcu = try S.parseNone(version_text) }, + .tvos => return Os{ .tvos = try S.parseNone(version_text) }, + .watchos => return Os{ .watchos = try S.parseNone(version_text) }, + .mesa3d => return Os{ .mesa3d = try S.parseNone(version_text) }, + .contiki => return Os{ .contiki = try S.parseNone(version_text) }, + .amdpal => return Os{ .amdpal = try S.parseNone(version_text) }, + .hermit => return Os{ .hermit = try S.parseNone(version_text) }, + .hurd => return Os{ .hurd = try S.parseNone(version_text) }, + .wasi => return Os{ .wasi = try S.parseNone(version_text) }, + .emscripten => return Os{ .emscripten = try S.parseNone(version_text) }, + .uefi => return Os{ .uefi = try S.parseNone(version_text) }, + .other => return Os{ .other = try S.parseNone(version_text) }, + } + } + + pub fn nameToTag(name: []const u8) ?@TagType(Os) { const info = @typeInfo(Os); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { + inline for (info.Union.fields) |field| { + if (mem.eql(u8, name, field.name)) { return @field(Os, field.name); } } - return error.UnknownOperatingSystem; + return null; } }; @@ -149,14 +388,39 @@ pub const Target = union(enum) { } } - pub fn parse(text: []const u8) !Abi { + pub fn nameToTag(text: []const u8) ?Abi { const info = @typeInfo(Abi); inline for (info.Enum.fields) |field| { if (mem.eql(u8, text, field.name)) { return @field(Abi, field.name); } } - return error.UnknownApplicationBinaryInterface; + return null; + } + + pub fn parse(text: []const u8, os: *Os) !Abi { + var it = mem.separate(text, "."); + const tag = nameToTag(it.next().?) orelse return error.UnknownApplicationBinaryInterface; + const version_text = it.rest(); + if (version_text.len != 0) { + if (@as(@TagType(Os), os.*) == .linux and tag.isGnu()) { + os.linux.glibc = Version.parse(version_text) catch |err| switch (err) { + error.Overflow => return error.InvalidGlibcVersion, + error.InvalidCharacter => return error.InvalidGlibcVersion, + error.InvalidVersion => return error.InvalidGlibcVersion, + }; + } else { + return error.InvalidAbiVersion; + } + } + return tag; + } + + pub fn isGnu(abi: Abi) bool { + return switch (abi) { + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + else => false, + }; } }; @@ -179,12 +443,6 @@ pub const Target = union(enum) { EfiRuntimeDriver, }; - pub const Cross = struct { - cpu: Cpu, - os: Os, - abi: Abi, - }; - pub const Cpu = struct { /// Architecture arch: Arch, @@ -641,20 +899,19 @@ pub const Target = union(enum) { }; 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; + /// TODO add OS version ranges and glibc version pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), + @tagName(self.os), + @tagName(self.abi), }); } @@ -678,7 +935,7 @@ pub const Target = union(enum) { else => return error.VcpkgNoSuchArchitecture, }; - const os = switch (target.getOs()) { + const os = switch (target.os) { .windows => "windows", .linux => "linux", .macosx => "macos", @@ -701,16 +958,16 @@ pub const Target = union(enum) { pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), + @tagName(self.os), + @tagName(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()), + @tagName(self.os), + @tagName(self.abi), }); } @@ -760,11 +1017,11 @@ pub const Target = union(enum) { diags.arch = arch; const os_name = it.next() orelse return error.MissingOperatingSystem; - const os = try Os.parse(os_name); + var os = try Os.parse(os_name); // var because Abi.parse can update linux.glibc version diags.os = os; const abi_name = it.next(); - const abi = if (abi_name) |n| try Abi.parse(n) else Abi.default(arch, os); + const abi = if (abi_name) |n| try Abi.parse(n, &os) else Abi.default(arch, os); diags.abi = abi; if (it.next() != null) return error.UnexpectedExtraField; @@ -817,16 +1074,15 @@ pub const Target = union(enum) { .features = set, }; }; - var cross = Cross{ + return Target{ .cpu = cpu, .os = os, .abi = abi, }; - return Target{ .Cross = cross }; } pub fn oFileExt(self: Target) []const u8 { - return switch (self.getAbi()) { + return switch (self.abi) { .msvc => ".obj", else => ".o", }; @@ -848,7 +1104,7 @@ pub const Target = union(enum) { if (self.isWasm()) { return ".wasm"; } - switch (self.getAbi()) { + switch (self.abi) { .msvc => return ".lib", else => return ".a", } @@ -858,7 +1114,7 @@ pub const Target = union(enum) { if (self.isDarwin()) { return ".dylib"; } - switch (self.getOs()) { + switch (self.os) { .windows => return ".dll", else => return ".so", } @@ -868,52 +1124,41 @@ pub const Target = union(enum) { if (self.isWasm()) { return ""; } - switch (self.getAbi()) { + switch (self.abi) { .msvc => return "", else => return "lib", } } - pub fn getOs(self: Target) Os { - return switch (self) { - .Native => builtin.os, - .Cross => |t| t.os, - }; + /// Deprecated; access the `os` field directly. + pub fn getOs(self: Target) @TagType(Os) { + return self.os; } + /// Deprecated; access the `cpu` field directly. pub fn getCpu(self: Target) Cpu { - return switch (self) { - .Native => builtin.cpu, - .Cross => |cross| cross.cpu, - }; + return self.cpu; + } + + /// Deprecated; access the `abi` field directly. + pub fn getAbi(self: Target) Abi { + return self.abi; } pub fn getArch(self: Target) Cpu.Arch { - return self.getCpu().arch; - } - - pub fn getAbi(self: Target) Abi { - switch (self) { - .Native => return builtin.abi, - .Cross => |t| return t.abi, - } + return self.cpu.arch; } 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.isWindows() or self.isUefi()) { + return .coff; + } else if (self.isDarwin()) { + return .macho; } + if (self.isWasm()) { + return .wasm; + } + return .elf; } pub fn isMinGW(self: Target) bool { @@ -921,56 +1166,53 @@ pub const Target = union(enum) { } 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()) { + return switch (self.abi) { .musl, .musleabi, .musleabihf => true, else => false, }; } pub fn isDarwin(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .ios, .macosx, .watchos, .tvos => true, else => false, }; } pub fn isWindows(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .windows => true, else => false, }; } pub fn isLinux(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .linux => true, else => false, }; } 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()) { + return switch (self.os) { .dragonfly => true, else => false, }; } pub fn isUefi(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .uefi => true, else => false, }; @@ -984,14 +1226,14 @@ pub const Target = union(enum) { } pub fn isFreeBSD(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .freebsd => true, else => false, }; } pub fn isNetBSD(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .netbsd => true, else => false, }; @@ -1081,7 +1323,7 @@ pub const Target = union(enum) { 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) { + if (self.os == builtin.os) { return switch (self.getArch()) { .aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, @@ -1112,7 +1354,7 @@ pub const Target = union(enum) { } } - if (self.getOs() == .wasi) { + if (self.os == .wasi) { switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, @@ -1129,7 +1371,7 @@ pub const Target = union(enum) { }; pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getAbi()) { + return switch (self.abi) { .gnueabihf, .eabihf, .musleabihf, @@ -1145,7 +1387,7 @@ pub const Target = union(enum) { => return false, else => {}, } - switch (self.getOs()) { + switch (self.os) { .freestanding, .ios, .tvos, @@ -1200,7 +1442,7 @@ pub const Target = union(enum) { return result.toOwnedSlice(); } - switch (self.getOs()) { + switch (self.os) { .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"), @@ -1233,7 +1475,7 @@ pub const Target = union(enum) { .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()) { + .x86_64 => return mem.dupeZ(a, u8, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", }), @@ -1292,10 +1534,10 @@ pub const Target = union(enum) { test "Target.parse" { { - const target = (try 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); @@ -1307,10 +1549,10 @@ test "Target.parse" { std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); } { - const target = (try Target.parse(.{ + 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); diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b776da930..ceb232c79d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3963,7 +3963,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; - if (explicit_type != nullptr && explicit_type->id == ZigTypeIdInvalid) { + if (explicit_type != nullptr && type_is_invalid(explicit_type)) { implicit_type = explicit_type; } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, diff --git a/test/tests.zig b/test/tests.zig index e9fdde2e4b..60008a2bc6 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -56,7 +56,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -65,7 +65,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -75,7 +75,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -86,7 +86,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -95,7 +95,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -106,7 +106,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -115,7 +115,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -125,7 +125,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -158,7 +158,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.mipsel), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -167,7 +167,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.mipsel), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -178,7 +178,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .macosx, + .os = Target.Os.defaultVersionRange(.macosx), .abi = .gnu, }, }, @@ -190,7 +190,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -200,7 +200,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -210,7 +210,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -221,7 +221,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, From 4616af0ca459358ffa09ba27f9daa8527a38fd35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 01:52:27 -0500 Subject: [PATCH 02/37] introduce operating system version ranges as part of the target * re-introduce `std.build.Target` which is distinct from `std.Target`. `std.build.Target` wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. * `std.Target.Os` is moved to `std.Target.Os.Tag`. The former is now a struct which has the tag as well as version range information. * `std.elf` gains some more ELF header constants. * `std.Target.parse` gains the ability to parse operating system version ranges as well as glibc version. * Added `std.Target.isGnuLibC()`. * self-hosted dynamic linker detection and glibc version detection. This also adds the improved logic using `/usr/bin/env` rather than invoking the system C compiler to find the dynamic linker when zig is statically linked. Related: #2084 Note: this `/usr/bin/env` code is work-in-progress. * `-target-glibc` CLI option is removed in favor of the new `-target` syntax. Example: `-target x86_64-linux-gnu.2.27` closes #1907 --- lib/std/build.zig | 40 +- lib/std/build/run.zig | 2 +- lib/std/builtin.zig | 4 +- lib/std/c.zig | 25 +- lib/std/c/linux.zig | 2 +- lib/std/child_process.zig | 26 +- lib/std/cstr.zig | 4 +- lib/std/debug.zig | 24 +- lib/std/dynamic_library.zig | 4 +- lib/std/elf.zig | 25 +- lib/std/event/channel.zig | 2 +- lib/std/event/future.zig | 2 +- lib/std/event/lock.zig | 2 +- lib/std/event/loop.zig | 26 +- lib/std/fs.zig | 46 +- lib/std/fs/file.zig | 12 +- lib/std/fs/get_app_data_dir.zig | 2 +- lib/std/fs/path.zig | 38 +- lib/std/fs/watch.zig | 8 +- lib/std/heap.zig | 14 +- lib/std/io.zig | 6 +- lib/std/mutex.zig | 4 +- lib/std/net.zig | 2 +- lib/std/net/test.zig | 4 +- lib/std/os.zig | 136 ++-- lib/std/os/bits.zig | 4 +- lib/std/os/linux.zig | 2 +- lib/std/os/test.zig | 16 +- lib/std/packed_int_array.zig | 4 +- lib/std/process.zig | 22 +- lib/std/reset_event.zig | 6 +- lib/std/special/c.zig | 8 +- lib/std/special/compiler_rt.zig | 13 +- .../special/compiler_rt/extendXfYf2_test.zig | 2 +- lib/std/spinlock.zig | 2 +- lib/std/start.zig | 16 +- lib/std/target.zig | 597 +++++++++--------- lib/std/thread.zig | 20 +- lib/std/time.zig | 18 +- lib/std/zig/system.zig | 330 +++++++++- src-self-hosted/c_int.zig | 2 +- src-self-hosted/clang.zig | 2 +- src-self-hosted/introspect.zig | 10 +- src-self-hosted/libc_installation.zig | 112 +--- src-self-hosted/link.zig | 4 +- src-self-hosted/main.zig | 6 +- src-self-hosted/print_targets.zig | 12 +- src-self-hosted/stage2.zig | 404 ++++++++---- src-self-hosted/util.zig | 22 - src/codegen.cpp | 38 +- src/error.cpp | 2 + src/main.cpp | 29 +- src/stage2.cpp | 8 - src/stage2.h | 10 +- test/stage1/behavior/asm.zig | 7 +- test/stage1/behavior/byteswap.zig | 5 +- .../namespace_depends_on_compile_var.zig | 8 +- test/stage1/behavior/vector.zig | 3 +- test/tests.zig | 2 +- 59 files changed, 1274 insertions(+), 932 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 29837d56d9..6c89e7b9d1 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -971,9 +971,9 @@ 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 buf: [1000]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } @@ -981,11 +981,37 @@ test "builder.findProgram compiles" { /// Deprecated. Use `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; +pub const CrossTarget = std.Target; + +/// Wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. +pub const Target = union(enum) { + Native, + Cross: std.Target, + + pub fn getTarget(self: Target) std.Target { + return switch (self) { + .Native => std.Target.current, + .Cross => |t| t, + }; + } + + pub fn getOs(self: Target) std.Target.Os.Tag { + return self.getTarget().os.tag; + } + + pub fn getCpu(self: Target) std.Target.Cpu { + return self.getTarget().cpu; + } + + pub fn getAbi(self: Target) std.Target.Abi { + return self.getTarget().abi; + } + + pub fn getArch(self: Target) std.Target.Cpu.Arch { + return self.getCpu().arch; + } +}; pub const Pkg = struct { name: []const u8, 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/builtin.zig b/lib/std/builtin.zig index 1bb8fd85ae..3204ce905e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -411,7 +411,7 @@ pub const Version = struct { } }; - pub fn order(lhs: Version, rhs: version) std.math.Order { + pub fn order(lhs: Version, rhs: Version) std.math.Order { if (lhs.major < rhs.major) return .lt; if (lhs.major > rhs.major) return .gt; if (lhs.minor < rhs.minor) return .lt; @@ -504,7 +504,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn root.os.panic(msg, error_return_trace); unreachable; } - switch (os) { + switch (os.tag) { .freestanding => { while (true) { @breakpoint(); diff --git a/lib/std/c.zig b/lib/std/c.zig index f6c0e07dbd..9072d2c7cd 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; } }; }; 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..34f45894fb 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, @@ -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..007a01bb90 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; 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/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/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..6e96413d78 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, @@ -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,7 +3294,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var uts: utsname = undefined; switch (errno(system.uname(&uts))) { 0 => { @@ -3611,7 +3611,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..118b5d9ab4 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) { @@ -615,7 +615,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0] .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..3126e81b9d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -1,11 +1,12 @@ -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_mingw = builtin.os.tag == .windows and is_gnu; comptime { const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; @@ -180,7 +181,7 @@ comptime { @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_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 +251,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 +289,7 @@ comptime { else => {}, } } else { - if (builtin.glibc_version != null) { + if (std.Target.current.isGnuLibC()) { @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); } @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @@ -307,7 +308,7 @@ comptime { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - @import("std").debug.panic("{}", .{msg}); + std.debug.panic("{}", .{msg}); } else { unreachable; } 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/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 84056eae02..c9f8a247fe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -11,143 +11,48 @@ pub const Target = struct { os: Os, abi: Abi, - /// 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 Os = union(enum) { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd: Version.Range, - fuchsia, - ios, - kfreebsd, - linux: LinuxVersionRange, - lv2, - macosx: Version.Range, - netbsd: Version.Range, - openbsd: Version.Range, - solaris, - windows: WindowsVersion.Range, - haiku, - minix, - rtems, - nacl, - cnk, - aix, - cuda, - nvcl, - amdhsa, - ps4, - elfiamcu, - tvos, - watchos, - mesa3d, - contiki, - amdpal, - hermit, - hurd, - wasi, - emscripten, - uefi, - other, + pub const Os = struct { + tag: Tag, + version_range: VersionRange, - /// See the documentation for `Os` for an explanation of the default version range. - pub fn defaultVersionRange(tag: @TagType(Os)) Os { - switch (tag) { - .freestanding => return .freestanding, - .ananas => return .ananas, - .cloudabi => return .cloudabi, - .dragonfly => return .dragonfly, - .freebsd => return .{ - .freebsd = Version.Range{ - .min = .{ .major = 12, .minor = 0 }, - .max = .{ .major = 12, .minor = 1 }, - }, - }, - .fuchsia => return .fuchsia, - .ios => return .ios, - .kfreebsd => return .kfreebsd, - .linux => return .{ - .linux = .{ - .range = .{ - .min = .{ .major = 3, .minor = 16 }, - .max = .{ .major = 5, .minor = 5, .patch = 5 }, - }, - .glibc = .{ .major = 2, .minor = 17 }, - }, - }, - .lv2 => return .lv2, - .macosx => return .{ - .min = .{ .major = 10, .minor = 13 }, - .max = .{ .major = 10, .minor = 15, .patch = 3 }, - }, - .netbsd => return .{ - .min = .{ .major = 8, .minor = 0 }, - .max = .{ .major = 9, .minor = 0 }, - }, - .openbsd => return .{ - .min = .{ .major = 6, .minor = 6 }, - .max = .{ .major = 6, .minor = 6 }, - }, - solaris => return .solaris, - windows => return .{ - .windows = .{ - .min = .win8_1, - .max = .win10_19h1, - }, - }, - haiku => return .haiku, - minix => return .minix, - rtems => return .rtems, - nacl => return .nacl, - cnk => return .cnk, - aix => return .aix, - cuda => return .cuda, - nvcl => return .nvcl, - amdhsa => return .amdhsa, - ps4 => return .ps4, - elfiamcu => return .elfiamcu, - tvos => return .tvos, - watchos => return .watchos, - mesa3d => return .mesa3d, - contiki => return .contiki, - amdpal => return .amdpal, - hermit => return .hermit, - hurd => return .hurd, - wasi => return .wasi, - emscripten => return .emscripten, - uefi => return .uefi, - other => return .other, - } - } - - pub const LinuxVersionRange = struct { - range: Version.Range, - glibc: Version, - - pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { - return self.range.includesVersion(ver); - } + 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, }; /// Based on NTDDI version constants from @@ -178,29 +83,137 @@ pub const Target = struct { return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max); } }; + }; - pub fn nameToTag(name: []const u8) ?WindowsVersion { - const info = @typeInfo(WindowsVersion); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, name, field.name)) { - return @field(WindowsVersion, field.name); - } + pub const LinuxVersionRange = struct { + range: Version.Range, + glibc: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + return self.range.includesVersion(ver); + } + }; + + /// The version ranges here represent the minimum OS version to be supported + /// and the maximum OS version to be supported. The default values represent + /// the range that the Zig Standard Library bases its abstractions on. + /// + /// The minimum version of the range is the main setting to tweak for a target. + /// Usually, the maximum target OS version will remain the default, which is + /// the latest released version of the OS. + /// + /// To test at compile time if the target is guaranteed to support a given OS feature, + /// one should check that the minimum version of the range is greater than or equal to + /// the version the feature was introduced in. + /// + /// To test at compile time if the target certainly will not support a given OS feature, + /// one should check that the maximum version of the range is less than the version the + /// feature was introduced in. + /// + /// If neither of these cases apply, a runtime check should be used to determine if the + /// target supports a given OS feature. + /// + /// Binaries built with a given maximum version will continue to function on newer operating system + /// versions. However, such a binary may not take full advantage of the newer operating system APIs. + pub const VersionRange = union { + none: void, + semver: Version.Range, + linux: LinuxVersionRange, + windows: WindowsVersion.Range, + + /// The default `VersionRange` represents the range that the Zig Standard Library + /// bases its abstractions on. + pub fn default(tag: Tag) VersionRange { + switch (tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => return .{ .none = {} }, + + .freebsd => return .{ + .semver = Version.Range{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 12, .minor = 1 }, + }, + }, + .macosx => return .{ + .semver = .{ + .min = .{ .major = 10, .minor = 13 }, + .max = .{ .major = 10, .minor = 15, .patch = 3 }, + }, + }, + .netbsd => return .{ + .semver = .{ + .min = .{ .major = 8, .minor = 0 }, + .max = .{ .major = 9, .minor = 0 }, + }, + }, + .openbsd => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 6 }, + .max = .{ .major = 6, .minor = 6 }, + }, + }, + + .linux => return .{ + .linux = .{ + .range = .{ + .min = .{ .major = 3, .minor = 16 }, + .max = .{ .major = 5, .minor = 5, .patch = 5 }, + }, + .glibc = .{ .major = 2, .minor = 17 }, + }, + }, + + .windows => return .{ + .windows = .{ + .min = .win8_1, + .max = .win10_19h1, + }, + }, } - return null; } }; pub fn parse(text: []const u8) !Os { var it = mem.separate(text, "."); const os_name = it.next().?; - const tag = nameToTag(os_name) orelse return error.UnknownOperatingSystem; + const tag = std.meta.stringToEnum(Tag, os_name) orelse return error.UnknownOperatingSystem; const version_text = it.rest(); const S = struct { fn parseNone(s: []const u8) !void { if (s.len != 0) return error.InvalidOperatingSystemVersion; } - fn parseSemVer(s: []const u8, default: Version.Range) !Version.Range { - if (s.len == 0) return default; + fn parseSemVer(s: []const u8, d_range: Version.Range) !Version.Range { + if (s.len == 0) return d_range; var range_it = mem.separate(s, "..."); const min_text = range_it.next().?; @@ -212,7 +225,7 @@ pub const Target = struct { const max_text = range_it.next() orelse return Version.Range{ .min = min_ver, - .max = default.max, + .max = d_range.max, }; const max_ver = Version.parse(max_text) catch |err| switch (err) { error.Overflow => return error.InvalidOperatingSystemVersion, @@ -222,79 +235,93 @@ pub const Target = struct { return Version.Range{ .min = min_ver, .max = max_ver }; } - fn parseWindows(s: []const u8, default: WindowsVersion.Range) !WindowsVersion.Range { - if (s.len == 0) return default; + fn parseWindows(s: []const u8, d_range: WindowsVersion.Range) !WindowsVersion.Range { + if (s.len == 0) return d_range; var range_it = mem.separate(s, "..."); const min_text = range_it.next().?; - const min_ver = WindowsVersion.nameToTag(min_text) orelse + const min_ver = std.meta.stringToEnum(WindowsVersion, min_text) orelse return error.InvalidOperatingSystemVersion; const max_text = range_it.next() orelse return WindowsVersion.Range{ .min = min_ver, - .max = default.max, + .max = d_range.max, }; - const max_ver = WindowsVersion.nameToTag(max_text) orelse + const max_ver = std.meta.stringToEnum(WindowsVersion, max_text) orelse return error.InvalidOperatingSystemVersion; return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; } }; - const default = defaultVersionRange(tag); + const d_range = VersionRange.default(tag); switch (tag) { - .freestanding => return Os{ .freestanding = try S.parseNone(version_text) }, - .ananas => return Os{ .ananas = try S.parseNone(version_text) }, - .cloudabi => return Os{ .cloudabi = try S.parseNone(version_text) }, - .dragonfly => return Os{ .dragonfly = try S.parseNone(version_text) }, - .freebsd => return Os{ .freebsd = try S.parseSemVer(version_text, default.freebsd) }, - .fuchsia => return Os{ .fuchsia = try S.parseNone(version_text) }, - .ios => return Os{ .ios = try S.parseNone(version_text) }, - .kfreebsd => return Os{ .kfreebsd = try S.parseNone(version_text) }, + .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 Os{ + .tag = tag, + .version_range = .{ .none = try S.parseNone(version_text) }, + }, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => return Os{ + .tag = tag, + .version_range = .{ .semver = try S.parseSemVer(version_text, d_range.semver) }, + }, + .linux => return Os{ - .linux = .{ - .range = try S.parseSemVer(version_text, default.linux.range), - .glibc = default.linux.glibc, + .tag = tag, + .version_range = .{ + .linux = .{ + .range = try S.parseSemVer(version_text, d_range.linux.range), + .glibc = d_range.linux.glibc, + }, }, }, - .lv2 => return Os{ .lv2 = try S.parseNone(version_text) }, - .macosx => return Os{ .macosx = try S.parseSemVer(version_text, default.macosx) }, - .netbsd => return Os{ .netbsd = try S.parseSemVer(version_text, default.netbsd) }, - .openbsd => return Os{ .openbsd = try S.parseSemVer(version_text, default.openbsd) }, - .solaris => return Os{ .solaris = try S.parseNone(version_text) }, - .windows => return Os{ .windows = try S.parseWindows(version_text, default.windows) }, - .haiku => return Os{ .haiku = try S.parseNone(version_text) }, - .minix => return Os{ .minix = try S.parseNone(version_text) }, - .rtems => return Os{ .rtems = try S.parseNone(version_text) }, - .nacl => return Os{ .nacl = try S.parseNone(version_text) }, - .cnk => return Os{ .cnk = try S.parseNone(version_text) }, - .aix => return Os{ .aix = try S.parseNone(version_text) }, - .cuda => return Os{ .cuda = try S.parseNone(version_text) }, - .nvcl => return Os{ .nvcl = try S.parseNone(version_text) }, - .amdhsa => return Os{ .amdhsa = try S.parseNone(version_text) }, - .ps4 => return Os{ .ps4 = try S.parseNone(version_text) }, - .elfiamcu => return Os{ .elfiamcu = try S.parseNone(version_text) }, - .tvos => return Os{ .tvos = try S.parseNone(version_text) }, - .watchos => return Os{ .watchos = try S.parseNone(version_text) }, - .mesa3d => return Os{ .mesa3d = try S.parseNone(version_text) }, - .contiki => return Os{ .contiki = try S.parseNone(version_text) }, - .amdpal => return Os{ .amdpal = try S.parseNone(version_text) }, - .hermit => return Os{ .hermit = try S.parseNone(version_text) }, - .hurd => return Os{ .hurd = try S.parseNone(version_text) }, - .wasi => return Os{ .wasi = try S.parseNone(version_text) }, - .emscripten => return Os{ .emscripten = try S.parseNone(version_text) }, - .uefi => return Os{ .uefi = try S.parseNone(version_text) }, - .other => return Os{ .other = try S.parseNone(version_text) }, + + .windows => return Os{ + .tag = tag, + .version_range = .{ .windows = try S.parseWindows(version_text, d_range.windows) }, + }, } } - pub fn nameToTag(name: []const u8) ?@TagType(Os) { - const info = @typeInfo(Os); - inline for (info.Union.fields) |field| { - if (mem.eql(u8, name, field.name)) { - return @field(Os, field.name); - } - } - return null; + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), + }; } }; @@ -339,11 +366,10 @@ pub const Target = struct { 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, @@ -388,40 +414,19 @@ pub const Target = struct { } } - pub fn nameToTag(text: []const u8) ?Abi { - const info = @typeInfo(Abi); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(Abi, field.name); - } - } - return null; - } - - pub fn parse(text: []const u8, os: *Os) !Abi { - var it = mem.separate(text, "."); - const tag = nameToTag(it.next().?) orelse return error.UnknownApplicationBinaryInterface; - const version_text = it.rest(); - if (version_text.len != 0) { - if (@as(@TagType(Os), os.*) == .linux and tag.isGnu()) { - os.linux.glibc = Version.parse(version_text) catch |err| switch (err) { - error.Overflow => return error.InvalidGlibcVersion, - error.InvalidCharacter => return error.InvalidGlibcVersion, - error.InvalidVersion => return error.InvalidGlibcVersion, - }; - } else { - return error.InvalidAbiVersion; - } - } - return tag; - } - pub fn isGnu(abi: Abi) bool { 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 const ObjectFormat = enum { @@ -909,15 +914,15 @@ pub const Target = struct { /// TODO add OS version ranges and glibc version pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } /// 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()) { + const arch = switch (target.cpu.arch) { .i386 => "x86", .x86_64 => "x64", @@ -957,16 +962,16 @@ pub const Target = struct { pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } @@ -1017,11 +1022,28 @@ pub const Target = struct { diags.arch = arch; const os_name = it.next() orelse return error.MissingOperatingSystem; - var os = try Os.parse(os_name); // var because Abi.parse can update linux.glibc version + var os = try Os.parse(os_name); diags.os = os; - const abi_name = it.next(); - const abi = if (abi_name) |n| try Abi.parse(n, &os) else Abi.default(arch, os); + const opt_abi_text = it.next(); + const abi = if (opt_abi_text) |abi_text| blk: { + var abi_it = mem.separate(abi_text, "."); + const abi = std.meta.stringToEnum(Abi, abi_it.next().?) orelse + return error.UnknownApplicationBinaryInterface; + const abi_ver_text = abi_it.rest(); + if (abi_ver_text.len != 0) { + if (os.tag == .linux and abi.isGnu()) { + os.version_range.linux.glibc = Version.parse(abi_ver_text) catch |err| switch (err) { + error.Overflow => return error.InvalidAbiVersion, + error.InvalidCharacter => return error.InvalidAbiVersion, + error.InvalidVersion => return error.InvalidAbiVersion, + }; + } else { + return error.InvalidAbiVersion; + } + } + break :blk abi; + } else Abi.default(arch, os); diags.abi = abi; if (it.next() != null) return error.UnexpectedExtraField; @@ -1130,25 +1152,6 @@ pub const Target = struct { } } - /// Deprecated; access the `os` field directly. - pub fn getOs(self: Target) @TagType(Os) { - return self.os; - } - - /// Deprecated; access the `cpu` field directly. - pub fn getCpu(self: Target) Cpu { - return self.cpu; - } - - /// Deprecated; access the `abi` field directly. - pub fn getAbi(self: Target) Abi { - return self.abi; - } - - pub fn getArch(self: Target) Cpu.Arch { - return self.cpu.arch; - } - pub fn getObjectFormat(self: Target) ObjectFormat { if (self.isWindows() or self.isUefi()) { return .coff; @@ -1170,28 +1173,25 @@ pub const Target = struct { } pub fn isMusl(self: Target) bool { - return switch (self.abi) { - .musl, .musleabi, .musleabihf => true, - else => false, - }; + return self.abi.isMusl(); } pub fn isDarwin(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .ios, .macosx, .watchos, .tvos => true, else => false, }; } pub fn isWindows(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .windows => true, else => false, }; } pub fn isLinux(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .linux => true, else => false, }; @@ -1205,40 +1205,41 @@ pub const Target = struct { } pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .dragonfly => true, else => false, }; } pub fn isUefi(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .uefi => true, else => false, }; } pub fn isWasm(self: Target) bool { - return switch (self.getArch()) { - .wasm32, .wasm64 => true, - else => false, - }; + return self.cpu.arch.isWasm(); } pub fn isFreeBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .freebsd => true, else => false, }; } pub fn isNetBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .netbsd => true, else => false, }; } + pub fn isGnuLibC(self: Target) bool { + return self.os.tag == .linux and self.abi.isGnu(); + } + pub fn wantSharedLibSymLinks(self: Target) bool { return !self.isWindows(); } @@ -1248,7 +1249,7 @@ pub const Target = struct { } pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.getArch()) { + switch (self.cpu.arch) { .avr, .msp430, => return 16, @@ -1323,8 +1324,8 @@ pub const Target = struct { 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.os == builtin.os) { - return switch (self.getArch()) { + if (self.os.tag == builtin.os.tag) { + return switch (self.cpu.arch) { .aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, .arm => Executor{ .qemu = "qemu-arm" }, @@ -1381,13 +1382,10 @@ pub const Target = struct { } pub fn hasDynamicLinker(self: Target) bool { - switch (self.getArch()) { - .wasm32, - .wasm64, - => return false, - else => {}, + if (self.cpu.arch.isWasm()) { + return false; } - switch (self.os) { + switch (self.os.tag) { .freestanding, .ios, .tvos, @@ -1424,7 +1422,7 @@ pub const Target = struct { defer result.deinit(); var is_arm = false; - switch (self.getArch()) { + switch (self.cpu.arch) { .arm, .thumb => { try result.append("arm"); is_arm = true; @@ -1442,11 +1440,11 @@ pub const Target = struct { return result.toOwnedSlice(); } - switch (self.os) { + switch (self.os.tag) { .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()) { + .linux => switch (self.cpu.arch) { .i386, .sparc, .sparcel, @@ -1539,7 +1537,7 @@ test "Target.parse" { .cpu_features = "x86_64-sse-sse2-avx-cx8", }); - std.testing.expect(target.os == .linux); + std.testing.expect(target.os.tag == .linux); std.testing.expect(target.abi == .gnu); std.testing.expect(target.cpu.arch == .x86_64); std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); @@ -1554,10 +1552,29 @@ test "Target.parse" { .cpu_features = "generic+v8a", }); - std.testing.expect(target.os == .linux); + std.testing.expect(target.os.tag == .linux); std.testing.expect(target.abi == .musleabihf); std.testing.expect(target.cpu.arch == .arm); std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); } + { + const target = try Target.parse(.{ + .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", + .cpu_features = "generic+v8a", + }); + + std.testing.expect(target.cpu.arch == .aarch64); + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.os.version_range.linux.min.major == 3); + std.testing.expect(target.os.version_range.linux.min.minor == 10); + std.testing.expect(target.os.version_range.linux.min.patch == 0); + std.testing.expect(target.os.version_range.linux.max.major == 4); + std.testing.expect(target.os.version_range.linux.max.minor == 4); + std.testing.expect(target.os.version_range.linux.max.patch == 1); + std.testing.expect(target.os.version_range.linux.glibc.major == 2); + std.testing.expect(target.os.version_range.linux.glibc.minor == 27); + std.testing.expect(target.os.version_range.linux.glibc.patch == 0); + std.testing.expect(target.abi == .gnu); + } } 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/system.zig b/lib/std/zig/system.zig index 3931e362c6..5c9b71001b 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -1,11 +1,14 @@ 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 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 +80,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 +164,326 @@ pub const NativePaths = struct { try array.append(item); } }; + +pub const NativeTargetInfo = struct { + target: Target, + dynamic_linker: ?[:0]u8, + + pub const DetectError = error{ + OutOfMemory, + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + DeviceBusy, + }; + + /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. + /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { + const arch = Target.current.cpu.arch; + const os_tag = Target.current.os.tag; + + // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. + const cpu = Target.Cpu.baseline(arch); + + // TODO Detect native operating system version. Until that is implemented we use the minimum version + // of the default range. + const os = Target.Os.defaultVersionRange(os_tag); + + return detectAbiAndDynamicLinker(allocator, cpu, os); + } + + /// Must be the same `Allocator` passed to `detect`. + pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void { + if (self.dynamic_linker) |dl| allocator.free(dl); + self.* = undefined; + } + + /// First we attempt to use the executable's own binary. If it is dynamically + /// linked, then it should answer both the C ABI question and the dynamic linker question. + /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then + /// we fall back to the defaults. + fn detectAbiAndDynamicLinker( + allocator: *Allocator, + cpu: Target.Cpu, + os: Target.Os, + ) DetectError!NativeTargetInfo { + if (!comptime Target.current.hasDynamicLinker()) { + return defaultAbiAndDynamicLinker(allocator, cpu, os); + } + // The current target's ABI cannot be relied on for this. For example, we may build the zig + // compiler for target riscv64-linux-musl and provide a tarball for users to download. + // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined + // and supported by Zig. But that means that we must detect the system ABI here rather than + // relying on `Target.current`. + const LdInfo = struct { + ld_path: []u8, + abi: Target.Abi, + }; + var ld_info_list = std.ArrayList(LdInfo).init(allocator); + defer { + for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); + ld_info_list.deinit(); + } + + const all_abis = comptime blk: { + assert(@enumToInt(Target.Abi.none) == 0); + const fields = std.meta.fields(Target.Abi)[1..]; + var array: [fields.len]Target.Abi = undefined; + inline for (fields) |field, i| { + array[i] = @field(Target.Abi, field.name); + } + break :blk array; + }; + for (all_abis) |abi| { + // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and + // skip adding it to `ld_info_list`. + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = abi, + }; + const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, + }; + errdefer allocator.free(standard_ld_path); + try ld_info_list.append(.{ + .ld_path = standard_ld_path, + .abi = abi, + }); + } + + // Best case scenario: the executable is dynamically linked, and we can iterate + // over our own shared objects and find a dynamic linker. + self_exe: { + const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); + defer allocator.free(lib_paths); + + var found_ld_info: LdInfo = undefined; + var found_ld_path: [:0]const u8 = undefined; + + // Look for dynamic linker. + // This is O(N^M) but typical case here is N=2 and M=10. + find_ld: for (lib_paths) |lib_path| { + for (ld_info_list.toSlice()) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld_path); + if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { + found_ld_info = ld_info; + found_ld_path = lib_path; + break :find_ld; + } + } + } else break :self_exe; + + // Look for glibc version. + var os_adjusted = os; + if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu()) { + for (lib_paths) |lib_path| { + if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) { + os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName => continue, + error.InvalidGnuLibCVersion => continue, + error.GnuLibCVersionUnavailable => continue, + else => |e| return e, + }; + break; + } + } + } + + return NativeTargetInfo{ + .target = .{ + .cpu = cpu, + .os = os_adjusted, + .abi = found_ld_info.abi, + }, + .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path), + }; + } + + // If Zig is statically linked, such as via distributed binary static builds, the above + // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. + // Since that path is hard-coded into the shebang line of many portable scripts, it's a + // reasonably reliable path to check for. + return abiAndDynamicLinkerFromUsrBinEnv(allocator, cpu, os) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.FileSystem => return error.FileSystem, + error.SystemResources => return error.SystemResources, + error.SymLinkLoop => return error.SymLinkLoop, + error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded, + error.DeviceBusy => return error.DeviceBusy, + + error.UnableToReadElfFile, + error.ElfNotADynamicExecutable, + error.InvalidElfProgramHeaders, + error.InvalidElfClass, + error.InvalidElfVersion, + error.InvalidElfEndian, + error.InvalidElfFile, + error.InvalidElfMagic, + error.UsrBinEnvNotAvailable, + error.Unexpected, + // Finally, we fall back on the standard path. + => defaultAbiAndDynamicLinker(allocator, cpu, os), + }; + } + + const glibc_so_basename = "libc.so.6"; + + fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version { + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlinkC(so_path.ptr, &link_buf) catch |err| switch (err) { + error.AccessDenied => return error.GnuLibCVersionUnavailable, + error.FileSystem => return error.FileSystem, + error.SymLinkLoop => return error.SymLinkLoop, + error.NameTooLong => unreachable, + error.FileNotFound => return error.GnuLibCVersionUnavailable, + error.SystemResources => return error.SystemResources, + error.NotDir => return error.GnuLibCVersionUnavailable, + error.Unexpected => return error.GnuLibCVersionUnavailable, + }; + // example: "libc-2.3.4.so" + // example: "libc-2.27.so" + const prefix = "libc-"; + const suffix = ".so"; + if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) { + return error.UnrecognizedGnuLibCFileName; + } + // chop off "libc-" and ".so" + const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; + return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) { + error.Overflow => return error.InvalidGnuLibCVersion, + error.InvalidCharacter => return error.InvalidGnuLibCVersion, + error.InvalidVersion => return error.InvalidGnuLibCVersion, + }; + } + + fn abiAndDynamicLinkerFromUsrBinEnv( + allocator: *Allocator, + cpu: Target.Cpu, + os: Target.Os, + ) !NativeTargetInfo { + const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + error.NameTooLong => unreachable, + error.PathAlreadyExists => unreachable, + error.SharingViolation => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.PipeBusy => unreachable, + + error.IsDir => return error.UsrBinEnvNotAvailable, + error.NotDir => return error.UsrBinEnvNotAvailable, + error.AccessDenied => return error.UsrBinEnvNotAvailable, + error.NoDevice => return error.UsrBinEnvNotAvailable, + error.FileNotFound => return error.UsrBinEnvNotAvailable, + error.FileTooBig => return error.UsrBinEnvNotAvailable, + + else => |e| return e, + }; + var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; + const hdr_bytes_len = try wrapRead(env_file.pread(&hdr_buf, 0)); + if (hdr_bytes_len < @sizeOf(elf.Elf32_Ehdr)) return error.InvalidElfFile; + const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); + const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); + if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { + elf.ELFDATA2LSB => .Little, + elf.ELFDATA2MSB => .Big, + else => return error.InvalidElfEndian, + }; + const need_bswap = elf_endian != std.builtin.endian; + if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; + + const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { + elf.ELFCLASS32 => false, + elf.ELFCLASS64 => true, + else => return error.InvalidElfClass, + }; + var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); + const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); + const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + + const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { + error.Overflow => return error.InvalidElfProgramHeaders, + }; + var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + var ph_i: u16 = 0; + while (ph_i < phnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields even when the ELF file is 32-bits. + const reserve = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); + const read_byte_len = try wrapRead(env_file.pread(ph_buf[0 .. ph_buf.len - reserve], phoff)); + if (read_byte_len < phentsize) return error.ElfNotADynamicExecutable; + var buf_i: usize = 0; + while (buf_i < read_byte_len and ph_i < phnum) : ({ + ph_i += 1; + phoff += phentsize; + buf_i += phentsize; + }) { + const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[buf_i])); + const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[buf_i])); + const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); + switch (p_type) { + elf.PT_INTERP => { + std.debug.warn("found PT_INTERP\n", .{}); + }, + elf.PT_DYNAMIC => { + std.debug.warn("found PT_DYNAMIC\n", .{}); + }, + else => continue, + } + } + } + + return error.OutOfMemory; // TODO + } + + fn wrapRead(res: std.os.ReadError!usize) !usize { + return res catch |err| switch (err) { + error.OperationAborted => unreachable, // Windows-only + error.WouldBlock => unreachable, // Did not request blocking mode + error.SystemResources => return error.SystemResources, + error.IsDir => return error.UnableToReadElfFile, + error.BrokenPipe => return error.UnableToReadElfFile, + error.ConnectionResetByPeer => return error.UnableToReadElfFile, + error.Unexpected => return error.Unexpected, + error.InputOutput => return error.FileSystem, + }; + } + + fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }; + return @as(NativeTargetInfo, .{ + .target = target, + .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null, + }, + }); + } +}; + +fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { + if (is_64) { + if (need_bswap) { + return @byteSwap(@TypeOf(int_64), int_64); + } else { + return int_64; + } + } else { + if (need_bswap) { + return @byteSwap(@TypeOf(int_32), int_32); + } else { + return int_32; + } + } +} diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig index 2a840372b9..1ee27c7596 100644 --- a/src-self-hosted/c_int.zig +++ b/src-self-hosted/c_int.zig @@ -70,7 +70,7 @@ pub const CInt = struct { pub fn sizeInBits(cint: CInt, self: Target) u32 { const arch = self.getArch(); - switch (self.getOs()) { + switch (self.os.tag) { .freestanding, .other => switch (self.getArch()) { .msp430 => switch (cint.id) { .Short, diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index d2db306be8..bdaa8b86dc 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1050,7 +1050,7 @@ pub const struct_ZigClangExprEvalResult = extern struct { pub const struct_ZigClangAPValue = extern struct { Kind: ZigClangAPValueKind, - Data: if (builtin.os == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, + Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, }; pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType; diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 11838e7e63..c7f5690cc3 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -1,4 +1,4 @@ -// Introspection and determination of system libraries needed by zig. +//! Introspection and determination of system libraries needed by zig. const std = @import("std"); const mem = std.mem; @@ -6,14 +6,6 @@ const fs = std.fs; const warn = std.debug.warn; -pub fn detectDynamicLinker(allocator: *mem.Allocator, target: std.Target) ![:0]u8 { - if (target == .Native) { - return @import("libc_installation.zig").detectNativeDynamicLinker(allocator); - } else { - return target.getStandardDynamicLinkerPath(allocator); - } -} - /// Caller must free result pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 85740240ab..d617abb821 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -99,27 +99,27 @@ pub const LibCInstallation = struct { return error.ParseError; } if (self.crt_dir == null and !is_darwin) { - try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())}); + try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)}); return error.ParseError; } if (self.static_crt_dir == null and is_windows and is_gnu) { try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } if (self.msvc_lib_dir == null and is_windows and !is_gnu) { try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } @@ -616,104 +616,6 @@ fn printVerboseInvocation( } } -/// Caller owns returned memory. -pub fn detectNativeDynamicLinker(allocator: *Allocator) error{ - OutOfMemory, - TargetHasNoDynamicLinker, - UnknownDynamicLinkerPath, -}![:0]u8 { - if (!comptime Target.current.hasDynamicLinker()) { - return error.TargetHasNoDynamicLinker; - } - - // The current target's ABI cannot be relied on for this. For example, we may build the zig - // compiler for target riscv64-linux-musl and provide a tarball for users to download. - // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined - // and supported by Zig. But that means that we must detect the system ABI here rather than - // relying on `std.Target.current`. - - const LdInfo = struct { - ld_path: []u8, - abi: Target.Abi, - }; - var ld_info_list = std.ArrayList(LdInfo).init(allocator); - defer { - for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); - ld_info_list.deinit(); - } - - const all_abis = comptime blk: { - const fields = std.meta.fields(Target.Abi); - var array: [fields.len]Target.Abi = undefined; - inline for (fields) |field, i| { - array[i] = @field(Target.Abi, field.name); - } - break :blk array; - }; - for (all_abis) |abi| { - // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and - // skip adding it to `ld_info_list`. - const target: Target = .{ - .Cross = .{ - .cpu = Target.Cpu.baseline(Target.current.getArch()), - .os = Target.current.getOs(), - .abi = abi, - }, - }; - const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, - }; - errdefer allocator.free(standard_ld_path); - try ld_info_list.append(.{ - .ld_path = standard_ld_path, - .abi = abi, - }); - } - - // Best case scenario: the zig compiler is dynamically linked, and we can iterate - // over our own shared objects and find a dynamic linker. - { - const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); - defer allocator.free(lib_paths); - - // This is O(N^M) but typical case here is N=2 and M=10. - for (lib_paths) |lib_path| { - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); - if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { - return std.mem.dupeZ(allocator, u8, lib_path); - } - } - } - } - - // If Zig is statically linked, such as via distributed binary static builds, the above - // trick won't work. What are we left with? Try to run the system C compiler and get - // it to tell us the dynamic linker path. - // TODO: instead of this, look at the shared libs of /usr/bin/env. - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); - - const full_ld_path = ccPrintFileName(.{ - .allocator = allocator, - .search_basename = standard_ld_basename, - .want_dirname = .full_path, - }) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LibCRuntimeNotFound, - error.CCompilerExitCode, - error.CCompilerCrashed, - error.UnableToSpawnCCompiler, - => continue, - }; - return full_ld_path; - } - - // Finally, we fall back on the standard path. - return Target.current.getStandardDynamicLinkerPath(allocator); -} - const Search = struct { path: []const u8, version: []const u8, diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 0a29da4778..1efa15574a 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -515,7 +515,7 @@ const DarwinPlatform = struct { break :blk ver; }, .None => blk: { - assert(comp.target.getOs() == .macosx); + assert(comp.target.os.tag == .macosx); result.kind = .MacOS; break :blk "10.14"; }, @@ -534,7 +534,7 @@ const DarwinPlatform = struct { } if (result.kind == .IPhoneOS) { - switch (comp.target.getArch()) { + switch (comp.target.cpu.arch) { .i386, .x86_64, => result.kind = .IPhoneOSSimulator, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fbfd1e2642..264a896f46 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -79,9 +79,9 @@ pub fn main() !void { } else if (mem.eql(u8, cmd, "libc")) { return cmdLibC(allocator, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { - // TODO figure out the current target rather than using the target that was specified when - // compiling the compiler - return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, Target.current); + const info = try std.zig.system.NativeTargetInfo.detect(allocator); + defer info.deinit(allocator); + return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target); } else if (mem.eql(u8, cmd, "version")) { return cmdVersion(allocator, cmd_args); } else if (mem.eql(u8, cmd, "zen")) { diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig index 16f1891164..be024a2a04 100644 --- a/src-self-hosted/print_targets.zig +++ b/src-self-hosted/print_targets.zig @@ -124,7 +124,7 @@ pub fn cmdTargets( try jws.objectField("os"); try jws.beginArray(); - inline for (@typeInfo(Target.Os).Enum.fields) |field| { + inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| { try jws.arrayElem(); try jws.emitString(field.name); } @@ -201,16 +201,16 @@ pub fn cmdTargets( try jws.objectField("cpu"); try jws.beginObject(); try jws.objectField("arch"); - try jws.emitString(@tagName(native_target.getArch())); + try jws.emitString(@tagName(native_target.cpu.arch)); try jws.objectField("name"); - const cpu = native_target.getCpu(); + const cpu = native_target.cpu; try jws.emitString(cpu.model.name); { try jws.objectField("features"); try jws.beginArray(); - for (native_target.getArch().allFeaturesList()) |feature, i_usize| { + for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| { const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize); if (cpu.features.isEnabled(index)) { try jws.arrayElem(); @@ -222,9 +222,9 @@ pub fn cmdTargets( try jws.endObject(); } try jws.objectField("os"); - try jws.emitString(@tagName(native_target.getOs())); + try jws.emitString(@tagName(native_target.os.tag)); try jws.objectField("abi"); - try jws.emitString(@tagName(native_target.getAbi())); + try jws.emitString(@tagName(native_target.abi)); // TODO implement native glibc version detection in self-hosted try jws.endObject(); diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index dacdb86bcd..7f0f35abe0 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -110,6 +110,8 @@ const Error = extern enum { WindowsSdkNotFound, UnknownDynamicLinkerPath, TargetHasNoDynamicLinker, + InvalidAbiVersion, + InvalidOperatingSystemVersion, }; const FILE = std.c.FILE; @@ -633,11 +635,11 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { fn cmdTargets(zig_triple: [*:0]const u8) !void { var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); - target.Cross.cpu = blk: { + target.cpu = blk: { const llvm = @import("llvm.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); + break :blk try detectNativeCpuWithLLVM(target.cpu.arch, llvm_cpu_name, llvm_cpu_features); }; return @import("print_targets.zig").cmdTargets( std.heap.c_allocator, @@ -662,6 +664,14 @@ export fn stage2_target_parse( error.MissingArchitecture => return .MissingArchitecture, error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, error.UnexpectedExtraField => return .SemanticAnalyzeFail, + error.InvalidAbiVersion => return .InvalidAbiVersion, + error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion, + error.FileSystem => return .FileSystem, + error.SymLinkLoop => return .SymLinkLoop, + error.SystemResources => return .SystemResources, + error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, + error.DeviceBusy => return .DeviceBusy, }; return .None; } @@ -671,108 +681,48 @@ fn stage2TargetParse( zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, ) !void { - const target: Target = if (zig_triple_oz) |zig_triple_z| blk: { + const target: std.build.Target = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; var diags: std.Target.ParseOptions.Diagnostics = .{}; - break :blk Target.parse(.{ - .arch_os_abi = zig_triple, - .cpu_features = mcpu, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpu => { - 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); + break :blk std.build.Target{ + .Cross = Target.parse(.{ + .arch_os_abi = zig_triple, + .cpu_features = mcpu, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpu => { + 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); + }, + else => |e| return e, }, - error.UnknownCpuFeature => { - std.debug.warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': - \\ - , .{ - diags.unknown_feature_name, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); - } - process.exit(1); - }, - else => |e| return e, }; - } else Target.Native; + } else std.build.Target.Native; try stage1_target.fromTarget(target); } -fn initStage1TargetCpuFeatures(stage1_target: *Stage2Target, cpu: Target.Cpu) !void { - const allocator = std.heap.c_allocator; - const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{ - cpu.model.name, - cpu.features.asBytes(), - }); - errdefer allocator.free(cache_hash); - - const generic_arch_name = cpu.arch.genericName(); - var builtin_str_buffer = try std.Buffer.allocPrint(allocator, - \\Cpu{{ - \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ - \\ - , .{ - @tagName(cpu.arch), - generic_arch_name, - cpu.model.name, - generic_arch_name, - generic_arch_name, - }); - defer builtin_str_buffer.deinit(); - - var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); - defer llvm_features_buffer.deinit(); - - for (cpu.arch.allFeaturesList()) |feature, index_usize| { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - const is_enabled = cpu.features.isEnabled(index); - - if (feature.llvm_name) |llvm_name| { - const plus_or_minus = "-+"[@boolToInt(is_enabled)]; - try llvm_features_buffer.appendByte(plus_or_minus); - try llvm_features_buffer.append(llvm_name); - try llvm_features_buffer.append(","); - } - - if (is_enabled) { - // TODO some kind of "zig identifier escape" function rather than - // unconditionally using @"" syntax - try builtin_str_buffer.append(" .@\""); - try builtin_str_buffer.append(feature.name); - try builtin_str_buffer.append("\",\n"); - } - } - - try builtin_str_buffer.append( - \\ }), - \\}; - \\ - ); - - assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); - llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); - - stage1_target.llvm_cpu_name = if (cpu.model.llvm_name) |s| s.ptr else null; - stage1_target.llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr; - stage1_target.builtin_str = builtin_str_buffer.toOwnedSlice().ptr; - stage1_target.cache_hash = cache_hash.ptr; -} - // ABI warning const Stage2LibCInstallation = extern struct { include_dir: [*:0]const u8, @@ -952,10 +902,13 @@ const Stage2Target = extern struct { llvm_cpu_name: ?[*:0]const u8, llvm_cpu_features: ?[*:0]const u8, - builtin_str: ?[*:0]const u8, + cpu_builtin_str: ?[*:0]const u8, cache_hash: ?[*:0]const u8, + os_builtin_str: ?[*:0]const u8, - fn toTarget(in_target: Stage2Target) Target { + dynamic_linker: ?[*:0]const u8, + + fn toTarget(in_target: Stage2Target) std.build.Target { if (in_target.is_native) return .Native; const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch @@ -965,39 +918,244 @@ const Stage2Target = extern struct { return .{ .Cross = .{ .cpu = Target.Cpu.baseline(enumInt(Target.Cpu.Arch, in_arch)), - .os = enumInt(Target.Os, in_os), + .os = Target.Os.defaultVersionRange(enumInt(Target.Os.Tag, in_os)), .abi = enumInt(Target.Abi, in_abi), }, }; } - fn fromTarget(self: *Stage2Target, target: Target) !void { - const cpu = switch (target) { + fn fromTarget(self: *Stage2Target, build_target: std.build.Target) !void { + const allocator = std.heap.c_allocator; + var dynamic_linker: ?[*:0]u8 = null; + const target = switch (build_target) { .Native => blk: { - // TODO self-host CPU model and feature detection instead of relying on LLVM + const info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); + if (info.dynamic_linker) |dl| { + dynamic_linker = dl.ptr; + } + + // TODO we want to just use info.target but implementing CPU model & feature detection is todo + // so here we rely on LLVM const llvm = @import("llvm.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); + const arch = std.Target.current.cpu.arch; + var t = info.target; + t.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + break :blk t; }, - .Cross => target.getCpu(), + .Cross => |t| t, }; + + var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{ + target.cpu.model.name, + target.cpu.features.asBytes(), + }); + defer cache_hash.deinit(); + + const generic_arch_name = target.cpu.arch.genericName(); + var cpu_builtin_str_buffer = try std.Buffer.allocPrint(allocator, + \\Cpu{{ + \\ .arch = .{}, + \\ .model = &Target.{}.cpu.{}, + \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ + , .{ + @tagName(target.cpu.arch), + generic_arch_name, + target.cpu.model.name, + generic_arch_name, + generic_arch_name, + }); + defer cpu_builtin_str_buffer.deinit(); + + var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); + defer llvm_features_buffer.deinit(); + + for (target.cpu.arch.allFeaturesList()) |feature, index_usize| { + const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = target.cpu.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + const plus_or_minus = "-+"[@boolToInt(is_enabled)]; + try llvm_features_buffer.appendByte(plus_or_minus); + try llvm_features_buffer.append(llvm_name); + try llvm_features_buffer.append(","); + } + + if (is_enabled) { + // TODO some kind of "zig identifier escape" function rather than + // unconditionally using @"" syntax + try cpu_builtin_str_buffer.append(" .@\""); + try cpu_builtin_str_buffer.append(feature.name); + try cpu_builtin_str_buffer.append("\",\n"); + } + } + + try cpu_builtin_str_buffer.append( + \\ }), + \\}; + \\ + ); + + assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); + llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); + + var os_builtin_str_buffer = try std.Buffer.allocPrint(allocator, + \\Os{{ + \\ .tag = .{}, + \\ .version_range = .{{ + , .{@tagName(target.os.tag)}); + defer os_builtin_str_buffer.deinit(); + + // We'll re-use the OS version range builtin string for the cache hash. + const os_builtin_str_ver_start_index = os_builtin_str_buffer.len(); + + @setEvalBranchQuota(2000); + switch (target.os.tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => try os_builtin_str_buffer.append(" .none = {} }\n"), + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => try os_builtin_str_buffer.print( + \\.semver = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + , .{ + target.os.version_range.semver.min.major, + target.os.version_range.semver.min.minor, + target.os.version_range.semver.min.patch, + + target.os.version_range.semver.max.major, + target.os.version_range.semver.max.minor, + target.os.version_range.semver.max.patch, + }), + + .linux => try os_builtin_str_buffer.print( + \\.linux = .{{ + \\ .range = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}, + \\ .glibc = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + target.os.version_range.linux.range.min.major, + target.os.version_range.linux.range.min.minor, + target.os.version_range.linux.range.min.patch, + + target.os.version_range.linux.range.max.major, + target.os.version_range.linux.range.max.minor, + target.os.version_range.linux.range.max.patch, + + target.os.version_range.linux.glibc.major, + target.os.version_range.linux.glibc.minor, + target.os.version_range.linux.glibc.patch, + }), + + .windows => try os_builtin_str_buffer.print( + \\.semver = .{{ + \\ .min = .{}, + \\ .max = .{}, + \\ }}}}, + , .{ + @tagName(target.os.version_range.windows.min), + @tagName(target.os.version_range.windows.max), + }), + } + try os_builtin_str_buffer.append("};\n"); + + try cache_hash.append( + os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()], + ); + + const glibc_version = if (target.isGnuLibC()) blk: { + const stage1_glibc = try std.heap.c_allocator.create(Stage2GLibCVersion); + const stage2_glibc = target.os.version_range.linux.glibc; + stage1_glibc.* = .{ + .major = stage2_glibc.major, + .minor = stage2_glibc.minor, + .patch = stage2_glibc.patch, + }; + break :blk stage1_glibc; + } else null; + self.* = .{ - .arch = @enumToInt(target.getArch()) + 1, // skip over ZigLLVM_UnknownArch + .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch .vendor = 0, - .os = @enumToInt(target.getOs()), - .abi = @enumToInt(target.getAbi()), - .llvm_cpu_name = null, - .llvm_cpu_features = null, - .builtin_str = null, - .cache_hash = null, - .is_native = target == .Native, - .glibc_version = null, + .os = @enumToInt(target.os.tag), + .abi = @enumToInt(target.abi), + .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, + .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr, + .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr, + .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, + .cache_hash = cache_hash.toOwnedSlice().ptr, + .is_native = build_target == .Native, + .glibc_version = glibc_version, + .dynamic_linker = dynamic_linker, }; - try initStage1TargetCpuFeatures(self, cpu); } }; +fn enumInt(comptime Enum: type, int: c_int) Enum { + return @intToEnum(Enum, @intCast(@TagType(Enum), int)); +} + // ABI warning const Stage2GLibCVersion = extern struct { major: u32, @@ -1005,26 +1163,6 @@ const Stage2GLibCVersion = extern struct { patch: u32, }; -// ABI warning -export fn stage2_detect_dynamic_linker(in_target: *const Stage2Target, out_ptr: *[*:0]u8, out_len: *usize) Error { - const target = in_target.toTarget(); - const result = @import("introspect.zig").detectDynamicLinker( - std.heap.c_allocator, - target, - ) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath, - error.TargetHasNoDynamicLinker => return .TargetHasNoDynamicLinker, - }; - out_ptr.* = result.ptr; - out_len.* = result.len; - return .None; -} - -fn enumInt(comptime Enum: type, int: c_int) Enum { - return @intToEnum(Enum, @intCast(@TagType(Enum), int)); -} - // ABI warning const Stage2NativePaths = extern struct { include_dirs_ptr: [*][*:0]u8, diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index 04c5420d26..2a7bf4d9cc 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -34,25 +34,3 @@ pub fn initializeAllTargets() void { llvm.InitializeAllAsmPrinters(); llvm.InitializeAllAsmParsers(); } - -pub fn getTriple(allocator: *std.mem.Allocator, self: std.Target) !std.Buffer { - var result = try std.Buffer.initSize(allocator, 0); - errdefer result.deinit(); - - // LLVM WebAssembly output support requires the target to be activated at - // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. - // - // LLVM determines the output format based on the abi suffix, - // defaulting to an object based on the architecture. The default format in - // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to - // explicitly set this ourself in order for it to work. - // - // This is fixed in LLVM 7 and you will be able to get wasm output by - // using the target triple `wasm32-unknown-unknown-unknown`. - const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi()); - - var out = &std.io.BufferOutStream.init(&result).stream; - try out.print("{}-unknown-{}-{}", .{ @tagName(self.getArch()), @tagName(self.getOs()), env_name }); - - return result; -} diff --git a/src/codegen.cpp b/src/codegen.cpp index ee3e05a801..15122d5e11 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4483,7 +4483,7 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *execu if (!type_has_bits(field->type_entry)) { ZigType *tag_type = union_type->data.unionation.tag_type; - if (!instruction->initializing || !type_has_bits(tag_type)) + if (!instruction->initializing || tag_type == nullptr || !type_has_bits(tag_type)) return nullptr; // The field has no bits but we still have to change the discriminant @@ -8543,25 +8543,24 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); - buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); + buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n"); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); { buf_append_str(contents, "pub const cpu: Cpu = "); - if (g->zig_target->builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->builtin_str); + if (g->zig_target->cpu_builtin_str != nullptr) { + buf_append_str(contents, g->zig_target->cpu_builtin_str); } else { - buf_append_str(contents, "Target.Cpu.baseline(arch);\n"); + buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch); } } - if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) { - buf_appendf(contents, - "pub const glibc_version: ?Version = Version{.major = %d, .minor = %d, .patch = %d};\n", - g->zig_target->glibc_version->major, - g->zig_target->glibc_version->minor, - g->zig_target->glibc_version->patch); - } else { - buf_appendf(contents, "pub const glibc_version: ?Version = null;\n"); + { + buf_append_str(contents, "pub const os = "); + if (g->zig_target->os_builtin_str != nullptr) { + buf_append_str(contents, g->zig_target->os_builtin_str); + } else { + buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os); + } } buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); @@ -8867,8 +8866,6 @@ static void init(CodeGen *g) { } static void detect_dynamic_linker(CodeGen *g) { - Error err; - if (g->dynamic_linker_path != nullptr) return; if (!g->have_dynamic_link) @@ -8876,16 +8873,9 @@ static void detect_dynamic_linker(CodeGen *g) { if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) return; - char *dynamic_linker_ptr; - size_t dynamic_linker_len; - if ((err = stage2_detect_dynamic_linker(g->zig_target, &dynamic_linker_ptr, &dynamic_linker_len))) { - if (err == ErrorTargetHasNoDynamicLinker) return; - fprintf(stderr, "Unable to detect dynamic linker: %s\n", err_str(err)); - exit(1); + if (g->zig_target->dynamic_linker != nullptr) { + g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker); } - g->dynamic_linker_path = buf_create_from_mem(dynamic_linker_ptr, dynamic_linker_len); - // Skips heap::c_allocator because the memory is allocated by stage2 library. - free(dynamic_linker_ptr); } static void detect_libc(CodeGen *g) { diff --git a/src/error.cpp b/src/error.cpp index 730c6e7193..2e92a98217 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -81,6 +81,8 @@ const char *err_str(Error err) { case ErrorWindowsSdkNotFound: return "Windows SDK not found"; case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; + case ErrorInvalidAbiVersion: return "invalid C ABI version"; + case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; } return "(invalid error)"; } diff --git a/src/main.cpp b/src/main.cpp index a4ab719123..11612402ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,8 +89,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --single-threaded source may assume it is only used single-threaded\n" " -dynamic create a shared library (.so; .dll; .dylib)\n" " --strip exclude debug symbols\n" - " -target [name] -- see the targets command\n" - " -target-glibc [version] target a specific glibc version (default: 2.17)\n" + " -target [name] -- see the targets command\n" " --verbose-tokenize enable compiler debug output for tokenization\n" " --verbose-ast enable compiler debug output for AST parsing\n" " --verbose-link enable compiler debug output for linking\n" @@ -419,7 +418,6 @@ static int main0(int argc, char **argv) { const char *mios_version_min = nullptr; const char *linker_script = nullptr; Buf *version_script = nullptr; - const char *target_glibc = nullptr; ZigList rpath_list = {0}; bool each_lib_rpath = false; ZigList objects = {0}; @@ -853,8 +851,6 @@ static int main0(int argc, char **argv) { linker_script = argv[i]; } else if (strcmp(arg, "--version-script") == 0) { version_script = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "-target-glibc") == 0) { - target_glibc = argv[i]; } else if (strcmp(arg, "-rpath") == 0) { rpath_list.append(argv[i]); } else if (strcmp(arg, "--test-filter") == 0) { @@ -982,29 +978,6 @@ static int main0(int argc, char **argv) { "See `%s targets` to display valid targets.\n", err_str(err), arg0); return print_error_usage(arg0); } - if (target_is_glibc(&target)) { - target.glibc_version = heap::c_allocator.create(); - - if (target_glibc != nullptr) { - if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) { - fprintf(stderr, "invalid glibc version '%s': %s\n", target_glibc, err_str(err)); - return print_error_usage(arg0); - } - } else { - target_init_default_glibc_version(&target); -#if defined(ZIG_OS_LINUX) - if (target.is_native) { - // TODO self-host glibc version detection, and then this logic can go away - if ((err = glibc_detect_native_version(target.glibc_version))) { - // Fall back to the default version. - } - } -#endif - } - } else if (target_glibc != nullptr) { - fprintf(stderr, "'%s' is not a glibc-compatible target", target_string); - return print_error_usage(arg0); - } Buf zig_triple_buf = BUF_INIT; target_triple_zig(&zig_triple_buf, &target); diff --git a/src/stage2.cpp b/src/stage2.cpp index 736f11622e..2023b45aaf 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -100,13 +100,11 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons if (mcpu == nullptr) { target->llvm_cpu_name = ZigLLVMGetHostCPUName(); target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "native\n\n"; } else if (strcmp(mcpu, "baseline") == 0) { target->is_native = false; target->llvm_cpu_name = ""; target->llvm_cpu_features = ""; - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "baseline\n\n"; } else { const char *msg = "stage0 can't handle CPU/features in the target"; @@ -148,7 +146,6 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons const char *msg = "stage0 can't handle CPU/features in the target"; stage2_panic(msg, strlen(msg)); } - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "\n\n"; } @@ -186,11 +183,6 @@ enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) { stage2_panic(msg, strlen(msg)); } -enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, char **out_ptr, size_t *out_len) { - const char *msg = "stage0 called stage2_detect_dynamic_linker"; - stage2_panic(msg, strlen(msg)); -} - enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) { native_paths->include_dirs_ptr = nullptr; native_paths->include_dirs_len = 0; diff --git a/src/stage2.h b/src/stage2.h index e6db241e88..96222e3138 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -103,6 +103,8 @@ enum Error { ErrorWindowsSdkNotFound, ErrorUnknownDynamicLinkerPath, ErrorTargetHasNoDynamicLinker, + ErrorInvalidAbiVersion, + ErrorInvalidOperatingSystemVersion, }; // ABI warning @@ -290,14 +292,12 @@ struct ZigTarget { const char *llvm_cpu_name; const char *llvm_cpu_features; - const char *builtin_str; + const char *cpu_builtin_str; const char *cache_hash; + const char *os_builtin_str; + const char *dynamic_linker; }; -// ABI warning -ZIG_EXTERN_C enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, - char **out_ptr, size_t *out_len); - // ABI warning ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu); diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig index d1404c6f45..170ad3325d 100644 --- a/test/stage1/behavior/asm.zig +++ b/test/stage1/behavior/asm.zig @@ -1,9 +1,10 @@ const std = @import("std"); -const config = @import("builtin"); const expect = std.testing.expect; +const is_x86_64_linux = std.Target.current.cpu.arch == .x86_64 and std.Target.current.os.tag == .linux; + comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + if (is_x86_64_linux) { asm ( \\.globl this_is_my_alias; \\.type this_is_my_alias, @function; @@ -13,7 +14,7 @@ comptime { } test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + if (is_x86_64_linux) { expect(this_is_my_alias() == 1234); } } diff --git a/test/stage1/behavior/byteswap.zig b/test/stage1/behavior/byteswap.zig index c799ba4849..d6e07e7a56 100644 --- a/test/stage1/behavior/byteswap.zig +++ b/test/stage1/behavior/byteswap.zig @@ -1,6 +1,5 @@ const std = @import("std"); const expect = std.testing.expect; -const builtin = @import("builtin"); test "@byteSwap integers" { const ByteSwapIntTest = struct { @@ -41,10 +40,10 @@ test "@byteSwap integers" { test "@byteSwap vectors" { // https://github.com/ziglang/zig/issues/3563 - if (builtin.os == .dragonfly) return error.SkipZigTest; + if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/3317 - if (builtin.arch == .mipsel) return error.SkipZigTest; + if (std.Target.current.cpu.arch == .mipsel) return error.SkipZigTest; const ByteSwapVectorTest = struct { fn run() void { diff --git a/test/stage1/behavior/namespace_depends_on_compile_var.zig b/test/stage1/behavior/namespace_depends_on_compile_var.zig index 4c4fc4eefe..8c5c19d733 100644 --- a/test/stage1/behavior/namespace_depends_on_compile_var.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); -const expect = @import("std").testing.expect; +const std = @import("std"); +const expect = std.testing.expect; test "namespace depends on compile var" { if (some_namespace.a_bool) { @@ -8,7 +8,7 @@ test "namespace depends on compile var" { expect(!some_namespace.a_bool); } } -const some_namespace = switch (builtin.os) { - builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"), +const some_namespace = switch (std.builtin.os.tag) { + .linux => @import("namespace_depends_on_compile_var/a.zig"), else => @import("namespace_depends_on_compile_var/b.zig"), }; diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index e89399c5e2..01e5ac1fb8 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -2,7 +2,6 @@ const std = @import("std"); const mem = std.mem; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -const builtin = @import("builtin"); test "implicit cast vector to array - bool" { const S = struct { @@ -114,7 +113,7 @@ test "array to vector" { test "vector casts of sizes not divisable by 8" { // https://github.com/ziglang/zig/issues/3563 - if (builtin.os == .dragonfly) return error.SkipZigTest; + if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest; const S = struct { fn doTheTest() void { diff --git a/test/tests.zig b/test/tests.zig index 60008a2bc6..c088aafc5f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -31,7 +31,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; const TestTarget = struct { - target: Target = .Native, + target: build.Target = .Native, mode: builtin.Mode = .Debug, link_libc: bool = false, single_threaded: bool = false, From d4f375c46be2e509ee9161b0577d8a25d6620b3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 02:52:49 -0500 Subject: [PATCH 03/37] stage1: remove get_self_libc_path and glibc_detect_native_version --- src/compiler.cpp | 25 ------------------------- src/compiler.hpp | 1 - src/glibc.cpp | 37 ------------------------------------- src/glibc.hpp | 3 --- 4 files changed, 66 deletions(-) diff --git a/src/compiler.cpp b/src/compiler.cpp index 31bac4ee24..cddecc2025 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -4,31 +4,6 @@ #include -Buf *get_self_libc_path(void) { - static Buf saved_libc_path = BUF_INIT; - static bool searched_for_libc = false; - - for (;;) { - if (saved_libc_path.list.length != 0) { - return &saved_libc_path; - } - if (searched_for_libc) - return nullptr; - ZigList lib_paths = {}; - Error err; - if ((err = os_self_exe_shared_libs(lib_paths))) - return nullptr; - for (size_t i = 0; i < lib_paths.length; i += 1) { - Buf *lib_path = lib_paths.at(i); - if (buf_ends_with_str(lib_path, "libc.so.6")) { - buf_init_from_buf(&saved_libc_path, lib_path); - return &saved_libc_path; - } - } - searched_for_libc = true; - } -} - Error get_compiler_id(Buf **result) { static Buf saved_compiler_id = BUF_INIT; diff --git a/src/compiler.hpp b/src/compiler.hpp index 4a1699b782..47841af5dc 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -12,7 +12,6 @@ #include "error.hpp" Error get_compiler_id(Buf **result); -Buf *get_self_libc_path(void); Buf *get_zig_lib_dir(void); Buf *get_zig_special_dir(Buf *zig_lib_dir); diff --git a/src/glibc.cpp b/src/glibc.cpp index 849aac6c77..91e2f9dfc1 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -362,43 +362,6 @@ bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) { a->abi == b->abi; } -#ifdef ZIG_OS_LINUX -#include -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { - Buf *self_libc_path = get_self_libc_path(); - if (self_libc_path == nullptr) { - // TODO There is still more we could do to detect the native glibc version. For example, - // we could look at the ELF file of `/usr/bin/env`, find `libc.so.6`, and then `readlink` - // to find out the glibc version. This is relevant for the static zig builds distributed - // on the download page, since the above detection based on zig's own dynamic linking - // will not work. - - return ErrorUnknownABI; - } - Buf *link_name = buf_alloc(); - buf_resize(link_name, 4096); - ssize_t amt = readlink(buf_ptr(self_libc_path), buf_ptr(link_name), buf_len(link_name)); - if (amt == -1) { - return ErrorUnknownABI; - } - buf_resize(link_name, amt); - if (!buf_starts_with_str(link_name, "libc-") || !buf_ends_with_str(link_name, ".so")) { - return ErrorUnknownABI; - } - // example: "libc-2.3.4.so" - // example: "libc-2.27.so" - buf_resize(link_name, buf_len(link_name) - 3); // chop off ".so" - glibc_ver->major = 2; - glibc_ver->minor = 0; - glibc_ver->patch = 0; - return target_parse_glibc_version(glibc_ver, buf_ptr(link_name) + 5); -} -#else -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { - return ErrorUnknownABI; -} -#endif - size_t glibc_lib_count(void) { return array_length(glibc_libs); } diff --git a/src/glibc.hpp b/src/glibc.hpp index 42c2099371..8e4c7888ad 100644 --- a/src/glibc.hpp +++ b/src/glibc.hpp @@ -43,9 +43,6 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node); -// returns ErrorUnknownABI when glibc is not the native libc -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver); - size_t glibc_lib_count(void); const ZigGLibCLib *glibc_lib_enum(size_t index); const ZigGLibCLib *glibc_lib_find(const char *name); From 87b9e744dda465ecf7663e2463066ea26a44e63a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 03:43:21 -0500 Subject: [PATCH 04/37] update std lib to new Target API --- lib/std/build.zig | 81 ++++++++++- lib/std/build/translate_c.zig | 4 +- lib/std/fmt/parse_float.zig | 2 +- lib/std/io/test.zig | 2 +- lib/std/math/fabs.zig | 2 +- lib/std/math/isinf.zig | 6 +- lib/std/math/isnan.zig | 2 +- lib/std/os.zig | 4 +- lib/std/special/compiler_rt/addXf3_test.zig | 4 +- lib/std/special/compiler_rt/fixtfdi_test.zig | 2 +- lib/std/special/compiler_rt/fixtfsi_test.zig | 2 +- lib/std/special/compiler_rt/fixtfti_test.zig | 2 +- .../special/compiler_rt/fixunstfdi_test.zig | 2 +- .../special/compiler_rt/fixunstfsi_test.zig | 2 +- .../special/compiler_rt/fixunstfti_test.zig | 2 +- .../special/compiler_rt/floattitf_test.zig | 2 +- .../special/compiler_rt/floatuntitf_test.zig | 2 +- lib/std/special/compiler_rt/mulXf3_test.zig | 2 +- .../special/compiler_rt/truncXfYf2_test.zig | 4 +- lib/std/target.zig | 118 +++++----------- src-self-hosted/libc_installation.zig | 14 +- test/assemble_and_link.zig | 4 +- test/compile_errors.zig | 11 +- test/src/translate_c.zig | 4 +- test/stack_traces.zig | 77 ++++++----- test/stage1/behavior/math.zig | 20 +-- test/standalone.zig | 4 +- test/tests.zig | 127 +++++++++--------- test/translate_c.zig | 34 ++--- 29 files changed, 281 insertions(+), 261 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 6c89e7b9d1..25f7d536b1 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -971,7 +971,7 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - var buf: [1000]u8 = undefined; + var buf: [50000]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buf); const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); @@ -1011,6 +1011,77 @@ pub const Target = union(enum) { pub fn getArch(self: Target) std.Target.Cpu.Arch { return self.getCpu().arch; } + + pub fn isFreeBSD(self: Target) bool { + return self.getTarget().os.tag == .freebsd; + } + + pub fn isDarwin(self: Target) bool { + return self.getTarget().os.tag.isDarwin(); + } + + pub fn isNetBSD(self: Target) bool { + return self.getTarget().os.tag == .netbsd; + } + + pub fn isUefi(self: Target) bool { + return self.getTarget().os.tag == .uefi; + } + + pub fn isDragonFlyBSD(self: Target) bool { + return self.getTarget().os.tag == .dragonfly; + } + + pub fn isLinux(self: Target) bool { + return self.getTarget().os.tag == .linux; + } + + pub fn isWindows(self: Target) bool { + return self.getTarget().os.tag == .windows; + } + + pub fn oFileExt(self: Target) []const u8 { + return self.getTarget().oFileExt(); + } + + pub fn exeFileExt(self: Target) []const u8 { + return self.getTarget().exeFileExt(); + } + + pub fn staticLibSuffix(self: Target) []const u8 { + return self.getTarget().staticLibSuffix(); + } + + pub fn libPrefix(self: Target) []const u8 { + return self.getTarget().libPrefix(); + } + + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return self.getTarget().zigTriple(allocator); + } + + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return self.getTarget().linuxTriple(allocator); + } + + pub fn wantSharedLibSymLinks(self: Target) bool { + return self.getTarget().wantSharedLibSymLinks(); + } + + pub fn vcpkgTriplet(self: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { + return self.getTarget().vcpkgTriplet(allocator, linkage); + } + + pub fn getExternalExecutor(self: Target) std.Target.Executor { + switch (self) { + .Native => return .native, + .Cross => |t| return t.getExternalExecutor(), + } + } + + pub fn isGnuLibC(self: Target) bool { + return self.getTarget().isGnuLibC(); + } }; pub const Pkg = struct { @@ -1718,7 +1789,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" }); @@ -1944,7 +2015,7 @@ pub const LibExeObjStep = struct { 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) { + if (cross.cpu.model != std.Target.Cpu.baseline(self.target.getArch()).model) { try zig_args.append("-mcpu"); try zig_args.append(cross.cpu.model.name); } @@ -1953,7 +2024,7 @@ pub const LibExeObjStep = struct { 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 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) { @@ -2001,7 +2072,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 diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig index 3d1bdad677..90e95be5e1 100644 --- a/lib/std/build/translate_c.zig +++ b/lib/std/build/translate_c.zig @@ -14,7 +14,7 @@ pub const TranslateCStep = struct { source: build.FileSource, output_dir: ?[]const u8, out_basename: []const u8, - target: std.Target = .Native, + target: build.Target = .Native, pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { const self = builder.allocator.create(TranslateCStep) catch unreachable; @@ -39,7 +39,7 @@ pub const TranslateCStep = struct { ) catch unreachable; } - pub fn setTarget(self: *TranslateCStep, target: std.Target) void { + pub fn setTarget(self: *TranslateCStep, target: build.Target) void { self.target = target; } 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/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/os.zig b/lib/std/os.zig index 6e96413d78..9f349e7dc4 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -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); 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/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_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/target.zig b/lib/std/target.zig index c9f8a247fe..9007771c1a 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -53,6 +53,13 @@ pub const Target = struct { emscripten, uefi, other, + + pub fn isDarwin(tag: Tag) bool { + return switch (tag) { + .ios, .macosx, .watchos, .tvos => true, + else => false, + }; + } }; /// Based on NTDDI version constants from @@ -921,7 +928,7 @@ pub const Target = struct { } /// Returned slice must be freed by the caller. - pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { + pub fn vcpkgTriplet(target: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { const arch = switch (target.cpu.arch) { .i386 => "x86", .x86_64 => "x64", @@ -960,14 +967,6 @@ pub const Target = struct { return self.zigTriple(allocator); } - pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); - } - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.cpu.arch), @@ -1111,11 +1110,11 @@ pub const Target = struct { } pub fn exeFileExt(self: Target) []const u8 { - if (self.isWindows()) { + if (self.os.tag == .windows) { return ".exe"; - } else if (self.isUefi()) { + } else if (self.os.tag == .uefi) { return ".efi"; - } else if (self.isWasm()) { + } else if (self.cpu.arch.isWasm()) { return ".wasm"; } else { return ""; @@ -1123,7 +1122,7 @@ pub const Target = struct { } pub fn staticLibSuffix(self: Target) []const u8 { - if (self.isWasm()) { + if (self.cpu.arch.isWasm()) { return ".wasm"; } switch (self.abi) { @@ -1143,7 +1142,7 @@ pub const Target = struct { } pub fn libPrefix(self: Target) []const u8 { - if (self.isWasm()) { + if (self.cpu.arch.isWasm()) { return ""; } switch (self.abi) { @@ -1153,19 +1152,19 @@ pub const Target = struct { } pub fn getObjectFormat(self: Target) ObjectFormat { - if (self.isWindows() or self.isUefi()) { + if (self.os.tag == .windows or self.os.tag == .uefi) { return .coff; } else if (self.isDarwin()) { return .macho; } - if (self.isWasm()) { + 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 { @@ -1176,27 +1175,6 @@ pub const Target = struct { return self.abi.isMusl(); } - pub fn isDarwin(self: Target) bool { - return switch (self.os.tag) { - .ios, .macosx, .watchos, .tvos => true, - else => false, - }; - } - - pub fn isWindows(self: Target) bool { - return switch (self.os.tag) { - .windows => true, - else => false, - }; - } - - pub fn isLinux(self: Target) bool { - return switch (self.os.tag) { - .linux => true, - else => false, - }; - } - pub fn isAndroid(self: Target) bool { return switch (self.abi) { .android => true, @@ -1204,36 +1182,12 @@ pub const Target = struct { }; } - pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.os.tag) { - .dragonfly => true, - else => false, - }; - } - - pub fn isUefi(self: Target) bool { - return switch (self.os.tag) { - .uefi => true, - else => false, - }; - } - pub fn isWasm(self: Target) bool { return self.cpu.arch.isWasm(); } - pub fn isFreeBSD(self: Target) bool { - return switch (self.os.tag) { - .freebsd => true, - else => false, - }; - } - - pub fn isNetBSD(self: Target) bool { - return switch (self.os.tag) { - .netbsd => true, - else => false, - }; + pub fn isDarwin(self: Target) bool { + return self.os.tag.isDarwin(); } pub fn isGnuLibC(self: Target) bool { @@ -1241,11 +1195,11 @@ pub const Target = struct { } pub fn wantSharedLibSymLinks(self: Target) bool { - return !self.isWindows(); + return self.os.tag != .windows; } pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.isFreeBSD() or self.isNetBSD(); + return self.isDarwin() or self.os.tag == .freebsd or self.os.tag == .netbsd; } pub fn getArchPtrBitWidth(self: Target) u32 { @@ -1309,7 +1263,7 @@ pub const Target = struct { } pub fn supportsNewStackCall(self: Target) bool { - return !self.isWasm(); + return !self.cpu.arch.isWasm(); } pub const Executor = union(enum) { @@ -1321,8 +1275,6 @@ pub const Target = struct { }; 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.os.tag == builtin.os.tag) { return switch (self.cpu.arch) { @@ -1347,22 +1299,18 @@ pub const Target = struct { }; } - if (self.isWindows()) { - switch (self.getArchPtrBitWidth()) { + switch (self.os.tag) { + .windows => switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wine = "wine" }, 64 => return Executor{ .wine = "wine64" }, else => return .unavailable, - } - } - - if (self.os == .wasi) { - switch (self.getArchPtrBitWidth()) { + }, + .wasi => switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, - } + }, + else => return .unavailable, } - - return .unavailable; } pub const FloatAbi = enum { @@ -1566,12 +1514,12 @@ test "Target.parse" { std.testing.expect(target.cpu.arch == .aarch64); std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.os.version_range.linux.min.major == 3); - std.testing.expect(target.os.version_range.linux.min.minor == 10); - std.testing.expect(target.os.version_range.linux.min.patch == 0); - std.testing.expect(target.os.version_range.linux.max.major == 4); - std.testing.expect(target.os.version_range.linux.max.minor == 4); - std.testing.expect(target.os.version_range.linux.max.patch == 1); + std.testing.expect(target.os.version_range.linux.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); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index d617abb821..41def38126 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -7,11 +7,7 @@ const Allocator = std.mem.Allocator; const Batch = std.event.Batch; const is_darwin = Target.current.isDarwin(); -const is_windows = Target.current.isWindows(); -const is_freebsd = Target.current.isFreeBSD(); -const is_netbsd = Target.current.isNetBSD(); -const is_linux = Target.current.isLinux(); -const is_dragonfly = Target.current.isDragonFlyBSD(); +const is_windows = Target.current.os.tag == .windows; const is_gnu = Target.current.isGnu(); usingnamespace @import("windows_sdk.zig"); @@ -216,10 +212,10 @@ pub const LibCInstallation = struct { var batch = Batch(FindError!void, 2, .auto_async).init(); errdefer batch.wait() catch {}; batch.add(&async self.findNativeIncludeDirPosix(args)); - if (is_freebsd or is_netbsd) { - self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"); - } else if (is_linux or is_dragonfly) { - batch.add(&async self.findNativeCrtDirPosix(args)); + switch (Target.current.os.tag) { + .freebsd, .netbsd => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"), + .linux, .dragonfly => batch.add(&async self.findNativeCrtDirPosix(args)), + else => {}, } break :blk batch.wait(); }; diff --git a/test/assemble_and_link.zig b/test/assemble_and_link.zig index 8c727e87b5..86209bd034 100644 --- a/test/assemble_and_link.zig +++ b/test/assemble_and_link.zig @@ -1,8 +1,8 @@ -const builtin = @import("builtin"); +const std = @import("std"); const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { - if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { + if (std.Target.current.os.tag == .linux and std.Target.current.cpu.arch == .x86_64) { cases.addAsm("hello world linux x86_64", \\.text \\.globl _start diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 91c17d8807..7c1eb6d409 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,5 +1,4 @@ const tests = @import("tests.zig"); -const builtin = @import("builtin"); const Target = @import("std").Target; pub fn addCases(cases: *tests.CompileErrorContext) void { @@ -387,10 +386,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", }); - tc.target = Target{ + tc.target = tests.Target{ .Cross = .{ .cpu = Target.Cpu.baseline(.wasm32), - .os = .wasi, + .os = Target.Os.defaultVersionRange(.wasi), .abi = .none, }, }; @@ -788,10 +787,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", }); - tc.target = Target{ + tc.target = tests.Target{ .Cross = .{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }; @@ -1453,7 +1452,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", }); - if (builtin.os == builtin.Os.linux) { + if (Target.current.os.tag == .linux) { cases.addTest("implicit dependency on libc", \\extern "c" fn exit(u8) void; \\export fn entry() void { diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 21aa92537c..968f09eeb8 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -19,7 +19,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, - target: std.Target = .Native, + target: build.Target = .Native, const SourceFile = struct { filename: []const u8, @@ -75,7 +75,7 @@ pub const TranslateCContext = struct { pub fn addWithTarget( self: *TranslateCContext, name: []const u8, - target: std.Target, + target: build.Target, source: []const u8, expected_lines: []const []const u8, ) void { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index fd4ff69964..ab1156c3cb 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); @@ -43,32 +42,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\} ; - switch (builtin.os) { + switch (builtin.os.tag) { .freebsd => { cases.addCase( "return", source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -77,7 +76,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -87,7 +86,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -97,11 +96,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -110,7 +109,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_try_return_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -126,7 +125,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -142,11 +141,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -157,25 +156,25 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -184,7 +183,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -194,7 +193,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -204,11 +203,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -217,7 +216,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_try_return_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -233,7 +232,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -249,11 +248,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -278,11 +277,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -311,11 +310,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -356,11 +355,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -371,7 +370,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test.obj) \\ return error.TheSkyIsFalling; \\ ^ @@ -381,11 +380,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -407,11 +406,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -439,11 +438,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 65a7d9e1f1..e657b5472b 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -529,7 +529,7 @@ test "comptime_int xor" { } test "f128" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -631,7 +631,7 @@ test "NaN comparison" { // TODO: https://github.com/ziglang/zig/issues/3338 return error.SkipZigTest; } - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -666,14 +666,14 @@ test "128-bit multiplication" { test "vector comparison" { const S = struct { fn doTheTest() void { - var a: @Vector(6, i32) = [_]i32{1, 3, -1, 5, 7, 9}; - var b: @Vector(6, i32) = [_]i32{-1, 3, 0, 6, 10, -10}; - expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{false, false, true, true, true, false})); - expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{false, true, true, true, true, false})); - expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{false, true, false, false, false, false})); - expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{true, false, true, true, true, true})); - expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{true, false, false, false, false, true})); - expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{true, true, false, false, false, true})); + var a: @Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; + var b: @Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; + expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); + expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); + expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); + expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); + expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); + expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); } }; S.doTheTest(); diff --git a/test/standalone.zig b/test/standalone.zig index 2c5b9c790e..aec9c82726 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -18,10 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); cases.addBuildFile("test/standalone/empty_env/build.zig"); - if (std.Target.current.getOs() != .wasi) { + if (std.Target.current.os.tag != .wasi) { cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); } - if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures + if (std.Target.current.cpu.arch == .x86_64) { // TODO add C ABI support for other architectures cases.addBuildFile("test/stage1/c_abi/build.zig"); } } diff --git a/test/tests.zig b/test/tests.zig index c088aafc5f..78eaf56273 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1,16 +1,15 @@ const std = @import("std"); +const builtin = std.builtin; const debug = std.debug; const warn = debug.warn; const build = std.build; pub const Target = build.Target; -pub const CrossTarget = build.CrossTarget; const Buffer = std.Buffer; const io = std.io; const fs = std.fs; const mem = std.mem; const fmt = std.fmt; const ArrayList = std.ArrayList; -const builtin = @import("builtin"); const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; @@ -54,18 +53,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -73,9 +72,9 @@ const test_targets = blk: { }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -84,18 +83,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -104,18 +103,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -123,9 +122,9 @@ const test_targets = blk: { }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -133,21 +132,25 @@ const test_targets = blk: { }, TestTarget{ - .target = Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .Cross = std.Target.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, }, TestTarget{ - .target = Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .Cross = std.Target.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, .link_libc = true, }, // TODO https://github.com/ziglang/zig/issues/3287 //TestTarget{ - // .target = Target.parse(.{ + // .target = std.Target.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -156,18 +159,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.mipsel), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.mipsel), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.mipsel), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.mipsel), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -176,9 +179,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.macosx), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.macosx), .abi = .gnu, }, }, @@ -188,9 +191,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -198,9 +201,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -208,9 +211,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -219,9 +222,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -438,7 +441,7 @@ pub fn addPkgTests( if (skip_libc and test_target.link_libc) continue; - if (test_target.link_libc and test_target.target.osRequiresLibC()) { + if (test_target.link_libc and test_target.target.getTarget().osRequiresLibC()) { // This would be a redundant test. continue; } @@ -448,8 +451,8 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and - test_target.target.getOs() == builtin.os and - test_target.target.getArch() == builtin.arch) + test_target.target.getOs() == std.Target.current.os.tag and + test_target.target.getArch() == std.Target.current.cpu.arch) { continue; } @@ -459,7 +462,7 @@ pub fn addPkgTests( } else false; if (!want_this_mode) continue; - const libc_prefix = if (test_target.target.osRequiresLibC()) + const libc_prefix = if (test_target.target.getTarget().osRequiresLibC()) "" else if (test_target.link_libc) "c" @@ -469,7 +472,7 @@ pub fn addPkgTests( const triple_prefix = if (test_target.target == .Native) @as([]const u8, "native") else - test_target.target.zigTripleNoSubArch(b.allocator) catch unreachable; + test_target.target.zigTriple(b.allocator) catch unreachable; const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; @@ -660,7 +663,7 @@ pub const StackTracesContext = struct { const delims = [_][]const u8{ ":", ":", ":", " in " }; var marks = [_]usize{0} ** 4; // offset search past `[drive]:` on windows - var pos: usize = if (builtin.os == .windows) 2 else 0; + var pos: usize = if (std.Target.current.os.tag == .windows) 2 else 0; for (delims) |delim, i| { marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { try buf.append(line); diff --git a/test/translate_c.zig b/test/translate_c.zig index 701513153c..07364fb032 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -const builtin = @import("builtin"); -const Target = @import("std").Target; +const std = @import("std"); +const Target = std.Target; pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro line continuation", @@ -665,7 +665,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != builtin.Os.windows) { + if (Target.current.os.tag != .windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", \\enum EnumWithInits { @@ -1064,7 +1064,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != builtin.Os.windows) { + if (Target.current.os.tag != .windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", \\void __attribute__((sysv_abi)) foo(void); @@ -1093,10 +1093,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const fn1 = ?fn (u8) callconv(.C) void; }); - cases.addWithTarget("Calling convention", tests.Target{ + cases.addWithTarget("Calling convention", .{ .Cross = .{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -1113,10 +1113,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void; }); - cases.addWithTarget("Calling convention", Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, + cases.addWithTarget("Calling convention", .{ + .Cross = Target.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, + }, \\void __attribute__((pcs("aapcs"))) foo1(float *a); \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a); , &[_][]const u8{ @@ -1124,10 +1126,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; }); - cases.addWithTarget("Calling convention", Target.parse(.{ - .arch_os_abi = "aarch64-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, + cases.addWithTarget("Calling convention", .{ + .Cross = Target.parse(.{ + .arch_os_abi = "aarch64-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, + }, \\void __attribute__((aarch64_vector_pcs)) foo1(float *a); , &[_][]const u8{ \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void; @@ -1596,7 +1600,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != .windows) { + if (Target.current.os.tag != .windows) { // When clang uses the -windows-none triple it behaves as MSVC and // interprets the inner `struct Bar` as an anonymous structure cases.add("type referenced struct", From dbe4d72bcfb20fc43713781679a0d23aea0a17d9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 01:18:23 -0500 Subject: [PATCH 05/37] separate std.Target and std.zig.CrossTarget Zig now supports a more fine-grained sense of what is native and what is not. Some examples: This is now allowed: -target native Different OS but native CPU, default Windows C ABI: -target native-windows This could be useful for example when running in Wine. Different CPU but native OS, native C ABI. -target x86_64-native -mcpu=skylake Different C ABI but otherwise native target: -target native-native-musl -target native-native-gnu Lots of breaking changes to related std lib APIs. Calls to getOs() will need to be changed to getOsTag(). Calls to getArch() will need to be changed to getCpuArch(). Usage of Target.Cross and Target.Native need to be updated to use CrossTarget API. `std.build.Builder.standardTargetOptions` is changed to accept its parameters as a struct with default values. It now has the ability to specify a whitelist of targets allowed, as well as the default target. Rather than two different ways of collecting the target, it's now always a string that is validated, and prints helpful diagnostics for invalid targets. This feature should now be actually useful, and contributions welcome to further improve the user experience. `std.build.LibExeObjStep.setTheTarget` is removed. `std.build.LibExeObjStep.setTarget` is updated to take a CrossTarget parameter. `std.build.LibExeObjStep.setTargetGLibC` is removed. glibc versions are handled in the CrossTarget API and can be specified with the `-target` triple. `std.builtin.Version` gains a `format` method. --- build.zig | 2 +- doc/docgen.zig | 4 +- lib/std/build.zig | 298 +++++-------- lib/std/build/translate_c.zig | 14 +- lib/std/builtin.zig | 23 + lib/std/target.zig | 695 +++++++++--------------------- lib/std/testing.zig | 10 +- lib/std/zig.zig | 9 +- lib/std/zig/cross_target.zig | 766 ++++++++++++++++++++++++++++++++++ src-self-hosted/stage2.zig | 143 ++++--- test/compile_errors.zig | 24 +- test/src/translate_c.zig | 5 +- test/tests.zig | 236 +++++------ test/translate_c.zig | 36 +- 14 files changed, 1307 insertions(+), 958 deletions(-) create mode 100644 lib/std/zig/cross_target.zig diff --git a/build.zig b/build.zig index e1ae5cf3cd..32694afb0a 100644 --- a/build.zig +++ b/build.zig @@ -298,7 +298,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { } dependOnLib(b, exe, ctx.llvm); - if (exe.target.getOs() == .linux) { + if (exe.target.getOsTag() == .linux) { try addCxxKnownPath(b, ctx, exe, "libstdc++.a", \\Unable to determine path to libstdc++.a \\On Fedora, install libstdc++-static and try again. diff --git a/doc/docgen.zig b/doc/docgen.zig index b429c93e65..9b8aca18d0 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -10,8 +10,8 @@ const testing = std.testing; const max_doc_file_size = 10 * 1024 * 1024; -const exe_ext = @as(std.build.Target, std.build.Target.Native).exeFileExt(); -const obj_ext = @as(std.build.Target, std.build.Target.Native).oFileExt(); +const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt(); +const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt(); const tmp_dir_name = "docgen_tmp"; const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; diff --git a/lib/std/build.zig b/lib/std/build.zig index 25f7d536b1..92b06a0261 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,77 @@ 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 = .{}, + }; + + /// 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 Arch, OS, and ABI to build for.", + ) orelse return args.default_target; + + // TODO add cpu and features as part of the target triple + + var diags: std.Target.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); + }, + else => |e| return e, + }; + + 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 +850,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)) { @@ -978,111 +1032,11 @@ test "builder.findProgram compiles" { _ = 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`. -pub const CrossTarget = std.Target; - -/// Wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. -pub const Target = union(enum) { - Native, - Cross: std.Target, - - pub fn getTarget(self: Target) std.Target { - return switch (self) { - .Native => std.Target.current, - .Cross => |t| t, - }; - } - - pub fn getOs(self: Target) std.Target.Os.Tag { - return self.getTarget().os.tag; - } - - pub fn getCpu(self: Target) std.Target.Cpu { - return self.getTarget().cpu; - } - - pub fn getAbi(self: Target) std.Target.Abi { - return self.getTarget().abi; - } - - pub fn getArch(self: Target) std.Target.Cpu.Arch { - return self.getCpu().arch; - } - - pub fn isFreeBSD(self: Target) bool { - return self.getTarget().os.tag == .freebsd; - } - - pub fn isDarwin(self: Target) bool { - return self.getTarget().os.tag.isDarwin(); - } - - pub fn isNetBSD(self: Target) bool { - return self.getTarget().os.tag == .netbsd; - } - - pub fn isUefi(self: Target) bool { - return self.getTarget().os.tag == .uefi; - } - - pub fn isDragonFlyBSD(self: Target) bool { - return self.getTarget().os.tag == .dragonfly; - } - - pub fn isLinux(self: Target) bool { - return self.getTarget().os.tag == .linux; - } - - pub fn isWindows(self: Target) bool { - return self.getTarget().os.tag == .windows; - } - - pub fn oFileExt(self: Target) []const u8 { - return self.getTarget().oFileExt(); - } - - pub fn exeFileExt(self: Target) []const u8 { - return self.getTarget().exeFileExt(); - } - - pub fn staticLibSuffix(self: Target) []const u8 { - return self.getTarget().staticLibSuffix(); - } - - pub fn libPrefix(self: Target) []const u8 { - return self.getTarget().libPrefix(); - } - - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return self.getTarget().zigTriple(allocator); - } - - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return self.getTarget().linuxTriple(allocator); - } - - pub fn wantSharedLibSymLinks(self: Target) bool { - return self.getTarget().wantSharedLibSymLinks(); - } - - pub fn vcpkgTriplet(self: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { - return self.getTarget().vcpkgTriplet(allocator, linkage); - } - - pub fn getExternalExecutor(self: Target) std.Target.Executor { - switch (self) { - .Native => return .native, - .Cross => |t| return t.getExternalExecutor(), - } - } - - pub fn isGnuLibC(self: Target) bool { - return self.getTarget().isGnuLibC(); - } -}; +/// Deprecated. Use `std.zig.CrossTarget`. +pub const Target = std.zig.CrossTarget; pub const Pkg = struct { name: []const u8, @@ -1135,7 +1089,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, @@ -1188,7 +1142,6 @@ pub const LibExeObjStep = struct { install_step: ?*InstallArtifactStep, libc_file: ?[]const u8 = null, - target_glibc: ?Version = null, valgrind_support: ?bool = null, @@ -1288,7 +1241,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, @@ -1379,36 +1331,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); } @@ -2002,47 +1929,41 @@ 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 != std.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(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()); + 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); - if (self.target_glibc) |ver| { - try zig_args.append("-target-glibc"); - try zig_args.append(builder.fmt("{}.{}.{}", .{ ver.major, ver.minor, ver.patch })); + 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()); + } } if (self.linker_script) |linker_script| { @@ -2517,10 +2438,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/translate_c.zig b/lib/std/build/translate_c.zig index 90e95be5e1..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: build.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: build.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 3204ce905e..de37fda903 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -429,6 +429,29 @@ pub const Version = struct { .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 diff --git a/lib/std/target.zig b/lib/std/target.zig index 9007771c1a..440f50b811 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -60,6 +60,16 @@ pub const Target = struct { 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 @@ -210,64 +220,31 @@ pub const Target = struct { } }; - pub fn parse(text: []const u8) !Os { - var it = mem.separate(text, "."); - const os_name = it.next().?; - const tag = std.meta.stringToEnum(Tag, os_name) orelse return error.UnknownOperatingSystem; - const version_text = it.rest(); - const S = struct { - fn parseNone(s: []const u8) !void { - if (s.len != 0) return error.InvalidOperatingSystemVersion; - } - fn parseSemVer(s: []const u8, d_range: Version.Range) !Version.Range { - if (s.len == 0) return d_range; - var range_it = mem.separate(s, "..."); - - const min_text = range_it.next().?; - const min_ver = Version.parse(min_text) catch |err| switch (err) { - error.Overflow => return error.InvalidOperatingSystemVersion, - error.InvalidCharacter => return error.InvalidOperatingSystemVersion, - error.InvalidVersion => return error.InvalidOperatingSystemVersion, - }; - - const max_text = range_it.next() orelse return Version.Range{ - .min = min_ver, - .max = d_range.max, - }; - const max_ver = Version.parse(max_text) catch |err| switch (err) { - error.Overflow => return error.InvalidOperatingSystemVersion, - error.InvalidCharacter => return error.InvalidOperatingSystemVersion, - error.InvalidVersion => return error.InvalidOperatingSystemVersion, - }; - - return Version.Range{ .min = min_ver, .max = max_ver }; - } - fn parseWindows(s: []const u8, d_range: WindowsVersion.Range) !WindowsVersion.Range { - if (s.len == 0) return d_range; - var range_it = mem.separate(s, "..."); - - const min_text = range_it.next().?; - const min_ver = std.meta.stringToEnum(WindowsVersion, min_text) orelse - return error.InvalidOperatingSystemVersion; - - const max_text = range_it.next() orelse return WindowsVersion.Range{ - .min = min_ver, - .max = d_range.max, - }; - const max_ver = std.meta.stringToEnum(WindowsVersion, max_text) orelse - return error.InvalidOperatingSystemVersion; - - return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; - } + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), }; - const d_range = VersionRange.default(tag); - switch (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, - .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -282,8 +259,6 @@ pub const Target = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -293,41 +268,7 @@ pub const Target = struct { .emscripten, .uefi, .other, - => return Os{ - .tag = tag, - .version_range = .{ .none = try S.parseNone(version_text) }, - }, - - .freebsd, - .macosx, - .netbsd, - .openbsd, - => return Os{ - .tag = tag, - .version_range = .{ .semver = try S.parseSemVer(version_text, d_range.semver) }, - }, - - .linux => return Os{ - .tag = tag, - .version_range = .{ - .linux = .{ - .range = try S.parseSemVer(version_text, d_range.linux.range), - .glibc = d_range.linux.glibc, - }, - }, - }, - - .windows => return Os{ - .tag = tag, - .version_range = .{ .windows = try S.parseWindows(version_text, d_range.windows) }, - }, - } - } - - pub fn defaultVersionRange(tag: Tag) Os { - return .{ - .tag = tag, - .version_range = VersionRange.default(tag), + => false, }; } }; @@ -434,6 +375,13 @@ pub const Target = struct { else => false, }; } + + pub fn oFileExt(abi: Abi) [:0]const u8 { + return switch (abi) { + .msvc => ".obj", + else => ".o", + }; + } }; pub const ObjectFormat = enum { @@ -500,6 +448,12 @@ pub const Target = struct { 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)); @@ -526,6 +480,15 @@ pub const Target = struct { 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); @@ -663,7 +626,7 @@ pub const Target = struct { return cpu; } } - return error.UnknownCpu; + return error.UnknownCpuModel; } pub fn toElfMachine(arch: Arch) std.elf.EM { @@ -779,6 +742,66 @@ pub const Target = struct { }; } + 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) { @@ -846,16 +869,6 @@ pub const Target = struct { 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 { @@ -872,41 +885,44 @@ pub const Target = struct { .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); } }; @@ -918,239 +934,70 @@ pub const Target = struct { pub const stack_align = 16; - /// TODO add OS version ranges and glibc version - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator); } - /// Returned slice must be freed by the caller. - pub fn vcpkgTriplet(target: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { - const arch = switch (target.cpu.arch) { - .i386 => "x86", - .x86_64 => "x64", + 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) }); + } - .arm, - .armeb, - .thumb, - .thumbeb, - .aarch64_32, - => "arm", + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); + } - .aarch64, - .aarch64_be, - => "arm64", + pub fn oFileExt(self: Target) [:0]const u8 { + return self.abi.oFileExt(); + } - else => return error.VcpkgNoSuchArchitecture, - }; - - const os = switch (target.os) { - .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 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 ""; + }, } } - 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 exeFileExt(self: Target) [:0]const u8 { + return exeFileExtSimple(self.cpu.arch, self.os.tag); } - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); - } - - 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; - var os = try Os.parse(os_name); - diags.os = os; - - const opt_abi_text = it.next(); - const abi = if (opt_abi_text) |abi_text| blk: { - var abi_it = mem.separate(abi_text, "."); - const abi = std.meta.stringToEnum(Abi, abi_it.next().?) orelse - return error.UnknownApplicationBinaryInterface; - const abi_ver_text = abi_it.rest(); - if (abi_ver_text.len != 0) { - if (os.tag == .linux and abi.isGnu()) { - os.version_range.linux.glibc = Version.parse(abi_ver_text) catch |err| switch (err) { - error.Overflow => return error.InvalidAbiVersion, - error.InvalidCharacter => return error.InvalidAbiVersion, - error.InvalidVersion => return error.InvalidAbiVersion, - }; - } else { - return error.InvalidAbiVersion; - } - } - break :blk abi; - } else Abi.default(arch, os); - diags.abi = abi; - - 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; - } - 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, - }; - }; - return Target{ - .cpu = cpu, - .os = os, - .abi = abi, - }; - } - - pub fn oFileExt(self: Target) []const u8 { - return switch (self.abi) { - .msvc => ".obj", - else => ".o", - }; - } - - pub fn exeFileExt(self: Target) []const u8 { - if (self.os.tag == .windows) { - return ".exe"; - } else if (self.os.tag == .uefi) { - return ".efi"; - } else if (self.cpu.arch.isWasm()) { - return ".wasm"; - } else { - return ""; - } - } - - pub fn staticLibSuffix(self: Target) []const u8 { - if (self.cpu.arch.isWasm()) { + pub fn staticLibSuffix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ".wasm"; } - switch (self.abi) { + switch (abi) { .msvc => return ".lib", else => return ".a", } } - pub fn dynamicLibSuffix(self: Target) []const u8 { - if (self.isDarwin()) { - return ".dylib"; - } - switch (self.os) { - .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 libPrefix(self: Target) []const u8 { - if (self.cpu.arch.isWasm()) { + pub fn dynamicLibSuffix(self: Target) [:0]const u8 { + return self.os.tag.dynamicLibSuffix(); + } + + pub fn libPrefix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ""; } - switch (self.abi) { + switch (abi) { .msvc => return "", else => return "lib", } } + pub fn libPrefix(self: Target) [:0]const u8 { + return libPrefix_cpu_arch_abi(self.cpu.arch, self.abi); + } + pub fn getObjectFormat(self: Target) ObjectFormat { if (self.os.tag == .windows or self.os.tag == .uefi) { return .coff; @@ -1190,129 +1037,18 @@ pub const Target = struct { return self.os.tag.isDarwin(); } + pub fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool { + return os_tag == .linux and abi.isGnu(); + } + pub fn isGnuLibC(self: Target) bool { - return self.os.tag == .linux and self.abi.isGnu(); - } - - pub fn wantSharedLibSymLinks(self: Target) bool { - return self.os.tag != .windows; - } - - pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.os.tag == .freebsd or self.os.tag == .netbsd; - } - - pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.cpu.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, - } + return isGnuLibC_os_tag_abi(self.os.tag, self.abi); } pub fn supportsNewStackCall(self: Target) bool { return !self.cpu.arch.isWasm(); } - pub const Executor = union(enum) { - native, - qemu: []const u8, - wine: []const u8, - wasmtime: []const u8, - unavailable, - }; - - pub fn getExternalExecutor(self: Target) Executor { - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (self.os.tag == builtin.os.tag) { - return switch (self.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 (self.os.tag) { - .windows => switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wine = "wine" }, - 64 => return Executor{ .wine = "wine64" }, - else => return .unavailable, - }, - .wasi => switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wasmtime = "wasmtime" }, - else => return .unavailable, - }, - else => return .unavailable, - } - } - pub const FloatAbi = enum { hard, soft, @@ -1359,7 +1095,7 @@ pub const Target = struct { }![:0]u8 { const a = allocator; if (self.isAndroid()) { - return mem.dupeZ(a, u8, if (self.getArchPtrBitWidth() == 64) + return mem.dupeZ(a, u8, if (self.cpu.arch.ptrBitWidth() == 64) "/system/bin/linker64" else "/system/bin/linker"); @@ -1477,52 +1213,3 @@ pub const Target = struct { } } }; - -test "Target.parse" { - { - const target = try Target.parse(.{ - .arch_os_abi = "x86_64-linux-gnu", - .cpu_features = "x86_64-sse-sse2-avx-cx8", - }); - - 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 target = try Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }); - - 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 target = try Target.parse(.{ - .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", - .cpu_features = "generic+v8a", - }); - - std.testing.expect(target.cpu.arch == .aarch64); - std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.os.version_range.linux.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); - } -} 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/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..e785c40073 --- /dev/null +++ b/lib/std/zig/cross_target.zig @@ -0,0 +1,766 @@ +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. +pub const CrossTarget = struct { + /// `null` means native. + cpu_arch: ?Target.Cpu.Arch = null, + + /// If `cpu_arch` is native, `null` means native. Otherwise it means baseline. + /// If this is non-null, `cpu_arch` must be specified. + cpu_model: ?*const Target.Cpu.Model = null, + + /// Sparse set of CPU features to add to the set from `cpu_model`. + /// If this is non-empty, `cpu_arch` must be specified. + 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`. + /// If this is non-empty, `cpu_arch` must be specified. + 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 the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. + abi: ?Target.Abi = 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, + + pub const OsVersion = union(enum) { + none: void, + semver: SemVer, + windows: Target.Os.WindowsVersion, + }; + + pub const SemVer = std.builtin.Version; + + pub fn fromTarget(target: Target) CrossTarget { + var result: CrossTarget = .{ + .cpu_arch = target.cpu.arch, + .cpu_model = 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, + .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, + => { + self.os_version_min = .{ .none = {} }; + self.os_version_max = .{ .none = {} }; + }, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => { + 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 }; + }, + } + } + + 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, + + /// 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 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; + + // Start with everything initialized to default values. + var result: CrossTarget = .{}; + + 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; + 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 = null; + } else if (mem.eql(u8, cpu_name, "baseline")) { + result.cpu_model = Target.Cpu.Model.baseline(arch); + } else { + result.cpu_model = 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; + } + + pub fn getCpu(self: CrossTarget) Target.Cpu { + if (self.cpu_arch) |arch| { + if (self.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + self.updateCpuFeatures(&adjusted_model.features); + return adjusted_model; + } else { + var adjusted_baseline = Target.Cpu.baseline(arch); + self.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + } + } else { + assert(self.cpu_model == null); + assert(self.cpu_features_sub.isEmpty()); + assert(self.cpu_features_add.isEmpty()); + // 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; + } + } + + 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; + } + + 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; + } + + 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.?; + } + + 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.?; + } + + pub fn getAbi(self: CrossTarget) Target.Abi { + if (self.abi) |abi| return abi; + + if (self.isNativeOs()) { + // 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 isNativeCpu(self: CrossTarget) bool { + return self.cpu_arch == null and self.cpu_model == null and + self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); + } + + pub fn isNativeOs(self: CrossTarget) bool { + return self.os_tag == null and self.os_version_min == null and self.os_version_max == null; + } + + pub fn isNativeAbi(self: CrossTarget) bool { + return self.abi == null and self.glibc_version == null; + } + + pub fn isNative(self: CrossTarget) bool { + return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); + } + + 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.isNativeCpu()) "native" else @tagName(self.getCpuArch()); + 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.abi) |abi| { + try result.print("-{}", .{@tagName(abi)}); + if (self.glibc_version) |v| { + try result.print(".{}", .{v}); + } + } else { + assert(self.glibc_version == null); + } + + 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, + }; + + pub fn getExternalExecutor(self: CrossTarget) Executor { + const os_tag = self.getOsTag(); + const cpu_arch = self.getCpuArch(); + + // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. + if (os_tag == Target.current.os.tag) { + 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().?; + 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" { + { + 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/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 7f0f35abe0..ecaad0daf2 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -10,6 +10,7 @@ const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const Buffer = std.Buffer; const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; const self_hosted_main = @import("main.zig"); const errmsg = @import("errmsg.zig"); const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; @@ -87,7 +88,7 @@ const Error = extern enum { NotLazy, IsAsync, ImportOutsidePkgPath, - UnknownCpu, + UnknownCpuModel, UnknownCpuFeature, InvalidCpuFeatures, InvalidLlvmCpuFeaturesFormat, @@ -634,13 +635,9 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { } fn cmdTargets(zig_triple: [*:0]const u8) !void { - var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); - target.cpu = blk: { - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.cpu.arch, llvm_cpu_name, llvm_cpu_features); - }; + var cross_target = try CrossTarget.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); + var dynamic_linker: ?[*:0]u8 = null; + const target = try crossTargetToTarget(cross_target, &dynamic_linker); return @import("print_targets.zig").cmdTargets( std.heap.c_allocator, &[0][]u8{}, @@ -661,7 +658,6 @@ export fn stage2_target_parse( error.UnknownOperatingSystem => return .UnknownOperatingSystem, error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface, error.MissingOperatingSystem => return .MissingOperatingSystem, - error.MissingArchitecture => return .MissingArchitecture, error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, error.UnexpectedExtraField => return .SemanticAnalyzeFail, error.InvalidAbiVersion => return .InvalidAbiVersion, @@ -681,44 +677,42 @@ fn stage2TargetParse( zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, ) !void { - const target: std.build.Target = if (zig_triple_oz) |zig_triple_z| blk: { + const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; - var diags: std.Target.ParseOptions.Diagnostics = .{}; - break :blk std.build.Target{ - .Cross = Target.parse(.{ - .arch_os_abi = zig_triple, - .cpu_features = mcpu, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpu => { - 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); - }, - else => |e| return e, + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; + break :blk CrossTarget.parse(.{ + .arch_os_abi = zig_triple, + .cpu_features = mcpu, + .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); + }, + else => |e| return e, }; - } else std.build.Target.Native; + } else .{}; try stage1_target.fromTarget(target); } @@ -908,8 +902,8 @@ const Stage2Target = extern struct { dynamic_linker: ?[*:0]const u8, - fn toTarget(in_target: Stage2Target) std.build.Target { - if (in_target.is_native) return .Native; + fn toTarget(in_target: Stage2Target) CrossTarget { + if (in_target.is_native) return .{}; const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch const in_os = in_target.os; @@ -924,28 +918,11 @@ const Stage2Target = extern struct { }; } - fn fromTarget(self: *Stage2Target, build_target: std.build.Target) !void { + fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void { const allocator = std.heap.c_allocator; - var dynamic_linker: ?[*:0]u8 = null; - const target = switch (build_target) { - .Native => blk: { - const info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (info.dynamic_linker) |dl| { - dynamic_linker = dl.ptr; - } - // TODO we want to just use info.target but implementing CPU model & feature detection is todo - // so here we rely on LLVM - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const arch = std.Target.current.cpu.arch; - var t = info.target; - t.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - break :blk t; - }, - .Cross => |t| t, - }; + var dynamic_linker: ?[*:0]u8 = null; + const target = try crossTargetToTarget(cross_target, &dynamic_linker); var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{ target.cpu.model.name, @@ -1145,7 +1122,7 @@ const Stage2Target = extern struct { .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr, .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, .cache_hash = cache_hash.toOwnedSlice().ptr, - .is_native = build_target == .Native, + .is_native = cross_target.isNative(), .glibc_version = glibc_version, .dynamic_linker = dynamic_linker, }; @@ -1156,6 +1133,40 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { return @intToEnum(Enum, @intCast(@TagType(Enum), int)); } +/// TODO move dynamic linker to be part of the target +/// TODO self-host this function +fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { + var adjusted_target = cross_target.toTarget(); + if (cross_target.isNativeCpu() or cross_target.isNativeOs()) { + const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); + if (cross_target.isNativeCpu()) { + adjusted_target.cpu = detected_info.target.cpu; + + // TODO We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + // There is another occurrence of this; search for detectNativeCpuWithLLVM. + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + const arch = std.Target.current.cpu.arch; + adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + } + if (cross_target.isNativeOs()) { + adjusted_target.os = detected_info.target.os; + + if (detected_info.dynamic_linker) |dl| { + dynamic_linker_ptr.* = dl.ptr; + } + if (cross_target.abi == null) { + adjusted_target.abi = detected_info.target.abi; + } + } else if (cross_target.abi == null) { + adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); + } + } + return adjusted_target; +} + // ABI warning const Stage2GLibCVersion = extern struct { major: u32, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7c1eb6d409..979bf45bbe 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,5 +1,5 @@ const tests = @import("tests.zig"); -const Target = @import("std").Target; +const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.addTest("type mismatch with tuple concatenation", @@ -386,12 +386,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", }); - tc.target = tests.Target{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.wasm32), - .os = Target.Os.defaultVersionRange(.wasi), - .abi = .none, - }, + tc.target = std.zig.CrossTarget{ + .cpu_arch = .wasm32, + .os_tag = .wasi, + .abi = .none, }; break :x tc; }); @@ -787,12 +785,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", }); - tc.target = tests.Target{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + tc.target = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .gnu, }; break :x tc; }); @@ -1452,7 +1448,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", }); - if (Target.current.os.tag == .linux) { + if (std.Target.current.os.tag == .linux) { cases.addTest("implicit dependency on libc", \\extern "c" fn exit(u8) void; \\export fn entry() void { diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 968f09eeb8..9a6bd0d323 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -7,6 +7,7 @@ const fmt = std.fmt; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; +const CrossTarget = std.zig.CrossTarget; pub const TranslateCContext = struct { b: *build.Builder, @@ -19,7 +20,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, - target: build.Target = .Native, + target: CrossTarget = CrossTarget{}, const SourceFile = struct { filename: []const u8, @@ -75,7 +76,7 @@ pub const TranslateCContext = struct { pub fn addWithTarget( self: *TranslateCContext, name: []const u8, - target: build.Target, + target: CrossTarget, source: []const u8, expected_lines: []const []const u8, ) void { diff --git a/test/tests.zig b/test/tests.zig index 78eaf56273..9cf4e7bd98 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -3,7 +3,7 @@ const builtin = std.builtin; const debug = std.debug; const warn = debug.warn; const build = std.build; -pub const Target = build.Target; +const CrossTarget = std.zig.CrossTarget; const Buffer = std.Buffer; const io = std.io; const fs = std.fs; @@ -30,7 +30,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; const TestTarget = struct { - target: build.Target = .Native, + target: CrossTarget = @as(CrossTarget, .{}), mode: builtin.Mode = .Debug, link_libc: bool = false, single_threaded: bool = false, @@ -52,105 +52,85 @@ const test_targets = blk: { }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .gnu, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, - }, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, - }, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, TestTarget{ .target = .{ - .Cross = std.Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8a", - }) catch unreachable, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ .target = .{ - .Cross = std.Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, + + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .none, + }, + }, + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .gnu, + }, + .link_libc = true, + }, + + TestTarget{ + .target = CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, + TestTarget{ + .target = CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }) catch unreachable, + .link_libc = true, + }, // TODO https://github.com/ziglang/zig/issues/3287 //TestTarget{ - // .target = std.Target.parse(.{ + // .target = CrossTarget.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -158,75 +138,61 @@ const test_targets = blk: { //}, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.mipsel), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.mipsel), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.macosx), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .macosx, + .abi = .gnu, }, // TODO https://github.com/ziglang/zig/issues/3295 .disable_native = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .msvc, - }, + .target = .{ + .cpu_arch = .i386, + .os_tag = .windows, + .abi = .msvc, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .msvc, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .msvc, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .i386, + .os_tag = .windows, + .abi = .gnu, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, }, .link_libc = true, }, @@ -435,13 +401,13 @@ pub fn addPkgTests( const step = b.step(b.fmt("test-{}", .{name}), desc); for (test_targets) |test_target| { - if (skip_non_native and test_target.target != .Native) + if (skip_non_native and !test_target.target.isNative()) continue; if (skip_libc and test_target.link_libc) continue; - if (test_target.link_libc and test_target.target.getTarget().osRequiresLibC()) { + if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { // This would be a redundant test. continue; } @@ -451,8 +417,8 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and - test_target.target.getOs() == std.Target.current.os.tag and - test_target.target.getArch() == std.Target.current.cpu.arch) + test_target.target.getOsTag() == std.Target.current.os.tag and + test_target.target.getCpuArch() == std.Target.current.cpu.arch) { continue; } @@ -462,17 +428,14 @@ pub fn addPkgTests( } else false; if (!want_this_mode) continue; - const libc_prefix = if (test_target.target.getTarget().osRequiresLibC()) + const libc_prefix = if (test_target.target.getOs().requiresLibC()) "" else if (test_target.link_libc) "c" else "bare"; - const triple_prefix = if (test_target.target == .Native) - @as([]const u8, "native") - else - test_target.target.zigTriple(b.allocator) catch unreachable; + const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; @@ -486,7 +449,7 @@ pub fn addPkgTests( these_tests.single_threaded = test_target.single_threaded; these_tests.setFilter(test_filter); these_tests.setBuildMode(test_target.mode); - these_tests.setTheTarget(test_target.target); + these_tests.setTarget(test_target.target); if (test_target.link_libc) { these_tests.linkSystemLibrary("c"); } @@ -716,7 +679,7 @@ pub const CompileErrorContext = struct { link_libc: bool, is_exe: bool, is_test: bool, - target: Target = .Native, + target: CrossTarget = CrossTarget{}, const SourceFile = struct { filename: []const u8, @@ -808,12 +771,9 @@ pub const CompileErrorContext = struct { zig_args.append("--output-dir") catch unreachable; zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; - switch (self.case.target) { - .Native => {}, - .Cross => { - try zig_args.append("-target"); - try zig_args.append(try self.case.target.zigTriple(b.allocator)); - }, + if (!self.case.target.isNative()) { + try zig_args.append("-target"); + try zig_args.append(try self.case.target.zigTriple(b.allocator)); } switch (self.build_mode) { diff --git a/test/translate_c.zig b/test/translate_c.zig index 07364fb032..1d6c2a60ae 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); const std = @import("std"); -const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro line continuation", @@ -665,7 +665,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", \\enum EnumWithInits { @@ -1064,7 +1064,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", \\void __attribute__((sysv_abi)) foo(void); @@ -1094,11 +1094,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addWithTarget("Calling convention", .{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .none, }, \\void __attribute__((fastcall)) foo1(float *a); \\void __attribute__((stdcall)) foo2(float *a); @@ -1113,12 +1111,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void; }); - cases.addWithTarget("Calling convention", .{ - .Cross = Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, - }, + cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, \\void __attribute__((pcs("aapcs"))) foo1(float *a); \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a); , &[_][]const u8{ @@ -1126,12 +1122,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; }); - cases.addWithTarget("Calling convention", .{ - .Cross = Target.parse(.{ - .arch_os_abi = "aarch64-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, - }, + cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, \\void __attribute__((aarch64_vector_pcs)) foo1(float *a); , &[_][]const u8{ \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void; @@ -1600,7 +1594,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // When clang uses the -windows-none triple it behaves as MSVC and // interprets the inner `struct Bar` as an anonymous structure cases.add("type referenced struct", From c8669a4cf834a6d1dadd9260e94f1781ceed0ec3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:10 -0500 Subject: [PATCH 06/37] improve debug info for optionals --- src/analyze.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index ceb232c79d..2c8244d053 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -8719,7 +8719,6 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return; } - LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type); ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type); if (type->data.maybe.resolve_status >= wanted_resolve_status) return; @@ -8729,35 +8728,28 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus }; LLVMStructSetBody(type->llvm_type, elem_types, 2, false); - uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type); - uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type); - uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0); + uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index); + uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index); - uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_llvm_type); - uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_llvm_type); - uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1); - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type); - - ZigLLVMDIType *di_element_types[] = { + ZigLLVMDIType *di_element_types[2]; + di_element_types[maybe_child_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), "val", di_file, line, - val_debug_size_in_bits, - val_debug_align_in_bits, + 8 * child_type->abi_size, + 8 * child_type->abi_align, val_offset_in_bits, - ZigLLVM_DIFlags_Zero, child_llvm_di_type), + ZigLLVM_DIFlags_Zero, child_llvm_di_type); + di_element_types[maybe_null_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), "maybe", di_file, line, - maybe_debug_size_in_bits, - maybe_debug_align_in_bits, + 8*g->builtin_types.entry_bool->abi_size, + 8*g->builtin_types.entry_bool->abi_align, maybe_offset_in_bits, - ZigLLVM_DIFlags_Zero, bool_llvm_di_type), - }; + ZigLLVM_DIFlags_Zero, bool_llvm_di_type); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&type->name), - di_file, line, debug_size_in_bits, debug_align_in_bits, ZigLLVM_DIFlags_Zero, + di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero, nullptr, di_element_types, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); From cf233bad5884b9bd782256eb6808fcc6abda645c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:31 -0500 Subject: [PATCH 07/37] fix target parsing --- lib/std/zig/cross_target.zig | 15 +++++++++++++++ src-self-hosted/stage2.zig | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index e785c40073..6654635b89 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -177,6 +177,9 @@ pub const CrossTarget = 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, @@ -219,6 +222,7 @@ pub const CrossTarget = struct { 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(); @@ -614,6 +618,7 @@ pub const CrossTarget = struct { 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 @@ -702,6 +707,16 @@ pub const CrossTarget = struct { }; test "CrossTarget.parse" { + { + 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", diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index ecaad0daf2..b2963c8a71 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -679,7 +679,7 @@ fn stage2TargetParse( ) !void { const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); - const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; + const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null; var diags: CrossTarget.ParseOptions.Diagnostics = .{}; break :blk CrossTarget.parse(.{ .arch_os_abi = zig_triple, From cebcacd872a05d8ee3edaafe3eff5cc6e73657b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:36 -0500 Subject: [PATCH 08/37] fix standardTargetOptions and improve init-exe to use it --- lib/std/build.zig | 24 +++++++++++++++++++----- lib/std/special/init-exe/build.zig | 10 ++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 92b06a0261..792781df85 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -525,7 +525,7 @@ pub const Builder = struct { pub const StandardTargetOptionsArgs = struct { whitelist: ?[]const CrossTarget = null, - default_target: CrossTarget = .{}, + default_target: CrossTarget = CrossTarget{}, }; /// Exposes standard `zig build` options for choosing a target. @@ -533,12 +533,12 @@ pub const Builder = struct { const triple = self.option( []const u8, "target", - "The Arch, OS, and ABI to build for.", + "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: std.Target.ParseOptions.Diagnostics = .{}; + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; const selected_target = CrossTarget.parse(.{ .arch_os_abi = triple, .diagnostics = &diags, @@ -567,7 +567,21 @@ pub const Builder = struct { } process.exit(1); }, - else => |e| return e, + 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; @@ -585,7 +599,7 @@ pub const Builder = struct { }); for (list) |t| { const t_triple = t.zigTriple(self.allocator) catch unreachable; - std.debug.warn(" {}\n", t_triple); + 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 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(); From 0912484c4f1a8b278a4f04b3baa76cfa38daef6e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 15:35:48 -0500 Subject: [PATCH 09/37] improve the "external executor" detection logic --- lib/std/build.zig | 3 +-- lib/std/zig/cross_target.zig | 36 +++++++++++++++++------------------- src-self-hosted/stage2.zig | 6 +++--- test/stack_traces.zig | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 792781df85..23db4c3f8e 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1141,7 +1141,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, @@ -1273,7 +1273,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, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 6654635b89..1e16072815 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -383,7 +383,7 @@ pub const CrossTarget = struct { pub fn getAbi(self: CrossTarget) Target.Abi { if (self.abi) |abi| return abi; - if (self.isNativeOs()) { + 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`. @@ -441,21 +441,11 @@ pub const CrossTarget = struct { return Target.libPrefix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); } - pub fn isNativeCpu(self: CrossTarget) bool { - return self.cpu_arch == null and self.cpu_model == null and - self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); - } - - pub fn isNativeOs(self: CrossTarget) bool { - return self.os_tag == null and self.os_version_min == null and self.os_version_max == null; - } - - pub fn isNativeAbi(self: CrossTarget) bool { - return self.abi == null and self.glibc_version == null; - } - pub fn isNative(self: CrossTarget) bool { - return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); + return self.cpu_arch == null and self.cpu_model == null 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; } pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { @@ -463,7 +453,7 @@ pub const CrossTarget = struct { return mem.dupeZ(allocator, u8, "native"); } - const arch_name = if (self.isNativeCpu()) "native" else @tagName(self.getCpuArch()); + 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 }); @@ -557,12 +547,20 @@ pub const CrossTarget = struct { 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 os_tag = self.getOsTag(); const cpu_arch = self.getCpuArch(); + const os_tag = self.getOsTag(); + const os_match = os_tag == Target.current.os.tag; - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (os_tag == Target.current.os.tag) { + // If the OS matches, and the CPU arch matches, the binary is considered native. + if (self.os_tag == null and cpu_arch == Target.current.cpu.arch) { + 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" }, diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index b2963c8a71..351c6c5357 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1137,9 +1137,9 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { /// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var adjusted_target = cross_target.toTarget(); - if (cross_target.isNativeCpu() or cross_target.isNativeOs()) { + if (cross_target.cpu_arch == null or cross_target.os_tag == null) { const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (cross_target.isNativeCpu()) { + if (cross_target.cpu_arch == null) { adjusted_target.cpu = detected_info.target.cpu; // TODO We want to just use detected_info.target but implementing @@ -1151,7 +1151,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) const arch = std.Target.current.cpu.arch; adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); } - if (cross_target.isNativeOs()) { + if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; if (detected_info.dynamic_linker) |dl| { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index ab1156c3cb..b16bf485c0 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -42,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\} ; - switch (builtin.os.tag) { + switch (std.Target.current.os.tag) { .freebsd => { cases.addCase( "return", From 622b5b62c293b767787786e753cecff40f29d7e7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 16:32:28 -0500 Subject: [PATCH 10/37] fix not setting the dynamic linker path when cross compiling --- src-self-hosted/stage2.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 351c6c5357..61068f1737 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -668,6 +668,7 @@ export fn stage2_target_parse( error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, error.DeviceBusy => return .DeviceBusy, + error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath, }; return .None; } @@ -1137,6 +1138,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { /// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var adjusted_target = cross_target.toTarget(); + var have_native_dl = false; if (cross_target.cpu_arch == null or cross_target.os_tag == null) { const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); if (cross_target.cpu_arch == null) { @@ -1155,6 +1157,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) adjusted_target.os = detected_info.target.os; if (detected_info.dynamic_linker) |dl| { + have_native_dl = true; dynamic_linker_ptr.* = dl.ptr; } if (cross_target.abi == null) { @@ -1164,6 +1167,14 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); } } + if (!have_native_dl) { + dynamic_linker_ptr.* = adjusted_target.getStandardDynamicLinkerPath( + std.heap.c_allocator, + ) catch |err| switch (err) { + error.TargetHasNoDynamicLinker => null, + else => |e| return e, + }; + } return adjusted_target; } From 34d2700af42d8f45caaa168b34c34b33bb14cf8d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 17:35:43 -0500 Subject: [PATCH 11/37] clean up CrossTarget.getExternalExecutor --- lib/std/zig/cross_target.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 1e16072815..fac90ddba8 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -5,6 +5,7 @@ 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, @@ -554,9 +555,13 @@ pub const CrossTarget = struct { const os_tag = self.getOsTag(); const os_match = os_tag == Target.current.os.tag; - // If the OS matches, and the CPU arch matches, the binary is considered native. - if (self.os_tag == null and cpu_arch == Target.current.cpu.arch) { - return .native; + // 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. From 7927764cc21592e19591095105c397736f060edf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 18:56:43 -0500 Subject: [PATCH 12/37] mips: implement Target.getStandardDynamicLinkerPath --- lib/std/target.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 440f50b811..110aa2dcff 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1151,7 +1151,16 @@ pub const Target = struct { .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 std.fmt.allocPrint0(a, "/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"), From 2387f48d5c5a5ba1410e29098a0cdbf03c3bd9a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 20:16:14 -0500 Subject: [PATCH 13/37] fix incorrect builtin import code for windows --- src-self-hosted/stage2.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 61068f1737..e7265ef6d3 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1029,7 +1029,7 @@ const Stage2Target = extern struct { .netbsd, .openbsd, => try os_builtin_str_buffer.print( - \\.semver = .{{ + \\ .semver = .{{ \\ .min = .{{ \\ .major = {}, \\ .minor = {}, @@ -1041,6 +1041,7 @@ const Stage2Target = extern struct { \\ .patch = {}, \\ }}, \\ }}}}, + \\ , .{ target.os.version_range.semver.min.major, target.os.version_range.semver.min.minor, @@ -1052,7 +1053,7 @@ const Stage2Target = extern struct { }), .linux => try os_builtin_str_buffer.print( - \\.linux = .{{ + \\ .linux = .{{ \\ .range = .{{ \\ .min = .{{ \\ .major = {}, @@ -1087,10 +1088,11 @@ const Stage2Target = extern struct { }), .windows => try os_builtin_str_buffer.print( - \\.semver = .{{ + \\ .windows = .{{ \\ .min = .{}, \\ .max = .{}, \\ }}}}, + \\ , .{ @tagName(target.os.version_range.windows.min), @tagName(target.os.version_range.windows.max), From 6226726571cf6fd500fbc3735e604c85abae641c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 21:29:31 -0500 Subject: [PATCH 14/37] fix builder.findProgram test --- lib/std/build.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 23db4c3f8e..7852828f7c 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1039,9 +1039,10 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - var buf: [50000]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&buf); - const builder = try Builder.create(&fba.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; } From 36aa3c8e7ff1c945c9014bcd5431a4eedd217e8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 11:52:55 -0500 Subject: [PATCH 15/37] fix __stack_chk_guard emitted even when not linking libc --- lib/std/special/compiler_rt.zig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 3126e81b9d..9ed866f62d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -2,10 +2,7 @@ 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_gnu = std.Target.current.abi.isGnu(); const is_mingw = builtin.os.tag == .windows and is_gnu; comptime { @@ -289,7 +286,7 @@ comptime { else => {}, } } else { - if (std.Target.current.isGnuLibC()) { + 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 }); From bafa895561a6603351be8d5c3521c9a447645d08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 12:19:16 -0500 Subject: [PATCH 16/37] compiler-rt: inline at call site to workaround a bug The bug is #2154 --- lib/std/special/compiler_rt/truncXfYf2.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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); From 2536e4c70c6805b096992d4e54dfd2ab9f7fcae3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:31:59 -0500 Subject: [PATCH 17/37] WASI has no dynamic linker --- lib/std/target.zig | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 110aa2dcff..9cd65e7661 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1207,6 +1207,31 @@ pub const Target = struct { => return error.UnknownDynamicLinkerPath, }, + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .openbsd, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + => return error.UnknownDynamicLinkerPath, + .freestanding, .ios, .tvos, @@ -1216,9 +1241,8 @@ pub const Target = struct { .windows, .emscripten, .other, + .wasi, => return error.TargetHasNoDynamicLinker, - - else => return error.UnknownDynamicLinkerPath, } } }; From 3c3316d4ba3ede6172fc0a92b6c4c9b8e601adde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:32:35 -0500 Subject: [PATCH 18/37] update tests to new Target API --- test/cli.zig | 3 +-- test/compare_output.zig | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/test/cli.zig b/test/cli.zig index b7d03d9e21..bc5a29be44 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const testing = std.testing; const process = std.process; const fs = std.fs; @@ -97,7 +96,7 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { - if (builtin.os != .linux or builtin.arch != .x86_64) return; + if (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64) return; const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" }); const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" }); diff --git a/test/compare_output.zig b/test/compare_output.zig index 7a41d46f54..ec89af35f8 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); @@ -131,8 +130,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , "Hello, world!\n 12 12 a\n"); cases.addC("number literals", - \\const builtin = @import("builtin"); - \\const is_windows = builtin.os == builtin.Os.windows; + \\const std = @import("std"); + \\const is_windows = std.Target.current.os.tag == .windows; \\const c = @cImport({ \\ if (is_windows) { \\ // See https://github.com/ziglang/zig/issues/515 @@ -306,8 +305,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , ""); cases.addC("casting between float and integer types", - \\const builtin = @import("builtin"); - \\const is_windows = builtin.os == builtin.Os.windows; + \\const std = @import("std"); + \\const is_windows = std.Target.current.os.tag == .windows; \\const c = @cImport({ \\ if (is_windows) { \\ // See https://github.com/ziglang/zig/issues/515 From 70bf8874d7b358545c3370d5e1ae71dfcdf55b93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:32:49 -0500 Subject: [PATCH 19/37] update docgen to new Target API --- doc/docgen.zig | 81 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 9b8aca18d0..83d1a66179 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = std.builtin; const io = std.io; const fs = std.fs; const process = std.process; @@ -521,7 +521,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", .{code_kind_str}); } - var mode = builtin.Mode.Debug; + var mode: builtin.Mode = .Debug; var link_objects = std.ArrayList([]const u8).init(allocator); defer link_objects.deinit(); var target_str: ?[]const u8 = null; @@ -533,9 +533,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { const end_code_tag = try eatToken(tokenizer, Token.Id.TagContent); const end_tag_name = tokenizer.buffer[end_code_tag.start..end_code_tag.end]; if (mem.eql(u8, end_tag_name, "code_release_fast")) { - mode = builtin.Mode.ReleaseFast; + mode = .ReleaseFast; } else if (mem.eql(u8, end_tag_name, "code_release_safe")) { - mode = builtin.Mode.ReleaseSafe; + mode = .ReleaseSafe; } else if (mem.eql(u8, end_tag_name, "code_link_object")) { _ = try eatToken(tokenizer, Token.Id.Separator); const obj_tok = try eatToken(tokenizer, Token.Id.TagContent); @@ -1001,30 +1001,30 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var for (toc.nodes) |node| { switch (node) { - Node.Content => |data| { + .Content => |data| { try out.write(data); }, - Node.Link => |info| { + .Link => |info| { if (!toc.urls.contains(info.url)) { return parseError(tokenizer, info.token, "url not found: {}", .{info.url}); } try out.print("{}", .{ info.url, info.name }); }, - Node.Nav => { + .Nav => { try out.write(toc.toc); }, - Node.Builtin => |tok| { + .Builtin => |tok| { try out.write("
");
                 try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);
                 try out.write("
"); }, - Node.HeaderOpen => |info| { + .HeaderOpen => |info| { try out.print( "{} §\n", .{ info.n, info.url, info.url, info.name, info.url, info.n }, ); }, - Node.SeeAlso => |items| { + .SeeAlso => |items| { try out.write("

See also:

    \n"); for (items) |item| { const url = try urlize(allocator, item.name); @@ -1035,10 +1035,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } try out.write("
\n"); }, - Node.Syntax => |content_tok| { + .Syntax => |content_tok| { try tokenizeAndPrint(tokenizer, out, content_tok); }, - Node.Code => |code| { + .Code => |code| { code_progress_index += 1; warn("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); @@ -1075,16 +1075,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }); try out.print("
$ zig build-exe {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try build_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try build_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try build_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1142,13 +1142,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                             try out.print("\n{}
\n", .{colored_stderr}); break :code_block; } - const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile", .{}); + const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch + return parseError(tokenizer, code.source_token, "example failed to compile", .{}); if (code.target_str) |triple| { - if (mem.startsWith(u8, triple, "wasm32") or + if ((mem.startsWith(u8, triple, "wasm32") or mem.startsWith(u8, triple, "riscv64-linux") or - mem.startsWith(u8, triple, "x86_64-linux") and - (builtin.os != .linux or builtin.arch != .x86_64)) + mem.startsWith(u8, triple, "x86_64-linux")) and + (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64)) { // skip execution try out.print("\n", .{}); @@ -1207,16 +1208,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }); try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1249,16 +1250,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1306,16 +1307,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         var mode_arg: []const u8 = "";
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 mode_arg = " --release-safe";
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 mode_arg = " --release-fast";
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 mode_arg = " --release-small";
                             },
@@ -1386,20 +1387,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         }
 
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try build_args.append("--release-safe");
                                 if (!code.is_inline) {
                                     try out.print(" --release-safe", .{});
                                 }
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try build_args.append("--release-fast");
                                 if (!code.is_inline) {
                                     try out.print(" --release-fast", .{});
                                 }
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try build_args.append("--release-small");
                                 if (!code.is_inline) {
                                     try out.print(" --release-small", .{});
@@ -1461,16 +1462,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         try out.print("
$ zig build-lib {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },

From f89a1844bfc97f2023fc9961cdf12900ccb77cf8 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Thu, 27 Feb 2020 13:33:01 -0500
Subject: [PATCH 20/37] don't error out for targets with unknown standard
 dynamic linker path

---
 src-self-hosted/stage2.zig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
index e7265ef6d3..14673b0e98 100644
--- a/src-self-hosted/stage2.zig
+++ b/src-self-hosted/stage2.zig
@@ -668,7 +668,6 @@ export fn stage2_target_parse(
         error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
         error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
         error.DeviceBusy => return .DeviceBusy,
-        error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath,
     };
     return .None;
 }
@@ -1174,6 +1173,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8)
             std.heap.c_allocator,
         ) catch |err| switch (err) {
             error.TargetHasNoDynamicLinker => null,
+            error.UnknownDynamicLinkerPath => null,
             else => |e| return e,
         };
     }

From 662b5f7c6045ba857d0d9abd72350a82f0b7ddf3 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Thu, 27 Feb 2020 14:41:44 -0500
Subject: [PATCH 21/37] update docs to latest Target API

---
 doc/docgen.zig      |  6 +++---
 doc/langref.html.in | 30 +++++++++++++++---------------
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/doc/docgen.zig b/doc/docgen.zig
index 83d1a66179..5d7f2b7b38 100644
--- a/doc/docgen.zig
+++ b/doc/docgen.zig
@@ -1146,10 +1146,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                             return parseError(tokenizer, code.source_token, "example failed to compile", .{});
 
                         if (code.target_str) |triple| {
-                            if ((mem.startsWith(u8, triple, "wasm32") or
+                            if (mem.startsWith(u8, triple, "wasm32") or
                                 mem.startsWith(u8, triple, "riscv64-linux") or
-                                mem.startsWith(u8, triple, "x86_64-linux")) and
-                                (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64))
+                                (mem.startsWith(u8, triple, "x86_64-linux") and
+                                std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64))
                             {
                                 // skip execution
                                 try out.print("
\n", .{}); diff --git a/doc/langref.html.in b/doc/langref.html.in index e244d69e25..c10fc3ed30 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -965,7 +965,8 @@ const nan = std.math.nan(f128); but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:

{#code_begin|obj|foo#} {#code_release_fast#} -const builtin = @import("builtin"); +const std = @import("std"); +const builtin = std.builtin; const big = @as(f64, 1 << 40); export fn foo_strict(x: f64) f64 { @@ -2063,15 +2064,15 @@ test "pointer child type" { alignment of the underlying type, it can be omitted from the type:

{#code_begin|test#} -const assert = @import("std").debug.assert; -const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; test "variable alignment" { var x: i32 = 1234; const align_of_i32 = @alignOf(@TypeOf(x)); assert(@TypeOf(&x) == *i32); assert(*i32 == *align(align_of_i32) i32); - if (builtin.arch == builtin.Arch.x86_64) { + if (std.Target.current.cpu.arch == .x86_64) { assert((*i32).alignment == 4); } } @@ -2474,7 +2475,7 @@ test "default struct initialization fields" {

{#code_begin|test#} const std = @import("std"); -const builtin = @import("builtin"); +const builtin = std.builtin; const assert = std.debug.assert; const Full = packed struct { @@ -3204,8 +3205,8 @@ test "separate scopes" { {#header_open|switch#} {#code_begin|test|switch#} -const assert = @import("std").debug.assert; -const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; test "switch simple" { const a: u64 = 10; @@ -3249,16 +3250,16 @@ test "switch simple" { } // Switch expressions can be used outside a function: -const os_msg = switch (builtin.os) { - builtin.Os.linux => "we found a linux user", +const os_msg = switch (std.Target.current.os.tag) { + .linux => "we found a linux user", else => "not a linux user", }; // Inside a function, switch statements implicitly are compile-time // evaluated if the target expression is compile-time known. test "switch inside function" { - switch (builtin.os) { - builtin.Os.fuchsia => { + switch (std.Target.current.os.tag) { + .fuchsia => { // On an OS other than fuchsia, block is not even analyzed, // so this compile error is not triggered. // On fuchsia this compile error would be triggered. @@ -7364,8 +7365,6 @@ test "main" { the {#syntax#}export{#endsyntax#} keyword used on a function:

{#code_begin|obj#} -const builtin = @import("builtin"); - comptime { @export(internalName, .{ .name = "foo", .linkage = .Strong }); } @@ -9397,7 +9396,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';

{#code_begin|test|detect_test#} const std = @import("std"); -const builtin = @import("builtin"); +const builtin = std.builtin; const assert = std.debug.assert; test "builtin.is_test" { @@ -9715,7 +9714,8 @@ WebAssembly.instantiate(typedArray, {
$ node test.js
 The result is 3
{#header_open|WASI#} -

Zig's support for WebAssembly System Interface (WASI) is under active development. Example of using the standard library and reading command line arguments:

+

Zig's support for WebAssembly System Interface (WASI) is under active development. + Example of using the standard library and reading command line arguments:

{#code_begin|exe|wasi#} {#target_wasi#} const std = @import("std"); From 60f2f3457dd73772e7cd60bf5d90358079361d11 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 16:38:10 -0500 Subject: [PATCH 22/37] getStandardDynamicLinkerPath renamed and no allocator * `std.Target.getStandardDynamicLinkerPath` => `std.Target.standardDynamicLinkerPath` * it now takes a pointer to fixed size array rather than an allocator * `std.zig.system.NativeTargetInfo.detect` now supports reading PT_INTERP from /usr/bin/env --- lib/std/target.zig | 142 +++++++++++++++++++------------------ lib/std/zig/system.zig | 115 +++++++++++++++++++++--------- src-self-hosted/stage2.zig | 16 ++--- 3 files changed, 159 insertions(+), 114 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 9cd65e7661..33019b6bbe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1084,65 +1084,59 @@ pub const Target = struct { } } - /// Caller owns returned memory. - pub fn getStandardDynamicLinkerPath( - self: Target, - allocator: *mem.Allocator, - ) error{ - OutOfMemory, - UnknownDynamicLinkerPath, - TargetHasNoDynamicLinker, - }![:0]u8 { - const a = allocator; + /// The result will be a slice of `buffer`, pointing at position 0. + /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. + pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?[]u8 { + const S = struct { + fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 { + return std.fmt.bufPrint(b, fmt, args) catch unreachable; + } + fn copy(b: *[255]u8, s: []const u8) []u8 { + mem.copy(u8, b, s); + return b[0..s.len]; + } + }; + const print = S.print; + const copy = S.copy; + if (self.isAndroid()) { - return mem.dupeZ(a, u8, if (self.cpu.arch.ptrBitWidth() == 64) - "/system/bin/linker64" - else - "/system/bin/linker"); + const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; + return print(buffer, "/system/bin/linker{}", .{suffix}); } if (self.isMusl()) { - var result = try std.Buffer.init(allocator, "/lib/ld-musl-"); - defer result.deinit(); - - var is_arm = false; - switch (self.cpu.arch) { - .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)), - } - if (is_arm and self.getFloatAbi() == .hard) { - try result.append("hf"); - } - try result.append(".so.1"); - return result.toOwnedSlice(); + 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(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { - .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"), + .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"), + .netbsd => return copy(buffer, "/libexec/ld.elf_so"), + .dragonfly => return copy(buffer, "/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(buffer, "/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(buffer, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return copy(buffer, "/lib/ld-linux-aarch64_32.so.1"), .arm, .armeb, .thumb, .thumbeb, - => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) { + => return copy(buffer, switch (self.getFloatAbi()) { .hard => "/lib/ld-linux-armhf.so.3", else => "/lib/ld-linux.so.3", }), @@ -1159,29 +1153,35 @@ pub const Target = struct { }; 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 std.fmt.allocPrint0(a, "/lib{}/{}", .{ lib_suffix, loader }); + return print(buffer, "/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.abi) { + .powerpc => return copy(buffer, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"), + .s390x => return copy(buffer, "/lib64/ld64.so.1"), + .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"), + .x86_64 => return copy(buffer, 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(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return copy(buffer, "/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, - - .arc, - .avr, .bpfel, .bpfeb, + .nvptx, + .nvptx64, + => return null, + + // 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, .hexagon, .msp430, .r600, @@ -1189,8 +1189,6 @@ pub const Target = struct { .tce, .tcele, .xcore, - .nvptx, - .nvptx64, .le32, .le64, .amdil, @@ -1204,9 +1202,25 @@ pub const Target = struct { .lanai, .renderscript32, .renderscript64, - => return error.UnknownDynamicLinkerPath, + => return null, }, + // Operating systems in this list have been verified as not having a standard + // dynamic linker path. + .freestanding, + .ios, + .tvos, + .watchos, + .macosx, + .uefi, + .windows, + .emscripten, + .other, + .wasi, + => return null, + + // 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, @@ -1230,19 +1244,7 @@ pub const Target = struct { .amdpal, .hermit, .hurd, - => return error.UnknownDynamicLinkerPath, - - .freestanding, - .ios, - .tvos, - .watchos, - .macosx, - .uefi, - .windows, - .emscripten, - .other, - .wasi, - => return error.TargetHasNoDynamicLinker, + => return null, } } }; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5c9b71001b..d2828a88a1 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -167,7 +167,15 @@ pub const NativePaths = struct { pub const NativeTargetInfo = struct { target: Target, - dynamic_linker: ?[:0]u8, + + /// Contains the memory used to store the dynamic linker path. This field should + /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field + /// exists so that this API requires no allocator. + dynamic_linker_buffer: [255]u8 = undefined, + + /// Used to construct the dynamic linker path. This field should not be used + /// directly. See `dynamicLinker` and `setDynamicLinker`. + dynamic_linker_max: ?u8 = null, pub const DetectError = error{ OutOfMemory, @@ -181,6 +189,7 @@ pub const NativeTargetInfo = struct { /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + /// TODO Remove the allocator requirement from this. pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { const arch = Target.current.cpu.arch; const os_tag = Target.current.os.tag; @@ -188,8 +197,7 @@ pub const NativeTargetInfo = struct { // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. const cpu = Target.Cpu.baseline(arch); - // TODO Detect native operating system version. Until that is implemented we use the minimum version - // of the default range. + // TODO Detect native operating system version. Until that is implemented we use the default range. const os = Target.Os.defaultVersionRange(os_tag); return detectAbiAndDynamicLinker(allocator, cpu, os); @@ -201,6 +209,21 @@ pub const NativeTargetInfo = struct { self.* = undefined; } + /// The returned memory has the same lifetime as the `NativeTargetInfo`. + pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { + const m = self.dynamic_linker_max orelse return null; + return self.dynamic_linker_buffer[0 .. m + 1]; + } + + pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void { + if (dl_or_null) |dl| { + mem.copy(u8, &self.dynamic_linker_buffer, dl); + self.dynamic_linker_max = @intCast(u8, dl.len - 1); + } else { + self.dynamic_linker_max = null; + } + } + /// 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 @@ -245,10 +268,11 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, - }; + var buf: [255]u8 = undefined; + const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| + try mem.dupe(allocator, u8, s) + else + continue; errdefer allocator.free(standard_ld_path); try ld_info_list.append(.{ .ld_path = standard_ld_path, @@ -294,28 +318,29 @@ pub const NativeTargetInfo = struct { } } - return NativeTargetInfo{ + var result: NativeTargetInfo = .{ .target = .{ .cpu = cpu, .os = os_adjusted, .abi = found_ld_info.abi, }, - .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path), }; + result.setDynamicLinker(found_ld_path); + 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(allocator, cpu, os) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.FileSystem => return error.FileSystem, - error.SystemResources => return error.SystemResources, - error.SymLinkLoop => return error.SymLinkLoop, - error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded, - error.DeviceBusy => return error.DeviceBusy, + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os) catch |err| switch (err) { + error.FileSystem, + error.SystemResources, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.DeviceBusy, + => |e| return e, error.UnableToReadElfFile, error.ElfNotADynamicExecutable, @@ -327,6 +352,8 @@ pub const NativeTargetInfo = struct { error.InvalidElfMagic, error.UsrBinEnvNotAvailable, error.Unexpected, + error.UnexpectedEndOfFile, + error.NameTooLong, // Finally, we fall back on the standard path. => defaultAbiAndDynamicLinker(allocator, cpu, os), }; @@ -362,11 +389,7 @@ pub const NativeTargetInfo = struct { }; } - fn abiAndDynamicLinkerFromUsrBinEnv( - allocator: *Allocator, - cpu: Target.Cpu, - os: Target.Os, - ) !NativeTargetInfo { + fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, error.NameTooLong => unreachable, @@ -409,6 +432,14 @@ pub const NativeTargetInfo = struct { const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }, + }; + const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { error.Overflow => return error.InvalidElfProgramHeaders, }; @@ -430,7 +461,22 @@ pub const NativeTargetInfo = struct { const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); switch (p_type) { elf.PT_INTERP => { - std.debug.warn("found PT_INTERP\n", .{}); + 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); + var interp_buf: [255]u8 = undefined; + if (p_filesz > interp_buf.len) return error.NameTooLong; + var read_offset: usize = 0; + while (true) { + const len = try wrapRead(env_file.pread( + interp_buf[read_offset .. p_filesz - read_offset], + p_offset + read_offset, + )); + if (len == 0) return error.UnexpectedEndOfFile; + read_offset += len; + if (read_offset == p_filesz) break; + } + // PT_INTERP includes a null byte in p_filesz. + result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]); }, elf.PT_DYNAMIC => { std.debug.warn("found PT_DYNAMIC\n", .{}); @@ -440,7 +486,7 @@ pub const NativeTargetInfo = struct { } } - return error.OutOfMemory; // TODO + return result; } fn wrapRead(res: std.os.ReadError!usize) !usize { @@ -457,18 +503,17 @@ pub const NativeTargetInfo = struct { } fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { - const target: Target = .{ - .cpu = cpu, - .os = os, - .abi = Target.Abi.default(cpu.arch, os), - }; - return @as(NativeTargetInfo, .{ - .target = target, - .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null, + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), }, - }); + }; + if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { + result.dynamic_linker_max = @intCast(u8, s.len - 1); + } + return result; } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 14673b0e98..5358c4fedc 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1157,9 +1157,9 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; - if (detected_info.dynamic_linker) |dl| { + if (detected_info.dynamicLinker()) |dl| { have_native_dl = true; - dynamic_linker_ptr.* = dl.ptr; + dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); } if (cross_target.abi == null) { adjusted_target.abi = detected_info.target.abi; @@ -1169,13 +1169,11 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } } if (!have_native_dl) { - dynamic_linker_ptr.* = adjusted_target.getStandardDynamicLinkerPath( - std.heap.c_allocator, - ) catch |err| switch (err) { - error.TargetHasNoDynamicLinker => null, - error.UnknownDynamicLinkerPath => null, - else => |e| return e, - }; + var buf: [255]u8 = undefined; + dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s| + try mem.dupeZ(std.heap.c_allocator, u8, s) + else + null; } return adjusted_target; } From fd006c1c74a43827a6f1c32e289ba57cafa874be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 17:20:40 -0500 Subject: [PATCH 23/37] std.zig.system.NativeTargetInfo.detect: almost no Allocator --- lib/std/process.zig | 4 +++ lib/std/target.zig | 12 ++++---- lib/std/zig/system.zig | 62 ++++++++++++++++++-------------------- src-self-hosted/stage2.zig | 4 +-- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 118b5d9ab4..01b9947518 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -609,6 +609,10 @@ 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{}, diff --git a/lib/std/target.zig b/lib/std/target.zig index 33019b6bbe..379ae72afe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1084,16 +1084,16 @@ pub const Target = struct { } } - /// The result will be a slice of `buffer`, pointing at position 0. + /// 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, buffer: *[255]u8) ?[]u8 { + pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?u8 { const S = struct { - fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 { - return std.fmt.bufPrint(b, fmt, args) catch unreachable; + fn print(b: *[255]u8, comptime fmt: []const u8, args: var) u8 { + return @intCast(u8, (std.fmt.bufPrint(b, fmt, args) catch unreachable).len - 1); } - fn copy(b: *[255]u8, s: []const u8) []u8 { + fn copy(b: *[255]u8, s: []const u8) u8 { mem.copy(u8, b, s); - return b[0..s.len]; + return @intCast(u8, s.len - 1); } }; const print = S.print; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index d2828a88a1..e8c95f5125 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -189,7 +189,9 @@ pub const NativeTargetInfo = struct { /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. - /// TODO Remove the allocator requirement from this. + /// 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) DetectError!NativeTargetInfo { const arch = Target.current.cpu.arch; const os_tag = Target.current.os.tag; @@ -203,15 +205,9 @@ pub const NativeTargetInfo = struct { return detectAbiAndDynamicLinker(allocator, cpu, os); } - /// Must be the same `Allocator` passed to `detect`. - pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void { - if (self.dynamic_linker) |dl| allocator.free(dl); - self.* = undefined; - } - /// The returned memory has the same lifetime as the `NativeTargetInfo`. pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { - const m = self.dynamic_linker_max orelse return null; + const m: usize = self.dynamic_linker_max orelse return null; return self.dynamic_linker_buffer[0 .. m + 1]; } @@ -228,13 +224,14 @@ pub const NativeTargetInfo = struct { /// 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, ) DetectError!NativeTargetInfo { if (!comptime Target.current.hasDynamicLinker()) { - return defaultAbiAndDynamicLinker(allocator, cpu, os); + return defaultAbiAndDynamicLinker(cpu, os); } // The current target's ABI cannot be relied on for this. For example, we may build the zig // compiler for target riscv64-linux-musl and provide a tarball for users to download. @@ -242,15 +239,15 @@ pub const NativeTargetInfo = struct { // and supported by Zig. But that means that we must detect the system ABI here rather than // relying on `Target.current`. const LdInfo = struct { - ld_path: []u8, + ld_path_buffer: [255]u8, + ld_path_max: u8, abi: Target.Abi, - }; - var ld_info_list = std.ArrayList(LdInfo).init(allocator); - defer { - for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); - ld_info_list.deinit(); - } + pub fn ldPath(self: *const @This()) []const u8 { + const m: usize = self.ld_path_max; + return self.ld_path_buffer[0 .. m + 1]; + } + }; const all_abis = comptime blk: { assert(@enumToInt(Target.Abi.none) == 0); const fields = std.meta.fields(Target.Abi)[1..]; @@ -260,6 +257,9 @@ pub const NativeTargetInfo = struct { } 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`. @@ -268,17 +268,17 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - var buf: [255]u8 = undefined; - const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupe(allocator, u8, s) - else - continue; - errdefer allocator.free(standard_ld_path); - try ld_info_list.append(.{ - .ld_path = standard_ld_path, + const ld_info = &ld_info_list_buffer[ld_info_list_len]; + ld_info_list_len += 1; + + ld_info.* = .{ + .ld_path_buffer = undefined, + .ld_path_max = undefined, .abi = abi, - }); + }; + ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue; } + const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; // Best case scenario: the executable is dynamically linked, and we can iterate // over our own shared objects and find a dynamic linker. @@ -292,8 +292,8 @@ pub const NativeTargetInfo = struct { // Look for dynamic linker. // This is O(N^M) but typical case here is N=2 and M=10. find_ld: for (lib_paths) |lib_path| { - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ldPath()); if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { found_ld_info = ld_info; found_ld_path = lib_path; @@ -355,7 +355,7 @@ pub const NativeTargetInfo = struct { error.UnexpectedEndOfFile, error.NameTooLong, // Finally, we fall back on the standard path. - => defaultAbiAndDynamicLinker(allocator, cpu, os), + => defaultAbiAndDynamicLinker(cpu, os), }; } @@ -502,7 +502,7 @@ pub const NativeTargetInfo = struct { }; } - fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { var result: NativeTargetInfo = .{ .target = .{ .cpu = cpu, @@ -510,9 +510,7 @@ pub const NativeTargetInfo = struct { .abi = Target.Abi.default(cpu.arch, os), }, }; - if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { - result.dynamic_linker_max = @intCast(u8, s.len - 1); - } + result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); return result; } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 5358c4fedc..cd3f49edfa 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1170,8 +1170,8 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } if (!have_native_dl) { var buf: [255]u8 = undefined; - dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupeZ(std.heap.c_allocator, u8, s) + dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |m| + try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1]) else null; } From 3683ba87ac5e5dfb6ea65c21c9fb714ed0582124 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 19:49:00 -0500 Subject: [PATCH 24/37] complete the native target detection based on /usr/bin/env --- lib/std/c.zig | 1 + lib/std/dynamic_library.zig | 4 +- lib/std/elf.zig | 10 +- lib/std/zig/system.zig | 306 ++++++++++++++++++++++++++++-------- 4 files changed, 244 insertions(+), 77 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9072d2c7cd..9d7d9524d6 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -108,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; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 34f45894fb..0d14f8d032 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -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]); diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 007a01bb90..99084f1897 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -708,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/zig/system.zig b/lib/std/zig/system.zig index e8c95f5125..fe2f4c7e04 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -238,16 +238,6 @@ pub const NativeTargetInfo = struct { // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined // and supported by Zig. But that means that we must detect the system ABI here rather than // relying on `Target.current`. - const LdInfo = struct { - ld_path_buffer: [255]u8, - ld_path_max: u8, - abi: Target.Abi, - - pub fn ldPath(self: *const @This()) []const u8 { - const m: usize = self.ld_path_max; - return self.ld_path_buffer[0 .. m + 1]; - } - }; const all_abis = comptime blk: { assert(@enumToInt(Target.Abi.none) == 0); const fields = std.meta.fields(Target.Abi)[1..]; @@ -333,7 +323,7 @@ pub const NativeTargetInfo = struct { // 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) catch |err| switch (err) { + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list) catch |err| switch (err) { error.FileSystem, error.SystemResources, error.SymLinkLoop, @@ -343,8 +333,6 @@ pub const NativeTargetInfo = struct { => |e| return e, error.UnableToReadElfFile, - error.ElfNotADynamicExecutable, - error.InvalidElfProgramHeaders, error.InvalidElfClass, error.InvalidElfVersion, error.InvalidElfEndian, @@ -373,6 +361,10 @@ pub const NativeTargetInfo = struct { 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-"; @@ -389,7 +381,11 @@ pub const NativeTargetInfo = struct { }; } - fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn abiAndDynamicLinkerFromUsrBinEnv( + cpu: Target.Cpu, + os: Target.Os, + ld_info_list: []const LdInfo, + ) !NativeTargetInfo { const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, error.NameTooLong => unreachable, @@ -409,8 +405,7 @@ pub const NativeTargetInfo = struct { else => |e| return e, }; var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; - const hdr_bytes_len = try wrapRead(env_file.pread(&hdr_buf, 0)); - if (hdr_bytes_len < @sizeOf(elf.Elf32_Ehdr)) return error.InvalidElfFile; + _ = 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; @@ -430,7 +425,6 @@ pub const NativeTargetInfo = struct { var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); - const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); var result: NativeTargetInfo = .{ .target = .{ @@ -439,67 +433,234 @@ pub const NativeTargetInfo = struct { .abi = Target.Abi.default(cpu.arch, os), }, }; + var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC - const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { - error.Overflow => return error.InvalidElfProgramHeaders, - }; var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + 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 reserve = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); - const read_byte_len = try wrapRead(env_file.pread(ph_buf[0 .. ph_buf.len - reserve], phoff)); - if (read_byte_len < phentsize) return error.ElfNotADynamicExecutable; - var buf_i: usize = 0; - while (buf_i < read_byte_len and ph_i < phnum) : ({ + // 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; - buf_i += phentsize; + ph_buf_i += phentsize; }) { - const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[buf_i])); - const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[buf_i])); + const 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 => { 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); - var interp_buf: [255]u8 = undefined; - if (p_filesz > interp_buf.len) return error.NameTooLong; - var read_offset: usize = 0; - while (true) { - const len = try wrapRead(env_file.pread( - interp_buf[read_offset .. p_filesz - read_offset], - p_offset + read_offset, - )); - if (len == 0) return error.UnexpectedEndOfFile; - read_offset += len; - if (read_offset == p_filesz) break; - } + 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. - result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]); + const len = p_filesz - 1; + // dynamic_linker_max is "max", not "len". + // We know it will fit in u8 because we check against dynamic_linker_buffer.len above. + result.dynamic_linker_max = @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.ldPath()); + if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { + result.target.abi = ld_info.abi; + break; + } + } }, - elf.PT_DYNAMIC => { - std.debug.warn("found PT_DYNAMIC\n", .{}); + // We only need this for detecting glibc version. + elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + 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()) { + 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 wrapRead(res: std.os.ReadError!usize) !usize { - return res catch |err| switch (err) { - error.OperationAborted => unreachable, // Windows-only - error.WouldBlock => unreachable, // Did not request blocking mode - error.SystemResources => return error.SystemResources, - error.IsDir => return error.UnableToReadElfFile, - error.BrokenPipe => return error.UnableToReadElfFile, - error.ConnectionResetByPeer => return error.UnableToReadElfFile, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.FileSystem, - }; + fn 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) !NativeTargetInfo { @@ -513,20 +674,31 @@ pub const NativeTargetInfo = struct { result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); return result; } -}; -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; + const LdInfo = struct { + ld_path_buffer: [255]u8, + ld_path_max: u8, + abi: Target.Abi, + + pub fn ldPath(self: *const LdInfo) []const u8 { + const m: usize = self.ld_path_max; + return self.ld_path_buffer[0 .. m + 1]; } - } else { - if (need_bswap) { - return @byteSwap(@TypeOf(int_32), int_32); + }; + + 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 { - return int_32; + if (need_bswap) { + return @byteSwap(@TypeOf(int_32), int_32); + } else { + return int_32; + } } } -} +}; From d45ea4d89d7232fc4bcf56a5e088d7d6b5004ebb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 02:00:20 -0500 Subject: [PATCH 25/37] stage1: make get_native_target go through self-hosted --- lib/std/special/init-exe/src/main.zig | 2 +- lib/std/target.zig | 2 +- src/main.cpp | 10 ++- src/stage2.cpp | 100 ++++++++++++++++++++++++++ src/target.cpp | 100 -------------------------- src/target.hpp | 1 - 6 files changed, 110 insertions(+), 105 deletions(-) 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/target.zig b/lib/std/target.zig index 379ae72afe..a72bd921b0 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1215,8 +1215,8 @@ pub const Target = struct { .uefi, .windows, .emscripten, - .other, .wasi, + .other, => return null, // TODO go over each item in this list and either move it to the above list, or diff --git a/src/main.cpp b/src/main.cpp index 11612402ee..f741cafd40 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -500,7 +500,10 @@ static int main0(int argc, char **argv) { os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); ZigTarget target; - get_native_target(&target); + if ((err = target_parse_triple(&target, "native", nullptr))) { + fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); + return EXIT_FAILURE; + } Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig"); Buf build_file_abs = os_path_resolve(&build_file_buf, 1); @@ -1337,7 +1340,10 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_SUCCESS); } else if (cmd == CmdTest) { ZigTarget native; - get_native_target(&native); + if ((err = target_parse_triple(&native, "native", nullptr))) { + fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); + return EXIT_FAILURE; + } g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr); codegen_build_and_link(g); diff --git a/src/stage2.cpp b/src/stage2.cpp index 2023b45aaf..bc8e65fd56 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -91,6 +91,106 @@ void stage2_progress_complete_one(Stage2ProgressNode *node) {} void stage2_progress_disable_tty(Stage2Progress *progress) {} void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} +static Os get_zig_os_type(ZigLLVM_OSType os_type) { + switch (os_type) { + case ZigLLVM_UnknownOS: + return OsFreestanding; + case ZigLLVM_Ananas: + return OsAnanas; + case ZigLLVM_CloudABI: + return OsCloudABI; + case ZigLLVM_DragonFly: + return OsDragonFly; + case ZigLLVM_FreeBSD: + return OsFreeBSD; + case ZigLLVM_Fuchsia: + return OsFuchsia; + case ZigLLVM_IOS: + return OsIOS; + case ZigLLVM_KFreeBSD: + return OsKFreeBSD; + case ZigLLVM_Linux: + return OsLinux; + case ZigLLVM_Lv2: + return OsLv2; + case ZigLLVM_Darwin: + case ZigLLVM_MacOSX: + return OsMacOSX; + case ZigLLVM_NetBSD: + return OsNetBSD; + case ZigLLVM_OpenBSD: + return OsOpenBSD; + case ZigLLVM_Solaris: + return OsSolaris; + case ZigLLVM_Win32: + return OsWindows; + case ZigLLVM_Haiku: + return OsHaiku; + case ZigLLVM_Minix: + return OsMinix; + case ZigLLVM_RTEMS: + return OsRTEMS; + case ZigLLVM_NaCl: + return OsNaCl; + case ZigLLVM_CNK: + return OsCNK; + case ZigLLVM_AIX: + return OsAIX; + case ZigLLVM_CUDA: + return OsCUDA; + case ZigLLVM_NVCL: + return OsNVCL; + case ZigLLVM_AMDHSA: + return OsAMDHSA; + case ZigLLVM_PS4: + return OsPS4; + case ZigLLVM_ELFIAMCU: + return OsELFIAMCU; + case ZigLLVM_TvOS: + return OsTvOS; + case ZigLLVM_WatchOS: + return OsWatchOS; + case ZigLLVM_Mesa3D: + return OsMesa3D; + case ZigLLVM_Contiki: + return OsContiki; + case ZigLLVM_AMDPAL: + return OsAMDPAL; + case ZigLLVM_HermitCore: + return OsHermitCore; + case ZigLLVM_Hurd: + return OsHurd; + case ZigLLVM_WASI: + return OsWASI; + case ZigLLVM_Emscripten: + return OsEmscripten; + } + zig_unreachable(); +} + +static void get_native_target(ZigTarget *target) { + // first zero initialize + *target = {}; + + ZigLLVM_OSType os_type; + ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os + ZigLLVMGetNativeTarget( + &target->arch, + &target->vendor, + &os_type, + &target->abi, + &oformat); + target->os = get_zig_os_type(os_type); + target->is_native = true; + if (target->abi == ZigLLVM_UnknownEnvironment) { + target->abi = target_default_abi(target->arch, target->os); + } + if (target_is_glibc(target)) { + target->glibc_version = heap::c_allocator.create(); + target_init_default_glibc_version(target); + } +} + Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) { Error err; diff --git a/src/target.cpp b/src/target.cpp index 8a68afbd83..4df2cfca85 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -287,83 +287,6 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) { zig_unreachable(); } -static Os get_zig_os_type(ZigLLVM_OSType os_type) { - switch (os_type) { - case ZigLLVM_UnknownOS: - return OsFreestanding; - case ZigLLVM_Ananas: - return OsAnanas; - case ZigLLVM_CloudABI: - return OsCloudABI; - case ZigLLVM_DragonFly: - return OsDragonFly; - case ZigLLVM_FreeBSD: - return OsFreeBSD; - case ZigLLVM_Fuchsia: - return OsFuchsia; - case ZigLLVM_IOS: - return OsIOS; - case ZigLLVM_KFreeBSD: - return OsKFreeBSD; - case ZigLLVM_Linux: - return OsLinux; - case ZigLLVM_Lv2: - return OsLv2; - case ZigLLVM_Darwin: - case ZigLLVM_MacOSX: - return OsMacOSX; - case ZigLLVM_NetBSD: - return OsNetBSD; - case ZigLLVM_OpenBSD: - return OsOpenBSD; - case ZigLLVM_Solaris: - return OsSolaris; - case ZigLLVM_Win32: - return OsWindows; - case ZigLLVM_Haiku: - return OsHaiku; - case ZigLLVM_Minix: - return OsMinix; - case ZigLLVM_RTEMS: - return OsRTEMS; - case ZigLLVM_NaCl: - return OsNaCl; - case ZigLLVM_CNK: - return OsCNK; - case ZigLLVM_AIX: - return OsAIX; - case ZigLLVM_CUDA: - return OsCUDA; - case ZigLLVM_NVCL: - return OsNVCL; - case ZigLLVM_AMDHSA: - return OsAMDHSA; - case ZigLLVM_PS4: - return OsPS4; - case ZigLLVM_ELFIAMCU: - return OsELFIAMCU; - case ZigLLVM_TvOS: - return OsTvOS; - case ZigLLVM_WatchOS: - return OsWatchOS; - case ZigLLVM_Mesa3D: - return OsMesa3D; - case ZigLLVM_Contiki: - return OsContiki; - case ZigLLVM_AMDPAL: - return OsAMDPAL; - case ZigLLVM_HermitCore: - return OsHermitCore; - case ZigLLVM_Hurd: - return OsHurd; - case ZigLLVM_WASI: - return OsWASI; - case ZigLLVM_Emscripten: - return OsEmscripten; - } - zig_unreachable(); -} - const char *target_os_name(Os os_type) { switch (os_type) { case OsFreestanding: @@ -447,29 +370,6 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { return ErrorNone; } -void get_native_target(ZigTarget *target) { - // first zero initialize - *target = {}; - - ZigLLVM_OSType os_type; - ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os - ZigLLVMGetNativeTarget( - &target->arch, - &target->vendor, - &os_type, - &target->abi, - &oformat); - target->os = get_zig_os_type(os_type); - target->is_native = true; - if (target->abi == ZigLLVM_UnknownEnvironment) { - target->abi = target_default_abi(target->arch, target->os); - } - if (target_is_glibc(target)) { - target->glibc_version = heap::c_allocator.create(); - target_init_default_glibc_version(target); - } -} - void target_init_default_glibc_version(ZigTarget *target) { *target->glibc_version = {2, 17, 0}; } diff --git a/src/target.hpp b/src/target.hpp index 9396eb2623..e72b6a6f49 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -73,7 +73,6 @@ ZigLLVM_ObjectFormatType target_oformat_enum(size_t index); const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat); ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); -void get_native_target(ZigTarget *target); void target_triple_llvm(Buf *triple, const ZigTarget *target); void target_triple_zig(Buf *triple, const ZigTarget *target); From ef24f2dd93729493531c427aaac54444597f6e66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 02:36:16 -0500 Subject: [PATCH 26/37] remove special darwin os version min handling now it is integrated with zig's target OS range. --- lib/std/target.zig | 21 +++++- lib/std/zig/cross_target.zig | 6 +- src-self-hosted/stage2.zig | 39 +++++++---- src/all_types.hpp | 2 - src/codegen.cpp | 59 +++------------- src/codegen.hpp | 2 - src/glibc.cpp | 26 +++---- src/glibc.hpp | 2 +- src/link.cpp | 127 +++++------------------------------ src/main.cpp | 20 ------ src/stage2.cpp | 2 +- src/stage2.h | 9 ++- src/target.cpp | 4 +- src/target.hpp | 2 +- test/cli.zig | 2 +- 15 files changed, 95 insertions(+), 228 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index a72bd921b0..6ecb679f29 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -147,7 +147,6 @@ pub const Target = struct { .cloudabi, .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -162,8 +161,6 @@ pub const Target = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -187,6 +184,24 @@ pub const Target = struct { .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 }, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index fac90ddba8..c7fd1f0464 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -88,7 +88,6 @@ pub const CrossTarget = struct { .cloudabi, .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -103,8 +102,6 @@ pub const CrossTarget = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -121,8 +118,11 @@ pub const CrossTarget = struct { .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 }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index cd3f49edfa..3dae15056e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -892,7 +892,7 @@ const Stage2Target = extern struct { is_native: bool, - glibc_version: ?*Stage2GLibCVersion, // null means default + glibc_or_darwin_version: ?*Stage2SemVer, llvm_cpu_name: ?[*:0]const u8, llvm_cpu_features: ?[*:0]const u8, @@ -1103,16 +1103,29 @@ const Stage2Target = extern struct { os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()], ); - const glibc_version = if (target.isGnuLibC()) blk: { - const stage1_glibc = try std.heap.c_allocator.create(Stage2GLibCVersion); - const stage2_glibc = target.os.version_range.linux.glibc; - stage1_glibc.* = .{ - .major = stage2_glibc.major, - .minor = stage2_glibc.minor, - .patch = stage2_glibc.patch, - }; - break :blk stage1_glibc; - } else null; + const glibc_or_darwin_version = blk: { + if (target.isGnuLibC()) { + const stage1_glibc = try std.heap.c_allocator.create(Stage2SemVer); + const stage2_glibc = target.os.version_range.linux.glibc; + stage1_glibc.* = .{ + .major = stage2_glibc.major, + .minor = stage2_glibc.minor, + .patch = stage2_glibc.patch, + }; + break :blk stage1_glibc; + } else if (target.isDarwin()) { + const stage1_semver = try std.heap.c_allocator.create(Stage2SemVer); + const stage2_semver = target.os.version_range.semver.min; + stage1_semver.* = .{ + .major = stage2_semver.major, + .minor = stage2_semver.minor, + .patch = stage2_semver.patch, + }; + break :blk stage1_semver; + } else { + break :blk null; + } + }; self.* = .{ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch @@ -1125,7 +1138,7 @@ const Stage2Target = extern struct { .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, .cache_hash = cache_hash.toOwnedSlice().ptr, .is_native = cross_target.isNative(), - .glibc_version = glibc_version, + .glibc_or_darwin_version = glibc_or_darwin_version, .dynamic_linker = dynamic_linker, }; } @@ -1179,7 +1192,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } // ABI warning -const Stage2GLibCVersion = extern struct { +const Stage2SemVer = extern struct { major: u32, minor: u32, patch: u32, diff --git a/src/all_types.hpp b/src/all_types.hpp index 7277d04359..838709b9c0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2250,8 +2250,6 @@ struct CodeGen { bool test_is_evented; CodeModel code_model; - Buf *mmacosx_version_min; - Buf *mios_version_min; Buf *root_out_name; Buf *test_filter; Buf *test_name_prefix; diff --git a/src/codegen.cpp b/src/codegen.cpp index 15122d5e11..8ae730616d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -32,31 +32,6 @@ enum ResumeId { ResumeIdCall, }; -static void init_darwin_native(CodeGen *g) { - char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); - char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); - - // Allow conflicts among OSX and iOS, but choose the default platform. - if (osx_target && ios_target) { - if (g->zig_target->arch == ZigLLVM_arm || - g->zig_target->arch == ZigLLVM_aarch64 || - g->zig_target->arch == ZigLLVM_thumb) - { - osx_target = nullptr; - } else { - ios_target = nullptr; - } - } - - if (osx_target) { - g->mmacosx_version_min = buf_create_from_str(osx_target); - } else if (ios_target) { - g->mios_version_min = buf_create_from_str(ios_target); - } else if (g->zig_target->os != OsIOS) { - g->mmacosx_version_min = buf_create_from_str("10.14"); - } -} - static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) { ZigPackage *entry = heap::c_allocator.create(); entry->package_table.init(4); @@ -160,14 +135,6 @@ void codegen_add_framework(CodeGen *g, const char *framework) { g->darwin_frameworks.append(buf_create_from_str(framework)); } -void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min) { - g->mmacosx_version_min = mmacosx_version_min; -} - -void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min) { - g->mios_version_min = mios_version_min; -} - void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { g->linker_rdynamic = rdynamic; } @@ -8655,10 +8622,10 @@ static Error define_builtin_compile_vars(CodeGen *g) { if (g->zig_target->cache_hash != nullptr) { cache_str(&cache_hash, g->zig_target->cache_hash); } - if (g->zig_target->glibc_version != nullptr) { - cache_int(&cache_hash, g->zig_target->glibc_version->major); - cache_int(&cache_hash, g->zig_target->glibc_version->minor); - cache_int(&cache_hash, g->zig_target->glibc_version->patch); + if (g->zig_target->glibc_or_darwin_version != nullptr) { + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major); + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor); + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch); } cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); @@ -10313,10 +10280,10 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { if (g->zig_target->cache_hash != nullptr) { cache_str(ch, g->zig_target->cache_hash); } - if (g->zig_target->glibc_version != nullptr) { - cache_int(ch, g->zig_target->glibc_version->major); - cache_int(ch, g->zig_target->glibc_version->minor); - cache_int(ch, g->zig_target->glibc_version->patch); + if (g->zig_target->glibc_or_darwin_version != nullptr) { + cache_int(ch, g->zig_target->glibc_or_darwin_version->major); + cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); + cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); } cache_int(ch, detect_subsystem(g)); cache_bool(ch, g->strip_debug_symbols); @@ -10344,8 +10311,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->emit_bin); cache_bool(ch, g->emit_llvm_ir); cache_bool(ch, g->emit_asm); - cache_buf_opt(ch, g->mmacosx_version_min); - cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); cache_usize(ch, g->version_minor); cache_usize(ch, g->version_patch); @@ -10662,9 +10627,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o codegen_set_errmsg_color(child_gen, parent_gen->err_color); - codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); - codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); - child_gen->enable_cache = true; return child_gen; @@ -10772,11 +10734,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->each_lib_rpath = false; } else { g->each_lib_rpath = true; - - if (target_os_is_darwin(g->zig_target->os)) { - init_darwin_native(g); - } - } if (target_os_requires_libc(g->zig_target->os)) { diff --git a/src/codegen.hpp b/src/codegen.hpp index 6329c59a5e..191da9a04b 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -35,8 +35,6 @@ LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); void codegen_add_framework(CodeGen *codegen, const char *name); void codegen_add_rpath(CodeGen *codegen, const char *name); void codegen_set_rdynamic(CodeGen *g, bool rdynamic); -void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min); -void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min); void codegen_set_linker_script(CodeGen *g, const char *linker_script); void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); diff --git a/src/glibc.cpp b/src/glibc.cpp index 91e2f9dfc1..da5c1d5290 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -55,7 +55,7 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo Optional> opt_component = SplitIterator_next(&it); if (!opt_component.is_some) break; Buf *ver_buf = buf_create_from_slice(opt_component.value); - ZigGLibCVersion *this_ver = glibc_abi->all_versions.add_one(); + Stage2SemVer *this_ver = glibc_abi->all_versions.add_one(); if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) { if (verbose) { fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err)); @@ -186,9 +186,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con cache_buf(cache_hash, compiler_id); cache_int(cache_hash, target->arch); cache_int(cache_hash, target->abi); - cache_int(cache_hash, target->glibc_version->major); - cache_int(cache_hash, target->glibc_version->minor); - cache_int(cache_hash, target->glibc_version->patch); + cache_int(cache_hash, target->glibc_or_darwin_version->major); + cache_int(cache_hash, target->glibc_or_darwin_version->minor); + cache_int(cache_hash, target->glibc_or_darwin_version->patch); Buf digest = BUF_INIT; buf_resize(&digest, 0); @@ -224,10 +224,10 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con uint8_t target_ver_index = 0; for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) { - const ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(target_ver_index); - if (this_ver->major == target->glibc_version->major && - this_ver->minor == target->glibc_version->minor && - this_ver->patch == target->glibc_version->patch) + const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index); + if (this_ver->major == target->glibc_or_darwin_version->major && + this_ver->minor == target->glibc_or_darwin_version->minor && + this_ver->patch == target->glibc_or_darwin_version->patch) { break; } @@ -235,9 +235,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con if (target_ver_index == glibc_abi->all_versions.length) { if (verbose) { fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n", - target->glibc_version->major, - target->glibc_version->minor, - target->glibc_version->patch); + target->glibc_or_darwin_version->major, + target->glibc_or_darwin_version->minor, + target->glibc_or_darwin_version->patch); } return ErrorUnknownABI; } @@ -246,7 +246,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con Buf *map_contents = buf_alloc(); for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) { - const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_i); + const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i); if (ver->patch == 0) { buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor); } else { @@ -294,7 +294,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con uint8_t ver_index = ver_list->versions[ver_i]; Buf *stub_name; - const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_index); + const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index); const char *sym_name = buf_ptr(libc_fn->name); if (ver->patch == 0) { stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor); diff --git a/src/glibc.hpp b/src/glibc.hpp index 8e4c7888ad..c04dcb4629 100644 --- a/src/glibc.hpp +++ b/src/glibc.hpp @@ -32,7 +32,7 @@ struct ZigGLibCAbi { Buf *abi_txt_path; Buf *vers_txt_path; Buf *fns_txt_path; - ZigList all_versions; + ZigList all_versions; ZigList all_functions; // The value is a pointer to all_functions.length items and each item is an index // into all_functions. diff --git a/src/link.cpp b/src/link.cpp index 901390feae..588771d8bf 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -2371,99 +2371,6 @@ static void construct_linker_job_coff(LinkJob *lj) { } } - -// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the -// grouped values as integers. Numbers which are not provided are set to 0. -// return true if the entire string was parsed (9.2), or all groups were -// parsed (10.3.5extrastuff). -static bool darwin_get_release_version(const char *str, int *major, int *minor, int *micro, bool *had_extra) { - *had_extra = false; - - *major = 0; - *minor = 0; - *micro = 0; - - if (*str == '\0') - return false; - - char *end; - *major = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (*end != '.') - return false; - - str = end + 1; - *minor = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (*end != '.') - return false; - - str = end + 1; - *micro = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (str == end) - return false; - *had_extra = true; - return true; -} - -enum DarwinPlatformKind { - MacOS, - IPhoneOS, - IPhoneOSSimulator, -}; - -struct DarwinPlatform { - DarwinPlatformKind kind; - int major; - int minor; - int micro; -}; - -static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { - CodeGen *g = lj->codegen; - - if (g->mmacosx_version_min) { - platform->kind = MacOS; - } else if (g->mios_version_min) { - platform->kind = IPhoneOS; - } else if (g->zig_target->os == OsMacOSX) { - platform->kind = MacOS; - g->mmacosx_version_min = buf_create_from_str("10.14"); - } else { - zig_panic("unable to infer -mmacosx-version-min or -mios-version-min"); - } - - bool had_extra; - if (platform->kind == MacOS) { - if (!darwin_get_release_version(buf_ptr(g->mmacosx_version_min), - &platform->major, &platform->minor, &platform->micro, &had_extra) || - had_extra || platform->major != 10 || platform->minor >= 100 || platform->micro >= 100) - { - zig_panic("invalid -mmacosx-version-min"); - } - } else if (platform->kind == IPhoneOS) { - if (!darwin_get_release_version(buf_ptr(g->mios_version_min), - &platform->major, &platform->minor, &platform->micro, &had_extra) || - had_extra || platform->major >= 10 || platform->minor >= 100 || platform->micro >= 100) - { - zig_panic("invalid -mios-version-min"); - } - } else { - zig_unreachable(); - } - - if (platform->kind == IPhoneOS && - (g->zig_target->arch == ZigLLVM_x86 || - g->zig_target->arch == ZigLLVM_x86_64)) - { - platform->kind = IPhoneOSSimulator; - } -} - static void construct_linker_job_macho(LinkJob *lj) { CodeGen *g = lj->codegen; @@ -2507,25 +2414,25 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-arch"); lj->args.append(get_darwin_arch_string(g->zig_target)); - DarwinPlatform platform; - get_darwin_platform(lj, &platform); - switch (platform.kind) { - case MacOS: + if (g->zig_target->glibc_or_darwin_version != nullptr) { + if (g->zig_target->os == OsMacOSX) { lj->args.append("-macosx_version_min"); - break; - case IPhoneOS: - lj->args.append("-iphoneos_version_min"); - break; - case IPhoneOSSimulator: - lj->args.append("-ios_simulator_version_min"); - break; + } else if (g->zig_target->os == OsIOS) { + if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) { + lj->args.append("-ios_simulator_version_min"); + } else { + lj->args.append("-iphoneos_version_min"); + } + } + Buf *version_string = buf_sprintf("%d.%d.%d", + g->zig_target->glibc_or_darwin_version->major, + g->zig_target->glibc_or_darwin_version->minor, + g->zig_target->glibc_or_darwin_version->patch); + lj->args.append(buf_ptr(version_string)); + + lj->args.append("-sdk_version"); + lj->args.append(buf_ptr(version_string)); } - Buf *version_string = buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro); - lj->args.append(buf_ptr(version_string)); - - lj->args.append("-sdk_version"); - lj->args.append(buf_ptr(version_string)); - if (g->out_type == OutTypeExe) { lj->args.append("-pie"); diff --git a/src/main.cpp b/src/main.cpp index f741cafd40..f471bda374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,8 +127,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" " -F[dir] (darwin) add search path for frameworks\n" " -framework [name] (darwin) link against framework\n" - " -mios-version-min [ver] (darwin) set iOS deployment target\n" - " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" " --ver-major [ver] dynamic library semver major version\n" " --ver-minor [ver] dynamic library semver minor version\n" " --ver-patch [ver] dynamic library semver patch version\n" @@ -414,8 +412,6 @@ static int main0(int argc, char **argv) { bool have_libc = false; const char *target_string = nullptr; bool rdynamic = false; - const char *mmacosx_version_min = nullptr; - const char *mios_version_min = nullptr; const char *linker_script = nullptr; Buf *version_script = nullptr; ZigList rpath_list = {0}; @@ -844,10 +840,6 @@ static int main0(int argc, char **argv) { cache_dir = argv[i]; } else if (strcmp(arg, "-target") == 0) { target_string = argv[i]; - } else if (strcmp(arg, "-mmacosx-version-min") == 0) { - mmacosx_version_min = argv[i]; - } else if (strcmp(arg, "-mios-version-min") == 0) { - mios_version_min = argv[i]; } else if (strcmp(arg, "-framework") == 0) { frameworks.append(argv[i]); } else if (strcmp(arg, "--linker-script") == 0) { @@ -1240,18 +1232,6 @@ static int main0(int argc, char **argv) { } codegen_set_rdynamic(g, rdynamic); - if (mmacosx_version_min && mios_version_min) { - fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); - return main_exit(root_progress_node, EXIT_FAILURE); - } - - if (mmacosx_version_min) { - codegen_set_mmacosx_version_min(g, buf_create_from_str(mmacosx_version_min)); - } - - if (mios_version_min) { - codegen_set_mios_version_min(g, buf_create_from_str(mios_version_min)); - } if (test_filter) { codegen_set_test_filter(g, buf_create_from_str(test_filter)); diff --git a/src/stage2.cpp b/src/stage2.cpp index bc8e65fd56..67a518e1fa 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -186,7 +186,7 @@ static void get_native_target(ZigTarget *target) { target->abi = target_default_abi(target->arch, target->os); } if (target_is_glibc(target)) { - target->glibc_version = heap::c_allocator.create(); + target->glibc_or_darwin_version = heap::c_allocator.create(); target_init_default_glibc_version(target); } } diff --git a/src/stage2.h b/src/stage2.h index 96222e3138..20311788b2 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -270,14 +270,12 @@ enum Os { }; // ABI warning -struct ZigGLibCVersion { - uint32_t major; // always 2 +struct Stage2SemVer { + uint32_t major; uint32_t minor; uint32_t patch; }; -struct Stage2TargetData; - // ABI warning struct ZigTarget { enum ZigLLVM_ArchType arch; @@ -288,7 +286,8 @@ struct ZigTarget { bool is_native; - struct ZigGLibCVersion *glibc_version; // null means default + // null means default. this is double-purposed to be darwin min version + struct Stage2SemVer *glibc_or_darwin_version; const char *llvm_cpu_name; const char *llvm_cpu_features; diff --git a/src/target.cpp b/src/target.cpp index 4df2cfca85..96ac2e1666 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -347,7 +347,7 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) { return ZigLLVMGetEnvironmentTypeName(abi); } -Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { +Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) { glibc_ver->major = 2; glibc_ver->minor = 0; glibc_ver->patch = 0; @@ -371,7 +371,7 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { } void target_init_default_glibc_version(ZigTarget *target) { - *target->glibc_version = {2, 17, 0}; + *target->glibc_or_darwin_version = {2, 17, 0}; } Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) { diff --git a/src/target.hpp b/src/target.hpp index e72b6a6f49..9c6e8ce46e 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -46,7 +46,7 @@ Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arc Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); -Error target_parse_glibc_version(ZigGLibCVersion *out, const char *text); +Error target_parse_glibc_version(Stage2SemVer *out, const char *text); void target_init_default_glibc_version(ZigTarget *target); size_t target_arch_count(void); diff --git a/test/cli.zig b/test/cli.zig index bc5a29be44..117c714a29 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -92,7 +92,7 @@ fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" }); - testing.expect(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n")); + testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n")); } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { From 07f52119de2a8bdb84389c73332e113cf12ac997 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 03:11:37 -0500 Subject: [PATCH 27/37] implement native OS version detection for linux --- lib/std/c.zig | 1 + lib/std/os.zig | 24 +++++++++++++----------- lib/std/zig/system.zig | 17 ++++++++++++++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9d7d9524d6..48a3039f51 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -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/os.zig b/lib/std/os.zig index 9f349e7dc4..49e88bf9c7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3295,22 +3295,24 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { } } if (builtin.os.tag == .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), - } + 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, diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index fe2f4c7e04..ffae5c6015 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -200,7 +200,22 @@ pub const NativeTargetInfo = struct { const cpu = Target.Cpu.baseline(arch); // TODO Detect native operating system version. Until that is implemented we use the default range. - const os = Target.Os.defaultVersionRange(os_tag); + var os = Target.Os.defaultVersionRange(os_tag); + 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 => {}, + } + }, + else => {}, + } return detectAbiAndDynamicLinker(allocator, cpu, os); } From 500dde32d5cf59f5700fb3f69ae6c7a0defd8a93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 13:27:52 -0500 Subject: [PATCH 28/37] dynamic_linker becomes a field of std.zig.CrossTarget --- lib/std/build.zig | 12 ++--- lib/std/target.zig | 92 +++++++++++++++++++++++++----------- lib/std/zig/cross_target.zig | 17 +++++-- lib/std/zig/system.zig | 77 ++++++++++-------------------- src-self-hosted/stage2.zig | 15 +++--- src/all_types.hpp | 1 - src/codegen.cpp | 19 ++------ src/link.cpp | 4 +- src/main.cpp | 11 ++--- src/stage2.cpp | 7 ++- src/stage2.h | 3 +- src/target.cpp | 4 +- src/target.hpp | 2 +- 13 files changed, 137 insertions(+), 127 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 7852828f7c..3ca59ae4cb 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1177,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, @@ -1978,6 +1976,11 @@ pub const LibExeObjStep = struct { } try zig_args.append(mcpu_buffer.toSliceConst()); } + + 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| { @@ -1985,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)); diff --git a/lib/std/target.zig b/lib/std/target.zig index 6ecb679f29..3054c60467 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1099,16 +1099,52 @@ pub const Target = struct { } } + 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; + } + + /// 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, buffer: *[255]u8) ?u8 { + pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { + var result: DynamicLinker = .{}; const S = struct { - fn print(b: *[255]u8, comptime fmt: []const u8, args: var) u8 { - return @intCast(u8, (std.fmt.bufPrint(b, fmt, args) catch unreachable).len - 1); + 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.*; } - fn copy(b: *[255]u8, s: []const u8) u8 { - mem.copy(u8, b, s); - return @intCast(u8, s.len - 1); + fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { + mem.copy(u8, &r.buffer, s); + r.max_byte = @intCast(u8, s.len - 1); + return r.*; } }; const print = S.print; @@ -1116,7 +1152,7 @@ pub const Target = struct { if (self.isAndroid()) { const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; - return print(buffer, "/system/bin/linker{}", .{suffix}); + return print(&result, "/system/bin/linker{}", .{suffix}); } if (self.isMusl()) { @@ -1130,28 +1166,28 @@ pub const Target = struct { else => |arch| @tagName(arch), }; const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else ""; - return print(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); + return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { - .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"), - .netbsd => return copy(buffer, "/libexec/ld.elf_so"), - .dragonfly => return copy(buffer, "/libexec/ld-elf.so.2"), + .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 copy(buffer, "/lib/ld-linux.so.2"), + => return copy(&result, "/lib/ld-linux.so.2"), - .aarch64 => return copy(buffer, "/lib/ld-linux-aarch64.so.1"), - .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"), - .aarch64_32 => return copy(buffer, "/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 copy(buffer, switch (self.getFloatAbi()) { + => return copy(&result, switch (self.getFloatAbi()) { .hard => "/lib/ld-linux-armhf.so.3", else => "/lib/ld-linux.so.3", }), @@ -1168,20 +1204,20 @@ pub const Target = struct { }; 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(buffer, "/lib{}/{}", .{ lib_suffix, loader }); + return print(&result, "/lib{}/{}", .{ lib_suffix, loader }); }, - .powerpc => return copy(buffer, "/lib/ld.so.1"), - .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"), - .s390x => return copy(buffer, "/lib64/ld64.so.1"), - .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"), - .x86_64 => return copy(buffer, switch (self.abi) { + .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 copy(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"), - .riscv64 => return copy(buffer, "/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. @@ -1191,7 +1227,7 @@ pub const Target = struct { .bpfeb, .nvptx, .nvptx64, - => return null, + => 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. @@ -1217,7 +1253,7 @@ pub const Target = struct { .lanai, .renderscript32, .renderscript64, - => return null, + => return result, }, // Operating systems in this list have been verified as not having a standard @@ -1232,7 +1268,7 @@ pub const Target = struct { .emscripten, .wasi, .other, - => return null, + => 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. @@ -1259,7 +1295,7 @@ pub const Target = struct { .amdpal, .hermit, .hurd, - => return null, + => return result, } } }; diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index c7fd1f0464..212bc8eb9a 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -40,6 +40,10 @@ pub const CrossTarget = struct { /// If `isGnuLibC()` is `false`, this must be `null` and is ignored. glibc_version: ?SemVer = 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 OsVersion = union(enum) { none: void, semver: SemVer, @@ -48,6 +52,8 @@ pub const CrossTarget = struct { 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, @@ -170,6 +176,10 @@ pub const CrossTarget = struct { /// 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, @@ -199,8 +209,9 @@ pub const CrossTarget = struct { var dummy_diags: ParseOptions.Diagnostics = undefined; const diags = args.diagnostics orelse &dummy_diags; - // Start with everything initialized to default values. - var result: CrossTarget = .{}; + var result: CrossTarget = .{ + .dynamic_linker = DynamicLinker.init(args.dynamic_linker), + }; var it = mem.separate(args.arch_os_abi, "-"); const arch_name = it.next().?; @@ -446,7 +457,7 @@ pub const CrossTarget = struct { return self.cpu_arch == null and self.cpu_model == null 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; + self.abi == null and self.dynamic_linker.get() == null; } pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index ffae5c6015..44ff9af674 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -168,14 +168,9 @@ pub const NativePaths = struct { pub const NativeTargetInfo = struct { target: Target, - /// Contains the memory used to store the dynamic linker path. This field should - /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field - /// exists so that this API requires no allocator. - dynamic_linker_buffer: [255]u8 = undefined, + dynamic_linker: DynamicLinker = DynamicLinker{}, - /// Used to construct the dynamic linker path. This field should not be used - /// directly. See `dynamicLinker` and `setDynamicLinker`. - dynamic_linker_max: ?u8 = null, + pub const DynamicLinker = Target.DynamicLinker; pub const DetectError = error{ OutOfMemory, @@ -220,21 +215,6 @@ pub const NativeTargetInfo = struct { return detectAbiAndDynamicLinker(allocator, cpu, os); } - /// The returned memory has the same lifetime as the `NativeTargetInfo`. - pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { - const m: usize = self.dynamic_linker_max orelse return null; - return self.dynamic_linker_buffer[0 .. m + 1]; - } - - pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void { - if (dl_or_null) |dl| { - mem.copy(u8, &self.dynamic_linker_buffer, dl); - self.dynamic_linker_max = @intCast(u8, dl.len - 1); - } else { - self.dynamic_linker_max = null; - } - } - /// 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 @@ -273,15 +253,14 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - const ld_info = &ld_info_list_buffer[ld_info_list_len]; - ld_info_list_len += 1; + const ld = target.standardDynamicLinkerPath(); + if (ld.get() == null) continue; - ld_info.* = .{ - .ld_path_buffer = undefined, - .ld_path_max = undefined, + ld_info_list_buffer[ld_info_list_len] = .{ + .ld = ld, .abi = abi, }; - ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue; + ld_info_list_len += 1; } const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; @@ -298,7 +277,7 @@ pub const NativeTargetInfo = struct { // 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.ldPath()); + 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; @@ -329,8 +308,8 @@ pub const NativeTargetInfo = struct { .os = os_adjusted, .abi = found_ld_info.abi, }, + .dynamic_linker = DynamicLinker.init(found_ld_path), }; - result.setDynamicLinker(found_ld_path); return result; } @@ -472,18 +451,18 @@ pub const NativeTargetInfo = struct { elf.PT_INTERP => { 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); + 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 is "max", not "len". - // We know it will fit in u8 because we check against dynamic_linker_buffer.len above. - result.dynamic_linker_max = @intCast(u8, len - 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]; + 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.ldPath()); + 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; @@ -679,26 +658,20 @@ pub const NativeTargetInfo = struct { } fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { - var result: NativeTargetInfo = .{ - .target = .{ - .cpu = cpu, - .os = os, - .abi = Target.Abi.default(cpu.arch, os), - }, + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }; + return NativeTargetInfo{ + .target = target, + .dynamic_linker = target.standardDynamicLinkerPath(), }; - result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); - return result; } const LdInfo = struct { - ld_path_buffer: [255]u8, - ld_path_max: u8, + ld: DynamicLinker, abi: Target.Abi, - - pub fn ldPath(self: *const LdInfo) []const u8 { - const m: usize = self.ld_path_max; - return self.ld_path_buffer[0 .. m + 1]; - } }; fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 3dae15056e..726c65937e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -651,8 +651,9 @@ export fn stage2_target_parse( target: *Stage2Target, zig_triple: ?[*:0]const u8, mcpu: ?[*:0]const u8, + dynamic_linker: ?[*:0]const u8, ) Error { - stage2TargetParse(target, zig_triple, mcpu) catch |err| switch (err) { + stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) { error.OutOfMemory => return .OutOfMemory, error.UnknownArchitecture => return .UnknownArchitecture, error.UnknownOperatingSystem => return .UnknownOperatingSystem, @@ -676,14 +677,17 @@ fn stage2TargetParse( stage1_target: *Stage2Target, zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, + dynamic_linker_oz: ?[*:0]const u8, ) !void { const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null; + const dynamic_linker = if (dynamic_linker_oz) |dl_z| mem.toSliceConst(u8, dl_z) else null; var diags: CrossTarget.ParseOptions.Diagnostics = .{}; break :blk CrossTarget.parse(.{ .arch_os_abi = zig_triple, .cpu_features = mcpu, + .dynamic_linker = dynamic_linker, .diagnostics = &diags, }) catch |err| switch (err) { error.UnknownCpuModel => { @@ -1170,7 +1174,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; - if (detected_info.dynamicLinker()) |dl| { + if (detected_info.dynamic_linker.get()) |dl| { have_native_dl = true; dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); } @@ -1182,11 +1186,8 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } } if (!have_native_dl) { - var buf: [255]u8 = undefined; - dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |m| - try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1]) - else - null; + const dl = adjusted_target.standardDynamicLinkerPath(); + dynamic_linker_ptr.* = if (dl.get()) |s| try mem.dupeZ(std.heap.c_allocator, u8, s) else null; } return adjusted_target; } diff --git a/src/all_types.hpp b/src/all_types.hpp index 838709b9c0..9f6bfd80a5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2255,7 +2255,6 @@ struct CodeGen { Buf *test_name_prefix; Buf *zig_lib_dir; Buf *zig_std_dir; - Buf *dynamic_linker_path; Buf *version_script_path; const char **llvm_argv; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8ae730616d..62f2c7a8ba 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8832,19 +8832,6 @@ static void init(CodeGen *g) { } } -static void detect_dynamic_linker(CodeGen *g) { - if (g->dynamic_linker_path != nullptr) - return; - if (!g->have_dynamic_link) - return; - if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) - return; - - if (g->zig_target->dynamic_linker != nullptr) { - g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker); - } -} - static void detect_libc(CodeGen *g) { Error err; @@ -10285,6 +10272,9 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); } + if (g->zig_target->dynamic_linker != nullptr) { + cache_str(ch, g->zig_target->dynamic_linker); + } cache_int(ch, detect_subsystem(g)); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); @@ -10325,7 +10315,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_str(ch, g->libc->msvc_lib_dir); cache_str(ch, g->libc->kernel32_lib_dir); } - cache_buf_opt(ch, g->dynamic_linker_path); cache_buf_opt(ch, g->version_script_path); // gen_c_objects appends objects to g->link_objects which we want to include in the hash @@ -10422,7 +10411,6 @@ void codegen_build_and_link(CodeGen *g) { g->have_err_ret_tracing = detect_err_ret_tracing(g); g->have_sanitize_c = detect_sanitize_c(g); detect_libc(g); - detect_dynamic_linker(g); Buf digest = BUF_INIT; if (g->enable_cache) { @@ -10619,7 +10607,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o child_gen->verbose_cc = parent_gen->verbose_cc; child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features; child_gen->llvm_argv = parent_gen->llvm_argv; - child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path; codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; diff --git a/src/link.cpp b/src/link.cpp index 588771d8bf..03534db3b2 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1751,9 +1751,9 @@ static void construct_linker_job_elf(LinkJob *lj) { } if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) { - assert(g->dynamic_linker_path != nullptr); + assert(g->zig_target->dynamic_linker != nullptr); lj->args.append("-dynamic-linker"); - lj->args.append(buf_ptr(g->dynamic_linker_path)); + lj->args.append(g->zig_target->dynamic_linker); } } diff --git a/src/main.cpp b/src/main.cpp index f471bda374..92d08bed4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -401,7 +401,7 @@ static int main0(int argc, char **argv) { bool link_eh_frame_hdr = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; - Buf *dynamic_linker = nullptr; + const char *dynamic_linker = nullptr; const char *libc_txt = nullptr; ZigList clang_argv = {0}; ZigList lib_dirs = {0}; @@ -496,7 +496,7 @@ static int main0(int argc, char **argv) { os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); ZigTarget target; - if ((err = target_parse_triple(&target, "native", nullptr))) { + if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) { fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); return EXIT_FAILURE; } @@ -766,7 +766,7 @@ static int main0(int argc, char **argv) { } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; } else if (strcmp(arg, "--dynamic-linker") == 0) { - dynamic_linker = buf_create_from_str(argv[i]); + dynamic_linker = argv[i]; } else if (strcmp(arg, "--libc") == 0) { libc_txt = argv[i]; } else if (strcmp(arg, "-D") == 0) { @@ -968,7 +968,7 @@ static int main0(int argc, char **argv) { init_all_targets(); ZigTarget target; - if ((err = target_parse_triple(&target, target_string, mcpu))) { + if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { fprintf(stderr, "invalid target: %s\n" "See `%s targets` to display valid targets.\n", err_str(err), arg0); return print_error_usage(arg0); @@ -1193,7 +1193,6 @@ static int main0(int argc, char **argv) { codegen_set_strip(g, strip); g->is_dynamic = is_dynamic; - g->dynamic_linker_path = dynamic_linker; g->verbose_tokenize = verbose_tokenize; g->verbose_ast = verbose_ast; g->verbose_link = verbose_link; @@ -1320,7 +1319,7 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_SUCCESS); } else if (cmd == CmdTest) { ZigTarget native; - if ((err = target_parse_triple(&native, "native", nullptr))) { + if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) { fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); return EXIT_FAILURE; } diff --git a/src/stage2.cpp b/src/stage2.cpp index 67a518e1fa..1f6cb2d6aa 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -191,7 +191,9 @@ static void get_native_target(ZigTarget *target) { } } -Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) { +Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker) +{ Error err; if (zig_triple == nullptr) { @@ -249,6 +251,9 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons target->cache_hash = "\n\n"; } + if (dynamic_linker != nullptr) { + target->dynamic_linker = dynamic_linker; + } return ErrorNone; } diff --git a/src/stage2.h b/src/stage2.h index 20311788b2..b73269f4e8 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -298,7 +298,8 @@ struct ZigTarget { }; // ABI warning -ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu); +ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker); // ABI warning diff --git a/src/target.cpp b/src/target.cpp index 96ac2e1666..f0030e05ac 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -410,8 +410,8 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si return ErrorUnknownABI; } -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu) { - return stage2_target_parse(target, triple, mcpu); +Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) { + return stage2_target_parse(target, triple, mcpu, dynamic_linker); } const char *target_arch_name(ZigLLVM_ArchType arch) { diff --git a/src/target.hpp b/src/target.hpp index 9c6e8ce46e..01f2c6b168 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -41,7 +41,7 @@ enum CIntType { CIntTypeCount, }; -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu); +Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker); Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len); Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); From 578dc16910c7fb6c9e3a059915b13fa6609e066f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 15:41:30 -0500 Subject: [PATCH 29/37] fix compiler crash when comptime parsing targets --- src/analyze.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 2c8244d053..90037a1191 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5401,6 +5401,8 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { static bool can_mutate_comptime_var_state(ZigValue *value) { assert(value != nullptr); + if (value->special == ConstValSpecialUndef) + return false; switch (value->type->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -6701,8 +6703,16 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { } static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) { - assert(a->data.x_array.special != ConstArraySpecialUndef); - assert(b->data.x_array.special != ConstArraySpecialUndef); + if (a->data.x_array.special == ConstArraySpecialUndef && + b->data.x_array.special == ConstArraySpecialUndef) + { + return true; + } + if (a->data.x_array.special == ConstArraySpecialUndef || + b->data.x_array.special == ConstArraySpecialUndef) + { + return false; + } if (a->data.x_array.special == ConstArraySpecialBuf && b->data.x_array.special == ConstArraySpecialBuf) { @@ -9390,13 +9400,24 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) { dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i; } } else if (dest->type->id == ZigTypeIdArray) { - if (dest->data.x_array.special == ConstArraySpecialNone) { - dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); - for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { - copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); - dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + switch (dest->data.x_array.special) { + case ConstArraySpecialNone: { + dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); + for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { + copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); + dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + } + break; + } + case ConstArraySpecialUndef: { + // Nothing to copy; the above memcpy did everything we needed. + break; + } + case ConstArraySpecialBuf: { + dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf); + break; } } } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) { From 8691d3c50d65c3e9e399c83cf2fc1a1173acb6d2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 17:23:16 -0500 Subject: [PATCH 30/37] improve std.zig.system.NativeTargetInfo.detect It now takes a std.zig.CrossTarget parameter, and only resolves native things, leaving explicitly overridden things alone. --- lib/std/zig/cross_target.zig | 18 ++-- lib/std/zig/system.zig | 154 ++++++++++++++++++++++++++--------- src-self-hosted/stage2.zig | 50 ++++-------- 3 files changed, 141 insertions(+), 81 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 212bc8eb9a..7ce35a26dc 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -7,19 +7,17 @@ const mem = std.mem; /// 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. + /// `null` means native. If this is `null` then `cpu_model` must be `null`. cpu_arch: ?Target.Cpu.Arch = null, - /// If `cpu_arch` is native, `null` means native. Otherwise it means baseline. + /// `null` means native. /// If this is non-null, `cpu_arch` must be specified. cpu_model: ?*const Target.Cpu.Model = null, /// Sparse set of CPU features to add to the set from `cpu_model`. - /// If this is non-empty, `cpu_arch` must be specified. 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`. - /// If this is non-empty, `cpu_arch` must be specified. cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, /// `null` means native. @@ -33,13 +31,13 @@ pub const CrossTarget = struct { /// When `os_tag` is native, `null` means equal to the native OS version. os_version_max: ?OsVersion = null, - /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. - abi: ?Target.Abi = 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{}, @@ -146,6 +144,7 @@ pub const CrossTarget = struct { } } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn toTarget(self: CrossTarget) Target { return .{ .cpu = self.getCpu(), @@ -307,6 +306,7 @@ pub const CrossTarget = struct { return result; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getCpu(self: CrossTarget) Target.Cpu { if (self.cpu_arch) |arch| { if (self.cpu_model) |model| { @@ -342,6 +342,7 @@ pub const CrossTarget = struct { 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 @@ -378,6 +379,7 @@ pub const CrossTarget = struct { 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; @@ -385,6 +387,7 @@ pub const CrossTarget = struct { 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; @@ -392,6 +395,7 @@ pub const CrossTarget = struct { 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; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 44ff9af674..f31044b811 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -7,6 +7,7 @@ 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 = Target.current.os.tag == .windows; @@ -182,37 +183,87 @@ pub const NativeTargetInfo = struct { DeviceBusy, }; - /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. - /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + /// 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) DetectError!NativeTargetInfo { - const arch = Target.current.cpu.arch; - const os_tag = Target.current.os.tag; - - // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. - const cpu = Target.Cpu.baseline(arch); - - // TODO Detect native operating system version. Until that is implemented we use the default range. - var os = Target.Os.defaultVersionRange(os_tag); - 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 => {}, + pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { + const cpu = blk: { + if (cross_target.cpu_arch) |arch| { + if (cross_target.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + cross_target.updateCpuFeatures(&adjusted_model.features); + break :blk adjusted_model; + } else { + // TODO Detect native CPU model. Until that is implemented we use baseline. + var adjusted_baseline = Target.Cpu.baseline(arch); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + break :blk adjusted_baseline; } - }, - else => {}, + } else { + assert(cross_target.cpu_model == null); + // TODO Detect native CPU model & features. Until that is implemented we use baseline. + var adjusted_baseline = Target.Cpu.baseline(Target.current.cpu.arch); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + break :blk adjusted_baseline; + } + }; + + 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 => {}, + } } - return detectAbiAndDynamicLinker(allocator, cpu, os); + 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 @@ -224,9 +275,14 @@ pub const NativeTargetInfo = struct { allocator: *Allocator, cpu: Target.Cpu, os: Target.Os, + cross_target: CrossTarget, ) DetectError!NativeTargetInfo { - if (!comptime Target.current.hasDynamicLinker()) { - return defaultAbiAndDynamicLinker(cpu, os); + 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()); + if (!native_target_has_ld or have_all_info) { + 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. @@ -264,6 +320,13 @@ pub const NativeTargetInfo = struct { } 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: { @@ -288,7 +351,9 @@ pub const NativeTargetInfo = struct { // Look for glibc version. var os_adjusted = os; - if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu()) { + 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) { @@ -306,9 +371,12 @@ pub const NativeTargetInfo = struct { .target = .{ .cpu = cpu, .os = os_adjusted, - .abi = found_ld_info.abi, + .abi = cross_target.abi orelse found_ld_info.abi, }, - .dynamic_linker = DynamicLinker.init(found_ld_path), + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + DynamicLinker.init(found_ld_path) + else + cross_target.dynamic_linker, }; return result; } @@ -317,7 +385,7 @@ pub const NativeTargetInfo = struct { // 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) catch |err| switch (err) { + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list, cross_target) catch |err| switch (err) { error.FileSystem, error.SystemResources, error.SymLinkLoop, @@ -337,7 +405,7 @@ pub const NativeTargetInfo = struct { error.UnexpectedEndOfFile, error.NameTooLong, // Finally, we fall back on the standard path. - => defaultAbiAndDynamicLinker(cpu, os), + => defaultAbiAndDynamicLinker(cpu, os, cross_target), }; } @@ -379,6 +447,7 @@ pub const NativeTargetInfo = struct { 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, @@ -424,10 +493,12 @@ pub const NativeTargetInfo = struct { .target = .{ .cpu = cpu, .os = os, - .abi = Target.Abi.default(cpu.arch, 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; @@ -448,7 +519,7 @@ pub const NativeTargetInfo = struct { 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 => { + 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; @@ -470,7 +541,9 @@ pub const NativeTargetInfo = struct { } }, // We only need this for detecting glibc version. - elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + 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); @@ -515,7 +588,7 @@ pub const NativeTargetInfo = struct { } } - if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + 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); @@ -657,15 +730,18 @@ pub const NativeTargetInfo = struct { return i; } - fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, cross_target: CrossTarget) !NativeTargetInfo { const target: Target = .{ .cpu = cpu, .os = os, - .abi = Target.Abi.default(cpu.arch, os), + .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), }; return NativeTargetInfo{ .target = target, - .dynamic_linker = target.standardDynamicLinkerPath(), + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + target.standardDynamicLinkerPath() + else + cross_target.dynamic_linker, }; } diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 726c65937e..bef13a08e9 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1152,44 +1152,24 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { return @intToEnum(Enum, @intCast(@TagType(Enum), int)); } -/// TODO move dynamic linker to be part of the target -/// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { - var adjusted_target = cross_target.toTarget(); - var have_native_dl = false; - if (cross_target.cpu_arch == null or cross_target.os_tag == null) { - const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (cross_target.cpu_arch == null) { - adjusted_target.cpu = detected_info.target.cpu; - - // TODO We want to just use detected_info.target but implementing - // CPU model & feature detection is todo so here we rely on LLVM. - // There is another occurrence of this; search for detectNativeCpuWithLLVM. - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const arch = std.Target.current.cpu.arch; - adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - } - if (cross_target.os_tag == null) { - adjusted_target.os = detected_info.target.os; - - if (detected_info.dynamic_linker.get()) |dl| { - have_native_dl = true; - dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); - } - if (cross_target.abi == null) { - adjusted_target.abi = detected_info.target.abi; - } - } else if (cross_target.abi == null) { - adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); - } + var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); + if (cross_target.cpu_arch == null or cross_target.cpu_model == null) { + // TODO We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + const arch = std.Target.current.cpu.arch; + info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + cross_target.updateCpuFeatures(&info.target.cpu.features); } - if (!have_native_dl) { - const dl = adjusted_target.standardDynamicLinkerPath(); - dynamic_linker_ptr.* = if (dl.get()) |s| try mem.dupeZ(std.heap.c_allocator, u8, s) else null; + if (info.dynamic_linker.get()) |dl| { + dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); + } else { + dynamic_linker_ptr.* = null; } - return adjusted_target; + return info.target; } // ABI warning From aa13f339d4d91f8e39f005821c172290e1a0227f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:09:33 -0500 Subject: [PATCH 31/37] fix handling of CrossTarget.cpu_model --- lib/std/zig/cross_target.zig | 16 ++++++++++++++-- lib/std/zig/system.zig | 19 ++++++------------- src-self-hosted/stage2.zig | 1 + 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 7ce35a26dc..a2d265707b 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -10,8 +10,7 @@ pub const CrossTarget = struct { /// `null` means native. If this is `null` then `cpu_model` must be `null`. cpu_arch: ?Target.Cpu.Arch = null, - /// `null` means native. - /// If this is non-null, `cpu_arch` must be specified. + /// `null` means native. If this is non-null, `cpu_arch` must be specified. cpu_model: ?*const Target.Cpu.Model = null, /// Sparse set of CPU features to add to the set from `cpu_model`. @@ -301,6 +300,10 @@ pub const CrossTarget = struct { return error.UnknownCpuFeature; } } + } else if (arch_is_native) { + result.cpu_model = null; + } else { + result.cpu_model = Target.Cpu.Model.baseline(arch); } return result; @@ -725,6 +728,15 @@ pub const CrossTarget = struct { }; test "CrossTarget.parse" { + { + 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 == null); + } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index f31044b811..b712c7c062 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -192,21 +192,14 @@ pub const NativeTargetInfo = struct { /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { const cpu = blk: { - if (cross_target.cpu_arch) |arch| { - if (cross_target.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); - cross_target.updateCpuFeatures(&adjusted_model.features); - break :blk adjusted_model; - } else { - // TODO Detect native CPU model. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(arch); - cross_target.updateCpuFeatures(&adjusted_baseline.features); - break :blk adjusted_baseline; - } + const arch = cross_target.getCpuArch(); + if (cross_target.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + cross_target.updateCpuFeatures(&adjusted_model.features); + break :blk adjusted_model; } else { - assert(cross_target.cpu_model == null); // TODO Detect native CPU model & features. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(Target.current.cpu.arch); + var adjusted_baseline = Target.Cpu.baseline(arch); cross_target.updateCpuFeatures(&adjusted_baseline.features); break :blk adjusted_baseline; } diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index bef13a08e9..696d7ea760 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1163,6 +1163,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) const arch = std.Target.current.cpu.arch; info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); cross_target.updateCpuFeatures(&info.target.cpu.features); + info.target.cpu.arch = cross_target.getCpuArch(); } if (info.dynamic_linker.get()) |dl| { dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); From 4591236ae1de25e55a7d134d5cc0dcc3e1ad6a6e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:31:46 -0500 Subject: [PATCH 32/37] CrossTarget.cpu_model: communicate intent precisely --- lib/std/zig/cross_target.zig | 75 ++++++++++++++++++++++-------------- lib/std/zig/system.zig | 31 ++++++++++----- src-self-hosted/stage2.zig | 2 +- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index a2d265707b..16c0a64f6b 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -7,11 +7,10 @@ const mem = std.mem; /// 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. If this is `null` then `cpu_model` must be `null`. + /// `null` means native. cpu_arch: ?Target.Cpu.Arch = null, - /// `null` means native. If this is non-null, `cpu_arch` must be specified. - cpu_model: ?*const Target.Cpu.Model = 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, @@ -41,6 +40,20 @@ pub const CrossTarget = struct { /// 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, @@ -54,7 +67,7 @@ pub const CrossTarget = struct { pub fn fromTarget(target: Target) CrossTarget { var result: CrossTarget = .{ .cpu_arch = target.cpu.arch, - .cpu_model = target.cpu.model, + .cpu_model = .{ .explicit = target.cpu.model }, .os_tag = target.os.tag, .os_version_min = undefined, .os_version_max = undefined, @@ -266,11 +279,11 @@ pub const CrossTarget = struct { const add_set = &result.cpu_features_add; const sub_set = &result.cpu_features_sub; if (mem.eql(u8, cpu_name, "native")) { - result.cpu_model = null; + result.cpu_model = .native; } else if (mem.eql(u8, cpu_name, "baseline")) { - result.cpu_model = Target.Cpu.Model.baseline(arch); + result.cpu_model = .baseline; } else { - result.cpu_model = try arch.parseCpuModel(cpu_name); + result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) }; } while (index < cpu_features.len) { @@ -300,10 +313,6 @@ pub const CrossTarget = struct { return error.UnknownCpuFeature; } } - } else if (arch_is_native) { - result.cpu_model = null; - } else { - result.cpu_model = Target.Cpu.Model.baseline(arch); } return result; @@ -311,24 +320,33 @@ pub const CrossTarget = struct { /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getCpu(self: CrossTarget) Target.Cpu { - if (self.cpu_arch) |arch| { - if (self.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); - self.updateCpuFeatures(&adjusted_model.features); - return adjusted_model; - } else { - var adjusted_baseline = Target.Cpu.baseline(arch); + 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; - } - } else { - assert(self.cpu_model == null); - assert(self.cpu_features_sub.isEmpty()); - assert(self.cpu_features_add.isEmpty()); - // 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; + }, + .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; + }, } } @@ -461,7 +479,8 @@ pub const CrossTarget = struct { } pub fn isNative(self: CrossTarget) bool { - return self.cpu_arch == null and self.cpu_model == null and + 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; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index b712c7c062..5ff17bae23 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -191,18 +191,18 @@ pub const NativeTargetInfo = struct { /// deinitialization method. /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { - const cpu = blk: { - const arch = cross_target.getCpuArch(); - if (cross_target.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); + 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; - } else { - // TODO Detect native CPU model & features. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(arch); - cross_target.updateCpuFeatures(&adjusted_baseline.features); - break :blk adjusted_baseline; - } + }, }; var os = Target.Os.defaultVersionRange(cross_target.getOsTag()); @@ -758,4 +758,15 @@ pub const NativeTargetInfo = struct { } } } + + 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; + } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 696d7ea760..a8aa10d91e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1154,7 +1154,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); - if (cross_target.cpu_arch == null or cross_target.cpu_model == null) { + if (cross_target.cpu_arch == null or cross_target.cpu_model == .native) { // TODO We want to just use detected_info.target but implementing // CPU model & feature detection is todo so here we rely on LLVM. const llvm = @import("llvm.zig"); From 7e3bb00a0e919bdb2a778a7efb648dfc6afa8318 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:48:19 -0500 Subject: [PATCH 33/37] don't choose native ld path when os is non native --- lib/std/zig/system.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5ff17bae23..aa8def32a9 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -274,7 +274,8 @@ pub const NativeTargetInfo = struct { 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()); - if (!native_target_has_ld or have_all_info) { + 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 From 4bc893c346aa45972ae884188388269b66efc3b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:05:14 -0500 Subject: [PATCH 34/37] update test to newer API --- lib/std/zig/cross_target.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 16c0a64f6b..66b7344840 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -754,7 +754,7 @@ test "CrossTarget.parse" { }); std.testing.expect(cross_target.cpu_arch.? == .aarch64); - std.testing.expect(cross_target.cpu_model == null); + std.testing.expect(cross_target.cpu_model == .native); } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); From e683eee415bc4362f9c0e095ceecebe577c47d06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:18:27 -0500 Subject: [PATCH 35/37] fix CrossTarget.isNative, setGnuLibCVersion, zigTriple --- lib/std/zig/cross_target.zig | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 66b7344840..08a8d21fad 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -483,7 +483,7 @@ pub const CrossTarget = struct { (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; + 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 { @@ -514,13 +514,10 @@ pub const CrossTarget = struct { } } - if (self.abi) |abi| { + if (self.glibc_version) |v| { + try result.print("-{}.{}", .{ @tagName(self.getAbi()), v }); + } else if (self.abi) |abi| { try result.print("-{}", .{@tagName(abi)}); - if (self.glibc_version) |v| { - try result.print(".{}", .{v}); - } - } else { - assert(self.glibc_version == null); } return result.toOwnedSlice(); @@ -643,7 +640,7 @@ pub const CrossTarget = struct { return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); } - pub fn setGnuLibCVersion(self: CrossTarget, major: u32, minor: u32, patch: u32) void { + 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 }; } @@ -747,6 +744,14 @@ pub const CrossTarget = struct { }; 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", From 1aef0bef754ddf1943e22740ef045fab7769cde1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:24:52 -0500 Subject: [PATCH 36/37] std.Target.Os.WindowsVersion: non-exhaustive enum --- lib/std/target.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/target.zig b/lib/std/target.zig index 3054c60467..5807ba65ef 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -91,6 +91,7 @@ pub const Target = struct { win10_rs4 = 0x0A000005, win10_rs5 = 0x0A000006, win10_19h1 = 0x0A000007, + _, pub const Range = struct { min: WindowsVersion, From 3cba603eae1a1c8b0338f5584041c73d55682c0a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 01:05:11 -0500 Subject: [PATCH 37/37] fix crash when building docgen --- src/analyze.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 90037a1191..1a9172c030 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6734,8 +6734,6 @@ static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_ bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { if (a->type->id != b->type->id) return false; - assert(a->special == ConstValSpecialStatic); - assert(b->special == ConstValSpecialStatic); if (a->type == b->type) { switch (type_has_one_possible_value(g, a->type)) { case OnePossibleValueInvalid: @@ -6746,6 +6744,11 @@ bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { return true; } } + if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) { + return a->special == b->special; + } + assert(a->special == ConstValSpecialStatic); + assert(b->special == ConstValSpecialStatic); switch (a->type->id) { case ZigTypeIdOpaque: zig_unreachable();