diff options
author | 2018-04-03 08:54:05 -0700 | |
---|---|---|
committer | 2018-04-03 08:55:55 -0700 | |
commit | eba28176a97a0d0ddf2d1ef8505d37f392a2c363 (patch) | |
tree | c41a17d6911745d370aa46ba4f195e6a7d90bc37 /src/main/java/com/google | |
parent | 3f6c57b3a2c86444ce4be119f51578ab7198071e (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')
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 |