| // Copyright 2016 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 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.Strings; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| 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.Action; |
| import com.google.devtools.build.lib.actions.ActionOwner; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.ParameterFile; |
| import com.google.devtools.build.lib.analysis.AnalysisEnvironment; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.collect.CollectionUtils; |
| import com.google.devtools.build.lib.collect.ImmutableIterable; |
| import com.google.devtools.build.lib.collect.IterablesChain; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.collect.nestedset.Order; |
| import com.google.devtools.build.lib.packages.RuleErrorConsumer; |
| 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.CppLinkAction.Context; |
| import com.google.devtools.build.lib.rules.cpp.CppLinkAction.LinkArtifactFactory; |
| 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; |
| import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| 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 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"; |
| |
| /** |
| * Temporary build variable for migrating osx crosstool. |
| * TODO(b/37271982): Remove after blaze with ar action_config release |
| */ |
| public static final String USES_ACTION_CONFIG_FOR_AR_VARIABLE = |
| "uses_action_configs_for_cc_archiving"; |
| |
| /** |
| * 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). |
| */ |
| 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"; |
| |
| // Builder-only |
| // Null when invoked from tests (e.g. via createTestBuilder). |
| @Nullable private final RuleContext ruleContext; |
| private final AnalysisEnvironment analysisEnvironment; |
| private final Artifact output; |
| private final CppSemantics unusedCppSemantics; |
| @Nullable private String mnemonic; |
| |
| // can be null for CppLinkAction.createTestBuilder() |
| @Nullable private final CcToolchainProvider toolchain; |
| private final FdoSupportProvider fdoSupport; |
| private Artifact interfaceOutput; |
| private Artifact symbolCounts; |
| private PathFragment runtimeSolibDir; |
| protected final BuildConfiguration configuration; |
| private final CppConfiguration cppConfiguration; |
| private FeatureConfiguration featureConfiguration; |
| |
| // Morally equivalent with {@link Context}, except these are mutable. |
| // Keep these in sync with {@link Context}. |
| private final Set<LinkerInput> objectFiles = new LinkedHashSet<>(); |
| private final Set<Artifact> nonCodeInputs = new LinkedHashSet<>(); |
| private final NestedSetBuilder<LibraryToLink> libraries = NestedSetBuilder.linkOrder(); |
| private NestedSet<Artifact> crosstoolInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| private Artifact runtimeMiddleman; |
| private ArtifactCategory runtimeType = null; |
| private NestedSet<Artifact> runtimeInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| private final NestedSetBuilder<Artifact> compilationInputs = NestedSetBuilder.stableOrder(); |
| private final Set<Artifact> linkstamps = new LinkedHashSet<>(); |
| private ImmutableList<String> additionalLinkstampDefines = ImmutableList.of(); |
| private final List<String> linkopts = new ArrayList<>(); |
| private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; |
| private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC; |
| private String libraryIdentifier = null; |
| private ImmutableMap<Artifact, Artifact> ltoBitcodeFiles; |
| private Artifact defFile; |
| |
| private boolean fake; |
| private boolean isNativeDeps; |
| private boolean useTestOnlyFlags; |
| private boolean wholeArchive; |
| private LinkArtifactFactory linkArtifactFactory = CppLinkAction.DEFAULT_ARTIFACT_FACTORY; |
| |
| private boolean isLtoIndexing = false; |
| private boolean usePicForLtoBackendActions = false; |
| private Iterable<LtoBackendArtifacts> allLtoArtifacts = null; |
| |
| private final List<VariablesExtension> variablesExtensions = new ArrayList<>(); |
| private final NestedSetBuilder<Artifact> linkActionInputs = NestedSetBuilder.stableOrder(); |
| private final ImmutableList.Builder<Artifact> linkActionOutputs = ImmutableList.builder(); |
| |
| /** |
| * Creates a builder that builds {@link CppLinkAction} instances. |
| * |
| * @param ruleContext the rule that owns the action |
| * @param output the output artifact |
| * @param toolchain the C++ toolchain provider |
| * @param fdoSupport the C++ FDO optimization support |
| * @param cppSemantics to be used for linkstamp compiles |
| */ |
| public CppLinkActionBuilder( |
| RuleContext ruleContext, |
| Artifact output, |
| CcToolchainProvider toolchain, |
| FdoSupportProvider fdoSupport, |
| FeatureConfiguration featureConfiguration, |
| CppSemantics cppSemantics) { |
| this( |
| ruleContext, |
| output, |
| ruleContext.getConfiguration(), |
| ruleContext.getAnalysisEnvironment(), |
| toolchain, |
| fdoSupport, |
| featureConfiguration, |
| cppSemantics); |
| } |
| |
| /** |
| * Creates a builder that builds {@link CppLinkAction} instances. |
| * |
| * @param ruleContext the rule that owns the action |
| * @param output the output artifact |
| * @param configuration build configuration |
| * @param toolchain C++ toolchain provider |
| * @param fdoSupport the C++ FDO optimization support |
| * @param cppSemantics to be used for linkstamp compiles |
| */ |
| public CppLinkActionBuilder( |
| RuleContext ruleContext, |
| Artifact output, |
| BuildConfiguration configuration, |
| CcToolchainProvider toolchain, |
| FdoSupportProvider fdoSupport, |
| FeatureConfiguration featureConfiguration, |
| CppSemantics cppSemantics) { |
| this( |
| ruleContext, |
| output, |
| configuration, |
| ruleContext.getAnalysisEnvironment(), |
| toolchain, |
| fdoSupport, |
| featureConfiguration, |
| cppSemantics); |
| } |
| |
| /** |
| * Creates a builder that builds {@link CppLinkAction}s. |
| * |
| * @param ruleContext the rule that owns the action |
| * @param output the output artifact |
| * @param configuration the configuration used to determine the tool chain and the default link |
| * options |
| * @param toolchain the C++ toolchain provider |
| * @param fdoSupport the C++ FDO optimization support |
| * @param cppSemantics to be used for linkstamp compiles |
| */ |
| private CppLinkActionBuilder( |
| @Nullable RuleContext ruleContext, |
| Artifact output, |
| BuildConfiguration configuration, |
| AnalysisEnvironment analysisEnvironment, |
| CcToolchainProvider toolchain, |
| FdoSupportProvider fdoSupport, |
| FeatureConfiguration featureConfiguration, |
| CppSemantics cppSemantics) { |
| this.ruleContext = ruleContext; |
| this.analysisEnvironment = Preconditions.checkNotNull(analysisEnvironment); |
| this.output = Preconditions.checkNotNull(output); |
| this.configuration = Preconditions.checkNotNull(configuration); |
| this.cppConfiguration = configuration.getFragment(CppConfiguration.class); |
| this.toolchain = toolchain; |
| this.fdoSupport = fdoSupport; |
| if (toolchain.supportsEmbeddedRuntimes() && toolchain != null) { |
| runtimeSolibDir = toolchain.getDynamicRuntimeSolibDir(); |
| } |
| this.featureConfiguration = featureConfiguration; |
| this.unusedCppSemantics = Preconditions.checkNotNull(cppSemantics); |
| } |
| |
| /** |
| * Given a Context, creates a Builder that builds {@link CppLinkAction}s. Note well: Keep the |
| * Builder->Context and Context->Builder transforms consistent! |
| * |
| * @param ruleContext the rule that owns the action |
| * @param output the output artifact |
| * @param linkContext an immutable CppLinkAction.Context from the original builder |
| * @param configuration build configuration |
| * @param toolchain the C++ toolchain provider |
| * @param fdoSupport the C++ FDO optimization support |
| * @param cppSemantics to be used for linkstamp compiles |
| */ |
| public CppLinkActionBuilder( |
| RuleContext ruleContext, |
| Artifact output, |
| Context linkContext, |
| BuildConfiguration configuration, |
| CcToolchainProvider toolchain, |
| FdoSupportProvider fdoSupport, |
| FeatureConfiguration featureConfiguration, |
| CppSemantics cppSemantics) { |
| // These Builder-only fields get set in the constructor: |
| // ruleContext, analysisEnvironment, outputPath, configuration, runtimeSolibDir |
| this( |
| ruleContext, |
| output, |
| configuration, |
| ruleContext.getAnalysisEnvironment(), |
| toolchain, |
| fdoSupport, |
| featureConfiguration, |
| cppSemantics); |
| Preconditions.checkNotNull(linkContext); |
| |
| // All linkContext fields should be transferred to this Builder. |
| this.objectFiles.addAll(linkContext.objectFiles); |
| this.nonCodeInputs.addAll(linkContext.nonCodeInputs); |
| this.libraries.addTransitive(linkContext.libraries); |
| this.crosstoolInputs = linkContext.crosstoolInputs; |
| this.ltoBitcodeFiles = linkContext.ltoBitcodeFiles; |
| this.runtimeMiddleman = linkContext.runtimeMiddleman; |
| this.runtimeInputs = linkContext.runtimeInputs; |
| this.runtimeType = linkContext.runtimeType; |
| this.compilationInputs.addTransitive(linkContext.compilationInputs); |
| this.linkstamps.addAll(linkContext.linkstamps); |
| this.linkopts.addAll(linkContext.linkopts); |
| this.linkType = linkContext.linkType; |
| this.linkStaticness = linkContext.linkStaticness; |
| this.fake = linkContext.fake; |
| this.isNativeDeps = linkContext.isNativeDeps; |
| this.useTestOnlyFlags = linkContext.useTestOnlyFlags; |
| } |
| |
| /** Returns the action name for purposes of querying the crosstool. */ |
| private String getActionName() { |
| return linkType.getActionName(); |
| } |
| |
| /** Returns linker inputs that are not libraries. */ |
| public Set<LinkerInput> getObjectFiles() { |
| return objectFiles; |
| } |
| |
| public Set<Artifact> getNonCodeInputs() { |
| return nonCodeInputs; |
| } |
| |
| /** |
| * Returns linker inputs that are libraries. |
| */ |
| public NestedSetBuilder<LibraryToLink> getLibraries() { |
| return libraries; |
| } |
| |
| /** |
| * Returns inputs arising from the crosstool. |
| */ |
| public NestedSet<Artifact> getCrosstoolInputs() { |
| return this.crosstoolInputs; |
| } |
| |
| /** |
| * Returns the runtime middleman artifact. |
| */ |
| public Artifact getRuntimeMiddleman() { |
| return this.runtimeMiddleman; |
| } |
| |
| /** |
| * Returns runtime inputs for this link action. |
| */ |
| public NestedSet<Artifact> getRuntimeInputs() { |
| return this.runtimeInputs; |
| } |
| |
| public ArtifactCategory getRuntimeType() { |
| return runtimeType; |
| } |
| |
| /** |
| * Returns compilation inputs for this link action. |
| */ |
| public final NestedSetBuilder<Artifact> getCompilationInputs() { |
| return this.compilationInputs; |
| } |
| |
| /** |
| * Returns linkstamps for this link action. |
| */ |
| public final Set<Artifact> getLinkstamps() { |
| return this.linkstamps; |
| } |
| |
| /** |
| * Returns command line options for this link action. |
| */ |
| public final List<String> getLinkopts() { |
| return this.linkopts; |
| } |
| |
| /** |
| * Returns the type of this link action. |
| */ |
| public LinkTargetType getLinkType() { |
| return this.linkType; |
| } |
| /** |
| * Returns the staticness of this link action. |
| */ |
| public LinkStaticness getLinkStaticness() { |
| return this.linkStaticness; |
| } |
| /** |
| * Returns linker inputs that are lto bitcode files in a map from the full bitcode file used by |
| * the LTO Backend to the minimized bitcode used by the LTO indexing. |
| */ |
| public ImmutableMap<Artifact, Artifact> getLtoBitcodeFiles() { |
| return this.ltoBitcodeFiles; |
| } |
| |
| /** |
| * Returns true for a cc_fake_binary. |
| */ |
| public boolean isFake() { |
| return this.fake; |
| } |
| |
| /** |
| * Returns true for native dependencies of another language. |
| */ |
| public boolean isNativeDeps() { |
| return this.isNativeDeps; |
| } |
| |
| public CppLinkActionBuilder setLinkArtifactFactory(LinkArtifactFactory linkArtifactFactory) { |
| this.linkArtifactFactory = linkArtifactFactory; |
| return this; |
| } |
| |
| /** |
| * Returns true if this link action uses test only flags. |
| */ |
| public boolean useTestOnlyFlags() { |
| return this.useTestOnlyFlags; |
| } |
| |
| /** |
| * Maps bitcode object files used by the LTO backends to the corresponding minimized bitcode file |
| * used as input to the LTO indexing step. |
| */ |
| private ImmutableSet<LinkerInput> computeLtoIndexingObjectFileInputs() { |
| ImmutableSet.Builder<LinkerInput> objectFileInputsBuilder = ImmutableSet.builder(); |
| for (LinkerInput input : objectFiles) { |
| Artifact objectFile = input.getArtifact(); |
| objectFileInputsBuilder.add( |
| LinkerInputs.simpleLinkerInput( |
| this.ltoBitcodeFiles.getOrDefault(objectFile, objectFile), |
| ArtifactCategory.OBJECT_FILE)); |
| } |
| return objectFileInputsBuilder.build(); |
| } |
| |
| /** |
| * Maps bitcode library files used by the LTO backends to the corresponding minimized bitcode file |
| * used as input to the LTO indexing step. |
| */ |
| private static NestedSet<LibraryToLink> computeLtoIndexingUniqueLibraries( |
| NestedSet<LibraryToLink> originalUniqueLibraries) { |
| NestedSetBuilder<LibraryToLink> uniqueLibrariesBuilder = NestedSetBuilder.linkOrder(); |
| for (LibraryToLink lib : originalUniqueLibraries) { |
| if (!lib.containsObjectFiles()) { |
| uniqueLibrariesBuilder.add(lib); |
| continue; |
| } |
| ImmutableSet.Builder<Artifact> newObjectFilesBuilder = ImmutableSet.builder(); |
| for (Artifact a : lib.getObjectFiles()) { |
| newObjectFilesBuilder.add(lib.getLtoBitcodeFiles().getOrDefault(a, a)); |
| } |
| uniqueLibrariesBuilder.add( |
| LinkerInputs.newInputLibrary( |
| lib.getArtifact(), |
| lib.getArtifactCategory(), |
| lib.getLibraryIdentifier(), |
| newObjectFilesBuilder.build(), |
| lib.getLtoBitcodeFiles())); |
| } |
| return uniqueLibrariesBuilder.build(); |
| } |
| |
| /** |
| * Returns true if there are any LTO bitcode inputs to this link, either directly transitively via |
| * library inputs. |
| */ |
| boolean hasLtoBitcodeInputs() { |
| if (!ltoBitcodeFiles.isEmpty()) { |
| return true; |
| } |
| for (LibraryToLink lib : libraries.build()) { |
| if (!lib.getLtoBitcodeFiles().isEmpty()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Iterable<LtoBackendArtifacts> createLtoArtifacts( |
| PathFragment ltoOutputRootPrefix, |
| NestedSet<LibraryToLink> uniqueLibraries, |
| ImmutableSet<String> features) { |
| Set<Artifact> compiled = new LinkedHashSet<>(); |
| for (LibraryToLink lib : uniqueLibraries) { |
| compiled.addAll(lib.getLtoBitcodeFiles().keySet()); |
| } |
| |
| // This flattens the set of object files, so for M binaries and N .o files, |
| // this is O(M*N). If we had a nested set of .o files, we could have O(M + N) instead. |
| Map<PathFragment, Artifact> allBitcode = new HashMap<>(); |
| for (LibraryToLink lib : uniqueLibraries) { |
| if (!lib.containsObjectFiles()) { |
| continue; |
| } |
| for (Artifact a : lib.getObjectFiles()) { |
| if (compiled.contains(a)) { |
| allBitcode.put(a.getExecPath(), a); |
| } |
| } |
| } |
| for (LinkerInput input : objectFiles) { |
| if (this.ltoBitcodeFiles.containsKey(input.getArtifact())) { |
| allBitcode.put(input.getArtifact().getExecPath(), input.getArtifact()); |
| } |
| } |
| |
| List<String> argv = new ArrayList<>(); |
| argv.addAll(toolchain.getLinkOptions()); |
| argv.addAll(cppConfiguration.getCompilerOptions(features)); |
| ImmutableList.Builder<LtoBackendArtifacts> ltoOutputs = ImmutableList.builder(); |
| for (Artifact a : allBitcode.values()) { |
| LtoBackendArtifacts ltoArtifacts = |
| new LtoBackendArtifacts( |
| ltoOutputRootPrefix, |
| a, |
| allBitcode, |
| ruleContext, |
| configuration, |
| linkArtifactFactory, |
| featureConfiguration, |
| toolchain, |
| fdoSupport, |
| usePicForLtoBackendActions, |
| cppConfiguration.useFission(), |
| argv); |
| ltoOutputs.add(ltoArtifacts); |
| } |
| return ltoOutputs.build(); |
| } |
| |
| @VisibleForTesting |
| boolean canSplitCommandLine() { |
| if (fake) { |
| return false; |
| } |
| |
| if (toolchain == null || !toolchain.supportsParamFiles()) { |
| return false; |
| } |
| |
| switch (linkType) { |
| // On Unix, we currently can't split dynamic library links if they have interface outputs. |
| // That was probably an unintended side effect of the change that introduced interface |
| // outputs. |
| // On Windows, We can always split the command line when building DLL. |
| case DYNAMIC_LIBRARY: |
| return (interfaceOutput == null |
| || featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)); |
| case EXECUTABLE: |
| case STATIC_LIBRARY: |
| case PIC_STATIC_LIBRARY: |
| case ALWAYS_LINK_STATIC_LIBRARY: |
| case ALWAYS_LINK_PIC_STATIC_LIBRARY: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /** Builds the Action as configured and returns it. */ |
| public CppLinkAction build() throws InterruptedException { |
| // Executable links do not have library identifiers. |
| boolean hasIdentifier = (libraryIdentifier != null); |
| boolean isExecutable = linkType.isExecutable(); |
| Preconditions.checkState(hasIdentifier != isExecutable); |
| Preconditions.checkNotNull(featureConfiguration); |
| |
| if (interfaceOutput != null && (fake || linkType != LinkTargetType.DYNAMIC_LIBRARY)) { |
| throw new RuntimeException( |
| "Interface output can only be used " + "with non-fake DYNAMIC_LIBRARY targets"); |
| } |
| |
| if (!featureConfiguration.actionIsConfigured(linkType.getActionName())) { |
| ruleContext.ruleError( |
| String.format( |
| "Expected action_config for '%s' to be configured", linkType.getActionName())); |
| } |
| |
| final ImmutableList<Artifact> buildInfoHeaderArtifacts = |
| !linkstamps.isEmpty() |
| ? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration) |
| : ImmutableList.of(); |
| |
| boolean needWholeArchive = |
| wholeArchive |
| || needWholeArchive(linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration); |
| |
| NestedSet<LibraryToLink> originalUniqueLibraries = libraries.build(); |
| |
| // Get the set of object files and libraries containing the correct |
| // inputs for this link, depending on whether this is LTO indexing or |
| // a native link. |
| NestedSet<LibraryToLink> uniqueLibraries; |
| ImmutableSet<LinkerInput> objectFileInputs; |
| if (isLtoIndexing) { |
| objectFileInputs = computeLtoIndexingObjectFileInputs(); |
| uniqueLibraries = computeLtoIndexingUniqueLibraries(originalUniqueLibraries); |
| } else { |
| objectFileInputs = ImmutableSet.copyOf(objectFiles); |
| uniqueLibraries = originalUniqueLibraries; |
| } |
| final Iterable<Artifact> objectArtifacts = LinkerInputs.toLibraryArtifacts(objectFileInputs); |
| |
| final Iterable<LinkerInput> linkerInputs = |
| IterablesChain.<LinkerInput>builder() |
| .add(objectFileInputs) |
| .add( |
| ImmutableIterable.from( |
| Link.mergeInputsCmdLine( |
| uniqueLibraries, needWholeArchive, cppConfiguration.archiveType()))) |
| .build(); |
| |
| // ruleContext can only be null during testing. This is kind of ugly. |
| final ImmutableSet<String> features = |
| (ruleContext == null) ? ImmutableSet.of() : ruleContext.getFeatures(); |
| |
| final LibraryToLink outputLibrary = linkType.isExecutable() |
| ? null |
| : LinkerInputs.newInputLibrary(output, |
| linkType.getLinkerOutput(), |
| libraryIdentifier, |
| objectArtifacts, this.ltoBitcodeFiles); |
| final LibraryToLink interfaceOutputLibrary = |
| (interfaceOutput == null) |
| ? null |
| : LinkerInputs.newInputLibrary(interfaceOutput, |
| ArtifactCategory.DYNAMIC_LIBRARY, |
| libraryIdentifier, |
| objectArtifacts, this.ltoBitcodeFiles); |
| |
| final ImmutableMap<Artifact, Artifact> linkstampMap = |
| mapLinkstampsToOutputs(linkstamps, ruleContext, configuration, output, linkArtifactFactory); |
| |
| PathFragment ltoOutputRootPrefix = null; |
| if (isLtoIndexing && allLtoArtifacts == null) { |
| ltoOutputRootPrefix = |
| FileSystemUtils.appendExtension( |
| output.getRootRelativePath(), ".lto"); |
| // Use the originalUniqueLibraries which contains the full bitcode files |
| // needed by the LTO backends (as opposed to the minimized bitcode files |
| // that can be used by the LTO indexing step). |
| allLtoArtifacts = createLtoArtifacts(ltoOutputRootPrefix, originalUniqueLibraries, features); |
| } |
| |
| @Nullable Artifact thinltoParamFile = null; |
| if (allLtoArtifacts != null) { |
| // Create artifact for the file that the LTO indexing step will emit |
| // object file names into for any that were included in the link as |
| // determined by the linker's symbol resolution. It will be used to |
| // provide the inputs for the subsequent final native object link. |
| // Note that the paths emitted into this file will have their prefixes |
| // replaced with the final output directory, so they will be the paths |
| // of the native object files not the input bitcode files. |
| PathFragment linkerParamFileRootPath = |
| ParameterFile.derivePath(output.getRootRelativePath(), "lto-final"); |
| thinltoParamFile = |
| linkArtifactFactory.create(ruleContext, configuration, linkerParamFileRootPath); |
| } |
| |
| final ImmutableList<Artifact> actionOutputs; |
| if (isLtoIndexing) { |
| ImmutableList.Builder<Artifact> builder = ImmutableList.builder(); |
| for (LtoBackendArtifacts ltoA : allLtoArtifacts) { |
| ltoA.addIndexingOutputs(builder); |
| } |
| if (thinltoParamFile != null) { |
| builder.add(thinltoParamFile); |
| } |
| actionOutputs = builder.build(); |
| } else { |
| actionOutputs = |
| constructOutputs( |
| output, |
| Iterables.concat(linkstampMap.values(), linkActionOutputs.build()), |
| interfaceOutputLibrary == null ? null : interfaceOutputLibrary.getArtifact(), |
| symbolCounts); |
| } |
| |
| ImmutableList<LinkerInput> runtimeLinkerInputs = |
| ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs, runtimeType)); |
| |
| PathFragment paramRootPath = |
| ParameterFile.derivePath(output.getRootRelativePath(), (isLtoIndexing) ? "lto-index" : "2"); |
| |
| @Nullable |
| final Artifact paramFile = |
| canSplitCommandLine() |
| ? linkArtifactFactory.create(ruleContext, configuration, paramRootPath) |
| : null; |
| |
| // Add build variables necessary to template link args into the crosstool. |
| Variables.Builder buildVariablesBuilder = new Variables.Builder(toolchain.getBuildVariables()); |
| CppLinkVariablesExtension variablesExtension = |
| isLtoIndexing |
| ? new CppLinkVariablesExtension( |
| configuration, |
| /* linkstampMap= */ ImmutableMap.of(), |
| needWholeArchive, |
| linkerInputs, |
| runtimeLinkerInputs, |
| /* output= */ null, |
| paramFile, |
| thinltoParamFile, |
| ltoOutputRootPrefix, |
| /* interfaceLibraryBuilder= */ null, |
| /* interfaceLibraryOutput= */ null) |
| : new CppLinkVariablesExtension( |
| configuration, |
| linkstampMap, |
| needWholeArchive, |
| linkerInputs, |
| runtimeLinkerInputs, |
| output, |
| paramFile, |
| thinltoParamFile, |
| /* ltoOutputRootPrefix= */ PathFragment.EMPTY_FRAGMENT, |
| toolchain.getInterfaceSoBuilder(), |
| interfaceOutput); |
| variablesExtension.addVariables(buildVariablesBuilder); |
| for (VariablesExtension extraVariablesExtension : variablesExtensions) { |
| extraVariablesExtension.addVariables(buildVariablesBuilder); |
| } |
| Variables buildVariables = buildVariablesBuilder.build(); |
| |
| Preconditions.checkArgument( |
| linkType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, |
| "you can't link an interface dynamic library directly"); |
| if (linkType != LinkTargetType.DYNAMIC_LIBRARY) { |
| Preconditions.checkArgument( |
| interfaceOutput == null, |
| "interface output may only be non-null for dynamic library links"); |
| } |
| if (linkType.staticness() == Staticness.STATIC) { |
| // solib dir must be null for static links |
| runtimeSolibDir = null; |
| |
| Preconditions.checkArgument( |
| linkStaticness == LinkStaticness.FULLY_STATIC, "static library link must be static"); |
| Preconditions.checkArgument( |
| symbolCounts == null, "the symbol counts output must be null for static links"); |
| Preconditions.checkArgument( |
| !isNativeDeps, "the native deps flag must be false for static links"); |
| Preconditions.checkArgument( |
| !needWholeArchive, "the need whole archive flag must be false for static links"); |
| } |
| |
| LinkCommandLine.Builder linkCommandLineBuilder = |
| new LinkCommandLine.Builder(configuration, getOwner(), ruleContext) |
| .setLinkerInputs(linkerInputs) |
| .setRuntimeInputs(runtimeLinkerInputs) |
| .setLinkTargetType(linkType) |
| .setLinkStaticness(linkStaticness) |
| .setFeatures(features) |
| .setRuntimeSolibDir(linkType.staticness() == Staticness.STATIC ? null : runtimeSolibDir) |
| .setNativeDeps(isNativeDeps) |
| .setUseTestOnlyFlags(useTestOnlyFlags) |
| .setParamFile(paramFile) |
| .setToolchain(toolchain) |
| .setFdoSupport(fdoSupport.getFdoSupport()) |
| .setBuildVariables(buildVariables) |
| .setFeatureConfiguration(featureConfiguration); |
| |
| // TODO(b/62693279): Cleanup once internal crosstools specify ifso building correctly. |
| if (shouldUseLinkDynamicLibraryTool()) { |
| linkCommandLineBuilder.forceToolPath( |
| toolchain.getLinkDynamicLibraryTool().getExecPathString()); |
| } |
| |
| if (!isLtoIndexing) { |
| linkCommandLineBuilder |
| .setOutput(output) |
| .setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts) |
| .setLinkstamps(linkstampMap) |
| .setLinkopts(ImmutableList.copyOf(linkopts)) |
| .setAdditionalLinkstampDefines(additionalLinkstampDefines); |
| } else { |
| List<String> opts = new ArrayList<>(linkopts); |
| opts.addAll(featureConfiguration.getCommandLine("lto-indexing", buildVariables)); |
| opts.addAll(cppConfiguration.getLtoIndexOptions()); |
| linkCommandLineBuilder.setLinkopts(ImmutableList.copyOf(opts)); |
| } |
| |
| LinkCommandLine linkCommandLine = linkCommandLineBuilder.build(); |
| |
| // Compute the set of inputs - we only need stable order here. |
| NestedSetBuilder<Artifact> dependencyInputsBuilder = NestedSetBuilder.stableOrder(); |
| dependencyInputsBuilder.addTransitive(crosstoolInputs); |
| dependencyInputsBuilder.addTransitive(linkActionInputs.build()); |
| // TODO(b/62693279): Cleanup once internal crosstools specify ifso building correctly. |
| if (shouldUseLinkDynamicLibraryTool()) { |
| dependencyInputsBuilder.add(toolchain.getLinkDynamicLibraryTool()); |
| } |
| if (runtimeMiddleman != null) { |
| dependencyInputsBuilder.add(runtimeMiddleman); |
| } |
| if (!isLtoIndexing) { |
| dependencyInputsBuilder.addAll(buildInfoHeaderArtifacts); |
| dependencyInputsBuilder.addAll(linkstamps); |
| dependencyInputsBuilder.addTransitive(compilationInputs.build()); |
| } |
| if (defFile != null) { |
| dependencyInputsBuilder.add(defFile); |
| } |
| |
| Iterable<Artifact> expandedInputs = |
| LinkerInputs.toLibraryArtifacts( |
| Link.mergeInputsDependencies( |
| uniqueLibraries, needWholeArchive, cppConfiguration.archiveType())); |
| Iterable<Artifact> expandedNonLibraryInputs = LinkerInputs.toLibraryArtifacts(objectFileInputs); |
| |
| if (!isLtoIndexing && allLtoArtifacts != null) { |
| // We are doing LTO, and this is the real link, so substitute |
| // the LTO bitcode files with the real object files they were translated into. |
| Map<Artifact, Artifact> ltoMapping = new HashMap<>(); |
| for (LtoBackendArtifacts a : allLtoArtifacts) { |
| ltoMapping.put(a.getBitcodeFile(), a.getObjectFile()); |
| } |
| |
| // Handle libraries. |
| List<Artifact> renamedInputs = new ArrayList<>(); |
| for (Artifact a : expandedInputs) { |
| Artifact renamed = ltoMapping.get(a); |
| renamedInputs.add(renamed == null ? a : renamed); |
| } |
| expandedInputs = renamedInputs; |
| |
| // Handle non-libraries. |
| List<Artifact> renamedNonLibraryInputs = new ArrayList<>(); |
| for (Artifact a : expandedNonLibraryInputs) { |
| Artifact renamed = ltoMapping.get(a); |
| renamedNonLibraryInputs.add(renamed == null ? a : renamed); |
| } |
| expandedNonLibraryInputs = renamedNonLibraryInputs; |
| } |
| |
| // getPrimaryInput returns the first element, and that is a public interface - therefore the |
| // order here is important. |
| IterablesChain.Builder<Artifact> inputsBuilder = |
| IterablesChain.<Artifact>builder() |
| .add(ImmutableList.copyOf(expandedNonLibraryInputs)) |
| .add(ImmutableList.copyOf(nonCodeInputs)) |
| .add(dependencyInputsBuilder.build()) |
| .add(ImmutableIterable.from(expandedInputs)); |
| |
| if (thinltoParamFile != null && !isLtoIndexing) { |
| inputsBuilder.add(ImmutableList.of(thinltoParamFile)); |
| } |
| if (linkCommandLine.getParamFile() != null) { |
| inputsBuilder.add(ImmutableList.of(linkCommandLine.getParamFile())); |
| Action parameterFileWriteAction = |
| new ParameterFileWriteAction( |
| getOwner(), |
| paramFile, |
| linkCommandLine.paramCmdLine(), |
| ParameterFile.ParameterFileType.UNQUOTED, |
| ISO_8859_1); |
| analysisEnvironment.registerAction(parameterFileWriteAction); |
| } |
| |
| ImmutableMap<String, String> toolchainEnv = |
| featureConfiguration.getEnvironmentVariables(getActionName(), buildVariables); |
| |
| // If the crosstool uses action_configs to configure cc compilation, collect execution info |
| // from there, otherwise, use no execution info. |
| // TODO(b/27903698): Assert that the crosstool has an action_config for this action. |
| ImmutableSet.Builder<String> executionRequirements = ImmutableSet.builder(); |
| if (featureConfiguration.actionIsConfigured(getActionName())) { |
| executionRequirements.addAll( |
| featureConfiguration.getToolForAction(getActionName()).getExecutionRequirements()); |
| } |
| |
| return new CppLinkAction( |
| getOwner(), |
| mnemonic, |
| inputsBuilder.deduplicate().build(), |
| actionOutputs, |
| cppConfiguration, |
| outputLibrary, |
| output, |
| interfaceOutputLibrary, |
| fake, |
| isLtoIndexing, |
| linkCommandLine, |
| configuration.getVariableShellEnvironment(), |
| configuration.getLocalShellEnvironment(), |
| toolchainEnv, |
| executionRequirements.build(), |
| toolchain); |
| } |
| |
| private boolean shouldUseLinkDynamicLibraryTool() { |
| return linkType.equals(LinkTargetType.DYNAMIC_LIBRARY) |
| && cppConfiguration.supportsInterfaceSharedObjects() |
| && !featureConfiguration.hasConfiguredLinkerPathInActionConfig(); |
| } |
| |
| /** The default heuristic on whether we need to use whole-archive for the link. */ |
| private static boolean needWholeArchive( |
| LinkStaticness staticness, |
| LinkTargetType type, |
| Collection<String> linkopts, |
| boolean isNativeDeps, |
| CppConfiguration cppConfig) { |
| boolean fullyStatic = (staticness == LinkStaticness.FULLY_STATIC); |
| boolean mostlyStatic = (staticness == LinkStaticness.MOSTLY_STATIC); |
| boolean sharedLinkopts = |
| type == LinkTargetType.DYNAMIC_LIBRARY |
| || linkopts.contains("-shared") |
| || cppConfig.hasSharedLinkOption(); |
| return (isNativeDeps || cppConfig.legacyWholeArchive()) |
| && (fullyStatic || mostlyStatic) |
| && sharedLinkopts; |
| } |
| |
| private static ImmutableList<Artifact> constructOutputs( |
| Artifact primaryOutput, Iterable<Artifact> outputList, Artifact... outputs) { |
| return new ImmutableList.Builder<Artifact>() |
| .add(primaryOutput) |
| .addAll(outputList) |
| .addAll(CollectionUtils.asListWithoutNulls(outputs)) |
| .build(); |
| } |
| |
| /** |
| * Translates a collection of linkstamp source files to an immutable mapping from source files to |
| * object files. In other words, given a set of source files, this method determines the output |
| * path to which each file should be compiled. |
| * |
| * @param linkstamps collection of linkstamp source files |
| * @param ruleContext the rule for which this link is being performed |
| * @param outputBinary the binary output path for this link |
| * @return an immutable map that pairs each source file with the corresponding object file that |
| * should be fed into the link |
| */ |
| public static ImmutableMap<Artifact, Artifact> mapLinkstampsToOutputs( |
| Collection<Artifact> linkstamps, |
| RuleContext ruleContext, |
| BuildConfiguration configuration, |
| Artifact outputBinary, |
| LinkArtifactFactory linkArtifactFactory) { |
| ImmutableMap.Builder<Artifact, Artifact> mapBuilder = ImmutableMap.builder(); |
| |
| PathFragment outputBinaryPath = outputBinary.getRootRelativePath(); |
| PathFragment stampOutputDirectory = |
| outputBinaryPath |
| .getParentDirectory() |
| .getRelative(CppHelper.OBJS) |
| .getRelative(outputBinaryPath.getBaseName()); |
| |
| for (Artifact linkstamp : linkstamps) { |
| PathFragment stampOutputPath = |
| stampOutputDirectory.getRelative( |
| FileSystemUtils.replaceExtension(linkstamp.getRootRelativePath(), ".o")); |
| mapBuilder.put( |
| linkstamp, |
| // Note that link stamp actions can be shared between link actions that output shared |
| // native dep libraries. |
| linkArtifactFactory.create(ruleContext, configuration, stampOutputPath)); |
| } |
| return mapBuilder.build(); |
| } |
| |
| protected ActionOwner getOwner() { |
| return ruleContext.getActionOwner(); |
| } |
| |
| /** Sets the mnemonic for the link action. */ |
| public CppLinkActionBuilder setMnemonic(String mnemonic) { |
| this.mnemonic = mnemonic; |
| return this; |
| } |
| |
| /** Set the crosstool inputs required for the action. */ |
| public CppLinkActionBuilder setCrosstoolInputs(NestedSet<Artifact> inputs) { |
| this.crosstoolInputs = inputs; |
| return this; |
| } |
| |
| /** Returns the set of LTO artifacts created during build() */ |
| public Iterable<LtoBackendArtifacts> getAllLtoBackendArtifacts() { |
| return allLtoArtifacts; |
| } |
| |
| /** |
| * This is the LTO indexing step, rather than the real link. |
| * |
| * <p>When using this, build() will store allLtoArtifacts as a side-effect so the next build() |
| * call can emit the real link. Do not call addInput() between the two build() calls. |
| */ |
| public CppLinkActionBuilder setLtoIndexing(boolean ltoIndexing) { |
| this.isLtoIndexing = ltoIndexing; |
| return this; |
| } |
| |
| /** Sets flag for using PIC in any scheduled LTO Backend actions. */ |
| public CppLinkActionBuilder setUsePicForLtoBackendActions(boolean usePic) { |
| this.usePicForLtoBackendActions = usePic; |
| return this; |
| } |
| |
| /** Sets the C++ runtime library inputs for the action. */ |
| public CppLinkActionBuilder setRuntimeInputs( |
| ArtifactCategory runtimeType, Artifact middleman, NestedSet<Artifact> inputs) { |
| Preconditions.checkArgument((middleman == null) == inputs.isEmpty()); |
| this.runtimeType = runtimeType; |
| this.runtimeMiddleman = middleman; |
| this.runtimeInputs = inputs; |
| return this; |
| } |
| |
| /** Adds a variables extension to template the toolchain for this link action. */ |
| public CppLinkActionBuilder addVariablesExtension(VariablesExtension variablesExtension) { |
| this.variablesExtensions.add(variablesExtension); |
| return this; |
| } |
| |
| /** Adds variables extensions to template the toolchain for this link action. */ |
| public CppLinkActionBuilder addVariablesExtensions(List<VariablesExtension> variablesExtensions) { |
| for (VariablesExtension variablesExtension : variablesExtensions) { |
| addVariablesExtension(variablesExtension); |
| } |
| return this; |
| } |
| |
| /** |
| * Sets the interface output of the link. A non-null argument can only be provided if the link |
| * type is {@code DYNAMIC_LIBRARY} and fake is false. |
| */ |
| public CppLinkActionBuilder setInterfaceOutput(Artifact interfaceOutput) { |
| this.interfaceOutput = interfaceOutput; |
| return this; |
| } |
| |
| public CppLinkActionBuilder setSymbolCountsOutput(Artifact symbolCounts) { |
| this.symbolCounts = symbolCounts; |
| return this; |
| } |
| |
| /** |
| * Add additional inputs needed for the linkstamp compilation that is being done as part of the |
| * link. |
| */ |
| public CppLinkActionBuilder addCompilationInputs(Iterable<Artifact> inputs) { |
| this.compilationInputs.addAll(inputs); |
| return this; |
| } |
| |
| public CppLinkActionBuilder addTransitiveCompilationInputs(NestedSet<Artifact> inputs) { |
| this.compilationInputs.addTransitive(inputs); |
| return this; |
| } |
| |
| private void addObjectFile(LinkerInput input) { |
| // We skip file extension checks for TreeArtifacts because they represent directory artifacts |
| // without a file extension. |
| String name = input.getArtifact().getFilename(); |
| Preconditions.checkArgument( |
| input.getArtifact().isTreeArtifact() || Link.OBJECT_FILETYPES.matches(name), name); |
| this.objectFiles.add(input); |
| } |
| |
| public CppLinkActionBuilder addLtoBitcodeFiles(ImmutableMap<Artifact, Artifact> files) { |
| Preconditions.checkState(ltoBitcodeFiles == null); |
| ltoBitcodeFiles = files; |
| return this; |
| } |
| |
| public CppLinkActionBuilder setDefFile(Artifact defFile) { |
| this.defFile = defFile; |
| return this; |
| } |
| |
| /** |
| * Adds a single object file to the set of inputs. |
| */ |
| public CppLinkActionBuilder addObjectFile(Artifact input) { |
| addObjectFile(LinkerInputs.simpleLinkerInput(input, ArtifactCategory.OBJECT_FILE)); |
| return this; |
| } |
| |
| /** |
| * Adds object files to the linker action. |
| */ |
| public CppLinkActionBuilder addObjectFiles(Iterable<Artifact> inputs) { |
| for (Artifact input : inputs) { |
| addObjectFile(LinkerInputs.simpleLinkerInput(input, ArtifactCategory.OBJECT_FILE)); |
| } |
| return this; |
| } |
| |
| /** |
| * Adds non-code files to the set of inputs. They will not be passed to the linker command line |
| * unless that is explicitly modified, too. |
| */ |
| public CppLinkActionBuilder addNonCodeInputs(Iterable<Artifact> inputs) { |
| for (Artifact input : inputs) { |
| addNonCodeInput(input); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Adds a single non-code file to the set of inputs. It will not be passed to the linker command |
| * line unless that is explicitly modified, too. |
| */ |
| public CppLinkActionBuilder addNonCodeInput(Artifact input) { |
| String basename = input.getFilename(); |
| Preconditions.checkArgument(!Link.ARCHIVE_LIBRARY_FILETYPES.matches(basename), basename); |
| Preconditions.checkArgument(!Link.SHARED_LIBRARY_FILETYPES.matches(basename), basename); |
| Preconditions.checkArgument(!Link.OBJECT_FILETYPES.matches(basename), basename); |
| |
| this.nonCodeInputs.add(input); |
| return this; |
| } |
| |
| public CppLinkActionBuilder addFakeObjectFiles(Iterable<Artifact> inputs) { |
| for (Artifact input : inputs) { |
| addObjectFile(LinkerInputs.fakeLinkerInput(input)); |
| } |
| return this; |
| } |
| |
| private void checkLibrary(LibraryToLink input) { |
| String name = input.getArtifact().getFilename(); |
| Preconditions.checkArgument( |
| Link.ARCHIVE_LIBRARY_FILETYPES.matches(name) || Link.SHARED_LIBRARY_FILETYPES.matches(name), |
| "'%s' is not a library file", |
| input); |
| } |
| |
| /** |
| * Adds a single artifact to the set of inputs. The artifact must be an archive or a shared |
| * library. Note that all directly added libraries are implicitly ordered before all nested sets |
| * added with {@link #addLibraries}, even if added in the opposite order. |
| */ |
| public CppLinkActionBuilder addLibrary(LibraryToLink input) { |
| checkLibrary(input); |
| libraries.add(input); |
| return this; |
| } |
| |
| /** |
| * Adds multiple artifact to the set of inputs. The artifacts must be archives or shared |
| * libraries. |
| */ |
| public CppLinkActionBuilder addLibraries(NestedSet<LibraryToLink> inputs) { |
| for (LibraryToLink input : inputs) { |
| checkLibrary(input); |
| } |
| this.libraries.addTransitive(inputs); |
| return this; |
| } |
| |
| /** |
| * Sets the type of ELF file to be created (.a, .so, .lo, executable). The default is {@link |
| * LinkTargetType#STATIC_LIBRARY}. |
| */ |
| public CppLinkActionBuilder setLinkType(LinkTargetType linkType) { |
| this.linkType = linkType; |
| return this; |
| } |
| |
| /** |
| * Sets the degree of "staticness" of the link: fully static (static binding of all symbols), |
| * mostly static (use dynamic binding only for symbols from glibc), dynamic (use dynamic binding |
| * wherever possible). The default is {@link LinkStaticness#FULLY_STATIC}. |
| */ |
| public CppLinkActionBuilder setLinkStaticness(LinkStaticness linkStaticness) { |
| this.linkStaticness = linkStaticness; |
| return this; |
| } |
| |
| /** |
| * Sets the identifier of the library produced by the action. See |
| * {@link LinkerInputs.LibraryToLink#getLibraryIdentifier()} |
| */ |
| public CppLinkActionBuilder setLibraryIdentifier(String libraryIdentifier) { |
| this.libraryIdentifier = libraryIdentifier; |
| return this; |
| } |
| |
| /** |
| * Adds a C++ source file which will be compiled at link time. This is used to embed various |
| * values from the build system into binaries to identify their provenance. |
| * |
| * <p>Link stamps are also automatically added to the inputs. |
| */ |
| public CppLinkActionBuilder addLinkstamps(Map<Artifact, NestedSet<Artifact>> linkstamps) { |
| this.linkstamps.addAll(linkstamps.keySet()); |
| // Add inputs for linkstamping. |
| if (!linkstamps.isEmpty()) { |
| for (Map.Entry<Artifact, NestedSet<Artifact>> entry : linkstamps.entrySet()) { |
| addCompilationInputs(entry.getValue()); |
| } |
| } |
| return this; |
| } |
| |
| public CppLinkActionBuilder setAdditionalLinkstampDefines( |
| ImmutableList<String> additionalLinkstampDefines) { |
| this.additionalLinkstampDefines = Preconditions.checkNotNull(additionalLinkstampDefines); |
| return this; |
| } |
| |
| /** Adds an additional linker option. */ |
| public CppLinkActionBuilder addLinkopt(String linkopt) { |
| this.linkopts.add(linkopt); |
| return this; |
| } |
| |
| /** |
| * Adds multiple linker options at once. |
| * |
| * @see #addLinkopt(String) |
| */ |
| public CppLinkActionBuilder addLinkopts(Collection<String> linkopts) { |
| this.linkopts.addAll(linkopts); |
| return this; |
| } |
| |
| /** |
| * Merges the given link params into this builder by calling {@link #addLinkopts}, {@link |
| * #addLibraries}, and {@link #addLinkstamps}. |
| */ |
| public CppLinkActionBuilder addLinkParams( |
| CcLinkParams linkParams, RuleErrorConsumer errorListener) throws InterruptedException { |
| addLinkopts(linkParams.flattenedLinkopts()); |
| addLibraries(linkParams.getLibraries()); |
| ExtraLinkTimeLibraries extraLinkTimeLibraries = linkParams.getExtraLinkTimeLibraries(); |
| if (extraLinkTimeLibraries != null) { |
| for (ExtraLinkTimeLibrary extraLibrary : extraLinkTimeLibraries.getExtraLibraries()) { |
| addLibraries(extraLibrary.buildLibraries(ruleContext)); |
| } |
| } |
| addLinkstamps(CppHelper.resolveLinkstamps(errorListener, linkParams)); |
| return this; |
| } |
| |
| /** Sets whether this link action will be used for a cc_fake_binary; false by default. */ |
| public CppLinkActionBuilder setFake(boolean fake) { |
| this.fake = fake; |
| return this; |
| } |
| |
| /** Sets whether this link action is used for a native dependency library. */ |
| public CppLinkActionBuilder setNativeDeps(boolean isNativeDeps) { |
| this.isNativeDeps = isNativeDeps; |
| return this; |
| } |
| |
| /** |
| * Setting this to true overrides the default whole-archive computation and force-enables whole |
| * archives for every archive in the link. This is only necessary for linking executable binaries |
| * that are supposed to export symbols. |
| * |
| * <p>Usually, the link action while use whole archives for dynamic libraries that are native deps |
| * (or the legacy whole archive flag is enabled), and that are not dynamically linked. |
| * |
| * <p>(Note that it is possible to build dynamic libraries with cc_binary rules by specifying |
| * linkshared = 1, and giving the rule a name that matches the pattern {@code |
| * lib<name>.so}.) |
| */ |
| public CppLinkActionBuilder setWholeArchive(boolean wholeArchive) { |
| this.wholeArchive = wholeArchive; |
| return this; |
| } |
| |
| /** |
| * Sets whether this link action should use test-specific flags (e.g. $EXEC_ORIGIN instead of |
| * $ORIGIN for the solib search path or lazy binding); false by default. |
| */ |
| public CppLinkActionBuilder setUseTestOnlyFlags(boolean useTestOnlyFlags) { |
| this.useTestOnlyFlags = useTestOnlyFlags; |
| return this; |
| } |
| |
| /** |
| * Sets the name of the directory where the solib symlinks for the dynamic runtime libraries live. |
| * This is usually automatically set from the cc_toolchain. |
| */ |
| public CppLinkActionBuilder setRuntimeSolibDir(PathFragment runtimeSolibDir) { |
| this.runtimeSolibDir = runtimeSolibDir; |
| return this; |
| } |
| |
| /** |
| * Adds an extra input artifact to the link action. |
| */ |
| public CppLinkActionBuilder addActionInput(Artifact input) { |
| this.linkActionInputs.add(input); |
| return this; |
| } |
| |
| /** |
| * Adds extra input artifacts to the link action. |
| */ |
| public CppLinkActionBuilder addActionInputs(Iterable<Artifact> inputs) { |
| this.linkActionInputs.addAll(inputs); |
| return this; |
| } |
| |
| /** |
| * Adds extra input artifacts to the link actions. |
| */ |
| public CppLinkActionBuilder addTransitiveActionInputs(NestedSet<Artifact> inputs) { |
| this.linkActionInputs.addTransitive(inputs); |
| return this; |
| } |
| |
| /** Adds an extra output artifact to the link action. */ |
| public CppLinkActionBuilder addActionOutput(Artifact output) { |
| 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 ImmutableMap<Artifact, Artifact> linkstampMap; |
| 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 PathFragment ltoOutputRootPrefix; |
| |
| private final LinkArgCollector linkArgCollector = new LinkArgCollector(); |
| |
| public CppLinkVariablesExtension( |
| BuildConfiguration configuration, |
| ImmutableMap<Artifact, Artifact> linkstampMap, |
| boolean needWholeArchive, |
| Iterable<LinkerInput> linkerInputs, |
| ImmutableList<LinkerInput> runtimeLinkerInputs, |
| Artifact output, |
| Artifact paramFile, |
| Artifact thinltoParamFile, |
| PathFragment ltoOutputRootPrefix, |
| Artifact interfaceLibraryBuilder, |
| Artifact interfaceLibraryOutput) { |
| this.configuration = configuration; |
| this.linkstampMap = linkstampMap; |
| 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.ltoOutputRootPrefix = ltoOutputRootPrefix; |
| |
| addInputFileLinkOptions(linkArgCollector); |
| } |
| |
| @Override |
| public void addVariables(Variables.Builder buildVariables) { |
| |
| // symbol counting |
| if (symbolCounts != null) { |
| buildVariables.addStringVariable( |
| SYMBOL_COUNTS_OUTPUT_VARIABLE, symbolCounts.getExecPathString()); |
| } |
| |
| // linkstamp |
| ImmutableSet.Builder<String> linkstampPaths = ImmutableSet.builder(); |
| for (Artifact linkstampOutput : linkstampMap.values()) { |
| linkstampPaths.add(linkstampOutput.getExecPathString()); |
| } |
| |
| buildVariables.addStringSequenceVariable( |
| LINKSTAMP_PATHS_VARIABLE, linkstampPaths.build()); |
| |
| // pic |
| if (cppConfiguration.forcePic()) { |
| buildVariables.addStringVariable(FORCE_PIC_VARIABLE, ""); |
| } |
| |
| if (cppConfiguration.shouldStripBinaries()) { |
| buildVariables.addStringVariable(STRIP_DEBUG_SYMBOLS_VARIABLE, ""); |
| } |
| |
| if (getLinkType().staticness().equals(Staticness.DYNAMIC) && cppConfiguration.useFission()) { |
| 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, ""); |
| } |
| |
| // TODO(b/37271982): Remove after blaze with ar action_config release |
| buildVariables.addStringVariable(USES_ACTION_CONFIG_FOR_AR_VARIABLE, ""); |
| |
| if (linkArgCollector.getRuntimeLibrarySearchDirectories() != null) { |
| buildVariables.addStringSequenceVariable( |
| RUNTIME_LIBRARY_SEARCH_DIRECTORIES_VARIABLE, |
| linkArgCollector.getRuntimeLibrarySearchDirectories()); |
| } |
| |
| buildVariables.addCustomBuiltVariable( |
| LIBRARIES_TO_LINK_VARIABLE, linkArgCollector.getLibrariesToLink()); |
| |
| 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())); |
| } 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 == LinkTargetType.DYNAMIC_LIBRARY |
| || (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(cppConfiguration.useStartEndLib()); |
| 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) { |
| PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); |
| 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) { |
| 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. |
| 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); |
| Preconditions.checkState(!Link.useStartEndLib(input, cppConfiguration.archiveType())); |
| |
| 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. |
| * @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); |
| |
| // start-lib/end-lib library: adds its input object files. |
| if (Link.useStartEndLib(input, cppConfiguration.archiveType())) { |
| Iterable<Artifact> archiveMembers = input.getObjectFiles(); |
| if (!Iterables.isEmpty(archiveMembers)) { |
| ImmutableList.Builder<String> nonLtoArchiveMembersBuilder = ImmutableList.builder(); |
| for (Artifact member : archiveMembers) { |
| if (ltoMap != null && ltoMap.remove(member) != null) { |
| // The LTO artifacts that should be included in the final link |
| // are listed in the thinltoParamFile. 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. |
| continue; |
| } |
| nonLtoArchiveMembersBuilder.add(member.getExecPathString()); |
| } |
| ImmutableList<String> 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(); |
| if (ltoMap != null && ltoMap.remove(inputArtifact) != null) { |
| // The LTO artifacts that should be included in the final link |
| // are listed in the thinltoParamFile. |
| return; |
| } |
| |
| String name; |
| if (input.isFake()) { |
| name = Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString(); |
| } else { |
| name = inputArtifact.getExecPathString(); |
| } |
| |
| librariesToLink.addValue( |
| artifactCategory.equals(ArtifactCategory.OBJECT_FILE) |
| ? LibraryToLinkValue.forObjectFile(name, inputIsWholeArchive) |
| : LibraryToLinkValue.forStaticLibrary(name, inputIsWholeArchive)); |
| } |
| } |
| } |
| } |