From 5d6521d2814e6768536103c233d2e283051b9f1e Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Fri, 15 Sep 2023 01:05:14 -0400 Subject: [PATCH] macos: better SDK version detection SDK version detection: - read SDKSettings.json before inferral from SDK path - vendored libc: add SDKSettings.json for SDK version info resolveLibSystem: - adjust search order to { search_dirs, { sysroot or vendored }} - previous search order was { sysroot, search_dirs, vendored } --- lib/libc/darwin/SDKSettings.json | 1 + src/Compilation.zig | 7 +++++++ src/link.zig | 8 ++++++++ src/link/MachO.zig | 24 +++++++++++++----------- src/link/MachO/load_commands.zig | 31 +++++++++++++++++++++++++++++-- src/link/MachO/zld.zig | 10 ++-------- 6 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 lib/libc/darwin/SDKSettings.json diff --git a/lib/libc/darwin/SDKSettings.json b/lib/libc/darwin/SDKSettings.json new file mode 100644 index 0000000000..79599d4fe7 --- /dev/null +++ b/lib/libc/darwin/SDKSettings.json @@ -0,0 +1 @@ +{"MinimalDisplayName":"14.0"} diff --git a/src/Compilation.zig b/src/Compilation.zig index 7cbb64e6c0..e9f6be89fd 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1554,6 +1554,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .use_lld = use_lld, .use_llvm = use_llvm, .use_lib_llvm = use_lib_llvm, + .libc_provider = libc_dirs.provider, .link_libc = link_libc, .link_libcpp = link_libcpp, .link_libunwind = link_libunwind, @@ -5654,6 +5655,7 @@ const LibCDirs = struct { libc_installation: ?*const LibCInstallation, libc_framework_dir_list: []const []const u8, sysroot: ?[]const u8, + provider: link.LibCProvider, }; fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs { @@ -5669,6 +5671,7 @@ fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) .libc_installation = null, .libc_framework_dir_list = &.{}, .sysroot = null, + .provider = .vendored, }; } @@ -5686,6 +5689,7 @@ pub fn detectLibCIncludeDirs( .libc_installation = null, .libc_framework_dir_list = &.{}, .sysroot = null, + .provider = .none, }; } @@ -5743,6 +5747,7 @@ pub fn detectLibCIncludeDirs( .libc_installation = null, .libc_framework_dir_list = &.{}, .sysroot = null, + .provider = .none, }; } @@ -5797,6 +5802,7 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const .libc_installation = lci, .libc_framework_dir_list = framework_list.items, .sysroot = sysroot, + .provider = if (sysroot == null) .installation else .sysroot, }; } @@ -5858,6 +5864,7 @@ fn detectLibCFromBuilding( .libc_installation = null, .libc_framework_dir_list = &.{}, .sysroot = null, + .provider = .vendored, }; } diff --git a/src/link.zig b/src/link.zig index fb71262f66..94b6517f0c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -134,6 +134,7 @@ pub const Options = struct { /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. use_llvm: bool, use_lib_llvm: bool, + libc_provider: LibCProvider, link_libc: bool, link_libcpp: bool, link_libunwind: bool, @@ -282,6 +283,13 @@ pub const HashStyle = enum { sysv, gnu, both }; pub const CompressDebugSections = enum { none, zlib }; +pub const LibCProvider = enum { + none, + installation, + sysroot, + vendored, +}; + pub const File = struct { tag: Tag, options: Options, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 437768b96c..893c181f48 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -558,10 +558,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No }); { const platform = Platform.fromTarget(self.base.options.target); - const sdk_version: ?std.SemanticVersion = if (self.base.options.sysroot) |path| - load_commands.inferSdkVersionFromSdkPath(path) - else - null; + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(comp); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else if (platform.isVersionMinCompatible()) { @@ -647,11 +644,6 @@ pub fn resolveLibSystem( var checked_paths = std.ArrayList([]const u8).init(tmp_arena); success: { - if (self.base.options.sysroot) |root| { - const dir = try fs.path.join(tmp_arena, &[_][]const u8{ root, "usr", "lib" }); - if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; - } - for (search_dirs) |dir| if (try accessLibPath( tmp_arena, &test_path, @@ -660,8 +652,18 @@ pub fn resolveLibSystem( "libSystem", )) break :success; - const dir = try comp.zig_lib_directory.join(tmp_arena, &[_][]const u8{ "libc", "darwin" }); - if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; + switch (self.base.options.libc_provider) { + .none => unreachable, + .installation => unreachable, + .sysroot => { + const dir = try fs.path.join(tmp_arena, &[_][]const u8{ self.base.options.sysroot.?, "usr", "lib" }); + if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; + }, + .vendored => { + const dir = try comp.zig_lib_directory.join(tmp_arena, &[_][]const u8{ "libc", "darwin" }); + if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; + }, + } try self.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); return; diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index cd1e015757..47cd0a54e3 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -467,8 +467,34 @@ pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion { }; } -pub fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion { - const stem = std.fs.path.stem(path); +fn readSdkVersionString(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; +} + +pub fn inferSdkVersion(comp: *const Compilation) ?std.SemanticVersion { + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + const options = comp.bin_file.options; + + const sdk_dir = switch (options.libc_provider) { + .none => unreachable, + .installation => unreachable, + .sysroot => options.sysroot.?, + .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null, + }; + + // prefer meta information if available + if (readSdkVersionString(arena, sdk_dir)) |ver| { + return parseSdkVersion(ver); + } else |_| {} + + // 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; @@ -532,3 +558,4 @@ const mem = std.mem; const Allocator = mem.Allocator; const Dylib = @import("Dylib.zig"); const MachO = @import("../MachO.zig"); +const Compilation = @import("../../Compilation.zig"); diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index d6fc1c9f03..ad9c59cb97 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -241,10 +241,7 @@ pub fn linkWithZld( try argv.append(@tagName(platform.os_tag)); try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version})); - const sdk_version: ?std.SemanticVersion = if (options.sysroot) |path| - load_commands.inferSdkVersionFromSdkPath(path) - else - null; + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(comp); if (sdk_version) |ver| { try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); } else { @@ -591,10 +588,7 @@ pub fn linkWithZld( }); { const platform = Platform.fromTarget(macho_file.base.options.target); - const sdk_version: ?std.SemanticVersion = if (macho_file.base.options.sysroot) |path| - load_commands.inferSdkVersionFromSdkPath(path) - else - null; + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(comp); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else {