mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #7355 from ziglang/lld-child-process
invoke LLD as a child process rather than a library
This commit is contained in:
parent
40e37d4324
commit
85c1db9222
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
113
src/link/Elf.zig
113
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) {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 {
|
||||
|
||||
18
src/llvm.zig
18
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,
|
||||
|
||||
39
src/main.zig
39
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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user