/** * Returns the linked artifact resulting from a linking of the given type. Consults the feature * configuration to obtain an action_config that provides the artifact. If the feature * configuration provides no artifact, uses a default. * * <p>We cannot assume that the feature configuration contains an action_config for the link * action, because the linux link action depends on hardcoded values in * LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is * not present. TODO(b/30393154): Assert that the given link action has an action_config. * * @throws RuleErrorException */ private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException { Artifact result = null; Artifact linuxDefault = CppHelper.getLinuxLinkedArtifact(ruleContext, linkTargetType); try { String templatedName = features.getArtifactNameForCategory( linkTargetType.getLinkerOutput(), ruleContext, ImmutableMap.<String, String>of()); PathFragment artifactFragment = new PathFragment(ruleContext.getLabel().getName()) .getParentDirectory() .getRelative(templatedName); result = ruleContext.getPackageRelativeArtifact( artifactFragment, ruleContext.getConfiguration().getBinDirectory()); } catch (ExpansionException e) { ruleContext.throwWithRuleError(e.getMessage()); } // If the linked artifact is not the linux default, then a FailAction is generated for the // linux default to satisfy the requirement of the implicit output. // TODO(b/30132703): Remove the implicit outputs of cc_library. if (!result.equals(linuxDefault)) { ruleContext.registerAction( new FailAction( ruleContext.getActionOwner(), ImmutableList.of(linuxDefault), String.format( "the given toolchain supports creation of %s instead of %s", linuxDefault.getExecPathString(), result.getExecPathString()))); } return result; }
private void addToolchainFlags(List<String> argv) { boolean fullyStatic = (linkStaticness == LinkStaticness.FULLY_STATIC); boolean mostlyStatic = (linkStaticness == LinkStaticness.MOSTLY_STATIC); boolean sharedLinkopts = linkTargetType == LinkTargetType.DYNAMIC_LIBRARY || linkopts.contains("-shared") || cppConfiguration.getLinkOptions().contains("-shared"); /* * For backwards compatibility, linkopts come _after_ inputFiles. * This is needed to allow linkopts to contain libraries and * positional library-related options such as * -Wl,--begin-group -lfoo -lbar -Wl,--end-group * or * -Wl,--as-needed -lfoo -Wl,--no-as-needed * * As for the relative order of the three different flavours of linkopts * (global defaults, per-target linkopts, and command-line linkopts), * we have no idea what the right order should be, or if anyone cares. */ argv.addAll(linkopts); // Extra toolchain link options based on the output's link staticness. if (fullyStatic) { argv.addAll(cppConfiguration.getFullyStaticLinkOptions(features, sharedLinkopts)); } else if (mostlyStatic) { argv.addAll(cppConfiguration.getMostlyStaticLinkOptions(features, sharedLinkopts)); } else { argv.addAll(cppConfiguration.getDynamicLinkOptions(features, sharedLinkopts)); } // Extra test-specific link options. if (useTestOnlyFlags) { argv.addAll(cppConfiguration.getTestOnlyLinkOptions()); } argv.addAll(cppConfiguration.getLinkOptions()); // -pie is not compatible with shared and should be // removed when the latter is part of the link command. Should we need to further // distinguish between shared libraries and executables, we could add additional // command line / CROSSTOOL flags that distinguish them. But as long as this is // the only relevant use case we're just special-casing it here. if (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) { Iterables.removeIf(argv, Predicates.equalTo("-pie")); } // Fission mode: debug info is in .dwo files instead of .o files. Inform the linker of this. if (!linkTargetType.isStaticLibraryLink() && cppConfiguration.useFission()) { argv.add("-Wl,--gdb-index"); } }
/** * Splits the link command-line into a part to be written to a parameter file, and the remaining * actual command line to be executed (which references the parameter file). Should only be used * if getParamFile() is not null. * * @throws IllegalStateException if the command-line cannot be split */ @VisibleForTesting final Pair<List<String>, List<String>> splitCommandline() { List<String> args = getRawLinkArgv(); if (linkTargetType.isStaticLibraryLink()) { // Ar link commands can also generate huge command lines. List<String> paramFileArgs = args.subList(1, args.size()); List<String> commandlineArgs = new ArrayList<>(); commandlineArgs.add(args.get(0)); commandlineArgs.add("@" + paramFile.getExecPath().getPathString()); return Pair.of(commandlineArgs, paramFileArgs); } else { // Gcc link commands tend to generate humongous commandlines for some targets, which may // not fit on some remote execution machines. To work around this we will employ the help of // a parameter file and pass any linker options through it. List<String> paramFileArgs = new ArrayList<>(); List<String> commandlineArgs = new ArrayList<>(); extractArgumentsForParamFile(args, commandlineArgs, paramFileArgs); commandlineArgs.add("-Wl,@" + paramFile.getExecPath().getPathString()); return Pair.of(commandlineArgs, paramFileArgs); } }
/** * 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(); }
/** Create the C++ compile and link actions, and the corresponding C++-related providers. */ public Info build() { // Fail early if there is no lipo context collector on the rule - otherwise we end up failing // in lipo optimization. Preconditions.checkState( // 'cc_inc_library' rules do not compile, and thus are not affected by LIPO. ruleContext.getRule().getRuleClass().equals("cc_inc_library") || ruleContext.getRule().isAttrDefined(":lipo_context_collector", BuildType.LABEL)); if (checkDepsGenerateCpp) { for (LanguageDependentFragment dep : AnalysisUtils.getProviders(deps, LanguageDependentFragment.class)) { LanguageDependentFragment.Checker.depSupportsLanguage( ruleContext, dep, CppRuleClasses.LANGUAGE); } } CppModel model = initializeCppModel(); CppCompilationContext cppCompilationContext = initializeCppCompilationContext(model); model.setContext(cppCompilationContext); boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES); Preconditions.checkState( !compileHeaderModules || cppCompilationContext.getCppModuleMap() != null, "All cc rules must support module maps."); // Create compile actions (both PIC and non-PIC). CcCompilationOutputs ccOutputs = model.createCcCompileActions(); if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) { // Merge the pre-compiled object files into the compiler outputs. ccOutputs = new CcCompilationOutputs.Builder() .merge(ccOutputs) .addLTOBitcodeFile(ccOutputs.getLtoBitcodeFiles()) .addObjectFiles(objectFiles) .addPicObjectFiles(picObjectFiles) .build(); } // Create link actions (only if there are object files or if explicitly requested). CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY; if (emitLinkActionsIfEmpty || !ccOutputs.isEmpty()) { // On some systems, the linker gives an error message if there are no input files. Even with // the check above, this can still happen if there is a .nopic.o or .o files in srcs, but no // other files. To fix that, we'd have to check for each link action individually. // // An additional pre-existing issue is that the header check tokens are dropped if we don't // generate any link actions, effectively disabling header checking in some cases. if (linkType.isStaticLibraryLink()) { // TODO(bazel-team): This can't create the link action for a cc_binary yet. ccLinkingOutputs = model.createCcLinkActions(ccOutputs); } } CcLinkingOutputs originalLinkingOutputs = ccLinkingOutputs; if (!(staticLibraries.isEmpty() && picStaticLibraries.isEmpty() && dynamicLibraries.isEmpty())) { // Merge the pre-compiled libraries (static & dynamic) into the linker outputs. ccLinkingOutputs = new CcLinkingOutputs.Builder() .merge(ccLinkingOutputs) .addStaticLibraries(staticLibraries) .addPicStaticLibraries(picStaticLibraries) .addDynamicLibraries(dynamicLibraries) .addExecutionDynamicLibraries(dynamicLibraries) .build(); } DwoArtifactsCollector dwoArtifacts = DwoArtifactsCollector.transitiveCollector(ccOutputs, deps); Runfiles cppStaticRunfiles = collectCppRunfiles(ccLinkingOutputs, true); Runfiles cppSharedRunfiles = collectCppRunfiles(ccLinkingOutputs, false); // By very careful when adding new providers here - it can potentially affect a lot of rules. // We should consider merging most of these providers into a single provider. Map<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider> providers = new LinkedHashMap<>(); providers.put( CppRunfilesProvider.class, new CppRunfilesProvider(cppStaticRunfiles, cppSharedRunfiles)); providers.put(CppCompilationContext.class, cppCompilationContext); providers.put( CppDebugFileProvider.class, new CppDebugFileProvider( dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts())); providers.put(TransitiveLipoInfoProvider.class, collectTransitiveLipoInfo(ccOutputs)); Map<String, NestedSet<Artifact>> outputGroups = new TreeMap<>(); outputGroups.put(OutputGroupProvider.TEMP_FILES, getTemps(ccOutputs)); if (emitCompileProviders) { boolean isLipoCollector = ruleContext.getFragment(CppConfiguration.class).isLipoContextCollector(); boolean processHeadersInDependencies = ruleContext.getFragment(CppConfiguration.class).processHeadersInDependencies(); boolean usePic = CppHelper.usePic(ruleContext, false); outputGroups.put( OutputGroupProvider.FILES_TO_COMPILE, ccOutputs.getFilesToCompile(isLipoCollector, processHeadersInDependencies, usePic)); outputGroups.put( OutputGroupProvider.COMPILATION_PREREQUISITES, CcCommon.collectCompilationPrerequisites(ruleContext, cppCompilationContext)); } // TODO(bazel-team): Maybe we can infer these from other data at the places where they are // used. if (emitCcNativeLibrariesProvider) { providers.put( CcNativeLibraryProvider.class, new CcNativeLibraryProvider(collectNativeCcLibraries(ccLinkingOutputs))); } providers.put( CcExecutionDynamicLibrariesProvider.class, collectExecutionDynamicLibraryArtifacts(ccLinkingOutputs.getExecutionDynamicLibraries())); boolean forcePic = ruleContext.getFragment(CppConfiguration.class).forcePic(); if (emitCcSpecificLinkParamsProvider) { providers.put( CcSpecificLinkParamsProvider.class, new CcSpecificLinkParamsProvider( createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic))); } else { providers.put( CcLinkParamsProvider.class, new CcLinkParamsProvider( createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic))); } return new Info( providers, outputGroups, ccOutputs, ccLinkingOutputs, originalLinkingOutputs, cppCompilationContext); }
private LinkCommandLine( String actionName, BuildConfiguration configuration, ActionOwner owner, Artifact output, @Nullable Artifact interfaceOutput, ImmutableList<Artifact> buildInfoHeaderArtifacts, Iterable<? extends LinkerInput> linkerInputs, Iterable<? extends LinkerInput> runtimeInputs, LinkTargetType linkTargetType, LinkStaticness linkStaticness, ImmutableList<String> linkopts, ImmutableSet<String> features, ImmutableMap<Artifact, Artifact> linkstamps, ImmutableList<String> linkstampCompileOptions, @Nullable String fdoBuildStamp, @Nullable PathFragment runtimeSolibDir, boolean nativeDeps, boolean useTestOnlyFlags, boolean needWholeArchive, @Nullable Artifact paramFile, Artifact interfaceSoBuilder, List<String> noWholeArchiveFlags, CcToolchainFeatures.Variables variables, @Nullable FeatureConfiguration featureConfiguration) { this.actionName = actionName; this.configuration = Preconditions.checkNotNull(configuration); this.cppConfiguration = configuration.getFragment(CppConfiguration.class); this.variables = variables; this.featureConfiguration = featureConfiguration; this.owner = Preconditions.checkNotNull(owner); this.output = output; this.interfaceOutput = interfaceOutput; if (interfaceOutput != null) { Preconditions.checkNotNull(this.output); } this.buildInfoHeaderArtifacts = Preconditions.checkNotNull(buildInfoHeaderArtifacts); this.linkerInputs = Preconditions.checkNotNull(linkerInputs); this.runtimeInputs = Preconditions.checkNotNull(runtimeInputs); this.linkTargetType = Preconditions.checkNotNull(linkTargetType); this.linkStaticness = Preconditions.checkNotNull(linkStaticness); // For now, silently ignore linkopts if this is a static library link. this.linkopts = linkTargetType.isStaticLibraryLink() ? ImmutableList.<String>of() : Preconditions.checkNotNull(linkopts); this.features = Preconditions.checkNotNull(features); this.linkstamps = Preconditions.checkNotNull(linkstamps); this.linkstampCompileOptions = linkstampCompileOptions; this.fdoBuildStamp = fdoBuildStamp; this.runtimeSolibDir = runtimeSolibDir; this.nativeDeps = nativeDeps; this.useTestOnlyFlags = useTestOnlyFlags; this.paramFile = paramFile; this.noWholeArchiveFlags = noWholeArchiveFlags; // For now, silently ignore interfaceSoBuilder if we don't build an interface dynamic library. this.interfaceSoBuilder = ((linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) && (interfaceOutput != null)) ? Preconditions.checkNotNull( interfaceSoBuilder, "cannot build interface dynamic library without builder") : null; }
public LinkCommandLine build() { if (linkTargetType.isStaticLibraryLink()) { Preconditions.checkArgument( linkstamps.isEmpty(), "linkstamps may only be present on dynamic library or executable links"); Preconditions.checkArgument( buildInfoHeaderArtifacts.isEmpty(), "build info headers may only be present on dynamic library or executable links"); } ImmutableList<String> actualLinkstampCompileOptions; if (linkstampCompileOptions.isEmpty()) { actualLinkstampCompileOptions = DEFAULT_LINKSTAMP_OPTIONS; } else { actualLinkstampCompileOptions = ImmutableList.copyOf( Iterables.concat(DEFAULT_LINKSTAMP_OPTIONS, linkstampCompileOptions)); } // The ruleContext can be null for some tests. if (ruleContext != null) { if (featureConfiguration == null) { if (toolchain != null) { featureConfiguration = CcCommon.configureFeatures( ruleContext, toolchain, CcLibraryHelper.SourceCategory.CC); } else { featureConfiguration = CcCommon.configureFeatures(ruleContext); } } } if (variables == null) { variables = Variables.EMPTY; } String actionName = linkTargetType.getActionName(); return new LinkCommandLine( actionName, configuration, owner, output, interfaceOutput, buildInfoHeaderArtifacts, linkerInputs, runtimeInputs, linkTargetType, linkStaticness, linkopts, features, linkstamps, actualLinkstampCompileOptions, CppHelper.getFdoBuildStamp(ruleContext), runtimeSolibDir, nativeDeps, useTestOnlyFlags, needWholeArchive, paramFile, interfaceSoBuilder, noWholeArchiveFlags, variables, featureConfiguration); }