diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContextInfo.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContextInfo.java | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContextInfo.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContextInfo.java new file mode 100644 index 0000000000..c7b64d7c05 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContextInfo.java @@ -0,0 +1,952 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.rules.cpp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +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.ActionOwner; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.MiddlemanFactory; +import com.google.devtools.build.lib.analysis.RuleContext; +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.NativeInfo; +import com.google.devtools.build.lib.packages.NativeProvider; +import com.google.devtools.build.lib.rules.cpp.CppHelper.PregreppedHeader; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +/** + * Immutable store of information needed for C++ compilation that is aggregated across dependencies. + */ +@Immutable +@AutoCodec +@SkylarkModule( + name = "cc_compilation_info", + documented = false, + category = SkylarkModuleCategory.PROVIDER, + doc = + "Immutable store of information needed for C++ compilation that is aggregated across " + + "dependencies." +) +public final class CcCompilationContextInfo extends NativeInfo { + public static final NativeProvider<CcCompilationContextInfo> PROVIDER = + new NativeProvider<CcCompilationContextInfo>( + CcCompilationContextInfo.class, "CcCompilationContextInfo") {}; + + /** An empty {@code CcCompilationContextInfo}. */ + public static final CcCompilationContextInfo EMPTY = new Builder(null).build(); + + private final CommandLineCcCompilationContextInfo commandLineCcCompilationContextInfo; + + private final NestedSet<PathFragment> declaredIncludeDirs; + private final NestedSet<PathFragment> declaredIncludeWarnDirs; + private final NestedSet<Artifact> declaredIncludeSrcs; + + /** + * Module maps from direct dependencies. + */ + private final NestedSet<Artifact> directModuleMaps; + + /** Non-code mandatory compilation inputs. */ + private final NestedSet<Artifact> nonCodeInputs; + + private final NestedSet<PregreppedHeader> pregreppedHdrs; + + private final ModuleInfo moduleInfo; + private final ModuleInfo picModuleInfo; + + private final CppModuleMap cppModuleMap; + private final CppModuleMap verificationModuleMap; + + private final boolean propagateModuleMapAsActionInput; + + // Derived from depsContexts. + private final ImmutableSet<Artifact> compilationPrerequisites; + + @AutoCodec.Instantiator + @VisibleForSerialization + CcCompilationContextInfo( + CommandLineCcCompilationContextInfo commandLineCcCompilationContextInfo, + ImmutableSet<Artifact> compilationPrerequisites, + NestedSet<PathFragment> declaredIncludeDirs, + NestedSet<PathFragment> declaredIncludeWarnDirs, + NestedSet<Artifact> declaredIncludeSrcs, + NestedSet<PregreppedHeader> pregreppedHdrs, + NestedSet<Artifact> nonCodeInputs, + ModuleInfo moduleInfo, + ModuleInfo picModuleInfo, + NestedSet<Artifact> directModuleMaps, + CppModuleMap cppModuleMap, + @Nullable CppModuleMap verificationModuleMap, + boolean propagateModuleMapAsActionInput) { + super(PROVIDER); + Preconditions.checkNotNull(commandLineCcCompilationContextInfo); + this.commandLineCcCompilationContextInfo = commandLineCcCompilationContextInfo; + this.declaredIncludeDirs = declaredIncludeDirs; + this.declaredIncludeWarnDirs = declaredIncludeWarnDirs; + this.declaredIncludeSrcs = declaredIncludeSrcs; + this.directModuleMaps = directModuleMaps; + this.pregreppedHdrs = pregreppedHdrs; + this.moduleInfo = moduleInfo; + this.picModuleInfo = picModuleInfo; + this.cppModuleMap = cppModuleMap; + this.nonCodeInputs = nonCodeInputs; + this.verificationModuleMap = verificationModuleMap; + this.compilationPrerequisites = compilationPrerequisites; + this.propagateModuleMapAsActionInput = propagateModuleMapAsActionInput; + } + + /** + * Returns the transitive compilation prerequisites consolidated into middlemen prerequisites, or + * an empty set if there are no prerequisites. + * + * <p>Transitive compilation prerequisites are the prerequisites that will be needed by all + * reverse dependencies; note that these do specifically not include any compilation prerequisites + * that are only needed by the rule itself (for example, compiled source files from the {@code + * srcs} attribute). + * + * <p>To reduce the number of edges in the action graph, we express the dependency on compilation + * prerequisites as a transitive dependency via a middleman. After they have been accumulated + * (using {@link Builder#addCompilationPrerequisites(Iterable)}, {@link + * Builder#mergeDependentCcCompilationContextInfo(CcCompilationContextInfo)}, and {@link + * Builder#mergeDependentCcCompilationContextInfos(Iterable)}, they are consolidated into a single + * middleman Artifact when {@link Builder#build()} is called. + * + * <p>The returned set can be empty if there are no prerequisites. Usually it contains a single + * middleman, but if LIPO is used there can be two. + */ + public ImmutableSet<Artifact> getTransitiveCompilationPrerequisites() { + return compilationPrerequisites; + } + + /** + * Returns the immutable list of include directories to be added with "-I" + * (possibly empty but never null). This includes the include dirs from the + * transitive deps closure of the target. This list does not contain + * duplicates. All fragments are either absolute or relative to the exec root + * (see {@link com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}). + */ + public ImmutableList<PathFragment> getIncludeDirs() { + return commandLineCcCompilationContextInfo.includeDirs; + } + + /** + * Returns the immutable list of include directories to be added with + * "-iquote" (possibly empty but never null). This includes the include dirs + * from the transitive deps closure of the target. This list does not contain + * duplicates. All fragments are either absolute or relative to the exec root + * (see {@link com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}). + */ + public ImmutableList<PathFragment> getQuoteIncludeDirs() { + return commandLineCcCompilationContextInfo.quoteIncludeDirs; + } + + /** + * Returns the immutable list of include directories to be added with + * "-isystem" (possibly empty but never null). This includes the include dirs + * from the transitive deps closure of the target. This list does not contain + * duplicates. All fragments are either absolute or relative to the exec root + * (see {@link com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}). + */ + public ImmutableList<PathFragment> getSystemIncludeDirs() { + return commandLineCcCompilationContextInfo.systemIncludeDirs; + } + + /** + * Returns the immutable set of declared include directories, relative to a "-I" or "-iquote" + * directory" (possibly empty but never null). + */ + public NestedSet<PathFragment> getDeclaredIncludeDirs() { + return declaredIncludeDirs; + } + + /** + * Returns the immutable set of include directories, relative to a "-I" or "-iquote" directory", + * from which inclusion will produce a warning (possibly empty but never null). + */ + public NestedSet<PathFragment> getDeclaredIncludeWarnDirs() { + return declaredIncludeWarnDirs; + } + + /** + * Returns the immutable set of headers that have been declared in the {@code srcs} or {@code + * hdrs} attribute (possibly empty but never null). + */ + public NestedSet<Artifact> getDeclaredIncludeSrcs() { + return declaredIncludeSrcs; + } + + /** + * Returns the immutable pairs of (header file, pregrepped header file). The value artifacts + * (pregrepped header file) are generated by {@link ExtractInclusionAction}. + */ + NestedSet<PregreppedHeader> getPregreppedHeaders() { + return pregreppedHdrs; + } + + public NestedSet<Artifact> getTransitiveModules(boolean usePic) { + return usePic ? picModuleInfo.transitiveModules : moduleInfo.transitiveModules; + } + + public Collection<TransitiveModuleHeaders> getUsedModules( + boolean usePic, Set<Artifact> usedHeaders) { + return usePic + ? picModuleInfo.getUsedModules(usedHeaders) + : moduleInfo.getUsedModules(usedHeaders); + } + + /** + * Returns the immutable set of additional transitive inputs needed for + * compilation, like C++ module map artifacts. + */ + public NestedSet<Artifact> getAdditionalInputs() { + NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder(); + builder.addTransitive(directModuleMaps); + builder.addTransitive(nonCodeInputs); + if (cppModuleMap != null && propagateModuleMapAsActionInput) { + builder.add(cppModuleMap.getArtifact()); + } + return builder.build(); + } + + /** + * @return modules maps from direct dependencies. + */ + public NestedSet<Artifact> getDirectModuleMaps() { + return directModuleMaps; + } + + /** + * @return all declared headers of the current module if the current target + * is compiled as a module. + */ + protected Set<Artifact> getHeaderModuleSrcs() { + return new ImmutableSet.Builder<Artifact>() + .addAll(moduleInfo.modularHeaders) + .addAll(moduleInfo.textualHeaders) + .build(); + } + + /** + * Returns the set of defines needed to compile this target (possibly empty + * but never null). This includes definitions from the transitive deps closure + * for the target. The order of the returned collection is deterministic. + */ + public ImmutableList<String> getDefines() { + return commandLineCcCompilationContextInfo.defines; + } + + /** + * Returns a {@code CcCompilationContextInfo} that is based on a given {@code + * CcCompilationContextInfo} but returns empty sets for {@link #getDeclaredIncludeDirs()} and + * {@link #getDeclaredIncludeWarnDirs()}. + */ + public static CcCompilationContextInfo disallowUndeclaredHeaders( + CcCompilationContextInfo ccCompilationContextInfo) { + return new CcCompilationContextInfo( + ccCompilationContextInfo.commandLineCcCompilationContextInfo, + ccCompilationContextInfo.compilationPrerequisites, + NestedSetBuilder.<PathFragment>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<PathFragment>emptySet(Order.STABLE_ORDER), + ccCompilationContextInfo.declaredIncludeSrcs, + ccCompilationContextInfo.pregreppedHdrs, + ccCompilationContextInfo.nonCodeInputs, + ccCompilationContextInfo.moduleInfo, + ccCompilationContextInfo.picModuleInfo, + ccCompilationContextInfo.directModuleMaps, + ccCompilationContextInfo.cppModuleMap, + ccCompilationContextInfo.verificationModuleMap, + ccCompilationContextInfo.propagateModuleMapAsActionInput); + } + + /** + * Returns the context for a LIPO compile action. This uses the include dirs and defines of the + * library, but the declared inclusion dirs/srcs from both the library and the owner binary. + * + * <p>TODO(bazel-team): this might make every LIPO target have an unnecessary large set of + * inclusion dirs/srcs. The correct behavior would be to merge only the contexts of actual + * referred targets (as listed in .imports file). + * + * <p>Undeclared inclusion checking ({@link #getDeclaredIncludeDirs()}, {@link + * #getDeclaredIncludeWarnDirs()}, and {@link #getDeclaredIncludeSrcs()}) needs to use the union + * of the contexts of the involved source files. + * + * <p>For include and define command line flags ({@link #getIncludeDirs()} {@link + * #getQuoteIncludeDirs()}, {@link #getSystemIncludeDirs()}, and {@link #getDefines()}) LIPO + * compilations use the same values as non-LIPO compilation. + * + * <p>Include scanning is not handled by this method. See {@code + * IncludeScannable#getAuxiliaryScannables()} instead. + * + * @param ownerCcCompilationContextInfo the {@code CcCompilationContextInfo} of the owner binary + * @param libCcCompilationContextInfo the {@code CcCompilationContextInfo} of the library + */ + public static CcCompilationContextInfo mergeForLipo( + CcCompilationContextInfo ownerCcCompilationContextInfo, + CcCompilationContextInfo libCcCompilationContextInfo) { + ImmutableSet.Builder<Artifact> prerequisites = ImmutableSet.builder(); + prerequisites.addAll(ownerCcCompilationContextInfo.compilationPrerequisites); + prerequisites.addAll(libCcCompilationContextInfo.compilationPrerequisites); + ModuleInfo.Builder moduleInfo = new ModuleInfo.Builder(); + moduleInfo.merge(ownerCcCompilationContextInfo.moduleInfo); + moduleInfo.merge(libCcCompilationContextInfo.moduleInfo); + ModuleInfo.Builder picModuleInfo = new ModuleInfo.Builder(); + picModuleInfo.merge(ownerCcCompilationContextInfo.picModuleInfo); + picModuleInfo.merge(libCcCompilationContextInfo.picModuleInfo); + return new CcCompilationContextInfo( + libCcCompilationContextInfo.commandLineCcCompilationContextInfo, + prerequisites.build(), + mergeSets( + ownerCcCompilationContextInfo.declaredIncludeDirs, + libCcCompilationContextInfo.declaredIncludeDirs), + mergeSets( + ownerCcCompilationContextInfo.declaredIncludeWarnDirs, + libCcCompilationContextInfo.declaredIncludeWarnDirs), + mergeSets( + ownerCcCompilationContextInfo.declaredIncludeSrcs, + libCcCompilationContextInfo.declaredIncludeSrcs), + mergeSets( + ownerCcCompilationContextInfo.pregreppedHdrs, + libCcCompilationContextInfo.pregreppedHdrs), + mergeSets( + ownerCcCompilationContextInfo.nonCodeInputs, libCcCompilationContextInfo.nonCodeInputs), + moduleInfo.build(), + picModuleInfo.build(), + mergeSets( + ownerCcCompilationContextInfo.directModuleMaps, + libCcCompilationContextInfo.directModuleMaps), + libCcCompilationContextInfo.cppModuleMap, + libCcCompilationContextInfo.verificationModuleMap, + libCcCompilationContextInfo.propagateModuleMapAsActionInput); + } + + /** + * Return a nested set containing all elements from {@code s1} and {@code s2}. + */ + private static <T> NestedSet<T> mergeSets(NestedSet<T> s1, NestedSet<T> s2) { + NestedSetBuilder<T> builder = NestedSetBuilder.stableOrder(); + builder.addTransitive(s1); + builder.addTransitive(s2); + return builder.build(); + } + + /** @return the C++ module map of the owner. */ + public CppModuleMap getCppModuleMap() { + return cppModuleMap; + } + + /** @return the C++ module map of the owner. */ + public CppModuleMap getVerificationModuleMap() { + return verificationModuleMap; + } + + /** + * The parts of the {@code CcCompilationContextInfo} that influence the command line of + * compilation actions. + */ + @Immutable + @AutoCodec + @VisibleForSerialization + static class CommandLineCcCompilationContextInfo { + private final ImmutableList<PathFragment> includeDirs; + private final ImmutableList<PathFragment> quoteIncludeDirs; + private final ImmutableList<PathFragment> systemIncludeDirs; + private final ImmutableList<String> defines; + + CommandLineCcCompilationContextInfo( + ImmutableList<PathFragment> includeDirs, + ImmutableList<PathFragment> quoteIncludeDirs, + ImmutableList<PathFragment> systemIncludeDirs, + ImmutableList<String> defines) { + this.includeDirs = includeDirs; + this.quoteIncludeDirs = quoteIncludeDirs; + this.systemIncludeDirs = systemIncludeDirs; + this.defines = defines; + } + } + + /** Builder class for {@link CcCompilationContextInfo}. */ + public static class Builder { + private String purpose; + private final Set<Artifact> compilationPrerequisites = new LinkedHashSet<>(); + private final Set<PathFragment> includeDirs = new LinkedHashSet<>(); + private final Set<PathFragment> quoteIncludeDirs = new LinkedHashSet<>(); + private final Set<PathFragment> systemIncludeDirs = new LinkedHashSet<>(); + private final NestedSetBuilder<PathFragment> declaredIncludeDirs = + NestedSetBuilder.stableOrder(); + private final NestedSetBuilder<PathFragment> declaredIncludeWarnDirs = + NestedSetBuilder.stableOrder(); + private final NestedSetBuilder<Artifact> declaredIncludeSrcs = + NestedSetBuilder.stableOrder(); + private final NestedSetBuilder<PregreppedHeader> pregreppedHdrs = + NestedSetBuilder.stableOrder(); + private final NestedSetBuilder<Artifact> nonCodeInputs = NestedSetBuilder.stableOrder(); + private final ModuleInfo.Builder moduleInfo = new ModuleInfo.Builder(); + private final ModuleInfo.Builder picModuleInfo = new ModuleInfo.Builder(); + private final NestedSetBuilder<Artifact> directModuleMaps = NestedSetBuilder.stableOrder(); + private final Set<String> defines = new LinkedHashSet<>(); + private CppModuleMap cppModuleMap; + private CppModuleMap verificationModuleMap; + private boolean propagateModuleMapAsActionInput = true; + + /** The rule that owns the context */ + private final RuleContext ruleContext; + + /** Creates a new builder for a {@link CcCompilationContextInfo} instance. */ + public Builder(RuleContext ruleContext) { + this.ruleContext = ruleContext; + } + + /** + * Overrides the purpose of this context. This is useful if a Target needs more than one + * CcCompilationContextInfo. (The purpose is used to construct the name of the prerequisites + * middleman for the context, and all artifacts for a given Target must have distinct names.) + * + * @param purpose must be a string which is suitable for use as a filename. A single rule may + * have many middlemen with distinct purposes. + * @see MiddlemanFactory#createErrorPropagatingMiddleman + */ + public Builder setPurpose(String purpose) { + this.purpose = purpose; + return this; + } + + public String getPurpose() { + return purpose; + } + + /** + * Merges the {@link CcCompilationContextInfo} of a dependency into this one by adding the + * contents of all of its attributes. + */ + public Builder mergeDependentCcCompilationContextInfo( + CcCompilationContextInfo otherCcCompilationContextInfo) { + Preconditions.checkNotNull(otherCcCompilationContextInfo); + compilationPrerequisites.addAll( + otherCcCompilationContextInfo.getTransitiveCompilationPrerequisites()); + includeDirs.addAll(otherCcCompilationContextInfo.getIncludeDirs()); + quoteIncludeDirs.addAll(otherCcCompilationContextInfo.getQuoteIncludeDirs()); + systemIncludeDirs.addAll(otherCcCompilationContextInfo.getSystemIncludeDirs()); + declaredIncludeDirs.addTransitive(otherCcCompilationContextInfo.getDeclaredIncludeDirs()); + declaredIncludeWarnDirs.addTransitive( + otherCcCompilationContextInfo.getDeclaredIncludeWarnDirs()); + declaredIncludeSrcs.addTransitive(otherCcCompilationContextInfo.getDeclaredIncludeSrcs()); + pregreppedHdrs.addTransitive(otherCcCompilationContextInfo.getPregreppedHeaders()); + moduleInfo.addTransitive(otherCcCompilationContextInfo.moduleInfo); + picModuleInfo.addTransitive(otherCcCompilationContextInfo.picModuleInfo); + nonCodeInputs.addTransitive(otherCcCompilationContextInfo.nonCodeInputs); + + // All module maps of direct dependencies are inputs to the current compile independently of + // the build type. + if (otherCcCompilationContextInfo.getCppModuleMap() != null) { + directModuleMaps.add(otherCcCompilationContextInfo.getCppModuleMap().getArtifact()); + } + + defines.addAll(otherCcCompilationContextInfo.getDefines()); + return this; + } + + /** + * Merges the {@code CcCompilationContextInfo}s of some targets into this one by adding the + * contents of all of their attributes. Targets that do not implement {@link + * CcCompilationContextInfo} are ignored. + */ + public Builder mergeDependentCcCompilationContextInfos( + Iterable<CcCompilationContextInfo> targets) { + for (CcCompilationContextInfo target : targets) { + mergeDependentCcCompilationContextInfo(target); + } + return this; + } + + /** + * Adds multiple compilation prerequisites. + * + * <p>There are two kinds of "compilation prerequisites": declared header files and pregrepped + * headers. + */ + public Builder addCompilationPrerequisites(Iterable<Artifact> prerequisites) { + // LIPO collector must not add compilation prerequisites in order to avoid + // the creation of a middleman action. + for (Artifact prerequisite : prerequisites) { + String basename = prerequisite.getFilename(); + Preconditions.checkArgument(!Link.OBJECT_FILETYPES.matches(basename)); + Preconditions.checkArgument(!Link.ARCHIVE_LIBRARY_FILETYPES.matches(basename)); + Preconditions.checkArgument(!Link.SHARED_LIBRARY_FILETYPES.matches(basename)); + } + Iterables.addAll(compilationPrerequisites, prerequisites); + return this; + } + + /** + * Add a single include directory to be added with "-I". It can be either + * relative to the exec root (see + * {@link com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}) or + * absolute. Before it is stored, the include directory is normalized. + */ + public Builder addIncludeDir(PathFragment includeDir) { + includeDirs.add(includeDir); + return this; + } + + /** + * Add multiple include directories to be added with "-I". These can be + * either relative to the exec root (see {@link + * com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}) or absolute. The + * entries are normalized before they are stored. + */ + public Builder addIncludeDirs(Iterable<PathFragment> includeDirs) { + for (PathFragment includeDir : includeDirs) { + addIncludeDir(includeDir); + } + return this; + } + + /** + * Add a single include directory to be added with "-iquote". It can be + * either relative to the exec root (see {@link + * com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}) or absolute. Before it + * is stored, the include directory is normalized. + */ + public Builder addQuoteIncludeDir(PathFragment quoteIncludeDir) { + quoteIncludeDirs.add(quoteIncludeDir); + return this; + } + + /** + * Add a single include directory to be added with "-isystem". It can be + * either relative to the exec root (see {@link + * com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}) or absolute. Before it + * is stored, the include directory is normalized. + */ + public Builder addSystemIncludeDir(PathFragment systemIncludeDir) { + systemIncludeDirs.add(systemIncludeDir); + return this; + } + + /** + * Add a single declared include dir, relative to a "-I" or "-iquote" + * directory". + */ + public Builder addDeclaredIncludeDir(PathFragment dir) { + declaredIncludeDirs.add(dir); + return this; + } + + /** + * Add a single declared include directory, relative to a "-I" or "-iquote" + * directory", from which inclusion will produce a warning. + */ + public Builder addDeclaredIncludeWarnDir(PathFragment dir) { + declaredIncludeWarnDirs.add(dir); + return this; + } + + /** + * Adds a header that has been declared in the {@code src} or {@code headers attribute}. The + * header will also be added to the compilation prerequisites. + * + * <p>Filters out fileset directory artifacts, which are not valid inputs. + */ + public Builder addDeclaredIncludeSrc(Artifact header) { + if (!header.isFileset()) { + declaredIncludeSrcs.add(header); + compilationPrerequisites.add(header); + } + return this; + } + + /** + * Adds multiple headers that have been declared in the {@code src} or {@code headers + * attribute}. The headers will also be added to the compilation prerequisites. + * + * <p>Filters out fileset directory artifacts, which are not valid inputs. + */ + public Builder addDeclaredIncludeSrcs(Collection<Artifact> declaredIncludeSrcs) { + for (Artifact source : declaredIncludeSrcs) { + addDeclaredIncludeSrc(source); + } + return this; + } + + public Builder addModularHdrs(Collection<Artifact> headers) { + this.moduleInfo.addHeaders(headers); + this.picModuleInfo.addHeaders(headers); + return this; + } + + public Builder addTextualHdrs(Collection<Artifact> headers) { + this.moduleInfo.addTextualHeaders(headers); + this.picModuleInfo.addTextualHeaders(headers); + return this; + } + + /** + * Add a map of generated source or header Artifact to an output Artifact after grepping the + * file for include statements. + */ + public Builder addPregreppedHeaders(List<PregreppedHeader> pregrepped) { + addCompilationPrerequisites( + pregrepped + .stream() + .map(pregreppedHeader -> pregreppedHeader.greppedHeader()) + .collect(Collectors.toList())); + this.pregreppedHdrs.addAll(pregrepped); + return this; + } + + /** Add a set of required non-code compilation input. */ + public Builder addNonCodeInputs(Iterable<Artifact> inputs) { + nonCodeInputs.addAll(inputs); + return this; + } + + /** + * Adds a single define. + */ + public Builder addDefine(String define) { + defines.add(define); + return this; + } + + /** + * Adds multiple defines. + */ + public Builder addDefines(Iterable<String> defines) { + Iterables.addAll(this.defines, defines); + return this; + } + + /** Sets the C++ module map. */ + public Builder setCppModuleMap(CppModuleMap cppModuleMap) { + this.cppModuleMap = cppModuleMap; + return this; + } + + /** Sets the C++ module map used to verify that headers are modules compatible. */ + public Builder setVerificationModuleMap(CppModuleMap verificationModuleMap) { + this.verificationModuleMap = verificationModuleMap; + return this; + } + + /** + * Causes the module map to be passed as an action input to dependant compilations. + */ + public Builder setPropagateCppModuleMapAsActionInput(boolean propagateModuleMap) { + this.propagateModuleMapAsActionInput = propagateModuleMap; + return this; + } + + /** + * Sets the C++ header module in non-pic mode. + * + * @param headerModule The .pcm file generated for this library. + */ + public Builder setHeaderModule(Artifact headerModule) { + this.moduleInfo.setHeaderModule(headerModule); + return this; + } + + /** + * Sets the C++ header module in pic mode. + * @param picHeaderModule The .pic.pcm file generated for this library. + */ + public Builder setPicHeaderModule(Artifact picHeaderModule) { + this.picModuleInfo.setHeaderModule(picHeaderModule); + return this; + } + + /** Builds the {@link CcCompilationContextInfo}. */ + public CcCompilationContextInfo build() { + return build( + ruleContext == null ? null : ruleContext.getActionOwner(), + ruleContext == null ? null : ruleContext.getAnalysisEnvironment().getMiddlemanFactory()); + } + + @VisibleForTesting // productionVisibility = Visibility.PRIVATE + public CcCompilationContextInfo build(ActionOwner owner, MiddlemanFactory middlemanFactory) { + // We don't create middlemen in LIPO collector subtree, because some target CT + // will do that instead. + Artifact prerequisiteStampFile = (ruleContext != null + && ruleContext.getFragment(CppConfiguration.class).isLipoContextCollector()) + ? getMiddlemanArtifact(middlemanFactory) + : createMiddleman(owner, middlemanFactory); + + return new CcCompilationContextInfo( + new CommandLineCcCompilationContextInfo( + ImmutableList.copyOf(includeDirs), + ImmutableList.copyOf(quoteIncludeDirs), + ImmutableList.copyOf(systemIncludeDirs), + ImmutableList.copyOf(defines)), + prerequisiteStampFile == null + ? ImmutableSet.<Artifact>of() + : ImmutableSet.of(prerequisiteStampFile), + declaredIncludeDirs.build(), + declaredIncludeWarnDirs.build(), + declaredIncludeSrcs.build(), + pregreppedHdrs.build(), + nonCodeInputs.build(), + moduleInfo.build(), + picModuleInfo.build(), + directModuleMaps.build(), + cppModuleMap, + verificationModuleMap, + propagateModuleMapAsActionInput); + } + + /** + * Creates a middleman for the compilation prerequisites. + * + * @return the middleman or null if there are no prerequisites + */ + private Artifact createMiddleman(ActionOwner owner, + MiddlemanFactory middlemanFactory) { + if (compilationPrerequisites.isEmpty()) { + return null; + } + + // Compilation prerequisites gathered in the compilationPrerequisites + // must be generated prior to executing C++ compilation step that depends + // on them (since these prerequisites include all potential header files, etc + // that could be referenced during compilation). So there is a definite need + // to ensure scheduling edge dependency. However, those prerequisites should + // have no effect on the decision whether C++ compilation should happen in + // the first place - only CppCompileAction outputs (*.o and *.d files) and + // all files referenced by the *.d file should be used to make that decision. + // If this action was never executed, then *.d file would be missing, forcing + // compilation to occur. If *.d file is present and has not changed then the + // only reason that would force us to re-compile would be change in one of + // the files referenced by the *.d file, since no other files participated + // in the compilation. We also need to propagate errors through this + // dependency link. So we use an error propagating middleman. + // Such middleman will be ignored by the dependency checker yet will still + // represent an edge in the action dependency graph - forcing proper execution + // order and error propagation. + String name = + cppModuleMap != null ? cppModuleMap.getName() : ruleContext.getLabel().toString(); + return middlemanFactory.createErrorPropagatingMiddleman( + owner, name, purpose, + ImmutableList.copyOf(compilationPrerequisites), + ruleContext.getConfiguration().getMiddlemanDirectory( + ruleContext.getRule().getRepository())); + } + + /** + * Returns the same set of artifacts as createMiddleman() would, but without + * actually creating middlemen. + */ + private Artifact getMiddlemanArtifact(MiddlemanFactory middlemanFactory) { + if (compilationPrerequisites.isEmpty()) { + return null; + } + + return middlemanFactory.getErrorPropagatingMiddlemanArtifact( + ruleContext.getLabel().toString(), + purpose, + ruleContext.getConfiguration().getMiddlemanDirectory( + ruleContext.getRule().getRepository())); + } + } + + /** + * Gathers data about the direct and transitive .pcm files belonging to this context. Can be to + * either gather data on PIC or on no-PIC .pcm files. + */ + @Immutable + @AutoCodec + public static final class ModuleInfo { + /** + * The module built for this context. If null, then no module is being compiled for this + * context. + */ + private final Artifact headerModule; + + /** All header files that are compiled into this module. */ + private final ImmutableSet<Artifact> modularHeaders; + + /** All header files that are contained in this module. */ + private final ImmutableSet<Artifact> textualHeaders; + + /** + * All transitive modules that this context depends on, excluding headerModule. + */ + private final NestedSet<Artifact> transitiveModules; + + /** + * All information about mapping transitive headers to transitive modules. + */ + public final NestedSet<TransitiveModuleHeaders> transitiveModuleHeaders; + + public ModuleInfo( + Artifact headerModule, + ImmutableSet<Artifact> modularHeaders, + ImmutableSet<Artifact> textualHeaders, + NestedSet<Artifact> transitiveModules, + NestedSet<TransitiveModuleHeaders> transitiveModuleHeaders) { + this.headerModule = headerModule; + this.modularHeaders = modularHeaders; + this.textualHeaders = textualHeaders; + this.transitiveModules = transitiveModules; + this.transitiveModuleHeaders = transitiveModuleHeaders; + } + + public Collection<TransitiveModuleHeaders> getUsedModules(Set<Artifact> usedHeaders) { + List<TransitiveModuleHeaders> result = new ArrayList<>(); + for (TransitiveModuleHeaders transitiveModule : transitiveModuleHeaders) { + if (transitiveModule.module.equals(headerModule)) { + // Do not add the module of the current rule for both: + // 1. the module compile itself + // 2. compiles of other translation units of the same rule. + continue; + } + boolean providesUsedHeader = false; + for (Artifact header : transitiveModule.headers) { + if (usedHeaders.contains(header)) { + providesUsedHeader = true; + break; + } + } + if (providesUsedHeader) { + result.add(transitiveModule); + } + } + return result; + } + + /** + * Builder class for {@link ModuleInfo}. + */ + public static class Builder { + private Artifact headerModule = null; + private final Set<Artifact> modularHeaders = new LinkedHashSet<>(); + private final Set<Artifact> textualHeaders = new LinkedHashSet<>(); + private final NestedSetBuilder<Artifact> transitiveModules = NestedSetBuilder.stableOrder(); + private final NestedSetBuilder<TransitiveModuleHeaders> transitiveModuleHeaders = + NestedSetBuilder.stableOrder(); + + public Builder setHeaderModule(Artifact headerModule) { + this.headerModule = headerModule; + return this; + } + + public Builder addHeaders(Collection<Artifact> headers) { + this.modularHeaders.addAll(headers); + return this; + } + + public Builder addTextualHeaders(Collection<Artifact> headers) { + this.textualHeaders.addAll(headers); + return this; + } + + /** + * Merges a {@link ModuleInfo} into this one. In contrast to addTransitive, this doesn't add + * the dependent module to transitiveModules, but just merges the transitive sets. The main + * usage is to merge multiple {@link ModuleInfo} instances for Lipo. + */ + public Builder merge(ModuleInfo other) { + if (headerModule == null) { + headerModule = other.headerModule; + } + modularHeaders.addAll(other.modularHeaders); + textualHeaders.addAll(other.textualHeaders); + transitiveModules.addTransitive(other.transitiveModules); + transitiveModuleHeaders.addTransitive(other.transitiveModuleHeaders); + return this; + } + + /** + * Adds the {@link ModuleInfo} of a dependency and builds up the transitive data structures. + */ + public Builder addTransitive(ModuleInfo moduleInfo) { + if (moduleInfo.headerModule != null) { + transitiveModules.add(moduleInfo.headerModule); + } + transitiveModules.addTransitive(moduleInfo.transitiveModules); + transitiveModuleHeaders.addTransitive(moduleInfo.transitiveModuleHeaders); + return this; + } + + public ModuleInfo build() { + ImmutableSet<Artifact> modularHeaders = ImmutableSet.copyOf(this.modularHeaders); + NestedSet<Artifact> transitiveModules = this.transitiveModules.build(); + if (headerModule != null) { + transitiveModuleHeaders.add( + new TransitiveModuleHeaders(headerModule, modularHeaders, transitiveModules)); + } + return new ModuleInfo( + headerModule, + modularHeaders, + ImmutableSet.copyOf(this.textualHeaders), + transitiveModules, + transitiveModuleHeaders.build()); + } + } + } + + /** Collects data for a specific module in a special format that makes pruning easy. */ + @Immutable + @AutoCodec + public static final class TransitiveModuleHeaders { + /** + * The module that we are calculating information for. + */ + private final Artifact module; + + /** + * The headers compiled into this module. + */ + private final ImmutableSet<Artifact> headers; + + /** + * This nested set contains 'module' as well as all targets it transitively depends on. + * If any of the 'headers' is used, all of these modules a required for the compilation. + */ + private final NestedSet<Artifact> transitiveModules; + + public TransitiveModuleHeaders( + Artifact module, + ImmutableSet<Artifact> headers, + NestedSet<Artifact> transitiveModules) { + this.module = module; + this.headers = headers; + this.transitiveModules = transitiveModules; + } + + public Artifact getModule() { + return module; + } + + public Collection<Artifact> getTransitiveModules() { + return transitiveModules.toCollection(); + } + } +} |