| author | |
| committer | |
| log | 85c1db9222f6aa9abbc50309dfb7ce56b3cb6cf0 |
| tree | 5e58184b91fe86ce5fc060b5bfda6b9be99b709b |
| parent | 40e37d4324dd4e74c2a3285a73942b6ae50d003e |
invoke LLD as a child process rather than a library11 files changed, 332 insertions(+), 254 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| ... | @@ -1756,7 +1756,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * | ... | @@ -1756,7 +1756,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * |
| 1756 | if (comp.clang_preprocessor_mode == .stdout) | 1756 | if (comp.clang_preprocessor_mode == .stdout) |
| 1757 | std.process.exit(0); | 1757 | std.process.exit(0); |
| 1758 | }, | 1758 | }, |
| 1759 | else => std.process.exit(1), | 1759 | else => std.process.abort(), |
| 1760 | } | 1760 | } |
| 1761 | } else { | 1761 | } else { |
| 1762 | child.stdin_behavior = .Ignore; | 1762 | child.stdin_behavior = .Ignore; |
src/link/Coff.zig+60-52| ... | @@ -907,8 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -907,8 +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 | // Even though we're calling LLD as a library it thinks the first argument is its own exe name. | 910 | // We will invoke ourselves as a child process to gain access to LLD. |
| 911 | try argv.append("lld"); | 911 | // This is necessary because LLD does not behave properly as a library - |
| 912 | // it calls exit() and does not reset all global data between invocations. | ||
| 913 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" }); | ||
| 912 | 914 | ||
| 913 | try argv.append("-ERRORLIMIT:0"); | 915 | try argv.append("-ERRORLIMIT:0"); |
| 914 | try argv.append("-NOLOGO"); | 916 | try argv.append("-NOLOGO"); |
| ... | @@ -1146,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1146,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1146 | } | 1148 | } |
| 1147 | 1149 | ||
| 1148 | if (self.base.options.verbose_link) { | 1150 | if (self.base.options.verbose_link) { |
| 1149 | 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..]); | ||
| 1150 | } | 1153 | } |
| 1151 | 1154 | ||
| 1152 | 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 |
| 1153 | for (argv.items) |arg, i| { | 1156 | // properly as a library. |
| 1154 | new_argv[i] = try arena.dupeZ(u8, arg); | 1157 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1155 | } | 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 | } | ||
| 1156 | 1206 | ||
| 1157 | var stderr_context: LLDContext = .{ | 1207 | if (stderr.len != 0) { |
| 1158 | .coff = self, | 1208 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 1159 | .data = std.ArrayList(u8).init(self.base.allocator), | 1209 | } |
| 1160 | }; | ||
| 1161 | defer stderr_context.data.deinit(); | ||
| 1162 | var stdout_context: LLDContext = .{ | ||
| 1163 | .coff = self, | ||
| 1164 | .data = std.ArrayList(u8).init(self.base.allocator), | ||
| 1165 | }; | ||
| 1166 | defer stdout_context.data.deinit(); | ||
| 1167 | const llvm = @import("../llvm.zig"); | ||
| 1168 | const ok = llvm.Link( | ||
| 1169 | .COFF, | ||
| 1170 | new_argv.ptr, | ||
| 1171 | new_argv.len, | ||
| 1172 | append_diagnostic, | ||
| 1173 | @ptrToInt(&stdout_context), | ||
| 1174 | @ptrToInt(&stderr_context), | ||
| 1175 | ); | ||
| 1176 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | ||
| 1177 | if (stdout_context.data.items.len != 0) { | ||
| 1178 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | ||
| 1179 | } | ||
| 1180 | if (!ok) { | ||
| 1181 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1182 | // directly outputting to stderr here. | ||
| 1183 | std.debug.print("{}", .{stderr_context.data.items}); | ||
| 1184 | return error.LLDReportedFailure; | ||
| 1185 | } | ||
| 1186 | if (stderr_context.data.items.len != 0) { | ||
| 1187 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | ||
| 1188 | } | 1210 | } |
| 1189 | } | 1211 | } |
| 1190 | 1212 | ||
| ... | @@ -1204,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1204,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1204 | } | 1226 | } |
| 1205 | } | 1227 | } |
| 1206 | 1228 | ||
| 1207 | const LLDContext = struct { | ||
| 1208 | data: std.ArrayList(u8), | ||
| 1209 | coff: *Coff, | ||
| 1210 | oom: bool = false, | ||
| 1211 | }; | ||
| 1212 | |||
| 1213 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 1214 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 1215 | const msg = ptr[0..len]; | ||
| 1216 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 1217 | error.OutOfMemory => lld_context.oom = true, | ||
| 1218 | }; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { | 1229 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { |
| 1222 | return self.text_section_virtual_address + decl.link.coff.text_offset; | 1230 | return self.text_section_virtual_address + decl.link.coff.text_offset; |
| 1223 | } | 1231 | } |
src/link/Elf.zig+60-53| ... | @@ -1360,8 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1360,8 +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 | // Even though we're calling LLD as a library it thinks the first argument is its own exe name. | 1363 | // We will invoke ourselves as a child process to gain access to LLD. |
| 1364 | try argv.append("lld"); | 1364 | // This is necessary because LLD does not behave properly as a library - |
| 1365 | // it calls exit() and does not reset all global data between invocations. | ||
| 1366 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" }); | ||
| 1365 | if (is_obj) { | 1367 | if (is_obj) { |
| 1366 | try argv.append("-r"); | 1368 | try argv.append("-r"); |
| 1367 | } | 1369 | } |
| ... | @@ -1621,46 +1623,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1621,46 +1623,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1621 | } | 1623 | } |
| 1622 | 1624 | ||
| 1623 | if (self.base.options.verbose_link) { | 1625 | if (self.base.options.verbose_link) { |
| 1624 | Compilation.dump_argv(argv.items); | 1626 | // Skip over our own name so that the LLD linker name is the first argv item. |
| 1627 | Compilation.dump_argv(argv.items[1..]); | ||
| 1625 | } | 1628 | } |
| 1626 | 1629 | ||
| 1627 | // Oh, snapplesauce! We need null terminated argv. | 1630 | // Sadly, we must run LLD as a child process because it does not behave |
| 1628 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 1631 | // properly as a library. |
| 1629 | for (argv.items) |arg, i| { | 1632 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1630 | new_argv[i] = try arena.dupeZ(u8, arg); | 1633 | defer child.deinit(); |
| 1631 | } | ||
| 1632 | 1634 | ||
| 1633 | var stderr_context: LLDContext = .{ | 1635 | if (comp.clang_passthrough_mode) { |
| 1634 | .elf = self, | 1636 | child.stdin_behavior = .Inherit; |
| 1635 | .data = std.ArrayList(u8).init(self.base.allocator), | 1637 | child.stdout_behavior = .Inherit; |
| 1636 | }; | 1638 | child.stderr_behavior = .Inherit; |
| 1637 | defer stderr_context.data.deinit(); | 1639 | |
| 1638 | var stdout_context: LLDContext = .{ | 1640 | const term = child.spawnAndWait() catch |err| { |
| 1639 | .elf = self, | 1641 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1640 | .data = std.ArrayList(u8).init(self.base.allocator), | 1642 | return error.UnableToSpawnSelf; |
| 1641 | }; | 1643 | }; |
| 1642 | defer stdout_context.data.deinit(); | 1644 | switch (term) { |
| 1643 | const llvm = @import("../llvm.zig"); | 1645 | .Exited => |code| { |
| 1644 | const ok = llvm.Link( | 1646 | if (code != 0) { |
| 1645 | .ELF, | 1647 | // TODO https://github.com/ziglang/zig/issues/6342 |
| 1646 | new_argv.ptr, | 1648 | std.process.exit(1); |
| 1647 | new_argv.len, | 1649 | } |
| 1648 | append_diagnostic, | 1650 | }, |
| 1649 | @ptrToInt(&stdout_context), | 1651 | else => std.process.abort(), |
| 1650 | @ptrToInt(&stderr_context), | 1652 | } |
| 1651 | ); | 1653 | } else { |
| 1652 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 1654 | child.stdin_behavior = .Ignore; |
| 1653 | if (stdout_context.data.items.len != 0) { | 1655 | child.stdout_behavior = .Ignore; |
| 1654 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 1656 | child.stderr_behavior = .Pipe; |
| 1655 | } | 1657 | |
| 1656 | if (!ok) { | 1658 | try child.spawn(); |
| 1657 | // TODO parse this output and surface with the Compilation API rather than | 1659 | |
| 1658 | // directly outputting to stderr here. | 1660 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); |
| 1659 | std.debug.print("{}", .{stderr_context.data.items}); | 1661 | |
| 1660 | return error.LLDReportedFailure; | 1662 | const term = child.wait() catch |err| { |
| 1661 | } | 1663 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1662 | if (stderr_context.data.items.len != 0) { | 1664 | return error.UnableToSpawnSelf; |
| 1663 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 1665 | }; |
| 1666 | |||
| 1667 | switch (term) { | ||
| 1668 | .Exited => |code| { | ||
| 1669 | if (code != 0) { | ||
| 1670 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1671 | // directly outputting to stderr here. | ||
| 1672 | std.debug.print("{s}", .{stderr}); | ||
| 1673 | return error.LLDReportedFailure; | ||
| 1674 | } | ||
| 1675 | }, | ||
| 1676 | else => { | ||
| 1677 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 1678 | return error.LLDCrashed; | ||
| 1679 | }, | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | if (stderr.len != 0) { | ||
| 1683 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 1684 | } | ||
| 1664 | } | 1685 | } |
| 1665 | 1686 | ||
| 1666 | if (!self.base.options.disable_lld_caching) { | 1687 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -1679,20 +1700,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1679,20 +1700,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1679 | } | 1700 | } |
| 1680 | } | 1701 | } |
| 1681 | 1702 | ||
| 1682 | const LLDContext = struct { | ||
| 1683 | data: std.ArrayList(u8), | ||
| 1684 | elf: *Elf, | ||
| 1685 | oom: bool = false, | ||
| 1686 | }; | ||
| 1687 | |||
| 1688 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 1689 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 1690 | const msg = ptr[0..len]; | ||
| 1691 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 1692 | error.OutOfMemory => lld_context.oom = true, | ||
| 1693 | }; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { | 1703 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1697 | const target_endian = self.base.options.target.cpu.arch.endian(); | 1704 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1698 | switch (self.ptr_width) { | 1705 | switch (self.ptr_width) { |
src/link/MachO.zig+61-52| ... | @@ -544,8 +544,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -544,8 +544,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 544 | if (self.base.options.system_linker_hack) { | 544 | if (self.base.options.system_linker_hack) { |
| 545 | try argv.append("ld"); | 545 | try argv.append("ld"); |
| 546 | } else { | 546 | } else { |
| 547 | // Even though we're calling LLD as a library it thinks the first argument is its own exe name. | 547 | // We will invoke ourselves as a child process to gain access to LLD. |
| 548 | try argv.append("lld"); | 548 | // This is necessary because LLD does not behave properly as a library - |
| 549 | // it calls exit() and does not reset all global data between invocations. | ||
| 550 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" }); | ||
| 549 | 551 | ||
| 550 | try argv.append("-error-limit"); | 552 | try argv.append("-error-limit"); |
| 551 | try argv.append("0"); | 553 | try argv.append("0"); |
| ... | @@ -711,7 +713,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -711,7 +713,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 711 | } | 713 | } |
| 712 | 714 | ||
| 713 | if (self.base.options.verbose_link) { | 715 | if (self.base.options.verbose_link) { |
| 714 | Compilation.dump_argv(argv.items); | 716 | // Potentially skip over our own name so that the LLD linker name is the first argv item. |
| 717 | const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..]; | ||
| 718 | Compilation.dump_argv(adjusted_argv); | ||
| 715 | } | 719 | } |
| 716 | 720 | ||
| 717 | // TODO https://github.com/ziglang/zig/issues/6971 | 721 | // TODO https://github.com/ziglang/zig/issues/6971 |
| ... | @@ -736,42 +740,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -736,42 +740,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 736 | return error.LDReportedFailure; | 740 | return error.LDReportedFailure; |
| 737 | } | 741 | } |
| 738 | } else { | 742 | } else { |
| 739 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | 743 | // Sadly, we must run LLD as a child process because it does not behave |
| 740 | for (argv.items) |arg, i| { | 744 | // properly as a library. |
| 741 | new_argv[i] = try arena.dupeZ(u8, arg); | 745 | const child = try std.ChildProcess.init(argv.items, arena); |
| 742 | } | 746 | defer child.deinit(); |
| 747 | |||
| 748 | if (comp.clang_passthrough_mode) { | ||
| 749 | child.stdin_behavior = .Inherit; | ||
| 750 | child.stdout_behavior = .Inherit; | ||
| 751 | child.stderr_behavior = .Inherit; | ||
| 752 | |||
| 753 | const term = child.spawnAndWait() catch |err| { | ||
| 754 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 755 | return error.UnableToSpawnSelf; | ||
| 756 | }; | ||
| 757 | switch (term) { | ||
| 758 | .Exited => |code| { | ||
| 759 | if (code != 0) { | ||
| 760 | // TODO https://github.com/ziglang/zig/issues/6342 | ||
| 761 | std.process.exit(1); | ||
| 762 | } | ||
| 763 | }, | ||
| 764 | else => std.process.abort(), | ||
| 765 | } | ||
| 766 | } else { | ||
| 767 | child.stdin_behavior = .Ignore; | ||
| 768 | child.stdout_behavior = .Ignore; | ||
| 769 | child.stderr_behavior = .Pipe; | ||
| 770 | |||
| 771 | try child.spawn(); | ||
| 772 | |||
| 773 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 774 | |||
| 775 | const term = child.wait() catch |err| { | ||
| 776 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 777 | return error.UnableToSpawnSelf; | ||
| 778 | }; | ||
| 779 | |||
| 780 | switch (term) { | ||
| 781 | .Exited => |code| { | ||
| 782 | if (code != 0) { | ||
| 783 | // TODO parse this output and surface with the Compilation API rather than | ||
| 784 | // directly outputting to stderr here. | ||
| 785 | std.debug.print("{s}", .{stderr}); | ||
| 786 | return error.LLDReportedFailure; | ||
| 787 | } | ||
| 788 | }, | ||
| 789 | else => { | ||
| 790 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 791 | return error.LLDCrashed; | ||
| 792 | }, | ||
| 793 | } | ||
| 743 | 794 | ||
| 744 | var stderr_context: LLDContext = .{ | 795 | if (stderr.len != 0) { |
| 745 | .macho = self, | 796 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 746 | .data = std.ArrayList(u8).init(self.base.allocator), | 797 | } |
| 747 | }; | ||
| 748 | defer stderr_context.data.deinit(); | ||
| 749 | var stdout_context: LLDContext = .{ | ||
| 750 | .macho = self, | ||
| 751 | .data = std.ArrayList(u8).init(self.base.allocator), | ||
| 752 | }; | ||
| 753 | defer stdout_context.data.deinit(); | ||
| 754 | const llvm = @import("../llvm.zig"); | ||
| 755 | const ok = llvm.Link( | ||
| 756 | .MachO, | ||
| 757 | new_argv.ptr, | ||
| 758 | new_argv.len, | ||
| 759 | append_diagnostic, | ||
| 760 | @ptrToInt(&stdout_context), | ||
| 761 | @ptrToInt(&stderr_context), | ||
| 762 | ); | ||
| 763 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | ||
| 764 | if (stdout_context.data.items.len != 0) { | ||
| 765 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | ||
| 766 | } | ||
| 767 | if (!ok) { | ||
| 768 | // TODO parse this output and surface with the Compilation API rather than | ||
| 769 | // directly outputting to stderr here. | ||
| 770 | std.debug.print("{}", .{stderr_context.data.items}); | ||
| 771 | return error.LLDReportedFailure; | ||
| 772 | } | ||
| 773 | if (stderr_context.data.items.len != 0) { | ||
| 774 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | ||
| 775 | } | 798 | } |
| 776 | } | 799 | } |
| 777 | } | 800 | } |
| ... | @@ -792,20 +815,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -792,20 +815,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 792 | } | 815 | } |
| 793 | } | 816 | } |
| 794 | 817 | ||
| 795 | const LLDContext = struct { | ||
| 796 | data: std.ArrayList(u8), | ||
| 797 | macho: *MachO, | ||
| 798 | oom: bool = false, | ||
| 799 | }; | ||
| 800 | |||
| 801 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 802 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 803 | const msg = ptr[0..len]; | ||
| 804 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 805 | error.OutOfMemory => lld_context.oom = true, | ||
| 806 | }; | ||
| 807 | } | ||
| 808 | |||
| 809 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | 818 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 810 | return switch (arch) { | 819 | return switch (arch) { |
| 811 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", | 820 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", |
src/link/Wasm.zig+60-52| ... | @@ -345,8 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -345,8 +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 | // Even though we're calling LLD as a library it thinks the first argument is its own exe name. | 348 | // We will invoke ourselves as a child process to gain access to LLD. |
| 349 | try argv.append("lld"); | 349 | // This is necessary because LLD does not behave properly as a library - |
| 350 | // it calls exit() and does not reset all global data between invocations. | ||
| 351 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" }); | ||
| 350 | if (is_obj) { | 352 | if (is_obj) { |
| 351 | try argv.append("-r"); | 353 | try argv.append("-r"); |
| 352 | } | 354 | } |
| ... | @@ -396,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -396,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 396 | } | 398 | } |
| 397 | 399 | ||
| 398 | if (self.base.options.verbose_link) { | 400 | if (self.base.options.verbose_link) { |
| 399 | 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..]); | ||
| 400 | } | 403 | } |
| 401 | 404 | ||
| 402 | 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 |
| 403 | for (argv.items) |arg, i| { | 406 | // properly as a library. |
| 404 | new_argv[i] = try arena.dupeZ(u8, arg); | 407 | const child = try std.ChildProcess.init(argv.items, arena); |
| 405 | } | 408 | defer child.deinit(); |
| 406 | 409 | ||
| 407 | var stderr_context: LLDContext = .{ | 410 | if (comp.clang_passthrough_mode) { |
| 408 | .wasm = self, | 411 | child.stdin_behavior = .Inherit; |
| 409 | .data = std.ArrayList(u8).init(self.base.allocator), | 412 | child.stdout_behavior = .Inherit; |
| 410 | }; | 413 | child.stderr_behavior = .Inherit; |
| 411 | defer stderr_context.data.deinit(); | 414 | |
| 412 | var stdout_context: LLDContext = .{ | 415 | const term = child.spawnAndWait() catch |err| { |
| 413 | .wasm = self, | 416 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 414 | .data = std.ArrayList(u8).init(self.base.allocator), | 417 | return error.UnableToSpawnSelf; |
| 415 | }; | 418 | }; |
| 416 | defer stdout_context.data.deinit(); | 419 | switch (term) { |
| 417 | const llvm = @import("../llvm.zig"); | 420 | .Exited => |code| { |
| 418 | const ok = llvm.Link( | 421 | if (code != 0) { |
| 419 | .Wasm, | 422 | // TODO https://github.com/ziglang/zig/issues/6342 |
| 420 | new_argv.ptr, | 423 | std.process.exit(1); |
| 421 | new_argv.len, | 424 | } |
| 422 | append_diagnostic, | 425 | }, |
| 423 | @ptrToInt(&stdout_context), | 426 | else => std.process.abort(), |
| 424 | @ptrToInt(&stderr_context), | 427 | } |
| 425 | ); | 428 | } else { |
| 426 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 429 | child.stdin_behavior = .Ignore; |
| 427 | if (stdout_context.data.items.len != 0) { | 430 | child.stdout_behavior = .Ignore; |
| 428 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 431 | child.stderr_behavior = .Pipe; |
| 429 | } | 432 | |
| 430 | if (!ok) { | 433 | try child.spawn(); |
| 431 | // TODO parse this output and surface with the Compilation API rather than | 434 | |
| 432 | // directly outputting to stderr here. | 435 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); |
| 433 | std.debug.print("{}", .{stderr_context.data.items}); | 436 | |
| 434 | return error.LLDReportedFailure; | 437 | const term = child.wait() catch |err| { |
| 435 | } | 438 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 436 | if (stderr_context.data.items.len != 0) { | 439 | return error.UnableToSpawnSelf; |
| 437 | 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 | } | ||
| 438 | } | 460 | } |
| 439 | 461 | ||
| 440 | if (!self.base.options.disable_lld_caching) { | 462 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -453,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -453,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 453 | } | 475 | } |
| 454 | } | 476 | } |
| 455 | 477 | ||
| 456 | const LLDContext = struct { | ||
| 457 | data: std.ArrayList(u8), | ||
| 458 | wasm: *Wasm, | ||
| 459 | oom: bool = false, | ||
| 460 | }; | ||
| 461 | |||
| 462 | fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { | ||
| 463 | const lld_context = @intToPtr(*LLDContext, context); | ||
| 464 | const msg = ptr[0..len]; | ||
| 465 | lld_context.data.appendSlice(msg) catch |err| switch (err) { | ||
| 466 | error.OutOfMemory => lld_context.oom = true, | ||
| 467 | }; | ||
| 468 | } | ||
| 469 | |||
| 470 | /// 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 |
| 471 | /// TODO: we could maintain a hash map to potentially make this | 479 | /// TODO: we could maintain a hash map to potentially make this |
| 472 | 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")) { |
| ... | @@ -2786,6 +2792,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} | ... | @@ -2786,6 +2792,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} |
| 2786 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); | 2792 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); |
| 2787 | } | 2793 | } |
| 2788 | 2794 | ||
| 2795 | /// The first argument determines which backend is invoked. The options are: | ||
| 2796 | /// * `ld.lld` - ELF | ||
| 2797 | /// * `ld64.lld` - Mach-O | ||
| 2798 | /// * `lld-link` - COFF | ||
| 2799 | /// * `wasm-ld` - WebAssembly | ||
| 2800 | /// TODO https://github.com/ziglang/zig/issues/3257 | ||
| 2801 | pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { | ||
| 2802 | if (!build_options.have_llvm) | ||
| 2803 | fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); | ||
| 2804 | // Convert the args to the format LLD expects. | ||
| 2805 | // We subtract 1 to shave off the zig binary from args[0]. | ||
| 2806 | const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null); | ||
| 2807 | for (args[1..]) |arg, i| { | ||
| 2808 | argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. | ||
| 2809 | } | ||
| 2810 | const exit_code = rc: { | ||
| 2811 | const llvm = @import("llvm.zig"); | ||
| 2812 | const argc = @intCast(c_int, argv.len); | ||
| 2813 | if (mem.eql(u8, args[1], "ld.lld")) { | ||
| 2814 | break :rc llvm.LinkELF(argc, argv.ptr, true); | ||
| 2815 | } else if (mem.eql(u8, args[1], "ld64.lld")) { | ||
| 2816 | break :rc llvm.LinkMachO(argc, argv.ptr, true); | ||
| 2817 | } else if (mem.eql(u8, args[1], "lld-link")) { | ||
| 2818 | break :rc llvm.LinkCOFF(argc, argv.ptr, true); | ||
| 2819 | } else if (mem.eql(u8, args[1], "wasm-ld")) { | ||
| 2820 | break :rc llvm.LinkWasm(argc, argv.ptr, true); | ||
| 2821 | } else { | ||
| 2822 | unreachable; | ||
| 2823 | } | ||
| 2824 | }; | ||
| 2825 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); | ||
| 2826 | } | ||
| 2827 | |||
| 2789 | const clang_args = @import("clang_options.zig").list; | 2828 | const clang_args = @import("clang_options.zig").list; |
| 2790 | 2829 | ||
| 2791 | pub const ClangArgIterator = struct { | 2830 | pub const ClangArgIterator = struct { |
src/zig_llvm.cpp+15-30| ... | @@ -1048,39 +1048,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size | ... | @@ -1048,39 +1048,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size |
| 1048 | return false; | 1048 | return false; |
| 1049 | } | 1049 | } |
| 1050 | 1050 | ||
| 1051 | int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) { | ||
| 1052 | std::vector<const char *> args(argv, argv + argc); | ||
| 1053 | return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs()); | ||
| 1054 | } | ||
| 1051 | 1055 | ||
| 1052 | bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | 1056 | int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) { |
| 1053 | void (*append_diagnostic)(void *, const char *, size_t), | 1057 | std::vector<const char *> args(argv, argv + argc); |
| 1054 | void *context_stdout, void *context_stderr) | 1058 | return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs()); |
| 1055 | { | 1059 | } |
| 1056 | ArrayRef<const char *> array_ref_args(args, arg_count); | ||
| 1057 | |||
| 1058 | MyOStream diag_stdout(append_diagnostic, context_stdout); | ||
| 1059 | MyOStream diag_stderr(append_diagnostic, context_stderr); | ||
| 1060 | |||
| 1061 | switch (oformat) { | ||
| 1062 | case ZigLLVM_UnknownObjectFormat: | ||
| 1063 | case ZigLLVM_XCOFF: | ||
| 1064 | assert(false); // unreachable | ||
| 1065 | break; | ||
| 1066 | |||
| 1067 | case ZigLLVM_COFF: | ||
| 1068 | return lld::coff::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1069 | |||
| 1070 | case ZigLLVM_ELF: | ||
| 1071 | return lld::elf::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1072 | |||
| 1073 | case ZigLLVM_MachO: | ||
| 1074 | return lld::mach_o::link(array_ref_args, false, diag_stdout, diag_stderr); | ||
| 1075 | 1060 | ||
| 1076 | case ZigLLVM_Wasm: | 1061 | int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) { |
| 1077 | return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr); | 1062 | std::vector<const char *> args(argv, argv + argc); |
| 1063 | return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs()); | ||
| 1064 | } | ||
| 1078 | 1065 | ||
| 1079 | default: | 1066 | int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) { |
| 1080 | break; | 1067 | std::vector<const char *> args(argv, argv + argc); |
| 1081 | } | 1068 | return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs()); |
| 1082 | assert(false); // unreachable | ||
| 1083 | abort(); | ||
| 1084 | } | 1069 | } |
| 1085 | 1070 | ||
| 1086 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { | 1071 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { |
src/zig_llvm.h+4-3| ... | @@ -505,9 +505,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor | ... | @@ -505,9 +505,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor |
| 505 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); | 505 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); |
| 506 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); | 506 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); |
| 507 | 507 | ||
| 508 | ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | 508 | ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early); |
| 509 | void (*append_diagnostic)(void *, const char *, size_t), | 509 | ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early); |
| 510 | void *context_stdout, void *context_stderr); | 510 | ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early); |
| 511 | ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early); | ||
| 511 | 512 | ||
| 512 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, | 513 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, |
| 513 | enum ZigLLVM_OSType os_type); | 514 | 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 { |