C++: Split C++ library into two classes, compilation and linking
These will be separate calls in the Skylark API.
RELNOTES:none
PiperOrigin-RevId: 184961734
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index d72d44d..61f6c2a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -48,7 +48,7 @@
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.rules.apple.ApplePlatform;
import com.google.devtools.build.lib.rules.cpp.CcCommon.CcFlagsSupplier;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+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.CppConfiguration.DynamicMode;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
@@ -212,14 +212,15 @@
return null;
}
- CcLibraryHelper compilationHelper =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CcCompilationHelper compilationHelper =
+ new CcCompilationHelper(
+ ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.fromCommon(common)
.addSources(common.getSources())
.addDeps(ImmutableList.of(CppHelper.mallocForTarget(ruleContext)))
.setFake(fake)
.addPrecompiledFiles(precompiledFiles);
- Info.CompilationInfo compilationInfo = compilationHelper.compile();
+ CompilationInfo compilationInfo = compilationHelper.compile();
CppCompilationContext cppCompilationContext = compilationInfo.getCppCompilationContext();
CcCompilationOutputs ccCompilationOutputs = compilationInfo.getCcCompilationOutputs();
@@ -232,11 +233,11 @@
&& cppConfiguration.getLinkCompileOutputSeparately()
&& linkStaticness == LinkStaticness.DYNAMIC;
// When linking the object files directly into the resulting binary, we do not need
- // library-level link outputs; thus, we do not let CcLibraryHelper produce link outputs
+ // library-level link outputs; thus, we do not let CcCompilationHelper produce link outputs
// (either shared object files or archives) for a non-library link type [*], and add
// the object files explicitly in determineLinkerArguments.
//
- // When linking the object files into their own library, we want CcLibraryHelper to
+ // When linking the object files into their own library, we want CcCompilationHelper to
// take care of creating the library link outputs for us, so we need to set the link
// type to STATIC_LIBRARY.
//
@@ -245,8 +246,14 @@
// output matching a shared object, for example cc_binary(name="foo.so", ...) on linux.
CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY;
if (linkCompileOutputSeparately) {
- CcLibraryHelper linkingHelper =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CcLinkingHelper linkingHelper =
+ new CcLinkingHelper(
+ ruleContext,
+ semantics,
+ featureConfiguration,
+ ccToolchain,
+ fdoSupport,
+ ruleContext.getConfiguration())
.fromCommon(common)
.addDeps(ImmutableList.of(CppHelper.mallocForTarget(ruleContext)))
.setFake(fake)
@@ -868,7 +875,7 @@
instrumentedObjectFiles, !TargetUtils.isTestRule(ruleContext.getRule()) && !fake);
NestedSet<Artifact> headerTokens =
- CcLibraryHelper.collectHeaderTokens(ruleContext, ccCompilationOutputs);
+ CcCompilationHelper.collectHeaderTokens(ruleContext, ccCompilationOutputs);
NestedSet<Artifact> filesToCompile =
ccCompilationOutputs.getFilesToCompile(
cppConfiguration.isLipoContextCollector(),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
index 07e2ca9..8d599be 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
@@ -43,7 +43,7 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.rules.apple.ApplePlatform;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.SourceCategory;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.SourceCategory;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.CollidingProvidesException;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables;
@@ -60,6 +60,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.annotation.Nullable;
@@ -149,6 +150,31 @@
}
/**
+ * 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(
+ ImmutableList<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;
+ }
+
+ /**
* Returns our own linkopts from the rule attribute. This determines linker
* options to use when building this target and anything that depends on it.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
new file mode 100644
index 0000000..d6464d1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
@@ -0,0 +1,1112 @@
+// 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.toCollection;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.AnalysisUtils;
+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.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.CcCommon.CoptsFilter;
+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.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 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.
+ */
+public final class CcCompilationHelper {
+ /** 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 group of source file types and action names for builds controlled by CcCompilationHelper.
+ * Determines what file types CcCompilationHelper 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. */
+ private 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 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;
+ }
+ }
+
+ 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 ImmutableList<String> copts = ImmutableList.of();
+ private CoptsFilter coptsFilter = CoptsFilter.alwaysPasses();
+ private final Set<String> defines = new LinkedHashSet<>();
+ private final List<TransitiveInfoCollection> deps = new ArrayList<>();
+ private final List<CppCompilationContext> depContexts = new ArrayList<>();
+ private final List<PathFragment> looseIncludeDirs = new ArrayList<>();
+ private final List<PathFragment> systemIncludeDirs = new ArrayList<>();
+ private final List<PathFragment> includeDirs = new ArrayList<>();
+
+ private HeadersCheckingMode headersCheckingMode = HeadersCheckingMode.LOOSE;
+ private boolean fake;
+
+ private boolean checkDepsGenerateCpp = true;
+ private boolean emitCompileProviders;
+ private final SourceCategory sourceCategory;
+ private final List<VariablesExtension> variablesExtensions = new ArrayList<>();
+ @Nullable private CppModuleMap cppModuleMap;
+ private boolean propagateModuleMapToCompileAction = true;
+
+ private final FeatureConfiguration featureConfiguration;
+ private final CcToolchainProvider ccToolchain;
+ private final FdoSupportProvider fdoSupport;
+ private boolean useDeps = true;
+ private boolean generateModuleMap = true;
+ private String purpose = null;
+ private boolean generateNoPic = true;
+
+ /**
+ * Creates a CcCompilationHelper.
+ *
+ * @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 CcCompilationHelper(
+ RuleContext ruleContext,
+ CppSemantics semantics,
+ FeatureConfiguration featureConfiguration,
+ SourceCategory sourceCatagory,
+ CcToolchainProvider ccToolchain,
+ FdoSupportProvider fdoSupport) {
+ this(
+ ruleContext,
+ semantics,
+ featureConfiguration,
+ sourceCatagory,
+ ccToolchain,
+ fdoSupport,
+ ruleContext.getConfiguration());
+ }
+
+ /**
+ * Creates a CcCompilationHelper 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 CcCompilationHelper(
+ 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 CcCompilationHelper 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 CcCompilationHelper(
+ 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 CcCompilationHelper fromCommon(CcCommon common) {
+ setCopts(common.getCopts());
+ addDefines(common.getDefines());
+ addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET));
+ addLooseIncludeDirs(common.getLooseIncludeDirs());
+ 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper 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 CcCompilationHelper addSources(Artifact... sources) {
+ return addSources(Arrays.asList(sources));
+ }
+
+ /** Add the corresponding files as non-header, non-source input files. */
+ public CcCompilationHelper 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 CcCompilationHelper 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 void 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;
+ }
+ 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));
+ }
+
+ 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 CcCompilationHelper 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 CcCompilationHelper addPicObjectFiles(Iterable<Artifact> picObjectFiles) {
+ for (Artifact objectFile : objectFiles) {
+ Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
+ }
+ Iterables.addAll(this.picObjectFiles, picObjectFiles);
+ return this;
+ }
+
+ public CcCompilationHelper 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. */
+ private void setCoptsFilter(CoptsFilter coptsFilter) {
+ this.coptsFilter = Preconditions.checkNotNull(coptsFilter);
+ }
+
+ /** Adds the given defines to the compiler command line. */
+ public CcCompilationHelper 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 CcCompilationHelper addDeps(Iterable<? extends TransitiveInfoCollection> deps) {
+ for (TransitiveInfoCollection dep : deps) {
+ this.deps.add(dep);
+ }
+ return this;
+ }
+
+ public CcCompilationHelper addDepContext(CppCompilationContext dep) {
+ this.depContexts.add(Preconditions.checkNotNull(dep));
+ 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 CcCompilationHelper 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}.
+ */
+ private void addLooseIncludeDirs(Iterable<PathFragment> looseIncludeDirs) {
+ Iterables.addAll(this.looseIncludeDirs, looseIncludeDirs);
+ }
+
+ /**
+ * 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 CcCompilationHelper 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 CcCompilationHelper addIncludeDirs(Iterable<PathFragment> includeDirs) {
+ Iterables.addAll(this.includeDirs, includeDirs);
+ return this;
+ }
+
+ /** Adds a variableExtension to template the crosstool. */
+ public CcCompilationHelper addVariableExtension(VariablesExtension variableExtension) {
+ Preconditions.checkNotNull(variableExtension);
+ this.variablesExtensions.add(variableExtension);
+ return this;
+ }
+
+ /** Sets a module map artifact for this build. */
+ public CcCompilationHelper 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 CcCompilationHelper setPropagateModuleMapToCompileAction(boolean propagatesModuleMap) {
+ this.propagateModuleMapToCompileAction = propagatesModuleMap;
+ return this;
+ }
+
+ /** Sets the given headers checking mode. The default is {@link HeadersCheckingMode#LOOSE}. */
+ public CcCompilationHelper 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 CcCompilationHelper setFake(boolean fake) {
+ this.fake = fake;
+ 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 CcCompilationHelper 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 CcCompilationHelper enableCompileProviders() {
+ this.emitCompileProviders = true;
+ return this;
+ }
+
+ /**
+ * Causes actions generated from this CcCompilationHelper not to use build semantics (includes,
+ * headers, srcs) from dependencies.
+ */
+ public CcCompilationHelper doNotUseDeps() {
+ this.useDeps = false;
+ return this;
+ }
+
+ /** non-PIC actions won't be generated. */
+ public CcCompilationHelper setGenerateNoPic(boolean generateNoPic) {
+ this.generateNoPic = generateNoPic;
+ return this;
+ }
+
+ /** Adds mandatory inputs for the compilation action. */
+ public CcCompilationHelper 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 CcCompilationHelper addAditionalIncludeScanningRoots(
+ Collection<Artifact> additionalIncludeScanningRoots) {
+ this.additionalIncludeScanningRoots.addAll(additionalIncludeScanningRoots);
+ return this;
+ }
+
+ /**
+ * Create the C++ compile actions, and the corresponding compilation related providers.
+ *
+ * @throws RuleErrorException
+ */
+ public 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 CompilationInfo(providers.build(), outputGroups, ccOutputs, cppCompilationContext);
+ }
+
+ /** Creates the C/C++ compilation action creator. */
+ private CppModel initializeCppModel() {
+ return new CppModel(
+ ruleContext, semantics, ccToolchain, fdoSupport, configuration, copts, coptsFilter)
+ .addCompilationUnitSources(compilationUnitSources)
+ .addCompilationMandatoryInputs(compilationMandatoryInputs)
+ .addAdditionalIncludeScanningRoots(additionalIncludeScanningRoots)
+ .setFake(fake)
+ .setGenerateNoPic(generateNoPic)
+ // 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)
+ .setFeatureConfiguration(featureConfiguration)
+ .addVariablesExtension(variablesExtensions);
+ }
+
+ @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;
+ }
+
+ private ImmutableList<Artifact> getHeaders() {
+ return headers;
+ }
+
+ private ImmutableList<Artifact> getModuleMapHeaders() {
+ return moduleMapHeaders;
+ }
+
+ @Nullable
+ private 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.
+ *
+ * <p>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.addPregreppedHeaders(
+ CppHelper.createExtractInclusions(ruleContext, semantics, publicHeaders.getHeaders()));
+ contextBuilder.addPregreppedHeaders(
+ CppHelper.createExtractInclusions(ruleContext, semantics, publicTextualHeaders));
+ contextBuilder.addPregreppedHeaders(
+ 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(CcCompilationHelper.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 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 CcCompilationHelper 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 CcCompilationHelper setPurpose(@Nullable String purpose) {
+ this.purpose = purpose;
+ return this;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
index f5e9f9d..b70ace4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcImport.java
@@ -23,7 +23,8 @@
import com.google.devtools.build.lib.analysis.Runfiles;
import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.HeadersCheckingMode;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
@@ -62,7 +63,7 @@
"'shared_library' should be specified when 'system_provided' is false");
}
- // Create CcLibraryHelper
+ // Create CcCompilationHelper
CcToolchainProvider ccToolchain =
CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
FeatureConfiguration featureConfiguration =
@@ -72,8 +73,9 @@
// Add headers to compilation step.
final CcCommon common = new CcCommon(ruleContext);
- Info.CompilationInfo compilationInfo =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CompilationInfo compilationInfo =
+ new CcCompilationHelper(
+ ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.addPublicHeaders(common.getHeaders())
.setHeadersCheckingMode(HeadersCheckingMode.STRICT)
.compile();
@@ -90,8 +92,14 @@
.getRelative(labelName.replaceName("lib" + labelName.getBaseName()))
.getPathString();
- CcLibraryHelper linkingHelper =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport);
+ CcLinkingHelper linkingHelper =
+ new CcLinkingHelper(
+ ruleContext,
+ semantics,
+ featureConfiguration,
+ ccToolchain,
+ fdoSupport,
+ ruleContext.getConfiguration());
if (staticLibrary != null) {
if (CppFileTypes.PIC_ARCHIVE.matches(staticLibrary.getPath())) {
@@ -159,7 +167,7 @@
linkingHelper.addDynamicLibraries(dynamicLibraryList);
}
- Info.LinkingInfo linkingInfo =
+ LinkingInfo linkingInfo =
linkingHelper.link(
compilationInfo.getCcCompilationOutputs(), compilationInfo.getCppCompilationContext());
@@ -168,8 +176,8 @@
.addProviders(linkingInfo.getProviders())
.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
.addOutputGroups(
- Info.mergeOutputGroups(
- compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups()))
+ CcCommon.mergeOutputGroups(
+ ImmutableList.of(compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups())))
.addProvider(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY))
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java
index ad05f41..a844ef6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java
@@ -26,7 +26,8 @@
import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.Path;
@@ -130,14 +131,21 @@
new CreateIncSymlinkAction(ruleContext.getActionOwner(), virtualArtifactMap, includeRoot));
FdoSupportProvider fdoSupport =
CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext);
- Info.CompilationInfo compilationInfo =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CompilationInfo compilationInfo =
+ new CcCompilationHelper(
+ ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.addIncludeDirs(Arrays.asList(includePath))
.addPublicHeaders(virtualArtifactMap.keySet())
.addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET))
.compile();
- Info.LinkingInfo linkingInfo =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ LinkingInfo linkingInfo =
+ new CcLinkingHelper(
+ ruleContext,
+ semantics,
+ featureConfiguration,
+ ccToolchain,
+ fdoSupport,
+ ruleContext.getConfiguration())
.addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET))
.link(
compilationInfo.getCcCompilationOutputs(),
@@ -154,8 +162,8 @@
.addProviders(linkingInfo.getProviders())
.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
.addOutputGroups(
- Info.mergeOutputGroups(
- compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups()))
+ CcCommon.mergeOutputGroups(
+ ImmutableList.of(compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups())))
.add(InstrumentedFilesProvider.class, instrumentedFilesProvider)
.add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY))
.build();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
index 963414a..d07dd05 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
@@ -37,7 +37,8 @@
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.RawAttributeMapper;
import com.google.devtools.build.lib.rules.cpp.CcCommon.CcFlagsSupplier;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
@@ -132,16 +133,23 @@
return;
}
- CcLibraryHelper compilationHelper =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CcCompilationHelper compilationHelper =
+ new CcCompilationHelper(
+ ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
.fromCommon(common)
.addSources(common.getSources())
.addPublicHeaders(common.getHeaders())
.enableCompileProviders()
.addPrecompiledFiles(precompiledFiles);
- CcLibraryHelper linkingHelper =
- new CcLibraryHelper(ruleContext, semantics, featureConfiguration, ccToolchain, fdoSupport)
+ CcLinkingHelper linkingHelper =
+ new CcLinkingHelper(
+ ruleContext,
+ semantics,
+ featureConfiguration,
+ ccToolchain,
+ fdoSupport,
+ ruleContext.getConfiguration())
.fromCommon(common)
.addLinkopts(common.getLinkopts())
.enableCcNativeLibrariesProvider()
@@ -272,8 +280,8 @@
linkingHelper.addDynamicLibraries(dynamicLibraries);
linkingHelper.addExecutionDynamicLibraries(dynamicLibraries);
- Info.CompilationInfo compilationInfo = compilationHelper.compile();
- Info.LinkingInfo linkingInfo =
+ CompilationInfo compilationInfo = compilationHelper.compile();
+ LinkingInfo linkingInfo =
linkingHelper.link(
compilationInfo.getCcCompilationOutputs(), compilationInfo.getCppCompilationContext());
@@ -322,8 +330,8 @@
.addProviders(linkingInfo.getProviders())
.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
.addOutputGroups(
- Info.mergeOutputGroups(
- compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups()))
+ CcCommon.mergeOutputGroups(
+ ImmutableList.of(compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups())))
.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider)
.addProvider(
RunfilesProvider.class, RunfilesProvider.withData(staticRunfiles, sharedRunfiles))
@@ -335,8 +343,8 @@
collectHiddenTopLevelArtifacts(
ruleContext, ccToolchain, compilationInfo.getCcCompilationOutputs()))
.addOutputGroup(
- CcLibraryHelper.HIDDEN_HEADER_TOKENS,
- CcLibraryHelper.collectHeaderTokens(
+ CcCompilationHelper.HIDDEN_HEADER_TOKENS,
+ CcCompilationHelper.collectHeaderTokens(
ruleContext, compilationInfo.getCcCompilationOutputs()));
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
deleted file mode 100644
index e898aa4..0000000
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
+++ /dev/null
@@ -1,1762 +0,0 @@
-// 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.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.CcCommon.CoptsFilter;
-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 CoptsFilter coptsFilter = CoptsFilter.alwaysPasses();
- 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(CoptsFilter 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.addPregreppedHeaders(
- CppHelper.createExtractInclusions(ruleContext, semantics, publicHeaders.getHeaders()));
- contextBuilder.addPregreppedHeaders(
- CppHelper.createExtractInclusions(ruleContext, semantics, publicTextualHeaders));
- contextBuilder.addPregreppedHeaders(
- 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;
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
new file mode 100644
index 0000000..77dc80f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
@@ -0,0 +1,706 @@
+// 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 com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+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.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.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
+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.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter;
+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.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 java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.annotation.Nullable;
+
+/**
+ * A class to create C/C++ 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.
+ */
+public final class CcLinkingHelper {
+
+ /** 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";
+
+ /** 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);
+ }
+ }
+
+ private final RuleContext ruleContext;
+ private final CppSemantics semantics;
+ private final BuildConfiguration configuration;
+
+ private final List<Artifact> nonCodeLinkerInputs = new ArrayList<>();
+ private final List<String> linkopts = new ArrayList<>();
+ private final List<TransitiveInfoCollection> deps = new ArrayList<>();
+ private final NestedSetBuilder<Artifact> linkstamps = NestedSetBuilder.stableOrder();
+ private final List<Artifact> linkActionInputs = new ArrayList<>();
+
+ @Nullable private Artifact dynamicLibrary;
+ private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
+ 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 checkDepsGenerateCpp = true;
+ private boolean emitLinkActionsIfEmpty;
+ private boolean emitCcNativeLibrariesProvider;
+ private boolean emitCcSpecificLinkParamsProvider;
+ private boolean emitInterfaceSharedObjects;
+ private boolean createDynamicLibrary = true;
+ private boolean createStaticLibraries = true;
+ private final List<VariablesExtension> variablesExtensions = new ArrayList<>();
+
+ private final FeatureConfiguration featureConfiguration;
+ private final CcToolchainProvider ccToolchain;
+ private final FdoSupportProvider fdoSupport;
+ private String linkedArtifactNameSuffix = "";
+
+ /**
+ * Creates a CcLinkingHelper 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 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 CcLinkingHelper(
+ RuleContext ruleContext,
+ CppSemantics semantics,
+ FeatureConfiguration featureConfiguration,
+ CcToolchainProvider ccToolchain,
+ FdoSupportProvider fdoSupport,
+ BuildConfiguration configuration) {
+ this.ruleContext = Preconditions.checkNotNull(ruleContext);
+ this.semantics = Preconditions.checkNotNull(semantics);
+ this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration);
+ this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
+ this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
+ this.configuration = Preconditions.checkNotNull(configuration);
+ }
+
+ /** Sets fields that overlap for cc_library and cc_binary rules. */
+ public CcLinkingHelper fromCommon(CcCommon common) {
+ addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET));
+ addNonCodeLinkerInputs(common.getLinkerScripts());
+ return this;
+ }
+
+ /** Adds the corresponding non-code files as linker inputs. */
+ public void 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);
+ }
+ }
+
+ /**
+ * 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 CcLinkingHelper 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 CcLinkingHelper 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 CcLinkingHelper addDynamicLibraries(Iterable<LibraryToLink> libraries) {
+ Iterables.addAll(dynamicLibraries, libraries);
+ return this;
+ }
+
+ /** Add the corresponding files as dynamic libraries required at runtime */
+ public CcLinkingHelper addExecutionDynamicLibraries(Iterable<LibraryToLink> libraries) {
+ Iterables.addAll(executionDynamicLibraries, libraries);
+ return this;
+ }
+
+ /** Adds the given options as linker options to the link command. */
+ public CcLinkingHelper addLinkopts(Iterable<String> linkopts) {
+ Iterables.addAll(this.linkopts, linkopts);
+ 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 CcLinkingHelper addDeps(Iterable<? extends TransitiveInfoCollection> deps) {
+ for (TransitiveInfoCollection dep : deps) {
+ this.deps.add(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 CcLinkingHelper addLinkstamps(Iterable<? extends TransitiveInfoCollection> linkstamps) {
+ for (TransitiveInfoCollection linkstamp : linkstamps) {
+ this.linkstamps.addTransitive(linkstamp.getProvider(FileProvider.class).getFilesToBuild());
+ }
+ return this;
+ }
+
+ /** Adds the given artifact to the input of any generated link actions. */
+ public CcLinkingHelper addLinkActionInput(Artifact input) {
+ Preconditions.checkNotNull(input);
+ this.linkActionInputs.add(input);
+ return this;
+ }
+
+ /** Adds a variableExtension to template the crosstool. */
+ public CcLinkingHelper addVariableExtension(VariablesExtension variableExtension) {
+ Preconditions.checkNotNull(variableExtension);
+ this.variablesExtensions.add(variableExtension);
+ 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 CcLinkingHelper 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 CcLinkingHelper 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 CcLinkingHelper 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 CcLinkingHelper setNeverLink(boolean neverlink) {
+ this.neverlink = neverlink;
+ return this;
+ }
+
+ /**
+ * Disables checking that the deps actually are C++ rules. By default, the {@link #link} method
+ * uses {@link LanguageDependentFragment.Checker#depSupportsLanguage} to check that all deps
+ * provide C++ providers.
+ */
+ public CcLinkingHelper setCheckDepsGenerateCpp(boolean checkDepsGenerateCpp) {
+ this.checkDepsGenerateCpp = checkDepsGenerateCpp;
+ 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 CcLinkingHelper 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 CcLinkingHelper setLinkedArtifactNameSuffix(String suffix) {
+ this.linkedArtifactNameSuffix = Preconditions.checkNotNull(suffix);
+ return this;
+ }
+
+ /** This adds the {@link CcNativeLibraryProvider} to the providers created by this class. */
+ public CcLinkingHelper 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 CcLinkingHelper enableCcSpecificLinkParamsProvider() {
+ this.emitCcSpecificLinkParamsProvider = true;
+ 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 CcLinkingHelper 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 CcLinkingHelper 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 CcLinkingHelper setCreateDynamicLibrary(boolean emitDynamicLibrary) {
+ this.createDynamicLibrary = emitDynamicLibrary;
+ return this;
+ }
+
+ /** When createStaticLibraries is true, there are no actions created for static libraries. */
+ public CcLinkingHelper setCreateStaticLibraries(boolean emitStaticLibraries) {
+ this.createStaticLibraries = emitStaticLibraries;
+ return this;
+ }
+
+ public CcLinkingHelper setNeverlink(boolean neverlink) {
+ this.neverlink = neverlink;
+ return this;
+ }
+
+ /**
+ * Create the C++ link actions, and the corresponding linking related providers.
+ *
+ * @throws RuleErrorException
+ */
+ public 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 (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 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() {
+ // TODO(plf): Split CppModel into compilation and linking and stop passing last two arguments.
+ return new CppModel(
+ ruleContext,
+ semantics,
+ ccToolchain,
+ fdoSupport,
+ configuration,
+ ImmutableList.of(),
+ CoptsFilter.alwaysPasses())
+ .setLinkTargetType(linkType)
+ .setNeverLink(neverlink)
+ .addLinkActionInputs(linkActionInputs)
+ .setFake(fake)
+ .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);
+ }
+
+ 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());
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
index d3037ff..247d3ef 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
@@ -261,7 +261,7 @@
} else if (CppFileTypes.CPP_MODULE.matches(sourcePath)) {
return CppCompileAction.CPP_MODULE_CODEGEN;
}
- // CcLibraryHelper ensures CppCompileAction only gets instantiated for supported file types.
+ // CcCompilationHelper ensures CppCompileAction only gets instantiated for supported file types.
throw new IllegalStateException();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java
index a3cc7bf..2f4e780 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java
@@ -28,7 +28,6 @@
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
-import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
import com.google.devtools.build.lib.cmdline.Label;
@@ -41,8 +40,10 @@
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.AspectLegalCppSemantics;
import com.google.devtools.build.lib.rules.cpp.CcCommon;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchain;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
@@ -114,9 +115,7 @@
.requireProviders(ProtoSupportDataProvider.class)
.add(
attr(PROTO_TOOLCHAIN_ATTR, LABEL)
- .mandatoryNativeProviders(
- ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
- ProtoLangToolchainProvider.class))
+ .mandatoryNativeProviders(ImmutableList.of(ProtoLangToolchainProvider.class))
.value(PROTO_TOOLCHAIN_LABEL))
.add(
attr(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME, LABEL)
@@ -149,7 +148,7 @@
FeatureConfiguration featureConfiguration = getFeatureConfiguration(supportData);
ProtoConfiguration protoConfiguration = ruleContext.getFragment(ProtoConfiguration.class);
- CcLibraryHelper compilationHelper = initializeCompilationHelper(featureConfiguration);
+ CcCompilationHelper compilationHelper = initializeCompilationHelper(featureConfiguration);
// Compute and register files generated by this proto library.
Collection<Artifact> outputs = new ArrayList<>();
@@ -190,8 +189,8 @@
filesBuilder.addAll(outputs);
createProtoCompileAction(supportData, outputs);
- Info.CompilationInfo compilationInfo = compilationHelper.compile();
- Info.LinkingInfo linkingInfo =
+ CompilationInfo compilationInfo = compilationHelper.compile();
+ LinkingInfo linkingInfo =
initializeLinkingHelper(featureConfiguration)
.link(
compilationInfo.getCcCompilationOutputs(),
@@ -204,8 +203,9 @@
.build();
outputGroups =
ImmutableMap.copyOf(
- Info.mergeOutputGroups(
- compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups()));
+ CcCommon.mergeOutputGroups(
+ ImmutableList.of(
+ compilationInfo.getOutputGroups(), linkingInfo.getOutputGroups())));
// On Windows, dynamic library is not built by default, so don't add them to filesToBuild.
linkingInfo.addLinkingOutputsTo(
filesBuilder, !featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS));
@@ -236,9 +236,10 @@
return featureConfiguration;
}
- private CcLibraryHelper initializeCcLibraryHelper(FeatureConfiguration featureConfiguration) {
- CcLibraryHelper helper =
- new CcLibraryHelper(
+ private CcCompilationHelper initializeCompilationHelper(
+ FeatureConfiguration featureConfiguration) {
+ CcCompilationHelper helper =
+ new CcCompilationHelper(
ruleContext,
cppSemantics,
featureConfiguration,
@@ -253,15 +254,22 @@
return helper;
}
- private CcLibraryHelper initializeCompilationHelper(FeatureConfiguration featureConfiguration) {
- return initializeCcLibraryHelper(featureConfiguration);
- }
-
- private CcLibraryHelper initializeLinkingHelper(FeatureConfiguration featureConfiguration) {
- CcLibraryHelper helper =
- initializeCcLibraryHelper(featureConfiguration)
+ private CcLinkingHelper initializeLinkingHelper(FeatureConfiguration featureConfiguration) {
+ CcLinkingHelper helper =
+ new CcLinkingHelper(
+ ruleContext,
+ cppSemantics,
+ featureConfiguration,
+ ccToolchain(ruleContext),
+ CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext),
+ ruleContext.getConfiguration())
.enableCcSpecificLinkParamsProvider()
.enableCcNativeLibrariesProvider();
+ TransitiveInfoCollection runtime = getProtoToolchainProvider().runtime();
+ if (runtime != null) {
+ helper.addDeps(ImmutableList.of(runtime));
+ }
+ helper.addDeps(ruleContext.getPrerequisites("deps", TARGET));
// TODO(dougk): Configure output artifact with action_config
// once proto compile action is configurable from the crosstool.
if (!ccToolchain(ruleContext).supportsDynamicLinker()) {
@@ -287,7 +295,7 @@
return result.build();
}
- private void registerBlacklistedSrcs(SupportData supportData, CcLibraryHelper helper) {
+ private void registerBlacklistedSrcs(SupportData supportData, CcCompilationHelper helper) {
// Hack: This is a proto_library for descriptor.proto or similar.
//
// The headers of those libraries are precomputed . They are also explicitly part of normal
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
index 6f574ab..f768eda 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
@@ -88,9 +88,12 @@
import com.google.devtools.build.lib.rules.apple.AppleToolchain;
import com.google.devtools.build.lib.rules.apple.XcodeConfig;
import com.google.devtools.build.lib.rules.apple.XcodeConfigProvider;
+import com.google.devtools.build.lib.rules.cpp.CcCommon;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper;
-import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper.Info;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper;
+import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchain;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.CollidingProvidesException;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
@@ -301,7 +304,7 @@
}
}
- private Info.CompilationInfo compile(
+ private CompilationInfo compile(
ObjcProvider objcProvider,
VariablesExtension extension,
ExtraCompileArgs extraCompileArgs,
@@ -318,12 +321,12 @@
ObjcCppSemantics semantics,
String purpose)
throws RuleErrorException, InterruptedException {
- CcLibraryHelper result =
- new CcLibraryHelper(
+ CcCompilationHelper result =
+ new CcCompilationHelper(
ruleContext,
semantics,
getFeatureConfiguration(ruleContext, ccToolchain, buildConfiguration, objcProvider),
- CcLibraryHelper.SourceCategory.CC_AND_OBJC,
+ CcCompilationHelper.SourceCategory.CC_AND_OBJC,
ccToolchain,
fdoSupport,
buildConfiguration)
@@ -398,7 +401,7 @@
String purpose = String.format("%s_objc_arc", semantics.getPurpose());
extensionBuilder.setArcEnabled(true);
- Info.CompilationInfo objcArcCompilationInfo =
+ CompilationInfo objcArcCompilationInfo =
compile(
objcProvider,
extensionBuilder.build(),
@@ -417,7 +420,7 @@
purpose = String.format("%s_non_objc_arc", semantics.getPurpose());
extensionBuilder.setArcEnabled(false);
- Info.CompilationInfo nonObjcArcCompilationInfo =
+ CompilationInfo nonObjcArcCompilationInfo =
compile(
objcProvider,
extensionBuilder.build(),
@@ -434,23 +437,18 @@
semantics,
purpose);
- CcLibraryHelper resultLink =
- new CcLibraryHelper(
+ CcLinkingHelper resultLink =
+ new CcLinkingHelper(
ruleContext,
semantics,
getFeatureConfiguration(ruleContext, ccToolchain, buildConfiguration, objcProvider),
- CcLibraryHelper.SourceCategory.CC_AND_OBJC,
ccToolchain,
fdoSupport,
buildConfiguration)
- .addPrecompiledFiles(precompiledFiles)
.addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET))
- // Not all our dependencies need to export cpp information.
- // For example, objc_proto_library can depend on a proto_library rule that does not
- // generate C++ protos.
- .setCheckDepsGenerateCpp(false)
.setLinkedArtifactNameSuffix(intermediateArtifacts.archiveFileNameSuffix())
.setNeverLink(true)
+ .setCheckDepsGenerateCpp(false)
.addVariableExtension(extensionBuilder.build());
if (linkType != null) {
@@ -475,15 +473,15 @@
compilationOutputsBuilder.merge(objcArcCompilationInfo.getCcCompilationOutputs());
compilationOutputsBuilder.merge(nonObjcArcCompilationInfo.getCcCompilationOutputs());
- Info.LinkingInfo linkingInfo =
- resultLink.link(
- compilationOutputsBuilder.build(), compilationContextBuilder.build());
+ LinkingInfo linkingInfo =
+ resultLink.link(compilationOutputsBuilder.build(), compilationContextBuilder.build());
Map<String, NestedSet<Artifact>> mergedOutputGroups =
- Info.mergeOutputGroups(
- objcArcCompilationInfo.getOutputGroups(),
- nonObjcArcCompilationInfo.getOutputGroups(),
- linkingInfo.getOutputGroups());
+ CcCommon.mergeOutputGroups(
+ ImmutableList.of(
+ objcArcCompilationInfo.getOutputGroups(),
+ nonObjcArcCompilationInfo.getOutputGroups(),
+ linkingInfo.getOutputGroups()));
return new Pair<>(compilationOutputsBuilder.build(), ImmutableMap.copyOf(mergedOutputGroups));
}
@@ -572,36 +570,6 @@
}
}
- private void registerHeaderScanningActions(
- CcCompilationOutputs ccCompilationOutputs,
- ObjcProvider objcProvider,
- CompilationArtifacts compilationArtifacts) {
- // PIC is not used for Obj-C builds, if that changes this method will need to change
- if (!isHeaderThinningEnabled() || ccCompilationOutputs.getObjectFiles(false).isEmpty()) {
- return;
- }
-
- ImmutableList.Builder<ObjcHeaderThinningInfo> headerThinningInfos = ImmutableList.builder();
- AnalysisEnvironment analysisEnvironment = ruleContext.getAnalysisEnvironment();
- for (Artifact objectFile : ccCompilationOutputs.getObjectFiles(false)) {
- ActionAnalysisMetadata generatingAction =
- analysisEnvironment.getLocalGeneratingAction(objectFile);
- if (generatingAction instanceof CppCompileAction) {
- CppCompileAction action = (CppCompileAction) generatingAction;
- Artifact sourceFile = action.getSourceFile();
- if (!sourceFile.isTreeArtifact()
- && SOURCES_FOR_HEADER_THINNING.matches(sourceFile.getFilename())) {
- headerThinningInfos.add(
- new ObjcHeaderThinningInfo(
- sourceFile,
- intermediateArtifacts.headersListFile(sourceFile),
- action.getCompilerOptions()));
- }
- }
- }
- registerHeaderScanningActions(headerThinningInfos.build(), objcProvider, compilationArtifacts);
- }
-
/**
* Iterable wrapper providing strong type safety for arguments to binary linking.
*/
@@ -890,6 +858,74 @@
}
/**
+ * Returns a provider that collects this target's instrumented sources as well as those of its
+ * dependencies.
+ *
+ * @param common common information about this rule and its dependencies
+ * @return an instrumented files provider
+ */
+ public InstrumentedFilesProvider getInstrumentedFilesProvider(ObjcCommon common) {
+ ImmutableList.Builder<Artifact> oFiles = ImmutableList.builder();
+
+ if (common.getCompilationArtifacts().isPresent()) {
+ CompilationArtifacts artifacts = common.getCompilationArtifacts().get();
+ for (Artifact artifact : Iterables.concat(artifacts.getSrcs(), artifacts.getNonArcSrcs())) {
+ oFiles.add(intermediateArtifacts.objFile(artifact));
+ }
+ }
+
+ return InstrumentedFilesCollector.collect(
+ ruleContext,
+ INSTRUMENTATION_SPEC,
+ new ObjcCoverageMetadataCollector(),
+ oFiles.build(),
+ NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER),
+ // The COVERAGE_GCOV_PATH environment variable is added in TestSupport#getExtraProviders()
+ NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER),
+ !isTestRule);
+ }
+
+ /**
+ * Validates compilation-related attributes on this rule.
+ *
+ * @return this compilation support
+ * @throws RuleErrorException if there are attribute errors
+ */
+ CompilationSupport validateAttributes() throws RuleErrorException {
+ for (PathFragment absoluteInclude :
+ Iterables.filter(attributes.includes(), PathFragment::isAbsolute)) {
+ ruleContext.attributeError(
+ "includes", String.format(ABSOLUTE_INCLUDES_PATH_FORMAT, absoluteInclude));
+ }
+
+ if (ruleContext.attributes().has("srcs", BuildType.LABEL_LIST)) {
+ ImmutableSet<Artifact> hdrsSet = ImmutableSet.copyOf(attributes.hdrs());
+ ImmutableSet<Artifact> srcsSet =
+ ImmutableSet.copyOf(ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET).list());
+
+ // Check for overlap between srcs and hdrs.
+ for (Artifact header : Sets.intersection(hdrsSet, srcsSet)) {
+ String path = header.getRootRelativePath().toString();
+ ruleContext.attributeWarning(
+ "srcs", String.format(FILE_IN_SRCS_AND_HDRS_WARNING_FORMAT, path));
+ }
+
+ // Check for overlap between srcs and non_arc_srcs.
+ ImmutableSet<Artifact> nonArcSrcsSet =
+ ImmutableSet.copyOf(
+ ruleContext.getPrerequisiteArtifacts("non_arc_srcs", Mode.TARGET).list());
+ for (Artifact conflict : Sets.intersection(nonArcSrcsSet, srcsSet)) {
+ String path = conflict.getRootRelativePath().toString();
+ ruleContext.attributeError(
+ "srcs", String.format(FILE_IN_SRCS_AND_NON_ARC_SRCS_ERROR_FORMAT, path));
+ }
+ }
+
+ ruleContext.assertNoErrors();
+ return this;
+ }
+
+ /**
* Registers all actions necessary to compile this rule's sources and archive them.
*
* @param compilationArtifacts collection of artifacts required for the compilation
@@ -940,111 +976,6 @@
}
/**
- * Registers an action to create an archive artifact by fully (statically) linking all transitive
- * dependencies of this rule.
- *
- * @param objcProvider provides all compiling and linking information to create this artifact
- * @param outputArchive the output artifact for this action
- */
- public CompilationSupport registerFullyLinkAction(
- ObjcProvider objcProvider, Artifact outputArchive) throws InterruptedException {
- return registerFullyLinkAction(
- objcProvider,
- outputArchive,
- toolchain,
- maybeGetFdoSupport());
- }
-
- /**
- * Returns a provider that collects this target's instrumented sources as well as those of its
- * dependencies.
- *
- * @param common common information about this rule and its dependencies
- * @return an instrumented files provider
- */
- public InstrumentedFilesProvider getInstrumentedFilesProvider(ObjcCommon common) {
- ImmutableList.Builder<Artifact> oFiles = ImmutableList.builder();
-
- if (common.getCompilationArtifacts().isPresent()) {
- CompilationArtifacts artifacts = common.getCompilationArtifacts().get();
- for (Artifact artifact : Iterables.concat(artifacts.getSrcs(), artifacts.getNonArcSrcs())) {
- oFiles.add(intermediateArtifacts.objFile(artifact));
- }
- }
-
- return InstrumentedFilesCollector.collect(
- ruleContext,
- INSTRUMENTATION_SPEC,
- new ObjcCoverageMetadataCollector(),
- oFiles.build(),
- NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER),
- // The COVERAGE_GCOV_PATH environment variable is added in TestSupport#getExtraProviders()
- NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER),
- !isTestRule);
- }
-
- /**
- * Registers an action that will generate a clang module map for this target, using the hdrs
- * attribute of this rule.
- */
- CompilationSupport registerGenerateModuleMapAction(CompilationArtifacts compilationArtifacts) {
- // TODO(bazel-team): Include textual headers in the module map when Xcode 6 support is
- // dropped.
- // TODO(b/32225593): Include private headers in the module map.
- Iterable<Artifact> publicHeaders = attributes.hdrs();
- publicHeaders = Iterables.concat(publicHeaders, compilationArtifacts.getAdditionalHdrs());
- CppModuleMap moduleMap = intermediateArtifacts.moduleMap();
- registerGenerateModuleMapAction(moduleMap, publicHeaders);
-
- Optional<Artifact> umbrellaHeader = moduleMap.getUmbrellaHeader();
- if (umbrellaHeader.isPresent()) {
- registerGenerateUmbrellaHeaderAction(umbrellaHeader.get(), publicHeaders);
- }
-
- return this;
- }
-
- /**
- * Validates compilation-related attributes on this rule.
- *
- * @return this compilation support
- * @throws RuleErrorException if there are attribute errors
- */
- CompilationSupport validateAttributes() throws RuleErrorException {
- for (PathFragment absoluteInclude :
- Iterables.filter(attributes.includes(), PathFragment::isAbsolute)) {
- ruleContext.attributeError(
- "includes", String.format(ABSOLUTE_INCLUDES_PATH_FORMAT, absoluteInclude));
- }
-
- if (ruleContext.attributes().has("srcs", BuildType.LABEL_LIST)) {
- ImmutableSet<Artifact> hdrsSet = ImmutableSet.copyOf(attributes.hdrs());
- ImmutableSet<Artifact> srcsSet =
- ImmutableSet.copyOf(ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET).list());
-
- // Check for overlap between srcs and hdrs.
- for (Artifact header : Sets.intersection(hdrsSet, srcsSet)) {
- String path = header.getRootRelativePath().toString();
- ruleContext.attributeWarning(
- "srcs", String.format(FILE_IN_SRCS_AND_HDRS_WARNING_FORMAT, path));
- }
-
- // Check for overlap between srcs and non_arc_srcs.
- ImmutableSet<Artifact> nonArcSrcsSet =
- ImmutableSet.copyOf(
- ruleContext.getPrerequisiteArtifacts("non_arc_srcs", Mode.TARGET).list());
- for (Artifact conflict : Sets.intersection(nonArcSrcsSet, srcsSet)) {
- String path = conflict.getRootRelativePath().toString();
- ruleContext.attributeError(
- "srcs", String.format(FILE_IN_SRCS_AND_NON_ARC_SRCS_ERROR_FORMAT, path));
- }
- }
-
- ruleContext.assertNoErrors();
- return this;
- }
-
- /**
* Registers all actions necessary to compile this rule's sources and archive them.
*
* @param compilationArtifacts collection of artifacts required for the compilation
@@ -1381,6 +1312,18 @@
*
* @param objcProvider provides all compiling and linking information to create this artifact
* @param outputArchive the output artifact for this action
+ */
+ public CompilationSupport registerFullyLinkAction(
+ ObjcProvider objcProvider, Artifact outputArchive) throws InterruptedException {
+ return registerFullyLinkAction(objcProvider, outputArchive, toolchain, maybeGetFdoSupport());
+ }
+
+ /**
+ * Registers an action to create an archive artifact by fully (statically) linking all transitive
+ * dependencies of this rule.
+ *
+ * @param objcProvider provides all compiling and linking information to create this artifact
+ * @param outputArchive the output artifact for this action
* @param ccToolchain the cpp toolchain provider, may be null
* @param fdoSupport the cpp FDO support provider, may be null
* @return this {@link CompilationSupport} instance
@@ -1745,6 +1688,27 @@
}
/**
+ * Registers an action that will generate a clang module map for this target, using the hdrs
+ * attribute of this rule.
+ */
+ CompilationSupport registerGenerateModuleMapAction(CompilationArtifacts compilationArtifacts) {
+ // TODO(bazel-team): Include textual headers in the module map when Xcode 6 support is
+ // dropped.
+ // TODO(b/32225593): Include private headers in the module map.
+ Iterable<Artifact> publicHeaders = attributes.hdrs();
+ publicHeaders = Iterables.concat(publicHeaders, compilationArtifacts.getAdditionalHdrs());
+ CppModuleMap moduleMap = intermediateArtifacts.moduleMap();
+ registerGenerateModuleMapAction(moduleMap, publicHeaders);
+
+ Optional<Artifact> umbrellaHeader = moduleMap.getUmbrellaHeader();
+ if (umbrellaHeader.isPresent()) {
+ registerGenerateUmbrellaHeaderAction(umbrellaHeader.get(), publicHeaders);
+ }
+
+ return this;
+ }
+
+ /**
* Registers an action that will generate a clang module map.
* @param moduleMap the module map to generate
* @param publicHeaders the headers that should be directly accessible by dependers
@@ -1840,6 +1804,36 @@
.getProvider(FilesToRunProvider.class);
}
+ private void registerHeaderScanningActions(
+ CcCompilationOutputs ccCompilationOutputs,
+ ObjcProvider objcProvider,
+ CompilationArtifacts compilationArtifacts) {
+ // PIC is not used for Obj-C builds, if that changes this method will need to change
+ if (!isHeaderThinningEnabled() || ccCompilationOutputs.getObjectFiles(false).isEmpty()) {
+ return;
+ }
+
+ ImmutableList.Builder<ObjcHeaderThinningInfo> headerThinningInfos = ImmutableList.builder();
+ AnalysisEnvironment analysisEnvironment = ruleContext.getAnalysisEnvironment();
+ for (Artifact objectFile : ccCompilationOutputs.getObjectFiles(false)) {
+ ActionAnalysisMetadata generatingAction =
+ analysisEnvironment.getLocalGeneratingAction(objectFile);
+ if (generatingAction instanceof CppCompileAction) {
+ CppCompileAction action = (CppCompileAction) generatingAction;
+ Artifact sourceFile = action.getSourceFile();
+ if (!sourceFile.isTreeArtifact()
+ && SOURCES_FOR_HEADER_THINNING.matches(sourceFile.getFilename())) {
+ headerThinningInfos.add(
+ new ObjcHeaderThinningInfo(
+ sourceFile,
+ intermediateArtifacts.headersListFile(sourceFile),
+ action.getCompilerOptions()));
+ }
+ }
+ }
+ registerHeaderScanningActions(headerThinningInfos.build(), objcProvider, compilationArtifacts);
+ }
+
/**
* Creates and registers ObjcHeaderScanning {@link SpawnAction}. Groups all the actions by their
* compilation command line arguments and creates a ObjcHeaderScanning action for each unique one.