commit e9bee08f8869bc97312f37a76cb00b98b8c6ee3d (tree)
parent 2371a63bd46cb1af7e3b9857136ea7677f6abdcc
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Fri, 6 Aug 2021 11:36:40 +0200
Try audodetecting sysroot when building Darwin on Darwin
This is now no longer limited to targeting macOS natively but also
tries to detect the sysroot when targeting different Apple platforms
from macOS; for instance targeting iPhone Simulator from macOS. In
this case, Zig will try detecting the SDK path by invoking
`xcrun --sdk iphonesimulator --show-sdk-path`, and if the command
fails because the SDK doesn't exist (case when having CLT installed only)
or not having either Xcode or CLT installed, we simply return null
signaling that the user has to provide the sysroot themselves.
Diffstat:
6 files changed, 507 insertions(+), 484 deletions(-)
diff --git 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
@@ -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 <target_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
@@ -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 = .{
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>7B85</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>Apple Computer, Inc. 1983-2003</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.3</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.3</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 3 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>7W98</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>Apple Computer, Inc. 1983-2004</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.3.9</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.3.9</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 3, .patch = 9 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>19G68</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.15.6</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.15.6</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>13.6</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 15, .patch = 6 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>20A2408</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>macOS</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>11.0</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>11.0</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>14.2</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 11, .minor = 0 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>20C63</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>macOS</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>11.1</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>11.1</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>14.3</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .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
@@ -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 = .{
- .{
- \\<?xml version="1.0" encoding="UTF-8"?>
- \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- \\<plist version="1.0">
- \\<dict>
- \\ <key>ProductBuildVersion</key>
- \\ <string>7B85</string>
- \\ <key>ProductCopyright</key>
- \\ <string>Apple Computer, Inc. 1983-2003</string>
- \\ <key>ProductName</key>
- \\ <string>Mac OS X</string>
- \\ <key>ProductUserVisibleVersion</key>
- \\ <string>10.3</string>
- \\ <key>ProductVersion</key>
- \\ <string>10.3</string>
- \\</dict>
- \\</plist>
- ,
- .{ .major = 10, .minor = 3 },
- },
- .{
- \\<?xml version="1.0" encoding="UTF-8"?>
- \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- \\<plist version="1.0">
- \\<dict>
- \\ <key>ProductBuildVersion</key>
- \\ <string>7W98</string>
- \\ <key>ProductCopyright</key>
- \\ <string>Apple Computer, Inc. 1983-2004</string>
- \\ <key>ProductName</key>
- \\ <string>Mac OS X</string>
- \\ <key>ProductUserVisibleVersion</key>
- \\ <string>10.3.9</string>
- \\ <key>ProductVersion</key>
- \\ <string>10.3.9</string>
- \\</dict>
- \\</plist>
- ,
- .{ .major = 10, .minor = 3, .patch = 9 },
- },
- .{
- \\<?xml version="1.0" encoding="UTF-8"?>
- \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- \\<plist version="1.0">
- \\<dict>
- \\ <key>ProductBuildVersion</key>
- \\ <string>19G68</string>
- \\ <key>ProductCopyright</key>
- \\ <string>1983-2020 Apple Inc.</string>
- \\ <key>ProductName</key>
- \\ <string>Mac OS X</string>
- \\ <key>ProductUserVisibleVersion</key>
- \\ <string>10.15.6</string>
- \\ <key>ProductVersion</key>
- \\ <string>10.15.6</string>
- \\ <key>iOSSupportVersion</key>
- \\ <string>13.6</string>
- \\</dict>
- \\</plist>
- ,
- .{ .major = 10, .minor = 15, .patch = 6 },
- },
- .{
- \\<?xml version="1.0" encoding="UTF-8"?>
- \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- \\<plist version="1.0">
- \\<dict>
- \\ <key>ProductBuildVersion</key>
- \\ <string>20A2408</string>
- \\ <key>ProductCopyright</key>
- \\ <string>1983-2020 Apple Inc.</string>
- \\ <key>ProductName</key>
- \\ <string>macOS</string>
- \\ <key>ProductUserVisibleVersion</key>
- \\ <string>11.0</string>
- \\ <key>ProductVersion</key>
- \\ <string>11.0</string>
- \\ <key>iOSSupportVersion</key>
- \\ <string>14.2</string>
- \\</dict>
- \\</plist>
- ,
- .{ .major = 11, .minor = 0 },
- },
- .{
- \\<?xml version="1.0" encoding="UTF-8"?>
- \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- \\<plist version="1.0">
- \\<dict>
- \\ <key>ProductBuildVersion</key>
- \\ <string>20C63</string>
- \\ <key>ProductCopyright</key>
- \\ <string>1983-2020 Apple Inc.</string>
- \\ <key>ProductName</key>
- \\ <string>macOS</string>
- \\ <key>ProductUserVisibleVersion</key>
- \\ <string>11.1</string>
- \\ <key>ProductVersion</key>
- \\ <string>11.1</string>
- \\ <key>iOSSupportVersion</key>
- \\ <string>14.3</string>
- \\</dict>
- \\</plist>
- ,
- .{ .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
@@ -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
@@ -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);