Commit 85c1db9222
2020-12-09 08:26:13
1 parent
40e37d4Changed files (11)
lib/std/testing.zig
@@ -247,6 +247,7 @@ test "expectWithinEpsilon" {
/// This function is intended to be used only in tests. When the two slices are not
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then aborts.
+/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void {
// TODO better printing of the difference
// 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 {
}
}
+pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) void {
+ if (std.mem.endsWith(u8, actual, expected_ends_with))
+ return;
+
+ const shortened_actual = if (actual.len >= expected_ends_with.len)
+ actual[0..expected_ends_with.len]
+ else
+ actual;
+
+ print("\n====== expected to end with: =========\n", .{});
+ printWithVisibleNewlines(expected_ends_with);
+ print("\n====== instead ended with: ===========\n", .{});
+ printWithVisibleNewlines(shortened_actual);
+ print("\n========= full output: ==============\n", .{});
+ printWithVisibleNewlines(actual);
+ print("\n======================================\n", .{});
+
+ @panic("test failure");
+}
+
fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
line_begin + 1
src/link/Coff.zig
@@ -907,8 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(self.base.allocator);
defer argv.deinit();
- // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
- try argv.append("lld");
+ // We will invoke ourselves as a child process to gain access to LLD.
+ // This is necessary because LLD does not behave properly as a library -
+ // it calls exit() and does not reset all global data between invocations.
+ try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
try argv.append("-ERRORLIMIT:0");
try argv.append("-NOLOGO");
@@ -1146,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
if (self.base.options.verbose_link) {
- Compilation.dump_argv(argv.items);
+ // Skip over our own name so that the LLD linker name is the first argv item.
+ Compilation.dump_argv(argv.items[1..]);
}
- const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
+ // Sadly, we must run LLD as a child process because it does not behave
+ // properly as a library.
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
+
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ },
+ else => std.process.abort(),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Ignore;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{s}", .{stderr});
+ return error.LLDReportedFailure;
+ }
+ },
+ else => {
+ log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
+ return error.LLDCrashed;
+ },
+ }
- var stderr_context: LLDContext = .{
- .coff = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stderr_context.data.deinit();
- var stdout_context: LLDContext = .{
- .coff = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stdout_context.data.deinit();
- const llvm = @import("../llvm.zig");
- const ok = llvm.Link(
- .COFF,
- new_argv.ptr,
- new_argv.len,
- append_diagnostic,
- @ptrToInt(&stdout_context),
- @ptrToInt(&stderr_context),
- );
- if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
- if (stdout_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
- }
- if (!ok) {
- // TODO parse this output and surface with the Compilation API rather than
- // directly outputting to stderr here.
- std.debug.print("{}", .{stderr_context.data.items});
- return error.LLDReportedFailure;
- }
- if (stderr_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ if (stderr.len != 0) {
+ std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
+ }
}
}
@@ -1204,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
}
-const LLDContext = struct {
- data: std.ArrayList(u8),
- coff: *Coff,
- oom: bool = false,
-};
-
-fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
- const lld_context = @intToPtr(*LLDContext, context);
- const msg = ptr[0..len];
- lld_context.data.appendSlice(msg) catch |err| switch (err) {
- error.OutOfMemory => lld_context.oom = true,
- };
-}
-
pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
return self.text_section_virtual_address + decl.link.coff.text_offset;
}
src/link/Elf.zig
@@ -1360,8 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(self.base.allocator);
defer argv.deinit();
- // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
- try argv.append("lld");
+ // We will invoke ourselves as a child process to gain access to LLD.
+ // This is necessary because LLD does not behave properly as a library -
+ // it calls exit() and does not reset all global data between invocations.
+ try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
if (is_obj) {
try argv.append("-r");
}
@@ -1621,46 +1623,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
}
if (self.base.options.verbose_link) {
- Compilation.dump_argv(argv.items);
+ // Skip over our own name so that the LLD linker name is the first argv item.
+ Compilation.dump_argv(argv.items[1..]);
}
- // Oh, snapplesauce! We need null terminated argv.
- const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
+ // Sadly, we must run LLD as a child process because it does not behave
+ // properly as a library.
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
- var stderr_context: LLDContext = .{
- .elf = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stderr_context.data.deinit();
- var stdout_context: LLDContext = .{
- .elf = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stdout_context.data.deinit();
- const llvm = @import("../llvm.zig");
- const ok = llvm.Link(
- .ELF,
- new_argv.ptr,
- new_argv.len,
- append_diagnostic,
- @ptrToInt(&stdout_context),
- @ptrToInt(&stderr_context),
- );
- if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
- if (stdout_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
- }
- if (!ok) {
- // TODO parse this output and surface with the Compilation API rather than
- // directly outputting to stderr here.
- std.debug.print("{}", .{stderr_context.data.items});
- return error.LLDReportedFailure;
- }
- if (stderr_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ },
+ else => std.process.abort(),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Ignore;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{s}", .{stderr});
+ return error.LLDReportedFailure;
+ }
+ },
+ else => {
+ log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
+ return error.LLDCrashed;
+ },
+ }
+
+ if (stderr.len != 0) {
+ std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
+ }
}
if (!self.base.options.disable_lld_caching) {
@@ -1679,20 +1700,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
}
}
-const LLDContext = struct {
- data: std.ArrayList(u8),
- elf: *Elf,
- oom: bool = false,
-};
-
-fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
- const lld_context = @intToPtr(*LLDContext, context);
- const msg = ptr[0..len];
- lld_context.data.appendSlice(msg) catch |err| switch (err) {
- error.OutOfMemory => lld_context.oom = true,
- };
-}
-
fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
const target_endian = self.base.options.target.cpu.arch.endian();
switch (self.ptr_width) {
src/link/MachO.zig
@@ -544,8 +544,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
if (self.base.options.system_linker_hack) {
try argv.append("ld");
} else {
- // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
- try argv.append("lld");
+ // We will invoke ourselves as a child process to gain access to LLD.
+ // This is necessary because LLD does not behave properly as a library -
+ // it calls exit() and does not reset all global data between invocations.
+ try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" });
try argv.append("-error-limit");
try argv.append("0");
@@ -711,7 +713,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
if (self.base.options.verbose_link) {
- Compilation.dump_argv(argv.items);
+ // Potentially skip over our own name so that the LLD linker name is the first argv item.
+ const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..];
+ Compilation.dump_argv(adjusted_argv);
}
// TODO https://github.com/ziglang/zig/issues/6971
@@ -736,42 +740,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
return error.LDReportedFailure;
}
} else {
- const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
+ // Sadly, we must run LLD as a child process because it does not behave
+ // properly as a library.
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
+
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ },
+ else => std.process.abort(),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Ignore;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{s}", .{stderr});
+ return error.LLDReportedFailure;
+ }
+ },
+ else => {
+ log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
+ return error.LLDCrashed;
+ },
+ }
- var stderr_context: LLDContext = .{
- .macho = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stderr_context.data.deinit();
- var stdout_context: LLDContext = .{
- .macho = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stdout_context.data.deinit();
- const llvm = @import("../llvm.zig");
- const ok = llvm.Link(
- .MachO,
- new_argv.ptr,
- new_argv.len,
- append_diagnostic,
- @ptrToInt(&stdout_context),
- @ptrToInt(&stderr_context),
- );
- if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
- if (stdout_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
- }
- if (!ok) {
- // TODO parse this output and surface with the Compilation API rather than
- // directly outputting to stderr here.
- std.debug.print("{}", .{stderr_context.data.items});
- return error.LLDReportedFailure;
- }
- if (stderr_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ if (stderr.len != 0) {
+ std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
+ }
}
}
}
@@ -792,20 +815,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
}
-const LLDContext = struct {
- data: std.ArrayList(u8),
- macho: *MachO,
- oom: bool = false,
-};
-
-fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
- const lld_context = @intToPtr(*LLDContext, context);
- const msg = ptr[0..len];
- lld_context.data.appendSlice(msg) catch |err| switch (err) {
- error.OutOfMemory => lld_context.oom = true,
- };
-}
-
fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
return switch (arch) {
.aarch64, .aarch64_be, .aarch64_32 => "arm64",
src/link/Wasm.zig
@@ -345,8 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(self.base.allocator);
defer argv.deinit();
- // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
- try argv.append("lld");
+ // We will invoke ourselves as a child process to gain access to LLD.
+ // This is necessary because LLD does not behave properly as a library -
+ // it calls exit() and does not reset all global data between invocations.
+ try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" });
if (is_obj) {
try argv.append("-r");
}
@@ -396,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
}
if (self.base.options.verbose_link) {
- Compilation.dump_argv(argv.items);
+ // Skip over our own name so that the LLD linker name is the first argv item.
+ Compilation.dump_argv(argv.items[1..]);
}
- const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
+ // Sadly, we must run LLD as a child process because it does not behave
+ // properly as a library.
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
- var stderr_context: LLDContext = .{
- .wasm = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stderr_context.data.deinit();
- var stdout_context: LLDContext = .{
- .wasm = self,
- .data = std.ArrayList(u8).init(self.base.allocator),
- };
- defer stdout_context.data.deinit();
- const llvm = @import("../llvm.zig");
- const ok = llvm.Link(
- .Wasm,
- new_argv.ptr,
- new_argv.len,
- append_diagnostic,
- @ptrToInt(&stdout_context),
- @ptrToInt(&stderr_context),
- );
- if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
- if (stdout_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
- }
- if (!ok) {
- // TODO parse this output and surface with the Compilation API rather than
- // directly outputting to stderr here.
- std.debug.print("{}", .{stderr_context.data.items});
- return error.LLDReportedFailure;
- }
- if (stderr_context.data.items.len != 0) {
- std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ },
+ else => std.process.abort(),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Ignore;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
+ return error.UnableToSpawnSelf;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{s}", .{stderr});
+ return error.LLDReportedFailure;
+ }
+ },
+ else => {
+ log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
+ return error.LLDCrashed;
+ },
+ }
+
+ if (stderr.len != 0) {
+ std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
+ }
}
if (!self.base.options.disable_lld_caching) {
@@ -453,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
}
}
-const LLDContext = struct {
- data: std.ArrayList(u8),
- wasm: *Wasm,
- oom: bool = false,
-};
-
-fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
- const lld_context = @intToPtr(*LLDContext, context);
- const msg = ptr[0..len];
- lld_context.data.appendSlice(msg) catch |err| switch (err) {
- error.OutOfMemory => lld_context.oom = true,
- };
-}
-
/// Get the current index of a given Decl in the function list
/// TODO: we could maintain a hash map to potentially make this
fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
src/Compilation.zig
@@ -1756,7 +1756,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
if (comp.clang_preprocessor_mode == .stdout)
std.process.exit(0);
},
- else => std.process.exit(1),
+ else => std.process.abort(),
}
} else {
child.stdin_behavior = .Ignore;
src/llvm.zig
@@ -1,15 +1,15 @@
//! We do this instead of @cImport because the self-hosted compiler is easier
//! to bootstrap if it does not depend on translate-c.
-pub const Link = ZigLLDLink;
-extern fn ZigLLDLink(
- oformat: ObjectFormatType,
- args: [*:null]const ?[*:0]const u8,
- arg_count: usize,
- append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void,
- context_stdout: usize,
- context_stderr: usize,
-) bool;
+extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
+extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
+extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
+extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
+
+pub const LinkCOFF = ZigLLDLinkCOFF;
+pub const LinkELF = ZigLLDLinkELF;
+pub const LinkMachO = ZigLLDLinkMachO;
+pub const LinkWasm = ZigLLDLinkWasm;
pub const ObjectFormatType = extern enum(c_int) {
Unknown,
src/main.zig
@@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
{
return punt_to_clang(arena, args);
+ } else if (mem.eql(u8, cmd, "ld.lld") or
+ mem.eql(u8, cmd, "ld64.lld") or
+ mem.eql(u8, cmd, "lld-link") or
+ mem.eql(u8, cmd, "wasm-ld"))
+ {
+ return punt_to_lld(arena, args);
} else if (mem.eql(u8, cmd, "build")) {
return cmdBuild(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "fmt")) {
@@ -2786,6 +2792,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}
process.exit(@bitCast(u8, @truncate(i8, exit_code)));
}
+/// The first argument determines which backend is invoked. The options are:
+/// * `ld.lld` - ELF
+/// * `ld64.lld` - Mach-O
+/// * `lld-link` - COFF
+/// * `wasm-ld` - WebAssembly
+/// TODO https://github.com/ziglang/zig/issues/3257
+pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
+ if (!build_options.have_llvm)
+ fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
+ // Convert the args to the format LLD expects.
+ // We subtract 1 to shave off the zig binary from args[0].
+ const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
+ for (args[1..]) |arg, i| {
+ argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
+ }
+ const exit_code = rc: {
+ const llvm = @import("llvm.zig");
+ const argc = @intCast(c_int, argv.len);
+ if (mem.eql(u8, args[1], "ld.lld")) {
+ break :rc llvm.LinkELF(argc, argv.ptr, true);
+ } else if (mem.eql(u8, args[1], "ld64.lld")) {
+ break :rc llvm.LinkMachO(argc, argv.ptr, true);
+ } else if (mem.eql(u8, args[1], "lld-link")) {
+ break :rc llvm.LinkCOFF(argc, argv.ptr, true);
+ } else if (mem.eql(u8, args[1], "wasm-ld")) {
+ break :rc llvm.LinkWasm(argc, argv.ptr, true);
+ } else {
+ unreachable;
+ }
+ };
+ process.exit(@bitCast(u8, @truncate(i8, exit_code)));
+}
+
const clang_args = @import("clang_options.zig").list;
pub const ClangArgIterator = struct {
src/zig_llvm.cpp
@@ -1048,39 +1048,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size
return false;
}
+int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) {
+ std::vector<const char *> args(argv, argv + argc);
+ return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs());
+}
-bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
- void (*append_diagnostic)(void *, const char *, size_t),
- void *context_stdout, void *context_stderr)
-{
- ArrayRef<const char *> array_ref_args(args, arg_count);
-
- MyOStream diag_stdout(append_diagnostic, context_stdout);
- MyOStream diag_stderr(append_diagnostic, context_stderr);
-
- switch (oformat) {
- case ZigLLVM_UnknownObjectFormat:
- case ZigLLVM_XCOFF:
- assert(false); // unreachable
- break;
-
- case ZigLLVM_COFF:
- return lld::coff::link(array_ref_args, false, diag_stdout, diag_stderr);
-
- case ZigLLVM_ELF:
- return lld::elf::link(array_ref_args, false, diag_stdout, diag_stderr);
-
- case ZigLLVM_MachO:
- return lld::mach_o::link(array_ref_args, false, diag_stdout, diag_stderr);
+int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) {
+ std::vector<const char *> args(argv, argv + argc);
+ return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs());
+}
- case ZigLLVM_Wasm:
- return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr);
+int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) {
+ std::vector<const char *> args(argv, argv + argc);
+ return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs());
+}
- default:
- break;
- }
- assert(false); // unreachable
- abort();
+int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) {
+ std::vector<const char *> args(argv, argv + argc);
+ return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs());
}
static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {
src/zig_llvm.h
@@ -505,9 +505,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor
ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);
ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);
-ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
- void (*append_diagnostic)(void *, const char *, size_t),
- void *context_stdout, void *context_stderr);
+ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early);
+ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early);
+ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early);
+ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early);
ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
enum ZigLLVM_OSType os_type);
test/cli.zig
@@ -92,13 +92,13 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess
fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
- testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));
+ testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n");
}
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
- testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));
+ testing.expectEqualStrings("info: All your codebase are belong to us.\n", run_result.stderr);
}
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {