diff --git a/lib/std/build.zig b/lib/std/build.zig index a1ac3f88f2..98f6730be0 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1207,6 +1207,7 @@ pub const LibExeObjStep = struct { name_only_filename: []const u8, strip: bool, lib_paths: ArrayList([]const u8), + syslibroot: ?[]const u8 = null, framework_dirs: ArrayList([]const u8), frameworks: BufSet, verbose_link: bool, @@ -1841,6 +1842,10 @@ pub const LibExeObjStep = struct { self.lib_paths.append(self.builder.dupe(path)) catch unreachable; } + pub fn addSyslibroot(self: *LibExeObjStep, path: []const u8) void { + self.syslibroot = path; + } + pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void { self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; } @@ -1915,11 +1920,18 @@ pub const LibExeObjStep = struct { } } - // Inherit dependencies on darwin frameworks - if (self.target.isDarwin() and !other.isDynamicLibrary()) { - var it = other.frameworks.iterator(); - while (it.next()) |entry| { - self.frameworks.put(entry.key) catch unreachable; + if (self.target.isDarwin()) { + // Inherit syslibroot + if (other.syslibroot) |path| { + self.syslibroot = path; + } + + // Inherit dependencies on darwin frameworks + if (!other.isDynamicLibrary()) { + var it = other.frameworks.iterator(); + while (it.next()) |entry| { + self.frameworks.put(entry.key) catch unreachable; + } } } } @@ -2271,6 +2283,18 @@ pub const LibExeObjStep = struct { } if (self.target.isDarwin()) { + if (self.syslibroot) |path| { + try zig_args.append("-syslibroot"); + try zig_args.append(path); + } else { + if (self.target.isNative()) { + const syslibroot = try std.zig.system.getSDKPath(builder.allocator); + errdefer builder.allocator.free(syslibroot); + try zig_args.append("-syslibroot"); + try zig_args.append(syslibroot); + } + } + for (self.framework_dirs.span()) |dir| { try zig_args.append("-F"); try zig_args.append(dir); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 33ef137eb6..b969fe8d96 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -17,6 +17,8 @@ const macos = @import("system/macos.zig"); const is_windows = Target.current.os.tag == .windows; +pub const getSDKPath = macos.getSDKPath; + pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), lib_dirs: ArrayList([:0]u8), diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/macos.zig index e0e69b6b8b..4347e1404a 100644 --- a/lib/std/zig/system/macos.zig +++ b/lib/std/zig/system/macos.zig @@ -4,6 +4,8 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; pub fn version_from_build(build: []const u8) !std.builtin.Version { // build format: @@ -452,3 +454,25 @@ test "version_from_build" { std.testing.expect(std.mem.eql(u8, sver, pair[1])); } } + +/// Detect SDK path on Darwin. +/// Calls `xcrun --show-sdk-path` which result can be used to specify +/// `-syslibroot` param of the linker. +/// The caller needs to free the resulting path slice. +pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { + assert(std.Target.current.isDarwin()); + const argv = &[_][]const u8{ "/usr/bin/xcrun", "--show-sdk-path" }; + const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); + defer { + allocator.free(result.stderr); + allocator.free(result.stdout); + } + if (result.stderr.len != 0) { + std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {}", .{result.stderr}); + } + if (result.term.Exited != 0) { + return error.ProcessTerminated; + } + const syslibroot = mem.trimRight(u8, result.stdout, "\r\n"); + return mem.dupe(allocator, u8, syslibroot); +} diff --git a/src/Compilation.zig b/src/Compilation.zig index 472f367b91..872eb1a78b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -333,6 +333,7 @@ pub const InitOptions = struct { keep_source_files_loaded: bool = false, clang_argv: []const []const u8 = &[0][]const u8{}, lld_argv: []const []const u8 = &[0][]const u8{}, + syslibroot: ?[]const u8 = null, lib_dirs: []const []const u8 = &[0][]const u8{}, rpath_list: []const []const u8 = &[0][]const u8{}, c_source_files: []const CSourceFile = &[0]CSourceFile{}, @@ -773,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, .system_libs = system_libs, + .syslibroot = options.syslibroot, .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, .strip = strip, diff --git a/src/link.zig b/src/link.zig index 3f6ceb7ac3..02433ecde7 100644 --- a/src/link.zig +++ b/src/link.zig @@ -88,6 +88,8 @@ pub const Options = struct { llvm_cpu_features: ?[*:0]const u8, /// Extra args passed directly to LLD. Ignored when not linking with LLD. extra_lld_args: []const []const u8, + /// Darwin-only. Set the root path to the system libraries and frameworks. + syslibroot: ?[]const u8, objects: []const []const u8, framework_dirs: []const []const u8, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7344a698f9..01830c1561 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -628,6 +628,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } } + if (self.base.options.syslibroot) |dir| { + try argv.append("-syslibroot"); + try argv.append(dir); + } + for (self.base.options.lib_dirs) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); diff --git a/src/main.zig b/src/main.zig index 45fcb5ce61..87cd3e9dc7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -315,8 +315,9 @@ const usage_build_generic = \\ --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image + \\ -syslibroot [dir] (darwin) prepend a prefix to all search paths \\ -framework [name] (darwin) link against framework - \\ -F[dir] (darwin) add search path for frameworks + \\ -F [dir] (darwin) add search path for frameworks \\ \\Test Options: \\ --test-filter [text] Skip tests that do not match filter @@ -492,6 +493,7 @@ fn buildOutputType( var main_pkg_path: ?[]const u8 = null; var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var subsystem: ?std.Target.SubSystem = null; + var syslibroot: ?[]const u8 = null; var system_libs = std.ArrayList([]const u8).init(gpa); defer system_libs.deinit(); @@ -682,6 +684,10 @@ fn buildOutputType( if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); i += 1; try lib_dirs.append(args[i]); + } else if (mem.eql(u8, arg, "-syslibroot")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + syslibroot = args[i]; } else if (mem.eql(u8, arg, "-F")) { if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); i += 1; @@ -1380,6 +1386,12 @@ fn buildOutputType( } } + if (cross_target.isNativeOs() and std.Target.current.isDarwin()) { + if (syslibroot == null) { + syslibroot = try std.zig.system.getSDKPath(arena); + } + } + const object_format: std.Target.ObjectFormat = blk: { const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat(); if (mem.eql(u8, ofmt, "elf")) { @@ -1628,6 +1640,7 @@ fn buildOutputType( .rpath_list = rpath_list.items, .c_source_files = c_source_files.items, .link_objects = link_objects.items, + .syslibroot = syslibroot, .framework_dirs = framework_dirs.items, .frameworks = frameworks.items, .system_libs = system_libs.items, @@ -2159,6 +2172,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v var override_lib_dir: ?[]const u8 = null; var override_global_cache_dir: ?[]const u8 = null; var override_local_cache_dir: ?[]const u8 = null; + var syslibroot: ?[]const u8 = null; var child_argv = std.ArrayList([]const u8).init(arena); const argv_index_exe = child_argv.items.len; @@ -2200,6 +2214,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v override_global_cache_dir = args[i]; try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); continue; + } else if (mem.eql(u8, arg, "--syslibroot")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + syslibroot = args[i]; + continue; } } try child_argv.append(arg); @@ -2305,6 +2324,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v const cross_target: std.zig.CrossTarget = .{}; const target_info = try detectNativeTargetInfo(gpa, cross_target); + if (cross_target.isNativeOs() and target_info.target.isDarwin()) { + if (syslibroot == null) { + syslibroot = try std.zig.system.getSDKPath(arena); + } + } + const exe_basename = try std.zig.binNameAlloc(arena, .{ .root_name = "build", .target = target_info.target, @@ -2328,6 +2353,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v .target = target_info.target, .is_native_os = cross_target.isNativeOs(), .dynamic_linker = target_info.dynamic_linker.get(), + .syslibroot = syslibroot, .output_mode = .Exe, .root_pkg = &root_pkg, .emit_bin = emit_bin,