authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-11 19:36:21+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-11 19:36:21+02:00
log60a5552d414ffedf84117df57963fd5bf099c2ea
tree586178dc0f24bf9875e58a830af1e2fdde51af2f
parentf2bf1390a29a9decaa5ca49d3ae720b360583b35
parent509fe33d10e4e89a351678f4d466f30a7870ebcf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9532 from ziglang/basic-ios-support

Add minimal support to Zig toolchain to support building iOS binaries

14 files changed, 724 insertions(+), 596 deletions(-)

lib/std/build.zig+11-1
......@@ -2672,7 +2672,11 @@ pub const LibExeObjStep = struct {
26722672 try zig_args.append(self.builder.pathFromRoot(include_path));
26732673 },
26742674 .raw_path_system => |include_path| {
2675 try zig_args.append("-isystem");
2675 if (builder.sysroot != null) {
2676 try zig_args.append("-iwithsysroot");
2677 } else {
2678 try zig_args.append("-isystem");
2679 }
26762680 try zig_args.append(self.builder.pathFromRoot(include_path));
26772681 },
26782682 .other_step => |other| if (other.emit_h) {
......@@ -2700,6 +2704,12 @@ pub const LibExeObjStep = struct {
27002704
27012705 if (self.target.isDarwin()) {
27022706 for (self.framework_dirs.items) |dir| {
2707 if (builder.sysroot != null) {
2708 try zig_args.append("-iframeworkwithsysroot");
2709 } else {
2710 try zig_args.append("-iframework");
2711 }
2712 try zig_args.append(dir);
27032713 try zig_args.append("-F");
27042714 try zig_args.append(dir);
27052715 }
lib/std/macho.zig+15
......@@ -116,6 +116,21 @@ pub const build_tool_version = extern struct {
116116 version: u32,
117117};
118118
119pub const PLATFORM_MACOS: u32 = 0x1;
120pub const PLATFORM_IOS: u32 = 0x2;
121pub const PLATFORM_TVOS: u32 = 0x3;
122pub const PLATFORM_WATCHOS: u32 = 0x4;
123pub const PLATFORM_BRIDGEOS: u32 = 0x5;
124pub const PLATFORM_MACCATALYST: u32 = 0x6;
125pub const PLATFORM_IOSSIMULATOR: u32 = 0x7;
126pub const PLATFORM_TVOSSIMULATOR: u32 = 0x8;
127pub const PLATFORM_WATCHOSSIMULATOR: u32 = 0x9;
128pub const PLATFORM_DRIVERKIT: u32 = 0x10;
129
130pub const TOOL_CLANG: u32 = 0x1;
131pub const TOOL_SWIFT: u32 = 0x2;
132pub const TOOL_LD: u32 = 0x3;
133
119134/// The entry_point_command is a replacement for thread_command.
120135/// It is used for main executables to specify the location (file offset)
121136/// of main(). If -stack_size was used at link time, the stacksize
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/link/MachO.zig+86-67
......@@ -82,8 +82,8 @@ data_in_code_cmd_index: ?u16 = null,
8282function_starts_cmd_index: ?u16 = null,
8383main_cmd_index: ?u16 = null,
8484dylib_id_cmd_index: ?u16 = null,
85version_min_cmd_index: ?u16 = null,
8685source_version_cmd_index: ?u16 = null,
86build_version_cmd_index: ?u16 = null,
8787uuid_cmd_index: ?u16 = null,
8888code_signature_cmd_index: ?u16 = null,
8989/// Path to libSystem
......@@ -794,15 +794,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
794794 }
795795 }
796796
797 // If we're compiling native and we can find libSystem.B.{dylib, tbd},
798 // we link against that instead of embedded libSystem.B.tbd file.
799 var native_libsystem_available = false;
800 if (self.base.options.is_native_os) blk: {
797 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
798 var libsystem_available = false;
799 if (self.base.options.sysroot != null) blk: {
801800 // Try stub file first. If we hit it, then we're done as the stub file
802801 // re-exports every single symbol definition.
803802 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
804803 try libs.append(full_path);
805 native_libsystem_available = true;
804 libsystem_available = true;
806805 break :blk;
807806 }
808807 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
......@@ -811,12 +810,12 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
811810 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
812811 try libs.append(libsystem_path);
813812 try libs.append(libc_path);
814 native_libsystem_available = true;
813 libsystem_available = true;
815814 break :blk;
816815 }
817816 }
818817 }
819 if (!native_libsystem_available) {
818 if (!libsystem_available) {
820819 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
821820 "libc", "darwin", "libSystem.B.tbd",
822821 });
......@@ -841,7 +840,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
841840 break;
842841 }
843842 } else {
844 log.warn("framework not found for '-f{s}'", .{framework});
843 log.warn("framework not found for '-framework {s}'", .{framework});
845844 framework_not_found = true;
846845 }
847846 }
......@@ -901,10 +900,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
901900 try argv.append("-o");
902901 try argv.append(full_out_path);
903902
904 if (native_libsystem_available) {
905 try argv.append("-lSystem");
906 try argv.append("-lc");
907 }
903 try argv.append("-lSystem");
904 try argv.append("-lc");
908905
909906 for (search_lib_names.items) |l_name| {
910907 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
......@@ -914,6 +911,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
914911 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
915912 }
916913
914 for (self.base.options.frameworks) |framework| {
915 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));
916 }
917
918 for (self.base.options.framework_dirs) |framework_dir| {
919 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
920 }
921
917922 Compilation.dump_argv(argv.items);
918923 }
919924
......@@ -990,7 +995,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
990995}
991996
992997fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void {
993 const arch = self.base.options.target.cpu.arch;
994998 for (files) |file_name| {
995999 const full_path = full_path: {
9961000 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
......@@ -999,17 +1003,17 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
9991003 };
10001004 defer self.base.allocator.free(full_path);
10011005
1002 if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| {
1006 if (try Object.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |object| {
10031007 try self.objects.append(self.base.allocator, object);
10041008 continue;
10051009 }
10061010
1007 if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| {
1011 if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |archive| {
10081012 try self.archives.append(self.base.allocator, archive);
10091013 continue;
10101014 }
10111015
1012 if (try Dylib.createAndParseFromPath(self.base.allocator, arch, full_path, .{
1016 if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path, .{
10131017 .syslibroot = syslibroot,
10141018 })) |dylibs| {
10151019 defer self.base.allocator.free(dylibs);
......@@ -1027,9 +1031,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
10271031}
10281032
10291033fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
1030 const arch = self.base.options.target.cpu.arch;
10311034 for (libs) |lib| {
1032 if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{
1035 if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, lib, .{
10331036 .syslibroot = syslibroot,
10341037 })) |dylibs| {
10351038 defer self.base.allocator.free(dylibs);
......@@ -1042,7 +1045,7 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v
10421045 continue;
10431046 }
10441047
1045 if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| {
1048 if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, lib)) |archive| {
10461049 try self.archives.append(self.base.allocator, archive);
10471050 continue;
10481051 }
......@@ -2231,11 +2234,7 @@ fn resolveSymbols(self: *MachO) !void {
22312234
22322235 const object_id = @intCast(u16, self.objects.items.len);
22332236 const object = try self.objects.addOne(self.base.allocator);
2234 object.* = try archive.parseObject(
2235 self.base.allocator,
2236 self.base.options.target.cpu.arch,
2237 offsets.items[0],
2238 );
2237 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);
22392238 try self.resolveSymbolsInObject(object_id);
22402239
22412240 continue :loop;
......@@ -2717,27 +2716,6 @@ fn populateMetadata(self: *MachO) !void {
27172716 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
27182717 }
27192718
2720 if (self.version_min_cmd_index == null) {
2721 self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
2722 const cmd: u32 = switch (self.base.options.target.os.tag) {
2723 .macos => macho.LC_VERSION_MIN_MACOSX,
2724 .ios => macho.LC_VERSION_MIN_IPHONEOS,
2725 .tvos => macho.LC_VERSION_MIN_TVOS,
2726 .watchos => macho.LC_VERSION_MIN_WATCHOS,
2727 else => unreachable, // wrong OS
2728 };
2729 const ver = self.base.options.target.os.version_range.semver.min;
2730 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
2731 try self.load_commands.append(self.base.allocator, .{
2732 .VersionMin = .{
2733 .cmd = cmd,
2734 .cmdsize = @sizeOf(macho.version_min_command),
2735 .version = version,
2736 .sdk = version,
2737 },
2738 });
2739 }
2740
27412719 if (self.source_version_cmd_index == null) {
27422720 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
27432721 try self.load_commands.append(self.base.allocator, .{
......@@ -2749,6 +2727,40 @@ fn populateMetadata(self: *MachO) !void {
27492727 });
27502728 }
27512729
2730 if (self.build_version_cmd_index == null) {
2731 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
2732 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2733 u64,
2734 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
2735 @sizeOf(u64),
2736 ));
2737 const ver = self.base.options.target.os.version_range.semver.min;
2738 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
2739 const is_simulator_abi = self.base.options.target.abi == .simulator;
2740 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
2741 .cmd = macho.LC_BUILD_VERSION,
2742 .cmdsize = cmdsize,
2743 .platform = switch (self.base.options.target.os.tag) {
2744 .macos => macho.PLATFORM_MACOS,
2745 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,
2746 .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS,
2747 .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
2748 else => unreachable,
2749 },
2750 .minos = version,
2751 .sdk = version,
2752 .ntools = 1,
2753 });
2754 const ld_ver = macho.build_tool_version{
2755 .tool = macho.TOOL_LD,
2756 .version = 0x0,
2757 };
2758 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
2759 mem.set(u8, cmd.data, 0);
2760 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
2761 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
2762 }
2763
27522764 if (self.uuid_cmd_index == null) {
27532765 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
27542766 var uuid_cmd: macho.uuid_command = .{
......@@ -2880,11 +2892,6 @@ fn flushZld(self: *MachO) !void {
28802892 if (self.base.options.target.cpu.arch == .aarch64) {
28812893 try self.writeCodeSignature();
28822894 }
2883
2884 // if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
2885 // const out_path = self.output.?.path;
2886 // try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{});
2887 // }
28882895}
28892896
28902897fn writeGotEntries(self: *MachO) !void {
......@@ -4340,26 +4347,38 @@ pub fn populateMissingMetadata(self: *MachO) !void {
43404347 });
43414348 self.load_commands_dirty = true;
43424349 }
4343 if (self.version_min_cmd_index == null) {
4344 self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
4345 const cmd: u32 = switch (self.base.options.target.os.tag) {
4346 .macos => macho.LC_VERSION_MIN_MACOSX,
4347 .ios => macho.LC_VERSION_MIN_IPHONEOS,
4348 .tvos => macho.LC_VERSION_MIN_TVOS,
4349 .watchos => macho.LC_VERSION_MIN_WATCHOS,
4350 else => unreachable, // wrong OS
4351 };
4350 if (self.build_version_cmd_index == null) {
4351 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4352 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4353 u64,
4354 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
4355 @sizeOf(u64),
4356 ));
43524357 const ver = self.base.options.target.os.version_range.semver.min;
43534358 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
4354 try self.load_commands.append(self.base.allocator, .{
4355 .VersionMin = .{
4356 .cmd = cmd,
4357 .cmdsize = @sizeOf(macho.version_min_command),
4358 .version = version,
4359 .sdk = version,
4359 const is_simulator_abi = self.base.options.target.abi == .simulator;
4360 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
4361 .cmd = macho.LC_BUILD_VERSION,
4362 .cmdsize = cmdsize,
4363 .platform = switch (self.base.options.target.os.tag) {
4364 .macos => macho.PLATFORM_MACOS,
4365 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,
4366 .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS,
4367 .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
4368 else => unreachable,
43604369 },
4370 .minos = version,
4371 .sdk = version,
4372 .ntools = 1,
43614373 });
4362 self.load_commands_dirty = true;
4374 const ld_ver = macho.build_tool_version{
4375 .tool = macho.TOOL_LD,
4376 .version = 0x0,
4377 };
4378 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4379 mem.set(u8, cmd.data, 0);
4380 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4381 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
43634382 }
43644383 if (self.source_version_cmd_index == null) {
43654384 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
src/link/MachO/Archive.zig+6-7
......@@ -9,7 +9,6 @@ const mem = std.mem;
99const fat = @import("fat.zig");
1010
1111const Allocator = mem.Allocator;
12const Arch = std.Target.Cpu.Arch;
1312const Object = @import("Object.zig");
1413
1514file: fs.File,
......@@ -104,7 +103,7 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void {
104103 allocator.free(self.name);
105104}
106105
107pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive {
106pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Archive {
108107 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
109108 error.FileNotFound => return null,
110109 else => |e| return e,
......@@ -119,7 +118,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
119118 .file = file,
120119 };
121120
122 archive.parse(allocator, arch) catch |err| switch (err) {
121 archive.parse(allocator, target) catch |err| switch (err) {
123122 error.EndOfStream, error.NotArchive => {
124123 archive.deinit(allocator);
125124 return null;
......@@ -130,9 +129,9 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
130129 return archive;
131130}
132131
133pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void {
132pub fn parse(self: *Archive, allocator: *Allocator, target: std.Target) !void {
134133 const reader = self.file.reader();
135 self.library_offset = try fat.getLibraryOffset(reader, arch);
134 self.library_offset = try fat.getLibraryOffset(reader, target);
136135 try self.file.seekTo(self.library_offset);
137136
138137 const magic = try reader.readBytesNoEof(SARMAG);
......@@ -215,7 +214,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype)
215214 }
216215}
217216
218pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32) !Object {
217pub fn parseObject(self: Archive, allocator: *Allocator, target: std.Target, offset: u32) !Object {
219218 const reader = self.file.reader();
220219 try reader.context.seekTo(offset + self.library_offset);
221220
......@@ -244,7 +243,7 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32
244243 .mtime = try self.header.?.date(),
245244 };
246245
247 try object.parse(allocator, arch);
246 try object.parse(allocator, target);
248247 try reader.context.seekTo(0);
249248
250249 return object;
src/link/MachO/Dylib.zig+74-23
......@@ -12,7 +12,6 @@ const fat = @import("fat.zig");
1212const commands = @import("commands.zig");
1313
1414const Allocator = mem.Allocator;
15const Arch = std.Target.Cpu.Arch;
1615const LibStub = @import("../tapi.zig").LibStub;
1716const LoadCommand = commands.LoadCommand;
1817const MachO = @import("../MachO.zig");
......@@ -139,11 +138,12 @@ pub const Error = error{
139138pub const CreateOpts = struct {
140139 syslibroot: ?[]const u8 = null,
141140 id: ?Id = null,
141 target: ?std.Target = null,
142142};
143143
144144pub fn createAndParseFromPath(
145145 allocator: *Allocator,
146 arch: Arch,
146 target: std.Target,
147147 path: []const u8,
148148 opts: CreateOpts,
149149) Error!?[]Dylib {
......@@ -161,7 +161,7 @@ pub fn createAndParseFromPath(
161161 .file = file,
162162 };
163163
164 dylib.parse(allocator, arch) catch |err| switch (err) {
164 dylib.parse(allocator, target) catch |err| switch (err) {
165165 error.EndOfStream, error.NotDylib => {
166166 try file.seekTo(0);
167167
......@@ -171,7 +171,7 @@ pub fn createAndParseFromPath(
171171 };
172172 defer lib_stub.deinit();
173173
174 try dylib.parseFromStub(allocator, arch, lib_stub);
174 try dylib.parseFromStub(allocator, target, lib_stub);
175175 },
176176 else => |e| return e,
177177 };
......@@ -195,7 +195,7 @@ pub fn createAndParseFromPath(
195195 try dylibs.append(dylib);
196196 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
197197 // See ld64 manpages.
198 try dylib.parseDependentLibs(allocator, arch, &dylibs, opts.syslibroot);
198 try dylib.parseDependentLibs(allocator, target, &dylibs, opts.syslibroot);
199199
200200 return dylibs.toOwnedSlice();
201201}
......@@ -222,10 +222,10 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {
222222 }
223223}
224224
225pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void {
225pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {
226226 log.debug("parsing shared library '{s}'", .{self.name});
227227
228 self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch);
228 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
229229
230230 try self.file.seekTo(self.library_offset);
231231
......@@ -237,10 +237,10 @@ pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void {
237237 return error.NotDylib;
238238 }
239239
240 const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true);
240 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
241241
242 if (this_arch != arch) {
243 log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch });
242 if (this_arch != target.cpu.arch) {
243 log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch });
244244 return error.MismatchedCpuArchitecture;
245245 }
246246
......@@ -334,7 +334,61 @@ fn addObjCClassSymbols(self: *Dylib, allocator: *Allocator, sym_name: []const u8
334334 }
335335}
336336
337pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: LibStub) !void {
337fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 {
338 const arch = switch (target.cpu.arch) {
339 .aarch64 => "arm64",
340 .x86_64 => "x86_64",
341 else => unreachable,
342 };
343 const os = @tagName(target.os.tag);
344 const abi: ?[]const u8 = switch (target.abi) {
345 .gnu => null,
346 .simulator => "simulator",
347 else => unreachable,
348 };
349 if (abi) |x| {
350 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch, os, x });
351 }
352 return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, os });
353}
354
355const TargetMatcher = struct {
356 allocator: *Allocator,
357 target_strings: std.ArrayListUnmanaged([]const u8) = .{},
358
359 fn init(allocator: *Allocator, target: std.Target) !TargetMatcher {
360 var self = TargetMatcher{ .allocator = allocator };
361 try self.target_strings.append(allocator, try targetToAppleString(allocator, target));
362
363 if (target.abi == .simulator) {
364 // For Apple simulator targets, linking gets tricky as we need to link against the simulator
365 // hosts dylibs too.
366 const host_target = try targetToAppleString(allocator, (std.zig.CrossTarget{
367 .cpu_arch = target.cpu.arch,
368 .os_tag = .macos,
369 }).toTarget());
370 try self.target_strings.append(allocator, host_target);
371 }
372
373 return self;
374 }
375
376 fn deinit(self: *TargetMatcher) void {
377 for (self.target_strings.items) |t| {
378 self.allocator.free(t);
379 }
380 self.target_strings.deinit(self.allocator);
381 }
382
383 fn matches(self: TargetMatcher, targets: []const []const u8) bool {
384 for (self.target_strings.items) |t| {
385 if (hasTarget(targets, t)) return true;
386 }
387 return false;
388 }
389};
390
391pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void {
338392 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
339393
340394 log.debug("parsing shared library from stub '{s}'", .{self.name});
......@@ -350,17 +404,14 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub:
350404 }
351405 self.id = id;
352406
353 const target_string: []const u8 = switch (arch) {
354 .aarch64 => "arm64-macos",
355 .x86_64 => "x86_64-macos",
356 else => unreachable,
357 };
407 var matcher = try TargetMatcher.init(allocator, target);
408 defer matcher.deinit();
358409
359410 var umbrella_libs = std.StringHashMap(void).init(allocator);
360411 defer umbrella_libs.deinit();
361412
362413 for (lib_stub.inner) |stub, stub_index| {
363 if (!hasTarget(stub.targets, target_string)) continue;
414 if (!matcher.matches(stub.targets)) continue;
364415
365416 if (stub_index > 0) {
366417 // TODO I thought that we could switch on presence of `parent-umbrella` map;
......@@ -371,7 +422,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub:
371422
372423 if (stub.exports) |exports| {
373424 for (exports) |exp| {
374 if (!hasTarget(exp.targets, target_string)) continue;
425 if (!matcher.matches(exp.targets)) continue;
375426
376427 if (exp.symbols) |symbols| {
377428 for (symbols) |sym_name| {
......@@ -390,7 +441,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub:
390441
391442 if (stub.reexports) |reexports| {
392443 for (reexports) |reexp| {
393 if (!hasTarget(reexp.targets, target_string)) continue;
444 if (!matcher.matches(reexp.targets)) continue;
394445
395446 if (reexp.symbols) |symbols| {
396447 for (symbols) |sym_name| {
......@@ -418,11 +469,11 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub:
418469
419470 // TODO track which libs were already parsed in different steps
420471 for (lib_stub.inner) |stub| {
421 if (!hasTarget(stub.targets, target_string)) continue;
472 if (!matcher.matches(stub.targets)) continue;
422473
423474 if (stub.reexported_libraries) |reexports| {
424475 for (reexports) |reexp| {
425 if (!hasTarget(reexp.targets, target_string)) continue;
476 if (!matcher.matches(reexp.targets)) continue;
426477
427478 for (reexp.libraries) |lib| {
428479 if (umbrella_libs.contains(lib)) {
......@@ -443,7 +494,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub:
443494pub fn parseDependentLibs(
444495 self: *Dylib,
445496 allocator: *Allocator,
446 arch: Arch,
497 target: std.Target,
447498 out: *std.ArrayList(Dylib),
448499 syslibroot: ?[]const u8,
449500) !void {
......@@ -475,7 +526,7 @@ pub fn parseDependentLibs(
475526
476527 const dylibs = (try createAndParseFromPath(
477528 allocator,
478 arch,
529 target,
479530 full_path,
480531 .{
481532 .id = id,
src/link/MachO/Object.zig+6-7
......@@ -15,7 +15,6 @@ const segmentName = commands.segmentName;
1515const sectionName = commands.sectionName;
1616
1717const Allocator = mem.Allocator;
18const Arch = std.Target.Cpu.Arch;
1918const LoadCommand = commands.LoadCommand;
2019const MachO = @import("../MachO.zig");
2120const TextBlock = @import("TextBlock.zig");
......@@ -154,7 +153,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
154153 }
155154}
156155
157pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object {
156pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Object {
158157 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
159158 error.FileNotFound => return null,
160159 else => |e| return e,
......@@ -169,7 +168,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
169168 .file = file,
170169 };
171170
172 object.parse(allocator, arch) catch |err| switch (err) {
171 object.parse(allocator, target) catch |err| switch (err) {
173172 error.EndOfStream, error.NotObject => {
174173 object.deinit(allocator);
175174 return null;
......@@ -180,7 +179,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
180179 return object;
181180}
182181
183pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
182pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {
184183 const reader = self.file.reader();
185184 if (self.file_offset) |offset| {
186185 try reader.context.seekTo(offset);
......@@ -195,7 +194,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
195194 return error.NotObject;
196195 }
197196
198 const this_arch: Arch = switch (header.cputype) {
197 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
199198 macho.CPU_TYPE_ARM64 => .aarch64,
200199 macho.CPU_TYPE_X86_64 => .x86_64,
201200 else => |value| {
......@@ -203,8 +202,8 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
203202 return error.UnsupportedCpuArchitecture;
204203 },
205204 };
206 if (this_arch != arch) {
207 log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch });
205 if (this_arch != target.cpu.arch) {
206 log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch });
208207 return error.MismatchedCpuArchitecture;
209208 }
210209
src/link/MachO/commands.zig+9
......@@ -43,6 +43,7 @@ pub const LoadCommand = union(enum) {
4343 Main: macho.entry_point_command,
4444 VersionMin: macho.version_min_command,
4545 SourceVersion: macho.source_version_command,
46 BuildVersion: GenericCommandWithData(macho.build_version_command),
4647 Uuid: macho.uuid_command,
4748 LinkeditData: macho.linkedit_data_command,
4849 Rpath: GenericCommandWithData(macho.rpath_command),
......@@ -97,6 +98,9 @@ pub const LoadCommand = union(enum) {
9798 macho.LC_SOURCE_VERSION => LoadCommand{
9899 .SourceVersion = try stream.reader().readStruct(macho.source_version_command),
99100 },
101 macho.LC_BUILD_VERSION => LoadCommand{
102 .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()),
103 },
100104 macho.LC_UUID => LoadCommand{
101105 .Uuid = try stream.reader().readStruct(macho.uuid_command),
102106 },
......@@ -129,6 +133,7 @@ pub const LoadCommand = union(enum) {
129133 .Dylinker => |x| x.write(writer),
130134 .Dylib => |x| x.write(writer),
131135 .Rpath => |x| x.write(writer),
136 .BuildVersion => |x| x.write(writer),
132137 .Unknown => |x| x.write(writer),
133138 };
134139 }
......@@ -147,6 +152,7 @@ pub const LoadCommand = union(enum) {
147152 .Dylinker => |x| x.inner.cmd,
148153 .Dylib => |x| x.inner.cmd,
149154 .Rpath => |x| x.inner.cmd,
155 .BuildVersion => |x| x.inner.cmd,
150156 .Unknown => |x| x.inner.cmd,
151157 };
152158 }
......@@ -165,6 +171,7 @@ pub const LoadCommand = union(enum) {
165171 .Dylinker => |x| x.inner.cmdsize,
166172 .Dylib => |x| x.inner.cmdsize,
167173 .Rpath => |x| x.inner.cmdsize,
174 .BuildVersion => |x| x.inner.cmdsize,
168175 .Unknown => |x| x.inner.cmdsize,
169176 };
170177 }
......@@ -175,6 +182,7 @@ pub const LoadCommand = union(enum) {
175182 .Dylinker => |*x| x.deinit(allocator),
176183 .Dylib => |*x| x.deinit(allocator),
177184 .Rpath => |*x| x.deinit(allocator),
185 .BuildVersion => |*x| x.deinit(allocator),
178186 .Unknown => |*x| x.deinit(allocator),
179187 else => {},
180188 };
......@@ -193,6 +201,7 @@ pub const LoadCommand = union(enum) {
193201 .Main => |x| meta.eql(x, other.Main),
194202 .VersionMin => |x| meta.eql(x, other.VersionMin),
195203 .SourceVersion => |x| meta.eql(x, other.SourceVersion),
204 .BuildVersion => |x| x.eql(other.BuildVersion),
196205 .Uuid => |x| meta.eql(x, other.Uuid),
197206 .LinkeditData => |x| meta.eql(x, other.LinkeditData),
198207 .Segment => |x| x.eql(other.Segment),
src/link/MachO/fat.zig+4-6
......@@ -5,10 +5,8 @@ const macho = std.macho;
55const mem = std.mem;
66const native_endian = builtin.target.cpu.arch.endian();
77
8const Arch = std.Target.Cpu.Arch;
9
108pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch {
11 const arch: Arch = switch (cputype) {
9 const arch: std.Target.Cpu.Arch = switch (cputype) {
1210 macho.CPU_TYPE_ARM64 => .aarch64,
1311 macho.CPU_TYPE_X86_64 => .x86_64,
1412 else => {
......@@ -31,7 +29,7 @@ fn readFatStruct(reader: anytype, comptime T: type) !T {
3129 return res;
3230}
3331
34pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 {
32pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 {
3533 const fat_header = try readFatStruct(reader, macho.fat_header);
3634 if (fat_header.magic != macho.FAT_MAGIC) return 0;
3735
......@@ -44,12 +42,12 @@ pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 {
4442 error.UnsupportedCpuArchitecture => continue,
4543 else => |e| return e,
4644 };
47 if (lib_arch == arch) {
45 if (lib_arch == target.cpu.arch) {
4846 // We have found a matching architecture!
4947 return fat_arch.offset;
5048 }
5149 } else {
52 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch});
50 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{target.cpu.arch});
5351 return error.MismatchedCpuArchitecture;
5452 }
5553}
src/main.zig+17-8
......@@ -830,7 +830,10 @@ fn buildOutputType(
830830 } else if (mem.eql(u8, arg, "-D") or
831831 mem.eql(u8, arg, "-isystem") or
832832 mem.eql(u8, arg, "-I") or
833 mem.eql(u8, arg, "-dirafter"))
833 mem.eql(u8, arg, "-dirafter") or
834 mem.eql(u8, arg, "-iwithsysroot") or
835 mem.eql(u8, arg, "-iframework") or
836 mem.eql(u8, arg, "-iframeworkwithsysroot"))
834837 {
835838 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
836839 i += 1;
......@@ -873,6 +876,8 @@ fn buildOutputType(
873876 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
874877 i += 1;
875878 sysroot = args[i];
879 try clang_argv.append("-isysroot");
880 try clang_argv.append(args[i]);
876881 } else if (mem.eql(u8, arg, "--libc")) {
877882 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
878883 i += 1;
......@@ -1673,7 +1678,9 @@ fn buildOutputType(
16731678 want_native_include_dirs = true;
16741679 }
16751680
1676 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
16771684 (system_libs.items.len != 0 or want_native_include_dirs))
16781685 {
16791686 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
......@@ -1684,16 +1691,18 @@ fn buildOutputType(
16841691 }
16851692
16861693 const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: {
1687 const min = target_info.target.os.getVersionRange().semver.min;
1688 const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14);
1689 if (at_least_mojave) {
1690 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| {
16911701 try clang_argv.ensureCapacity(clang_argv.items.len + 2);
16921702 clang_argv.appendAssumeCapacity("-isysroot");
16931703 clang_argv.appendAssumeCapacity(sdk_path);
16941704 break :outer true;
1695 }
1696 break :outer false;
1705 } else break :outer false;
16971706 } else false;
16981707
16991708 try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);