Skip to content

Commit

Permalink
C++: Make cc_binary's linking go through CcLinkingHelper.
Browse files Browse the repository at this point in the history
CcLinkingHelper has new functionality to support transitive linking. This is
not exposed to Skylark yet.

GitHub issue #4570

RELNOTES:none
PiperOrigin-RevId: 213824305
  • Loading branch information
oquenchil authored and Copybara-Service committed Sep 20, 2018
1 parent 8c6a889 commit d37c675
Show file tree
Hide file tree
Showing 5 changed files with 374 additions and 234 deletions.
276 changes: 130 additions & 146 deletions src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
ruleContext.getConfiguration())
.fromCommon(common)
.addDeps(ImmutableList.of(CppHelper.mallocForTarget(ruleContext)))
.enableInterfaceSharedObjects()
.emitInterfaceSharedObjects(true)
.setAlwayslink(false);
ccLinkingOutputs = linkingHelper.link(ccCompilationOutputs);
}
Expand All @@ -294,58 +294,10 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
linkingMode != Link.LinkingMode.DYNAMIC,
isLinkShared(ruleContext),
common.getLinkopts());
CppLinkActionBuilder linkActionBuilder =
determineLinkerArguments(
ruleContext,
ccToolchain,
featureConfiguration,
fdoProvider,
common,
precompiledFiles,
ccCompilationOutputs,
ccLinkingOutputs,
ccCompilationContext.getTransitiveCompilationPrerequisites(),
fake,
binary,
linkParams,
linkCompileOutputSeparately,
semantics);
linkActionBuilder.setUseTestOnlyFlags(ruleContext.isTestTarget());
if (linkingMode == Link.LinkingMode.DYNAMIC) {
linkActionBuilder.setRuntimeInputs(
ArtifactCategory.DYNAMIC_LIBRARY,
ccToolchain.getDynamicRuntimeLinkMiddleman(featureConfiguration),
ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration));
} else {
linkActionBuilder.setRuntimeInputs(
ArtifactCategory.STATIC_LIBRARY,
ccToolchain.getStaticRuntimeLinkMiddleman(featureConfiguration),
ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration));
if (!cppConfiguration.disableEmittingStaticLibgcc()) {
// Only force a static link of libgcc if static runtime linking is enabled (which
// can't be true if runtimeInputs is empty).
// TODO(bazel-team): Move this to CcToolchain.
if (!ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration).isEmpty()) {
linkActionBuilder.addLinkopt("-static-libgcc");
}
}
}

linkActionBuilder.setLinkType(linkType);
linkActionBuilder.setLinkingMode(linkingMode);
linkActionBuilder.setFake(fake);

if (CppLinkAction.enableSymbolsCounts(
cppConfiguration, ccToolchain.supportsGoldLinker(), fake, linkType)) {
linkActionBuilder.setSymbolCountsOutput(ruleContext.getBinArtifact(
CppLinkAction.symbolCountsFileName(binaryPath)));
}

Artifact generatedDefFile = null;
Artifact interfaceLibrary = null;
Artifact customDefFile = null;
if (isLinkShared(ruleContext)) {
linkActionBuilder.setLibraryIdentifier(CcLinkingOutputs.libraryIdentifierOf(binary));

if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
ImmutableList.Builder<Artifact> objectFiles = ImmutableList.builder();
objectFiles.addAll(ccCompilationOutputs.getObjectFiles(false));
Expand All @@ -362,62 +314,56 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST),
objectFiles.build(),
binary.getFilename());

if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
linkActionBuilder.setDefFile(generatedDefFile);
}

Artifact customDefFile = common.getWinDefFile();
if (customDefFile != null) {
linkActionBuilder.setDefFile(customDefFile);
}

// If we are using a toolchain supporting interface library and targeting Windows, we build
// the interface library with the link action and add it to `interface_output` output group.
if (CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain)) {
interfaceLibrary = CppHelper.getLinkedArtifact(
ruleContext,
ccToolchain,
ruleContext.getConfiguration(),
LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
linkActionBuilder.setInterfaceOutput(interfaceLibrary);
}
customDefFile = common.getWinDefFile();
}
}

