diff options
author | 2017-09-20 10:25:48 +0200 | |
---|---|---|
committer | 2017-09-20 11:59:04 +0200 | |
commit | 481657d6149b3c68b893ad8221ac1bccccf1060d (patch) | |
tree | db33e89dbfd5f9df16143de5553cbaf1ab638159 /src/main/java/com/google/devtools/build/lib/rules | |
parent | e28b772d85a463e4fe154767d0d82c0fb9e63c3d (diff) |
Windows: Make dynamic libraries available to binary at runtime
When copy_dynamic_libraries_to_binary is enabled, we copy the shared
libraries required by the binary to the binary's directory.
Bazel will throw errors if there are confilct actions generating the
same artifacts.
Change-Id: I09a5a599ca0ec7a67efd49d5aa89481450fa4e90
PiperOrigin-RevId: 169364498
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules')
6 files changed, 108 insertions, 29 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java index 20b8ae5780..14eeb3bbe8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java @@ -36,6 +36,7 @@ import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.ParamFileInfo; import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.analysis.actions.SymlinkAction; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.test.ExecutionInfo; import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider; @@ -236,6 +237,14 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { ruleContext.attributeError("linkshared", "'linkshared' used in non-shared library"); return null; } + + CcLinkParams linkParams = + collectCcLinkParams( + ruleContext, + linkStaticness != LinkStaticness.DYNAMIC, + isLinkShared(ruleContext), + linkopts); + CppLinkActionBuilder linkActionBuilder = determineLinkerArguments( ruleContext, @@ -248,8 +257,7 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { cppCompilationContext.getTransitiveCompilationPrerequisites(), fake, binary, - linkStaticness, - linkopts, + linkParams, linkCompileOutputSeparately); linkActionBuilder.setUseTestOnlyFlags(ruleContext.isTestTarget()); if (linkStaticness == LinkStaticness.DYNAMIC) { @@ -363,6 +371,17 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { } } + // If the binary is linked dynamically and COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, collect + // all the dynamic libraries we need at runtime. Then copy these libraries next to the binary. + if (featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { + filesToBuild = + NestedSetBuilder.fromNestedSet(filesToBuild) + .addAll( + createDynamicLibrariesCopyActions( + ruleContext, linkParams.getExecutionDynamicLibraries())) + .build(); + } + // TODO(bazel-team): Do we need to put original shared libraries (along with // mangled symlinks) into the RunfilesSupport object? It does not seem // logical since all symlinked libraries will be linked anyway and would @@ -462,8 +481,7 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { ImmutableSet<Artifact> compilationPrerequisites, boolean fake, Artifact binary, - LinkStaticness linkStaticness, - List<String> linkopts, + CcLinkParams linkParams, boolean linkCompileOutputSeparately) throws InterruptedException { CppLinkActionBuilder builder = @@ -512,9 +530,8 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { } // Then the link params from the closure of deps. - CcLinkParams linkParams = collectCcLinkParams( - context, linkStaticness != LinkStaticness.DYNAMIC, isLinkShared(context), linkopts); builder.addLinkParams(linkParams, context); + return builder; } @@ -689,6 +706,29 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { } /** + * Create the actions to symlink/copy execution dynamic libraries to binary directory so that they + * are available at runtime. + * + * @param executionDynamicLibraries The libraries to be copied. + * @return The result artifacts of the copies. + */ + private static ImmutableList<Artifact> createDynamicLibrariesCopyActions( + RuleContext ruleContext, NestedSet<Artifact> executionDynamicLibraries) { + ImmutableList.Builder<Artifact> result = ImmutableList.builder(); + for (Artifact target : executionDynamicLibraries) { + if (!ruleContext.getLabel().getPackageName().equals(target.getOwner().getPackageName())) { + // SymlinkAction on file is actually copy on Windows. + Artifact copy = ruleContext.getBinArtifact(target.getFilename()); + ruleContext.registerAction( + new SymlinkAction( + ruleContext.getActionOwner(), target, copy, "Copying Execution Dynamic Library")); + result.add(copy); + } + } + return result.build(); + } + + /** * Returns a new SpawnAction builder for generating dwp files, pre-initialized with standard * settings. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java index 4fe0b4bebd..fd7a3cbb0a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java @@ -1558,13 +1558,15 @@ public final class CcLibraryHelper { CcLinkParams.Builder builder, boolean linkingStatically, boolean linkShared) { builder.addLinkstamps(linkstamps.build(), cppCompilationContext); builder.addTransitiveTargets( - deps, - CcLinkParamsInfo.TO_LINK_PARAMS, - CcSpecificLinkParamsProvider.TO_LINK_PARAMS); + deps, CcLinkParamsInfo.TO_LINK_PARAMS, CcSpecificLinkParamsProvider.TO_LINK_PARAMS); if (!neverlink) { builder.addLibraries( ccLinkingOutputs.getPreferredLibraries( linkingStatically, /*preferPic=*/ linkShared || forcePic)); + if (!linkingStatically) { + builder.addExecutionDynamicLibraries( + LinkerInputs.toLibraryArtifacts(ccLinkingOutputs.getExecutionDynamicLibraries())); + } builder.addLinkOpts(linkopts); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParams.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParams.java index 75b5662b1e..afb863101c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParams.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParams.java @@ -69,15 +69,19 @@ public final class CcLinkParams { private final NestedSet<LinkOptions> linkOpts; private final NestedSet<Linkstamp> linkstamps; private final NestedSet<LibraryToLink> libraries; + private final NestedSet<Artifact> executionDynamicLibraries; private final ExtraLinkTimeLibraries extraLinkTimeLibraries; - private CcLinkParams(NestedSet<LinkOptions> linkOpts, - NestedSet<Linkstamp> linkstamps, - NestedSet<LibraryToLink> libraries, - ExtraLinkTimeLibraries extraLinkTimeLibraries) { + private CcLinkParams( + NestedSet<LinkOptions> linkOpts, + NestedSet<Linkstamp> linkstamps, + NestedSet<LibraryToLink> libraries, + NestedSet<Artifact> executionDynamicLibraries, + ExtraLinkTimeLibraries extraLinkTimeLibraries) { this.linkOpts = linkOpts; this.linkstamps = linkstamps; this.libraries = libraries; + this.executionDynamicLibraries = executionDynamicLibraries; this.extraLinkTimeLibraries = extraLinkTimeLibraries; } @@ -106,6 +110,11 @@ public final class CcLinkParams { return libraries; } + /** @return the executionDynamicLibraries */ + public NestedSet<Artifact> getExecutionDynamicLibraries() { + return executionDynamicLibraries; + } + /** * The extra link time libraries; will be null if there are no such libraries. */ @@ -146,6 +155,8 @@ public final class CcLinkParams { NestedSetBuilder.compileOrder(); private final NestedSetBuilder<LibraryToLink> librariesBuilder = NestedSetBuilder.linkOrder(); + private final NestedSetBuilder<Artifact> executionDynamicLibrariesBuilder = + NestedSetBuilder.stableOrder(); /** * A builder for the list of link time libraries. Most builds @@ -176,8 +187,12 @@ public final class CcLinkParams { if (extraLinkTimeLibrariesBuilder != null) { extraLinkTimeLibraries = extraLinkTimeLibrariesBuilder.build(); } - return new CcLinkParams(linkOptsBuilder.build(), linkstampsBuilder.build(), - librariesBuilder.build(), extraLinkTimeLibraries); + return new CcLinkParams( + linkOptsBuilder.build(), + linkstampsBuilder.build(), + librariesBuilder.build(), + executionDynamicLibrariesBuilder.build(), + extraLinkTimeLibraries); } public boolean add(CcLinkParamsStore store) { @@ -263,6 +278,7 @@ public final class CcLinkParams { linkOptsBuilder.addTransitive(args.getLinkopts()); linkstampsBuilder.addTransitive(args.getLinkstamps()); librariesBuilder.addTransitive(args.getLibraries()); + executionDynamicLibrariesBuilder.addTransitive(args.getExecutionDynamicLibraries()); if (args.getExtraLinkTimeLibraries() != null) { if (extraLinkTimeLibrariesBuilder == null) { extraLinkTimeLibrariesBuilder = ExtraLinkTimeLibraries.builder(); @@ -306,6 +322,12 @@ public final class CcLinkParams { return this; } + /** Adds a collection of library artifacts. */ + public Builder addExecutionDynamicLibraries(Iterable<Artifact> libraries) { + executionDynamicLibrariesBuilder.addAll(libraries); + return this; + } + /** * Adds an extra link time library, a library that is actually * built at link time. @@ -334,6 +356,10 @@ public final class CcLinkParams { if (!neverlink) { addLibraries(linkingOutputs.getPreferredLibraries(linkingStatically, linkShared || context.getFragment(CppConfiguration.class).forcePic())); + if (!linkingStatically) { + addExecutionDynamicLibraries( + LinkerInputs.toLibraryArtifacts(linkingOutputs.getExecutionDynamicLibraries())); + } addLinkOpts(linkopts); } return this; @@ -388,12 +414,12 @@ public final class CcLinkParams { } } - /** - * Empty CcLinkParams. - */ - public static final CcLinkParams EMPTY = new CcLinkParams( - NestedSetBuilder.<LinkOptions>emptySet(Order.LINK_ORDER), - NestedSetBuilder.<Linkstamp>emptySet(Order.COMPILE_ORDER), - NestedSetBuilder.<LibraryToLink>emptySet(Order.LINK_ORDER), - null); + /** Empty CcLinkParams. */ + public static final CcLinkParams EMPTY = + new CcLinkParams( + NestedSetBuilder.<LinkOptions>emptySet(Order.LINK_ORDER), + NestedSetBuilder.<Linkstamp>emptySet(Order.COMPILE_ORDER), + NestedSetBuilder.<LibraryToLink>emptySet(Order.LINK_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java index 194bb00484..2df7719e49 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java @@ -1736,11 +1736,15 @@ public class CppLinkActionBuilder { for (LinkerInput input : linkerInputs) { if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY) { PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); - Preconditions.checkState( - libDir.startsWith(solibDir), - "Artifact '%s' is not under directory '%s'.", - input.getArtifact(), - solibDir); + // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, dynamic libraries are not symlinked + // under solibDir, so don't check it. + if (!featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { + Preconditions.checkState( + libDir.startsWith(solibDir), + "Artifact '%s' is not under directory '%s'.", + input.getArtifact(), + solibDir); + } if (libDir.equals(solibDir)) { includeSolibDir = true; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java index 10b3aea37a..42a596c992 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java @@ -1543,7 +1543,11 @@ public final class CppModel { // If shared library has neverlink=1, then leave it untouched. Otherwise, // create a mangled symlink for it and from now on reference it through // mangled name only. - if (neverLink) { + // + // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special + // solibDir, instead we use the original interface library and dynamic library. + if (neverLink + || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { result.addDynamicLibrary(interfaceLibrary); result.addExecutionDynamicLibrary(dynamicLibrary); } else { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java index a4ad59ec05..97ad0f391e 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java @@ -300,6 +300,9 @@ public class CppRuleClasses { /** A string constant for a feature to disable WINDOWS_EXPORT_ALL_SYMBOLS. */ public static final String NO_WINDOWS_EXPORT_ALL_SYMBOLS = "no_windows_export_all_symbols"; + /** A string constant for a feature to copy dynamic libraries to the binary's directory. */ + public static final String COPY_DYNAMIC_LIBRARIES_TO_BINARY = "copy_dynamic_libraries_to_binary"; + /** * A string constant for a feature that indicates we are using a toolchain building for Windows. */ |