authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-10 09:55:40+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:38+01:00
log7588eeccea02b155f934a453cc47d8641686ab22
treef4bce4ad3411da2537391572439823d81208e5ae
parentdd0addab1fe11b019e83c0050eedfa0ec67eb408

macho: re-enable --verbose-link


1 files changed, 345 insertions(+), 99 deletions(-)

src/link/MachO.zig+345-99
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1base: File,1base: File,
2entry_name: ?[]const u8,
32
4/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.3/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
5llvm_object: ?*LlvmObject = null,4llvm_object: ?*LlvmObject = null,
...@@ -47,9 +46,9 @@ atoms: std.ArrayListUnmanaged(Atom) = .{},...@@ -47,9 +46,9 @@ atoms: std.ArrayListUnmanaged(Atom) = .{},
4746
48sdk_layout: ?SdkLayout,47sdk_layout: ?SdkLayout,
49/// Size of the __PAGEZERO segment.48/// Size of the __PAGEZERO segment.
50pagezero_vmsize: u64,49pagezero_vmsize: ?u64,
51/// Minimum space for future expansion of the load commands.50/// Minimum space for future expansion of the load commands.
52headerpad_size: u32,51headerpad_size: ?u32,
53/// Set enough space as if all paths were MATPATHLEN.52/// Set enough space as if all paths were MATPATHLEN.
54headerpad_max_install_names: bool,53headerpad_max_install_names: bool,
55/// Remove dylibs that are unreachable by the entry point or exported symbols.54/// Remove dylibs that are unreachable by the entry point or exported symbols.
...@@ -61,6 +60,8 @@ install_name: ?[]const u8,...@@ -61,6 +60,8 @@ install_name: ?[]const u8,
61/// Path to entitlements file.60/// Path to entitlements file.
62entitlements: ?[]const u8,61entitlements: ?[]const u8,
63compatibility_version: ?std.SemanticVersion,62compatibility_version: ?std.SemanticVersion,
63/// Entry name
64entry_name: ?[]const u8,
6465
65/// Hot-code swapping state.66/// Hot-code swapping state.
66hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},67hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
...@@ -128,8 +129,8 @@ pub fn createEmpty(...@@ -128,8 +129,8 @@ pub fn createEmpty(
128 .build_id = options.build_id,129 .build_id = options.build_id,
129 .rpath_list = options.rpath_list,130 .rpath_list = options.rpath_list,
130 },131 },
131 .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,132 .pagezero_vmsize = options.pagezero_size,
132 .headerpad_size = options.headerpad_size orelse default_headerpad_size,133 .headerpad_size = options.headerpad_size,
133 .headerpad_max_install_names = options.headerpad_max_install_names,134 .headerpad_max_install_names = options.headerpad_max_install_names,
134 .dead_strip_dylibs = options.dead_strip_dylibs,135 .dead_strip_dylibs = options.dead_strip_dylibs,
135 .sdk_layout = options.darwin_sdk_layout,136 .sdk_layout = options.darwin_sdk_layout,
...@@ -249,9 +250,173 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -249,9 +250,173 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
249250
250/// --verbose-link output251/// --verbose-link output
251fn dumpArgv(self: *MachO, comp: *Compilation) !void {252fn dumpArgv(self: *MachO, comp: *Compilation) !void {
252 _ = self;253 const gpa = self.base.comp.gpa;
253 _ = comp;254 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
254 @panic("TODO dumpArgv");255 defer arena_allocator.deinit();
256 const arena = arena_allocator.allocator();
257
258 const target = self.base.comp.root_mod.resolved_target.result;
259 const directory = self.base.emit.directory;
260 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
261 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
262 if (fs.path.dirname(full_out_path)) |dirname| {
263 break :blk try fs.path.join(arena, &.{ dirname, path });
264 } else {
265 break :blk path;
266 }
267 } else null;
268
269 var argv = std.ArrayList([]const u8).init(arena);
270
271 try argv.append("zig");
272
273 if (self.base.isStaticLib()) {
274 try argv.append("ar");
275 } else {
276 try argv.append("ld");
277 }
278
279 if (self.base.isObject()) {
280 try argv.append("-r");
281 }
282
283 try argv.append("-o");
284 try argv.append(full_out_path);
285
286 if (self.base.isRelocatable()) {
287 for (comp.objects) |obj| {
288 try argv.append(obj.path);
289 }
290
291 for (comp.c_object_table.keys()) |key| {
292 try argv.append(key.status.success.object_path);
293 }
294
295 if (module_obj_path) |p| {
296 try argv.append(p);
297 }
298 } else {
299 if (!self.base.isStatic()) {
300 try argv.append("-dynamic");
301 }
302
303 if (self.base.isDynLib()) {
304 try argv.append("-dylib");
305
306 if (self.install_name) |install_name| {
307 try argv.append("-install_name");
308 try argv.append(install_name);
309 }
310 }
311
312 {
313 const platform = Platform.fromTarget(target);
314 try argv.append("-platform_version");
315 try argv.append(@tagName(platform.os_tag));
316 try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
317
318 const sdk_version: ?std.SemanticVersion = self.inferSdkVersion();
319 if (sdk_version) |ver| {
320 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
321 } else {
322 try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
323 }
324 }
325
326 if (comp.sysroot) |syslibroot| {
327 try argv.append("-syslibroot");
328 try argv.append(syslibroot);
329 }
330
331 for (self.base.rpath_list) |rpath| {
332 try argv.append("-rpath");
333 try argv.append(rpath);
334 }
335
336 if (self.pagezero_vmsize) |size| {
337 try argv.append("-pagezero_size");
338 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size}));
339 }
340
341 if (self.headerpad_size) |size| {
342 try argv.append("-headerpad_size");
343 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size}));
344 }
345
346 if (self.headerpad_max_install_names) {
347 try argv.append("-headerpad_max_install_names");
348 }
349
350 if (self.base.gc_sections) {
351 try argv.append("-dead_strip");
352 }
353
354 if (self.dead_strip_dylibs) {
355 try argv.append("-dead_strip_dylibs");
356 }
357
358 if (self.entry_name) |entry_name| {
359 try argv.appendSlice(&.{ "-e", entry_name });
360 }
361
362 for (comp.objects) |obj| {
363 // TODO: verify this
364 if (obj.must_link) {
365 try argv.append("-force_load");
366 }
367 try argv.append(obj.path);
368 }
369
370 for (comp.c_object_table.keys()) |key| {
371 try argv.append(key.status.success.object_path);
372 }
373
374 if (module_obj_path) |p| {
375 try argv.append(p);
376 }
377
378 if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path);
379 if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path);
380
381 if (comp.config.link_libcpp) {
382 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
383 try argv.append(comp.libcxx_static_lib.?.full_object_path);
384 }
385
386 try argv.append("-o");
387 try argv.append(full_out_path);
388
389 try argv.append("-lSystem");
390
391 for (comp.system_libs.keys()) |l_name| {
392 const info = comp.system_libs.get(l_name).?;
393 const arg = if (info.needed)
394 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
395 else if (info.weak)
396 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
397 else
398 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
399 try argv.append(arg);
400 }
401
402 for (self.frameworks) |framework| {
403 const name = std.fs.path.stem(framework.path);
404 const arg = if (framework.needed)
405 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
406 else if (framework.weak)
407 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
408 else
409 try std.fmt.allocPrint(arena, "-framework {s}", .{name});
410 try argv.append(arg);
411 }
412
413 if (self.base.isDynLib() and self.base.allow_shlib_undefined) {
414 try argv.append("-undefined");
415 try argv.append("dynamic_lookup");
416 }
417 }
418
419 Compilation.dump_argv(argv.items);
255}420}
256421
257/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.422/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
...@@ -1034,38 +1199,6 @@ pub const Section = struct {...@@ -1034,38 +1199,6 @@ pub const Section = struct {
1034 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},1199 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
1035};1200};
10361201
1037const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata);
1038
1039const LazySymbolMetadata = struct {
1040 const State = enum { unused, pending_flush, flushed };
1041 text_atom: Atom.Index = undefined,
1042 data_const_atom: Atom.Index = undefined,
1043 text_state: State = .unused,
1044 data_const_state: State = .unused,
1045};
1046
1047const DeclMetadata = struct {
1048 atom: Atom.Index,
1049 section: u8,
1050 /// A list of all exports aliases of this Decl.
1051 /// TODO do we actually need this at all?
1052 exports: std.ArrayListUnmanaged(u32) = .{},
1053
1054 fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
1055 for (m.exports.items) |exp| {
1056 if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp }))) return exp;
1057 }
1058 return null;
1059 }
1060
1061 fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
1062 for (m.exports.items) |*exp| {
1063 if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp.* }))) return exp;
1064 }
1065 return null;
1066 }
1067};
1068
1069const HotUpdateState = struct {1202const HotUpdateState = struct {
1070 mach_task: ?std.os.darwin.MachTask = null,1203 mach_task: ?std.os.darwin.MachTask = null,
1071};1204};
...@@ -1091,18 +1224,32 @@ pub const null_sym = macho.nlist_64{...@@ -1091,18 +1224,32 @@ pub const null_sym = macho.nlist_64{
1091};1224};
10921225
1093pub const Platform = struct {1226pub const Platform = struct {
1094 platform: macho.PLATFORM,1227 os_tag: std.Target.Os.Tag,
1095 version: Version,1228 abi: std.Target.Abi,
1229 version: std.SemanticVersion,
10961230
1097 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to1231 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
1098 /// the extracted minimum platform version.1232 /// the extracted minimum platform version.
1099 pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {1233 pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
1100 switch (lc.cmd()) {1234 switch (lc.cmd()) {
1101 .BUILD_VERSION => {1235 .BUILD_VERSION => {
1102 const lc_cmd = lc.cast(macho.build_version_command).?;1236 const cmd = lc.cast(macho.build_version_command).?;
1103 return .{1237 return .{
1104 .platform = lc_cmd.platform,1238 .os_tag = switch (cmd.platform) {
1105 .version = .{ .value = lc_cmd.minos },1239 .MACOS => .macos,
1240 .IOS, .IOSSIMULATOR => .ios,
1241 .TVOS, .TVOSSIMULATOR => .tvos,
1242 .WATCHOS, .WATCHOSSIMULATOR => .watchos,
1243 else => @panic("TODO"),
1244 },
1245 .abi = switch (cmd.platform) {
1246 .IOSSIMULATOR,
1247 .TVOSSIMULATOR,
1248 .WATCHOSSIMULATOR,
1249 => .simulator,
1250 else => .none,
1251 },
1252 .version = appleVersionToSemanticVersion(cmd.minos),
1106 };1253 };
1107 },1254 },
1108 .VERSION_MIN_MACOSX,1255 .VERSION_MIN_MACOSX,
...@@ -1110,22 +1257,45 @@ pub const Platform = struct {...@@ -1110,22 +1257,45 @@ pub const Platform = struct {
1110 .VERSION_MIN_TVOS,1257 .VERSION_MIN_TVOS,
1111 .VERSION_MIN_WATCHOS,1258 .VERSION_MIN_WATCHOS,
1112 => {1259 => {
1113 const lc_cmd = lc.cast(macho.version_min_command).?;1260 const cmd = lc.cast(macho.version_min_command).?;
1114 return .{1261 return .{
1115 .platform = switch (lc.cmd()) {1262 .os_tag = switch (lc.cmd()) {
1116 .VERSION_MIN_MACOSX => .MACOS,1263 .VERSION_MIN_MACOSX => .macos,
1117 .VERSION_MIN_IPHONEOS => .IOS,1264 .VERSION_MIN_IPHONEOS => .ios,
1118 .VERSION_MIN_TVOS => .TVOS,1265 .VERSION_MIN_TVOS => .tvos,
1119 .VERSION_MIN_WATCHOS => .WATCHOS,1266 .VERSION_MIN_WATCHOS => .watchos,
1120 else => unreachable,1267 else => unreachable,
1121 },1268 },
1122 .version = .{ .value = lc_cmd.version },1269 .abi = .none,
1270 .version = appleVersionToSemanticVersion(cmd.version),
1123 };1271 };
1124 },1272 },
1125 else => unreachable,1273 else => unreachable,
1126 }1274 }
1127 }1275 }
11281276
1277 pub fn fromTarget(target: std.Target) Platform {
1278 return .{
1279 .os_tag = target.os.tag,
1280 .abi = target.abi,
1281 .version = target.os.version_range.semver.min,
1282 };
1283 }
1284
1285 pub fn toAppleVersion(plat: Platform) u32 {
1286 return semanticVersionToAppleVersion(plat.version);
1287 }
1288
1289 pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
1290 return switch (plat.os_tag) {
1291 .macos => .MACOS,
1292 .ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS,
1293 .tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS,
1294 .watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS,
1295 else => unreachable,
1296 };
1297 }
1298
1129 pub fn isBuildVersionCompatible(plat: Platform) bool {1299 pub fn isBuildVersionCompatible(plat: Platform) bool {
1130 inline for (supported_platforms) |sup_plat| {1300 inline for (supported_platforms) |sup_plat| {
1131 if (sup_plat[0] == plat.platform) {1301 if (sup_plat[0] == plat.platform) {
...@@ -1134,76 +1304,152 @@ pub const Platform = struct {...@@ -1134,76 +1304,152 @@ pub const Platform = struct {
1134 }1304 }
1135 return false;1305 return false;
1136 }1306 }
1137};
1138
1139pub const Version = struct {
1140 value: u32,
1141
1142 pub fn major(v: Version) u16 {
1143 return @as(u16, @truncate(v.value >> 16));
1144 }
1145
1146 pub fn minor(v: Version) u8 {
1147 return @as(u8, @truncate(v.value >> 8));
1148 }
11491307
1150 pub fn patch(v: Version) u8 {1308 pub fn isVersionMinCompatible(plat: Platform) bool {
1151 return @as(u8, @truncate(v.value));1309 inline for (supported_platforms) |sup_plat| {
1152 }1310 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
11531311 return sup_plat[3] <= plat.toAppleVersion();
1154 pub fn parse(raw: []const u8) ?Version {1312 }
1155 var parsed: [3]u16 = [_]u16{0} ** 3;
1156 var count: usize = 0;
1157 var it = std.mem.splitAny(u8, raw, ".");
1158 while (it.next()) |comp| {
1159 if (count >= 3) return null;
1160 parsed[count] = std.fmt.parseInt(u16, comp, 10) catch return null;
1161 count += 1;
1162 }1313 }
1163 if (count == 0) return null;1314 return false;
1164 const maj = parsed[0];
1165 const min = std.math.cast(u8, parsed[1]) orelse return null;
1166 const pat = std.math.cast(u8, parsed[2]) orelse return null;
1167 return Version.new(maj, min, pat);
1168 }1315 }
11691316
1170 pub fn new(maj: u16, min: u8, pat: u8) Version {1317 pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) {
1171 return .{ .value = (@as(u32, @intCast(maj)) << 16) | (@as(u32, @intCast(min)) << 8) | pat };1318 return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
1172 }1319 }
11731320
1174 pub fn format(1321 const FmtCtx = struct {
1175 v: Version,1322 platform: Platform,
1323 cpu_arch: std.Target.Cpu.Arch,
1324 };
1325
1326 pub fn formatTarget(
1327 ctx: FmtCtx,
1176 comptime unused_fmt_string: []const u8,1328 comptime unused_fmt_string: []const u8,
1177 options: std.fmt.FormatOptions,1329 options: std.fmt.FormatOptions,
1178 writer: anytype,1330 writer: anytype,
1179 ) !void {1331 ) !void {
1180 _ = unused_fmt_string;1332 _ = unused_fmt_string;
1181 _ = options;1333 _ = options;
1182 try writer.print("{d}.{d}.{d}", .{1334 try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
1183 v.major(),1335 if (ctx.platform.abi != .none) {
1184 v.minor(),1336 try writer.print("-{s}", .{@tagName(ctx.platform.abi)});
1185 v.patch(),1337 }
1186 });1338 }
1339
1340 /// Caller owns the memory.
1341 pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
1342 var buffer = std.ArrayList(u8).init(gpa);
1343 defer buffer.deinit();
1344 try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});
1345 return buffer.toOwnedSlice();
1346 }
1347
1348 pub fn eqlTarget(plat: Platform, other: Platform) bool {
1349 return plat.os_tag == other.os_tag and plat.abi == other.abi;
1187 }1350 }
1188};1351};
11891352
1190const SupportedPlatforms = struct {1353const SupportedPlatforms = struct {
1191 macho.PLATFORM, // Platform identifier1354 std.Target.Os.Tag,
1355 std.Target.Abi,
1192 u32, // Min platform version for which to emit LC_BUILD_VERSION1356 u32, // Min platform version for which to emit LC_BUILD_VERSION
1193 u32, // Min supported platform version1357 u32, // Min supported platform version
1194 ?[]const u8, // Env var to look for
1195};1358};
11961359
1197// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L521360// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
1361// zig fmt: off
1198const supported_platforms = [_]SupportedPlatforms{1362const supported_platforms = [_]SupportedPlatforms{
1199 .{ .MACOS, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" },1363 .{ .macos, .none, 0xA0E00, 0xA0800 },
1200 .{ .IOS, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" },1364 .{ .ios, .none, 0xC0000, 0x70000 },
1201 .{ .TVOS, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" },1365 .{ .tvos, .none, 0xC0000, 0x70000 },
1202 .{ .WATCHOS, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" },1366 .{ .watchos, .none, 0x50000, 0x20000 },
1203 .{ .IOSSIMULATOR, 0xD0000, 0x80000, null },1367 .{ .ios, .simulator, 0xD0000, 0x80000 },
1204 .{ .TVOSSIMULATOR, 0xD0000, 0x80000, null },1368 .{ .tvos, .simulator, 0xD0000, 0x80000 },
1205 .{ .WATCHOSSIMULATOR, 0x60000, 0x20000, null },1369 .{ .watchos, .simulator, 0x60000, 0x20000 },
1206};1370};
1371// zig fmt: on
1372
1373inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
1374 const major = version.major;
1375 const minor = version.minor;
1376 const patch = version.patch;
1377 return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
1378}
1379
1380pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
1381 return .{
1382 .major = @as(u16, @truncate(version >> 16)),
1383 .minor = @as(u8, @truncate(version >> 8)),
1384 .patch = @as(u8, @truncate(version)),
1385 };
1386}
1387
1388fn inferSdkVersion(self: *MachO) ?std.SemanticVersion {
1389 const comp = self.base.comp;
1390 const gpa = comp.gpa;
1391
1392 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1393 defer arena_allocator.deinit();
1394 const arena = arena_allocator.allocator();
1395
1396 const sdk_layout = self.sdk_layout orelse return null;
1397 const sdk_dir = switch (sdk_layout) {
1398 .sdk => comp.sysroot.?,
1399 .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null,
1400 };
1401 if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| {
1402 return parseSdkVersion(ver);
1403 } else |_| {
1404 // Read from settings should always succeed when vendored.
1405 if (sdk_layout == .vendored) @panic("zig installation bug: unable to parse SDK version");
1406 }
1407
1408 // infer from pathname
1409 const stem = std.fs.path.stem(sdk_dir);
1410 const start = for (stem, 0..) |c, i| {
1411 if (std.ascii.isDigit(c)) break i;
1412 } else stem.len;
1413 const end = for (stem[start..], start..) |c, i| {
1414 if (std.ascii.isDigit(c) or c == '.') continue;
1415 break i;
1416 } else stem.len;
1417 return parseSdkVersion(stem[start..end]);
1418}
1419
1420// Official Apple SDKs ship with a `SDKSettings.json` located at the top of SDK fs layout.
1421// Use property `MinimalDisplayName` to determine version.
1422// The file/property is also available with vendored libc.
1423fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {
1424 const sdk_path = try std.fs.path.join(arena, &.{ dir, "SDKSettings.json" });
1425 const contents = try std.fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));
1426 const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
1427 if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
1428 return error.SdkVersionFailure;
1429}
1430
1431// Versions reported by Apple aren't exactly semantically valid as they usually omit
1432// the patch component, so we parse SDK value by hand.
1433fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion {
1434 var parsed: std.SemanticVersion = .{
1435 .major = 0,
1436 .minor = 0,
1437 .patch = 0,
1438 };
1439
1440 const parseNext = struct {
1441 fn parseNext(it: anytype) ?u16 {
1442 const nn = it.next() orelse return null;
1443 return std.fmt.parseInt(u16, nn, 10) catch null;
1444 }
1445 }.parseNext;
1446
1447 var it = std.mem.splitAny(u8, raw, ".");
1448 parsed.major = parseNext(&it) orelse return null;
1449 parsed.minor = parseNext(&it) orelse return null;
1450 parsed.patch = parseNext(&it) orelse 0;
1451 return parsed;
1452}
12071453
1208/// When allocating, the ideal_capacity is calculated by1454/// When allocating, the ideal_capacity is calculated by
1209/// actual_capacity + (actual_capacity / ideal_factor)1455/// actual_capacity + (actual_capacity / ideal_factor)