authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-24 18:08:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-24 18:12:56-07:00
log27c5c7fb23fceb0a333444408a1dea4188a14c32
tree5c995301673743c3398940c0ebed49647e0e3119
parent7e23b3245a9bf6e002009e6c18c10a9995671afa

stage2: proper `-femit-implib` frontend support

* Improve the logic for determining whether emitting an import lib is eligible, and improve the error message when the user provides contradictory arguments. * Integrate with the EmitLoc / Emit system that already exists, and use the `-femit-implib[=path]`/`-fno-emit-implib` convention that already exists. * Proper integration with the caching system. * CLI: fix bug in error reporting for resolving EmitLoc values for other parameters.

4 files changed, 117 insertions(+), 50 deletions(-)

src/Compilation.zig+26-4
...@@ -654,6 +654,8 @@ pub const InitOptions = struct {...@@ -654,6 +654,8 @@ pub const InitOptions = struct {
654 emit_analysis: ?EmitLoc = null,654 emit_analysis: ?EmitLoc = null,
655 /// `null` means to not emit docs.655 /// `null` means to not emit docs.
656 emit_docs: ?EmitLoc = null,656 emit_docs: ?EmitLoc = null,
657 /// `null` means to not emit an import lib.
658 emit_implib: ?EmitLoc = null,
657 link_mode: ?std.builtin.LinkMode = null,659 link_mode: ?std.builtin.LinkMode = null,
658 dll_export_fns: ?bool = false,660 dll_export_fns: ?bool = false,
659 /// Normally when using LLD to link, Zig uses a file named "lld.id" in the661 /// Normally when using LLD to link, Zig uses a file named "lld.id" in the
...@@ -766,8 +768,6 @@ pub const InitOptions = struct {...@@ -766,8 +768,6 @@ pub const InitOptions = struct {
766 test_filter: ?[]const u8 = null,768 test_filter: ?[]const u8 = null,
767 test_name_prefix: ?[]const u8 = null,769 test_name_prefix: ?[]const u8 = null,
768 subsystem: ?std.Target.SubSystem = null,770 subsystem: ?std.Target.SubSystem = null,
769 /// Windows/PE only. Where to output the import library, can contain directories.
770 out_implib: ?[]const u8 = null,
771 /// WASI-only. Type of WASI execution model ("command" or "reactor").771 /// WASI-only. Type of WASI execution model ("command" or "reactor").
772 wasi_exec_model: ?std.builtin.WasiExecModel = null,772 wasi_exec_model: ?std.builtin.WasiExecModel = null,
773 /// (Zig compiler development) Enable dumping linker's state as JSON.773 /// (Zig compiler development) Enable dumping linker's state as JSON.
...@@ -947,7 +947,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -947,7 +947,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
947 options.output_mode == .Lib or947 options.output_mode == .Lib or
948 options.image_base_override != null or948 options.image_base_override != null or
949 options.linker_script != null or options.version_script != null or949 options.linker_script != null or options.version_script != null or
950 options.out_implib != null)950 options.emit_implib != null)
951 {951 {
952 break :blk true;952 break :blk true;
953 }953 }
...@@ -1183,6 +1183,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1183,6 +1183,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1183 cache.hash.add(options.output_mode);1183 cache.hash.add(options.output_mode);
1184 cache.hash.add(options.machine_code_model);1184 cache.hash.add(options.machine_code_model);
1185 cache.hash.addOptionalEmitLoc(options.emit_bin);1185 cache.hash.addOptionalEmitLoc(options.emit_bin);
1186 cache.hash.addOptionalEmitLoc(options.emit_implib);
1186 cache.hash.addBytes(options.root_name);1187 cache.hash.addBytes(options.root_name);
1187 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);1188 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
1188 // TODO audit this and make sure everything is in it1189 // TODO audit this and make sure everything is in it
...@@ -1339,18 +1340,21 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1339,18 +1340,21 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13391340
1340 const bin_file_emit: ?link.Emit = blk: {1341 const bin_file_emit: ?link.Emit = blk: {
1341 const emit_bin = options.emit_bin orelse break :blk null;1342 const emit_bin = options.emit_bin orelse break :blk null;
1343
1342 if (emit_bin.directory) |directory| {1344 if (emit_bin.directory) |directory| {
1343 break :blk link.Emit{1345 break :blk link.Emit{
1344 .directory = directory,1346 .directory = directory,
1345 .sub_path = emit_bin.basename,1347 .sub_path = emit_bin.basename,
1346 };1348 };
1347 }1349 }
1350
1348 if (module) |zm| {1351 if (module) |zm| {
1349 break :blk link.Emit{1352 break :blk link.Emit{
1350 .directory = zm.zig_cache_artifact_directory,1353 .directory = zm.zig_cache_artifact_directory,
1351 .sub_path = emit_bin.basename,1354 .sub_path = emit_bin.basename,
1352 };1355 };
1353 }1356 }
1357
1354 // We could use the cache hash as is no problem, however, we increase1358 // We could use the cache hash as is no problem, however, we increase
1355 // the likelihood of cache hits by adding the first C source file1359 // the likelihood of cache hits by adding the first C source file
1356 // path name (not contents) to the hash. This way if the user is compiling1360 // path name (not contents) to the hash. This way if the user is compiling
...@@ -1377,6 +1381,24 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1377,6 +1381,24 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1377 };1381 };
1378 };1382 };
13791383
1384 const implib_emit: ?link.Emit = blk: {
1385 const emit_implib = options.emit_implib orelse break :blk null;
1386
1387 if (emit_implib.directory) |directory| {
1388 break :blk link.Emit{
1389 .directory = directory,
1390 .sub_path = emit_implib.basename,
1391 };
1392 }
1393
1394 // Use the same directory as the bin. The CLI already emits an
1395 // error if -fno-emit-bin is combined with -femit-implib.
1396 break :blk link.Emit{
1397 .directory = bin_file_emit.?.directory,
1398 .sub_path = emit_implib.basename,
1399 };
1400 };
1401
1380 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{};1402 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{};
1381 errdefer system_libs.deinit(gpa);1403 errdefer system_libs.deinit(gpa);
1382 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len);1404 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len);
...@@ -1386,6 +1408,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1386,6 +1408,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13861408
1387 const bin_file = try link.File.openPath(gpa, .{1409 const bin_file = try link.File.openPath(gpa, .{
1388 .emit = bin_file_emit,1410 .emit = bin_file_emit,
1411 .implib_emit = implib_emit,
1389 .root_name = root_name,1412 .root_name = root_name,
1390 .module = module,1413 .module = module,
1391 .target = options.target,1414 .target = options.target,
...@@ -1461,7 +1484,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1461,7 +1484,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1461 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,1484 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
1462 .disable_lld_caching = options.disable_lld_caching,1485 .disable_lld_caching = options.disable_lld_caching,
1463 .subsystem = options.subsystem,1486 .subsystem = options.subsystem,
1464 .out_implib = options.out_implib,
1465 .is_test = options.is_test,1487 .is_test = options.is_test,
1466 .wasi_exec_model = wasi_exec_model,1488 .wasi_exec_model = wasi_exec_model,
1467 .use_stage1 = use_stage1,1489 .use_stage1 = use_stage1,
src/link.zig+2-1
...@@ -47,6 +47,8 @@ pub const Options = struct {...@@ -47,6 +47,8 @@ pub const Options = struct {
47 /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,47 /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,
48 /// it will have already been null-checked.48 /// it will have already been null-checked.
49 emit: ?Emit,49 emit: ?Emit,
50 /// This is `null` not building a Windows DLL, or when -fno-emit-implib is used.
51 implib_emit: ?Emit,
50 target: std.Target,52 target: std.Target,
51 output_mode: std.builtin.OutputMode,53 output_mode: std.builtin.OutputMode,
52 link_mode: std.builtin.LinkMode,54 link_mode: std.builtin.LinkMode,
...@@ -127,7 +129,6 @@ pub const Options = struct {...@@ -127,7 +129,6 @@ pub const Options = struct {
127 gc_sections: ?bool = null,129 gc_sections: ?bool = null,
128 allow_shlib_undefined: ?bool,130 allow_shlib_undefined: ?bool,
129 subsystem: ?std.Target.SubSystem,131 subsystem: ?std.Target.SubSystem,
130 out_implib: ?[]const u8,
131 linker_script: ?[]const u8,132 linker_script: ?[]const u8,
132 version_script: ?[]const u8,133 version_script: ?[]const u8,
133 soname: ?[]const u8,134 soname: ?[]const u8,
src/link/Coff.zig+5-6
...@@ -947,7 +947,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -947,7 +947,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
947 man.hash.add(self.base.options.dynamicbase);947 man.hash.add(self.base.options.dynamicbase);
948 man.hash.addOptional(self.base.options.major_subsystem_version);948 man.hash.addOptional(self.base.options.major_subsystem_version);
949 man.hash.addOptional(self.base.options.minor_subsystem_version);949 man.hash.addOptional(self.base.options.minor_subsystem_version);
950 man.hash.addOptionalBytes(self.base.options.out_implib);
951950
952 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.951 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
953 _ = try man.hit();952 _ = try man.hit();
...@@ -978,7 +977,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -978,7 +977,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
978 }977 }
979978
980 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});979 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
981
982 if (self.base.options.output_mode == .Obj) {980 if (self.base.options.output_mode == .Obj) {
983 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy981 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
984 // here. TODO: think carefully about how we can avoid this redundant operation when doing982 // here. TODO: think carefully about how we can avoid this redundant operation when doing
...@@ -1070,6 +1068,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1070,6 +1068,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
10701068
1071 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));1069 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
10721070
1071 if (self.base.options.implib_emit) |emit| {
1072 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
1073 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
1074 }
1075
1073 if (self.base.options.link_libc) {1076 if (self.base.options.link_libc) {
1074 if (self.base.options.libc_installation) |libc_installation| {1077 if (self.base.options.libc_installation) |libc_installation| {
1075 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));1078 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
...@@ -1095,10 +1098,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1095,10 +1098,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1095 try argv.append(p);1098 try argv.append(p);
1096 }1099 }
10971100
1098 if (self.base.options.out_implib != null) {
1099 try argv.append(try allocPrint(arena, "-IMPLIB:{s}.lib", .{full_out_path}));
1100 }
1101
1102 const resolved_subsystem: ?std.Target.SubSystem = blk: {1101 const resolved_subsystem: ?std.Target.SubSystem = blk: {
1103 if (self.base.options.subsystem) |explicit| break :blk explicit;1102 if (self.base.options.subsystem) |explicit| break :blk explicit;
1104 switch (target.os.tag) {1103 switch (target.os.tag) {
src/main.zig+84-39
...@@ -317,6 +317,8 @@ const usage_build_generic =...@@ -317,6 +317,8 @@ const usage_build_generic =
317 \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation317 \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation
318 \\ -femit-analysis[=path] Write analysis JSON file with type information318 \\ -femit-analysis[=path] Write analysis JSON file with type information
319 \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information319 \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information
320 \\ -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL
321 \\ -fno-emit-implib Do not produce an import .lib when building a Windows DLL
320 \\ --show-builtin Output the source of @import("builtin") then exit322 \\ --show-builtin Output the source of @import("builtin") then exit
321 \\ --cache-dir [path] Override the local cache directory323 \\ --cache-dir [path] Override the local cache directory
322 \\ --global-cache-dir [path] Override the global cache directory324 \\ --global-cache-dir [path] Override the global cache directory
...@@ -585,6 +587,8 @@ fn buildOutputType(...@@ -585,6 +587,8 @@ fn buildOutputType(
585 var emit_llvm_bc: Emit = .no;587 var emit_llvm_bc: Emit = .no;
586 var emit_docs: Emit = .no;588 var emit_docs: Emit = .no;
587 var emit_analysis: Emit = .no;589 var emit_analysis: Emit = .no;
590 var emit_implib: Emit = .yes_default_path;
591 var emit_implib_arg_provided = false;
588 var target_arch_os_abi: []const u8 = "native";592 var target_arch_os_abi: []const u8 = "native";
589 var target_mcpu: ?[]const u8 = null;593 var target_mcpu: ?[]const u8 = null;
590 var target_dynamic_linker: ?[]const u8 = null;594 var target_dynamic_linker: ?[]const u8 = null;
...@@ -654,7 +658,6 @@ fn buildOutputType(...@@ -654,7 +658,6 @@ fn buildOutputType(
654 var main_pkg_path: ?[]const u8 = null;658 var main_pkg_path: ?[]const u8 = null;
655 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;659 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
656 var subsystem: ?std.Target.SubSystem = null;660 var subsystem: ?std.Target.SubSystem = null;
657 var out_implib: ?[]const u8 = null;
658 var major_subsystem_version: ?u32 = null;661 var major_subsystem_version: ?u32 = null;
659 var minor_subsystem_version: ?u32 = null;662 var minor_subsystem_version: ?u32 = null;
660 var wasi_exec_model: ?std.builtin.WasiExecModel = null;663 var wasi_exec_model: ?std.builtin.WasiExecModel = null;
...@@ -1091,6 +1094,15 @@ fn buildOutputType(...@@ -1091,6 +1094,15 @@ fn buildOutputType(
1091 emit_analysis = .{ .yes = arg["-femit-analysis=".len..] };1094 emit_analysis = .{ .yes = arg["-femit-analysis=".len..] };
1092 } else if (mem.eql(u8, arg, "-fno-emit-analysis")) {1095 } else if (mem.eql(u8, arg, "-fno-emit-analysis")) {
1093 emit_analysis = .no;1096 emit_analysis = .no;
1097 } else if (mem.eql(u8, arg, "-femit-implib")) {
1098 emit_implib = .yes_default_path;
1099 emit_implib_arg_provided = true;
1100 } else if (mem.startsWith(u8, arg, "-femit-implib=")) {
1101 emit_implib = .{ .yes = arg["-femit-implib=".len..] };
1102 emit_implib_arg_provided = true;
1103 } else if (mem.eql(u8, arg, "-fno-emit-implib")) {
1104 emit_implib = .no;
1105 emit_implib_arg_provided = true;
1094 } else if (mem.eql(u8, arg, "-dynamic")) {1106 } else if (mem.eql(u8, arg, "-dynamic")) {
1095 link_mode = .Dynamic;1107 link_mode = .Dynamic;
1096 } else if (mem.eql(u8, arg, "-static")) {1108 } else if (mem.eql(u8, arg, "-static")) {
...@@ -1650,7 +1662,8 @@ fn buildOutputType(...@@ -1650,7 +1662,8 @@ fn buildOutputType(
1650 if (i >= linker_args.items.len) {1662 if (i >= linker_args.items.len) {
1651 fatal("expected linker arg after '{s}'", .{arg});1663 fatal("expected linker arg after '{s}'", .{arg});
1652 }1664 }
1653 out_implib = linker_args.items[i];1665 emit_implib = .{ .yes = linker_args.items[i] };
1666 emit_implib_arg_provided = true;
1654 } else {1667 } else {
1655 warn("unsupported linker arg: {s}", .{arg});1668 warn("unsupported linker arg: {s}", .{arg});
1656 }1669 }
...@@ -1998,11 +2011,15 @@ fn buildOutputType(...@@ -1998,11 +2011,15 @@ fn buildOutputType(
1998 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});2011 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});
1999 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {2012 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
2000 switch (emit_h) {2013 switch (emit_h) {
2001 .yes => {2014 .yes => |p| {
2002 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });2015 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{
2016 p, @errorName(err),
2017 });
2003 },2018 },
2004 .yes_default_path => {2019 .yes_default_path => {
2005 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_h_basename, @errorName(err) });2020 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
2021 default_h_basename, @errorName(err),
2022 });
2006 },2023 },
2007 .no => unreachable,2024 .no => unreachable,
2008 }2025 }
...@@ -2012,11 +2029,15 @@ fn buildOutputType(...@@ -2012,11 +2029,15 @@ fn buildOutputType(
2012 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});2029 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});
2013 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {2030 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
2014 switch (emit_asm) {2031 switch (emit_asm) {
2015 .yes => {2032 .yes => |p| {
2016 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });2033 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{
2034 p, @errorName(err),
2035 });
2017 },2036 },
2018 .yes_default_path => {2037 .yes_default_path => {
2019 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });2038 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
2039 default_asm_basename, @errorName(err),
2040 });
2020 },2041 },
2021 .no => unreachable,2042 .no => unreachable,
2022 }2043 }
...@@ -2026,11 +2047,15 @@ fn buildOutputType(...@@ -2026,11 +2047,15 @@ fn buildOutputType(
2026 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});2047 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});
2027 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {2048 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
2028 switch (emit_llvm_ir) {2049 switch (emit_llvm_ir) {
2029 .yes => {2050 .yes => |p| {
2030 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });2051 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{
2052 p, @errorName(err),
2053 });
2031 },2054 },
2032 .yes_default_path => {2055 .yes_default_path => {
2033 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });2056 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
2057 default_llvm_ir_basename, @errorName(err),
2058 });
2034 },2059 },
2035 .no => unreachable,2060 .no => unreachable,
2036 }2061 }
...@@ -2040,11 +2065,15 @@ fn buildOutputType(...@@ -2040,11 +2065,15 @@ fn buildOutputType(
2040 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});2065 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
2041 var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {2066 var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {
2042 switch (emit_llvm_bc) {2067 switch (emit_llvm_bc) {
2043 .yes => {2068 .yes => |p| {
2044 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ emit_llvm_bc.yes, @errorName(err) });2069 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{
2070 p, @errorName(err),
2071 });
2045 },2072 },
2046 .yes_default_path => {2073 .yes_default_path => {
2047 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_bc_basename, @errorName(err) });2074 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
2075 default_llvm_bc_basename, @errorName(err),
2076 });
2048 },2077 },
2049 .no => unreachable,2078 .no => unreachable,
2050 }2079 }
...@@ -2054,11 +2083,15 @@ fn buildOutputType(...@@ -2054,11 +2083,15 @@ fn buildOutputType(
2054 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});2083 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
2055 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {2084 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
2056 switch (emit_analysis) {2085 switch (emit_analysis) {
2057 .yes => {2086 .yes => |p| {
2058 fatal("unable to open directory from argument 'femit-analysis', '{s}': {s}", .{ emit_analysis.yes, @errorName(err) });2087 fatal("unable to open directory from argument '-femit-analysis', '{s}': {s}", .{
2088 p, @errorName(err),
2089 });
2059 },2090 },
2060 .yes_default_path => {2091 .yes_default_path => {
2061 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_analysis_basename, @errorName(err) });2092 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{
2093 default_analysis_basename, @errorName(err),
2094 });
2062 },2095 },
2063 .no => unreachable,2096 .no => unreachable,
2064 }2097 }
...@@ -2067,8 +2100,10 @@ fn buildOutputType(...@@ -2067,8 +2100,10 @@ fn buildOutputType(
20672100
2068 var emit_docs_resolved = emit_docs.resolve("docs") catch |err| {2101 var emit_docs_resolved = emit_docs.resolve("docs") catch |err| {
2069 switch (emit_docs) {2102 switch (emit_docs) {
2070 .yes => {2103 .yes => |p| {
2071 fatal("unable to open directory from argument 'femit-docs', '{s}': {s}", .{ emit_h.yes, @errorName(err) });2104 fatal("unable to open directory from argument '-femit-docs', '{s}': {s}", .{
2105 p, @errorName(err),
2106 });
2072 },2107 },
2073 .yes_default_path => {2108 .yes_default_path => {
2074 fatal("unable to open directory 'docs': {s}", .{@errorName(err)});2109 fatal("unable to open directory 'docs': {s}", .{@errorName(err)});
...@@ -2078,6 +2113,35 @@ fn buildOutputType(...@@ -2078,6 +2113,35 @@ fn buildOutputType(
2078 };2113 };
2079 defer emit_docs_resolved.deinit();2114 defer emit_docs_resolved.deinit();
20802115
2116 const is_dyn_lib = switch (output_mode) {
2117 .Obj, .Exe => false,
2118 .Lib => (link_mode orelse .Static) == .Dynamic,
2119 };
2120 const implib_eligible = is_dyn_lib and
2121 emit_bin_loc != null and target_info.target.os.tag == .windows;
2122 if (!implib_eligible) {
2123 if (!emit_implib_arg_provided) {
2124 emit_implib = .no;
2125 } else if (emit_implib != .no) {
2126 fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{});
2127 }
2128 }
2129 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
2130 var emit_implib_resolved = emit_implib.resolve(default_implib_basename) catch |err| {
2131 switch (emit_implib) {
2132 .yes => |p| {
2133 fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{
2134 p, @errorName(err),
2135 });
2136 },
2137 .yes_default_path => {
2138 fatal("unable to open directory 'docs': {s}", .{@errorName(err)});
2139 },
2140 .no => unreachable,
2141 }
2142 };
2143 defer emit_implib_resolved.deinit();
2144
2081 const main_pkg: ?*Package = if (root_src_file) |src_path| blk: {2145 const main_pkg: ?*Package = if (root_src_file) |src_path| blk: {
2082 if (main_pkg_path) |p| {2146 if (main_pkg_path) |p| {
2083 const rel_src_path = try fs.path.relative(gpa, p, src_path);2147 const rel_src_path = try fs.path.relative(gpa, p, src_path);
...@@ -2168,16 +2232,6 @@ fn buildOutputType(...@@ -2168,16 +2232,6 @@ fn buildOutputType(
2168 else => false,2232 else => false,
2169 };2233 };
21702234
2171 // Always output import libraries (.lib) when building for msvc to replicate
2172 // `link` behavior. lld does not always output import libraries so on the
2173 // gnu abi users must set out_implib.
2174 if (output_mode == .Lib and emit_bin == .yes and target_info.target.abi == .msvc and out_implib == null) {
2175 const emit_bin_ext = fs.path.extension(emit_bin.yes);
2176 out_implib = try std.fmt.allocPrint(gpa, "{s}.lib", .{
2177 emit_bin.yes[0 .. emit_bin.yes.len - emit_bin_ext.len],
2178 });
2179 }
2180
2181 gimmeMoreOfThoseSweetSweetFileDescriptors();2235 gimmeMoreOfThoseSweetSweetFileDescriptors();
21822236
2183 const comp = Compilation.create(gpa, .{2237 const comp = Compilation.create(gpa, .{
...@@ -2199,6 +2253,7 @@ fn buildOutputType(...@@ -2199,6 +2253,7 @@ fn buildOutputType(
2199 .emit_llvm_bc = emit_llvm_bc_resolved.data,2253 .emit_llvm_bc = emit_llvm_bc_resolved.data,
2200 .emit_docs = emit_docs_resolved.data,2254 .emit_docs = emit_docs_resolved.data,
2201 .emit_analysis = emit_analysis_resolved.data,2255 .emit_analysis = emit_analysis_resolved.data,
2256 .emit_implib = emit_implib_resolved.data,
2202 .link_mode = link_mode,2257 .link_mode = link_mode,
2203 .dll_export_fns = dll_export_fns,2258 .dll_export_fns = dll_export_fns,
2204 .object_format = object_format,2259 .object_format = object_format,
...@@ -2286,7 +2341,6 @@ fn buildOutputType(...@@ -2286,7 +2341,6 @@ fn buildOutputType(
2286 .test_name_prefix = test_name_prefix,2341 .test_name_prefix = test_name_prefix,
2287 .disable_lld_caching = !have_enable_cache,2342 .disable_lld_caching = !have_enable_cache,
2288 .subsystem = subsystem,2343 .subsystem = subsystem,
2289 .out_implib = out_implib,
2290 .wasi_exec_model = wasi_exec_model,2344 .wasi_exec_model = wasi_exec_model,
2291 .debug_compile_errors = debug_compile_errors,2345 .debug_compile_errors = debug_compile_errors,
2292 .enable_link_snapshots = enable_link_snapshots,2346 .enable_link_snapshots = enable_link_snapshots,
...@@ -2685,15 +2739,6 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi...@@ -2685,15 +2739,6 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
26852739
2686 _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{});2740 _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{});
2687 }2741 }
2688
2689 if (comp.bin_file.options.out_implib) |out_implib| {
2690 const src_implib_path = try std.fmt.allocPrint(gpa, "{s}.lib", .{bin_sub_path});
2691 defer gpa.free(src_implib_path);
2692 if (std.fs.path.dirname(out_implib)) |implib_dir| {
2693 try cwd.makePath(implib_dir);
2694 }
2695 _ = try cache_dir.updateFile(src_implib_path, cwd, out_implib, .{});
2696 }
2697 },2742 },
2698 }2743 }
2699}2744}