in src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java [546:1027]
public CppLinkAction build() throws InterruptedException, RuleErrorException {
NestedSet<LinkerInputs.LibraryToLink> originalUniqueLibraries = libraries.build();
// Executable links do not have library identifiers.
boolean hasIdentifier = (libraryIdentifier != null);
boolean isExecutable = linkType.isExecutable();
Preconditions.checkState(hasIdentifier != isExecutable);
Preconditions.checkNotNull(featureConfiguration);
ImmutableSet<Linkstamp> linkstamps = linkstampsBuilder.build();
final ImmutableMap<Linkstamp, Artifact> linkstampMap =
mapLinkstampsToOutputs(
linkstamps,
actionConstructionContext,
repositoryName,
configuration,
output,
linkArtifactFactory);
if (interfaceOutput != null && !linkType.isDynamicLibrary()) {
throw new RuntimeException(
"Interface output can only be used " + "with DYNAMIC_LIBRARY targets");
}
if (!featureConfiguration.actionIsConfigured(linkType.getActionName())) {
ruleErrorConsumer.ruleError(
String.format(
"Expected action_config for '%s' to be configured", linkType.getActionName()));
}
final ImmutableList<Artifact> buildInfoHeaderArtifacts =
!linkstamps.isEmpty()
? actionConstructionContext
.getAnalysisEnvironment()
.getBuildInfo(isStampingEnabled, CppBuildInfo.KEY, configuration)
: ImmutableList.of();
boolean needWholeArchive =
wholeArchive
|| needWholeArchive(
featureConfiguration, linkingMode, linkType, linkopts, cppConfiguration);
// Disallow LTO indexing for test targets that link statically, and optionally for any
// linkstatic target (which can be used to disable LTO indexing for non-testonly cc_binary
// built due to data dependences for a blaze test invocation). Otherwise this will provoke
// Blaze OOM errors in the case where multiple static tests are invoked together,
// since each target needs a separate set of LTO Backend actions. With dynamic linking,
// the targest share the dynamic libraries which were produced via smaller subsets of
// LTO indexing/backends. ThinLTO on the tests will be different than the ThinLTO
// optimizations applied to the associated main binaries anyway.
// Even for dynamically linked tests, disallow linkstatic libraries from participating
// in the test's LTO indexing step for similar reasons.
boolean canIncludeAnyLinkStaticInLtoIndexing =
!featureConfiguration.isEnabled(
CppRuleClasses.THIN_LTO_ALL_LINKSTATIC_USE_SHARED_NONLTO_BACKENDS);
boolean canIncludeAnyLinkStaticTestTargetInLtoIndexing =
!featureConfiguration.isEnabled(
CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS);
boolean includeLinkStaticInLtoIndexing =
canIncludeAnyLinkStaticInLtoIndexing
&& (canIncludeAnyLinkStaticTestTargetInLtoIndexing || !isTestOrTestOnlyTarget);
boolean allowLtoIndexing =
includeLinkStaticInLtoIndexing
|| (linkingMode == Link.LinkingMode.DYNAMIC && !ltoCompilationContext.isEmpty());
PathFragment ltoOutputRootPrefix = null;
if (isLtoIndexing) {
Preconditions.checkState(allLtoArtifacts == null);
ltoOutputRootPrefix =
allowLtoIndexing
? FileSystemUtils.appendExtension(output.getRootRelativePath(), ".lto")
: PathFragment.create(SHARED_NONLTO_BACKEND_ROOT_PREFIX);
// Use the originalUniqueLibraries which contains the full bitcode files
// needed by the LTO backends (as opposed to the minimized bitcode files
// containing just the summaries and symbol information that can be used by
// the LTO indexing step).
allLtoArtifacts =
createLtoArtifacts(
ltoOutputRootPrefix,
originalUniqueLibraries,
allowLtoIndexing,
includeLinkStaticInLtoIndexing);
if (!allowLtoIndexing) {
return null;
}
}
// 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<LinkerInputs.LibraryToLink> uniqueLibraries;
ImmutableSet<LinkerInput> objectFileInputs;
ImmutableSet<LinkerInput> linkstampObjectFileInputs;
if (isLtoIndexing) {
objectFileInputs = computeLtoIndexingObjectFileInputs();
uniqueLibraries =
computeLtoIndexingUniqueLibraries(
originalUniqueLibraries, includeLinkStaticInLtoIndexing);
linkstampObjectFileInputs = ImmutableSet.of();
} else {
objectFileInputs = ImmutableSet.copyOf(objectFiles);
linkstampObjectFileInputs =
ImmutableSet.copyOf(LinkerInputs.linkstampLinkerInputs(linkstampMap.values()));
uniqueLibraries = originalUniqueLibraries;
}
Map<Artifact, Artifact> ltoMapping = new HashMap<>();
if (isFinalLinkOfLtoBuild()) {
for (LtoBackendArtifacts a : allLtoArtifacts) {
ltoMapping.put(a.getBitcodeFile(), a.getObjectFile());
}
}
NestedSet<Artifact> objectArtifacts =
getArtifactsPossiblyLtoMapped(objectFileInputs, ltoMapping);
NestedSet<Artifact> linkstampObjectArtifacts =
getArtifactsPossiblyLtoMapped(linkstampObjectFileInputs, ltoMapping);
ImmutableSet<Artifact> combinedObjectArtifacts =
ImmutableSet.<Artifact>builder()
.addAll(objectArtifacts.toList())
.addAll(linkstampObjectArtifacts.toList())
.build();
final LinkerInputs.LibraryToLink outputLibrary =
linkType.isExecutable()
? null
: LinkerInputs.newInputLibrary(
output,
linkType.getLinkerOutput(),
libraryIdentifier,
linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER
? combinedObjectArtifacts
: ImmutableSet.of(),
linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER
? ltoCompilationContext
: LtoCompilationContext.EMPTY,
createSharedNonLtoArtifacts(isLtoIndexing),
/* mustKeepDebug= */ false);
final LinkerInputs.LibraryToLink interfaceOutputLibrary =
(interfaceOutput == null)
? null
: LinkerInputs.newInputLibrary(
interfaceOutput,
ArtifactCategory.DYNAMIC_LIBRARY,
libraryIdentifier,
combinedObjectArtifacts,
ltoCompilationContext,
/* sharedNonLtoBackends= */ null,
/* mustKeepDebug= */ false);
@Nullable Artifact thinltoParamFile = null;
@Nullable Artifact thinltoMergedObjectFile = null;
PathFragment outputRootPath =
output.getOutputDirRelativePath(configuration.isSiblingRepositoryLayout());
if (allowLtoIndexing && 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(outputRootPath, "lto-final");
thinltoParamFile =
linkArtifactFactory.create(
actionConstructionContext, repositoryName, configuration, linkerParamFileRootPath);
// Create artifact for the merged object file, which is an object file that is created
// during the LTO indexing step and needs to be passed to the final link.
PathFragment thinltoMergedObjectFileRootPath =
outputRootPath.replaceName(outputRootPath.getBaseName() + ".lto.merged.o");
thinltoMergedObjectFile =
linkArtifactFactory.create(
actionConstructionContext,
repositoryName,
configuration,
thinltoMergedObjectFileRootPath);
}
final ImmutableSet<Artifact> actionOutputs;
if (isLtoIndexing) {
ImmutableSet.Builder<Artifact> builder = ImmutableSet.builder();
for (LtoBackendArtifacts ltoA : allLtoArtifacts) {
ltoA.addIndexingOutputs(builder);
}
if (thinltoParamFile != null) {
builder.add(thinltoParamFile);
}
if (thinltoMergedObjectFile != null) {
builder.add(thinltoMergedObjectFile);
addObjectFile(thinltoMergedObjectFile);
}
actionOutputs = builder.build();
} else {
actionOutputs =
constructOutputs(
output,
linkActionOutputs.build(),
interfaceOutputLibrary == null ? null : interfaceOutputLibrary.getArtifact());
}
// Linker inputs without any start/end lib expansions.
final Iterable<LinkerInput> nonExpandedLinkerInputs =
IterablesChain.<LinkerInput>builder()
.add(objectFileInputs)
.add(linkstampObjectFileInputs)
.add(uniqueLibraries.toList())
.add(
// Adding toolchain libraries without whole archive no-matter-what. People don't
// want to include whole libstdc++ in their binary ever.
ImmutableSet.copyOf(
LinkerInputs.simpleLinkerInputs(
toolchainLibrariesInputs.toList(),
toolchainLibrariesType,
/* disableWholeArchive= */ true)))
.build();
PathFragment paramRootPath =
ParameterFile.derivePath(outputRootPath, (isLtoIndexing) ? "lto-index" : "2");
@Nullable
final Artifact paramFile =
canSplitCommandLine()
? linkArtifactFactory.create(
actionConstructionContext, repositoryName, configuration, paramRootPath)
: null;
// Add build variables necessary to template link args into the crosstool.
CcToolchainVariables.Builder buildVariablesBuilder =
CcToolchainVariables.builder(
toolchain.getBuildVariables(configuration.getOptions(), cppConfiguration));
Preconditions.checkState(!isLtoIndexing || allowLtoIndexing);
Preconditions.checkState(allowLtoIndexing || thinltoParamFile == null);
Preconditions.checkState(allowLtoIndexing || thinltoMergedObjectFile == null);
PathFragment solibDir =
configuration
.getBinDirectory(repositoryName)
.getExecPath()
.getRelative(toolchain.getSolibDirectory());
LibrariesToLinkCollector librariesToLinkCollector =
new LibrariesToLinkCollector(
isNativeDeps,
cppConfiguration,
toolchain,
toolchainLibrariesSolibDir,
linkType,
linkingMode,
output,
solibDir,
isLtoIndexing,
allLtoArtifacts,
featureConfiguration,
thinltoParamFile,
allowLtoIndexing,
nonExpandedLinkerInputs,
needWholeArchive,
ruleErrorConsumer);
CollectedLibrariesToLink collectedLibrariesToLink =
librariesToLinkCollector.collectLibrariesToLink();
NestedSet<Artifact> expandedLinkerArtifacts =
getArtifactsPossiblyLtoMapped(
collectedLibrariesToLink.getExpandedLinkerInputs().toList(), ltoMapping);
CcToolchainVariables variables;
try {
ImmutableList.Builder<String> userLinkFlags =
ImmutableList.<String>builder().addAll(linkopts).addAll(cppConfiguration.getLinkopts());
if (isLtoIndexing && cppConfiguration.useStandaloneLtoIndexingCommandLines()) {
userLinkFlags.addAll(cppConfiguration.getLtoIndexOptions());
}
variables =
LinkBuildVariables.setupVariables(
getLinkType().linkerOrArchiver().equals(LinkerOrArchiver.LINKER),
configuration.getBinDirectory(repositoryName).getExecPath(),
output.getExecPathString(),
output.getRootRelativePath().getBaseName(),
linkType.equals(LinkTargetType.DYNAMIC_LIBRARY),
paramFile != null ? paramFile.getExecPathString() : null,
thinltoParamFile != null ? thinltoParamFile.getExecPathString() : null,
thinltoMergedObjectFile != null ? thinltoMergedObjectFile.getExecPathString() : null,
mustKeepDebug,
toolchain,
cppConfiguration,
configuration.getOptions(),
featureConfiguration,
useTestOnlyFlags,
isLtoIndexing,
userLinkFlags.build(),
toolchain.getInterfaceSoBuilder().getExecPathString(),
interfaceOutput != null ? interfaceOutput.getExecPathString() : null,
ltoOutputRootPrefix,
defFile != null ? defFile.getExecPathString() : null,
fdoContext,
collectedLibrariesToLink.getRuntimeLibrarySearchDirectories(),
collectedLibrariesToLink.getLibrariesToLink(),
collectedLibrariesToLink.getLibrarySearchDirectories(),
/* addIfsoRelatedVariables= */ true);
} catch (EvalException e) {
ruleErrorConsumer.ruleError(e.getMessage());
variables = CcToolchainVariables.EMPTY;
}
buildVariablesBuilder.addAllNonTransitive(variables);
for (VariablesExtension extraVariablesExtension : variablesExtensions) {
extraVariablesExtension.addVariables(buildVariablesBuilder);
}
CcToolchainVariables buildVariables = buildVariablesBuilder.build();
Preconditions.checkArgument(
linkType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY,
"you can't link an interface dynamic library directly");
if (!linkType.isDynamicLibrary()) {
Preconditions.checkArgument(
interfaceOutput == null,
"interface output may only be non-null for dynamic library links");
}
if (linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER) {
// solib dir must be null for static links
toolchainLibrariesSolibDir = null;
Preconditions.checkArgument(
linkingMode == Link.LinkingMode.STATIC, "static library link must be static");
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()
.setActionName(getActionName())
.setLinkerInputArtifacts(
NestedSetBuilder.<Artifact>stableOrder()
.addTransitive(expandedLinkerArtifacts)
.addTransitive(linkstampObjectArtifacts)
.build())
.setLinkTargetType(linkType)
.setLinkingMode(linkingMode)
.setToolchainLibrariesSolibDir(
linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER
? null
: toolchainLibrariesSolibDir)
.setNativeDeps(isNativeDeps)
.setUseTestOnlyFlags(useTestOnlyFlags)
.setParamFile(paramFile)
.setFeatureConfiguration(featureConfiguration);
// TODO(b/62693279): Cleanup once internal crosstools specify ifso building correctly.
if (shouldUseLinkDynamicLibraryTool()) {
linkCommandLineBuilder.forceToolPath(
toolchain.getLinkDynamicLibraryTool().getExecPathString());
}
if (!isLtoIndexing) {
linkCommandLineBuilder.setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts);
}
linkCommandLineBuilder.setBuildVariables(buildVariables);
LinkCommandLine linkCommandLine = linkCommandLineBuilder.build();
// Compute the set of inputs - we only need stable order here.
NestedSetBuilder<Artifact> dependencyInputsBuilder = NestedSetBuilder.stableOrder();
dependencyInputsBuilder.addTransitive(linkerFiles);
dependencyInputsBuilder.addTransitive(linkActionInputs.build());
// TODO(b/62693279): Cleanup once internal crosstools specify ifso building correctly.
if (shouldUseLinkDynamicLibraryTool()) {
dependencyInputsBuilder.add(toolchain.getLinkDynamicLibraryTool());
}
if (defFile != null) {
dependencyInputsBuilder.add(defFile);
}
NestedSet<Artifact> nonCodeInputsAsNestedSet =
NestedSetBuilder.wrap(Order.STABLE_ORDER, nonCodeInputs);
// getPrimaryInput returns the first element, and that is a public interface - therefore the
// order here is important.
NestedSetBuilder<Artifact> inputsBuilder =
NestedSetBuilder.<Artifact>stableOrder()
.addTransitive(expandedLinkerArtifacts)
.addTransitive(nonCodeInputsAsNestedSet)
.addTransitive(dependencyInputsBuilder.build());
if (thinltoParamFile != null && !isLtoIndexing) {
inputsBuilder.add(thinltoParamFile);
}
if (linkCommandLine.getParamFile() != null) {
inputsBuilder.add(linkCommandLine.getParamFile());
// Pass along tree artifacts, so they can be properly expanded.
NestedSet<Artifact> paramFileActionInputs =
NestedSetBuilder.wrap(
Order.STABLE_ORDER,
Iterables.filter(expandedLinkerArtifacts.toList(), Artifact::isTreeArtifact));
Action parameterFileWriteAction =
new ParameterFileWriteAction(
getOwner(),
paramFileActionInputs,
paramFile,
linkCommandLine.paramCmdLine(),
ParameterFile.ParameterFileType.UNQUOTED);
actionConstructionContext.registerAction(parameterFileWriteAction);
}
ImmutableMap<String, String> toolchainEnv =
CppHelper.getEnvironmentVariables(
ruleErrorConsumer, featureConfiguration, buildVariables, getActionName());
// 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.
if (featureConfiguration.actionIsConfigured(getActionName())) {
for (String req : featureConfiguration.getToolRequirementsForAction(getActionName())) {
executionInfo.put(req, "");
}
}
configuration.modifyExecutionInfo(
executionInfo, CppLinkAction.getMnemonic(mnemonic, isLtoIndexing));
if (!isLtoIndexing) {
Set<String> seenLinkstampSources = new HashSet<>();
for (Map.Entry<Linkstamp, Artifact> linkstampEntry : linkstampMap.entrySet()) {
Artifact source = linkstampEntry.getKey().getArtifact();
if (seenLinkstampSources.contains(source.getExecPathString())) {
continue;
}
seenLinkstampSources.add(source.getExecPathString());
actionConstructionContext.registerAction(
CppLinkstampCompileHelper.createLinkstampCompileAction(
ruleErrorConsumer,
actionConstructionContext,
grepIncludes,
configuration,
source,
linkstampEntry.getValue(),
linkstampEntry.getKey().getDeclaredIncludeSrcs(),
nonCodeInputsAsNestedSet,
inputsBuilder.build(),
buildInfoHeaderArtifacts,
additionalLinkstampDefines,
toolchain,
configuration.isCodeCoverageEnabled(),
cppConfiguration,
CppHelper.getFdoBuildStamp(cppConfiguration, fdoContext, featureConfiguration),
featureConfiguration,
cppConfiguration.forcePic()
|| (linkType.isDynamicLibrary()
&& toolchain.usePicForDynamicLibraries(
cppConfiguration, featureConfiguration)),
Matcher.quoteReplacement(
isNativeDeps && cppConfiguration.shareNativeDeps()
? output.getExecPathString()
: Label.print(getOwner().getLabel())),
Matcher.quoteReplacement(output.getExecPathString()),
cppSemantics));
}
inputsBuilder.addAll(linkstampMap.values());
}
inputsBuilder.addTransitive(linkstampObjectArtifacts);
return new CppLinkAction(
getOwner(),
mnemonic,
inputsBuilder.build(),
actionOutputs,
outputLibrary,
output,
interfaceOutputLibrary,
isLtoIndexing,
linkstampMap,
linkCommandLine,
configuration.getActionEnvironment(),
toolchainEnv,
ImmutableMap.copyOf(executionInfo),
toolchain.getToolPathFragment(Tool.LD, ruleErrorConsumer),
toolchain.getTargetCpu());
}