linkopts,
CppConfiguration cppConfiguration,
CcToolchainProvider toolchain) {
if (CppHelper.getDynamicMode(cppConfiguration, toolchain) == DynamicMode.FULLY) {
return LinkingMode.DYNAMIC;
} else if (dashStaticInLinkopts(linkopts, cppConfiguration)) {
return Link.LinkingMode.LEGACY_FULLY_STATIC;
} else if (CppHelper.getDynamicMode(cppConfiguration, toolchain) == DynamicMode.OFF
|| context.attributes().get("linkstatic", Type.BOOLEAN)) {
return LinkingMode.STATIC;
} else {
return LinkingMode.DYNAMIC;
}
}
/**
* Collects .dwo artifacts either transitively or directly, depending on the link type.
*
* For a cc_binary, we only include the .dwo files corresponding to the .o files that are
* passed into the link. For static linking, this includes all transitive dependencies. But for
* dynamic linking, dependencies are separately linked into their own shared libraries, so we
* don't need them here.
*/
private static DwoArtifactsCollector collectTransitiveDwoArtifacts(
RuleContext context,
CcCompilationOutputs compilationOutputs,
Link.LinkingMode linkingMode,
boolean generateDwo,
boolean ltoBackendArtifactsUsePic,
Iterable ltoBackendArtifacts) {
if (linkingMode == LinkingMode.DYNAMIC) {
return DwoArtifactsCollector.directCollector(
compilationOutputs, generateDwo, ltoBackendArtifactsUsePic, ltoBackendArtifacts);
} else {
return CcCommon.collectTransitiveDwoArtifacts(
context, compilationOutputs, generateDwo, ltoBackendArtifactsUsePic, ltoBackendArtifacts);
}
}
@VisibleForTesting
public static Iterable getDwpInputs(
RuleContext context,
CcToolchainProvider toolchain,
NestedSet picDwoArtifacts,
NestedSet dwoArtifacts) {
return usePic(context, toolchain) ? picDwoArtifacts : dwoArtifacts;
}
/**
* Creates the actions needed to generate this target's "debug info package" (i.e. its .dwp file).
*/
private static void createDebugPackagerActions(
RuleContext context,
CcToolchainProvider toolchain,
Artifact dwpOutput,
DwoArtifactsCollector dwoArtifactsCollector) {
Iterable allInputs =
getDwpInputs(
context,
toolchain,
dwoArtifactsCollector.getPicDwoArtifacts(),
dwoArtifactsCollector.getDwoArtifacts());
// No inputs? Just generate a trivially empty .dwp.
//
// Note this condition automatically triggers for any build where fission is disabled.
// Because rules referencing .dwp targets may be invoked with or without fission, we need
// to support .dwp generation even when fission is disabled. Since no actual functionality
// is expected then, an empty file is appropriate.
if (Iterables.isEmpty(allInputs)) {
context.registerAction(FileWriteAction.create(context, dwpOutput, "", false));
return;
}
// Get the tool inputs necessary to run the dwp command.
NestedSet dwpTools = toolchain.getDwp();
Preconditions.checkState(!dwpTools.isEmpty());
// We apply a hierarchical action structure to limit the maximum number of inputs to any
// single action.
//
// While the dwp tool consumes .dwo files, it can also consume intermediate .dwp files,
// allowing us to split a large input set into smaller batches of arbitrary size and order.
// Aside from the parallelism performance benefits this offers, this also reduces input
// size requirements: if a.dwo, b.dwo, c.dwo, and e.dwo are each 1 KB files, we can apply
// two intermediate actions DWP(a.dwo, b.dwo) --> i1.dwp and DWP(c.dwo, e.dwo) --> i2.dwp.
// When we then apply the final action DWP(i1.dwp, i2.dwp) --> finalOutput.dwp, the inputs
// to this action will usually total far less than 4 KB.
//
// The actions form an n-ary tree with n == MAX_INPUTS_PER_DWP_ACTION. The tree is fuller
// at the leaves than the root, but that both increases parallelism and reduces the final
// action's input size.
Packager packager =
createIntermediateDwpPackagers(context, dwpOutput, toolchain, dwpTools, allInputs, 1);
packager.spawnAction.setMnemonic("CcGenerateDwp").addOutput(dwpOutput);
packager.commandLine.addExecPath("-o", dwpOutput);
context.registerAction(packager.build(context));
}
private static class Packager {
SpawnAction.Builder spawnAction = new SpawnAction.Builder();
CustomCommandLine.Builder commandLine = CustomCommandLine.builder();
Action[] build(RuleContext context) {
spawnAction.addCommandLine(
commandLine.build(), ParamFileInfo.builder(ParameterFileType.UNQUOTED).build());
return spawnAction.build(context);
}
}
/**
* Creates the intermediate actions needed to generate this target's "debug info package" (i.e.
* its .dwp file).
*/
private static Packager createIntermediateDwpPackagers(
RuleContext context,
Artifact dwpOutput,
CcToolchainProvider toolchain,
NestedSet dwpTools,
Iterable inputs,
int intermediateDwpCount) {
List packagers = new ArrayList<>();
// Step 1: generate our batches. We currently break into arbitrary batches of fixed maximum
// input counts, but we can always apply more intelligent heuristics if the need arises.
Packager currentPackager = newDwpAction(toolchain, dwpTools);
int inputsForCurrentPackager = 0;
for (Artifact dwoInput : inputs) {
if (inputsForCurrentPackager == MAX_INPUTS_PER_DWP_ACTION) {
packagers.add(currentPackager);
currentPackager = newDwpAction(toolchain, dwpTools);
inputsForCurrentPackager = 0;
}
currentPackager.spawnAction.addInput(dwoInput);
currentPackager.commandLine.addExecPath(dwoInput);
inputsForCurrentPackager++;
}
packagers.add(currentPackager);
// Step 2: given the batches, create the actions.
if (packagers.size() > 1) {
// If we have multiple batches, make them all intermediate actions, then pipe their outputs
// into an additional level.
List intermediateOutputs = new ArrayList<>();
for (Packager packager : packagers) {
Artifact intermediateOutput =
getIntermediateDwpFile(context, dwpOutput, intermediateDwpCount++);
packager.spawnAction.setMnemonic("CcGenerateIntermediateDwp").addOutput(intermediateOutput);
packager.commandLine.addExecPath("-o", intermediateOutput);
context.registerAction(packager.build(context));
intermediateOutputs.add(intermediateOutput);
}
return createIntermediateDwpPackagers(
context, dwpOutput, toolchain, dwpTools, intermediateOutputs, intermediateDwpCount);
}
return Iterables.getOnlyElement(packagers);
}
/**
* Create the actions to symlink/copy execution dynamic libraries to binary directory so that they
* are available at runtime.
*
* @param executionDynamicLibraries The libraries to be copied.
* @return The result artifacts of the copies.
*/
private static ImmutableList createDynamicLibrariesCopyActions(
RuleContext ruleContext, NestedSet executionDynamicLibraries) {
ImmutableList.Builder result = ImmutableList.builder();
for (Artifact target : executionDynamicLibraries) {
if (!ruleContext.getLabel().getPackageName().equals(target.getOwner().getPackageName())) {
// SymlinkAction on file is actually copy on Windows.
Artifact copy = ruleContext.getBinArtifact(target.getFilename());
ruleContext.registerAction(
new SymlinkAction(
ruleContext.getActionOwner(), target, copy, "Copying Execution Dynamic Library"));
result.add(copy);
}
}
return result.build();
}
/**
* Returns a new SpawnAction builder for generating dwp files, pre-initialized with standard
* settings.
*/
private static Packager newDwpAction(
CcToolchainProvider toolchain, NestedSet dwpTools) {
Packager packager = new Packager();
packager
.spawnAction
.addTransitiveInputs(dwpTools)
.setExecutable(toolchain.getToolPathFragment(Tool.DWP));
return packager;
}
/**
* Creates an intermediate dwp file keyed off the name and path of the final output.
*/
private static Artifact getIntermediateDwpFile(RuleContext ruleContext, Artifact dwpOutput,
int orderNumber) {
PathFragment outputPath = dwpOutput.getRootRelativePath();
PathFragment intermediatePath =
FileSystemUtils.appendWithoutExtension(outputPath, "-" + orderNumber);
return ruleContext.getPackageRelativeArtifact(
PathFragment.create(INTERMEDIATE_DWP_DIR + "/" + intermediatePath.getPathString()),
dwpOutput.getRoot());
}
/**
* Collect link parameters from the transitive closure.
*/
private static CcLinkParams collectCcLinkParams(RuleContext context,
boolean linkingStatically, boolean linkShared, List linkopts) {
CcLinkParams.Builder builder = CcLinkParams.builder(linkingStatically, linkShared);
builder.addCcLibrary(context);
if (!isLinkShared(context)) {
builder.addTransitiveTarget(CppHelper.mallocForTarget(context));
}
builder.addLinkOpts(linkopts);
return builder.build();
}
private static void addTransitiveInfoProviders(
RuleContext ruleContext,
CcToolchainProvider toolchain,
CppConfiguration cppConfiguration,
CcCommon common,
RuleConfiguredTargetBuilder builder,
NestedSet filesToBuild,
CcCompilationOutputs ccCompilationOutputs,
CcCompilationContextInfo ccCompilationContextInfo,
CcLinkingOutputs linkingOutputs,
DwoArtifactsCollector dwoArtifacts,
TransitiveLipoInfoProvider transitiveLipoInfo,
boolean fake) {
List instrumentedObjectFiles = new ArrayList<>();
instrumentedObjectFiles.addAll(ccCompilationOutputs.getObjectFiles(false));
instrumentedObjectFiles.addAll(ccCompilationOutputs.getObjectFiles(true));
InstrumentedFilesProvider instrumentedFilesProvider = common.getInstrumentedFilesProvider(
instrumentedObjectFiles, !TargetUtils.isTestRule(ruleContext.getRule()) && !fake);
NestedSet headerTokens =
CcCompilationHelper.collectHeaderTokens(ruleContext, ccCompilationOutputs);
NestedSet filesToCompile =
ccCompilationOutputs.getFilesToCompile(
cppConfiguration.isLipoContextCollector(),
cppConfiguration.processHeadersInDependencies(),
CppHelper.usePicForDynamicLibraries(ruleContext, toolchain));
CcCompilationInfo.Builder ccCompilationInfoBuilder = CcCompilationInfo.Builder.create();
ccCompilationInfoBuilder.setCcCompilationContextInfo(ccCompilationContextInfo);
CcLinkingInfo.Builder ccLinkingInfoBuilder = CcLinkingInfo.Builder.create();
ccLinkingInfoBuilder.setCcExecutionDynamicLibrariesInfo(
new CcExecutionDynamicLibrariesInfo(
collectExecutionDynamicLibraryArtifacts(
ruleContext, linkingOutputs.getExecutionDynamicLibraries())));
builder
.setFilesToBuild(filesToBuild)
.addNativeDeclaredProvider(ccCompilationInfoBuilder.build())
.addProvider(TransitiveLipoInfoProvider.class, transitiveLipoInfo)
.addNativeDeclaredProvider(ccLinkingInfoBuilder.build())
.addProvider(
CcNativeLibraryProvider.class,
new CcNativeLibraryProvider(
collectTransitiveCcNativeLibraries(
ruleContext, linkingOutputs.getDynamicLibraries())))
.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider)
.addProvider(
CppDebugFileProvider.class,
new CppDebugFileProvider(
dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts()))
.addOutputGroup(
OutputGroupInfo.TEMP_FILES, getTemps(cppConfiguration, ccCompilationOutputs))
.addOutputGroup(OutputGroupInfo.FILES_TO_COMPILE, filesToCompile)
// For CcBinary targets, we only want to ensure that we process headers in dependencies and
// thus only add header tokens to HIDDEN_TOP_LEVEL. If we add all HIDDEN_TOP_LEVEL artifacts
// from dependent CcLibrary targets, we'd be building .pic.o files in nopic builds.
.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, headerTokens)
.addOutputGroup(
OutputGroupInfo.COMPILATION_PREREQUISITES,
CcCommon.collectCompilationPrerequisites(ruleContext, ccCompilationContextInfo));
CppHelper.maybeAddStaticLinkMarkerProvider(builder, ruleContext);
}
private static NestedSet collectExecutionDynamicLibraryArtifacts(
RuleContext ruleContext,
List executionDynamicLibraries) {
Iterable artifacts = LinkerInputs.toLibraryArtifacts(executionDynamicLibraries);
if (!Iterables.isEmpty(artifacts)) {
return NestedSetBuilder.wrap(Order.STABLE_ORDER, artifacts);
}
NestedSetBuilder builder = NestedSetBuilder.stableOrder();
for (CcLinkingInfo ccLinkingInfo :
ruleContext.getPrerequisites("deps", Mode.TARGET, CcLinkingInfo.PROVIDER)) {
CcExecutionDynamicLibrariesInfo ccExecutionDynamicLibrariesInfo =
ccLinkingInfo.getCcExecutionDynamicLibrariesInfo();
if (ccExecutionDynamicLibrariesInfo != null) {
builder.addTransitive(
ccExecutionDynamicLibrariesInfo.getExecutionDynamicLibraryArtifacts());
}
}
return builder.build();
}
private static NestedSet collectTransitiveCcNativeLibraries(
RuleContext ruleContext,
List extends LinkerInput> dynamicLibraries) {
NestedSetBuilder builder = NestedSetBuilder.linkOrder();
builder.addAll(dynamicLibraries);
for (CcNativeLibraryProvider dep :
ruleContext.getPrerequisites("deps", Mode.TARGET, CcNativeLibraryProvider.class)) {
builder.addTransitive(dep.getTransitiveCcNativeLibraries());
}
return builder.build();
}
private static NestedSet getTemps(CppConfiguration cppConfiguration,
CcCompilationOutputs compilationOutputs) {
return cppConfiguration.isLipoContextCollector()
? NestedSetBuilder.emptySet(Order.STABLE_ORDER)
: compilationOutputs.getTemps();
}
private static boolean usePic(RuleContext ruleContext, CcToolchainProvider ccToolchainProvider) {
if (isLinkShared(ruleContext)) {
return CppHelper.usePicForDynamicLibraries(ruleContext, ccToolchainProvider);
} else {
return CppHelper.usePicForBinaries(ruleContext, ccToolchainProvider);
}
}
}