| ... | ... | @@ -1163,37 +1163,59 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 1163 | 1163 | return false; |
| 1164 | 1164 | } |
| 1165 | 1165 | |
| 1166 | | fn accessPath(path: []const u8) !bool { |
| 1166 | fn accessPath( |
| 1167 | arena: Allocator, |
| 1168 | test_path: *std.ArrayList(u8), |
| 1169 | checked_paths: *std.ArrayList([]const u8), |
| 1170 | path: []const u8, |
| 1171 | ) !bool { |
| 1172 | test_path.clearRetainingCapacity(); |
| 1173 | try test_path.appendSlice(path); |
| 1167 | 1174 | std.fs.cwd().access(path, .{}) catch |err| switch (err) { |
| 1168 | | error.FileNotFound => return false, |
| 1175 | error.FileNotFound => { |
| 1176 | try checked_paths.append(try arena.dupe(u8, test_path.items)); |
| 1177 | return false; |
| 1178 | }, |
| 1169 | 1179 | else => |e| return e, |
| 1170 | 1180 | }; |
| 1171 | 1181 | return true; |
| 1172 | 1182 | } |
| 1173 | 1183 | |
| 1174 | | fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 { |
| 1184 | fn resolveLib( |
| 1185 | arena: Allocator, |
| 1186 | test_path: *std.ArrayList(u8), |
| 1187 | checked_paths: *std.ArrayList([]const u8), |
| 1188 | search_dirs: []const []const u8, |
| 1189 | name: []const u8, |
| 1190 | ) !bool { |
| 1175 | 1191 | const path = try std.fmt.allocPrint(arena, "lib{s}", .{name}); |
| 1176 | 1192 | for (search_dirs) |dir| { |
| 1177 | 1193 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1178 | 1194 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1179 | 1195 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); |
| 1180 | | if (try accessPath(full_path)) return full_path; |
| 1196 | if (try accessPath(arena, test_path, checked_paths, full_path)) return true; |
| 1181 | 1197 | } |
| 1182 | 1198 | } |
| 1183 | | return null; |
| 1199 | return false; |
| 1184 | 1200 | } |
| 1185 | 1201 | |
| 1186 | | fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 { |
| 1202 | fn resolveFramework( |
| 1203 | arena: Allocator, |
| 1204 | test_path: *std.ArrayList(u8), |
| 1205 | checked_paths: *std.ArrayList([]const u8), |
| 1206 | search_dirs: []const []const u8, |
| 1207 | name: []const u8, |
| 1208 | ) !bool { |
| 1187 | 1209 | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); |
| 1188 | 1210 | const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name }); |
| 1189 | 1211 | for (search_dirs) |dir| { |
| 1190 | 1212 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1191 | 1213 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1192 | 1214 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); |
| 1193 | | if (try accessPath(full_path)) return full_path; |
| 1215 | if (try accessPath(arena, test_path, checked_paths, full_path)) return true; |
| 1194 | 1216 | } |
| 1195 | 1217 | } |
| 1196 | | return null; |
| 1218 | return false; |
| 1197 | 1219 | } |
| 1198 | 1220 | |
| 1199 | 1221 | fn parseDependentDylibs(self: *MachO) !void { |
| ... | ... | @@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1204 | 1226 | const lib_dirs = self.lib_dirs; |
| 1205 | 1227 | const framework_dirs = self.framework_dirs; |
| 1206 | 1228 | |
| 1207 | | var arena = std.heap.ArenaAllocator.init(gpa); |
| 1208 | | defer arena.deinit(); |
| 1229 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1230 | defer arena_alloc.deinit(); |
| 1231 | const arena = arena_alloc.allocator(); |
| 1209 | 1232 | |
| 1210 | 1233 | // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times |
| 1211 | 1234 | // in which case we should track that and return File.Index immediately instead re-parsing paths. |
| ... | ... | @@ -1215,7 +1238,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1215 | 1238 | while (index < self.dylibs.items.len) : (index += 1) { |
| 1216 | 1239 | const dylib_index = self.dylibs.items[index]; |
| 1217 | 1240 | |
| 1218 | | var dependents = std.ArrayList(File.Index).init(gpa); |
| 1241 | var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa); |
| 1219 | 1242 | defer dependents.deinit(); |
| 1220 | 1243 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); |
| 1221 | 1244 | |
| ... | ... | @@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1228 | 1251 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with |
| 1229 | 1252 | // dependees list of rpaths, and search there. |
| 1230 | 1253 | // 4. Finally, just search the provided relative path directly in CWD. |
| 1254 | var test_path = std.ArrayList(u8).init(arena); |
| 1255 | var checked_paths = std.ArrayList([]const u8).init(arena); |
| 1256 | |
| 1231 | 1257 | const full_path = full_path: { |
| 1232 | 1258 | fail: { |
| 1233 | 1259 | const stem = std.fs.path.stem(id.name); |
| ... | ... | @@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1239 | 1265 | |
| 1240 | 1266 | if (mem.endsWith(u8, id.name, framework_name)) { |
| 1241 | 1267 | // Framework |
| 1242 | | const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail; |
| 1243 | | break :full_path full_path; |
| 1268 | if (try resolveFramework( |
| 1269 | arena, |
| 1270 | &test_path, |
| 1271 | &checked_paths, |
| 1272 | framework_dirs, |
| 1273 | stem, |
| 1274 | )) break :full_path test_path.items; |
| 1275 | break :fail; |
| 1244 | 1276 | } |
| 1245 | 1277 | |
| 1246 | 1278 | // Library |
| 1247 | 1279 | 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; |
| 1280 | if (try resolveLib( |
| 1281 | arena, |
| 1282 | &test_path, |
| 1283 | &checked_paths, |
| 1284 | lib_dirs, |
| 1285 | lib_name, |
| 1286 | )) break :full_path test_path.items; |
| 1287 | break :fail; |
| 1250 | 1288 | } |
| 1251 | 1289 | |
| 1252 | 1290 | if (std.fs.path.isAbsolute(id.name)) { |
| 1253 | 1291 | const path = if (self.base.comp.sysroot) |root| |
| 1254 | | try std.fs.path.join(arena.allocator(), &.{ root, id.name }) |
| 1292 | try std.fs.path.join(arena, &.{ root, id.name }) |
| 1255 | 1293 | else |
| 1256 | 1294 | id.name; |
| 1257 | 1295 | 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; |
| 1296 | const full_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1297 | if (try accessPath(arena, &test_path, &checked_paths, full_path)) break :full_path test_path.items; |
| 1260 | 1298 | } |
| 1261 | 1299 | } |
| 1262 | 1300 | |
| ... | ... | @@ -1264,7 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1264 | 1302 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1265 | 1303 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { |
| 1266 | 1304 | const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath; |
| 1267 | | const rel_path = try std.fs.path.join(arena.allocator(), &.{ prefix, path }); |
| 1305 | const rel_path = try std.fs.path.join(arena, &.{ prefix, path }); |
| 1268 | 1306 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1269 | 1307 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; |
| 1270 | 1308 | break :full_path full_path; |
| ... | ... | @@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1279 | 1317 | |
| 1280 | 1318 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1281 | 1319 | const full_path = std.fs.realpath(id.name, &buffer) catch { |
| 1282 | | dependents.appendAssumeCapacity(0); |
| 1320 | try self.reportMissingDependencyError( |
| 1321 | self.getFile(dylib_index).?.dylib.getUmbrella(self).index, |
| 1322 | id.name, |
| 1323 | checked_paths.items, |
| 1324 | "unable to resolve dependency", |
| 1325 | .{}, |
| 1326 | ); |
| 1327 | has_errors = true; |
| 1283 | 1328 | continue; |
| 1284 | 1329 | }; |
| 1285 | 1330 | break :full_path full_path; |
| ... | ... | @@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1304 | 1349 | break :file_index file_index; |
| 1305 | 1350 | } |
| 1306 | 1351 | }; |
| 1307 | | dependents.appendAssumeCapacity(file_index); |
| 1352 | dependents.appendAssumeCapacity(.{ .id = id, .file = file_index }); |
| 1308 | 1353 | } |
| 1309 | 1354 | |
| 1310 | 1355 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1311 | | for (dylib.dependents.items, dependents.items) |id, file_index| { |
| 1356 | for (dependents.items) |entry| { |
| 1357 | const id = entry.id; |
| 1358 | const file_index = entry.file; |
| 1312 | 1359 | if (self.getFile(file_index)) |file| { |
| 1313 | 1360 | const dep_dylib = file.dylib; |
| 1314 | 1361 | dep_dylib.hoisted = self.isHoisted(id.name); |
| ... | ... | @@ -3857,18 +3904,33 @@ fn reportMissingLibraryError( |
| 3857 | 3904 | } |
| 3858 | 3905 | } |
| 3859 | 3906 | |
| 3907 | fn reportMissingDependencyError( |
| 3908 | self: *MachO, |
| 3909 | parent: File.Index, |
| 3910 | path: []const u8, |
| 3911 | checked_paths: []const []const u8, |
| 3912 | comptime format: []const u8, |
| 3913 | args: anytype, |
| 3914 | ) error{OutOfMemory}!void { |
| 3915 | var err = try self.addErrorWithNotes(2 + checked_paths.len); |
| 3916 | try err.addMsg(self, format, args); |
| 3917 | try err.addNote(self, "while resolving {s}", .{path}); |
| 3918 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3919 | for (checked_paths) |p| { |
| 3920 | try err.addNote(self, "tried {s}", .{p}); |
| 3921 | } |
| 3922 | } |
| 3923 | |
| 3860 | 3924 | fn reportDependencyError( |
| 3861 | 3925 | self: *MachO, |
| 3862 | 3926 | parent: File.Index, |
| 3863 | | path: ?[]const u8, |
| 3927 | path: []const u8, |
| 3864 | 3928 | comptime format: []const u8, |
| 3865 | 3929 | args: anytype, |
| 3866 | 3930 | ) error{OutOfMemory}!void { |
| 3867 | 3931 | var err = try self.addErrorWithNotes(2); |
| 3868 | 3932 | try err.addMsg(self, format, args); |
| 3869 | | if (path) |p| { |
| 3870 | | try err.addNote(self, "while parsing {s}", .{p}); |
| 3871 | | } |
| 3933 | try err.addNote(self, "while parsing {s}", .{path}); |
| 3872 | 3934 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3873 | 3935 | } |
| 3874 | 3936 | |