aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java918
1 files changed, 918 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java
new file mode 100644
index 0000000000..cf39ef57c5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContext.java
@@ -0,0 +1,918 @@
+// Copyright 2014 Google Inc. 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.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+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.util.Pair;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Immutable store of information needed for C++ compilation that is aggregated
+ * across dependencies.
+ */
+@Immutable
+public final class CppCompilationContext implements TransitiveInfoProvider {
+ /** An empty compilation context. */
+ public static final CppCompilationContext EMPTY = new Builder(null).build();
+
+ private final CommandLineContext commandLineContext;
+ private final ImmutableList<DepsContext> depsContexts;
+ private final CppModuleMap cppModuleMap;
+ private final Artifact headerModule;
+ private final Artifact picHeaderModule;
+ private final ImmutableSet<Artifact> compilationPrerequisites;
+
+ private CppCompilationContext(CommandLineContext commandLineContext,
+ List<DepsContext> depsContexts, CppModuleMap cppModuleMap, Artifact headerModule,
+ Artifact picHeaderModule) {
+ Preconditions.checkNotNull(commandLineContext);
+ Preconditions.checkArgument(!depsContexts.isEmpty());
+ this.commandLineContext = commandLineContext;
+ this.depsContexts = ImmutableList.copyOf(depsContexts);
+ this.cppModuleMap = cppModuleMap;
+ this.headerModule = headerModule;
+ this.picHeaderModule = picHeaderModule;
+
+ if (depsContexts.size() == 1) {
+ // Only LIPO targets have more than one DepsContexts. This codepath avoids creating
+ // an ImmutableSet.Builder for the vast majority of the cases.
+ compilationPrerequisites = (depsContexts.get(0).compilationPrerequisiteStampFile != null)
+ ? ImmutableSet.<Artifact>of(depsContexts.get(0).compilationPrerequisiteStampFile)
+ : ImmutableSet.<Artifact>of();
+ } else {
+ ImmutableSet.Builder<Artifact> prerequisites = ImmutableSet.builder();
+ for (DepsContext depsContext : depsContexts) {
+ if (depsContext.compilationPrerequisiteStampFile != null) {
+ prerequisites.add(depsContext.compilationPrerequisiteStampFile);
+ }
+ }
+ compilationPrerequisites = prerequisites.build();
+ }
+ }
+
+ /**
+ * Returns the compilation prerequisites consolidated into middlemen
+ * prerequisites, or an empty set if there are no prerequisites.
+ *
+ * <p>For correct dependency tracking, and to reduce the overhead to establish
+ * dependencies on generated headers, 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#mergeDependentContext(CppCompilationContext)}, and
+ * {@link Builder#mergeDependentContexts(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> getCompilationPrerequisites() {
+ 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 BuildConfiguration#getExecRoot}).
+ */
+ public ImmutableList<PathFragment> getIncludeDirs() {
+ return commandLineContext.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 BuildConfiguration#getExecRoot}).
+ */
+ public ImmutableList<PathFragment> getQuoteIncludeDirs() {
+ return commandLineContext.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 BuildConfiguration#getExecRoot}).
+ */
+ public ImmutableList<PathFragment> getSystemIncludeDirs() {
+ return commandLineContext.systemIncludeDirs;
+ }
+
+ /**
+ * Returns the immutable set of declared include directories, relative to a
+ * "-I" or "-iquote" directory" (possibly empty but never null). The returned
+ * collection may contain duplicate elements.
+ *
+ * <p>Note: The iteration order of this list is preserved as ide_build_info
+ * writes these directories and sources out and the ordering will help when
+ * used by consumers.
+ */
+ public NestedSet<PathFragment> getDeclaredIncludeDirs() {
+ if (depsContexts.isEmpty()) {
+ return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ }
+
+ if (depsContexts.size() == 1) {
+ return depsContexts.get(0).declaredIncludeDirs;
+ }
+
+ NestedSetBuilder<PathFragment> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.declaredIncludeDirs);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * 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). The returned collection may contain duplicate
+ * elements.
+ *
+ * <p>Note: The iteration order of this list is preserved as ide_build_info
+ * writes these directories and sources out and the ordering will help when
+ * used by consumers.
+ */
+ public NestedSet<PathFragment> getDeclaredIncludeWarnDirs() {
+ if (depsContexts.isEmpty()) {
+ return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ }
+
+ if (depsContexts.size() == 1) {
+ return depsContexts.get(0).declaredIncludeWarnDirs;
+ }
+
+ NestedSetBuilder<PathFragment> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.declaredIncludeWarnDirs);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns the immutable set of headers that have been declared in the
+ * {@code src} or {@code headers attribute} (possibly empty but never null).
+ * The returned collection may contain duplicate elements.
+ *
+ * <p>Note: The iteration order of this list is preserved as ide_build_info
+ * writes these directories and sources out and the ordering will help when
+ * used by consumers.
+ */
+ public NestedSet<Artifact> getDeclaredIncludeSrcs() {
+ if (depsContexts.isEmpty()) {
+ return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ }
+
+ if (depsContexts.size() == 1) {
+ return depsContexts.get(0).declaredIncludeSrcs;
+ }
+
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.declaredIncludeSrcs);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns the immutable pairs of (header file, pregrepped header file).
+ */
+ public NestedSet<Pair<Artifact, Artifact>> getPregreppedHeaders() {
+ if (depsContexts.isEmpty()) {
+ return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ }
+
+ if (depsContexts.size() == 1) {
+ return depsContexts.get(0).pregreppedHdrs;
+ }
+
+ NestedSetBuilder<Pair<Artifact, Artifact>> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.pregreppedHdrs);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns the immutable set of additional transitive inputs needed for
+ * compilation, like C++ module map artifacts.
+ */
+ public NestedSet<Artifact> getAdditionalInputs() {
+ if (depsContexts.isEmpty()) {
+ return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ }
+
+ if (depsContexts.size() == 1) {
+ return depsContexts.get(0).auxiliaryInputs;
+ }
+
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.auxiliaryInputs);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns optional inputs that are needed by any C++ compilations that use header modules.
+ *
+ * <p>For every target that the current target depends on transitively and that is built as header
+ * module, contains:
+ * <ul>
+ * <li>the pic/non-pic header module (pcm file)</li>
+ * <li>the transitive list of module maps.</li>
+ * </ul>
+ */
+ private NestedSet<Artifact> getTransitiveAuxiliaryInputs() {
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.transitiveAuxiliaryInputs);
+ }
+ return builder.build();
+ }
+
+ /**
+ * @return all modules maps in the transitive closure.
+ */
+ private NestedSet<Artifact> getTransitiveModuleMaps() {
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.transitiveModuleMaps);
+ }
+ return builder.build();
+ }
+
+ /**
+ * @return all headers whose transitive closure of includes needs to be
+ * available when compiling anything in the current target.
+ */
+ protected NestedSet<Artifact> getTransitiveHeaderModuleSrcs() {
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.transitiveHeaderModuleSrcs);
+ }
+ return builder.build();
+ }
+
+ /**
+ * @return all declared headers of the current module if the current target
+ * is compiled as a module.
+ */
+ protected NestedSet<Artifact> getHeaderModuleSrcs() {
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ for (DepsContext depsContext : depsContexts) {
+ builder.addTransitive(depsContext.headerModuleSrcs);
+ }
+ return builder.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 commandLineContext.defines;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof CppCompilationContext)) {
+ return false;
+ }
+ CppCompilationContext other = (CppCompilationContext) obj;
+ return Objects.equals(headerModule, other.headerModule)
+ && Objects.equals(picHeaderModule, other.picHeaderModule)
+ && commandLineContext.equals(other.commandLineContext)
+ && depsContexts.equals(other.depsContexts);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(headerModule, picHeaderModule, commandLineContext, depsContexts);
+ }
+
+ /**
+ * Returns a context that is based on a given context but returns empty sets
+ * for {@link #getDeclaredIncludeDirs()} and {@link #getDeclaredIncludeWarnDirs()}.
+ */
+ public static CppCompilationContext disallowUndeclaredHeaders(CppCompilationContext context) {
+ ImmutableList.Builder<DepsContext> builder = ImmutableList.builder();
+ for (DepsContext depsContext : context.depsContexts) {
+ builder.add(new DepsContext(
+ depsContext.compilationPrerequisiteStampFile,
+ NestedSetBuilder.<PathFragment>emptySet(Order.STABLE_ORDER),
+ NestedSetBuilder.<PathFragment>emptySet(Order.STABLE_ORDER),
+ depsContext.declaredIncludeSrcs,
+ depsContext.pregreppedHdrs,
+ depsContext.auxiliaryInputs,
+ depsContext.headerModuleSrcs,
+ depsContext.transitiveAuxiliaryInputs,
+ depsContext.transitiveHeaderModuleSrcs,
+ depsContext.transitiveModuleMaps));
+ }
+ return new CppCompilationContext(context.commandLineContext, builder.build(),
+ context.cppModuleMap, context.headerModule, context.picHeaderModule);
+ }
+
+ /**
+ * 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.
+
+ * 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 ownerContext the compilation context of the owner binary
+ * @param libContext the compilation context of the library
+ */
+ public static CppCompilationContext mergeForLipo(CppCompilationContext ownerContext,
+ CppCompilationContext libContext) {
+ return new CppCompilationContext(libContext.commandLineContext,
+ ImmutableList.copyOf(Iterables.concat(ownerContext.depsContexts, libContext.depsContexts)),
+ libContext.cppModuleMap, libContext.headerModule, libContext.picHeaderModule);
+ }
+
+ /**
+ * @return the C++ module map of the owner.
+ */
+ public CppModuleMap getCppModuleMap() {
+ return cppModuleMap;
+ }
+
+ /**
+ * @return the non-pic C++ header module of the owner.
+ */
+ private Artifact getHeaderModule() {
+ return headerModule;
+ }
+
+ /**
+ * @return the pic C++ header module of the owner.
+ */
+ private Artifact getPicHeaderModule() {
+ return picHeaderModule;
+ }
+
+ /**
+ * The parts of the compilation context that influence the command line of
+ * compilation actions.
+ */
+ @Immutable
+ private static class CommandLineContext {
+ private final ImmutableList<PathFragment> includeDirs;
+ private final ImmutableList<PathFragment> quoteIncludeDirs;
+ private final ImmutableList<PathFragment> systemIncludeDirs;
+ private final ImmutableList<String> defines;
+
+ CommandLineContext(ImmutableList<PathFragment> includeDirs,
+ ImmutableList<PathFragment> quoteIncludeDirs,
+ ImmutableList<PathFragment> systemIncludeDirs,
+ ImmutableList<String> defines) {
+ this.includeDirs = includeDirs;
+ this.quoteIncludeDirs = quoteIncludeDirs;
+ this.systemIncludeDirs = systemIncludeDirs;
+ this.defines = defines;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof CommandLineContext)) {
+ return false;
+ }
+ CommandLineContext other = (CommandLineContext) obj;
+ return Objects.equals(includeDirs, other.includeDirs)
+ && Objects.equals(quoteIncludeDirs, other.quoteIncludeDirs)
+ && Objects.equals(systemIncludeDirs, other.systemIncludeDirs)
+ && Objects.equals(defines, other.defines);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(includeDirs, quoteIncludeDirs, systemIncludeDirs, defines);
+ }
+ }
+
+ /**
+ * The parts of the compilation context that defined the dependencies of
+ * actions of scheduling and inclusion validity checking.
+ */
+ @Immutable
+ private static class DepsContext {
+ private final Artifact compilationPrerequisiteStampFile;
+ private final NestedSet<PathFragment> declaredIncludeDirs;
+ private final NestedSet<PathFragment> declaredIncludeWarnDirs;
+ private final NestedSet<Artifact> declaredIncludeSrcs;
+ private final NestedSet<Pair<Artifact, Artifact>> pregreppedHdrs;
+
+ /**
+ * Optional inputs that are used by some forms of compilation, containing:
+ * <ul>
+ * <li>module map of the current target</li>
+ * <li>module maps of all direct dependencies that are not compiled as header modules</li>
+ * <li>all transitiveAuxiliaryInputs.</li>
+ * </ul>
+ */
+ private final NestedSet<Artifact> auxiliaryInputs;
+
+ /**
+ * All declared headers of the current module, if compiled as a header module.
+ */
+ private final NestedSet<Artifact> headerModuleSrcs;
+
+ private final NestedSet<Artifact> transitiveAuxiliaryInputs;
+
+ /**
+ * Headers whose transitive closure of includes needs to be available when compiling the current
+ * target. For every target that the current target depends on transitively and that is built as
+ * header module, contains all headers that are part of its header module.
+ */
+ private final NestedSet<Artifact> transitiveHeaderModuleSrcs;
+
+ /**
+ * The module maps from all targets the current target depends on transitively.
+ */
+ private final NestedSet<Artifact> transitiveModuleMaps;
+
+ DepsContext(Artifact compilationPrerequisiteStampFile,
+ NestedSet<PathFragment> declaredIncludeDirs,
+ NestedSet<PathFragment> declaredIncludeWarnDirs,
+ NestedSet<Artifact> declaredIncludeSrcs,
+ NestedSet<Pair<Artifact, Artifact>> pregreppedHdrs,
+ NestedSet<Artifact> auxiliaryInputs,
+ NestedSet<Artifact> headerModuleSrcs,
+ NestedSet<Artifact> transitiveAuxiliaryInputs,
+ NestedSet<Artifact> transitiveHeaderModuleSrcs,
+ NestedSet<Artifact> transitiveModuleMaps) {
+ this.compilationPrerequisiteStampFile = compilationPrerequisiteStampFile;
+ this.declaredIncludeDirs = declaredIncludeDirs;
+ this.declaredIncludeWarnDirs = declaredIncludeWarnDirs;
+ this.declaredIncludeSrcs = declaredIncludeSrcs;
+ this.pregreppedHdrs = pregreppedHdrs;
+ this.auxiliaryInputs = auxiliaryInputs;
+ this.headerModuleSrcs = headerModuleSrcs;
+ this.transitiveAuxiliaryInputs = transitiveAuxiliaryInputs;
+ this.transitiveHeaderModuleSrcs = transitiveHeaderModuleSrcs;
+ this.transitiveModuleMaps = transitiveModuleMaps;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof DepsContext)) {
+ return false;
+ }
+ DepsContext other = (DepsContext) obj;
+ return Objects.equals(
+ compilationPrerequisiteStampFile, other.compilationPrerequisiteStampFile)
+ && Objects.equals(declaredIncludeDirs, other.declaredIncludeDirs)
+ && Objects.equals(declaredIncludeWarnDirs, other.declaredIncludeWarnDirs)
+ && Objects.equals(declaredIncludeSrcs, other.declaredIncludeSrcs)
+ && Objects.equals(auxiliaryInputs, other.auxiliaryInputs)
+ && Objects.equals(headerModuleSrcs, other.headerModuleSrcs)
+ // Due to the NestedSet equals being ==, and the code flow only setting them if at least
+ // auxiliaryInputs is set, these checks cannot be executed. We leave them in so the equals
+ // is still correct if that connection ever changes.R
+ && Objects.equals(transitiveAuxiliaryInputs, other.transitiveAuxiliaryInputs)
+ && Objects.equals(transitiveHeaderModuleSrcs, other.transitiveHeaderModuleSrcs)
+ && Objects.equals(transitiveModuleMaps, other.transitiveModuleMaps)
+ ;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(compilationPrerequisiteStampFile,
+ declaredIncludeDirs,
+ declaredIncludeWarnDirs,
+ declaredIncludeSrcs,
+ auxiliaryInputs,
+ headerModuleSrcs,
+ transitiveAuxiliaryInputs,
+ transitiveHeaderModuleSrcs,
+ transitiveModuleMaps);
+ }
+ }
+
+ /**
+ * Builder class for {@link CppCompilationContext}.
+ */
+ public static class Builder {
+ private String purpose = "cpp_compilation_prerequisites";
+ 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<Pair<Artifact, Artifact>> pregreppedHdrs =
+ NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> auxiliaryInputs =
+ NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> headerModuleSrcs =
+ NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> transitiveAuxiliaryInputs =
+ NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> transitiveHeaderModuleSrcs =
+ NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> transitiveModuleMaps =
+ NestedSetBuilder.stableOrder();
+ private final Set<String> defines = new LinkedHashSet<>();
+ private CppModuleMap cppModuleMap;
+ private Artifact headerModule;
+ private Artifact picHeaderModule;
+
+ /** The rule that owns the context */
+ private final RuleContext ruleContext;
+
+ /**
+ * Creates a new builder for a {@link CppCompilationContext} instance.
+ */
+ public Builder(RuleContext ruleContext) {
+ this.ruleContext = ruleContext;
+ }
+
+ /**
+ * Overrides the purpose of this context. This is useful if a Target
+ * needs more than one CppCompilationContext. (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 context of a dependency into this one by adding the contents
+ * of all of its attributes.
+ */
+ public Builder mergeDependentContext(CppCompilationContext otherContext) {
+ Preconditions.checkNotNull(otherContext);
+ compilationPrerequisites.addAll(otherContext.getCompilationPrerequisites());
+ includeDirs.addAll(otherContext.getIncludeDirs());
+ quoteIncludeDirs.addAll(otherContext.getQuoteIncludeDirs());
+ systemIncludeDirs.addAll(otherContext.getSystemIncludeDirs());
+ declaredIncludeDirs.addTransitive(otherContext.getDeclaredIncludeDirs());
+ declaredIncludeWarnDirs.addTransitive(otherContext.getDeclaredIncludeWarnDirs());
+ declaredIncludeSrcs.addTransitive(otherContext.getDeclaredIncludeSrcs());
+ pregreppedHdrs.addTransitive(otherContext.getPregreppedHeaders());
+
+ // Forward transitive information.
+ transitiveAuxiliaryInputs.addTransitive(otherContext.getTransitiveAuxiliaryInputs());
+ transitiveModuleMaps.addTransitive(otherContext.getTransitiveModuleMaps());
+ transitiveHeaderModuleSrcs.addTransitive(otherContext.getTransitiveHeaderModuleSrcs());
+
+ // All module maps of direct dependencies are inputs to the current compile independently of
+ // the build type.
+ if (otherContext.getCppModuleMap() != null) {
+ auxiliaryInputs.add(otherContext.getCppModuleMap().getArtifact());
+ }
+ if (otherContext.getHeaderModule() != null || otherContext.getPicHeaderModule() != null) {
+ // If we depend directly on a target that has a compiled header module, all targets
+ // transitively depending on us will need that header module, and all transitive module
+ // maps.
+ if (otherContext.getHeaderModule() != null) {
+ transitiveAuxiliaryInputs.add(otherContext.getHeaderModule());
+ }
+ if (otherContext.getPicHeaderModule() != null) {
+ transitiveAuxiliaryInputs.add(otherContext.getPicHeaderModule());
+ }
+ transitiveAuxiliaryInputs.addAll(otherContext.getTransitiveModuleMaps());
+
+ // All targets transitively depending on us will need to have the full transitive #include
+ // closure of the headers in that module available.
+ transitiveHeaderModuleSrcs.addAll(otherContext.getHeaderModuleSrcs());
+ }
+ // All compile actions in the current target will need the transitive inputs.
+ auxiliaryInputs.addAll(transitiveAuxiliaryInputs.build().toCollection());
+
+ defines.addAll(otherContext.getDefines());
+ return this;
+ }
+
+ /**
+ * Merges the context of some targets into this one by adding the contents
+ * of all of their attributes. Targets that do not implement
+ * {@link CppCompilationContext} are ignored.
+ */
+ public Builder mergeDependentContexts(Iterable<CppCompilationContext> targets) {
+ for (CppCompilationContext target : targets) {
+ mergeDependentContext(target);
+ }
+ return this;
+ }
+
+ /**
+ * Adds multiple compilation prerequisites.
+ */
+ public Builder addCompilationPrerequisites(Iterable<Artifact> prerequisites) {
+ // LIPO collector must not add compilation prerequisites in order to avoid
+ // the creation of a middleman action.
+ 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 BuildConfiguration#getExecRoot}) or
+ * absolute. Before it is stored, the include directory is normalized.
+ */
+ public Builder addIncludeDir(PathFragment includeDir) {
+ includeDirs.add(includeDir.normalize());
+ return this;
+ }
+
+ /**
+ * Add multiple include directories to be added with "-I". These can be
+ * either relative to the exec root (see {@link
+ * BuildConfiguration#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
+ * BuildConfiguration#getExecRoot}) or absolute. Before it is stored, the
+ * include directory is normalized.
+ */
+ public Builder addQuoteIncludeDir(PathFragment quoteIncludeDir) {
+ quoteIncludeDirs.add(quoteIncludeDir.normalize());
+ return this;
+ }
+
+ /**
+ * Add a single include directory to be added with "-isystem". It can be
+ * either relative to the exec root (see {@link
+ * BuildConfiguration#getExecRoot}) or absolute. Before it is stored, the
+ * include directory is normalized.
+ */
+ public Builder addSystemIncludeDir(PathFragment systemIncludeDir) {
+ systemIncludeDirs.add(systemIncludeDir.normalize());
+ 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.
+ */
+ public Builder addDeclaredIncludeSrc(Artifact header) {
+ declaredIncludeSrcs.add(header);
+ compilationPrerequisites.add(header);
+ headerModuleSrcs.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.
+ */
+ public Builder addDeclaredIncludeSrcs(Iterable<Artifact> declaredIncludeSrcs) {
+ this.declaredIncludeSrcs.addAll(declaredIncludeSrcs);
+ this.headerModuleSrcs.addAll(declaredIncludeSrcs);
+ return addCompilationPrerequisites(declaredIncludeSrcs);
+ }
+
+ /**
+ * Add a map of generated source or header Artifact to an output Artifact after grepping
+ * the file for include statements.
+ */
+ public Builder addPregreppedHeaderMap(Map<Artifact, Artifact> pregrepped) {
+ addCompilationPrerequisites(pregrepped.values());
+ for (Map.Entry<Artifact, Artifact> entry : pregrepped.entrySet()) {
+ this.pregreppedHdrs.add(Pair.of(entry.getKey(), entry.getValue()));
+ }
+ 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++ header module in non-pic mode.
+ */
+ public Builder setHeaderModule(Artifact headerModule) {
+ this.headerModule = headerModule;
+ return this;
+ }
+
+ /**
+ * Sets the C++ header module in pic mode.
+ */
+ public Builder setPicHeaderModule(Artifact picHeaderModule) {
+ this.picHeaderModule = picHeaderModule;
+ return this;
+ }
+
+ /**
+ * Builds the {@link CppCompilationContext}.
+ */
+ public CppCompilationContext build() {
+ return build(
+ ruleContext == null ? null : ruleContext.getActionOwner(),
+ ruleContext == null ? null : ruleContext.getAnalysisEnvironment().getMiddlemanFactory());
+ }
+
+ @VisibleForTesting // productionVisibility = Visibility.PRIVATE
+ public CppCompilationContext build(ActionOwner owner, MiddlemanFactory middlemanFactory) {
+ if (cppModuleMap != null) {
+ // .cppmap files should also be mandatory inputs for compile actions
+ auxiliaryInputs.add(cppModuleMap.getArtifact());
+ transitiveModuleMaps.add(cppModuleMap.getArtifact());
+ }
+
+ // 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 CppCompilationContext(
+ new CommandLineContext(ImmutableList.copyOf(includeDirs),
+ ImmutableList.copyOf(quoteIncludeDirs), ImmutableList.copyOf(systemIncludeDirs),
+ ImmutableList.copyOf(defines)),
+ ImmutableList.of(new DepsContext(prerequisiteStampFile,
+ declaredIncludeDirs.build(),
+ declaredIncludeWarnDirs.build(),
+ declaredIncludeSrcs.build(),
+ pregreppedHdrs.build(),
+ auxiliaryInputs.build(),
+ headerModuleSrcs.build(),
+ transitiveAuxiliaryInputs.build(),
+ transitiveHeaderModuleSrcs.build(),
+ transitiveModuleMaps.build())),
+ cppModuleMap,
+ headerModule,
+ picHeaderModule);
+ }
+
+ /**
+ * 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.
+ return middlemanFactory.createErrorPropagatingMiddleman(
+ owner, ruleContext.getLabel().toString(), purpose,
+ ImmutableList.copyOf(compilationPrerequisites),
+ ruleContext.getConfiguration().getMiddlemanDirectory());
+ }
+
+ /**
+ * 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());
+ }
+ }
+}