macho: refactor resolving libs and frameworks when searching dependent dylibs

This commit is contained in:
Jakub Konka 2024-01-25 10:30:17 +01:00
parent 51d60d1a60
commit dc9b6ecb55

View File

@ -1173,58 +1173,54 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
return false;
}
fn accessPath(
fn accessLibPath2(
arena: Allocator,
test_path: *std.ArrayList(u8),
checked_paths: *std.ArrayList([]const u8),
path: []const u8,
) !bool {
test_path.clearRetainingCapacity();
try test_path.appendSlice(path);
std.fs.cwd().access(path, .{}) catch |err| switch (err) {
error.FileNotFound => {
try checked_paths.append(try arena.dupe(u8, test_path.items));
return false;
},
else => |e| return e,
};
return true;
}
fn resolveLib(
arena: Allocator,
test_path: *std.ArrayList(u8),
checked_paths: *std.ArrayList([]const u8),
search_dirs: []const []const u8,
search_dir: []const u8,
name: []const u8,
) !bool {
const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});
for (search_dirs) |dir| {
for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
if (try accessPath(arena, test_path, checked_paths, full_path)) return true;
}
const sep = fs.path.sep_str;
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity();
try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
try checked_paths.append(try arena.dupe(u8, test_path.items));
std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
return true;
}
return false;
}
fn resolveFramework(
fn accessFrameworkPath(
arena: Allocator,
test_path: *std.ArrayList(u8),
checked_paths: *std.ArrayList([]const u8),
search_dirs: []const []const u8,
search_dir: []const u8,
name: []const u8,
) !bool {
const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });
for (search_dirs) |dir| {
for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
if (try accessPath(arena, test_path, checked_paths, full_path)) return true;
}
const sep = fs.path.sep_str;
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity();
try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
search_dir,
name,
name,
ext,
});
try checked_paths.append(try arena.dupe(u8, test_path.items));
std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
return true;
}
return false;
}
@ -1265,38 +1261,39 @@ fn parseDependentDylibs(self: *MachO) !void {
var checked_paths = std.ArrayList([]const u8).init(arena);
const full_path = full_path: {
fail: {
{
const stem = std.fs.path.stem(id.name);
// Framework
if (try resolveFramework(
arena,
&test_path,
&checked_paths,
framework_dirs,
stem,
)) break :full_path test_path.items;
for (framework_dirs) |dir| {
test_path.clearRetainingCapacity();
if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
}
// Library
const lib_name = eatPrefix(stem, "lib") orelse stem;
if (try resolveLib(
arena,
&test_path,
&checked_paths,
lib_dirs,
lib_name,
)) break :full_path test_path.items;
break :fail;
for (lib_dirs) |dir| {
test_path.clearRetainingCapacity();
if (try accessLibPath2(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;
}
}
if (std.fs.path.isAbsolute(id.name)) {
const path = if (self.base.comp.sysroot) |root|
try std.fs.path.join(arena, &.{ root, id.name })
else
id.name;
for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {
const full_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
if (try accessPath(arena, &test_path, &checked_paths, full_path)) break :full_path test_path.items;
const existing_ext = std.fs.path.extension(id.name);
const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity();
if (self.base.comp.sysroot) |root| {
try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
} else {
try test_path.writer().print("{s}{s}", .{ path, ext });
}
try checked_paths.append(try arena.dupe(u8, test_path.items));
std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
break :full_path test_path.items;
}
}
@ -1305,6 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void {
for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;
const rel_path = try std.fs.path.join(arena, &.{ prefix, path });
try checked_paths.append(rel_path);
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
break :full_path full_path;
@ -1317,8 +1315,11 @@ fn parseDependentDylibs(self: *MachO) !void {
return error.Unhandled;
}
try checked_paths.append(try arena.dupe(u8, id.name));
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const full_path = std.fs.realpath(id.name, &buffer) catch {
if (std.fs.realpath(id.name, &buffer)) |full_path| {
break :full_path full_path;
} else |_| {
try self.reportMissingDependencyError(
self.getFile(dylib_index).?.dylib.getUmbrella(self).index,
id.name,
@ -1328,8 +1329,7 @@ fn parseDependentDylibs(self: *MachO) !void {
);
has_errors = true;
continue;
};
break :full_path full_path;
}
};
const lib = SystemLib{
.path = full_path,