blob: 7c19b43c899efe4bf62590661bee1ca0e7525dc6 [file] [log] [blame]
// 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);
boolean linkedStaticallyBySpecified =
ruleContext.attributes().isAttributeValueExplicitlySpecified("linked_statically_by");
boolean linkedStaticallyByAllSpecified =
ruleContext.attributes().isAttributeValueExplicitlySpecified("linked_statically_by_all");
if ((linkedStaticallyBySpecified || linkedStaticallyByAllSpecified)
&& !ruleContext
.getAnalysisEnvironment()
.getSkylarkSemantics()
.experimentalCcSharedLibrary()) {
ruleContext.ruleError(
"The attributes 'linked_statically_by' and 'linked_statically_by_all' can only be used "
+ "with the flag --experimental_cc_shared_library.");
}
if (linkedStaticallyBySpecified && linkedStaticallyByAllSpecified) {
ruleContext.ruleError(
"Cannot specify both 'linked_statically_by' and 'linked_statically_by_all'");
}
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);
PrecompiledFiles precompiledFiles = new PrecompiledFiles(ruleContext);
semantics.validateAttributes(ruleContext);
if (ruleContext.hasErrors()) {
addEmptyRequiredProviders(targetBuilder);
return;
}
ImmutableList<TransitiveInfoCollection> deps =
ImmutableList.copyOf(ruleContext.getPrerequisites("deps", Mode.TARGET));
CppHelper.checkProtoLibrariesInDeps(ruleContext, deps);
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);
}
}