stage2: implement writing archive files
parent
40cb712d13
commit
26798018b7
|
@ -535,6 +535,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
|||
var hash = cache.hash;
|
||||
if (options.c_source_files.len >= 1) {
|
||||
hash.addBytes(options.c_source_files[0].src_path);
|
||||
} else if (options.link_objects.len >= 1) {
|
||||
hash.addBytes(options.link_objects[0]);
|
||||
}
|
||||
|
||||
const digest = hash.final();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const Module = @import("Module.zig");
|
||||
|
@ -9,6 +10,7 @@ const Type = @import("type.zig").Type;
|
|||
const Cache = @import("Cache.zig");
|
||||
const build_options = @import("build_options");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const log = std.log.scoped(.link);
|
||||
|
||||
pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
|
||||
|
||||
|
@ -279,9 +281,11 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Commit pending changes and write headers. Takes into account final output mode
|
||||
/// and `use_lld`, not only `effectiveOutputMode`.
|
||||
pub fn flush(base: *File, comp: *Compilation) !void {
|
||||
const use_lld = build_options.have_llvm and base.options.use_lld;
|
||||
if (base.options.output_mode == .Lib and base.options.link_mode == .Static and
|
||||
if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static and
|
||||
!base.options.target.isWasm())
|
||||
{
|
||||
return base.linkAsArchive(comp);
|
||||
|
@ -295,6 +299,18 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Commit pending changes and write headers. Works based on `effectiveOutputMode`
|
||||
/// rather than final output mode.
|
||||
pub fn flushModule(base: *File, comp: *Compilation) !void {
|
||||
switch (base.tag) {
|
||||
.coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp),
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
|
||||
.c => return @fieldParentPtr(C, "base", base).flushModule(comp),
|
||||
.wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freeDecl(base: *File, decl: *Module.Decl) void {
|
||||
switch (base.tag) {
|
||||
.coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
|
||||
|
@ -343,9 +359,108 @@ pub const File = struct {
|
|||
}
|
||||
|
||||
fn linkAsArchive(base: *File, comp: *Compilation) !void {
|
||||
// TODO follow pattern from ELF linkWithLLD
|
||||
// ZigLLVMWriteArchive
|
||||
return error.TODOMakeArchive;
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(base.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
const directory = base.options.directory; // Just an alias to make it shorter to type.
|
||||
|
||||
// If there is no Zig code to compile, then we should skip flushing the output file because it
|
||||
// will not be part of the linker line anyway.
|
||||
const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: {
|
||||
try base.flushModule(comp);
|
||||
|
||||
const obj_basename = base.intermediary_basename.?;
|
||||
const full_obj_path = if (directory.path) |dir_path|
|
||||
try std.fs.path.join(arena, &[_][]const u8{ dir_path, obj_basename })
|
||||
else
|
||||
obj_basename;
|
||||
break :blk full_obj_path;
|
||||
} else null;
|
||||
|
||||
// This function follows the same pattern as link.Elf.linkWithLLD so if you want some
|
||||
// insight as to what's going on here you can read that function body which is more
|
||||
// well-commented.
|
||||
|
||||
const id_symlink_basename = "llvm-ar.id";
|
||||
|
||||
base.releaseLock();
|
||||
|
||||
var ch = comp.cache_parent.obtain();
|
||||
defer ch.deinit();
|
||||
|
||||
try ch.addListOfFiles(base.options.objects);
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
_ = try ch.addFile(entry.key.status.success.object_path, null);
|
||||
}
|
||||
try ch.addOptionalFile(module_obj_path);
|
||||
|
||||
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
|
||||
_ = try ch.hit();
|
||||
const digest = ch.final();
|
||||
|
||||
var prev_digest_buf: [digest.len]u8 = undefined;
|
||||
const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: {
|
||||
log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) });
|
||||
break :b prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("archive digest={} match - skipping invocation", .{digest});
|
||||
base.lock = ch.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
error.FileNotFound => {},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
|
||||
defer object_files.deinit();
|
||||
|
||||
try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 1);
|
||||
for (base.options.objects) |obj_path| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));
|
||||
}
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, entry.key.status.success.object_path));
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
|
||||
}
|
||||
|
||||
const full_out_path = if (directory.path) |dir_path|
|
||||
try std.fs.path.join(arena, &[_][]const u8{ dir_path, base.options.sub_path })
|
||||
else
|
||||
base.options.sub_path;
|
||||
const full_out_path_z = try arena.dupeZ(u8, full_out_path);
|
||||
|
||||
if (base.options.debug_link) {
|
||||
std.debug.print("ar rcs {}", .{full_out_path_z});
|
||||
for (object_files.items) |arg| {
|
||||
std.debug.print(" {}", .{arg});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
}
|
||||
|
||||
const llvm = @import("llvm.zig");
|
||||
const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
|
||||
const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
|
||||
if (bad) return error.UnableToWriteArchive;
|
||||
|
||||
directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
|
||||
std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)});
|
||||
};
|
||||
|
||||
ch.writeManifest() catch |err| {
|
||||
std.log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)});
|
||||
};
|
||||
|
||||
base.lock = ch.toOwnedLock();
|
||||
}
|
||||
|
||||
pub const Tag = enum {
|
||||
|
|
|
@ -74,6 +74,10 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
|
|||
}
|
||||
|
||||
pub fn flush(self: *C, comp: *Compilation) !void {
|
||||
return self.flushModule(comp);
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *C, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ const Module = @import("../Module.zig");
|
|||
const Compilation = @import("../Compilation.zig");
|
||||
const codegen = @import("../codegen.zig");
|
||||
const link = @import("../link.zig");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
const allocation_padding = 4 / 3;
|
||||
const minimum_text_block_size = 64 * allocation_padding;
|
||||
|
@ -724,6 +725,14 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
|
|||
}
|
||||
|
||||
pub fn flush(self: *Coff, comp: *Compilation) !void {
|
||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||
return error.CoffLinkingWithLLDUnimplemented;
|
||||
} else {
|
||||
return self.flushModule(comp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *Coff, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
|
|
@ -719,12 +719,11 @@ pub fn flush(self: *Elf, comp: *Compilation) !void {
|
|||
.Exe, .Obj => {},
|
||||
.Lib => return error.TODOImplementWritingLibFiles,
|
||||
}
|
||||
return self.flushInner(comp);
|
||||
return self.flushModule(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit pending changes and write headers.
|
||||
fn flushInner(self: *Elf, comp: *Compilation) !void {
|
||||
pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
@ -1221,7 +1220,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
|||
// If there is no Zig code to compile, then we should skip flushing the output file because it
|
||||
// will not be part of the linker line anyway.
|
||||
const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
|
||||
try self.flushInner(comp);
|
||||
try self.flushModule(comp);
|
||||
|
||||
const obj_basename = self.base.intermediary_basename.?;
|
||||
const full_obj_path = if (directory.path) |dir_path|
|
||||
|
@ -1239,7 +1238,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
|||
// After a successful link, we store the id in the metadata of a symlink named "id.txt" in
|
||||
// the artifact directory. So, now, we check if this symlink exists, and if it matches
|
||||
// our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
|
||||
const id_symlink_basename = "id.txt";
|
||||
const id_symlink_basename = "lld.id";
|
||||
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
|
|
@ -9,9 +9,10 @@ const macho = std.macho;
|
|||
const codegen = @import("../codegen.zig");
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const Type = @import("../type.zig").Type;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
const Module = @import("../Module.zig");
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const link = @import("../link.zig");
|
||||
|
@ -178,6 +179,14 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
|
|||
}
|
||||
|
||||
pub fn flush(self: *MachO, comp: *Compilation) !void {
|
||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||
return error.MachOLLDLinkingUnimplemented;
|
||||
} else {
|
||||
return self.flushModule(comp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *MachO, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ const Compilation = @import("../Compilation.zig");
|
|||
const codegen = @import("../codegen/wasm.zig");
|
||||
const link = @import("../link.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const build_options = @import("build_options");
|
||||
|
||||
/// Various magic numbers defined by the wasm spec
|
||||
const spec = struct {
|
||||
|
@ -135,6 +136,14 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
|
|||
}
|
||||
|
||||
pub fn flush(self: *Wasm, comp: *Compilation) !void {
|
||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||
return error.WasmLinkingWithLLDUnimplemented;
|
||||
} else {
|
||||
return self.flushModule(comp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! to bootstrap if it does not depend on translate-c.
|
||||
|
||||
pub const Link = ZigLLDLink;
|
||||
pub extern fn ZigLLDLink(
|
||||
extern fn ZigLLDLink(
|
||||
oformat: ObjectFormatType,
|
||||
args: [*:null]const ?[*:0]const u8,
|
||||
arg_count: usize,
|
||||
|
@ -25,3 +25,50 @@ extern fn LLVMGetHostCPUName() ?[*:0]u8;
|
|||
|
||||
pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
|
||||
extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
|
||||
|
||||
pub const WriteArchive = ZigLLVMWriteArchive;
|
||||
extern fn ZigLLVMWriteArchive(
|
||||
archive_name: [*:0]const u8,
|
||||
file_names_ptr: [*]const [*:0]const u8,
|
||||
file_names_len: usize,
|
||||
os_type: OSType,
|
||||
) bool;
|
||||
|
||||
pub const OSType = extern enum(c_int) {
|
||||
UnknownOS = 0,
|
||||
Ananas = 1,
|
||||
CloudABI = 2,
|
||||
Darwin = 3,
|
||||
DragonFly = 4,
|
||||
FreeBSD = 5,
|
||||
Fuchsia = 6,
|
||||
IOS = 7,
|
||||
KFreeBSD = 8,
|
||||
Linux = 9,
|
||||
Lv2 = 10,
|
||||
MacOSX = 11,
|
||||
NetBSD = 12,
|
||||
OpenBSD = 13,
|
||||
Solaris = 14,
|
||||
Win32 = 15,
|
||||
Haiku = 16,
|
||||
Minix = 17,
|
||||
RTEMS = 18,
|
||||
NaCl = 19,
|
||||
CNK = 20,
|
||||
AIX = 21,
|
||||
CUDA = 22,
|
||||
NVCL = 23,
|
||||
AMDHSA = 24,
|
||||
PS4 = 25,
|
||||
ELFIAMCU = 26,
|
||||
TvOS = 27,
|
||||
WatchOS = 28,
|
||||
Mesa3D = 29,
|
||||
Contiki = 30,
|
||||
AMDPAL = 31,
|
||||
HermitCore = 32,
|
||||
Hurd = 33,
|
||||
WASI = 34,
|
||||
Emscripten = 35,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const llvm = @import("llvm.zig");
|
||||
|
||||
pub const ArchOsAbi = struct {
|
||||
arch: std.Target.Cpu.Arch,
|
||||
|
@ -168,3 +169,43 @@ pub fn supportsStackProbing(target: std.Target) bool {
|
|||
return target.os.tag != .windows and target.os.tag != .uefi and
|
||||
(target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
|
||||
}
|
||||
|
||||
pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
|
||||
return switch (os_tag) {
|
||||
.freestanding, .other => .UnknownOS,
|
||||
.windows, .uefi => .Win32,
|
||||
.ananas => .Ananas,
|
||||
.cloudabi => .CloudABI,
|
||||
.dragonfly => .DragonFly,
|
||||
.freebsd => .FreeBSD,
|
||||
.fuchsia => .Fuchsia,
|
||||
.ios => .IOS,
|
||||
.kfreebsd => .KFreeBSD,
|
||||
.linux => .Linux,
|
||||
.lv2 => .Lv2,
|
||||
.macosx => .MacOSX,
|
||||
.netbsd => .NetBSD,
|
||||
.openbsd => .OpenBSD,
|
||||
.solaris => .Solaris,
|
||||
.haiku => .Haiku,
|
||||
.minix => .Minix,
|
||||
.rtems => .RTEMS,
|
||||
.nacl => .NaCl,
|
||||
.cnk => .CNK,
|
||||
.aix => .AIX,
|
||||
.cuda => .CUDA,
|
||||
.nvcl => .NVCL,
|
||||
.amdhsa => .AMDHSA,
|
||||
.ps4 => .PS4,
|
||||
.elfiamcu => .ELFIAMCU,
|
||||
.tvos => .TvOS,
|
||||
.watchos => .WatchOS,
|
||||
.mesa3d => .Mesa3D,
|
||||
.contiki => .Contiki,
|
||||
.amdpal => .AMDPAL,
|
||||
.hermit => .HermitCore,
|
||||
.hurd => .Hurd,
|
||||
.wasi => .WASI,
|
||||
.emscripten => .Emscripten,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue