From 076022c1fe7fe15614959c0d50d2866f1149f78f Mon Sep 17 00:00:00 2001 From: "richard (rikki) andrew cattermole" Date: Mon, 6 Mar 2023 09:39:17 +1300 Subject: [PATCH] Make all static library exports contribute to dlls on Windows --- changelog/static_libraries_exported_dll.dd | 8 +++++ source/dub/compilers/compiler.d | 3 ++ source/dub/compilers/dmd.d | 6 ++++ source/dub/compilers/gdc.d | 5 +++ source/dub/compilers/ldc.d | 6 ++++ source/dub/generators/build.d | 38 +++++++++++++++++++++- 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 changelog/static_libraries_exported_dll.dd diff --git a/changelog/static_libraries_exported_dll.dd b/changelog/static_libraries_exported_dll.dd new file mode 100644 index 000000000..ba981f322 --- /dev/null +++ b/changelog/static_libraries_exported_dll.dd @@ -0,0 +1,8 @@ +Static libraries now contribute towards DLL exports on Windows + +Previously if you did not explicitly pull in an object file within a static library, it was elided by the linker automatically. +This pulls them in automatically for linkers compatible with Microsoft's linker via the ``/WHOLEARCHIVE:file`` flag. Supports LLD. + +It does not affect executables, although DLL's being built as dependencies by DUB will include it. + +If you have previously used a linker script (.def) or ``/WHOLEARCHIVE`` you may be able to remove them from your builds. diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index ece488403..9059e605a 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -217,6 +217,9 @@ interface Compiler { return build_platform; } + + /// Given a platform specification, determine if a compiler is on Windows and PE-COFF with MSVC link compatible linker. + bool isWindowsCOFF(in BuildPlatform platform); } private { diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index 78fd9d4b5..26ae7dedb 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -434,4 +434,10 @@ config /etc/dmd.conf || arg.startsWith("-defaultlib="); } } + + bool isWindowsCOFF(in BuildPlatform platform) + { + // x86_omf and x86_mscoff shouldn't be something you have to worry about here, but just in case something leaks + return platform.isWindows && platform.architecture.canFind("x86", "x86_64", "x86_mscoff") && !platform.architecture.canFind("x86_omf"); + } } diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index 0d34446ec..3291072e3 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -259,6 +259,11 @@ class GDCCompiler : Compiler { return dflags; } + + bool isWindowsCOFF(in BuildPlatform platform) + { + return false; + } } private string extractTarget(const string[] args) { auto i = args.countUntil("-o"); return i >= 0 ? args[i+1] : null; } diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index 3cb90e5b2..045c035f1 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -322,4 +322,10 @@ config /etc/ldc2.conf (x86_64-pc-linux-gnu) || arg.startsWith("-mtriple="); } } + + bool isWindowsCOFF(in BuildPlatform platform) + { + // What will happen on ARM Windows? Who knows. Once LDC ships for ARM, lets find out! + return platform.isWindows(); + } } diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 8583eab84..ee317d180 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -97,7 +97,32 @@ class BuildGenerator : ProjectGenerator { const copyDynamicLibDepsLinkerFiles = rootTT == TargetType.dynamicLibrary || rootTT == TargetType.none; const copyDynamicLibDepsRuntimeFiles = copyDynamicLibDepsLinkerFiles || rootTT == TargetType.executable; - bool[string] visited; + // Check to see if given a compiler and platform target + // are Windows and linking using a MSVC link compatible linker. + const isWindowsCOFF = settings.compiler.isWindowsCOFF(settings.platform); + + bool[string] visited, visitedStaticInDll; + + void visitStaticLibsInDll(ref BuildSettings bs, string target) { + if (target in visitedStaticInDll) return; + visitedStaticInDll[target] = true; + + auto ti = targets[target]; + if (ti.buildSettings.targetType != TargetType.staticLibrary) + return; + + const ldepPath = target_paths[target].toNativeString(); + + // Add the MSVC link /WHOLEARCHIVE flag with static library path passed in + // the purpose of this is to allow all exports from a static library to contribute + // towards the dll's exports. + bs.addLFlags("/WHOLEARCHIVE:" ~ ldepPath); + + foreach (ldep; ti.linkDependencies) { + visitStaticLibsInDll(bs, ldep); + } + } + void buildTargetRec(string target) { if (target in visited) return; @@ -111,6 +136,17 @@ class BuildGenerator : ProjectGenerator { NativePath[] additional_dep_files; auto bs = ti.buildSettings.dup; const tt = bs.targetType; + + // Windows only behavior for DLL's with static library dependencies + if (tt == TargetType.dynamicLibrary && isWindowsCOFF) { + // discover all static libraries that are going into our DLL + visitedStaticInDll = null; + + foreach (ldep; ti.linkDependencies) { + visitStaticLibsInDll(bs, ldep); + } + } + foreach (ldep; ti.linkDependencies) { const ldepPath = target_paths[ldep].toNativeString(); const doLink = tt != TargetType.staticLibrary && !(bs.options & BuildOption.syntaxOnly);