linkopts, CppConfiguration cppConfiguration) {
if (cppConfiguration.getDynamicMode() == DynamicMode.FULLY) {
return LinkStaticness.DYNAMIC;
} else if (dashStaticInLinkopts(linkopts, cppConfiguration)) {
return LinkStaticness.FULLY_STATIC;
} else if (cppConfiguration.getDynamicMode() == DynamicMode.OFF
|| context.attributes().get("linkstatic", Type.BOOLEAN)) {
return LinkStaticness.MOSTLY_STATIC;
} else {
return LinkStaticness.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,
LinkStaticness linkStaticness,
boolean generateDwo,
boolean ltoBackendArtifactsUsePic,
Iterable ltoBackendArtifacts) {
if (linkStaticness == LinkStaticness.DYNAMIC) {
return DwoArtifactsCollector.directCollector(
context, compilationOutputs, generateDwo, ltoBackendArtifactsUsePic, ltoBackendArtifacts);
} else {
return CcCommon.collectTransitiveDwoArtifacts(
context, compilationOutputs, generateDwo, ltoBackendArtifactsUsePic, ltoBackendArtifacts);
}
}
@VisibleForTesting
public static Iterable getDwpInputs(
RuleContext context, NestedSet picDwoArtifacts, NestedSet dwoArtifacts) {
return CppHelper.usePic(context, !isLinkShared(context)) ? 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,
CppConfiguration cppConfiguration, Artifact dwpOutput,
DwoArtifactsCollector dwoArtifactsCollector) {
Iterable allInputs = getDwpInputs(context,
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());
List packagers = createIntermediateDwpPackagers(
context, dwpOutput, cppConfiguration, dwpTools, allInputs, 1);
// 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.
context.registerAction(Iterables.getOnlyElement(packagers)
.addArgument("-o")
.addOutputArgument(dwpOutput)
.setMnemonic("CcGenerateDwp")
.build(context));
}
/**
* Creates the intermediate actions needed to generate this target's
* "debug info package" (i.e. its .dwp file).
*/
private static List createIntermediateDwpPackagers(RuleContext context,
Artifact dwpOutput, CppConfiguration cppConfiguration, 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.
SpawnAction.Builder currentPackager = newDwpAction(cppConfiguration, dwpTools);
int inputsForCurrentPackager = 0;
for (Artifact dwoInput : inputs) {
if (inputsForCurrentPackager == MAX_INPUTS_PER_DWP_ACTION) {
packagers.add(currentPackager);
currentPackager = newDwpAction(cppConfiguration, dwpTools);
inputsForCurrentPackager = 0;
}
currentPackager.addInputArgument(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 (SpawnAction.Builder packager : packagers) {
Artifact intermediateOutput =
getIntermediateDwpFile(context, dwpOutput, intermediateDwpCount++);
context.registerAction(packager
.addArgument("-o")
.addOutputArgument(intermediateOutput)
.setMnemonic("CcGenerateIntermediateDwp")
.build(context));
intermediateOutputs.add(intermediateOutput);
}
return createIntermediateDwpPackagers(
context, dwpOutput, cppConfiguration, dwpTools, intermediateOutputs,
intermediateDwpCount);
}
return packagers;
}
/**
* Returns a new SpawnAction builder for generating dwp files, pre-initialized with
* standard settings.
*/
private static SpawnAction.Builder newDwpAction(CppConfiguration cppConfiguration,
NestedSet dwpTools) {
return new SpawnAction.Builder()
.addTransitiveInputs(dwpTools)
.setExecutable(cppConfiguration.getDwpExecutable())
.useParameterFile(ParameterFile.ParameterFileType.UNQUOTED);
}
/**
* 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);
if (isLinkShared(context)) {
// CcLinkingOutputs is empty because this target is not configured yet
builder.addCcLibrary(context, false, linkopts, CcLinkingOutputs.EMPTY);
} else {
builder.addTransitiveTargets(
context.getPrerequisites("deps", Mode.TARGET),
CcLinkParamsProvider.TO_LINK_PARAMS, CcSpecificLinkParamsProvider.TO_LINK_PARAMS);
builder.addTransitiveTarget(CppHelper.mallocForTarget(context));
builder.addLinkOpts(linkopts);
}
return builder.build();
}
private static void addTransitiveInfoProviders(
RuleContext ruleContext,
CppConfiguration cppConfiguration,
CcCommon common,
RuleConfiguredTargetBuilder builder,
NestedSet filesToBuild,
CcCompilationOutputs ccCompilationOutputs,
CppCompilationContext cppCompilationContext,
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 =
CcLibraryHelper.collectHeaderTokens(ruleContext, ccCompilationOutputs);
NestedSet filesToCompile =
ccCompilationOutputs.getFilesToCompile(
cppConfiguration.isLipoContextCollector(),
cppConfiguration.processHeadersInDependencies(),
CppHelper.usePic(ruleContext, false));
builder
.setFilesToBuild(filesToBuild)
.addProvider(CppCompilationContext.class, cppCompilationContext)
.addProvider(TransitiveLipoInfoProvider.class, transitiveLipoInfo)
.addProvider(
CcExecutionDynamicLibrariesProvider.class,
new CcExecutionDynamicLibrariesProvider(
collectExecutionDynamicLibraryArtifacts(
ruleContext, linkingOutputs.getExecutionDynamicLibraries())))
.addProvider(
CcNativeLibraryProvider.class,
new CcNativeLibraryProvider(
collectTransitiveCcNativeLibraries(
ruleContext, linkingOutputs.getDynamicLibraries())))
.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider)
.addProvider(
CppDebugFileProvider.class,
new CppDebugFileProvider(
dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts()))
.addOutputGroup(
OutputGroupProvider.TEMP_FILES, getTemps(cppConfiguration, ccCompilationOutputs))
.addOutputGroup(OutputGroupProvider.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(OutputGroupProvider.HIDDEN_TOP_LEVEL, headerTokens)
.addOutputGroup(
OutputGroupProvider.COMPILATION_PREREQUISITES,
CcCommon.collectCompilationPrerequisites(ruleContext, cppCompilationContext));
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);
}
Iterable deps = ruleContext
.getPrerequisites("deps", Mode.TARGET, CcExecutionDynamicLibrariesProvider.class);
NestedSetBuilder builder = NestedSetBuilder.stableOrder();
for (CcExecutionDynamicLibrariesProvider dep : deps) {
builder.addTransitive(dep.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();
}
}