| ... | @@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { | ... | @@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 844 | try argv.append(arg); | 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 | for (self.frameworks) |framework| { | 852 | for (self.frameworks) |framework| { |
| 848 | const name = std.fs.path.stem(framework.path); | 853 | const name = std.fs.path.stem(framework.path); |
| 849 | const arg = if (framework.needed) | 854 | const arg = if (framework.needed) |
| ... | @@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { | ... | @@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 855 | try argv.append(arg); | 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 | if (self.base.isDynLib() and self.base.allow_shlib_undefined) { | 868 | if (self.base.isDynLib() and self.base.allow_shlib_undefined) { |
| 859 | try argv.append("-undefined"); | 869 | try argv.append("-undefined"); |
| 860 | try argv.append("dynamic_lookup"); | 870 | try argv.append("dynamic_lookup"); |
| ... | @@ -887,11 +897,11 @@ pub fn resolveLibSystem( | ... | @@ -887,11 +897,11 @@ pub fn resolveLibSystem( |
| 887 | if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { | 897 | if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { |
| 888 | .sdk => { | 898 | .sdk => { |
| 889 | const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" }); | 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 | .vendored => { | 902 | .vendored => { |
| 893 | const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" }); | 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,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 | const ParseError = error{ | 919 | const ParseError = error{ |
| 955 | MalformedObject, | 920 | MalformedObject, |
| 956 | MalformedArchive, | 921 | MalformedArchive, |
| ... | @@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { | ... | @@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 1163 | return false; | 1128 | return false; |
| 1164 | } | 1129 | } |
| 1165 | | 1130 | |
| 1166 | fn accessPath(path: []const u8) !bool { | 1131 | fn accessLibPath( |
| 1167 | std.fs.cwd().access(path, .{}) catch |err| switch (err) { | 1132 | arena: Allocator, |
| 1168 | error.FileNotFound => return false, | 1133 | test_path: *std.ArrayList(u8), |
| 1169 | else => |e| return e, | 1134 | checked_paths: *std.ArrayList([]const u8), |
| 1170 | }; | 1135 | search_dir: []const u8, |
| 1171 | return true; | 1136 | name: []const u8, |
| 1172 | } | 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 { | 1140 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1175 | const path = try std.fmt.allocPrint(arena, "lib{s}", .{name}); | 1141 | test_path.clearRetainingCapacity(); |
| 1176 | for (search_dirs) |dir| { | 1142 | try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); |
| 1177 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { | 1143 | try checked_paths.append(try arena.dupe(u8, test_path.items)); |
| 1178 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); | 1144 | std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 1179 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); | 1145 | error.FileNotFound => continue, |
| 1180 | if (try accessPath(full_path)) return full_path; | 1146 | else => |e| return e, |
| 1181 | } | 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 { | 1154 | fn accessFrameworkPath( |
| 1187 | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); | 1155 | arena: Allocator, |
| 1188 | const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name }); | 1156 | test_path: *std.ArrayList(u8), |
| 1189 | for (search_dirs) |dir| { | 1157 | checked_paths: *std.ArrayList([]const u8), |
| 1190 | for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { | 1158 | search_dir: []const u8, |
| 1191 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext }); | 1159 | name: []const u8, |
| 1192 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext }); | 1160 | ) !bool { |
| 1193 | if (try accessPath(full_path)) return full_path; | 1161 | const sep = fs.path.sep_str; |
| 1194 | } | 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 | fn parseDependentDylibs(self: *MachO) !void { | 1182 | fn parseDependentDylibs(self: *MachO) !void { |
| ... | @@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1204 | const lib_dirs = self.lib_dirs; | 1187 | const lib_dirs = self.lib_dirs; |
| 1205 | const framework_dirs = self.framework_dirs; | 1188 | const framework_dirs = self.framework_dirs; |
| 1206 | | 1189 | |
| 1207 | var arena = std.heap.ArenaAllocator.init(gpa); | 1190 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1208 | defer arena.deinit(); | 1191 | defer arena_alloc.deinit(); |
| | 1192 | const arena = arena_alloc.allocator(); |
| 1209 | | 1193 | |
| 1210 | // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times | 1194 | // 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. | 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,7 +1199,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1215 | while (index < self.dylibs.items.len) : (index += 1) { | 1199 | while (index < self.dylibs.items.len) : (index += 1) { |
| 1216 | const dylib_index = self.dylibs.items[index]; | 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 | defer dependents.deinit(); | 1203 | defer dependents.deinit(); |
| 1220 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); | 1204 | try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len); |
| 1221 | | 1205 | |
| ... | @@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1228 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with | 1212 | // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with |
| 1229 | // dependees list of rpaths, and search there. | 1213 | // dependees list of rpaths, and search there. |
| 1230 | // 4. Finally, just search the provided relative path directly in CWD. | 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 | const full_path = full_path: { | 1218 | const full_path = full_path: { |
| 1232 | fail: { | 1219 | { |
| 1233 | const stem = std.fs.path.stem(id.name); | 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)) { | 1222 | // Framework |
| 1241 | // Framework | 1223 | for (framework_dirs) |dir| { |
| 1242 | const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail; | 1224 | test_path.clearRetainingCapacity(); |
| 1243 | break :full_path full_path; | 1225 | if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items; |
| 1244 | } | 1226 | } |
| 1245 | | 1227 | |
| 1246 | // Library | 1228 | // Library |
| 1247 | const lib_name = eatPrefix(stem, "lib") orelse stem; | 1229 | const lib_name = eatPrefix(stem, "lib") orelse stem; |
| 1248 | const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail; | 1230 | for (lib_dirs) |dir| { |
| 1249 | break :full_path full_path; | 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 | if (std.fs.path.isAbsolute(id.name)) { | 1236 | if (std.fs.path.isAbsolute(id.name)) { |
| 1253 | const path = if (self.base.comp.sysroot) |root| | 1237 | const existing_ext = std.fs.path.extension(id.name); |
| 1254 | try std.fs.path.join(arena.allocator(), &.{ root, id.name }) | 1238 | const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name; |
| 1255 | else | 1239 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1256 | id.name; | 1240 | test_path.clearRetainingCapacity(); |
| 1257 | for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| { | 1241 | if (self.base.comp.sysroot) |root| { |
| 1258 | const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext }); | 1242 | try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext }); |
| 1259 | if (try accessPath(full_path)) break :full_path full_path; | 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,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1264 | const dylib = self.getFile(dylib_index).?.dylib; | 1256 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1265 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { | 1257 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { |
| 1266 | const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath; | 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 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 1261 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1269 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; | 1262 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; |
| 1270 | break :full_path full_path; | 1263 | break :full_path full_path; |
| ... | @@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1277 | return error.Unhandled; | 1270 | return error.Unhandled; |
| 1278 | } | 1271 | } |
| 1279 | | 1272 | |
| | 1273 | try checked_paths.append(try arena.dupe(u8, id.name)); |
| 1280 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 1274 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1281 | const full_path = std.fs.realpath(id.name, &buffer) catch { | 1275 | if (std.fs.realpath(id.name, &buffer)) |full_path| { |
| 1282 | dependents.appendAssumeCapacity(0); | 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 | continue; | 1286 | continue; |
| 1284 | }; | 1287 | } |
| 1285 | break :full_path full_path; | | |
| 1286 | }; | 1288 | }; |
| 1287 | const lib = SystemLib{ | 1289 | const lib = SystemLib{ |
| 1288 | .path = full_path, | 1290 | .path = full_path, |
| ... | @@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void { | ... | @@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1304 | break :file_index file_index; | 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 | const dylib = self.getFile(dylib_index).?.dylib; | 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 | if (self.getFile(file_index)) |file| { | 1316 | if (self.getFile(file_index)) |file| { |
| 1313 | const dep_dylib = file.dylib; | 1317 | const dep_dylib = file.dylib; |
| 1314 | dep_dylib.hoisted = self.isHoisted(id.name); | 1318 | dep_dylib.hoisted = self.isHoisted(id.name); |
| ... | @@ -3857,18 +3861,33 @@ fn reportMissingLibraryError( | ... | @@ -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 | fn reportDependencyError( | 3881 | fn reportDependencyError( |
| 3861 | self: *MachO, | 3882 | self: *MachO, |
| 3862 | parent: File.Index, | 3883 | parent: File.Index, |
| 3863 | path: ?[]const u8, | 3884 | path: []const u8, |
| 3864 | comptime format: []const u8, | 3885 | comptime format: []const u8, |
| 3865 | args: anytype, | 3886 | args: anytype, |
| 3866 | ) error{OutOfMemory}!void { | 3887 | ) error{OutOfMemory}!void { |
| 3867 | var err = try self.addErrorWithNotes(2); | 3888 | var err = try self.addErrorWithNotes(2); |
| 3868 | try err.addMsg(self, format, args); | 3889 | try err.addMsg(self, format, args); |
| 3869 | if (path) |p| { | 3890 | 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()}); | 3891 | try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3873 | } | 3892 | } |
| 3874 | | 3893 | |