Commit aa3b41247f
Changed files (8)
src-self-hosted/codegen.zig
@@ -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();
src-self-hosted/compilation.zig
@@ -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.
src-self-hosted/libc_installation.zig
@@ -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) FindError!void {
+ self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
+ }
+
+ 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;
+ }
+ }
}
- async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
- self.static_lib_dir = try await (async ccPrintFileNameDir(loop, "crtbegin.o") catch unreachable);
+ 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) Error!void {
+ 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);
+ }
}
src-self-hosted/link.zig
@@ -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));
-
- // lj->args.append("-L");
- // lj->args.append(buf_ptr(g->libc_static_lib_dir));
- //}
-
- //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.haveLibC()) {
+ try ctx.args.append(c"-L");
+ try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
+
+ try ctx.args.append(c"-L");
+ try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
+
+ 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 {
src-self-hosted/main.zig
@@ -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 {
src-self-hosted/target.zig
@@ -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;
std/event/group.zig
@@ -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 {
std/event/loop.zig
@@ -444,7 +444,7 @@ pub const Loop = struct {
.next = undefined,
.data = p,
};
- loop.onNextTick(&my_tick_node);
+ self.onNextTick(&my_tick_node);
}
}