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.