authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-11-02 00:07:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-11-02 00:54:34-04:00
log1554dd9697b77b4fe4a309247982c3e29048f124
treef1ed649ac17a98dfb49067e04ea0c169cb34a725
parent5c97aff627394439089267132a6c86f5e6162f92
signaturelock-open Commit is signed but in an unrecognized format.

support building static self hosted compiler on macos

* add a --system-linker-hack command line parameter to work around poor LLD macho code. See #1535 * build.zig correctly handles static as well as dynamic dependencies when building the self hosted compiler. - no more unnecessary libxml2 dependency - a static build on macos produces a completely static self-hosted compiler for macos (except for libSystem as intended).

6 files changed, 122 insertions(+), 41 deletions(-)

build.zig+84-38
...@@ -121,12 +121,23 @@ pub fn build(b: *Builder) !void {...@@ -121,12 +121,23 @@ pub fn build(b: *Builder) !void {
121 test_step.dependOn(docs_step);121 test_step.dependOn(docs_step);
122}122}
123123
124fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void {124fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
125 for (dep.libdirs.toSliceConst()) |lib_dir| {125 for (dep.libdirs.toSliceConst()) |lib_dir| {
126 lib_exe_obj.addLibPath(lib_dir);126 lib_exe_obj.addLibPath(lib_dir);
127 }127 }
128 const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable;
128 for (dep.system_libs.toSliceConst()) |lib| {129 for (dep.system_libs.toSliceConst()) |lib| {
129 lib_exe_obj.linkSystemLibrary(lib);130 const static_bare_name = if (mem.eql(u8, lib, "curses"))
131 ([]const u8)("libncurses.a")
132 else
133 b.fmt("lib{}.a", lib);
134 const static_lib_name = os.path.join(b.allocator, lib_dir, static_bare_name) catch unreachable;
135 const have_static = fileExists(static_lib_name) catch unreachable;
136 if (have_static) {
137 lib_exe_obj.addObjectFile(static_lib_name);
138 } else {
139 lib_exe_obj.linkSystemLibrary(lib);
140 }
130 }141 }
131 for (dep.libs.toSliceConst()) |lib| {142 for (dep.libs.toSliceConst()) |lib| {
132 lib_exe_obj.addObjectFile(lib);143 lib_exe_obj.addObjectFile(lib);
...@@ -136,12 +147,23 @@ fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void {...@@ -136,12 +147,23 @@ fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void {
136 }147 }
137}148}
138149
150fn fileExists(filename: []const u8) !bool {
151 os.File.access(filename) catch |err| switch (err) {
152 error.PermissionDenied,
153 error.FileNotFound,
154 => return false,
155 else => return err,
156 };
157 return true;
158}
159
139fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {160fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
140 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";161 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
141 lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);162 lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
142}163}
143164
144const LibraryDep = struct.{165const LibraryDep = struct.{
166 prefix: []const u8,
145 libdirs: ArrayList([]const u8),167 libdirs: ArrayList([]const u8),
146 libs: ArrayList([]const u8),168 libs: ArrayList([]const u8),
147 system_libs: ArrayList([]const u8),169 system_libs: ArrayList([]const u8),
...@@ -149,21 +171,25 @@ const LibraryDep = struct.{...@@ -149,21 +171,25 @@ const LibraryDep = struct.{
149};171};
150172
151fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {173fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
152 const libs_output = try b.exec([][]const u8.{174 const shared_mode = try b.exec([][]const u8.{ llvm_config_exe, "--shared-mode" });
153 llvm_config_exe,175 const is_static = mem.startsWith(u8, shared_mode, "static");
154 "--libs",176 const libs_output = if (is_static)
155 "--system-libs",177 try b.exec([][]const u8.{
156 });178 llvm_config_exe,
157 const includes_output = try b.exec([][]const u8.{179 "--libfiles",
158 llvm_config_exe,180 "--system-libs",
159 "--includedir",181 })
160 });182 else
161 const libdir_output = try b.exec([][]const u8.{183 try b.exec([][]const u8.{
162 llvm_config_exe,184 llvm_config_exe,
163 "--libdir",185 "--libs",
164 });186 });
187 const includes_output = try b.exec([][]const u8.{ llvm_config_exe, "--includedir" });
188 const libdir_output = try b.exec([][]const u8.{ llvm_config_exe, "--libdir" });
189 const prefix_output = try b.exec([][]const u8.{ llvm_config_exe, "--prefix" });
165190
166 var result = LibraryDep.{191 var result = LibraryDep.{
192 .prefix = mem.split(prefix_output, " \r\n").next().?,
167 .libs = ArrayList([]const u8).init(b.allocator),193 .libs = ArrayList([]const u8).init(b.allocator),
168 .system_libs = ArrayList([]const u8).init(b.allocator),194 .system_libs = ArrayList([]const u8).init(b.allocator),
169 .includes = ArrayList([]const u8).init(b.allocator),195 .includes = ArrayList([]const u8).init(b.allocator),
...@@ -244,10 +270,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {...@@ -244,10 +270,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {
244}270}
245271
246fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {272fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
247 // This is for finding /lib/libz.a on alpine linux.
248 // TODO turn this into -Dextra-lib-path=/lib option
249 exe.addLibPath("/lib");
250
251 exe.setNoRoSegment(ctx.no_rosegment);273 exe.setNoRoSegment(ctx.no_rosegment);
252274
253 exe.addIncludeDir("src");275 exe.addIncludeDir("src");
...@@ -265,39 +287,63 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {...@@ -265,39 +287,63 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
265 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");287 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");
266 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");288 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");
267 }289 }
268 dependOnLib(exe, ctx.llvm);290 dependOnLib(b, exe, ctx.llvm);
269291
270 if (exe.target.getOs() == builtin.Os.linux) {292 if (exe.target.getOs() == builtin.Os.linux) {
271 const libstdcxx_path_padded = try b.exec([][]const u8.{293 try addCxxKnownPath(b, ctx, exe, "libstdc++.a",
272 ctx.cxx_compiler,294 \\Unable to determine path to libstdc++.a
273 "-print-file-name=libstdc++.a",295 \\On Fedora, install libstdc++-static and try again.
274 });296 \\
275 const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;297 );
276 if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
277 warn(
278 \\Unable to determine path to libstdc++.a
279 \\On Fedora, install libstdc++-static and try again.
280 \\
281 );
282 return error.RequiredLibraryNotFound;
283 }
284 exe.addObjectFile(libstdcxx_path);
285298
286 exe.linkSystemLibrary("pthread");299 exe.linkSystemLibrary("pthread");
287 } else if (exe.target.isDarwin()) {300 } else if (exe.target.isDarwin()) {
288 exe.linkSystemLibrary("c++");301 if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
302 // Compiler is GCC.
303 try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
304 exe.linkSystemLibrary("pthread");
305 // TODO LLD cannot perform this link.
306 // See https://github.com/ziglang/zig/issues/1535
307 exe.enableSystemLinkerHack();
308 } else |err| switch (err) {
309 error.RequiredLibraryNotFound => {
310 // System compiler, not gcc.
311 exe.linkSystemLibrary("c++");
312 },
313 else => return err,
314 }
289 }315 }
290316
291 if (ctx.dia_guids_lib.len != 0) {317 if (ctx.dia_guids_lib.len != 0) {
292 exe.addObjectFile(ctx.dia_guids_lib);318 exe.addObjectFile(ctx.dia_guids_lib);
293 }319 }
294320
295 if (exe.target.getOs() != builtin.Os.windows) {
296 exe.linkSystemLibrary("xml2");
297 }
298 exe.linkSystemLibrary("c");321 exe.linkSystemLibrary("c");
299}322}
300323
324fn addCxxKnownPath(
325 b: *Builder,
326 ctx: Context,
327 exe: var,
328 objname: []const u8,
329 errtxt: ?[]const u8,
330) !void {
331 const path_padded = try b.exec([][]const u8.{
332 ctx.cxx_compiler,
333 b.fmt("-print-file-name={}", objname),
334 });
335 const path_unpadded = mem.split(path_padded, "\r\n").next().?;
336 if (mem.eql(u8, path_unpadded, objname)) {
337 if (errtxt) |msg| {
338 warn("{}", msg);
339 } else {
340 warn("Unable to determine path to {}\n", objname);
341 }
342 return error.RequiredLibraryNotFound;
343 }
344 exe.addObjectFile(path_unpadded);
345}
346
301const Context = struct.{347const Context = struct.{
302 cmake_binary_dir: []const u8,348 cmake_binary_dir: []const u8,
303 cxx_compiler: []const u8,349 cxx_compiler: []const u8,
src/all_types.hpp+1
...@@ -1731,6 +1731,7 @@ struct CodeGen {...@@ -1731,6 +1731,7 @@ struct CodeGen {
1731 bool generate_error_name_table;1731 bool generate_error_name_table;
1732 bool enable_cache;1732 bool enable_cache;
1733 bool enable_time_report;1733 bool enable_time_report;
1734 bool system_linker_hack;
17341735
1735 //////////////////////////// Participates in Input Parameter Cache Hash1736 //////////////////////////// Participates in Input Parameter Cache Hash
1736 ZigList<LinkLib *> link_libs_list;1737 ZigList<LinkLib *> link_libs_list;
src/link.cpp+13-2
...@@ -778,7 +778,8 @@ static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) {...@@ -778,7 +778,8 @@ static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) {
778static void construct_linker_job_macho(LinkJob *lj) {778static void construct_linker_job_macho(LinkJob *lj) {
779 CodeGen *g = lj->codegen;779 CodeGen *g = lj->codegen;
780780
781 lj->args.append("-error-limit=0");781 // LLD MACH-O has no error limit option.
782 //lj->args.append("-error-limit=0");
782 lj->args.append("-demangle");783 lj->args.append("-demangle");
783784
784 if (g->linker_rdynamic) {785 if (g->linker_rdynamic) {
...@@ -1007,7 +1008,17 @@ void codegen_link(CodeGen *g) {...@@ -1007,7 +1008,17 @@ void codegen_link(CodeGen *g) {
1007 Buf diag = BUF_INIT;1008 Buf diag = BUF_INIT;
10081009
1009 codegen_add_time_event(g, "LLVM Link");1010 codegen_add_time_event(g, "LLVM Link");
1010 if (!zig_lld_link(g->zig_target.oformat, lj.args.items, lj.args.length, &diag)) {1011 if (g->system_linker_hack && g->zig_target.os == OsMacOSX) {
1012 Termination term;
1013 ZigList<const char *> args = {};
1014 for (size_t i = 1; i < lj.args.length; i += 1) {
1015 args.append(lj.args.at(i));
1016 }
1017 os_spawn_process("ld", args, &term);
1018 if (term.how != TerminationIdClean || term.code != 0) {
1019 exit(1);
1020 }
1021 } else if (!zig_lld_link(g->zig_target.oformat, lj.args.items, lj.args.length, &diag)) {
1011 fprintf(stderr, "%s\n", buf_ptr(&diag));1022 fprintf(stderr, "%s\n", buf_ptr(&diag));
1012 exit(1);1023 exit(1);
1013 }1024 }
src/main.cpp+4
...@@ -394,6 +394,7 @@ int main(int argc, char **argv) {...@@ -394,6 +394,7 @@ int main(int argc, char **argv) {
394 ZigList<const char *> test_exec_args = {0};394 ZigList<const char *> test_exec_args = {0};
395 int runtime_args_start = -1;395 int runtime_args_start = -1;
396 bool no_rosegment_workaround = false;396 bool no_rosegment_workaround = false;
397 bool system_linker_hack = false;
397398
398 if (argc >= 2 && strcmp(argv[1], "build") == 0) {399 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
399 Buf zig_exe_path_buf = BUF_INIT;400 Buf zig_exe_path_buf = BUF_INIT;
...@@ -560,6 +561,8 @@ int main(int argc, char **argv) {...@@ -560,6 +561,8 @@ int main(int argc, char **argv) {
560 timing_info = true;561 timing_info = true;
561 } else if (strcmp(arg, "--disable-pic") == 0) {562 } else if (strcmp(arg, "--disable-pic") == 0) {
562 disable_pic = true;563 disable_pic = true;
564 } else if (strcmp(arg, "--system-linker-hack") == 0) {
565 system_linker_hack = true;
563 } else if (strcmp(arg, "--test-cmd-bin") == 0) {566 } else if (strcmp(arg, "--test-cmd-bin") == 0) {
564 test_exec_args.append(nullptr);567 test_exec_args.append(nullptr);
565 } else if (arg[1] == 'L' && arg[2] != 0) {568 } else if (arg[1] == 'L' && arg[2] != 0) {
...@@ -893,6 +896,7 @@ int main(int argc, char **argv) {...@@ -893,6 +896,7 @@ int main(int argc, char **argv) {
893 g->verbose_llvm_ir = verbose_llvm_ir;896 g->verbose_llvm_ir = verbose_llvm_ir;
894 g->verbose_cimport = verbose_cimport;897 g->verbose_cimport = verbose_cimport;
895 codegen_set_errmsg_color(g, color);898 codegen_set_errmsg_color(g, color);
899 g->system_linker_hack = system_linker_hack;
896900
897 for (size_t i = 0; i < lib_dirs.length; i += 1) {901 for (size_t i = 0; i < lib_dirs.length; i += 1) {
898 codegen_add_lib_dir(g, lib_dirs.at(i));902 codegen_add_lib_dir(g, lib_dirs.at(i));
src/os.cpp+1-1
...@@ -103,7 +103,7 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,...@@ -103,7 +103,7 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,
103 }103 }
104104
105 pid_t pid;105 pid_t pid;
106 int rc = posix_spawn(&pid, exe, nullptr, nullptr, const_cast<char *const*>(argv), environ);106 int rc = posix_spawnp(&pid, exe, nullptr, nullptr, const_cast<char *const*>(argv), environ);
107 if (rc != 0) {107 if (rc != 0) {
108 zig_panic("posix_spawn failed: %s", strerror(rc));108 zig_panic("posix_spawn failed: %s", strerror(rc));
109 }109 }
std/build.zig+19
...@@ -836,6 +836,7 @@ pub const LibExeObjStep = struct.{...@@ -836,6 +836,7 @@ pub const LibExeObjStep = struct.{
836 assembly_files: ArrayList([]const u8),836 assembly_files: ArrayList([]const u8),
837 packages: ArrayList(Pkg),837 packages: ArrayList(Pkg),
838 build_options_contents: std.Buffer,838 build_options_contents: std.Buffer,
839 system_linker_hack: bool,
839840
840 // C only stuff841 // C only stuff
841 source_files: ArrayList([]const u8),842 source_files: ArrayList([]const u8),
...@@ -930,6 +931,7 @@ pub const LibExeObjStep = struct.{...@@ -930,6 +931,7 @@ pub const LibExeObjStep = struct.{
930 .disable_libc = true,931 .disable_libc = true,
931 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,932 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
932 .c_std = Builder.CStd.C99,933 .c_std = Builder.CStd.C99,
934 .system_linker_hack = false,
933 };935 };
934 self.computeOutFileNames();936 self.computeOutFileNames();
935 return self;937 return self;
...@@ -965,6 +967,7 @@ pub const LibExeObjStep = struct.{...@@ -965,6 +967,7 @@ pub const LibExeObjStep = struct.{
965 .is_zig = false,967 .is_zig = false,
966 .linker_script = null,968 .linker_script = null,
967 .c_std = Builder.CStd.C99,969 .c_std = Builder.CStd.C99,
970 .system_linker_hack = false,
968971
969 .root_src = undefined,972 .root_src = undefined,
970 .verbose_link = false,973 .verbose_link = false,
...@@ -1162,6 +1165,10 @@ pub const LibExeObjStep = struct.{...@@ -1162,6 +1165,10 @@ pub const LibExeObjStep = struct.{
1162 self.disable_libc = disable;1165 self.disable_libc = disable;
1163 }1166 }
11641167
1168 pub fn enableSystemLinkerHack(self: *LibExeObjStep) void {
1169 self.system_linker_hack = true;
1170 }
1171
1165 fn make(step: *Step) !void {1172 fn make(step: *Step) !void {
1166 const self = @fieldParentPtr(LibExeObjStep, "step", step);1173 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1167 return if (self.is_zig) self.makeZig() else self.makeC();1174 return if (self.is_zig) self.makeZig() else self.makeC();
...@@ -1338,6 +1345,9 @@ pub const LibExeObjStep = struct.{...@@ -1338,6 +1345,9 @@ pub const LibExeObjStep = struct.{
1338 if (self.no_rosegment) {1345 if (self.no_rosegment) {
1339 try zig_args.append("--no-rosegment");1346 try zig_args.append("--no-rosegment");
1340 }1347 }
1348 if (self.system_linker_hack) {
1349 try zig_args.append("--system-linker-hack");
1350 }
13411351
1342 try builder.spawnChild(zig_args.toSliceConst());1352 try builder.spawnChild(zig_args.toSliceConst());
13431353
...@@ -1646,6 +1656,7 @@ pub const TestStep = struct.{...@@ -1646,6 +1656,7 @@ pub const TestStep = struct.{
1646 object_files: ArrayList([]const u8),1656 object_files: ArrayList([]const u8),
1647 no_rosegment: bool,1657 no_rosegment: bool,
1648 output_path: ?[]const u8,1658 output_path: ?[]const u8,
1659 system_linker_hack: bool,
16491660
1650 pub fn init(builder: *Builder, root_src: []const u8) TestStep {1661 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
1651 const step_name = builder.fmt("test {}", root_src);1662 const step_name = builder.fmt("test {}", root_src);
...@@ -1665,6 +1676,7 @@ pub const TestStep = struct.{...@@ -1665,6 +1676,7 @@ pub const TestStep = struct.{
1665 .object_files = ArrayList([]const u8).init(builder.allocator),1676 .object_files = ArrayList([]const u8).init(builder.allocator),
1666 .no_rosegment = false,1677 .no_rosegment = false,
1667 .output_path = null,1678 .output_path = null,
1679 .system_linker_hack = false,
1668 };1680 };
1669 }1681 }
16701682
...@@ -1747,6 +1759,10 @@ pub const TestStep = struct.{...@@ -1747,6 +1759,10 @@ pub const TestStep = struct.{
1747 self.exec_cmd_args = args;1759 self.exec_cmd_args = args;
1748 }1760 }
17491761
1762 pub fn enableSystemLinkerHack(self: *TestStep) void {
1763 self.system_linker_hack = true;
1764 }
1765
1750 fn make(step: *Step) !void {1766 fn make(step: *Step) !void {
1751 const self = @fieldParentPtr(TestStep, "step", step);1767 const self = @fieldParentPtr(TestStep, "step", step);
1752 const builder = self.builder;1768 const builder = self.builder;
...@@ -1851,6 +1867,9 @@ pub const TestStep = struct.{...@@ -1851,6 +1867,9 @@ pub const TestStep = struct.{
1851 if (self.no_rosegment) {1867 if (self.no_rosegment) {
1852 try zig_args.append("--no-rosegment");1868 try zig_args.append("--no-rosegment");
1853 }1869 }
1870 if (self.system_linker_hack) {
1871 try zig_args.append("--system-linker-hack");
1872 }
18541873
1855 try builder.spawnChild(zig_args.toSliceConst());1874 try builder.spawnChild(zig_args.toSliceConst());
1856 }1875 }