// Store immutable context for use in other *_binary rules that are implemented by
// linking the interpreter (Java, Python, etc.) together with native deps.
CppLinkAction.Context linkContext = new CppLinkAction.Context(linkActionBuilder);
boolean usePic = usePic(ruleContext, ccToolchain);

if (linkActionBuilder.hasLtoBitcodeInputs()
&& featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
linkActionBuilder.setLtoIndexing(true);
linkActionBuilder.setUsePicForLtoBackendActions(usePic);
CppLinkAction indexAction = linkActionBuilder.build();
if (indexAction != null) {
ruleContext.registerAction(indexAction);
}

linkActionBuilder.setLtoIndexing(false);
}

// On Windows, if GENERATE_PDB_FILE feature is enabled
// then a pdb file will be built along with the executable.
Artifact pdbFile = null;
if (featureConfiguration.isEnabled(CppRuleClasses.GENERATE_PDB_FILE)) {
pdbFile = ruleContext.getRelatedArtifact(binary.getRootRelativePath(), ".pdb");
linkActionBuilder.addActionOutput(pdbFile);
}

CppLinkAction linkAction = linkActionBuilder.build();
Iterable<LtoBackendArtifacts> ltoBackendArtifacts =
linkActionBuilder.getAllLtoBackendArtifacts();
ruleContext.registerAction(linkAction);
LibraryToLink outputLibrary = linkAction.getOutputLibrary();
CcLinkingOutputs ccLinkingOutputsBinary =
createTransitiveLinkingActions(
ruleContext,
ccToolchain,
featureConfiguration,
fdoProvider,
common,
precompiledFiles,
ccCompilationOutputs,
ccLinkingOutputs,
ccCompilationContext,
fake,
binary,
linkParams,
linkCompileOutputSeparately,
semantics,
linkingMode,
cppConfiguration,
linkType,
binaryPath,
pdbFile,
generatedDefFile,
customDefFile);

// Store immutable context for use in other *_binary rules that are implemented by
// linking the interpreter (Java, Python, etc.) together with native deps.
CppLinkAction.Context linkContext = ccLinkingOutputsBinary.getCppLinkActionContext();

LibraryToLink outputLibrary = null;
List<LibraryToLink> dynamicLibrariesForRuntime =
ccLinkingOutputsBinary.getDynamicLibrariesForRuntime();
if (!dynamicLibrariesForRuntime.isEmpty()) {
Preconditions.checkState(dynamicLibrariesForRuntime.size() == 1);
outputLibrary = dynamicLibrariesForRuntime.get(0);
}
Iterable<Artifact> fakeLinkerInputs =
fake ? linkAction.getInputs() : ImmutableList.<Artifact>of();
Artifact executable = linkAction.getLinkOutput();
fake ? ccLinkingOutputsBinary.getLinkActionInputs() : ImmutableList.<Artifact>of();
CcLinkingOutputs.Builder linkingOutputsBuilder = new CcLinkingOutputs.Builder();
if (isLinkShared(ruleContext)) {
linkingOutputsBuilder.addDynamicLibraryForLinking(outputLibrary);
Expand All @@ -432,13 +378,13 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
linkingOutputsBuilder.addDynamicLibraryForRuntime(symlinkLibrary);
}
CcLinkingOutputs linkingOutputs = linkingOutputsBuilder.build();
NestedSet<Artifact> filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, executable);
NestedSet<Artifact> filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, binary);

// Create the stripped binary, but don't add it to filesToBuild; it's only built when requested.
Artifact strippedFile = ruleContext.getImplicitOutputArtifact(
CppRuleClasses.CC_BINARY_STRIPPED);
CppHelper.createStripAction(
ruleContext, ccToolchain, cppConfiguration, executable, strippedFile, featureConfiguration);
ruleContext, ccToolchain, cppConfiguration, binary, strippedFile, featureConfiguration);

