authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-24 13:07:38+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-26 18:21:21+01:00
logcdfeda8f0d7734dc3930caf3363a5f227773c6a1
tree39ac78d0bc642ca0b6b99e8a68173132708d4a2d
parent5125399764909dcf19e861806e8a73688121461d
signature Commit is signed but in an unrecognized format.

compiler: move Darwin framework resolution to the frontend

There's not much to say about this; it's essentially just another way of specifying link libraries. Framework directories are handled nearly identically to library directories, and frameworks are resolved in `link.resolveInputs` just like other link libraries. The new function `link.resolveFrameworkInput` is responsible for trying to find the framework's tbd or dylib file. As in 41d3b9a3e, if the framework contains a file with no extension, we will attempt to figure out whether it is a dylib, static archive, or tbd---this logic is quite similar to how we handle GNU ld scripts.

7 files changed, 411 insertions(+), 338 deletions(-)

lib/std/macho.zig+16-8
......@@ -1145,16 +1145,16 @@ pub const LC = enum(u32) {
11451145};
11461146
11471147/// the mach magic number
1148pub const MH_MAGIC = 0xfeedface;
1148pub const MH_MAGIC: u32 = 0xfeedface;
11491149
11501150/// NXSwapInt(MH_MAGIC)
1151pub const MH_CIGAM = 0xcefaedfe;
1151pub const MH_CIGAM: u32 = 0xcefaedfe;
11521152
11531153/// the 64-bit mach magic number
1154pub const MH_MAGIC_64 = 0xfeedfacf;
1154pub const MH_MAGIC_64: u32 = 0xfeedfacf;
11551155
11561156/// NXSwapInt(MH_MAGIC_64)
1157pub const MH_CIGAM_64 = 0xcffaedfe;
1157pub const MH_CIGAM_64: u32 = 0xcffaedfe;
11581158
11591159/// relocatable object file
11601160pub const MH_OBJECT = 0x1;
......@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;
12811281// Constants for the flags field of the fat_header
12821282
12831283/// the fat magic number
1284pub const FAT_MAGIC = 0xcafebabe;
1284pub const FAT_MAGIC: u32 = 0xcafebabe;
12851285
12861286/// NXSwapLong(FAT_MAGIC)
1287pub const FAT_CIGAM = 0xbebafeca;
1287pub const FAT_CIGAM: u32 = 0xbebafeca;
12881288
12891289/// the 64-bit fat magic number
1290pub const FAT_MAGIC_64 = 0xcafebabf;
1290pub const FAT_MAGIC_64: u32 = 0xcafebabf;
12911291
12921292/// NXSwapLong(FAT_MAGIC_64)
1293pub const FAT_CIGAM_64 = 0xbfbafeca;
1293pub const FAT_CIGAM_64: u32 = 0xbfbafeca;
12941294
12951295/// Segment flags
12961296/// The file contents for this segment is for the high part of the VM space, the low part
......@@ -2212,3 +2212,11 @@ pub const CompactUnwindEncoding = packed struct(u32) {
22122212 has_lsda: u1,
22132213 start: u1,
22142214};
2215
2216// Archive files start with the ARMAG identifying string. Then follows a
2217// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
2218// member indicates, for each member file.
2219/// String that begins an archive file.
2220pub const ARMAG = "!<arch>\n";
2221/// String in ar_fmag at the end of each header.
2222pub const ARFMAG = "`\n";
src/Compilation.zig+8-10
......@@ -83,7 +83,7 @@ zigc_strat: RtStrat,
8383/// Resolved into known paths, any GNU ld scripts already resolved.
8484link_inputs: []const link.Input,
8585/// Needed only for passing -F args to clang.
86framework_dirs: []const []const u8,
86framework_dirs: []const Cache.Directory,
8787/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
8888/// with Zig. Static libraries are provided as `link.Input` values.
8989windows_libs: std.array_hash_map.String(void),
......@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {
13501350 version,
13511351};
13521352
1353pub const Framework = link.File.MachO.Framework;
13541353pub const SystemLib = link.SystemLib;
13551354
13561355pub const CacheMode = enum {
......@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {
14871486 /// The ELF implementation no longer uses this data, however the MachO and COFF
14881487 /// implementations still do.
14891488 lib_directories: []const Cache.Directory = &.{},
1489 framework_directories: []const Cache.Directory = &.{},
14901490 rpath_list: []const []const u8 = &[0][]const u8{},
14911491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,
14921492 c_source_files: []const CSourceFile = &.{},
......@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {
14941494 manifest_file: ?[]const u8 = null,
14951495 rc_includes: std.zig.RcIncludes = .any,
14961496 link_inputs: []const link.Input = &.{},
1497 framework_dirs: []const []const u8 = &[0][]const u8{},
1498 frameworks: []const Framework = &.{},
14991497 windows_lib_names: []const []const u8 = &.{},
15001498 /// This means that if the output mode is an executable it will be a
15011499 /// Position Independent Executable. If the output mode is not an
......@@ -2146,7 +2144,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
21462144 .ubsan_rt_strat = ubsan_rt_strat,
21472145 .zigc_strat = zigc_strat,
21482146 .link_inputs = options.link_inputs,
2149 .framework_dirs = options.framework_dirs,
2147 .framework_dirs = options.framework_directories,
21502148 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
21512149 .skip_linker_dependencies = options.skip_linker_dependencies,
21522150 .queued_jobs = .{},
......@@ -2208,9 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
22082206 .z_relro = options.linker_z_relro,
22092207 .z_common_page_size = options.linker_z_common_page_size,
22102208 .z_max_page_size = options.linker_z_max_page_size,
2211 .frameworks = options.frameworks,
22122209 .lib_directories = options.lib_directories,
2213 .framework_dirs = options.framework_dirs,
22142210 .rpath_list = options.rpath_list,
22152211 .symbol_wrap_set = options.symbol_wrap_set,
22162212 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),
......@@ -3416,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
34163412 man.hash.add(comp.zigc_strat);
34173413 man.hash.add(comp.rc_includes);
34183414 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
3419 man.hash.addListOfBytes(comp.framework_dirs);
3415 man.hash.add(comp.framework_dirs.len);
3416 for (comp.framework_dirs) |framework_dir| {
3417 man.hash.addBytes(framework_dir.path orelse ".");
3418 }
34203419 man.hash.addListOfBytes(comp.windows_libs.keys());
34213420
34223421 man.hash.addListOfBytes(comp.global_cc_argv);
......@@ -3482,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
34823481 man.hash.add(opts.growable_table);
34833482
34843483 // Mach-O specific stuff
3485 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
34863484 try man.addInputPathOptional(opts.entitlements, .{});
34873485 man.hash.addOptional(opts.pagezero_size);
34883486 man.hash.addOptional(opts.headerpad_size);
......@@ -6460,7 +6458,7 @@ fn addCommonCCArgs(
64606458
64616459 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
64626460 for (comp.framework_dirs) |framework_dir| {
6463 try argv.appendSlice(&.{ "-F", framework_dir });
6461 try argv.appendSlice(&.{ "-F", framework_dir.path orelse "." });
64646462 }
64656463 }
64666464 }
src/link.zig+246-94
......@@ -481,7 +481,6 @@ pub const File = struct {
481481
482482 // TODO: remove this. libraries are resolved by the frontend.
483483 lib_directories: []const Directory,
484 framework_dirs: []const []const u8,
485484 rpath_list: []const []const u8,
486485
487486 /// Zig compiler development linker flags.
......@@ -501,7 +500,6 @@ pub const File = struct {
501500 headerpad_max_install_names: bool,
502501 /// Remove dylibs that are unreachable by the entry point or exported symbols
503502 dead_strip_dylibs: bool,
504 frameworks: []const MachO.Framework,
505503 /// Force load all members of static archives that implement an
506504 /// Objective-C class or category
507505 force_load_objc: bool,
......@@ -1958,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {
19581956 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
19591957 /// Who knows! Fuck around and find out.
19601958 ambiguous_name: AmbiguousNameQuery,
1959 framework_query: FrameworkQuery,
19611960
19621961 pub const NameQuery = struct {
19631962 name: []const u8,
......@@ -1996,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {
19961995 }
19971996 };
19981997
1998 pub const FrameworkQuery = struct {
1999 name: []const u8,
2000 needed: bool,
2001 weak: bool,
2002 };
2003
19992004 pub const SearchStrategy = enum {
20002005 paths_first,
20012006 mode_first,
......@@ -2118,6 +2123,7 @@ pub fn resolveInputs(
21182123 /// Allocated with `gpa`.
21192124 resolved_inputs: *std.ArrayList(Input),
21202125 lib_directories: []const Cache.Directory,
2126 framework_directories: []const Cache.Directory,
21212127 color: std.zig.Color,
21222128) Allocator.Error!void {
21232129 var checked_paths: std.ArrayList(u8) = .empty;
......@@ -2129,6 +2135,7 @@ pub fn resolveInputs(
21292135 var archive_dedup: ArchiveDedupMap = .empty;
21302136 defer archive_dedup.deinit(gpa);
21312137
2138 // Allocated with `arena`.
21322139 var failed_libs: std.ArrayList(struct {
21332140 name: []const u8,
21342141 strategy: UnresolvedInput.SearchStrategy,
......@@ -2136,6 +2143,12 @@ pub fn resolveInputs(
21362143 preferred_mode: std.lang.LinkMode,
21372144 }) = .empty;
21382145
2146 // Allocated with `arena`.
2147 var failed_frameworks: std.ArrayList(struct {
2148 name: []const u8,
2149 checked_paths: []const u8,
2150 }) = .empty;
2151
21392152 // Convert external system libs into a stack so that items can be
21402153 // pushed to it.
21412154 //
......@@ -2337,17 +2350,47 @@ pub fn resolveInputs(
23372350 }
23382351 continue;
23392352 },
2353 .framework_query => |framework_query| {
2354 checked_paths.clearRetainingCapacity();
2355 for (framework_directories) |framework_directory| {
2356 switch (try resolveFrameworkInput(
2357 gpa,
2358 arena,
2359 io,
2360 resolved_inputs,
2361 &checked_paths,
2362 &ld_script_bytes,
2363 &archive_dedup,
2364 framework_directory,
2365 framework_query,
2366 )) {
2367 .ok => continue :syslib,
2368 .no_match => {},
2369 }
2370 }
2371 try failed_frameworks.append(arena, .{
2372 .name = framework_query.name,
2373 .checked_paths = try arena.dupe(u8, checked_paths.items),
2374 });
2375 continue :syslib;
2376 },
23402377 }
23412378 comptime unreachable;
23422379 }
23432380
2344 if (failed_libs.items.len > 0) {
2381 if (failed_libs.items.len > 0 or failed_frameworks.items.len > 0) {
23452382 for (failed_libs.items) |f| {
23462383 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
23472384 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{
23482385 f.preferred_mode, f.name, f.strategy, searched_paths,
23492386 });
23502387 }
2388 for (failed_frameworks.items) |f| {
2389 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2390 std.log.err("unable to find system framework {q}. searched paths:{s}", .{
2391 f.name, searched_paths,
2392 });
2393 }
23512394 std.process.exit(1);
23522395 }
23532396}
......@@ -2437,16 +2480,15 @@ fn resolveLibInput(
24372480 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
24382481 };
24392482 errdefer file.close(io);
2440 return finishResolveLibInput(
2441 io,
2442 resolved_inputs,
2443 archive_dedup,
2444 test_path,
2445 file,
2446 link_mode,
2447 name_query.query,
2448 .basename,
2449 );
2483 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2484 .path = test_path,
2485 .file = file,
2486 .needed = name_query.query.needed,
2487 .weak = name_query.query.weak,
2488 .reexport = name_query.query.reexport,
2489 .fallback_soname = .basename,
2490 } });
2491 return .ok;
24502492 }
24512493
24522494 // In the case of MinGW, the main check will be .lib but we also need to
......@@ -2462,16 +2504,13 @@ fn resolveLibInput(
24622504 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
24632505 };
24642506 errdefer file.close(io);
2465 return finishResolveLibInput(
2466 io,
2467 resolved_inputs,
2468 archive_dedup,
2469 test_path,
2470 file,
2471 link_mode,
2472 name_query.query,
2473 .basename,
2474 );
2507 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2508 .path = test_path,
2509 .file = file,
2510 .must_link = name_query.query.must_link,
2511 .hidden = name_query.query.hidden,
2512 });
2513 return .ok;
24752514 }
24762515
24772516 // In the case of OpenBSD, dynamic libraries are always versioned, without
......@@ -2533,6 +2572,145 @@ fn resolveLibInput(
25332572 return .no_match;
25342573}
25352574
2575fn resolveFrameworkInput(
2576 gpa: Allocator,
2577 arena: Allocator,
2578 io: Io,
2579 /// Allocated via `gpa`.
2580 resolved_inputs: *std.ArrayList(Input),
2581 /// Allocated via `gpa`.
2582 checked_paths: *std.ArrayList(u8),
2583 /// Allocated via `gpa`.
2584 ld_script_bytes: *std.ArrayList(u8),
2585 /// Allocated via `gpa`.
2586 archive_dedup: *ArchiveDedupMap,
2587 framework_directory: Directory,
2588 framework_query: UnresolvedInput.FrameworkQuery,
2589) Allocator.Error!ResolveLibInputResult {
2590 const sep = std.fs.path.sep_str;
2591
2592 tbd: {
2593 const test_path: Path = .{
2594 .root_dir = framework_directory,
2595 .sub_path = try std.fmt.allocPrint(
2596 arena,
2597 "{s}.framework" ++ sep ++ "{s}.tbd",
2598 .{ framework_query.name, framework_query.name },
2599 ),
2600 };
2601 try checked_paths.print(gpa, "\n {f}", .{test_path});
2602 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2603 error.FileNotFound => break :tbd,
2604 else => |e| fatal("unable to search for tbd library {qf}: {t}", .{ test_path, e }),
2605 };
2606 errdefer file.close(io);
2607 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2608 .path = test_path,
2609 .file = file,
2610 .needed = framework_query.needed,
2611 .weak = framework_query.weak,
2612 .reexport = false,
2613 } });
2614 return .ok;
2615 }
2616
2617 dylib: {
2618 const test_path: Path = .{
2619 .root_dir = framework_directory,
2620 .sub_path = try std.fmt.allocPrint(
2621 arena,
2622 "{s}.framework" ++ sep ++ "{s}.dylib",
2623 .{ framework_query.name, framework_query.name },
2624 ),
2625 };
2626 try checked_paths.print(gpa, "\n {f}", .{test_path});
2627 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2628 error.FileNotFound => break :dylib,
2629 else => |e| fatal("unable to search for dynamic library {qf}: {t}", .{ test_path, e }),
2630 };
2631 errdefer file.close(io);
2632 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2633 .path = test_path,
2634 .file = file,
2635 .needed = framework_query.needed,
2636 .weak = framework_query.weak,
2637 .reexport = false,
2638 .fallback_soname = .basename,
2639 } });
2640 return .ok;
2641 }
2642
2643 ambiguous: {
2644 const test_path: Path = .{
2645 .root_dir = framework_directory,
2646 .sub_path = try std.fmt.allocPrint(
2647 arena,
2648 "{s}.framework" ++ sep ++ "{s}",
2649 .{ framework_query.name, framework_query.name },
2650 ),
2651 };
2652 try checked_paths.print(gpa, "\n {f}", .{test_path});
2653 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2654 error.FileNotFound => break :ambiguous,
2655 else => |e| fatal("unable to search for framework library {qf}: {t}", .{ test_path, e }),
2656 };
2657 errdefer file.close(io);
2658
2659 const macho_magics: []const [4]u8 = &.{
2660 @bitCast(std.macho.MH_MAGIC),
2661 @bitCast(std.macho.MH_CIGAM),
2662 @bitCast(std.macho.MH_MAGIC_64),
2663 @bitCast(std.macho.MH_CIGAM_64),
2664 @bitCast(std.macho.FAT_MAGIC),
2665 @bitCast(std.macho.FAT_CIGAM),
2666 @bitCast(std.macho.FAT_MAGIC_64),
2667 @bitCast(std.macho.FAT_CIGAM_64),
2668 };
2669 try ld_script_bytes.resize(gpa, @max(4, std.macho.ARMAG.len));
2670 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
2671 fatal("failed to read {qf}: {t}", .{ test_path, err });
2672 const buf = ld_script_bytes.items[0..n];
2673
2674 for (macho_magics) |*macho_magic| {
2675 if (mem.startsWith(u8, buf, macho_magic)) {
2676 // Appears to be a Mach-O file, so a dylib.
2677 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2678 .path = test_path,
2679 .file = file,
2680 .needed = framework_query.needed,
2681 .weak = framework_query.weak,
2682 .reexport = false,
2683 .fallback_soname = .basename,
2684 } });
2685 return .ok;
2686 }
2687 }
2688
2689 if (mem.startsWith(u8, buf, std.macho.ARMAG)) {
2690 // Appears to be an archive file.
2691 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2692 .path = test_path,
2693 .file = file,
2694 .must_link = false,
2695 .hidden = false,
2696 });
2697 return .ok;
2698 }
2699
2700 // It doesn't look like a dylib or archive, so assume it's a tbd.
2701 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2702 .path = test_path,
2703 .file = file,
2704 .needed = framework_query.needed,
2705 .weak = framework_query.weak,
2706 .reexport = false,
2707 } });
2708 return .ok;
2709 }
2710
2711 return .no_match;
2712}
2713
25362714/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so
25372715/// that linker implementations do not need to open and scan the archive just to determine that they
25382716/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions
......@@ -2556,45 +2734,21 @@ const ArchiveDedupAdapter = struct {
25562734 }
25572735};
25582736
2559fn finishResolveLibInput(
2737fn addResolvedStaticLibInput(
25602738 io: Io,
25612739 resolved_inputs: *std.ArrayList(Input),
25622740 archive_dedup: *ArchiveDedupMap,
2563 path: Path,
2564 file: Io.File,
2565 link_mode: std.lang.LinkMode,
2566 query: UnresolvedInput.Query,
2567 fallback_soname: Input.Dso.FallbackSoname,
2568) ResolveLibInputResult {
2569 switch (link_mode) {
2570 .static => {
2571 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2572 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx);
2573 if (gop.found_existing) {
2574 // Ignore duplicate archive input
2575 file.close(io);
2576 return .ok;
2577 }
2578 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2579 resolved_inputs.appendAssumeCapacity(.{ .archive = .{
2580 .path = path,
2581 .file = file,
2582 .must_link = query.must_link,
2583 .hidden = query.hidden,
2584 } });
2585 },
2586 .dynamic => resolved_inputs.appendAssumeCapacity(.{
2587 .dso = .{
2588 .path = path,
2589 .file = file,
2590 .needed = query.needed,
2591 .weak = query.weak,
2592 .reexport = query.reexport,
2593 .fallback_soname = fallback_soname,
2594 },
2595 }),
2741 archive: Input.Object,
2742) void {
2743 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2744 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(archive.path, ctx);
2745 if (gop.found_existing) {
2746 // Ignore duplicate archive input
2747 archive.file.close(io);
2748 } else {
2749 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2750 resolved_inputs.appendAssumeCapacity(.{ .archive = archive });
25962751 }
2597 return .ok;
25982752}
25992753
26002754fn resolvePathInput(
......@@ -2690,19 +2844,24 @@ fn resolvePathInputLib(
26902844 try archive_dedup.ensureUnusedCapacity(gpa, 1);
26912845
26922846 const test_path: Path = pq.path;
2847
2848 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2849 error.FileNotFound => return .no_match,
2850 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{
2851 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2852 }),
2853 };
2854 errdefer file.close(io);
2855
26932856 // In the case of shared libraries, they might actually be "linker scripts"
26942857 // that contain references to other libraries.
2695 if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) {
2696 .static_library, .shared_library => true,
2697 else => false,
2698 }) {
2699 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2700 error.FileNotFound => return .no_match,
2701 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{
2702 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2703 }),
2704 };
2705 errdefer file.close(io);
2858 ld_script: {
2859 if (!pq.query.allow_so_scripts) break :ld_script;
2860 if (target.ofmt != .elf) break :ld_script;
2861 switch (Compilation.classifyFileExt(test_path.sub_path)) {
2862 .static_library, .shared_library => {},
2863 else => break :ld_script,
2864 }
27062865 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
27072866 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
27082867 fatal("failed to read {qf}: {t}", .{ test_path, err });
......@@ -2712,16 +2871,7 @@ fn resolvePathInputLib(
27122871 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
27132872 {
27142873 // Appears to be an ELF or archive file.
2715 return finishResolveLibInput(
2716 io,
2717 resolved_inputs,
2718 archive_dedup,
2719 test_path,
2720 file,
2721 link_mode,
2722 pq.query,
2723 fallback_soname,
2724 );
2874 break :ld_script;
27252875 }
27262876 const stat = file.stat(io) catch |err|
27272877 fatal("failed to stat {f}: {t}", .{ test_path, err });
......@@ -2785,21 +2935,23 @@ fn resolvePathInputLib(
27852935 return .ok;
27862936 }
27872937
2788 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2789 error.FileNotFound => return .no_match,
2790 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),
2791 };
2792 errdefer file.close(io);
2793 return finishResolveLibInput(
2794 io,
2795 resolved_inputs,
2796 archive_dedup,
2797 test_path,
2798 file,
2799 link_mode,
2800 pq.query,
2801 fallback_soname,
2802 );
2938 switch (link_mode) {
2939 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2940 .path = test_path,
2941 .file = file,
2942 .needed = pq.query.needed,
2943 .weak = pq.query.weak,
2944 .reexport = pq.query.reexport,
2945 .fallback_soname = fallback_soname,
2946 } }),
2947 .static => addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2948 .path = test_path,
2949 .file = file,
2950 .must_link = pq.query.must_link,
2951 .hidden = pq.query.hidden,
2952 }),
2953 }
2954 return .ok;
28032955}
28042956
28052957pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {
src/link/MachO.zig+9-59
......@@ -119,10 +119,6 @@ dead_strip_dylibs: bool,
119119undefined_treatment: UndefinedTreatment,
120120/// TODO: delete this, libraries need to be resolved by the frontend instead
121121lib_directories: []const Directory,
122/// Resolved list of framework search directories
123framework_dirs: []const []const u8,
124/// List of input frameworks
125frameworks: []const Framework,
126122/// Install name for the dylib.
127123/// TODO: unify with soname
128124install_name: ?[]const u8,
......@@ -196,7 +192,6 @@ pub fn createEmpty(
196192 .headerpad_size = options.headerpad_size,
197193 .headerpad_max_install_names = options.headerpad_max_install_names,
198194 .dead_strip_dylibs = options.dead_strip_dylibs,
199 .frameworks = options.frameworks,
200195 .install_name = options.install_name,
201196 .entitlements = options.entitlements,
202197 .compatibility_version = options.compatibility_version,
......@@ -211,7 +206,6 @@ pub fn createEmpty(
211206 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
212207 // TODO delete this, directories must instead be resolved by the frontend
213208 .lib_directories = options.lib_directories,
214 .framework_dirs = options.framework_dirs,
215209 .force_load_objc = options.force_load_objc,
216210 .discard_local_symbols = options.discard_local_symbols,
217211 };
......@@ -372,35 +366,6 @@ pub fn flush(
372366 diags.addParseError(input.path(), "failed to read input file: {t}", .{err});
373367 }
374368
375 // frameworks
376 for (self.frameworks) |framework| {
377 switch (Compilation.classifyFileExt(framework.path.sub_path)) {
378 .shared_library => {
379 const dso_input = try link.openDsoInput(io, diags, framework.path, framework.needed, framework.weak, false);
380 self.classifyInputFile(dso_input) catch |err|
381 diags.addParseError(framework.path, "failed to parse input file: {s}", .{@errorName(err)});
382 },
383 .static_library => {
384 const archive_input = try link.openArchiveInput(io, diags, framework.path, false, false);
385 self.classifyInputFile(archive_input) catch |err|
386 diags.addParseError(framework.path, "failed to parse input file: {s}", .{@errorName(err)});
387 },
388 else => {
389 dso: {
390 const dso_input = link.openDsoInput(io, diags, framework.path, framework.needed, framework.weak, false) catch break :dso;
391 self.classifyInputFile(dso_input) catch break :dso;
392 continue;
393 }
394 ar: {
395 const archive_input = link.openArchiveInput(io, diags, framework.path, false, false) catch break :ar;
396 self.classifyInputFile(archive_input) catch break :ar;
397 continue;
398 }
399 diags.addParseError(framework.path, "unknown file extension", .{});
400 },
401 }
402 }
403
404369 try self.parseInputFiles();
405370 self.parseDependentDylibs() catch |err| {
406371 switch (err) {
......@@ -716,20 +681,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
716681 },
717682 };
718683
719 for (self.framework_dirs) |f_dir| {
684 for (comp.framework_dirs) |f_dir| {
720685 try argv.append("-F");
721 try argv.append(f_dir);
722 }
723
724 for (self.frameworks) |framework| {
725 const name = framework.path.stem();
726 const arg = if (framework.needed)
727 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
728 else if (framework.weak)
729 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
730 else
731 try std.fmt.allocPrint(arena, "-framework {s}", .{name});
732 try argv.append(arg);
686 try argv.append(f_dir.path orelse ".");
733687 }
734688
735689 if (comp.config.link_libcpp) {
......@@ -763,7 +717,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
763717 log.debug("classifying input file {f}", .{path});
764718
765719 const fh = try self.addFileHandle(file);
766 var buffer: [Archive.SARMAG]u8 = undefined;
720 var buffer: [macho.ARMAG.len]u8 = undefined;
767721
768722 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
769723 const offset = if (fat_arch) |fa| fa.offset else 0;
......@@ -778,7 +732,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
778732 return;
779733 }
780734 if (readArMagic(io, file, offset, &buffer) catch null) |ar_magic| blk: {
781 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
735 if (!mem.eql(u8, ar_magic, macho.ARMAG)) break :blk;
782736 try self.addArchive(input.archive, fh, fat_arch);
783737 return;
784738 }
......@@ -808,10 +762,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6
808762 return hdr;
809763}
810764
811pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 {
765pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[macho.ARMAG.len]u8) ![]const u8 {
812766 const nread = try file.readPositionalAll(io, buffer, offset);
813767 if (nread != buffer.len) return error.InputOutput;
814 return buffer[0..Archive.SARMAG];
768 return buffer[0..macho.ARMAG.len];
815769}
816770
817771fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {
......@@ -1034,10 +988,6 @@ fn parseDependentDylibs(self: *MachO) !void {
1034988 const comp = self.base.comp;
1035989 const gpa = comp.gpa;
1036990 const io = comp.io;
1037 const framework_dirs = self.framework_dirs;
1038
1039 // TODO delete this, directories must instead be resolved by the frontend
1040 const lib_directories = self.lib_directories;
1041991
1042992 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1043993 defer arena_alloc.deinit();
......@@ -1072,14 +1022,14 @@ fn parseDependentDylibs(self: *MachO) !void {
10721022 const stem = fs.path.stem(id.name);
10731023
10741024 // Framework
1075 for (framework_dirs) |dir| {
1025 for (comp.framework_dirs) |dir| {
10761026 test_path.clearRetainingCapacity();
1077 if (try accessFrameworkPath(arena, io, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
1027 if (try accessFrameworkPath(arena, io, &test_path, &checked_paths, dir.path orelse ".", stem)) break :full_path test_path.items;
10781028 }
10791029
10801030 // Library
10811031 const lib_name = eatPrefix(stem, "lib") orelse stem;
1082 for (lib_directories) |lib_directory| {
1032 for (self.lib_directories) |lib_directory| {
10831033 test_path.clearRetainingCapacity();
10841034 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
10851035 }
src/link/MachO/Archive.zig+4-15
......@@ -17,7 +17,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
1717 const offset = if (fat_arch) |ar| ar.offset else 0;
1818 const end_pos = if (fat_arch) |ar| offset + ar.size else (try handle.stat(io)).size;
1919
20 var pos: usize = offset + SARMAG;
20 var pos: usize = offset + macho.ARMAG.len;
2121 while (true) {
2222 if (pos >= end_pos) break;
2323 if (!mem.isAligned(pos, 2)) pos += 1;
......@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
3030 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
3131 pos += @sizeOf(ar_hdr);
3232
33 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
33 if (!mem.eql(u8, &hdr.ar_fmag, macho.ARFMAG)) {
3434 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{
35 std.ascii.hexEscape(ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower),
35 std.ascii.hexEscape(macho.ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower),
3636 });
3737 }
3838
......@@ -116,17 +116,6 @@ pub fn writeHeader(
116116 }
117117}
118118
119// Archive files start with the ARMAG identifying string. Then follows a
120// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
121// member indicates, for each member file.
122/// String that begins an archive file.
123pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
124/// Size of that string.
125pub const SARMAG: u4 = 8;
126
127/// String in ar_fmag at the end of each header.
128const ARFMAG: *const [2:0]u8 = "`\n";
129
130119pub const SYMDEF = "__.SYMDEF";
131120pub const SYMDEF64 = "__.SYMDEF_64";
132121pub const SYMDEF_SORTED = "__.SYMDEF SORTED";
......@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {
146135 /// File size, in ASCII decimal.
147136 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,
148137 /// Always contains ARFMAG.
149 ar_fmag: [2]u8 = ARFMAG.*,
138 ar_fmag: [2]u8 = macho.ARFMAG.*,
150139
151140 fn date(self: ar_hdr) !u64 {
152141 const value = mem.trimEnd(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});
src/link/MachO/relocatable.zig+2-2
......@@ -151,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {
151151
152152 // Update file offsets of contributing objects
153153 const total_size: usize = blk: {
154 var pos: usize = Archive.SARMAG;
154 var pos: usize = macho.ARMAG.len;
155155 pos += @sizeOf(Archive.ar_hdr);
156156 pos += Archive.SYMDEF.len + 1;
157157 pos = mem.alignForward(usize, pos, 8);
......@@ -194,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {
194194 var writer: Writer = .fixed(buffer);
195195
196196 // Write magic
197 writer.writeAll(Archive.ARMAG) catch unreachable;
197 writer.writeAll(macho.ARMAG) catch unreachable;
198198
199199 // Write symtab
200200 ar_symtab.write(format, macho_file, &writer) catch |err|
src/main.zig+126-150
......@@ -893,13 +893,6 @@ const ArgsIterator = struct {
893893 }
894894};
895895
896/// Similar to `link.Framework` except it doesn't store yet unresolved
897/// path to the framework.
898const Framework = struct {
899 needed: bool = false,
900 weak: bool = false,
901};
902
903896const CliModule = struct {
904897 root_path: []const u8,
905898 root_src_path: []const u8,
......@@ -1117,12 +1110,12 @@ fn buildOutputType(
11171110
11181111 .llvm_m_args = .empty,
11191112 .sysroot = null,
1120 .lib_directories = .empty, // populated by createModule()
11211113 .lib_dir_args = .empty, // populated from CLI arg parsing
1114 .lib_directories = .empty, // populated by createModule()
1115 .framework_dir_args = .empty, // populated from CLI arg parsing
1116 .framework_directories = .empty, // populated by createModule()
11221117 .libc_installation = null,
11231118 .want_native_include_dirs = false,
1124 .frameworks = .empty,
1125 .framework_dirs = .empty,
11261119 .rpath_list = .empty,
11271120 .each_lib_rpath = null,
11281121 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),
......@@ -1275,13 +1268,25 @@ fn buildOutputType(
12751268 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
12761269 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
12771270 } else if (mem.eql(u8, arg, "-F")) {
1278 try create_module.framework_dirs.append(arena, args_iter.nextOrFatal());
1271 try create_module.framework_dir_args.append(arena, args_iter.nextOrFatal());
12791272 } else if (mem.eql(u8, arg, "-framework")) {
1280 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{});
1273 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1274 .name = args_iter.nextOrFatal(),
1275 .needed = false,
1276 .weak = false,
1277 } });
12811278 } else if (mem.eql(u8, arg, "-weak_framework")) {
1282 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true });
1279 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1280 .name = args_iter.nextOrFatal(),
1281 .needed = false,
1282 .weak = true,
1283 } });
12831284 } else if (mem.eql(u8, arg, "-needed_framework")) {
1284 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
1285 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1286 .name = args_iter.nextOrFatal(),
1287 .needed = true,
1288 .weak = false,
1289 } });
12851290 } else if (mem.eql(u8, arg, "-install_name")) {
12861291 install_name = args_iter.nextOrFatal();
12871292 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
......@@ -1396,11 +1401,11 @@ fn buildOutputType(
13961401 } else if (mem.eql(u8, arg, "-iframework")) {
13971402 const path = args_iter.nextOrFatal();
13981403 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1399 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1404 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
14001405 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
14011406 const path = args_iter.nextOrFatal();
14021407 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1403 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1408 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
14041409 } else if (mem.eql(u8, arg, "--version")) {
14051410 const next_arg = args_iter.nextOrFatal();
14061411 version = std.SemanticVersion.parse(next_arg) catch |err| {
......@@ -1828,7 +1833,7 @@ fn buildOutputType(
18281833 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
18291834 try create_module.lib_dir_args.append(arena, rest);
18301835 } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {
1831 try create_module.framework_dirs.append(arena, rest);
1836 try create_module.framework_dir_args.append(arena, rest);
18321837 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
18331838 // We don't know whether this library is part of libc
18341839 // or libc++ until we resolve the target, so we append
......@@ -2532,8 +2537,12 @@ fn buildOutputType(
25322537 disable_c_depfile = true;
25332538 try cc_argv.appendSlice(arena, it.other_args);
25342539 },
2535 .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),
2536 .framework => try create_module.frameworks.put(arena, it.only_arg, .{}),
2540 .framework_dir => try create_module.framework_dir_args.append(arena, it.only_arg),
2541 .framework => try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2542 .name = it.only_arg,
2543 .needed = false,
2544 .weak = false,
2545 } }),
25372546 .nostdlibinc => create_module.want_native_include_dirs = false,
25382547 .strip => mod_opts.strip = true,
25392548 .exec_model => {
......@@ -2561,7 +2570,11 @@ fn buildOutputType(
25612570 },
25622571 .name_done = false,
25632572 } }),
2564 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2573 .weak_framework => try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2574 .name = it.only_arg,
2575 .needed = false,
2576 .weak = true,
2577 } }),
25652578 .headerpad_max_install_names => headerpad_max_install_names = true,
25662579 .compress_debug_sections => {
25672580 if (it.only_arg.len == 0) {
......@@ -2914,11 +2927,23 @@ fn buildOutputType(
29142927 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });
29152928 };
29162929 } else if (mem.eql(u8, arg, "-framework")) {
2917 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{});
2930 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2931 .name = linker_args_it.nextOrFatal(),
2932 .needed = false,
2933 .weak = false,
2934 } });
29182935 } else if (mem.eql(u8, arg, "-weak_framework")) {
2919 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true });
2936 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2937 .name = linker_args_it.nextOrFatal(),
2938 .needed = false,
2939 .weak = true,
2940 } });
29202941 } else if (mem.eql(u8, arg, "-needed_framework")) {
2921 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2942 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2943 .name = linker_args_it.nextOrFatal(),
2944 .needed = true,
2945 .weak = false,
2946 } });
29222947 } else if (mem.eql(u8, arg, "-needed_library")) {
29232948 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
29242949 .name = linker_args_it.nextOrFatal(),
......@@ -3400,59 +3425,6 @@ fn buildOutputType(
34003425 }
34013426 }
34023427
3403 var resolved_frameworks = std.array_list.Managed(Compilation.Framework).init(arena);
3404
3405 if (create_module.frameworks.keys().len > 0) {
3406 var test_path = std.array_list.Managed(u8).init(gpa);
3407 defer test_path.deinit();
3408
3409 var checked_paths = std.array_list.Managed(u8).init(gpa);
3410 defer checked_paths.deinit();
3411
3412 var failed_frameworks = std.array_list.Managed(struct {
3413 name: []const u8,
3414 checked_paths: []const u8,
3415 }).init(arena);
3416
3417 framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| {
3418 checked_paths.clearRetainingCapacity();
3419
3420 for (create_module.framework_dirs.items) |framework_dir_path| {
3421 if (try accessFrameworkPath(
3422 io,
3423 &test_path,
3424 &checked_paths,
3425 framework_dir_path,
3426 framework_name,
3427 )) {
3428 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3429 try resolved_frameworks.append(.{
3430 .needed = info.needed,
3431 .weak = info.weak,
3432 .path = path,
3433 });
3434 continue :framework;
3435 }
3436 }
3437
3438 try failed_frameworks.append(.{
3439 .name = framework_name,
3440 .checked_paths = try arena.dupe(u8, checked_paths.items),
3441 });
3442 }
3443
3444 if (failed_frameworks.items.len > 0) {
3445 for (failed_frameworks.items) |f| {
3446 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
3447 std.log.err("unable to find framework {q}. searched paths: {s}", .{
3448 f.name, searched_paths,
3449 });
3450 }
3451 process.exit(1);
3452 }
3453 }
3454 // After this point, resolved_frameworks is used instead of frameworks.
3455
34563428 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
34573429 const total_obj_count = create_module.c_source_files.items.len +
34583430 @intFromBool(root_src_file != null) +
......@@ -3689,6 +3661,7 @@ fn buildOutputType(
36893661 .emit_docs = emit_docs_resolved,
36903662 .emit_implib = emit_implib_resolved,
36913663 .lib_directories = create_module.lib_directories.items,
3664 .framework_directories = create_module.framework_directories.items,
36923665 .rpath_list = create_module.rpath_list.items,
36933666 .symbol_wrap_set = symbol_wrap_set,
36943667 .c_source_files = create_module.c_source_files.items,
......@@ -3697,8 +3670,6 @@ fn buildOutputType(
36973670 .rc_includes = rc_includes,
36983671 .mingw_unicode_entry_point = mingw_unicode_entry_point,
36993672 .link_inputs = create_module.link_inputs.items,
3700 .framework_dirs = create_module.framework_dirs.items,
3701 .frameworks = resolved_frameworks.items,
37023673 .windows_lib_names = create_module.windows_libs.keys(),
37033674 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,
37043675 .want_ubsan_rt = want_ubsan_rt,
......@@ -4041,13 +4012,13 @@ const CreateModule = struct {
40414012 /// CPU features.
40424013 llvm_m_args: std.ArrayList([]const u8),
40434014 sysroot: ?[]const u8,
4044 lib_directories: std.ArrayList(Directory),
40454015 lib_dir_args: std.ArrayList([]const u8),
4016 lib_directories: std.ArrayList(Directory),
4017 framework_dir_args: std.ArrayList([]const u8),
4018 framework_directories: std.ArrayList(Directory),
40464019 libc_installation: ?LibCInstallation,
40474020 want_native_include_dirs: bool,
4048 frameworks: std.array_hash_map.String(Framework),
40494021 native_system_include_paths: []const []const u8,
4050 framework_dirs: std.ArrayList([]const u8),
40514022 rpath_list: std.ArrayList([]const u8),
40524023 each_lib_rpath: ?bool,
40534024 libc_paths_file: ?[]const u8,
......@@ -4158,7 +4129,7 @@ fn createModule(
41584129 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
41594130 defer unresolved_link_inputs.deinit(gpa);
41604131 try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);
4161 var any_name_queries_remaining = false;
4132 var any_named_library_queries = false;
41624133 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
41634134 .name_query => |nq| {
41644135 const lib_name = nq.name;
......@@ -4199,39 +4170,52 @@ fn createModule(
41994170 }
42004171
42014172 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4202 any_name_queries_remaining = true;
4173 any_named_library_queries = true;
4174 },
4175 .framework_query => {
4176 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4177 any_named_library_queries = true;
42034178 },
42044179 else => {
42054180 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
42064181 },
42074182 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
42084183
4209 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
4184 if (any_named_library_queries) create_module.want_native_include_dirs = true;
42104185
42114186 // Resolve the library path arguments with respect to sysroot.
42124187 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
4213 if (create_module.sysroot) |root| {
4214 for (create_module.lib_dir_args.items) |lib_dir_arg| {
4215 if (fs.path.isAbsolute(lib_dir_arg)) {
4216 const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];
4217 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
4218 addLibDirectoryWarn(io, &create_module.lib_directories, full_path);
4219 } else {
4220 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4188 for (create_module.lib_dir_args.items) |dir_arg| {
4189 const path: []const u8 = path: {
4190 if (fs.path.isAbsolute(dir_arg)) {
4191 if (create_module.sysroot) |sysroot| {
4192 // Change the path root to the given sysroot.
4193 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4194 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4195 }
42214196 }
4222 }
4223 } else {
4224 for (create_module.lib_dir_args.items) |lib_dir_arg| {
4225 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4226 }
4197 break :path dir_arg;
4198 };
4199 appendLibDirOrWarn(io, &create_module.lib_directories, path);
42274200 }
42284201 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
42294202
4230 if (resolved_target.is_native_os and target.os.tag.isDarwin()) {
4231 // If we want to link against frameworks, we need system headers.
4232 if (create_module.frameworks.count() > 0)
4233 create_module.want_native_include_dirs = true;
4203 // Likewise for framework path arguments.
4204 try create_module.framework_directories.ensureUnusedCapacity(arena, create_module.framework_dir_args.items.len);
4205 for (create_module.framework_dir_args.items) |dir_arg| {
4206 const path: []const u8 = path: {
4207 if (fs.path.isAbsolute(dir_arg)) {
4208 if (create_module.sysroot) |sysroot| {
4209 // Change the path root to the given sysroot.
4210 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4211 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4212 }
4213 }
4214 break :path dir_arg;
4215 };
4216 appendLibDirOrWarn(io, &create_module.framework_directories, path);
42344217 }
4218 create_module.framework_dir_args = undefined; // From here we use framework_directories instead.
42354219
42364220 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
42374221 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
......@@ -4253,11 +4237,17 @@ fn createModule(
42534237
42544238 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
42554239
4256 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
42574240 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
42584241
42594242 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
4260 for (paths.lib_dirs.items) |path| addLibDirectoryWarn2(io, &create_module.lib_directories, path, true);
4243 for (paths.lib_dirs.items) |path| {
4244 appendLibDirOrWarnAllowMissing(io, &create_module.lib_directories, path);
4245 }
4246
4247 try create_module.framework_directories.ensureUnusedCapacity(arena, paths.framework_dirs.items.len);
4248 for (paths.framework_dirs.items) |path| {
4249 appendLibDirOrWarnAllowMissing(io, &create_module.framework_directories, path);
4250 }
42614251 }
42624252
42634253 if (create_module.libc_paths_file) |paths_file| {
......@@ -4266,7 +4256,7 @@ fn createModule(
42664256 }
42674257
42684258 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
4269 any_name_queries_remaining)
4259 any_named_library_queries)
42704260 {
42714261 if (create_module.libc_installation == null) {
42724262 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{
......@@ -4277,9 +4267,19 @@ fn createModule(
42774267 fatal("unable to find native libc installation: {t}", .{err});
42784268 };
42794269 }
4270
42804271 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
4281 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
4282 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);
4272
4273 appendLibDirOrWarn(
4274 io,
4275 &create_module.lib_directories,
4276 create_module.libc_installation.?.msvc_lib_dir.?,
4277 );
4278 appendLibDirOrWarn(
4279 io,
4280 &create_module.lib_directories,
4281 create_module.libc_installation.?.kernel32_lib_dir.?,
4282 );
42834283 }
42844284
42854285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
......@@ -4291,6 +4291,7 @@ fn createModule(
42914291 &unresolved_link_inputs,
42924292 &create_module.link_inputs,
42934293 create_module.lib_directories.items,
4294 create_module.framework_directories.items,
42944295 color,
42954296 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});
42964297
......@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {
60856086 };
60866087};
60876088
6088fn accessFrameworkPath(
6089 io: Io,
6090 test_path: *std.array_list.Managed(u8),
6091 checked_paths: *std.array_list.Managed(u8),
6092 framework_dir_path: []const u8,
6093 framework_name: []const u8,
6094) !bool {
6095 const sep = fs.path.sep_str;
6096
6097 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
6098 test_path.clearRetainingCapacity();
6099 try test_path.print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
6100 framework_dir_path, framework_name, framework_name, ext,
6101 });
6102 try checked_paths.print("\n {s}", .{test_path.items});
6103 Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) {
6104 error.FileNotFound => continue,
6105 else => |e| fatal("unable to search for {s} framework {q}: {t}", .{
6106 ext, test_path.items, e,
6107 }),
6108 };
6109 return true;
6110 }
6111
6112 return false;
6113}
6114
61156089fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
61166090 return stringToEnum(std.zig.RcIncludes, arg) orelse
61176091 fatal("unsupported rc includes type: {q}", .{arg});
......@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
62226196 return false;
62236197}
62246198
6225fn addLibDirectoryWarn(io: Io, lib_directories: *std.ArrayList(Directory), path: []const u8) void {
6226 return addLibDirectoryWarn2(io, lib_directories, path, false);
6199fn appendLibDirOrWarn(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6200 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6201 dirs.appendAssumeCapacity(.{
6202 .path = path,
6203 .handle = handle,
6204 });
6205 } else |err| {
6206 warn("unable to open library directory {q}: {t}", .{ path, err });
6207 }
62276208}
6228
6229fn addLibDirectoryWarn2(
6230 io: Io,
6231 lib_directories: *std.ArrayList(Directory),
6232 path: []const u8,
6233 ignore_not_found: bool,
6234) void {
6235 lib_directories.appendAssumeCapacity(.{
6236 .handle = Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
6237 if (err == error.FileNotFound and ignore_not_found) return;
6238 warn("unable to open library directory {q}: {t}", .{ path, err });
6239 return;
6240 },
6241 .path = path,
6242 });
6209fn appendLibDirOrWarnAllowMissing(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6210 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6211 dirs.appendAssumeCapacity(.{
6212 .path = path,
6213 .handle = handle,
6214 });
6215 } else |err| switch (err) {
6216 error.FileNotFound => {}, // ignore
6217 else => |e| warn("unable to open library directory {q}: {t}", .{ path, e }),
6218 }
62436219}
62446220
62456221const IoImpl = switch (build_options.io_mode) {