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) {...@@ -1145,16 +1145,16 @@ pub const LC = enum(u32) {
1145};1145};
11461146
1147/// the mach magic number1147/// the mach magic number
1148pub const MH_MAGIC = 0xfeedface;1148pub const MH_MAGIC: u32 = 0xfeedface;
11491149
1150/// NXSwapInt(MH_MAGIC)1150/// NXSwapInt(MH_MAGIC)
1151pub const MH_CIGAM = 0xcefaedfe;1151pub const MH_CIGAM: u32 = 0xcefaedfe;
11521152
1153/// the 64-bit mach magic number1153/// the 64-bit mach magic number
1154pub const MH_MAGIC_64 = 0xfeedfacf;1154pub const MH_MAGIC_64: u32 = 0xfeedfacf;
11551155
1156/// NXSwapInt(MH_MAGIC_64)1156/// NXSwapInt(MH_MAGIC_64)
1157pub const MH_CIGAM_64 = 0xcffaedfe;1157pub const MH_CIGAM_64: u32 = 0xcffaedfe;
11581158
1159/// relocatable object file1159/// relocatable object file
1160pub const MH_OBJECT = 0x1;1160pub const MH_OBJECT = 0x1;
...@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;...@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;
1281// Constants for the flags field of the fat_header1281// Constants for the flags field of the fat_header
12821282
1283/// the fat magic number1283/// the fat magic number
1284pub const FAT_MAGIC = 0xcafebabe;1284pub const FAT_MAGIC: u32 = 0xcafebabe;
12851285
1286/// NXSwapLong(FAT_MAGIC)1286/// NXSwapLong(FAT_MAGIC)
1287pub const FAT_CIGAM = 0xbebafeca;1287pub const FAT_CIGAM: u32 = 0xbebafeca;
12881288
1289/// the 64-bit fat magic number1289/// the 64-bit fat magic number
1290pub const FAT_MAGIC_64 = 0xcafebabf;1290pub const FAT_MAGIC_64: u32 = 0xcafebabf;
12911291
1292/// NXSwapLong(FAT_MAGIC_64)1292/// NXSwapLong(FAT_MAGIC_64)
1293pub const FAT_CIGAM_64 = 0xbfbafeca;1293pub const FAT_CIGAM_64: u32 = 0xbfbafeca;
12941294
1295/// Segment flags1295/// Segment flags
1296/// The file contents for this segment is for the high part of the VM space, the low part1296/// 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) {...@@ -2212,3 +2212,11 @@ pub const CompactUnwindEncoding = packed struct(u32) {
2212 has_lsda: u1,2212 has_lsda: u1,
2213 start: u1,2213 start: u1,
2214};2214};
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,...@@ -83,7 +83,7 @@ zigc_strat: RtStrat,
83/// Resolved into known paths, any GNU ld scripts already resolved.83/// Resolved into known paths, any GNU ld scripts already resolved.
84link_inputs: []const link.Input,84link_inputs: []const link.Input,
85/// Needed only for passing -F args to clang.85/// Needed only for passing -F args to clang.
86framework_dirs: []const []const u8,86framework_dirs: []const Cache.Directory,
87/// These are only for DLLs dependencies fulfilled by the `.def` files shipped87/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
88/// with Zig. Static libraries are provided as `link.Input` values.88/// with Zig. Static libraries are provided as `link.Input` values.
89windows_libs: std.array_hash_map.String(void),89windows_libs: std.array_hash_map.String(void),
...@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {...@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {
1350 version,1350 version,
1351};1351};
13521352
1353pub const Framework = link.File.MachO.Framework;
1354pub const SystemLib = link.SystemLib;1353pub const SystemLib = link.SystemLib;
13551354
1356pub const CacheMode = enum {1355pub const CacheMode = enum {
...@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {...@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {
1487 /// The ELF implementation no longer uses this data, however the MachO and COFF1486 /// The ELF implementation no longer uses this data, however the MachO and COFF
1488 /// implementations still do.1487 /// implementations still do.
1489 lib_directories: []const Cache.Directory = &.{},1488 lib_directories: []const Cache.Directory = &.{},
1489 framework_directories: []const Cache.Directory = &.{},
1490 rpath_list: []const []const u8 = &[0][]const u8{},1490 rpath_list: []const []const u8 = &[0][]const u8{},
1491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,1491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,
1492 c_source_files: []const CSourceFile = &.{},1492 c_source_files: []const CSourceFile = &.{},
...@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {...@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {
1494 manifest_file: ?[]const u8 = null,1494 manifest_file: ?[]const u8 = null,
1495 rc_includes: std.zig.RcIncludes = .any,1495 rc_includes: std.zig.RcIncludes = .any,
1496 link_inputs: []const link.Input = &.{},1496 link_inputs: []const link.Input = &.{},
1497 framework_dirs: []const []const u8 = &[0][]const u8{},
1498 frameworks: []const Framework = &.{},
1499 windows_lib_names: []const []const u8 = &.{},1497 windows_lib_names: []const []const u8 = &.{},
1500 /// This means that if the output mode is an executable it will be a1498 /// This means that if the output mode is an executable it will be a
1501 /// Position Independent Executable. If the output mode is not an1499 /// 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,...@@ -2146,7 +2144,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2146 .ubsan_rt_strat = ubsan_rt_strat,2144 .ubsan_rt_strat = ubsan_rt_strat,
2147 .zigc_strat = zigc_strat,2145 .zigc_strat = zigc_strat,
2148 .link_inputs = options.link_inputs,2146 .link_inputs = options.link_inputs,
2149 .framework_dirs = options.framework_dirs,2147 .framework_dirs = options.framework_directories,
2150 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,2148 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
2151 .skip_linker_dependencies = options.skip_linker_dependencies,2149 .skip_linker_dependencies = options.skip_linker_dependencies,
2152 .queued_jobs = .{},2150 .queued_jobs = .{},
...@@ -2208,9 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,...@@ -2208,9 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2208 .z_relro = options.linker_z_relro,2206 .z_relro = options.linker_z_relro,
2209 .z_common_page_size = options.linker_z_common_page_size,2207 .z_common_page_size = options.linker_z_common_page_size,
2210 .z_max_page_size = options.linker_z_max_page_size,2208 .z_max_page_size = options.linker_z_max_page_size,
2211 .frameworks = options.frameworks,
2212 .lib_directories = options.lib_directories,2209 .lib_directories = options.lib_directories,
2213 .framework_dirs = options.framework_dirs,
2214 .rpath_list = options.rpath_list,2210 .rpath_list = options.rpath_list,
2215 .symbol_wrap_set = options.symbol_wrap_set,2211 .symbol_wrap_set = options.symbol_wrap_set,
2216 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),2212 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),
...@@ -3416,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -3416,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
3416 man.hash.add(comp.zigc_strat);3412 man.hash.add(comp.zigc_strat);
3417 man.hash.add(comp.rc_includes);3413 man.hash.add(comp.rc_includes);
3418 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());3414 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 }
3420 man.hash.addListOfBytes(comp.windows_libs.keys());3419 man.hash.addListOfBytes(comp.windows_libs.keys());
34213420
3422 man.hash.addListOfBytes(comp.global_cc_argv);3421 man.hash.addListOfBytes(comp.global_cc_argv);
...@@ -3482,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -3482,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
3482 man.hash.add(opts.growable_table);3481 man.hash.add(opts.growable_table);
34833482
3484 // Mach-O specific stuff3483 // Mach-O specific stuff
3485 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
3486 try man.addInputPathOptional(opts.entitlements, .{});3484 try man.addInputPathOptional(opts.entitlements, .{});
3487 man.hash.addOptional(opts.pagezero_size);3485 man.hash.addOptional(opts.pagezero_size);
3488 man.hash.addOptional(opts.headerpad_size);3486 man.hash.addOptional(opts.headerpad_size);
...@@ -6460,7 +6458,7 @@ fn addCommonCCArgs(...@@ -6460,7 +6458,7 @@ fn addCommonCCArgs(
64606458
6461 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);6459 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
6462 for (comp.framework_dirs) |framework_dir| {6460 for (comp.framework_dirs) |framework_dir| {
6463 try argv.appendSlice(&.{ "-F", framework_dir });6461 try argv.appendSlice(&.{ "-F", framework_dir.path orelse "." });
6464 }6462 }
6465 }6463 }
6466 }6464 }
src/link.zig+246-94
...@@ -481,7 +481,6 @@ pub const File = struct {...@@ -481,7 +481,6 @@ pub const File = struct {
481481
482 // TODO: remove this. libraries are resolved by the frontend.482 // TODO: remove this. libraries are resolved by the frontend.
483 lib_directories: []const Directory,483 lib_directories: []const Directory,
484 framework_dirs: []const []const u8,
485 rpath_list: []const []const u8,484 rpath_list: []const []const u8,
486485
487 /// Zig compiler development linker flags.486 /// Zig compiler development linker flags.
...@@ -501,7 +500,6 @@ pub const File = struct {...@@ -501,7 +500,6 @@ pub const File = struct {
501 headerpad_max_install_names: bool,500 headerpad_max_install_names: bool,
502 /// Remove dylibs that are unreachable by the entry point or exported symbols501 /// Remove dylibs that are unreachable by the entry point or exported symbols
503 dead_strip_dylibs: bool,502 dead_strip_dylibs: bool,
504 frameworks: []const MachO.Framework,
505 /// Force load all members of static archives that implement an503 /// Force load all members of static archives that implement an
506 /// Objective-C class or category504 /// Objective-C class or category
507 force_load_objc: bool,505 force_load_objc: bool,
...@@ -1958,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {...@@ -1958,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {
1958 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?1956 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1959 /// Who knows! Fuck around and find out.1957 /// Who knows! Fuck around and find out.
1960 ambiguous_name: AmbiguousNameQuery,1958 ambiguous_name: AmbiguousNameQuery,
1959 framework_query: FrameworkQuery,
19611960
1962 pub const NameQuery = struct {1961 pub const NameQuery = struct {
1963 name: []const u8,1962 name: []const u8,
...@@ -1996,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {...@@ -1996,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {
1996 }1995 }
1997 };1996 };
19981997
1998 pub const FrameworkQuery = struct {
1999 name: []const u8,
2000 needed: bool,
2001 weak: bool,
2002 };
2003
1999 pub const SearchStrategy = enum {2004 pub const SearchStrategy = enum {
2000 paths_first,2005 paths_first,
2001 mode_first,2006 mode_first,
...@@ -2118,6 +2123,7 @@ pub fn resolveInputs(...@@ -2118,6 +2123,7 @@ pub fn resolveInputs(
2118 /// Allocated with `gpa`.2123 /// Allocated with `gpa`.
2119 resolved_inputs: *std.ArrayList(Input),2124 resolved_inputs: *std.ArrayList(Input),
2120 lib_directories: []const Cache.Directory,2125 lib_directories: []const Cache.Directory,
2126 framework_directories: []const Cache.Directory,
2121 color: std.zig.Color,2127 color: std.zig.Color,
2122) Allocator.Error!void {2128) Allocator.Error!void {
2123 var checked_paths: std.ArrayList(u8) = .empty;2129 var checked_paths: std.ArrayList(u8) = .empty;
...@@ -2129,6 +2135,7 @@ pub fn resolveInputs(...@@ -2129,6 +2135,7 @@ pub fn resolveInputs(
2129 var archive_dedup: ArchiveDedupMap = .empty;2135 var archive_dedup: ArchiveDedupMap = .empty;
2130 defer archive_dedup.deinit(gpa);2136 defer archive_dedup.deinit(gpa);
21312137
2138 // Allocated with `arena`.
2132 var failed_libs: std.ArrayList(struct {2139 var failed_libs: std.ArrayList(struct {
2133 name: []const u8,2140 name: []const u8,
2134 strategy: UnresolvedInput.SearchStrategy,2141 strategy: UnresolvedInput.SearchStrategy,
...@@ -2136,6 +2143,12 @@ pub fn resolveInputs(...@@ -2136,6 +2143,12 @@ pub fn resolveInputs(
2136 preferred_mode: std.lang.LinkMode,2143 preferred_mode: std.lang.LinkMode,
2137 }) = .empty;2144 }) = .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
2139 // Convert external system libs into a stack so that items can be2152 // Convert external system libs into a stack so that items can be
2140 // pushed to it.2153 // pushed to it.
2141 //2154 //
...@@ -2337,17 +2350,47 @@ pub fn resolveInputs(...@@ -2337,17 +2350,47 @@ pub fn resolveInputs(
2337 }2350 }
2338 continue;2351 continue;
2339 },2352 },
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 },
2340 }2377 }
2341 comptime unreachable;2378 comptime unreachable;
2342 }2379 }
23432380
2344 if (failed_libs.items.len > 0) {2381 if (failed_libs.items.len > 0 or failed_frameworks.items.len > 0) {
2345 for (failed_libs.items) |f| {2382 for (failed_libs.items) |f| {
2346 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;2383 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2347 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{2384 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{
2348 f.preferred_mode, f.name, f.strategy, searched_paths,2385 f.preferred_mode, f.name, f.strategy, searched_paths,
2349 });2386 });
2350 }2387 }
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 }
2351 std.process.exit(1);2394 std.process.exit(1);
2352 }2395 }
2353}2396}
...@@ -2437,16 +2480,15 @@ fn resolveLibInput(...@@ -2437,16 +2480,15 @@ fn resolveLibInput(
2437 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),2480 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
2438 };2481 };
2439 errdefer file.close(io);2482 errdefer file.close(io);
2440 return finishResolveLibInput(2483 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2441 io,2484 .path = test_path,
2442 resolved_inputs,2485 .file = file,
2443 archive_dedup,2486 .needed = name_query.query.needed,
2444 test_path,2487 .weak = name_query.query.weak,
2445 file,2488 .reexport = name_query.query.reexport,
2446 link_mode,2489 .fallback_soname = .basename,
2447 name_query.query,2490 } });
2448 .basename,2491 return .ok;
2449 );
2450 }2492 }
24512493
2452 // In the case of MinGW, the main check will be .lib but we also need to2494 // In the case of MinGW, the main check will be .lib but we also need to
...@@ -2462,16 +2504,13 @@ fn resolveLibInput(...@@ -2462,16 +2504,13 @@ fn resolveLibInput(
2462 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),2504 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
2463 };2505 };
2464 errdefer file.close(io);2506 errdefer file.close(io);
2465 return finishResolveLibInput(2507 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2466 io,2508 .path = test_path,
2467 resolved_inputs,2509 .file = file,
2468 archive_dedup,2510 .must_link = name_query.query.must_link,
2469 test_path,2511 .hidden = name_query.query.hidden,
2470 file,2512 });
2471 link_mode,2513 return .ok;
2472 name_query.query,
2473 .basename,
2474 );
2475 }2514 }
24762515
2477 // In the case of OpenBSD, dynamic libraries are always versioned, without2516 // In the case of OpenBSD, dynamic libraries are always versioned, without
...@@ -2533,6 +2572,145 @@ fn resolveLibInput(...@@ -2533,6 +2572,145 @@ fn resolveLibInput(
2533 return .no_match;2572 return .no_match;
2534}2573}
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
2536/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so2714/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so
2537/// that linker implementations do not need to open and scan the archive just to determine that they2715/// that linker implementations do not need to open and scan the archive just to determine that they
2538/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions2716/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions
...@@ -2556,45 +2734,21 @@ const ArchiveDedupAdapter = struct {...@@ -2556,45 +2734,21 @@ const ArchiveDedupAdapter = struct {
2556 }2734 }
2557};2735};
25582736
2559fn finishResolveLibInput(2737fn addResolvedStaticLibInput(
2560 io: Io,2738 io: Io,
2561 resolved_inputs: *std.ArrayList(Input),2739 resolved_inputs: *std.ArrayList(Input),
2562 archive_dedup: *ArchiveDedupMap,2740 archive_dedup: *ArchiveDedupMap,
2563 path: Path,2741 archive: Input.Object,
2564 file: Io.File,2742) void {
2565 link_mode: std.lang.LinkMode,2743 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2566 query: UnresolvedInput.Query,2744 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(archive.path, ctx);
2567 fallback_soname: Input.Dso.FallbackSoname,2745 if (gop.found_existing) {
2568) ResolveLibInputResult {2746 // Ignore duplicate archive input
2569 switch (link_mode) {2747 archive.file.close(io);
2570 .static => {2748 } else {
2571 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };2749 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2572 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx);2750 resolved_inputs.appendAssumeCapacity(.{ .archive = archive });
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 }),
2596 }2751 }
2597 return .ok;
2598}2752}
25992753
2600fn resolvePathInput(2754fn resolvePathInput(
...@@ -2690,19 +2844,24 @@ fn resolvePathInputLib(...@@ -2690,19 +2844,24 @@ fn resolvePathInputLib(
2690 try archive_dedup.ensureUnusedCapacity(gpa, 1);2844 try archive_dedup.ensureUnusedCapacity(gpa, 1);
26912845
2692 const test_path: Path = pq.path;2846 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
2693 // In the case of shared libraries, they might actually be "linker scripts"2856 // In the case of shared libraries, they might actually be "linker scripts"
2694 // that contain references to other libraries.2857 // 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)) {2858 ld_script: {
2696 .static_library, .shared_library => true,2859 if (!pq.query.allow_so_scripts) break :ld_script;
2697 else => false,2860 if (target.ofmt != .elf) break :ld_script;
2698 }) {2861 switch (Compilation.classifyFileExt(test_path.sub_path)) {
2699 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2862 .static_library, .shared_library => {},
2700 error.FileNotFound => return .no_match,2863 else => break :ld_script,
2701 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{2864 }
2702 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2703 }),
2704 };
2705 errdefer file.close(io);
2706 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));2865 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
2707 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|2866 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
2708 fatal("failed to read {qf}: {t}", .{ test_path, err });2867 fatal("failed to read {qf}: {t}", .{ test_path, err });
...@@ -2712,16 +2871,7 @@ fn resolvePathInputLib(...@@ -2712,16 +2871,7 @@ fn resolvePathInputLib(
2712 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))2871 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
2713 {2872 {
2714 // Appears to be an ELF or archive file.2873 // Appears to be an ELF or archive file.
2715 return finishResolveLibInput(2874 break :ld_script;
2716 io,
2717 resolved_inputs,
2718 archive_dedup,
2719 test_path,
2720 file,
2721 link_mode,
2722 pq.query,
2723 fallback_soname,
2724 );
2725 }2875 }
2726 const stat = file.stat(io) catch |err|2876 const stat = file.stat(io) catch |err|
2727 fatal("failed to stat {f}: {t}", .{ test_path, err });2877 fatal("failed to stat {f}: {t}", .{ test_path, err });
...@@ -2785,21 +2935,23 @@ fn resolvePathInputLib(...@@ -2785,21 +2935,23 @@ fn resolvePathInputLib(
2785 return .ok;2935 return .ok;
2786 }2936 }
27872937
2788 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2938 switch (link_mode) {
2789 error.FileNotFound => return .no_match,2939 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2790 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),2940 .path = test_path,
2791 };2941 .file = file,
2792 errdefer file.close(io);2942 .needed = pq.query.needed,
2793 return finishResolveLibInput(2943 .weak = pq.query.weak,
2794 io,2944 .reexport = pq.query.reexport,
2795 resolved_inputs,2945 .fallback_soname = fallback_soname,
2796 archive_dedup,2946 } }),
2797 test_path,2947 .static => addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2798 file,2948 .path = test_path,
2799 link_mode,2949 .file = file,
2800 pq.query,2950 .must_link = pq.query.must_link,
2801 fallback_soname,2951 .hidden = pq.query.hidden,
2802 );2952 }),
2953 }
2954 return .ok;
2803}2955}
28042956
2805pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {2957pub 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,...@@ -119,10 +119,6 @@ dead_strip_dylibs: bool,
119undefined_treatment: UndefinedTreatment,119undefined_treatment: UndefinedTreatment,
120/// TODO: delete this, libraries need to be resolved by the frontend instead120/// TODO: delete this, libraries need to be resolved by the frontend instead
121lib_directories: []const Directory,121lib_directories: []const Directory,
122/// Resolved list of framework search directories
123framework_dirs: []const []const u8,
124/// List of input frameworks
125frameworks: []const Framework,
126/// Install name for the dylib.122/// Install name for the dylib.
127/// TODO: unify with soname123/// TODO: unify with soname
128install_name: ?[]const u8,124install_name: ?[]const u8,
...@@ -196,7 +192,6 @@ pub fn createEmpty(...@@ -196,7 +192,6 @@ pub fn createEmpty(
196 .headerpad_size = options.headerpad_size,192 .headerpad_size = options.headerpad_size,
197 .headerpad_max_install_names = options.headerpad_max_install_names,193 .headerpad_max_install_names = options.headerpad_max_install_names,
198 .dead_strip_dylibs = options.dead_strip_dylibs,194 .dead_strip_dylibs = options.dead_strip_dylibs,
199 .frameworks = options.frameworks,
200 .install_name = options.install_name,195 .install_name = options.install_name,
201 .entitlements = options.entitlements,196 .entitlements = options.entitlements,
202 .compatibility_version = options.compatibility_version,197 .compatibility_version = options.compatibility_version,
...@@ -211,7 +206,6 @@ pub fn createEmpty(...@@ -211,7 +206,6 @@ pub fn createEmpty(
211 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",206 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
212 // TODO delete this, directories must instead be resolved by the frontend207 // TODO delete this, directories must instead be resolved by the frontend
213 .lib_directories = options.lib_directories,208 .lib_directories = options.lib_directories,
214 .framework_dirs = options.framework_dirs,
215 .force_load_objc = options.force_load_objc,209 .force_load_objc = options.force_load_objc,
216 .discard_local_symbols = options.discard_local_symbols,210 .discard_local_symbols = options.discard_local_symbols,
217 };211 };
...@@ -372,35 +366,6 @@ pub fn flush(...@@ -372,35 +366,6 @@ pub fn flush(
372 diags.addParseError(input.path(), "failed to read input file: {t}", .{err});366 diags.addParseError(input.path(), "failed to read input file: {t}", .{err});
373 }367 }
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
404 try self.parseInputFiles();369 try self.parseInputFiles();
405 self.parseDependentDylibs() catch |err| {370 self.parseDependentDylibs() catch |err| {
406 switch (err) {371 switch (err) {
...@@ -716,20 +681,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -716,20 +681,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
716 },681 },
717 };682 };
718683
719 for (self.framework_dirs) |f_dir| {684 for (comp.framework_dirs) |f_dir| {
720 try argv.append("-F");685 try argv.append("-F");
721 try argv.append(f_dir);686 try argv.append(f_dir.path orelse ".");
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);
733 }687 }
734688
735 if (comp.config.link_libcpp) {689 if (comp.config.link_libcpp) {
...@@ -763,7 +717,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {...@@ -763,7 +717,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
763 log.debug("classifying input file {f}", .{path});717 log.debug("classifying input file {f}", .{path});
764718
765 const fh = try self.addFileHandle(file);719 const fh = try self.addFileHandle(file);
766 var buffer: [Archive.SARMAG]u8 = undefined;720 var buffer: [macho.ARMAG.len]u8 = undefined;
767721
768 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);722 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
769 const offset = if (fat_arch) |fa| fa.offset else 0;723 const offset = if (fat_arch) |fa| fa.offset else 0;
...@@ -778,7 +732,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {...@@ -778,7 +732,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
778 return;732 return;
779 }733 }
780 if (readArMagic(io, file, offset, &buffer) catch null) |ar_magic| blk: {734 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;
782 try self.addArchive(input.archive, fh, fat_arch);736 try self.addArchive(input.archive, fh, fat_arch);
783 return;737 return;
784 }738 }
...@@ -808,10 +762,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6...@@ -808,10 +762,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6
808 return hdr;762 return hdr;
809}763}
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 {
812 const nread = try file.readPositionalAll(io, buffer, offset);766 const nread = try file.readPositionalAll(io, buffer, offset);
813 if (nread != buffer.len) return error.InputOutput;767 if (nread != buffer.len) return error.InputOutput;
814 return buffer[0..Archive.SARMAG];768 return buffer[0..macho.ARMAG.len];
815}769}
816770
817fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {771fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {
...@@ -1034,10 +988,6 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1034,10 +988,6 @@ fn parseDependentDylibs(self: *MachO) !void {
1034 const comp = self.base.comp;988 const comp = self.base.comp;
1035 const gpa = comp.gpa;989 const gpa = comp.gpa;
1036 const io = comp.io;990 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
1042 var arena_alloc = std.heap.ArenaAllocator.init(gpa);992 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1043 defer arena_alloc.deinit();993 defer arena_alloc.deinit();
...@@ -1072,14 +1022,14 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1072,14 +1022,14 @@ fn parseDependentDylibs(self: *MachO) !void {
1072 const stem = fs.path.stem(id.name);1022 const stem = fs.path.stem(id.name);
10731023
1074 // Framework1024 // Framework
1075 for (framework_dirs) |dir| {1025 for (comp.framework_dirs) |dir| {
1076 test_path.clearRetainingCapacity();1026 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;
1078 }1028 }
10791029
1080 // Library1030 // Library
1081 const lib_name = eatPrefix(stem, "lib") orelse stem;1031 const lib_name = eatPrefix(stem, "lib") orelse stem;
1082 for (lib_directories) |lib_directory| {1032 for (self.lib_directories) |lib_directory| {
1083 test_path.clearRetainingCapacity();1033 test_path.clearRetainingCapacity();
1084 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;1034 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
1085 }1035 }
src/link/MachO/Archive.zig+4-15
...@@ -17,7 +17,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File...@@ -17,7 +17,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
17 const offset = if (fat_arch) |ar| ar.offset else 0;17 const offset = if (fat_arch) |ar| ar.offset else 0;
18 const end_pos = if (fat_arch) |ar| offset + ar.size else (try handle.stat(io)).size;18 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;
21 while (true) {21 while (true) {
22 if (pos >= end_pos) break;22 if (pos >= end_pos) break;
23 if (!mem.isAligned(pos, 2)) pos += 1;23 if (!mem.isAligned(pos, 2)) pos += 1;
...@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File...@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
30 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;30 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
31 pos += @sizeOf(ar_hdr);31 pos += @sizeOf(ar_hdr);
3232
33 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {33 if (!mem.eql(u8, &hdr.ar_fmag, macho.ARFMAG)) {
34 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{34 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),
36 });36 });
37 }37 }
3838
...@@ -116,17 +116,6 @@ pub fn writeHeader(...@@ -116,17 +116,6 @@ pub fn writeHeader(
116 }116 }
117}117}
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
130pub const SYMDEF = "__.SYMDEF";119pub const SYMDEF = "__.SYMDEF";
131pub const SYMDEF64 = "__.SYMDEF_64";120pub const SYMDEF64 = "__.SYMDEF_64";
132pub const SYMDEF_SORTED = "__.SYMDEF SORTED";121pub const SYMDEF_SORTED = "__.SYMDEF SORTED";
...@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {...@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {
146 /// File size, in ASCII decimal.135 /// File size, in ASCII decimal.
147 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,136 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,
148 /// Always contains ARFMAG.137 /// Always contains ARFMAG.
149 ar_fmag: [2]u8 = ARFMAG.*,138 ar_fmag: [2]u8 = macho.ARFMAG.*,
150139
151 fn date(self: ar_hdr) !u64 {140 fn date(self: ar_hdr) !u64 {
152 const value = mem.trimEnd(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});141 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 {...@@ -151,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {
151151
152 // Update file offsets of contributing objects152 // Update file offsets of contributing objects
153 const total_size: usize = blk: {153 const total_size: usize = blk: {
154 var pos: usize = Archive.SARMAG;154 var pos: usize = macho.ARMAG.len;
155 pos += @sizeOf(Archive.ar_hdr);155 pos += @sizeOf(Archive.ar_hdr);
156 pos += Archive.SYMDEF.len + 1;156 pos += Archive.SYMDEF.len + 1;
157 pos = mem.alignForward(usize, pos, 8);157 pos = mem.alignForward(usize, pos, 8);
...@@ -194,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {...@@ -194,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {
194 var writer: Writer = .fixed(buffer);194 var writer: Writer = .fixed(buffer);
195195
196 // Write magic196 // Write magic
197 writer.writeAll(Archive.ARMAG) catch unreachable;197 writer.writeAll(macho.ARMAG) catch unreachable;
198198
199 // Write symtab199 // Write symtab
200 ar_symtab.write(format, macho_file, &writer) catch |err|200 ar_symtab.write(format, macho_file, &writer) catch |err|
src/main.zig+126-150
...@@ -893,13 +893,6 @@ const ArgsIterator = struct {...@@ -893,13 +893,6 @@ const ArgsIterator = struct {
893 }893 }
894};894};
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
903const CliModule = struct {896const CliModule = struct {
904 root_path: []const u8,897 root_path: []const u8,
905 root_src_path: []const u8,898 root_src_path: []const u8,
...@@ -1117,12 +1110,12 @@ fn buildOutputType(...@@ -1117,12 +1110,12 @@ fn buildOutputType(
11171110
1118 .llvm_m_args = .empty,1111 .llvm_m_args = .empty,
1119 .sysroot = null,1112 .sysroot = null,
1120 .lib_directories = .empty, // populated by createModule()
1121 .lib_dir_args = .empty, // populated from CLI arg parsing1113 .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()
1122 .libc_installation = null,1117 .libc_installation = null,
1123 .want_native_include_dirs = false,1118 .want_native_include_dirs = false,
1124 .frameworks = .empty,
1125 .framework_dirs = .empty,
1126 .rpath_list = .empty,1119 .rpath_list = .empty,
1127 .each_lib_rpath = null,1120 .each_lib_rpath = null,
1128 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),1121 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),
...@@ -1275,13 +1268,25 @@ fn buildOutputType(...@@ -1275,13 +1268,25 @@ fn buildOutputType(
1275 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {1268 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1276 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());1269 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
1277 } else if (mem.eql(u8, arg, "-F")) {1270 } 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());
1279 } else if (mem.eql(u8, arg, "-framework")) {1272 } 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 } });
1281 } else if (mem.eql(u8, arg, "-weak_framework")) {1278 } 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 } });
1283 } else if (mem.eql(u8, arg, "-needed_framework")) {1284 } 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 } });
1285 } else if (mem.eql(u8, arg, "-install_name")) {1290 } else if (mem.eql(u8, arg, "-install_name")) {
1286 install_name = args_iter.nextOrFatal();1291 install_name = args_iter.nextOrFatal();
1287 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {1292 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
...@@ -1396,11 +1401,11 @@ fn buildOutputType(...@@ -1396,11 +1401,11 @@ fn buildOutputType(
1396 } else if (mem.eql(u8, arg, "-iframework")) {1401 } else if (mem.eql(u8, arg, "-iframework")) {
1397 const path = args_iter.nextOrFatal();1402 const path = args_iter.nextOrFatal();
1398 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);1403 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1399 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F1404 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
1400 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {1405 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
1401 const path = args_iter.nextOrFatal();1406 const path = args_iter.nextOrFatal();
1402 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);1407 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1403 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F1408 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
1404 } else if (mem.eql(u8, arg, "--version")) {1409 } else if (mem.eql(u8, arg, "--version")) {
1405 const next_arg = args_iter.nextOrFatal();1410 const next_arg = args_iter.nextOrFatal();
1406 version = std.SemanticVersion.parse(next_arg) catch |err| {1411 version = std.SemanticVersion.parse(next_arg) catch |err| {
...@@ -1828,7 +1833,7 @@ fn buildOutputType(...@@ -1828,7 +1833,7 @@ fn buildOutputType(
1828 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {1833 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
1829 try create_module.lib_dir_args.append(arena, rest);1834 try create_module.lib_dir_args.append(arena, rest);
1830 } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {1835 } 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);
1832 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {1837 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
1833 // We don't know whether this library is part of libc1838 // We don't know whether this library is part of libc
1834 // or libc++ until we resolve the target, so we append1839 // or libc++ until we resolve the target, so we append
...@@ -2532,8 +2537,12 @@ fn buildOutputType(...@@ -2532,8 +2537,12 @@ fn buildOutputType(
2532 disable_c_depfile = true;2537 disable_c_depfile = true;
2533 try cc_argv.appendSlice(arena, it.other_args);2538 try cc_argv.appendSlice(arena, it.other_args);
2534 },2539 },
2535 .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),2540 .framework_dir => try create_module.framework_dir_args.append(arena, it.only_arg),
2536 .framework => try create_module.frameworks.put(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 } }),
2537 .nostdlibinc => create_module.want_native_include_dirs = false,2546 .nostdlibinc => create_module.want_native_include_dirs = false,
2538 .strip => mod_opts.strip = true,2547 .strip => mod_opts.strip = true,
2539 .exec_model => {2548 .exec_model => {
...@@ -2561,7 +2570,11 @@ fn buildOutputType(...@@ -2561,7 +2570,11 @@ fn buildOutputType(
2561 },2570 },
2562 .name_done = false,2571 .name_done = false,
2563 } }),2572 } }),
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 } }),
2565 .headerpad_max_install_names => headerpad_max_install_names = true,2578 .headerpad_max_install_names => headerpad_max_install_names = true,
2566 .compress_debug_sections => {2579 .compress_debug_sections => {
2567 if (it.only_arg.len == 0) {2580 if (it.only_arg.len == 0) {
...@@ -2914,11 +2927,23 @@ fn buildOutputType(...@@ -2914,11 +2927,23 @@ fn buildOutputType(
2914 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });2927 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });
2915 };2928 };
2916 } else if (mem.eql(u8, arg, "-framework")) {2929 } 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 } });
2918 } else if (mem.eql(u8, arg, "-weak_framework")) {2935 } 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 } });
2920 } else if (mem.eql(u8, arg, "-needed_framework")) {2941 } 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 } });
2922 } else if (mem.eql(u8, arg, "-needed_library")) {2947 } else if (mem.eql(u8, arg, "-needed_library")) {
2923 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{2948 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2924 .name = linker_args_it.nextOrFatal(),2949 .name = linker_args_it.nextOrFatal(),
...@@ -3400,59 +3425,6 @@ fn buildOutputType(...@@ -3400,59 +3425,6 @@ fn buildOutputType(
3400 }3425 }
3401 }3426 }
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
3456 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {3428 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
3457 const total_obj_count = create_module.c_source_files.items.len +3429 const total_obj_count = create_module.c_source_files.items.len +
3458 @intFromBool(root_src_file != null) +3430 @intFromBool(root_src_file != null) +
...@@ -3689,6 +3661,7 @@ fn buildOutputType(...@@ -3689,6 +3661,7 @@ fn buildOutputType(
3689 .emit_docs = emit_docs_resolved,3661 .emit_docs = emit_docs_resolved,
3690 .emit_implib = emit_implib_resolved,3662 .emit_implib = emit_implib_resolved,
3691 .lib_directories = create_module.lib_directories.items,3663 .lib_directories = create_module.lib_directories.items,
3664 .framework_directories = create_module.framework_directories.items,
3692 .rpath_list = create_module.rpath_list.items,3665 .rpath_list = create_module.rpath_list.items,
3693 .symbol_wrap_set = symbol_wrap_set,3666 .symbol_wrap_set = symbol_wrap_set,
3694 .c_source_files = create_module.c_source_files.items,3667 .c_source_files = create_module.c_source_files.items,
...@@ -3697,8 +3670,6 @@ fn buildOutputType(...@@ -3697,8 +3670,6 @@ fn buildOutputType(
3697 .rc_includes = rc_includes,3670 .rc_includes = rc_includes,
3698 .mingw_unicode_entry_point = mingw_unicode_entry_point,3671 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3699 .link_inputs = create_module.link_inputs.items,3672 .link_inputs = create_module.link_inputs.items,
3700 .framework_dirs = create_module.framework_dirs.items,
3701 .frameworks = resolved_frameworks.items,
3702 .windows_lib_names = create_module.windows_libs.keys(),3673 .windows_lib_names = create_module.windows_libs.keys(),
3703 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,3674 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,
3704 .want_ubsan_rt = want_ubsan_rt,3675 .want_ubsan_rt = want_ubsan_rt,
...@@ -4041,13 +4012,13 @@ const CreateModule = struct {...@@ -4041,13 +4012,13 @@ const CreateModule = struct {
4041 /// CPU features.4012 /// CPU features.
4042 llvm_m_args: std.ArrayList([]const u8),4013 llvm_m_args: std.ArrayList([]const u8),
4043 sysroot: ?[]const u8,4014 sysroot: ?[]const u8,
4044 lib_directories: std.ArrayList(Directory),
4045 lib_dir_args: std.ArrayList([]const u8),4015 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),
4046 libc_installation: ?LibCInstallation,4019 libc_installation: ?LibCInstallation,
4047 want_native_include_dirs: bool,4020 want_native_include_dirs: bool,
4048 frameworks: std.array_hash_map.String(Framework),
4049 native_system_include_paths: []const []const u8,4021 native_system_include_paths: []const []const u8,
4050 framework_dirs: std.ArrayList([]const u8),
4051 rpath_list: std.ArrayList([]const u8),4022 rpath_list: std.ArrayList([]const u8),
4052 each_lib_rpath: ?bool,4023 each_lib_rpath: ?bool,
4053 libc_paths_file: ?[]const u8,4024 libc_paths_file: ?[]const u8,
...@@ -4158,7 +4129,7 @@ fn createModule(...@@ -4158,7 +4129,7 @@ fn createModule(
4158 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;4129 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
4159 defer unresolved_link_inputs.deinit(gpa);4130 defer unresolved_link_inputs.deinit(gpa);
4160 try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);4131 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;
4162 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {4133 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
4163 .name_query => |nq| {4134 .name_query => |nq| {
4164 const lib_name = nq.name;4135 const lib_name = nq.name;
...@@ -4199,39 +4170,52 @@ fn createModule(...@@ -4199,39 +4170,52 @@ fn createModule(
4199 }4170 }
42004171
4201 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);4172 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;
4203 },4178 },
4204 else => {4179 else => {
4205 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);4180 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4206 },4181 },
4207 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.4182 }; // 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
4211 // Resolve the library path arguments with respect to sysroot.4186 // Resolve the library path arguments with respect to sysroot.
4212 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);4187 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
4213 if (create_module.sysroot) |root| {4188 for (create_module.lib_dir_args.items) |dir_arg| {
4214 for (create_module.lib_dir_args.items) |lib_dir_arg| {4189 const path: []const u8 = path: {
4215 if (fs.path.isAbsolute(lib_dir_arg)) {4190 if (fs.path.isAbsolute(dir_arg)) {
4216 const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];4191 if (create_module.sysroot) |sysroot| {
4217 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });4192 // Change the path root to the given sysroot.
4218 addLibDirectoryWarn(io, &create_module.lib_directories, full_path);4193 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4219 } else {4194 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4220 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);4195 }
4221 }4196 }
4222 }4197 break :path dir_arg;
4223 } else {4198 };
4224 for (create_module.lib_dir_args.items) |lib_dir_arg| {4199 appendLibDirOrWarn(io, &create_module.lib_directories, path);
4225 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4226 }
4227 }4200 }
4228 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.4201 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()) {4203 // Likewise for framework path arguments.
4231 // If we want to link against frameworks, we need system headers.4204 try create_module.framework_directories.ensureUnusedCapacity(arena, create_module.framework_dir_args.items.len);
4232 if (create_module.frameworks.count() > 0)4205 for (create_module.framework_dir_args.items) |dir_arg| {
4233 create_module.want_native_include_dirs = true;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);
4234 }4217 }
4218 create_module.framework_dir_args = undefined; // From here we use framework_directories instead.
42354219
4236 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {4220 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
4237 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);4221 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
...@@ -4253,11 +4237,17 @@ fn createModule(...@@ -4253,11 +4237,17 @@ fn createModule(
42534237
4254 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);4238 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);
4257 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);4240 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
42584241
4259 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);4242 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 }
4261 }4251 }
42624252
4263 if (create_module.libc_paths_file) |paths_file| {4253 if (create_module.libc_paths_file) |paths_file| {
...@@ -4266,7 +4256,7 @@ fn createModule(...@@ -4266,7 +4256,7 @@ fn createModule(
4266 }4256 }
42674257
4268 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and4258 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
4269 any_name_queries_remaining)4259 any_named_library_queries)
4270 {4260 {
4271 if (create_module.libc_installation == null) {4261 if (create_module.libc_installation == null) {
4272 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{4262 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{
...@@ -4277,9 +4267,19 @@ fn createModule(...@@ -4277,9 +4267,19 @@ fn createModule(
4277 fatal("unable to find native libc installation: {t}", .{err});4267 fatal("unable to find native libc installation: {t}", .{err});
4278 };4268 };
4279 }4269 }
4270
4280 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);4271 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
4281 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);4272
4282 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);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 );
4283 }4283 }
42844284
4285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.4285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
...@@ -4291,6 +4291,7 @@ fn createModule(...@@ -4291,6 +4291,7 @@ fn createModule(
4291 &unresolved_link_inputs,4291 &unresolved_link_inputs,
4292 &create_module.link_inputs,4292 &create_module.link_inputs,
4293 create_module.lib_directories.items,4293 create_module.lib_directories.items,
4294 create_module.framework_directories.items,
4294 color,4295 color,
4295 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});4296 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});
42964297
...@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {...@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {
6085 };6086 };
6086};6087};
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
6115fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {6089fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
6116 return stringToEnum(std.zig.RcIncludes, arg) orelse6090 return stringToEnum(std.zig.RcIncludes, arg) orelse
6117 fatal("unsupported rc includes type: {q}", .{arg});6091 fatal("unsupported rc includes type: {q}", .{arg});
...@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {...@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
6222 return false;6196 return false;
6223}6197}
62246198
6225fn addLibDirectoryWarn(io: Io, lib_directories: *std.ArrayList(Directory), path: []const u8) void {6199fn appendLibDirOrWarn(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6226 return addLibDirectoryWarn2(io, lib_directories, path, false);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 }
6227}6208}
62286209fn appendLibDirOrWarnAllowMissing(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6229fn addLibDirectoryWarn2(6210 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6230 io: Io,6211 dirs.appendAssumeCapacity(.{
6231 lib_directories: *std.ArrayList(Directory),6212 .path = path,
6232 path: []const u8,6213 .handle = handle,
6233 ignore_not_found: bool,6214 });
6234) void {6215 } else |err| switch (err) {
6235 lib_directories.appendAssumeCapacity(.{6216 error.FileNotFound => {}, // ignore
6236 .handle = Io.Dir.cwd().openDir(io, path, .{}) catch |err| {6217 else => |e| warn("unable to open library directory {q}: {t}", .{ path, e }),
6237 if (err == error.FileNotFound and ignore_not_found) return;6218 }
6238 warn("unable to open library directory {q}: {t}", .{ path, err });
6239 return;
6240 },
6241 .path = path,
6242 });
6243}6219}
62446220
6245const IoImpl = switch (build_options.io_mode) {6221const IoImpl = switch (build_options.io_mode) {