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 {
448448 stdout,
449449};
450450
451pub const Framework = link.Framework;
451452pub const SystemLib = link.SystemLib;
452453pub const CacheMode = link.CacheMode;
453454
......@@ -505,7 +506,7 @@ pub const InitOptions = struct {
505506 c_source_files: []const CSourceFile = &[0]CSourceFile{},
506507 link_objects: []LinkObject = &[0]LinkObject{},
507508 framework_dirs: []const []const u8 = &[0][]const u8{},
508 frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
509 frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},
509510 system_lib_names: []const []const u8 = &.{},
510511 system_lib_infos: []const SystemLib = &.{},
511512 /// These correspond to the WASI libc emulated subcomponents including:
......@@ -644,8 +645,6 @@ pub const InitOptions = struct {
644645 entitlements: ?[]const u8 = null,
645646 /// (Darwin) size of the __PAGEZERO segment
646647 pagezero_size: ?u64 = null,
647 /// (Darwin) search strategy for system libraries
648 search_strategy: ?link.File.MachO.SearchStrategy = null,
649648 /// (Darwin) set minimum space for future expansion of the load commands
650649 headerpad_size: ?u32 = null,
651650 /// (Darwin) set enough space as if all paths were MATPATHLEN
......@@ -1567,7 +1566,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15671566 .install_name = options.install_name,
15681567 .entitlements = options.entitlements,
15691568 .pagezero_size = options.pagezero_size,
1570 .search_strategy = options.search_strategy,
15711569 .headerpad_size = options.headerpad_size,
15721570 .headerpad_max_install_names = options.headerpad_max_install_names,
15731571 .dead_strip_dylibs = options.dead_strip_dylibs,
......@@ -1727,15 +1725,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17271725
17281726 // When linking mingw-w64 there are some import libs we always need.
17291727 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 });
17311733 }
17321734 }
17331735 // Generate Windows import libs.
17341736 if (target.os.tag == .windows) {
17351737 const count = comp.bin_file.options.system_libs.count();
17361738 try comp.work_queue.ensureUnusedCapacity(count);
1737 var i: usize = 0;
1738 while (i < count) : (i += 1) {
1739 for (0..count) |i| {
17391740 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
17401741 }
17411742 }
......@@ -2377,7 +2378,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23772378 }
23782379 man.hash.addOptionalBytes(comp.bin_file.options.soname);
23792380 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);
23812382 man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys());
23822383 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);
23832384 man.hash.add(comp.bin_file.options.bind_global_refs_locally);
......@@ -2395,10 +2396,9 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23952396
23962397 // Mach-O specific stuff
23972398 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);
23992400 try man.addOptionalFile(comp.bin_file.options.entitlements);
24002401 man.hash.addOptional(comp.bin_file.options.pagezero_size);
2401 man.hash.addOptional(comp.bin_file.options.search_strategy);
24022402 man.hash.addOptional(comp.bin_file.options.headerpad_size);
24032403 man.hash.add(comp.bin_file.options.headerpad_max_install_names);
24042404 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;
2121const TypedValue = @import("TypedValue.zig");
2222
2323/// When adding a new field, remember to update `hashAddSystemLibs`.
24/// These are *always* dynamically linked. Static libraries will be
25/// provided as positional arguments.
2426pub 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 {
2534 needed: bool = false,
2635 weak: bool = false,
2736};
......@@ -31,11 +40,23 @@ pub const SortSection = enum { name, alignment };
3140pub const CacheMode = enum { incremental, whole };
3241
3342pub fn hashAddSystemLibs(
34 hh: *Cache.HashHelper,
43 man: *Cache.Manifest,
3544 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),
3658) void {
3759 const keys = hm.keys();
38 hh.add(keys.len);
3960 hh.addListOfBytes(keys);
4061 for (hm.values()) |value| {
4162 hh.add(value.needed);
......@@ -183,9 +204,12 @@ pub const Options = struct {
183204
184205 objects: []Compilation.LinkObject,
185206 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.
187210 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
188211 wasi_emulated_libs: []const wasi_libc.CRTFile,
212 // TODO: remove this. libraries are resolved by the frontend.
189213 lib_dirs: []const []const u8,
190214 rpath_list: []const []const u8,
191215
......@@ -225,9 +249,6 @@ pub const Options = struct {
225249 /// (Darwin) size of the __PAGEZERO segment
226250 pagezero_size: ?u64 = null,
227251
228 /// (Darwin) search strategy for system libraries
229 search_strategy: ?File.MachO.SearchStrategy = null,
230
231252 /// (Darwin) set minimum space for future expansion of the load commands
232253 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
8888 }
8989 }
9090 }
91 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
91 try link.hashAddSystemLibs(&man, self.base.options.system_libs);
9292 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
9393 man.hash.addOptional(self.base.options.subsystem);
9494 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
14281428 }
14291429 man.hash.addOptionalBytes(self.base.options.soname);
14301430 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);
14321432 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
14331433 man.hash.add(allow_shlib_undefined);
14341434 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
18241824 argv.appendAssumeCapacity("--as-needed");
18251825 var as_needed = true;
18261826
1827 for (system_libs, 0..) |link_lib, i| {
1828 const lib_as_needed = !system_libs_values[i].needed;
1827 for (system_libs_values) |lib_info| {
1828 const lib_as_needed = !lib_info.needed;
18291829 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
18301830 0b00, 0b11 => {},
18311831 0b01 => {
......@@ -1842,9 +1842,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
18421842 // libraries and not static libraries (the check for that needs to be earlier),
18431843 // but they could be full paths to .so files, in which case we
18441844 // want to avoid prepending "-l".
1845 const ext = Compilation.classifyFileExt(link_lib);
1846 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1847 argv.appendAssumeCapacity(arg);
1845 argv.appendAssumeCapacity(lib_info.path);
18481846 }
18491847
18501848 if (!as_needed) {
src/link/MachO.zig+20-9
......@@ -58,11 +58,6 @@ const Rebase = @import("MachO/dyld_info/Rebase.zig");
5858
5959pub const base_tag: File.Tag = File.Tag.macho;
6060
61pub const SearchStrategy = enum {
62 paths_first,
63 dylibs_first,
64};
65
6661/// Mode of operation of the linker.
6762pub const Mode = enum {
6863 /// Incremental mode will preallocate segments/sections and is compatible with
......@@ -840,7 +835,11 @@ pub fn resolveLibSystem(
840835 // re-exports every single symbol definition.
841836 for (search_dirs) |dir| {
842837 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 });
844843 libsystem_available = true;
845844 break :blk;
846845 }
......@@ -850,8 +849,16 @@ pub fn resolveLibSystem(
850849 for (search_dirs) |dir| {
851850 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
852851 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
853 try out_libs.put(libsystem_path, .{ .needed = true });
854 try out_libs.put(libc_path, .{ .needed = true });
852 try out_libs.put(libsystem_path, .{
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 });
855862 libsystem_available = true;
856863 break :blk;
857864 }
......@@ -865,7 +872,11 @@ pub fn resolveLibSystem(
865872 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
866873 "libc", "darwin", libsystem_name,
867874 });
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 });
869880 }
870881}
871882
src/link/MachO/zld.zig+11-80
......@@ -3410,7 +3410,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
34103410 // installation sources because they are always a product of the compiler version + target information.
34113411 man.hash.add(stack_size);
34123412 man.hash.addOptional(options.pagezero_size);
3413 man.hash.addOptional(options.search_strategy);
34143413 man.hash.addOptional(options.headerpad_size);
34153414 man.hash.add(options.headerpad_max_install_names);
34163415 man.hash.add(gc_sections);
......@@ -3418,13 +3417,13 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
34183417 man.hash.add(options.strip);
34193418 man.hash.addListOfBytes(options.lib_dirs);
34203419 man.hash.addListOfBytes(options.framework_dirs);
3421 link.hashAddSystemLibs(&man.hash, options.frameworks);
3420 link.hashAddFrameworks(&man.hash, options.frameworks);
34223421 man.hash.addListOfBytes(options.rpath_list);
34233422 if (is_dyn_lib) {
34243423 man.hash.addOptionalBytes(options.install_name);
34253424 man.hash.addOptional(options.version);
34263425 }
3427 link.hashAddSystemLibs(&man.hash, options.system_libs);
3426 try link.hashAddSystemLibs(&man, options.system_libs);
34283427 man.hash.addOptionalBytes(options.sysroot);
34293428 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
34303429 try man.addOptionalFile(options.entitlements);
......@@ -3550,84 +3549,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35503549 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
35513550 }
35523551
3553 // Shared and static libraries passed via `-l` flag.
3554 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3555
3556 const system_lib_names = options.system_libs.keys();
3557 for (system_lib_names) |system_lib_name| {
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 }
3552 {
3553 // Add all system library paths to positionals.
3554 const vals = options.system_libs.values();
3555 try positionals.ensureUnusedCapacity(vals.len);
3556 for (vals) |info| positionals.appendAssumeCapacity(info.path);
35803557 }
35813558
35823559 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
35833560
3584 // Assume ld64 default -search_paths_first if no strategy specified.
3585 const search_strategy = options.search_strategy orelse .paths_first;
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 }
3561 for (options.system_libs.values()) |v| {
3562 try libs.put(v.path, v);
36283563 }
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
36323567 // frameworks
36333568 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
36473582 try libs.put(full_path, .{
36483583 .needed = info.needed,
36493584 .weak = info.weak,
3585 .path = full_path,
36503586 });
36513587 continue :outer;
36523588 }
......@@ -3698,11 +3634,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
36983634 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
36993635 }
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
37063637 if (options.headerpad_size) |headerpad_size| {
37073638 try argv.append("-headerpad_size");
37083639 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 =
527527 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
528528 \\ --stack [size] Override default stack size
529529 \\ --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 weak
530 \\ -weak-l[lib] link against system library and mark it and all referenced symbols as weak
531531 \\ -weak_library [lib]
532532 \\ -framework [name] (Darwin) link against framework
533533 \\ -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)
535535 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
536536 \\ -F[dir] (Darwin) add search path for frameworks
537537 \\ -install_name=[value] (Darwin) add dylib's install name
......@@ -716,6 +716,27 @@ const ArgsIterator = struct {
716716 }
717717};
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
719740fn buildOutputType(
720741 gpa: Allocator,
721742 arena: Allocator,
......@@ -854,7 +875,8 @@ fn buildOutputType(
854875 var hash_style: link.HashStyle = .both;
855876 var entitlements: ?[]const u8 = null;
856877 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;
858880 var headerpad_size: ?u32 = null;
859881 var headerpad_max_install_names: bool = false;
860882 var dead_strip_dylibs: bool = false;
......@@ -869,11 +891,7 @@ fn buildOutputType(
869891 var llvm_m_args = std.ArrayList([]const u8).init(gpa);
870892 defer llvm_m_args.deinit();
871893
872 var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);
873 defer system_libs.deinit();
874
875 var static_libs = std.ArrayList([]const u8).init(gpa);
876 defer static_libs.deinit();
894 var system_libs = std.StringArrayHashMap(SystemLib).init(arena);
877895
878896 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);
879897 defer wasi_emulated_libs.deinit();
......@@ -884,8 +902,8 @@ fn buildOutputType(
884902 var extra_cflags = std.ArrayList([]const u8).init(gpa);
885903 defer extra_cflags.deinit();
886904
887 var lib_dirs = std.ArrayList([]const u8).init(gpa);
888 defer lib_dirs.deinit();
905 // These are before resolving sysroot.
906 var lib_dir_args = std.ArrayList([]const u8).init(arena);
889907
890908 var rpath_list = std.ArrayList([]const u8).init(gpa);
891909 defer rpath_list.deinit();
......@@ -901,7 +919,7 @@ fn buildOutputType(
901919 var framework_dirs = std.ArrayList([]const u8).init(gpa);
902920 defer framework_dirs.deinit();
903921
904 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{};
922 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};
905923
906924 // null means replace with the test executable binary
907925 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
......@@ -1061,7 +1079,7 @@ fn buildOutputType(
10611079 } else if (mem.eql(u8, arg, "-rpath")) {
10621080 try rpath_list.append(args_iter.nextOrFatal());
10631081 } 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());
10651083 } else if (mem.eql(u8, arg, "-F")) {
10661084 try framework_dirs.append(args_iter.nextOrFatal());
10671085 } else if (mem.eql(u8, arg, "-framework")) {
......@@ -1085,9 +1103,11 @@ fn buildOutputType(
10851103 fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
10861104 };
10871105 } 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;
10891108 } 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;
10911111 } else if (mem.eql(u8, arg, "-headerpad")) {
10921112 const next_arg = args_iter.nextOrFatal();
10931113 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
......@@ -1104,17 +1124,36 @@ fn buildOutputType(
11041124 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
11051125 version_script = args_iter.nextOrFatal();
11061126 } 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++ until
1108 // we resolve the target, so we simply append to the list for now.
1109 try system_libs.put(args_iter.nextOrFatal(), .{});
1127 // We don't know whether this library is part of libc
1128 // or libc++ until we resolve the target, so we append
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 });
11101139 } else if (mem.eql(u8, arg, "--needed-library") or
11111140 mem.eql(u8, arg, "-needed-l") or
11121141 mem.eql(u8, arg, "-needed_library"))
11131142 {
11141143 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 });
11161150 } 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 });
11181157 } else if (mem.eql(u8, arg, "-D")) {
11191158 try clang_argv.append(arg);
11201159 try clang_argv.append(args_iter.nextOrFatal());
......@@ -1486,17 +1525,36 @@ fn buildOutputType(
14861525 } else if (mem.startsWith(u8, arg, "-T")) {
14871526 linker_script = arg[2..];
14881527 } else if (mem.startsWith(u8, arg, "-L")) {
1489 try lib_dirs.append(arg[2..]);
1528 try lib_dir_args.append(arg[2..]);
14901529 } else if (mem.startsWith(u8, arg, "-F")) {
14911530 try framework_dirs.append(arg[2..]);
14921531 } else if (mem.startsWith(u8, arg, "-l")) {
1493 // We don't know whether this library is part of libc or libc++ until
1494 // we resolve the target, so we simply append to the list for now.
1495 try system_libs.put(arg["-l".len..], .{});
1532 // We don't know whether this library is part of libc
1533 // or libc++ until we resolve the target, so we append
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 });
14961544 } 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 });
14981551 } 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 });
15001558 } else if (mem.startsWith(u8, arg, "-D")) {
15011559 try clang_argv.append(arg);
15021560 } else if (mem.startsWith(u8, arg, "-I")) {
......@@ -1642,9 +1700,22 @@ fn buildOutputType(
16421700 .loption = true,
16431701 });
16441702 } 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 });
16461709 } 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 });
16481719 }
16491720 },
16501721 .ignore => {},
......@@ -1748,9 +1819,11 @@ fn buildOutputType(
17481819 {
17491820 force_static_libs = true;
17501821 } 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;
17521824 } 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;
17541827 } else {
17551828 try linker_args.append(linker_arg);
17561829 }
......@@ -1828,7 +1901,7 @@ fn buildOutputType(
18281901 try linker_args.append("-z");
18291902 try linker_args.append(it.only_arg);
18301903 },
1831 .lib_dir => try lib_dirs.append(it.only_arg),
1904 .lib_dir => try lib_dir_args.append(it.only_arg),
18321905 .mcpu => target_mcpu = it.only_arg,
18331906 .m => try llvm_m_args.append(it.only_arg),
18341907 .dep_file => {
......@@ -1860,7 +1933,12 @@ fn buildOutputType(
18601933 .force_undefined_symbol => {
18611934 try force_undefined_symbols.put(gpa, it.only_arg, {});
18621935 },
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 }),
18641942 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
18651943 .headerpad_max_install_names => headerpad_max_install_names = true,
18661944 .compress_debug_sections => {
......@@ -2156,11 +2234,26 @@ fn buildOutputType(
21562234 } else if (mem.eql(u8, arg, "-needed_framework")) {
21572235 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
21582236 } 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 });
21602243 } 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 });
21622250 } 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 });
21642257 } else if (mem.eql(u8, arg, "-compatibility_version")) {
21652258 const compat_version = linker_args_it.nextOrFatal();
21662259 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
......@@ -2458,40 +2551,63 @@ fn buildOutputType(
24582551 }
24592552 }
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
24612570 // Now that we have target info, we can find out if any of the system libraries
24622571 // are part of libc or libc++. We remove them from the list and communicate their
24632572 // 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
24642580 {
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;
24682581 var test_path = std.ArrayList(u8).init(gpa);
24692582 defer test_path.deinit();
24702583
2471 var i: usize = 0;
2472 syslib: while (i < system_libs.count()) {
2473 const lib_name = system_libs.keys()[i];
2584 var checked_paths = std.ArrayList(u8).init(gpa);
2585 defer checked_paths.deinit();
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| {
24752595 if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
24762596 link_libc = true;
2477 system_libs.orderedRemoveAt(i);
24782597 continue;
24792598 }
24802599 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
24812600 link_libcpp = true;
2482 system_libs.orderedRemoveAt(i);
24832601 continue;
24842602 }
24852603 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
24862604 .none => {},
24872605 .only_libunwind, .both => {
24882606 link_libunwind = true;
2489 system_libs.orderedRemoveAt(i);
24902607 continue;
24912608 },
24922609 .only_compiler_rt => {
24932610 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);
24952611 continue;
24962612 },
24972613 }
......@@ -2503,53 +2619,150 @@ fn buildOutputType(
25032619 if (target_info.target.os.tag == .wasi) {
25042620 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
25052621 try wasi_emulated_libs.append(crt_file);
2506 system_libs.orderedRemoveAt(i);
25072622 continue;
25082623 }
25092624 }
25102625
2511 for (lib_dirs.items) |lib_dir_path| {
2512 if (cross_target.isDarwin()) break; // Targeting Darwin we let the linker resolve the libraries in the correct order
2513 test_path.clearRetainingCapacity();
2514 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
2515 lib_dir_path,
2516 target_info.target.libPrefix(),
2517 lib_name,
2518 target_info.target.staticLibSuffix(),
2519 });
2520 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2521 error.FileNotFound => continue,
2522 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2523 test_path.items, @errorName(e),
2524 }),
2525 };
2526 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2527 system_libs.orderedRemoveAt(i);
2528 continue :syslib;
2529 }
2626 checked_paths.clearRetainingCapacity();
2627
2628 switch (info.search_strategy) {
2629 .mode_first, .no_fallback => {
2630 // check for preferred mode
2631 for (lib_dirs.items) |lib_dir_path| {
2632 if (try accessLibPath(
2633 &test_path,
2634 &checked_paths,
2635 lib_dir_path,
2636 lib_name,
2637 target_info.target,
2638 info.preferred_mode,
2639 )) {
2640 const path = try arena.dupe(u8, test_path.items);
2641 switch (info.preferred_mode) {
2642 .Static => try link_objects.append(.{ .path = path }),
2643 .Dynamic => try resolved_system_libs.append(arena, .{
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`.
2532 if (target_info.target.isMinGW()) {
2533 for (lib_dirs.items) |lib_dir_path| {
2534 test_path.clearRetainingCapacity();
2535 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
2536 lib_dir_path, lib_name,
2723 // check for fallback mode
2724 if (try accessLibPath(
2725 &test_path,
2726 &checked_paths,
2727 lib_dir_path,
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,
25372752 });
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);
25462753 continue :syslib;
2547 }
2754 },
25482755 }
2756 @compileError("unreachable");
2757 }
25492758
2550 std.log.scoped(.cli).debug("depending on system for -l{s}", .{lib_name});
2551
2552 i += 1;
2759 if (failed_libs.items.len > 0) {
2760 for (failed_libs.items) |f| {
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);
25532766 }
25542767 }
25552768 // libc++ depends on libc
......@@ -2576,7 +2789,7 @@ fn buildOutputType(
25762789 }
25772790
25782791 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))
25802793 {
25812794 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
25822795 fatal("unable to detect native system paths: {s}", .{@errorName(err)});
......@@ -2613,54 +2826,8 @@ fn buildOutputType(
26132826 framework_dirs.appendAssumeCapacity(framework_dir);
26142827 }
26152828
2616 for (paths.lib_dirs.items) |lib_dir| {
2617 try lib_dirs.append(lib_dir);
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 }
2829 try lib_dirs.appendSlice(paths.lib_dirs.items);
2830 try rpath_list.appendSlice(paths.rpaths.items);
26642831 }
26652832
26662833 const object_format = target_info.target.ofmt;
......@@ -3086,8 +3253,8 @@ fn buildOutputType(
30863253 .link_objects = link_objects.items,
30873254 .framework_dirs = framework_dirs.items,
30883255 .frameworks = frameworks,
3089 .system_lib_names = system_libs.keys(),
3090 .system_lib_infos = system_libs.values(),
3256 .system_lib_names = resolved_system_libs.items(.name),
3257 .system_lib_infos = resolved_system_libs.items(.lib),
30913258 .wasi_emulated_libs = wasi_emulated_libs.items,
30923259 .link_libc = link_libc,
30933260 .link_libcpp = link_libcpp,
......@@ -3196,7 +3363,6 @@ fn buildOutputType(
31963363 .install_name = install_name,
31973364 .entitlements = entitlements,
31983365 .pagezero_size = pagezero_size,
3199 .search_strategy = search_strategy,
32003366 .headerpad_size = headerpad_size,
32013367 .headerpad_max_install_names = headerpad_max_install_names,
32023368 .dead_strip_dylibs = dead_strip_dylibs,
......@@ -6068,3 +6234,84 @@ fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
60686234 .include_reference_trace = ttyconf != .no_color,
60696235 };
60706236}
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}