| author | |
| committer | |
| log | 4793dafa0433d758b126f2f97d1de031b99e0fa1 |
| tree | db8757b4acee55a28e1d6b5b6616fc2c3626eb7d |
| parent | e687c87d691518d63414aed4f355dabbd8565dc3 |
5 files changed, 121 insertions(+), 62 deletions(-)
src/Compilation.zig+11-3| ... | ... | @@ -507,7 +507,8 @@ pub const InitOptions = struct { |
| 507 | 507 | c_source_files: []const CSourceFile = &[0]CSourceFile{}, |
| 508 | 508 | link_objects: []LinkObject = &[0]LinkObject{}, |
| 509 | 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 | 512 | system_lib_names: []const []const u8 = &.{}, |
| 512 | 513 | system_lib_infos: []const SystemLib = &.{}, |
| 513 | 514 | /// These correspond to the WASI libc emulated subcomponents including: |
| ... | ... | @@ -830,7 +831,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 830 | 831 | // Our linker can't handle objects or most advanced options yet. |
| 831 | 832 | if (options.link_objects.len != 0 or |
| 832 | 833 | options.c_source_files.len != 0 or |
| 833 | options.frameworks.count() != 0 or | |
| 834 | options.framework_names.len != 0 or | |
| 834 | 835 | options.system_lib_names.len != 0 or |
| 835 | 836 | options.link_libc or options.link_libcpp or |
| 836 | 837 | link_eh_frame_hdr or |
| ... | ... | @@ -1446,6 +1447,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1446 | 1447 | system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]); |
| 1447 | 1448 | } |
| 1448 | 1449 | |
| 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 | 1457 | const bin_file = try link.File.openPath(gpa, .{ |
| 1450 | 1458 | .emit = bin_file_emit, |
| 1451 | 1459 | .implib_emit = implib_emit, |
| ... | ... | @@ -1465,7 +1473,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1465 | 1473 | .link_libcpp = link_libcpp, |
| 1466 | 1474 | .link_libunwind = link_libunwind, |
| 1467 | 1475 | .objects = options.link_objects, |
| 1468 | .frameworks = options.frameworks, | |
| 1476 | .frameworks = frameworks, | |
| 1469 | 1477 | .framework_dirs = options.framework_dirs, |
| 1470 | 1478 | .system_libs = system_libs, |
| 1471 | 1479 | .wasi_emulated_libs = options.wasi_emulated_libs, |
src/link.zig+1| ... | ... | @@ -37,6 +37,7 @@ pub const SystemLib = struct { |
| 37 | 37 | pub const Framework = struct { |
| 38 | 38 | needed: bool = false, |
| 39 | 39 | weak: bool = false, |
| 40 | path: []const u8, | |
| 40 | 41 | }; |
| 41 | 42 | |
| 42 | 43 | pub 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 | 870 | return false; |
| 871 | 871 | } |
| 872 | 872 | |
| 873 | pub fn resolveLib( | |
| 873 | fn resolveLib( | |
| 874 | 874 | arena: Allocator, |
| 875 | 875 | search_dir: []const u8, |
| 876 | 876 | name: []const u8, |
| ... | ... | @@ -889,26 +889,6 @@ pub fn resolveLib( |
| 889 | 889 | return full_path; |
| 890 | 890 | } |
| 891 | 891 | |
| 892 | pub 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 | ||
| 912 | 892 | const ParseDylibError = error{ |
| 913 | 893 | OutOfMemory, |
| 914 | 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 | 3512 | try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom |
| 3513 | 3513 | try zld.strtab.buffer.append(gpa, 0); |
| 3514 | 3514 | |
| 3515 | var lib_not_found = false; | |
| 3516 | var framework_not_found = false; | |
| 3517 | ||
| 3518 | 3515 | // Positional arguments to the linker such as object files and static archives. |
| 3519 | 3516 | var positionals = std.ArrayList([]const u8).init(arena); |
| 3520 | 3517 | try positionals.ensureUnusedCapacity(options.objects.len); |
| ... | ... | @@ -3557,34 +3554,17 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3557 | 3554 | for (vals) |v| libs.putAssumeCapacity(v.path.?, v); |
| 3558 | 3555 | } |
| 3559 | 3556 | |
| 3560 | try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs); | |
| 3561 | ||
| 3562 | // frameworks | |
| 3563 | outer: for (options.frameworks.keys()) |f_name| { | |
| 3564 | for (options.framework_dirs) |dir| { | |
| 3565 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { | |
| 3566 | if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| { | |
| 3567 | const info = options.frameworks.get(f_name).?; | |
| 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 | } | |
| 3557 | { | |
| 3558 | const vals = options.frameworks.values(); | |
| 3559 | try libs.ensureUnusedCapacity(vals.len); | |
| 3560 | for (vals) |v| libs.putAssumeCapacity(v.path, .{ | |
| 3561 | .needed = v.needed, | |
| 3562 | .weak = v.weak, | |
| 3563 | .path = v.path, | |
| 3564 | }); | |
| 3580 | 3565 | } |
| 3581 | 3566 | |
| 3582 | if (framework_not_found) { | |
| 3583 | log.warn("Framework search paths:", .{}); | |
| 3584 | for (options.framework_dirs) |dir| { | |
| 3585 | log.warn(" {s}", .{dir}); | |
| 3586 | } | |
| 3587 | } | |
| 3567 | try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs); | |
| 3588 | 3568 | |
| 3589 | 3569 | if (options.verbose_link) { |
| 3590 | 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 | 3711 | if (resolver.unresolved.count() > 0) { |
| 3732 | 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 | } | |
| 3740 | 3714 | |
| 3741 | 3715 | if (options.output_mode == .Exe) { |
| 3742 | 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 | 746 | } |
| 747 | 747 | }; |
| 748 | 748 | |
| 749 | /// Similar to `link.Framework` except it doesn't store yet unresolved | |
| 750 | /// path to the framework. | |
| 751 | const Framework = struct { | |
| 752 | needed: bool = false, | |
| 753 | weak: bool = false, | |
| 754 | }; | |
| 755 | ||
| 749 | 756 | const CliModule = struct { |
| 750 | 757 | mod: *Package, |
| 751 | 758 | /// still in CLI arg format |
| ... | ... | @@ -919,7 +926,7 @@ fn buildOutputType( |
| 919 | 926 | var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); |
| 920 | 927 | var link_objects = std.ArrayList(Compilation.LinkObject).init(arena); |
| 921 | 928 | var framework_dirs = std.ArrayList([]const u8).init(arena); |
| 922 | var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{}; | |
| 929 | var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{}; | |
| 923 | 930 | // null means replace with the test executable binary |
| 924 | 931 | var test_exec_args = std.ArrayList(?[]const u8).init(arena); |
| 925 | 932 | var linker_export_symbol_names = std.ArrayList([]const u8).init(arena); |
| ... | ... | @@ -2566,7 +2573,7 @@ fn buildOutputType( |
| 2566 | 2573 | want_native_include_dirs = true; |
| 2567 | 2574 | } |
| 2568 | 2575 | |
| 2569 | // Resolve the library and framework path arguments with respect to sysroot. | |
| 2576 | // Resolve the library path arguments with respect to sysroot. | |
| 2570 | 2577 | var lib_dirs = std.ArrayList([]const u8).init(arena); |
| 2571 | 2578 | if (sysroot) |root| { |
| 2572 | 2579 | for (lib_dir_args.items) |dir| { |
| ... | ... | @@ -2868,6 +2875,65 @@ fn buildOutputType( |
| 2868 | 2875 | } |
| 2869 | 2876 | // After this point, resolved_system_libs is used instead of external_system_libs. |
| 2870 | 2877 | |
| 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 | 2937 | const object_format = target_info.target.ofmt; |
| 2872 | 2938 | |
| 2873 | 2939 | if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) { |
| ... | ... | @@ -3261,7 +3327,8 @@ fn buildOutputType( |
| 3261 | 3327 | .c_source_files = c_source_files.items, |
| 3262 | 3328 | .link_objects = link_objects.items, |
| 3263 | 3329 | .framework_dirs = framework_dirs.items, |
| 3264 | .frameworks = frameworks, | |
| 3330 | .framework_names = resolved_frameworks.items(.name), | |
| 3331 | .framework_infos = resolved_frameworks.items(.framework), | |
| 3265 | 3332 | .system_lib_names = resolved_system_libs.items(.name), |
| 3266 | 3333 | .system_lib_infos = resolved_system_libs.items(.lib), |
| 3267 | 3334 | .wasi_emulated_libs = wasi_emulated_libs.items, |
| ... | ... | @@ -6336,3 +6403,32 @@ fn accessLibPath( |
| 6336 | 6403 | |
| 6337 | 6404 | return false; |
| 6338 | 6405 | } |
| 6406 | ||
| 6407 | fn 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 | } |