blob: f0ca362f9a0ea021ef0f666f669e68e05c03b18c [file] [log] [blame]
// 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.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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.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.util.Preconditions;
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 clang flags that set the root of the linker search path. */
public static final String RUNTIME_ROOT_FLAGS_VARIABLE = "runtime_root_flags";
/** A build variable for entries in the linker search path. */
public static final String RUNTIME_ROOT_ENTRIES_VARIABLE = "runtime_root_entries";
/**
* A build variable for options applying to specific libraries in the linker invocation that
* either identify a library to be linked or add a directory to the runtime library search path.
*/
public static final String LIBOPTS_VARIABLE = "libopts";
/**
* A build variable for flags providing files to link as inputs in the linker invocation that
* should not go in a -whole_archive block.
*/
public static final String LINKER_INPUT_PARAMS_VARIABLE = "linker_input_params";
/**
* A build variable for flags providing files to link as inputs in the linker invocation that
* should go in a -whole_archive block.
*/
public static final String WHOLE_ARCHIVE_LINKER_INPUT_PARAMS_VARIABLE =
"whole_archive_linker_params";
/** A build variable whose presence indicates that whole archive flags should be applied. */
public static final String GLOBAL_WHOLE_ARCHIVE_VARIABLE = "global_whole_archive";
/** A build variable for the execpath of the output of the linker. */
public static final String OUTPUT_EXECPATH_VARIABLE = "output_execpath";
/**
* A build variable that is set to indicate a mostly static linking for which the linked binary
* should be piped to /dev/null.
*/
public static final String SKIP_MOSTLY_STATIC_VARIABLE = "skip_mostly_static";
/** 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";
// Builder-only
// Null when invoked from tests (e.g. via createTestBuilder).
@Nullable private final RuleContext ruleContext;
private final AnalysisEnvironment analysisEnvironment;
private final Artifact output;
// can be null for CppLinkAction.createTestBuilder()
@Nullable private final CcToolchainProvider toolchain;
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 List<String> linkstampOptions = new ArrayList<>();
private final List<String> linkopts = new ArrayList<>();
private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC;
private String libraryIdentifier = null;
private List<Artifact> ltoBitcodeFiles = new ArrayList<>();
private boolean fake;
private boolean isNativeDeps;
private boolean useTestOnlyFlags;
private boolean wholeArchive;
private LinkArtifactFactory linkArtifactFactory = CppLinkAction.DEFAULT_ARTIFACT_FACTORY;
private boolean isLTOIndexing = false;
private Iterable<LTOBackendArtifacts> allLTOArtifacts = null;
private final List<VariablesExtension> variablesExtensions = new ArrayList<>();
private final List<Artifact> linkActionInputs = new ArrayList<>();
/**
* Creates a builder that builds {@link CppLinkAction} instances.
*
* @param ruleContext the rule that owns the action
* @param output the output artifact
*/
public CppLinkActionBuilder(RuleContext ruleContext, Artifact output) {
this(
ruleContext,
output,
ruleContext.getConfiguration(),
ruleContext.getAnalysisEnvironment(),
CppHelper.getToolchain(ruleContext));
}
/**
* Creates a builder that builds {@link CppLinkAction} instances.
*
* @param ruleContext the rule that owns the action
* @param output the output artifact
*/
public CppLinkActionBuilder(
RuleContext ruleContext,
Artifact output,
BuildConfiguration configuration,
CcToolchainProvider toolchain) {
this(ruleContext, output, configuration, ruleContext.getAnalysisEnvironment(), toolchain);
}
/**
* 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
*/
private CppLinkActionBuilder(
@Nullable RuleContext ruleContext,
Artifact output,
BuildConfiguration configuration,
AnalysisEnvironment analysisEnvironment,
CcToolchainProvider toolchain) {
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;
if (cppConfiguration.supportsEmbeddedRuntimes() && toolchain != null) {
runtimeSolibDir = toolchain.getDynamicRuntimeSolibDir();
}
}
/**
* 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
*/
public CppLinkActionBuilder(
RuleContext ruleContext,
Artifact output,
Context linkContext,
BuildConfiguration configuration) {
// These Builder-only fields get set in the constructor:
// ruleContext, analysisEnvironment, outputPath, configuration, runtimeSolibDir
this(
ruleContext,
output,
configuration,
ruleContext.getAnalysisEnvironment(),
CppHelper.getToolchain(ruleContext));
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.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 linkstamp options for this link action.
*/
public List<String> getLinkstampOptions() {
return this.linkstampOptions;
}
/**
* 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 lto bitcode files for this link action.
*/
public List<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;
}
private Iterable<LTOBackendArtifacts> createLTOArtifacts(
PathFragment ltoOutputRootPrefix, NestedSet<LibraryToLink> uniqueLibraries) {
Set<Artifact> compiled = new LinkedHashSet<>();
for (LibraryToLink lib : uniqueLibraries) {
Iterables.addAll(compiled, lib.getLTOBitcodeFiles());
}
// 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.contains(input.getArtifact())) {
allBitcode.put(input.getArtifact().getExecPath(), input.getArtifact());
}
}
ImmutableList.Builder<LTOBackendArtifacts> ltoOutputs = ImmutableList.builder();
for (Artifact a : allBitcode.values()) {
LTOBackendArtifacts ltoArtifacts =
new LTOBackendArtifacts(
ltoOutputRootPrefix, a, allBitcode, ruleContext, configuration, linkArtifactFactory);
ltoOutputs.add(ltoArtifacts);
}
return ltoOutputs.build();
}
@VisibleForTesting
boolean canSplitCommandLine() {
if (toolchain == null || !toolchain.supportsParamFiles()) {
return false;
}
switch (linkType) {
// 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.
case DYNAMIC_LIBRARY:
return interfaceOutput == null;
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 {
Preconditions.checkState(
(libraryIdentifier == null) == (linkType == LinkTargetType.EXECUTABLE));
if (interfaceOutput != null && (fake || linkType != LinkTargetType.DYNAMIC_LIBRARY)) {
throw new RuntimeException(
"Interface output can only be used " + "with non-fake DYNAMIC_LIBRARY targets");
}
final ImmutableList<Artifact> buildInfoHeaderArtifacts =
!linkstamps.isEmpty()
? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration)
: ImmutableList.<Artifact>of();
boolean needWholeArchive =
wholeArchive
|| needWholeArchive(linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration);
NestedSet<LibraryToLink> uniqueLibraries = libraries.build();
final Iterable<Artifact> objectArtifacts = LinkerInputs.toLibraryArtifacts(objectFiles);
final Iterable<LinkerInput> linkerInputs =
IterablesChain.<LinkerInput>builder()
.add(ImmutableList.copyOf(objectFiles))
.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.<String>of() : ruleContext.getFeatures();
// For backwards compatibility, and for tests, we permit the link action to be
// instantiated without a feature configuration.
if (featureConfiguration == null) {
if (toolchain != null) {
featureConfiguration =
CcCommon.configureFeatures(ruleContext, toolchain, CcLibraryHelper.SourceCategory.CC);
} else {
featureConfiguration = CcCommon.configureFeatures(ruleContext);
}
}
final LibraryToLink outputLibrary = linkType == LinkTargetType.EXECUTABLE
? 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");
allLTOArtifacts = createLTOArtifacts(ltoOutputRootPrefix, uniqueLibraries);
}
PathFragment linkerParamsFileRootPath = null;
@Nullable Artifact linkerParamsFile = 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.
linkerParamsFileRootPath =
ParameterFile.derivePath(output.getRootRelativePath(), "lto-final");
linkerParamsFile =
linkArtifactFactory.create(ruleContext, configuration, linkerParamsFileRootPath);
}
final ImmutableList<Artifact> actionOutputs;
if (isLTOIndexing) {
ImmutableList.Builder<Artifact> builder = ImmutableList.builder();
for (LTOBackendArtifacts ltoA : allLTOArtifacts) {
ltoA.addIndexingOutputs(builder);
}
if (linkerParamsFile != null) {
builder.add(linkerParamsFile);
}
actionOutputs = builder.build();
} else {
actionOutputs =
constructOutputs(
output,
linkstampMap.values(),
interfaceOutputLibrary == null ? null : interfaceOutputLibrary.getArtifact(),
symbolCounts);
}
ImmutableList<LinkerInput> runtimeLinkerInputs =
ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs, runtimeType));
// Add build variables necessary to template link args into the crosstool.
Variables.Builder buildVariablesBuilder = new Variables.Builder();
CppLinkVariablesExtension variablesExtension =
isLTOIndexing
? new CppLinkVariablesExtension(
configuration,
linkstampMap,
needWholeArchive,
linkerInputs,
runtimeLinkerInputs,
null,
linkerParamsFile,
ltoOutputRootPrefix)
: new CppLinkVariablesExtension(
configuration,
linkstampMap,
needWholeArchive,
linkerInputs,
runtimeLinkerInputs,
output,
linkerParamsFile,
PathFragment.EMPTY_FRAGMENT);
variablesExtension.addVariables(buildVariablesBuilder);
for (VariablesExtension extraVariablesExtension : variablesExtensions) {
extraVariablesExtension.addVariables(buildVariablesBuilder);
}
Variables buildVariables = buildVariablesBuilder.build();
PathFragment paramRootPath =
ParameterFile.derivePath(output.getRootRelativePath(), (isLTOIndexing) ? "lto-index" : "2");
@Nullable
final Artifact paramFile =
canSplitCommandLine()
? linkArtifactFactory.create(ruleContext, configuration, paramRootPath)
: null;
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)
.setBuildVariables(buildVariables)
.setFeatureConfiguration(featureConfiguration);
// TODO(b/30228443): Refactor noWholeArchiveInputs into action_configs, and remove this.
if (needWholeArchive) {
linkCommandLineBuilder.setNoWholeArchiveFlags(variablesExtension.getNoWholeArchiveInputs());
}
if (!isLTOIndexing) {
linkCommandLineBuilder
.setOutput(output)
.setInterfaceOutput(interfaceOutput)
.setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts)
.setInterfaceSoBuilder(getInterfaceSoBuilder())
.setLinkstamps(linkstampMap)
.setLinkopts(ImmutableList.copyOf(linkopts))
.addLinkstampCompileOptions(linkstampOptions);
} else {
List<String> opts = new ArrayList<>(linkopts);
opts.addAll(featureConfiguration.getCommandLine("lto-indexing", buildVariables));
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.addAll(linkActionInputs);
if (runtimeMiddleman != null) {
dependencyInputsBuilder.add(runtimeMiddleman);
}
if (!isLTOIndexing) {
dependencyInputsBuilder.addAll(buildInfoHeaderArtifacts);
dependencyInputsBuilder.addAll(linkstamps);
dependencyInputsBuilder.addTransitive(compilationInputs.build());
}
Iterable<Artifact> expandedInputs =
LinkerInputs.toLibraryArtifacts(
Link.mergeInputsDependencies(
uniqueLibraries, needWholeArchive, cppConfiguration.archiveType()));
Iterable<Artifact> expandedNonLibraryInputs = LinkerInputs.toLibraryArtifacts(objectFiles);
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;
} else if (isLTOIndexing && allLTOArtifacts != null) {
for (LTOBackendArtifacts a : allLTOArtifacts) {
List<String> argv = new ArrayList<>();
argv.addAll(cppConfiguration.getLinkOptions());
argv.addAll(cppConfiguration.getCompilerOptions(features));
a.setCommandLine(argv);
}
}
// 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 (linkerParamsFile != null && !isLTOIndexing) {
inputsBuilder.add(ImmutableList.of(linkerParamsFile));
}
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);
}
Map<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.<String>builder();
if (featureConfiguration.actionIsConfigured(getActionName())) {
executionRequirements.addAll(
featureConfiguration.getToolForAction(getActionName()).getExecutionRequirements());
}
return new CppLinkAction(
getOwner(),
inputsBuilder.deduplicate().build(),
actionOutputs,
cppConfiguration,
outputLibrary,
output,
interfaceOutputLibrary,
fake,
isLTOIndexing,
allLTOArtifacts,
linkCommandLine,
toolchainEnv,
executionRequirements.build());
}
/** 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.getLinkOptions().contains("-shared");
return (isNativeDeps || cppConfig.legacyWholeArchive())
&& (fullyStatic || mostlyStatic)
&& sharedLinkopts;
}
private static ImmutableList<Artifact> constructOutputs(
Artifact primaryOutput, Collection<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("_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();
}
protected Artifact getInterfaceSoBuilder() {
return analysisEnvironment.getEmbeddedToolArtifact(CppRuleClasses.BUILD_INTERFACE_SO);
}
/** Set the crosstool inputs required for the action. */
public CppLinkActionBuilder setCrosstoolInputs(NestedSet<Artifact> inputs) {
this.crosstoolInputs = inputs;
return this;
}
/** Sets the feature configuration for the action. */
public CppLinkActionBuilder setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
this.featureConfiguration = featureConfiguration;
return this;
}
/**
* 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 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) {
String name = input.getArtifact().getFilename();
Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(name), name);
this.objectFiles.add(input);
}
public CppLinkActionBuilder addLTOBitcodeFiles(Iterable<Artifact> files) {
for (Artifact a : files) {
ltoBitcodeFiles.add(a);
}
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 addLinkstampCompilerOptions(ImmutableList<String> linkstampOptions) {
this.linkstampOptions = linkstampOptions;
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&lt;name&gt;.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;
}
/**
* Sets extra input artifacts to the link action.
*/
public CppLinkActionBuilder addActionInputs(Collection<Artifact> inputs) {
this.linkActionInputs.addAll(inputs);
return this;
}
private static class LinkArgCollector {
String rpathRoot;
List<String> rpathEntries;
Set<String> libopts;
List<String> linkerInputParams;
List<String> wholeArchiveLinkerInputParams;
List<String> noWholeArchiveInputs;
public void setRpathRoot(String rPathRoot) {
this.rpathRoot = rPathRoot;
}
public void setRpathEntries(List<String> rpathEntries) {
this.rpathEntries = rpathEntries;
}
public void setLibopts(Set<String> libopts) {
this.libopts = libopts;
}
public void setLinkerInputParams(List<String> linkerInputParams) {
this.linkerInputParams = linkerInputParams;
}
public void setWholeArchiveLinkerInputParams(List<String> wholeArchiveInputParams) {
this.wholeArchiveLinkerInputParams = wholeArchiveInputParams;
}
public void setNoWholeArchiveInputs(List<String> noWholeArchiveInputs) {
this.noWholeArchiveInputs = noWholeArchiveInputs;
}
public String getRpathRoot() {
return rpathRoot;
}
public List<String> getRpathEntries() {
return rpathEntries;
}
public Set<String> getLibopts() {
return libopts;
}
public List<String> getLinkerInputParams() {
return linkerInputParams;
}
public List<String> getWholeArchiveLinkerInputParams() {
return wholeArchiveLinkerInputParams;
}
public List<String> getNoWholeArchiveInputs() {
return noWholeArchiveInputs;
}
}
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 linkerParamsFile;
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 linkerParamsFile,
PathFragment ltoOutputRootPrefix) {
this.configuration = configuration;
this.linkstampMap = linkstampMap;
this.needWholeArchive = needWholeArchive;
this.linkerInputs = linkerInputs;
this.runtimeLinkerInputs = runtimeLinkerInputs;
this.outputArtifact = output;
this.linkerParamsFile = linkerParamsFile;
this.ltoOutputRootPrefix = ltoOutputRootPrefix;
addInputFileLinkOptions(linkArgCollector);
}
/**
* Returns linker parameters indicating libraries that should not be linked inside a
* --whole_archive block.
*
* <p>TODO(b/30228443): Refactor into action configs
*/
public List<String> getNoWholeArchiveInputs() {
return linkArgCollector.getNoWholeArchiveInputs();
}
@Override
public void addVariables(Variables.Builder buildVariables) {
// symbol counting
if (symbolCounts != null) {
buildVariables.addVariable(SYMBOL_COUNTS_OUTPUT_VARIABLE, symbolCounts.getExecPathString());
}
// linkstamp
ImmutableSet.Builder<String> linkstampPaths = ImmutableSet.<String>builder();
for (Artifact linkstampOutput : linkstampMap.values()) {
linkstampPaths.add(linkstampOutput.getExecPathString());
}
buildVariables.addSequenceVariable(LINKSTAMP_PATHS_VARIABLE, linkstampPaths.build());
// pic
boolean forcePic = cppConfiguration.forcePic();
if (forcePic) {
buildVariables.addVariable(FORCE_PIC_VARIABLE, "");
}
// rpath
if (linkArgCollector.getRpathRoot() != null) {
buildVariables.addVariable(RUNTIME_ROOT_FLAGS_VARIABLE, linkArgCollector.getRpathRoot());
}
if (linkArgCollector.getRpathEntries() != null) {
buildVariables.addSequenceVariable(
RUNTIME_ROOT_ENTRIES_VARIABLE, linkArgCollector.getRpathEntries());
}
buildVariables.addSequenceVariable(LIBOPTS_VARIABLE, linkArgCollector.getLibopts());
buildVariables.addSequenceVariable(
LINKER_INPUT_PARAMS_VARIABLE, linkArgCollector.getLinkerInputParams());
buildVariables.addSequenceVariable(
WHOLE_ARCHIVE_LINKER_INPUT_PARAMS_VARIABLE,
linkArgCollector.getWholeArchiveLinkerInputParams());
// global archive
if (needWholeArchive) {
buildVariables.addVariable(GLOBAL_WHOLE_ARCHIVE_VARIABLE, "");
}
// mostly static
if (linkStaticness == LinkStaticness.MOSTLY_STATIC && cppConfiguration.skipStaticOutputs()) {
buildVariables.addVariable(SKIP_MOSTLY_STATIC_VARIABLE, "");
}
// output exec path
if (this.outputArtifact != null) {
buildVariables.addVariable(
OUTPUT_EXECPATH_VARIABLE, this.outputArtifact.getExecPathString());
}
if (!ltoOutputRootPrefix.equals(PathFragment.EMPTY_FRAGMENT)) {
if (linkerParamsFile != null) {
buildVariables.addVariable(
"thinlto_optional_params_file", "=" + linkerParamsFile.getExecPathString());
} else {
buildVariables.addVariable("thinlto_optional_params_file", "");
}
buildVariables.addVariable(
"thinlto_prefix_replace",
configuration.getBinDirectory().getExecPathString()
+ ";"
+ configuration.getBinDirectory().getExecPath().getRelative(ltoOutputRootPrefix));
}
// Variables arising from the toolchain
buildVariables
.addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables())
.build();
CppHelper.getFdoSupport(ruleContext).getLinkOptions(featureConfiguration, buildVariables);
}
private boolean isSharedNativeLibrary() {
return isNativeDeps && cppConfiguration.shareNativeDeps();
}
private boolean inputNeedsWholeArchive(LinkerInput input) {
if (Link.useStartEndLib(input, cppConfiguration.archiveType())) {
return false;
}
return input.getArtifactCategory() == ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY
&& !wholeArchive && !needWholeArchive;
}
/**
* 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) {
// Used to collect -L and -Wl,-rpath options, ensuring that each used only once.
Set<String> libOpts = new LinkedHashSet<>();
// List of command line parameters that need to be placed *outside* of
// --whole-archive ... --no-whole-archive.
List<String> noWholeArchiveInputs = new ArrayList<>();
PathFragment solibDir =
configuration
.getBinDirectory(ruleContext.getRule().getRepository())
.getExecPath()
.getRelative(cppConfiguration.getSolibDirectory());
String runtimeSolibName = runtimeSolibDir != null ? runtimeSolibDir.getBaseName() : null;
boolean runtimeRpath =
runtimeSolibDir != null
&& (linkType == LinkTargetType.DYNAMIC_LIBRARY
|| (linkType == LinkTargetType.EXECUTABLE
&& linkStaticness == LinkStaticness.DYNAMIC));
String rpathRoot = null;
List<String> runtimeRpathEntries = new ArrayList<>();
if (output != null) {
String origin =
useTestOnlyFlags && cppConfiguration.supportsExecOrigin()
? "$EXEC_ORIGIN/"
: "$ORIGIN/";
if (runtimeRpath) {
runtimeRpathEntries.add("-Wl,-rpath," + origin + runtimeSolibName + "/");
}
// 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 =
"-Wl,-rpath," + origin + ":" + origin + cppConfiguration.getSolibDirectory() + "/";
if (runtimeRpath) {
runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + 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) {
runtimeRpathEntries.add(
"-Wl,-rpath,"
+ origin
+ Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1)
+ runtimeSolibName
+ "/");
}
rpathRoot =
"-Wl,-rpath,"
+ origin
+ Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1)
+ cppConfiguration.getSolibDirectory()
+ "/";
if (isNativeDeps) {
// We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to
// the package directory)
if (runtimeRpath) {
runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + runtimeSolibName + "/");
}
rpathRoot += ":" + origin;
}
}
}
boolean includeSolibDir = false;
Map<Artifact, Artifact> ltoMap = null;
if (!isLTOIndexing && (allLTOArtifacts != 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());
ltoMap = new HashMap<>();
for (LTOBackendArtifacts l : allLTOArtifacts) {
ltoMap.put(l.getBitcodeFile(), l.getObjectFile());
}
}
List<String> wholeArchiveInputParams = new ArrayList<>();
List<String> standardArchiveInputParams = new ArrayList<>();
for (LinkerInput input : linkerInputs) {
if (input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY) {
PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory();
Preconditions.checkState(
libDir.startsWith(solibDir),
"Artifact '%s' is not under directory '%s'.",
input.getArtifact(),
solibDir);
if (libDir.equals(solibDir)) {
includeSolibDir = true;
}
addDynamicInputLinkOptions(
input, standardArchiveInputParams, libOpts, solibDir, rpathRoot);
} else {
addStaticInputLinkOptions(
input, wholeArchiveInputParams, standardArchiveInputParams, ltoMap);
}
}
boolean includeRuntimeSolibDir = false;
for (LinkerInput input : runtimeLinkerInputs) {
List<String> optionsList = needWholeArchive
? noWholeArchiveInputs : standardArchiveInputParams;
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, optionsList, libOpts, solibDir, rpathRoot);
} else {
addStaticInputLinkOptions(input,
needWholeArchive ? noWholeArchiveInputs : wholeArchiveInputParams,
needWholeArchive ? noWholeArchiveInputs : standardArchiveInputParams,
ltoMap);
}
}
if (linkerParamsFile != null && ltoOutputRootPrefix.equals(PathFragment.EMPTY_FRAGMENT)) {
standardArchiveInputParams.add("-Wl,@" + linkerParamsFile.getExecPathString());
}
// rpath ordering matters for performance; first add the one where most libraries are found.
if (includeSolibDir && rpathRoot != null) {
linkArgCollector.setRpathRoot(rpathRoot);
}
if (includeRuntimeSolibDir) {
linkArgCollector.setRpathEntries(runtimeRpathEntries);
}
linkArgCollector.setLibopts(libOpts);
linkArgCollector.setLinkerInputParams(standardArchiveInputParams);
linkArgCollector.setWholeArchiveLinkerInputParams(wholeArchiveInputParams);
linkArgCollector.setNoWholeArchiveInputs(noWholeArchiveInputs);
if (ltoMap != null) {
Preconditions.checkState(ltoMap.isEmpty(), "Still have LTO objects left: %s", ltoMap);
}
}
/** Adds command-line options for a dynamic library input file into options and libOpts. */
private void addDynamicInputLinkOptions(
LinkerInput input,
List<String> options,
Set<String> libOpts,
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 (rpathRoot != null
&& !libDir.equals(solibDir)
&& (runtimeSolibDir == null || !runtimeSolibDir.equals(libDir))) {
String dotdots = "";
PathFragment commonParent = solibDir;
while (!libDir.startsWith(commonParent)) {
dotdots += "../";
commonParent = commonParent.getParentDirectory();
}
libOpts.add(rpathRoot + dotdots + libDir.relativeTo(commonParent).getPathString());
}
libOpts.add("-L" + inputArtifact.getExecPath().getParentDirectory().getPathString());
String name = inputArtifact.getFilename();
if (CppFileTypes.SHARED_LIBRARY.matches(name)) {
String libName = name.replaceAll("(^lib|\\.(so|dylib)$)", "");
options.add("-l" + libName);
} 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.
options.add(inputArtifact.getExecPathString());
}
}
/**
* Adds command-line options for a static library or non-library input into options.
*
* @param ltoMap is a mutable list of exec paths that should be on the command-line, which must
* be supplied for LTO final links.
*/
private void addStaticInputLinkOptions(
LinkerInput input, List<String> wholeArchiveOptions, List<String> standardOptions,
@Nullable Map<Artifact, Artifact> ltoMap) {
Preconditions.checkState(!(input.getArtifactCategory() == ArtifactCategory.DYNAMIC_LIBRARY));
// If we had any LTO artifacts, ltoMap whould be non-null. In that case,
// we should have created a linkerParamsFile 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 || linkerParamsFile != 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)) {
List<String> nonLTOArchiveMembers = new ArrayList<>();
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 linkerParamsFile. 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;
}
nonLTOArchiveMembers.add(member.getExecPathString());
}
if (!nonLTOArchiveMembers.isEmpty()) {
standardOptions.add("-Wl,--start-lib");
standardOptions.addAll(nonLTOArchiveMembers);
standardOptions.add("-Wl,--end-lib");
}
}
} else {
List<String> options = inputNeedsWholeArchive(input)
? wholeArchiveOptions : standardOptions;
// For anything else, add the input directly.
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 linkerParamsFile.
return;
}
if (input.isFake()) {
options.add(Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString());
} else {
options.add(inputArtifact.getExecPathString());
}
}
}
}
}