macos: detect SDK path and version, then pass to the linker

Since we are already detecting the path to the native SDK,
if available, also fetch SDK's version and route that to the linker.
The linker can then use it to correctly populate LC_BUILD_VERSION
load command.
This commit is contained in:
Jakub Konka 2021-11-26 16:08:44 +01:00
parent 02d8ca71f9
commit 8317dbd1cb
5 changed files with 88 additions and 35 deletions

View File

@ -2,14 +2,15 @@ const std = @import("std");
const mem = std.mem; const mem = std.mem;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Target = std.Target; const Target = std.Target;
const Version = std.builtin.Version;
pub const macos = @import("darwin/macos.zig"); pub const macos = @import("darwin/macos.zig");
/// Detect SDK path on Darwin. /// Detect SDK on Darwin.
/// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which result can be used to specify /// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK sysroot (if any).
/// `--sysroot` of the compiler. /// Subsequently calls `xcrun --sdk <target_sdk> --show-sdk-version` which fetches version of the SDK.
/// The caller needs to free the resulting path slice. /// The caller needs to deinit the resulting struct.
pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 { pub fn getDarwinSDK(allocator: *Allocator, target: Target) !?DarwinSDK {
const is_simulator_abi = target.abi == .simulator; const is_simulator_abi = target.abi == .simulator;
const sdk = switch (target.os.tag) { const sdk = switch (target.os.tag) {
.macos => "macosx", .macos => "macosx",
@ -18,22 +19,55 @@ pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 {
.tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos",
else => return null, else => return null,
}; };
const path = path: {
const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" };
const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv });
defer { defer {
allocator.free(result.stderr); allocator.free(result.stderr);
allocator.free(result.stdout); allocator.free(result.stdout);
} }
if (result.stderr.len != 0 or result.term.Exited != 0) { if (result.stderr.len != 0 or result.term.Exited != 0) {
// We don't actually care if there were errors as this is best-effort check anyhow // We don't actually care if there were errors as this is best-effort check anyhow
// and in the worst case the user can specify the sysroot manually. // and in the worst case the user can specify the sysroot manually.
return null; return null;
} }
const sysroot = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")); const path = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n"));
return sysroot; break :path path;
};
const version = version: {
const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-version" };
const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv });
defer {
allocator.free(result.stderr);
allocator.free(result.stdout);
}
if (result.stderr.len != 0 or result.term.Exited != 0) {
// We don't actually care if there were errors as this is best-effort check anyhow
// and in the worst case the user can specify the sysroot manually.
return null;
}
const raw_version = mem.trimRight(u8, result.stdout, "\r\n");
const version = Version.parse(raw_version) catch Version{
.major = 0,
.minor = 0,
};
break :version version;
};
return DarwinSDK{
.path = path,
.version = version,
};
} }
pub const DarwinSDK = struct {
path: []const u8,
version: Version,
pub fn deinit(self: DarwinSDK, allocator: *Allocator) void {
allocator.free(self.path);
}
};
test "" { test "" {
_ = @import("darwin/macos.zig"); _ = @import("darwin/macos.zig");
} }

View File

