| author | |
| committer | |
| log | 1554dd9697b77b4fe4a309247982c3e29048f124 |
| tree | f1ed649ac17a98dfb49067e04ea0c169cb34a725 |
| parent | 5c97aff627394439089267132a6c86f5e6162f92 |
| signature | Commit is signed but in an unrecognized format. |
* 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 | 121 | test_step.dependOn(docs_step); |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void { | |
| 124 | fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { | |
| 125 | 125 | for (dep.libdirs.toSliceConst()) |lib_dir| { |
| 126 | 126 | lib_exe_obj.addLibPath(lib_dir); |
| 127 | 127 | } |
| 128 | const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable; | |
| 128 | 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 | 142 | for (dep.libs.toSliceConst()) |lib| { |
| 132 | 143 | lib_exe_obj.addObjectFile(lib); |
| ... | ... | @@ -136,12 +147,23 @@ fn dependOnLib(lib_exe_obj: var, dep: LibraryDep) void { |
| 136 | 147 | } |
| 137 | 148 | } |
| 138 | 149 | |
| 150 | fn 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 | ||
| 139 | 160 | fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { |
| 140 | 161 | const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; |
| 141 | 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 | } |
| 143 | 164 | |
| 144 | 165 | const LibraryDep = struct.{ |
| 166 | prefix: []const u8, | |
| 145 | 167 | libdirs: ArrayList([]const u8), |
| 146 | 168 | libs: ArrayList([]const u8), |
| 147 | 169 | system_libs: ArrayList([]const u8), |
| ... | ... | @@ -149,21 +171,25 @@ const LibraryDep = struct.{ |
| 149 | 171 | }; |
| 150 | 172 | |
| 151 | 173 | fn 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" }); | |
| 165 | 190 | |
| 166 | 191 | var result = LibraryDep.{ |
| 192 | .prefix = mem.split(prefix_output, " \r\n").next().?, | |
| 167 | 193 | .libs = ArrayList([]const u8).init(b.allocator), |
| 168 | 194 | .system_libs = ArrayList([]const u8).init(b.allocator), |
| 169 | 195 | .includes = ArrayList([]const u8).init(b.allocator), |
| ... | ... | @@ -244,10 +270,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { |
| 244 | 270 | } |
| 245 | 271 | |
| 246 | 272 | fn 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 | 273 | exe.setNoRoSegment(ctx.no_rosegment); |
| 252 | 274 | |
| 253 | 275 | exe.addIncludeDir("src"); |
| ... | ... | @@ -265,39 +287,63 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { |
| 265 | 287 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); |
| 266 | 288 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); |
| 267 | 289 | } |
| 268 | dependOnLib(exe, ctx.llvm); | |
| 290 | dependOnLib(b, exe, ctx.llvm); | |
| 269 | 291 | |
| 270 | 292 | 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 | ); | |
| 285 | 298 | |
| 286 | 299 | exe.linkSystemLibrary("pthread"); |
| 287 | 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 | } |
| 290 | 316 | |
| 291 | 317 | if (ctx.dia_guids_lib.len != 0) { |
| 292 | 318 | exe.addObjectFile(ctx.dia_guids_lib); |
| 293 | 319 | } |
| 294 | 320 | |
| 295 | if (exe.target.getOs() != builtin.Os.windows) { | |
| 296 | exe.linkSystemLibrary("xml2"); | |
| 297 | } | |
| 298 | 321 | exe.linkSystemLibrary("c"); |
| 299 | 322 | } |
| 300 | 323 | |
| 324 | fn 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 | ||
| 301 | 347 | const Context = struct.{ |
| 302 | 348 | cmake_binary_dir: []const u8, |
| 303 | 349 | cxx_compiler: []const u8, |
src/all_types.hpp+1| ... | ... | @@ -1731,6 +1731,7 @@ struct CodeGen { |
| 1731 | 1731 | bool generate_error_name_table; |
| 1732 | 1732 | bool enable_cache; |
| 1733 | 1733 | bool enable_time_report; |
| 1734 | bool system_linker_hack; | |
| 1734 | 1735 | |
| 1735 | 1736 | //////////////////////////// Participates in Input Parameter Cache Hash |
| 1736 | 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 | 778 | static void construct_linker_job_macho(LinkJob *lj) { |
| 779 | 779 | CodeGen *g = lj->codegen; |
| 780 | 780 | |
| 781 | lj->args.append("-error-limit=0"); | |
| 781 | // LLD MACH-O has no error limit option. | |
| 782 | //lj->args.append("-error-limit=0"); | |
| 782 | 783 | lj->args.append("-demangle"); |
| 783 | 784 | |
| 784 | 785 | if (g->linker_rdynamic) { |
| ... | ... | @@ -1007,7 +1008,17 @@ void codegen_link(CodeGen *g) { |
| 1007 | 1008 | Buf diag = BUF_INIT; |
| 1008 | 1009 | |
| 1009 | 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 | 1022 | fprintf(stderr, "%s\n", buf_ptr(&diag)); |
| 1012 | 1023 | exit(1); |
| 1013 | 1024 | } |
src/main.cpp+4| ... | ... | @@ -394,6 +394,7 @@ int main(int argc, char **argv) { |
| 394 | 394 | ZigList<const char *> test_exec_args = {0}; |
| 395 | 395 | int runtime_args_start = -1; |
| 396 | 396 | bool no_rosegment_workaround = false; |
| 397 | bool system_linker_hack = false; | |
| 397 | 398 | |
| 398 | 399 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { |
| 399 | 400 | Buf zig_exe_path_buf = BUF_INIT; |
| ... | ... | @@ -560,6 +561,8 @@ int main(int argc, char **argv) { |
| 560 | 561 | timing_info = true; |
| 561 | 562 | } else if (strcmp(arg, "--disable-pic") == 0) { |
| 562 | 563 | disable_pic = true; |
| 564 | } else if (strcmp(arg, "--system-linker-hack") == 0) { | |
| 565 | system_linker_hack = true; | |
| 563 | 566 | } else if (strcmp(arg, "--test-cmd-bin") == 0) { |
| 564 | 567 | test_exec_args.append(nullptr); |
| 565 | 568 | } else if (arg[1] == 'L' && arg[2] != 0) { |
| ... | ... | @@ -893,6 +896,7 @@ int main(int argc, char **argv) { |
| 893 | 896 | g->verbose_llvm_ir = verbose_llvm_ir; |
| 894 | 897 | g->verbose_cimport = verbose_cimport; |
| 895 | 898 | codegen_set_errmsg_color(g, color); |
| 899 | g->system_linker_hack = system_linker_hack; | |
| 896 | 900 | |
| 897 | 901 | for (size_t i = 0; i < lib_dirs.length; i += 1) { |
| 898 | 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 | 103 | } |
| 104 | 104 | |
| 105 | 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 | 107 | if (rc != 0) { |
| 108 | 108 | zig_panic("posix_spawn failed: %s", strerror(rc)); |
| 109 | 109 | } |
std/build.zig+19| ... | ... | @@ -836,6 +836,7 @@ pub const LibExeObjStep = struct.{ |
| 836 | 836 | assembly_files: ArrayList([]const u8), |
| 837 | 837 | packages: ArrayList(Pkg), |
| 838 | 838 | build_options_contents: std.Buffer, |
| 839 | system_linker_hack: bool, | |
| 839 | 840 | |
| 840 | 841 | // C only stuff |
| 841 | 842 | source_files: ArrayList([]const u8), |
| ... | ... | @@ -930,6 +931,7 @@ pub const LibExeObjStep = struct.{ |
| 930 | 931 | .disable_libc = true, |
| 931 | 932 | .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, |
| 932 | 933 | .c_std = Builder.CStd.C99, |
| 934 | .system_linker_hack = false, | |
| 933 | 935 | }; |
| 934 | 936 | self.computeOutFileNames(); |
| 935 | 937 | return self; |
| ... | ... | @@ -965,6 +967,7 @@ pub const LibExeObjStep = struct.{ |
| 965 | 967 | .is_zig = false, |
| 966 | 968 | .linker_script = null, |
| 967 | 969 | .c_std = Builder.CStd.C99, |
| 970 | .system_linker_hack = false, | |
| 968 | 971 | |
| 969 | 972 | .root_src = undefined, |
| 970 | 973 | .verbose_link = false, |
| ... | ... | @@ -1162,6 +1165,10 @@ pub const LibExeObjStep = struct.{ |
| 1162 | 1165 | self.disable_libc = disable; |
| 1163 | 1166 | } |
| 1164 | 1167 | |
| 1168 | pub fn enableSystemLinkerHack(self: *LibExeObjStep) void { | |
| 1169 | self.system_linker_hack = true; | |
| 1170 | } | |
| 1171 | ||
| 1165 | 1172 | fn make(step: *Step) !void { |
| 1166 | 1173 | const self = @fieldParentPtr(LibExeObjStep, "step", step); |
| 1167 | 1174 | return if (self.is_zig) self.makeZig() else self.makeC(); |
| ... | ... | @@ -1338,6 +1345,9 @@ pub const LibExeObjStep = struct.{ |
| 1338 | 1345 | if (self.no_rosegment) { |
| 1339 | 1346 | try zig_args.append("--no-rosegment"); |
| 1340 | 1347 | } |
| 1348 | if (self.system_linker_hack) { | |
| 1349 | try zig_args.append("--system-linker-hack"); | |
| 1350 | } | |
| 1341 | 1351 | |
| 1342 | 1352 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1343 | 1353 | |
| ... | ... | @@ -1646,6 +1656,7 @@ pub const TestStep = struct.{ |
| 1646 | 1656 | object_files: ArrayList([]const u8), |
| 1647 | 1657 | no_rosegment: bool, |
| 1648 | 1658 | output_path: ?[]const u8, |
| 1659 | system_linker_hack: bool, | |
| 1649 | 1660 | |
| 1650 | 1661 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1651 | 1662 | const step_name = builder.fmt("test {}", root_src); |
| ... | ... | @@ -1665,6 +1676,7 @@ pub const TestStep = struct.{ |
| 1665 | 1676 | .object_files = ArrayList([]const u8).init(builder.allocator), |
| 1666 | 1677 | .no_rosegment = false, |
| 1667 | 1678 | .output_path = null, |
| 1679 | .system_linker_hack = false, | |
| 1668 | 1680 | }; |
| 1669 | 1681 | } |
| 1670 | 1682 | |
| ... | ... | @@ -1747,6 +1759,10 @@ pub const TestStep = struct.{ |
| 1747 | 1759 | self.exec_cmd_args = args; |
| 1748 | 1760 | } |
| 1749 | 1761 | |
| 1762 | pub fn enableSystemLinkerHack(self: *TestStep) void { | |
| 1763 | self.system_linker_hack = true; | |
| 1764 | } | |
| 1765 | ||
| 1750 | 1766 | fn make(step: *Step) !void { |
| 1751 | 1767 | const self = @fieldParentPtr(TestStep, "step", step); |
| 1752 | 1768 | const builder = self.builder; |
| ... | ... | @@ -1851,6 +1867,9 @@ pub const TestStep = struct.{ |
| 1851 | 1867 | if (self.no_rosegment) { |
| 1852 | 1868 | try zig_args.append("--no-rosegment"); |
| 1853 | 1869 | } |
| 1870 | if (self.system_linker_hack) { | |
| 1871 | try zig_args.append("--system-linker-hack"); | |
| 1872 | } | |
| 1854 | 1873 | |
| 1855 | 1874 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1856 | 1875 | } |