| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.devtools.build.lib.rules.cpp; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSortedMap; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Streams; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.FailAction; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.analysis.AnalysisUtils; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.FileProvider; |
| import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
| import com.google.devtools.build.lib.analysis.MakeVariableSupplier.MapBackedMakeVariableSupplier; |
| import com.google.devtools.build.lib.analysis.OutputGroupInfo; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.Runfiles; |
| import com.google.devtools.build.lib.analysis.RunfilesProvider; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.analysis.test.InstrumentedFilesInfo; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.collect.nestedset.Order; |
| import com.google.devtools.build.lib.packages.AttributeMap; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; |
| import com.google.devtools.build.lib.packages.RawAttributeMapper; |
| import com.google.devtools.build.lib.packages.TargetUtils; |
| import com.google.devtools.build.lib.packages.Type; |
| import com.google.devtools.build.lib.rules.cpp.CcCommon.CcFlagsSupplier; |
| import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; |
| import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType; |
| import com.google.devtools.build.lib.syntax.EvalException; |
| import com.google.devtools.build.lib.util.FileTypeSet; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.annotation.Nullable; |
| |
| /** |
| * A ConfiguredTarget for <code>cc_library</code> rules. |
| */ |
| public abstract class CcLibrary implements RuleConfiguredTargetFactory { |
| |
| /** A string constant for the name of archive library(.a, .lo) output group. */ |
| public static final String ARCHIVE_LIBRARY_OUTPUT_GROUP_NAME = "archive"; |
| |
| /** A string constant for the name of dynamic library output group. */ |
| public static final String DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME = "dynamic_library"; |
| |
| /** A string constant for the name of Windows def file output group. */ |
| public static final String DEF_FILE_OUTPUT_GROUP_NAME = "def_file"; |
| |
| private final CppSemantics semantics; |
| |
| protected CcLibrary(CppSemantics semantics) { |
| this.semantics = semantics; |
| } |
| |
| // These file extensions don't generate object files. |
| private static final FileTypeSet NO_OBJECT_GENERATING_FILETYPES = FileTypeSet.of( |
| CppFileTypes.CPP_HEADER, CppFileTypes.ARCHIVE, CppFileTypes.PIC_ARCHIVE, |
| CppFileTypes.ALWAYS_LINK_LIBRARY, CppFileTypes.ALWAYS_LINK_PIC_LIBRARY, |
| CppFileTypes.SHARED_LIBRARY, CppFileTypes.VERSIONED_SHARED_LIBRARY); |
| |
| @Override |
| public ConfiguredTarget create(RuleContext context) |
| throws InterruptedException, RuleErrorException, ActionConflictException { |
| RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(context); |
| boolean linkStatic = context.attributes().get("linkstatic", Type.BOOLEAN); |
| init( |
| semantics, |
| context, |
| builder, |
| /* additionalCopts= */ ImmutableList.of(), |
| /* soFilename= */ null, |
| context.attributes().get("alwayslink", Type.BOOLEAN), |
| /* neverLink= */ false, |
| linkStatic, |
| /* addDynamicRuntimeInputArtifactsToRunfiles= */ false); |
| return builder.build(); |
| } |
| |
| public static void init( |
| CppSemantics semantics, |
| RuleContext ruleContext, |
| RuleConfiguredTargetBuilder targetBuilder, |
| ImmutableList<String> additionalCopts, |
| PathFragment soFilename, |
| boolean alwaysLink, |
| boolean neverLink, |
| boolean linkStatic, |
| boolean addDynamicRuntimeInputArtifactsToRunfiles) |
| throws RuleErrorException, InterruptedException { |
| CcCommon.checkRuleLoadedThroughMacro(ruleContext); |
| semantics.validateDeps(ruleContext); |
| if (ruleContext.hasErrors()) { |
| addEmptyRequiredProviders(targetBuilder); |
| return; |
| } |
| |
| final CcCommon common = new CcCommon(ruleContext); |
| common.reportInvalidOptions(ruleContext); |
| |
| CcToolchainProvider ccToolchain = common.getToolchain(); |
| CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class); |
| |
| ImmutableMap.Builder<String, String> toolchainMakeVariables = ImmutableMap.builder(); |
| ccToolchain.addGlobalMakeVariables(toolchainMakeVariables); |
| ruleContext.initConfigurationMakeVariableContext( |
| new MapBackedMakeVariableSupplier(toolchainMakeVariables.build()), |
| new CcFlagsSupplier(ruleContext)); |
| |
| FdoContext fdoContext = common.getFdoContext(); |
| FeatureConfiguration featureConfiguration = |
| CcCommon.configureFeaturesOrReportRuleError(ruleContext, ccToolchain, semantics); |
| PrecompiledFiles precompiledFiles = new PrecompiledFiles(ruleContext); |
| |
| semantics.validateAttributes(ruleContext); |
| if (ruleContext.hasErrors()) { |
| addEmptyRequiredProviders(targetBuilder); |
| return; |
| } |
| |
| ImmutableList<TransitiveInfoCollection> deps = |
| ImmutableList.copyOf(ruleContext.getPrerequisites("deps", Mode.TARGET)); |
| if (ruleContext.hasErrors()) { |
| addEmptyRequiredProviders(targetBuilder); |
| return; |
| } |
| Iterable<CcInfo> ccInfosFromDeps = AnalysisUtils.getProviders(deps, CcInfo.PROVIDER); |
| CcCompilationHelper compilationHelper = |
| new CcCompilationHelper( |
| ruleContext, |
| ruleContext, |
| ruleContext.getLabel(), |
| CppHelper.getGrepIncludes(ruleContext), |
| semantics, |
| featureConfiguration, |
| ccToolchain, |
| fdoContext, |
| TargetUtils.getExecutionInfo( |
| ruleContext.getRule(), ruleContext.isAllowTagsPropagation())) |
| .fromCommon(common, additionalCopts) |
| .addSources(common.getSources()) |
| .addPrivateHeaders(common.getPrivateHeaders()) |
| .addPublicHeaders(common.getHeaders()) |
| .setCodeCoverageEnabled(CcCompilationHelper.isCodeCoverageEnabled(ruleContext)) |
| .addCcCompilationContexts( |
| Streams.stream(ccInfosFromDeps) |
| .map(CcInfo::getCcCompilationContext) |
| .collect(ImmutableList.toImmutableList())) |
| .addCcCompilationContexts( |
| ImmutableList.of(CcCompilationHelper.getStlCcCompilationContext(ruleContext))) |
| .setHeadersCheckingMode(semantics.determineHeadersCheckingMode(ruleContext)); |
| |
| CcLinkingHelper linkingHelper = |
| new CcLinkingHelper( |
| ruleContext, |
| ruleContext.getLabel(), |
| ruleContext, |
| ruleContext, |
| semantics, |
| featureConfiguration, |
| ccToolchain, |
| fdoContext, |
| ruleContext.getConfiguration(), |
| ruleContext.getFragment(CppConfiguration.class), |
| ruleContext.getSymbolGenerator(), |
| TargetUtils.getExecutionInfo( |
| ruleContext.getRule(), ruleContext.isAllowTagsPropagation())) |
| .fromCommon(ruleContext, common) |
| .setGrepIncludes(CppHelper.getGrepIncludes(ruleContext)) |
| .setTestOrTestOnlyTarget(ruleContext.isTestOnlyTarget()) |
| .addLinkopts(common.getLinkopts()) |
| .emitInterfaceSharedLibraries(true) |
| .setAlwayslink(alwaysLink) |
| .setNeverLink(neverLink) |
| .addLinkstamps(ruleContext.getPrerequisites("linkstamp", Mode.TARGET)); |
| |
| Artifact soImplArtifact = null; |
| boolean supportsDynamicLinker = ccToolchain.supportsDynamicLinker(featureConfiguration); |
| // TODO(djasper): This is hacky. We should actually try to figure out whether we generate |
| // ccOutputs. |
| boolean createDynamicLibrary = |
| !linkStatic |
| && supportsDynamicLinker |
| && (appearsToHaveObjectFiles(ruleContext.attributes()) |
| || featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)); |
| if (soFilename != null) { |
| if (!soFilename.getPathString().endsWith(".so")) { // Sanity check. |
| ruleContext.attributeError("outs", "file name must end in '.so'"); |
| } |
| if (createDynamicLibrary) { |
| soImplArtifact = ruleContext.getBinArtifact(soFilename); |
| } |
| } |
| |
| if (ruleContext.getRule().isAttrDefined("textual_hdrs", BuildType.LABEL_LIST)) { |
| compilationHelper.addPublicTextualHeaders( |
| ruleContext.getPrerequisiteArtifacts("textual_hdrs", Mode.TARGET).list()); |
| } |
| if (ruleContext.getRule().isAttrDefined("include_prefix", Type.STRING) |
| && ruleContext.attributes().isAttributeValueExplicitlySpecified("include_prefix")) { |
| compilationHelper.setIncludePrefix( |
| ruleContext.attributes().get("include_prefix", Type.STRING)); |
| } |
| if (ruleContext.getRule().isAttrDefined("strip_include_prefix", Type.STRING) |
| && ruleContext.attributes().isAttributeValueExplicitlySpecified("strip_include_prefix")) { |
| compilationHelper.setStripIncludePrefix( |
| ruleContext.attributes().get("strip_include_prefix", Type.STRING)); |
| } |
| |
| if (common.getLinkopts().contains("-static")) { |
| ruleContext.attributeWarning("linkopts", "Using '-static' here won't work. " |
| + "Did you mean to use 'linkstatic=1' instead?"); |
| } |
| |
| linkingHelper.setShouldCreateDynamicLibrary(createDynamicLibrary); |
| linkingHelper.setLinkerOutputArtifact(soImplArtifact); |
| |
| // If the reason we're not creating a dynamic library is that the toolchain |
| // doesn't support it, then register an action which complains when triggered, |
| // which only happens when some rule explicitly depends on the dynamic library. |
| if (!createDynamicLibrary && !supportsDynamicLinker) { |
| ImmutableList.Builder<Artifact> dynamicLibraries = ImmutableList.builder(); |
| dynamicLibraries.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| ruleContext.getConfiguration(), |
| LinkTargetType.NODEPS_DYNAMIC_LIBRARY)); |
| if (CppHelper.useInterfaceSharedLibraries( |
| cppConfiguration, ccToolchain, featureConfiguration)) { |
| dynamicLibraries.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| ruleContext.getConfiguration(), |
| LinkTargetType.INTERFACE_DYNAMIC_LIBRARY)); |
| } |
| ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(), |
| dynamicLibraries.build(), "Toolchain does not support dynamic linking")); |
| } else if (!createDynamicLibrary |
| && ruleContext.attributes().isConfigurable("srcs")) { |
| // If "srcs" is configurable, the .so output is always declared because the logic that |
| // determines implicit outs doesn't know which value of "srcs" will ultimately get chosen. |
| // Here, where we *do* have the correct value, it may not contain any source files to |
| // generate an .so with. If that's the case, register a fake generating action to prevent |
| // a "no generating action for this artifact" error. |
| ImmutableList.Builder<Artifact> dynamicLibraries = ImmutableList.builder(); |
| dynamicLibraries.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| ruleContext.getConfiguration(), |
| LinkTargetType.NODEPS_DYNAMIC_LIBRARY)); |
| if (CppHelper.useInterfaceSharedLibraries( |
| cppConfiguration, ccToolchain, featureConfiguration)) { |
| dynamicLibraries.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| ruleContext.getConfiguration(), |
| LinkTargetType.INTERFACE_DYNAMIC_LIBRARY)); |
| } |
| ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(), |
| dynamicLibraries.build(), "configurable \"srcs\" triggers an implicit .so output " |
| + "even though there are no sources to compile in this configuration")); |
| } |
| |
| CompilationInfo compilationInfo = compilationHelper.compile(); |
| CcCompilationOutputs precompiledFilesObjects = |
| CcCompilationOutputs.builder() |
| .addObjectFiles(precompiledFiles.getObjectFiles(/* usePic= */ true)) |
| .addPicObjectFiles(precompiledFiles.getObjectFiles(/* usePic= */ true)) |
| .build(); |
| CcCompilationOutputs ccCompilationOutputs = |
| CcCompilationOutputs.builder() |
| .merge(precompiledFilesObjects) |
| .merge(compilationInfo.getCcCompilationOutputs()) |
| .build(); |
| |
| // Generate .a and .so outputs even without object files to fulfill the rule class |
| // contract wrt. implicit output files, if the contract says so. Behavior here differs |
| // between Bazel and Blaze. |
| CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY; |
| if (ruleContext.getRule().getImplicitOutputsFunction() != ImplicitOutputsFunction.NONE |
| || !ccCompilationOutputs.isEmpty()) { |
| if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { |
| Artifact generatedDefFile = null; |
| |
| Artifact defParser = common.getDefParser(); |
| if (defParser != null) { |
| try { |
| generatedDefFile = |
| CppHelper.createDefFileActions( |
| ruleContext, |
| defParser, |
| ccCompilationOutputs.getObjectFiles(false), |
| ccToolchain |
| .getFeatures() |
| .getArtifactNameForCategory( |
| ArtifactCategory.DYNAMIC_LIBRARY, ruleContext.getLabel().getName())); |
| targetBuilder.addOutputGroup(DEF_FILE_OUTPUT_GROUP_NAME, generatedDefFile); |
| } catch (EvalException e) { |
| throw ruleContext.throwWithRuleError(e); |
| } |
| } |
| linkingHelper.setDefFile( |
| CppHelper.getWindowsDefFileForLinking( |
| ruleContext, common.getWinDefFile(), generatedDefFile, featureConfiguration)); |
| } |
| ccLinkingOutputs = linkingHelper.link(ccCompilationOutputs); |
| } |
| |
| ImmutableSortedMap.Builder<String, NestedSet<Artifact>> outputGroups = |
| ImmutableSortedMap.naturalOrder(); |
| if (!ccLinkingOutputs.isEmpty()) { |
| outputGroups.putAll( |
| addLinkerOutputArtifacts( |
| ruleContext, |
| ccToolchain, |
| cppConfiguration, |
| ruleContext.getConfiguration(), |
| ccCompilationOutputs, |
| featureConfiguration)); |
| } |
| List<LibraryToLink> precompiledLibraries = |
| convertPrecompiledLibrariesToLibraryToLink( |
| common, ruleContext.getFragment(CppConfiguration.class).forcePic(), precompiledFiles); |
| |
| if (!ccCompilationOutputs.isEmpty()) { |
| checkIfLinkOutputsCollidingWithPrecompiledFiles( |
| ruleContext, ccLinkingOutputs, precompiledLibraries); |
| } |
| |
| ImmutableList<LibraryToLink> libraryToLinks = |
| createLibrariesToLinkList( |
| ccLinkingOutputs.getLibraryToLink(), |
| precompiledLibraries, |
| ccCompilationOutputs.isEmpty()); |
| |
| CcLinkingContext ccLinkingContext = |
| linkingHelper.buildCcLinkingContextFromLibrariesToLink( |
| neverLink ? ImmutableList.of() : libraryToLinks, |
| compilationInfo.getCcCompilationContext()); |
| CcNativeLibraryProvider ccNativeLibraryProvider = |
| CppHelper.collectNativeCcLibraries( |
| ruleContext.getPrerequisites("deps", Mode.TARGET), libraryToLinks); |
| |
| /* |
| * We always generate a static library, even if there aren't any source files. |
| * This keeps things simpler by avoiding special cases when making use of the library. |
| * For example, this is needed to ensure that building a library with "bazel build" |
| * will also build all of the library's "deps". |
| * However, we only generate a dynamic library if there are source files. |
| */ |
| // For now, we don't add the precompiled libraries to the files to build. |
| |
| NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder(); |
| if (!ccLinkingOutputs.isEmpty()) { |
| LibraryToLink artifactsToBuild = ccLinkingOutputs.getLibraryToLink(); |
| if (artifactsToBuild.getStaticLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getStaticLibrary()); |
| } |
| if (artifactsToBuild.getPicStaticLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getPicStaticLibrary()); |
| } |
| if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { |
| if (artifactsToBuild.getResolvedSymlinkDynamicLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getResolvedSymlinkDynamicLibrary()); |
| } else if (artifactsToBuild.getDynamicLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getDynamicLibrary()); |
| } |
| if (artifactsToBuild.getResolvedSymlinkInterfaceLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getResolvedSymlinkInterfaceLibrary()); |
| } else if (artifactsToBuild.getInterfaceLibrary() != null) { |
| filesBuilder.add(artifactsToBuild.getInterfaceLibrary()); |
| } |
| } |
| } |
| |
| if (!featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) { |
| warnAboutEmptyLibraries(ruleContext, ccCompilationOutputs, linkStatic); |
| } |
| NestedSet<Artifact> filesToBuild = filesBuilder.build(); |
| |
| List<Artifact> instrumentedObjectFiles = new ArrayList<>(); |
| instrumentedObjectFiles.addAll(compilationInfo.getCcCompilationOutputs().getObjectFiles(false)); |
| instrumentedObjectFiles.addAll(compilationInfo.getCcCompilationOutputs().getObjectFiles(true)); |
| InstrumentedFilesInfo instrumentedFilesProvider = |
| common.getInstrumentedFilesProvider( |
| instrumentedObjectFiles, |
| /* withBaselineCoverage= */ true, |
| /* virtualToOriginalHeaders= */ NestedSetBuilder.create(Order.STABLE_ORDER)); |
| CppHelper.maybeAddStaticLinkMarkerProvider(targetBuilder, ruleContext); |
| |
| Runfiles.Builder builder = new Runfiles.Builder(ruleContext.getWorkspaceName()); |
| builder.addDataDeps(ruleContext); |
| builder.add(ruleContext, RunfilesProvider.DEFAULT_RUNFILES); |
| if (addDynamicRuntimeInputArtifactsToRunfiles) { |
| try { |
| builder.addTransitiveArtifacts( |
| ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration)); |
| } catch (EvalException e) { |
| throw ruleContext.throwWithRuleError(e); |
| } |
| } |
| Runfiles runfiles = builder.build(); |
| Runfiles.Builder defaultRunfiles = |
| new Runfiles.Builder(ruleContext.getWorkspaceName()) |
| .merge(runfiles) |
| .addArtifacts(LibraryToLink.getDynamicLibrariesForRuntime(!neverLink, libraryToLinks)); |
| |
| Runfiles.Builder dataRunfiles = |
| new Runfiles.Builder(ruleContext.getWorkspaceName()) |
| .merge(runfiles) |
| .addArtifacts( |
| LibraryToLink.getDynamicLibrariesForRuntime( |
| /* linkingStatically= */ false, libraryToLinks)); |
| |
| Map<String, NestedSet<Artifact>> currentOutputGroups = |
| CcCompilationHelper.buildOutputGroupsForEmittingCompileProviders( |
| compilationInfo.getCcCompilationOutputs(), |
| compilationInfo.getCcCompilationContext(), |
| ruleContext.getFragment(CppConfiguration.class), |
| ccToolchain, |
| featureConfiguration, |
| ruleContext); |
| CcSkylarkApiProvider.maybeAdd(ruleContext, targetBuilder); |
| targetBuilder |
| .setFilesToBuild(filesToBuild) |
| .addProvider(ccNativeLibraryProvider) |
| .addNativeDeclaredProvider( |
| CcInfo.builder() |
| .setCcCompilationContext(compilationInfo.getCcCompilationContext()) |
| .setCcLinkingContext(ccLinkingContext) |
| .setCcDebugInfoContext( |
| CppHelper.mergeCcDebugInfoContexts( |
| compilationInfo.getCcCompilationOutputs(), ccInfosFromDeps)) |
| .build()) |
| .addOutputGroups( |
| CcCommon.mergeOutputGroups(ImmutableList.of(currentOutputGroups, outputGroups.build()))) |
| .addNativeDeclaredProvider(instrumentedFilesProvider) |
| .addProvider(RunfilesProvider.withData(defaultRunfiles.build(), dataRunfiles.build())) |
| .addOutputGroup( |
| OutputGroupInfo.HIDDEN_TOP_LEVEL, |
| collectHiddenTopLevelArtifacts( |
| ruleContext, ccToolchain, ccCompilationOutputs, featureConfiguration)) |
| .addOutputGroup( |
| CcCompilationHelper.HIDDEN_HEADER_TOKENS, |
| CcCompilationHelper.collectHeaderTokens( |
| ruleContext, |
| ruleContext.getFragment(CppConfiguration.class), |
| ccCompilationOutputs)); |
| } |
| |
| private static NestedSet<Artifact> collectHiddenTopLevelArtifacts( |
| RuleContext ruleContext, |
| CcToolchainProvider toolchain, |
| CcCompilationOutputs ccCompilationOutputs, |
| FeatureConfiguration featureConfiguration) { |
| // Ensure that we build all the dependencies, otherwise users may get confused. |
| NestedSetBuilder<Artifact> artifactsToForceBuilder = NestedSetBuilder.stableOrder(); |
| CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class); |
| boolean processHeadersInDependencies = cppConfiguration.processHeadersInDependencies(); |
| boolean usePic = toolchain.usePicForDynamicLibraries(cppConfiguration, featureConfiguration); |
| artifactsToForceBuilder.addTransitive( |
| ccCompilationOutputs.getFilesToCompile(processHeadersInDependencies, usePic)); |
| for (OutputGroupInfo dep : |
| ruleContext.getPrerequisites( |
| "deps", Mode.TARGET, OutputGroupInfo.SKYLARK_CONSTRUCTOR)) { |
| artifactsToForceBuilder.addTransitive( |
| dep.getOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL)); |
| } |
| return artifactsToForceBuilder.build(); |
| } |
| |
| private static void warnAboutEmptyLibraries(RuleContext ruleContext, |
| CcCompilationOutputs ccCompilationOutputs, |
| boolean linkstaticAttribute) { |
| if (ccCompilationOutputs.getObjectFiles(false).isEmpty() |
| && ccCompilationOutputs.getObjectFiles(true).isEmpty()) { |
| if (!linkstaticAttribute && appearsToHaveObjectFiles(ruleContext.attributes())) { |
| ruleContext.attributeWarning("linkstatic", |
| "setting 'linkstatic=1' is recommended if there are no object files"); |
| } |
| } else { |
| if (!linkstaticAttribute && !appearsToHaveObjectFiles(ruleContext.attributes())) { |
| Artifact element = Iterables.getFirst( |
| ccCompilationOutputs.getObjectFiles(false), |
| ccCompilationOutputs.getObjectFiles(true).get(0)); |
| ruleContext.attributeWarning("srcs", |
| "this library appears at first glance to have no object files, " |
| + "but on closer inspection it does have something to link, e.g. " |
| + element.prettyPrint() + ". " |
| + "(You may have used some very confusing rule names in srcs? " |
| + "Or the library consists entirely of a linker script?) " |
| + "Bazel assumed linkstatic=1, but this may be inappropriate. " |
| + "You may need to add an explicit '.cc' file to 'srcs'. " |
| + "Alternatively, add 'linkstatic=1' to suppress this warning"); |
| } |
| } |
| } |
| |
| /** |
| * Returns true if the rule (which must be a cc_library rule) appears to have object files. |
| * This only looks at the rule itself, not at any other rules (from this package or other |
| * packages) that it might reference. |
| * |
| * <p>In some cases, this may return "true" even though the rule actually has no object files. |
| * For example, it will return true for a rule such as |
| * <code>cc_library(name = 'foo', srcs = [':bar'])</code> because we can't tell what ':bar' is; |
| * it might be a genrule that generates a source file, or it might be a genrule that generates a |
| * header file. Likewise, |
| * <code>cc_library(name = 'foo', srcs = select({':a': ['foo.cc'], ':b': []}))</code> returns |
| * "true" even though the sources *may* be empty. This reflects the fact that there's no way |
| * to tell which value "srcs" will take without knowing the rule's configuration. |
| * |
| * <p>In other cases, this may return "false" even though the rule actually does have object |
| * files. For example, it will return false for a rule such as |
| * <code>cc_library(name = 'foo', srcs = ['bar.h'])</code> but as in the other example above, |
| * we can't tell whether 'bar.h' is a file name or a rule name, and 'bar.h' could in fact be the |
| * name of a genrule that generates a source file. |
| */ |
| public static boolean appearsToHaveObjectFiles(AttributeMap rule) { |
| if ((rule instanceof RawAttributeMapper) && rule.isConfigurable("srcs")) { |
| // Since this method gets called by loading phase logic (e.g. the cc_library implicit outputs |
| // function), the attribute mapper may not be able to resolve configurable attributes. When |
| // that's the case, there's no way to know which value a configurable "srcs" will take, so |
| // we conservatively assume object files are possible. |
| return true; |
| } |
| |
| List<Label> srcs = rule.get("srcs", BuildType.LABEL_LIST); |
| if (srcs != null) { |
| for (Label srcfile : srcs) { |
| /* |
| * We cheat a little bit here by looking at the file extension |
| * of the Label treated as file name. In general that might |
| * not necessarily work, because of the possibility that the |
| * user might give a rule a funky name ending in one of these |
| * extensions, e.g. |
| * genrule(name = 'foo.h', outs = ['foo.cc'], ...) // Funky rule name! |
| * cc_library(name = 'bar', srcs = ['foo.h']) // This DOES have object files. |
| */ |
| if (!NO_OBJECT_GENERATING_FILETYPES.matches(srcfile.getName())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Adds linker output artifacts to the given map, to be registered on the configured target as |
| * output groups. |
| */ |
| private static Map<String, NestedSet<Artifact>> addLinkerOutputArtifacts( |
| RuleContext ruleContext, |
| CcToolchainProvider ccToolchain, |
| CppConfiguration cppConfiguration, |
| BuildConfiguration configuration, |
| CcCompilationOutputs ccCompilationOutputs, |
| FeatureConfiguration featureConfiguration) |
| throws RuleErrorException { |
| |
| NestedSetBuilder<Artifact> archiveFile = new NestedSetBuilder<>(Order.STABLE_ORDER); |
| NestedSetBuilder<Artifact> dynamicLibrary = new NestedSetBuilder<>(Order.STABLE_ORDER); |
| |
| ImmutableSortedMap.Builder<String, NestedSet<Artifact>> outputGroups = |
| ImmutableSortedMap.naturalOrder(); |
| if (!ruleContext.attributes().has("alwayslink", Type.BOOLEAN) |
| || !ruleContext.attributes().has("linkstatic", Type.BOOLEAN)) { |
| return outputGroups.build(); |
| } |
| |
| if (ruleContext.attributes().get("alwayslink", Type.BOOLEAN)) { |
| archiveFile.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| configuration, |
| Link.LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY, |
| /* linkedArtifactNameSuffix= */ "")); |
| } else { |
| archiveFile.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| configuration, |
| Link.LinkTargetType.STATIC_LIBRARY, |
| /* linkedArtifactNameSuffix= */ "")); |
| } |
| |
| if (!ruleContext.attributes().get("linkstatic", Type.BOOLEAN) |
| && !ccCompilationOutputs.isEmpty()) { |
| dynamicLibrary.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| configuration, |
| Link.LinkTargetType.NODEPS_DYNAMIC_LIBRARY, |
| /* linkedArtifactNameSuffix= */ "")); |
| |
| if (CppHelper.useInterfaceSharedLibraries( |
| cppConfiguration, ccToolchain, featureConfiguration)) { |
| dynamicLibrary.add( |
| CppHelper.getLinkedArtifact( |
| ruleContext, |
| ccToolchain, |
| configuration, |
| LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, |
| /* linkedArtifactNameSuffix= */ "")); |
| } |
| } |
| |
| outputGroups.put(ARCHIVE_LIBRARY_OUTPUT_GROUP_NAME, archiveFile.build()); |
| outputGroups.put(DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME, dynamicLibrary.build()); |
| return outputGroups.build(); |
| } |
| |
| private static ImmutableList<LibraryToLink> createLibrariesToLinkList( |
| @Nullable LibraryToLink outputLibrary, |
| List<LibraryToLink> precompiledLibraries, |
| boolean ccCompilationOutputsIsEmpty) { |
| ImmutableList.Builder<LibraryToLink> librariesToLink = ImmutableList.builder(); |
| librariesToLink.addAll(precompiledLibraries); |
| |
| // For cc_library if it contains precompiled libraries we link them. If it contains normal |
| // sources we link them as well, if it doesn't contain normal sources, then we don't do |
| // anything else if there were precompiled libraries. However, if there are no precompiled |
| // libraries and there are no normal sources, then we use the implicitly created link output |
| // files if they exist. |
| if (!ccCompilationOutputsIsEmpty |
| || (precompiledLibraries.isEmpty() |
| && isContentsOfCcLinkingOutputsImplicitlyCreated( |
| ccCompilationOutputsIsEmpty, outputLibrary == null))) { |
| if (outputLibrary != null) { |
| librariesToLink.add(outputLibrary); |
| } |
| } |
| |
| return librariesToLink.build(); |
| } |
| |
| private static boolean isContentsOfCcLinkingOutputsImplicitlyCreated( |
| boolean ccCompilationOutputsIsEmpty, boolean ccLinkingOutputsIsEmpty) { |
| return ccCompilationOutputsIsEmpty && !ccLinkingOutputsIsEmpty; |
| } |
| |
| private static Map<String, Artifact> buildMapIdentifierToArtifact(Iterable<Artifact> artifacts) { |
| ImmutableMap.Builder<String, Artifact> libraries = ImmutableMap.builder(); |
| for (Artifact artifact : artifacts) { |
| libraries.put(CcLinkingOutputs.libraryIdentifierOf(artifact), artifact); |
| } |
| return libraries.build(); |
| } |
| |
| /* |
| * Add the libraries from srcs, if any. For static/mostly static |
| * linking we setup the dynamic libraries if there are no static libraries |
| * to choose from. Path to the libraries will be mangled to avoid using |
| * absolute path names on the -rpath, but library filenames will be |
| * preserved (since some libraries might have SONAME tag) - symlink will |
| * be created to the parent directory instead. |
| * |
| * For compatibility with existing BUILD files, any ".a" or ".lo" files listed in |
| * srcs are assumed to be position-independent code, or at least suitable for |
| * inclusion in shared libraries, unless they end with ".nopic.a" or ".nopic.lo". |
| * |
| * Note that some target platforms do not require shared library code to be PIC. |
| */ |
| private static List<LibraryToLink> convertPrecompiledLibrariesToLibraryToLink( |
| CcCommon common, boolean forcePic, PrecompiledFiles precompiledFiles) { |
| ImmutableList.Builder<LibraryToLink> librariesToLink = ImmutableList.builder(); |
| |
| Map<String, Artifact> staticLibraries = |
| buildMapIdentifierToArtifact(precompiledFiles.getStaticLibraries()); |
| Map<String, Artifact> picStaticLibraries = |
| buildMapIdentifierToArtifact(precompiledFiles.getPicStaticLibraries()); |
| Map<String, Artifact> alwayslinkStaticLibraries = |
| buildMapIdentifierToArtifact(precompiledFiles.getAlwayslinkStaticLibraries()); |
| Map<String, Artifact> alwayslinkPicStaticLibraries = |
| buildMapIdentifierToArtifact(precompiledFiles.getPicAlwayslinkLibraries()); |
| Map<String, Artifact> dynamicLibraries = |
| buildMapIdentifierToArtifact(precompiledFiles.getSharedLibraries()); |
| |
| Set<String> identifiersUsed = new HashSet<>(); |
| for (Map.Entry<String, Artifact> staticLibraryEntry : |
| Iterables.concat(staticLibraries.entrySet(), alwayslinkStaticLibraries.entrySet())) { |
| LibraryToLink.Builder libraryToLinkBuilder = LibraryToLink.builder(); |
| String identifier = staticLibraryEntry.getKey(); |
| libraryToLinkBuilder.setLibraryIdentifier(identifier); |
| boolean hasPic = picStaticLibraries.containsKey(identifier); |
| boolean hasAlwaysPic = alwayslinkPicStaticLibraries.containsKey(identifier); |
| if (hasPic || hasAlwaysPic) { |
| Artifact picStaticLibrary = null; |
| if (hasPic) { |
| picStaticLibrary = picStaticLibraries.get(identifier); |
| } else { |
| picStaticLibrary = alwayslinkPicStaticLibraries.get(identifier); |
| } |
| libraryToLinkBuilder.setPicStaticLibrary(picStaticLibrary); |
| } |
| if (!forcePic || !(hasPic || hasAlwaysPic)) { |
| libraryToLinkBuilder.setStaticLibrary(staticLibraryEntry.getValue()); |
| } |
| if (dynamicLibraries.containsKey(identifier)) { |
| Artifact library = dynamicLibraries.get(identifier); |
| Artifact symlink = common.getDynamicLibrarySymlink(library, true); |
| libraryToLinkBuilder.setDynamicLibrary(symlink); |
| libraryToLinkBuilder.setResolvedSymlinkDynamicLibrary(library); |
| } |
| libraryToLinkBuilder.setAlwayslink(alwayslinkStaticLibraries.containsKey(identifier)); |
| identifiersUsed.add(identifier); |
| librariesToLink.add(libraryToLinkBuilder.build()); |
| } |
| |
| for (Map.Entry<String, Artifact> picStaticLibraryEntry : |
| Iterables.concat(picStaticLibraries.entrySet(), alwayslinkPicStaticLibraries.entrySet())) { |
| String identifier = picStaticLibraryEntry.getKey(); |
| if (identifiersUsed.contains(identifier)) { |
| continue; |
| } |
| LibraryToLink.Builder libraryToLinkBuilder = LibraryToLink.builder(); |
| libraryToLinkBuilder.setLibraryIdentifier(identifier); |
| libraryToLinkBuilder.setPicStaticLibrary(picStaticLibraryEntry.getValue()); |
| if (dynamicLibraries.containsKey(identifier)) { |
| Artifact library = dynamicLibraries.get(identifier); |
| Artifact symlink = common.getDynamicLibrarySymlink(library, true); |
| libraryToLinkBuilder.setDynamicLibrary(symlink); |
| libraryToLinkBuilder.setResolvedSymlinkDynamicLibrary(library); |
| } |
| libraryToLinkBuilder.setAlwayslink(alwayslinkPicStaticLibraries.containsKey(identifier)); |
| identifiersUsed.add(identifier); |
| librariesToLink.add(libraryToLinkBuilder.build()); |
| } |
| |
| for (Map.Entry<String, Artifact> dynamicLibraryEntry : dynamicLibraries.entrySet()) { |
| String identifier = dynamicLibraryEntry.getKey(); |
| if (identifiersUsed.contains(identifier)) { |
| continue; |
| } |
| LibraryToLink.Builder libraryToLinkBuilder = LibraryToLink.builder(); |
| libraryToLinkBuilder.setLibraryIdentifier(identifier); |
| Artifact library = dynamicLibraryEntry.getValue(); |
| Artifact symlink = common.getDynamicLibrarySymlink(library, true); |
| libraryToLinkBuilder.setDynamicLibrary(symlink); |
| libraryToLinkBuilder.setResolvedSymlinkDynamicLibrary(library); |
| librariesToLink.add(libraryToLinkBuilder.build()); |
| } |
| return librariesToLink.build(); |
| } |
| |
| private static void checkIfLinkOutputsCollidingWithPrecompiledFiles( |
| RuleContext ruleContext, |
| CcLinkingOutputs ccLinkingOutputs, |
| List<LibraryToLink> precompiledLibraries) { |
| String identifier = ccLinkingOutputs.getLibraryToLink().getLibraryIdentifier(); |
| for (LibraryToLink precompiledLibrary : precompiledLibraries) { |
| if (identifier.equals(precompiledLibrary.getLibraryIdentifier())) { |
| ruleContext.ruleError( |
| "Can't put library with identifier '" |
| + precompiledLibrary.getLibraryIdentifier() |
| + "' into the srcs of a " |
| + ruleContext.getRuleClassNameForLogging() |
| + " with the same name (" |
| + ruleContext.getRule().getName() |
| + ") which also contains other code or objects to link"); |
| } |
| } |
| } |
| |
| private static void addEmptyRequiredProviders(RuleConfiguredTargetBuilder builder) { |
| builder.addProvider(RunfilesProvider.EMPTY); |
| builder.addProvider(FileProvider.EMPTY); |
| builder.addProvider(FilesToRunProvider.EMPTY); |
| } |
| } |