authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-01 17:43:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-03 09:52:14-07:00
loga1e21ceec8ecb475f96c5e83ee9f8d59ceecc82d
tree083646afc78a3ca0cc6d2e04fd4dc270d6f44a16
parenta08cc7d2ae3bd6e90f8d26ac13f2e0652687dc30

frontend: fix linking to Windows DLLs as system libs


3 files changed, 85 insertions(+), 39 deletions(-)

src/Compilation.zig+5
...@@ -5618,6 +5618,11 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -5618,6 +5618,11 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
5618 // to queue up a work item to produce the DLL import library for this.5618 // to queue up a work item to produce the DLL import library for this.
5619 const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);5619 const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
5620 if (!gop.found_existing and comp.getTarget().os.tag == .windows) {5620 if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
5621 gop.value_ptr.* = .{
5622 .needed = true,
5623 .weak = false,
5624 .path = undefined,
5625 };
5621 try comp.work_queue.writeItem(.{5626 try comp.work_queue.writeItem(.{
5622 .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,5627 .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
5623 });5628 });
src/main.zig+55-32
...@@ -28,6 +28,7 @@ const target_util = @import("target.zig");...@@ -28,6 +28,7 @@ const target_util = @import("target.zig");
28const crash_report = @import("crash_report.zig");28const crash_report = @import("crash_report.zig");
29const Module = @import("Module.zig");29const Module = @import("Module.zig");
30const AstGen = @import("AstGen.zig");30const AstGen = @import("AstGen.zig");
31const mingw = @import("mingw.zig");
31const Server = std.zig.Server;32const Server = std.zig.Server;
3233
33pub const std_options = struct {34pub const std_options = struct {
...@@ -477,6 +478,8 @@ const usage_build_generic =...@@ -477,6 +478,8 @@ const usage_build_generic =
477 \\ -needed-l[lib], Link against system library (even if unused)478 \\ -needed-l[lib], Link against system library (even if unused)
478 \\ --needed-library [lib]479 \\ --needed-library [lib]
479 \\ -L[d], --library-directory [d] Add a directory to the library search path480 \\ -L[d], --library-directory [d] Add a directory to the library search path
481 \\ -search_paths_first Search each library search path for dynamic libs then static libs
482 \\ -search_dylibs_first Search for dynamic libs in each library search path, then static libs.
480 \\ -T[script], --script [script] Use a custom linker script483 \\ -T[script], --script [script] Use a custom linker script
481 \\ --version-script [path] Provide a version .map file484 \\ --version-script [path] Provide a version .map file
482 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)485 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
...@@ -537,8 +540,6 @@ const usage_build_generic =...@@ -537,8 +540,6 @@ const usage_build_generic =
537 \\ -install_name=[value] (Darwin) add dylib's install name540 \\ -install_name=[value] (Darwin) add dylib's install name
538 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature541 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
539 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation542 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
540 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
541 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
542 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation543 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
543 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN544 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
544 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols545 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
...@@ -2567,6 +2568,34 @@ fn buildOutputType(...@@ -2567,6 +2568,34 @@ fn buildOutputType(
2567 }2568 }
2568 lib_dir_args = undefined; // From here we use lib_dirs instead.2569 lib_dir_args = undefined; // From here we use lib_dirs instead.
25692570
2571 const self_exe_path: ?[]const u8 = if (!process.can_spawn)
2572 null
2573 else
2574 introspect.findZigExePath(arena) catch |err| {
2575 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
2576 };
2577
2578 var zig_lib_directory: Compilation.Directory = d: {
2579 if (override_lib_dir) |unresolved_lib_dir| {
2580 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
2581 break :d .{
2582 .path = lib_dir,
2583 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
2584 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
2585 },
2586 };
2587 } else if (builtin.os.tag == .wasi) {
2588 break :d getWasiPreopen("/lib");
2589 } else if (self_exe_path) |p| {
2590 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
2591 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2592 };
2593 } else {
2594 unreachable;
2595 }
2596 };
2597 defer zig_lib_directory.handle.close();
2598
2570 // Now that we have target info, we can find out if any of the system libraries2599 // Now that we have target info, we can find out if any of the system libraries
2571 // are part of libc or libc++. We remove them from the list and communicate their2600 // are part of libc or libc++. We remove them from the list and communicate their
2572 // existence via flags instead.2601 // existence via flags instead.
...@@ -2612,6 +2641,25 @@ fn buildOutputType(...@@ -2612,6 +2641,25 @@ fn buildOutputType(
2612 },2641 },
2613 }2642 }
26142643
2644 if (target_info.target.os.tag == .windows) {
2645 const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| {
2646 fatal("failed to check zig installation for DLL import libs: {s}", .{
2647 @errorName(err),
2648 });
2649 };
2650 if (exists) {
2651 try resolved_system_libs.append(arena, .{
2652 .name = lib_name,
2653 .lib = .{
2654 .needed = true,
2655 .weak = false,
2656 .path = undefined,
2657 },
2658 });
2659 continue;
2660 }
2661 }
2662
2615 if (fs.path.isAbsolute(lib_name)) {2663 if (fs.path.isAbsolute(lib_name)) {
2616 fatal("cannot use absolute path as a system library: {s}", .{lib_name});2664 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
2617 }2665 }
...@@ -2758,9 +2806,13 @@ fn buildOutputType(...@@ -2758,9 +2806,13 @@ fn buildOutputType(
27582806
2759 if (failed_libs.items.len > 0) {2807 if (failed_libs.items.len > 0) {
2760 for (failed_libs.items) |f| {2808 for (failed_libs.items) |f| {
2809 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2761 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{2810 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
2762 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), f.checked_paths,2811 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
2763 });2812 });
2813 if (f.preferred_mode == .Dynamic and f.strategy == .no_fallback) {
2814 std.log.info("to link statically, pass the library as a positional argument", .{});
2815 }
2764 }2816 }
2765 process.exit(1);2817 process.exit(1);
2766 }2818 }
...@@ -3079,35 +3131,6 @@ fn buildOutputType(...@@ -3079,35 +3131,6 @@ fn buildOutputType(
3079 }3131 }
3080 }3132 }
30813133
3082 const self_exe_path: ?[]const u8 = if (!process.can_spawn)
3083 null
3084 else
3085 introspect.findZigExePath(arena) catch |err| {
3086 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
3087 };
3088
3089 var zig_lib_directory: Compilation.Directory = d: {
3090 if (override_lib_dir) |unresolved_lib_dir| {
3091 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
3092 break :d .{
3093 .path = lib_dir,
3094 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
3095 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
3096 },
3097 };
3098 } else if (builtin.os.tag == .wasi) {
3099 break :d getWasiPreopen("/lib");
3100 } else if (self_exe_path) |p| {
3101 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
3102 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
3103 };
3104 } else {
3105 unreachable;
3106 }
3107 };
3108
3109 defer zig_lib_directory.handle.close();
3110
3111 var thread_pool: ThreadPool = undefined;3134 var thread_pool: ThreadPool = undefined;
3112 try thread_pool.init(.{ .allocator = gpa });3135 try thread_pool.init(.{ .allocator = gpa });
3113 defer thread_pool.deinit();3136 defer thread_pool.deinit();
src/mingw.zig+25-7
...@@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
283 defer arena_allocator.deinit();283 defer arena_allocator.deinit();
284 const arena = arena_allocator.allocator();284 const arena = arena_allocator.allocator();
285285
286 const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {286 const def_file_path = findDef(arena, comp.getTarget(), comp.zig_lib_directory, lib_name) catch |err| switch (err) {
287 error.FileNotFound => {287 error.FileNotFound => {
288 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });288 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
289 // In this case we will end up putting foo.lib onto the linker line and letting the linker289 // In this case we will end up putting foo.lib onto the linker line and letting the linker
...@@ -431,10 +431,28 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -431,10 +431,28 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
431 });431 });
432}432}
433433
434/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.434pub fn libExists(
435fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8 {435 allocator: Allocator,
436 const target = comp.getTarget();436 target: std.Target,
437 zig_lib_directory: Cache.Directory,
438 lib_name: []const u8,
439) !bool {
440 const s = findDef(allocator, target, zig_lib_directory, lib_name) catch |err| switch (err) {
441 error.FileNotFound => return false,
442 else => |e| return e,
443 };
444 defer allocator.free(s);
445 return true;
446}
437447
448/// This function body is verbose but all it does is test 3 different paths and
449/// see if a .def file exists.
450fn findDef(
451 allocator: Allocator,
452 target: std.Target,
453 zig_lib_directory: Cache.Directory,
454 lib_name: []const u8,
455) ![]u8 {
438 const lib_path = switch (target.cpu.arch) {456 const lib_path = switch (target.cpu.arch) {
439 .x86 => "lib32",457 .x86 => "lib32",
440 .x86_64 => "lib64",458 .x86_64 => "lib64",
...@@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8...@@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
451 {469 {
452 // Try the archtecture-specific path first.470 // Try the archtecture-specific path first.
453 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";471 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
454 if (comp.zig_lib_directory.path) |p| {472 if (zig_lib_directory.path) |p| {
455 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });473 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
456 } else {474 } else {
457 try override_path.writer().print(fmt_path, .{ lib_path, lib_name });475 try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
...@@ -468,7 +486,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8...@@ -468,7 +486,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
468 // Try the generic version.486 // Try the generic version.
469 override_path.shrinkRetainingCapacity(0);487 override_path.shrinkRetainingCapacity(0);
470 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";488 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
471 if (comp.zig_lib_directory.path) |p| {489 if (zig_lib_directory.path) |p| {
472 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });490 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
473 } else {491 } else {
474 try override_path.writer().print(fmt_path, .{lib_name});492 try override_path.writer().print(fmt_path, .{lib_name});
...@@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8...@@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
485 // Try the generic version and preprocess it.503 // Try the generic version and preprocess it.
486 override_path.shrinkRetainingCapacity(0);504 override_path.shrinkRetainingCapacity(0);
487 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";505 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
488 if (comp.zig_lib_directory.path) |p| {506 if (zig_lib_directory.path) |p| {
489 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });507 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
490 } else {508 } else {
491 try override_path.writer().print(fmt_path, .{lib_name});509 try override_path.writer().print(fmt_path, .{lib_name});