Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On Windows support disabling of forcing of all object files in a static library to be in a DLL #2615

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/static_libraries_exported_dll.dd
Original file line number Diff line number Diff line change
Expand Up @@ -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.
24 changes: 13 additions & 11 deletions source/dub/compilers/buildsettings.d
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
53 changes: 48 additions & 5 deletions source/dub/generators/build.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -119,7 +151,7 @@ class BuildGenerator : ProjectGenerator {
bs.addLFlags("/WHOLEARCHIVE:" ~ ldepPath);

foreach (ldep; ti.linkDependencies) {
visitStaticLibsInDll(bs, ldep);
elideStaticLibsInDll(bs, ldep);
}
}

Expand All @@ -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);
}
}
}

Expand Down