authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-15 17:10:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-03 09:52:14-07:00
loga08cc7d2ae3bd6e90f8d26ac13f2e0652687dc30
tree85cd80a1276d31cb543ae2d4317b257e39bed146
parentf887b0251822f75dc4a3e24ca5337cb681c1eb1f

compiler: resolve library paths in the frontend

search_strategy is no longer passed to Compilation at all; instead it is used in the CLI code only. When using Zig CLI mode, `-l` no longer has the ability to link statically; use positional arguments for this. The CLI has a small abstraction around library resolution handling which is used to remove some code duplication regarding static libraries, as well as handle the difference between zig cc CLI mode and zig CLI mode. Thanks to this, system libraries are now included in the cache hash, and thus changes to them will correctly cause cache misses. In the future, lib_dirs should no longer be passed to Compilation at all, because it is a frontend-only concept. Previously, -search_paths_first and -search_dylibs_first were Darwin-only arguments; they now work the same for all targets. Same thing with --sysroot. Improved the error reporting for failure to find a system library. An example error now looks like this: ``` $ zig build-exe test.zig -lfoo -L. -L/a -target x86_64-macos --sysroot /home/andy/local error: unable to find Dynamic system library 'foo' using strategy 'no_fallback'. search paths: ./libfoo.tbd ./libfoo.dylib ./libfoo.so /home/andy/local/a/libfoo.tbd /home/andy/local/a/libfoo.dylib /home/andy/local/a/libfoo.so /a/libfoo.tbd /a/libfoo.dylib /a/libfoo.so ``` closes #14963

7 files changed, 454 insertions(+), 246 deletions(-)

