| ... | @@ -1163,37 +1163,59 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { | ... | @@ -1163,37 +1163,59 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 1163 | return false; | 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 | std.fs.cwd().access(path, .{}) catch |err| switch (err) { | 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 | else => |e| return e, | 1179 | else => |e| return e, |
| 1170 | }; | 1180 | }; |
| 1171 | return true; | 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 | const path = try std.fmt.allocPrint(arena, "lib{s}", .{name}); | 1191 | const path = try std.fmt.allocPrint(arena, "lib{s}", .{name}); |
| 1176 | for (search_dirs) |dir| { | 1192 | for (search_dirs) |dir| { |
| 1177 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { | 1193 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1178 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); | 1194 | 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 }); | 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 | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); | 1209 | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); |
| 1188 | const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name }); | 1210 | const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name }); |
| 1189 | for (search_dirs) |dir| { | 1211 | for (search_dirs) |dir| { |
| 1190 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { | 1212 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { |
| 1191 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); | 1213 | 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 }); | 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 | fn parseDependentDylibs(self: *MachO) !void { | 1221 | fn parseDependentDylibs(self: *MachO) !void { |
| ... | @@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1204 | const lib_dirs = self.lib_dirs; | 1226 | const lib_dirs = self.lib_dirs; |
| 1205 | const framework_dirs = self.framework_dirs; | 1227 | const framework_dirs = self.framework_dirs; |
| 1206 | | 1228 | |
| 1207 | var arena = std.heap.ArenaAllocator.init(gpa); | 1229 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1208 | defer arena.deinit(); | 1230 | defer arena_alloc.deinit(); |
| | 1231 | const arena = arena_alloc.allocator(); |
| 1209 | | 1232 | |
| 1210 | // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times | 1233 | // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times |
| 1211 | // in which case we should track that and return File.Index immediately instead re-parsing paths. | 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,7 +1238,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1215 | while (index < self.dylibs.items.len) : (index += 1) { | 1238 | while (index < self.dylibs.items.len) : (index += 1) { |
| 1216 | const dylib_index = self.dylibs.items[index]; | 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 | defer dependents.deinit(); | 1242 | defer dependents.deinit(); |
| 1220 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); | 1243 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); |
| 1221 | | 1244 | |
| ... | @@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1228 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with | 1251 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with |
| 1229 | // dependees list of rpaths, and search there. | 1252 | // dependees list of rpaths, and search there. |
| 1230 | // 4. Finally, just search the provided relative path directly in CWD. | 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 | const full_path = full_path: { | 1257 | const full_path = full_path: { |
| 1232 | fail: { | 1258 | fail: { |
| 1233 | const stem = std.fs.path.stem(id.name); | 1259 | const stem = std.fs.path.stem(id.name); |
| ... | @@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1239 | | 1265 | |
| 1240 | if (mem.endsWith(u8, id.name, framework_name)) { | 1266 | if (mem.endsWith(u8, id.name, framework_name)) { |
| 1241 | // Framework | 1267 | // Framework |
| 1242 | const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail; | 1268 | if (try resolveFramework( |
| 1243 | break :full_path full_path; | 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 | // Library | 1278 | // Library |
| 1247 | const lib_name = eatPrefix(stem, "lib") orelse stem; | 1279 | const lib_name = eatPrefix(stem, "lib") orelse stem; |
| 1248 | const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail; | 1280 | if (try resolveLib( |
| 1249 | break :full_path full_path; | 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 | if (std.fs.path.isAbsolute(id.name)) { | 1290 | if (std.fs.path.isAbsolute(id.name)) { |
| 1253 | const path = if (self.base.comp.sysroot) |root| | 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 | else | 1293 | else |
| 1256 | id.name; | 1294 | id.name; |
| 1257 | for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| { | 1295 | for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| { |
| 1258 | const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext }); | 1296 | const full_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); |
| 1259 | if (try accessPath(full_path)) break :full_path full_path; | 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,7 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1264 | const dylib = self.getFile(dylib_index).?.dylib; | 1302 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1265 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { | 1303 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { |
| 1266 | const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath; | 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 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 1306 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1269 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; | 1307 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; |
| 1270 | break :full_path full_path; | 1308 | break :full_path full_path; |
| ... | @@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1279 | | 1317 | |
| 1280 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 1318 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1281 | const full_path = std.fs.realpath(id.name, &buffer) catch { | 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 | continue; | 1328 | continue; |
| 1284 | }; | 1329 | }; |
| 1285 | break :full_path full_path; | 1330 | break :full_path full_path; |
| ... | @@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1304 | break :file_index file_index; | 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 | const dylib = self.getFile(dylib_index).?.dylib; | 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 | if (self.getFile(file_index)) |file| { | 1359 | if (self.getFile(file_index)) |file| { |
| 1313 | const dep_dylib = file.dylib; | 1360 | const dep_dylib = file.dylib; |
| 1314 | dep_dylib.hoisted = self.isHoisted(id.name); | 1361 | dep_dylib.hoisted = self.isHoisted(id.name); |
| ... | @@ -3857,18 +3904,33 @@ fn reportMissingLibraryError( | ... | @@ -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 | fn reportDependencyError( | 3924 | fn reportDependencyError( |
| 3861 | self: *MachO, | 3925 | self: *MachO, |
| 3862 | parent: File.Index, | 3926 | parent: File.Index, |
| 3863 | path: ?[]const u8, | 3927 | path: []const u8, |
| 3864 | comptime format: []const u8, | 3928 | comptime format: []const u8, |
| 3865 | args: anytype, | 3929 | args: anytype, |
| 3866 | ) error{OutOfMemory}!void { | 3930 | ) error{OutOfMemory}!void { |
| 3867 | var err = try self.addErrorWithNotes(2); | 3931 | var err = try self.addErrorWithNotes(2); |
| 3868 | try err.addMsg(self, format, args); | 3932 | try err.addMsg(self, format, args); |
| 3869 | if (path) |p| { | 3933 | try err.addNote(self, "while parsing {s}", .{path}); |
| 3870 | try err.addNote(self, "while parsing {s}", .{p}); | | |
| 3871 | } | | |
| 3872 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | 3934 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3873 | } | 3935 | } |
| 3874 | | 3936 | |