mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
MachO: add the same workaround for no -r LLD flag support
This is the MachO equivalent for the code added to COFF for doing the file copy when the input and output are both just one object file.
This commit is contained in:
parent
2a893efae1
commit
3249e5d952
@ -319,7 +319,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
break :blk full_obj_path;
|
||||
} else null;
|
||||
|
||||
const is_obj = self.base.options.output_mode == .Obj;
|
||||
const is_lib = self.base.options.output_mode == .Lib;
|
||||
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
|
||||
const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
|
||||
@ -391,215 +390,238 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
};
|
||||
}
|
||||
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// Even though we're calling LLD as a library it thinks the first argument is its own exe name.
|
||||
try argv.append("lld");
|
||||
if (is_obj) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
|
||||
try argv.append("-error-limit");
|
||||
try argv.append("0");
|
||||
|
||||
try argv.append("-demangle");
|
||||
|
||||
if (self.base.options.rdynamic) {
|
||||
try argv.append("--export-dynamic");
|
||||
}
|
||||
|
||||
try argv.appendSlice(self.base.options.extra_lld_args);
|
||||
|
||||
if (self.base.options.z_nodelete) {
|
||||
try argv.append("-z");
|
||||
try argv.append("nodelete");
|
||||
}
|
||||
if (self.base.options.z_defs) {
|
||||
try argv.append("-z");
|
||||
try argv.append("defs");
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-static");
|
||||
} else {
|
||||
try argv.append("-dynamic");
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-dylib");
|
||||
|
||||
if (self.base.options.version) |ver| {
|
||||
const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
|
||||
try argv.append("-compatibility_version");
|
||||
try argv.append(compat_vers);
|
||||
|
||||
const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append("-current_version");
|
||||
try argv.append(cur_vers);
|
||||
}
|
||||
|
||||
// TODO getting an error when running an executable when doing this rpath thing
|
||||
//Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
|
||||
// buf_ptr(g->root_out_name), g->version_major);
|
||||
//try argv.append("-install_name");
|
||||
//try argv.append(buf_ptr(dylib_install_name));
|
||||
}
|
||||
|
||||
try argv.append("-arch");
|
||||
try argv.append(darwinArchString(target.cpu.arch));
|
||||
|
||||
switch (target.os.tag) {
|
||||
.macosx => {
|
||||
try argv.append("-macosx_version_min");
|
||||
},
|
||||
.ios, .tvos, .watchos => switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
try argv.append("-ios_simulator_version_min");
|
||||
},
|
||||
else => {
|
||||
try argv.append("-iphoneos_version_min");
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
const ver = target.os.version_range.semver.min;
|
||||
const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append(version_string);
|
||||
|
||||
try argv.append("-sdk_version");
|
||||
try argv.append(version_string);
|
||||
|
||||
if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-pie");
|
||||
}
|
||||
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringHashMap(void).init(self.base.allocator);
|
||||
defer rpath_table.deinit();
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if ((try rpath_table.fetchPut(rpath, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
if (self.base.options.output_mode == .Obj) {
|
||||
// LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy
|
||||
// here. TODO: think carefully about how we can avoid this redundant operation when doing
|
||||
// build-obj. See also the corresponding TODO in linkAsArchive.
|
||||
const the_object_path = blk: {
|
||||
if (self.base.options.objects.len != 0)
|
||||
break :blk self.base.options.objects[0];
|
||||
|
||||
if (comp.c_object_table.count() != 0)
|
||||
break :blk comp.c_object_table.items()[0].key.status.success.object_path;
|
||||
|
||||
if (module_obj_path) |p|
|
||||
break :blk p;
|
||||
|
||||
// TODO I think this is unreachable. Audit this situation when solving the above TODO
|
||||
// regarding eliding redundant object -> object transformations.
|
||||
return error.NoObjectsToLink;
|
||||
};
|
||||
// This can happen when using --enable-cache and using the stage1 backend. In this case
|
||||
// we can skip the file copy.
|
||||
if (!mem.eql(u8, the_object_path, full_out_path)) {
|
||||
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
|
||||
}
|
||||
}
|
||||
if (is_dyn_lib) {
|
||||
if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(full_out_path);
|
||||
} else {
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// Even though we're calling LLD as a library it thinks the first argument is its own exe name.
|
||||
try argv.append("lld");
|
||||
|
||||
try argv.append("-error-limit");
|
||||
try argv.append("0");
|
||||
|
||||
try argv.append("-demangle");
|
||||
|
||||
if (self.base.options.rdynamic) {
|
||||
try argv.append("--export-dynamic");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append("-L");
|
||||
try argv.append(lib_dir);
|
||||
}
|
||||
try argv.appendSlice(self.base.options.extra_lld_args);
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
if (self.base.options.z_nodelete) {
|
||||
try argv.append("-z");
|
||||
try argv.append("nodelete");
|
||||
}
|
||||
if (self.base.options.z_defs) {
|
||||
try argv.append("-z");
|
||||
try argv.append("defs");
|
||||
}
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-static");
|
||||
} else {
|
||||
try argv.append("-dynamic");
|
||||
}
|
||||
|
||||
// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
|
||||
if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
|
||||
try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
}
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-dylib");
|
||||
|
||||
// Shared libraries.
|
||||
const system_libs = self.base.options.system_libs.items();
|
||||
try argv.ensureCapacity(argv.items.len + system_libs.len);
|
||||
for (system_libs) |entry| {
|
||||
const link_lib = entry.key;
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
const ext = Compilation.classifyFileExt(link_lib);
|
||||
const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
|
||||
argv.appendAssumeCapacity(arg);
|
||||
}
|
||||
if (self.base.options.version) |ver| {
|
||||
const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
|
||||
try argv.append("-compatibility_version");
|
||||
try argv.append(compat_vers);
|
||||
|
||||
// libc++ dep
|
||||
if (!is_obj and self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try argv.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append("-current_version");
|
||||
try argv.append(cur_vers);
|
||||
}
|
||||
|
||||
// On Darwin, libSystem has libc in it, but also you have to use it
|
||||
// to make syscalls because the syscall numbers are not documented
|
||||
// and change between versions. So we always link against libSystem.
|
||||
// LLD craps out if you do -lSystem cross compiling, so until that
|
||||
// codebase gets some love from the new maintainers we're left with
|
||||
// this dirty hack.
|
||||
if (self.base.options.is_native_os) {
|
||||
try argv.append("-lSystem");
|
||||
}
|
||||
// TODO getting an error when running an executable when doing this rpath thing
|
||||
//Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
|
||||
// buf_ptr(g->root_out_name), g->version_major);
|
||||
//try argv.append("-install_name");
|
||||
//try argv.append(buf_ptr(dylib_install_name));
|
||||
}
|
||||
|
||||
for (self.base.options.framework_dirs) |framework_dir| {
|
||||
try argv.append("-F");
|
||||
try argv.append(framework_dir);
|
||||
}
|
||||
for (self.base.options.frameworks) |framework| {
|
||||
try argv.append("-framework");
|
||||
try argv.append(framework);
|
||||
}
|
||||
try argv.append("-arch");
|
||||
try argv.append(darwinArchString(target.cpu.arch));
|
||||
|
||||
if (allow_shlib_undefined) {
|
||||
try argv.append("-undefined");
|
||||
try argv.append("dynamic_lookup");
|
||||
}
|
||||
if (self.base.options.bind_global_refs_locally) {
|
||||
try argv.append("-Bsymbolic");
|
||||
}
|
||||
switch (target.os.tag) {
|
||||
.macosx => {
|
||||
try argv.append("-macosx_version_min");
|
||||
},
|
||||
.ios, .tvos, .watchos => switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
try argv.append("-ios_simulator_version_min");
|
||||
},
|
||||
else => {
|
||||
try argv.append("-iphoneos_version_min");
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
const ver = target.os.version_range.semver.min;
|
||||
const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append(version_string);
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
try argv.append("-sdk_version");
|
||||
try argv.append(version_string);
|
||||
|
||||
const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
|
||||
for (argv.items) |arg, i| {
|
||||
new_argv[i] = try arena.dupeZ(u8, arg);
|
||||
}
|
||||
if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-pie");
|
||||
}
|
||||
|
||||
var stderr_context: LLDContext = .{
|
||||
.macho = self,
|
||||
.data = std.ArrayList(u8).init(self.base.allocator),
|
||||
};
|
||||
defer stderr_context.data.deinit();
|
||||
var stdout_context: LLDContext = .{
|
||||
.macho = self,
|
||||
.data = std.ArrayList(u8).init(self.base.allocator),
|
||||
};
|
||||
defer stdout_context.data.deinit();
|
||||
const llvm = @import("../llvm.zig");
|
||||
const ok = llvm.Link(
|
||||
.MachO,
|
||||
new_argv.ptr,
|
||||
new_argv.len,
|
||||
append_diagnostic,
|
||||
@ptrToInt(&stdout_context),
|
||||
@ptrToInt(&stderr_context),
|
||||
);
|
||||
if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
|
||||
if (stdout_context.data.items.len != 0) {
|
||||
std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
|
||||
}
|
||||
if (!ok) {
|
||||
// TODO parse this output and surface with the Compilation API rather than
|
||||
// directly outputting to stderr here.
|
||||
std.debug.print("{}", .{stderr_context.data.items});
|
||||
return error.LLDReportedFailure;
|
||||
}
|
||||
if (stderr_context.data.items.len != 0) {
|
||||
std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringHashMap(void).init(self.base.allocator);
|
||||
defer rpath_table.deinit();
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if ((try rpath_table.fetchPut(rpath, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
}
|
||||
if (is_dyn_lib) {
|
||||
if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(full_out_path);
|
||||
}
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append("-L");
|
||||
try argv.append(lib_dir);
|
||||
}
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
|
||||
if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
|
||||
try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
const system_libs = self.base.options.system_libs.items();
|
||||
try argv.ensureCapacity(argv.items.len + system_libs.len);
|
||||
for (system_libs) |entry| {
|
||||
const link_lib = entry.key;
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
const ext = Compilation.classifyFileExt(link_lib);
|
||||
const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
|
||||
argv.appendAssumeCapacity(arg);
|
||||
}
|
||||
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try argv.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// On Darwin, libSystem has libc in it, but also you have to use it
|
||||
// to make syscalls because the syscall numbers are not documented
|
||||
// and change between versions. So we always link against libSystem.
|
||||
// LLD craps out if you do -lSystem cross compiling, so until that
|
||||
// codebase gets some love from the new maintainers we're left with
|
||||
// this dirty hack.
|
||||
if (self.base.options.is_native_os) {
|
||||
try argv.append("-lSystem");
|
||||
}
|
||||
|
||||
for (self.base.options.framework_dirs) |framework_dir| {
|
||||
try argv.append("-F");
|
||||
try argv.append(framework_dir);
|
||||
}
|
||||
for (self.base.options.frameworks) |framework| {
|
||||
try argv.append("-framework");
|
||||
try argv.append(framework);
|
||||
}
|
||||
|
||||
if (allow_shlib_undefined) {
|
||||
try argv.append("-undefined");
|
||||
try argv.append("dynamic_lookup");
|
||||
}
|
||||
if (self.base.options.bind_global_refs_locally) {
|
||||
try argv.append("-Bsymbolic");
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
|
||||
for (argv.items) |arg, i| {
|
||||
new_argv[i] = try arena.dupeZ(u8, arg);
|
||||
}
|
||||
|
||||
var stderr_context: LLDContext = .{
|
||||
.macho = self,
|
||||
.data = std.ArrayList(u8).init(self.base.allocator),
|
||||
};
|
||||
defer stderr_context.data.deinit();
|
||||
var stdout_context: LLDContext = .{
|
||||
.macho = self,
|
||||
.data = std.ArrayList(u8).init(self.base.allocator),
|
||||
};
|
||||
defer stdout_context.data.deinit();
|
||||
const llvm = @import("../llvm.zig");
|
||||
const ok = llvm.Link(
|
||||
.MachO,
|
||||
new_argv.ptr,
|
||||
new_argv.len,
|
||||
append_diagnostic,
|
||||
@ptrToInt(&stdout_context),
|
||||
@ptrToInt(&stderr_context),
|
||||
);
|
||||
if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
|
||||
if (stdout_context.data.items.len != 0) {
|
||||
std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
|
||||
}
|
||||
if (!ok) {
|
||||
// TODO parse this output and surface with the Compilation API rather than
|
||||
// directly outputting to stderr here.
|
||||
std.debug.print("{}", .{stderr_context.data.items});
|
||||
return error.LLDReportedFailure;
|
||||
}
|
||||
if (stderr_context.data.items.len != 0) {
|
||||
std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.base.options.disable_lld_caching) {
|
||||
|
||||
@ -1337,12 +1337,12 @@ fn buildOutputType(
|
||||
}
|
||||
};
|
||||
|
||||
if (output_mode == .Obj and object_format == .coff) {
|
||||
if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
|
||||
const total_obj_count = c_source_files.items.len +
|
||||
@boolToInt(root_src_file != null) +
|
||||
link_objects.items.len;
|
||||
if (total_obj_count > 1) {
|
||||
fatal("COFF does not support linking multiple objects into one", .{});
|
||||
fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user