authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-02 09:04:18+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-02 17:06:09+01:00
log317c555a5cdc52612e998d8b4bcbaf730be1a6f1
tree0c414e11cfb2de91e0c7ee6485d784f61dc4330a
parent909aae8153e7ac71197bc9f864e4ceb2793317f2

Fix linking issues on BigSur

This commit fixes linking issue on macOS 11 BigSur by appending a prefix path to all lib and framework search paths known as `-syslibroot`. The reason this is needed is that in macOS 11, the system libraries and frameworks are no longer readily available in the filesystem. Instead, the new macOS ships with a built-in dynamic linker cache of all system-provided libraries, and hence, when linking with either `lld.ld64` or `ld64`, it is required to pass in `-syslibroot [dir]`. The latter can usually be obtained by invoking `xcrun --show-sdk-path`. With this commit, Zig will do this automatically when compiling natively on macOS. However, it also provides a flag `-syslibroot` which can be used to overwrite the automtically populated value. To summarise, with this change, the user of Zig is not required to generate and append their own syslibroot path. Standard invocations such as `zig build-exe hello.zig` or `zig build` for projects will work out of the box. The only missing bit is `zig cc` and `zig c++` since the addition of the `-syslibroot` option would be a mismatch between the values provided by `clang` itself and Zig's wrapper.

7 files changed, 91 insertions(+), 6 deletions(-)

