| ... | ... | @@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 844 | 844 | try argv.append(arg); |
| 845 | 845 | } |
| 846 | 846 | |
| 847 | for (self.lib_dirs) |lib_dir| { |
| 848 | const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}); |
| 849 | try argv.append(arg); |
| 850 | } |
| 851 | |
| 847 | 852 | for (self.frameworks) |framework| { |
| 848 | 853 | const name = std.fs.path.stem(framework.path); |
| 849 | 854 | const arg = if (framework.needed) |
| ... | ... | @@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 855 | 860 | try argv.append(arg); |
| 856 | 861 | } |
| 857 | 862 | |
| 863 | for (self.framework_dirs) |f_dir| { |
| 864 | try argv.append("-F"); |
| 865 | try argv.append(f_dir); |
| 866 | } |
| 867 | |
| 858 | 868 | if (self.base.isDynLib() and self.base.allow_shlib_undefined) { |
| 859 | 869 | try argv.append("-undefined"); |
| 860 | 870 | try argv.append("dynamic_lookup"); |
| ... | ... | @@ -887,11 +897,11 @@ pub fn resolveLibSystem( |
| 887 | 897 | if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { |
| 888 | 898 | .sdk => { |
| 889 | 899 | const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" }); |
| 890 | | if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success; |
| 900 | if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; |
| 891 | 901 | }, |
| 892 | 902 | .vendored => { |
| 893 | 903 | const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" }); |
| 894 | | if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success; |
| 904 | if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; |
| 895 | 905 | }, |
| 896 | 906 | }; |
| 897 | 907 | |
| ... | ... | @@ -906,51 +916,6 @@ pub fn resolveLibSystem( |
| 906 | 916 | }); |
| 907 | 917 | } |
| 908 | 918 | |
| 909 | | fn accessLibPath( |
| 910 | | gpa: Allocator, |
| 911 | | test_path: *std.ArrayList(u8), |
| 912 | | checked_paths: *std.ArrayList([]const u8), |
| 913 | | search_dir: []const u8, |
| 914 | | lib_name: []const u8, |
| 915 | | ) !bool { |
| 916 | | const sep = fs.path.sep_str; |
| 917 | | |
| 918 | | tbd: { |
| 919 | | test_path.clearRetainingCapacity(); |
| 920 | | try test_path.writer().print("{s}" ++ sep ++ "{s}.tbd", .{ search_dir, lib_name }); |
| 921 | | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 922 | | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 923 | | error.FileNotFound => break :tbd, |
| 924 | | else => |e| return e, |
| 925 | | }; |
| 926 | | return true; |
| 927 | | } |
| 928 | | |
| 929 | | dylib: { |
| 930 | | test_path.clearRetainingCapacity(); |
| 931 | | try test_path.writer().print("{s}" ++ sep ++ "{s}.dylib", .{ search_dir, lib_name }); |
| 932 | | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 933 | | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 934 | | error.FileNotFound => break :dylib, |
| 935 | | else => |e| return e, |
| 936 | | }; |
| 937 | | return true; |
| 938 | | } |
| 939 | | |
| 940 | | noextension: { |
| 941 | | test_path.clearRetainingCapacity(); |
| 942 | | try test_path.writer().print("{s}" ++ sep ++ "{s}", .{ search_dir, lib_name }); |
| 943 | | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 944 | | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 945 | | error.FileNotFound => break :noextension, |
| 946 | | else => |e| return e, |
| 947 | | }; |
| 948 | | return true; |
| 949 | | } |
| 950 | | |
| 951 | | return false; |
| 952 | | } |
| 953 | | |
| 954 | 919 | const ParseError = error{ |
| 955 | 920 | MalformedObject, |
| 956 | 921 | MalformedArchive, |
| ... | ... | @@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 1163 | 1128 | return false; |
| 1164 | 1129 | } |
| 1165 | 1130 | |
| 1166 | | fn accessPath(path: []const u8) !bool { |
| 1167 | | std.fs.cwd().access(path, .{}) catch |err| switch (err) { |
| 1168 | | error.FileNotFound => return false, |
| 1169 | | else => |e| return e, |
| 1170 | | }; |
| 1171 | | return true; |
| 1172 | | } |
| 1131 | fn accessLibPath( |
| 1132 | arena: Allocator, |
| 1133 | test_path: *std.ArrayList(u8), |
| 1134 | checked_paths: *std.ArrayList([]const u8), |
| 1135 | search_dir: []const u8, |
| 1136 | name: []const u8, |
| 1137 | ) !bool { |
| 1138 | const sep = fs.path.sep_str; |
| 1173 | 1139 | |
| 1174 | | fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 { |
| 1175 | | const path = try std.fmt.allocPrint(arena, "lib{s}", .{name}); |
| 1176 | | for (search_dirs) |dir| { |
| 1177 | | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1178 | | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1179 | | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); |
| 1180 | | if (try accessPath(full_path)) return full_path; |
| 1181 | | } |
| 1140 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1141 | test_path.clearRetainingCapacity(); |
| 1142 | try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); |
| 1143 | try checked_paths.append(try arena.dupe(u8, test_path.items)); |
| 1144 | std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 1145 | error.FileNotFound => continue, |
| 1146 | else => |e| return e, |
| 1147 | }; |
| 1148 | return true; |
| 1182 | 1149 | } |
| 1183 | | return null; |
| 1150 | |
| 1151 | return false; |
| 1184 | 1152 | } |
| 1185 | 1153 | |
| 1186 | | fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 { |
| 1187 | | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); |
| 1188 | | const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name }); |
| 1189 | | for (search_dirs) |dir| { |
| 1190 | | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1191 | | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1192 | | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); |
| 1193 | | if (try accessPath(full_path)) return full_path; |
| 1194 | | } |
| 1154 | fn accessFrameworkPath( |
| 1155 | arena: Allocator, |
| 1156 | test_path: *std.ArrayList(u8), |
| 1157 | checked_paths: *std.ArrayList([]const u8), |
| 1158 | search_dir: []const u8, |
| 1159 | name: []const u8, |
| 1160 | ) !bool { |
| 1161 | const sep = fs.path.sep_str; |
| 1162 | |
| 1163 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1164 | test_path.clearRetainingCapacity(); |
| 1165 | try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{ |
| 1166 | search_dir, |
| 1167 | name, |
| 1168 | name, |
| 1169 | ext, |
| 1170 | }); |
| 1171 | try checked_paths.append(try arena.dupe(u8, test_path.items)); |
| 1172 | std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 1173 | error.FileNotFound => continue, |
| 1174 | else => |e| return e, |
| 1175 | }; |
| 1176 | return true; |
| 1195 | 1177 | } |
| 1196 | | return null; |
| 1178 | |
| 1179 | return false; |
| 1197 | 1180 | } |
| 1198 | 1181 | |
| 1199 | 1182 | fn parseDependentDylibs(self: *MachO) !void { |
| ... | ... | @@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1204 | 1187 | const lib_dirs = self.lib_dirs; |
| 1205 | 1188 | const framework_dirs = self.framework_dirs; |
| 1206 | 1189 | |
| 1207 | | var arena = std.heap.ArenaAllocator.init(gpa); |
| 1208 | | defer arena.deinit(); |
| 1190 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1191 | defer arena_alloc.deinit(); |
| 1192 | const arena = arena_alloc.allocator(); |
| 1209 | 1193 | |
| 1210 | 1194 | // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times |
| 1211 | 1195 | // in which case we should track that and return File.Index immediately instead re-parsing paths. |
| ... | ... | @@ -1215,7 +1199,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1215 | 1199 | while (index < self.dylibs.items.len) : (index += 1) { |
| 1216 | 1200 | const dylib_index = self.dylibs.items[index]; |
| 1217 | 1201 | |
| 1218 | | var dependents = std.ArrayList(File.Index).init(gpa); |
| 1202 | var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa); |
| 1219 | 1203 | defer dependents.deinit(); |
| 1220 | 1204 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); |
| 1221 | 1205 | |
| ... | ... | @@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1228 | 1212 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with |
| 1229 | 1213 | // dependees list of rpaths, and search there. |
| 1230 | 1214 | // 4. Finally, just search the provided relative path directly in CWD. |
| 1215 | var test_path = std.ArrayList(u8).init(arena); |
| 1216 | var checked_paths = std.ArrayList([]const u8).init(arena); |
| 1217 | |
| 1231 | 1218 | const full_path = full_path: { |
| 1232 | | fail: { |
| 1219 | { |
| 1233 | 1220 | const stem = std.fs.path.stem(id.name); |
| 1234 | | const framework_name = try std.fmt.allocPrint(gpa, "{s}.framework" ++ std.fs.path.sep_str ++ "{s}", .{ |
| 1235 | | stem, |
| 1236 | | stem, |
| 1237 | | }); |
| 1238 | | defer gpa.free(framework_name); |
| 1239 | 1221 | |
| 1240 | | if (mem.endsWith(u8, id.name, framework_name)) { |
| 1241 | | // Framework |
| 1242 | | const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail; |
| 1243 | | break :full_path full_path; |
| 1222 | // Framework |
| 1223 | for (framework_dirs) |dir| { |
| 1224 | test_path.clearRetainingCapacity(); |
| 1225 | if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items; |
| 1244 | 1226 | } |
| 1245 | 1227 | |
| 1246 | 1228 | // Library |
| 1247 | 1229 | const lib_name = eatPrefix(stem, "lib") orelse stem; |
| 1248 | | const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail; |
| 1249 | | break :full_path full_path; |
| 1230 | for (lib_dirs) |dir| { |
| 1231 | test_path.clearRetainingCapacity(); |
| 1232 | if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items; |
| 1233 | } |
| 1250 | 1234 | } |
| 1251 | 1235 | |
| 1252 | 1236 | if (std.fs.path.isAbsolute(id.name)) { |
| 1253 | | const path = if (self.base.comp.sysroot) |root| |
| 1254 | | try std.fs.path.join(arena.allocator(), &.{ root, id.name }) |
| 1255 | | else |
| 1256 | | id.name; |
| 1257 | | for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| { |
| 1258 | | const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext }); |
| 1259 | | if (try accessPath(full_path)) break :full_path full_path; |
| 1237 | const existing_ext = std.fs.path.extension(id.name); |
| 1238 | const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name; |
| 1239 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1240 | test_path.clearRetainingCapacity(); |
| 1241 | if (self.base.comp.sysroot) |root| { |
| 1242 | try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext }); |
| 1243 | } else { |
| 1244 | try test_path.writer().print("{s}{s}", .{ path, ext }); |
| 1245 | } |
| 1246 | try checked_paths.append(try arena.dupe(u8, test_path.items)); |
| 1247 | std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 1248 | error.FileNotFound => continue, |
| 1249 | else => |e| return e, |
| 1250 | }; |
| 1251 | break :full_path test_path.items; |
| 1260 | 1252 | } |
| 1261 | 1253 | } |
| 1262 | 1254 | |
| ... | ... | @@ -1264,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1264 | 1256 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1265 | 1257 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { |
| 1266 | 1258 | const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath; |
| 1267 | | const rel_path = try std.fs.path.join(arena.allocator(), &.{ prefix, path }); |
| 1259 | const rel_path = try std.fs.path.join(arena, &.{ prefix, path }); |
| 1260 | try checked_paths.append(rel_path); |
| 1268 | 1261 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1269 | 1262 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; |
| 1270 | 1263 | break :full_path full_path; |
| ... | ... | @@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1277 | 1270 | return error.Unhandled; |
| 1278 | 1271 | } |
| 1279 | 1272 | |
| 1273 | try checked_paths.append(try arena.dupe(u8, id.name)); |
| 1280 | 1274 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1281 | | const full_path = std.fs.realpath(id.name, &buffer) catch { |
| 1282 | | dependents.appendAssumeCapacity(0); |
| 1275 | if (std.fs.realpath(id.name, &buffer)) |full_path| { |
| 1276 | break :full_path full_path; |
| 1277 | } else |_| { |
| 1278 | try self.reportMissingDependencyError( |
| 1279 | self.getFile(dylib_index).?.dylib.getUmbrella(self).index, |
| 1280 | id.name, |
| 1281 | checked_paths.items, |
| 1282 | "unable to resolve dependency", |
| 1283 | .{}, |
| 1284 | ); |
| 1285 | has_errors = true; |
| 1283 | 1286 | continue; |
| 1284 | | }; |
| 1285 | | break :full_path full_path; |
| 1287 | } |
| 1286 | 1288 | }; |
| 1287 | 1289 | const lib = SystemLib{ |
| 1288 | 1290 | .path = full_path, |
| ... | ... | @@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1304 | 1306 | break :file_index file_index; |
| 1305 | 1307 | } |
| 1306 | 1308 | }; |
| 1307 | | dependents.appendAssumeCapacity(file_index); |
| 1309 | dependents.appendAssumeCapacity(.{ .id = id, .file = file_index }); |
| 1308 | 1310 | } |
| 1309 | 1311 | |
| 1310 | 1312 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1311 | | for (dylib.dependents.items, dependents.items) |id, file_index| { |
| 1313 | for (dependents.items) |entry| { |
| 1314 | const id = entry.id; |
| 1315 | const file_index = entry.file; |
| 1312 | 1316 | if (self.getFile(file_index)) |file| { |
| 1313 | 1317 | const dep_dylib = file.dylib; |
| 1314 | 1318 | dep_dylib.hoisted = self.isHoisted(id.name); |
| ... | ... | @@ -3857,18 +3861,33 @@ fn reportMissingLibraryError( |
| 3857 | 3861 | } |
| 3858 | 3862 | } |
| 3859 | 3863 | |
| 3864 | fn reportMissingDependencyError( |
| 3865 | self: *MachO, |
| 3866 | parent: File.Index, |
| 3867 | path: []const u8, |
| 3868 | checked_paths: []const []const u8, |
| 3869 | comptime format: []const u8, |
| 3870 | args: anytype, |
| 3871 | ) error{OutOfMemory}!void { |
| 3872 | var err = try self.addErrorWithNotes(2 + checked_paths.len); |
| 3873 | try err.addMsg(self, format, args); |
| 3874 | try err.addNote(self, "while resolving {s}", .{path}); |
| 3875 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3876 | for (checked_paths) |p| { |
| 3877 | try err.addNote(self, "tried {s}", .{p}); |
| 3878 | } |
| 3879 | } |
| 3880 | |
| 3860 | 3881 | fn reportDependencyError( |
| 3861 | 3882 | self: *MachO, |
| 3862 | 3883 | parent: File.Index, |
| 3863 | | path: ?[]const u8, |
| 3884 | path: []const u8, |
| 3864 | 3885 | comptime format: []const u8, |
| 3865 | 3886 | args: anytype, |
| 3866 | 3887 | ) error{OutOfMemory}!void { |
| 3867 | 3888 | var err = try self.addErrorWithNotes(2); |
| 3868 | 3889 | try err.addMsg(self, format, args); |
| 3869 | | if (path) |p| { |
| 3870 | | try err.addNote(self, "while parsing {s}", .{p}); |
| 3871 | | } |
| 3890 | try err.addNote(self, "while parsing {s}", .{path}); |
| 3872 | 3891 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3873 | 3892 | } |
| 3874 | 3893 | |