DwoArtifactsCollector dwoArtifacts =
collectTransitiveDwoArtifacts(
Expand All @@ -447,7 +393,7 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
linkingMode,
ccToolchain.useFission(),
usePic,
ltoBackendArtifacts);
ccLinkingOutputsBinary.getAllLtoArtifacts());
Artifact dwpFile =
ruleContext.getImplicitOutputArtifact(CppRuleClasses.CC_BINARY_DEBUG_PACKAGE);
createDebugPackagerActions(ruleContext, ccToolchain, dwpFile, dwoArtifacts);
Expand Down Expand Up @@ -497,8 +443,7 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
fake,
compilationHelper.getCompilationUnitSources(),
linkCompileOutputSeparately);
RunfilesSupport runfilesSupport = RunfilesSupport.withExecutable(
ruleContext, runfiles, executable);
RunfilesSupport runfilesSupport = RunfilesSupport.withExecutable(ruleContext, runfiles, binary);

RuleConfiguredTargetBuilder ruleBuilder = new RuleConfiguredTargetBuilder(ruleContext);
addTransitiveInfoProviders(
Expand Down Expand Up @@ -530,92 +475,131 @@ public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleCont
ruleBuilder.addOutputGroup("def_file", generatedDefFile);
}

if (interfaceLibrary != null) {
ruleBuilder.addOutputGroup("interface_library", interfaceLibrary);
List<LibraryToLink> dynamicLibraryForLinking =
ccLinkingOutputsBinary.getDynamicLibrariesForLinking();
if (!dynamicLibraryForLinking.isEmpty()) {
Preconditions.checkState(dynamicLibraryForLinking.size() == 1);
ruleBuilder.addOutputGroup(
"interface_library", dynamicLibraryForLinking.get(0).getOriginalLibraryArtifact());
}

return ruleBuilder
.addProvider(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
.addProvider(
DebugPackageProvider.class,
new DebugPackageProvider(
ruleContext.getLabel(), strippedFile, executable, explicitDwpFile))
.setRunfilesSupport(runfilesSupport, executable)
new DebugPackageProvider(ruleContext.getLabel(), strippedFile, binary, explicitDwpFile))
.setRunfilesSupport(runfilesSupport, binary)
.addProvider(CppLinkAction.Context.class, linkContext)
.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
.build();
}

