authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-28 19:20:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-28 19:20:58-07:00
logada19c498d6cb52dd8f0de71d42baf845cfadc21
tree0f1d1d3298d0dc619f9decb43dd381defbeb713d
parent412a2f966e18aa792089ac1f41482222d7f2434f

stage2: building DLL import lib files


7 files changed, 372 insertions(+), 4 deletions(-)

BRANCH_TODO+1-1
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1 * the have_foo flags that we get from stage1 have to be stored in the cache otherwise we get1 * the have_foo flags that we get from stage1 have to be stored in the cache otherwise we get
2 a different result for subsystem when we have a cached stage1 execution result.2 a different result for subsystem when we have a cached stage1 execution result.
3 same deal with extern "foo" libraries used3 same deal with extern "foo" libraries used
4 * add jobs to build import libs for windows DLLs for explicitly linked libs
5 * add jobs to build import libs for windows DLLs for extern "foo" functions used4 * add jobs to build import libs for windows DLLs for extern "foo" functions used
6 * MachO LLD linking5 * MachO LLD linking
7 * WASM LLD linking6 * WASM LLD linking
...@@ -53,3 +52,4 @@...@@ -53,3 +52,4 @@
53 * make proposal about log levels52 * make proposal about log levels
54 * proposal for changing fs Z/W functions to be native paths and have a way to do native path string literals53 * proposal for changing fs Z/W functions to be native paths and have a way to do native path string literals
55 * proposal for block { break x; }54 * proposal for block { break x; }
55 * generally look for the "TODO surface this as a real compile error message" and fix all that stuff
src/Compilation.zig+19
...@@ -168,6 +168,9 @@ const Job = union(enum) {...@@ -168,6 +168,9 @@ const Job = union(enum) {
168 generate_builtin_zig: void,168 generate_builtin_zig: void,
169 /// Use stage1 C++ code to compile zig code into an object file.169 /// Use stage1 C++ code to compile zig code into an object file.
170 stage1_module: void,170 stage1_module: void,
171
172 /// The value is the index into `link.File.Options.system_libs`.
173 windows_import_lib: usize,
171};174};
172175
173pub const CObject = struct {176pub const CObject = struct {
...@@ -868,6 +871,15 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -868,6 +871,15 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
868 comp.work_queue.writeAssumeCapacity(&static_lib_jobs);871 comp.work_queue.writeAssumeCapacity(&static_lib_jobs);
869 comp.work_queue.writeItemAssumeCapacity(crt_job);872 comp.work_queue.writeItemAssumeCapacity(crt_job);
870 }873 }
874 // Generate Windows import libs.
875 if (comp.getTarget().os.tag == .windows) {
876 const count = comp.bin_file.options.system_libs.count();
877 try comp.work_queue.ensureUnusedCapacity(count);
878 var i: usize = 0;
879 while (i < count) : (i += 1) {
880 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
881 }
882 }
871 if (comp.wantBuildLibUnwindFromSource()) {883 if (comp.wantBuildLibUnwindFromSource()) {
872 try comp.work_queue.writeItem(.{ .libunwind = {} });884 try comp.work_queue.writeItem(.{ .libunwind = {} });
873 }885 }
...@@ -1233,6 +1245,13 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {...@@ -1233,6 +1245,13 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
1233 fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)});1245 fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)});
1234 };1246 };
1235 },1247 },
1248 .windows_import_lib => |index| {
1249 const link_lib = self.bin_file.options.system_libs.items()[index].key;
1250 mingw.buildImportLib(self, link_lib) catch |err| {
1251 // TODO Expose this as a normal compile error rather than crashing here.
1252 fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)});
1253 };
1254 },
1236 .libunwind => {1255 .libunwind => {
1237 libunwind.buildStaticLib(self) catch |err| {1256 libunwind.buildStaticLib(self) catch |err| {
1238 // TODO Expose this as a normal compile error rather than crashing here.1257 // TODO Expose this as a normal compile error rather than crashing here.
src/llvm.zig+63
...@@ -73,5 +73,68 @@ pub const OSType = extern enum(c_int) {...@@ -73,5 +73,68 @@ pub const OSType = extern enum(c_int) {
73 Emscripten = 35,73 Emscripten = 35,
74};74};
7575
76pub const ArchType = extern enum(c_int) {
77 UnknownArch = 0,
78 arm = 1,
79 armeb = 2,
80 aarch64 = 3,
81 aarch64_be = 4,
82 aarch64_32 = 5,
83 arc = 6,
84 avr = 7,
85 bpfel = 8,
86 bpfeb = 9,
87 hexagon = 10,
88 mips = 11,
89 mipsel = 12,
90 mips64 = 13,
91 mips64el = 14,
92 msp430 = 15,
93 ppc = 16,
94 ppc64 = 17,
95 ppc64le = 18,
96 r600 = 19,
97 amdgcn = 20,
98 riscv32 = 21,
99 riscv64 = 22,
100 sparc = 23,
101 sparcv9 = 24,
102 sparcel = 25,
103 systemz = 26,
104 tce = 27,
105 tcele = 28,
106 thumb = 29,
107 thumbeb = 30,
108 x86 = 31,
109 x86_64 = 32,
110 xcore = 33,
111 nvptx = 34,
112 nvptx64 = 35,
113 le32 = 36,
114 le64 = 37,
115 amdil = 38,
116 amdil64 = 39,
117 hsail = 40,
118 hsail64 = 41,
119 spir = 42,
120 spir64 = 43,
121 kalimba = 44,
122 shave = 45,
123 lanai = 46,
124 wasm32 = 47,
125 wasm64 = 48,
126 renderscript32 = 49,
127 renderscript64 = 50,
128 ve = 51,
129};
130
76pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions;131pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions;
77extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void;132extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void;
133
134pub const WriteImportLibrary = ZigLLVMWriteImportLibrary;
135extern fn ZigLLVMWriteImportLibrary(
136 def_path: [*:0]const u8,
137 arch: ArchType,
138 output_lib_path: [*c]const u8,
139 kill_at: bool,
140) bool;
src/mingw.zig+229
...@@ -3,10 +3,12 @@ const Allocator = std.mem.Allocator;...@@ -3,10 +3,12 @@ const Allocator = std.mem.Allocator;
3const mem = std.mem;3const mem = std.mem;
4const path = std.fs.path;4const path = std.fs.path;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const log = std.log.scoped(.mingw);
67
7const target_util = @import("target.zig");8const target_util = @import("target.zig");
8const Compilation = @import("Compilation.zig");9const Compilation = @import("Compilation.zig");
9const build_options = @import("build_options");10const build_options = @import("build_options");
11const Cache = @import("Cache.zig");
1012
11pub const CRTFile = enum {13pub const CRTFile = enum {
12 crt2_o,14 crt2_o,
...@@ -277,6 +279,233 @@ fn add_cc_args(...@@ -277,6 +279,233 @@ fn add_cc_args(
277 });279 });
278}280}
279281
282pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
283 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
284 defer arena_allocator.deinit();
285 const arena = &arena_allocator.allocator;
286
287 const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {
288 error.FileNotFound => {
289 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
290 // In this case we will end up putting foo.lib onto the linker line and letting the linker
291 // use its library paths to look for libraries and report any problems.
292 return;
293 },
294 else => |e| return e,
295 };
296
297 // We need to invoke `zig clang` to use the preprocessor.
298 if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
299 const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled;
300
301 const target = comp.getTarget();
302
303 var cache: Cache = .{
304 .gpa = comp.gpa,
305 .manifest_dir = comp.cache_parent.manifest_dir,
306 };
307 cache.hash.addBytes(build_options.version);
308 cache.hash.addOptionalBytes(comp.zig_lib_directory.path);
309 cache.hash.add(target.cpu.arch);
310
311 var man = cache.obtain();
312 defer man.deinit();
313
314 _ = try man.addFile(def_file_path, null);
315
316 const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name});
317 errdefer comp.gpa.free(final_lib_basename);
318
319 if (try man.hit()) {
320 const digest = man.final();
321
322 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
323 comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
324 .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
325 "o", &digest, final_lib_basename,
326 }),
327 .lock = man.toOwnedLock(),
328 });
329 return;
330 }
331
332 const digest = man.final();
333 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
334 var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{});
335 defer o_dir.close();
336
337 const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name});
338 const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{
339 "o", &digest, final_def_basename,
340 });
341
342 const target_def_arg = switch (target.cpu.arch) {
343 .i386 => "-DDEF_I386",
344 .x86_64 => "-DDEF_X64",
345 .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
346 32 => "-DDEF_ARM32",
347 64 => "-DDEF_ARM64",
348 else => unreachable,
349 },
350 else => unreachable,
351 };
352
353 const args = [_][]const u8{
354 self_exe_path,
355 "clang",
356 "-x",
357 "c",
358 def_file_path,
359 "-Wp,-w",
360 "-undef",
361 "-P",
362 "-I",
363 try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }),
364 target_def_arg,
365 "-E",
366 "-o",
367 def_final_path,
368 };
369
370 if (comp.verbose_cc) {
371 Compilation.dump_argv(&args);
372 }
373
374 const child = try std.ChildProcess.init(&args, arena);
375 defer child.deinit();
376
377 child.stdin_behavior = .Ignore;
378 child.stdout_behavior = .Pipe;
379 child.stderr_behavior = .Pipe;
380
381 try child.spawn();
382
383 const stdout_reader = child.stdout.?.reader();
384 const stderr_reader = child.stderr.?.reader();
385
386 // TODO https://github.com/ziglang/zig/issues/6343
387 const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
388 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
389
390 const term = child.wait() catch |err| {
391 // TODO surface a proper error here
392 log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) });
393 return error.ClangPreprocessorFailed;
394 };
395
396 switch (term) {
397 .Exited => |code| {
398 if (code != 0) {
399 // TODO surface a proper error here
400 log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr });
401 return error.ClangPreprocessorFailed;
402 }
403 },
404 else => {
405 // TODO surface a proper error here
406 log.err("clang terminated unexpectedly with stderr: {}", .{stderr});
407 return error.ClangPreprocessorFailed;
408 },
409 }
410
411 const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
412 "o", &digest, final_lib_basename,
413 });
414 errdefer comp.gpa.free(lib_final_path);
415
416 const llvm = @import("llvm.zig");
417 const arch_type = @import("target.zig").archToLLVM(target.cpu.arch);
418 const def_final_path_z = try arena.dupeZ(u8, def_final_path);
419 const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
420 if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
421 // TODO surface a proper error here
422 log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
423 return error.WritingImportLibFailed;
424 }
425
426 man.writeManifest() catch |err| {
427 log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) });
428 };
429
430 try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{
431 .full_object_path = lib_final_path,
432 .lock = man.toOwnedLock(),
433 });
434}
435
436/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
437fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 {
438 const target = comp.getTarget();
439
440 const lib_path = switch (target.cpu.arch) {
441 .i386 => "lib32",
442 .x86_64 => "lib64",
443 .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
444 32 => "libarm32",
445 64 => "libarm64",
446 else => unreachable,
447 },
448 else => unreachable,
449 };
450
451 var override_path = std.ArrayList(u8).init(allocator);
452 defer override_path.deinit();
453
454 const s = path.sep_str;
455
456 {
457 // Try the archtecture-specific path first.
458 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
459 if (comp.zig_lib_directory.path) |p| {
460 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
461 } else {
462 try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
463 }
464 if (std.fs.cwd().access(override_path.items, .{})) |_| {
465 return override_path.toOwnedSlice();
466 } else |err| switch (err) {
467 error.FileNotFound => {},
468 else => |e| return e,
469 }
470 }
471
472 {
473 // Try the generic version.
474 override_path.shrinkRetainingCapacity(0);
475 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
476 if (comp.zig_lib_directory.path) |p| {
477 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
478 } else {
479 try override_path.writer().print(fmt_path, .{lib_name});
480 }
481 if (std.fs.cwd().access(override_path.items, .{})) |_| {
482 return override_path.toOwnedSlice();
483 } else |err| switch (err) {
484 error.FileNotFound => {},
485 else => |e| return e,
486 }
487 }
488
489 {
490 // Try the generic version and preprocess it.
491 override_path.shrinkRetainingCapacity(0);
492 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
493 if (comp.zig_lib_directory.path) |p| {
494 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
495 } else {
496 try override_path.writer().print(fmt_path, .{lib_name});
497 }
498 if (std.fs.cwd().access(override_path.items, .{})) |_| {
499 return override_path.toOwnedSlice();
500 } else |err| switch (err) {
501 error.FileNotFound => {},
502 else => |e| return e,
503 }
504 }
505
506 return error.FileNotFound;
507}
508
280const mingw32_lib_deps = [_][]const u8{509const mingw32_lib_deps = [_][]const u8{
281 "crt0_c.c",510 "crt0_c.c",
282 "dll_argv.c",511 "dll_argv.c",
src/target.zig+57
...@@ -224,6 +224,63 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {...@@ -224,6 +224,63 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
224 };224 };
225}225}
226226
227pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
228 return switch (arch_tag) {
229 .arm => .arm,
230 .armeb => .armeb,
231 .aarch64 => .aarch64,
232 .aarch64_be => .aarch64_be,
233 .aarch64_32 => .aarch64_32,
234 .arc => .arc,
235 .avr => .avr,
236 .bpfel => .bpfel,
237 .bpfeb => .bpfeb,
238 .hexagon => .hexagon,
239 .mips => .mips,
240 .mipsel => .mipsel,
241 .mips64 => .mips64,
242 .mips64el => .mips64el,
243 .msp430 => .msp430,
244 .powerpc => .ppc,
245 .powerpc64 => .ppc64,
246 .powerpc64le => .ppc64le,
247 .r600 => .r600,
248 .amdgcn => .amdgcn,
249 .riscv32 => .riscv32,
250 .riscv64 => .riscv64,
251 .sparc => .sparc,
252 .sparcv9 => .sparcv9,
253 .sparcel => .sparcel,
254 .s390x => .systemz,
255 .tce => .tce,
256 .tcele => .tcele,
257 .thumb => .thumb,
258 .thumbeb => .thumbeb,
259 .i386 => .x86,
260 .x86_64 => .x86_64,
261 .xcore => .xcore,
262 .nvptx => .nvptx,
263 .nvptx64 => .nvptx64,
264 .le32 => .le32,
265 .le64 => .le64,
266 .amdil => .amdil,
267 .amdil64 => .amdil64,
268 .hsail => .hsail,
269 .hsail64 => .hsail64,
270 .spir => .spir,
271 .spir64 => .spir64,
272 .kalimba => .kalimba,
273 .shave => .shave,
274 .lanai => .lanai,
275 .wasm32 => .wasm32,
276 .wasm64 => .wasm64,
277 .renderscript32 => .renderscript32,
278 .renderscript64 => .renderscript64,
279 .ve => .ve,
280 .spu_2 => .UnknownArch,
281 };
282}
283
227fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {284fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
228 if (ignore_case) {285 if (ignore_case) {
229 return std.ascii.eqlIgnoreCase(a, b);286 return std.ascii.eqlIgnoreCase(a, b);
src/zig_llvm.cpp+1-1
...@@ -927,7 +927,7 @@ class MyOStream: public raw_ostream {...@@ -927,7 +927,7 @@ class MyOStream: public raw_ostream {
927};927};
928928
929bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch,929bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch,
930 const char *output_lib_path, const bool kill_at)930 const char *output_lib_path, bool kill_at)
931{931{
932 COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;932 COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
933933
src/zig_llvm.h+2-2
...@@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char *...@@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char *
500ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,500ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
501 enum ZigLLVM_OSType os_type);501 enum ZigLLVM_OSType os_type);
502502
503bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,503ZIG_EXTERN_C bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,
504 const char *output_lib_path, const bool kill_at);504 const char *output_lib_path, bool kill_at);
505505
506ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type,506ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type,
507 enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type,507 enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type,