lib/std/build.zig+29-5
...@@ -1207,6 +1207,7 @@ pub const LibExeObjStep = struct {...@@ -1207,6 +1207,7 @@ pub const LibExeObjStep = struct {
1207 name_only_filename: []const u8,1207 name_only_filename: []const u8,
1208 strip: bool,1208 strip: bool,
1209 lib_paths: ArrayList([]const u8),1209 lib_paths: ArrayList([]const u8),
1210 syslibroot: ?[]const u8 = null,
1210 framework_dirs: ArrayList([]const u8),1211 framework_dirs: ArrayList([]const u8),
1211 frameworks: BufSet,1212 frameworks: BufSet,
1212 verbose_link: bool,1213 verbose_link: bool,
...@@ -1841,6 +1842,10 @@ pub const LibExeObjStep = struct {...@@ -1841,6 +1842,10 @@ pub const LibExeObjStep = struct {
1841 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;1842 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
1842 }1843 }
18431844
1845 pub fn addSyslibroot(self: *LibExeObjStep, path: []const u8) void {
1846 self.syslibroot = path;
1847 }
1848
1844 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {1849 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {
1845 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;1850 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
1846 }1851 }
...@@ -1915,11 +1920,18 @@ pub const LibExeObjStep = struct {...@@ -1915,11 +1920,18 @@ pub const LibExeObjStep = struct {
1915 }1920 }
1916 }1921 }
19171922
1918 // Inherit dependencies on darwin frameworks1923 if (self.target.isDarwin()) {
1919 if (self.target.isDarwin() and !other.isDynamicLibrary()) {1924 // Inherit syslibroot
1920 var it = other.frameworks.iterator();1925 if (other.syslibroot) |path| {
1921 while (it.next()) |entry| {1926 self.syslibroot = path;
1922 self.frameworks.put(entry.key) catch unreachable;1927 }
1928
1929 // Inherit dependencies on darwin frameworks
1930 if (!other.isDynamicLibrary()) {
1931 var it = other.frameworks.iterator();
1932 while (it.next()) |entry| {
1933 self.frameworks.put(entry.key) catch unreachable;
1934 }
1923 }1935 }
1924 }1936 }
1925 }1937 }
...@@ -2271,6 +2283,18 @@ pub const LibExeObjStep = struct {...@@ -2271,6 +2283,18 @@ pub const LibExeObjStep = struct {
2271 }2283 }
22722284
2273 if (self.target.isDarwin()) {2285 if (self.target.isDarwin()) {
2286 if (self.syslibroot) |path| {
2287 try zig_args.append("-syslibroot");
2288 try zig_args.append(path);
2289 } else {
2290 if (self.target.isNative()) {
2291 const syslibroot = try std.zig.system.getSDKPath(builder.allocator);
2292 errdefer builder.allocator.free(syslibroot);
2293 try zig_args.append("-syslibroot");
2294 try zig_args.append(syslibroot);
2295 }
2296 }
2297
2274 for (self.framework_dirs.span()) |dir| {2298 for (self.framework_dirs.span()) |dir| {
2275 try zig_args.append("-F");2299 try zig_args.append("-F");
2276 try zig_args.append(dir);2300 try zig_args.append(dir);
lib/std/zig/system.zig+2
...@@ -17,6 +17,8 @@ const macos = @import("system/macos.zig");...@@ -17,6 +17,8 @@ const macos = @import("system/macos.zig");
1717
18const is_windows = Target.current.os.tag == .windows;18const is_windows = Target.current.os.tag == .windows;
1919
20pub const getSDKPath = macos.getSDKPath;
21
20pub const NativePaths = struct {22pub const NativePaths = struct {
21 include_dirs: ArrayList([:0]u8),23 include_dirs: ArrayList([:0]u8),
22 lib_dirs: ArrayList([:0]u8),24 lib_dirs: ArrayList([:0]u8),
lib/std/zig/system/macos.zig+24
...@@ -4,6 +4,8 @@...@@ -4,6 +4,8 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("std");
7const assert = std.debug.assert;
8const mem = std.mem;
79
8pub fn version_from_build(build: []const u8) !std.builtin.Version {10pub fn version_from_build(build: []const u8) !std.builtin.Version {
9 // build format:11 // build format:
...@@ -452,3 +454,25 @@ test "version_from_build" {...@@ -452,3 +454,25 @@ test "version_from_build" {
452 std.testing.expect(std.mem.eql(u8, sver, pair[1]));454 std.testing.expect(std.mem.eql(u8, sver, pair[1]));
453 }455 }
454}456}
457
458/// Detect SDK path on Darwin.
459/// Calls `xcrun --show-sdk-path` which result can be used to specify
460/// `-syslibroot` param of the linker.
461/// The caller needs to free the resulting path slice.
462pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 {
463 assert(std.Target.current.isDarwin());
464 const argv = &[_][]const u8{ "/usr/bin/xcrun", "--show-sdk-path" };
465 const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv });
466 defer {
467 allocator.free(result.stderr);
468 allocator.free(result.stdout);
469 }
470 if (result.stderr.len != 0) {
471 std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {}", .{result.stderr});
472 }
473 if (result.term.Exited != 0) {
474 return error.ProcessTerminated;
475 }
476 const syslibroot = mem.trimRight(u8, result.stdout, "\r\n");
477 return mem.dupe(allocator, u8, syslibroot);
478}
src/Compilation.zig+2
...@@ -333,6 +333,7 @@ pub const InitOptions = struct {...@@ -333,6 +333,7 @@ pub const InitOptions = struct {
333 keep_source_files_loaded: bool = false,333 keep_source_files_loaded: bool = false,
334 clang_argv: []const []const u8 = &[0][]const u8{},334 clang_argv: []const []const u8 = &[0][]const u8{},
335 lld_argv: []const []const u8 = &[0][]const u8{},335 lld_argv: []const []const u8 = &[0][]const u8{},
336 syslibroot: ?[]const u8 = null,
336 lib_dirs: []const []const u8 = &[0][]const u8{},337 lib_dirs: []const []const u8 = &[0][]const u8{},
337 rpath_list: []const []const u8 = &[0][]const u8{},338 rpath_list: []const []const u8 = &[0][]const u8{},
338 c_source_files: []const CSourceFile = &[0]CSourceFile{},339 c_source_files: []const CSourceFile = &[0]CSourceFile{},
...@@ -773,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -773,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
773 .frameworks = options.frameworks,774 .frameworks = options.frameworks,
774 .framework_dirs = options.framework_dirs,775 .framework_dirs = options.framework_dirs,
775 .system_libs = system_libs,776 .system_libs = system_libs,
777 .syslibroot = options.syslibroot,
776 .lib_dirs = options.lib_dirs,778 .lib_dirs = options.lib_dirs,
777 .rpath_list = options.rpath_list,779 .rpath_list = options.rpath_list,
778 .strip = strip,780 .strip = strip,
src/link.zig+2
...@@ -88,6 +88,8 @@ pub const Options = struct {...@@ -88,6 +88,8 @@ pub const Options = struct {
88 llvm_cpu_features: ?[*:0]const u8,88 llvm_cpu_features: ?[*:0]const u8,
89 /// Extra args passed directly to LLD. Ignored when not linking with LLD.89 /// Extra args passed directly to LLD. Ignored when not linking with LLD.
90 extra_lld_args: []const []const u8,90 extra_lld_args: []const []const u8,
91 /// Darwin-only. Set the root path to the system libraries and frameworks.
92 syslibroot: ?[]const u8,
9193
92 objects: []const []const u8,94 objects: []const []const u8,
93 framework_dirs: []const []const u8,95 framework_dirs: []const []const u8,
src/link/MachO.zig+5
...@@ -628,6 +628,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -628,6 +628,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
628 }628 }
629 }629 }
630630
631 if (self.base.options.syslibroot) |dir| {
632 try argv.append("-syslibroot");
633 try argv.append(dir);
634 }
635
631 for (self.base.options.lib_dirs) |lib_dir| {636 for (self.base.options.lib_dirs) |lib_dir| {
632 try argv.append("-L");637 try argv.append("-L");
633 try argv.append(lib_dir);638 try argv.append(lib_dir);
src/main.zig+27-1
...@@ -315,8 +315,9 @@ const usage_build_generic =...@@ -315,8 +315,9 @@ const usage_build_generic =
315 \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"315 \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
316 \\ --stack [size] Override default stack size316 \\ --stack [size] Override default stack size
317 \\ --image-base [addr] Set base address for executable image317 \\ --image-base [addr] Set base address for executable image
318 \\ -syslibroot [dir] (darwin) prepend a prefix to all search paths
318 \\ -framework [name] (darwin) link against framework319 \\ -framework [name] (darwin) link against framework
319 \\ -F[dir] (darwin) add search path for frameworks320 \\ -F [dir] (darwin) add search path for frameworks
320 \\321 \\
321 \\Test Options:322 \\Test Options:
322 \\ --test-filter [text] Skip tests that do not match filter323 \\ --test-filter [text] Skip tests that do not match filter
...@@ -492,6 +493,7 @@ fn buildOutputType(...@@ -492,6 +493,7 @@ fn buildOutputType(
492 var main_pkg_path: ?[]const u8 = null;493 var main_pkg_path: ?[]const u8 = null;
493 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;494 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
494 var subsystem: ?std.Target.SubSystem = null;495 var subsystem: ?std.Target.SubSystem = null;
496 var syslibroot: ?[]const u8 = null;
495497
496 var system_libs = std.ArrayList([]const u8).init(gpa);498 var system_libs = std.ArrayList([]const u8).init(gpa);
497 defer system_libs.deinit();499 defer system_libs.deinit();
...@@ -682,6 +684,10 @@ fn buildOutputType(...@@ -682,6 +684,10 @@ fn buildOutputType(
682 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});684 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
683 i += 1;685 i += 1;
684 try lib_dirs.append(args[i]);686 try lib_dirs.append(args[i]);
687 } else if (mem.eql(u8, arg, "-syslibroot")) {
688 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
689 i += 1;
690 syslibroot = args[i];
685 } else if (mem.eql(u8, arg, "-F")) {691 } else if (mem.eql(u8, arg, "-F")) {
686 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});692 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
687 i += 1;693 i += 1;
...@@ -1380,6 +1386,12 @@ fn buildOutputType(...@@ -1380,6 +1386,12 @@ fn buildOutputType(
1380 }1386 }
1381 }1387 }
13821388
1389 if (cross_target.isNativeOs() and std.Target.current.isDarwin()) {
1390 if (syslibroot == null) {
1391 syslibroot = try std.zig.system.getSDKPath(arena);
1392 }
1393 }
1394
1383 const object_format: std.Target.ObjectFormat = blk: {1395 const object_format: std.Target.ObjectFormat = blk: {
1384 const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat();1396 const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat();
1385 if (mem.eql(u8, ofmt, "elf")) {1397 if (mem.eql(u8, ofmt, "elf")) {
...@@ -1628,6 +1640,7 @@ fn buildOutputType(...@@ -1628,6 +1640,7 @@ fn buildOutputType(
1628 .rpath_list = rpath_list.items,1640 .rpath_list = rpath_list.items,
1629 .c_source_files = c_source_files.items,1641 .c_source_files = c_source_files.items,
1630 .link_objects = link_objects.items,1642 .link_objects = link_objects.items,
1643 .syslibroot = syslibroot,
1631 .framework_dirs = framework_dirs.items,1644 .framework_dirs = framework_dirs.items,
1632 .frameworks = frameworks.items,1645 .frameworks = frameworks.items,
1633 .system_libs = system_libs.items,1646 .system_libs = system_libs.items,
...@@ -2159,6 +2172,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2159,6 +2172,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2159 var override_lib_dir: ?[]const u8 = null;2172 var override_lib_dir: ?[]const u8 = null;
2160 var override_global_cache_dir: ?[]const u8 = null;2173 var override_global_cache_dir: ?[]const u8 = null;
2161 var override_local_cache_dir: ?[]const u8 = null;2174 var override_local_cache_dir: ?[]const u8 = null;
2175 var syslibroot: ?[]const u8 = null;
2162 var child_argv = std.ArrayList([]const u8).init(arena);2176 var child_argv = std.ArrayList([]const u8).init(arena);
21632177
2164 const argv_index_exe = child_argv.items.len;2178 const argv_index_exe = child_argv.items.len;
...@@ -2200,6 +2214,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2200,6 +2214,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2200 override_global_cache_dir = args[i];2214 override_global_cache_dir = args[i];
2201 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });2215 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
2202 continue;2216 continue;
2217 } else if (mem.eql(u8, arg, "--syslibroot")) {
2218 if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
2219 i += 1;
2220 syslibroot = args[i];
2221 continue;
2203 }2222 }
2204 }2223 }
2205 try child_argv.append(arg);2224 try child_argv.append(arg);
...@@ -2305,6 +2324,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2305,6 +2324,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2305 const cross_target: std.zig.CrossTarget = .{};2324 const cross_target: std.zig.CrossTarget = .{};
2306 const target_info = try detectNativeTargetInfo(gpa, cross_target);2325 const target_info = try detectNativeTargetInfo(gpa, cross_target);
23072326
2327 if (cross_target.isNativeOs() and target_info.target.isDarwin()) {
2328 if (syslibroot == null) {
2329 syslibroot = try std.zig.system.getSDKPath(arena);
2330 }
2331 }
2332
2308 const exe_basename = try std.zig.binNameAlloc(arena, .{2333 const exe_basename = try std.zig.binNameAlloc(arena, .{
2309 .root_name = "build",2334 .root_name = "build",
2310 .target = target_info.target,2335 .target = target_info.target,
...@@ -2328,6 +2353,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2328,6 +2353,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2328 .target = target_info.target,2353 .target = target_info.target,
2329 .is_native_os = cross_target.isNativeOs(),2354 .is_native_os = cross_target.isNativeOs(),
2330 .dynamic_linker = target_info.dynamic_linker.get(),2355 .dynamic_linker = target_info.dynamic_linker.get(),
2356 .syslibroot = syslibroot,
2331 .output_mode = .Exe,2357 .output_mode = .Exe,
2332 .root_pkg = &root_pkg,2358 .root_pkg = &root_pkg,
2333 .emit_bin = emit_bin,2359 .emit_bin = emit_bin,