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
signature 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 {
121121 test_step.dependOn(docs_step);
122122}
123123
124fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void {
124fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
125125 for (dep.libdirs.toSliceConst()) |lib_dir| {
126126 lib_exe_obj.addLibPath(lib_dir);
127127 }
128 const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable;
128129 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 }
130141 }
131142 for (dep.libs.toSliceConst()) |lib| {
132143 lib_exe_obj.addObjectFile(lib);
......@@ -136,12 +147,23 @@ fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void {
136147 }
137148}
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
139160fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
140161 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
141162 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);
142163}
143164
144165const LibraryDep = struct.{
166 prefix: []const u8,
145167 libdirs: ArrayList([]const u8),
146168 libs: ArrayList([]const u8),
147169 system_libs: ArrayList([]const u8),
......@@ -149,21 +171,25 @@ const LibraryDep = struct.{
149171};
150172
151173fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
152 const libs_output = try b.exec([][]const u8.{
153 llvm_config_exe,
154 "--libs",
155 "--system-libs",
156 });
157 const includes_output = try b.exec([][]const u8.{
158 llvm_config_exe,
159 "--includedir",
160 });
161 const libdir_output = try b.exec([][]const u8.{
162 llvm_config_exe,
163 "--libdir",
164 });
174 const shared_mode = try b.exec([][]const u8.{ llvm_config_exe, "--shared-mode" });
175 const is_static = mem.startsWith(u8, shared_mode, "static");
176 const libs_output = if (is_static)
177 try b.exec([][]const u8.{
178 llvm_config_exe,
179 "--libfiles",
180 "--system-libs",
181 })
182 else
183 try b.exec([][]const u8.{
184 llvm_config_exe,
185 "--libs",
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
166191 var result = LibraryDep.{
192 .prefix = mem.split(prefix_output, " \r\n").next().?,
167193 .libs = ArrayList([]const u8).init(b.allocator),
168194 .system_libs = ArrayList([]const u8).init(b.allocator),
169195 .includes = ArrayList([]const u8).init(b.allocator),
......@@ -244,10 +270,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {
244270}
245271
246272fn 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
251273 exe.setNoRoSegment(ctx.no_rosegment);
252274
253275 exe.addIncludeDir("src");
......@@ -265,39 +287,63 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
265287 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");
266288 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");
267289 }
268 dependOnLib(exe, ctx.llvm);
290 dependOnLib(b, exe, ctx.llvm);
269291
270292 if (exe.target.getOs() == builtin.Os.linux) {
271 const libstdcxx_path_padded = try b.exec([][]const u8.{
272 ctx.cxx_compiler,
273 "-print-file-name=libstdc++.a",
274 });
275 const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
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);
293 try addCxxKnownPath(b, ctx, exe, "libstdc++.a",
294 \\Unable to determine path to libstdc++.a
295 \\On Fedora, install libstdc++-static and try again.
296 \\
297 );
285298
286299 exe.linkSystemLibrary("pthread");
287300 } 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 }
289315 }
290316
291317 if (ctx.dia_guids_lib.len != 0) {
292318 exe.addObjectFile(ctx.dia_guids_lib);
293319 }
294320
295 if (exe.target.getOs() != builtin.Os.windows) {
296 exe.linkSystemLibrary("xml2");
297 }
298321 exe.linkSystemLibrary("c");
299322}
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
301347const Context = struct.{
302348 cmake_binary_dir: []const u8,
303349 cxx_compiler: []const u8,
src/all_types.hpp+1
......@@ -1731,6 +1731,7 @@ struct CodeGen {
17311731 bool generate_error_name_table;
17321732 bool enable_cache;
17331733 bool enable_time_report;
1734 bool system_linker_hack;
17341735
17351736 //////////////////////////// Participates in Input Parameter Cache Hash
17361737 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) {
778778static void construct_linker_job_macho(LinkJob *lj) {
779779 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");
782783 lj->args.append("-demangle");
783784
784785 if (g->linker_rdynamic) {
......@@ -1007,7 +1008,17 @@ void codegen_link(CodeGen *g) {
10071008 Buf diag = BUF_INIT;
10081009
10091010 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)) {
10111022 fprintf(stderr, "%s\n", buf_ptr(&diag));
10121023 exit(1);
10131024 }
src/main.cpp+4
......@@ -394,6 +394,7 @@ int main(int argc, char **argv) {
394394 ZigList<const char *> test_exec_args = {0};
395395 int runtime_args_start = -1;
396396 bool no_rosegment_workaround = false;
397 bool system_linker_hack = false;
397398
398399 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
399400 Buf zig_exe_path_buf = BUF_INIT;
......@@ -560,6 +561,8 @@ int main(int argc, char **argv) {
560561 timing_info = true;
561562 } else if (strcmp(arg, "--disable-pic") == 0) {
562563 disable_pic = true;
564 } else if (strcmp(arg, "--system-linker-hack") == 0) {
565 system_linker_hack = true;
563566 } else if (strcmp(arg, "--test-cmd-bin") == 0) {
564567 test_exec_args.append(nullptr);
565568 } else if (arg[1] == 'L' && arg[2] != 0) {
......@@ -893,6 +896,7 @@ int main(int argc, char **argv) {
893896 g->verbose_llvm_ir = verbose_llvm_ir;
894897 g->verbose_cimport = verbose_cimport;
895898 codegen_set_errmsg_color(g, color);
899 g->system_linker_hack = system_linker_hack;
896900
897901 for (size_t i = 0; i < lib_dirs.length; i += 1) {
898902 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,
103103 }
104104
105105 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);
107107 if (rc != 0) {
108108 zig_panic("posix_spawn failed: %s", strerror(rc));
109109 }
std/build.zig+19
......@@ -836,6 +836,7 @@ pub const LibExeObjStep = struct.{
836836 assembly_files: ArrayList([]const u8),
837837 packages: ArrayList(Pkg),
838838 build_options_contents: std.Buffer,
839 system_linker_hack: bool,
839840
840841 // C only stuff
841842 source_files: ArrayList([]const u8),
......@@ -930,6 +931,7 @@ pub const LibExeObjStep = struct.{
930931 .disable_libc = true,
931932 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
932933 .c_std = Builder.CStd.C99,
934 .system_linker_hack = false,
933935 };
934936 self.computeOutFileNames();
935937 return self;
......@@ -965,6 +967,7 @@ pub const LibExeObjStep = struct.{
965967 .is_zig = false,
966968 .linker_script = null,
967969 .c_std = Builder.CStd.C99,
970 .system_linker_hack = false,
968971
969972 .root_src = undefined,
970973 .verbose_link = false,
......@@ -1162,6 +1165,10 @@ pub const LibExeObjStep = struct.{
11621165 self.disable_libc = disable;
11631166 }
11641167
1168 pub fn enableSystemLinkerHack(self: *LibExeObjStep) void {
1169 self.system_linker_hack = true;
1170 }
1171
11651172 fn make(step: *Step) !void {
11661173 const self = @fieldParentPtr(LibExeObjStep, "step", step);
11671174 return if (self.is_zig) self.makeZig() else self.makeC();
......@@ -1338,6 +1345,9 @@ pub const LibExeObjStep = struct.{
13381345 if (self.no_rosegment) {
13391346 try zig_args.append("--no-rosegment");
13401347 }
1348 if (self.system_linker_hack) {
1349 try zig_args.append("--system-linker-hack");
1350 }
13411351
13421352 try builder.spawnChild(zig_args.toSliceConst());
13431353
......@@ -1646,6 +1656,7 @@ pub const TestStep = struct.{
16461656 object_files: ArrayList([]const u8),
16471657 no_rosegment: bool,
16481658 output_path: ?[]const u8,
1659 system_linker_hack: bool,
16491660
16501661 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
16511662 const step_name = builder.fmt("test {}", root_src);
......@@ -1665,6 +1676,7 @@ pub const TestStep = struct.{
16651676 .object_files = ArrayList([]const u8).init(builder.allocator),
16661677 .no_rosegment = false,
16671678 .output_path = null,
1679 .system_linker_hack = false,
16681680 };
16691681 }
16701682
......@@ -1747,6 +1759,10 @@ pub const TestStep = struct.{
17471759 self.exec_cmd_args = args;
17481760 }
17491761
1762 pub fn enableSystemLinkerHack(self: *TestStep) void {
1763 self.system_linker_hack = true;
1764 }
1765
17501766 fn make(step: *Step) !void {
17511767 const self = @fieldParentPtr(TestStep, "step", step);
17521768 const builder = self.builder;
......@@ -1851,6 +1867,9 @@ pub const TestStep = struct.{
18511867 if (self.no_rosegment) {
18521868 try zig_args.append("--no-rosegment");
18531869 }
1870 if (self.system_linker_hack) {
1871 try zig_args.append("--system-linker-hack");
1872 }
18541873
18551874 try builder.spawnChild(zig_args.toSliceConst());
18561875 }