diff --git a/changelog/static_libraries_exported_dll.dd b/changelog/static_libraries_exported_dll.dd index ba981f322..ef5b2c9f6 100644 --- a/changelog/static_libraries_exported_dll.dd +++ b/changelog/static_libraries_exported_dll.dd @@ -6,3 +6,10 @@ This pulls them in automatically for linkers compatible with Microsoft's linker 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. + +Warning: This may have the unfortunate side affect of preventing previously working builds of DLLs to no longer link. +If this occurs use the build requirement flags of ``allowDLLObjectFileEliding`` or ``allowDLLObjectFileElidingAll`` via the ``buildRequirements`` directive. +The difference between the two is that the latter can be applied anywhere in the dependency tree and the former must be placed on the DLL package recipe or any of its static library dependencies. + +Warning: DLL's may grow in size compared to previous versions of dub due to all the object files being included by default. +If it was working for you previously, you may allow eliding. diff --git a/source/dub/compilers/buildsettings.d b/source/dub/compilers/buildsettings.d index 9d5652c94..ef7128246 100644 --- a/source/dub/compilers/buildsettings.d +++ b/source/dub/compilers/buildsettings.d @@ -355,17 +355,19 @@ enum TargetType { } enum BuildRequirement { - none = 0, /// No special requirements - allowWarnings = 1<<0, /// Warnings do not abort compilation - silenceWarnings = 1<<1, /// Don't show warnings - disallowDeprecations = 1<<2, /// Using deprecated features aborts compilation - silenceDeprecations = 1<<3, /// Don't show deprecation warnings - disallowInlining = 1<<4, /// Avoid function inlining, even in release builds - disallowOptimization = 1<<5, /// Avoid optimizations, even in release builds - requireBoundsCheck = 1<<6, /// Always perform bounds checks - requireContracts = 1<<7, /// Leave assertions and contracts enabled in release builds - relaxProperties = 1<<8, /// DEPRECATED: Do not enforce strict property handling (-property) - noDefaultFlags = 1<<9, /// Do not issue any of the default build flags (e.g. -debug, -w, -property etc.) - use only for development purposes + none = 0, /// No special requirements + allowWarnings = 1<<0, /// Warnings do not abort compilation + silenceWarnings = 1<<1, /// Don't show warnings + disallowDeprecations = 1<<2, /// Using deprecated features aborts compilation + silenceDeprecations = 1<<3, /// Don't show deprecation warnings + disallowInlining = 1<<4, /// Avoid function inlining, even in release builds + disallowOptimization = 1<<5, /// Avoid optimizations, even in release builds + requireBoundsCheck = 1<<6, /// Always perform bounds checks + requireContracts = 1<<7, /// Leave assertions and contracts enabled in release builds + relaxProperties = 1<<8, /// DEPRECATED: Do not enforce strict property handling (-property) + noDefaultFlags = 1<<9, /// Do not issue any of the default build flags (e.g. -debug, -w, -property etc.) - use only for development purposes + allowDLLObjectFileEliding = 1 << 10, /// Do not force all object files in static libraries to form a DLL + allowDLLObjectFileElidingAll = 1 << 11, /// Do not force all object files in static libraries to form a DLL for entire dependency tree } enum BuildOption { diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index ee317d180..1bce0a970 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -100,10 +100,42 @@ class BuildGenerator : ProjectGenerator { // 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 anyAllowDLLEliding = isWindowsCOFF; + + // Prevent forcing of DLL eliding if allowDLLObjectFileElidingAll is provided by any target in our dependency tree + if (isWindowsCOFF) { + foreach(dep, ref ti; targets) { + if (ti.buildSettings.requirements.allowDLLObjectFileElidingAll) { + anyAllowDLLEliding = false; + break; + } + } + } bool[string] visited, visitedStaticInDll; - void visitStaticLibsInDll(ref BuildSettings bs, string target) { + // Windows only behavior + // check a given target and its dependencies to see if we want to allow eliding of object files + bool doesAllowElideStaticLibsInDll(string target) { + if (target in visitedStaticInDll) return false; + visitedStaticInDll[target] = true; + + auto ti = targets[target]; + if (ti.buildSettings.targetType != TargetType.staticLibrary) + return false; + else if (ti.buildSettings.requirements.allowDLLObjectFileEliding) + return true; + + foreach (ldep; ti.linkDependencies) { + if (doesAllowElideStaticLibsInDll(ldep)) + return true; + } + + return false; + } + + // Windows only behavior + void elideStaticLibsInDll(ref BuildSettings bs, string target) { if (target in visitedStaticInDll) return; visitedStaticInDll[target] = true; @@ -119,7 +151,7 @@ class BuildGenerator : ProjectGenerator { bs.addLFlags("/WHOLEARCHIVE:" ~ ldepPath); foreach (ldep; ti.linkDependencies) { - visitStaticLibsInDll(bs, ldep); + elideStaticLibsInDll(bs, ldep); } } @@ -138,12 +170,23 @@ class BuildGenerator : ProjectGenerator { const tt = bs.targetType; // Windows only behavior for DLL's with static library dependencies - if (tt == TargetType.dynamicLibrary && isWindowsCOFF) { + if (tt == TargetType.dynamicLibrary && anyAllowDLLEliding && !bs.requirements.allowDLLObjectFileEliding) { // discover all static libraries that are going into our DLL + bool allowEliding; + + // allow eliding in this DLL if it or any static library dependencies are marked as requiring it. visitedStaticInDll = null; + foreach(ldep; ti.linkDependencies) { + allowEliding = doesAllowElideStaticLibsInDll(ldep); + if (allowEliding) + break; + } - foreach (ldep; ti.linkDependencies) { - visitStaticLibsInDll(bs, ldep); + if (!allowEliding) { + visitedStaticInDll = null; + foreach (ldep; ti.linkDependencies) { + elideStaticLibsInDll(bs, ldep); + } } }