self-hosted: linking against libc

also introduce `zig libc` command to display paths
`zig libc file.txt` will parse equivalent text and use that for libc
paths.
master
Andrew Kelley 2018-07-18 00:34:42 -04:00
parent 3e4a3fa5b7
commit aa3b41247f
8 changed files with 707 additions and 170 deletions

View File

@ -15,7 +15,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
defer fn_val.base.deref(comp);
defer code.destroy(comp.gpa());
var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable);
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
errdefer output_path.deinit();
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();

View File

@ -120,7 +120,6 @@ pub const Compilation = struct {
linker_script: ?[]const u8,
cache_dir: []const u8,
dynamic_linker: ?[]const u8,
out_h_path: ?[]const u8,
is_test: bool,
@ -201,6 +200,13 @@ pub const Compilation = struct {
root_package: *Package,
std_package: *Package,
override_libc: ?*LibCInstallation,
/// need to wait on this group before deinitializing
deinit_group: event.Group(void),
destroy_handle: promise,
const CompileErrList = std.ArrayList(*errmsg.Msg);
// TODO handle some of these earlier and report them in a way other than error codes
@ -246,6 +252,8 @@ pub const Compilation = struct {
EnvironmentVariableNotFound,
AppDataDirUnavailable,
LinkFailed,
LibCRequiredButNotProvidedOrFound,
LibCMissingDynamicLinker,
};
pub const Event = union(enum) {
@ -324,7 +332,6 @@ pub const Compilation = struct {
.verbose_link = false,
.linker_script = null,
.dynamic_linker = null,
.out_h_path = null,
.is_test = false,
.each_lib_rpath = false,
@ -351,6 +358,7 @@ pub const Compilation = struct {
.link_out_file = null,
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
.prelink_group = event.Group(BuildError!void).init(loop),
.deinit_group = event.Group(void).init(loop),
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
.meta_type = undefined,
@ -368,6 +376,9 @@ pub const Compilation = struct {
.root_package = undefined,
.std_package = undefined,
.override_libc = null,
.destroy_handle = undefined,
});
errdefer {
comp.arena_allocator.deinit();
@ -431,6 +442,9 @@ pub const Compilation = struct {
}
try comp.initTypes();
errdefer comp.derefTypes();
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
return comp;
}
@ -526,11 +540,7 @@ pub const Compilation = struct {
errdefer comp.gpa().destroy(comp.noreturn_value);
}
pub fn destroy(self: *Compilation) void {
if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
os.deleteTree(self.arena(), tmp_dir) catch {};
} else |_| {};
fn derefTypes(self: *Compilation) void {
self.noreturn_value.base.deref(self);
self.void_value.base.deref(self);
self.false_value.base.deref(self);
@ -538,6 +548,17 @@ pub const Compilation = struct {
self.noreturn_type.base.base.deref(self);
self.void_type.base.base.deref(self);
self.meta_type.base.base.deref(self);
}
async fn internalDeinit(self: *Compilation) void {
suspend;
await (async self.deinit_group.wait() catch unreachable);
if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
// TODO evented I/O?
os.deleteTree(self.arena(), tmp_dir) catch {};
} else |_| {};
self.derefTypes();
self.events.destroy();
@ -549,6 +570,10 @@ pub const Compilation = struct {
self.gpa().destroy(self);
}
pub fn destroy(self: *Compilation) void {
resume self.destroy_handle;
}
pub fn build(self: *Compilation) !void {
if (self.llvm_argv.len != 0) {
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
@ -680,7 +705,7 @@ pub const Compilation = struct {
};
if (!any_prelink_errors) {
try link(self);
try await (async link(self) catch unreachable);
}
}
@ -765,8 +790,8 @@ pub const Compilation = struct {
self.libc_link_lib = link_lib;
// get a head start on looking for the native libc
if (self.target == Target.Native) {
try async<self.loop.allocator> self.startFindingNativeLibC();
if (self.target == Target.Native and self.override_libc == null) {
try self.deinit_group.call(startFindingNativeLibC, self);
}
}
return link_lib;
@ -774,11 +799,9 @@ pub const Compilation = struct {
/// cancels itself so no need to await or cancel the promise.
async fn startFindingNativeLibC(self: *Compilation) void {
await (async self.loop.yield() catch unreachable);
// we don't care if it fails, we're just trying to kick off the future resolution
_ = (await (async self.loop.call(EventLoopLocal.getNativeLibC, self.event_loop_local) catch unreachable)) catch {};
suspend |p| {
cancel p;
}
_ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
}
/// General Purpose Allocator. Must free when done.

View File

@ -1,31 +1,18 @@
const std = @import("std");
const builtin = @import("builtin");
const event = std.event;
const Target = @import("target.zig").Target;
/// See the render function implementation for documentation of the fields.
pub const LibCInstallation = struct {
/// The directory that contains `stdlib.h`.
/// On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
include_dir: []const u8,
/// The directory that contains `crt1.o`.
/// On Linux, can be found with `cc -print-file-name=crt1.o`.
/// Not needed when targeting MacOS.
lib_dir: ?[]const u8,
/// The directory that contains `crtbegin.o`.
/// On Linux, can be found with `cc -print-file-name=crt1.o`.
/// Not needed when targeting MacOS or Windows.
static_lib_dir: ?[]const u8,
/// The directory that contains `vcruntime.lib`.
/// Only needed when targeting Windows.
msvc_lib_dir: ?[]const u8,
/// The directory that contains `kernel32.lib`.
/// Only needed when targeting Windows.
kernel32_lib_dir: ?[]const u8,
dynamic_linker_path: ?[]const u8,
pub const Error = error{
pub const FindError = error{
OutOfMemory,
FileSystem,
UnableToSpawnCCompiler,
@ -36,16 +23,124 @@ pub const LibCInstallation = struct {
LibCStdLibHeaderNotFound,
};
pub fn parse(
self: *LibCInstallation,
allocator: *std.mem.Allocator,
libc_file: []const u8,
stderr: *std.io.OutStream(std.io.FileOutStream.Error),
) !void {
self.initEmpty();
const keys = []const []const u8{
"include_dir",
"lib_dir",
"static_lib_dir",
"msvc_lib_dir",
"kernel32_lib_dir",
"dynamic_linker_path",
};
const FoundKey = struct {
found: bool,
allocated: ?[]u8,
};
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
errdefer {
self.initEmpty();
for (found_keys) |found_key| {
if (found_key.allocated) |s| allocator.free(s);
}
}
const contents = try std.io.readFileAlloc(allocator, libc_file);
defer allocator.free(contents);
var it = std.mem.split(contents, "\n");
while (it.next()) |line| {
if (line.len == 0 or line[0] == '#') continue;
var line_it = std.mem.split(line, "=");
const name = line_it.next() orelse {
try stderr.print("missing equal sign after field name\n");
return error.ParseError;
};
const value = line_it.rest();
inline for (keys) |key, i| {
if (std.mem.eql(u8, name, key)) {
found_keys[i].found = true;
switch (@typeInfo(@typeOf(@field(self, key)))) {
builtin.TypeId.Optional => {
if (value.len == 0) {
@field(self, key) = null;
} else {
found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
@field(self, key) = found_keys[i].allocated;
}
},
else => {
if (value.len == 0) {
try stderr.print("field cannot be empty: {}\n", key);
return error.ParseError;
}
const dupe = try std.mem.dupe(allocator, u8, value);
found_keys[i].allocated = dupe;
@field(self, key) = dupe;
},
}
break;
}
}
}
for (found_keys) |found_key, i| {
if (!found_key.found) {
try stderr.print("missing field: {}\n", keys[i]);
return error.ParseError;
}
}
}
pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
@setEvalBranchQuota(4000);
try out.print(
\\# The directory that contains `stdlib.h`.
\\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
\\include_dir={}
\\
\\# The directory that contains `crt1.o`.
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
\\# Not needed when targeting MacOS.
\\lib_dir={}
\\
\\# The directory that contains `crtbegin.o`.
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
\\# Not needed when targeting MacOS or Windows.
\\static_lib_dir={}
\\
\\# The directory that contains `vcruntime.lib`.
\\# Only needed when targeting Windows.
\\msvc_lib_dir={}
\\
\\# The directory that contains `kernel32.lib`.
\\# Only needed when targeting Windows.
\\kernel32_lib_dir={}
\\
\\# The full path to the dynamic linker.
\\# Only needed when targeting Linux.
\\dynamic_linker_path={}
\\
,
self.include_dir,
self.lib_dir orelse "",
self.static_lib_dir orelse "",
self.msvc_lib_dir orelse "",
self.kernel32_lib_dir orelse "",
self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
);
}
/// Finds the default, native libc.
pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
self.* = LibCInstallation{
.lib_dir = null,
.include_dir = ([*]const u8)(undefined)[0..0],
.static_lib_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
};
var group = event.Group(Error!void).init(loop);
self.initEmpty();
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
switch (builtin.os) {
builtin.Os.windows => {
try group.call(findNativeIncludeDirWindows, self, loop);
@ -57,6 +152,7 @@ pub const LibCInstallation = struct {
try group.call(findNativeIncludeDirLinux, self, loop);
try group.call(findNativeLibDirLinux, self, loop);
try group.call(findNativeStaticLibDir, self, loop);
try group.call(findNativeDynamicLinker, self, loop);
},
builtin.Os.macosx => {
try group.call(findNativeIncludeDirMacOS, self, loop);
@ -147,7 +243,7 @@ pub const LibCInstallation = struct {
self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
}
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) Error!void {
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void {
// TODO
//ZigWindowsSDK *sdk = get_windows_sdk(g);
@ -180,31 +276,83 @@ pub const LibCInstallation = struct {
@panic("TODO");
}
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) Error!void {
self.lib_dir = try await (async ccPrintFileNameDir(loop, "crt1.o") catch unreachable);
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
}
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
self.static_lib_dir = try await (async ccPrintFileNameDir(loop, "crtbegin.o") catch unreachable);
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
}
async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
var dyn_tests = []DynTest{
DynTest{
.name = "ld-linux-x86-64.so.2",
.result = null,
},
DynTest{
.name = "ld-musl-x86_64.so.1",
.result = null,
},
};
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
for (dyn_tests) |*dyn_test| {
try group.call(testNativeDynamicLinker, self, loop, dyn_test);
}
try await (async group.wait() catch unreachable);
for (dyn_tests) |*dyn_test| {
if (dyn_test.result) |result| {
self.dynamic_linker_path = result;
return;
}
}
}
const DynTest = struct {
name: []const u8,
result: ?[]const u8,
};
async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
dyn_test.result = result;
return;
} else |err| switch (err) {
error.CCompilerCannotFindCRuntime => return,
else => return err,
}
}
async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
@panic("TODO");
}
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
@panic("TODO");
}
fn initEmpty(self: *LibCInstallation) void {
self.* = LibCInstallation{
.include_dir = ([*]const u8)(undefined)[0..0],
.lib_dir = null,
.static_lib_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker_path = null,
};
}
};
/// caller owns returned memory
async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 {
async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
defer loop.allocator.free(arg1);
const argv = []const []const u8{ cc_exe, arg1 };
// TODO evented I/O
// TODO This simulates evented I/O for the child process exec
await (async loop.yield() catch unreachable);
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
@ -230,5 +378,9 @@ async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 {
const line = it.next() orelse return error.CCompilerCannotFindCRuntime;
const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime;
return std.mem.dupe(loop.allocator, u8, dirname);
if (want_dirname) {
return std.mem.dupe(loop.allocator, u8, dirname);
} else {
return std.mem.dupe(loop.allocator, u8, line);
}
}

View File

@ -3,6 +3,8 @@ const c = @import("c.zig");
const builtin = @import("builtin");
const ObjectFormat = builtin.ObjectFormat;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const Context = struct {
comp: *Compilation,
@ -12,9 +14,12 @@ const Context = struct {
link_err: error{OutOfMemory}!void,
link_msg: std.Buffer,
libc: *LibCInstallation,
out_file_path: std.Buffer,
};
pub fn link(comp: *Compilation) !void {
pub async fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
@ -22,15 +27,45 @@ pub fn link(comp: *Compilation) !void {
.link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
.link_err = {},
.link_msg = undefined,
.libc = undefined,
.out_file_path = undefined,
};
defer ctx.arena.deinit();
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
} else {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
switch (comp.kind) {
Compilation.Kind.Exe => {
try ctx.out_file_path.append(comp.target.exeFileExt());
},
Compilation.Kind.Lib => {
try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
},
Compilation.Kind.Obj => {
try ctx.out_file_path.append(comp.target.objFileExt());
},
}
}
// even though we're calling LLD as a library it thinks the first
// argument is its own exe name
try ctx.args.append(c"lld");
if (comp.haveLibC()) {
ctx.libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
},
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
}
try constructLinkerArgs(&ctx);
if (comp.verbose_link) {
@ -43,6 +78,7 @@ pub fn link(comp: *Compilation) !void {
const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
const args_slice = ctx.args.toSlice();
// Not evented I/O. LLD does its own multithreading internally.
if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
if (!ctx.link_msg.isNull()) {
// TODO capture these messages and pass them through the system, reporting them through the
@ -95,10 +131,7 @@ fn constructLinkerArgs(ctx: *Context) !void {
}
fn constructLinkerArgsElf(ctx: *Context) !void {
//if (g->libc_link_lib != nullptr) {
// find_libc_lib_path(g);
//}
// TODO commented out code in this function
//if (g->linker_script) {
// lj->args.append("-T");
// lj->args.append(g->linker_script);
@ -107,7 +140,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//if (g->no_rosegment_workaround) {
// lj->args.append("--no-rosegment");
//}
//lj->args.append("--gc-sections");
try ctx.args.append(c"--gc-sections");
//lj->args.append("-m");
//lj->args.append(getLDMOption(&g->zig_target));
@ -115,14 +148,13 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//bool is_lib = g->out_type == OutTypeLib;
//bool shared = !g->is_static && is_lib;
//Buf *soname = nullptr;
//if (g->is_static) {
// if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
// g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
// {
// lj->args.append("-Bstatic");
// } else {
// lj->args.append("-static");
// }
if (ctx.comp.is_static) {
if (ctx.comp.target.isArmOrThumb()) {
try ctx.args.append(c"-Bstatic");
} else {
try ctx.args.append(c"-static");
}
}
//} else if (shared) {
// lj->args.append("-shared");
@ -133,23 +165,16 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
//}
//lj->args.append("-o");
//lj->args.append(buf_ptr(&lj->out_file));
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
//if (lj->link_in_crt) {
// const char *crt1o;
// const char *crtbegino;
// if (g->is_static) {
// crt1o = "crt1.o";
// crtbegino = "crtbeginT.o";
// } else {
// crt1o = "Scrt1.o";
// crtbegino = "crtbegin.o";
// }
// lj->args.append(get_libc_file(g, crt1o));
// lj->args.append(get_libc_file(g, "crti.o"));
// lj->args.append(get_libc_static_file(g, crtbegino));
//}
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
}
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
@ -182,25 +207,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// lj->args.append(lib_dir);
//}
//if (g->libc_link_lib != nullptr) {
// lj->args.append("-L");
// lj->args.append(buf_ptr(g->libc_lib_dir));
if (ctx.comp.haveLibC()) {
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
// lj->args.append("-L");
// lj->args.append(buf_ptr(g->libc_static_lib_dir));
//}
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
//if (!g->is_static) {
// if (g->dynamic_linker != nullptr) {
// assert(buf_len(g->dynamic_linker) != 0);
// lj->args.append("-dynamic-linker");
// lj->args.append(buf_ptr(g->dynamic_linker));
// } else {
// Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
// lj->args.append("-dynamic-linker");
// lj->args.append(buf_ptr(resolved_dynamic_linker));
// }
//}
if (!ctx.comp.is_static) {
const dl = blk: {
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
return error.LibCMissingDynamicLinker;
};
try ctx.args.append(c"-dynamic-linker");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
}
}
//if (shared) {
// lj->args.append("-soname");
@ -241,45 +264,51 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// lj->args.append(buf_ptr(arg));
//}
//// libc dep
//if (g->libc_link_lib != nullptr) {
// if (g->is_static) {
// lj->args.append("--start-group");
// lj->args.append("-lgcc");
// lj->args.append("-lgcc_eh");
// lj->args.append("-lc");
// lj->args.append("-lm");
// lj->args.append("--end-group");
// } else {
// lj->args.append("-lgcc");
// lj->args.append("--as-needed");
// lj->args.append("-lgcc_s");
// lj->args.append("--no-as-needed");
// lj->args.append("-lc");
// lj->args.append("-lm");
// lj->args.append("-lgcc");
// lj->args.append("--as-needed");
// lj->args.append("-lgcc_s");
// lj->args.append("--no-as-needed");
// }
//}
// libc dep
if (ctx.comp.haveLibC()) {
if (ctx.comp.is_static) {
try ctx.args.append(c"--start-group");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"-lgcc_eh");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"--end-group");
} else {
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
}
}
//// crt end
//if (lj->link_in_crt) {
// lj->args.append(get_libc_static_file(g, "crtend.o"));
// lj->args.append(get_libc_file(g, "crtn.o"));
//}
// crt end
if (ctx.link_in_crt) {
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
}
//if (!g->is_native_target) {
// lj->args.append("--allow-shlib-undefined");
//}
if (ctx.comp.target != Target.Native) {
try ctx.args.append(c"--allow-shlib-undefined");
}
//if (g->zig_target.os == OsZen) {
// lj->args.append("-e");
// lj->args.append("_start");
if (ctx.comp.target.getOs() == builtin.Os.zen) {
try ctx.args.append(c"-e");
try ctx.args.append(c"_start");
// lj->args.append("--image-base=0x10000000");
//}
try ctx.args.append(c"--image-base=0x10000000");
}
}
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
}
fn constructLinkerArgsCoff(ctx: *Context) void {

View File

@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const errmsg = @import("errmsg.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
var stderr_file: os.File = undefined;
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
@ -28,14 +29,14 @@ const usage =
\\
\\Commands:
\\
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
\\ find-libc Show native libc installation paths
\\ fmt [source] Parse file and render in canonical zig format
\\ targets List available compilation targets
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
\\ fmt [source] Parse file and render in canonical zig format
\\ libc [paths_file] Display native libc paths file or validate one
\\ targets List available compilation targets
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
\\
;
@ -82,14 +83,14 @@ pub fn main() !void {
.name = "build-obj",
.exec = cmdBuildObj,
},
Command{
.name = "find-libc",
.exec = cmdFindLibc,
},
Command{
.name = "fmt",
.exec = cmdFmt,
},
Command{
.name = "libc",
.exec = cmdLibC,
},
Command{
.name = "targets",
.exec = cmdTargets,
@ -135,6 +136,7 @@ const usage_build_generic =
\\ --color [auto|off|on] Enable or disable colored error messages
\\
\\Compile Options:
\\ --libc [file] Provide a file which specifies libc paths
\\ --assembly [source] Add assembly file to build
\\ --cache-dir [path] Override the cache directory
\\ --emit [filetype] Emit a specific file format as compilation output
@ -167,7 +169,6 @@ const usage_build_generic =
\\
\\Link Options:
\\ --ar-path [path] Set the path to ar
\\ --dynamic-linker [path] Set the path to ld.so
\\ --each-lib-rpath Add rpath for each used dynamic library
\\ --library [lib] Link against lib
\\ --forbid-library [lib] Make it an error to link against lib
@ -210,6 +211,7 @@ const args_build_generic = []Flag{
"llvm-ir",
}),
Flag.Bool("--enable-timing-info"),
Flag.Arg1("--libc"),
Flag.Arg1("--name"),
Flag.Arg1("--output"),
Flag.Arg1("--output-h"),
@ -233,7 +235,6 @@ const args_build_generic = []Flag{
Flag.Arg1("-mllvm"),
Flag.Arg1("--ar-path"),
Flag.Arg1("--dynamic-linker"),
Flag.Bool("--each-lib-rpath"),
Flag.ArgMergeN("--library", 1),
Flag.ArgMergeN("--forbid-library", 1),
@ -382,6 +383,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);
var override_libc: LibCInstallation = undefined;
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
@ -402,6 +405,15 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
);
defer comp.destroy();
if (flags.single("libc")) |libc_path| {
parseLibcPaths(loop.allocator, &override_libc, libc_path);
comp.override_libc = &override_libc;
}
for (flags.many("library")) |lib| {
_ = try comp.addLinkLib(lib, true);
}
comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
@ -425,10 +437,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.strip = flags.present("strip");
if (flags.single("dynamic-linker")) |dynamic_linker| {
comp.dynamic_linker = dynamic_linker;
}
comp.verbose_tokenize = flags.present("verbose-tokenize");
comp.verbose_ast_tree = flags.present("verbose-ast-tree");
comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
@ -479,7 +487,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
switch (build_event) {
Compilation.Event.Ok => {
std.debug.warn("Build succeeded\n");
return;
},
Compilation.Event.Error => |err| {
@ -559,7 +566,32 @@ const Fmt = struct {
}
};
fn cmdFindLibc(allocator: *Allocator, args: []const []const u8) !void {
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
libc.parse(allocator, libc_paths_file, stderr) catch |err| {
stderr.print(
"Unable to parse libc path file '{}': {}.\n" ++
"Try running `zig libc` to see an example for the native target.\n",
libc_paths_file,
@errorName(err),
) catch os.exit(1);
os.exit(1);
};
}
fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
switch (args.len) {
0 => {},
1 => {
var libc_installation: LibCInstallation = undefined;
parseLibcPaths(allocator, &libc_installation, args[0]);
return;
},
else => {
try stderr.print("unexpected extra parameter: {}\n", args[1]);
os.exit(1);
},
}
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
@ -578,20 +610,7 @@ async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
os.exit(1);
};
stderr.print(
\\include_dir={}
\\lib_dir={}
\\static_lib_dir={}
\\msvc_lib_dir={}
\\kernel32_lib_dir={}
\\
,
libc.include_dir,
libc.lib_dir,
libc.static_lib_dir orelse "",
libc.msvc_lib_dir orelse "",
libc.kernel32_lib_dir orelse "",
) catch os.exit(1);
libc.render(stdout) catch os.exit(1);
}
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {

View File

@ -2,6 +2,12 @@ const std = @import("std");
const builtin = @import("builtin");
const llvm = @import("llvm.zig");
pub const FloatAbi = enum {
Hard,
Soft,
SoftFp,
};
pub const Target = union(enum) {
Native,
Cross: Cross,
@ -13,7 +19,7 @@ pub const Target = union(enum) {
object_format: builtin.ObjectFormat,
};
pub fn oFileExt(self: Target) []const u8 {
pub fn objFileExt(self: Target) []const u8 {
return switch (self.getObjectFormat()) {
builtin.ObjectFormat.coff => ".obj",
else => ".o",
@ -27,6 +33,13 @@ pub const Target = union(enum) {
};
}
pub fn libFileExt(self: Target, is_static: bool) []const u8 {
return switch (self.getOs()) {
builtin.Os.windows => if (is_static) ".lib" else ".dll",
else => if (is_static) ".a" else ".so",
};
}
pub fn getOs(self: Target) builtin.Os {
return switch (self) {
Target.Native => builtin.os,
@ -76,6 +89,56 @@ pub const Target = union(enum) {
};
}
/// TODO expose the arch and subarch separately
pub fn isArmOrThumb(self: Target) bool {
return switch (self.getArch()) {
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
=> true,
else => false,
};
}
pub fn initializeAll() void {
llvm.InitializeAllTargets();
llvm.InitializeAllTargetInfos();
@ -106,6 +169,257 @@ pub const Target = union(enum) {
return result;
}
pub fn is64bit(self: Target) bool {
return self.getArchPtrBitWidth() == 64;
}
pub fn getArchPtrBitWidth(self: Target) u8 {
switch (self.getArch()) {
builtin.Arch.avr,
builtin.Arch.msp430,
=> return 16,
builtin.Arch.arc,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.hexagon,
builtin.Arch.le32,
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.nios2,
builtin.Arch.powerpc,
builtin.Arch.r600,
builtin.Arch.riscv32,
builtin.Arch.sparc,
builtin.Arch.sparcel,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
builtin.Arch.i386,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.amdil,
builtin.Arch.hsail,
builtin.Arch.spir,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.renderscript32,
=> return 32,
builtin.Arch.aarch64,
builtin.Arch.aarch64_be,
builtin.Arch.mips64,
builtin.Arch.mips64el,
builtin.Arch.powerpc64,
builtin.Arch.powerpc64le,
builtin.Arch.riscv64,
builtin.Arch.x86_64,
builtin.Arch.nvptx64,
builtin.Arch.le64,
builtin.Arch.amdil64,
builtin.Arch.hsail64,
builtin.Arch.spir64,
builtin.Arch.wasm64,
builtin.Arch.renderscript64,
builtin.Arch.amdgcn,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.sparcv9,
builtin.Arch.s390x,
=> return 64,
}
}
pub fn getFloatAbi(self: Target) FloatAbi {
return switch (self.getEnviron()) {
builtin.Environ.gnueabihf,
builtin.Environ.eabihf,
builtin.Environ.musleabihf,
=> FloatAbi.Hard,
else => FloatAbi.Soft,
};
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
const env = self.getEnviron();
const arch = self.getArch();
switch (env) {
builtin.Environ.android => {
if (self.is64bit()) {
return "/system/bin/linker64";
} else {
return "/system/bin/linker";
}
},
builtin.Environ.gnux32 => {
if (arch == builtin.Arch.x86_64) {
return "/libx32/ld-linux-x32.so.2";
}
},
builtin.Environ.musl,
builtin.Environ.musleabi,
builtin.Environ.musleabihf,
=> {
if (arch == builtin.Arch.x86_64) {
return "/lib/ld-musl-x86_64.so.1";
}
},
else => {},
}
switch (arch) {
builtin.Arch.i386,
builtin.Arch.sparc,
builtin.Arch.sparcel,
=> return "/lib/ld-linux.so.2",
builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.thumb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumbeb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.mips64,
builtin.Arch.mips64el,
=> return null,
builtin.Arch.powerpc => return "/lib/ld.so.1",
builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
builtin.Arch.s390x => return "/lib64/ld64.so.1",
builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
builtin.Arch.arc,
builtin.Arch.avr,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.hexagon,
builtin.Arch.msp430,
builtin.Arch.nios2,
builtin.Arch.r600,
builtin.Arch.amdgcn,
builtin.Arch.riscv32,
builtin.Arch.riscv64,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.nvptx64,
builtin.Arch.le32,
builtin.Arch.le64,
builtin.Arch.amdil,
builtin.Arch.amdil64,
builtin.Arch.hsail,
builtin.Arch.hsail64,
builtin.Arch.spir,
builtin.Arch.spir64,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.wasm64,
builtin.Arch.renderscript32,
builtin.Arch.renderscript64,
=> return null,
}
}
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
var result: llvm.TargetRef = undefined;
var err_msg: [*]u8 = undefined;

View File

@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const assert = std.debug.assert;
/// ReturnType should be `void` or `E!void`
/// ReturnType must be `void` or `E!void`
pub fn Group(comptime ReturnType: type) type {
return struct {
coro_stack: Stack,
@ -39,7 +39,7 @@ pub fn Group(comptime ReturnType: type) type {
}
/// This is equivalent to an async call, but the async function is added to the group, instead
/// of returning a promise. func must be async and have return type void.
/// of returning a promise. func must be async and have return type ReturnType.
/// Thread-safe.
pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
const S = struct {

View File

@ -444,7 +444,7 @@ pub const Loop = struct {
.next = undefined,
.data = p,
};
loop.onNextTick(&my_tick_node);
self.onNextTick(&my_tick_node);
}
}