diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 4d671efe948da8caaa71d6db8af4b9b3e69603cf..c5c62911ccf85a8cc62802a0034b06049c397427 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -13,12 +13,10 @@ const assert = std.debug.assert; const process = std.process; const Target = std.Target; const CrossTarget = std.zig.CrossTarget; -const macos = @import("system/macos.zig"); const native_endian = std.Target.current.cpu.arch.endian(); const linux = @import("system/linux.zig"); pub const windows = @import("system/windows.zig"); - -pub const getSDKPath = macos.getSDKPath; +pub const darwin = @import("system/darwin.zig"); pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), @@ -255,7 +253,7 @@ pub const NativeTargetInfo = struct { os.version_range.windows.min = detected_version; os.version_range.windows.max = detected_version; }, - .macos => try macos.detect(&os), + .macos => try darwin.macos.detect(&os), .freebsd, .netbsd, .dragonfly => { const key = switch (Target.current.os.tag) { .freebsd => "kern.osreldate", @@ -972,7 +970,7 @@ pub const NativeTargetInfo = struct { switch (std.Target.current.os.tag) { .linux => return linux.detectNativeCpuAndFeatures(), - .macos => return macos.detectNativeCpuAndFeatures(), + .macos => return darwin.macos.detectNativeCpuAndFeatures(), else => {}, } @@ -983,6 +981,6 @@ pub const NativeTargetInfo = struct { }; test { - _ = @import("system/macos.zig"); + _ = @import("system/darwin.zig"); _ = @import("system/linux.zig"); } diff --git a/lib/std/zig/system/darwin.zig b/lib/std/zig/system/darwin.zig new file mode 100644 index 0000000000000000000000000000000000000000..1e8d9e4b48ea29f9bba7b073f23da615dba9cdb4 --- /dev/null +++ b/lib/std/zig/system/darwin.zig @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("std"); +const mem = std.mem; +const Allocator = mem.Allocator; +const Target = std.Target; + +pub const macos = @import("darwin/macos.zig"); + +/// Detect SDK path on Darwin. +/// Calls `xcrun --sdk --show-sdk-path` which result can be used to specify +/// `--sysroot` of the compiler. +/// The caller needs to free the resulting path slice. +pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 { + const is_simulator_abi = target.abi == .simulator; + const sdk = switch (target.os.tag) { + .macos => "macosx", + .ios => if (is_simulator_abi) "iphonesimulator" else "iphoneos", + .watchos => if (is_simulator_abi) "watchsimulator" else "watchos", + .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", + else => return null, + }; + + const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; + const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); + defer { + allocator.free(result.stderr); + allocator.free(result.stdout); + } + if (result.stderr.len != 0 or result.term.Exited != 0) { + // We don't actually care if there were errors as this is best-effort check anyhow + // and in the worst case the user can specify the sysroot manually. + return null; + } + const sysroot = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")); + return sysroot; +} + +test "" { + _ = @import("darwin/macos.zig"); +} diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig new file mode 100644 index 0000000000000000000000000000000000000000..c8f48800c548a374d919bf5c4644f1d3620791a6 --- /dev/null +++ b/lib/std/zig/system/darwin/macos.zig @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const testing = std.testing; +const os = std.os; + +const Target = std.Target; + +/// Detect macOS version. +/// `target_os` is not modified in case of error. +pub fn detect(target_os: *Target.Os) !void { + // Drop use of osproductversion sysctl because: + // 1. only available 10.13.4 High Sierra and later + // 2. when used from a binary built against < SDK 11.0 it returns 10.16 and masks Big Sur 11.x version + // + // NEW APPROACH, STEP 1, parse file: + // + // /System/Library/CoreServices/SystemVersion.plist + // + // NOTE: Historically `SystemVersion.plist` first appeared circa '2003 + // with the release of Mac OS X 10.3.0 Panther. + // + // and if it contains a `10.16` value where the `16` is `>= 16` then it is non-canonical, + // discarded, and we move on to next step. Otherwise we accept the version. + // + // BACKGROUND: `10.(16+)` is not a proper version and does not have enough fidelity to + // indicate minor/point version of Big Sur and later. It is a context-sensitive result + // issued by the kernel for backwards compatibility purposes. Likely the kernel checks + // if the executable was linked against an SDK older than Big Sur. + // + // STEP 2, parse next file: + // + // /System/Library/CoreServices/.SystemVersionPlatform.plist + // + // NOTE: Historically `SystemVersionPlatform.plist` first appeared circa '2020 + // with the release of macOS 11.0 Big Sur. + // + // Accessing the content via this path circumvents a context-sensitive result and + // yields a canonical Big Sur version. + // + // At this time there is no other known way for a < SDK 11.0 executable to obtain a + // canonical Big Sur version. + // + // This implementation uses a reasonably simplified approach to parse .plist file + // that while it is an xml document, we have good history on the file and its format + // such that I am comfortable with implementing a minimalistic parser. + // Things like string and general escapes are not supported. + const prefixSlash = "/System/Library/CoreServices/"; + const paths = [_][]const u8{ + prefixSlash ++ "SystemVersion.plist", + prefixSlash ++ ".SystemVersionPlatform.plist", + }; + for (paths) |path| { + // approx. 4 times historical file size + var buf: [2048]u8 = undefined; + + if (std.fs.cwd().readFile(path, &buf)) |bytes| { + if (parseSystemVersion(bytes)) |ver| { + // never return non-canonical `10.(16+)` + if (!(ver.major == 10 and ver.minor >= 16)) { + target_os.version_range.semver.min = ver; + target_os.version_range.semver.max = ver; + return; + } + continue; + } else |_| { + return error.OSVersionDetectionFail; + } + } else |_| { + return error.OSVersionDetectionFail; + } + } + return error.OSVersionDetectionFail; +} + +fn parseSystemVersion(buf: []const u8) !std.builtin.Version { + var svt = SystemVersionTokenizer{ .bytes = buf }; + try svt.skipUntilTag(.start, "dict"); + while (true) { + try svt.skipUntilTag(.start, "key"); + const content = try svt.expectContent(); + try svt.skipUntilTag(.end, "key"); + if (std.mem.eql(u8, content, "ProductVersion")) break; + } + try svt.skipUntilTag(.start, "string"); + const ver = try svt.expectContent(); + try svt.skipUntilTag(.end, "string"); + + return std.builtin.Version.parse(ver); +} + +const SystemVersionTokenizer = struct { + bytes: []const u8, + index: usize = 0, + state: State = .begin, + + fn next(self: *@This()) !?Token { + var mark: usize = self.index; + var tag = Tag{}; + var content: []const u8 = ""; + + while (self.index < self.bytes.len) { + const char = self.bytes[self.index]; + switch (self.state) { + .begin => switch (char) { + '<' => { + self.state = .tag0; + self.index += 1; + tag = Tag{}; + mark = self.index; + }, + '>' => { + return error.BadToken; + }, + else => { + self.state = .content; + content = ""; + mark = self.index; + }, + }, + .tag0 => switch (char) { + '<' => { + return error.BadToken; + }, + '>' => { + self.state = .begin; + self.index += 1; + tag.name = self.bytes[mark..self.index]; + return Token{ .tag = tag }; + }, + '"' => { + self.state = .tag_string; + self.index += 1; + }, + '/' => { + self.state = .tag0_end_or_empty; + self.index += 1; + }, + 'A'...'Z', 'a'...'z' => { + self.state = .tagN; + tag.kind = .start; + self.index += 1; + }, + else => { + self.state = .tagN; + self.index += 1; + }, + }, + .tag0_end_or_empty => switch (char) { + '<' => { + return error.BadToken; + }, + '>' => { + self.state = .begin; + tag.kind = .empty; + tag.name = self.bytes[self.index..self.index]; + self.index += 1; + return Token{ .tag = tag }; + }, + else => { + self.state = .tagN; + tag.kind = .end; + mark = self.index; + self.index += 1; + }, + }, + .tagN => switch (char) { + '<' => { + return error.BadToken; + }, + '>' => { + self.state = .begin; + tag.name = self.bytes[mark..self.index]; + self.index += 1; + return Token{ .tag = tag }; + }, + '"' => { + self.state = .tag_string; + self.index += 1; + }, + '/' => { + self.state = .tagN_end; + tag.kind = .end; + self.index += 1; + }, + else => { + self.index += 1; + }, + }, + .tagN_end => switch (char) { + '>' => { + self.state = .begin; + tag.name = self.bytes[mark..self.index]; + self.index += 1; + return Token{ .tag = tag }; + }, + else => { + return error.BadToken; + }, + }, + .tag_string => switch (char) { + '"' => { + self.state = .tagN; + self.index += 1; + }, + else => { + self.index += 1; + }, + }, + .content => switch (char) { + '<' => { + self.state = .tag0; + content = self.bytes[mark..self.index]; + self.index += 1; + tag = Tag{}; + mark = self.index; + return Token{ .content = content }; + }, + '>' => { + return error.BadToken; + }, + else => { + self.index += 1; + }, + }, + } + } + + return null; + } + + fn expectContent(self: *@This()) ![]const u8 { + if (try self.next()) |tok| { + switch (tok) { + .content => |content| { + return content; + }, + else => {}, + } + } + return error.UnexpectedToken; + } + + fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void { + while (try self.next()) |tok| { + switch (tok) { + .tag => |tag| { + if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return; + }, + else => {}, + } + } + return error.TagNotFound; + } + + const State = enum { + begin, + tag0, + tag0_end_or_empty, + tagN, + tagN_end, + tag_string, + content, + }; + + const Token = union(enum) { + tag: Tag, + content: []const u8, + }; + + const Tag = struct { + kind: Kind = .unknown, + name: []const u8 = "", + + const Kind = enum { unknown, start, end, empty }; + }; +}; + +test "detect" { + const cases = .{ + .{ + \\ + \\ + \\ + \\ + \\ ProductBuildVersion + \\ 7B85 + \\ ProductCopyright + \\ Apple Computer, Inc. 1983-2003 + \\ ProductName + \\ Mac OS X + \\ ProductUserVisibleVersion + \\ 10.3 + \\ ProductVersion + \\ 10.3 + \\ + \\ + , + .{ .major = 10, .minor = 3 }, + }, + .{ + \\ + \\ + \\ + \\ + \\ ProductBuildVersion + \\ 7W98 + \\ ProductCopyright + \\ Apple Computer, Inc. 1983-2004 + \\ ProductName + \\ Mac OS X + \\ ProductUserVisibleVersion + \\ 10.3.9 + \\ ProductVersion + \\ 10.3.9 + \\ + \\ + , + .{ .major = 10, .minor = 3, .patch = 9 }, + }, + .{ + \\ + \\ + \\ + \\ + \\ ProductBuildVersion + \\ 19G68 + \\ ProductCopyright + \\ 1983-2020 Apple Inc. + \\ ProductName + \\ Mac OS X + \\ ProductUserVisibleVersion + \\ 10.15.6 + \\ ProductVersion + \\ 10.15.6 + \\ iOSSupportVersion + \\ 13.6 + \\ + \\ + , + .{ .major = 10, .minor = 15, .patch = 6 }, + }, + .{ + \\ + \\ + \\ + \\ + \\ ProductBuildVersion + \\ 20A2408 + \\ ProductCopyright + \\ 1983-2020 Apple Inc. + \\ ProductName + \\ macOS + \\ ProductUserVisibleVersion + \\ 11.0 + \\ ProductVersion + \\ 11.0 + \\ iOSSupportVersion + \\ 14.2 + \\ + \\ + , + .{ .major = 11, .minor = 0 }, + }, + .{ + \\ + \\ + \\ + \\ + \\ ProductBuildVersion + \\ 20C63 + \\ ProductCopyright + \\ 1983-2020 Apple Inc. + \\ ProductName + \\ macOS + \\ ProductUserVisibleVersion + \\ 11.1 + \\ ProductVersion + \\ 11.1 + \\ iOSSupportVersion + \\ 14.3 + \\ + \\ + , + .{ .major = 11, .minor = 1 }, + }, + }; + + inline for (cases) |case| { + const ver0 = try parseSystemVersion(case[0]); + const ver1: std.builtin.Version = case[1]; + try testVersionEquality(ver1, ver0); + } +} + +fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void { + var b_expected: [64]u8 = undefined; + const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected}); + + var b_got: [64]u8 = undefined; + const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got}); + + try testing.expectEqualStrings(s_expected, s_got); +} + +pub fn detectNativeCpuAndFeatures() ?Target.Cpu { + var cpu_family: os.CPUFAMILY = undefined; + var len: usize = @sizeOf(os.CPUFAMILY); + os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) { + error.NameTooLong => unreachable, // constant, known good value + error.PermissionDenied => unreachable, // only when setting values, + error.SystemResources => unreachable, // memory already on the stack + error.UnknownName => unreachable, // constant, known good value + error.Unexpected => unreachable, // EFAULT: stack should be safe, EISDIR/ENOTDIR: constant, known good value + }; + + const current_arch = Target.current.cpu.arch; + switch (current_arch) { + .aarch64, .aarch64_be, .aarch64_32 => { + const model = switch (cpu_family) { + .ARM_FIRESTORM_ICESTORM => &Target.aarch64.cpu.apple_a14, + .ARM_LIGHTNING_THUNDER => &Target.aarch64.cpu.apple_a13, + .ARM_VORTEX_TEMPEST => &Target.aarch64.cpu.apple_a12, + .ARM_MONSOON_MISTRAL => &Target.aarch64.cpu.apple_a11, + .ARM_HURRICANE => &Target.aarch64.cpu.apple_a10, + .ARM_TWISTER => &Target.aarch64.cpu.apple_a9, + .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8, + .ARM_CYCLONE => &Target.aarch64.cpu.cyclone, + else => return null, + }; + + return Target.Cpu{ + .arch = current_arch, + .model = model, + .features = model.features, + }; + }, + else => {}, + } + + return null; +} diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/macos.zig deleted file mode 100644 index ae450ecae5dad2feb8a76ec7489fc428500743f3..0000000000000000000000000000000000000000 --- a/lib/std/zig/system/macos.zig +++ /dev/null @@ -1,469 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2015-2021 Zig Contributors -// This file is part of [zig](https://ziglang.org/), which is MIT licensed. -// The MIT license requires this copyright notice to be included in all copies -// and substantial portions of the software. -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; -const testing = std.testing; -const os = std.os; - -const Target = std.Target; - -/// Detect macOS version. -/// `target_os` is not modified in case of error. -pub fn detect(target_os: *Target.Os) !void { - // Drop use of osproductversion sysctl because: - // 1. only available 10.13.4 High Sierra and later - // 2. when used from a binary built against < SDK 11.0 it returns 10.16 and masks Big Sur 11.x version - // - // NEW APPROACH, STEP 1, parse file: - // - // /System/Library/CoreServices/SystemVersion.plist - // - // NOTE: Historically `SystemVersion.plist` first appeared circa '2003 - // with the release of Mac OS X 10.3.0 Panther. - // - // and if it contains a `10.16` value where the `16` is `>= 16` then it is non-canonical, - // discarded, and we move on to next step. Otherwise we accept the version. - // - // BACKGROUND: `10.(16+)` is not a proper version and does not have enough fidelity to - // indicate minor/point version of Big Sur and later. It is a context-sensitive result - // issued by the kernel for backwards compatibility purposes. Likely the kernel checks - // if the executable was linked against an SDK older than Big Sur. - // - // STEP 2, parse next file: - // - // /System/Library/CoreServices/.SystemVersionPlatform.plist - // - // NOTE: Historically `SystemVersionPlatform.plist` first appeared circa '2020 - // with the release of macOS 11.0 Big Sur. - // - // Accessing the content via this path circumvents a context-sensitive result and - // yields a canonical Big Sur version. - // - // At this time there is no other known way for a < SDK 11.0 executable to obtain a - // canonical Big Sur version. - // - // This implementation uses a reasonably simplified approach to parse .plist file - // that while it is an xml document, we have good history on the file and its format - // such that I am comfortable with implementing a minimalistic parser. - // Things like string and general escapes are not supported. - const prefixSlash = "/System/Library/CoreServices/"; - const paths = [_][]const u8{ - prefixSlash ++ "SystemVersion.plist", - prefixSlash ++ ".SystemVersionPlatform.plist", - }; - for (paths) |path| { - // approx. 4 times historical file size - var buf: [2048]u8 = undefined; - - if (std.fs.cwd().readFile(path, &buf)) |bytes| { - if (parseSystemVersion(bytes)) |ver| { - // never return non-canonical `10.(16+)` - if (!(ver.major == 10 and ver.minor >= 16)) { - target_os.version_range.semver.min = ver; - target_os.version_range.semver.max = ver; - return; - } - continue; - } else |_| { - return error.OSVersionDetectionFail; - } - } else |_| { - return error.OSVersionDetectionFail; - } - } - return error.OSVersionDetectionFail; -} - -fn parseSystemVersion(buf: []const u8) !std.builtin.Version { - var svt = SystemVersionTokenizer{ .bytes = buf }; - try svt.skipUntilTag(.start, "dict"); - while (true) { - try svt.skipUntilTag(.start, "key"); - const content = try svt.expectContent(); - try svt.skipUntilTag(.end, "key"); - if (std.mem.eql(u8, content, "ProductVersion")) break; - } - try svt.skipUntilTag(.start, "string"); - const ver = try svt.expectContent(); - try svt.skipUntilTag(.end, "string"); - - return std.builtin.Version.parse(ver); -} - -const SystemVersionTokenizer = struct { - bytes: []const u8, - index: usize = 0, - state: State = .begin, - - fn next(self: *@This()) !?Token { - var mark: usize = self.index; - var tag = Tag{}; - var content: []const u8 = ""; - - while (self.index < self.bytes.len) { - const char = self.bytes[self.index]; - switch (self.state) { - .begin => switch (char) { - '<' => { - self.state = .tag0; - self.index += 1; - tag = Tag{}; - mark = self.index; - }, - '>' => { - return error.BadToken; - }, - else => { - self.state = .content; - content = ""; - mark = self.index; - }, - }, - .tag0 => switch (char) { - '<' => { - return error.BadToken; - }, - '>' => { - self.state = .begin; - self.index += 1; - tag.name = self.bytes[mark..self.index]; - return Token{ .tag = tag }; - }, - '"' => { - self.state = .tag_string; - self.index += 1; - }, - '/' => { - self.state = .tag0_end_or_empty; - self.index += 1; - }, - 'A'...'Z', 'a'...'z' => { - self.state = .tagN; - tag.kind = .start; - self.index += 1; - }, - else => { - self.state = .tagN; - self.index += 1; - }, - }, - .tag0_end_or_empty => switch (char) { - '<' => { - return error.BadToken; - }, - '>' => { - self.state = .begin; - tag.kind = .empty; - tag.name = self.bytes[self.index..self.index]; - self.index += 1; - return Token{ .tag = tag }; - }, - else => { - self.state = .tagN; - tag.kind = .end; - mark = self.index; - self.index += 1; - }, - }, - .tagN => switch (char) { - '<' => { - return error.BadToken; - }, - '>' => { - self.state = .begin; - tag.name = self.bytes[mark..self.index]; - self.index += 1; - return Token{ .tag = tag }; - }, - '"' => { - self.state = .tag_string; - self.index += 1; - }, - '/' => { - self.state = .tagN_end; - tag.kind = .end; - self.index += 1; - }, - else => { - self.index += 1; - }, - }, - .tagN_end => switch (char) { - '>' => { - self.state = .begin; - tag.name = self.bytes[mark..self.index]; - self.index += 1; - return Token{ .tag = tag }; - }, - else => { - return error.BadToken; - }, - }, - .tag_string => switch (char) { - '"' => { - self.state = .tagN; - self.index += 1; - }, - else => { - self.index += 1; - }, - }, - .content => switch (char) { - '<' => { - self.state = .tag0; - content = self.bytes[mark..self.index]; - self.index += 1; - tag = Tag{}; - mark = self.index; - return Token{ .content = content }; - }, - '>' => { - return error.BadToken; - }, - else => { - self.index += 1; - }, - }, - } - } - - return null; - } - - fn expectContent(self: *@This()) ![]const u8 { - if (try self.next()) |tok| { - switch (tok) { - .content => |content| { - return content; - }, - else => {}, - } - } - return error.UnexpectedToken; - } - - fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void { - while (try self.next()) |tok| { - switch (tok) { - .tag => |tag| { - if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return; - }, - else => {}, - } - } - return error.TagNotFound; - } - - const State = enum { - begin, - tag0, - tag0_end_or_empty, - tagN, - tagN_end, - tag_string, - content, - }; - - const Token = union(enum) { - tag: Tag, - content: []const u8, - }; - - const Tag = struct { - kind: Kind = .unknown, - name: []const u8 = "", - - const Kind = enum { unknown, start, end, empty }; - }; -}; - -test "detect" { - const cases = .{ - .{ - \\ - \\ - \\ - \\ - \\ ProductBuildVersion - \\ 7B85 - \\ ProductCopyright - \\ Apple Computer, Inc. 1983-2003 - \\ ProductName - \\ Mac OS X - \\ ProductUserVisibleVersion - \\ 10.3 - \\ ProductVersion - \\ 10.3 - \\ - \\ - , - .{ .major = 10, .minor = 3 }, - }, - .{ - \\ - \\ - \\ - \\ - \\ ProductBuildVersion - \\ 7W98 - \\ ProductCopyright - \\ Apple Computer, Inc. 1983-2004 - \\ ProductName - \\ Mac OS X - \\ ProductUserVisibleVersion - \\ 10.3.9 - \\ ProductVersion - \\ 10.3.9 - \\ - \\ - , - .{ .major = 10, .minor = 3, .patch = 9 }, - }, - .{ - \\ - \\ - \\ - \\ - \\ ProductBuildVersion - \\ 19G68 - \\ ProductCopyright - \\ 1983-2020 Apple Inc. - \\ ProductName - \\ Mac OS X - \\ ProductUserVisibleVersion - \\ 10.15.6 - \\ ProductVersion - \\ 10.15.6 - \\ iOSSupportVersion - \\ 13.6 - \\ - \\ - , - .{ .major = 10, .minor = 15, .patch = 6 }, - }, - .{ - \\ - \\ - \\ - \\ - \\ ProductBuildVersion - \\ 20A2408 - \\ ProductCopyright - \\ 1983-2020 Apple Inc. - \\ ProductName - \\ macOS - \\ ProductUserVisibleVersion - \\ 11.0 - \\ ProductVersion - \\ 11.0 - \\ iOSSupportVersion - \\ 14.2 - \\ - \\ - , - .{ .major = 11, .minor = 0 }, - }, - .{ - \\ - \\ - \\ - \\ - \\ ProductBuildVersion - \\ 20C63 - \\ ProductCopyright - \\ 1983-2020 Apple Inc. - \\ ProductName - \\ macOS - \\ ProductUserVisibleVersion - \\ 11.1 - \\ ProductVersion - \\ 11.1 - \\ iOSSupportVersion - \\ 14.3 - \\ - \\ - , - .{ .major = 11, .minor = 1 }, - }, - }; - - inline for (cases) |case| { - const ver0 = try parseSystemVersion(case[0]); - const ver1: std.builtin.Version = case[1]; - try testVersionEquality(ver1, ver0); - } -} - -fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void { - var b_expected: [64]u8 = undefined; - const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected}); - - var b_got: [64]u8 = undefined; - const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got}); - - try testing.expectEqualStrings(s_expected, s_got); -} - -/// Detect SDK path on Darwin. -/// Calls `xcrun --show-sdk-path` which result can be used to specify -/// `-syslibroot` param of the linker. -/// The caller needs to free the resulting path slice. -pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { - assert(Target.current.isDarwin()); - const argv = &[_][]const u8{ "xcrun", "--show-sdk-path" }; - const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.stderr.len != 0) { - std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr}); - } - if (result.term.Exited != 0) { - return error.ProcessTerminated; - } - const syslibroot = mem.trimRight(u8, result.stdout, "\r\n"); - return mem.dupe(allocator, u8, syslibroot); -} - -pub fn detectNativeCpuAndFeatures() ?Target.Cpu { - var cpu_family: os.CPUFAMILY = undefined; - var len: usize = @sizeOf(os.CPUFAMILY); - os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) { - error.NameTooLong => unreachable, // constant, known good value - error.PermissionDenied => unreachable, // only when setting values, - error.SystemResources => unreachable, // memory already on the stack - error.UnknownName => unreachable, // constant, known good value - error.Unexpected => unreachable, // EFAULT: stack should be safe, EISDIR/ENOTDIR: constant, known good value - }; - - const current_arch = Target.current.cpu.arch; - switch (current_arch) { - .aarch64, .aarch64_be, .aarch64_32 => { - const model = switch (cpu_family) { - .ARM_FIRESTORM_ICESTORM => &Target.aarch64.cpu.apple_a14, - .ARM_LIGHTNING_THUNDER => &Target.aarch64.cpu.apple_a13, - .ARM_VORTEX_TEMPEST => &Target.aarch64.cpu.apple_a12, - .ARM_MONSOON_MISTRAL => &Target.aarch64.cpu.apple_a11, - .ARM_HURRICANE => &Target.aarch64.cpu.apple_a10, - .ARM_TWISTER => &Target.aarch64.cpu.apple_a9, - .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8, - .ARM_CYCLONE => &Target.aarch64.cpu.cyclone, - else => return null, - }; - - return Target.Cpu{ - .arch = current_arch, - .model = model, - .features = model.features, - }; - }, - else => {}, - } - - return null; -} diff --git a/src/Compilation.zig b/src/Compilation.zig index 24e2d9e67f3c6aa9be3e0edcc9a604fa0467013c..7987ed95df8ab80dacf530099d97f5017a0a7eb7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -944,8 +944,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { if (options.sysroot) |sysroot| { break :blk sysroot; } else if (darwin_can_use_system_sdk) { - const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; - break :blk if (at_least_big_sur) try std.zig.system.getSDKPath(arena) else null; + break :blk try std.zig.system.darwin.getSDKPath(arena, options.target); } else { break :blk null; } diff --git a/src/main.zig b/src/main.zig index 73c4560ddf225c751ea1cc0668234c9efd0b65e1..285b6d23161c6b3a71aaa67a6a095d874c5ac396 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1678,7 +1678,9 @@ fn buildOutputType( want_native_include_dirs = true; } - if (sysroot == null and cross_target.isNativeOs() and + const is_darwin_on_darwin = (comptime std.Target.current.isDarwin()) and cross_target.isDarwin(); + + if (sysroot == null and (cross_target.isNativeOs() or is_darwin_on_darwin) and (system_libs.items.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { @@ -1689,16 +1691,18 @@ fn buildOutputType( } const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: { - const min = target_info.target.os.getVersionRange().semver.min; - const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); - if (at_least_mojave) { - const sdk_path = try std.zig.system.getSDKPath(arena); + const should_get_sdk_path = if (cross_target.isNativeOs() and target_info.target.os.tag == .macos) inner: { + const min = target_info.target.os.getVersionRange().semver.min; + const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); + break :inner at_least_mojave; + } else true; + if (!should_get_sdk_path) break :outer false; + if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| { try clang_argv.ensureCapacity(clang_argv.items.len + 2); clang_argv.appendAssumeCapacity("-isysroot"); clang_argv.appendAssumeCapacity(sdk_path); break :outer true; - } - break :outer false; + } else break :outer false; } else false; try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);