mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
macho: re-enable --verbose-link
This commit is contained in:
parent
dd0addab1f
commit
7588eeccea
@ -1,5 +1,4 @@
|
||||
base: File,
|
||||
entry_name: ?[]const u8,
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
|
||||
llvm_object: ?*LlvmObject = null,
|
||||
@ -47,9 +46,9 @@ atoms: std.ArrayListUnmanaged(Atom) = .{},
|
||||
|
||||
sdk_layout: ?SdkLayout,
|
||||
/// Size of the __PAGEZERO segment.
|
||||
pagezero_vmsize: u64,
|
||||
pagezero_vmsize: ?u64,
|
||||
/// Minimum space for future expansion of the load commands.
|
||||
headerpad_size: u32,
|
||||
headerpad_size: ?u32,
|
||||
/// Set enough space as if all paths were MATPATHLEN.
|
||||
headerpad_max_install_names: bool,
|
||||
/// Remove dylibs that are unreachable by the entry point or exported symbols.
|
||||
@ -61,6 +60,8 @@ install_name: ?[]const u8,
|
||||
/// Path to entitlements file.
|
||||
entitlements: ?[]const u8,
|
||||
compatibility_version: ?std.SemanticVersion,
|
||||
/// Entry name
|
||||
entry_name: ?[]const u8,
|
||||
|
||||
/// Hot-code swapping state.
|
||||
hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
|
||||
@ -128,8 +129,8 @@ pub fn createEmpty(
|
||||
.build_id = options.build_id,
|
||||
.rpath_list = options.rpath_list,
|
||||
},
|
||||
.pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,
|
||||
.headerpad_size = options.headerpad_size orelse default_headerpad_size,
|
||||
.pagezero_vmsize = options.pagezero_size,
|
||||
.headerpad_size = options.headerpad_size,
|
||||
.headerpad_max_install_names = options.headerpad_max_install_names,
|
||||
.dead_strip_dylibs = options.dead_strip_dylibs,
|
||||
.sdk_layout = options.darwin_sdk_layout,
|
||||
@ -249,9 +250,173 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
|
||||
|
||||
/// --verbose-link output
|
||||
fn dumpArgv(self: *MachO, comp: *Compilation) !void {
|
||||
_ = self;
|
||||
_ = comp;
|
||||
@panic("TODO dumpArgv");
|
||||
const gpa = self.base.comp.gpa;
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const directory = self.base.emit.directory;
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
|
||||
const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
|
||||
if (fs.path.dirname(full_out_path)) |dirname| {
|
||||
break :blk try fs.path.join(arena, &.{ dirname, path });
|
||||
} else {
|
||||
break :blk path;
|
||||
}
|
||||
} else null;
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try argv.append("zig");
|
||||
|
||||
if (self.base.isStaticLib()) {
|
||||
try argv.append("ar");
|
||||
} else {
|
||||
try argv.append("ld");
|
||||
}
|
||||
|
||||
if (self.base.isObject()) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
if (self.base.isRelocatable()) {
|
||||
for (comp.objects) |obj| {
|
||||
try argv.append(obj.path);
|
||||
}
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try argv.append(key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
} else {
|
||||
if (!self.base.isStatic()) {
|
||||
try argv.append("-dynamic");
|
||||
}
|
||||
|
||||
if (self.base.isDynLib()) {
|
||||
try argv.append("-dylib");
|
||||
|
||||
if (self.install_name) |install_name| {
|
||||
try argv.append("-install_name");
|
||||
try argv.append(install_name);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const platform = Platform.fromTarget(target);
|
||||
try argv.append("-platform_version");
|
||||
try argv.append(@tagName(platform.os_tag));
|
||||
try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
|
||||
|
||||
const sdk_version: ?std.SemanticVersion = self.inferSdkVersion();
|
||||
if (sdk_version) |ver| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
|
||||
} else {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
|
||||
}
|
||||
}
|
||||
|
||||
if (comp.sysroot) |syslibroot| {
|
||||
try argv.append("-syslibroot");
|
||||
try argv.append(syslibroot);
|
||||
}
|
||||
|
||||
for (self.base.rpath_list) |rpath| {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
|
||||
if (self.pagezero_vmsize) |size| {
|
||||
try argv.append("-pagezero_size");
|
||||
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size}));
|
||||
}
|
||||
|
||||
if (self.headerpad_size) |size| {
|
||||
try argv.append("-headerpad_size");
|
||||
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size}));
|
||||
}
|
||||
|
||||
if (self.headerpad_max_install_names) {
|
||||
try argv.append("-headerpad_max_install_names");
|
||||
}
|
||||
|
||||
if (self.base.gc_sections) {
|
||||
try argv.append("-dead_strip");
|
||||
}
|
||||
|
||||
if (self.dead_strip_dylibs) {
|
||||
try argv.append("-dead_strip_dylibs");
|
||||
}
|
||||
|
||||
if (self.entry_name) |entry_name| {
|
||||
try argv.appendSlice(&.{ "-e", entry_name });
|
||||
}
|
||||
|
||||
for (comp.objects) |obj| {
|
||||
// TODO: verify this
|
||||
if (obj.must_link) {
|
||||
try argv.append("-force_load");
|
||||
}
|
||||
try argv.append(obj.path);
|
||||
}
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try argv.append(key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path);
|
||||
if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path);
|
||||
|
||||
if (comp.config.link_libcpp) {
|
||||
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try argv.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
try argv.append("-lSystem");
|
||||
|
||||
for (comp.system_libs.keys()) |l_name| {
|
||||
const info = comp.system_libs.get(l_name).?;
|
||||
const arg = if (info.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
|
||||
else if (info.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
|
||||
try argv.append(arg);
|
||||
}
|
||||
|
||||
for (self.frameworks) |framework| {
|
||||
const name = std.fs.path.stem(framework.path);
|
||||
const arg = if (framework.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
|
||||
else if (framework.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "-framework {s}", .{name});
|
||||
try argv.append(arg);
|
||||
}
|
||||
|
||||
if (self.base.isDynLib() and self.base.allow_shlib_undefined) {
|
||||
try argv.append("-undefined");
|
||||
try argv.append("dynamic_lookup");
|
||||
}
|
||||
}
|
||||
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
|
||||
@ -1034,38 +1199,6 @@ pub const Section = struct {
|
||||
free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
};
|
||||
|
||||
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata);
|
||||
|
||||
const LazySymbolMetadata = struct {
|
||||
const State = enum { unused, pending_flush, flushed };
|
||||
text_atom: Atom.Index = undefined,
|
||||
data_const_atom: Atom.Index = undefined,
|
||||
text_state: State = .unused,
|
||||
data_const_state: State = .unused,
|
||||
};
|
||||
|
||||
const DeclMetadata = struct {
|
||||
atom: Atom.Index,
|
||||
section: u8,
|
||||
/// A list of all exports aliases of this Decl.
|
||||
/// TODO do we actually need this at all?
|
||||
exports: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
|
||||
for (m.exports.items) |exp| {
|
||||
if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp }))) return exp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
|
||||
for (m.exports.items) |*exp| {
|
||||
if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp.* }))) return exp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const HotUpdateState = struct {
|
||||
mach_task: ?std.os.darwin.MachTask = null,
|
||||
};
|
||||
@ -1091,18 +1224,32 @@ pub const null_sym = macho.nlist_64{
|
||||
};
|
||||
|
||||
pub const Platform = struct {
|
||||
platform: macho.PLATFORM,
|
||||
version: Version,
|
||||
os_tag: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
version: std.SemanticVersion,
|
||||
|
||||
/// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
|
||||
/// the extracted minimum platform version.
|
||||
pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
|
||||
switch (lc.cmd()) {
|
||||
.BUILD_VERSION => {
|
||||
const lc_cmd = lc.cast(macho.build_version_command).?;
|
||||
const cmd = lc.cast(macho.build_version_command).?;
|
||||
return .{
|
||||
.platform = lc_cmd.platform,
|
||||
.version = .{ .value = lc_cmd.minos },
|
||||
.os_tag = switch (cmd.platform) {
|
||||
.MACOS => .macos,
|
||||
.IOS, .IOSSIMULATOR => .ios,
|
||||
.TVOS, .TVOSSIMULATOR => .tvos,
|
||||
.WATCHOS, .WATCHOSSIMULATOR => .watchos,
|
||||
else => @panic("TODO"),
|
||||
},
|
||||
.abi = switch (cmd.platform) {
|
||||
.IOSSIMULATOR,
|
||||
.TVOSSIMULATOR,
|
||||
.WATCHOSSIMULATOR,
|
||||
=> .simulator,
|
||||
else => .none,
|
||||
},
|
||||
.version = appleVersionToSemanticVersion(cmd.minos),
|
||||
};
|
||||
},
|
||||
.VERSION_MIN_MACOSX,
|
||||
@ -1110,22 +1257,45 @@ pub const Platform = struct {
|
||||
.VERSION_MIN_TVOS,
|
||||
.VERSION_MIN_WATCHOS,
|
||||
=> {
|
||||
const lc_cmd = lc.cast(macho.version_min_command).?;
|
||||
const cmd = lc.cast(macho.version_min_command).?;
|
||||
return .{
|
||||
.platform = switch (lc.cmd()) {
|
||||
.VERSION_MIN_MACOSX => .MACOS,
|
||||
.VERSION_MIN_IPHONEOS => .IOS,
|
||||
.VERSION_MIN_TVOS => .TVOS,
|
||||
.VERSION_MIN_WATCHOS => .WATCHOS,
|
||||
.os_tag = switch (lc.cmd()) {
|
||||
.VERSION_MIN_MACOSX => .macos,
|
||||
.VERSION_MIN_IPHONEOS => .ios,
|
||||
.VERSION_MIN_TVOS => .tvos,
|
||||
.VERSION_MIN_WATCHOS => .watchos,
|
||||
else => unreachable,
|
||||
},
|
||||
.version = .{ .value = lc_cmd.version },
|
||||
.abi = .none,
|
||||
.version = appleVersionToSemanticVersion(cmd.version),
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromTarget(target: std.Target) Platform {
|
||||
return .{
|
||||
.os_tag = target.os.tag,
|
||||
.abi = target.abi,
|
||||
.version = target.os.version_range.semver.min,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toAppleVersion(plat: Platform) u32 {
|
||||
return semanticVersionToAppleVersion(plat.version);
|
||||
}
|
||||
|
||||
pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
|
||||
return switch (plat.os_tag) {
|
||||
.macos => .MACOS,
|
||||
.ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS,
|
||||
.tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS,
|
||||
.watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isBuildVersionCompatible(plat: Platform) bool {
|
||||
inline for (supported_platforms) |sup_plat| {
|
||||
if (sup_plat[0] == plat.platform) {
|
||||
@ -1134,76 +1304,152 @@ pub const Platform = struct {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Version = struct {
|
||||
value: u32,
|
||||
|
||||
pub fn major(v: Version) u16 {
|
||||
return @as(u16, @truncate(v.value >> 16));
|
||||
}
|
||||
|
||||
pub fn minor(v: Version) u8 {
|
||||
return @as(u8, @truncate(v.value >> 8));
|
||||
}
|
||||
|
||||
pub fn patch(v: Version) u8 {
|
||||
return @as(u8, @truncate(v.value));
|
||||
}
|
||||
|
||||
pub fn parse(raw: []const u8) ?Version {
|
||||
var parsed: [3]u16 = [_]u16{0} ** 3;
|
||||
var count: usize = 0;
|
||||
var it = std.mem.splitAny(u8, raw, ".");
|
||||
while (it.next()) |comp| {
|
||||
if (count >= 3) return null;
|
||||
parsed[count] = std.fmt.parseInt(u16, comp, 10) catch return null;
|
||||
count += 1;
|
||||
pub fn isVersionMinCompatible(plat: Platform) bool {
|
||||
inline for (supported_platforms) |sup_plat| {
|
||||
if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
|
||||
return sup_plat[3] <= plat.toAppleVersion();
|
||||
}
|
||||
}
|
||||
if (count == 0) return null;
|
||||
const maj = parsed[0];
|
||||
const min = std.math.cast(u8, parsed[1]) orelse return null;
|
||||
const pat = std.math.cast(u8, parsed[2]) orelse return null;
|
||||
return Version.new(maj, min, pat);
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn new(maj: u16, min: u8, pat: u8) Version {
|
||||
return .{ .value = (@as(u32, @intCast(maj)) << 16) | (@as(u32, @intCast(min)) << 8) | pat };
|
||||
pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) {
|
||||
return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
v: Version,
|
||||
const FmtCtx = struct {
|
||||
platform: Platform,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
};
|
||||
|
||||
pub fn formatTarget(
|
||||
ctx: FmtCtx,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.print("{d}.{d}.{d}", .{
|
||||
v.major(),
|
||||
v.minor(),
|
||||
v.patch(),
|
||||
});
|
||||
try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
|
||||
if (ctx.platform.abi != .none) {
|
||||
try writer.print("-{s}", .{@tagName(ctx.platform.abi)});
|
||||
}
|
||||
}
|
||||
|
||||
/// Caller owns the memory.
|
||||
pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
|
||||
var buffer = std.ArrayList(u8).init(gpa);
|
||||
defer buffer.deinit();
|
||||
try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});
|
||||
return buffer.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn eqlTarget(plat: Platform, other: Platform) bool {
|
||||
return plat.os_tag == other.os_tag and plat.abi == other.abi;
|
||||
}
|
||||
};
|
||||
|
||||
const SupportedPlatforms = struct {
|
||||
macho.PLATFORM, // Platform identifier
|
||||
std.Target.Os.Tag,
|
||||
std.Target.Abi,
|
||||
u32, // Min platform version for which to emit LC_BUILD_VERSION
|
||||
u32, // Min supported platform version
|
||||
?[]const u8, // Env var to look for
|
||||
};
|
||||
|
||||
// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
|
||||
// zig fmt: off
|
||||
const supported_platforms = [_]SupportedPlatforms{
|
||||
.{ .MACOS, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" },
|
||||
.{ .IOS, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" },
|
||||
.{ .TVOS, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" },
|
||||
.{ .WATCHOS, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" },
|
||||
.{ .IOSSIMULATOR, 0xD0000, 0x80000, null },
|
||||
.{ .TVOSSIMULATOR, 0xD0000, 0x80000, null },
|
||||
.{ .WATCHOSSIMULATOR, 0x60000, 0x20000, null },
|
||||
.{ .macos, .none, 0xA0E00, 0xA0800 },
|
||||
.{ .ios, .none, 0xC0000, 0x70000 },
|
||||
.{ .tvos, .none, 0xC0000, 0x70000 },
|
||||
.{ .watchos, .none, 0x50000, 0x20000 },
|
||||
.{ .ios, .simulator, 0xD0000, 0x80000 },
|
||||
.{ .tvos, .simulator, 0xD0000, 0x80000 },
|
||||
.{ .watchos, .simulator, 0x60000, 0x20000 },
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
|
||||
const major = version.major;
|
||||
const minor = version.minor;
|
||||
const patch = version.patch;
|
||||
return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
|
||||
}
|
||||
|
||||
pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
|
||||
return .{
|
||||
.major = @as(u16, @truncate(version >> 16)),
|
||||
.minor = @as(u8, @truncate(version >> 8)),
|
||||
.patch = @as(u8, @truncate(version)),
|
||||
};
|
||||
}
|
||||
|
||||
fn inferSdkVersion(self: *MachO) ?std.SemanticVersion {
|
||||
const comp = self.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const sdk_layout = self.sdk_layout orelse return null;
|
||||
const sdk_dir = switch (sdk_layout) {
|
||||
.sdk => comp.sysroot.?,
|
||||
.vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null,
|
||||
};
|
||||
if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| {
|
||||
return parseSdkVersion(ver);
|
||||
} else |_| {
|
||||
// Read from settings should always succeed when vendored.
|
||||
if (sdk_layout == .vendored) @panic("zig installation bug: unable to parse SDK version");
|
||||
}
|
||||
|
||||
// infer from pathname
|
||||
const stem = std.fs.path.stem(sdk_dir);
|
||||
const start = for (stem, 0..) |c, i| {
|
||||
if (std.ascii.isDigit(c)) break i;
|
||||
} else stem.len;
|
||||
const end = for (stem[start..], start..) |c, i| {
|
||||
if (std.ascii.isDigit(c) or c == '.') continue;
|
||||
break i;
|
||||
} else stem.len;
|
||||
return parseSdkVersion(stem[start..end]);
|
||||
}
|
||||
|
||||
// Official Apple SDKs ship with a `SDKSettings.json` located at the top of SDK fs layout.
|
||||
// Use property `MinimalDisplayName` to determine version.
|
||||
// The file/property is also available with vendored libc.
|
||||
fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {
|
||||
const sdk_path = try std.fs.path.join(arena, &.{ dir, "SDKSettings.json" });
|
||||
const contents = try std.fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));
|
||||
const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
|
||||
if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
|
||||
return error.SdkVersionFailure;
|
||||
}
|
||||
|
||||
// Versions reported by Apple aren't exactly semantically valid as they usually omit
|
||||
// the patch component, so we parse SDK value by hand.
|
||||
fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion {
|
||||
var parsed: std.SemanticVersion = .{
|
||||
.major = 0,
|
||||
.minor = 0,
|
||||
.patch = 0,
|
||||
};
|
||||
|
||||
const parseNext = struct {
|
||||
fn parseNext(it: anytype) ?u16 {
|
||||
const nn = it.next() orelse return null;
|
||||
return std.fmt.parseInt(u16, nn, 10) catch null;
|
||||
}
|
||||
}.parseNext;
|
||||
|
||||
var it = std.mem.splitAny(u8, raw, ".");
|
||||
parsed.major = parseNext(&it) orelse return null;
|
||||
parsed.minor = parseNext(&it) orelse return null;
|
||||
parsed.patch = parseNext(&it) orelse 0;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/// When allocating, the ideal_capacity is calculated by
|
||||
/// actual_capacity + (actual_capacity / ideal_factor)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user