Skip to content

Commit

Permalink
Merge pull request ziglang#16888 from ziglang/macho-frameworks
Browse files Browse the repository at this point in the history
compiler: resolve framework paths in the frontend
  • Loading branch information
kubkon authored Aug 21, 2023
2 parents 283afb5 + b73ef34 commit 8e96be0
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 139 deletions.
10 changes: 5 additions & 5 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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{
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 7 additions & 12 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/link/Coff/lld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
64 changes: 1 addition & 63 deletions src/link/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
70 changes: 17 additions & 53 deletions src/link/MachO/zld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -3557,43 +3554,16 @@ 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
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;
}
{
try libs.ensureUnusedCapacity(options.frameworks.len);
for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{
.needed = v.needed,
.weak = v.weak,
.path = v.path,
});
}

if (framework_not_found) {
log.warn("Framework search paths:", .{});
for (framework_dirs.items) |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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/link/Wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 8e96be0

Please sign in to comment.