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.