stage2: building DLL import lib files

master
Andrew Kelley 2020-09-28 19:20:58 -07:00
parent 412a2f966e
commit ada19c498d
7 changed files with 372 additions and 4 deletions

View File

@ -1,7 +1,6 @@
* the have_foo flags that we get from stage1 have to be stored in the cache otherwise we get
a different result for subsystem when we have a cached stage1 execution result.
same deal with extern "foo" libraries used
* add jobs to build import libs for windows DLLs for explicitly linked libs
* add jobs to build import libs for windows DLLs for extern "foo" functions used
* MachO LLD linking
* WASM LLD linking
@ -53,3 +52,4 @@
* make proposal about log levels
* proposal for changing fs Z/W functions to be native paths and have a way to do native path string literals
* proposal for block { break x; }
* generally look for the "TODO surface this as a real compile error message" and fix all that stuff

View File

@ -168,6 +168,9 @@ const Job = union(enum) {
generate_builtin_zig: void,
/// Use stage1 C++ code to compile zig code into an object file.
stage1_module: void,
/// The value is the index into `link.File.Options.system_libs`.
windows_import_lib: usize,
};
pub const CObject = struct {
@ -868,6 +871,15 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
comp.work_queue.writeAssumeCapacity(&static_lib_jobs);
comp.work_queue.writeItemAssumeCapacity(crt_job);
}
// Generate Windows import libs.
if (comp.getTarget().os.tag == .windows) {
const count = comp.bin_file.options.system_libs.count();
try comp.work_queue.ensureUnusedCapacity(count);
var i: usize = 0;
while (i < count) : (i += 1) {
comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
}
}
if (comp.wantBuildLibUnwindFromSource()) {
try comp.work_queue.writeItem(.{ .libunwind = {} });
}
@ -1233,6 +1245,13 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)});
};
},
.windows_import_lib => |index| {
const link_lib = self.bin_file.options.system_libs.items()[index].key;
mingw.buildImportLib(self, link_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)});
};
},
.libunwind => {
libunwind.buildStaticLib(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.

View File

@ -73,5 +73,68 @@ pub const OSType = extern enum(c_int) {
Emscripten = 35,
};
pub const ArchType = extern enum(c_int) {
UnknownArch = 0,
arm = 1,
armeb = 2,
aarch64 = 3,
aarch64_be = 4,
aarch64_32 = 5,
arc = 6,
avr = 7,
bpfel = 8,
bpfeb = 9,
hexagon = 10,
mips = 11,
mipsel = 12,
mips64 = 13,
mips64el = 14,
msp430 = 15,
ppc = 16,
ppc64 = 17,
ppc64le = 18,
r600 = 19,
amdgcn = 20,
riscv32 = 21,
riscv64 = 22,
sparc = 23,
sparcv9 = 24,
sparcel = 25,
systemz = 26,
tce = 27,
tcele = 28,
thumb = 29,
thumbeb = 30,
x86 = 31,
x86_64 = 32,
xcore = 33,
nvptx = 34,
nvptx64 = 35,
le32 = 36,
le64 = 37,
amdil = 38,
amdil64 = 39,
hsail = 40,
hsail64 = 41,
spir = 42,
spir64 = 43,
kalimba = 44,
shave = 45,
lanai = 46,
wasm32 = 47,
wasm64 = 48,
renderscript32 = 49,
renderscript64 = 50,
ve = 51,
};
pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions;
extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void;
pub const WriteImportLibrary = ZigLLVMWriteImportLibrary;
extern fn ZigLLVMWriteImportLibrary(
def_path: [*:0]const u8,
arch: ArchType,
output_lib_path: [*c]const u8,
kill_at: bool,
) bool;

View File

@ -3,10 +3,12 @@ const Allocator = std.mem.Allocator;
const mem = std.mem;
const path = std.fs.path;
const assert = std.debug.assert;
const log = std.log.scoped(.mingw);
const target_util = @import("target.zig");
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
const Cache = @import("Cache.zig");
pub const CRTFile = enum {
crt2_o,
@ -277,6 +279,233 @@ fn add_cc_args(
});
}
pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
defer arena_allocator.deinit();
const arena = &arena_allocator.allocator;
const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {
error.FileNotFound => {
log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
// In this case we will end up putting foo.lib onto the linker line and letting the linker
// use its library paths to look for libraries and report any problems.
return;
},
else => |e| return e,
};
// We need to invoke `zig clang` to use the preprocessor.
if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled;
const target = comp.getTarget();
var cache: Cache = .{
.gpa = comp.gpa,
.manifest_dir = comp.cache_parent.manifest_dir,
};
cache.hash.addBytes(build_options.version);
cache.hash.addOptionalBytes(comp.zig_lib_directory.path);
cache.hash.add(target.cpu.arch);
var man = cache.obtain();
defer man.deinit();
_ = try man.addFile(def_file_path, null);
const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name});
errdefer comp.gpa.free(final_lib_basename);
if (try man.hit()) {
const digest = man.final();
try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
.full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
"o", &digest, final_lib_basename,
}),
.lock = man.toOwnedLock(),
});
return;
}
const digest = man.final();
const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{});
defer o_dir.close();
const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name});
const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{
"o", &digest, final_def_basename,
});
const target_def_arg = switch (target.cpu.arch) {
.i386 => "-DDEF_I386",
.x86_64 => "-DDEF_X64",
.arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
32 => "-DDEF_ARM32",
64 => "-DDEF_ARM64",
else => unreachable,
},
else => unreachable,
};
const args = [_][]const u8{
self_exe_path,
"clang",
"-x",
"c",
def_file_path,
"-Wp,-w",
"-undef",
"-P",
"-I",
try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }),
target_def_arg,
"-E",
"-o",
def_final_path,
};
if (comp.verbose_cc) {
Compilation.dump_argv(&args);
}
const child = try std.ChildProcess.init(&args, arena);
defer child.deinit();
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
const stdout_reader = child.stdout.?.reader();
const stderr_reader = child.stderr.?.reader();
// TODO https://github.com/ziglang/zig/issues/6343
const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
const term = child.wait() catch |err| {
// TODO surface a proper error here
log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) });
return error.ClangPreprocessorFailed;
};
switch (term) {
.Exited => |code| {
if (code != 0) {
// TODO surface a proper error here
log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr });
return error.ClangPreprocessorFailed;
}
},
else => {
// TODO surface a proper error here
log.err("clang terminated unexpectedly with stderr: {}", .{stderr});
return error.ClangPreprocessorFailed;
},
}
const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
"o", &digest, final_lib_basename,
});
errdefer comp.gpa.free(lib_final_path);
const llvm = @import("llvm.zig");
const arch_type = @import("target.zig").archToLLVM(target.cpu.arch);
const def_final_path_z = try arena.dupeZ(u8, def_final_path);
const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
// TODO surface a proper error here
log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
return error.WritingImportLibFailed;
}
man.writeManifest() catch |err| {
log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) });
};
try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{
.full_object_path = lib_final_path,
.lock = man.toOwnedLock(),
});
}
/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 {
const target = comp.getTarget();
const lib_path = switch (target.cpu.arch) {
.i386 => "lib32",
.x86_64 => "lib64",
.arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
32 => "libarm32",
64 => "libarm64",
else => unreachable,
},
else => unreachable,
};
var override_path = std.ArrayList(u8).init(allocator);
defer override_path.deinit();
const s = path.sep_str;
{
// Try the archtecture-specific path first.
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
if (comp.zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
} else {
try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
}
if (std.fs.cwd().access(override_path.items, .{})) |_| {
return override_path.toOwnedSlice();
} else |err| switch (err) {
error.FileNotFound => {},
else => |e| return e,
}
}
{
// Try the generic version.
override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
if (comp.zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else {
try override_path.writer().print(fmt_path, .{lib_name});
}
if (std.fs.cwd().access(override_path.items, .{})) |_| {
return override_path.toOwnedSlice();
} else |err| switch (err) {
error.FileNotFound => {},
else => |e| return e,
}
}
{
// Try the generic version and preprocess it.
override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
if (comp.zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else {
try override_path.writer().print(fmt_path, .{lib_name});
}
if (std.fs.cwd().access(override_path.items, .{})) |_| {
return override_path.toOwnedSlice();
} else |err| switch (err) {
error.FileNotFound => {},
else => |e| return e,
}
}
return error.FileNotFound;
}
const mingw32_lib_deps = [_][]const u8{
"crt0_c.c",
"dll_argv.c",

View File

@ -224,6 +224,63 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
};
}
pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
return switch (arch_tag) {
.arm => .arm,
.armeb => .armeb,
.aarch64 => .aarch64,
.aarch64_be => .aarch64_be,
.aarch64_32 => .aarch64_32,
.arc => .arc,
.avr => .avr,
.bpfel => .bpfel,
.bpfeb => .bpfeb,
.hexagon => .hexagon,
.mips => .mips,
.mipsel => .mipsel,
.mips64 => .mips64,
.mips64el => .mips64el,
.msp430 => .msp430,
.powerpc => .ppc,
.powerpc64 => .ppc64,
.powerpc64le => .ppc64le,
.r600 => .r600,
.amdgcn => .amdgcn,
.riscv32 => .riscv32,
.riscv64 => .riscv64,
.sparc => .sparc,
.sparcv9 => .sparcv9,
.sparcel => .sparcel,
.s390x => .systemz,
.tce => .tce,
.tcele => .tcele,
.thumb => .thumb,
.thumbeb => .thumbeb,
.i386 => .x86,
.x86_64 => .x86_64,
.xcore => .xcore,
.nvptx => .nvptx,
.nvptx64 => .nvptx64,
.le32 => .le32,
.le64 => .le64,
.amdil => .amdil,
.amdil64 => .amdil64,
.hsail => .hsail,
.hsail64 => .hsail64,
.spir => .spir,
.spir64 => .spir64,
.kalimba => .kalimba,
.shave => .shave,
.lanai => .lanai,
.wasm32 => .wasm32,
.wasm64 => .wasm64,
.renderscript32 => .renderscript32,
.renderscript64 => .renderscript64,
.ve => .ve,
.spu_2 => .UnknownArch,
};
}
fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
if (ignore_case) {
return std.ascii.eqlIgnoreCase(a, b);

View File

@ -927,7 +927,7 @@ class MyOStream: public raw_ostream {
};
bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch,
const char *output_lib_path, const bool kill_at)
const char *output_lib_path, bool kill_at)
{
COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;

View File

@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char *
ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
enum ZigLLVM_OSType os_type);
bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,
const char *output_lib_path, const bool kill_at);
ZIG_EXTERN_C bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,
const char *output_lib_path, bool kill_at);
ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type,
enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type,