Commit 3249e5d952
src/link/MachO.zig
@@ -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");
- }
+ const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
- try argv.append("-error-limit");
- try argv.append("0");
+ 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];
- try argv.append("-demangle");
+ if (comp.c_object_table.count() != 0)
+ break :blk comp.c_object_table.items()[0].key.status.success.object_path;
- if (self.base.options.rdynamic) {
- try argv.append("--export-dynamic");
- }
+ if (module_obj_path) |p|
+ break :blk p;
- try argv.appendSlice(self.base.options.extra_lld_args);
+ // 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, .{});
+ }
+ } 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");
- 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");
- }
+ try argv.append("-error-limit");
+ try argv.append("0");
- if (is_dyn_lib) {
- try argv.append("-static");
- } else {
- try argv.append("-dynamic");
- }
+ try argv.append("-demangle");
- if (is_dyn_lib) {
- try argv.append("-dylib");
+ if (self.base.options.rdynamic) {
+ try argv.append("--export-dynamic");
+ }
- 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);
+ try argv.appendSlice(self.base.options.extra_lld_args);
- 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);
+ 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");
}
- // 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));
- }
+ if (is_dyn_lib) {
+ try argv.append("-static");
+ } else {
+ try argv.append("-dynamic");
+ }
- try argv.append("-arch");
- try argv.append(darwinArchString(target.cpu.arch));
+ if (is_dyn_lib) {
+ try argv.append("-dylib");
- 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");
+ 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");
},
- else => {
- try argv.append("-iphoneos_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);
+ 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);
+ 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");
- }
+ 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);
+ 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);
+ 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);
- }
+ 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);
+ // 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);
- }
+ 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);
- }
+ // 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);
- }
+ // 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 (!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);
- }
+ // 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");
- }
+ // 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);
- }
+ 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 (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);
- }
+ 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);
- }
+ 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});
+ 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) {
src/main.zig
@@ -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)});
}
}