| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | 1 | base: File, |
| 2 | | entry_name: ?[]const u8, |
| 3 | 2 | |
| 4 | 3 | /// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. |
| 5 | 4 | llvm_object: ?*LlvmObject = null, |
| ... | ... | @@ -47,9 +46,9 @@ atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 47 | 46 | |
| 48 | 47 | sdk_layout: ?SdkLayout, |
| 49 | 48 | /// Size of the __PAGEZERO segment. |
| 50 | | pagezero_vmsize: u64, |
| 49 | pagezero_vmsize: ?u64, |
| 51 | 50 | /// Minimum space for future expansion of the load commands. |
| 52 | | headerpad_size: u32, |
| 51 | headerpad_size: ?u32, |
| 53 | 52 | /// Set enough space as if all paths were MATPATHLEN. |
| 54 | 53 | headerpad_max_install_names: bool, |
| 55 | 54 | /// Remove dylibs that are unreachable by the entry point or exported symbols. |
| ... | ... | @@ -61,6 +60,8 @@ install_name: ?[]const u8, |
| 61 | 60 | /// Path to entitlements file. |
| 62 | 61 | entitlements: ?[]const u8, |
| 63 | 62 | compatibility_version: ?std.SemanticVersion, |
| 63 | /// Entry name |
| 64 | entry_name: ?[]const u8, |
| 64 | 65 | |
| 65 | 66 | /// Hot-code swapping state. |
| 66 | 67 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, |
| ... | ... | @@ -128,8 +129,8 @@ pub fn createEmpty( |
| 128 | 129 | .build_id = options.build_id, |
| 129 | 130 | .rpath_list = options.rpath_list, |
| 130 | 131 | }, |
| 131 | | .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize, |
| 132 | | .headerpad_size = options.headerpad_size orelse default_headerpad_size, |
| 132 | .pagezero_vmsize = options.pagezero_size, |
| 133 | .headerpad_size = options.headerpad_size, |
| 133 | 134 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 134 | 135 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 135 | 136 | .sdk_layout = options.darwin_sdk_layout, |
| ... | ... | @@ -249,9 +250,173 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 249 | 250 | |
| 250 | 251 | /// --verbose-link output |
| 251 | 252 | fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 252 | | _ = self; |
| 253 | | _ = comp; |
| 254 | | @panic("TODO dumpArgv"); |
| 253 | const gpa = self.base.comp.gpa; |
| 254 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 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 | } |
| 256 | 421 | |
| 257 | 422 | /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. |
| ... | ... | @@ -1034,38 +1199,6 @@ pub const Section = struct { |
| 1034 | 1199 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 1035 | 1200 | }; |
| 1036 | 1201 | |
| 1037 | | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); |
| 1038 | | |
| 1039 | | const 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 | | |
| 1047 | | const 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 | | |
| 1069 | 1202 | const HotUpdateState = struct { |
| 1070 | 1203 | mach_task: ?std.os.darwin.MachTask = null, |
| 1071 | 1204 | }; |
| ... | ... | @@ -1091,18 +1224,32 @@ pub const null_sym = macho.nlist_64{ |
| 1091 | 1224 | }; |
| 1092 | 1225 | |
| 1093 | 1226 | pub const Platform = struct { |
| 1094 | | platform: macho.PLATFORM, |
| 1095 | | version: Version, |
| 1227 | os_tag: std.Target.Os.Tag, |
| 1228 | abi: std.Target.Abi, |
| 1229 | version: std.SemanticVersion, |
| 1096 | 1230 | |
| 1097 | 1231 | /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to |
| 1098 | 1232 | /// the extracted minimum platform version. |
| 1099 | 1233 | pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform { |
| 1100 | 1234 | switch (lc.cmd()) { |
| 1101 | 1235 | .BUILD_VERSION => { |
| 1102 | | const lc_cmd = lc.cast(macho.build_version_command).?; |
| 1236 | const cmd = lc.cast(macho.build_version_command).?; |
| 1103 | 1237 | return .{ |
| 1104 | | .platform = lc_cmd.platform, |
| 1105 | | .version = .{ .value = lc_cmd.minos }, |
| 1238 | .os_tag = switch (cmd.platform) { |
| 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 | 1255 | .VERSION_MIN_MACOSX, |
| ... | ... | @@ -1110,22 +1257,45 @@ pub const Platform = struct { |
| 1110 | 1257 | .VERSION_MIN_TVOS, |
| 1111 | 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 | 1261 | return .{ |
| 1115 | | .platform = switch (lc.cmd()) { |
| 1116 | | .VERSION_MIN_MACOSX => .MACOS, |
| 1117 | | .VERSION_MIN_IPHONEOS => .IOS, |
| 1118 | | .VERSION_MIN_TVOS => .TVOS, |
| 1119 | | .VERSION_MIN_WATCHOS => .WATCHOS, |
| 1262 | .os_tag = switch (lc.cmd()) { |
| 1263 | .VERSION_MIN_MACOSX => .macos, |
| 1264 | .VERSION_MIN_IPHONEOS => .ios, |
| 1265 | .VERSION_MIN_TVOS => .tvos, |
| 1266 | .VERSION_MIN_WATCHOS => .watchos, |
| 1120 | 1267 | else => unreachable, |
| 1121 | 1268 | }, |
| 1122 | | .version = .{ .value = lc_cmd.version }, |
| 1269 | .abi = .none, |
| 1270 | .version = appleVersionToSemanticVersion(cmd.version), |
| 1123 | 1271 | }; |
| 1124 | 1272 | }, |
| 1125 | 1273 | else => unreachable, |
| 1126 | 1274 | } |
| 1127 | 1275 | } |
| 1128 | 1276 | |
| 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 | 1299 | pub fn isBuildVersionCompatible(plat: Platform) bool { |
| 1130 | 1300 | inline for (supported_platforms) |sup_plat| { |
| 1131 | 1301 | if (sup_plat[0] == plat.platform) { |
| ... | ... | @@ -1134,76 +1304,152 @@ pub const Platform = struct { |
| 1134 | 1304 | } |
| 1135 | 1305 | return false; |
| 1136 | 1306 | } |
| 1137 | | }; |
| 1138 | | |
| 1139 | | pub 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 | | } |
| 1149 | 1307 | |
| 1150 | | pub fn patch(v: Version) u8 { |
| 1151 | | return @as(u8, @truncate(v.value)); |
| 1152 | | } |
| 1153 | | |
| 1154 | | pub fn parse(raw: []const u8) ?Version { |
| 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; |
| 1308 | pub fn isVersionMinCompatible(plat: Platform) bool { |
| 1309 | inline for (supported_platforms) |sup_plat| { |
| 1310 | if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) { |
| 1311 | return sup_plat[3] <= plat.toAppleVersion(); |
| 1312 | } |
| 1162 | 1313 | } |
| 1163 | | if (count == 0) return null; |
| 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); |
| 1314 | return false; |
| 1168 | 1315 | } |
| 1169 | 1316 | |
| 1170 | | pub fn new(maj: u16, min: u8, pat: u8) Version { |
| 1171 | | return .{ .value = (@as(u32, @intCast(maj)) << 16) | (@as(u32, @intCast(min)) << 8) | pat }; |
| 1317 | pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) { |
| 1318 | return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } }; |
| 1172 | 1319 | } |
| 1173 | 1320 | |
| 1174 | | pub fn format( |
| 1175 | | v: Version, |
| 1321 | const FmtCtx = struct { |
| 1322 | platform: Platform, |
| 1323 | cpu_arch: std.Target.Cpu.Arch, |
| 1324 | }; |
| 1325 | |
| 1326 | pub fn formatTarget( |
| 1327 | ctx: FmtCtx, |
| 1176 | 1328 | comptime unused_fmt_string: []const u8, |
| 1177 | 1329 | options: std.fmt.FormatOptions, |
| 1178 | 1330 | writer: anytype, |
| 1179 | 1331 | ) !void { |
| 1180 | 1332 | _ = unused_fmt_string; |
| 1181 | 1333 | _ = options; |
| 1182 | | try writer.print("{d}.{d}.{d}", .{ |
| 1183 | | v.major(), |
| 1184 | | v.minor(), |
| 1185 | | v.patch(), |
| 1186 | | }); |
| 1334 | try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) }); |
| 1335 | if (ctx.platform.abi != .none) { |
| 1336 | try writer.print("-{s}", .{@tagName(ctx.platform.abi)}); |
| 1337 | } |
| 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 | }; |
| 1189 | 1352 | |
| 1190 | 1353 | const SupportedPlatforms = struct { |
| 1191 | | macho.PLATFORM, // Platform identifier |
| 1354 | std.Target.Os.Tag, |
| 1355 | std.Target.Abi, |
| 1192 | 1356 | u32, // Min platform version for which to emit LC_BUILD_VERSION |
| 1193 | 1357 | u32, // Min supported platform version |
| 1194 | | ?[]const u8, // Env var to look for |
| 1195 | 1358 | }; |
| 1196 | 1359 | |
| 1197 | 1360 | // Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52 |
| 1361 | // zig fmt: off |
| 1198 | 1362 | const supported_platforms = [_]SupportedPlatforms{ |
| 1199 | | .{ .MACOS, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" }, |
| 1200 | | .{ .IOS, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" }, |
| 1201 | | .{ .TVOS, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" }, |
| 1202 | | .{ .WATCHOS, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" }, |
| 1203 | | .{ .IOSSIMULATOR, 0xD0000, 0x80000, null }, |
| 1204 | | .{ .TVOSSIMULATOR, 0xD0000, 0x80000, null }, |
| 1205 | | .{ .WATCHOSSIMULATOR, 0x60000, 0x20000, null }, |
| 1363 | .{ .macos, .none, 0xA0E00, 0xA0800 }, |
| 1364 | .{ .ios, .none, 0xC0000, 0x70000 }, |
| 1365 | .{ .tvos, .none, 0xC0000, 0x70000 }, |
| 1366 | .{ .watchos, .none, 0x50000, 0x20000 }, |
| 1367 | .{ .ios, .simulator, 0xD0000, 0x80000 }, |
| 1368 | .{ .tvos, .simulator, 0xD0000, 0x80000 }, |
| 1369 | .{ .watchos, .simulator, 0x60000, 0x20000 }, |
| 1206 | 1370 | }; |
| 1371 | // zig fmt: on |
| 1372 | |
| 1373 | inline 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 | |
| 1380 | pub 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 | |
| 1388 | fn 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. |
| 1423 | fn 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. |
| 1433 | fn 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 | } |
| 1207 | 1453 | |
| 1208 | 1454 | /// When allocating, the ideal_capacity is calculated by |
| 1209 | 1455 | /// actual_capacity + (actual_capacity / ideal_factor) |