authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-06 11:36:40+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-10 13:41:10+02:00
loge9bee08f8869bc97312f37a76cb00b98b8c6ee3d
treeb6d4b2e6153740ccb7f0503d13c7613a5a45f30b
parent2371a63bd46cb1af7e3b9857136ea7677f6abdcc

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.

6 files changed, 507 insertions(+), 484 deletions(-)

lib/std/zig/system.zig+4-6
......@@ -13,12 +13,10 @@ const assert = std.debug.assert;
1313const process = std.process;
1414const Target = std.Target;
1515const CrossTarget = std.zig.CrossTarget;
16const macos = @import("system/macos.zig");
1716const native_endian = std.Target.current.cpu.arch.endian();
1817const linux = @import("system/linux.zig");
1918pub const windows = @import("system/windows.zig");
20
21pub const getSDKPath = macos.getSDKPath;
19pub const darwin = @import("system/darwin.zig");
2220
2321pub const NativePaths = struct {
2422 include_dirs: ArrayList([:0]u8),
......@@ -255,7 +253,7 @@ pub const NativeTargetInfo = struct {
255253 os.version_range.windows.min = detected_version;
256254 os.version_range.windows.max = detected_version;
257255 },
258 .macos => try macos.detect(&os),
256 .macos => try darwin.macos.detect(&os),
259257 .freebsd, .netbsd, .dragonfly => {
260258 const key = switch (Target.current.os.tag) {
261259 .freebsd => "kern.osreldate",
......@@ -972,7 +970,7 @@ pub const NativeTargetInfo = struct {
972970
973971 switch (std.Target.current.os.tag) {
974972 .linux => return linux.detectNativeCpuAndFeatures(),
975 .macos => return macos.detectNativeCpuAndFeatures(),
973 .macos => return darwin.macos.detectNativeCpuAndFeatures(),
976974 else => {},
977975 }
978976
......@@ -983,6 +981,6 @@ pub const NativeTargetInfo = struct {
983981};
984982
985983test {
986 _ = @import("system/macos.zig");
984 _ = @import("system/darwin.zig");
987985 _ = @import("system/linux.zig");
988986}
lib/std/zig/system/darwin.zig created+44
......@@ -0,0 +1,44 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const mem = std.mem;
8const Allocator = mem.Allocator;
9const Target = std.Target;
10
11pub const macos = @import("darwin/macos.zig");
12
13/// Detect SDK path on Darwin.
14/// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which result can be used to specify
15/// `--sysroot` of the compiler.
16/// The caller needs to free the resulting path slice.
17pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 {
18 const is_simulator_abi = target.abi == .simulator;
19 const sdk = switch (target.os.tag) {
20 .macos => "macosx",
21 .ios => if (is_simulator_abi) "iphonesimulator" else "iphoneos",
22 .watchos => if (is_simulator_abi) "watchsimulator" else "watchos",
23 .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos",
24 else => return null,
25 };
26
27 const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" };
28 const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv });
29 defer {
30 allocator.free(result.stderr);
31 allocator.free(result.stdout);
32 }
33 if (result.stderr.len != 0 or result.term.Exited != 0) {
34 // We don't actually care if there were errors as this is best-effort check anyhow
35 // and in the worst case the user can specify the sysroot manually.
36 return null;
37 }
38 const sysroot = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n"));
39 return sysroot;
40}
41
42test "" {
43 _ = @import("darwin/macos.zig");
44}
lib/std/zig/system/darwin/macos.zig created+447
......@@ -0,0 +1,447 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const assert = std.debug.assert;
8const mem = std.mem;
9const testing = std.testing;
10const os = std.os;
11
12const Target = std.Target;
13
14/// Detect macOS version.
15/// `target_os` is not modified in case of error.
16pub fn detect(target_os: *Target.Os) !void {
17 // Drop use of osproductversion sysctl because:
18 // 1. only available 10.13.4 High Sierra and later
19 // 2. when used from a binary built against < SDK 11.0 it returns 10.16 and masks Big Sur 11.x version
20 //
21 // NEW APPROACH, STEP 1, parse file:
22 //
23 // /System/Library/CoreServices/SystemVersion.plist
24 //
25 // NOTE: Historically `SystemVersion.plist` first appeared circa '2003
26 // with the release of Mac OS X 10.3.0 Panther.
27 //
28 // and if it contains a `10.16` value where the `16` is `>= 16` then it is non-canonical,
29 // discarded, and we move on to next step. Otherwise we accept the version.
30 //
31 // BACKGROUND: `10.(16+)` is not a proper version and does not have enough fidelity to
32 // indicate minor/point version of Big Sur and later. It is a context-sensitive result
33 // issued by the kernel for backwards compatibility purposes. Likely the kernel checks
34 // if the executable was linked against an SDK older than Big Sur.
35 //
36 // STEP 2, parse next file:
37 //
38 // /System/Library/CoreServices/.SystemVersionPlatform.plist
39 //
40 // NOTE: Historically `SystemVersionPlatform.plist` first appeared circa '2020
41 // with the release of macOS 11.0 Big Sur.
42 //
43 // Accessing the content via this path circumvents a context-sensitive result and
44 // yields a canonical Big Sur version.
45 //
46 // At this time there is no other known way for a < SDK 11.0 executable to obtain a
47 // canonical Big Sur version.
48 //
49 // This implementation uses a reasonably simplified approach to parse .plist file
50 // that while it is an xml document, we have good history on the file and its format
51 // such that I am comfortable with implementing a minimalistic parser.
52 // Things like string and general escapes are not supported.
53 const prefixSlash = "/System/Library/CoreServices/";
54 const paths = [_][]const u8{
55 prefixSlash ++ "SystemVersion.plist",
56 prefixSlash ++ ".SystemVersionPlatform.plist",
57 };
58 for (paths) |path| {
59 // approx. 4 times historical file size
60 var buf: [2048]u8 = undefined;
61
62 if (std.fs.cwd().readFile(path, &buf)) |bytes| {
63 if (parseSystemVersion(bytes)) |ver| {
64 // never return non-canonical `10.(16+)`
65 if (!(ver.major == 10 and ver.minor >= 16)) {
66 target_os.version_range.semver.min = ver;
67 target_os.version_range.semver.max = ver;
68 return;
69 }
70 continue;
71 } else |_| {
72 return error.OSVersionDetectionFail;
73 }
74 } else |_| {
75 return error.OSVersionDetectionFail;
76 }
77 }
78 return error.OSVersionDetectionFail;
79}
80
81fn parseSystemVersion(buf: []const u8) !std.builtin.Version {
82 var svt = SystemVersionTokenizer{ .bytes = buf };
83 try svt.skipUntilTag(.start, "dict");
84 while (true) {
85 try svt.skipUntilTag(.start, "key");
86 const content = try svt.expectContent();
87 try svt.skipUntilTag(.end, "key");
88 if (std.mem.eql(u8, content, "ProductVersion")) break;
89 }
90 try svt.skipUntilTag(.start, "string");
91 const ver = try svt.expectContent();
92 try svt.skipUntilTag(.end, "string");
93
94 return std.builtin.Version.parse(ver);
95}
96
97const SystemVersionTokenizer = struct {
98 bytes: []const u8,
99 index: usize = 0,
100 state: State = .begin,
101
102 fn next(self: *@This()) !?Token {
103 var mark: usize = self.index;
104 var tag = Tag{};
105 var content: []const u8 = "";
106
107 while (self.index < self.bytes.len) {
108 const char = self.bytes[self.index];
109 switch (self.state) {
110 .begin => switch (char) {
111 '<' => {
112 self.state = .tag0;
113 self.index += 1;
114 tag = Tag{};
115 mark = self.index;
116 },
117 '>' => {
118 return error.BadToken;
119 },
120 else => {
121 self.state = .content;
122 content = "";
123 mark = self.index;
124 },
125 },
126 .tag0 => switch (char) {
127 '<' => {
128 return error.BadToken;
129 },
130 '>' => {
131 self.state = .begin;
132 self.index += 1;
133 tag.name = self.bytes[mark..self.index];
134 return Token{ .tag = tag };
135 },
136 '"' => {
137 self.state = .tag_string;
138 self.index += 1;
139 },
140 '/' => {
141 self.state = .tag0_end_or_empty;
142 self.index += 1;
143 },
144 'A'...'Z', 'a'...'z' => {
145 self.state = .tagN;
146 tag.kind = .start;
147 self.index += 1;
148 },
149 else => {
150 self.state = .tagN;
151 self.index += 1;
152 },
153 },
154 .tag0_end_or_empty => switch (char) {
155 '<' => {
156 return error.BadToken;
157 },
158 '>' => {
159 self.state = .begin;
160 tag.kind = .empty;
161 tag.name = self.bytes[self.index..self.index];
162 self.index += 1;
163 return Token{ .tag = tag };
164 },
165 else => {
166 self.state = .tagN;
167 tag.kind = .end;
168 mark = self.index;
169 self.index += 1;
170 },
171 },
172 .tagN => switch (char) {
173 '<' => {
174 return error.BadToken;
175 },
176 '>' => {
177 self.state = .begin;
178 tag.name = self.bytes[mark..self.index];
179 self.index += 1;
180 return Token{ .tag = tag };
181 },
182 '"' => {
183 self.state = .tag_string;
184 self.index += 1;
185 },
186 '/' => {
187 self.state = .tagN_end;
188 tag.kind = .end;
189 self.index += 1;
190 },
191 else => {
192 self.index += 1;
193 },
194 },
195 .tagN_end => switch (char) {
196 '>' => {
197 self.state = .begin;
198 tag.name = self.bytes[mark..self.index];
199 self.index += 1;
200 return Token{ .tag = tag };
201 },
202 else => {
203 return error.BadToken;
204 },
205 },
206 .tag_string => switch (char) {
207 '"' => {
208 self.state = .tagN;
209 self.index += 1;
210 },
211 else => {
212 self.index += 1;
213 },
214 },
215 .content => switch (char) {
216 '<' => {
217 self.state = .tag0;
218 content = self.bytes[mark..self.index];
219 self.index += 1;
220 tag = Tag{};
221 mark = self.index;
222 return Token{ .content = content };
223 },
224 '>' => {
225 return error.BadToken;
226 },
227 else => {
228 self.index += 1;
229 },
230 },
231 }
232 }
233
234 return null;
235 }
236
237 fn expectContent(self: *@This()) ![]const u8 {
238 if (try self.next()) |tok| {
239 switch (tok) {
240 .content => |content| {
241 return content;
242 },
243 else => {},
244 }
245 }
246 return error.UnexpectedToken;
247 }
248
249 fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void {
250 while (try self.next()) |tok| {
251 switch (tok) {
252 .tag => |tag| {
253 if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return;
254 },
255 else => {},
256 }
257 }
258 return error.TagNotFound;
259 }
260
261 const State = enum {
262 begin,
263 tag0,
264 tag0_end_or_empty,
265 tagN,
266 tagN_end,
267 tag_string,
268 content,
269 };
270
271 const Token = union(enum) {
272 tag: Tag,
273 content: []const u8,
274 };
275
276 const Tag = struct {
277 kind: Kind = .unknown,
278 name: []const u8 = "",
279
280 const Kind = enum { unknown, start, end, empty };
281 };
282};
283
284test "detect" {
285 const cases = .{
286 .{
287 \\<?xml version="1.0" encoding="UTF-8"?>
288 \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
289 \\<plist version="1.0">
290 \\<dict>
291 \\ <key>ProductBuildVersion</key>
292 \\ <string>7B85</string>
293 \\ <key>ProductCopyright</key>
294 \\ <string>Apple Computer, Inc. 1983-2003</string>
295 \\ <key>ProductName</key>
296 \\ <string>Mac OS X</string>
297 \\ <key>ProductUserVisibleVersion</key>
298 \\ <string>10.3</string>
299 \\ <key>ProductVersion</key>
300 \\ <string>10.3</string>
301 \\</dict>
302 \\</plist>
303 ,
304 .{ .major = 10, .minor = 3 },
305 },
306 .{
307 \\<?xml version="1.0" encoding="UTF-8"?>
308 \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
309 \\<plist version="1.0">
310 \\<dict>
311 \\ <key>ProductBuildVersion</key>
312 \\ <string>7W98</string>
313 \\ <key>ProductCopyright</key>
314 \\ <string>Apple Computer, Inc. 1983-2004</string>
315 \\ <key>ProductName</key>
316 \\ <string>Mac OS X</string>
317 \\ <key>ProductUserVisibleVersion</key>
318 \\ <string>10.3.9</string>
319 \\ <key>ProductVersion</key>
320 \\ <string>10.3.9</string>
321 \\</dict>
322 \\</plist>
323 ,
324 .{ .major = 10, .minor = 3, .patch = 9 },
325 },
326 .{
327 \\<?xml version="1.0" encoding="UTF-8"?>
328 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
329 \\<plist version="1.0">
330 \\<dict>
331 \\ <key>ProductBuildVersion</key>
332 \\ <string>19G68</string>
333 \\ <key>ProductCopyright</key>
334 \\ <string>1983-2020 Apple Inc.</string>
335 \\ <key>ProductName</key>
336 \\ <string>Mac OS X</string>
337 \\ <key>ProductUserVisibleVersion</key>
338 \\ <string>10.15.6</string>
339 \\ <key>ProductVersion</key>
340 \\ <string>10.15.6</string>
341 \\ <key>iOSSupportVersion</key>
342 \\ <string>13.6</string>
343 \\</dict>
344 \\</plist>
345 ,
346 .{ .major = 10, .minor = 15, .patch = 6 },
347 },
348 .{
349 \\<?xml version="1.0" encoding="UTF-8"?>
350 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
351 \\<plist version="1.0">
352 \\<dict>
353 \\ <key>ProductBuildVersion</key>
354 \\ <string>20A2408</string>
355 \\ <key>ProductCopyright</key>
356 \\ <string>1983-2020 Apple Inc.</string>
357 \\ <key>ProductName</key>
358 \\ <string>macOS</string>
359 \\ <key>ProductUserVisibleVersion</key>
360 \\ <string>11.0</string>
361 \\ <key>ProductVersion</key>
362 \\ <string>11.0</string>
363 \\ <key>iOSSupportVersion</key>
364 \\ <string>14.2</string>
365 \\</dict>
366 \\</plist>
367 ,
368 .{ .major = 11, .minor = 0 },
369 },
370 .{
371 \\<?xml version="1.0" encoding="UTF-8"?>
372 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
373 \\<plist version="1.0">
374 \\<dict>
375 \\ <key>ProductBuildVersion</key>
376 \\ <string>20C63</string>
377 \\ <key>ProductCopyright</key>
378 \\ <string>1983-2020 Apple Inc.</string>
379 \\ <key>ProductName</key>
380 \\ <string>macOS</string>
381 \\ <key>ProductUserVisibleVersion</key>
382 \\ <string>11.1</string>
383 \\ <key>ProductVersion</key>
384 \\ <string>11.1</string>
385 \\ <key>iOSSupportVersion</key>
386 \\ <string>14.3</string>
387 \\</dict>
388 \\</plist>
389 ,
390 .{ .major = 11, .minor = 1 },
391 },
392 };
393
394 inline for (cases) |case| {
395 const ver0 = try parseSystemVersion(case[0]);
396 const ver1: std.builtin.Version = case[1];
397 try testVersionEquality(ver1, ver0);
398 }
399}
400
401fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void {
402 var b_expected: [64]u8 = undefined;
403 const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected});
404
405 var b_got: [64]u8 = undefined;
406 const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got});
407
408 try testing.expectEqualStrings(s_expected, s_got);
409}
410
411pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
412 var cpu_family: os.CPUFAMILY = undefined;
413 var len: usize = @sizeOf(os.CPUFAMILY);
414 os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
415 error.NameTooLong => unreachable, // constant, known good value
416 error.PermissionDenied => unreachable, // only when setting values,
417 error.SystemResources => unreachable, // memory already on the stack
418 error.UnknownName => unreachable, // constant, known good value
419 error.Unexpected => unreachable, // EFAULT: stack should be safe, EISDIR/ENOTDIR: constant, known good value
420 };
421
422 const current_arch = Target.current.cpu.arch;
423 switch (current_arch) {
424 .aarch64, .aarch64_be, .aarch64_32 => {
425 const model = switch (cpu_family) {
426 .ARM_FIRESTORM_ICESTORM => &Target.aarch64.cpu.apple_a14,
427 .ARM_LIGHTNING_THUNDER => &Target.aarch64.cpu.apple_a13,
428 .ARM_VORTEX_TEMPEST => &Target.aarch64.cpu.apple_a12,
429 .ARM_MONSOON_MISTRAL => &Target.aarch64.cpu.apple_a11,
430 .ARM_HURRICANE => &Target.aarch64.cpu.apple_a10,
431 .ARM_TWISTER => &Target.aarch64.cpu.apple_a9,
432 .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8,
433 .ARM_CYCLONE => &Target.aarch64.cpu.cyclone,
434 else => return null,
435 };
436
437 return Target.Cpu{
438 .arch = current_arch,
439 .model = model,
440 .features = model.features,
441 };
442 },
443 else => {},
444 }
445
446 return null;
447}
lib/std/zig/system/macos.zig deleted-469
......@@ -1,469 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const assert = std.debug.assert;
8const mem = std.mem;
9const testing = std.testing;
10const os = std.os;
11
12const Target = std.Target;
13
14/// Detect macOS version.
15/// `target_os` is not modified in case of error.
16pub fn detect(target_os: *Target.Os) !void {
17 // Drop use of osproductversion sysctl because:
18 // 1. only available 10.13.4 High Sierra and later
19 // 2. when used from a binary built against < SDK 11.0 it returns 10.16 and masks Big Sur 11.x version
20 //
21 // NEW APPROACH, STEP 1, parse file:
22 //
23 // /System/Library/CoreServices/SystemVersion.plist
24 //
25 // NOTE: Historically `SystemVersion.plist` first appeared circa '2003
26 // with the release of Mac OS X 10.3.0 Panther.
27 //
28 // and if it contains a `10.16` value where the `16` is `>= 16` then it is non-canonical,
29 // discarded, and we move on to next step. Otherwise we accept the version.
30 //
31 // BACKGROUND: `10.(16+)` is not a proper version and does not have enough fidelity to
32 // indicate minor/point version of Big Sur and later. It is a context-sensitive result
33 // issued by the kernel for backwards compatibility purposes. Likely the kernel checks
34 // if the executable was linked against an SDK older than Big Sur.
35 //
36 // STEP 2, parse next file:
37 //
38 // /System/Library/CoreServices/.SystemVersionPlatform.plist
39 //
40 // NOTE: Historically `SystemVersionPlatform.plist` first appeared circa '2020
41 // with the release of macOS 11.0 Big Sur.
42 //
43 // Accessing the content via this path circumvents a context-sensitive result and
44 // yields a canonical Big Sur version.
45 //
46 // At this time there is no other known way for a < SDK 11.0 executable to obtain a
47 // canonical Big Sur version.
48 //
49 // This implementation uses a reasonably simplified approach to parse .plist file
50 // that while it is an xml document, we have good history on the file and its format
51 // such that I am comfortable with implementing a minimalistic parser.
52 // Things like string and general escapes are not supported.
53 const prefixSlash = "/System/Library/CoreServices/";
54 const paths = [_][]const u8{
55 prefixSlash ++ "SystemVersion.plist",
56 prefixSlash ++ ".SystemVersionPlatform.plist",
57 };
58 for (paths) |path| {
59 // approx. 4 times historical file size
60 var buf: [2048]u8 = undefined;
61
62 if (std.fs.cwd().readFile(path, &buf)) |bytes| {
63 if (parseSystemVersion(bytes)) |ver| {
64 // never return non-canonical `10.(16+)`
65 if (!(ver.major == 10 and ver.minor >= 16)) {
66 target_os.version_range.semver.min = ver;
67 target_os.version_range.semver.max = ver;
68 return;
69 }
70 continue;
71 } else |_| {
72 return error.OSVersionDetectionFail;
73 }
74 } else |_| {
75 return error.OSVersionDetectionFail;
76 }
77 }
78 return error.OSVersionDetectionFail;
79}
80
81fn parseSystemVersion(buf: []const u8) !std.builtin.Version {
82 var svt = SystemVersionTokenizer{ .bytes = buf };
83 try svt.skipUntilTag(.start, "dict");
84 while (true) {
85 try svt.skipUntilTag(.start, "key");
86 const content = try svt.expectContent();
87 try svt.skipUntilTag(.end, "key");
88 if (std.mem.eql(u8, content, "ProductVersion")) break;
89 }
90 try svt.skipUntilTag(.start, "string");
91 const ver = try svt.expectContent();
92 try svt.skipUntilTag(.end, "string");
93
94 return std.builtin.Version.parse(ver);
95}
96
97const SystemVersionTokenizer = struct {
98 bytes: []const u8,
99 index: usize = 0,
100 state: State = .begin,
101
102 fn next(self: *@This()) !?Token {
103 var mark: usize = self.index;
104 var tag = Tag{};
105 var content: []const u8 = "";
106
107 while (self.index < self.bytes.len) {
108 const char = self.bytes[self.index];
109 switch (self.state) {
110 .begin => switch (char) {
111 '<' => {
112 self.state = .tag0;
113 self.index += 1;
114 tag = Tag{};
115 mark = self.index;
116 },
117 '>' => {
118 return error.BadToken;
119 },
120 else => {
121 self.state = .content;
122 content = "";
123 mark = self.index;
124 },
125 },
126 .tag0 => switch (char) {
127 '<' => {
128 return error.BadToken;
129 },
130 '>' => {
131 self.state = .begin;
132 self.index += 1;
133 tag.name = self.bytes[mark..self.index];
134 return Token{ .tag = tag };
135 },
136 '"' => {
137 self.state = .tag_string;
138 self.index += 1;
139 },
140 '/' => {
141 self.state = .tag0_end_or_empty;
142 self.index += 1;
143 },
144 'A'...'Z', 'a'...'z' => {
145 self.state = .tagN;
146 tag.kind = .start;
147 self.index += 1;
148 },
149 else => {
150 self.state = .tagN;
151 self.index += 1;
152 },
153 },
154 .tag0_end_or_empty => switch (char) {
155 '<' => {
156 return error.BadToken;
157 },
158 '>' => {
159 self.state = .begin;
160 tag.kind = .empty;
161 tag.name = self.bytes[self.index..self.index];
162 self.index += 1;
163 return Token{ .tag = tag };
164 },
165 else => {
166 self.state = .tagN;
167 tag.kind = .end;
168 mark = self.index;
169 self.index += 1;
170 },
171 },
172 .tagN => switch (char) {
173 '<' => {
174 return error.BadToken;
175 },
176 '>' => {
177 self.state = .begin;
178 tag.name = self.bytes[mark..self.index];
179 self.index += 1;
180 return Token{ .tag = tag };
181 },
182 '"' => {
183 self.state = .tag_string;
184 self.index += 1;
185 },
186 '/' => {
187 self.state = .tagN_end;
188 tag.kind = .end;
189 self.index += 1;
190 },
191 else => {
192 self.index += 1;
193 },
194 },
195 .tagN_end => switch (char) {
196 '>' => {
197 self.state = .begin;
198 tag.name = self.bytes[mark..self.index];
199 self.index += 1;
200 return Token{ .tag = tag };
201 },
202 else => {
203 return error.BadToken;
204 },
205 },
206 .tag_string => switch (char) {
207 '"' => {
208 self.state = .tagN;
209 self.index += 1;
210 },
211 else => {
212 self.index += 1;
213 },
214 },
215 .content => switch (char) {
216 '<' => {
217 self.state = .tag0;
218 content = self.bytes[mark..self.index];
219 self.index += 1;
220 tag = Tag{};
221 mark = self.index;
222 return Token{ .content = content };
223 },
224 '>' => {
225 return error.BadToken;
226 },
227 else => {
228 self.index += 1;
229 },
230 },
231 }
232 }
233
234 return null;
235 }
236
237 fn expectContent(self: *@This()) ![]const u8 {
238 if (try self.next()) |tok| {
239 switch (tok) {
240 .content => |content| {
241 return content;
242 },
243 else => {},
244 }
245 }
246 return error.UnexpectedToken;
247 }
248
249 fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void {
250 while (try self.next()) |tok| {
251 switch (tok) {
252 .tag => |tag| {
253 if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return;
254 },
255 else => {},
256 }
257 }
258 return error.TagNotFound;
259 }
260
261 const State = enum {
262 begin,
263 tag0,
264 tag0_end_or_empty,
265 tagN,
266 tagN_end,
267 tag_string,
268 content,
269 };
270
271 const Token = union(enum) {
272 tag: Tag,
273 content: []const u8,
274 };
275
276 const Tag = struct {
277 kind: Kind = .unknown,
278 name: []const u8 = "",
279
280 const Kind = enum { unknown, start, end, empty };
281 };
282};
283
284test "detect" {
285 const cases = .{
286 .{
287 \\<?xml version="1.0" encoding="UTF-8"?>
288 \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
289 \\<plist version="1.0">
290 \\<dict>
291 \\ <key>ProductBuildVersion</key>
292 \\ <string>7B85</string>
293 \\ <key>ProductCopyright</key>
294 \\ <string>Apple Computer, Inc. 1983-2003</string>
295 \\ <key>ProductName</key>
296 \\ <string>Mac OS X</string>
297 \\ <key>ProductUserVisibleVersion</key>
298 \\ <string>10.3</string>
299 \\ <key>ProductVersion</key>
300 \\ <string>10.3</string>
301 \\</dict>
302 \\</plist>
303 ,
304 .{ .major = 10, .minor = 3 },
305 },
306 .{
307 \\<?xml version="1.0" encoding="UTF-8"?>
308 \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
309 \\<plist version="1.0">
310 \\<dict>
311 \\ <key>ProductBuildVersion</key>
312 \\ <string>7W98</string>
313 \\ <key>ProductCopyright</key>
314 \\ <string>Apple Computer, Inc. 1983-2004</string>
315 \\ <key>ProductName</key>
316 \\ <string>Mac OS X</string>
317 \\ <key>ProductUserVisibleVersion</key>
318 \\ <string>10.3.9</string>
319 \\ <key>ProductVersion</key>
320 \\ <string>10.3.9</string>
321 \\</dict>
322 \\</plist>
323 ,
324 .{ .major = 10, .minor = 3, .patch = 9 },
325 },
326 .{
327 \\<?xml version="1.0" encoding="UTF-8"?>
328 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
329 \\<plist version="1.0">
330 \\<dict>
331 \\ <key>ProductBuildVersion</key>
332 \\ <string>19G68</string>
333 \\ <key>ProductCopyright</key>
334 \\ <string>1983-2020 Apple Inc.</string>
335 \\ <key>ProductName</key>
336 \\ <string>Mac OS X</string>
337 \\ <key>ProductUserVisibleVersion</key>
338 \\ <string>10.15.6</string>
339 \\ <key>ProductVersion</key>
340 \\ <string>10.15.6</string>
341 \\ <key>iOSSupportVersion</key>
342 \\ <string>13.6</string>
343 \\</dict>
344 \\</plist>
345 ,
346 .{ .major = 10, .minor = 15, .patch = 6 },
347 },
348 .{
349 \\<?xml version="1.0" encoding="UTF-8"?>
350 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
351 \\<plist version="1.0">
352 \\<dict>
353 \\ <key>ProductBuildVersion</key>
354 \\ <string>20A2408</string>
355 \\ <key>ProductCopyright</key>
356 \\ <string>1983-2020 Apple Inc.</string>
357 \\ <key>ProductName</key>
358 \\ <string>macOS</string>
359 \\ <key>ProductUserVisibleVersion</key>
360 \\ <string>11.0</string>
361 \\ <key>ProductVersion</key>
362 \\ <string>11.0</string>
363 \\ <key>iOSSupportVersion</key>
364 \\ <string>14.2</string>
365 \\</dict>
366 \\</plist>
367 ,
368 .{ .major = 11, .minor = 0 },
369 },
370 .{
371 \\<?xml version="1.0" encoding="UTF-8"?>
372 \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
373 \\<plist version="1.0">
374 \\<dict>
375 \\ <key>ProductBuildVersion</key>
376 \\ <string>20C63</string>
377 \\ <key>ProductCopyright</key>
378 \\ <string>1983-2020 Apple Inc.</string>
379 \\ <key>ProductName</key>
380 \\ <string>macOS</string>
381 \\ <key>ProductUserVisibleVersion</key>
382 \\ <string>11.1</string>
383 \\ <key>ProductVersion</key>
384 \\ <string>11.1</string>
385 \\ <key>iOSSupportVersion</key>
386 \\ <string>14.3</string>
387 \\</dict>
388 \\</plist>
389 ,
390 .{ .major = 11, .minor = 1 },
391 },
392 };
393
394 inline for (cases) |case| {
395 const ver0 = try parseSystemVersion(case[0]);
396 const ver1: std.builtin.Version = case[1];
397 try testVersionEquality(ver1, ver0);
398 }
399}
400
401fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void {
402 var b_expected: [64]u8 = undefined;
403 const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected});
404
405 var b_got: [64]u8 = undefined;
406 const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got});
407
408 try testing.expectEqualStrings(s_expected, s_got);
409}
410
411/// Detect SDK path on Darwin.
412/// Calls `xcrun --show-sdk-path` which result can be used to specify
413/// `-syslibroot` param of the linker.
414/// The caller needs to free the resulting path slice.
415pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 {
416 assert(Target.current.isDarwin());
417 const argv = &[_][]const u8{ "xcrun", "--show-sdk-path" };
418 const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv });
419 defer {
420 allocator.free(result.stderr);
421 allocator.free(result.stdout);
422 }
423 if (result.stderr.len != 0) {
424 std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr});
425 }
426 if (result.term.Exited != 0) {
427 return error.ProcessTerminated;
428 }
429 const syslibroot = mem.trimRight(u8, result.stdout, "\r\n");
430 return mem.dupe(allocator, u8, syslibroot);
431}
432
433pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
434 var cpu_family: os.CPUFAMILY = undefined;
435 var len: usize = @sizeOf(os.CPUFAMILY);
436 os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
437 error.NameTooLong => unreachable, // constant, known good value
438 error.PermissionDenied => unreachable, // only when setting values,
439 error.SystemResources => unreachable, // memory already on the stack
440 error.UnknownName => unreachable, // constant, known good value
441 error.Unexpected => unreachable, // EFAULT: stack should be safe, EISDIR/ENOTDIR: constant, known good value
442 };
443
444 const current_arch = Target.current.cpu.arch;
445 switch (current_arch) {
446 .aarch64, .aarch64_be, .aarch64_32 => {
447 const model = switch (cpu_family) {
448 .ARM_FIRESTORM_ICESTORM => &Target.aarch64.cpu.apple_a14,
449 .ARM_LIGHTNING_THUNDER => &Target.aarch64.cpu.apple_a13,
450 .ARM_VORTEX_TEMPEST => &Target.aarch64.cpu.apple_a12,
451 .ARM_MONSOON_MISTRAL => &Target.aarch64.cpu.apple_a11,
452 .ARM_HURRICANE => &Target.aarch64.cpu.apple_a10,
453 .ARM_TWISTER => &Target.aarch64.cpu.apple_a9,
454 .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8,
455 .ARM_CYCLONE => &Target.aarch64.cpu.cyclone,
456 else => return null,
457 };
458
459 return Target.Cpu{
460 .arch = current_arch,
461 .model = model,
462 .features = model.features,
463 };
464 },
465 else => {},
466 }
467
468 return null;
469}
src/Compilation.zig+1-2
......@@ -944,8 +944,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
944944 if (options.sysroot) |sysroot| {
945945 break :blk sysroot;
946946 } else if (darwin_can_use_system_sdk) {
947 const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11;
948 break :blk if (at_least_big_sur) try std.zig.system.getSDKPath(arena) else null;
947 break :blk try std.zig.system.darwin.getSDKPath(arena, options.target);
949948 } else {
950949 break :blk null;
951950 }
src/main.zig+11-7
......@@ -1678,7 +1678,9 @@ fn buildOutputType(
16781678 want_native_include_dirs = true;
16791679 }
16801680
1681 if (sysroot == null and cross_target.isNativeOs() and
1681 const is_darwin_on_darwin = (comptime std.Target.current.isDarwin()) and cross_target.isDarwin();
1682
1683 if (sysroot == null and (cross_target.isNativeOs() or is_darwin_on_darwin) and
16821684 (system_libs.items.len != 0 or want_native_include_dirs))
16831685 {
16841686 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
......@@ -1689,16 +1691,18 @@ fn buildOutputType(
16891691 }
16901692
16911693 const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: {
1692 const min = target_info.target.os.getVersionRange().semver.min;
1693 const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14);
1694 if (at_least_mojave) {
1695 const sdk_path = try std.zig.system.getSDKPath(arena);
1694 const should_get_sdk_path = if (cross_target.isNativeOs() and target_info.target.os.tag == .macos) inner: {
1695 const min = target_info.target.os.getVersionRange().semver.min;
1696 const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14);
1697 break :inner at_least_mojave;
1698 } else true;
1699 if (!should_get_sdk_path) break :outer false;
1700 if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| {
16961701 try clang_argv.ensureCapacity(clang_argv.items.len + 2);
16971702 clang_argv.appendAssumeCapacity("-isysroot");
16981703 clang_argv.appendAssumeCapacity(sdk_path);
16991704 break :outer true;
1700 }
1701 break :outer false;
1705 } else break :outer false;
17021706 } else false;
17031707
17041708 try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);