Merge pull request #7355 from ziglang/lld-child-process

invoke LLD as a child process rather than a library
This commit is contained in:
Andrew Kelley 2020-12-09 02:26:13 -05:00
parent 40e37d4324
commit 85c1db9222
11 changed files with 332 additions and 254 deletions

View File

@ -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

View File

@ -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;

View File

@ -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();
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 (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});
}
}
}
@ -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;
}

View File

@ -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) {

View File

@ -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();
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 (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});
}
}
}
}
@ -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",

View File

@ -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 {

View File

@ -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,

View File

@ -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 {

View File

@ -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);
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());
}
MyOStream diag_stdout(append_diagnostic, context_stdout);
MyOStream diag_stderr(append_diagnostic, context_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());
}
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);
case ZigLLVM_Wasm:
return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr);
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) {

View File

@ -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);

View File

@ -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 {