src/Compilation.zig+10-10
...@@ -448,6 +448,7 @@ pub const ClangPreprocessorMode = enum {...@@ -448,6 +448,7 @@ pub const ClangPreprocessorMode = enum {
448 stdout,448 stdout,
449};449};
450450
451pub const Framework = link.Framework;
451pub const SystemLib = link.SystemLib;452pub const SystemLib = link.SystemLib;
452pub const CacheMode = link.CacheMode;453pub const CacheMode = link.CacheMode;
453454
...@@ -505,7 +506,7 @@ pub const InitOptions = struct {...@@ -505,7 +506,7 @@ pub const InitOptions = struct {
505 c_source_files: []const CSourceFile = &[0]CSourceFile{},506 c_source_files: []const CSourceFile = &[0]CSourceFile{},
506 link_objects: []LinkObject = &[0]LinkObject{},507 link_objects: []LinkObject = &[0]LinkObject{},
507 framework_dirs: []const []const u8 = &[0][]const u8{},508 framework_dirs: []const []const u8 = &[0][]const u8{},
508 frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},509 frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},
509 system_lib_names: []const []const u8 = &.{},510 system_lib_names: []const []const u8 = &.{},
510 system_lib_infos: []const SystemLib = &.{},511 system_lib_infos: []const SystemLib = &.{},
511 /// These correspond to the WASI libc emulated subcomponents including:512 /// These correspond to the WASI libc emulated subcomponents including:
...@@ -644,8 +645,6 @@ pub const InitOptions = struct {...@@ -644,8 +645,6 @@ pub const InitOptions = struct {
644 entitlements: ?[]const u8 = null,645 entitlements: ?[]const u8 = null,
645 /// (Darwin) size of the __PAGEZERO segment646 /// (Darwin) size of the __PAGEZERO segment
646 pagezero_size: ?u64 = null,647 pagezero_size: ?u64 = null,
647 /// (Darwin) search strategy for system libraries
648 search_strategy: ?link.File.MachO.SearchStrategy = null,
649 /// (Darwin) set minimum space for future expansion of the load commands648 /// (Darwin) set minimum space for future expansion of the load commands
650 headerpad_size: ?u32 = null,649 headerpad_size: ?u32 = null,
651 /// (Darwin) set enough space as if all paths were MATPATHLEN650 /// (Darwin) set enough space as if all paths were MATPATHLEN
...@@ -1567,7 +1566,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1567,7 +1566,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1567 .install_name = options.install_name,1566 .install_name = options.install_name,
1568 .entitlements = options.entitlements,1567 .entitlements = options.entitlements,
1569 .pagezero_size = options.pagezero_size,1568 .pagezero_size = options.pagezero_size,
1570 .search_strategy = options.search_strategy,
1571 .headerpad_size = options.headerpad_size,1569 .headerpad_size = options.headerpad_size,
1572 .headerpad_max_install_names = options.headerpad_max_install_names,1570 .headerpad_max_install_names = options.headerpad_max_install_names,
1573 .dead_strip_dylibs = options.dead_strip_dylibs,1571 .dead_strip_dylibs = options.dead_strip_dylibs,
...@@ -1727,15 +1725,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1727,15 +1725,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17271725
1728 // When linking mingw-w64 there are some import libs we always need.1726 // When linking mingw-w64 there are some import libs we always need.
1729 for (mingw.always_link_libs) |name| {1727 for (mingw.always_link_libs) |name| {
1730 try comp.bin_file.options.system_libs.put(comp.gpa, name, .{});1728 try comp.bin_file.options.system_libs.put(comp.gpa, name, .{
1729 .needed = false,
1730 .weak = false,
1731 .path = name,
1732 });
1731 }1733 }
1732 }1734 }
1733 // Generate Windows import libs.1735 // Generate Windows import libs.
1734 if (target.os.tag == .windows) {1736 if (target.os.tag == .windows) {
1735 const count = comp.bin_file.options.system_libs.count();1737 const count = comp.bin_file.options.system_libs.count();
1736 try comp.work_queue.ensureUnusedCapacity(count);1738 try comp.work_queue.ensureUnusedCapacity(count);
1737 var i: usize = 0;1739 for (0..count) |i| {
1738 while (i < count) : (i += 1) {
1739 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });1740 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
1740 }1741 }
1741 }1742 }
...@@ -2377,7 +2378,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2377,7 +2378,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2377 }2378 }
2378 man.hash.addOptionalBytes(comp.bin_file.options.soname);2379 man.hash.addOptionalBytes(comp.bin_file.options.soname);
2379 man.hash.addOptional(comp.bin_file.options.version);2380 man.hash.addOptional(comp.bin_file.options.version);
2380 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs);2381 try link.hashAddSystemLibs(man, comp.bin_file.options.system_libs);
2381 man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys());2382 man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys());
2382 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);2383 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);
2383 man.hash.add(comp.bin_file.options.bind_global_refs_locally);2384 man.hash.add(comp.bin_file.options.bind_global_refs_locally);
...@@ -2395,10 +2396,9 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2395,10 +2396,9 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23952396
2396 // Mach-O specific stuff2397 // Mach-O specific stuff
2397 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);2398 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2398 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);2399 link.hashAddFrameworks(&man.hash, comp.bin_file.options.frameworks);
2399 try man.addOptionalFile(comp.bin_file.options.entitlements);2400 try man.addOptionalFile(comp.bin_file.options.entitlements);
2400 man.hash.addOptional(comp.bin_file.options.pagezero_size);2401 man.hash.addOptional(comp.bin_file.options.pagezero_size);
2401 man.hash.addOptional(comp.bin_file.options.search_strategy);
2402 man.hash.addOptional(comp.bin_file.options.headerpad_size);2402 man.hash.addOptional(comp.bin_file.options.headerpad_size);
2403 man.hash.add(comp.bin_file.options.headerpad_max_install_names);2403 man.hash.add(comp.bin_file.options.headerpad_max_install_names);
2404 man.hash.add(comp.bin_file.options.dead_strip_dylibs);2404 man.hash.add(comp.bin_file.options.dead_strip_dylibs);
src/link.zig+27-6
...@@ -21,7 +21,16 @@ const Type = @import("type.zig").Type;...@@ -21,7 +21,16 @@ const Type = @import("type.zig").Type;
21const TypedValue = @import("TypedValue.zig");21const TypedValue = @import("TypedValue.zig");
2222
23/// When adding a new field, remember to update `hashAddSystemLibs`.23/// When adding a new field, remember to update `hashAddSystemLibs`.
24/// These are *always* dynamically linked. Static libraries will be
25/// provided as positional arguments.
24pub const SystemLib = struct {26pub const SystemLib = struct {
27 needed: bool,
28 weak: bool,
29 path: []const u8,
30};
31
32/// When adding a new field, remember to update `hashAddFrameworks`.
33pub const Framework = struct {
25 needed: bool = false,34 needed: bool = false,
26 weak: bool = false,35 weak: bool = false,
27};36};
...@@ -31,11 +40,23 @@ pub const SortSection = enum { name, alignment };...@@ -31,11 +40,23 @@ pub const SortSection = enum { name, alignment };
31pub const CacheMode = enum { incremental, whole };40pub const CacheMode = enum { incremental, whole };
3241
33pub fn hashAddSystemLibs(42pub fn hashAddSystemLibs(
34 hh: *Cache.HashHelper,43 man: *Cache.Manifest,
35 hm: std.StringArrayHashMapUnmanaged(SystemLib),44 hm: std.StringArrayHashMapUnmanaged(SystemLib),
45) !void {
46 const keys = hm.keys();
47 man.hash.addListOfBytes(keys);
48 for (hm.values()) |value| {
49 man.hash.add(value.needed);
50 man.hash.add(value.weak);
51 _ = try man.addFile(value.path, null);
52 }
53}
54
55pub fn hashAddFrameworks(
56 hh: *Cache.HashHelper,
57 hm: std.StringArrayHashMapUnmanaged(Framework),
36) void {58) void {
37 const keys = hm.keys();59 const keys = hm.keys();
38 hh.add(keys.len);
39 hh.addListOfBytes(keys);60 hh.addListOfBytes(keys);
40 for (hm.values()) |value| {61 for (hm.values()) |value| {
41 hh.add(value.needed);62 hh.add(value.needed);
...@@ -183,9 +204,12 @@ pub const Options = struct {...@@ -183,9 +204,12 @@ pub const Options = struct {
183204
184 objects: []Compilation.LinkObject,205 objects: []Compilation.LinkObject,
185 framework_dirs: []const []const u8,206 framework_dirs: []const []const u8,
186 frameworks: std.StringArrayHashMapUnmanaged(SystemLib),207 frameworks: std.StringArrayHashMapUnmanaged(Framework),
208 /// These are *always* dynamically linked. Static libraries will be
209 /// provided as positional arguments.
187 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),210 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
188 wasi_emulated_libs: []const wasi_libc.CRTFile,211 wasi_emulated_libs: []const wasi_libc.CRTFile,
212 // TODO: remove this. libraries are resolved by the frontend.
189 lib_dirs: []const []const u8,213 lib_dirs: []const []const u8,
190 rpath_list: []const []const u8,214 rpath_list: []const []const u8,
191215
...@@ -225,9 +249,6 @@ pub const Options = struct {...@@ -225,9 +249,6 @@ pub const Options = struct {
225 /// (Darwin) size of the __PAGEZERO segment249 /// (Darwin) size of the __PAGEZERO segment
226 pagezero_size: ?u64 = null,250 pagezero_size: ?u64 = null,
227251
228 /// (Darwin) search strategy for system libraries
229 search_strategy: ?File.MachO.SearchStrategy = null,
230
231 /// (Darwin) set minimum space for future expansion of the load commands252 /// (Darwin) set minimum space for future expansion of the load commands
232 headerpad_size: ?u32 = null,253 headerpad_size: ?u32 = null,
233254
src/link/Coff/lld.zig+1-1
...@@ -88,7 +88,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -88,7 +88,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
88 }88 }
89 }89 }
90 }90 }
91 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);91 try link.hashAddSystemLibs(&man, self.base.options.system_libs);
92 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());92 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
93 man.hash.addOptional(self.base.options.subsystem);93 man.hash.addOptional(self.base.options.subsystem);
94 man.hash.add(self.base.options.is_test);94 man.hash.add(self.base.options.is_test);
src/link/Elf.zig+4-6
...@@ -1428,7 +1428,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1428,7 +1428,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1428 }1428 }
1429 man.hash.addOptionalBytes(self.base.options.soname);1429 man.hash.addOptionalBytes(self.base.options.soname);
1430 man.hash.addOptional(self.base.options.version);1430 man.hash.addOptional(self.base.options.version);
1431 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);1431 try link.hashAddSystemLibs(&man, self.base.options.system_libs);
1432 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());1432 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
1433 man.hash.add(allow_shlib_undefined);1433 man.hash.add(allow_shlib_undefined);
1434 man.hash.add(self.base.options.bind_global_refs_locally);1434 man.hash.add(self.base.options.bind_global_refs_locally);
...@@ -1824,8 +1824,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1824,8 +1824,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1824 argv.appendAssumeCapacity("--as-needed");1824 argv.appendAssumeCapacity("--as-needed");
1825 var as_needed = true;1825 var as_needed = true;
18261826
1827 for (system_libs, 0..) |link_lib, i| {1827 for (system_libs_values) |lib_info| {
1828 const lib_as_needed = !system_libs_values[i].needed;1828 const lib_as_needed = !lib_info.needed;
1829 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {1829 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1830 0b00, 0b11 => {},1830 0b00, 0b11 => {},
1831 0b01 => {1831 0b01 => {
...@@ -1842,9 +1842,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1842,9 +1842,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1842 // libraries and not static libraries (the check for that needs to be earlier),1842 // libraries and not static libraries (the check for that needs to be earlier),
1843 // but they could be full paths to .so files, in which case we1843 // but they could be full paths to .so files, in which case we
1844 // want to avoid prepending "-l".1844 // want to avoid prepending "-l".
1845 const ext = Compilation.classifyFileExt(link_lib);1845 argv.appendAssumeCapacity(lib_info.path);
1846 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1847 argv.appendAssumeCapacity(arg);
1848 }1846 }
18491847
1850 if (!as_needed) {1848 if (!as_needed) {
src/link/MachO.zig+20-9
...@@ -58,11 +58,6 @@ const Rebase = @import("MachO/dyld_info/Rebase.zig");...@@ -58,11 +58,6 @@ const Rebase = @import("MachO/dyld_info/Rebase.zig");
5858
59pub const base_tag: File.Tag = File.Tag.macho;59pub const base_tag: File.Tag = File.Tag.macho;
6060
61pub const SearchStrategy = enum {
62 paths_first,
63 dylibs_first,
64};
65
66/// Mode of operation of the linker.61/// Mode of operation of the linker.
67pub const Mode = enum {62pub const Mode = enum {
68 /// Incremental mode will preallocate segments/sections and is compatible with63 /// Incremental mode will preallocate segments/sections and is compatible with
...@@ -840,7 +835,11 @@ pub fn resolveLibSystem(...@@ -840,7 +835,11 @@ pub fn resolveLibSystem(
840 // re-exports every single symbol definition.835 // re-exports every single symbol definition.
841 for (search_dirs) |dir| {836 for (search_dirs) |dir| {
842 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {837 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
843 try out_libs.put(full_path, .{ .needed = true });838 try out_libs.put(full_path, .{
839 .needed = true,
840 .weak = false,
841 .path = full_path,
842 });
844 libsystem_available = true;843 libsystem_available = true;
845 break :blk;844 break :blk;
846 }845 }
...@@ -850,8 +849,16 @@ pub fn resolveLibSystem(...@@ -850,8 +849,16 @@ pub fn resolveLibSystem(
850 for (search_dirs) |dir| {849 for (search_dirs) |dir| {
851 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {850 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
852 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {851 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
853 try out_libs.put(libsystem_path, .{ .needed = true });852 try out_libs.put(libsystem_path, .{
854 try out_libs.put(libc_path, .{ .needed = true });853 .needed = true,
854 .weak = false,
855 .path = libsystem_path,
856 });
857 try out_libs.put(libc_path, .{
858 .needed = true,
859 .weak = false,
860 .path = libc_path,
861 });
855 libsystem_available = true;862 libsystem_available = true;
856 break :blk;863 break :blk;
857 }864 }
...@@ -865,7 +872,11 @@ pub fn resolveLibSystem(...@@ -865,7 +872,11 @@ pub fn resolveLibSystem(
865 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{872 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
866 "libc", "darwin", libsystem_name,873 "libc", "darwin", libsystem_name,
867 });874 });
868 try out_libs.put(full_path, .{ .needed = true });875 try out_libs.put(full_path, .{
876 .needed = true,
877 .weak = false,
878 .path = full_path,
879 });
869 }880 }
870}881}
871882
src/link/MachO/zld.zig+11-80
...@@ -3410,7 +3410,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3410,7 +3410,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3410 // installation sources because they are always a product of the compiler version + target information.3410 // installation sources because they are always a product of the compiler version + target information.
3411 man.hash.add(stack_size);3411 man.hash.add(stack_size);
3412 man.hash.addOptional(options.pagezero_size);3412 man.hash.addOptional(options.pagezero_size);
3413 man.hash.addOptional(options.search_strategy);
3414 man.hash.addOptional(options.headerpad_size);3413 man.hash.addOptional(options.headerpad_size);
3415 man.hash.add(options.headerpad_max_install_names);3414 man.hash.add(options.headerpad_max_install_names);
3416 man.hash.add(gc_sections);3415 man.hash.add(gc_sections);
...@@ -3418,13 +3417,13 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3418,13 +3417,13 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3418 man.hash.add(options.strip);3417 man.hash.add(options.strip);
3419 man.hash.addListOfBytes(options.lib_dirs);3418 man.hash.addListOfBytes(options.lib_dirs);
3420 man.hash.addListOfBytes(options.framework_dirs);3419 man.hash.addListOfBytes(options.framework_dirs);
3421 link.hashAddSystemLibs(&man.hash, options.frameworks);3420 link.hashAddFrameworks(&man.hash, options.frameworks);
3422 man.hash.addListOfBytes(options.rpath_list);3421 man.hash.addListOfBytes(options.rpath_list);
3423 if (is_dyn_lib) {3422 if (is_dyn_lib) {
3424 man.hash.addOptionalBytes(options.install_name);3423 man.hash.addOptionalBytes(options.install_name);
3425 man.hash.addOptional(options.version);3424 man.hash.addOptional(options.version);
3426 }3425 }
3427 link.hashAddSystemLibs(&man.hash, options.system_libs);3426 try link.hashAddSystemLibs(&man, options.system_libs);
3428 man.hash.addOptionalBytes(options.sysroot);3427 man.hash.addOptionalBytes(options.sysroot);
3429 man.hash.addListOfBytes(options.force_undefined_symbols.keys());3428 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
3430 try man.addOptionalFile(options.entitlements);3429 try man.addOptionalFile(options.entitlements);
...@@ -3550,84 +3549,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3550,84 +3549,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3550 try positionals.append(comp.libcxx_static_lib.?.full_object_path);3549 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
3551 }3550 }
35523551
3553 // Shared and static libraries passed via `-l` flag.3552 {
3554 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);3553 // Add all system library paths to positionals.
35553554 const vals = options.system_libs.values();
3556 const system_lib_names = options.system_libs.keys();3555 try positionals.ensureUnusedCapacity(vals.len);
3557 for (system_lib_names) |system_lib_name| {3556 for (vals) |info| positionals.appendAssumeCapacity(info.path);
3558 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
3559 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
3560 // case we want to avoid prepending "-l".
3561 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
3562 try positionals.append(system_lib_name);
3563 continue;
3564 }
3565
3566 const system_lib_info = options.system_libs.get(system_lib_name).?;
3567 try candidate_libs.put(system_lib_name, .{
3568 .needed = system_lib_info.needed,
3569 .weak = system_lib_info.weak,
3570 });
3571 }
3572
3573 var lib_dirs = std.ArrayList([]const u8).init(arena);
3574 for (options.lib_dirs) |dir| {
3575 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
3576 try lib_dirs.append(search_dir);
3577 } else {
3578 log.warn("directory not found for '-L{s}'", .{dir});
3579 }
3580 }3557 }
35813558
3582 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);3559 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
35833560
3584 // Assume ld64 default -search_paths_first if no strategy specified.3561 for (options.system_libs.values()) |v| {
3585 const search_strategy = options.search_strategy orelse .paths_first;3562 try libs.put(v.path, v);
3586 outer: for (candidate_libs.keys()) |lib_name| {
3587 switch (search_strategy) {
3588 .paths_first => {
3589 // Look in each directory for a dylib (stub first), and then for archive
3590 for (lib_dirs.items) |dir| {
3591 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
3592 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3593 try libs.put(full_path, candidate_libs.get(lib_name).?);
3594 continue :outer;
3595 }
3596 }
3597 } else {
3598 log.warn("library not found for '-l{s}'", .{lib_name});
3599 lib_not_found = true;
3600 }
3601 },
3602 .dylibs_first => {
3603 // First, look for a dylib in each search dir
3604 for (lib_dirs.items) |dir| {
3605 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
3606 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3607 try libs.put(full_path, candidate_libs.get(lib_name).?);
3608 continue :outer;
3609 }
3610 }
3611 } else for (lib_dirs.items) |dir| {
3612 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
3613 try libs.put(full_path, candidate_libs.get(lib_name).?);
3614 } else {
3615 log.warn("library not found for '-l{s}'", .{lib_name});
3616 lib_not_found = true;
3617 }
3618 }
3619 },
3620 }
3621 }
3622
3623 if (lib_not_found) {
3624 log.warn("Library search paths:", .{});
3625 for (lib_dirs.items) |dir| {
3626 log.warn(" {s}", .{dir});
3627 }
3628 }3563 }
36293564
3630 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, lib_dirs.items, &libs);3565 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
36313566
3632 // frameworks3567 // frameworks
3633 var framework_dirs = std.ArrayList([]const u8).init(arena);3568 var framework_dirs = std.ArrayList([]const u8).init(arena);
...@@ -3647,6 +3582,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3647,6 +3582,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3647 try libs.put(full_path, .{3582 try libs.put(full_path, .{
3648 .needed = info.needed,3583 .needed = info.needed,
3649 .weak = info.weak,3584 .weak = info.weak,
3585 .path = full_path,
3650 });3586 });
3651 continue :outer;3587 continue :outer;
3652 }3588 }
...@@ -3698,11 +3634,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3698,11 +3634,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3698 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));3634 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
3699 }3635 }
37003636
3701 if (options.search_strategy) |strat| switch (strat) {
3702 .paths_first => try argv.append("-search_paths_first"),
3703 .dylibs_first => try argv.append("-search_dylibs_first"),
3704 };
3705
3706 if (options.headerpad_size) |headerpad_size| {3637 if (options.headerpad_size) |headerpad_size| {
3707 try argv.append("-headerpad_size");3638 try argv.append("-headerpad_size");
3708 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));3639 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
src/main.zig+381-134
...@@ -527,11 +527,11 @@ const usage_build_generic =...@@ -527,11 +527,11 @@ const usage_build_generic =
527 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker527 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
528 \\ --stack [size] Override default stack size528 \\ --stack [size] Override default stack size
529 \\ --image-base [addr] Set base address for executable image529 \\ --image-base [addr] Set base address for executable image
530 \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak530 \\ -weak-l[lib] link against system library and mark it and all referenced symbols as weak
531 \\ -weak_library [lib]531 \\ -weak_library [lib]
532 \\ -framework [name] (Darwin) link against framework532 \\ -framework [name] (Darwin) link against framework
533 \\ -needed_framework [name] (Darwin) link against framework (even if unused)533 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
534 \\ -needed_library [lib] (Darwin) link against system library (even if unused)534 \\ -needed_library [lib] link against system library (even if unused)
535 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak535 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
536 \\ -F[dir] (Darwin) add search path for frameworks536 \\ -F[dir] (Darwin) add search path for frameworks
537 \\ -install_name=[value] (Darwin) add dylib's install name537 \\ -install_name=[value] (Darwin) add dylib's install name
...@@ -716,6 +716,27 @@ const ArgsIterator = struct {...@@ -716,6 +716,27 @@ const ArgsIterator = struct {
716 }716 }
717};717};
718718
719/// In contrast to `link.SystemLib`, this stores arguments that may need to be
720/// resolved into static libraries so that we can pass only dynamic libraries
721/// as system libs to `Compilation`.
722const SystemLib = struct {
723 needed: bool,
724 weak: bool,
725
726 preferred_mode: std.builtin.LinkMode,
727 search_strategy: SearchStrategy,
728
729 const SearchStrategy = enum { paths_first, mode_first, no_fallback };
730
731 fn fallbackMode(this: SystemLib) std.builtin.LinkMode {
732 assert(this.search_strategy != .no_fallback);
733 return switch (this.preferred_mode) {
734 .Dynamic => .Static,
735 .Static => .Dynamic,
736 };
737 }
738};
739
719fn buildOutputType(740fn buildOutputType(
720 gpa: Allocator,741 gpa: Allocator,
721 arena: Allocator,742 arena: Allocator,
...@@ -854,7 +875,8 @@ fn buildOutputType(...@@ -854,7 +875,8 @@ fn buildOutputType(
854 var hash_style: link.HashStyle = .both;875 var hash_style: link.HashStyle = .both;
855 var entitlements: ?[]const u8 = null;876 var entitlements: ?[]const u8 = null;
856 var pagezero_size: ?u64 = null;877 var pagezero_size: ?u64 = null;
857 var search_strategy: ?link.File.MachO.SearchStrategy = null;878 var lib_search_strategy: ?SystemLib.SearchStrategy = null;
879 var lib_preferred_mode: ?std.builtin.LinkMode = null;
858 var headerpad_size: ?u32 = null;880 var headerpad_size: ?u32 = null;
859 var headerpad_max_install_names: bool = false;881 var headerpad_max_install_names: bool = false;
860 var dead_strip_dylibs: bool = false;882 var dead_strip_dylibs: bool = false;
...@@ -869,11 +891,7 @@ fn buildOutputType(...@@ -869,11 +891,7 @@ fn buildOutputType(
869 var llvm_m_args = std.ArrayList([]const u8).init(gpa);891 var llvm_m_args = std.ArrayList([]const u8).init(gpa);
870 defer llvm_m_args.deinit();892 defer llvm_m_args.deinit();
871893
872 var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);894 var system_libs = std.StringArrayHashMap(SystemLib).init(arena);
873 defer system_libs.deinit();
874
875 var static_libs = std.ArrayList([]const u8).init(gpa);
876 defer static_libs.deinit();
877895
878 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);896 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);
879 defer wasi_emulated_libs.deinit();897 defer wasi_emulated_libs.deinit();
...@@ -884,8 +902,8 @@ fn buildOutputType(...@@ -884,8 +902,8 @@ fn buildOutputType(
884 var extra_cflags = std.ArrayList([]const u8).init(gpa);902 var extra_cflags = std.ArrayList([]const u8).init(gpa);
885 defer extra_cflags.deinit();903 defer extra_cflags.deinit();
886904
887 var lib_dirs = std.ArrayList([]const u8).init(gpa);905 // These are before resolving sysroot.
888 defer lib_dirs.deinit();906 var lib_dir_args = std.ArrayList([]const u8).init(arena);
889907
890 var rpath_list = std.ArrayList([]const u8).init(gpa);908 var rpath_list = std.ArrayList([]const u8).init(gpa);
891 defer rpath_list.deinit();909 defer rpath_list.deinit();
...@@ -901,7 +919,7 @@ fn buildOutputType(...@@ -901,7 +919,7 @@ fn buildOutputType(
901 var framework_dirs = std.ArrayList([]const u8).init(gpa);919 var framework_dirs = std.ArrayList([]const u8).init(gpa);
902 defer framework_dirs.deinit();920 defer framework_dirs.deinit();
903921
904 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{};922 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};
905923
906 // null means replace with the test executable binary924 // null means replace with the test executable binary
907 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);925 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
...@@ -1061,7 +1079,7 @@ fn buildOutputType(...@@ -1061,7 +1079,7 @@ fn buildOutputType(
1061 } else if (mem.eql(u8, arg, "-rpath")) {1079 } else if (mem.eql(u8, arg, "-rpath")) {
1062 try rpath_list.append(args_iter.nextOrFatal());1080 try rpath_list.append(args_iter.nextOrFatal());
1063 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {1081 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1064 try lib_dirs.append(args_iter.nextOrFatal());1082 try lib_dir_args.append(args_iter.nextOrFatal());
1065 } else if (mem.eql(u8, arg, "-F")) {1083 } else if (mem.eql(u8, arg, "-F")) {
1066 try framework_dirs.append(args_iter.nextOrFatal());1084 try framework_dirs.append(args_iter.nextOrFatal());
1067 } else if (mem.eql(u8, arg, "-framework")) {1085 } else if (mem.eql(u8, arg, "-framework")) {
...@@ -1085,9 +1103,11 @@ fn buildOutputType(...@@ -1085,9 +1103,11 @@ fn buildOutputType(
1085 fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });1103 fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
1086 };1104 };
1087 } else if (mem.eql(u8, arg, "-search_paths_first")) {1105 } else if (mem.eql(u8, arg, "-search_paths_first")) {
1088 search_strategy = .paths_first;1106 lib_search_strategy = .paths_first;
1107 lib_preferred_mode = .Dynamic;
1089 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {1108 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
1090 search_strategy = .dylibs_first;1109 lib_search_strategy = .mode_first;
1110 lib_preferred_mode = .Dynamic;
1091 } else if (mem.eql(u8, arg, "-headerpad")) {1111 } else if (mem.eql(u8, arg, "-headerpad")) {
1092 const next_arg = args_iter.nextOrFatal();1112 const next_arg = args_iter.nextOrFatal();
1093 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {1113 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
...@@ -1104,17 +1124,36 @@ fn buildOutputType(...@@ -1104,17 +1124,36 @@ fn buildOutputType(
1104 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {1124 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
1105 version_script = args_iter.nextOrFatal();1125 version_script = args_iter.nextOrFatal();
1106 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {1126 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
1107 // We don't know whether this library is part of libc or libc++ until1127 // We don't know whether this library is part of libc
1108 // we resolve the target, so we simply append to the list for now.1128 // or libc++ until we resolve the target, so we append
1109 try system_libs.put(args_iter.nextOrFatal(), .{});1129 // to the list for now.
1130 try system_libs.put(args_iter.nextOrFatal(), .{
1131 .needed = false,
1132 .weak = false,
1133 // -l always dynamic links. For static libraries,
1134 // users are expected to use positional arguments
1135 // which are always unambiguous.
1136 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1137 .search_strategy = lib_search_strategy orelse .no_fallback,
1138 });
1110 } else if (mem.eql(u8, arg, "--needed-library") or1139 } else if (mem.eql(u8, arg, "--needed-library") or
1111 mem.eql(u8, arg, "-needed-l") or1140 mem.eql(u8, arg, "-needed-l") or
1112 mem.eql(u8, arg, "-needed_library"))1141 mem.eql(u8, arg, "-needed_library"))
1113 {1142 {
1114 const next_arg = args_iter.nextOrFatal();1143 const next_arg = args_iter.nextOrFatal();
1115 try system_libs.put(next_arg, .{ .needed = true });1144 try system_libs.put(next_arg, .{
1145 .needed = true,
1146 .weak = false,
1147 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1148 .search_strategy = lib_search_strategy orelse .no_fallback,
1149 });
1116 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {1150 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1117 try system_libs.put(args_iter.nextOrFatal(), .{ .weak = true });1151 try system_libs.put(args_iter.nextOrFatal(), .{
1152 .needed = false,
1153 .weak = true,
1154 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1155 .search_strategy = lib_search_strategy orelse .no_fallback,
1156 });
1118 } else if (mem.eql(u8, arg, "-D")) {1157 } else if (mem.eql(u8, arg, "-D")) {
1119 try clang_argv.append(arg);1158 try clang_argv.append(arg);
1120 try clang_argv.append(args_iter.nextOrFatal());1159 try clang_argv.append(args_iter.nextOrFatal());
...@@ -1486,17 +1525,36 @@ fn buildOutputType(...@@ -1486,17 +1525,36 @@ fn buildOutputType(
1486 } else if (mem.startsWith(u8, arg, "-T")) {1525 } else if (mem.startsWith(u8, arg, "-T")) {
1487 linker_script = arg[2..];1526 linker_script = arg[2..];
1488 } else if (mem.startsWith(u8, arg, "-L")) {1527 } else if (mem.startsWith(u8, arg, "-L")) {
1489 try lib_dirs.append(arg[2..]);1528 try lib_dir_args.append(arg[2..]);
1490 } else if (mem.startsWith(u8, arg, "-F")) {1529 } else if (mem.startsWith(u8, arg, "-F")) {
1491 try framework_dirs.append(arg[2..]);1530 try framework_dirs.append(arg[2..]);
1492 } else if (mem.startsWith(u8, arg, "-l")) {1531 } else if (mem.startsWith(u8, arg, "-l")) {
1493 // We don't know whether this library is part of libc or libc++ until1532 // We don't know whether this library is part of libc
1494 // we resolve the target, so we simply append to the list for now.1533 // or libc++ until we resolve the target, so we append
1495 try system_libs.put(arg["-l".len..], .{});1534 // to the list for now.
1535 try system_libs.put(arg["-l".len..], .{
1536 .needed = false,
1537 .weak = false,
1538 // -l always dynamic links. For static libraries,
1539 // users are expected to use positional arguments
1540 // which are always unambiguous.
1541 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1542 .search_strategy = lib_search_strategy orelse .no_fallback,
1543 });
1496 } else if (mem.startsWith(u8, arg, "-needed-l")) {1544 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1497 try system_libs.put(arg["-needed-l".len..], .{ .needed = true });1545 try system_libs.put(arg["-needed-l".len..], .{
1546 .needed = true,
1547 .weak = false,
1548 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1549 .search_strategy = lib_search_strategy orelse .no_fallback,
1550 });
1498 } else if (mem.startsWith(u8, arg, "-weak-l")) {1551 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1499 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });1552 try system_libs.put(arg["-weak-l".len..], .{
1553 .needed = false,
1554 .weak = true,
1555 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1556 .search_strategy = lib_search_strategy orelse .no_fallback,
1557 });
1500 } else if (mem.startsWith(u8, arg, "-D")) {1558 } else if (mem.startsWith(u8, arg, "-D")) {
1501 try clang_argv.append(arg);1559 try clang_argv.append(arg);
1502 } else if (mem.startsWith(u8, arg, "-I")) {1560 } else if (mem.startsWith(u8, arg, "-I")) {
...@@ -1642,9 +1700,22 @@ fn buildOutputType(...@@ -1642,9 +1700,22 @@ fn buildOutputType(
1642 .loption = true,1700 .loption = true,
1643 });1701 });
1644 } else if (force_static_libs) {1702 } else if (force_static_libs) {
1645 try static_libs.append(it.only_arg);1703 try system_libs.put(it.only_arg, .{
1704 .needed = false,
1705 .weak = false,
1706 .preferred_mode = .Static,
1707 .search_strategy = .no_fallback,
1708 });
1646 } else {1709 } else {
1647 try system_libs.put(it.only_arg, .{ .needed = needed });1710 // C compilers are traditionally expected to look
1711 // first for dynamic libraries and then fall back
1712 // to static libraries.
1713 try system_libs.put(it.only_arg, .{
1714 .needed = needed,
1715 .weak = false,
1716 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1717 .search_strategy = lib_search_strategy orelse .paths_first,
1718 });
1648 }1719 }
1649 },1720 },
1650 .ignore => {},1721 .ignore => {},
...@@ -1748,9 +1819,11 @@ fn buildOutputType(...@@ -1748,9 +1819,11 @@ fn buildOutputType(
1748 {1819 {
1749 force_static_libs = true;1820 force_static_libs = true;
1750 } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {1821 } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
1751 search_strategy = .paths_first;1822 lib_search_strategy = .paths_first;
1823 lib_preferred_mode = .Dynamic;
1752 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {1824 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
1753 search_strategy = .dylibs_first;1825 lib_search_strategy = .mode_first;
1826 lib_preferred_mode = .Dynamic;
1754 } else {1827 } else {
1755 try linker_args.append(linker_arg);1828 try linker_args.append(linker_arg);
1756 }1829 }
...@@ -1828,7 +1901,7 @@ fn buildOutputType(...@@ -1828,7 +1901,7 @@ fn buildOutputType(
1828 try linker_args.append("-z");1901 try linker_args.append("-z");
1829 try linker_args.append(it.only_arg);1902 try linker_args.append(it.only_arg);
1830 },1903 },
1831 .lib_dir => try lib_dirs.append(it.only_arg),1904 .lib_dir => try lib_dir_args.append(it.only_arg),
1832 .mcpu => target_mcpu = it.only_arg,1905 .mcpu => target_mcpu = it.only_arg,
1833 .m => try llvm_m_args.append(it.only_arg),1906 .m => try llvm_m_args.append(it.only_arg),
1834 .dep_file => {1907 .dep_file => {
...@@ -1860,7 +1933,12 @@ fn buildOutputType(...@@ -1860,7 +1933,12 @@ fn buildOutputType(
1860 .force_undefined_symbol => {1933 .force_undefined_symbol => {
1861 try force_undefined_symbols.put(gpa, it.only_arg, {});1934 try force_undefined_symbols.put(gpa, it.only_arg, {});
1862 },1935 },
1863 .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),1936 .weak_library => try system_libs.put(it.only_arg, .{
1937 .needed = false,
1938 .weak = true,
1939 .preferred_mode = lib_preferred_mode orelse .Dynamic,
1940 .search_strategy = lib_search_strategy orelse .paths_first,
1941 }),
1864 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),1942 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
1865 .headerpad_max_install_names => headerpad_max_install_names = true,1943 .headerpad_max_install_names => headerpad_max_install_names = true,
1866 .compress_debug_sections => {1944 .compress_debug_sections => {
...@@ -2156,11 +2234,26 @@ fn buildOutputType(...@@ -2156,11 +2234,26 @@ fn buildOutputType(
2156 } else if (mem.eql(u8, arg, "-needed_framework")) {2234 } else if (mem.eql(u8, arg, "-needed_framework")) {
2157 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });2235 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
2158 } else if (mem.eql(u8, arg, "-needed_library")) {2236 } else if (mem.eql(u8, arg, "-needed_library")) {
2159 try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });2237 try system_libs.put(linker_args_it.nextOrFatal(), .{
2238 .weak = false,
2239 .needed = true,
2240 .preferred_mode = lib_preferred_mode orelse .Dynamic,
2241 .search_strategy = lib_search_strategy orelse .paths_first,
2242 });
2160 } else if (mem.startsWith(u8, arg, "-weak-l")) {2243 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2161 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });2244 try system_libs.put(arg["-weak-l".len..], .{
2245 .weak = true,
2246 .needed = false,
2247 .preferred_mode = lib_preferred_mode orelse .Dynamic,
2248 .search_strategy = lib_search_strategy orelse .paths_first,
2249 });
2162 } else if (mem.eql(u8, arg, "-weak_library")) {2250 } else if (mem.eql(u8, arg, "-weak_library")) {
2163 try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });2251 try system_libs.put(linker_args_it.nextOrFatal(), .{
2252 .weak = true,
2253 .needed = false,
2254 .preferred_mode = lib_preferred_mode orelse .Dynamic,
2255 .search_strategy = lib_search_strategy orelse .paths_first,
2256 });
2164 } else if (mem.eql(u8, arg, "-compatibility_version")) {2257 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2165 const compat_version = linker_args_it.nextOrFatal();2258 const compat_version = linker_args_it.nextOrFatal();
2166 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {2259 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
...@@ -2458,40 +2551,63 @@ fn buildOutputType(...@@ -2458,40 +2551,63 @@ fn buildOutputType(
2458 }2551 }
2459 }2552 }
24602553
2554 // Resolve the library path arguments with respect to sysroot.
2555 var lib_dirs = std.ArrayList([]const u8).init(arena);
2556 if (sysroot) |root| {
2557 for (lib_dir_args.items) |dir| {
2558 if (fs.path.isAbsolute(dir)) {
2559 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
2560 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
2561 try lib_dirs.append(full_path);
2562 }
2563 try lib_dirs.append(dir);
2564 }
2565 } else {
2566 lib_dirs = lib_dir_args;
2567 }
2568 lib_dir_args = undefined; // From here we use lib_dirs instead.
2569
2461 // Now that we have target info, we can find out if any of the system libraries2570 // Now that we have target info, we can find out if any of the system libraries
2462 // are part of libc or libc++. We remove them from the list and communicate their2571 // are part of libc or libc++. We remove them from the list and communicate their
2463 // existence via flags instead.2572 // existence via flags instead.
2573 // Similarly, if any libs in this list are statically provided, we omit
2574 // them from the resolved list and populate the link_objects array instead.
2575 var resolved_system_libs: std.MultiArrayList(struct {
2576 name: []const u8,
2577 lib: Compilation.SystemLib,
2578 }) = .{};
2579
2464 {2580 {
2465 // Similarly, if any libs in this list are statically provided, we remove
2466 // them from this list and populate the link_objects array instead.
2467 const sep = fs.path.sep_str;
2468 var test_path = std.ArrayList(u8).init(gpa);2581 var test_path = std.ArrayList(u8).init(gpa);
2469 defer test_path.deinit();2582 defer test_path.deinit();
24702583
2471 var i: usize = 0;2584 var checked_paths = std.ArrayList(u8).init(gpa);
2472 syslib: while (i < system_libs.count()) {2585 defer checked_paths.deinit();
2473 const lib_name = system_libs.keys()[i];
24742586
2587 var failed_libs = std.ArrayList(struct {
2588 name: []const u8,
2589 strategy: SystemLib.SearchStrategy,
2590 checked_paths: []const u8,
2591 preferred_mode: std.builtin.LinkMode,
2592 }).init(arena);
2593
2594 syslib: for (system_libs.keys(), system_libs.values()) |lib_name, info| {
2475 if (target_util.is_libc_lib_name(target_info.target, lib_name)) {2595 if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
2476 link_libc = true;2596 link_libc = true;
2477 system_libs.orderedRemoveAt(i);
2478 continue;2597 continue;
2479 }2598 }
2480 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {2599 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
2481 link_libcpp = true;2600 link_libcpp = true;
2482 system_libs.orderedRemoveAt(i);
2483 continue;2601 continue;
2484 }2602 }
2485 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {2603 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
2486 .none => {},2604 .none => {},
2487 .only_libunwind, .both => {2605 .only_libunwind, .both => {
2488 link_libunwind = true;2606 link_libunwind = true;
2489 system_libs.orderedRemoveAt(i);
2490 continue;2607 continue;
2491 },2608 },
2492 .only_compiler_rt => {2609 .only_compiler_rt => {
2493 std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});2610 std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
2494 system_libs.orderedRemoveAt(i);
2495 continue;2611 continue;
2496 },2612 },
2497 }2613 }
...@@ -2503,53 +2619,150 @@ fn buildOutputType(...@@ -2503,53 +2619,150 @@ fn buildOutputType(
2503 if (target_info.target.os.tag == .wasi) {2619 if (target_info.target.os.tag == .wasi) {
2504 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {2620 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
2505 try wasi_emulated_libs.append(crt_file);2621 try wasi_emulated_libs.append(crt_file);
2506 system_libs.orderedRemoveAt(i);
2507 continue;2622 continue;
2508 }2623 }
2509 }2624 }
25102625
2511 for (lib_dirs.items) |lib_dir_path| {2626 checked_paths.clearRetainingCapacity();
2512 if (cross_target.isDarwin()) break; // Targeting Darwin we let the linker resolve the libraries in the correct order2627
2513 test_path.clearRetainingCapacity();2628 switch (info.search_strategy) {
2514 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{2629 .mode_first, .no_fallback => {
2515 lib_dir_path,2630 // check for preferred mode
2516 target_info.target.libPrefix(),2631 for (lib_dirs.items) |lib_dir_path| {
2517 lib_name,2632 if (try accessLibPath(
2518 target_info.target.staticLibSuffix(),2633 &test_path,
2519 });2634 &checked_paths,
2520 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {2635 lib_dir_path,
2521 error.FileNotFound => continue,2636 lib_name,
2522 else => |e| fatal("unable to search for static library '{s}': {s}", .{2637 target_info.target,
2523 test_path.items, @errorName(e),2638 info.preferred_mode,
2524 }),2639 )) {
2525 };2640 const path = try arena.dupe(u8, test_path.items);
2526 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });2641 switch (info.preferred_mode) {
2527 system_libs.orderedRemoveAt(i);2642 .Static => try link_objects.append(.{ .path = path }),
2528 continue :syslib;2643 .Dynamic => try resolved_system_libs.append(arena, .{
2529 }2644 .name = lib_name,
2645 .lib = .{
2646 .needed = info.needed,
2647 .weak = info.weak,
2648 .path = path,
2649 },
2650 }),
2651 }
2652 continue :syslib;
2653 }
2654 }
2655 // check for fallback mode
2656 if (info.search_strategy == .no_fallback) {
2657 try failed_libs.append(.{
2658 .name = lib_name,
2659 .strategy = info.search_strategy,
2660 .checked_paths = try arena.dupe(u8, checked_paths.items),
2661 .preferred_mode = info.preferred_mode,
2662 });
2663 continue :syslib;
2664 }
2665 for (lib_dirs.items) |lib_dir_path| {
2666 if (try accessLibPath(
2667 &test_path,
2668 &checked_paths,
2669 lib_dir_path,
2670 lib_name,
2671 target_info.target,
2672 info.fallbackMode(),
2673 )) {
2674 const path = try arena.dupe(u8, test_path.items);
2675 switch (info.preferred_mode) {
2676 .Static => try link_objects.append(.{ .path = path }),
2677 .Dynamic => try resolved_system_libs.append(arena, .{
2678 .name = lib_name,
2679 .lib = .{
2680 .needed = info.needed,
2681 .weak = info.weak,
2682 .path = path,
2683 },
2684 }),
2685 }
2686 continue :syslib;
2687 }
2688 }
2689 try failed_libs.append(.{
2690 .name = lib_name,
2691 .strategy = info.search_strategy,
2692 .checked_paths = try arena.dupe(u8, checked_paths.items),
2693 .preferred_mode = info.preferred_mode,
2694 });
2695 continue :syslib;
2696 },
2697 .paths_first => {
2698 for (lib_dirs.items) |lib_dir_path| {
2699 // check for preferred mode
2700 if (try accessLibPath(
2701 &test_path,
2702 &checked_paths,
2703 lib_dir_path,
2704 lib_name,
2705 target_info.target,
2706 info.preferred_mode,
2707 )) {
2708 const path = try arena.dupe(u8, test_path.items);
2709 switch (info.preferred_mode) {
2710 .Static => try link_objects.append(.{ .path = path }),
2711 .Dynamic => try resolved_system_libs.append(arena, .{
2712 .name = lib_name,
2713 .lib = .{
2714 .needed = info.needed,
2715 .weak = info.weak,
2716 .path = path,
2717 },
2718 }),
2719 }
2720 continue :syslib;
2721 }
25302722
2531 // Unfortunately, in the case of MinGW we also need to look for `libfoo.a`.2723 // check for fallback mode
2532 if (target_info.target.isMinGW()) {2724 if (try accessLibPath(
2533 for (lib_dirs.items) |lib_dir_path| {2725 &test_path,
2534 test_path.clearRetainingCapacity();2726 &checked_paths,
2535 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{2727 lib_dir_path,
2536 lib_dir_path, lib_name,2728 lib_name,
2729 target_info.target,
2730 info.fallbackMode(),
2731 )) {
2732 const path = try arena.dupe(u8, test_path.items);
2733 switch (info.preferred_mode) {
2734 .Static => try link_objects.append(.{ .path = path }),
2735 .Dynamic => try resolved_system_libs.append(arena, .{
2736 .name = lib_name,
2737 .lib = .{
2738 .needed = info.needed,
2739 .weak = info.weak,
2740 .path = path,
2741 },
2742 }),
2743 }
2744 continue :syslib;
2745 }
2746 }
2747 try failed_libs.append(.{
2748 .name = lib_name,
2749 .strategy = info.search_strategy,
2750 .checked_paths = try arena.dupe(u8, checked_paths.items),
2751 .preferred_mode = info.preferred_mode,
2537 });2752 });
2538 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2539 error.FileNotFound => continue,
2540 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2541 test_path.items, @errorName(e),
2542 }),
2543 };
2544 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2545 system_libs.orderedRemoveAt(i);
2546 continue :syslib;2753 continue :syslib;
2547 }2754 },
2548 }2755 }
2756 @compileError("unreachable");
2757 }
25492758
2550 std.log.scoped(.cli).debug("depending on system for -l{s}", .{lib_name});2759 if (failed_libs.items.len > 0) {
25512760 for (failed_libs.items) |f| {
2552 i += 1;2761 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
2762 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), f.checked_paths,
2763 });
2764 }
2765 process.exit(1);
2553 }2766 }
2554 }2767 }
2555 // libc++ depends on libc2768 // libc++ depends on libc
...@@ -2576,7 +2789,7 @@ fn buildOutputType(...@@ -2576,7 +2789,7 @@ fn buildOutputType(
2576 }2789 }
25772790
2578 if (sysroot == null and cross_target.isNativeOs() and2791 if (sysroot == null and cross_target.isNativeOs() and
2579 (system_libs.count() != 0 or want_native_include_dirs))2792 (resolved_system_libs.len != 0 or want_native_include_dirs))
2580 {2793 {
2581 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {2794 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
2582 fatal("unable to detect native system paths: {s}", .{@errorName(err)});2795 fatal("unable to detect native system paths: {s}", .{@errorName(err)});
...@@ -2613,54 +2826,8 @@ fn buildOutputType(...@@ -2613,54 +2826,8 @@ fn buildOutputType(
2613 framework_dirs.appendAssumeCapacity(framework_dir);2826 framework_dirs.appendAssumeCapacity(framework_dir);
2614 }2827 }
26152828
2616 for (paths.lib_dirs.items) |lib_dir| {2829 try lib_dirs.appendSlice(paths.lib_dirs.items);
2617 try lib_dirs.append(lib_dir);2830 try rpath_list.appendSlice(paths.rpaths.items);
2618 }
2619 for (paths.rpaths.items) |rpath| {
2620 try rpath_list.append(rpath);
2621 }
2622 }
2623
2624 {
2625 // Resolve static libraries into full paths.
2626 const sep = fs.path.sep_str;
2627
2628 var test_path = std.ArrayList(u8).init(gpa);
2629 defer test_path.deinit();
2630
2631 for (static_libs.items) |static_lib| {
2632 for (lib_dirs.items) |lib_dir_path| {
2633 test_path.clearRetainingCapacity();
2634 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
2635 lib_dir_path,
2636 target_info.target.libPrefix(),
2637 static_lib,
2638 target_info.target.staticLibSuffix(),
2639 });
2640 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2641 error.FileNotFound => continue,
2642 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2643 test_path.items, @errorName(e),
2644 }),
2645 };
2646 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2647 break;
2648 } else {
2649 var search_paths = std.ArrayList(u8).init(arena);
2650 for (lib_dirs.items) |lib_dir_path| {
2651 try search_paths.writer().print("\n {s}" ++ sep ++ "{s}{s}{s}", .{
2652 lib_dir_path,
2653 target_info.target.libPrefix(),
2654 static_lib,
2655 target_info.target.staticLibSuffix(),
2656 });
2657 }
2658 try search_paths.appendSlice("\n suggestion: use full paths to static libraries on the command line rather than using -l and -L arguments");
2659 fatal("static library '{s}' not found. search paths: {s}", .{
2660 static_lib, search_paths.items,
2661 });
2662 }
2663 }
2664 }2831 }
26652832
2666 const object_format = target_info.target.ofmt;2833 const object_format = target_info.target.ofmt;
...@@ -3086,8 +3253,8 @@ fn buildOutputType(...@@ -3086,8 +3253,8 @@ fn buildOutputType(
3086 .link_objects = link_objects.items,3253 .link_objects = link_objects.items,
3087 .framework_dirs = framework_dirs.items,3254 .framework_dirs = framework_dirs.items,
3088 .frameworks = frameworks,3255 .frameworks = frameworks,
3089 .system_lib_names = system_libs.keys(),3256 .system_lib_names = resolved_system_libs.items(.name),
3090 .system_lib_infos = system_libs.values(),3257 .system_lib_infos = resolved_system_libs.items(.lib),
3091 .wasi_emulated_libs = wasi_emulated_libs.items,3258 .wasi_emulated_libs = wasi_emulated_libs.items,
3092 .link_libc = link_libc,3259 .link_libc = link_libc,
3093 .link_libcpp = link_libcpp,3260 .link_libcpp = link_libcpp,
...@@ -3196,7 +3363,6 @@ fn buildOutputType(...@@ -3196,7 +3363,6 @@ fn buildOutputType(
3196 .install_name = install_name,3363 .install_name = install_name,
3197 .entitlements = entitlements,3364 .entitlements = entitlements,
3198 .pagezero_size = pagezero_size,3365 .pagezero_size = pagezero_size,
3199 .search_strategy = search_strategy,
3200 .headerpad_size = headerpad_size,3366 .headerpad_size = headerpad_size,
3201 .headerpad_max_install_names = headerpad_max_install_names,3367 .headerpad_max_install_names = headerpad_max_install_names,
3202 .dead_strip_dylibs = dead_strip_dylibs,3368 .dead_strip_dylibs = dead_strip_dylibs,
...@@ -6068,3 +6234,84 @@ fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {...@@ -6068,3 +6234,84 @@ fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
6068 .include_reference_trace = ttyconf != .no_color,6234 .include_reference_trace = ttyconf != .no_color,
6069 };6235 };
6070}6236}
6237
6238fn accessLibPath(
6239 test_path: *std.ArrayList(u8),
6240 checked_paths: *std.ArrayList(u8),
6241 lib_dir_path: []const u8,
6242 lib_name: []const u8,
6243 target: std.Target,
6244 link_mode: std.builtin.LinkMode,
6245) !bool {
6246 const sep = fs.path.sep_str;
6247
6248 if (target.isDarwin() and link_mode == .Dynamic) tbd: {
6249 // Prefer .tbd over .dylib.
6250 test_path.clearRetainingCapacity();
6251 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
6252 try checked_paths.writer().print("\n {s}", .{test_path.items});
6253 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6254 error.FileNotFound => break :tbd,
6255 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6256 test_path.items, @errorName(e),
6257 }),
6258 };
6259 return true;
6260 }
6261
6262 main_check: {
6263 test_path.clearRetainingCapacity();
6264 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
6265 lib_dir_path,
6266 target.libPrefix(),
6267 lib_name,
6268 switch (link_mode) {
6269 .Static => target.staticLibSuffix(),
6270 .Dynamic => target.dynamicLibSuffix(),
6271 },
6272 });
6273 try checked_paths.writer().print("\n {s}", .{test_path.items});
6274 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6275 error.FileNotFound => break :main_check,
6276 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6277 @tagName(link_mode), test_path.items, @errorName(e),
6278 }),
6279 };
6280 return true;
6281 }
6282
6283 // In the case of Darwin, the main check will be .dylib, so here we
6284 // additionally check for .so files.
6285 if (target.isDarwin() and link_mode == .Dynamic) so: {
6286 // Prefer .tbd over .dylib.
6287 test_path.clearRetainingCapacity();
6288 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
6289 try checked_paths.writer().print("\n {s}", .{test_path.items});
6290 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6291 error.FileNotFound => break :so,
6292 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6293 test_path.items, @errorName(e),
6294 }),
6295 };
6296 return true;
6297 }
6298
6299 // In the case of MinGW, the main check will be .lib but we also need to
6300 // look for `libfoo.a`.
6301 if (target.isMinGW() and link_mode == .Static) mingw: {
6302 test_path.clearRetainingCapacity();
6303 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
6304 lib_dir_path, lib_name,
6305 });
6306 try checked_paths.writer().print("\n {s}", .{test_path.items});
6307 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6308 error.FileNotFound => break :mingw,
6309 else => |e| fatal("unable to search for static library '{s}': {s}", .{
6310 test_path.items, @errorName(e),
6311 }),
6312 };
6313 return true;
6314 }
6315
6316 return false;
6317}