blob: 1dfa570b35c8bb481e838d54d5c85de6f64b87df [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 static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
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.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.LanguageDependentFragment;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
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.TransitiveInfoProviderMap;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
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.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.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.HeadersCheckingMode;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* A class to create C/C++ compile and link actions in a way that is consistent with cc_library.
* Rules that generate source files and emulate cc_library on top of that should use this class
* instead of the lower-level APIs in CppHelper and CppModel.
*
* <p>Rules that want to use this class are required to have implicit dependencies on the toolchain,
* the STL, the lipo context, and so on. Optionally, they can also have copts, and malloc
* attributes, but note that these require explicit calls to the corresponding setter methods.
* TODO(plf): Split this class in two, one for compilation and other for linking.
*/
public final class CcLibraryHelper {
/**
* Similar to {@code OutputGroupInfo.HIDDEN_TOP_LEVEL}, but specific to header token files.
*/
public static final String HIDDEN_HEADER_TOKENS =
OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX
+ "hidden_header_tokens"
+ OutputGroupInfo.INTERNAL_SUFFIX;
/** 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 group of source file types and action names for builds controlled by CcLibraryHelper.
* Determines what file types CcLibraryHelper considers sources and what action configs are
* configured in the CROSSTOOL.
*/
public enum SourceCategory {
CC(
FileTypeSet.of(
CppFileTypes.CPP_SOURCE,
CppFileTypes.CPP_HEADER,
CppFileTypes.C_SOURCE,
CppFileTypes.ASSEMBLER,
CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR,
CppFileTypes.CLIF_INPUT_PROTO)),
CC_AND_OBJC(
FileTypeSet.of(
CppFileTypes.CPP_SOURCE,
CppFileTypes.CPP_HEADER,
CppFileTypes.OBJC_SOURCE,
CppFileTypes.OBJCPP_SOURCE,
CppFileTypes.C_SOURCE,
CppFileTypes.ASSEMBLER,
CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR));
private final FileTypeSet sourceTypeSet;
private SourceCategory(FileTypeSet sourceTypeSet) {
this.sourceTypeSet = sourceTypeSet;
}
/**
* Returns the set of file types that are valid for this category.
*/
public FileTypeSet getSourceTypes() {
return sourceTypeSet;
}
}
/** Function for extracting module maps from CppCompilationDependencies. */
public static final Function<TransitiveInfoCollection, CppModuleMap> CPP_DEPS_TO_MODULES =
dep -> {
CppCompilationContext context = dep.getProvider(CppCompilationContext.class);
return context == null ? null : context.getCppModuleMap();
};
/**
* Contains the providers as well as the compilation and linking outputs, and the compilation
* context. TODO(plf): Remove outer class Info.
*/
public static final class Info {
private final TransitiveInfoProviderMapBuilder providers;
private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
private final CcCompilationOutputs compilationOutputs;
private final CcLinkingOutputs linkingOutputsExcludingPrecompiledLibraries;
private final CppCompilationContext context;
/** Contains the providers as well as the compilation outputs, and the compilation context. */
public static final class CompilationInfo {
private final TransitiveInfoProviderMap providers;
private final Map<String, NestedSet<Artifact>> outputGroups;
private final CcCompilationOutputs compilationOutputs;
private final CppCompilationContext context;
private CompilationInfo(
TransitiveInfoProviderMap providers,
Map<String, NestedSet<Artifact>> outputGroups,
CcCompilationOutputs compilationOutputs,
CppCompilationContext context) {
this.providers = providers;
this.outputGroups = outputGroups;
this.compilationOutputs = compilationOutputs;
this.context = context;
}
public TransitiveInfoProviderMap getProviders() {
return providers;
}
public Map<String, NestedSet<Artifact>> getOutputGroups() {
return outputGroups;
}
public CcCompilationOutputs getCcCompilationOutputs() {
return compilationOutputs;
}
public CppCompilationContext getCppCompilationContext() {
return context;
}
}
/** Contains the providers as well as the linking outputs. */
public static final class LinkingInfo {
private final TransitiveInfoProviderMap providers;
private final Map<String, NestedSet<Artifact>> outputGroups;
private final CcLinkingOutputs linkingOutputs;
private final CcLinkingOutputs linkingOutputsExcludingPrecompiledLibraries;
private LinkingInfo(
TransitiveInfoProviderMap providers,
Map<String, NestedSet<Artifact>> outputGroups,
CcLinkingOutputs linkingOutputs,
CcLinkingOutputs linkingOutputsExcludingPrecompiledLibraries) {
this.providers = providers;
this.outputGroups = outputGroups;
this.linkingOutputs = linkingOutputs;
this.linkingOutputsExcludingPrecompiledLibraries =
linkingOutputsExcludingPrecompiledLibraries;
}
public TransitiveInfoProviderMap getProviders() {
return providers;
}
public Map<String, NestedSet<Artifact>> getOutputGroups() {
return outputGroups;
}
public CcLinkingOutputs getCcLinkingOutputs() {
return linkingOutputs;
}
/**
* Returns the linking outputs before adding the pre-compiled libraries. Avoid using this -
* pre-compiled and locally compiled libraries should be treated identically. This method only
* exists for backwards compatibility.
*/
public CcLinkingOutputs getCcLinkingOutputsExcludingPrecompiledLibraries() {
return linkingOutputsExcludingPrecompiledLibraries;
}
/**
* Adds the static, pic-static libraries to the given builder. If addDynamicLibraries
* parameter is true, it also adds dynamic(both compile-time and execution-time) libraries.
*/
public void addLinkingOutputsTo(
NestedSetBuilder<Artifact> filesBuilder, boolean addDynamicLibraries) {
filesBuilder
.addAll(LinkerInputs.toLibraryArtifacts(linkingOutputs.getStaticLibraries()))
.addAll(LinkerInputs.toLibraryArtifacts(linkingOutputs.getPicStaticLibraries()));
if (addDynamicLibraries) {
filesBuilder
.addAll(LinkerInputs.toNonSolibArtifacts(linkingOutputs.getDynamicLibraries()))
.addAll(
LinkerInputs.toNonSolibArtifacts(linkingOutputs.getExecutionDynamicLibraries()));
}
}
public void addLinkingOutputsTo(NestedSetBuilder<Artifact> filesBuilder) {
addLinkingOutputsTo(filesBuilder, true);
}
}
public Info(CompilationInfo compilationInfo, LinkingInfo linkingInfo) {
this.providers =
new TransitiveInfoProviderMapBuilder()
.addAll(compilationInfo.getProviders())
.addAll(linkingInfo.getProviders());
this.outputGroups =
ImmutableMap.copyOf(
Iterables.concat(
compilationInfo.getOutputGroups().entrySet(),
linkingInfo.getOutputGroups().entrySet()));
this.compilationOutputs = compilationInfo.getCcCompilationOutputs();
this.linkingOutputsExcludingPrecompiledLibraries =
linkingInfo.getCcLinkingOutputsExcludingPrecompiledLibraries();
this.context = compilationInfo.getCppCompilationContext();
}
public TransitiveInfoProviderMap getProviders() {
return providers.build();
}
public ImmutableMap<String, NestedSet<Artifact>> getOutputGroups() {
return outputGroups;
}
public CcCompilationOutputs getCcCompilationOutputs() {
return compilationOutputs;
}
/**
* Returns the linking outputs before adding the pre-compiled libraries. Avoid using this -
* pre-compiled and locally compiled libraries should be treated identically. This method only
* exists for backwards compatibility.
*/
public CcLinkingOutputs getCcLinkingOutputsExcludingPrecompiledLibraries() {
return linkingOutputsExcludingPrecompiledLibraries;
}
public CppCompilationContext getCppCompilationContext() {
return context;
}
/**
* Merges a list of output groups into one. The sets for each entry with a given key are merged.
*/
public static Map<String, NestedSet<Artifact>> mergeOutputGroups(
Map<String, NestedSet<Artifact>>... outputGroups) {
Map<String, NestedSetBuilder<Artifact>> mergedOutputGroupsBuilder = new TreeMap<>();
for (Map<String, NestedSet<Artifact>> outputGroup : outputGroups) {
for (Map.Entry<String, NestedSet<Artifact>> entryOutputGroup : outputGroup.entrySet()) {
String key = entryOutputGroup.getKey();
mergedOutputGroupsBuilder.computeIfAbsent(
key, (String k) -> NestedSetBuilder.compileOrder());
mergedOutputGroupsBuilder.get(key).addTransitive(entryOutputGroup.getValue());
}
}
Map<String, NestedSet<Artifact>> mergedOutputGroups = new TreeMap<>();
for (Map.Entry<String, NestedSetBuilder<Artifact>> entryOutputGroupBuilder :
mergedOutputGroupsBuilder.entrySet()) {
mergedOutputGroups.put(
entryOutputGroupBuilder.getKey(), entryOutputGroupBuilder.getValue().build());
}
return mergedOutputGroups;
}
}
private final RuleContext ruleContext;
private final CppSemantics semantics;
private final BuildConfiguration configuration;
private final List<Artifact> publicHeaders = new ArrayList<>();
private final List<Artifact> nonModuleMapHeaders = new ArrayList<>();
private final List<Artifact> publicTextualHeaders = new ArrayList<>();
private final List<Artifact> privateHeaders = new ArrayList<>();
private final List<Artifact> additionalInputs = new ArrayList<>();
private final List<Artifact> compilationMandatoryInputs = new ArrayList<>();
private final List<Artifact> additionalIncludeScanningRoots = new ArrayList<>();
private final List<PathFragment> additionalExportedHeaders = new ArrayList<>();
private final List<CppModuleMap> additionalCppModuleMaps = new ArrayList<>();
private final Set<CppSource> compilationUnitSources = new LinkedHashSet<>();
private final List<Artifact> objectFiles = new ArrayList<>();
private final List<Artifact> picObjectFiles = new ArrayList<>();
private final List<Artifact> nonCodeLinkerInputs = new ArrayList<>();
private ImmutableList<String> copts = ImmutableList.of();
private final List<String> linkopts = new ArrayList<>();
private Predicate<String> coptsFilter = Predicates.alwaysTrue();
private final Set<String> defines = new LinkedHashSet<>();
private final List<TransitiveInfoCollection> deps = new ArrayList<>();
private final List<CppCompilationContext> depContexts = new ArrayList<>();
private final NestedSetBuilder<Artifact> linkstamps = NestedSetBuilder.stableOrder();
private final List<PathFragment> looseIncludeDirs = new ArrayList<>();
private final List<PathFragment> systemIncludeDirs = new ArrayList<>();
private final List<PathFragment> includeDirs = new ArrayList<>();
private final List<Artifact> linkActionInputs = new ArrayList<>();
@Nullable private Artifact dynamicLibrary;
private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
private HeadersCheckingMode headersCheckingMode = HeadersCheckingMode.LOOSE;
private boolean neverlink;
private boolean fake;
private final List<LibraryToLink> staticLibraries = new ArrayList<>();
private final List<LibraryToLink> picStaticLibraries = new ArrayList<>();
private final List<LibraryToLink> dynamicLibraries = new ArrayList<>();
private final List<LibraryToLink> executionDynamicLibraries = new ArrayList<>();
private boolean emitLinkActions = true;
private boolean emitLinkActionsIfEmpty;
private boolean emitCcNativeLibrariesProvider;
private boolean emitCcSpecificLinkParamsProvider;
private boolean emitInterfaceSharedObjects;
private boolean createDynamicLibrary = true;
private boolean createStaticLibraries = true;
private boolean checkDepsGenerateCpp = true;
private boolean emitCompileProviders;
private final SourceCategory sourceCategory;
private List<VariablesExtension> variablesExtensions = new ArrayList<>();
@Nullable private CppModuleMap cppModuleMap;
private boolean propagateModuleMapToCompileAction = true;
private final FeatureConfiguration featureConfiguration;
private CcToolchainProvider ccToolchain;
private final FdoSupportProvider fdoSupport;
private String linkedArtifactNameSuffix = "";
private boolean useDeps = true;
private boolean generateModuleMap = true;
private String purpose = null;
private boolean generateNoPic = true;
/**
* Creates a CcLibraryHelper.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param sourceCatagory the candidate source types for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
*/
public CcLibraryHelper(
RuleContext ruleContext,
CppSemantics semantics,
FeatureConfiguration featureConfiguration,
SourceCategory sourceCatagory,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport) {
this(ruleContext, semantics, featureConfiguration, sourceCatagory, ccToolchain, fdoSupport,
ruleContext.getConfiguration());
}
/**
* Creates a CcLibraryHelper that outputs artifacts in a given configuration.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param sourceCatagory the candidate source types for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
* @param configuration the configuration that gives the directory of output artifacts
*/
public CcLibraryHelper(
RuleContext ruleContext,
CppSemantics semantics,
FeatureConfiguration featureConfiguration,
SourceCategory sourceCatagory,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport,
BuildConfiguration configuration) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = Preconditions.checkNotNull(semantics);
this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration);
this.sourceCategory = Preconditions.checkNotNull(sourceCatagory);
this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
this.configuration = Preconditions.checkNotNull(configuration);
}
/**
* Creates a CcLibraryHelper for cpp source files.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
*/
public CcLibraryHelper(
RuleContext ruleContext, CppSemantics semantics, FeatureConfiguration featureConfiguration,
CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport) {
this(ruleContext, semantics, featureConfiguration, SourceCategory.CC, ccToolchain, fdoSupport);
}
/** Sets fields that overlap for cc_library and cc_binary rules. */
public CcLibraryHelper fromCommon(CcCommon common) {
setCopts(common.getCopts());
addDefines(common.getDefines());
addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET));
addLooseIncludeDirs(common.getLooseIncludeDirs());
addNonCodeLinkerInputs(common.getLinkerScripts());
addSystemIncludeDirs(common.getSystemIncludeDirs());
setCoptsFilter(common.getCoptsFilter());
setHeadersCheckingMode(semantics.determineHeadersCheckingMode(ruleContext));
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcLibraryHelper addPublicHeaders(Collection<Artifact> headers) {
for (Artifact header : headers) {
addHeader(header, ruleContext.getLabel());
}
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcLibraryHelper addPublicHeaders(Artifact... headers) {
addPublicHeaders(Arrays.asList(headers));
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcLibraryHelper addPublicHeaders(Iterable<Pair<Artifact, Label>> headers) {
for (Pair<Artifact, Label> header : headers) {
addHeader(header.first, header.second);
}
return this;
}
/**
* Add the corresponding files as public header files, i.e., these files will not be compiled, but
* are made visible as includes to dependent rules in module maps.
*/
public CcLibraryHelper addAdditionalExportedHeaders(
Iterable<PathFragment> additionalExportedHeaders) {
Iterables.addAll(this.additionalExportedHeaders, additionalExportedHeaders);
return this;
}
/**
* Add the corresponding files as public textual header files. These files will not be compiled
* into a target's header module, but will be made visible as textual includes to dependent rules.
*/
public CcLibraryHelper addPublicTextualHeaders(Iterable<Artifact> textualHeaders) {
Iterables.addAll(this.publicTextualHeaders, textualHeaders);
for (Artifact header : textualHeaders) {
this.additionalExportedHeaders.add(header.getExecPath());
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules. The given build
* variables will be added to those used for compiling this source.
*/
public CcLibraryHelper addSources(Collection<Artifact> sources) {
for (Artifact source : sources) {
addSource(source, ruleContext.getLabel());
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules.
*/
public CcLibraryHelper addSources(Iterable<Pair<Artifact, Label>> sources) {
for (Pair<Artifact, Label> source : sources) {
addSource(source.first, source.second);
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules.
*/
public CcLibraryHelper addSources(Artifact... sources) {
return addSources(Arrays.asList(sources));
}
/** Add the corresponding files as non-header, non-source input files. */
public CcLibraryHelper addAdditionalInputs(Collection<Artifact> inputs) {
Iterables.addAll(additionalInputs, inputs);
return this;
}
/**
* Adds a header to {@code publicHeaders} and in case header processing is switched on for the
* file type also to compilationUnitSources.
*/
private void addHeader(Artifact header, Label label) {
// We assume TreeArtifacts passed in are directories containing proper headers.
boolean isHeader =
CppFileTypes.CPP_HEADER.matches(header.getExecPath()) || header.isTreeArtifact();
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(header.getExecPath());
publicHeaders.add(header);
if (isTextualInclude || !isHeader || !shouldProcessHeaders()) {
return;
}
compilationUnitSources.add(CppSource.create(header, label, CppSource.Type.HEADER));
}
/** Adds a header to {@code publicHeaders}, but not to this target's module map. */
public CcLibraryHelper addNonModuleMapHeader(Artifact header) {
Preconditions.checkNotNull(header);
nonModuleMapHeaders.add(header);
return this;
}
/**
* Adds a source to {@code compilationUnitSources} if it is a compiled file type (including
* parsed/preprocessed header) and to {@code privateHeaders} if it is a header.
*/
private CcLibraryHelper addSource(Artifact source, Label label) {
Preconditions.checkNotNull(featureConfiguration);
boolean isHeader = CppFileTypes.CPP_HEADER.matches(source.getExecPath());
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(source.getExecPath());
// We assume TreeArtifacts passed in are directories containing proper sources for compilation.
boolean isCompiledSource =
sourceCategory.getSourceTypes().matches(source.getExecPathString())
|| source.isTreeArtifact();
if (isHeader || isTextualInclude) {
privateHeaders.add(source);
}
if (isTextualInclude || !isCompiledSource || (isHeader && !shouldProcessHeaders())) {
return this;
}
boolean isClifInputProto = CppFileTypes.CLIF_INPUT_PROTO.matches(source.getExecPathString());
CppSource.Type type;
if (isHeader) {
type = CppSource.Type.HEADER;
} else if (isClifInputProto) {
type = CppSource.Type.CLIF_INPUT_PROTO;
} else {
type = CppSource.Type.SOURCE;
}
compilationUnitSources.add(CppSource.create(source, label, type));
return this;
}
private boolean shouldProcessHeaders() {
CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
// If parse_headers_verifies_modules is switched on, we verify that headers are
// self-contained by building the module instead.
return !cppConfiguration.getParseHeadersVerifiesModules()
&& (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESS_HEADERS)
|| featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS));
}
/**
* Returns the compilation unit sources. That includes all compiled source files as well as
* headers that will be parsed or preprocessed. Each source file contains the label it arises from
* in the build graph as well as {@code FeatureConfiguration} that should be used during its
* compilation.
*/
public ImmutableSet<CppSource> getCompilationUnitSources() {
return ImmutableSet.copyOf(this.compilationUnitSources);
}
/**
* Add the corresponding files as linker inputs for non-PIC links. If the corresponding files are
* compiled with PIC, the final link may or may not fail. Note that the final link may not happen
* here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
* depends on the current rule.
*/
public CcLibraryHelper addObjectFiles(Iterable<Artifact> objectFiles) {
for (Artifact objectFile : objectFiles) {
Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
}
Iterables.addAll(this.objectFiles, objectFiles);
return this;
}
/**
* Add the corresponding files as linker inputs for PIC links. If the corresponding files are not
* compiled with PIC, the final link may or may not fail. Note that the final link may not happen
* here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
* depends on the current rule.
*/
public CcLibraryHelper addPicObjectFiles(Iterable<Artifact> picObjectFiles) {
for (Artifact objectFile : objectFiles) {
Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
}
Iterables.addAll(this.picObjectFiles, picObjectFiles);
return this;
}
/**
* Adds the corresponding non-code files as linker inputs.
*/
public CcLibraryHelper addNonCodeLinkerInputs(Iterable<Artifact> nonCodeLinkerInputs) {
for (Artifact nonCodeLinkerInput : nonCodeLinkerInputs) {
String basename = nonCodeLinkerInput.getFilename();
Preconditions.checkArgument(!Link.OBJECT_FILETYPES.matches(basename));
Preconditions.checkArgument(!Link.ARCHIVE_LIBRARY_FILETYPES.matches(basename));
Preconditions.checkArgument(!Link.SHARED_LIBRARY_FILETYPES.matches(basename));
this.nonCodeLinkerInputs.add(nonCodeLinkerInput);
}
return this;
}
/**
* Add the corresponding files as static libraries into the linker outputs (i.e., after the linker
* action) - this makes them available for linking to binary rules that depend on this rule.
*/
public CcLibraryHelper addStaticLibraries(Iterable<LibraryToLink> libraries) {
Iterables.addAll(staticLibraries, libraries);
return this;
}
/**
* Add the corresponding files as static libraries into the linker outputs (i.e., after the linker
* action) - this makes them available for linking to binary rules that depend on this rule.
*/
public CcLibraryHelper addPicStaticLibraries(Iterable<LibraryToLink> libraries) {
Iterables.addAll(picStaticLibraries, libraries);
return this;
}
/**
* Add the corresponding files as dynamic libraries into the linker outputs (i.e., after the
* linker action) - this makes them available for linking to binary rules that depend on this
* rule.
*/
public CcLibraryHelper addDynamicLibraries(Iterable<LibraryToLink> libraries) {
Iterables.addAll(dynamicLibraries, libraries);
return this;
}
/** Add the corresponding files as dynamic libraries required at runtime */
public CcLibraryHelper addExecutionDynamicLibraries(Iterable<LibraryToLink> libraries) {
Iterables.addAll(executionDynamicLibraries, libraries);
return this;
}
public CcLibraryHelper setCopts(ImmutableList<String> copts) {
this.copts = Preconditions.checkNotNull(copts);
return this;
}
/** Sets a pattern that is used to filter copts; set to {@code null} for no filtering. */
public CcLibraryHelper setCoptsFilter(Predicate<String> coptsFilter) {
this.coptsFilter = Preconditions.checkNotNull(coptsFilter);
return this;
}
/**
* Adds the given options as linker options to the link command.
*/
public CcLibraryHelper addLinkopts(Iterable<String> linkopts) {
Iterables.addAll(this.linkopts, linkopts);
return this;
}
/**
* Adds the given defines to the compiler command line.
*/
public CcLibraryHelper addDefines(Iterable<String> defines) {
Iterables.addAll(this.defines, defines);
return this;
}
/**
* Adds the given targets as dependencies - this can include explicit dependencies on other
* rules (like from a "deps" attribute) and also implicit dependencies on runtime libraries.
*/
public CcLibraryHelper addDeps(Iterable<? extends TransitiveInfoCollection> deps) {
for (TransitiveInfoCollection dep : deps) {
this.deps.add(dep);
}
return this;
}
public CcLibraryHelper addDepContext(CppCompilationContext dep) {
this.depContexts.add(Preconditions.checkNotNull(dep));
return this;
}
/**
* Adds the given linkstamps. Note that linkstamps are usually not compiled at the library level,
* but only in the dependent binary rules.
*/
public CcLibraryHelper addLinkstamps(Iterable<? extends TransitiveInfoCollection> linkstamps) {
for (TransitiveInfoCollection linkstamp : linkstamps) {
this.linkstamps.addTransitive(linkstamp.getProvider(FileProvider.class).getFilesToBuild());
}
return this;
}
/**
* Adds the given precompiled files to this helper. Shared and static libraries are added as
* compilation prerequisites, and object files are added as pic or non-pic object files
* respectively.
*/
public CcLibraryHelper addPrecompiledFiles(PrecompiledFiles precompiledFiles) {
addObjectFiles(precompiledFiles.getObjectFiles(false));
addPicObjectFiles(precompiledFiles.getObjectFiles(true));
return this;
}
/**
* Adds the given directories to the loose include directories that are only allowed to be
* referenced when headers checking is {@link HeadersCheckingMode#LOOSE} or {@link
* HeadersCheckingMode#WARN}.
*/
public CcLibraryHelper addLooseIncludeDirs(Iterable<PathFragment> looseIncludeDirs) {
Iterables.addAll(this.looseIncludeDirs, looseIncludeDirs);
return this;
}
/**
* Adds the given directories to the system include directories (they are passed with {@code
* "-isystem"} to the compiler); these are also passed to dependent rules.
*/
public CcLibraryHelper addSystemIncludeDirs(Iterable<PathFragment> systemIncludeDirs) {
Iterables.addAll(this.systemIncludeDirs, systemIncludeDirs);
return this;
}
/**
* Adds the given directories to the include directories (they are passed with {@code "-I"} to
* the compiler); these are also passed to dependent rules.
*/
public CcLibraryHelper addIncludeDirs(Iterable<PathFragment> includeDirs) {
Iterables.addAll(this.includeDirs, includeDirs);
return this;
}
/** Adds the given artifact to the input of any generated link actions. */
public CcLibraryHelper addLinkActionInput(Artifact input) {
Preconditions.checkNotNull(input);
this.linkActionInputs.add(input);
return this;
}
/**
* Adds a variableExtension to template the crosstool.
*/
public CcLibraryHelper addVariableExtension(VariablesExtension variableExtension) {
Preconditions.checkNotNull(variableExtension);
this.variablesExtensions.add(variableExtension);
return this;
}
/**
* Sets a module map artifact for this build.
*/
public CcLibraryHelper setCppModuleMap(CppModuleMap cppModuleMap) {
Preconditions.checkNotNull(cppModuleMap);
this.cppModuleMap = cppModuleMap;
return this;
}
/**
* Signals that this target's module map should not be an input to c++ compile actions.
*/
public CcLibraryHelper setPropagateModuleMapToCompileAction(boolean propagatesModuleMap) {
this.propagateModuleMapToCompileAction = propagatesModuleMap;
return this;
}
/**
* Overrides the path for the generated dynamic library - this should only be called if the
* dynamic library is an implicit or explicit output of the rule, i.e., if it is accessible by
* name from other rules in the same package. Set to {@code null} to use the default computation.
*/
public CcLibraryHelper setDynamicLibrary(@Nullable Artifact dynamicLibrary) {
this.dynamicLibrary = dynamicLibrary;
return this;
}
/**
* Marks the output of this rule as alwayslink, i.e., the corresponding symbols will be retained
* by the linker even if they are not otherwise used. This is useful for libraries that register
* themselves somewhere during initialization.
*
* <p>This only sets the link type (see {@link #setStaticLinkType}), either to a static library or
* to an alwayslink static library (blaze uses a different file extension to signal alwayslink to
* downstream code).
*/
public CcLibraryHelper setAlwayslink(boolean alwayslink) {
linkType = alwayslink
? LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY
: LinkTargetType.STATIC_LIBRARY;
return this;
}
/**
* Directly set the link type. This can be used instead of {@link #setAlwayslink}. Setting
* anything other than a static link causes this class to skip the link action creation.
*/
public CcLibraryHelper setStaticLinkType(LinkTargetType linkType) {
Preconditions.checkNotNull(linkType);
Preconditions.checkState(linkType.staticness() == Staticness.STATIC);
this.linkType = linkType;
return this;
}
/**
* Marks the resulting code as neverlink, i.e., the code will not be linked into dependent
* libraries or binaries - the header files are still available.
*/
public CcLibraryHelper setNeverLink(boolean neverlink) {
this.neverlink = neverlink;
return this;
}
/**
* Sets the given headers checking mode. The default is {@link HeadersCheckingMode#LOOSE}.
*/
public CcLibraryHelper setHeadersCheckingMode(HeadersCheckingMode headersCheckingMode) {
this.headersCheckingMode = Preconditions.checkNotNull(headersCheckingMode);
return this;
}
/**
* Marks the resulting code as fake, i.e., the code will not actually be compiled or linked, but
* instead, the compile command is written to a file and added to the runfiles. This is currently
* used for non-compilation tests. Unfortunately, the design is problematic, so please don't add
* any further uses.
*/
public CcLibraryHelper setFake(boolean fake) {
this.fake = fake;
return this;
}
/*
* Adds a suffix for paths of linked artifacts. Normally their paths are derived solely from rule
* labels. In the case of multiple callers (e.g., aspects) acting on a single rule, they may
* generate the same linked artifact and therefore lead to artifact conflicts. This method
* provides a way to avoid this artifact conflict by allowing different callers acting on the same
* rule to provide a suffix that will be used to scope their own linked artifacts.
*/
public CcLibraryHelper setLinkedArtifactNameSuffix(String suffix) {
this.linkedArtifactNameSuffix = Preconditions.checkNotNull(suffix);
return this;
}
/**
* This adds the {@link CcNativeLibraryProvider} to the providers created by this class.
*/
public CcLibraryHelper enableCcNativeLibrariesProvider() {
this.emitCcNativeLibrariesProvider = true;
return this;
}
/**
* This adds the {@link CcSpecificLinkParamsProvider} to the providers created by this class.
* Otherwise the result will contain an instance of {@link CcLinkParamsInfo}.
*/
public CcLibraryHelper enableCcSpecificLinkParamsProvider() {
this.emitCcSpecificLinkParamsProvider = true;
return this;
}
public CcLibraryHelper setEmitLinkActions(boolean emitLinkActions) {
this.emitLinkActions = emitLinkActions;
return this;
}
/**
* Enables or disables generation of link actions if there are no object files. Some rules declare
* a <code>.a</code> or <code>.so</code> implicit output, which requires that these files are
* created even if there are no object files, so be careful when calling this.
*
* <p>This is disabled by default.
*/
public CcLibraryHelper setGenerateLinkActionsIfEmpty(boolean emitLinkActionsIfEmpty) {
this.emitLinkActionsIfEmpty = emitLinkActionsIfEmpty;
return this;
}
/**
* Enables the optional generation of interface dynamic libraries - this is only used when the
* linker generates a dynamic library, and only if the crosstool supports it. The default is not
* to generate interface dynamic libraries.
*/
public CcLibraryHelper enableInterfaceSharedObjects() {
this.emitInterfaceSharedObjects = true;
return this;
}
/**
* This enables or disables the generation of a dynamic library link action. The default is to
* generate a dynamic library. Note that the selection between dynamic or static linking is
* performed at the binary rule level.
*/
public CcLibraryHelper setCreateDynamicLibrary(boolean emitDynamicLibrary) {
this.createDynamicLibrary = emitDynamicLibrary;
return this;
}
/** When createStaticLibraries is true, there are no actions created for static libraries. */
public CcLibraryHelper setCreateStaticLibraries(boolean emitStaticLibraries) {
this.createStaticLibraries = emitStaticLibraries;
return this;
}
public CcLibraryHelper setNeverlink(boolean neverlink) {
this.neverlink = neverlink;
return this;
}
/**
* Disables checking that the deps actually are C++ rules. By default, the {@link #compile} method
* uses {@link LanguageDependentFragment.Checker#depSupportsLanguage} to check that all deps
* provide C++ providers.
*/
public CcLibraryHelper setCheckDepsGenerateCpp(boolean checkDepsGenerateCpp) {
this.checkDepsGenerateCpp = checkDepsGenerateCpp;
return this;
}
/**
* Enables the output of the {@code files_to_compile} and {@code compilation_prerequisites}
* output groups.
*/
// TODO(bazel-team): We probably need to adjust this for the multi-language rules.
public CcLibraryHelper enableCompileProviders() {
this.emitCompileProviders = true;
return this;
}
/**
* Causes actions generated from this CcLibraryHelper not to use build semantics (includes,
* headers, srcs) from dependencies.
*/
public CcLibraryHelper doNotUseDeps() {
this.useDeps = false;
return this;
}
/** non-PIC actions won't be generated. */
public CcLibraryHelper setGenerateNoPic(boolean generateNoPic) {
this.generateNoPic = generateNoPic;
return this;
}
/** Adds mandatory inputs for the compilation action. */
public CcLibraryHelper addCompilationMandatoryInputs(
Collection<Artifact> compilationMandatoryInputs) {
this.compilationMandatoryInputs.addAll(compilationMandatoryInputs);
return this;
}
/** Adds additional includes to be scanned. */
// TODO(plf): This is only needed for CLIF. Investigate whether this is strictly necessary or
// there is a way to avoid include scanning for CLIF rules.
public CcLibraryHelper addAditionalIncludeScanningRoots(
Collection<Artifact> additionalIncludeScanningRoots) {
this.additionalIncludeScanningRoots.addAll(additionalIncludeScanningRoots);
return this;
}
/**
* Create the C++ compile actions, and the corresponding compilation related providers.
*
* @throws RuleErrorException
*/
public Info.CompilationInfo compile() throws RuleErrorException {
if (checkDepsGenerateCpp) {
for (LanguageDependentFragment dep :
AnalysisUtils.getProviders(deps, LanguageDependentFragment.class)) {
LanguageDependentFragment.Checker.depSupportsLanguage(
ruleContext, dep, CppRuleClasses.LANGUAGE, "deps");
}
}
CppModel model = initializeCppModel();
CppCompilationContext cppCompilationContext = initializeCppCompilationContext(model);
model.setContext(cppCompilationContext);
boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES);
Preconditions.checkState(
!compileHeaderModules || cppCompilationContext.getCppModuleMap() != null,
"All cc rules must support module maps.");
// Create compile actions (both PIC and non-PIC).
CcCompilationOutputs ccOutputs = model.createCcCompileActions();
if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) {
// Merge the pre-compiled object files into the compiler outputs.
ccOutputs =
new CcCompilationOutputs.Builder()
.merge(ccOutputs)
.addLtoBitcodeFile(ccOutputs.getLtoBitcodeFiles())
.addObjectFiles(objectFiles)
.addPicObjectFiles(picObjectFiles)
.build();
}
DwoArtifactsCollector dwoArtifacts =
DwoArtifactsCollector.transitiveCollector(
ccOutputs,
deps,
/*generateDwo=*/ false,
/*ltoBackendArtifactsUsePic=*/ false,
/*ltoBackendArtifacts=*/ ImmutableList.of());
// Be very careful when adding new providers here - it can potentially affect a lot of rules.
// We should consider merging most of these providers into a single provider.
TransitiveInfoProviderMapBuilder providers =
new TransitiveInfoProviderMapBuilder()
.add(
cppCompilationContext,
new CppDebugFileProvider(
dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts()),
collectTransitiveLipoInfo(ccOutputs));
Map<String, NestedSet<Artifact>> outputGroups = new TreeMap<>();
outputGroups.put(OutputGroupInfo.TEMP_FILES, getTemps(ccOutputs));
CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
if (emitCompileProviders) {
boolean isLipoCollector = cppConfiguration.isLipoContextCollector();
boolean processHeadersInDependencies = cppConfiguration.processHeadersInDependencies();
boolean usePic = CppHelper.usePic(ruleContext, ccToolchain, false);
outputGroups.put(
OutputGroupInfo.FILES_TO_COMPILE,
ccOutputs.getFilesToCompile(isLipoCollector, processHeadersInDependencies, usePic));
outputGroups.put(
OutputGroupInfo.COMPILATION_PREREQUISITES,
CcCommon.collectCompilationPrerequisites(ruleContext, cppCompilationContext));
}
return new Info.CompilationInfo(
providers.build(), outputGroups, ccOutputs, cppCompilationContext);
}
/**
* Create the C++ link actions, and the corresponding linking related providers.
*
* @throws RuleErrorException
*/
public Info.LinkingInfo link(
CcCompilationOutputs ccOutputs, CppCompilationContext cppCompilationContext)
throws RuleErrorException, InterruptedException {
Preconditions.checkNotNull(ccOutputs);
Preconditions.checkNotNull(cppCompilationContext);
if (checkDepsGenerateCpp) {
for (LanguageDependentFragment dep :
AnalysisUtils.getProviders(deps, LanguageDependentFragment.class)) {
LanguageDependentFragment.Checker.depSupportsLanguage(
ruleContext, dep, CppRuleClasses.LANGUAGE, "deps");
}
}
CppModel model = initializeCppModel();
model.setContext(cppCompilationContext);
// Create link actions (only if there are object files or if explicitly requested).
CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY;
if (emitLinkActions && (emitLinkActionsIfEmpty || !ccOutputs.isEmpty())) {
// On some systems, the linker gives an error message if there are no input files. Even with
// the check above, this can still happen if there is a .nopic.o or .o files in srcs, but no
// other files. To fix that, we'd have to check for each link action individually.
//
// An additional pre-existing issue is that the header check tokens are dropped if we don't
// generate any link actions, effectively disabling header checking in some cases.
if (linkType.staticness() == Staticness.STATIC) {
// TODO(bazel-team): This can't create the link action for a cc_binary yet.
ccLinkingOutputs = model.createCcLinkActions(ccOutputs, nonCodeLinkerInputs);
}
}
CcLinkingOutputs originalLinkingOutputs = ccLinkingOutputs;
if (!(staticLibraries.isEmpty()
&& picStaticLibraries.isEmpty()
&& dynamicLibraries.isEmpty()
&& executionDynamicLibraries.isEmpty())) {
CcLinkingOutputs.Builder newOutputsBuilder = new CcLinkingOutputs.Builder();
if (!ccOutputs.isEmpty()) {
// Add the linked outputs of this rule iff we had anything to put in them, but then
// make sure we're not colliding with some library added from the inputs.
newOutputsBuilder.merge(originalLinkingOutputs);
ImmutableSetMultimap<String, LibraryToLink> precompiledLibraryMap =
CcLinkingOutputs.getLibrariesByIdentifier(
Iterables.concat(
staticLibraries, picStaticLibraries,
dynamicLibraries, executionDynamicLibraries));
ImmutableSetMultimap<String, LibraryToLink> linkedLibraryMap =
originalLinkingOutputs.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");
}
}
// Merge the pre-compiled libraries (static & dynamic) into the linker outputs.
ccLinkingOutputs =
newOutputsBuilder
.addStaticLibraries(staticLibraries)
.addPicStaticLibraries(picStaticLibraries)
.addDynamicLibraries(dynamicLibraries)
.addExecutionDynamicLibraries(executionDynamicLibraries)
.build();
}
Runfiles cppStaticRunfiles = collectCppRunfiles(ccLinkingOutputs, true);
Runfiles cppSharedRunfiles = collectCppRunfiles(ccLinkingOutputs, false);
// By very careful when adding new providers here - it can potentially affect a lot of rules.
// We should consider merging most of these providers into a single provider.
TransitiveInfoProviderMapBuilder providers =
new TransitiveInfoProviderMapBuilder()
.add(new CppRunfilesProvider(cppStaticRunfiles, cppSharedRunfiles));
Map<String, NestedSet<Artifact>> outputGroups = new TreeMap<>();
if (shouldAddLinkerOutputArtifacts(ruleContext, ccOutputs)) {
addLinkerOutputArtifacts(outputGroups, ccOutputs);
}
// TODO(bazel-team): Maybe we can infer these from other data at the places where they are
// used.
if (emitCcNativeLibrariesProvider) {
providers.add(new CcNativeLibraryProvider(collectNativeCcLibraries(ccLinkingOutputs)));
}
providers.put(
CcExecutionDynamicLibrariesProvider.class,
collectExecutionDynamicLibraryArtifacts(ccLinkingOutputs.getExecutionDynamicLibraries()));
CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
boolean forcePic = cppConfiguration.forcePic();
if (emitCcSpecificLinkParamsProvider) {
providers.add(
new CcSpecificLinkParamsProvider(
createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic)));
} else {
providers.put(
new CcLinkParamsInfo(
createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic)));
}
return new Info.LinkingInfo(
providers.build(), outputGroups, ccLinkingOutputs, originalLinkingOutputs);
}
/**
* Returns true if the appropriate attributes for linker output artifacts are defined, and either
* the compile action produces object files or the build is configured to produce an archive or
* dynamic library even in the absence of object files.
*/
private boolean shouldAddLinkerOutputArtifacts(
RuleContext ruleContext, CcCompilationOutputs ccOutputs) {
return (ruleContext.attributes().has("alwayslink", Type.BOOLEAN)
&& ruleContext.attributes().has("linkstatic", Type.BOOLEAN)
&& (emitLinkActionsIfEmpty || !ccOutputs.isEmpty()));
}
/**
* Adds linker output artifacts to the given map, to be registered on the configured target as
* output groups.
*/
private void addLinkerOutputArtifacts(Map<String, NestedSet<Artifact>> outputGroups,
CcCompilationOutputs ccOutputs) {
NestedSetBuilder<Artifact> archiveFile = new NestedSetBuilder<>(Order.STABLE_ORDER);
NestedSetBuilder<Artifact> dynamicLibrary = new NestedSetBuilder<>(Order.STABLE_ORDER);
if (ruleContext.attributes().get("alwayslink", Type.BOOLEAN)) {
archiveFile.add(
CppHelper.getLinuxLinkedArtifact(
ruleContext,
configuration,
Link.LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY,
linkedArtifactNameSuffix));
} else {
archiveFile.add(
CppHelper.getLinuxLinkedArtifact(
ruleContext,
configuration,
Link.LinkTargetType.STATIC_LIBRARY,
linkedArtifactNameSuffix));
}
if (!ruleContext.attributes().get("linkstatic", Type.BOOLEAN)
&& !ccOutputs.isEmpty()) {
dynamicLibrary.add(
CppHelper.getLinuxLinkedArtifact(
ruleContext,
configuration,
Link.LinkTargetType.DYNAMIC_LIBRARY,
linkedArtifactNameSuffix));
if (CppHelper.useInterfaceSharedObjects(ccToolchain.getCppConfiguration(), ccToolchain)
&& emitInterfaceSharedObjects) {
dynamicLibrary.add(
CppHelper.getLinuxLinkedArtifact(
ruleContext,
configuration,
LinkTargetType.INTERFACE_DYNAMIC_LIBRARY,
linkedArtifactNameSuffix));
}
}
outputGroups.put(ARCHIVE_LIBRARY_OUTPUT_GROUP_NAME, archiveFile.build());
outputGroups.put(DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME, dynamicLibrary.build());
}
/**
* Creates the C/C++ compilation action creator.
*/
private CppModel initializeCppModel() {
return new CppModel(
ruleContext, semantics, ccToolchain, fdoSupport, configuration, copts, coptsFilter)
.addCompilationUnitSources(compilationUnitSources)
.setLinkTargetType(linkType)
.setNeverLink(neverlink)
.addLinkActionInputs(linkActionInputs)
.addCompilationMandatoryInputs(compilationMandatoryInputs)
.addAdditionalIncludeScanningRoots(additionalIncludeScanningRoots)
.setFake(fake)
.setGenerateNoPic(generateNoPic)
.setAllowInterfaceSharedObjects(emitInterfaceSharedObjects)
.setCreateDynamicLibrary(createDynamicLibrary)
.setCreateStaticLibraries(createStaticLibraries)
// Note: this doesn't actually save the temps, it just makes the CppModel use the
// configurations --save_temps setting to decide whether to actually save the temps.
.setSaveTemps(true)
.setDynamicLibrary(dynamicLibrary)
.addLinkopts(linkopts)
.setFeatureConfiguration(featureConfiguration)
.addVariablesExtension(variablesExtensions)
.setLinkedArtifactNameSuffix(linkedArtifactNameSuffix);
}
@Immutable
private static class PublicHeaders {
private final ImmutableList<Artifact> headers;
private final ImmutableList<Artifact> moduleMapHeaders;
private final @Nullable PathFragment virtualIncludePath;
private PublicHeaders(
ImmutableList<Artifact> headers,
ImmutableList<Artifact> moduleMapHeaders,
PathFragment virtualIncludePath) {
this.headers = headers;
this.moduleMapHeaders = moduleMapHeaders;
this.virtualIncludePath = virtualIncludePath;
}
public ImmutableList<Artifact> getHeaders() {
return headers;
}
public ImmutableList<Artifact> getModuleMapHeaders() {
return moduleMapHeaders;
}
@Nullable
public PathFragment getVirtualIncludePath() {
return virtualIncludePath;
}
}
private PublicHeaders computePublicHeaders() {
if (!ruleContext.attributes().has("strip_include_prefix", Type.STRING)
|| !ruleContext.attributes().has("include_prefix", Type.STRING)) {
return new PublicHeaders(
ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
ImmutableList.copyOf(publicHeaders),
null);
}
PathFragment prefix =
ruleContext.attributes().isAttributeValueExplicitlySpecified("include_prefix")
? PathFragment.create(ruleContext.attributes().get("include_prefix", Type.STRING))
: null;
PathFragment stripPrefix;
if (ruleContext.attributes().isAttributeValueExplicitlySpecified("strip_include_prefix")) {
stripPrefix =
PathFragment.create(ruleContext.attributes().get("strip_include_prefix", Type.STRING));
if (stripPrefix.isAbsolute()) {
stripPrefix = ruleContext.getLabel().getPackageIdentifier().getRepository().getSourceRoot()
.getRelative(stripPrefix.toRelative());
} else {
stripPrefix = ruleContext.getPackageDirectory().getRelative(stripPrefix);
}
} else if (prefix != null) {
stripPrefix = ruleContext.getPackageDirectory();
} else {
stripPrefix = null;
}
if (stripPrefix == null && prefix == null) {
// Simple case, no magic needed
return new PublicHeaders(
ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
ImmutableList.copyOf(publicHeaders),
null);
}
if (stripPrefix.containsUplevelReferences()) {
ruleContext.attributeError("strip_include_prefix",
"should not contain uplevel references");
}
if (prefix != null && prefix.containsUplevelReferences()) {
ruleContext.attributeError("include_prefix", "should not contain uplevel references");
}
if (prefix != null && prefix.isAbsolute()) {
ruleContext.attributeError("include_prefix", "should be a relative path");
}
if (ruleContext.hasErrors()) {
return new PublicHeaders(ImmutableList.<Artifact>of(), ImmutableList.<Artifact>of(), null);
}
ImmutableList.Builder<Artifact> moduleHeadersBuilder = ImmutableList.builder();
for (Artifact originalHeader : publicHeaders) {
if (!originalHeader.getRootRelativePath().startsWith(stripPrefix)) {
ruleContext.ruleError(String.format(
"header '%s' is not under the specified strip prefix '%s'",
originalHeader.getExecPathString(), stripPrefix.getPathString()));
continue;
}
PathFragment includePath = originalHeader.getRootRelativePath().relativeTo(stripPrefix);
if (prefix != null) {
includePath = prefix.getRelative(includePath);
}
if (!originalHeader.getExecPath().equals(includePath)) {
Artifact virtualHeader =
ruleContext.getUniqueDirectoryArtifact(
"_virtual_includes", includePath, ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(
new SymlinkAction(
ruleContext.getActionOwner(),
originalHeader,
virtualHeader,
"Symlinking virtual headers for " + ruleContext.getLabel()));
moduleHeadersBuilder.add(virtualHeader);
} else {
moduleHeadersBuilder.add(originalHeader);
}
}
ImmutableList<Artifact> moduleMapHeaders = moduleHeadersBuilder.build();
ImmutableList<Artifact> virtualHeaders =
ImmutableList.<Artifact>builder()
.addAll(moduleMapHeaders)
.addAll(nonModuleMapHeaders)
.build();
return new PublicHeaders(
virtualHeaders,
moduleMapHeaders,
ruleContext
.getBinOrGenfilesDirectory()
.getExecPath()
.getRelative(ruleContext.getUniqueDirectory("_virtual_includes")));
}
/** Creates context for cc compile action from generated inputs. */
public CppCompilationContext initializeCppCompilationContext() {
return initializeCppCompilationContext(initializeCppModel());
}
/**
* Create context for cc compile action from generated inputs.
*
* TODO(plf): Try to pull out CppCompilationContext building out of this class.
*/
private CppCompilationContext initializeCppCompilationContext(CppModel model) {
CppCompilationContext.Builder contextBuilder = new CppCompilationContext.Builder(ruleContext);
// Setup the include path; local include directories come before those inherited from deps or
// from the toolchain; in case of aliasing (same include file found on different entries),
// prefer the local include rather than the inherited one.
// Add in the roots for well-formed include names for source files and
// generated files. It is important that the execRoot (EMPTY_FRAGMENT) comes
// before the genfilesFragment to preferably pick up source files. Otherwise
// we might pick up stale generated files.
PathFragment repositoryPath =
ruleContext.getLabel().getPackageIdentifier().getRepository().getPathUnderExecRoot();
contextBuilder.addQuoteIncludeDir(repositoryPath);
contextBuilder.addQuoteIncludeDir(
ruleContext.getConfiguration().getGenfilesFragment().getRelative(repositoryPath));
for (PathFragment systemIncludeDir : systemIncludeDirs) {
contextBuilder.addSystemIncludeDir(systemIncludeDir);
}
for (PathFragment includeDir : includeDirs) {
contextBuilder.addIncludeDir(includeDir);
}
PublicHeaders publicHeaders = computePublicHeaders();
if (publicHeaders.getVirtualIncludePath() != null) {
contextBuilder.addIncludeDir(publicHeaders.getVirtualIncludePath());
}
if (useDeps) {
contextBuilder.mergeDependentContexts(
AnalysisUtils.getProviders(deps, CppCompilationContext.class));
contextBuilder.mergeDependentContexts(depContexts);
}
CppHelper.mergeToolchainDependentContext(ruleContext, ccToolchain, contextBuilder);
// But defines come after those inherited from deps.
contextBuilder.addDefines(defines);
// There are no ordering constraints for declared include dirs/srcs, or the pregrepped headers.
contextBuilder.addDeclaredIncludeSrcs(publicHeaders.getHeaders());
contextBuilder.addDeclaredIncludeSrcs(publicTextualHeaders);
contextBuilder.addDeclaredIncludeSrcs(privateHeaders);
contextBuilder.addDeclaredIncludeSrcs(additionalInputs);
contextBuilder.addNonCodeInputs(additionalInputs);
contextBuilder.addModularHdrs(publicHeaders.getHeaders());
contextBuilder.addModularHdrs(privateHeaders);
contextBuilder.addTextualHdrs(publicTextualHeaders);
contextBuilder.addPregreppedHeaderMap(
CppHelper.createExtractInclusions(ruleContext, semantics, publicHeaders.getHeaders()));
contextBuilder.addPregreppedHeaderMap(
CppHelper.createExtractInclusions(ruleContext, semantics, publicTextualHeaders));
contextBuilder.addPregreppedHeaderMap(
CppHelper.createExtractInclusions(ruleContext, semantics, privateHeaders));
// Add this package's dir to declaredIncludeDirs, & this rule's headers to declaredIncludeSrcs
// Note: no include dir for STRICT mode.
if (headersCheckingMode == HeadersCheckingMode.WARN) {
contextBuilder.addDeclaredIncludeWarnDir(ruleContext.getLabel().getPackageFragment());
for (PathFragment looseIncludeDir : looseIncludeDirs) {
contextBuilder.addDeclaredIncludeWarnDir(looseIncludeDir);
}
} else if (headersCheckingMode == HeadersCheckingMode.LOOSE) {
contextBuilder.addDeclaredIncludeDir(ruleContext.getLabel().getPackageFragment());
for (PathFragment looseIncludeDir : looseIncludeDirs) {
contextBuilder.addDeclaredIncludeDir(looseIncludeDir);
}
}
if (featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAPS)) {
if (cppModuleMap == null) {
cppModuleMap = CppHelper.createDefaultCppModuleMap(ruleContext, /*suffix=*/ "");
}
contextBuilder.setPropagateCppModuleMapAsActionInput(propagateModuleMapToCompileAction);
contextBuilder.setCppModuleMap(cppModuleMap);
// There are different modes for module compilation:
// 1. We create the module map and compile the module so that libraries depending on us can
// use the resulting module artifacts in their compilation (compiled is true).
// 2. We create the module map so that libraries depending on us will include the headers
// textually (compiled is false).
boolean compiled =
featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
|| featureConfiguration.isEnabled(CppRuleClasses.COMPILE_ALL_MODULES);
Iterable<CppModuleMap> dependentModuleMaps = collectModuleMaps();
if (generateModuleMap) {
Optional<Artifact> umbrellaHeader = cppModuleMap.getUmbrellaHeader();
if (umbrellaHeader.isPresent()) {
ruleContext.registerAction(
createUmbrellaHeaderAction(umbrellaHeader.get(), publicHeaders));
}
ruleContext.registerAction(
createModuleMapAction(cppModuleMap, publicHeaders, dependentModuleMaps, compiled));
}
if (model.getGeneratesPicHeaderModule()) {
contextBuilder.setPicHeaderModule(model.getPicHeaderModule(cppModuleMap.getArtifact()));
}
if (model.getGeneratesNoPicHeaderModule()) {
contextBuilder.setHeaderModule(model.getHeaderModule(cppModuleMap.getArtifact()));
}
if (!compiled
&& featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS)
&& featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)
&& ruleContext.getFragment(CppConfiguration.class).getParseHeadersVerifiesModules()) {
// Here, we are creating a compiled module to verify that headers are self-contained and
// modules ready, but we don't use the corresponding module map or compiled file anywhere
// else.
CppModuleMap verificationMap =
CppHelper.createDefaultCppModuleMap(ruleContext, /*suffix=*/ ".verify");
ruleContext.registerAction(
createModuleMapAction(
verificationMap, publicHeaders, dependentModuleMaps, /*compiledModule=*/ true));
contextBuilder.setVerificationModuleMap(verificationMap);
}
}
contextBuilder.setPurpose(purpose);
semantics.setupCompilationContext(ruleContext, contextBuilder);
return contextBuilder.build();
}
private UmbrellaHeaderAction createUmbrellaHeaderAction(Artifact umbrellaHeader,
PublicHeaders publicHeaders) {
return new UmbrellaHeaderAction(
ruleContext.getActionOwner(),
umbrellaHeader,
featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
: publicHeaders.getModuleMapHeaders(),
additionalExportedHeaders);
}
private CppModuleMapAction createModuleMapAction(
CppModuleMap moduleMap,
PublicHeaders publicHeaders,
Iterable<CppModuleMap> dependentModuleMaps,
boolean compiledModule) {
return new CppModuleMapAction(
ruleContext.getActionOwner(),
moduleMap,
featureConfiguration.isEnabled(CppRuleClasses.EXCLUDE_PRIVATE_HEADERS_IN_MODULE_MAPS)
? ImmutableList.<Artifact>of()
: privateHeaders,
featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
: publicHeaders.getModuleMapHeaders(),
dependentModuleMaps,
additionalExportedHeaders,
compiledModule,
featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_HOME_CWD),
featureConfiguration.isEnabled(CppRuleClasses.GENERATE_SUBMODULES),
!featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_WITHOUT_EXTERN_MODULE));
}
private Iterable<CppModuleMap> collectModuleMaps() {
// Cpp module maps may be null for some rules. We filter the nulls out at the end.
List<CppModuleMap> result =
deps.stream().map(CPP_DEPS_TO_MODULES).collect(toCollection(ArrayList::new));
if (ruleContext.getRule().getAttributeDefinition(":stl") != null) {
CppCompilationContext stl =
ruleContext.getPrerequisite(":stl", Mode.TARGET, CppCompilationContext.class);
if (stl != null) {
result.add(stl.getCppModuleMap());
}
}
if (ccToolchain != null) {
result.add(ccToolchain.getCppCompilationContext().getCppModuleMap());
}
for (CppModuleMap additionalCppModuleMap : additionalCppModuleMaps) {
result.add(additionalCppModuleMap);
}
return Iterables.filter(result, Predicates.<CppModuleMap>notNull());
}
static NestedSet<Artifact> collectHeaderTokens(
RuleContext ruleContext, CcCompilationOutputs ccCompilationOutputs) {
NestedSetBuilder<Artifact> headerTokens = NestedSetBuilder.stableOrder();
for (OutputGroupInfo dep :
ruleContext.getPrerequisites(
"deps", Mode.TARGET, OutputGroupInfo.SKYLARK_CONSTRUCTOR)) {
headerTokens.addTransitive(dep.getOutputGroup(CcLibraryHelper.HIDDEN_HEADER_TOKENS));
}
if (ruleContext.getFragment(CppConfiguration.class).processHeadersInDependencies()) {
headerTokens.addAll(ccCompilationOutputs.getHeaderTokenFiles());
}
return headerTokens.build();
}
private TransitiveLipoInfoProvider collectTransitiveLipoInfo(CcCompilationOutputs outputs) {
if (fdoSupport.getFdoSupport().getFdoRoot() == null) {
return TransitiveLipoInfoProvider.EMPTY;
}
NestedSetBuilder<IncludeScannable> scannableBuilder = NestedSetBuilder.stableOrder();
// TODO(bazel-team): Only fetch the STL prerequisite in one place.
TransitiveInfoCollection stl = ruleContext.getPrerequisite(":stl", Mode.TARGET);
if (stl != null) {
TransitiveLipoInfoProvider provider = stl.getProvider(TransitiveLipoInfoProvider.class);
if (provider != null) {
scannableBuilder.addTransitive(provider.getTransitiveIncludeScannables());
}
}
for (TransitiveLipoInfoProvider dep :
AnalysisUtils.getProviders(deps, TransitiveLipoInfoProvider.class)) {
scannableBuilder.addTransitive(dep.getTransitiveIncludeScannables());
}
for (IncludeScannable scannable : outputs.getLipoScannables()) {
Preconditions.checkState(scannable.getIncludeScannerSources().size() == 1);
scannableBuilder.add(scannable);
}
return new TransitiveLipoInfoProvider(scannableBuilder.build());
}
private Runfiles collectCppRunfiles(
CcLinkingOutputs ccLinkingOutputs, boolean linkingStatically) {
Runfiles.Builder builder = new Runfiles.Builder(
ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles());
builder.addTargets(deps, RunfilesProvider.DEFAULT_RUNFILES);
builder.addTargets(deps, CppRunfilesProvider.runfilesFunction(linkingStatically));
// Add the shared libraries to the runfiles.
builder.addArtifacts(ccLinkingOutputs.getLibrariesForRunfiles(linkingStatically));
return builder.build();
}
private CcLinkParamsStore createCcLinkParamsStore(
final CcLinkingOutputs ccLinkingOutputs, final CppCompilationContext cppCompilationContext,
final boolean forcePic) {
return new CcLinkParamsStore() {
@Override
protected void collect(
CcLinkParams.Builder builder, boolean linkingStatically, boolean linkShared) {
builder.addLinkstamps(linkstamps.build(), cppCompilationContext);
builder.addTransitiveTargets(
deps, CcLinkParamsInfo.TO_LINK_PARAMS, CcSpecificLinkParamsProvider.TO_LINK_PARAMS);
if (!neverlink) {
builder.addLibraries(
ccLinkingOutputs.getPreferredLibraries(
linkingStatically, /*preferPic=*/ linkShared || forcePic));
if (!linkingStatically
|| (ccLinkingOutputs.getStaticLibraries().isEmpty()
&& ccLinkingOutputs.getPicStaticLibraries().isEmpty())) {
builder.addExecutionDynamicLibraries(
LinkerInputs.toLibraryArtifacts(ccLinkingOutputs.getExecutionDynamicLibraries()));
}
builder.addLinkOpts(linkopts);
builder.addNonCodeInputs(nonCodeLinkerInputs);
}
}
};
}
private NestedSet<LinkerInput> collectNativeCcLibraries(CcLinkingOutputs ccLinkingOutputs) {
NestedSetBuilder<LinkerInput> result = NestedSetBuilder.linkOrder();
result.addAll(ccLinkingOutputs.getDynamicLibraries());
for (CcNativeLibraryProvider dep :
AnalysisUtils.getProviders(deps, CcNativeLibraryProvider.class)) {
result.addTransitive(dep.getTransitiveCcNativeLibraries());
}
return result.build();
}
private CcExecutionDynamicLibrariesProvider collectExecutionDynamicLibraryArtifacts(
List<LibraryToLink> executionDynamicLibraries) {
Iterable<Artifact> artifacts = LinkerInputs.toLibraryArtifacts(executionDynamicLibraries);
if (!Iterables.isEmpty(artifacts)) {
return new CcExecutionDynamicLibrariesProvider(
NestedSetBuilder.wrap(Order.STABLE_ORDER, artifacts));
}
NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
for (CcExecutionDynamicLibrariesProvider dep :
AnalysisUtils.getProviders(deps, CcExecutionDynamicLibrariesProvider.class)) {
builder.addTransitive(dep.getExecutionDynamicLibraryArtifacts());
}
return builder.isEmpty()
? CcExecutionDynamicLibrariesProvider.EMPTY
: new CcExecutionDynamicLibrariesProvider(builder.build());
}
private NestedSet<Artifact> getTemps(CcCompilationOutputs compilationOutputs) {
return ruleContext.getFragment(CppConfiguration.class).isLipoContextCollector()
? NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)
: compilationOutputs.getTemps();
}
public void registerAdditionalModuleMap(CppModuleMap cppModuleMap) {
this.additionalCppModuleMaps.add(Preconditions.checkNotNull(cppModuleMap));
}
/**
* Don't generate a module map for this target if a custom module map is provided.
*/
public CcLibraryHelper doNotGenerateModuleMap() {
generateModuleMap = false;
return this;
}
/**
* Sets the purpose for the context.
*
* @see CppCompilationContext.Builder#setPurpose
* @param purpose must be a string which is suitable for use as a filename. A single rule may have
* many middlemen with distinct purposes.
*/
public CcLibraryHelper setPurpose(@Nullable String purpose) {
this.purpose = purpose;
return this;
}
}