| author | |
| committer | |
| log | 317c555a5cdc52612e998d8b4bcbaf730be1a6f1 |
| tree | 0c414e11cfb2de91e0c7ee6485d784f61dc4330a |
| parent | 909aae8153e7ac71197bc9f864e4ceb2793317f2 |
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 | } |
| 1843 | 1844 | ||
| 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 | } |
| 1917 | 1922 | ||
| 1918 | // Inherit dependencies on darwin frameworks | 1923 | 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 | } |
| 2272 | 2284 | ||
| 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"); |
| 17 | 17 | ||
| 18 | const is_windows = Target.current.os.tag == .windows; | 18 | const is_windows = Target.current.os.tag == .windows; |
| 19 | 19 | ||
| 20 | pub const getSDKPath = macos.getSDKPath; | ||
| 21 | |||
| 20 | pub const NativePaths = struct { | 22 | pub 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 copies | 4 | // 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. |
| 6 | const std = @import("std"); | 6 | const std = @import("std"); |
| 7 | const assert = std.debug.assert; | ||
| 8 | const mem = std.mem; | ||
| 7 | 9 | ||
| 8 | pub fn version_from_build(build: []const u8) !std.builtin.Version { | 10 | pub 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. | ||
| 462 | pub 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, | ||
| 91 | 93 | ||
| 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 | } |
| 630 | 630 | ||
| 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 size | 316 | \\ --stack [size] Override default stack size |
| 317 | \\ --image-base [addr] Set base address for executable image | 317 | \\ --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 framework | 319 | \\ -framework [name] (darwin) link against framework |
| 319 | \\ -F[dir] (darwin) add search path for frameworks | 320 | \\ -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 filter | 323 | \\ --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; | ||
| 495 | 497 | ||
| 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 | } |
| 1382 | 1388 | ||
| 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); |
| 2163 | 2177 | ||
| 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); |
| 2307 | 2326 | ||
| 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, |