| // 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 static com.google.devtools.build.lib.packages.BuildType.LABEL; |
| import static java.util.stream.Collectors.joining; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSetMultimap; |
| import com.google.common.collect.ImmutableSortedMap; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Sets; |
| 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.ConfiguredTarget; |
| 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.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.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.rules.cpp.LinkerInputs.LibraryToLink; |
| import com.google.devtools.build.lib.rules.cpp.LinkerInputs.SolibLibraryToLink; |
| import com.google.devtools.build.lib.syntax.EvalException; |
| import com.google.devtools.build.lib.syntax.Type; |
| 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; |
| |
| /** |
| * 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"; |
| |
| 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 { |
| |
| final CcCommon common = new CcCommon(ruleContext); |
| |
| CcToolchainProvider ccToolchain = common.getToolchain(); |
| |
| 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); |
| PrecompiledFiles precompiledFiles = new PrecompiledFiles(ruleContext); |
| |
| semantics.validateAttributes(ruleContext); |
| if (ruleContext.hasErrors()) { |
| return; |
| } |
| |
| CcCompilationHelper compilationHelper = |
| new CcCompilationHelper( |
| ruleContext, semantics, featureConfiguration, ccToolchain, fdoContext) |
| .fromCommon(common, additionalCopts) |
| .addSources(common.getSources()) |
| .addPrivateHeaders(common.getPrivateHeaders()) |
| .addPublicHeaders(common.getHeaders()) |
| .enableCompileProviders() |
| .addPrecompiledFiles(precompiledFiles); |
| |
| CcLinkingHelper linkingHelper = |
| new CcLinkingHelper( |
| ruleContext, |
| semantics, |
| featureConfiguration, |
| ccToolchain, |
| fdoContext, |
| ruleContext.getConfiguration()) |
| .fromCommon(common) |
| .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("srcs", BuildType.LABEL_LIST)) { |
| ruleContext.checkSrcsSamePackage(true); |
| } |
| 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( |
| ccToolchain.getCppConfiguration(), 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( |
| ccToolchain.getCppConfiguration(), 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 ccCompilationOutputs = compilationInfo.getCcCompilationOutputs(); |
| // 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)) { |
| // If windows_export_all_symbols feature is enabled, bazel parses object files to generate |
| // DEF file and use it to export symbols. The generated DEF file won't be used if a custom |
| // DEF file is specified by win_def_file attribute. |
| if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) { |
| try { |
| Artifact generatedDefFile = |
| CppHelper.createDefFileActions( |
| ruleContext, |
| ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST), |
| ccCompilationOutputs.getObjectFiles(false), |
| ccToolchain |
| .getFeatures() |
| .getArtifactNameForCategory( |
| ArtifactCategory.DYNAMIC_LIBRARY, ruleContext.getLabel().getName())); |
| linkingHelper.setDefFile(generatedDefFile); |
| } catch (EvalException e) { |
| ruleContext.throwWithRuleError(e.getMessage()); |
| throw new IllegalStateException("Should not be reached"); |
| } |
| } |
| |
| // If user specifies a custom DEF file, then we use this one instead of the generated one. |
| Artifact customDefFile = null; |
| if (ruleContext.isAttrDefined("win_def_file", LABEL)) { |
| customDefFile = ruleContext.getPrerequisiteArtifact("win_def_file", Mode.TARGET); |
| if (customDefFile != null) { |
| linkingHelper.setDefFile(customDefFile); |
| } |
| } |
| } |
| ccLinkingOutputs = linkingHelper.link(ccCompilationOutputs); |
| } |
| |
| /* |
| * 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. |
| */ |
| ImmutableList<LibraryToLink> precompiledStaticLibraries = |
| ImmutableList.<LibraryToLink>builder() |
| .addAll( |
| LinkerInputs.opaqueLibrariesToLink( |
| ArtifactCategory.STATIC_LIBRARY, precompiledFiles.getStaticLibraries())) |
| .addAll( |
| LinkerInputs.opaqueLibrariesToLink( |
| ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY, |
| precompiledFiles.getAlwayslinkStaticLibraries())) |
| .build(); |
| |
| ImmutableList<LibraryToLink> precompiledPicStaticLibraries = |
| ImmutableList.<LibraryToLink>builder() |
| .addAll( |
| LinkerInputs.opaqueLibrariesToLink( |
| ArtifactCategory.STATIC_LIBRARY, precompiledFiles.getPicStaticLibraries())) |
| .addAll( |
| LinkerInputs.opaqueLibrariesToLink( |
| ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY, |
| precompiledFiles.getPicAlwayslinkLibraries())) |
| .build(); |
| |
| List<LibraryToLink> dynamicLibraries = |
| ImmutableList.copyOf( |
| Iterables.transform( |
| precompiledFiles.getSharedLibraries(), |
| library -> |
| LinkerInputs.solibLibraryToLink( |
| common.getDynamicLibrarySymlink(library, true), |
| library, |
| CcLinkingOutputs.libraryIdentifierOf(library)))); |
| |
| ImmutableSortedMap.Builder<String, NestedSet<Artifact>> outputGroups = |
| ImmutableSortedMap.naturalOrder(); |
| if (!ccLinkingOutputs.isEmpty()) { |
| outputGroups.putAll( |
| addLinkerOutputArtifacts( |
| ruleContext, |
| ccToolchain, |
| ruleContext.getConfiguration(), |
| ccCompilationOutputs, |
| featureConfiguration)); |
| } |
| CcLinkingOutputs ccLinkingOutputsWithPrecompiledLibraries = |
| addPrecompiledLibrariesToLinkingOutputs( |
| ruleContext, |
| ccLinkingOutputs, |
| precompiledStaticLibraries, |
| precompiledPicStaticLibraries, |
| dynamicLibraries, |
| ccCompilationOutputs); |
| |
| ImmutableList<LibraryToLinkWrapper> libraryToLinkWrappers = ImmutableList.of(); |
| if (!neverLink) { |
| libraryToLinkWrappers = |
| createLibraryToLinkWrappersList( |
| ruleContext, |
| ccLinkingOutputs, |
| precompiledStaticLibraries, |
| precompiledPicStaticLibraries, |
| dynamicLibraries, |
| ccCompilationOutputs); |
| } |
| |
| CcLinkingInfo ccLinkingInfo = |
| linkingHelper.buildCcLinkingInfoFromLibraryToLinkWrappers( |
| libraryToLinkWrappers, compilationInfo.getCcCompilationContext()); |
| CcNativeLibraryProvider ccNativeLibraryProvider = |
| CppHelper.collectNativeCcLibraries( |
| ruleContext.getPrerequisites("deps", Mode.TARGET), |
| ccLinkingOutputsWithPrecompiledLibraries); |
| |
| /* |
| * 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(); |
| filesBuilder.addAll(LinkerInputs.toLibraryArtifacts(ccLinkingOutputs.getStaticLibraries())); |
| filesBuilder.addAll(LinkerInputs.toLibraryArtifacts(ccLinkingOutputs.getPicStaticLibraries())); |
| |
| if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { |
| filesBuilder.addAll( |
| LinkerInputs.toNonSolibArtifacts(ccLinkingOutputs.getDynamicLibrariesForLinking())); |
| filesBuilder.addAll( |
| LinkerInputs.toNonSolibArtifacts(ccLinkingOutputs.getDynamicLibrariesForRuntime())); |
| } |
| |
| if (!featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) { |
| warnAboutEmptyLibraries(ruleContext, compilationInfo.getCcCompilationOutputs(), 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) { |
| builder.addTransitiveArtifacts( |
| ccToolchain.getDynamicRuntimeLinkInputs(ruleContext, featureConfiguration)); |
| } |
| Runfiles runfiles = builder.build(); |
| Runfiles.Builder defaultRunfiles = |
| new Runfiles.Builder(ruleContext.getWorkspaceName()) |
| .merge(runfiles) |
| .addArtifacts( |
| ccLinkingOutputsWithPrecompiledLibraries.getLibrariesForRunfiles(!neverLink)); |
| |
| Runfiles.Builder dataRunfiles = |
| new Runfiles.Builder(ruleContext.getWorkspaceName()) |
| .merge(runfiles) |
| .addArtifacts(ccLinkingOutputsWithPrecompiledLibraries.getLibrariesForRunfiles(false)); |
| |
| CcSkylarkApiProvider.maybeAdd(ruleContext, targetBuilder); |
| targetBuilder |
| .setFilesToBuild(filesToBuild) |
| .addProvider(compilationInfo.getCppDebugFileProvider()) |
| .addProvider(ccNativeLibraryProvider) |
| .addNativeDeclaredProvider( |
| CcInfo.builder() |
| .setCcCompilationContext(compilationInfo.getCcCompilationContext()) |
| .setCcLinkingInfo(ccLinkingInfo) |
| .build()) |
| .addOutputGroups( |
| CcCommon.mergeOutputGroups( |
| ImmutableList.of(compilationInfo.getOutputGroups(), 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, 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(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, |
| 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( |
| ccToolchain.getCppConfiguration(), 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<LibraryToLinkWrapper> createLibraryToLinkWrappersList( |
| RuleContext ruleContext, |
| CcLinkingOutputs ccLinkingOutputs, |
| List<LibraryToLink> staticLibraries, |
| List<LibraryToLink> picStaticLibraries, |
| List<LibraryToLink> dynamicLibrariesForRuntime, |
| CcCompilationOutputs ccCompilationOutputs) { |
| Preconditions.checkState(ccLinkingOutputs.getStaticLibraries().size() <= 1); |
| Preconditions.checkState(ccLinkingOutputs.getPicStaticLibraries().size() <= 1); |
| Preconditions.checkState(ccLinkingOutputs.getDynamicLibrariesForLinking().size() <= 1); |
| Preconditions.checkState(ccLinkingOutputs.getDynamicLibrariesForRuntime().size() <= 1); |
| |
| ImmutableList.Builder<LibraryToLinkWrapper> libraryToLinkWrappers = ImmutableList.builder(); |
| |
| checkIfLinkOutputsCollidingWithPrecompiledFiles( |
| ruleContext, |
| ccLinkingOutputs, |
| staticLibraries, |
| picStaticLibraries, |
| dynamicLibrariesForRuntime, |
| ccCompilationOutputs); |
| |
| if (ruleContext.hasErrors()) { |
| return libraryToLinkWrappers.build(); |
| } |
| |
| // 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. |
| libraryToLinkWrappers.addAll( |
| convertPrecompiledLibrariesToLibraryToLinkWrapper( |
| staticLibraries, picStaticLibraries, dynamicLibrariesForRuntime)); |
| if (!ccCompilationOutputs.isEmpty() |
| || (staticLibraries.isEmpty() |
| && picStaticLibraries.isEmpty() |
| && dynamicLibrariesForRuntime.isEmpty() |
| && isContentsOfCcLinkingOutputsImplicitlyCreated( |
| ccCompilationOutputs, ccLinkingOutputs))) { |
| LibraryToLinkWrapper linkOutputsLibraryToLinkWrapper = |
| LibraryToLinkWrapper.convertLinkOutputsToLibraryToLinkWrapper(ccLinkingOutputs); |
| if (linkOutputsLibraryToLinkWrapper != null) { |
| libraryToLinkWrappers.add(linkOutputsLibraryToLinkWrapper); |
| } |
| } |
| |
| return libraryToLinkWrappers.build(); |
| } |
| |
| private static boolean isContentsOfCcLinkingOutputsImplicitlyCreated( |
| CcCompilationOutputs ccCompilationOutputs, CcLinkingOutputs ccLinkingOutputs) { |
| return ccCompilationOutputs.isEmpty() && !ccLinkingOutputs.isEmpty(); |
| } |
| |
| private static List<LibraryToLinkWrapper> convertPrecompiledLibrariesToLibraryToLinkWrapper( |
| List<LibraryToLink> staticLibraries, |
| List<LibraryToLink> picStaticLibraries, |
| List<LibraryToLink> dynamicLibrariesForRuntime) { |
| ImmutableList.Builder<LibraryToLinkWrapper> libraryToLinkWrappers = ImmutableList.builder(); |
| |
| Set<String> identifiersUsed = new HashSet<>(); |
| // Here we hae an O(n^2) algorithm, the size of the inputs is never big though, we only work |
| // here with the local libraries, none of the libraries of the transitive closure. |
| for (LibraryToLink staticLibrary : staticLibraries) { |
| LibraryToLinkWrapper.Builder libraryToLinkWrapperBuilder = LibraryToLinkWrapper.builder(); |
| libraryToLinkWrapperBuilder.setStaticLibrary(staticLibrary.getArtifact()); |
| String identifier = staticLibrary.getLibraryIdentifier(); |
| libraryToLinkWrapperBuilder.setLibraryIdentifier(identifier); |
| List<LibraryToLink> sameIdentifierPicStaticLibraries = |
| picStaticLibraries.stream() |
| .filter(x -> x.getLibraryIdentifier().equals(identifier)) |
| .collect(ImmutableList.toImmutableList()); |
| if (!sameIdentifierPicStaticLibraries.isEmpty()) { |
| libraryToLinkWrapperBuilder.setPicStaticLibrary( |
| sameIdentifierPicStaticLibraries.get(0).getArtifact()); |
| } |
| List<LibraryToLink> sameIdentifierDynamicLibraries = |
| dynamicLibrariesForRuntime.stream() |
| .filter(x -> x.getLibraryIdentifier().equals(identifier)) |
| .collect(ImmutableList.toImmutableList()); |
| if (!sameIdentifierDynamicLibraries.isEmpty()) { |
| LibraryToLink dynamicLibrary = sameIdentifierDynamicLibraries.get(0); |
| libraryToLinkWrapperBuilder.setDynamicLibrary(dynamicLibrary.getArtifact()); |
| if (dynamicLibrary instanceof SolibLibraryToLink) { |
| libraryToLinkWrapperBuilder.setResolvedSymlinkDynamicLibrary( |
| dynamicLibrary.getOriginalLibraryArtifact()); |
| } |
| } |
| libraryToLinkWrapperBuilder.setAlwayslink( |
| staticLibrary.getArtifactCategory() == ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY); |
| identifiersUsed.add(identifier); |
| libraryToLinkWrappers.add(libraryToLinkWrapperBuilder.build()); |
| } |
| |
| for (LibraryToLink picStaticLibrary : picStaticLibraries) { |
| String identifier = picStaticLibrary.getLibraryIdentifier(); |
| if (identifiersUsed.contains(identifier)) { |
| continue; |
| } |
| LibraryToLinkWrapper.Builder libraryToLinkWrapperBuilder = LibraryToLinkWrapper.builder(); |
| libraryToLinkWrapperBuilder.setPicStaticLibrary(picStaticLibrary.getArtifact()); |
| libraryToLinkWrapperBuilder.setLibraryIdentifier(identifier); |
| List<LibraryToLink> sameIdentifierDynamicLibraries = |
| dynamicLibrariesForRuntime.stream() |
| .filter(x -> x.getLibraryIdentifier().equals(identifier)) |
| .collect(ImmutableList.toImmutableList()); |
| if (!sameIdentifierDynamicLibraries.isEmpty()) { |
| LibraryToLink dynamicLibrary = sameIdentifierDynamicLibraries.get(0); |
| libraryToLinkWrapperBuilder.setDynamicLibrary(dynamicLibrary.getArtifact()); |
| if (dynamicLibrary instanceof SolibLibraryToLink) { |
| libraryToLinkWrapperBuilder.setResolvedSymlinkDynamicLibrary( |
| dynamicLibrary.getOriginalLibraryArtifact()); |
| } |
| } |
| libraryToLinkWrapperBuilder.setAlwayslink( |
| picStaticLibrary.getArtifactCategory() == ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY); |
| identifiersUsed.add(identifier); |
| libraryToLinkWrappers.add(libraryToLinkWrapperBuilder.build()); |
| } |
| |
| for (LibraryToLink dynamicLibrary : dynamicLibrariesForRuntime) { |
| String identifier = dynamicLibrary.getLibraryIdentifier(); |
| if (identifiersUsed.contains(identifier)) { |
| continue; |
| } |
| LibraryToLinkWrapper.Builder libraryToLinkWrapperBuilder = LibraryToLinkWrapper.builder(); |
| libraryToLinkWrapperBuilder.setDynamicLibrary(dynamicLibrary.getArtifact()); |
| libraryToLinkWrapperBuilder.setLibraryIdentifier(identifier); |
| if (dynamicLibrary instanceof SolibLibraryToLink) { |
| libraryToLinkWrapperBuilder.setResolvedSymlinkDynamicLibrary( |
| dynamicLibrary.getOriginalLibraryArtifact()); |
| } |
| libraryToLinkWrappers.add(libraryToLinkWrapperBuilder.build()); |
| } |
| return libraryToLinkWrappers.build(); |
| } |
| |
| private static CcLinkingOutputs addPrecompiledLibrariesToLinkingOutputs( |
| RuleContext ruleContext, |
| CcLinkingOutputs ccLinkingOutputs, |
| List<LibraryToLink> staticLibraries, |
| List<LibraryToLink> picStaticLibraries, |
| List<LibraryToLink> dynamicLibrariesForRuntime, |
| CcCompilationOutputs ccCompilationOutputs) { |
| if (staticLibraries.isEmpty() |
| && picStaticLibraries.isEmpty() |
| && dynamicLibrariesForRuntime.isEmpty()) { |
| return ccLinkingOutputs; |
| } |
| |
| CcLinkingOutputs.Builder newOutputsBuilder = new CcLinkingOutputs.Builder(); |
| if (!ccCompilationOutputs.isEmpty()) { |
| newOutputsBuilder.merge(ccLinkingOutputs); |
| } |
| |
| checkIfLinkOutputsCollidingWithPrecompiledFiles( |
| ruleContext, |
| ccLinkingOutputs, |
| staticLibraries, |
| picStaticLibraries, |
| dynamicLibrariesForRuntime, |
| ccCompilationOutputs); |
| |
| // Merge the pre-compiled libraries (static & dynamic) into the linker outputs. |
| return newOutputsBuilder |
| .addStaticLibraries(staticLibraries) |
| .addPicStaticLibraries(picStaticLibraries) |
| .addDynamicLibraries(dynamicLibrariesForRuntime) |
| .addDynamicLibrariesForRuntime(dynamicLibrariesForRuntime) |
| .build(); |
| } |
| |
| private static void checkIfLinkOutputsCollidingWithPrecompiledFiles( |
| RuleContext ruleContext, |
| CcLinkingOutputs ccLinkingOutputs, |
| List<LibraryToLink> staticLibraries, |
| List<LibraryToLink> picStaticLibraries, |
| List<LibraryToLink> dynamicLibrariesForRuntime, |
| CcCompilationOutputs ccCompilationOutputs) { |
| if (!ccCompilationOutputs.isEmpty()) { |
| ImmutableSetMultimap<String, LibraryToLink> precompiledLibraryMap = |
| CcLinkingOutputs.getLibrariesByIdentifier( |
| Iterables.concat( |
| staticLibraries, |
| picStaticLibraries, |
| dynamicLibrariesForRuntime)); |
| ImmutableSetMultimap<String, LibraryToLink> linkedLibraryMap = |
| ccLinkingOutputs.getLibrariesByIdentifier(); |
| for (String matchingIdentifier : |
| Sets.intersection(precompiledLibraryMap.keySet(), linkedLibraryMap.keySet())) { |
| Iterable<Artifact> matchingInputLibs = |
| LinkerInputs.toNonSolibArtifacts(precompiledLibraryMap.get(matchingIdentifier)); |
| Iterable<Artifact> matchingOutputLibs = |
| LinkerInputs.toNonSolibArtifacts(linkedLibraryMap.get(matchingIdentifier)); |
| ruleContext.ruleError( |
| "Can't put " |
| + Streams.stream(matchingInputLibs) |
| .map(Artifact::getFilename) |
| .collect(joining(", ")) |
| + " into the srcs of a " |
| + ruleContext.getRuleClassNameForLogging() |
| + " with the same name (" |
| + ruleContext.getRule().getName() |
| + ") which also contains other code or objects to link; it shares a name with " |
| + Streams.stream(matchingOutputLibs) |
| .map(Artifact::getFilename) |
| .collect(joining(", ")) |
| + " (output compiled and linked from the non-library sources of this rule), " |
| + "which could cause confusion"); |
| } |
| } |
| } |
| } |