mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 05:48:31 +00:00
Merge pull request #16888 from ziglang/macho-frameworks
compiler: resolve framework paths in the frontend
This commit is contained in:
commit
8e96be0088
@ -507,7 +507,7 @@ 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) = .{},
|
||||
frameworks: []const Framework = &.{},
|
||||
system_lib_names: []const []const u8 = &.{},
|
||||
system_lib_infos: []const SystemLib = &.{},
|
||||
/// These correspond to the WASI libc emulated subcomponents including:
|
||||
@ -830,7 +830,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.frameworks.len != 0 or
|
||||
options.system_lib_names.len != 0 or
|
||||
options.link_libc or options.link_libcpp or
|
||||
link_eh_frame_hdr or
|
||||
@ -2267,7 +2267,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
|
||||
/// to remind the programmer to update multiple related pieces of code that
|
||||
/// are in different locations. Bump this number when adding or deleting
|
||||
/// anything from the link cache manifest.
|
||||
pub const link_hash_implementation_version = 9;
|
||||
pub const link_hash_implementation_version = 10;
|
||||
|
||||
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
|
||||
const gpa = comp.gpa;
|
||||
@ -2277,7 +2277,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
comptime assert(link_hash_implementation_version == 9);
|
||||
comptime assert(link_hash_implementation_version == 10);
|
||||
|
||||
if (comp.bin_file.options.module) |mod| {
|
||||
const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
|
||||
@ -2386,7 +2386,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
|
||||
// Mach-O specific stuff
|
||||
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
|
||||
link.hashAddFrameworks(&man.hash, comp.bin_file.options.frameworks);
|
||||
try link.hashAddFrameworks(man, comp.bin_file.options.frameworks);
|
||||
try man.addOptionalFile(comp.bin_file.options.entitlements);
|
||||
man.hash.addOptional(comp.bin_file.options.pagezero_size);
|
||||
man.hash.addOptional(comp.bin_file.options.headerpad_size);
|
||||
|
||||
19
src/link.zig
19
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 };
|
||||
@ -56,15 +57,11 @@ pub fn hashAddSystemLibs(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hashAddFrameworks(
|
||||
hh: *Cache.HashHelper,
|
||||
hm: std.StringArrayHashMapUnmanaged(Framework),
|
||||
) void {
|
||||
const keys = hm.keys();
|
||||
hh.addListOfBytes(keys);
|
||||
for (hm.values()) |value| {
|
||||
hh.add(value.needed);
|
||||
hh.add(value.weak);
|
||||
pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void {
|
||||
for (hm) |value| {
|
||||
man.hash.add(value.needed);
|
||||
man.hash.add(value.weak);
|
||||
_ = try man.addFile(value.path, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +205,7 @@ pub const Options = struct {
|
||||
|
||||
objects: []Compilation.LinkObject,
|
||||
framework_dirs: []const []const u8,
|
||||
frameworks: std.StringArrayHashMapUnmanaged(Framework),
|
||||
frameworks: []const Framework,
|
||||
/// These are *always* dynamically linked. Static libraries will be
|
||||
/// provided as positional arguments.
|
||||
system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
|
||||
@ -276,7 +273,6 @@ pub const Options = struct {
|
||||
|
||||
pub fn move(self: *Options) Options {
|
||||
const copied_state = self.*;
|
||||
self.frameworks = .{};
|
||||
self.system_libs = .{};
|
||||
self.force_undefined_symbols = .{};
|
||||
return copied_state;
|
||||
@ -642,7 +638,6 @@ pub const File = struct {
|
||||
base.releaseLock();
|
||||
if (base.file) |f| f.close();
|
||||
if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
|
||||
base.options.frameworks.deinit(base.allocator);
|
||||
base.options.system_libs.deinit(base.allocator);
|
||||
base.options.force_undefined_symbols.deinit(base.allocator);
|
||||
switch (base.tag) {
|
||||
|
||||
@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
man = comp.cache_parent.obtain();
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 10);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -1367,7 +1367,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 10);
|
||||
|
||||
try man.addOptionalFile(self.base.options.linker_script);
|
||||
try man.addOptionalFile(self.base.options.version_script);
|
||||
|
||||
@ -870,49 +870,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn resolveSearchDir(
|
||||
arena: Allocator,
|
||||
dir: []const u8,
|
||||
syslibroot: ?[]const u8,
|
||||
) !?[]const u8 {
|
||||
var candidates = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
if (fs.path.isAbsolute(dir)) {
|
||||
if (syslibroot) |root| {
|
||||
const common_dir = if (builtin.os.tag == .windows) blk: {
|
||||
// We need to check for disk designator and strip it out from dir path so
|
||||
// that we can concat dir with syslibroot.
|
||||
// TODO we should backport this mechanism to 'MachO.Dylib.parseDependentLibs()'
|
||||
const disk_designator = fs.path.diskDesignatorWindows(dir);
|
||||
|
||||
if (mem.indexOf(u8, dir, disk_designator)) |where| {
|
||||
break :blk dir[where + disk_designator.len ..];
|
||||
}
|
||||
|
||||
break :blk dir;
|
||||
} else dir;
|
||||
const full_path = try fs.path.join(arena, &[_][]const u8{ root, common_dir });
|
||||
try candidates.append(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
try candidates.append(dir);
|
||||
|
||||
for (candidates.items) |candidate| {
|
||||
// Verify that search path actually exists
|
||||
var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer tmp.close();
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn resolveLib(
|
||||
fn resolveLib(
|
||||
arena: Allocator,
|
||||
search_dir: []const u8,
|
||||
name: []const u8,
|
||||
@ -931,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,
|
||||
|
||||
@ -3396,7 +3396,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
macho_file.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 10);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -3417,7 +3417,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
man.hash.add(options.strip);
|
||||
man.hash.addListOfBytes(options.lib_dirs);
|
||||
man.hash.addListOfBytes(options.framework_dirs);
|
||||
link.hashAddFrameworks(&man.hash, options.frameworks);
|
||||
try link.hashAddFrameworks(&man, options.frameworks);
|
||||
man.hash.addListOfBytes(options.rpath_list);
|
||||
if (is_dyn_lib) {
|
||||
man.hash.addOptionalBytes(options.install_name);
|
||||
@ -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,44 +3554,17 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
|
||||
}
|
||||
|
||||
{
|
||||
try libs.ensureUnusedCapacity(options.frameworks.len);
|
||||
for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{
|
||||
.needed = v.needed,
|
||||
.weak = v.weak,
|
||||
.path = v.path,
|
||||
});
|
||||
}
|
||||
|
||||
try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
|
||||
|
||||
// frameworks
|
||||
var framework_dirs = std.ArrayList([]const u8).init(arena);
|
||||
for (options.framework_dirs) |dir| {
|
||||
if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
|
||||
try framework_dirs.append(search_dir);
|
||||
} else {
|
||||
log.warn("directory not found for '-F{s}'", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
outer: for (options.frameworks.keys()) |f_name| {
|
||||
for (framework_dirs.items) |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;
|
||||
}
|
||||
}
|
||||
|
||||
if (framework_not_found) {
|
||||
log.warn("Framework search paths:", .{});
|
||||
for (framework_dirs.items) |dir| {
|
||||
log.warn(" {s}", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbose_link) {
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
@ -3693,14 +3663,14 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
|
||||
}
|
||||
|
||||
for (options.frameworks.keys()) |framework| {
|
||||
const info = options.frameworks.get(framework).?;
|
||||
const arg = if (info.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
|
||||
else if (info.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
|
||||
for (options.frameworks) |framework| {
|
||||
const name = std.fs.path.stem(framework.path);
|
||||
const arg = if (framework.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
|
||||
else if (framework.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
|
||||
try std.fmt.allocPrint(arena, "-framework {s}", .{name});
|
||||
try argv.append(arg);
|
||||
}
|
||||
|
||||
@ -3740,12 +3710,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;
|
||||
|
||||
@ -3193,7 +3193,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 10);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -4254,7 +4254,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 10);
|
||||
|
||||
for (wasm.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
93
src/main.zig
93
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);
|
||||
@ -2868,6 +2875,59 @@ 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.ArrayList(Compilation.Framework).init(arena);
|
||||
|
||||
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(.{
|
||||
.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 +3321,7 @@ fn buildOutputType(
|
||||
.c_source_files = c_source_files.items,
|
||||
.link_objects = link_objects.items,
|
||||
.framework_dirs = framework_dirs.items,
|
||||
.frameworks = frameworks,
|
||||
.frameworks = resolved_frameworks.items,
|
||||
.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 +6396,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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user