@ -773,8 +773,8 @@ pub const InitOptions = struct {
wasi_exec_model: ?std.builtin.WasiExecModel = null, wasi_exec_model: ?std.builtin.WasiExecModel = null,
/// (Zig compiler development) Enable dumping linker's state as JSON. /// (Zig compiler development) Enable dumping linker's state as JSON.
enable_link_snapshots: bool = false, enable_link_snapshots: bool = false,
/// (Darwin). Path to native macOS SDK if detected. /// (Darwin) Path and version of the native SDK if detected.
native_macos_sdk_path: ?[]const u8 = null, native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
}; };
fn addPackageTableToCacheHash( fn addPackageTableToCacheHash(
@ -967,8 +967,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
const sysroot = blk: { const sysroot = blk: {
if (options.sysroot) |sysroot| { if (options.sysroot) |sysroot| {
break :blk sysroot; break :blk sysroot;
} else if (options.native_macos_sdk_path) |sdk_path| { } else if (options.native_darwin_sdk) |sdk| {
break :blk sdk_path; break :blk sdk.path;
} else { } else {
break :blk null; break :blk null;
} }
@ -1055,7 +1055,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
link_libc, link_libc,
options.system_lib_names.len != 0 or options.frameworks.len != 0, options.system_lib_names.len != 0 or options.frameworks.len != 0,
options.libc_installation, options.libc_installation,
options.native_macos_sdk_path != null, options.native_darwin_sdk != null,
); );
const must_pie = target_util.requiresPIE(options.target); const must_pie = target_util.requiresPIE(options.target);
@ -1492,6 +1492,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.wasi_exec_model = wasi_exec_model, .wasi_exec_model = wasi_exec_model,
.use_stage1 = use_stage1, .use_stage1 = use_stage1,
.enable_link_snapshots = options.enable_link_snapshots, .enable_link_snapshots = options.enable_link_snapshots,
.native_darwin_sdk = options.native_darwin_sdk,
}); });
errdefer bin_file.destroy(); errdefer bin_file.destroy();
comp.* = .{ comp.* = .{
@ -3829,7 +3830,7 @@ fn detectLibCIncludeDirs(
if (link_system_libs and is_native_abi and !target.isMinGW()) { if (link_system_libs and is_native_abi and !target.isMinGW()) {
if (target.isDarwin()) { if (target.isDarwin()) {
return if (has_macos_sdk) return if (has_macos_sdk)
// For Darwin/macOS, we are all set with getSDKPath found earlier. // For Darwin/macOS, we are all set with getDarwinSDK found earlier.
LibCDirs{ LibCDirs{
.libc_include_dir_list = &[0][]u8{}, .libc_include_dir_list = &[0][]u8{},
.libc_installation = null, .libc_installation = null,
@ -3846,7 +3847,14 @@ fn detectLibCIncludeDirs(
// default if possible. // default if possible.
if (target_util.canBuildLibC(target)) { if (target_util.canBuildLibC(target)) {
switch (target.os.tag) { switch (target.os.tag) {
.macos => return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target), .macos => return if (has_macos_sdk)
// For Darwin/macOS, we are all set with getDarwinSDK found earlier.
LibCDirs{
.libc_include_dir_list = &[0][]u8{},
.libc_installation = null,
}
else
getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target),
else => { else => {
const generic_name = target_util.libCGenericName(target); const generic_name = target_util.libCGenericName(target);
// Some architectures are handled by the same set of headers. // Some architectures are handled by the same set of headers.

View File

@ -153,6 +153,9 @@ pub const Options = struct {
/// (Zig compiler development) Enable dumping of linker's state as JSON. /// (Zig compiler development) Enable dumping of linker's state as JSON.
enable_link_snapshots: bool = false, enable_link_snapshots: bool = false,
/// (Darwin) Path and version of the native SDK if detected.
native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
return if (options.use_lld) .Obj else options.output_mode; return if (options.use_lld) .Obj else options.output_mode;
} }

View File

@ -4077,8 +4077,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {
@sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version), @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
@sizeOf(u64), @sizeOf(u64),
)); ));
const ver = self.base.options.target.os.version_range.semver.min; const platform_version = blk: {
const version = ver.major << 16 | ver.minor << 8 | ver.patch; const ver = self.base.options.target.os.version_range.semver.min;
const platform_version = ver.major << 16 | ver.minor << 8;
break :blk platform_version;
};
const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
const ver = sdk.version;
const sdk_version = ver.major << 16 | ver.minor << 8;
break :blk sdk_version;
} else platform_version;
const is_simulator_abi = self.base.options.target.abi == .simulator; const is_simulator_abi = self.base.options.target.abi == .simulator;
var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
.cmd = macho.LC_BUILD_VERSION, .cmd = macho.LC_BUILD_VERSION,
@ -4090,8 +4098,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
.tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS, .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
else => unreachable, else => unreachable,
}, },
.minos = version, .minos = platform_version,
.sdk = version, .sdk = sdk_version,
.ntools = 1, .ntools = 1,
}); });
const ld_ver = macho.build_tool_version{ const ld_ver = macho.build_tool_version{

View File

@ -663,7 +663,7 @@ fn buildOutputType(
var minor_subsystem_version: ?u32 = null; var minor_subsystem_version: ?u32 = null;
var wasi_exec_model: ?std.builtin.WasiExecModel = null; var wasi_exec_model: ?std.builtin.WasiExecModel = null;
var enable_link_snapshots: bool = false; var enable_link_snapshots: bool = false;
var native_macos_sdk_path: ?[]const u8 = null; var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null;
var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa); var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);
defer system_libs.deinit(); defer system_libs.deinit();
@ -1858,11 +1858,11 @@ fn buildOutputType(
} }
const has_sysroot = if (comptime builtin.target.isDarwin()) outer: { const has_sysroot = if (comptime builtin.target.isDarwin()) outer: {
if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| { if (try std.zig.system.darwin.getDarwinSDK(arena, target_info.target)) |sdk| {
native_macos_sdk_path = sdk_path; native_darwin_sdk = sdk;
try clang_argv.ensureUnusedCapacity(2); try clang_argv.ensureUnusedCapacity(2);
clang_argv.appendAssumeCapacity("-isysroot"); clang_argv.appendAssumeCapacity("-isysroot");
clang_argv.appendAssumeCapacity(sdk_path); clang_argv.appendAssumeCapacity(sdk.path);
break :outer true; break :outer true;
} else break :outer false; } else break :outer false;
} else false; } else false;
@ -2342,7 +2342,7 @@ fn buildOutputType(
.wasi_exec_model = wasi_exec_model, .wasi_exec_model = wasi_exec_model,
.debug_compile_errors = debug_compile_errors, .debug_compile_errors = debug_compile_errors,
.enable_link_snapshots = enable_link_snapshots, .enable_link_snapshots = enable_link_snapshots,
.native_macos_sdk_path = native_macos_sdk_path, .native_darwin_sdk = native_darwin_sdk,
}) catch |err| { }) catch |err| {
fatal("unable to create compilation: {s}", .{@errorName(err)}); fatal("unable to create compilation: {s}", .{@errorName(err)});
}; };