aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google
diff options
context:
space:
mode:
authorGravatar hlopko <hlopko@google.com>2018-04-03 08:54:05 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-04-03 08:55:55 -0700
commiteba28176a97a0d0ddf2d1ef8505d37f392a2c363 (patch)
treec41a17d6911745d370aa46ba4f195e6a7d90bc37 /src/main/java/com/google
parent3f6c57b3a2c86444ce4be119f51578ab7198071e (diff)
Isolate C++ link build variables
This is a preparation work to expose Variables instance for all link actions to Skylark. Compile build variables were done in https://github.com/bazelbuild/bazel/commit/31ab0b88ec52f293e713b9369ea4a706b6c0a57d. This is also in line with our goal to make build variables more discoverable and better document. RELNOTES: None. PiperOrigin-RevId: 191446799
Diffstat (limited to 'src/main/java/com/google')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java791
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java457
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java230
3 files changed, 755 insertions, 723 deletions
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 7167c49321..3560f6b2d8 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
@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -45,12 +44,11 @@ import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.rules.cpp.CcLinkParams.Linkstamp;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables;
-import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.LibraryToLinkValue;
-import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.SequenceBuilder;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
import com.google.devtools.build.lib.rules.cpp.CppLinkAction.Context;
import com.google.devtools.build.lib.rules.cpp.CppLinkAction.LinkArtifactFactory;
+import com.google.devtools.build.lib.rules.cpp.LibrariesToLinkCollector.CollectedLibrariesToLink;
import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
@@ -71,93 +69,24 @@ import javax.annotation.Nullable;
/** Builder class to construct {@link CppLinkAction}s. */
public class CppLinkActionBuilder {
- /** A build variable for entries in the linker runtime search path (usually set by -rpath flag) */
- public static final String RUNTIME_LIBRARY_SEARCH_DIRECTORIES_VARIABLE =
- "runtime_library_search_directories";
-
- public static final String LIBRARY_SEARCH_DIRECTORIES_VARIABLE = "library_search_directories";
-
- /** A build variable for flags providing files to link as inputs in the linker invocation */
- public static final String LIBRARIES_TO_LINK_VARIABLE = "libraries_to_link";
-
- /**
- * A build variable for thinlto param file produced by thinlto-indexing action and consumed by
- * normal linking actions.
- */
- public static final String THINLTO_PARAM_FILE_VARIABLE = "thinlto_param_file";
-
- public static final String DEF_FILE_PATH_VARIABLE = "def_file_path";
-
- /**
- * A build variable to let thinlto know where it should write linker flags when indexing.
- */
- public static final String THINLTO_INDEXING_PARAM_FILE_VARIABLE = "thinlto_indexing_param_file";
-
- public static final String THINLTO_PREFIX_REPLACE_VARIABLE = "thinlto_prefix_replace";
-
- /**
- * A build variable to let the LTO indexing step know how to map from the minimized bitcode file
- * to the full bitcode file used by the LTO Backends.
- */
- public static final String THINLTO_OBJECT_SUFFIX_REPLACE_VARIABLE =
- "thinlto_object_suffix_replace";
-
- /**
- * A build variable for the path to the merged object file, which is an object file that is
- * created during the LTO indexing step and needs to be passed to the final link.
- */
- public static final String THINLTO_MERGED_OBJECT_FILE_VARIABLE = "thinlto_merged_object_file";
-
- /**
- * A build variable for linker param file created by Bazel to overcome the command line length
- * limit.
- */
- public static final String LINKER_PARAM_FILE_VARIABLE = "linker_param_file";
-
- /** A build variable for the execpath of the output of the linker. */
- public static final String OUTPUT_EXECPATH_VARIABLE = "output_execpath";
-
- /** A build variable setting if interface library should be generated. */
- public static final String GENERATE_INTERFACE_LIBRARY_VARIABLE = "generate_interface_library";
-
- /** A build variable for the path to the interface library builder tool. */
- public static final String INTERFACE_LIBRARY_BUILDER_VARIABLE = "interface_library_builder_path";
-
- /** A build variable for the input for the interface library builder tool. */
- public static final String INTERFACE_LIBRARY_INPUT_VARIABLE = "interface_library_input_path";
-
- /** A build variable for the path where to generate interface library using the builder tool. */
- public static final String INTERFACE_LIBRARY_OUTPUT_VARIABLE = "interface_library_output_path";
-
- /** A build variable for hard-coded linker flags currently only known by bazel. */
- public static final String LEGACY_LINK_FLAGS_VARIABLE = "legacy_link_flags";
-
- /** A build variable giving a path to which to write symbol counts. */
- public static final String SYMBOL_COUNTS_OUTPUT_VARIABLE = "symbol_counts_output";
-
- /** A build variable giving linkstamp paths. */
- public static final String LINKSTAMP_PATHS_VARIABLE = "linkstamp_paths";
-
- /** A build variable whose presence indicates that PIC code should be generated. */
- public static final String FORCE_PIC_VARIABLE = "force_pic";
-
- /** A build variable whose presence indicates that the debug symbols should be stripped. */
- public static final String STRIP_DEBUG_SYMBOLS_VARIABLE = "strip_debug_symbols";
-
- @Deprecated
- public static final String IS_CC_TEST_LINK_ACTION_VARIABLE = "is_cc_test_link_action";
-
- /** A build variable whose presence indicates that this action is a cc_test linking action. */
- public static final String IS_CC_TEST_VARIABLE = "is_cc_test";
-
/**
- * A build variable whose presence indicates that files were compiled with fission (debug
- * info is in .dwo files instead of .o files and linker needs to know).
+ * An implementation of {@link LinkArtifactFactory} that can create artifacts anywhere.
+ *
+ * <p>Necessary when the LTO backend actions of libraries should be shareable, and thus cannot be
+ * under the package directory.
*/
- public static final String IS_USING_FISSION_VARIABLE = "is_using_fission";
-
- @Deprecated
- public static final String IS_NOT_CC_TEST_LINK_ACTION_VARIABLE = "is_not_cc_test_link_action";
+ static final LinkArtifactFactory SHAREABLE_LINK_ARTIFACT_FACTORY =
+ new LinkArtifactFactory() {
+ @Override
+ public Artifact create(
+ RuleContext ruleContext,
+ BuildConfiguration configuration,
+ PathFragment rootRelativePath) {
+ return ruleContext.getShareableArtifact(
+ rootRelativePath,
+ configuration.getBinDirectory(ruleContext.getRule().getRepository()));
+ }
+ };
public static final String SHARED_NONLTO_BACKEND_ROOT_PREFIX = "shared.nonlto";
@@ -534,27 +463,6 @@ public class CppLinkActionBuilder {
return false;
}
- /**
- * An implementation of {@link
- * com.google.devtools.build.lib.rules.cpp.CppLinkAction.LinkArtifactFactory} that can create
- * artifacts anywhere.
- *
- * <p>Necessary when the LTO backend actions of libraries should be shareable, and thus cannot be
- * under the package directory.
- */
- private static final CppLinkAction.LinkArtifactFactory SHAREABLE_LINK_ARTIFACT_FACTORY =
- new CppLinkAction.LinkArtifactFactory() {
- @Override
- public Artifact create(
- RuleContext ruleContext,
- BuildConfiguration configuration,
- PathFragment rootRelativePath) {
- return ruleContext.getShareableArtifact(
- rootRelativePath,
- configuration.getBinDirectory(ruleContext.getRule().getRepository()));
- }
- };
-
/*
* Create an LtoBackendArtifacts object, using the appropriate constructor depending on whether
* the associated ThinLTO link will utilize LTO indexing (therefore unique LTO backend actions),
@@ -1044,38 +952,56 @@ public class CppLinkActionBuilder {
Preconditions.checkState(!isLtoIndexing || allowLtoIndexing);
Preconditions.checkState(allowLtoIndexing || thinltoParamFile == null);
Preconditions.checkState(allowLtoIndexing || thinltoMergedObjectFile == null);
- CppLinkVariablesExtension variablesExtension =
- isLtoIndexing
- ? new CppLinkVariablesExtension(
- configuration,
- needWholeArchive,
- linkerInputs,
- runtimeLinkerInputs,
- /* output= */ null,
- paramFile,
- thinltoParamFile,
- thinltoMergedObjectFile,
- ltoOutputRootPrefix,
- // If we reached here, then allowLtoIndexing must be true (checked above).
- /* allowLtoIndexing= */ true,
- /* interfaceLibraryBuilder= */ null,
- /* interfaceLibraryOutput= */ null,
- mustKeepDebug)
- : new CppLinkVariablesExtension(
- configuration,
- needWholeArchive,
- linkerInputs,
- runtimeLinkerInputs,
- output,
- paramFile,
- thinltoParamFile,
- thinltoMergedObjectFile,
- /* ltoOutputRootPrefix= */ PathFragment.EMPTY_FRAGMENT,
- allowLtoIndexing,
- toolchain.getInterfaceSoBuilder(),
- interfaceOutput,
- mustKeepDebug);
- variablesExtension.addVariables(buildVariablesBuilder);
+ PathFragment solibDir =
+ configuration
+ .getBinDirectory(ruleContext.getRule().getRepository())
+ .getExecPath()
+ .getRelative(toolchain.getSolibDirectory());
+ LibrariesToLinkCollector librariesToLinkCollector =
+ new LibrariesToLinkCollector(
+ isNativeDeps,
+ cppConfiguration,
+ toolchain,
+ runtimeSolibDir,
+ linkType,
+ linkStaticness,
+ output,
+ solibDir,
+ isLtoIndexing,
+ allLtoArtifacts,
+ runtimeLinkerInputs,
+ featureConfiguration,
+ thinltoParamFile,
+ allowLtoIndexing,
+ linkerInputs,
+ needWholeArchive);
+ CollectedLibrariesToLink collectedLibrariesToLink =
+ librariesToLinkCollector.collectLibrariesToLink();
+ Variables variables =
+ LinkBuildVariables.setupVariables(
+ this,
+ configuration,
+ output,
+ paramFile,
+ thinltoParamFile,
+ thinltoMergedObjectFile,
+ mustKeepDebug,
+ symbolCounts,
+ cppConfiguration,
+ toolchain,
+ featureConfiguration,
+ useTestOnlyFlags,
+ isLtoIndexing,
+ toolchain.getInterfaceSoBuilder(),
+ interfaceOutput,
+ ltoOutputRootPrefix,
+ defFile,
+ fdoSupport,
+ collectedLibrariesToLink.getRuntimeLibrarySearchDirectories(),
+ collectedLibrariesToLink.getLibrariesToLink(),
+ collectedLibrariesToLink.getLibrarySearchDirectories());
+
+ buildVariablesBuilder.addAllNonTransitive(variables);
for (VariablesExtension extraVariablesExtension : variablesExtensions) {
extraVariablesExtension.addVariables(buildVariablesBuilder);
}
@@ -1144,7 +1070,7 @@ public class CppLinkActionBuilder {
Variables patchedVariables =
new Variables.Builder(buildVariables)
.addStringSequenceVariable(
- CppLinkActionBuilder.LEGACY_LINK_FLAGS_VARIABLE,
+ LinkBuildVariables.LEGACY_LINK_FLAGS.getVariableName(),
getToolchainFlags(features, linkoptsForVariables))
.build();
@@ -1701,585 +1627,4 @@ public class CppLinkActionBuilder {
this.linkActionOutputs.add(output);
return this;
}
-
- private static class LinkArgCollector {
- ImmutableSet<String> runtimeLibrarySearchDirectories;
- ImmutableSet<String> librarySearchDirectories;
- SequenceBuilder librariesToLink;
-
- public void setRuntimeLibrarySearchDirectories(
- ImmutableSet<String> runtimeLibrarySearchDirectories) {
- this.runtimeLibrarySearchDirectories = runtimeLibrarySearchDirectories;
- }
-
- public void setLibrariesToLink(SequenceBuilder librariesToLink) {
- this.librariesToLink = librariesToLink;
- }
-
- public void setLibrarySearchDirectories(ImmutableSet<String> librarySearchDirectories) {
- this.librarySearchDirectories = librarySearchDirectories;
- }
-
- public ImmutableSet<String> getRuntimeLibrarySearchDirectories() {
- return runtimeLibrarySearchDirectories;
- }
-
- public SequenceBuilder getLibrariesToLink() {
- return librariesToLink;
- }
-
- public ImmutableSet<String> getLibrarySearchDirectories() {
- return librarySearchDirectories;
- }
-
- }
-
- private class CppLinkVariablesExtension implements VariablesExtension {
-
- private final BuildConfiguration configuration;
- private final boolean needWholeArchive;
- private final Iterable<LinkerInput> linkerInputs;
- private final ImmutableList<LinkerInput> runtimeLinkerInputs;
- private final Artifact outputArtifact;
- private final Artifact interfaceLibraryBuilder;
- private final Artifact interfaceLibraryOutput;
- private final Artifact paramFile;
- private final Artifact thinltoParamFile;
- private final Artifact thinltoMergedObjectFile;
- private final PathFragment ltoOutputRootPrefix;
- private final boolean allowLtoIndexing;
- private final boolean mustKeepDebug;
-
- private final LinkArgCollector linkArgCollector = new LinkArgCollector();
-
- public CppLinkVariablesExtension(
- BuildConfiguration configuration,
- boolean needWholeArchive,
- Iterable<LinkerInput> linkerInputs,
- ImmutableList<LinkerInput> runtimeLinkerInputs,
- Artifact output,
- Artifact paramFile,
- Artifact thinltoParamFile,
- Artifact thinltoMergedObjectFile,
- PathFragment ltoOutputRootPrefix,
- boolean allowLtoIndexing,
- Artifact interfaceLibraryBuilder,
- Artifact interfaceLibraryOutput,
- boolean mustKeepDebug) {
- this.configuration = configuration;
- this.needWholeArchive = needWholeArchive;
- this.linkerInputs = linkerInputs;
- this.runtimeLinkerInputs = runtimeLinkerInputs;
- this.outputArtifact = output;
- this.interfaceLibraryBuilder = interfaceLibraryBuilder;
- this.interfaceLibraryOutput = interfaceLibraryOutput;
- this.paramFile = paramFile;
- this.thinltoParamFile = thinltoParamFile;
- this.thinltoMergedObjectFile = thinltoMergedObjectFile;
- this.ltoOutputRootPrefix = ltoOutputRootPrefix;
- this.allowLtoIndexing = allowLtoIndexing;
- this.mustKeepDebug = mustKeepDebug;
-
- addInputFileLinkOptions(linkArgCollector);
- }
-
- @Override
- public void addVariables(Variables.Builder buildVariables) {
-
- // symbol counting
- if (symbolCounts != null) {
- buildVariables.addStringVariable(
- SYMBOL_COUNTS_OUTPUT_VARIABLE, symbolCounts.getExecPathString());
- }
-
- // pic
- if (cppConfiguration.forcePic()) {
- buildVariables.addStringVariable(FORCE_PIC_VARIABLE, "");
- }
-
- if (!mustKeepDebug && cppConfiguration.shouldStripBinaries()) {
- buildVariables.addStringVariable(STRIP_DEBUG_SYMBOLS_VARIABLE, "");
- }
-
- if (getLinkType().staticness().equals(Staticness.DYNAMIC)
- && CppHelper.shouldCreatePerObjectDebugInfo(
- cppConfiguration, toolchain, featureConfiguration)) {
- buildVariables.addStringVariable(IS_USING_FISSION_VARIABLE, "");
- }
-
- if (useTestOnlyFlags()) {
- buildVariables.addIntegerVariable(IS_CC_TEST_VARIABLE, 1);
- buildVariables.addStringVariable(IS_CC_TEST_LINK_ACTION_VARIABLE, "");
- } else {
- buildVariables.addIntegerVariable(IS_CC_TEST_VARIABLE, 0);
- buildVariables.addStringVariable(IS_NOT_CC_TEST_LINK_ACTION_VARIABLE, "");
- }
-
- if (linkArgCollector.getRuntimeLibrarySearchDirectories() != null) {
- buildVariables.addStringSequenceVariable(
- RUNTIME_LIBRARY_SEARCH_DIRECTORIES_VARIABLE,
- linkArgCollector.getRuntimeLibrarySearchDirectories());
- }
-
- buildVariables.addCustomBuiltVariable(
- LIBRARIES_TO_LINK_VARIABLE, linkArgCollector.getLibrariesToLink());
- // TODO(b/72803478): Remove once existing crosstools have been migrated
- buildVariables.addStringVariable("libs_to_link_dont_emit_objects_for_archiver", "");
-
- buildVariables.addStringSequenceVariable(
- LIBRARY_SEARCH_DIRECTORIES_VARIABLE, linkArgCollector.getLibrarySearchDirectories());
-
- if (paramFile != null) {
- buildVariables.addStringVariable(LINKER_PARAM_FILE_VARIABLE, paramFile.getExecPathString());
- }
-
- // output exec path
- if (outputArtifact != null) {
- buildVariables.addStringVariable(
- OUTPUT_EXECPATH_VARIABLE, outputArtifact.getExecPathString());
- }
-
- if (isLtoIndexing()) {
- if (thinltoParamFile != null) {
- // This is a lto-indexing action and we want it to populate param file.
- buildVariables.addStringVariable(
- THINLTO_INDEXING_PARAM_FILE_VARIABLE, thinltoParamFile.getExecPathString());
- // TODO(b/33846234): Remove once all the relevant crosstools don't depend on the variable.
- buildVariables.addStringVariable(
- "thinlto_optional_params_file", "=" + thinltoParamFile.getExecPathString());
- } else {
- buildVariables.addStringVariable(THINLTO_INDEXING_PARAM_FILE_VARIABLE, "");
- // TODO(b/33846234): Remove once all the relevant crosstools don't depend on the variable.
- buildVariables.addStringVariable("thinlto_optional_params_file", "");
- }
- buildVariables.addStringVariable(
- THINLTO_PREFIX_REPLACE_VARIABLE,
- configuration.getBinDirectory().getExecPathString()
- + ";"
- + configuration.getBinDirectory().getExecPath().getRelative(ltoOutputRootPrefix));
- buildVariables.addStringVariable(
- THINLTO_OBJECT_SUFFIX_REPLACE_VARIABLE,
- Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions())
- + ";"
- + Iterables.getOnlyElement(CppFileTypes.OBJECT_FILE.getExtensions()));
- if (thinltoMergedObjectFile != null) {
- buildVariables.addStringVariable(
- THINLTO_MERGED_OBJECT_FILE_VARIABLE, thinltoMergedObjectFile.getExecPathString());
- }
- } else {
- if (thinltoParamFile != null) {
- // This is a normal link action and we need to use param file created by lto-indexing.
- buildVariables.addStringVariable(
- THINLTO_PARAM_FILE_VARIABLE, thinltoParamFile.getExecPathString());
- }
- }
- boolean shouldGenerateInterfaceLibrary =
- outputArtifact != null
- && interfaceLibraryBuilder != null
- && interfaceLibraryOutput != null;
- buildVariables.addStringVariable(
- GENERATE_INTERFACE_LIBRARY_VARIABLE, shouldGenerateInterfaceLibrary ? "yes" : "no");
- buildVariables.addStringVariable(
- INTERFACE_LIBRARY_BUILDER_VARIABLE,
- shouldGenerateInterfaceLibrary ? interfaceLibraryBuilder.getExecPathString() : "ignored");
- buildVariables.addStringVariable(
- INTERFACE_LIBRARY_INPUT_VARIABLE,
- shouldGenerateInterfaceLibrary ? outputArtifact.getExecPathString() : "ignored");
- buildVariables.addStringVariable(
- INTERFACE_LIBRARY_OUTPUT_VARIABLE,
- shouldGenerateInterfaceLibrary ? interfaceLibraryOutput.getExecPathString() : "ignored");
-
- if (defFile != null) {
- buildVariables.addStringVariable(DEF_FILE_PATH_VARIABLE, defFile.getExecPathString());
- }
-
- fdoSupport.getFdoSupport().getLinkOptions(featureConfiguration, buildVariables);
- }
-
- private boolean isLtoIndexing() {
- return !ltoOutputRootPrefix.equals(PathFragment.EMPTY_FRAGMENT);
- }
-
- private boolean isSharedNativeLibrary() {
- return isNativeDeps && cppConfiguration.shareNativeDeps();
- }
-
- /**
- * When linking a shared library fully or mostly static then we need to link in *all* dependent
- * files, not just what the shared library needs for its own code. This is done by wrapping all
- * objects/libraries with -Wl,-whole-archive and -Wl,-no-whole-archive. For this case the
- * globalNeedWholeArchive parameter must be set to true. Otherwise only library objects (.lo)
- * need to be wrapped with -Wl,-whole-archive and -Wl,-no-whole-archive.
- *
- * <p>TODO: Factor out of the bazel binary into build variables for crosstool action_configs.
- */
- private void addInputFileLinkOptions(LinkArgCollector linkArgCollector) {
- ImmutableSet.Builder<String> librarySearchDirectories = ImmutableSet.builder();
- ImmutableSet.Builder<String> runtimeRpathRoots = ImmutableSet.builder();
- ImmutableSet.Builder<String> rpathRootsForExplicitSoDeps = ImmutableSet.builder();
-
- // List of command line parameters that need to be placed *outside* of
- // --whole-archive ... --no-whole-archive.
- SequenceBuilder librariesToLink = new SequenceBuilder();
-
- PathFragment solibDir =
- configuration
- .getBinDirectory(ruleContext.getRule().getRepository())
- .getExecPath()
- .getRelative(toolchain.getSolibDirectory());
- String runtimeSolibName = runtimeSolibDir != null ? runtimeSolibDir.getBaseName() : null;
- boolean runtimeRpath =
- runtimeSolibDir != null
- && (linkType.isDynamicLibrary()
- || (linkType == LinkTargetType.EXECUTABLE
- && linkStaticness == LinkStaticness.DYNAMIC));
-
- if (runtimeRpath) {
- if (isNativeDeps) {
- runtimeRpathRoots.add(".");
- }
- runtimeRpathRoots.add(runtimeSolibName + "/");
- }
-
- String rpathRoot;
- // Calculate the correct relative value for the "-rpath" link option (which sets
- // the search path for finding shared libraries).
- if (isSharedNativeLibrary()) {
- // For shared native libraries, special symlinking is applied to ensure C++
- // runtimes are available under $ORIGIN/_solib_[arch]. So we set the RPATH to find
- // them.
- //
- // Note that we have to do this because $ORIGIN points to different paths for
- // different targets. In other words, blaze-bin/d1/d2/d3/a_shareddeps.so and
- // blaze-bin/d4/b_shareddeps.so have different path depths. The first could
- // reference a standard blaze-bin/_solib_[arch] via $ORIGIN/../../../_solib[arch],
- // and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared
- // artifact, both are symlinks to the same place, so
- // there's no *one* RPATH setting that fits all targets involved in the sharing.
- rpathRoot = toolchain.getSolibDirectory() + "/";
- if (runtimeRpath) {
- runtimeRpathRoots.add("../" + runtimeSolibName + "/");
- }
- } else {
- // For all other links, calculate the relative path from the output file to _solib_[arch]
- // (the directory where all shared libraries are stored, which resides under the blaze-bin
- // directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be
- // "../../_solib_[arch]".
- if (runtimeRpath) {
- runtimeRpathRoots.add(
- Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1)
- + runtimeSolibName
- + "/");
- }
-
- rpathRoot =
- Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1)
- + toolchain.getSolibDirectory()
- + "/";
-
- if (isNativeDeps) {
- // We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to
- // the package directory)
- if (runtimeRpath) {
- runtimeRpathRoots.add("../" + runtimeSolibName + "/");
- }
- }
- }
-
- Map<Artifact, Artifact> ltoMap = generateLtoMap();
- boolean includeSolibDir =
- addLinkerInputs(
- librarySearchDirectories,
- rpathRootsForExplicitSoDeps,
- librariesToLink,
- solibDir,
- rpathRoot,
- ltoMap);
- boolean includeRuntimeSolibDir =
- addRuntimeLinkerInputs(
- librarySearchDirectories,
- rpathRootsForExplicitSoDeps,
- librariesToLink,
- solibDir,
- rpathRoot,
- ltoMap);
- Preconditions.checkState(
- ltoMap == null || ltoMap.isEmpty(), "Still have LTO objects left: %s", ltoMap);
-
- ImmutableSet.Builder<String> runtimeLibrarySearchDirectories = ImmutableSet.builder();
- // rpath ordering matters for performance; first add the one where most libraries are found.
- if (includeSolibDir) {
- runtimeLibrarySearchDirectories.add(rpathRoot);
- }
- runtimeLibrarySearchDirectories.addAll(rpathRootsForExplicitSoDeps.build());
- if (includeRuntimeSolibDir) {
- runtimeLibrarySearchDirectories.addAll(runtimeRpathRoots.build());
- }
-
- linkArgCollector.setLibrarySearchDirectories(librarySearchDirectories.build());
- linkArgCollector.setRuntimeLibrarySearchDirectories(runtimeLibrarySearchDirectories.build());
- linkArgCollector.setLibrariesToLink(librariesToLink);
- }
-
- private Map<Artifact, Artifact> generateLtoMap() {
- if (isLtoIndexing || allLtoArtifacts == null) {
- return null;
- }
- // TODO(bazel-team): The LTO final link can only work if there are individual .o files on
- // the command line. Rather than crashing, this should issue a nice error. We will get
- // this by
- // 1) moving supports_start_end_lib to a toolchain feature
- // 2) having thin_lto require start_end_lib
- // As a bonus, we can rephrase --nostart_end_lib as --features=-start_end_lib and get rid
- // of a command line option.
-
- Preconditions.checkState(CppHelper.useStartEndLib(cppConfiguration, toolchain));
- Map<Artifact, Artifact> ltoMap = new HashMap<>();
- for (LtoBackendArtifacts l : allLtoArtifacts) {
- ltoMap.put(l.getBitcodeFile(), l.getObjectFile());
- }
- return ltoMap;
- }
-
- private boolean addRuntimeLinkerInputs(
- Builder<String> librarySearchDirectories,
- Builder<String> rpathRootsForExplicitSoDeps,
- SequenceBuilder librariesToLink,
- PathFragment solibDir,
- String rpathRoot,
- Map<Artifact, Artifact> ltoMap) {
- boolean includeRuntimeSolibDir = false;
- for (LinkerInput input : runtimeLinkerInputs) {
- if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
- || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY) {
- PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory();
- if (!featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) {
- Preconditions.checkState(
- runtimeSolibDir != null && libDir.equals(runtimeSolibDir),
- "Artifact '%s' is not under directory '%s'.",
- input.getArtifact(),
- solibDir);
- includeRuntimeSolibDir = true;
- }
- addDynamicInputLinkOptions(
- input,
- librariesToLink,
- librarySearchDirectories,
- rpathRootsForExplicitSoDeps,
- solibDir,
- rpathRoot);
- } else {
- addStaticInputLinkOptions(input, librariesToLink, true, ltoMap);
- }
- }
- return includeRuntimeSolibDir;
- }
-
- private boolean addLinkerInputs(
- Builder<String> librarySearchDirectories,
- Builder<String> rpathEntries,
- SequenceBuilder librariesToLink,
- PathFragment solibDir,
- String rpathRoot,
- Map<Artifact, Artifact> ltoMap) {
- boolean includeSolibDir = false;
- for (LinkerInput input : linkerInputs) {
- if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
- || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY) {
- PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory();
- // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, dynamic libraries are not symlinked
- // under solibDir, so don't check it and don't include solibDir.
- 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;
- }
- }
- addDynamicInputLinkOptions(
- input,
- librariesToLink,
- librarySearchDirectories,
- rpathEntries,
- solibDir,
- rpathRoot);
- } else {
- addStaticInputLinkOptions(input, librariesToLink, false, ltoMap);
- }
- }
- return includeSolibDir;
- }
-
- /**
- * Adds command-line options for a dynamic library input file into options and libOpts.
- *
- * @param librariesToLink - a collection that will be exposed as a build variable.
- */
- private void addDynamicInputLinkOptions(
- LinkerInput input,
- SequenceBuilder librariesToLink,
- ImmutableSet.Builder<String> librarySearchDirectories,
- ImmutableSet.Builder<String> rpathRootsForExplicitSoDeps,
- PathFragment solibDir,
- String rpathRoot) {
- Preconditions.checkState(
- input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
- || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY);
- Preconditions.checkState(
- !Link.useStartEndLib(input, CppHelper.getArchiveType(cppConfiguration, toolchain)));
-
- Artifact inputArtifact = input.getArtifact();
- PathFragment libDir = inputArtifact.getExecPath().getParentDirectory();
- if (!libDir.equals(solibDir)
- && (runtimeSolibDir == null || !runtimeSolibDir.equals(libDir))) {
- String dotdots = "";
- PathFragment commonParent = solibDir;
- while (!libDir.startsWith(commonParent)) {
- dotdots += "../";
- commonParent = commonParent.getParentDirectory();
- }
-
- rpathRootsForExplicitSoDeps.add(
- rpathRoot + dotdots + libDir.relativeTo(commonParent).getPathString());
- }
-
- librarySearchDirectories.add(
- inputArtifact.getExecPath().getParentDirectory().getPathString());
-
- String name = inputArtifact.getFilename();
- if (CppFileTypes.SHARED_LIBRARY.matches(name)) {
- // Use normal shared library resolution rules for shared libraries.
- String libName = name.replaceAll("(^lib|\\.(so|dylib)$)", "");
- librariesToLink.addValue(
- LibraryToLinkValue.forDynamicLibrary(libName));
- } else if (CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(name)) {
- // Versioned shared libraries require the exact library filename, e.g.:
- // -lfoo -> libfoo.so
- // -l:libfoo.so.1 -> libfoo.so.1
- librariesToLink.addValue(
- LibraryToLinkValue.forVersionedDynamicLibrary(name));
- } else {
- // Interface shared objects have a non-standard extension
- // that the linker won't be able to find. So use the
- // filename directly rather than a -l option. Since the
- // library has an SONAME attribute, this will work fine.
- librariesToLink.addValue(
- LibraryToLinkValue.forInterfaceLibrary(inputArtifact.getExecPathString()));
- }
- }
-
- /**
- * Returns true if this artifact is produced from a bitcode file that will be input to the LTO
- * indexing step, in which case that step will add it to the generated thinltoParamFile for
- * inclusion in the final link step if the linker decides to include it.
- *
- * @param a is an artifact produced by an LTO backend.
- */
- private boolean handledByLtoIndexing(Artifact a) {
- // If no LTO indexing is allowed for this link, then none are handled by LTO indexing.
- // Otherwise, this may be from a linkstatic library that we decided not to include in
- // LTO indexing because we are linking a test, to improve scalability when linking many tests.
- return allowLtoIndexing
- && !a.getRootRelativePath()
- .startsWith(PathFragment.create(SHARED_NONLTO_BACKEND_ROOT_PREFIX));
- }
-
- /**
- * Adds command-line options for a static library or non-library input into options.
- *
- * @param librariesToLink - a collection that will be exposed as a build variable.
- * @param ltoMap is a mutable list of exec paths that should be on the command-line, which must
- */
- private void addStaticInputLinkOptions(
- LinkerInput input,
- SequenceBuilder librariesToLink,
- boolean isRuntimeLinkerInput,
- @Nullable Map<Artifact, Artifact> ltoMap) {
- ArtifactCategory artifactCategory = input.getArtifactCategory();
- Preconditions.checkState(artifactCategory != ArtifactCategory.DYNAMIC_LIBRARY);
- // If we had any LTO artifacts, ltoMap whould be non-null. In that case,
- // we should have created a thinltoParamFile which the LTO indexing
- // step will populate with the exec paths that correspond to the LTO
- // artifacts that the linker decided to include based on symbol resolution.
- // Those files will be included directly in the link (and not wrapped
- // in --start-lib/--end-lib) to ensure consistency between the two link
- // steps.
- Preconditions.checkState(ltoMap == null || thinltoParamFile != null || !allowLtoIndexing);
-
- // start-lib/end-lib library: adds its input object files.
- if (Link.useStartEndLib(input, CppHelper.getArchiveType(cppConfiguration, toolchain))) {
- Iterable<Artifact> archiveMembers = input.getObjectFiles();
- if (!Iterables.isEmpty(archiveMembers)) {
- ImmutableList.Builder<Artifact> nonLtoArchiveMembersBuilder = ImmutableList.builder();
- for (Artifact member : archiveMembers) {
- Artifact a;
- if (ltoMap != null && (a = ltoMap.remove(member)) != null) {
- // When ltoMap is non-null the backend artifact may be missing due to libraries that
- // list .o files explicitly, or generate .o files from assembler.
- if (handledByLtoIndexing(a)) {
- // The LTO artifacts that should be included in the final link
- // are listed in the thinltoParamFile, generated by the LTO indexing.
- continue;
- }
- // No LTO indexing step, so use the LTO backend's generated artifact directly
- // instead of the bitcode object.
- member = a;
- }
- nonLtoArchiveMembersBuilder.add(member);
- }
- ImmutableList<Artifact> nonLtoArchiveMembers = nonLtoArchiveMembersBuilder.build();
- if (!nonLtoArchiveMembers.isEmpty()) {
- boolean inputIsWholeArchive = !isRuntimeLinkerInput && needWholeArchive;
- librariesToLink.addValue(
- LibraryToLinkValue.forObjectFileGroup(nonLtoArchiveMembers, inputIsWholeArchive));
- }
- }
- } else {
- Preconditions.checkArgument(
- artifactCategory.equals(ArtifactCategory.OBJECT_FILE)
- || artifactCategory.equals(ArtifactCategory.STATIC_LIBRARY)
- || artifactCategory.equals(ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
- boolean isAlwaysLinkStaticLibrary =
- artifactCategory == ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY;
- boolean inputIsWholeArchive =
- (!isRuntimeLinkerInput && (isAlwaysLinkStaticLibrary || needWholeArchive))
- || (isRuntimeLinkerInput && isAlwaysLinkStaticLibrary && !needWholeArchive);
-
- Artifact inputArtifact = input.getArtifact();
- Artifact a;
- if (ltoMap != null && (a = ltoMap.remove(inputArtifact)) != null) {
- if (handledByLtoIndexing(a)) {
- // The LTO artifacts that should be included in the final link
- // are listed in the thinltoParamFile, generated by the LTO indexing.
- return;
- }
- // No LTO indexing step, so use the LTO backend's generated artifact directly
- // instead of the bitcode object.
- inputArtifact = a;
- }
-
- String name;
- if (input.isFake()) {
- name = Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString();
- } else {
- name = inputArtifact.getExecPathString();
- }
-
- if (artifactCategory.equals(ArtifactCategory.OBJECT_FILE)) {
- if (inputArtifact.isTreeArtifact()) {
- librariesToLink.addValue(
- LibraryToLinkValue.forObjectFileGroup(
- ImmutableList.<Artifact>of(inputArtifact), inputIsWholeArchive));
- } else {
- librariesToLink.addValue(LibraryToLinkValue.forObjectFile(name, inputIsWholeArchive));
- }
- } else {
- librariesToLink.addValue(LibraryToLinkValue.forStaticLibrary(name, inputIsWholeArchive));
- }
- }
- }
- }
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java
new file mode 100644
index 0000000000..dc5415f049
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LibrariesToLinkCollector.java
@@ -0,0 +1,457 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.LibraryToLinkValue;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.SequenceBuilder;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Class that goes over linker inputs and produces {@link LibraryToLinkValue}s */
+public class LibrariesToLinkCollector {
+
+ private final boolean isNativeDeps;
+ private final PathFragment runtimeSolibDir;
+ private final CppConfiguration cppConfiguration;
+ private final CcToolchainProvider ccToolchainProvider;
+ private final Artifact outputArtifact;
+ private final boolean isLtoIndexing;
+ private final PathFragment solibDir;
+ private final Iterable<? extends LinkerInput> linkerInputs;
+ private final Iterable<LtoBackendArtifacts> allLtoArtifacts;
+ private final boolean allowLtoIndexing;
+ private final Artifact thinltoParamFile;
+ private final Iterable<? extends LinkerInput> runtimeLinkerInputs;
+ private final FeatureConfiguration featureConfiguration;
+ private final boolean needWholeArchive;
+ private final String rpathRoot;
+ private final boolean runtimeRpath;
+ private final Map<Artifact, Artifact> ltoMap;
+
+ public LibrariesToLinkCollector(
+ boolean isNativeDeps,
+ CppConfiguration cppConfiguration,
+ CcToolchainProvider toolchain,
+ PathFragment runtimeSolibDir,
+ LinkTargetType linkType,
+ LinkStaticness linkStaticness,
+ Artifact output,
+ PathFragment solibDir,
+ boolean isLtoIndexing,
+ Iterable<LtoBackendArtifacts> allLtoArtifacts,
+ ImmutableList<LinkerInput> runtimeLinkerInputs,
+ FeatureConfiguration featureConfiguration,
+ Artifact thinltoParamFile,
+ boolean allowLtoIndexing,
+ Iterable<LinkerInput> linkerInputs,
+ boolean needWholeArchive) {
+ this.isNativeDeps = isNativeDeps;
+ this.cppConfiguration = cppConfiguration;
+ this.ccToolchainProvider = toolchain;
+ this.runtimeSolibDir = runtimeSolibDir;
+ this.outputArtifact = output;
+ this.solibDir = solibDir;
+ this.isLtoIndexing = isLtoIndexing;
+ this.allLtoArtifacts = allLtoArtifacts;
+ this.runtimeLinkerInputs = runtimeLinkerInputs;
+ this.featureConfiguration = featureConfiguration;
+ this.thinltoParamFile = thinltoParamFile;
+ this.allowLtoIndexing = allowLtoIndexing;
+ this.linkerInputs = linkerInputs;
+ this.needWholeArchive = needWholeArchive;
+
+ runtimeRpath =
+ runtimeSolibDir != null
+ && (linkType.isDynamicLibrary()
+ || (linkType == LinkTargetType.EXECUTABLE
+ && linkStaticness == LinkStaticness.DYNAMIC));
+
+ // Calculate the correct relative value for the "-rpath" link option (which sets
+ // the search path for finding shared libraries).
+ if (isNativeDeps && cppConfiguration.shareNativeDeps()) {
+ // For shared native libraries, special symlinking is applied to ensure C++
+ // runtimes are available under $ORIGIN/_solib_[arch]. So we set the RPATH to find
+ // them.
+ //
+ // Note that we have to do this because $ORIGIN points to different paths for
+ // different targets. In other words, blaze-bin/d1/d2/d3/a_shareddeps.so and
+ // blaze-bin/d4/b_shareddeps.so have different path depths. The first could
+ // reference a standard blaze-bin/_solib_[arch] via $ORIGIN/../../../_solib[arch],
+ // and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared
+ // artifact, both are symlinks to the same place, so
+ // there's no *one* RPATH setting that fits all targets involved in the sharing.
+ rpathRoot = ccToolchainProvider.getSolibDirectory() + "/";
+ } else {
+ rpathRoot =
+ Strings.repeat("../", outputArtifact.getRootRelativePath().segmentCount() - 1)
+ + ccToolchainProvider.getSolibDirectory()
+ + "/";
+ }
+
+ ltoMap = generateLtoMap();
+ }
+
+ /**
+ * Result of {@link LibrariesToLinkCollector#collectLibrariesToLink()}. Provides access to
+ * computed sequence of {@link LibraryToLinkValue}s and accompanying library search directories.
+ */
+ public static class CollectedLibrariesToLink {
+ private final SequenceBuilder librariesToLink;
+ private final ImmutableSet<String> librarySearchDirectories;
+ private final ImmutableSet<String> runtimeLibrarySearchDirectories;
+
+ public CollectedLibrariesToLink(
+ SequenceBuilder librariesToLink,
+ ImmutableSet<String> librarySearchDirectories,
+ ImmutableSet<String> runtimeLibrarySearchDirectories) {
+ this.librariesToLink = librariesToLink;
+ this.librarySearchDirectories = librarySearchDirectories;
+ this.runtimeLibrarySearchDirectories = runtimeLibrarySearchDirectories;
+ }
+
+ public SequenceBuilder getLibrariesToLink() {
+ return librariesToLink;
+ }
+
+ public ImmutableSet<String> getLibrarySearchDirectories() {
+ return librarySearchDirectories;
+ }
+
+ public ImmutableSet<String> getRuntimeLibrarySearchDirectories() {
+ return runtimeLibrarySearchDirectories;
+ }
+ }
+
+ /**
+ * When linking a shared library fully or mostly static then we need to link in *all* dependent
+ * files, not just what the shared library needs for its own code. This is done by wrapping all
+ * objects/libraries with -Wl,-whole-archive and -Wl,-no-whole-archive. For this case the
+ * globalNeedWholeArchive parameter must be set to true. Otherwise only library objects (.lo) need
+ * to be wrapped with -Wl,-whole-archive and -Wl,-no-whole-archive.
+ *
+ * <p>TODO: Factor out of the bazel binary into build variables for crosstool action_configs.
+ */
+ public CollectedLibrariesToLink collectLibrariesToLink() {
+ Builder<String> librarySearchDirectories = ImmutableSet.builder();
+ Builder<String> runtimeLibrarySearchDirectories = ImmutableSet.builder();
+ Builder<String> rpathRootsForExplicitSoDeps = ImmutableSet.builder();
+ // List of command line parameters that need to be placed *outside* of
+ // --whole-archive ... --no-whole-archive.
+ SequenceBuilder librariesToLink = new SequenceBuilder();
+
+ String runtimeSolibName = runtimeSolibDir != null ? runtimeSolibDir.getBaseName() : null;
+ if (isNativeDeps && cppConfiguration.shareNativeDeps()) {
+ if (runtimeRpath) {
+ runtimeLibrarySearchDirectories.add("../" + runtimeSolibName + "/");
+ }
+ } else {
+ // For all other links, calculate the relative path from the output file to _solib_[arch]
+ // (the directory where all shared libraries are stored, which resides under the blaze-bin
+ // directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be
+ // "../../_solib_[arch]".
+ if (runtimeRpath) {
+ runtimeLibrarySearchDirectories.add(
+ Strings.repeat("../", outputArtifact.getRootRelativePath().segmentCount() - 1)
+ + runtimeSolibName
+ + "/");
+ }
+ if (isNativeDeps) {
+ // We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to
+ // the package directory)
+ if (runtimeRpath) {
+ runtimeLibrarySearchDirectories.add("../" + runtimeSolibName + "/");
+ }
+ }
+ }
+
+ if (runtimeRpath) {
+ if (isNativeDeps) {
+ runtimeLibrarySearchDirectories.add(".");
+ }
+ runtimeLibrarySearchDirectories.add(runtimeSolibName + "/");
+ }
+
+ boolean includeSolibDir =
+ addLinkerInputs(librarySearchDirectories, rpathRootsForExplicitSoDeps, librariesToLink);
+ boolean includeRuntimeSolibDir =
+ addRuntimeLinkerInputs(
+ librarySearchDirectories, rpathRootsForExplicitSoDeps, librariesToLink);
+ Preconditions.checkState(
+ ltoMap == null || ltoMap.isEmpty(), "Still have LTO objects left: %s", ltoMap);
+
+ Builder<String> allRuntimeLibrarySearchDirectories = ImmutableSet.builder();
+ // rpath ordering matters for performance; first add the one where most libraries are found.
+ if (includeSolibDir) {
+ allRuntimeLibrarySearchDirectories.add(rpathRoot);
+ }
+ allRuntimeLibrarySearchDirectories.addAll(rpathRootsForExplicitSoDeps.build());
+ if (includeRuntimeSolibDir) {
+ allRuntimeLibrarySearchDirectories.addAll(runtimeLibrarySearchDirectories.build());
+ }
+
+ return new CollectedLibrariesToLink(
+ librariesToLink,
+ librarySearchDirectories.build(),
+ allRuntimeLibrarySearchDirectories.build());
+ }
+
+ private boolean addLinkerInputs(
+ Builder<String> librarySearchDirectories,
+ Builder<String> rpathEntries,
+ SequenceBuilder librariesToLink) {
+ boolean includeSolibDir = false;
+ for (LinkerInput input : linkerInputs) {
+ if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
+ || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY) {
+ PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory();
+ // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, dynamic libraries are not symlinked
+ // under solibDir, so don't check it and don't include solibDir.
+ 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;
+ }
+ }
+ addDynamicInputLinkOptions(input, librariesToLink, librarySearchDirectories, rpathEntries);
+ } else {
+ addStaticInputLinkOptions(input, librariesToLink, false);
+ }
+ }
+ return includeSolibDir;
+ }
+
+ /**
+ * Adds command-line options for a dynamic library input file into options and libOpts.
+ *
+ * @param librariesToLink - a collection that will be exposed as a build variable.
+ */
+ private void addDynamicInputLinkOptions(
+ LinkerInput input,
+ SequenceBuilder librariesToLink,
+ Builder<String> librarySearchDirectories,
+ Builder<String> rpathRootsForExplicitSoDeps) {
+ Preconditions.checkState(
+ input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
+ || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY);
+ Preconditions.checkState(
+ !Link.useStartEndLib(
+ input, CppHelper.getArchiveType(cppConfiguration, ccToolchainProvider)));
+
+ Artifact inputArtifact = input.getArtifact();
+ PathFragment libDir = inputArtifact.getExecPath().getParentDirectory();
+ if (!libDir.equals(solibDir) && (runtimeSolibDir == null || !runtimeSolibDir.equals(libDir))) {
+ String dotdots = "";
+ PathFragment commonParent = solibDir;
+ while (!libDir.startsWith(commonParent)) {
+ dotdots += "../";
+ commonParent = commonParent.getParentDirectory();
+ }
+
+ rpathRootsForExplicitSoDeps.add(
+ rpathRoot + dotdots + libDir.relativeTo(commonParent).getPathString());
+ }
+
+ librarySearchDirectories.add(inputArtifact.getExecPath().getParentDirectory().getPathString());
+
+ String name = inputArtifact.getFilename();
+ if (CppFileTypes.SHARED_LIBRARY.matches(name)) {
+ // Use normal shared library resolution rules for shared libraries.
+ String libName = name.replaceAll("(^lib|\\.(so|dylib)$)", "");
+ librariesToLink.addValue(LibraryToLinkValue.forDynamicLibrary(libName));
+ } else if (CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(name)) {
+ // Versioned shared libraries require the exact library filename, e.g.:
+ // -lfoo -> libfoo.so
+ // -l:libfoo.so.1 -> libfoo.so.1
+ librariesToLink.addValue(LibraryToLinkValue.forVersionedDynamicLibrary(name));
+ } else {
+ // Interface shared objects have a non-standard extension
+ // that the linker won't be able to find. So use the
+ // filename directly rather than a -l option. Since the
+ // library has an SONAME attribute, this will work fine.
+ librariesToLink.addValue(
+ LibraryToLinkValue.forInterfaceLibrary(inputArtifact.getExecPathString()));
+ }
+ }
+
+ /**
+ * Adds command-line options for a static library or non-library input into options.
+ *
+ * @param librariesToLink - a collection that will be exposed as a build variable.
+ */
+ private void addStaticInputLinkOptions(
+ LinkerInput input, SequenceBuilder librariesToLink, boolean isRuntimeLinkerInput) {
+ ArtifactCategory artifactCategory = input.getArtifactCategory();
+ Preconditions.checkState(artifactCategory != ArtifactCategory.DYNAMIC_LIBRARY);
+ // If we had any LTO artifacts, ltoMap whould be non-null. In that case,
+ // we should have created a thinltoParamFile which the LTO indexing
+ // step will populate with the exec paths that correspond to the LTO
+ // artifacts that the linker decided to include based on symbol resolution.
+ // Those files will be included directly in the link (and not wrapped
+ // in --start-lib/--end-lib) to ensure consistency between the two link
+ // steps.
+ Preconditions.checkState(ltoMap == null || thinltoParamFile != null || !allowLtoIndexing);
+
+ // start-lib/end-lib library: adds its input object files.
+ if (Link.useStartEndLib(
+ input, CppHelper.getArchiveType(cppConfiguration, ccToolchainProvider))) {
+ Iterable<Artifact> archiveMembers = input.getObjectFiles();
+ if (!Iterables.isEmpty(archiveMembers)) {
+ ImmutableList.Builder<Artifact> nonLtoArchiveMembersBuilder = ImmutableList.builder();
+ for (Artifact member : archiveMembers) {
+ Artifact a;
+ if (ltoMap != null && (a = ltoMap.remove(member)) != null) {
+ // When ltoMap is non-null the backend artifact may be missing due to libraries that
+ // list .o files explicitly, or generate .o files from assembler.
+ if (handledByLtoIndexing(a, allowLtoIndexing)) {
+ // The LTO artifacts that should be included in the final link
+ // are listed in the thinltoParamFile, generated by the LTO indexing.
+ continue;
+ }
+ // No LTO indexing step, so use the LTO backend's generated artifact directly
+ // instead of the bitcode object.
+ member = a;
+ }
+ nonLtoArchiveMembersBuilder.add(member);
+ }
+ ImmutableList<Artifact> nonLtoArchiveMembers = nonLtoArchiveMembersBuilder.build();
+ if (!nonLtoArchiveMembers.isEmpty()) {
+ boolean inputIsWholeArchive = !isRuntimeLinkerInput && needWholeArchive;
+ librariesToLink.addValue(
+ LibraryToLinkValue.forObjectFileGroup(nonLtoArchiveMembers, inputIsWholeArchive));
+ }
+ }
+ } else {
+ Preconditions.checkArgument(
+ artifactCategory.equals(ArtifactCategory.OBJECT_FILE)
+ || artifactCategory.equals(ArtifactCategory.STATIC_LIBRARY)
+ || artifactCategory.equals(ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
+ boolean isAlwaysLinkStaticLibrary =
+ artifactCategory == ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY;
+ boolean inputIsWholeArchive =
+ (!isRuntimeLinkerInput && (isAlwaysLinkStaticLibrary || needWholeArchive))
+ || (isRuntimeLinkerInput && isAlwaysLinkStaticLibrary && !needWholeArchive);
+
+ Artifact inputArtifact = input.getArtifact();
+ Artifact a;
+ if (ltoMap != null && (a = ltoMap.remove(inputArtifact)) != null) {
+ if (handledByLtoIndexing(a, allowLtoIndexing)) {
+ // The LTO artifacts that should be included in the final link
+ // are listed in the thinltoParamFile, generated by the LTO indexing.
+ return;
+ }
+ // No LTO indexing step, so use the LTO backend's generated artifact directly
+ // instead of the bitcode object.
+ inputArtifact = a;
+ }
+
+ String name;
+ if (input.isFake()) {
+ name = Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString();
+ } else {
+ name = inputArtifact.getExecPathString();
+ }
+
+ if (artifactCategory.equals(ArtifactCategory.OBJECT_FILE)) {
+ if (inputArtifact.isTreeArtifact()) {
+ librariesToLink.addValue(
+ LibraryToLinkValue.forObjectFileGroup(
+ ImmutableList.<Artifact>of(inputArtifact), inputIsWholeArchive));
+ } else {
+ librariesToLink.addValue(LibraryToLinkValue.forObjectFile(name, inputIsWholeArchive));
+ }
+ } else {
+ librariesToLink.addValue(LibraryToLinkValue.forStaticLibrary(name, inputIsWholeArchive));
+ }
+ }
+ }
+
+ /**
+ * Returns true if this artifact is produced from a bitcode file that will be input to the LTO
+ * indexing step, in which case that step will add it to the generated thinltoParamFile for
+ * inclusion in the final link step if the linker decides to include it.
+ *
+ * @param a is an artifact produced by an LTO backend.
+ * @param allowLtoIndexing
+ */
+ private static boolean handledByLtoIndexing(Artifact a, boolean allowLtoIndexing) {
+ // If no LTO indexing is allowed for this link, then none are handled by LTO indexing.
+ // Otherwise, this may be from a linkstatic library that we decided not to include in
+ // LTO indexing because we are linking a test, to improve scalability when linking many tests.
+ return allowLtoIndexing
+ && !a.getRootRelativePath()
+ .startsWith(
+ PathFragment.create(CppLinkActionBuilder.SHARED_NONLTO_BACKEND_ROOT_PREFIX));
+ }
+
+ private boolean addRuntimeLinkerInputs(
+ Builder<String> librarySearchDirectories,
+ Builder<String> rpathRootsForExplicitSoDeps,
+ SequenceBuilder librariesToLink) {
+ boolean includeRuntimeSolibDir = false;
+ for (LinkerInput input : runtimeLinkerInputs) {
+ if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY
+ || input.getArtifactCategory() == ArtifactCategory.INTERFACE_LIBRARY) {
+ PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory();
+ if (!featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) {
+ Preconditions.checkState(
+ runtimeSolibDir != null && libDir.equals(runtimeSolibDir),
+ "Artifact '%s' is not under directory '%s'.",
+ input.getArtifact(),
+ solibDir);
+ includeRuntimeSolibDir = true;
+ }
+ addDynamicInputLinkOptions(
+ input, librariesToLink, librarySearchDirectories, rpathRootsForExplicitSoDeps);
+ } else {
+ addStaticInputLinkOptions(input, librariesToLink, true);
+ }
+ }
+ return includeRuntimeSolibDir;
+ }
+
+ private Map<Artifact, Artifact> generateLtoMap() {
+ if (isLtoIndexing || allLtoArtifacts == null) {
+ return null;
+ }
+ // TODO(bazel-team): The LTO final link can only work if there are individual .o files on
+ // the command line. Rather than crashing, this should issue a nice error. We will get
+ // this by
+ // 1) moving supports_start_end_lib to a toolchain feature
+ // 2) having thin_lto require start_end_lib
+ // As a bonus, we can rephrase --nostart_end_lib as --features=-start_end_lib and get rid
+ // of a command line option.
+
+ Preconditions.checkState(CppHelper.useStartEndLib(cppConfiguration, ccToolchainProvider));
+ Map<Artifact, Artifact> ltoMap = new HashMap<>();
+ for (LtoBackendArtifacts l : allLtoArtifacts) {
+ ltoMap.put(l.getBitcodeFile(), l.getObjectFile());
+ }
+ return ltoMap;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java
new file mode 100644
index 0000000000..5975f41ec5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java
@@ -0,0 +1,230 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.SequenceBuilder;
+import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/** Enum covering all build variables we create for all various {@link CppLinkAction}. */
+public enum LinkBuildVariables {
+ /** Entries in the linker runtime search path (usually set by -rpath flag) */
+ RUNTIME_LIBRARY_SEARCH_DIRECTORIES("runtime_library_search_directories"),
+ /** Entries in the linker search path (usually set by -L flag) */
+ LIBRARY_SEARCH_DIRECTORIES("library_search_directories"),
+ /** Flags providing files to link as inputs in the linker invocation */
+ LIBRARIES_TO_LINK("libraries_to_link"),
+ /** Thinlto param file produced by thinlto-indexing action consumed by the final link action. */
+ THINLTO_PARAM_FILE("thinlto_param_file"),
+ /** Location of def file used on Windows with MSVC */
+ DEF_FILE_PATH("def_file_path"),
+ /** Location where hinlto should write thinlto_param_file flags when indexing. */
+ THINLTO_INDEXING_PARAM_FILE("thinlto_indexing_param_file"),
+
+ THINLTO_PREFIX_REPLACE("thinlto_prefix_replace"),
+ /**
+ * A build variable to let the LTO indexing step know how to map from the minimized bitcode file
+ * to the full bitcode file used by the LTO Backends.
+ */
+ THINLTO_OBJECT_SUFFIX_REPLACE("thinlto_object_suffix_replace"),
+ /**
+ * A build variable for the path to the merged object file, which is an object file that is
+ * created during the LTO indexing step and needs to be passed to the final link.
+ */
+ THINLTO_MERGED_OBJECT_FILE("thinlto_merged_object_file"),
+ /** Location of linker param file created by bazel to overcome command line length limit */
+ LINKER_PARAM_FILE("linker_param_file"),
+ /** execpath of the output of the linker. */
+ OUTPUT_EXECPATH("output_execpath"),
+ /** "yes"|"no" depending on whether interface library should be generated. */
+ GENERATE_INTERFACE_LIBRARY("generate_interface_library"),
+ /** Path to the interface library builder tool. */
+ INTERFACE_LIBRARY_BUILDER("interface_library_builder_path"),
+ /** Input for the interface library ifso builder tool. */
+ INTERFACE_LIBRARY_INPUT("interface_library_input_path"),
+ /** Path where to generate interface library using the ifso builder tool. */
+ INTERFACE_LIBRARY_OUTPUT("interface_library_output_path"),
+ /** Linker flags coming from the legacy crosstool fields. */
+ LEGACY_LINK_FLAGS("legacy_link_flags"),
+ /** Path to which to write symbol counts. */
+ SYMBOL_COUNTS_OUTPUT("symbol_counts_output"),
+ /** A build variable giving linkstamp paths. */
+ LINKSTAMP_PATHS("linkstamp_paths"),
+ /** Presence of this variable indicates that PIC code should be generated. */
+ FORCE_PIC("force_pic"),
+ /** Presence of this variable indicates that the debug symbols should be stripped. */
+ STRIP_DEBUG_SYMBOLS("strip_debug_symbols"),
+ @Deprecated
+ IS_CC_TEST_LINK_ACTION("is_cc_test_link_action"),
+ @Deprecated
+ IS_NOT_CC_TEST_LINK_ACTION("is_not_cc_test_link_action"),
+ /** Truthy when current action is a cc_test linking action, falsey otherwise. */
+ IS_CC_TEST("is_cc_test"),
+ /**
+ * Presence of this variable indicates that files were compiled with fission (debug info is in
+ * .dwo files instead of .o files and linker needs to know).
+ */
+ IS_USING_FISSION("is_using_fission");
+
+ private final String variableName;
+
+ LinkBuildVariables(String variableName) {
+ this.variableName = variableName;
+ }
+
+ public String getVariableName() {
+ return variableName;
+ }
+
+ public static Variables setupVariables(
+ CppLinkActionBuilder cppLinkActionBuilder,
+ BuildConfiguration configuration,
+ Artifact outputArtifact,
+ Artifact paramFile,
+ Artifact thinltoParamFile,
+ Artifact thinltoMergedObjectFile,
+ boolean mustKeepDebug,
+ Artifact symbolCounts,
+ CppConfiguration cppConfiguration,
+ CcToolchainProvider ccToolchainProvider,
+ FeatureConfiguration featureConfiguration,
+ boolean useTestOnlyFlags,
+ boolean isLtoIndexing,
+ Artifact interfaceLibraryBuilder,
+ Artifact interfaceLibraryOutput,
+ PathFragment ltoOutputRootPrefix,
+ ActionInput defFile,
+ FdoSupportProvider fdoSupport,
+ Iterable<String> runtimeLibrarySearchDirectories,
+ SequenceBuilder librariesToLink,
+ Iterable<String> librarySearchDirectories) {
+ Variables.Builder buildVariables = new Variables.Builder();
+
+ // symbol counting
+ if (symbolCounts != null) {
+ buildVariables.addStringVariable(
+ SYMBOL_COUNTS_OUTPUT.getVariableName(), symbolCounts.getExecPathString());
+ }
+
+ // pic
+ if (cppConfiguration.forcePic()) {
+ buildVariables.addStringVariable(FORCE_PIC.getVariableName(), "");
+ }
+
+ if (!mustKeepDebug && cppConfiguration.shouldStripBinaries()) {
+ buildVariables.addStringVariable(STRIP_DEBUG_SYMBOLS.getVariableName(), "");
+ }
+
+ if (cppLinkActionBuilder.getLinkType().staticness().equals(Staticness.DYNAMIC)
+ && CppHelper.shouldCreatePerObjectDebugInfo(
+ cppConfiguration, ccToolchainProvider, featureConfiguration)) {
+ buildVariables.addStringVariable(IS_USING_FISSION.getVariableName(), "");
+ }
+
+ if (useTestOnlyFlags) {
+ buildVariables.addIntegerVariable(IS_CC_TEST.getVariableName(), 1);
+ buildVariables.addStringVariable(IS_CC_TEST_LINK_ACTION.getVariableName(), "");
+ } else {
+ buildVariables.addIntegerVariable(IS_CC_TEST.getVariableName(), 0);
+ buildVariables.addStringVariable(IS_NOT_CC_TEST_LINK_ACTION.getVariableName(), "");
+ }
+
+ if (runtimeLibrarySearchDirectories != null) {
+ buildVariables.addStringSequenceVariable(
+ RUNTIME_LIBRARY_SEARCH_DIRECTORIES.getVariableName(), runtimeLibrarySearchDirectories);
+ }
+
+ buildVariables.addCustomBuiltVariable(LIBRARIES_TO_LINK.getVariableName(), librariesToLink);
+ // TODO(b/72803478): Remove once existing crosstools have been migrated
+ buildVariables.addStringVariable("libs_to_link_dont_emit_objects_for_archiver", "");
+
+ buildVariables.addStringSequenceVariable(
+ LIBRARY_SEARCH_DIRECTORIES.getVariableName(), librarySearchDirectories);
+
+ if (paramFile != null) {
+ buildVariables.addStringVariable(
+ LINKER_PARAM_FILE.getVariableName(), paramFile.getExecPathString());
+ }
+
+ // output exec path
+ if (outputArtifact != null) {
+ buildVariables.addStringVariable(
+ OUTPUT_EXECPATH.getVariableName(), outputArtifact.getExecPathString());
+ }
+
+ if (isLtoIndexing) {
+ if (thinltoParamFile != null) {
+ // This is a lto-indexing action and we want it to populate param file.
+ buildVariables.addStringVariable(
+ THINLTO_INDEXING_PARAM_FILE.getVariableName(), thinltoParamFile.getExecPathString());
+ // TODO(b/33846234): Remove once all the relevant crosstools don't depend on the variable.
+ buildVariables.addStringVariable(
+ "thinlto_optional_params_file", "=" + thinltoParamFile.getExecPathString());
+ } else {
+ buildVariables.addStringVariable(THINLTO_INDEXING_PARAM_FILE.getVariableName(), "");
+ // TODO(b/33846234): Remove once all the relevant crosstools don't depend on the variable.
+ buildVariables.addStringVariable("thinlto_optional_params_file", "");
+ }
+ buildVariables.addStringVariable(
+ THINLTO_PREFIX_REPLACE.getVariableName(),
+ configuration.getBinDirectory().getExecPathString()
+ + ";"
+ + configuration.getBinDirectory().getExecPath().getRelative(ltoOutputRootPrefix));
+ buildVariables.addStringVariable(
+ THINLTO_OBJECT_SUFFIX_REPLACE.getVariableName(),
+ Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions())
+ + ";"
+ + Iterables.getOnlyElement(CppFileTypes.OBJECT_FILE.getExtensions()));
+ if (thinltoMergedObjectFile != null) {
+ buildVariables.addStringVariable(
+ THINLTO_MERGED_OBJECT_FILE.getVariableName(),
+ thinltoMergedObjectFile.getExecPathString());
+ }
+ } else {
+ if (thinltoParamFile != null) {
+ // This is a normal link action and we need to use param file created by lto-indexing.
+ buildVariables.addStringVariable(
+ THINLTO_PARAM_FILE.getVariableName(), thinltoParamFile.getExecPathString());
+ }
+ }
+ boolean shouldGenerateInterfaceLibrary =
+ outputArtifact != null && interfaceLibraryBuilder != null && interfaceLibraryOutput != null;
+ buildVariables.addStringVariable(
+ GENERATE_INTERFACE_LIBRARY.getVariableName(),
+ shouldGenerateInterfaceLibrary ? "yes" : "no");
+ buildVariables.addStringVariable(
+ INTERFACE_LIBRARY_BUILDER.getVariableName(),
+ shouldGenerateInterfaceLibrary ? interfaceLibraryBuilder.getExecPathString() : "ignored");
+ buildVariables.addStringVariable(
+ INTERFACE_LIBRARY_INPUT.getVariableName(),
+ shouldGenerateInterfaceLibrary ? outputArtifact.getExecPathString() : "ignored");
+ buildVariables.addStringVariable(
+ INTERFACE_LIBRARY_OUTPUT.getVariableName(),
+ shouldGenerateInterfaceLibrary ? interfaceLibraryOutput.getExecPathString() : "ignored");
+
+ if (defFile != null) {
+ buildVariables.addStringVariable(
+ DEF_FILE_PATH.getVariableName(), defFile.getExecPathString());
+ }
+
+ fdoSupport.getFdoSupport().getLinkOptions(featureConfiguration, buildVariables);
+ return buildVariables.build();
+ }
+} \ No newline at end of file