From aa3b41247f297b4fd8b3bdb7920cb479f5aa004b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 00:34:42 -0400 Subject: [PATCH] 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. --- src-self-hosted/codegen.zig | 2 +- src-self-hosted/compilation.zig | 51 +++-- src-self-hosted/libc_installation.zig | 222 +++++++++++++++--- src-self-hosted/link.zig | 193 +++++++++------- src-self-hosted/main.zig | 87 ++++--- src-self-hosted/target.zig | 316 +++++++++++++++++++++++++- std/event/group.zig | 4 +- std/event/loop.zig | 2 +- 8 files changed, 707 insertions(+), 170 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index f8233bc795..8faef5f31c 100644 --- a/src-self-hosted/codegen.zig +++ b/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(); diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1eb4339bbb..57809c8e4c 100644 --- a/src-self-hosted/compilation.zig +++ b/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 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.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. diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 5606a467e9..8444c47310 100644 --- a/src-self-hosted/libc_installation.zig +++ b/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) 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); + } } diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 915451c931..16d9939fff 100644 --- a/src-self-hosted/link.zig +++ b/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)); + 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 { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index ff24677b6d..5c27e5f57e 100644 --- a/src-self-hosted/main.zig +++ b/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 { diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index db673e421a..5a1f43bcf9 100644 --- a/src-self-hosted/target.zig +++ b/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; diff --git a/std/event/group.zig b/std/event/group.zig index c286803b53..5ca8f5a0ca 100644 --- a/std/event/group.zig +++ b/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 { diff --git a/std/event/loop.zig b/std/event/loop.zig index 485a5be19c..cd805f891f 100644 --- a/std/event/loop.zig +++ b/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); } }