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 {
56185618 // to queue up a work item to produce the DLL import library for this.
56195619 const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
56205620 if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
5621 gop.value_ptr.* = .{
5622 .needed = true,
5623 .weak = false,
5624 .path = undefined,
5625 };
56215626 try comp.work_queue.writeItem(.{
56225627 .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
56235628 });
src/main.zig+55-32
......@@ -28,6 +28,7 @@ const target_util = @import("target.zig");
2828const crash_report = @import("crash_report.zig");
2929const Module = @import("Module.zig");
3030const AstGen = @import("AstGen.zig");
31const mingw = @import("mingw.zig");
3132const Server = std.zig.Server;
3233
3334pub const std_options = struct {
......@@ -477,6 +478,8 @@ const usage_build_generic =
477478 \\ -needed-l[lib], Link against system library (even if unused)
478479 \\ --needed-library [lib]
479480 \\ -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.
480483 \\ -T[script], --script [script] Use a custom linker script
481484 \\ --version-script [path] Provide a version .map file
482485 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
......@@ -537,8 +540,6 @@ const usage_build_generic =
537540 \\ -install_name=[value] (Darwin) add dylib's install name
538541 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
539542 \\ -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`
542543 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
543544 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
544545 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
......@@ -2567,6 +2568,34 @@ fn buildOutputType(
25672568 }
25682569 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
25702599 // Now that we have target info, we can find out if any of the system libraries
25712600 // are part of libc or libc++. We remove them from the list and communicate their
25722601 // existence via flags instead.
......@@ -2612,6 +2641,25 @@ fn buildOutputType(
26122641 },
26132642 }
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
26152663 if (fs.path.isAbsolute(lib_name)) {
26162664 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
26172665 }
......@@ -2758,9 +2806,13 @@ fn buildOutputType(
27582806
27592807 if (failed_libs.items.len > 0) {
27602808 for (failed_libs.items) |f| {
2809 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
27612810 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,
27632812 });
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 }
27642816 }
27652817 process.exit(1);
27662818 }
......@@ -3079,35 +3131,6 @@ fn buildOutputType(
30793131 }
30803132 }
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
31113134 var thread_pool: ThreadPool = undefined;
31123135 try thread_pool.init(.{ .allocator = gpa });
31133136 defer thread_pool.deinit();
src/mingw.zig+25-7
......@@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
283283 defer arena_allocator.deinit();
284284 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) {
287287 error.FileNotFound => {
288288 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
289289 // 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 {
431431 });
432432}
433433
434/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
435fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8 {
436 const target = comp.getTarget();
434pub fn libExists(
435 allocator: Allocator,
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 {
438456 const lib_path = switch (target.cpu.arch) {
439457 .x86 => "lib32",
440458 .x86_64 => "lib64",
......@@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
451469 {
452470 // Try the archtecture-specific path first.
453471 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| {
455473 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
456474 } else {
457475 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
468486 // Try the generic version.
469487 override_path.shrinkRetainingCapacity(0);
470488 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| {
472490 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
473491 } else {
474492 try override_path.writer().print(fmt_path, .{lib_name});
......@@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
485503 // Try the generic version and preprocess it.
486504 override_path.shrinkRetainingCapacity(0);
487505 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| {
489507 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
490508 } else {
491509 try override_path.writer().print(fmt_path, .{lib_name});