Commit ada19c498d
Changed files (7)
src/Compilation.zig
@@ -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.
src/llvm.zig
@@ -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;
src/mingw.zig
@@ -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",
src/target.zig
@@ -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);
src/zig_llvm.cpp
@@ -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;
src/zig_llvm.h
@@ -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,
BRANCH_TODO
@@ -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