| author | |
| committer | |
| log | 391d81a3802e48f59d804746eedc396d60ad3820 |
| tree | e4e634ad79891ae089b7f02f417bf661824f6679 |
| parent | 21550bb7cd5596e24a623f5ae1374502e879b553 |
| parent | 7dd4afb224f4ca747b8eb462c28337ce9a63d38c |
| signature |
invoke LLD as a child process rather than a library11 files changed, 332 insertions(+), 267 deletions(-)
lib/std/testing.zig+21| ... | @@ -247,6 +247,7 @@ test "expectWithinEpsilon" { | ... | @@ -247,6 +247,7 @@ test "expectWithinEpsilon" { |
| 247 | /// This function is intended to be used only in tests. When the two slices are not | 247 | /// This function is intended to be used only in tests. When the two slices are not |
| 248 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, | 248 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, |
| 249 | /// then aborts. | 249 | /// then aborts. |
| 250 | /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. | ||
| 250 | pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void { | 251 | pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void { |
| 251 | // TODO better printing of the difference | 252 | // TODO better printing of the difference |
| 252 | // If the arrays are small enough we could print the whole thing | 253 | // If the arrays are small enough we could print the whole thing |
| ... | @@ -368,6 +369,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void { | ... | @@ -368,6 +369,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void { |
| 368 | } | 369 | } |
| 369 | } | 370 | } |
| 370 | 371 | ||
| 372 | pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) void { | ||
| 373 | if (std.mem.endsWith(u8, actual, expected_ends_with)) | ||
| 374 | return; | ||
| 375 | |||
| 376 | const shortened_actual = if (actual.len >= expected_ends_with.len) | ||
| 377 | actual[0..expected_ends_with.len] | ||
| 378 | else | ||
| 379 | actual; | ||
| 380 | |||
| 381 | print("\n====== expected to end with: =========\n", .{}); | ||
| 382 | printWithVisibleNewlines(expected_ends_with); | ||
| 383 | print("\n====== instead ended with: ===========\n", .{}); | ||
| 384 | printWithVisibleNewlines(shortened_actual); | ||
| 385 | print("\n========= full output: ==============\n", .{}); | ||
| 386 | printWithVisibleNewlines(actual); | ||
| 387 | print("\n======================================\n", .{}); | ||
| 388 | |||
| 389 | @panic("test failure"); | ||
| 390 | } | ||
| 391 | |||
| 371 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { | 392 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { |
| 372 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| | 393 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| |
| 373 | line_begin + 1 | 394 | line_begin + 1 |
src/Compilation.zig+1-1| ... | @@ -1804,7 +1804,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * | ... | @@ -1804,7 +1804,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * |
| 1804 | if (comp.clang_preprocessor_mode == .stdout) | 1804 | if (comp.clang_preprocessor_mode == .stdout) |
| 1805 | std.process.exit(0); | 1805 | std.process.exit(0); |
| 1806 | }, | 1806 | }, |
| 1807 | else => std.process.exit(1), | 1807 | else => std.process.abort(), |
| 1808 | } | 1808 | } |
| 1809 | } else { | 1809 | } else { |
| 1810 | child.stdin_behavior = .Ignore; | 1810 | child.stdin_behavior = .Ignore; |
src/link/Coff.zig+60-55| ... | @@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 907 | // Create an LLD command line and invoke it. | 907 | // Create an LLD command line and invoke it. |
| 908 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | 908 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 909 | defer argv.deinit(); | 909 | defer argv.deinit(); |
| 910 | // The first argument is ignored as LLD is called as a library, set it | 910 | // We will invoke ourselves as a child process to gain access to LLD. |
| 911 | // anyway to the correct LLD driver name for this target so that it's | 911 | // This is necessary because LLD does not behave properly as a library - |
| 912 | // correctly printed when `verbose_link` is true. This is needed for some | 912 | // it calls exit() and does not reset all global data between invocations. |
| 913 | // tools such as CMake when Zig is used as C compiler. | 913 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" }); |
| 914 | try argv.append("lld-link"); | ||
| 915 | 914 | ||
| 916 | try argv.append("-ERRORLIMIT:0"); | 915 | try argv.append("-ERRORLIMIT:0"); |
| 917 | try argv.append("-NOLOGO"); | 916 | try argv.append("-NOLOGO"); |
| ... | @@ -1149,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1149,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1149 | } | 1148 | } |
| 1150 | 1149 | ||
| 1151 | if (self.base.options.verbose_link) { | 1150 | if (self.base.options.verbose_link) { |
| 1152 | Compilation.dump_argv(argv.items); | 1151 | // Skip over our own name so that the LLD linker name is the first argv item. |
| 1152 | Compilation.dump_argv(argv.items[1..]); | ||
| 1153 | } | 1153 | } |
| 1154 | 1154 | ||
| 1155 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 1155 | // Sadly, we must run LLD as a child process because it does not behave |
| 1156 | for (argv.items) |arg, i| { | 1156 | // properly as a library. |
| 1157 | new_argv[i] = try arena.dupeZ(u8, arg); | 1157 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1158 | } | 1158 | defer child.deinit(); |
| 1159 | |||
| 1160 | if (comp.clang_passthrough_mode) { | ||
| 1161 | child.stdin_behavior = .Inherit; | ||
| 1162 | child.stdout_behavior = .Inherit; | ||
| 1163 | child.stderr_behavior = .Inherit; | ||
| 1164 | |||
| 1165 | const term = child.spawnAndWait() catch |err| { | ||
| 1166 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1167 | return error.UnableToSpawnSelf; | ||
| 1168 | }; | ||
| 1169 | switch (term) { | ||
| 1170 | .Exited => |code| { | ||
| 1171 | if (code != 0) { | ||
| 1172 | // TODO https://github.com/ziglang/zig/issues/6342 | ||
| 1173 | std.process.exit(1); | ||
| 1174 | } | ||
| 1175 | }, | ||
| 1176 | else => std.process.abort(), | ||
| 1177 | } | ||
| 1178 | } else { | ||
| 1179 | child.stdin_behavior = .Ignore; | ||
| 1180 | child.stdout_behavior = .Ignore; | ||
| 1181 | child.stderr_behavior = .Pipe; | ||
| 1182 | |||
| 1183 | try child.spawn(); | ||
| 1184 | |||
| 1185 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 1186 | |||
| 1187 | const term = child.wait() catch |err| { | ||
| 1188 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1189 | return error.UnableToSpawnSelf; | ||
| 1190 | }; | ||
| 1191 | |||
| 1192 | switch (term) { | ||
| 1193 | .Exited => |code| { | ||
| 1194 | if (code != 0) { | ||
| 1195 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1196 | // directly outputting to stderr here. | ||
| 1197 | std.debug.print("{s}", .{stderr}); | ||
| 1198 | return error.LLDReportedFailure; | ||
| 1199 | } | ||
| 1200 | }, | ||
| 1201 | else => { | ||
| 1202 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 1203 | return error.LLDCrashed; | ||
| 1204 | }, | ||
| 1205 | } | ||
| 1159 | 1206 | ||
| 1160 | var stderr_context: LLDContext = .{ | 1207 | if (stderr.len != 0) { |
| 1161 | .coff = self, | 1208 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 1162 | .data = std.ArrayList(u8).init(self.base.allocator), | 1209 | } |
| 1163 | }; | ||
| 1164 | defer stderr_context.data.deinit(); | ||
| 1165 | var stdout_context: LLDContext = .{ | ||
| 1166 | .coff = self, | ||
| 1167 | .data = std.ArrayList(u8).init(self.base.allocator), | ||
| 1168 | }; | ||
| 1169 | defer stdout_context.data.deinit(); | ||
| 1170 | const llvm = @import("../llvm.zig"); | ||
| 1171 | const ok = llvm.Link( | ||
| 1172 | .COFF, | ||
| 1173 | new_argv.ptr, | ||
| 1174 | new_argv.len, | ||
| 1175 | append_diagnostic, | ||
| 1176 | @ptrToInt(&stdout_context), | ||
| 1177 | @ptrToInt(&stderr_context), | ||
| 1178 | ); | ||
| 1179 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | ||
| 1180 | if (stdout_context.data.items.len != 0) { | ||
| 1181 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | ||
| 1182 | } | ||
| 1183 | if (!ok) { | ||
| 1184 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1185 | // directly outputting to stderr here. | ||
| 1186 | std.debug.print("{}", .{stderr_context.data.items}); | ||
| 1187 | return error.LLDReportedFailure; | ||
| 1188 | } | ||
| 1189 | if (stderr_context.data.items.len != 0) { | ||
| 1190 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | ||
| 1191 | } | 1210 | } |
| 1192 | } | 1211 | } |
| 1193 | 1212 | ||
| ... | @@ -1207,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1207,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1207 | } | 1226 | } |
| 1208 | } | 1227 | } |
| 1209 | 1228 | ||
| 1210 | const LLDContext = struct { | ||
| 1211 | data: std.ArrayList(u8), | ||
| 1212 | coff: *Coff, | ||
| 1213 | oom: bool = false, | ||
| 1214 | }; | ||
| 1215 | |||
| 1216 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 1217 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 1218 | const msg = ptr[0..len]; | ||
| 1219 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 1220 | error.OutOfMemory => lld_context.oom = true, | ||
| 1221 | }; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { | 1229 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { |
| 1225 | return self.text_section_virtual_address + decl.link.coff.text_offset; | 1230 | return self.text_section_virtual_address + decl.link.coff.text_offset; |
| 1226 | } | 1231 | } |
src/link/Elf.zig+60-56| ... | @@ -1360,11 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1360,11 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1360 | // Create an LLD command line and invoke it. | 1360 | // Create an LLD command line and invoke it. |
| 1361 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | 1361 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 1362 | defer argv.deinit(); | 1362 | defer argv.deinit(); |
| 1363 | // The first argument is ignored as LLD is called as a library, set it | 1363 | // We will invoke ourselves as a child process to gain access to LLD. |
| 1364 | // anyway to the correct LLD driver name for this target so that it's | 1364 | // This is necessary because LLD does not behave properly as a library - |
| 1365 | // correctly printed when `verbose_link` is true. This is needed for some | 1365 | // it calls exit() and does not reset all global data between invocations. |
| 1366 | // tools such as CMake when Zig is used as C compiler. | 1366 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" }); |
| 1367 | try argv.append("ld.lld"); | ||
| 1368 | if (is_obj) { | 1367 | if (is_obj) { |
| 1369 | try argv.append("-r"); | 1368 | try argv.append("-r"); |
| 1370 | } | 1369 | } |
| ... | @@ -1628,46 +1627,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1628,46 +1627,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1628 | } | 1627 | } |
| 1629 | 1628 | ||
| 1630 | if (self.base.options.verbose_link) { | 1629 | if (self.base.options.verbose_link) { |
| 1631 | Compilation.dump_argv(argv.items); | 1630 | // Skip over our own name so that the LLD linker name is the first argv item. |
| 1631 | Compilation.dump_argv(argv.items[1..]); | ||
| 1632 | } | 1632 | } |
| 1633 | 1633 | ||
| 1634 | // Oh, snapplesauce! We need null terminated argv. | 1634 | // Sadly, we must run LLD as a child process because it does not behave |
| 1635 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 1635 | // properly as a library. |
| 1636 | for (argv.items) |arg, i| { | 1636 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1637 | new_argv[i] = try arena.dupeZ(u8, arg); | 1637 | defer child.deinit(); |
| 1638 | } | ||
| 1639 | 1638 | ||
| 1640 | var stderr_context: LLDContext = .{ | 1639 | if (comp.clang_passthrough_mode) { |
| 1641 | .elf = self, | 1640 | child.stdin_behavior = .Inherit; |
| 1642 | .data = std.ArrayList(u8).init(self.base.allocator), | 1641 | child.stdout_behavior = .Inherit; |
| 1643 | }; | 1642 | child.stderr_behavior = .Inherit; |
| 1644 | defer stderr_context.data.deinit(); | 1643 | |
| 1645 | var stdout_context: LLDContext = .{ | 1644 | const term = child.spawnAndWait() catch |err| { |
| 1646 | .elf = self, | 1645 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1647 | .data = std.ArrayList(u8).init(self.base.allocator), | 1646 | return error.UnableToSpawnSelf; |
| 1648 | }; | 1647 | }; |
| 1649 | defer stdout_context.data.deinit(); | 1648 | switch (term) { |
| 1650 | const llvm = @import("../llvm.zig"); | 1649 | .Exited => |code| { |
| 1651 | const ok = llvm.Link( | 1650 | if (code != 0) { |
| 1652 | .ELF, | 1651 | // TODO https://github.com/ziglang/zig/issues/6342 |
| 1653 | new_argv.ptr, | 1652 | std.process.exit(1); |
| 1654 | new_argv.len, | 1653 | } |
| 1655 | append_diagnostic, | 1654 | }, |
| 1656 | @ptrToInt(&stdout_context), | 1655 | else => std.process.abort(), |
| 1657 | @ptrToInt(&stderr_context), | 1656 | } |
| 1658 | ); | 1657 | } else { |
| 1659 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 1658 | child.stdin_behavior = .Ignore; |
| 1660 | if (stdout_context.data.items.len != 0) { | 1659 | child.stdout_behavior = .Ignore; |
| 1661 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 1660 | child.stderr_behavior = .Pipe; |
| 1662 | } | 1661 | |
| 1663 | if (!ok) { | 1662 | try child.spawn(); |
| 1664 | // TODO parse this output and surface with the Compilation API rather than | 1663 | |
| 1665 | // directly outputting to stderr here. | 1664 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); |
| 1666 | std.debug.print("{}", .{stderr_context.data.items}); | 1665 | |
| 1667 | return error.LLDReportedFailure; | 1666 | const term = child.wait() catch |err| { |
| 1668 | } | 1667 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1669 | if (stderr_context.data.items.len != 0) { | 1668 | return error.UnableToSpawnSelf; |
| 1670 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 1669 | }; |
| 1670 | |||
| 1671 | switch (term) { | ||
| 1672 | .Exited => |code| { | ||
| 1673 | if (code != 0) { | ||
| 1674 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1675 | // directly outputting to stderr here. | ||
| 1676 | std.debug.print("{s}", .{stderr}); | ||
| 1677 | return error.LLDReportedFailure; | ||
| 1678 | } | ||
| 1679 | }, | ||
| 1680 | else => { | ||
| 1681 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 1682 | return error.LLDCrashed; | ||
| 1683 | }, | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | if (stderr.len != 0) { | ||
| 1687 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 1688 | } | ||
| 1671 | } | 1689 | } |
| 1672 | 1690 | ||
| 1673 | if (!self.base.options.disable_lld_caching) { | 1691 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -1686,20 +1704,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1686,20 +1704,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1686 | } | 1704 | } |
| 1687 | } | 1705 | } |
| 1688 | 1706 | ||
| 1689 | const LLDContext = struct { | ||
| 1690 | data: std.ArrayList(u8), | ||
| 1691 | elf: *Elf, | ||
| 1692 | oom: bool = false, | ||
| 1693 | }; | ||
| 1694 | |||
| 1695 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 1696 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 1697 | const msg = ptr[0..len]; | ||
| 1698 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 1699 | error.OutOfMemory => lld_context.oom = true, | ||
| 1700 | }; | ||
| 1701 | } | ||
| 1702 | |||
| 1703 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { | 1707 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1704 | const target_endian = self.base.options.target.cpu.arch.endian(); | 1708 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1705 | switch (self.ptr_width) { | 1709 | switch (self.ptr_width) { |
src/link/MachO.zig+61-56| ... | @@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 489 | if (self.base.options.system_linker_hack) { | 489 | if (self.base.options.system_linker_hack) { |
| 490 | try argv.append("ld"); | 490 | try argv.append("ld"); |
| 491 | } else { | 491 | } else { |
| 492 | // The first argument is ignored as LLD is called as a library, set | 492 | // We will invoke ourselves as a child process to gain access to LLD. |
| 493 | // it anyway to the correct LLD driver name for this target so that | 493 | // This is necessary because LLD does not behave properly as a library - |
| 494 | // it's correctly printed when `verbose_link` is true. This is | 494 | // it calls exit() and does not reset all global data between invocations. |
| 495 | // needed for some tools such as CMake when Zig is used as C | 495 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" }); |
| 496 | // compiler. | ||
| 497 | try argv.append("ld64"); | ||
| 498 | 496 | ||
| 499 | try argv.append("-error-limit"); | 497 | try argv.append("-error-limit"); |
| 500 | try argv.append("0"); | 498 | try argv.append("0"); |
| ... | @@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 660 | } | 658 | } |
| 661 | 659 | ||
| 662 | if (self.base.options.verbose_link) { | 660 | if (self.base.options.verbose_link) { |
| 663 | Compilation.dump_argv(argv.items); | 661 | // Potentially skip over our own name so that the LLD linker name is the first argv item. |
| 662 | const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..]; | ||
| 663 | Compilation.dump_argv(adjusted_argv); | ||
| 664 | } | 664 | } |
| 665 | 665 | ||
| 666 | // TODO https://github.com/ziglang/zig/issues/6971 | 666 | // TODO https://github.com/ziglang/zig/issues/6971 |
| ... | @@ -685,42 +685,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -685,42 +685,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 685 | return error.LDReportedFailure; | 685 | return error.LDReportedFailure; |
| 686 | } | 686 | } |
| 687 | } else { | 687 | } else { |
| 688 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 688 | // Sadly, we must run LLD as a child process because it does not behave |
| 689 | for (argv.items) |arg, i| { | 689 | // properly as a library. |
| 690 | new_argv[i] = try arena.dupeZ(u8, arg); | 690 | const child = try std.ChildProcess.init(argv.items, arena); |
| 691 | } | 691 | defer child.deinit(); |
| 692 | |||
| 693 | if (comp.clang_passthrough_mode) { | ||
| 694 | child.stdin_behavior = .Inherit; | ||
| 695 | child.stdout_behavior = .Inherit; | ||
| 696 | child.stderr_behavior = .Inherit; | ||
| 697 | |||
| 698 | const term = child.spawnAndWait() catch |err| { | ||
| 699 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 700 | return error.UnableToSpawnSelf; | ||
| 701 | }; | ||
| 702 | switch (term) { | ||
| 703 | .Exited => |code| { | ||
| 704 | if (code != 0) { | ||
| 705 | // TODO https://github.com/ziglang/zig/issues/6342 | ||
| 706 | std.process.exit(1); | ||
| 707 | } | ||
| 708 | }, | ||
| 709 | else => std.process.abort(), | ||
| 710 | } | ||
| 711 | } else { | ||
| 712 | child.stdin_behavior = .Ignore; | ||
| 713 | child.stdout_behavior = .Ignore; | ||
| 714 | child.stderr_behavior = .Pipe; | ||
| 715 | |||
| 716 | try child.spawn(); | ||
| 717 | |||
| 718 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 719 | |||
| 720 | const term = child.wait() catch |err| { | ||
| 721 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 722 | return error.UnableToSpawnSelf; | ||
| 723 | }; | ||
| 724 | |||
| 725 | switch (term) { | ||
| 726 | .Exited => |code| { | ||
| 727 | if (code != 0) { | ||
| 728 | // TODO parse this output and surface with the Compilation API rather than | ||
| 729 | // directly outputting to stderr here. | ||
| 730 | std.debug.print("{s}", .{stderr}); | ||
| 731 | return error.LLDReportedFailure; | ||
| 732 | } | ||
| 733 | }, | ||
| 734 | else => { | ||
| 735 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 736 | return error.LLDCrashed; | ||
| 737 | }, | ||
| 738 | } | ||
| 692 | 739 | ||
| 693 | var stderr_context: LLDContext = .{ | 740 | if (stderr.len != 0) { |
| 694 | .macho = self, | 741 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 695 | .data = std.ArrayList(u8).init(self.base.allocator), | 742 | } |
| 696 | }; | ||
| 697 | defer stderr_context.data.deinit(); | ||
| 698 | var stdout_context: LLDContext = .{ | ||
| 699 | .macho = self, | ||
| 700 | .data = std.ArrayList(u8).init(self.base.allocator), | ||
| 701 | }; | ||
| 702 | defer stdout_context.data.deinit(); | ||
| 703 | const llvm = @import("../llvm.zig"); | ||
| 704 | const ok = llvm.Link( | ||
| 705 | .MachO, | ||
| 706 | new_argv.ptr, | ||
| 707 | new_argv.len, | ||
| 708 | append_diagnostic, | ||
| 709 | @ptrToInt(&stdout_context), | ||
| 710 | @ptrToInt(&stderr_context), | ||
| 711 | ); | ||
| 712 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | ||
| 713 | if (stdout_context.data.items.len != 0) { | ||
| 714 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | ||
| 715 | } | ||
| 716 | if (!ok) { | ||
| 717 | // TODO parse this output and surface with the Compilation API rather than | ||
| 718 | // directly outputting to stderr here. | ||
| 719 | std.log.err("{}", .{stderr_context.data.items}); | ||
| 720 | return error.LLDReportedFailure; | ||
| 721 | } | ||
| 722 | if (stderr_context.data.items.len != 0) { | ||
| 723 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | ||
| 724 | } | 743 | } |
| 725 | 744 | ||
| 726 | // At this stage, LLD has done its job. It is time to patch the resultant | 745 | // At this stage, LLD has done its job. It is time to patch the resultant |
| ... | @@ -785,20 +804,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -785,20 +804,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 785 | } | 804 | } |
| 786 | } | 805 | } |
| 787 | 806 | ||
| 788 | const LLDContext = struct { | ||
| 789 | data: std.ArrayList(u8), | ||
| 790 | macho: *MachO, | ||
| 791 | oom: bool = false, | ||
| 792 | }; | ||
| 793 | |||
| 794 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 795 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 796 | const msg = ptr[0..len]; | ||
| 797 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 798 | error.OutOfMemory => lld_context.oom = true, | ||
| 799 | }; | ||
| 800 | } | ||
| 801 | |||
| 802 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | 807 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 803 | return switch (arch) { | 808 | return switch (arch) { |
| 804 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", | 809 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", |
src/link/Wasm.zig+60-55| ... | @@ -345,11 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -345,11 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 345 | // Create an LLD command line and invoke it. | 345 | // Create an LLD command line and invoke it. |
| 346 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | 346 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 347 | defer argv.deinit(); | 347 | defer argv.deinit(); |
| 348 | // The first argument is ignored as LLD is called as a library, set it | 348 | // We will invoke ourselves as a child process to gain access to LLD. |
| 349 | // anyway to the correct LLD driver name for this target so that it's | 349 | // This is necessary because LLD does not behave properly as a library - |
| 350 | // correctly printed when `verbose_link` is true. This is needed for some | 350 | // it calls exit() and does not reset all global data between invocations. |
| 351 | // tools such as CMake when Zig is used as C compiler. | 351 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" }); |
| 352 | try argv.append("ld-wasm"); | ||
| 353 | if (is_obj) { | 352 | if (is_obj) { |
| 354 | try argv.append("-r"); | 353 | try argv.append("-r"); |
| 355 | } | 354 | } |
| ... | @@ -399,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -399,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 399 | } | 398 | } |
| 400 | 399 | ||
| 401 | if (self.base.options.verbose_link) { | 400 | if (self.base.options.verbose_link) { |
| 402 | Compilation.dump_argv(argv.items); | 401 | // Skip over our own name so that the LLD linker name is the first argv item. |
| 402 | Compilation.dump_argv(argv.items[1..]); | ||
| 403 | } | 403 | } |
| 404 | 404 | ||
| 405 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 405 | // Sadly, we must run LLD as a child process because it does not behave |
| 406 | for (argv.items) |arg, i| { | 406 | // properly as a library. |
| 407 | new_argv[i] = try arena.dupeZ(u8, arg); | 407 | const child = try std.ChildProcess.init(argv.items, arena); |
| 408 | } | 408 | defer child.deinit(); |
| 409 | 409 | ||
| 410 | var stderr_context: LLDContext = .{ | 410 | if (comp.clang_passthrough_mode) { |
| 411 | .wasm = self, | 411 | child.stdin_behavior = .Inherit; |
| 412 | .data = std.ArrayList(u8).init(self.base.allocator), | 412 | child.stdout_behavior = .Inherit; |
| 413 | }; | 413 | child.stderr_behavior = .Inherit; |
| 414 | defer stderr_context.data.deinit(); | 414 | |
| 415 | var stdout_context: LLDContext = .{ | 415 | const term = child.spawnAndWait() catch |err| { |
| 416 | .wasm = self, | 416 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 417 | .data = std.ArrayList(u8).init(self.base.allocator), | 417 | return error.UnableToSpawnSelf; |
| 418 | }; | 418 | }; |
| 419 | defer stdout_context.data.deinit(); | 419 | switch (term) { |
| 420 | const llvm = @import("../llvm.zig"); | 420 | .Exited => |code| { |
| 421 | const ok = llvm.Link( | 421 | if (code != 0) { |
| 422 | .Wasm, | 422 | // TODO https://github.com/ziglang/zig/issues/6342 |
| 423 | new_argv.ptr, | 423 | std.process.exit(1); |
| 424 | new_argv.len, | 424 | } |
| 425 | append_diagnostic, | 425 | }, |
| 426 | @ptrToInt(&stdout_context), | 426 | else => std.process.abort(), |
| 427 | @ptrToInt(&stderr_context), | 427 | } |
| 428 | ); | 428 | } else { |
| 429 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 429 | child.stdin_behavior = .Ignore; |
| 430 | if (stdout_context.data.items.len != 0) { | 430 | child.stdout_behavior = .Ignore; |
| 431 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 431 | child.stderr_behavior = .Pipe; |
| 432 | } | 432 | |
| 433 | if (!ok) { | 433 | try child.spawn(); |
| 434 | // TODO parse this output and surface with the Compilation API rather than | 434 | |
| 435 | // directly outputting to stderr here. | 435 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); |
| 436 | std.debug.print("{}", .{stderr_context.data.items}); | 436 | |
| 437 | return error.LLDReportedFailure; | 437 | const term = child.wait() catch |err| { |
| 438 | } | 438 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 439 | if (stderr_context.data.items.len != 0) { | 439 | return error.UnableToSpawnSelf; |
| 440 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 440 | }; |
| 441 | |||
| 442 | switch (term) { | ||
| 443 | .Exited => |code| { | ||
| 444 | if (code != 0) { | ||
| 445 | // TODO parse this output and surface with the Compilation API rather than | ||
| 446 | // directly outputting to stderr here. | ||
| 447 | std.debug.print("{s}", .{stderr}); | ||
| 448 | return error.LLDReportedFailure; | ||
| 449 | } | ||
| 450 | }, | ||
| 451 | else => { | ||
| 452 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 453 | return error.LLDCrashed; | ||
| 454 | }, | ||
| 455 | } | ||
| 456 | |||
| 457 | if (stderr.len != 0) { | ||
| 458 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 459 | } | ||
| 441 | } | 460 | } |
| 442 | 461 | ||
| 443 | if (!self.base.options.disable_lld_caching) { | 462 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -456,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -456,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 456 | } | 475 | } |
| 457 | } | 476 | } |
| 458 | 477 | ||
| 459 | const LLDContext = struct { | ||
| 460 | data: std.ArrayList(u8), | ||
| 461 | wasm: *Wasm, | ||
| 462 | oom: bool = false, | ||
| 463 | }; | ||
| 464 | |||
| 465 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 466 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 467 | const msg = ptr[0..len]; | ||
| 468 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 469 | error.OutOfMemory => lld_context.oom = true, | ||
| 470 | }; | ||
| 471 | } | ||
| 472 | |||
| 473 | /// Get the current index of a given Decl in the function list | 478 | /// Get the current index of a given Decl in the function list |
| 474 | /// TODO: we could maintain a hash map to potentially make this | 479 | /// TODO: we could maintain a hash map to potentially make this |
| 475 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { | 480 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { |
src/llvm.zig+9-9| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | //! We do this instead of @cImport because the self-hosted compiler is easier | 1 | //! We do this instead of @cImport because the self-hosted compiler is easier |
| 2 | //! to bootstrap if it does not depend on translate-c. | 2 | //! to bootstrap if it does not depend on translate-c. |
| 3 | 3 | ||
| 4 | pub const Link = ZigLLDLink; | 4 | extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 5 | extern fn ZigLLDLink( | 5 | extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 6 | oformat: ObjectFormatType, | 6 | extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 7 | args: [*:null]const ?[*:0]const u8, | 7 | extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 8 | arg_count: usize, | 8 | |
| 9 | append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void, | 9 | pub const LinkCOFF = ZigLLDLinkCOFF; |
| 10 | context_stdout: usize, | 10 | pub const LinkELF = ZigLLDLinkELF; |
| 11 | context_stderr: usize, | 11 | pub const LinkMachO = ZigLLDLinkMachO; |
| 12 | ) bool; | 12 | pub const LinkWasm = ZigLLDLinkWasm; |
| 13 | 13 | ||
| 14 | pub const ObjectFormatType = extern enum(c_int) { | 14 | pub const ObjectFormatType = extern enum(c_int) { |
| 15 | Unknown, | 15 | Unknown, |
src/main.zig+39| ... | @@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v | ... | @@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 176 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) | 176 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) |
| 177 | { | 177 | { |
| 178 | return punt_to_clang(arena, args); | 178 | return punt_to_clang(arena, args); |
| 179 | } else if (mem.eql(u8, cmd, "ld.lld") or | ||
| 180 | mem.eql(u8, cmd, "ld64.lld") or | ||
| 181 | mem.eql(u8, cmd, "lld-link") or | ||
| 182 | mem.eql(u8, cmd, "wasm-ld")) | ||
| 183 | { | ||
| 184 | return punt_to_lld(arena, args); | ||
| 179 | } else if (mem.eql(u8, cmd, "build")) { | 185 | } else if (mem.eql(u8, cmd, "build")) { |
| 180 | return cmdBuild(gpa, arena, cmd_args); | 186 | return cmdBuild(gpa, arena, cmd_args); |
| 181 | } else if (mem.eql(u8, cmd, "fmt")) { | 187 | } else if (mem.eql(u8, cmd, "fmt")) { |
| ... | @@ -2819,6 +2825,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} | ... | @@ -2819,6 +2825,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} |
| 2819 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); | 2825 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); |
| 2820 | } | 2826 | } |
| 2821 | 2827 | ||
| 2828 | /// The first argument determines which backend is invoked. The options are: | ||
| 2829 | /// * `ld.lld` - ELF | ||
| 2830 | /// * `ld64.lld` - Mach-O | ||
| 2831 | /// * `lld-link` - COFF | ||
| 2832 | /// * `wasm-ld` - WebAssembly | ||
| 2833 | /// TODO https://github.com/ziglang/zig/issues/3257 | ||
| 2834 | pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { | ||
| 2835 | if (!build_options.have_llvm) | ||
| 2836 | fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); | ||
| 2837 | // Convert the args to the format LLD expects. | ||
| 2838 | // We subtract 1 to shave off the zig binary from args[0]. | ||
| 2839 | const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null); | ||
| 2840 | for (args[1..]) |arg, i| { | ||
| 2841 | argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. | ||
| 2842 | } | ||
| 2843 | const exit_code = rc: { | ||
| 2844 | const llvm = @import("llvm.zig"); | ||
| 2845 | const argc = @intCast(c_int, argv.len); | ||
| 2846 | if (mem.eql(u8, args[1], "ld.lld")) { | ||
| 2847 | break :rc llvm.LinkELF(argc, argv.ptr, true); | ||
| 2848 | } else if (mem.eql(u8, args[1], "ld64.lld")) { | ||
| 2849 | break :rc llvm.LinkMachO(argc, argv.ptr, true); | ||
| 2850 | } else if (mem.eql(u8, args[1], "lld-link")) { | ||
| 2851 | break :rc llvm.LinkCOFF(argc, argv.ptr, true); | ||
| 2852 | } else if (mem.eql(u8, args[1], "wasm-ld")) { | ||
| 2853 | break :rc llvm.LinkWasm(argc, argv.ptr, true); | ||
| 2854 | } else { | ||
| 2855 | unreachable; | ||
| 2856 | } | ||
| 2857 | }; | ||
| 2858 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); | ||
| 2859 | } | ||
| 2860 | |||
| 2822 | const clang_args = @import("clang_options.zig").list; | 2861 | const clang_args = @import("clang_options.zig").list; |
| 2823 | 2862 | ||
| 2824 | pub const ClangArgIterator = struct { | 2863 | pub const ClangArgIterator = struct { |
src/zig_llvm.cpp+15-30| ... | @@ -1056,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size | ... | @@ -1056,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size |
| 1056 | return false; | 1056 | return false; |
| 1057 | } | 1057 | } |
| 1058 | 1058 | ||
| 1059 | int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) { | ||
| 1060 | std::vector<const char *> args(argv, argv + argc); | ||
| 1061 | return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs()); | ||
| 1062 | } | ||
| 1059 | 1063 | ||
| 1060 | bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | 1064 | int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) { |
| 1061 | void (*append_diagnostic)(void *, const char *, size_t), | 1065 | std::vector<const char *> args(argv, argv + argc); |
| 1062 | void *context_stdout, void *context_stderr) | 1066 | return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs()); |
| 1063 | { | 1067 | } |
| 1064 | ArrayRef<const char *> array_ref_args(args, arg_count); | ||
| 1065 | |||
| 1066 | MyOStream diag_stdout(append_diagnostic, context_stdout); | ||
| 1067 | MyOStream diag_stderr(append_diagnostic, context_stderr); | ||
| 1068 | |||
| 1069 | switch (oformat) { | ||
| 1070 | case ZigLLVM_UnknownObjectFormat: | ||
| 1071 | case ZigLLVM_XCOFF: | ||
| 1072 | assert(false); // unreachable | ||
| 1073 | break; | ||
| 1074 | |||
| 1075 | case ZigLLVM_COFF: | ||
| 1076 | return lld::coff::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1077 | |||
| 1078 | case ZigLLVM_ELF: | ||
| 1079 | return lld::elf::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1080 | |||
| 1081 | case ZigLLVM_MachO: | ||
| 1082 | return lld::mach_o::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1083 | 1068 | ||
| 1084 | case ZigLLVM_Wasm: | 1069 | int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) { |
| 1085 | return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr); | 1070 | std::vector<const char *> args(argv, argv + argc); |
| 1071 | return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs()); | ||
| 1072 | } | ||
| 1086 | 1073 | ||
| 1087 | default: | 1074 | int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) { |
| 1088 | break; | 1075 | std::vector<const char *> args(argv, argv + argc); |
| 1089 | } | 1076 | return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs()); |
| 1090 | assert(false); // unreachable | ||
| 1091 | abort(); | ||
| 1092 | } | 1077 | } |
| 1093 | 1078 | ||
| 1094 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { | 1079 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { |
src/zig_llvm.h+4-3| ... | @@ -507,9 +507,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor | ... | @@ -507,9 +507,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor |
| 507 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); | 507 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); |
| 508 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); | 508 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); |
| 509 | 509 | ||
| 510 | ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | 510 | ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early); |
| 511 | void (*append_diagnostic)(void *, const char *, size_t), | 511 | ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early); |
| 512 | void *context_stdout, void *context_stderr); | 512 | ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early); |
| 513 | ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early); | ||
| 513 | 514 | ||
| 514 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, | 515 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, |
| 515 | enum ZigLLVM_OSType os_type); | 516 | enum ZigLLVM_OSType os_type); |
test/cli.zig+2-2| ... | @@ -92,13 +92,13 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess | ... | @@ -92,13 +92,13 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess |
| 92 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { | 92 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { |
| 93 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); | 93 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); |
| 94 | const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); | 94 | const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); |
| 95 | testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n")); | 95 | testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n"); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { | 98 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { |
| 99 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); | 99 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); |
| 100 | const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); | 100 | const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); |
| 101 | testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n")); | 101 | testing.expectEqualStrings("info: All your codebase are belong to us.\n", run_result.stderr); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { | 104 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { |