@Test public void testCppLinkActionExtraActionInfoWithSharedLibraries() throws Exception { ConfiguredTarget hello = getConfiguredTarget("//hello:hello"); Artifact sharedObject = FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY).iterator().next(); CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject); ExtraActionInfo.Builder builder = action.getExtraActionInfo(); ExtraActionInfo info = builder.build(); assertEquals("CppLink", info.getMnemonic()); CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo); assertNotNull(cppLinkInfo); Iterable<String> inputs = Artifact.asExecPaths( LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs())); assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs); assertEquals(action.getPrimaryOutput().getExecPathString(), cppLinkInfo.getOutputFile()); Artifact interfaceOutput = action.getLinkCommandLine().getInterfaceOutput(); assertEquals(interfaceOutput.getExecPathString(), cppLinkInfo.getInterfaceOutputFile()); assertEquals( action.getLinkCommandLine().getLinkTargetType().name(), cppLinkInfo.getLinkTargetType()); assertEquals( action.getLinkCommandLine().getLinkStaticness().name(), cppLinkInfo.getLinkStaticness()); Iterable<String> linkstamps = Artifact.asExecPaths(action.getLinkCommandLine().getLinkstamps().values()); assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps); Iterable<String> buildInfoHeaderArtifacts = Artifact.asExecPaths(action.getLinkCommandLine().getBuildInfoHeaderArtifacts()); assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList()) .containsExactlyElementsIn(buildInfoHeaderArtifacts); assertThat(cppLinkInfo.getLinkOptList()) .containsExactlyElementsIn(action.getLinkCommandLine().getLinkopts()); }
@Test public void testSoName() throws Exception { // Without interface shared libraries. useConfiguration("--nointerface_shared_objects"); ConfiguredTarget hello = getConfiguredTarget("//hello:hello"); Artifact sharedObject = getOnlyElement(FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY)); CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject); for (String option : action.getLinkCommandLine().getLinkopts()) { assertThat(option).doesNotContain("-Wl,-soname"); } // With interface shared libraries. useConfiguration("--interface_shared_objects"); hello = getConfiguredTarget("//hello:hello"); sharedObject = FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY).iterator().next(); action = (CppLinkAction) getGeneratingAction(sharedObject); assertThat(action.getLinkCommandLine().getLinkopts()) .contains("-Wl,-soname=libhello_Slibhello.so"); }
/** * Constructs the C++ linker actions. It generally generates two actions, one for a static library * and one for a dynamic library. If PIC is required for shared libraries, but not for binaries, * it additionally creates a third action to generate a PIC static library. * * <p>For dynamic libraries, this method can additionally create an interface shared library that * can be used for linking, but doesn't contain any executable code. This increases the number of * cache hits for link actions. Call {@link #setAllowInterfaceSharedObjects(boolean)} to enable * this behavior. * * @throws RuleErrorException */ public CcLinkingOutputs createCcLinkActions(CcCompilationOutputs ccOutputs) throws RuleErrorException { // For now only handle static links. Note that the dynamic library link below ignores linkType. // TODO(bazel-team): Either support non-static links or move this check to setLinkType(). Preconditions.checkState(linkType.isStaticLibraryLink(), "can only handle static links"); CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder(); if (cppConfiguration.isLipoContextCollector()) { // Don't try to create LIPO link actions in collector mode, // because it needs some data that's not available at this point. return result.build(); } AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); boolean usePicForBinaries = CppHelper.usePic(ruleContext, true); boolean usePicForSharedLibs = CppHelper.usePic(ruleContext, false); // Create static library (.a). The linkType only reflects whether the library is alwayslink or // not. The PIC-ness is determined by whether we need to use PIC or not. There are three cases // for (usePicForSharedLibs usePicForBinaries): // // (1) (false false) -> no pic code // (2) (true false) -> shared libraries as pic, but not binaries // (3) (true true) -> both shared libraries and binaries as pic // // In case (3), we always need PIC, so only create one static library containing the PIC object // files. The name therefore does not match the content. // // Presumably, it is done this way because the .a file is an implicit output of every cc_library // rule, so we can't use ".pic.a" that in the always-PIC case. // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. Artifact linkedArtifact = getLinkedArtifact(linkType); CppLinkAction maybePicAction = newLinkActionBuilder(linkedArtifact) .addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForBinaries)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(linkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) .build(); env.registerAction(maybePicAction); result.addStaticLibrary(maybePicAction.getOutputLibrary()); // Create a second static library (.pic.a). Only in case (2) do we need both PIC and non-PIC // static libraries. In that case, the first static library contains the non-PIC code, and this // one contains the PIC code, so the names match the content. if (!usePicForBinaries && usePicForSharedLibs) { LinkTargetType picLinkType = (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY) ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY : LinkTargetType.PIC_STATIC_LIBRARY; // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. Artifact picArtifact = getLinkedArtifact(picLinkType); CppLinkAction picAction = newLinkActionBuilder(picArtifact) .addNonLibraryInputs(ccOutputs.getObjectFiles(true)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) .build(); env.registerAction(picAction); result.addPicStaticLibrary(picAction.getOutputLibrary()); } if (!createDynamicLibrary) { return result.build(); } // Create dynamic library. Artifact soImpl; if (soImplArtifact == null) { // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY); } else { soImpl = soImplArtifact; } List<String> sonameLinkopts = ImmutableList.of(); Artifact soInterface = null; if (cppConfiguration.useInterfaceSharedObjects() && allowInterfaceSharedObjects) { soInterface = CppHelper.getLinuxLinkedArtifact(ruleContext, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY); sonameLinkopts = ImmutableList.of( "-Wl,-soname=" + SolibSymlinkAction.getDynamicLibrarySoname( soImpl.getRootRelativePath(), false)); } // Should we also link in any libraries that this library depends on? // That is required on some systems... CppLinkActionBuilder linkActionBuilder = newLinkActionBuilder(soImpl) .setInterfaceOutput(soInterface) .addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForSharedLibs)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(LinkTargetType.DYNAMIC_LIBRARY) .setLinkStaticness(LinkStaticness.DYNAMIC) .addLinkopts(linkopts) .addLinkopts(sonameLinkopts) .setRuntimeInputs( CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(), CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs()) .setFeatureConfiguration(featureConfiguration); if (!ccOutputs.getLtoBitcodeFiles().isEmpty() && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { linkActionBuilder.setLTOIndexing(true); CppLinkAction indexAction = linkActionBuilder.build(); env.registerAction(indexAction); for (LTOBackendArtifacts ltoArtifacts : indexAction.getAllLTOBackendArtifacts()) { ltoArtifacts.scheduleLTOBackendAction(ruleContext, usePicForSharedLibs); } linkActionBuilder.setLTOIndexing(false); } CppLinkAction action = linkActionBuilder.build(); env.registerAction(action); LibraryToLink dynamicLibrary = action.getOutputLibrary(); LibraryToLink interfaceLibrary = action.getInterfaceOutputLibrary(); if (interfaceLibrary == null) { interfaceLibrary = dynamicLibrary; } // If shared library has neverlink=1, then leave it untouched. Otherwise, // create a mangled symlink for it and from now on reference it through // mangled name only. if (neverLink) { result.addDynamicLibrary(interfaceLibrary); result.addExecutionDynamicLibrary(dynamicLibrary); } else { LibraryToLink libraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, interfaceLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addDynamicLibrary(libraryLink); LibraryToLink implLibraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, dynamicLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addExecutionDynamicLibrary(implLibraryLink); } return result.build(); }