/**
* Given 'temps', traverse this target and its dependencies and collect up all the object files,
* libraries, linker options, linkstamps attributes and linker scripts.
*/
private static CppLinkActionBuilder determineLinkerArguments(
RuleContext context,
CcToolchainProvider toolchain,
public static CcLinkingOutputs createTransitiveLinkingActions(
RuleContext ruleContext,
CcToolchainProvider ccToolchain,
FeatureConfiguration featureConfiguration,
FdoProvider fdoProvider,
CcCommon common,
PrecompiledFiles precompiledFiles,
CcCompilationOutputs compilationOutputs,
CcLinkingOutputs linkingOutputs,
ImmutableSet<Artifact> compilationPrerequisites,
CcCompilationOutputs ccCompilationOutputs,
CcLinkingOutputs ccLinkingOutputs,
CcCompilationContext ccCompilationContext,
boolean fake,
Artifact binary,
CcLinkParams linkParams,
boolean linkCompileOutputSeparately,
CppSemantics cppSemantics)
CppSemantics cppSemantics,
LinkingMode linkingMode,
CppConfiguration cppConfiguration,
LinkTargetType linkType,
PathFragment binaryPath,
Artifact pdbFile,
Artifact generatedDefFile,
Artifact customDefFile)
throws InterruptedException, RuleErrorException {
CppLinkActionBuilder builder =
new CppLinkActionBuilder(
context, binary, toolchain, fdoProvider, featureConfiguration, cppSemantics)
.setCrosstoolInputs(toolchain.getLink())
.addNonCodeInputs(compilationPrerequisites);

// Either link in the .o files generated for the sources of this target or link in the
// generated dynamic library they are compiled into.
if (linkCompileOutputSeparately) {
for (LibraryToLink library : linkingOutputs.getDynamicLibrariesForLinking()) {
builder.addLibrary(library);
}
} else {
boolean usePic = usePic(context, toolchain);
Iterable<Artifact> objectFiles = compilationOutputs.getObjectFiles(usePic);
CcLinkingHelper ccLinkingHelper =
new CcLinkingHelper(
ruleContext,
cppSemantics,
featureConfiguration,
ccToolchain,
fdoProvider,
ruleContext.getConfiguration())
.addNonCodeLinkerInputs(ccCompilationContext.getTransitiveCompilationPrerequisites())
.addNonCodeLinkerInputs(common.getLinkerScripts());

if (fake) {
builder.addFakeObjectFiles(objectFiles);
} else {
builder.addObjectFiles(objectFiles);
CcLinkParams.Builder ccLinkParamsBuilder = CcLinkParams.builder();
ccLinkParamsBuilder.addTransitiveArgs(linkParams);

if (linkCompileOutputSeparately) {
for (LibraryToLink library : ccLinkingOutputs.getDynamicLibrariesForLinking()) {
ccLinkParamsBuilder.addLibrary(library);
}
ccCompilationOutputs = new CcCompilationOutputs.Builder().build();
}

builder.addLtoBitcodeFiles(compilationOutputs.getLtoBitcodeFiles());
builder.addNonCodeInputs(common.getLinkerScripts());

// Determine the libraries to link in.
// First libraries from srcs. Shared library artifacts here are substituted with mangled symlink
// artifacts generated by getDynamicLibraryLink(). This is done to minimize number of -rpath
// entries during linking process.
for (Artifact library : precompiledFiles.getLibraries()) {
if (Link.SHARED_LIBRARY_FILETYPES.matches(library.getFilename())) {
builder.addLibrary(LinkerInputs.solibLibraryToLink(
common.getDynamicLibrarySymlink(library, true), library,
CcLinkingOutputs.libraryIdentifierOf(library)));
ccLinkParamsBuilder.addLibrary(
LinkerInputs.solibLibraryToLink(
common.getDynamicLibrarySymlink(library, true),
library,
CcLinkingOutputs.libraryIdentifierOf(library)));
} else if (Link.LINK_LIBRARY_FILETYPES.matches(library.getFilename())) {
builder.addLibrary(LinkerInputs.precompiledLibraryToLink(
library, ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
ccLinkParamsBuilder.addLibrary(
LinkerInputs.precompiledLibraryToLink(
library, ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
} else if (Link.ARCHIVE_FILETYPES.matches(library.getFilename())) {
builder.addLibrary(LinkerInputs.precompiledLibraryToLink(
library, ArtifactCategory.STATIC_LIBRARY));
ccLinkParamsBuilder.addLibrary(
LinkerInputs.precompiledLibraryToLink(library, ArtifactCategory.STATIC_LIBRARY));
} else {
throw new IllegalStateException();
}
}

// Then the link params from the closure of deps.
builder.addLinkParams(linkParams, context);
CcLinkParams ccLinkParams = ccLinkParamsBuilder.build();
CcLinkingInfo.Builder ccLinkingInfo = CcLinkingInfo.Builder.create();
ccLinkingInfo.setStaticModeParamsForDynamicLibrary(ccLinkParams);
ccLinkingInfo.setStaticModeParamsForExecutable(ccLinkParams);
ccLinkingInfo.setDynamicModeParamsForDynamicLibrary(ccLinkParams);
ccLinkingInfo.setDynamicModeParamsForExecutable(ccLinkParams);

ccLinkingHelper
.addCcLinkingInfos(ImmutableList.of(ccLinkingInfo.build()))
.setUseTestOnlyFlags(ruleContext.isTestTarget())
.setShouldCreateStaticLibraries(false)
.setLinkingMode(linkingMode)
.setDynamicLinkType(linkType)
.setDynamicLibrary(binary)
.setNeverLink(true)
.emitInterfaceSharedObjects(
isLinkShared(ruleContext)
&& featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)
&& CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain))
.setPdbFile(pdbFile)
.setFake(fake);

if (customDefFile != null) {
ccLinkingHelper.setDefFile(customDefFile);
} else if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
ccLinkingHelper.setDefFile(generatedDefFile);
}

if (linkingMode != Link.LinkingMode.DYNAMIC
&& !cppConfiguration.disableEmittingStaticLibgcc()) {
// Only force a static link of libgcc if static runtime linking is enabled (which
// can't be true if runtimeInputs is empty).
// TODO(bazel-team): Move this to CcToolchain.
if (!ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration).isEmpty()) {
ccLinkingHelper.addLinkopts(ImmutableList.of("-static-libgcc"));
}
}

return builder;
return ccLinkingHelper.link(/* ccOutputs */ ccCompilationOutputs);
}

/**
Expand Down
Loading

0 comments on commit d37c675

Please sign in to comment.