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 {
12071207 name_only_filename: []const u8,
12081208 strip: bool,
12091209 lib_paths: ArrayList([]const u8),
1210 syslibroot: ?[]const u8 = null,
12101211 framework_dirs: ArrayList([]const u8),
12111212 frameworks: BufSet,
12121213 verbose_link: bool,
......@@ -1841,6 +1842,10 @@ pub const LibExeObjStep = struct {
18411842 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
18421843 }
18431844
1845 pub fn addSyslibroot(self: *LibExeObjStep, path: []const u8) void {
1846 self.syslibroot = path;
1847 }
1848
18441849 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {
18451850 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
18461851 }
......@@ -1915,11 +1920,18 @@ pub const LibExeObjStep = struct {
19151920 }
19161921 }
19171922
1918 // Inherit dependencies on darwin frameworks
1919 if (self.target.isDarwin() and !other.isDynamicLibrary()) {
1920 var it = other.frameworks.iterator();
1921 while (it.next()) |entry| {
1922 self.frameworks.put(entry.key) catch unreachable;
1923 if (self.target.isDarwin()) {
1924 // Inherit syslibroot
1925 if (other.syslibroot) |path| {
1926 self.syslibroot = path;
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 }
19231935 }
19241936 }
19251937 }
......@@ -2271,6 +2283,18 @@ pub const LibExeObjStep = struct {
22712283 }
22722284
22732285 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
22742298 for (self.framework_dirs.span()) |dir| {
22752299 try zig_args.append("-F");
22762300 try zig_args.append(dir);
lib/std/zig/system.zig+2
......@@ -17,6 +17,8 @@ const macos = @import("system/macos.zig");
1717
1818const is_windows = Target.current.os.tag == .windows;
1919
20pub const getSDKPath = macos.getSDKPath;
21
2022pub const NativePaths = struct {
2123 include_dirs: ArrayList([:0]u8),
2224 lib_dirs: ArrayList([:0]u8),
lib/std/zig/system/macos.zig+24
......@@ -4,6 +4,8 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66const std = @import("std");
7const assert = std.debug.assert;
8const mem = std.mem;
79
810pub fn version_from_build(build: []const u8) !std.builtin.Version {
911 // build format:
......@@ -452,3 +454,25 @@ test "version_from_build" {
452454 std.testing.expect(std.mem.eql(u8, sver, pair[1]));
453455 }
454456}
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 {
333333 keep_source_files_loaded: bool = false,
334334 clang_argv: []const []const u8 = &[0][]const u8{},
335335 lld_argv: []const []const u8 = &[0][]const u8{},
336 syslibroot: ?[]const u8 = null,
336337 lib_dirs: []const []const u8 = &[0][]const u8{},
337338 rpath_list: []const []const u8 = &[0][]const u8{},
338339 c_source_files: []const CSourceFile = &[0]CSourceFile{},
......@@ -773,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
773774 .frameworks = options.frameworks,
774775 .framework_dirs = options.framework_dirs,
775776 .system_libs = system_libs,
777 .syslibroot = options.syslibroot,
776778 .lib_dirs = options.lib_dirs,
777779 .rpath_list = options.rpath_list,
778780 .strip = strip,
src/link.zig+2
......@@ -88,6 +88,8 @@ pub const Options = struct {
8888 llvm_cpu_features: ?[*:0]const u8,
8989 /// Extra args passed directly to LLD. Ignored when not linking with LLD.
9090 extra_lld_args: []const []const u8,
91 /// Darwin-only. Set the root path to the system libraries and frameworks.
92 syslibroot: ?[]const u8,
9193
9294 objects: []const []const u8,
9395 framework_dirs: []const []const u8,
src/link/MachO.zig+5
......@@ -628,6 +628,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
628628 }
629629 }
630630
631 if (self.base.options.syslibroot) |dir| {
632 try argv.append("-syslibroot");
633 try argv.append(dir);
634 }
635
631636 for (self.base.options.lib_dirs) |lib_dir| {
632637 try argv.append("-L");
633638 try argv.append(lib_dir);
src/main.zig+27-1
......@@ -315,8 +315,9 @@ const usage_build_generic =
315315 \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
316316 \\ --stack [size] Override default stack size
317317 \\ --image-base [addr] Set base address for executable image
318 \\ -syslibroot [dir] (darwin) prepend a prefix to all search paths
318319 \\ -framework [name] (darwin) link against framework
319 \\ -F[dir] (darwin) add search path for frameworks
320 \\ -F [dir] (darwin) add search path for frameworks
320321 \\
321322 \\Test Options:
322323 \\ --test-filter [text] Skip tests that do not match filter
......@@ -492,6 +493,7 @@ fn buildOutputType(
492493 var main_pkg_path: ?[]const u8 = null;
493494 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
494495 var subsystem: ?std.Target.SubSystem = null;
496 var syslibroot: ?[]const u8 = null;
495497
496498 var system_libs = std.ArrayList([]const u8).init(gpa);
497499 defer system_libs.deinit();
......@@ -682,6 +684,10 @@ fn buildOutputType(
682684 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
683685 i += 1;
684686 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];
685691 } else if (mem.eql(u8, arg, "-F")) {
686692 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
687693 i += 1;
......@@ -1380,6 +1386,12 @@ fn buildOutputType(
13801386 }
13811387 }
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
13831395 const object_format: std.Target.ObjectFormat = blk: {
13841396 const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat();
13851397 if (mem.eql(u8, ofmt, "elf")) {
......@@ -1628,6 +1640,7 @@ fn buildOutputType(
16281640 .rpath_list = rpath_list.items,
16291641 .c_source_files = c_source_files.items,
16301642 .link_objects = link_objects.items,
1643 .syslibroot = syslibroot,
16311644 .framework_dirs = framework_dirs.items,
16321645 .frameworks = frameworks.items,
16331646 .system_libs = system_libs.items,
......@@ -2159,6 +2172,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
21592172 var override_lib_dir: ?[]const u8 = null;
21602173 var override_global_cache_dir: ?[]const u8 = null;
21612174 var override_local_cache_dir: ?[]const u8 = null;
2175 var syslibroot: ?[]const u8 = null;
21622176 var child_argv = std.ArrayList([]const u8).init(arena);
21632177
21642178 const argv_index_exe = child_argv.items.len;
......@@ -2200,6 +2214,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
22002214 override_global_cache_dir = args[i];
22012215 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
22022216 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;
22032222 }
22042223 }
22052224 try child_argv.append(arg);
......@@ -2305,6 +2324,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
23052324 const cross_target: std.zig.CrossTarget = .{};
23062325 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
23082333 const exe_basename = try std.zig.binNameAlloc(arena, .{
23092334 .root_name = "build",
23102335 .target = target_info.target,
......@@ -2328,6 +2353,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
23282353 .target = target_info.target,
23292354 .is_native_os = cross_target.isNativeOs(),
23302355 .dynamic_linker = target_info.dynamic_linker.get(),
2356 .syslibroot = syslibroot,
23312357 .output_mode = .Exe,
23322358 .root_pkg = &root_pkg,
23332359 .emit_bin = emit_bin,