authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-20 10:43:20+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-20 10:43:20+02:00
log4793dafa0433d758b126f2f97d1de031b99e0fa1
treedb8757b4acee55a28e1d6b5b6616fc2c3626eb7d
parente687c87d691518d63414aed4f355dabbd8565dc3

frontend: move framework path resolution from the linker to frontend


5 files changed, 121 insertions(+), 62 deletions(-)

src/Compilation.zig+11-3
...@@ -507,7 +507,8 @@ pub const InitOptions = struct {...@@ -507,7 +507,8 @@ pub const InitOptions = struct {
507 c_source_files: []const CSourceFile = &[0]CSourceFile{},507 c_source_files: []const CSourceFile = &[0]CSourceFile{},
508 link_objects: []LinkObject = &[0]LinkObject{},508 link_objects: []LinkObject = &[0]LinkObject{},
509 framework_dirs: []const []const u8 = &[0][]const u8{},509 framework_dirs: []const []const u8 = &[0][]const u8{},
510 frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},510 framework_names: []const []const u8 = &.{},
511 framework_infos: []const Framework = &.{},
511 system_lib_names: []const []const u8 = &.{},512 system_lib_names: []const []const u8 = &.{},
512 system_lib_infos: []const SystemLib = &.{},513 system_lib_infos: []const SystemLib = &.{},
513 /// These correspond to the WASI libc emulated subcomponents including:514 /// These correspond to the WASI libc emulated subcomponents including:
...@@ -830,7 +831,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -830,7 +831,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
830 // Our linker can't handle objects or most advanced options yet.831 // Our linker can't handle objects or most advanced options yet.
831 if (options.link_objects.len != 0 or832 if (options.link_objects.len != 0 or
832 options.c_source_files.len != 0 or833 options.c_source_files.len != 0 or
833 options.frameworks.count() != 0 or834 options.framework_names.len != 0 or
834 options.system_lib_names.len != 0 or835 options.system_lib_names.len != 0 or
835 options.link_libc or options.link_libcpp or836 options.link_libc or options.link_libcpp or
836 link_eh_frame_hdr or837 link_eh_frame_hdr or
...@@ -1446,6 +1447,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1446,6 +1447,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1446 system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]);1447 system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]);
1447 }1448 }
14481449
1450 var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
1451 errdefer frameworks.deinit(gpa);
1452 try frameworks.ensureTotalCapacity(gpa, options.framework_names.len);
1453 for (options.framework_names, options.framework_infos) |framework_name, info| {
1454 frameworks.putAssumeCapacity(framework_name, info);
1455 }
1456
1449 const bin_file = try link.File.openPath(gpa, .{1457 const bin_file = try link.File.openPath(gpa, .{
1450 .emit = bin_file_emit,1458 .emit = bin_file_emit,
1451 .implib_emit = implib_emit,1459 .implib_emit = implib_emit,
...@@ -1465,7 +1473,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1465,7 +1473,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1465 .link_libcpp = link_libcpp,1473 .link_libcpp = link_libcpp,
1466 .link_libunwind = link_libunwind,1474 .link_libunwind = link_libunwind,
1467 .objects = options.link_objects,1475 .objects = options.link_objects,
1468 .frameworks = options.frameworks,1476 .frameworks = frameworks,
1469 .framework_dirs = options.framework_dirs,1477 .framework_dirs = options.framework_dirs,
1470 .system_libs = system_libs,1478 .system_libs = system_libs,
1471 .wasi_emulated_libs = options.wasi_emulated_libs,1479 .wasi_emulated_libs = options.wasi_emulated_libs,
src/link.zig+1
...@@ -37,6 +37,7 @@ pub const SystemLib = struct {...@@ -37,6 +37,7 @@ pub const SystemLib = struct {
37pub const Framework = struct {37pub const Framework = struct {
38 needed: bool = false,38 needed: bool = false,
39 weak: bool = false,39 weak: bool = false,
40 path: []const u8,
40};41};
4142
42pub const SortSection = enum { name, alignment };43pub const SortSection = enum { name, alignment };
src/link/MachO.zig+1-21
...@@ -870,7 +870,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:...@@ -870,7 +870,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
870 return false;870 return false;
871}871}
872872
873pub fn resolveLib(873fn resolveLib(
874 arena: Allocator,874 arena: Allocator,
875 search_dir: []const u8,875 search_dir: []const u8,
876 name: []const u8,876 name: []const u8,
...@@ -889,26 +889,6 @@ pub fn resolveLib(...@@ -889,26 +889,6 @@ pub fn resolveLib(
889 return full_path;889 return full_path;
890}890}
891891
892pub fn resolveFramework(
893 arena: Allocator,
894 search_dir: []const u8,
895 name: []const u8,
896 ext: []const u8,
897) !?[]const u8 {
898 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
899 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
900 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name });
901
902 // Check if the file exists.
903 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
904 error.FileNotFound => return null,
905 else => |e| return e,
906 };
907 defer tmp.close();
908
909 return full_path;
910}
911
912const ParseDylibError = error{892const ParseDylibError = error{
913 OutOfMemory,893 OutOfMemory,
914 EmptyStubFile,894 EmptyStubFile,
src/link/MachO/zld.zig+9-35
...@@ -3512,9 +3512,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3512,9 +3512,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3512 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom3512 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom
3513 try zld.strtab.buffer.append(gpa, 0);3513 try zld.strtab.buffer.append(gpa, 0);
35143514
3515 var lib_not_found = false;
3516 var framework_not_found = false;
3517
3518 // Positional arguments to the linker such as object files and static archives.3515 // Positional arguments to the linker such as object files and static archives.
3519 var positionals = std.ArrayList([]const u8).init(arena);3516 var positionals = std.ArrayList([]const u8).init(arena);
3520 try positionals.ensureUnusedCapacity(options.objects.len);3517 try positionals.ensureUnusedCapacity(options.objects.len);
...@@ -3557,34 +3554,17 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3557,34 +3554,17 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3557 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);3554 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
3558 }3555 }
35593556
3560 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);3557 {
35613558 const vals = options.frameworks.values();
3562 // frameworks3559 try libs.ensureUnusedCapacity(vals.len);
3563 outer: for (options.frameworks.keys()) |f_name| {3560 for (vals) |v| libs.putAssumeCapacity(v.path, .{
3564 for (options.framework_dirs) |dir| {3561 .needed = v.needed,
3565 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {3562 .weak = v.weak,
3566 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {3563 .path = v.path,
3567 const info = options.frameworks.get(f_name).?;3564 });
3568 try libs.put(full_path, .{
3569 .needed = info.needed,
3570 .weak = info.weak,
3571 .path = full_path,
3572 });
3573 continue :outer;
3574 }
3575 }
3576 } else {
3577 log.warn("framework not found for '-framework {s}'", .{f_name});
3578 framework_not_found = true;
3579 }
3580 }3565 }
35813566
3582 if (framework_not_found) {3567 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
3583 log.warn("Framework search paths:", .{});
3584 for (options.framework_dirs) |dir| {
3585 log.warn(" {s}", .{dir});
3586 }
3587 }
35883568
3589 if (options.verbose_link) {3569 if (options.verbose_link) {
3590 var argv = std.ArrayList([]const u8).init(arena);3570 var argv = std.ArrayList([]const u8).init(arena);
...@@ -3731,12 +3711,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3731,12 +3711,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3731 if (resolver.unresolved.count() > 0) {3711 if (resolver.unresolved.count() > 0) {
3732 return error.UndefinedSymbolReference;3712 return error.UndefinedSymbolReference;
3733 }3713 }
3734 if (lib_not_found) {
3735 return error.LibraryNotFound;
3736 }
3737 if (framework_not_found) {
3738 return error.FrameworkNotFound;
3739 }
37403714
3741 if (options.output_mode == .Exe) {3715 if (options.output_mode == .Exe) {
3742 const entry_name = options.entry orelse load_commands.default_entry_point;3716 const entry_name = options.entry orelse load_commands.default_entry_point;
src/main.zig+99-3
...@@ -746,6 +746,13 @@ const SystemLib = struct {...@@ -746,6 +746,13 @@ const SystemLib = struct {
746 }746 }
747};747};
748748
749/// Similar to `link.Framework` except it doesn't store yet unresolved
750/// path to the framework.
751const Framework = struct {
752 needed: bool = false,
753 weak: bool = false,
754};
755
749const CliModule = struct {756const CliModule = struct {
750 mod: *Package,757 mod: *Package,
751 /// still in CLI arg format758 /// still in CLI arg format
...@@ -919,7 +926,7 @@ fn buildOutputType(...@@ -919,7 +926,7 @@ fn buildOutputType(
919 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);926 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
920 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);927 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
921 var framework_dirs = std.ArrayList([]const u8).init(arena);928 var framework_dirs = std.ArrayList([]const u8).init(arena);
922 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};929 var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
923 // null means replace with the test executable binary930 // null means replace with the test executable binary
924 var test_exec_args = std.ArrayList(?[]const u8).init(arena);931 var test_exec_args = std.ArrayList(?[]const u8).init(arena);
925 var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);932 var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);
...@@ -2566,7 +2573,7 @@ fn buildOutputType(...@@ -2566,7 +2573,7 @@ fn buildOutputType(
2566 want_native_include_dirs = true;2573 want_native_include_dirs = true;
2567 }2574 }
25682575
2569 // Resolve the library and framework path arguments with respect to sysroot.2576 // Resolve the library path arguments with respect to sysroot.
2570 var lib_dirs = std.ArrayList([]const u8).init(arena);2577 var lib_dirs = std.ArrayList([]const u8).init(arena);
2571 if (sysroot) |root| {2578 if (sysroot) |root| {
2572 for (lib_dir_args.items) |dir| {2579 for (lib_dir_args.items) |dir| {
...@@ -2868,6 +2875,65 @@ fn buildOutputType(...@@ -2868,6 +2875,65 @@ fn buildOutputType(
2868 }2875 }
2869 // After this point, resolved_system_libs is used instead of external_system_libs.2876 // After this point, resolved_system_libs is used instead of external_system_libs.
28702877
2878 // We now repeat part of the process for frameworks.
2879 var resolved_frameworks: std.MultiArrayList(struct {
2880 name: []const u8,
2881 framework: Compilation.Framework,
2882 }) = .{};
2883
2884 if (frameworks.keys().len > 0) {
2885 var test_path = std.ArrayList(u8).init(gpa);
2886 defer test_path.deinit();
2887
2888 var checked_paths = std.ArrayList(u8).init(gpa);
2889 defer checked_paths.deinit();
2890
2891 var failed_frameworks = std.ArrayList(struct {
2892 name: []const u8,
2893 checked_paths: []const u8,
2894 }).init(arena);
2895
2896 framework: for (frameworks.keys(), frameworks.values()) |framework_name, info| {
2897 checked_paths.clearRetainingCapacity();
2898
2899 for (framework_dirs.items) |framework_dir_path| {
2900 if (try accessFrameworkPath(
2901 &test_path,
2902 &checked_paths,
2903 framework_dir_path,
2904 framework_name,
2905 )) {
2906 const path = try arena.dupe(u8, test_path.items);
2907 try resolved_frameworks.append(arena, .{
2908 .name = framework_name,
2909 .framework = .{
2910 .needed = info.needed,
2911 .weak = info.weak,
2912 .path = path,
2913 },
2914 });
2915 continue :framework;
2916 }
2917 }
2918
2919 try failed_frameworks.append(.{
2920 .name = framework_name,
2921 .checked_paths = try arena.dupe(u8, checked_paths.items),
2922 });
2923 }
2924
2925 if (failed_frameworks.items.len > 0) {
2926 for (failed_frameworks.items) |f| {
2927 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2928 std.log.err("unable to find framework '{s}'. searched paths: {s}", .{
2929 f.name, searched_paths,
2930 });
2931 }
2932 process.exit(1);
2933 }
2934 }
2935 // After this point, resolved_frameworks is used instead of frameworks.
2936
2871 const object_format = target_info.target.ofmt;2937 const object_format = target_info.target.ofmt;
28722938
2873 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {2939 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
...@@ -3261,7 +3327,8 @@ fn buildOutputType(...@@ -3261,7 +3327,8 @@ fn buildOutputType(
3261 .c_source_files = c_source_files.items,3327 .c_source_files = c_source_files.items,
3262 .link_objects = link_objects.items,3328 .link_objects = link_objects.items,
3263 .framework_dirs = framework_dirs.items,3329 .framework_dirs = framework_dirs.items,
3264 .frameworks = frameworks,3330 .framework_names = resolved_frameworks.items(.name),
3331 .framework_infos = resolved_frameworks.items(.framework),
3265 .system_lib_names = resolved_system_libs.items(.name),3332 .system_lib_names = resolved_system_libs.items(.name),
3266 .system_lib_infos = resolved_system_libs.items(.lib),3333 .system_lib_infos = resolved_system_libs.items(.lib),
3267 .wasi_emulated_libs = wasi_emulated_libs.items,3334 .wasi_emulated_libs = wasi_emulated_libs.items,
...@@ -6336,3 +6403,32 @@ fn accessLibPath(...@@ -6336,3 +6403,32 @@ fn accessLibPath(
63366403
6337 return false;6404 return false;
6338}6405}
6406
6407fn accessFrameworkPath(
6408 test_path: *std.ArrayList(u8),
6409 checked_paths: *std.ArrayList(u8),
6410 framework_dir_path: []const u8,
6411 framework_name: []const u8,
6412) !bool {
6413 const sep = fs.path.sep_str;
6414
6415 for (&[_][]const u8{ "tbd", "dylib" }) |ext| {
6416 test_path.clearRetainingCapacity();
6417 try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}.{s}", .{
6418 framework_dir_path,
6419 framework_name,
6420 framework_name,
6421 ext,
6422 });
6423 try checked_paths.writer().print("\n {s}", .{test_path.items});
6424 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6425 error.FileNotFound => continue,
6426 else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{
6427 ext, test_path.items, @errorName(e),
6428 }),
6429 };
6430 return true;
6431 }
6432
6433 return false;
6434}