aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-08-20 10:43:20 +0200
committerJakub Konka <kubkon@jakubkonka.com>2023-08-20 10:43:20 +0200
commit4793dafa0433d758b126f2f97d1de031b99e0fa1 (patch)
treedb8757b4acee55a28e1d6b5b6616fc2c3626eb7d
parente687c87d691518d63414aed4f355dabbd8565dc3 (diff)
downloadzig-4793dafa0433d758b126f2f97d1de031b99e0fa1.tar.gz
zig-4793dafa0433d758b126f2f97d1de031b99e0fa1.zip
frontend: move framework path resolution from the linker to frontend
-rw-r--r--src/Compilation.zig14
-rw-r--r--src/link.zig1
-rw-r--r--src/link/MachO.zig22
-rw-r--r--src/link/MachO/zld.zig44
-rw-r--r--src/main.zig102
5 files changed, 121 insertions, 62 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 726862f231..80231c91d0 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -507,7 +507,8 @@ pub const InitOptions = struct {
c_source_files: []const CSourceFile = &[0]CSourceFile{},
link_objects: []LinkObject = &[0]LinkObject{},
framework_dirs: []const []const u8 = &[0][]const u8{},
- frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},
+ framework_names: []const []const u8 = &.{},
+ framework_infos: []const Framework = &.{},
system_lib_names: []const []const u8 = &.{},
system_lib_infos: []const SystemLib = &.{},
/// These correspond to the WASI libc emulated subcomponents including:
@@ -830,7 +831,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// Our linker can't handle objects or most advanced options yet.
if (options.link_objects.len != 0 or
options.c_source_files.len != 0 or
- options.frameworks.count() != 0 or
+ options.framework_names.len != 0 or
options.system_lib_names.len != 0 or
options.link_libc or options.link_libcpp or
link_eh_frame_hdr or
@@ -1446,6 +1447,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]);
}
+ var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
+ errdefer frameworks.deinit(gpa);
+ try frameworks.ensureTotalCapacity(gpa, options.framework_names.len);
+ for (options.framework_names, options.framework_infos) |framework_name, info| {
+ frameworks.putAssumeCapacity(framework_name, info);
+ }
+
const bin_file = try link.File.openPath(gpa, .{
.emit = bin_file_emit,
.implib_emit = implib_emit,
@@ -1465,7 +1473,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.link_libcpp = link_libcpp,
.link_libunwind = link_libunwind,
.objects = options.link_objects,
- .frameworks = options.frameworks,
+ .frameworks = frameworks,
.framework_dirs = options.framework_dirs,
.system_libs = system_libs,
.wasi_emulated_libs = options.wasi_emulated_libs,
diff --git a/src/link.zig b/src/link.zig
index 82aeb5dee8..90a09f8e11 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -37,6 +37,7 @@ pub const SystemLib = struct {
pub const Framework = struct {
needed: bool = false,
weak: bool = false,
+ path: []const u8,
};
pub const SortSection = enum { name, alignment };
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 0f6859e32a..92224da63b 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -870,7 +870,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
return false;
}
-pub fn resolveLib(
+fn resolveLib(
arena: Allocator,
search_dir: []const u8,
name: []const u8,
@@ -889,26 +889,6 @@ pub fn resolveLib(
return full_path;
}
-pub fn resolveFramework(
- arena: Allocator,
- search_dir: []const u8,
- name: []const u8,
- ext: []const u8,
-) !?[]const u8 {
- const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
- const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
- const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name });
-
- // Check if the file exists.
- const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
- error.FileNotFound => return null,
- else => |e| return e,
- };
- defer tmp.close();
-
- return full_path;
-}
-
const ParseDylibError = error{
OutOfMemory,
EmptyStubFile,
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index 9de287e3e8..7987bb4f1d 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -3512,9 +3512,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom
try zld.strtab.buffer.append(gpa, 0);
- var lib_not_found = false;
- var framework_not_found = false;
-
// Positional arguments to the linker such as object files and static archives.
var positionals = std.ArrayList([]const u8).init(arena);
try positionals.ensureUnusedCapacity(options.objects.len);
@@ -3557,34 +3554,17 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
}
- try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
-
- // frameworks
- outer: for (options.frameworks.keys()) |f_name| {
- for (options.framework_dirs) |dir| {
- for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
- if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
- const info = options.frameworks.get(f_name).?;
- try libs.put(full_path, .{
- .needed = info.needed,
- .weak = info.weak,
- .path = full_path,
- });
- continue :outer;
- }
- }
- } else {
- log.warn("framework not found for '-framework {s}'", .{f_name});
- framework_not_found = true;
- }
+ {
+ const vals = options.frameworks.values();
+ try libs.ensureUnusedCapacity(vals.len);
+ for (vals) |v| libs.putAssumeCapacity(v.path, .{
+ .needed = v.needed,
+ .weak = v.weak,
+ .path = v.path,
+ });
}
- if (framework_not_found) {
- log.warn("Framework search paths:", .{});
- for (options.framework_dirs) |dir| {
- log.warn(" {s}", .{dir});
- }
- }
+ try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
if (options.verbose_link) {
var argv = std.ArrayList([]const u8).init(arena);
@@ -3731,12 +3711,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
if (resolver.unresolved.count() > 0) {
return error.UndefinedSymbolReference;
}
- if (lib_not_found) {
- return error.LibraryNotFound;
- }
- if (framework_not_found) {
- return error.FrameworkNotFound;
- }
if (options.output_mode == .Exe) {
const entry_name = options.entry orelse load_commands.default_entry_point;
diff --git a/src/main.zig b/src/main.zig
index 01ef0d87be..3d59308ac1 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -746,6 +746,13 @@ const SystemLib = struct {
}
};
+/// Similar to `link.Framework` except it doesn't store yet unresolved
+/// path to the framework.
+const Framework = struct {
+ needed: bool = false,
+ weak: bool = false,
+};
+
const CliModule = struct {
mod: *Package,
/// still in CLI arg format
@@ -919,7 +926,7 @@ fn buildOutputType(
var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
var framework_dirs = std.ArrayList([]const u8).init(arena);
- var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};
+ var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
// null means replace with the test executable binary
var test_exec_args = std.ArrayList(?[]const u8).init(arena);
var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);
@@ -2566,7 +2573,7 @@ fn buildOutputType(
want_native_include_dirs = true;
}
- // Resolve the library and framework path arguments with respect to sysroot.
+ // Resolve the library path arguments with respect to sysroot.
var lib_dirs = std.ArrayList([]const u8).init(arena);
if (sysroot) |root| {
for (lib_dir_args.items) |dir| {
@@ -2868,6 +2875,65 @@ fn buildOutputType(
}
// After this point, resolved_system_libs is used instead of external_system_libs.
+ // We now repeat part of the process for frameworks.
+ var resolved_frameworks: std.MultiArrayList(struct {
+ name: []const u8,
+ framework: Compilation.Framework,
+ }) = .{};
+
+ if (frameworks.keys().len > 0) {
+ var test_path = std.ArrayList(u8).init(gpa);
+ defer test_path.deinit();
+
+ var checked_paths = std.ArrayList(u8).init(gpa);
+ defer checked_paths.deinit();
+
+ var failed_frameworks = std.ArrayList(struct {
+ name: []const u8,
+ checked_paths: []const u8,
+ }).init(arena);
+
+ framework: for (frameworks.keys(), frameworks.values()) |framework_name, info| {
+ checked_paths.clearRetainingCapacity();
+
+ for (framework_dirs.items) |framework_dir_path| {
+ if (try accessFrameworkPath(
+ &test_path,
+ &checked_paths,
+ framework_dir_path,
+ framework_name,
+ )) {
+ const path = try arena.dupe(u8, test_path.items);
+ try resolved_frameworks.append(arena, .{
+ .name = framework_name,
+ .framework = .{
+ .needed = info.needed,
+ .weak = info.weak,
+ .path = path,
+ },
+ });
+ continue :framework;
+ }
+ }
+
+ try failed_frameworks.append(.{
+ .name = framework_name,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ });
+ }
+
+ if (failed_frameworks.items.len > 0) {
+ for (failed_frameworks.items) |f| {
+ const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
+ std.log.err("unable to find framework '{s}'. searched paths: {s}", .{
+ f.name, searched_paths,
+ });
+ }
+ process.exit(1);
+ }
+ }
+ // After this point, resolved_frameworks is used instead of frameworks.
+
const object_format = target_info.target.ofmt;
if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
@@ -3261,7 +3327,8 @@ fn buildOutputType(
.c_source_files = c_source_files.items,
.link_objects = link_objects.items,
.framework_dirs = framework_dirs.items,
- .frameworks = frameworks,
+ .framework_names = resolved_frameworks.items(.name),
+ .framework_infos = resolved_frameworks.items(.framework),
.system_lib_names = resolved_system_libs.items(.name),
.system_lib_infos = resolved_system_libs.items(.lib),
.wasi_emulated_libs = wasi_emulated_libs.items,
@@ -6336,3 +6403,32 @@ fn accessLibPath(
return false;
}
+
+fn accessFrameworkPath(
+ test_path: *std.ArrayList(u8),
+ checked_paths: *std.ArrayList(u8),
+ framework_dir_path: []const u8,
+ framework_name: []const u8,
+) !bool {
+ 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}", .{
+ framework_dir_path,
+ framework_name,
+ framework_name,
+ ext,
+ });
+ try checked_paths.writer().print("\n {s}", .{test_path.items});
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{
+ ext, test_path.items, @errorName(e),
+ }),
+ };
+ return true;
+ }
+
+ return false;
+}