Migrate ObjcProvider compile info to CcCompilationContext

See https://github.com/bazelbuild/bazel/issues/10674.

RELNOTES: None
PiperOrigin-RevId: 295898085
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppSemantics.java
index 20879e4..5733e07 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppSemantics.java
@@ -14,14 +14,10 @@
 
 package com.google.devtools.build.lib.bazel.rules.cpp;
 
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 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.packages.Provider;
 import com.google.devtools.build.lib.packages.SkylarkProvider.SkylarkKey;
 import com.google.devtools.build.lib.packages.StructImpl;
@@ -76,11 +72,6 @@
   }
 
   @Override
-  public NestedSet<Artifact> getAdditionalPrunableIncludes() {
-    return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
-  }
-
-  @Override
   public HeadersCheckingMode determineHeadersCheckingMode(RuleContext ruleContext) {
     return HeadersCheckingMode.STRICT;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
index 940f88a..2cca679 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
@@ -965,12 +965,10 @@
     private final Artifact.DerivedArtifact picHeaderModule;
 
     /** All header files that are compiled into this module. */
-    // Note: this change will be reverted in the migration cl.
-    public final ImmutableList<Artifact> modularHeaders;
+    private final ImmutableList<Artifact> modularHeaders;
 
     /** All header files that are contained in this module. */
-    // Note: this change will be reverted in the migration cl.
-    public final ImmutableList<Artifact> textualHeaders;
+    private final ImmutableList<Artifact> textualHeaders;
 
     HeaderInfo(
         Artifact.DerivedArtifact headerModule,
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
index 079185f..c135a77 100644
--- 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
@@ -444,17 +444,6 @@
   }
 
   /**
-   * Add directly to privateHeaders, which are added to the compilation prerequisites and can be
-   * removed by include scanning. This is only used to work around cases where we are not
-   * propagating transitive dependencies properly via scheduling dependency middleman (i.e. objc
-   * compiles).
-   */
-  public CcCompilationHelper addPrivateHeadersUnchecked(Collection<Artifact> privateHeaders) {
-    this.privateHeaders.addAll(privateHeaders);
-    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.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index 80a98e0..2485bff 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -56,7 +56,6 @@
 import com.google.devtools.build.lib.analysis.skylark.Args;
 import com.google.devtools.build.lib.cmdline.LabelConstants;
 import com.google.devtools.build.lib.collect.CollectionUtils;
-import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet;
 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;
@@ -67,7 +66,6 @@
 import com.google.devtools.build.lib.profiler.ProfilerTask;
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter;
-import com.google.devtools.build.lib.rules.cpp.CcCompilationContext.IncludeScanningHeaderDataHelper;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
 import com.google.devtools.build.lib.rules.cpp.IncludeScanner.IncludeScanningHeaderData;
 import com.google.devtools.build.lib.skyframe.ActionExecutionValue;
@@ -92,8 +90,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -199,8 +195,6 @@
   private ParamFileActionInput paramFileActionInput;
   private PathFragment paramFilePath;
 
-  private final Iterable<Artifact> alternateIncludeScanningDataInputs;
-
   /**
    * Creates a new action to compile C/C++ source files.
    *
@@ -310,7 +304,6 @@
               .getParentDirectory()
               .getChild(outputFile.getFilename() + ".params");
     }
-    this.alternateIncludeScanningDataInputs = cppSemantics.getAlternateIncludeScanningDataInputs();
   }
 
   static CompileCommandLine buildCommandLine(
@@ -515,60 +508,6 @@
    * This method returns null when a required SkyValue is missing and a Skyframe restart is
    * required.
    */
-  // Note: this function will be deleted in the migration cl.
-  @Nullable
-  private static IncludeScanningHeaderData.Builder createIncludeScanningHeaderData(
-      SkyFunction.Environment env,
-      List<CcCompilationContext.HeaderInfo> headerInfos,
-      Iterable<Artifact> inputs)
-      throws InterruptedException {
-    Map<PathFragment, Artifact> pathToLegalOutputArtifact = new HashMap<>();
-    ArrayList<Artifact> treeArtifacts = new ArrayList<>();
-    // Not using range-based for loops here and below as the additional overhead of the
-    // ImmutableList iterators has shown up in profiles.
-    for (CcCompilationContext.HeaderInfo headerInfo : headerInfos) {
-      for (Artifact a : headerInfo.modularHeaders) {
-        IncludeScanningHeaderDataHelper.handleArtifact(a, pathToLegalOutputArtifact, treeArtifacts);
-      }
-      for (Artifact a : headerInfo.textualHeaders) {
-        IncludeScanningHeaderDataHelper.handleArtifact(a, pathToLegalOutputArtifact, treeArtifacts);
-      }
-    }
-    for (Artifact a : inputs) {
-      IncludeScanningHeaderDataHelper.handleArtifact(a, pathToLegalOutputArtifact, treeArtifacts);
-    }
-    if (!IncludeScanningHeaderDataHelper.handleTreeArtifacts(
-        env, pathToLegalOutputArtifact, treeArtifacts)) {
-      return null;
-    }
-    return new IncludeScanningHeaderData.Builder(
-        Collections.unmodifiableMap(pathToLegalOutputArtifact),
-        Collections.unmodifiableSet(CompactHashSet.create()));
-  }
-
-  /**
-   * This method returns null when a required SkyValue is missing and a Skyframe restart is
-   * required.
-   */
-  @Nullable
-  public IncludeScanningHeaderData.Builder createIncludeScanningHeaderData(
-      SkyFunction.Environment env,
-      boolean usePic,
-      boolean useHeaderModules,
-      List<CcCompilationContext.HeaderInfo> headerInfo)
-      throws InterruptedException {
-    if (alternateIncludeScanningDataInputs != null) {
-      return createIncludeScanningHeaderData(env, headerInfo, alternateIncludeScanningDataInputs);
-    } else {
-      return ccCompilationContext.createIncludeScanningHeaderData(
-          env, usePic, useHeaderModules, headerInfo);
-    }
-  }
-
-  /**
-   * This method returns null when a required SkyValue is missing and a Skyframe restart is
-   * required.
-   */
   @Nullable
   @Override
   public NestedSet<Artifact> discoverInputs(ActionExecutionContext actionExecutionContext)
@@ -593,7 +532,7 @@
       List<CcCompilationContext.HeaderInfo> headerInfo =
           ccCompilationContext.getTransitiveHeaderInfos();
       IncludeScanningHeaderData.Builder includeScanningHeaderData =
-          createIncludeScanningHeaderData(
+          ccCompilationContext.createIncludeScanningHeaderData(
               actionExecutionContext.getEnvironmentForDiscoveringInputs(),
               usePic,
               useHeaderModules,
@@ -1612,7 +1551,7 @@
       throws ActionExecutionException, InterruptedException {
     try {
       IncludeScanningHeaderData.Builder includeScanningHeaderData =
-          createIncludeScanningHeaderData(
+          ccCompilationContext.createIncludeScanningHeaderData(
               actionExecutionContext.getEnvironmentForDiscoveringInputs(),
               usePic,
               useHeaderModules,
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 d4fd87c..8c1d2d6 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
@@ -362,9 +362,7 @@
   }
 
   NestedSet<Artifact> buildPrunableHeaders() {
-    return NestedSetBuilder.fromNestedSet(cppSemantics.getAdditionalPrunableIncludes())
-        .addAll(additionalPrunableHeaders)
-        .build();
+    return NestedSetBuilder.<Artifact>stableOrder().addAll(additionalPrunableHeaders).build();
   }
 
   NestedSet<Artifact> buildInputsForInvalidation() {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSemantics.java
index 55f793c..72b9fa3 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSemantics.java
@@ -14,11 +14,9 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
 import com.google.devtools.build.lib.rules.cpp.CppConfiguration.HeadersCheckingMode;
@@ -36,16 +34,6 @@
       FeatureConfiguration featureConfiguration,
       CppCompileActionBuilder actionBuilder);
 
-  /**
-   * Returns the set of includes which are not mandatory and may be pruned by include processing.
-   */
-  NestedSet<Artifact> getAdditionalPrunableIncludes();
-
-  /** Return an alternate source of inputs for constructing the include scanning data. */
-  default Iterable<Artifact> getAlternateIncludeScanningDataInputs() {
-    return null;
-  }
-
   /** Determines the applicable mode of headers checking for the passed in ruleContext. */
   HeadersCheckingMode determineHeadersCheckingMode(RuleContext ruleContext);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
index 2400ac6..8a42747f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
@@ -201,7 +201,7 @@
             platform);
 
     ObjcProvider.Builder objcProviderBuilder =
-        new ObjcProvider.Builder(ruleContext.getAnalysisEnvironment().getSkylarkSemantics());
+        new ObjcProvider.NativeBuilder(ruleContext.getAnalysisEnvironment().getSkylarkSemantics());
     for (DependencySpecificConfiguration dependencySpecificConfiguration :
         dependencySpecificConfigurations) {
       objcProviderBuilder.addTransitiveAndPropagate(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
index 7f446cc..10a327e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
@@ -71,6 +71,10 @@
       "Value for key %s must be a set of %s, instead found %s.";
 
   @VisibleForTesting
+  public static final String BAD_FRAMEWORK_PATH_ERROR =
+      "Value for key framework_search_paths must end in .framework; instead found %s.";
+
+  @VisibleForTesting
   public static final String BAD_PROVIDERS_ITER_ERROR =
       "Value for argument 'providers' must be a list of ObjcProvider instances, instead found %s.";
 
@@ -187,7 +191,8 @@
   // This method is registered statically for skylark, and never called directly.
   public ObjcProvider newObjcProvider(Boolean usesSwift, Dict<?, ?> kwargs, StarlarkThread thread)
       throws EvalException {
-    ObjcProvider.Builder resultBuilder = new ObjcProvider.Builder(thread.getSemantics());
+    ObjcProvider.StarlarkBuilder resultBuilder =
+        new ObjcProvider.StarlarkBuilder(thread.getSemantics());
     if (usesSwift) {
       resultBuilder.add(ObjcProvider.FLAG, ObjcProvider.Flag.USES_SWIFT);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleStaticLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleStaticLibrary.java
index 1735084..2e7f19d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleStaticLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleStaticLibrary.java
@@ -100,7 +100,7 @@
             .add(ruleIntermediateArtifacts.combinedArchitectureArchive());
 
     ObjcProvider.Builder objcProviderBuilder =
-        new ObjcProvider.Builder(ruleContext.getAnalysisEnvironment().getSkylarkSemantics());
+        new ObjcProvider.NativeBuilder(ruleContext.getAnalysisEnvironment().getSkylarkSemantics());
 
     ImmutableListMultimap<BuildConfiguration, ObjcProtoProvider> objcProtoProvidersByConfig =
         ruleContext.getPrerequisitesByConfiguration(
@@ -146,7 +146,8 @@
               protosObjcProvider);
       ObjcProvider objcProvider =
           common
-              .getObjcProvider()
+              .getObjcProviderBuilder()
+              .build()
               .subtractSubtrees(
                   cpuToObjcAvoidDepsMap.get(childCpu),
                   cpuToCcAvoidDepsMap.get(childCpu).stream()
@@ -220,7 +221,7 @@
 
     CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder().build();
 
-    return new ObjcCommon.Builder(ruleContext, buildConfiguration)
+    return new ObjcCommon.Builder(ObjcCommon.Purpose.LINK_ONLY, ruleContext, buildConfiguration)
         .setCompilationAttributes(
             CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
         .setCompilationArtifacts(compilationArtifacts)
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 054bf65..a81f873 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
@@ -14,17 +14,14 @@
 
 package com.google.devtools.build.lib.rules.objc;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
 import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates;
 import static com.google.devtools.build.lib.rules.cpp.Link.LINK_LIBRARY_FILETYPES;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.DEFINE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.DYNAMIC_FRAMEWORK_FILE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_LIBRARY;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_SEARCH_PATHS;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE_SYSTEM;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IQUOTE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LINK_INPUTS;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_DYLIB;
@@ -42,7 +39,6 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
@@ -249,19 +245,6 @@
   public static final SafeImplicitOutputsFunction FULLY_LINKED_LIB =
       fromTemplates("%{name}_fully_linked.a");
 
-  /**
-   * Returns additional inputs to include processing, outside of the headers provided by
-   * ObjProvider.
-   */
-  private Iterable<Artifact> getExtraIncludeProcessingInputs(
-      Collection<Artifact> privateHdrs, Artifact pchHdr) {
-    Iterable<Artifact> extraInputs = privateHdrs;
-    if (pchHdr != null) {
-      extraInputs = Iterables.concat(extraInputs, ImmutableList.of(pchHdr));
-    }
-    return extraInputs;
-  }
-
   /** Create and return the include processing to be used. */
   private IncludeProcessing createIncludeProcessing() {
     switch (includeProcessingType) {
@@ -272,6 +255,14 @@
     }
   }
 
+  private static ImmutableList<String> pathsToIncludeArgs(Iterable<PathFragment> paths) {
+    ImmutableList.Builder<String> builder = ImmutableList.<String>builder();
+    for (PathFragment path : paths) {
+      builder.add("-I" + path);
+    }
+    return builder.build();
+  }
+
   private CompilationInfo compile(
       ObjcCompilationContext objcCompilationContext,
       VariablesExtension extension,
@@ -282,13 +273,11 @@
       Collection<Artifact> sources,
       Collection<Artifact> privateHdrs,
       Collection<Artifact> publicHdrs,
-      Collection<Artifact> dependentGeneratedHdrs,
       Artifact pchHdr,
       ObjcCppSemantics semantics,
       String purpose,
       boolean generateModuleMap)
       throws RuleErrorException, InterruptedException {
-    ObjcProvider depObjcProvidersSummary = objcCompilationContext.getDepObjcProvidersSummary();
     CcCompilationHelper result =
         new CcCompilationHelper(
                 ruleContext,
@@ -307,17 +296,12 @@
             .addPublicHeaders(publicHdrs)
             .addPublicTextualHeaders(objcCompilationContext.getPublicTextualHeaders())
             .addPrivateHeaders(privateHdrs)
-            .addPrivateHeadersUnchecked(dependentGeneratedHdrs)
-            .addDefines(depObjcProvidersSummary.get(DEFINE))
-            .addDefines(objcCompilationContext.getDefines())
+            .addDefines(
+                NestedSetBuilder.wrap(Order.LINK_ORDER, objcCompilationContext.getDefines()))
             .addIncludeDirs(priorityHeaders)
             .addIncludeDirs(objcCompilationContext.getIncludes())
-            .addIncludeDirs(depObjcProvidersSummary.get(INCLUDE))
             .addSystemIncludeDirs(objcCompilationContext.getSystemIncludes())
-            .addSystemIncludeDirs(depObjcProvidersSummary.get(INCLUDE_SYSTEM))
             .addQuoteIncludeDirs(objcCompilationContext.getQuoteIncludes())
-            .addQuoteIncludeDirs(depObjcProvidersSummary.get(IQUOTE))
-            .addFrameworkIncludeDirs(frameworkHeaderSearchPathFragments(depObjcProvidersSummary))
             .addCcCompilationContexts(objcCompilationContext.getDepCcCompilationContexts())
             .setCopts(
                 ImmutableList.<String>builder()
@@ -327,6 +311,8 @@
                             .getFragment(ObjcConfiguration.class)
                             .getCoptsForCompilationMode())
                     .addAll(extraCompileArgs)
+                    .addAll(
+                        pathsToIncludeArgs(objcCompilationContext.getStrictDependencyIncludes()))
                     .build())
             .setCppModuleMap(intermediateArtifacts.moduleMap())
             .setPropagateModuleMapToCompileAction(false)
@@ -336,7 +322,7 @@
             .setHeadersCheckingMode(semantics.determineHeadersCheckingMode(ruleContext));
 
     if (pchHdr != null) {
-      result.addAdditionalInputs(ImmutableList.of(pchHdr));
+      result.addPublicTextualHeaders(ImmutableList.of(pchHdr));
     }
 
     if (getCustomModuleMap(ruleContext).isPresent() || !generateModuleMap) {
@@ -346,7 +332,34 @@
     return result.compile();
   }
 
-  private Pair<CcCompilationOutputs, ImmutableMap<String, NestedSet<Artifact>>> ccCompileAndLink(
+  private static class CompilationResult {
+    private final CcCompilationContext ccCompilationContext;
+    private final CcCompilationOutputs ccCompilationOutputs;
+    private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
+
+    public CompilationResult(
+        CcCompilationContext ccCompilationContext,
+        CcCompilationOutputs ccCompilationOutputs,
+        ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
+      this.ccCompilationContext = ccCompilationContext;
+      this.ccCompilationOutputs = ccCompilationOutputs;
+      this.outputGroups = outputGroups;
+    }
+
+    public CcCompilationContext getCcCompilationContext() {
+      return ccCompilationContext;
+    }
+
+    public CcCompilationOutputs getCcCompilationOutputs() {
+      return ccCompilationOutputs;
+    }
+
+    public ImmutableMap<String, NestedSet<Artifact>> getOutputGroups() {
+      return outputGroups;
+    }
+  }
+
+  private CompilationResult ccCompileAndLink(
       ObjcCompilationContext objcCompilationContext,
       CompilationArtifacts compilationArtifacts,
       ObjcVariablesExtension.Builder extensionBuilder,
@@ -369,22 +382,8 @@
                 attributes.hdrs().toList().stream(),
                 compilationArtifacts.getAdditionalHdrs().toList().stream())
             .collect(toImmutableSortedSet(naturalOrder()));
-    // This is a hack to inject generated headers into the action graph for include scanning.  This
-    // is supposed to be done via the compilation prerequisite middleman artifact of dependent
-    // CcCompilationContexts, but ObjcProvider does not propagate that.  This issue will go away
-    // when we finish migrating the compile info in ObjcProvider to CcCompilationContext.
-    //
-    // To limit the extra work we're adding, we only add what is required, i.e. the generated
-    // headers.  Headers from own rule's attributes and from dependent CcCompilationContext are
-    // already accounted for, so we only need the ones from depObjcProviders.
-    Collection<Artifact> dependentGeneratedHdrs =
-        (includeProcessingType == IncludeProcessingType.INCLUDE_SCANNING)
-            ? objcCompilationContext.getDepObjcProvidersSummary().getGeneratedHeaderList()
-            : ImmutableList.of();
     Artifact pchHdr = getPchFile().orNull();
-    ObjcCppSemantics semantics =
-        createObjcCppSemantics(
-            objcCompilationContext.getDepObjcProvidersSummary(), privateHdrs, pchHdr);
+    ObjcCppSemantics semantics = createObjcCppSemantics();
 
     String purpose = String.format("%s_objc_arc", semantics.getPurpose());
     extensionBuilder.setArcEnabled(true);
@@ -399,7 +398,6 @@
             arcSources,
             privateHdrs,
             publicHdrs,
-            dependentGeneratedHdrs,
             pchHdr,
             semantics,
             purpose,
@@ -418,7 +416,6 @@
             nonArcSources,
             privateHdrs,
             publicHdrs,
-            dependentGeneratedHdrs,
             pchHdr,
             semantics,
             purpose,
@@ -511,17 +508,16 @@
     Map<String, NestedSet<Artifact>> mergedOutputGroups =
         CcCommon.mergeOutputGroups(ImmutableList.of(arcOutputGroups, nonArcOutputGroups));
 
-    return new Pair<>(compilationOutputsBuilder.build(), ImmutableMap.copyOf(mergedOutputGroups));
+    return new CompilationResult(
+        ccCompilationContextBuilder.build(),
+        compilationOutputsBuilder.build(),
+        ImmutableMap.copyOf(mergedOutputGroups));
   }
 
-  ObjcCppSemantics createObjcCppSemantics(
-      ObjcProvider objcProvider, Collection<Artifact> privateHdrs, Artifact pchHdr) {
-    Iterable<Artifact> extraInputs = getExtraIncludeProcessingInputs(privateHdrs, pchHdr);
+  ObjcCppSemantics createObjcCppSemantics() {
     return new ObjcCppSemantics(
-        objcProvider,
         includeProcessingType,
         createIncludeProcessing(),
-        extraInputs,
         ruleContext.getFragment(ObjcConfiguration.class),
         intermediateArtifacts,
         buildConfiguration,
@@ -666,11 +662,6 @@
         .build();
   }
 
-  /** Returns a list of framework header search path fragments. */
-  static ImmutableList<PathFragment> frameworkHeaderSearchPathFragments(ObjcProvider provider) {
-    return uniqueParentDirectories(provider.get(FRAMEWORK_SEARCH_PATHS)).asList();
-  }
-
   /** Returns a list of framework library search paths. */
   static ImmutableList<String> frameworkLibrarySearchPaths(ObjcProvider provider) {
     ImmutableList.Builder<String> searchPaths = new ImmutableList.Builder<>();
@@ -694,6 +685,17 @@
   private final boolean isTestRule;
   private final boolean usePch;
   private final IncludeProcessingType includeProcessingType;
+  private Optional<ObjcProvider> objcProvider;
+
+  private void setObjcProvider(ObjcProvider objcProvider) {
+    checkState(!this.objcProvider.isPresent());
+    this.objcProvider = Optional.of(objcProvider);
+  }
+
+  public ObjcProvider getObjcProvider() {
+    checkState(objcProvider.isPresent());
+    return objcProvider.get();
+  }
 
   /**
    * Creates a new compilation support for the given rule and build configuration.
@@ -728,6 +730,7 @@
     this.isTestRule = isTestRule;
     this.outputGroupCollector = outputGroupCollector;
     this.objectFilesCollector = objectFilesCollector;
+    this.objcProvider = Optional.absent();
     this.usePch = usePch;
     if (toolchain == null
         && ruleContext
@@ -833,7 +836,7 @@
 
     /** Returns a {@link CompilationSupport} instance. */
     public CompilationSupport build() throws InterruptedException {
-      Preconditions.checkNotNull(ruleContext, "CompilationSupport is missing RuleContext");
+      checkNotNull(ruleContext, "CompilationSupport is missing RuleContext");
 
       if (buildConfiguration == null) {
         buildConfiguration = ruleContext.getConfiguration();
@@ -948,6 +951,7 @@
     return registerCompileAndArchiveActions(
         compilationArtifacts,
         objcCompilationContext,
+        Optional.absent(),
         ExtraCompileArgs.NONE,
         ImmutableList.<PathFragment>of());
   }
@@ -992,11 +996,12 @@
   private CompilationSupport registerCompileAndArchiveActions(
       CompilationArtifacts compilationArtifacts,
       ObjcCompilationContext objcCompilationContext,
+      Optional<ObjcCommon> objcCommon,
       ExtraCompileArgs extraCompileArgs,
       List<PathFragment> priorityHeaders)
       throws RuleErrorException, InterruptedException {
-    Preconditions.checkNotNull(toolchain);
-    Preconditions.checkNotNull(toolchain.getFdoContext());
+    checkNotNull(toolchain);
+    checkNotNull(toolchain.getFdoContext());
     ObjcVariablesExtension.Builder extension =
         new ObjcVariablesExtension.Builder()
             .setRuleContext(ruleContext)
@@ -1004,14 +1009,14 @@
             .setIntermediateArtifacts(intermediateArtifacts)
             .setConfiguration(buildConfiguration);
 
-    Pair<CcCompilationOutputs, ImmutableMap<String, NestedSet<Artifact>>> compilationInfo;
+    CompilationResult compilationResult;
 
     if (compilationArtifacts.getArchive().isPresent()) {
       Artifact objList = intermediateArtifacts.archiveObjList();
 
       extension.addVariableCategory(VariableCategory.ARCHIVE_VARIABLES);
 
-      compilationInfo =
+      compilationResult =
           ccCompileAndLink(
               objcCompilationContext,
               compilationArtifacts,
@@ -1025,10 +1030,11 @@
 
       // TODO(b/30783125): Signal the need for this action in the CROSSTOOL.
       registerObjFilelistAction(
-          ImmutableSet.copyOf(compilationInfo.getFirst().getObjectFiles(/* usePic= */ false)),
+          ImmutableSet.copyOf(
+              compilationResult.getCcCompilationOutputs().getObjectFiles(/* usePic= */ false)),
           objList);
     } else {
-      compilationInfo =
+      compilationResult =
           ccCompileAndLink(
               objcCompilationContext,
               compilationArtifacts,
@@ -1041,8 +1047,21 @@
               /* linkActionInput */ null);
     }
 
-    objectFilesCollector.addAll(compilationInfo.getFirst().getObjectFiles(/* usePic= */ false));
-    outputGroupCollector.putAll(compilationInfo.getSecond());
+    objectFilesCollector.addAll(
+        compilationResult.getCcCompilationOutputs().getObjectFiles(/* usePic= */ false));
+    outputGroupCollector.putAll(compilationResult.getOutputGroups());
+
+    if (objcCommon.isPresent()
+        && objcCommon.get().getPurpose() == ObjcCommon.Purpose.COMPILE_AND_LINK) {
+
+      ObjcProvider.NativeBuilder objcProviderBuilder = objcCommon.get().getObjcProviderBuilder();
+      ObjcProvider objcProvider =
+          objcProviderBuilder
+              .setCcCompilationContext(compilationResult.getCcCompilationContext())
+              .build();
+
+      setObjcProvider(objcProvider);
+    }
 
     return this;
   }
@@ -1063,6 +1082,7 @@
       registerCompileAndArchiveActions(
           common.getCompilationArtifacts().get(),
           common.getObjcCompilationContext(),
+          Optional.of(common),
           extraCompileArgs,
           priorityHeaders);
     }
@@ -1156,8 +1176,7 @@
                 toolchain,
                 toolchain.getFdoContext(),
                 getFeatureConfiguration(ruleContext, toolchain, buildConfiguration),
-                createObjcCppSemantics(
-                    objcProvider, /* privateHdrs= */ ImmutableList.of(), /* pchHdr= */ null))
+                createObjcCppSemantics())
             .setGrepIncludes(CppHelper.getGrepIncludes(ruleContext))
             .setIsStampingEnabled(AnalysisUtils.isStampingEnabled(ruleContext))
             .setTestOrTestOnlyTarget(ruleContext.isTestOnlyTarget() || ruleContext.isTestTarget())
@@ -1286,8 +1305,8 @@
    */
   CompilationSupport registerFullyLinkAction(ObjcProvider objcProvider, Artifact outputArchive)
       throws InterruptedException, RuleErrorException {
-    Preconditions.checkNotNull(toolchain);
-    Preconditions.checkNotNull(toolchain.getFdoContext());
+    checkNotNull(toolchain);
+    checkNotNull(toolchain.getFdoContext());
     PathFragment labelName = PathFragment.create(ruleContext.getLabel().getName());
     String libraryIdentifier =
         ruleContext
@@ -1313,8 +1332,7 @@
                 toolchain,
                 toolchain.getFdoContext(),
                 getFeatureConfiguration(ruleContext, toolchain, buildConfiguration),
-                createObjcCppSemantics(
-                    objcProvider, /* privateHdrs= */ ImmutableList.of(), /* pchHdr= */ null))
+                createObjcCppSemantics())
             .setGrepIncludes(CppHelper.getGrepIncludes(ruleContext))
             .setIsStampingEnabled(AnalysisUtils.isStampingEnabled(ruleContext))
             .setTestOrTestOnlyTarget(ruleContext.isTestOnlyTarget() || ruleContext.isTestTarget())
@@ -1669,14 +1687,6 @@
     }
   }
 
-  private static ImmutableSet<PathFragment> uniqueParentDirectories(NestedSet<PathFragment> paths) {
-    ImmutableSet.Builder<PathFragment> parents = new ImmutableSet.Builder<>();
-    for (PathFragment path : paths.toList()) {
-      parents.add(path.getParentDirectory());
-    }
-    return parents.build();
-  }
-
   public static Optional<Artifact> getCustomModuleMap(RuleContext ruleContext) {
     if (ruleContext.attributes().has("module_map", BuildType.LABEL)) {
       return Optional.fromNullable(ruleContext.getPrerequisiteArtifact("module_map", Mode.TARGET));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
index 777a2ad..71ae723 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
@@ -258,10 +258,12 @@
       throws InterruptedException, ActionConflictException {
     ConfiguredAspect.Builder builder = new ConfiguredAspect.Builder(this, parameters, ruleContext);
     ObjcCommon common;
+    ObjcProvider objcProvider = null;
 
     if (!j2ObjcSource.getObjcSrcs().isEmpty()) {
       common =
           common(
+              ObjcCommon.Purpose.COMPILE_AND_LINK,
               ruleContext,
               j2ObjcSource.getObjcSrcs(),
               j2ObjcSource.getObjcHdrs(),
@@ -284,26 +286,29 @@
             .registerCompileAndArchiveActions(
                 common, EXTRA_COMPILE_ARGS, ImmutableList.<PathFragment>of())
             .registerFullyLinkAction(
-                common.getObjcProvider(),
+                compilationSupport.getObjcProvider(),
                 ruleContext.getImplicitOutputArtifact(CompilationSupport.FULLY_LINKED_LIB));
+        objcProvider = compilationSupport.getObjcProvider();
       } catch (RuleErrorException e) {
         ruleContext.ruleError(e.getMessage());
       }
     } else {
       common =
           common(
+              ObjcCommon.Purpose.LINK_ONLY,
               ruleContext,
               ImmutableList.<Artifact>of(),
               ImmutableList.<Artifact>of(),
               ImmutableList.<PathFragment>of(),
               depAttributes,
               otherDeps);
+      objcProvider = common.getObjcProviderBuilder().build();
     }
 
     return builder
         .addProvider(
             exportedJ2ObjcMappingFileProvider(base, ruleContext, directJ2ObjcMappingFileProvider))
-        .addNativeDeclaredProvider(common.getObjcProvider())
+        .addNativeDeclaredProvider(objcProvider)
         .build();
   }
 
@@ -832,6 +837,7 @@
 
   /** Sets up and returns an {@link ObjcCommon} object containing the J2ObjC-translated code. */
   private static ObjcCommon common(
+      ObjcCommon.Purpose purpose,
       RuleContext ruleContext,
       List<Artifact> transpiledSources,
       List<Artifact> transpiledHeaders,
@@ -839,7 +845,7 @@
       List<Attribute> dependentAttributes,
       List<TransitiveInfoCollection> otherObjcProviders)
       throws InterruptedException {
-    ObjcCommon.Builder builder = new ObjcCommon.Builder(ruleContext);
+    ObjcCommon.Builder builder = new ObjcCommon.Builder(purpose, ruleContext);
     IntermediateArtifacts intermediateArtifacts =
         ObjcRuleClasses.j2objcIntermediateArtifacts(ruleContext);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java
index b36115a..7839f14 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java
@@ -45,7 +45,7 @@
       ImmutableList.of("java_import", "java_library", "java_proto_library", "proto_library");
 
   private ObjcCommon common(RuleContext ruleContext) throws InterruptedException {
-    return new ObjcCommon.Builder(ruleContext)
+    return new ObjcCommon.Builder(ObjcCommon.Purpose.LINK_ONLY, ruleContext)
         .setCompilationAttributes(
             CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
         .addDeps(ruleContext.getPrerequisiteConfiguredTargetAndTargets("deps", Mode.TARGET))
@@ -70,7 +70,7 @@
         .build();
 
     ObjcCommon common = common(ruleContext);
-    ObjcProvider objcProvider = common.getObjcProvider();
+    ObjcProvider objcProvider = common.getObjcProviderBuilder().build();
 
     J2ObjcMappingFileProvider j2ObjcMappingFileProvider = J2ObjcMappingFileProvider.union(
         ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcMappingFileProvider.class));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/MultiArchBinarySupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/MultiArchBinarySupport.java
index 3bb3156..272f803 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/MultiArchBinarySupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/MultiArchBinarySupport.java
@@ -256,7 +256,7 @@
               intermediateArtifacts,
               nullToEmptyList(cpuToCTATDepsCollectionMap.get(childCpu)),
               additionalDepProviders);
-      ObjcProvider objcProviderWithDylibSymbols = common.getObjcProvider();
+      ObjcProvider objcProviderWithDylibSymbols = common.getObjcProviderBuilder().build();
       ObjcProvider objcProvider =
           objcProviderWithDylibSymbols.subtractSubtrees(dylibObjcProviders, ImmutableList.of());
 
@@ -300,7 +300,7 @@
       Iterable<ObjcProvider> additionalDepProviders) throws InterruptedException {
 
     ObjcCommon.Builder commonBuilder =
-        new ObjcCommon.Builder(ruleContext, buildConfiguration)
+        new ObjcCommon.Builder(ObjcCommon.Purpose.LINK_ONLY, ruleContext, buildConfiguration)
             .setCompilationAttributes(
                 CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
             .addDeps(propagatedConfiguredTargetAndDataDeps)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java
index 8b98f7d..258712b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java
@@ -15,15 +15,11 @@
 package com.google.devtools.build.lib.rules.objc;
 
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.CC_LIBRARY;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.DEFINE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FLAG;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_LIBRARY;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_CPP;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE_SYSTEM;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IQUOTE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.J2OBJC_LIBRARY;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LINKED_BINARY;
@@ -86,18 +82,29 @@
     return inputs.build();
   }
 
-  /** Filters fileset artifacts out of a group of artifacts. */
-  public static ImmutableList<Artifact> filterFileset(NestedSet<Artifact> artifacts) {
-    return filterFileset(artifacts.toList());
+  /**
+   * Indicates the purpose the ObjcCommon is used for.
+   *
+   * <p>The purpose determines whether ObjcCommon.build() will build an ObjcProvider or an
+   * ObjcProvider.Builder. In compile-and-link mode, ObjcCommon.build() will output an
+   * ObjcProvider.Builder. The builder is expected to combine with the CcCompilationContext from a
+   * compile action, to form a complete ObjcProvider. In link-only mode, ObjcCommon can (and does)
+   * output the full ObjcProvider.
+   */
+  public enum Purpose {
+    /** The ObjcCommon will be used for compile and link. */
+    COMPILE_AND_LINK,
+    /** The ObjcCommon will be used for linking only. */
+    LINK_ONLY,
   }
 
   static class Builder {
+    private final Purpose purpose;
     private final RuleContext context;
     private final StarlarkSemantics semantics;
     private final BuildConfiguration buildConfiguration;
     private Optional<CompilationAttributes> compilationAttributes = Optional.absent();
     private Optional<CompilationArtifacts> compilationArtifacts = Optional.absent();
-    private ImmutableSet.Builder<Artifact> textualHeaders = ImmutableSet.builder();
     private Iterable<ObjcProvider> depObjcProviders = ImmutableList.of();
     private Iterable<ObjcProvider> runtimeDepObjcProviders = ImmutableList.of();
     private Iterable<PathFragment> includes = ImmutableList.of();
@@ -110,11 +117,11 @@
     private Iterable<CcLinkingContext> depCcLinkProviders = ImmutableList.of();
 
     /**
-     * Builder for {@link ObjcCommon} obtaining both attribute data and configuration data from
-     * the given rule context.
+     * Builder for {@link ObjcCommon} obtaining both attribute data and configuration data from the
+     * given rule context.
      */
-    Builder(RuleContext context) throws InterruptedException {
-      this(context, context.getConfiguration());
+    Builder(Purpose purpose, RuleContext context) throws InterruptedException {
+      this(purpose, context, context.getConfiguration());
     }
 
     /**
@@ -122,8 +129,9 @@
      * configuration data from the given configuration object for use in situations where a single
      * target's outputs are under multiple configurations.
      */
-    Builder(RuleContext context, BuildConfiguration buildConfiguration)
+    Builder(Purpose purpose, RuleContext context, BuildConfiguration buildConfiguration)
         throws InterruptedException {
+      this.purpose = purpose;
       this.context = Preconditions.checkNotNull(context);
       this.semantics = context.getAnalysisEnvironment().getSkylarkSemantics();
       this.buildConfiguration = Preconditions.checkNotNull(buildConfiguration);
@@ -268,21 +276,24 @@
     ObjcCommon build() {
 
       ObjcCompilationContext.Builder objcCompilationContextBuilder =
-          ObjcCompilationContext.builder(semantics);
+          ObjcCompilationContext.builder();
 
-      ObjcProvider.Builder objcProvider =
-          new ObjcProvider.Builder(semantics)
-              .addAll(IMPORTED_LIBRARY, extraImportLibraries)
-              .addTransitiveAndPropagateNonCompileInfo(depObjcProviders);
+      ObjcProvider.Builder objcProvider = new ObjcProvider.NativeBuilder(semantics);
+
+      objcProvider
+          .addAll(IMPORTED_LIBRARY, extraImportLibraries)
+          .addTransitiveAndPropagate(depObjcProviders);
 
       objcCompilationContextBuilder
           .addIncludes(includes)
           .addDepObjcProviders(depObjcProviders)
           .addDepObjcProviders(runtimeDepObjcProviders)
+          // TODO(bazel-team): This pulls in stl via
+          // CcCompilationHelper.getStlCcCompilationContext(), but probably shouldn't.
           .addDepCcCompilationContexts(depCcHeaderProviders);
 
       for (CcCompilationContext headerProvider : depCcHeaderProviders) {
-        textualHeaders.addAll(headerProvider.getTextualHdrs());
+        objcProvider.addAllDirect(HEADER, headerProvider.getDeclaredIncludeSrcs().toList());
       }
 
       for (ObjcProvider provider : runtimeDepObjcProviders) {
@@ -323,7 +334,9 @@
         objcProvider
             .addAll(SDK_FRAMEWORK, attributes.sdkFrameworks())
             .addAll(WEAK_SDK_FRAMEWORK, attributes.weakSdkFrameworks())
-            .addAll(SDK_DYLIB, attributes.sdkDylibs());
+            .addAll(SDK_DYLIB, attributes.sdkDylibs())
+            .addAllDirect(HEADER, attributes.hdrs().toList())
+            .addAllDirect(HEADER, attributes.textualHdrs().toList());
         objcCompilationContextBuilder
             .addPublicHeaders(filterFileset(attributes.hdrs().toList()))
             .addPublicTextualHeaders(filterFileset(attributes.textualHdrs().toList()))
@@ -340,9 +353,11 @@
         // them.
         objcProvider
             .addAll(LIBRARY, artifacts.getArchive().asSet())
-            .addAll(SOURCE, allSources);
+            .addAll(SOURCE, allSources)
+            .addAllDirect(SOURCE, allSources)
+            .addAllDirect(HEADER, filterFileset(artifacts.getAdditionalHdrs().toList()));
         objcCompilationContextBuilder.addPublicHeaders(
-            filterFileset(artifacts.getAdditionalHdrs()));
+            filterFileset(artifacts.getAdditionalHdrs().toList()));
         objcCompilationContextBuilder.addPrivateHeaders(artifacts.getPrivateHdrs());
 
         if (artifacts.getArchive().isPresent()
@@ -378,43 +393,22 @@
           objcProvider.add(UMBRELLA_HEADER, umbrellaHeader.get());
         }
         objcProvider.add(MODULE_MAP, moduleMap.getArtifact());
+        objcProvider.addDirect(MODULE_MAP, moduleMap.getArtifact());
         objcProvider.add(TOP_LEVEL_MODULE_MAP, moduleMap);
       }
 
       objcProvider.addAll(LINKED_BINARY, linkedBinary.asSet());
 
       ObjcCompilationContext objcCompilationContext = objcCompilationContextBuilder.build();
-      addObjcCompilationContext(objcProvider, objcCompilationContext);
 
-      return new ObjcCommon(
-          objcProvider.build(),
-          objcCompilationContext,
-          compilationArtifacts,
-          textualHeaders.build());
-    }
-
-    private static void addObjcCompilationContext(
-        ObjcProvider.Builder objcProvider, ObjcCompilationContext objcCompilationContext) {
-      // No need to add frameworkIncludes because same info is used for linking so should
-      // already have been added.
-      objcProvider
-          .addAll(DEFINE, objcCompilationContext.getDefines())
-          .addAll(HEADER, objcCompilationContext.getPublicHeaders())
-          .addAll(HEADER, objcCompilationContext.getPublicTextualHeaders())
-          .addAll(INCLUDE, objcCompilationContext.getIncludes())
-          .addAll(INCLUDE_SYSTEM, objcCompilationContext.getSystemIncludes())
-          .addAll(IQUOTE, objcCompilationContext.getQuoteIncludes())
-          // We can't use the summary provider here because it would mess up the strict
-          // dependency and non-propagate items.
-          .addTransitiveAndPropagateCompileInfo(objcCompilationContext.getDepObjcProviders());
-      for (CcCompilationContext ccCompilationContext :
-          objcCompilationContext.getDepCcCompilationContexts()) {
-        objcProvider
-            .addAll(DEFINE, ccCompilationContext.getDefines())
-            .addAll(HEADER, ccCompilationContext.getDeclaredIncludeSrcs())
-            .addAll(INCLUDE, ccCompilationContext.getIncludeDirs())
-            .addAll(INCLUDE_SYSTEM, ccCompilationContext.getSystemIncludeDirs());
+      ObjcProvider.NativeBuilder objcProviderNativeBuilder =
+          (ObjcProvider.NativeBuilder) objcProvider;
+      if (purpose == Purpose.LINK_ONLY) {
+        objcProviderNativeBuilder.setCcCompilationContext(
+            objcCompilationContext.createCcCompilationContext());
       }
+      return new ObjcCommon(
+          purpose, objcProviderNativeBuilder, objcCompilationContext, compilationArtifacts);
     }
 
     private static boolean isCcLibrary(ConfiguredTargetAndData info) {
@@ -433,25 +427,29 @@
     }
   }
 
-  private final ObjcProvider objcProvider;
+  private final Purpose purpose;
+  private final ObjcProvider.NativeBuilder objcProviderBuilder;
   private final ObjcCompilationContext objcCompilationContext;
 
   private final Optional<CompilationArtifacts> compilationArtifacts;
-  private final ImmutableSet<Artifact> textualHdrs;
 
   private ObjcCommon(
-      ObjcProvider objcProvider,
+      Purpose purpose,
+      ObjcProvider.NativeBuilder objcProviderBuilder,
       ObjcCompilationContext objcCompilationContext,
-      Optional<CompilationArtifacts> compilationArtifacts,
-      ImmutableSet<Artifact> textualHdrs) {
-    this.objcProvider = Preconditions.checkNotNull(objcProvider);
+      Optional<CompilationArtifacts> compilationArtifacts) {
+    this.purpose = purpose;
+    this.objcProviderBuilder = Preconditions.checkNotNull(objcProviderBuilder);
     this.objcCompilationContext = Preconditions.checkNotNull(objcCompilationContext);
     this.compilationArtifacts = Preconditions.checkNotNull(compilationArtifacts);
-    this.textualHdrs = textualHdrs;
   }
 
-  public ObjcProvider getObjcProvider() {
-    return objcProvider;
+  public Purpose getPurpose() {
+    return purpose;
+  }
+
+  public ObjcProvider.NativeBuilder getObjcProviderBuilder() {
+    return objcProviderBuilder;
   }
 
   public ObjcCompilationContext getObjcCompilationContext() {
@@ -462,10 +460,6 @@
     return compilationArtifacts;
   }
 
-  public ImmutableSet<Artifact> getTextualHdrs() {
-    return textualHdrs;
-  }
-
   /**
    * Returns an {@link Optional} containing the compiled {@code .a} file, or
    * {@link Optional#absent()} if this object contains no {@link CompilationArtifacts} or the
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompilationContext.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompilationContext.java
index 774cca1..f408c3d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompilationContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompilationContext.java
@@ -14,14 +14,13 @@
 
 package com.google.devtools.build.lib.rules.objc;
 
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
+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.rules.cpp.CcCompilationContext;
-import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
 import java.util.List;
@@ -33,8 +32,7 @@
  */
 @Immutable
 public final class ObjcCompilationContext {
-  public static final ObjcCompilationContext EMPTY =
-      builder(StarlarkSemantics.DEFAULT_SEMANTICS).build();
+  public static final ObjcCompilationContext EMPTY = builder().build();
 
   private final ImmutableList<String> defines;
 
@@ -49,9 +47,8 @@
   private final ImmutableList<PathFragment> includes;
   private final ImmutableList<PathFragment> systemIncludes;
   private final ImmutableList<PathFragment> quoteIncludes;
-  private final ImmutableList<ObjcProvider> depObjcProviders;
+  private final ImmutableList<PathFragment> strictDependencyIncludes;
   private final ImmutableList<CcCompilationContext> depCcCompilationContexts;
-  private final ObjcProvider depObjcProvidersSummary;
 
   ObjcCompilationContext(
       Iterable<String> defines,
@@ -61,9 +58,8 @@
       Iterable<PathFragment> includes,
       Iterable<PathFragment> systemIncludes,
       Iterable<PathFragment> quoteIncludes,
-      Iterable<ObjcProvider> depObjcProviders,
-      Iterable<CcCompilationContext> depCcCompilationContexts,
-      ObjcProvider depObjcProvidersSummary) {
+      Iterable<PathFragment> strictDependencyIncludes,
+      Iterable<CcCompilationContext> depCcCompilationContexts) {
     this.defines = ImmutableList.copyOf(defines);
     this.publicHeaders = ImmutableList.copyOf(publicHeaders);
     this.publicTextualHeaders = ImmutableList.copyOf(publicTextualHeaders);
@@ -71,9 +67,8 @@
     this.includes = ImmutableList.copyOf(includes);
     this.systemIncludes = ImmutableList.copyOf(systemIncludes);
     this.quoteIncludes = ImmutableList.copyOf(quoteIncludes);
-    this.depObjcProviders = ImmutableList.copyOf(depObjcProviders);
+    this.strictDependencyIncludes = ImmutableList.copyOf(strictDependencyIncludes);
     this.depCcCompilationContexts = ImmutableList.copyOf(depCcCompilationContexts);
-    this.depObjcProvidersSummary = depObjcProvidersSummary;
   }
 
   public ImmutableList<String> getDefines() {
@@ -104,36 +99,38 @@
     return quoteIncludes;
   }
 
-  public ImmutableList<ObjcProvider> getDepObjcProviders() {
-    return depObjcProviders;
+  public ImmutableList<PathFragment> getStrictDependencyIncludes() {
+    return strictDependencyIncludes;
   }
 
   public ImmutableList<CcCompilationContext> getDepCcCompilationContexts() {
     return depCcCompilationContexts;
   }
 
-  public ObjcProvider getDepObjcProvidersSummary() {
-    return depObjcProvidersSummary;
+  public CcCompilationContext createCcCompilationContext() {
+    CcCompilationContext.Builder builder =
+        CcCompilationContext.builder(
+            /* actionConstructionContext= */ null, /* configuration= */ null, /* label= */ null);
+    builder
+        .addDefines(NestedSetBuilder.wrap(Order.LINK_ORDER, getDefines()))
+        .addDeclaredIncludeSrcs(getPublicHeaders())
+        .addDeclaredIncludeSrcs(getPrivateHeaders())
+        .addDeclaredIncludeSrcs(getPublicTextualHeaders())
+        .addModularHdrs(ImmutableList.copyOf(getPublicHeaders()))
+        .addModularHdrs(ImmutableList.copyOf(getPrivateHeaders()))
+        .addTextualHdrs(ImmutableList.copyOf(getPublicTextualHeaders()))
+        .addIncludeDirs(getIncludes())
+        .addSystemIncludeDirs(getSystemIncludes())
+        .addQuoteIncludeDirs(getQuoteIncludes())
+        .mergeDependentCcCompilationContexts(getDepCcCompilationContexts());
+    return builder.build();
   }
 
-  // Note: this function will be deleted in the migration cl.
-  public ImmutableList<Artifact> getAllTransitiveHeaders() {
-    ImmutableList.Builder<Artifact> allHeaders = ImmutableList.builder();
-    allHeaders.addAll(publicHeaders);
-    allHeaders.addAll(publicTextualHeaders);
-    allHeaders.addAll(depObjcProvidersSummary.get(HEADER).toList());
-    for (CcCompilationContext ccCompilationContext : depCcCompilationContexts) {
-      allHeaders.addAll(ccCompilationContext.getDeclaredIncludeSrcs().toList());
-    }
-    return allHeaders.build();
-  }
-
-  public static Builder builder(StarlarkSemantics semantics) {
-    return new Builder(semantics);
+  public static Builder builder() {
+    return new Builder();
   }
 
   static class Builder {
-    private final StarlarkSemantics semantics;
     private final List<String> defines = new ArrayList<>();
     private final List<Artifact> publicHeaders = new ArrayList<>();
     private final List<Artifact> publicTextualHeaders = new ArrayList<>();
@@ -141,12 +138,10 @@
     private final List<PathFragment> includes = new ArrayList<>();
     private final List<PathFragment> systemIncludes = new ArrayList<>();
     private final List<PathFragment> quoteIncludes = new ArrayList<>();
-    private final List<ObjcProvider> depObjcProviders = new ArrayList<>();
+    private final List<PathFragment> strictDependencyIncludes = new ArrayList<>();
     private final List<CcCompilationContext> depCcCompilationContexts = new ArrayList<>();
 
-    Builder(StarlarkSemantics semantics) {
-      this.semantics = semantics;
-    }
+    Builder() {}
 
     public Builder addDefines(Iterable<String> defines) {
       Iterables.addAll(this.defines, defines);
@@ -184,9 +179,11 @@
     }
 
     public Builder addDepObjcProviders(Iterable<ObjcProvider> objcProviders) {
-      // After migration, we will have nice embedded CcCompilationContexts that we can
-      // addDepCcCompilationContexts().
-      Iterables.addAll(this.depObjcProviders, objcProviders);
+      for (ObjcProvider objcProvider : objcProviders) {
+        this.depCcCompilationContexts.add(objcProvider.getCcCompilationContext());
+
+        this.strictDependencyIncludes.addAll(objcProvider.getStrictDependencyIncludes());
+      }
       return this;
     }
 
@@ -202,12 +199,6 @@
     }
 
     ObjcCompilationContext build() {
-      ObjcProvider.Builder depObjcProvidersSummaryBuilder = new ObjcProvider.Builder(semantics);
-      for (ObjcProvider dep : depObjcProviders) {
-        depObjcProvidersSummaryBuilder.addTransitiveAndPropagateCompileInfo(dep);
-      }
-      ObjcProvider depObjcProvidersSummary = depObjcProvidersSummaryBuilder.build();
-
       return new ObjcCompilationContext(
           defines,
           publicHeaders,
@@ -216,9 +207,8 @@
           includes,
           systemIncludes,
           quoteIncludes,
-          depObjcProviders,
-          depCcCompilationContexts,
-          depObjcProvidersSummary);
+          strictDependencyIncludes,
+          depCcCompilationContexts);
     }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCppSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCppSemantics.java
index 6513106..d5b08ef 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCppSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCppSemantics.java
@@ -15,15 +15,10 @@
 package com.google.devtools.build.lib.rules.objc;
 
 import static com.google.devtools.build.lib.rules.objc.CompilationSupport.IncludeProcessingType.INCLUDE_SCANNING;
-import static com.google.devtools.build.lib.rules.objc.CompilationSupport.IncludeProcessingType.NO_PROCESSING;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
 
-import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.rules.cpp.CcCompilationContext;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
@@ -41,8 +36,6 @@
 
   private final IncludeProcessingType includeProcessingType;
   private final IncludeProcessing includeProcessing;
-  private final Iterable<Artifact> extraIncludeScanningInputs;
-  private final ObjcProvider objcProvider;
   private final ObjcConfiguration config;
   private final IntermediateArtifacts intermediateArtifacts;
   private final BuildConfiguration buildConfiguration;
@@ -51,31 +44,23 @@
   /**
    * Creates an instance of ObjcCppSemantics
    *
-   * @param objcProvider the provider that should be used in determining objc-specific inputs to
-   *     actions
    * @param includeProcessingType The type of include processing to be run.
    * @param includeProcessing the closure providing the strategy for processing of includes for
    *     actions
-   * @param extraIncludeScanningInputs additional inputs to include scanning, outside of the headers
-   *     provided by ObjProvider.
    * @param config the ObjcConfiguration for this build
    * @param intermediateArtifacts used to create headers_list artifacts
    * @param buildConfiguration the build configuration for this build
    * @param enableModules whether modules are enabled
    */
   public ObjcCppSemantics(
-      ObjcProvider objcProvider,
       IncludeProcessingType includeProcessingType,
       IncludeProcessing includeProcessing,
-      Iterable<Artifact> extraIncludeScanningInputs,
       ObjcConfiguration config,
       IntermediateArtifacts intermediateArtifacts,
       BuildConfiguration buildConfiguration,
       boolean enableModules) {
-    this.objcProvider = objcProvider;
     this.includeProcessingType = includeProcessingType;
     this.includeProcessing = includeProcessing;
-    this.extraIncludeScanningInputs = extraIncludeScanningInputs;
     this.config = config;
     this.intermediateArtifacts = intermediateArtifacts;
     this.buildConfiguration = buildConfiguration;
@@ -94,33 +79,6 @@
         // TODO(waltl): do better with include scanning.
         .addTransitiveMandatoryInputs(actionBuilder.getToolchain().getAllFilesMiddleman())
         .setShouldScanIncludes(includeProcessingType == INCLUDE_SCANNING);
-
-    if (includeProcessingType == NO_PROCESSING) {
-      // TODO(b/62060839): Identify the mechanism used to add generated headers in c++, and recycle
-      // it here.
-      //
-      // This is used to patch the action graph of missing dependencies.  We only need to do this
-      // for headers in depObjcProviderCompileInfo.  We can rid of this in the migraiton cl.
-      actionBuilder.addTransitiveMandatoryInputs(objcProvider.getGeneratedHeaders());
-    }
-  }
-
-  @Override
-  public NestedSet<Artifact> getAdditionalPrunableIncludes() {
-    // This is a hook used when header processing is off, to supply CppCompilationAction with
-    // additional inputs that are not properly added to CcCompilationContext.  We will delete this
-    // in the migration cl.
-    return objcProvider.get(HEADER);
-  }
-
-  // This function now returns "additional" (not "alternate" include scanning data inputs, over
-  // those already accounted for in dependent CcCompilationContext.  We can delete this in the
-  // migration cl so no point renaming it.
-  @Override
-  public Iterable<Artifact> getAlternateIncludeScanningDataInputs() {
-    // Include scanning data only cares about generated artifacts.  Since the generated headers from
-    // objcProvider is readily available we provide that instead of the full header list.
-    return Iterables.concat(objcProvider.getGeneratedHeaderList(), extraIncludeScanningInputs);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java
index 7bcb3a2..51174cf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.rules.cpp.CcCommon;
+import com.google.devtools.build.lib.rules.cpp.CcInfo;
 import com.google.devtools.build.lib.rules.cpp.CppModuleMap;
 
 /**
@@ -34,11 +35,18 @@
   public ConfiguredTarget create(RuleContext ruleContext)
       throws InterruptedException, RuleErrorException, ActionConflictException {
     CcCommon.checkRuleLoadedThroughMacro(ruleContext);
+
+    CompilationAttributes compilationAttributes =
+        CompilationAttributes.Builder.fromRuleContext(ruleContext).build();
+    IntermediateArtifacts intermediateArtifacts =
+        ObjcRuleClasses.intermediateArtifacts(ruleContext);
+    CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder().build();
+
     ObjcCommon common =
-        new ObjcCommon.Builder(ruleContext)
-            .setCompilationAttributes(
-                CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
-            .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext))
+        new ObjcCommon.Builder(ObjcCommon.Purpose.LINK_ONLY, ruleContext)
+            .setCompilationArtifacts(compilationArtifacts)
+            .setCompilationAttributes(compilationAttributes)
+            .setIntermediateArtifacts(intermediateArtifacts)
             .setAlwayslink(ruleContext.attributes().get("alwayslink", Type.BOOLEAN))
             .setHasModuleMap()
             .addExtraImportLibraries(
@@ -47,11 +55,6 @@
 
     NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder();
 
-    CompilationAttributes compilationAttributes =
-        CompilationAttributes.Builder.fromRuleContext(ruleContext).build();
-    IntermediateArtifacts intermediateArtifacts =
-        ObjcRuleClasses.intermediateArtifacts(ruleContext);
-
     NestedSet<Artifact> publicHeaders = compilationAttributes.hdrs();
     CppModuleMap moduleMap = intermediateArtifacts.moduleMap();
 
@@ -61,10 +64,14 @@
         .registerGenerateModuleMapAction(moduleMap, publicHeaders)
         .validateAttributes();
 
-    ObjcProvider objcProvider = common.getObjcProvider();
+    ObjcProvider objcProvider = common.getObjcProviderBuilder().build();
 
     return ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build())
         .addNativeDeclaredProvider(objcProvider)
+        .addNativeDeclaredProvider(
+            CcInfo.builder()
+                .setCcCompilationContext(objcProvider.getCcCompilationContext())
+                .build())
         .addSkylarkTransitiveInfo(ObjcProvider.SKYLARK_NAME, objcProvider)
         .build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java
index c1fefa7..acb3ac6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java
@@ -46,7 +46,7 @@
    * Constructs an {@link ObjcCommon} instance based on the attributes of the given rule context.
    */
   private ObjcCommon common(RuleContext ruleContext) throws InterruptedException {
-    return new ObjcCommon.Builder(ruleContext)
+    return new ObjcCommon.Builder(ObjcCommon.Purpose.COMPILE_AND_LINK, ruleContext)
         .setCompilationAttributes(
             CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
         .setCompilationArtifacts(CompilationSupport.compilationArtifacts(ruleContext))
@@ -81,7 +81,7 @@
     compilationSupport
         .registerCompileAndArchiveActions(common)
         .registerFullyLinkAction(
-            common.getObjcProvider(),
+            compilationSupport.getObjcProvider(),
             ruleContext.getImplicitOutputArtifact(CompilationSupport.FULLY_LINKED_LIB))
         .validateAttributes();
 
@@ -90,23 +90,11 @@
     J2ObjcEntryClassProvider j2ObjcEntryClassProvider = new J2ObjcEntryClassProvider.Builder()
       .addTransitive(ruleContext.getPrerequisites("deps", Mode.TARGET,
           J2ObjcEntryClassProvider.class)).build();
-    ObjcProvider objcProvider = common.getObjcProvider();
-    CcCompilationContext ccCompilationContext =
-        CcCompilationContext.builder(
-                ruleContext, ruleContext.getConfiguration(), ruleContext.getLabel())
-            .addDeclaredIncludeSrcs(
-                CompilationAttributes.Builder.fromRuleContext(ruleContext).build().hdrs().toList())
-            .addTextualHdrs(common.getTextualHdrs())
-            .addDeclaredIncludeSrcs(common.getTextualHdrs())
-            .setPurpose(
-                compilationSupport
-                    .createObjcCppSemantics(
-                        objcProvider, /* privateHdrs= */ ImmutableList.of(), /* pchHdr= */ null)
-                    .getPurpose())
-            .build();
-
+    ObjcProvider objcProvider = compilationSupport.getObjcProvider();
+    CcCompilationContext ccCompilationContext = objcProvider.getCcCompilationContext();
     CcLinkingContext ccLinkingContext =
-        buildCcLinkingContext(ruleContext.getLabel(), common, ruleContext.getSymbolGenerator());
+        buildCcLinkingContext(
+            ruleContext.getLabel(), objcProvider, ruleContext.getSymbolGenerator());
 
     return ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build())
         .addNativeDeclaredProvider(objcProvider)
@@ -125,9 +113,8 @@
   }
 
   private CcLinkingContext buildCcLinkingContext(
-      Label label, ObjcCommon common, SymbolGenerator<?> symbolGenerator) {
+      Label label, ObjcProvider objcProvider, SymbolGenerator<?> symbolGenerator) {
     ImmutableSet.Builder<LibraryToLink> libraries = new ImmutableSet.Builder<>();
-    ObjcProvider objcProvider = common.getObjcProvider();
     for (Artifact library : objcProvider.get(ObjcProvider.LIBRARY).toList()) {
       libraries.add(
           LibraryToLink.builder()
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoAspect.java
index 053fd7a..7987a90 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoAspect.java
@@ -30,6 +30,7 @@
 import com.google.devtools.build.lib.packages.SkylarkNativeAspect;
 import com.google.devtools.build.lib.rules.proto.ProtoInfo;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.List;
 
 /**
@@ -100,9 +101,12 @@
       ObjcProvider protobufObjcProvider =
           ruleContext.getPrerequisite(
               ObjcRuleClasses.PROTO_LIB_ATTR, Mode.TARGET, ObjcProvider.SKYLARK_CONSTRUCTOR);
-      aspectObjcProtoProvider.addProtobufHeaders(protobufObjcProvider.get(ObjcProvider.HEADER));
+      aspectObjcProtoProvider.addProtobufHeaders(
+          protobufObjcProvider.getCcCompilationContext().getDeclaredIncludeSrcs());
       aspectObjcProtoProvider.addProtobufHeaderSearchPaths(
-          protobufObjcProvider.get(ObjcProvider.INCLUDE));
+          NestedSetBuilder.<PathFragment>linkOrder()
+              .addAll(protobufObjcProvider.getCcCompilationContext().getIncludeDirs())
+              .build());
     }
 
     // Only add the provider if it has any values, otherwise skip it.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
index c373885..1487259 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.NativeProvider.WithLegacySkylarkName;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationContext;
 import com.google.devtools.build.lib.rules.cpp.CcLinkingContext;
 import com.google.devtools.build.lib.rules.cpp.CppModuleMap;
 import com.google.devtools.build.lib.rules.cpp.LibraryToLink;
@@ -51,41 +52,27 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import javax.annotation.Nullable;
 
 /**
  * A provider that provides all compiling and linking information in the transitive closure of its
  * deps that are needed for building Objective-C rules.
  *
- * <p>The functional contents within the provider are stored in three maps, each of which maps a
- * {@link Key} to {@link NestedSet}. The three maps differ in how they are propagated to dependent
- * providers:
+ * <p>Most of the compilation information is stored in an embedded {@code CcCompilationContext}. The
+ * objc proto strict dependency include paths are stored in a special, non-propagated field {@code
+ * strictDependencyIncludes}.
+ *
+ * <p>The rest of the information is stored in two generic maps indexed by {@code ObjcProvider.Key}:
  *
  * <ul>
- *   <li>{@code items}: This map contains items that should be propagated transitively to all
- *       dependent ObjcProviders. Most items are stored in this map.
- *   <li>{@code strictDependencyItems}: This map contains items that should only be propagated to
- *       directly dependent ObjcProviders, but not to indirect ones. This is used to implement
- *       {@link ObjcProtoLibrary}'s requirement that its header path should only be propagated to
- *       its direct dependency, and also the experimental (and soon-to-be-deprecated) feature to
- *       propagate module maps only to direct dependencies.
- *   <li>{@code nonPropagatedItems}: This map contains items that should not be propagated. There is
- *       no longer any direct usage of this feature, but strictDependencyItems turn into
- *       nonPropagatedItems when they get propagated to their dependent ObjcProviders.
+ *   <li>{@code items}: This map contains items that are propagated transitively to all dependent
+ *       ObjcProviders.
+ *   <li>{@code directItems}: This multimap contains items whose values originate from this
+ *       ObjcProvider (as opposed to those that came from a dependent ObjcProvider). {@link
+ *       #KEYS_FOR_DIRECT} contains the keys whose items are inserted into this map. The map is
+ *       created as a performance optimization for IDEs (i.e. Tulsi), so that the IDEs don't have to
+ *       flatten large transitive nested sets returned by ObjcProvider queries. It does not
+ *       materially affect other operations of the ObjcProvider.
  * </ul>
- *
- * <p>All three maps contribute to the final value of a key in an ObjcProvider as returned by {@link
- * #get(Key<E>)}.
- *
- * <p>New usage of {@code strictDependencyItems} and {@code nonPropagatedItems} is strongly
- * discouraged, as they complicate ongoing tasks of migrating ObjcProvider to CcInfo.
- *
- * <p>There is a fourth map, {@code directItems}, that contains items whose values originate from
- * this ObjcProvider (as opposed to those that came from a dependent ObjcProvider). {@link
- * #KEYS_FOR_DIRECT} contains the keys whose items are inserted into this map. The map is created as
- * a performance optimization for IDEs (i.e. Tulsi), so that the IDEs don't have to flatten large
- * transitive nested sets returned by ObjcProvider queries. It does not materially affect other
- * operations of the ObjcProvider.
  */
 // TODO(adonovan): this is an info, not a provider; rename.
 @Immutable
@@ -320,6 +307,9 @@
   // Items which are propagated transitively to dependents.
   private final ImmutableMap<Key<?>, NestedSet<?>> items;
 
+  /** Strict dependency includes */
+  private final ImmutableList<PathFragment> strictDependencyIncludes;
+
   /**
    * This is intended to be used by clients which need to collect transitive information without
    * paying the O(n^2) behavior to flatten it during analysis time.
@@ -329,19 +319,9 @@
    */
   private final ImmutableListMultimap<Key<?>, ?> directItems;
 
-  // Items which should not be propagated to dependents.
-  private final ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems;
+  private final CcCompilationContext ccCompilationContext;
 
-  // Items which should be passed to strictly direct dependers, but not transitive dependers.
-  private final ImmutableMap<Key<?>, NestedSet<?>> strictDependencyItems;
-
-  // Lazily initialized because it's only needed when there is no include processing.
-  @Nullable private volatile NestedSet<Artifact> generatedHeaders;
-
-  // Lazily initialized because it's only needed for including scanning.
-  @Nullable private volatile ImmutableList<Artifact> generatedHeaderList;
-
-  /** All keys in ObjProvider corresponding to information needed for compile actions. */
+  /** Keys corresponding to compile information that has been migrated to CcCompilationContext. */
   static final ImmutableSet<Key<?>> KEYS_FOR_COMPILE_INFO =
       ImmutableSet.<Key<?>>of(
           DEFINE, FRAMEWORK_SEARCH_PATHS, HEADER, INCLUDE, INCLUDE_SYSTEM, IQUOTE);
@@ -378,9 +358,6 @@
           UMBRELLA_HEADER,
           WEAK_SDK_FRAMEWORK);
 
-  /** A white list of keys we support for strict-dependency / non-propagated items. */
-  static final ImmutableList<Key<?>> STRICT_DEPENDENCY_KEYS = ImmutableList.<Key<?>>of(INCLUDE);
-
   /**
    * Keys that should be kept as directItems. This is limited to a few keys that have larger
    * performance implications when flattened in a transitive fashion and/or require non-transitive
@@ -397,13 +374,17 @@
   static final ImmutableSet<Key<?>> KEYS_FOR_DIRECT =
       ImmutableSet.<Key<?>>of(HEADER, MODULE_MAP, SOURCE);
 
+  public ImmutableList<PathFragment> getStrictDependencyIncludes() {
+    return strictDependencyIncludes;
+  }
+
   @Override
   public Depset /*<String>*/ defineForStarlark() {
-    return Depset.of(SkylarkType.STRING, define());
+    return getCcCompilationContext().getSkylarkDefines();
   }
 
   public NestedSet<String> define() {
-    return get(DEFINE);
+    return getCcCompilationContext().getDefines();
   }
 
   @Override
@@ -421,8 +402,22 @@
   }
 
   @Override
-  public Depset frameworkSearchPathOnly() {
-    return ObjcProviderSkylarkConverters.convertPathFragmentsToSkylark(get(FRAMEWORK_SEARCH_PATHS));
+  public Depset frameworkIncludeForStarlark() {
+    // Starlark code expects the framework path to include the ".framework" directory, which is then
+    // stripped to get the actual framework search path.  CcCompilationContext only stores the
+    // framework search path, so the best we can do is to append a fake ".framework" directory.
+    // This at least preserves the behavior when the field is used for its intended purpose.
+    return Depset.of(
+        SkylarkType.STRING,
+        NestedSetBuilder.wrap(
+            Order.STABLE_ORDER,
+            frameworkInclude().stream()
+                .map(x -> x.getChild("fake.framework").getSafePathString())
+                .collect(ImmutableList.toImmutableList())));
+  }
+
+  public ImmutableList<PathFragment> frameworkInclude() {
+    return getCcCompilationContext().getFrameworkIncludeDirs();
   }
 
   @Override
@@ -435,8 +430,8 @@
     return Depset.of(Artifact.TYPE, header());
   }
 
-  NestedSet<Artifact> header() {
-    return get(HEADER);
+  public NestedSet<Artifact> header() {
+    return getCcCompilationContext().getDeclaredIncludeSrcs();
   }
 
   @Override
@@ -450,18 +445,51 @@
   }
 
   @Override
-  public Depset /*<String>*/ include() {
-    return ObjcProviderSkylarkConverters.convertPathFragmentsToSkylark(get(INCLUDE));
+  public Depset /*<String>*/ includeForStarlark() {
+    return Depset.of(
+        SkylarkType.STRING,
+        NestedSetBuilder.wrap(
+            Order.STABLE_ORDER,
+            include().stream()
+                .map(PathFragment::getSafePathString)
+                .collect(ImmutableList.toImmutableList())));
+  }
+
+  public ImmutableList<PathFragment> include() {
+    ImmutableList.Builder<PathFragment> listBuilder = ImmutableList.builder();
+    return listBuilder
+        .addAll(strictDependencyIncludes)
+        .addAll(getCcCompilationContext().getIncludeDirs())
+        .build();
   }
 
   @Override
-  public Depset includeSystem() {
-    return ObjcProviderSkylarkConverters.convertPathFragmentsToSkylark(get(INCLUDE_SYSTEM));
+  public Depset /*<String>*/ strictIncludeForStarlark() {
+    return Depset.of(
+        SkylarkType.STRING,
+        NestedSetBuilder.wrap(
+            Order.STABLE_ORDER,
+            getStrictDependencyIncludes().stream()
+                .map(PathFragment::getSafePathString)
+                .collect(ImmutableList.toImmutableList())));
   }
 
   @Override
-  public Depset iquote() {
-    return ObjcProviderSkylarkConverters.convertPathFragmentsToSkylark(get(IQUOTE));
+  public Depset systemIncludeForStarlark() {
+    return getCcCompilationContext().getSkylarkSystemIncludeDirs();
+  }
+
+  public ImmutableList<PathFragment> systemInclude() {
+    return getCcCompilationContext().getSystemIncludeDirs();
+  }
+
+  @Override
+  public Depset quoteIncludeForStarlark() {
+    return getCcCompilationContext().getSkylarkQuoteIncludeDirs();
+  }
+
+  public ImmutableList<PathFragment> quoteInclude() {
+    return getCcCompilationContext().getQuoteIncludeDirs();
   }
 
   @Override
@@ -574,6 +602,11 @@
         ObjcProviderSkylarkConverters.convertToSkylark(WEAK_SDK_FRAMEWORK, get(WEAK_SDK_FRAMEWORK));
   }
 
+  @Override
+  public CcCompilationContext getCcCompilationContext() {
+    return ccCompilationContext;
+  }
+
   /**
    * All keys in ObjcProvider that are explicitly not exposed to skylark. This is used for
    * testing and verification purposes to ensure that a conscious decision is made for all keys;
@@ -634,14 +667,14 @@
   private ObjcProvider(
       StarlarkSemantics semantics,
       ImmutableMap<Key<?>, NestedSet<?>> items,
-      ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems,
-      ImmutableMap<Key<?>, NestedSet<?>> strictDependencyItems,
-      ImmutableListMultimap<Key<?>, ?> directItems) {
+      ImmutableList<PathFragment> strictDependencyIncludes,
+      ImmutableListMultimap<Key<?>, ?> directItems,
+      CcCompilationContext ccCompilationContext) {
     this.semantics = semantics;
     this.items = Preconditions.checkNotNull(items);
-    this.nonPropagatedItems = Preconditions.checkNotNull(nonPropagatedItems);
-    this.strictDependencyItems = Preconditions.checkNotNull(strictDependencyItems);
+    this.strictDependencyIncludes = Preconditions.checkNotNull(strictDependencyIncludes);
     this.directItems = Preconditions.checkNotNull(directItems);
+    this.ccCompilationContext = ccCompilationContext;
   }
 
   @Override
@@ -655,17 +688,11 @@
   @SuppressWarnings("unchecked")
   public <E> NestedSet<E> get(Key<E> key) {
     Preconditions.checkNotNull(key);
-    NestedSetBuilder<E> builder = new NestedSetBuilder<>(key.order);
-    if (strictDependencyItems.containsKey(key)) {
-      builder.addTransitive((NestedSet<E>) strictDependencyItems.get(key));
-    }
-    if (nonPropagatedItems.containsKey(key)) {
-      builder.addTransitive((NestedSet<E>) nonPropagatedItems.get(key));
-    }
     if (items.containsKey(key)) {
-      builder.addTransitive((NestedSet<E>) items.get(key));
+      return (NestedSet<E>) items.get(key);
+    } else {
+      return new NestedSetBuilder<E>(key.order).build();
     }
-    return builder.build();
   }
 
   /** All direct artifacts, bundleable files, etc. of the type specified by {@code key}. */
@@ -678,18 +705,6 @@
   }
 
   /**
-   * Returns all keys that have at least one value in this provider (values may be propagable,
-   * non-propagable, or strict).
-   */
-  private Iterable<Key<?>> getValuedKeys() {
-    return ImmutableSet.<Key<?>>builder()
-        .addAll(strictDependencyItems.keySet())
-        .addAll(nonPropagatedItems.keySet())
-        .addAll(items.keySet())
-        .build();
-  }
-
-  /**
    * All artifacts, bundleable files, etc, that should be propagated to transitive dependers, of
    * the type specified by {@code key}.
    */
@@ -728,44 +743,6 @@
     return ccLinkingContext.getStaticModeParamsForExecutableLibraries();
   }
 
-  /** Returns the set of generated header files. */
-  NestedSet<Artifact> getGeneratedHeaders() {
-    if (generatedHeaders == null) {
-      synchronized (this) {
-        if (generatedHeaders == null) {
-          NestedSet<Artifact> headers = header();
-          NestedSetBuilder<Artifact> generatedHeadersBuilder =
-              new NestedSetBuilder<>(headers.getOrder());
-          for (Artifact header : headers.toList()) {
-            if (!header.isSourceArtifact()) {
-              generatedHeadersBuilder.add(header);
-            }
-          }
-          generatedHeaders = generatedHeadersBuilder.build();
-        }
-      }
-    }
-    return generatedHeaders;
-  }
-
-  /** Returns the list of generated header files. */
-  List<Artifact> getGeneratedHeaderList() {
-    if (generatedHeaderList == null) {
-      synchronized (this) {
-        if (generatedHeaderList == null) {
-          ImmutableList.Builder<Artifact> generatedHeadersBuilder = ImmutableList.builder();
-          for (Artifact header : header().toList()) {
-            if (!header.isSourceArtifact()) {
-              generatedHeadersBuilder.add(header);
-            }
-          }
-          generatedHeaderList = generatedHeadersBuilder.build();
-        }
-      }
-    }
-    return generatedHeaderList;
-  }
-
   /**
    * Subtracts dependency subtrees from this provider and returns the result (subtraction does not
    * mutate this provider). Note that not all provider keys are subtracted; generally only keys
@@ -803,8 +780,8 @@
         avoidLibrariesSet.add(libraryToAvoid.getRunfilesPath());
       }
     }
-    ObjcProvider.Builder objcProviderBuilder = new ObjcProvider.Builder(semantics);
-    for (Key<?> key : getValuedKeys()) {
+    ObjcProvider.NativeBuilder objcProviderBuilder = new ObjcProvider.NativeBuilder(semantics);
+    for (Key<?> key : items.keySet()) {
       if (key == CC_LIBRARY) {
         addTransitiveAndFilter(objcProviderBuilder, CC_LIBRARY,
             ccLibraryNotYetLinked(avoidLibrariesSet));
@@ -819,6 +796,8 @@
         addTransitiveAndAvoid(objcProviderBuilder, key, avoidObjcProviders);
       }
     }
+    objcProviderBuilder.addStrictDependencyIncludes(strictDependencyIncludes);
+    objcProviderBuilder.setCcCompilationContext(ccCompilationContext);
     return objcProviderBuilder.build();
   }
 
@@ -876,21 +855,10 @@
   private <T> void addTransitiveAndFilter(ObjcProvider.Builder objcProviderBuilder, Key<T> key,
       Predicate<T> filterPredicate) {
     NestedSet<T> propagableItems = (NestedSet<T>) items.get(key);
-    NestedSet<T> nonPropagableItems = (NestedSet<T>) nonPropagatedItems.get(key);
-    NestedSet<T> strictItems = (NestedSet<T>) strictDependencyItems.get(key);
-
     if (propagableItems != null) {
       objcProviderBuilder.addAll(key,
           Iterables.filter(propagableItems.toList(), filterPredicate));
     }
-    if (nonPropagableItems != null) {
-      objcProviderBuilder.addAllNonPropagable(key,
-          Iterables.filter(nonPropagableItems.toList(), filterPredicate));
-    }
-    if (strictItems != null) {
-      objcProviderBuilder.addAllForDirectDependents(key,
-          Iterables.filter(strictItems.toList(), filterPredicate));
-    }
   }
 
   private void addTransitiveAndAvoidArtifacts(ObjcProvider.Builder objcProviderBuilder,
@@ -1003,11 +971,12 @@
    * A builder for this context with an API that is optimized for collecting information from
    * several transitive dependencies.
    */
-  public static final class Builder {
+  public abstract static class Builder {
+
     private final StarlarkSemantics starlarkSemantics;
     private final Map<Key<?>, NestedSetBuilder<?>> items = new HashMap<>();
-    private final Map<Key<?>, NestedSetBuilder<?>> nonPropagatedItems = new HashMap<>();
-    private final Map<Key<?>, NestedSetBuilder<?>> strictDependencyItems = new HashMap<>();
+    private final ImmutableList.Builder<PathFragment> strictDependencyIncludes =
+        ImmutableList.builder();
 
     // Only includes items or lists added directly, never flattens any NestedSets.
     private final ImmutableListMultimap.Builder<Key<?>, ?> directItems =
@@ -1022,22 +991,20 @@
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    private void uncheckedAddAll(Key key, Iterable toAdd, Map<Key<?>, NestedSetBuilder<?>> set) {
-      maybeAddEmptyBuilder(set, key);
-      set.get(key).addAll(toAdd);
+    private void uncheckedAddAll(Key key, Iterable toAdd) {
+      maybeAddEmptyBuilder(items, key);
+      items.get(key).addAll(toAdd);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    private void uncheckedAddAllDirect(
-        Key key, Iterable<?> toAdd, ImmutableListMultimap.Builder<Key<?>, ?> builder) {
-      builder.putAll(key, (Iterable) toAdd);
+    protected void uncheckedAddAllDirect(Key key, Iterable<?> toAdd) {
+      directItems.putAll(key, (Iterable) toAdd);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    private void uncheckedAddTransitive(
-        Key key, NestedSet toAdd, Map<Key<?>, NestedSetBuilder<?>> set) {
-      maybeAddEmptyBuilder(set, key);
-      set.get(key).addTransitive(toAdd);
+    protected void uncheckedAddTransitive(Key key, NestedSet toAdd) {
+      maybeAddEmptyBuilder(items, key);
+      items.get(key).addTransitive(toAdd);
     }
 
     /**
@@ -1057,10 +1024,7 @@
      */
     public Builder addTransitiveAndPropagate(ObjcProvider provider) {
       for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) {
-        uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.items);
-      }
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.strictDependencyItems.entrySet()) {
-        uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.nonPropagatedItems);
+        uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue());
       }
       return this;
     }
@@ -1071,11 +1035,7 @@
      */
     public Builder addTransitiveAndPropagate(Key<?> key, ObjcProvider provider) {
       if (provider.items.containsKey(key)) {
-        uncheckedAddTransitive(key, provider.items.get(key), this.items);
-      }
-      if (provider.strictDependencyItems.containsKey(key)) {
-        uncheckedAddTransitive(
-            key, provider.strictDependencyItems.get(key), this.nonPropagatedItems);
+        uncheckedAddTransitive(key, provider.items.get(key));
       }
       return this;
     }
@@ -1085,68 +1045,7 @@
      * ObjcProvider.
      */
     public <E> Builder addTransitiveAndPropagate(Key<E> key, NestedSet<E> items) {
-      uncheckedAddTransitive(key, items, this.items);
-      return this;
-    }
-
-    // The following CompileInfo/NonCompileInfo family of methods will be deleted in the migration
-    // CL.
-
-    /**
-     * Add compile info from providers, and propagate it to any (transitive) dependers on this
-     * ObjcProvider.
-     */
-    public Builder addTransitiveAndPropagateCompileInfo(Iterable<ObjcProvider> providers) {
-      for (ObjcProvider provider : providers) {
-        addTransitiveAndPropagateCompileInfo(provider);
-      }
-      return this;
-    }
-
-    /**
-     * Add compile info from provider, and propagate it to any (transitive) dependers on this
-     * ObjcProvider.
-     */
-    public Builder addTransitiveAndPropagateCompileInfo(ObjcProvider provider) {
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) {
-        if (KEYS_FOR_COMPILE_INFO.contains(typeEntry.getKey())) {
-          uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.items);
-        }
-      }
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.strictDependencyItems.entrySet()) {
-        if (KEYS_FOR_COMPILE_INFO.contains(typeEntry.getKey())) {
-          uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.nonPropagatedItems);
-        }
-      }
-      return this;
-    }
-
-    /**
-     * Add non-compile info from providers, and propagate it to any (transitive) dependers on this
-     * ObjcProvider.
-     */
-    public Builder addTransitiveAndPropagateNonCompileInfo(Iterable<ObjcProvider> providers) {
-      for (ObjcProvider provider : providers) {
-        addTransitiveAndPropagateNonCompileInfo(provider);
-      }
-      return this;
-    }
-
-    /**
-     * Add non-compile info from provider, and propagate it to any (transitive) dependers on this
-     * ObjcProvider.
-     */
-    public Builder addTransitiveAndPropagateNonCompileInfo(ObjcProvider provider) {
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) {
-        if (!KEYS_FOR_COMPILE_INFO.contains(typeEntry.getKey())) {
-          uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.items);
-        }
-      }
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.strictDependencyItems.entrySet()) {
-        if (!KEYS_FOR_COMPILE_INFO.contains(typeEntry.getKey())) {
-          uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), this.nonPropagatedItems);
-        }
-      }
+      uncheckedAddTransitive(key, items);
       return this;
     }
 
@@ -1159,23 +1058,33 @@
     }
 
     /**
-     * Add all keys and values from the given provider, but propagate any normally-propagated items
-     * only to direct dependers of this ObjcProvider.
+     * Propagate keys and values from the given provider to direct dependers of this ObjcProvider.
+     * We no longer support this generically -- the only remaining use case we support is for
+     * includes.
      */
     public Builder addAsDirectDeps(ObjcProvider provider) throws EvalException {
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) {
-        Key<?> key = typeEntry.getKey();
-        if (!ObjcProvider.STRICT_DEPENDENCY_KEYS.contains(key)) {
-          throw badDirectDependencyKeyError(key);
-        }
-        uncheckedAddTransitive(key, typeEntry.getValue(), this.strictDependencyItems);
+      CcCompilationContext providerCcCompilationContext = provider.getCcCompilationContext();
+
+      strictDependencyIncludes.addAll(providerCcCompilationContext.getIncludeDirs());
+
+      // Emit an error if we find any other information in the provider.
+      for (Key<?> key : provider.items.keySet()) {
+        throw badDirectDependencyKeyError(key);
       }
-      for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.strictDependencyItems.entrySet()) {
-        Key<?> key = typeEntry.getKey();
-        if (!ObjcProvider.STRICT_DEPENDENCY_KEYS.contains(key)) {
-          throw badDirectDependencyKeyError(key);
-        }
-        uncheckedAddTransitive(key, typeEntry.getValue(), this.nonPropagatedItems);
+      if (!provider.define().isEmpty()) {
+        throw badDirectDependencyKeyError(DEFINE);
+      }
+      if (!provider.header().isEmpty()) {
+        throw badDirectDependencyKeyError(HEADER);
+      }
+      if (!providerCcCompilationContext.getFrameworkIncludeDirs().isEmpty()) {
+        throw badDirectDependencyKeyError(FRAMEWORK_SEARCH_PATHS);
+      }
+      if (!providerCcCompilationContext.getSystemIncludeDirs().isEmpty()) {
+        throw badDirectDependencyKeyError(INCLUDE_SYSTEM);
+      }
+      if (!providerCcCompilationContext.getQuoteIncludeDirs().isEmpty()) {
+        throw badDirectDependencyKeyError(IQUOTE);
       }
       return this;
     }
@@ -1184,10 +1093,13 @@
      * Add element, and propagate it to any (transitive) dependers on this ObjcProvider.
      */
     public <E> Builder add(Key<E> key, E toAdd) {
-      uncheckedAddAll(key, ImmutableList.of(toAdd), this.items);
-      if (ObjcProvider.KEYS_FOR_DIRECT.contains(key)) {
-        uncheckedAddAllDirect(key, ImmutableList.of(toAdd), this.directItems);
-      }
+      uncheckedAddAll(key, ImmutableList.of(toAdd));
+      return this;
+    }
+
+    public <E> Builder addDirect(Key<E> key, E toAdd) {
+      Preconditions.checkState(KEYS_FOR_DIRECT.contains(key));
+      uncheckedAddAllDirect(key, ImmutableList.of(toAdd));
       return this;
     }
 
@@ -1202,38 +1114,65 @@
      * Add elements in toAdd, and propagate them to any (transitive) dependers on this ObjcProvider.
      */
     public <E> Builder addAll(Key<E> key, Iterable<? extends E> toAdd) {
-      uncheckedAddAll(key, toAdd, this.items);
-      if (ObjcProvider.KEYS_FOR_DIRECT.contains(key)) {
-        uncheckedAddAllDirect(key, toAdd, this.directItems);
+      uncheckedAddAll(key, toAdd);
+      return this;
+    }
+
+    public <E> Builder addAllDirect(Key<E> key, Iterable<? extends E> toAdd) {
+      Preconditions.checkState(KEYS_FOR_DIRECT.contains(key));
+      uncheckedAddAllDirect(key, toAdd);
+      return this;
+    }
+
+    Builder addStrictDependencyIncludes(Iterable<PathFragment> includes) {
+      strictDependencyIncludes.addAll(includes);
+      return this;
+    }
+
+    abstract ObjcProvider build();
+
+    protected ObjcProvider build(CcCompilationContext ccCompilationContext) {
+      ImmutableMap.Builder<Key<?>, NestedSet<?>> propagatedBuilder = new ImmutableMap.Builder<>();
+      for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : items.entrySet()) {
+        propagatedBuilder.put(typeEntry.getKey(), typeEntry.getValue().build());
       }
+      return new ObjcProvider(
+          starlarkSemantics,
+          propagatedBuilder.build(),
+          strictDependencyIncludes.build(),
+          directItems.build(),
+          ccCompilationContext);
+    }
+  }
+
+  /** A builder for this context, specialized for native use. */
+  public static final class NativeBuilder extends Builder {
+    private CcCompilationContext ccCompilationContext = CcCompilationContext.EMPTY;
+
+    public NativeBuilder(StarlarkSemantics semantics) {
+      super(semantics);
+    }
+
+    Builder setCcCompilationContext(CcCompilationContext ccCompilationContext) {
+      Preconditions.checkState(this.ccCompilationContext == CcCompilationContext.EMPTY);
+      Preconditions.checkNotNull(ccCompilationContext);
+      this.ccCompilationContext = ccCompilationContext;
       return this;
     }
 
-    /**
-     * Add elements in toAdd, and do not propagate to dependents of this provider.
-     */
-    public <E> Builder addAllNonPropagable(Key<E> key, Iterable<? extends E> toAdd) {
-      Preconditions.checkState(ObjcProvider.STRICT_DEPENDENCY_KEYS.contains(key));
-      uncheckedAddAll(key, toAdd, this.nonPropagatedItems);
-      return this;
+    @Override
+    public ObjcProvider build() {
+      return build(ccCompilationContext);
     }
+  }
 
-    /**
-     * Add element toAdd, and propagate it only to direct dependents of this provider.
-     */
-    public <E> Builder addForDirectDependents(Key<E> key, E toAdd) {
-      Preconditions.checkState(ObjcProvider.STRICT_DEPENDENCY_KEYS.contains(key));
-      uncheckedAddAll(key, ImmutableList.of(toAdd), this.strictDependencyItems);
-      return this;
-    }
+  /** A builder for this context, specialized for Starlark use. */
+  public static final class StarlarkBuilder extends Builder {
+    private final CcCompilationContext.Builder ccCompilationContextBuilder =
+        CcCompilationContext.builder(null, null, null);
 
-    /**
-     * Add elements in toAdd, and propagate them only to direct dependents of this provider.
-     */
-    public <E> Builder addAllForDirectDependents(Key<E> key, Iterable<? extends E> toAdd) {
-      Preconditions.checkState(ObjcProvider.STRICT_DEPENDENCY_KEYS.contains(key));
-      uncheckedAddAll(key, toAdd, this.strictDependencyItems);
-      return this;
+    public StarlarkBuilder(StarlarkSemantics semantics) {
+      super(semantics);
     }
 
     /**
@@ -1242,9 +1181,60 @@
      */
     void addElementsFromSkylark(Key<?> key, Object skylarkToAdd) throws EvalException {
       NestedSet<?> toAdd = ObjcProviderSkylarkConverters.convertToJava(key, skylarkToAdd);
-      uncheckedAddTransitive(key, toAdd, this.items);
-      if (ObjcProvider.KEYS_FOR_DIRECT.contains(key)) {
-        uncheckedAddAllDirect(key, toAdd.toList(), this.directItems);
+      if (KEYS_FOR_COMPILE_INFO.contains(key)) {
+        String keyName = key.getSkylarkKeyName();
+
+        if (key == DEFINE) {
+          ccCompilationContextBuilder.addDefines(
+              Depset.getSetFromNoneableParam(skylarkToAdd, String.class, keyName));
+        } else if (key == FRAMEWORK_SEARCH_PATHS) {
+          // Due to legacy reasons, There is a mismatch between the starlark interface for the
+          // framework search path, and the internal representation.  The interface specifies that
+          // framework_search_paths include the framework directories, but internally we only store
+          // their parents.  We will eventually clean up the interface, but for now we need to do
+          // this ugly conversion.
+
+          ImmutableList<PathFragment> frameworks =
+              Depset.getSetFromNoneableParam(skylarkToAdd, String.class, keyName).toList().stream()
+                  .map(x -> PathFragment.create(x))
+                  .collect(ImmutableList.toImmutableList());
+
+          ImmutableList.Builder<PathFragment> frameworkSearchPaths = ImmutableList.builder();
+          for (PathFragment framework : frameworks) {
+            if (!framework.getSafePathString().endsWith(FRAMEWORK_SUFFIX)) {
+              throw new EvalException(
+                  null, String.format(AppleSkylarkCommon.BAD_FRAMEWORK_PATH_ERROR, framework));
+            }
+            frameworkSearchPaths.add(framework.getParentDirectory());
+          }
+          ccCompilationContextBuilder.addFrameworkIncludeDirs(frameworkSearchPaths.build());
+        } else if (key == HEADER) {
+          ImmutableList<Artifact> hdrs =
+              Depset.getSetFromNoneableParam(skylarkToAdd, Artifact.class, keyName).toList();
+          ccCompilationContextBuilder.addDeclaredIncludeSrcs(hdrs);
+          ccCompilationContextBuilder.addTextualHdrs(hdrs);
+        } else if (key == INCLUDE) {
+          ccCompilationContextBuilder.addIncludeDirs(
+              Depset.getSetFromNoneableParam(skylarkToAdd, String.class, keyName).toList().stream()
+                  .map(x -> PathFragment.create(x))
+                  .collect(ImmutableList.toImmutableList()));
+        } else if (key == INCLUDE_SYSTEM) {
+          ccCompilationContextBuilder.addSystemIncludeDirs(
+              Depset.getSetFromNoneableParam(skylarkToAdd, String.class, keyName).toList().stream()
+                  .map(x -> PathFragment.create(x))
+                  .collect(ImmutableList.toImmutableList()));
+        } else if (key == IQUOTE) {
+          ccCompilationContextBuilder.addQuoteIncludeDirs(
+              Depset.getSetFromNoneableParam(skylarkToAdd, String.class, keyName).toList().stream()
+                  .map(x -> PathFragment.create(x))
+                  .collect(ImmutableList.toImmutableList()));
+        }
+      } else {
+        uncheckedAddTransitive(key, toAdd);
+      }
+
+      if (KEYS_FOR_DIRECT.contains(key)) {
+        uncheckedAddAllDirect(key, toAdd.toList());
       }
     }
 
@@ -1269,7 +1259,10 @@
                     AppleSkylarkCommon.BAD_PROVIDERS_ELEM_ERROR,
                     EvalUtils.getDataTypeName(toAddObject)));
           } else {
-            this.addTransitiveAndPropagate((ObjcProvider) toAddObject);
+            ObjcProvider objcProvider = (ObjcProvider) toAddObject;
+            this.addTransitiveAndPropagate(objcProvider);
+            ccCompilationContextBuilder.mergeDependentCcCompilationContext(
+                objcProvider.getCcCompilationContext());
           }
         }
       }
@@ -1302,28 +1295,9 @@
       }
     }
 
+    @Override
     public ObjcProvider build() {
-      ImmutableMap.Builder<Key<?>, NestedSet<?>> propagatedBuilder = new ImmutableMap.Builder<>();
-      for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : items.entrySet()) {
-        propagatedBuilder.put(typeEntry.getKey(), typeEntry.getValue().build());
-      }
-      ImmutableMap.Builder<Key<?>, NestedSet<?>> nonPropagatedBuilder =
-          new ImmutableMap.Builder<>();
-      for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : nonPropagatedItems.entrySet()) {
-        nonPropagatedBuilder.put(typeEntry.getKey(), typeEntry.getValue().build());
-      }
-      ImmutableMap.Builder<Key<?>, NestedSet<?>> strictDependencyBuilder =
-          new ImmutableMap.Builder<>();
-      for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : strictDependencyItems.entrySet()) {
-        strictDependencyBuilder.put(typeEntry.getKey(), typeEntry.getValue().build());
-      }
-
-      return new ObjcProvider(
-          starlarkSemantics,
-          propagatedBuilder.build(),
-          nonPropagatedBuilder.build(),
-          strictDependencyBuilder.build(),
-          directItems.build());
+      return build(ccCompilationContextBuilder.build());
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtobufSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtobufSupport.java
index 72c4dd3..1cac4a4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtobufSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtobufSupport.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.rules.objc;
 
+import static com.google.common.base.Preconditions.checkState;
+
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
@@ -71,6 +73,7 @@
   private final NestedSet<Artifact> portableProtoFilters;
   private final CcToolchainProvider toolchain;
   private final ImmutableSet<Artifact> dylibHandledProtos;
+  private Optional<ObjcProvider> objcProvider;
 
   /**
    * Creates a new proto support for the protobuf library. This support code bundles up all the
@@ -102,6 +105,7 @@
     this.portableProtoFilters = portableProtoFilters;
     this.toolchain = toolchain;
     this.dylibHandledProtos = dylibHandledProtos.toSet();
+    this.objcProvider = Optional.absent();
   }
 
   /** Registers the action that will compile the generated code. */
@@ -133,36 +137,22 @@
             .build();
 
     compilationSupport.registerCompileAndArchiveActions(common, userHeaderSearchPaths);
+    setObjcProvider(compilationSupport.getObjcProvider());
 
     return this;
   }
 
+  private void setObjcProvider(ObjcProvider objcProvider) {
+    checkState(!this.objcProvider.isPresent());
+    this.objcProvider = Optional.of(objcProvider);
+  }
+
   /**
    * Returns the ObjcProvider for this target, or Optional.absent() if there were no protos to
    * generate.
    */
-  Optional<ObjcProvider> getObjcProvider() throws InterruptedException {
-    if (!hasOutputProtos()) {
-      return Optional.absent();
-    }
-
-    Iterable<PathFragment> includes = ImmutableList.of(getWorkspaceRelativeOutputDir());
-    ObjcCommon.Builder commonBuilder = new ObjcCommon.Builder(ruleContext);
-
-    CompilationArtifacts compilationArtifacts =
-        new CompilationArtifacts.Builder()
-            .setIntermediateArtifacts(getUniqueIntermediateArtifactsForSourceCompile())
-            .addNonArcSrcs(getGeneratedProtoOutputs(getOutputProtos(), SOURCE_SUFFIX))
-            .addAdditionalHdrs(getGeneratedProtoOutputs(getAllProtos(), HEADER_SUFFIX))
-            .addAdditionalHdrs(getProtobufHeaders())
-            .build();
-
-    ObjcCommon common =
-        getCommon(getUniqueIntermediateArtifactsForSourceCompile(), compilationArtifacts);
-    commonBuilder.addDepObjcProviders(ImmutableSet.of(common.getObjcProvider()));
-    commonBuilder.addIncludes(includes);
-
-    return Optional.of(commonBuilder.build().getObjcProvider());
+  public Optional<ObjcProvider> getObjcProvider() {
+    return objcProvider;
   }
 
   private NestedSet<Artifact> getProtobufHeaders() {
@@ -227,7 +217,7 @@
   private ObjcCommon getCommon(
       IntermediateArtifacts intermediateArtifacts, CompilationArtifacts compilationArtifacts)
       throws InterruptedException {
-    return new ObjcCommon.Builder(ruleContext)
+    return new ObjcCommon.Builder(ObjcCommon.Purpose.COMPILE_AND_LINK, ruleContext)
         .setIntermediateArtifacts(intermediateArtifacts)
         .setCompilationArtifacts(compilationArtifacts)
         .addIncludes(getProtobufHeaderSearchPaths())
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/BUILD b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/BUILD
index 0e80f2d..df8f475 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/BUILD
@@ -24,6 +24,7 @@
         "//src/main/java/com/google/devtools/build/lib:syntax",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/core",
+        "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform",
         "//src/main/java/com/google/devtools/build/lib/skylarkinterface",
         "//third_party:guava",
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
index 58c0b0a..1fbdcd2 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.skylarkbuildapi.apple;
 
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
+import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcCompilationContextApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -60,7 +61,7 @@
       doc =
           "Exec paths of .framework directories corresponding to frameworks to include "
               + "in search paths, but not to link.")
-  Depset /*<String>*/ frameworkSearchPathOnly();
+  Depset /*<String>*/ frameworkIncludeForStarlark();
 
   @SkylarkCallable(
       name = "force_load_library",
@@ -94,19 +95,27 @@
       doc =
           "Include search paths specified with '-I' on the command line. Also known as "
               + "header search paths (and distinct from <em>user</em> header search paths).")
-  Depset include();
+  Depset includeForStarlark();
+
+  @SkylarkCallable(
+      name = "strict_include",
+      structField = true,
+      doc =
+          "Non-propagated include search paths specified with '-I' on the command line. Also known "
+              + "as header search paths (and distinct from <em>user</em> header search paths).")
+  Depset strictIncludeForStarlark();
 
   @SkylarkCallable(
       name = "include_system",
       structField = true,
       doc = "System include search paths (typically specified with -isystem).")
-  Depset includeSystem();
+  Depset systemIncludeForStarlark();
 
   @SkylarkCallable(
       name = "iquote",
       structField = true,
       doc = "User header search paths (typically specified with -iquote).")
-  Depset iquote();
+  Depset quoteIncludeForStarlark();
 
   @SkylarkCallable(
       name = "j2objc_library",
@@ -261,4 +270,12 @@
       structField = true,
       doc = "Returns all framework paths to static frameworks in this provider.")
   Depset /*<String>*/ staticFrameworkPathsForStarlark();
+
+  @SkylarkCallable(
+      name = "compilation_context",
+      doc =
+          "Returns the embedded <code>CcCompilationContext</code> that contains the"
+              + "provider's compilation information.",
+      structField = true)
+  CcCompilationContextApi getCcCompilationContext();
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/BUILD b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/BUILD
index 0a2d946..7005165 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/BUILD
@@ -15,11 +15,11 @@
     name = "apple",
     srcs = glob(["*.java"]),
     deps = [
-        "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:syntax",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/core",
+        "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform",
         "//src/main/java/com/google/devtools/build/skydoc/fakebuildapi",
         "//third_party:guava",
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
index ebffe30..79d118b 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
@@ -16,6 +16,7 @@
 
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
 import com.google.devtools.build.lib.skylarkbuildapi.apple.ObjcProviderApi;
+import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcCompilationContextApi;
 import com.google.devtools.build.lib.syntax.Depset;
 import com.google.devtools.build.lib.syntax.Printer;
 import com.google.devtools.build.lib.syntax.Sequence;
@@ -41,7 +42,7 @@
   }
 
   @Override
-  public Depset frameworkSearchPathOnly() {
+  public Depset frameworkIncludeForStarlark() {
     return null;
   }
 
@@ -66,17 +67,22 @@
   }
 
   @Override
-  public Depset include() {
+  public Depset includeForStarlark() {
     return null;
   }
 
   @Override
-  public Depset includeSystem() {
+  public Depset /*<String>*/ strictIncludeForStarlark() {
     return null;
   }
 
   @Override
-  public Depset iquote() {
+  public Depset systemIncludeForStarlark() {
+    return null;
+  }
+
+  @Override
+  public Depset quoteIncludeForStarlark() {
     return null;
   }
 
@@ -181,6 +187,11 @@
   }
 
   @Override
+  public CcCompilationContextApi getCcCompilationContext() {
+    return null;
+  }
+
+  @Override
   public Depset /*<String>*/ dynamicFrameworkNamesForStarlark() {
     return null;
   }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/MockCppSemantics.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/MockCppSemantics.java
index fa4e15c..042083f 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/MockCppSemantics.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/MockCppSemantics.java
@@ -14,13 +14,9 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
@@ -44,11 +40,6 @@
       CppCompileActionBuilder actionBuilder) {}
 
   @Override
-  public NestedSet<Artifact> getAdditionalPrunableIncludes() {
-    return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
-  }
-
-  @Override
   public IncludeProcessing getIncludeProcessing() {
     return null;
   }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
index aa0ac0d..ee3cd05 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
@@ -91,7 +91,7 @@
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX
                 + "third_party/java/j2objc/libjre_core_lib.a",
             "java/com/google/dummy/test/libtest_j2objc.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h");
@@ -99,9 +99,7 @@
     String execPath =
         getConfiguration(j2objcLibraryTarget).getBinDirectory(RepositoryName.MAIN).getExecPath()
             + "/";
-    assertThat(
-            Iterables.transform(
-                provider.get(ObjcProvider.INCLUDE).toList(), PathFragment::getSafePathString))
+    assertThat(Iterables.transform(provider.include(), PathFragment::getSafePathString))
         .containsExactly(execPath + "java/com/google/dummy/test/_j2objc/test");
   }
 
@@ -135,7 +133,7 @@
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX
                 + "third_party/java/j2objc/libjre_core_lib.a",
             "java/com/google/test/libtest_j2objc.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "java/com/google/test/_j2objc/test/"
@@ -144,9 +142,7 @@
 
     String execPath =
         getConfiguration(target).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/";
-    assertThat(
-            Iterables.transform(
-                provider.get(ObjcProvider.INCLUDE).toList(), PathFragment::getSafePathString))
+    assertThat(Iterables.transform(provider.include(), PathFragment::getSafePathString))
         .containsExactly(
             execPath + "java/com/google/test/_j2objc/test/" + genfilesFragment,
             execPath + "java/com/google/test/_j2objc/test");
@@ -189,7 +185,7 @@
                 + "third_party/java/j2objc/libproto_runtime.a",
             "java/com/google/dummy/test/proto/libtest_j2objc.a",
             "java/com/google/dummy/test/proto/libtest_proto_j2objc.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/runtime.h",
@@ -299,7 +295,7 @@
     ObjcProvider objcProvider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
     Artifact headerFile = getGenfilesArtifact("test.j2objc.pb.h", test, getJ2ObjcAspect());
     Artifact sourceFile = getGenfilesArtifact("test.j2objc.pb.m", test, getJ2ObjcAspect());
-    assertThat(objcProvider.get(ObjcProvider.HEADER).toList()).contains(headerFile);
+    assertThat(objcProvider.header().toList()).contains(headerFile);
     assertThat(objcProvider.get(ObjcProvider.SOURCE).toList()).contains(sourceFile);
   }
 
@@ -350,9 +346,9 @@
         getGenfilesArtifact("../external/bla/foo/test.j2objc.pb.h", test, getJ2ObjcAspect());
     Artifact sourceFile =
         getGenfilesArtifact("../external/bla/foo/test.j2objc.pb.m", test, getJ2ObjcAspect());
-    assertThat(objcProvider.get(ObjcProvider.HEADER).toList()).contains(headerFile);
+    assertThat(objcProvider.header().toList()).contains(headerFile);
     assertThat(objcProvider.get(ObjcProvider.SOURCE).toList()).contains(sourceFile);
-    assertThat(objcProvider.get(ObjcProvider.INCLUDE).toList())
+    assertThat(objcProvider.include())
         .contains(getConfiguration(target).getGenfilesFragment().getRelative("external/bla"));
   }
 
@@ -423,7 +419,7 @@
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX
                 + "third_party/java/j2objc/libjre_core_lib.a",
             "java/com/google/dummy/test/libtest_j2objc.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h");
@@ -445,8 +441,7 @@
     ObjcProvider provider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
     Artifact srcJarSources = getFirstArtifactEndingWith(
         provider.get(ObjcProvider.SOURCE), "source_files");
-    Artifact srcJarHeaders = getFirstArtifactEndingWith(
-        provider.get(ObjcProvider.HEADER), "header_files");
+    Artifact srcJarHeaders = getFirstArtifactEndingWith(provider.header(), "header_files");
     assertThat(srcJarSources.getRootRelativePathString())
         .isEqualTo("java/com/google/transpile/_j2objc/src_jar_files/dummy/source_files");
     assertThat(srcJarHeaders.getRootRelativePathString())
@@ -462,8 +457,7 @@
     ConfiguredTarget j2objcLibraryTarget =
         getConfiguredTarget("//java/com/google/app/test:transpile");
     ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
-    Artifact headers =
-        getFirstArtifactEndingWith(provider.get(ObjcProvider.HEADER), "header_files");
+    Artifact headers = getFirstArtifactEndingWith(provider.header(), "header_files");
     Artifact sources =
         getFirstArtifactEndingWith(provider.get(ObjcProvider.SOURCE), "source_files");
     assertThat(headers.isTreeArtifact()).isTrue();
@@ -631,16 +625,14 @@
                 + "third_party/java/j2objc/libjre_core_lib.a",
             "java/com/google/dummy/test/libtest_j2objc.a",
             "app/liblib.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h");
 
     String execPath =
         getConfiguration(objcTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/";
-    assertThat(
-            Iterables.transform(
-                provider.get(ObjcProvider.INCLUDE).toList(), PathFragment::getSafePathString))
+    assertThat(Iterables.transform(provider.include(), PathFragment::getSafePathString))
         .containsExactly(execPath + "java/com/google/dummy/test/_j2objc/test");
   }
 
@@ -680,7 +672,7 @@
             "app/libdummyOne_j2objc.a",
             "app/libdummyTwo_j2objc.a",
             "app/liblib.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "app/_j2objc/dummyOne/app/dummyOne.h",
@@ -688,9 +680,7 @@
 
     String execPath =
         getConfiguration(objcTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/";
-    assertThat(
-            Iterables.transform(
-                provider.get(ObjcProvider.INCLUDE).toList(), PathFragment::getSafePathString))
+    assertThat(Iterables.transform(provider.include(), PathFragment::getSafePathString))
         .containsExactly(execPath + "app/_j2objc/dummyOne", execPath + "app/_j2objc/dummyTwo");
   }
 
@@ -829,8 +819,7 @@
     CppModuleMapAction moduleMapAction = (CppModuleMapAction) getGeneratingAction(moduleMap);
     UmbrellaHeaderAction umbrellaHeaderAction =
         (UmbrellaHeaderAction) getGeneratingAction(umbrellaHeader);
-    Artifact headers =
-        getFirstArtifactEndingWith(provider.get(ObjcProvider.HEADER), "header_files");
+    Artifact headers = getFirstArtifactEndingWith(provider.header(), "header_files");
 
     // Test that the module map action contains the header tree artifact as both the public header
     // and part of the action inputs.
@@ -1102,7 +1091,7 @@
             "tools/j2objc/libalt_proto_runtime.a",
             "java/com/google/dummy/test/proto/libtest_j2objc.a",
             "java/com/google/dummy/test/proto/libtest_proto_j2objc.a");
-    assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER)))
+    assertThat(Artifact.toRootRelativePaths(provider.header()))
         .containsExactly(
             TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h",
             "tools/j2objc/alt_proto_runtime.h",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcImportTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcImportTest.java
index 247fadf..c9ac71a 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcImportTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcImportTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.base.Optional;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.CommandAction;
 import com.google.devtools.build.lib.testutil.Scratch;
@@ -117,7 +118,7 @@
 
   @Test
   public void testProvidesHdrsAndIncludes() throws Exception {
-    checkProvidesHdrsAndIncludes(RULE_TYPE);
+    checkProvidesHdrsAndIncludes(RULE_TYPE, Optional.absent());
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
index 095351f..1bc8def 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
@@ -21,7 +21,6 @@
 import static com.google.devtools.build.lib.rules.objc.CompilationSupport.BOTH_MODULE_NAME_AND_MODULE_MAP_SPECIFIED;
 import static com.google.devtools.build.lib.rules.objc.CompilationSupport.FILE_IN_SRCS_AND_HDRS_WARNING_FORMAT;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.CC_LIBRARY;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_DYLIB;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_FRAMEWORK;
@@ -31,6 +30,7 @@
 import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
 
 import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -53,7 +53,6 @@
 import com.google.devtools.build.lib.rules.cpp.CppModuleMap;
 import com.google.devtools.build.lib.rules.cpp.CppModuleMapAction;
 import com.google.devtools.build.lib.rules.cpp.CppRuleClasses;
-import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key;
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.common.options.OptionsParsingException;
 import java.util.List;
@@ -377,10 +376,17 @@
             .setAndCreateFiles("hdrs", "d.h", "e.m")
             .setList("deps", "//objc:lib")
             .write();
-    assertThat(getArifactPaths(target, HEADER))
-        .containsExactly("objc/a.h", "objc/b.h", "objc/f.m");
-    assertThat(getArifactPaths(depender, HEADER))
-        .containsExactly("objc/a.h", "objc/b.h", "objc/f.m", "objc2/d.h", "objc2/e.m");
+    assertThat(getArifactPathsOfHeaders(target))
+        .containsExactly("objc/a.h", "objc/b.h", "objc/f.m", "objc/private.h");
+    assertThat(getArifactPathsOfHeaders(depender))
+        .containsExactly(
+            "objc/a.h",
+            "objc/b.h",
+            "objc/f.m",
+            "objc/private.h",
+            "objc2/d.h",
+            "objc2/e.m",
+            "objc2/private.h");
   }
 
   @Test
@@ -992,17 +998,23 @@
     assertThat(getArifactPaths(target, LIBRARY)).containsExactly("objc/liblib.a");
     assertThat(getArifactPaths(depender, LIBRARY)).containsExactly(
         "objc/liblib.a", "objc2/liblib.a");
-    assertThat(getArifactPaths(target, HEADER))
-        .containsExactly("objc/a.h", "objc/b.h");
-    assertThat(getArifactPaths(depender, HEADER))
-        .containsExactly("objc/a.h", "objc/b.h", "objc2/c.h", "objc2/d.h");
+    assertThat(getArifactPathsOfHeaders(target))
+        .containsExactly("objc/a.h", "objc/b.h", "objc/private.h");
+    assertThat(getArifactPathsOfHeaders(depender))
+        .containsExactly(
+            "objc/a.h", "objc/b.h", "objc/private.h", "objc2/c.h", "objc2/d.h", "objc2/private.h");
   }
 
-  private Iterable<String> getArifactPaths(ConfiguredTarget target, Key<Artifact> artifactKey) {
+  private static Iterable<String> getArifactPaths(
+      ConfiguredTarget target, ObjcProvider.Key<Artifact> artifactKey) {
     return Artifact.toRootRelativePaths(
         target.get(ObjcProvider.SKYLARK_CONSTRUCTOR).get(artifactKey));
   }
 
+  private static Iterable<String> getArifactPathsOfHeaders(ConfiguredTarget target) {
+    return Artifact.toRootRelativePaths(target.get(ObjcProvider.SKYLARK_CONSTRUCTOR).header());
+  }
+
   @Test
   public void testWeakSdkFrameworks_objcProvider() throws Exception {
     createLibraryTargetWriter("//base_lib:lib")
@@ -1565,7 +1577,7 @@
 
   @Test
   public void testProvidesHdrsAndIncludes() throws Exception {
-    checkProvidesHdrsAndIncludes(RULE_TYPE);
+    checkProvidesHdrsAndIncludes(RULE_TYPE, Optional.of("x/private.h"));
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcProviderTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcProviderTest.java
index b81a060..29d42f3 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcProviderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcProviderTest.java
@@ -27,12 +27,10 @@
 import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key;
 import com.google.devtools.build.lib.syntax.Depset;
 import com.google.devtools.build.lib.syntax.StarlarkSemantics;
-import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -41,8 +39,8 @@
 @RunWith(JUnit4.class)
 public class ObjcProviderTest {
 
-  private static ObjcProvider.Builder objcProviderBuilder() {
-    return new ObjcProvider.Builder(StarlarkSemantics.DEFAULT_SEMANTICS);
+  private static ObjcProvider.StarlarkBuilder objcProviderBuilder() {
+    return new ObjcProvider.StarlarkBuilder(StarlarkSemantics.DEFAULT_SEMANTICS);
   }
 
   private static ImmutableList<ObjcProvider.Key<?>> getAllKeys() throws Exception {
@@ -74,7 +72,7 @@
     Artifact rootArtifact = createArtifact("/root.m");
     ObjcProvider root =
         objcProviderBuilder()
-            .add(ObjcProvider.SOURCE, rootArtifact)
+            .addDirect(ObjcProvider.SOURCE, rootArtifact)
             .addTransitiveAndPropagate(leaf)
             .build();
     assertThat(root.getDirect(ObjcProvider.SOURCE)).containsExactly(rootArtifact);
@@ -87,9 +85,9 @@
     Artifact module = createArtifact("/module.modulemap");
     ObjcProvider provider =
         objcProviderBuilder()
-            .add(ObjcProvider.SOURCE, source)
-            .add(ObjcProvider.HEADER, header)
-            .add(ObjcProvider.MODULE_MAP, module)
+            .addDirect(ObjcProvider.SOURCE, source)
+            .addDirect(ObjcProvider.HEADER, header)
+            .addDirect(ObjcProvider.MODULE_MAP, module)
             .build();
     assertThat(provider.getDirect(ObjcProvider.SOURCE)).containsExactly(source);
     assertThat(provider.getDirect(ObjcProvider.HEADER)).containsExactly(header);
@@ -102,9 +100,9 @@
         ImmutableList.of(createArtifact("/foo"), createArtifact("/bar"));
     ObjcProvider provider =
         objcProviderBuilder()
-            .addAll(ObjcProvider.SOURCE, artifacts)
-            .addAll(ObjcProvider.HEADER, artifacts)
-            .addAll(ObjcProvider.MODULE_MAP, artifacts)
+            .addAllDirect(ObjcProvider.SOURCE, artifacts)
+            .addAllDirect(ObjcProvider.HEADER, artifacts)
+            .addAllDirect(ObjcProvider.MODULE_MAP, artifacts)
             .build();
     assertThat(provider.getDirect(ObjcProvider.SOURCE)).containsExactlyElementsIn(artifacts);
     assertThat(provider.getDirect(ObjcProvider.HEADER)).containsExactlyElementsIn(artifacts);
@@ -116,7 +114,7 @@
     ImmutableList<Artifact> artifacts =
         ImmutableList.of(createArtifact("/foo"), createArtifact("/bar"));
     Depset set = Depset.of(Artifact.TYPE, NestedSetBuilder.wrap(Order.STABLE_ORDER, artifacts));
-    ObjcProvider.Builder builder = objcProviderBuilder();
+    ObjcProvider.StarlarkBuilder builder = objcProviderBuilder();
     builder.addElementsFromSkylark(ObjcProvider.SOURCE, set);
     builder.addElementsFromSkylark(ObjcProvider.HEADER, set);
     builder.addElementsFromSkylark(ObjcProvider.MODULE_MAP, set);
@@ -127,28 +125,6 @@
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void directFieldsLimitedToCertainKeys() throws Exception {
-    ObjcProvider.Builder builder = objcProviderBuilder();
-    ImmutableList<String> values = ImmutableList.of("dummy", "fooey");
-
-    List<ObjcProvider.Key<?>> allKeys = getAllKeys();
-    for (ObjcProvider.Key<?> key : allKeys) {
-      // Use a List without a generic type to trick the compiler into allowing strings.
-      builder.addAll(key, (List) values);
-    }
-    ObjcProvider provider = builder.build();
-
-    for (ObjcProvider.Key<?> key : allKeys) {
-      if (ObjcProvider.KEYS_FOR_DIRECT.contains(key)) {
-        assertThat(provider.getDirect(key)).containsExactlyElementsIn(values);
-      } else {
-        assertThat(provider.getDirect(key)).isEmpty();
-      }
-    }
-  }
-
-  @Test
   public void onlyPropagatesProvider() {
     ObjcProvider onlyPropagates = objcProviderBuilder()
         .add(ObjcProvider.SDK_DYLIB, "foo")
@@ -157,53 +133,6 @@
   }
 
   @Test
-  public void strictDependencyDoesNotPropagateMoreThanOneLevel() {
-    PathFragment strictInclude = PathFragment.create("strict_path");
-    PathFragment propagatedInclude = PathFragment.create("propagated_path");
-
-    ObjcProvider strictDep =
-        objcProviderBuilder()
-            .addForDirectDependents(ObjcProvider.INCLUDE, strictInclude)
-            .build();
-    ObjcProvider propagatedDep =
-        objcProviderBuilder().add(ObjcProvider.INCLUDE, propagatedInclude).build();
-
-    ObjcProvider provider =
-        objcProviderBuilder()
-            .addTransitiveAndPropagate(ImmutableList.of(strictDep, propagatedDep))
-            .build();
-    ObjcProvider depender = objcProviderBuilder().addTransitiveAndPropagate(provider).build();
-
-    assertThat(provider.get(ObjcProvider.INCLUDE).toList())
-        .containsExactly(strictInclude, propagatedInclude);
-    assertThat(depender.get(ObjcProvider.INCLUDE).toList()).containsExactly(propagatedInclude);
-  }
-
-  @Test
-  public void strictDependencyDoesNotPropagateMoreThanOneLevelOnSkylark() {
-    PathFragment strictInclude = PathFragment.create("strict_path");
-    PathFragment propagatedInclude = PathFragment.create("propagated_path");
-
-    ObjcProvider strictDep =
-        objcProviderBuilder()
-            .addForDirectDependents(ObjcProvider.INCLUDE, strictInclude)
-            .build();
-    ObjcProvider propagatedDep =
-        objcProviderBuilder().add(ObjcProvider.INCLUDE, propagatedInclude).build();
-
-    ObjcProvider provider =
-        objcProviderBuilder()
-            .addTransitiveAndPropagate(ImmutableList.of(strictDep, propagatedDep))
-            .build();
-    ObjcProvider depender = objcProviderBuilder().addTransitiveAndPropagate(provider).build();
-
-    assertThat(provider.include().toCollection())
-        .containsExactly(strictInclude.toString(), propagatedInclude.toString());
-    assertThat(depender.include().toCollection())
-        .containsExactly(propagatedInclude.toString());
-  }
-
-  @Test
   public void keysExportedToSkylark() throws Exception {
     ImmutableSet<Key<?>> allRegisteredKeys = ImmutableSet.<Key<?>>builder()
         .addAll(ObjcProvider.KEYS_FOR_SKYLARK)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcRuleTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcRuleTestCase.java
index a32aeea..9695dfa 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcRuleTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcRuleTestCase.java
@@ -16,8 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
-import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MODULE_MAP;
 import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.LIPO;
 import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.SRCS_TYPE;
@@ -527,16 +525,20 @@
     assertContainsSublist(compileAction("//x:x", "b.o").getArguments(), includeFlags);
   }
 
-  protected void checkProvidesHdrsAndIncludes(RuleType ruleType) throws Exception {
+  protected void checkProvidesHdrsAndIncludes(RuleType ruleType, Optional<String> privateHdr)
+      throws Exception {
     scratch.file("x/a.h");
-    ruleType.scratchTarget(scratch,
-        "hdrs", "['a.h']",
-        "includes", "['incdir']");
+    ruleType.scratchTarget(scratch, "hdrs", "['a.h']", "includes", "['incdir']");
     ObjcProvider provider =
         getConfiguredTarget("//x:x", getAppleCrosstoolConfiguration())
             .get(ObjcProvider.SKYLARK_CONSTRUCTOR);
-    assertThat(provider.get(HEADER).toList()).containsExactly(getSourceArtifact("x/a.h"));
-    assertThat(provider.get(INCLUDE).toList())
+    if (privateHdr.isPresent()) {
+      assertThat(provider.header().toList())
+          .containsExactly(getSourceArtifact("x/a.h"), getSourceArtifact(privateHdr.get()));
+    } else {
+      assertThat(provider.header().toList()).containsExactly(getSourceArtifact("x/a.h"));
+    }
+    assertThat(provider.include())
         .containsExactly(
             PathFragment.create("x/incdir"),
             getAppleCrosstoolConfiguration().getGenfilesFragment().getRelative("x/incdir"));
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java
index ece89ea..0fb9679 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java
@@ -32,7 +32,6 @@
 import com.google.devtools.build.lib.rules.apple.AppleToolchain;
 import com.google.devtools.build.lib.rules.apple.DottedVersion;
 import com.google.devtools.build.lib.syntax.Depset;
-import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.List;
 import java.util.Map;
@@ -46,10 +45,6 @@
  */
 @RunWith(JUnit4.class)
 public class ObjcSkylarkTest extends ObjcRuleTestCase {
-  private static ObjcProvider.Builder objcProviderBuilder() {
-    return new ObjcProvider.Builder(StarlarkSemantics.DEFAULT_SEMANTICS);
-  }
-
   private void writeObjcSplitTransitionTestFiles() throws Exception {
     scratch.file(
         "examples/rule/apple_rules.bzl",
@@ -291,12 +286,20 @@
         "   srcs = ['a.m'],",
         "   defines = ['mock_define']",
         ")",
+        "objc_library(",
+        "   name = 'lib_root',",
+        "   deps = [':my_target']",
+        ")",
         "apple_binary(",
         "   name = 'bin',",
         "   platform_type = 'ios',",
-        "   deps = [':my_target']",
+        "   deps = [':lib_root']",
         ")");
 
+    ConfiguredTarget libRootTarget = getConfiguredTarget("//examples/apple_skylark:lib_root");
+    ObjcProvider libRootObjcProvider = libRootTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
+    assertThat(libRootObjcProvider.define().toList()).contains("mock_define");
+
     ConfiguredTarget binaryTarget = getConfiguredTarget("//examples/apple_skylark:bin");
     AppleExecutableBinaryInfo executableProvider =
         binaryTarget.get(AppleExecutableBinaryInfo.SKYLARK_CONSTRUCTOR);
@@ -304,7 +307,6 @@
 
     assertThat(Artifact.toRootRelativePaths(objcProvider.get(ObjcProvider.LIBRARY)))
         .contains("examples/apple_skylark/liblib.a");
-    assertThat(objcProvider.get(ObjcProvider.DEFINE).toList()).contains("mock_define");
   }
 
   @Test
@@ -337,12 +339,9 @@
         "   deps = [':lib']",
         ")");
 
-    ConfiguredTarget binaryTarget = getConfiguredTarget("//examples/apple_skylark:bin");
-    AppleExecutableBinaryInfo executableProvider =
-        binaryTarget.get(AppleExecutableBinaryInfo.SKYLARK_CONSTRUCTOR);
-    ObjcProvider objcProvider = executableProvider.getDepsObjcProvider();
-
-    assertThat(objcProvider.get(ObjcProvider.DEFINE).toList()).contains("mock_define");
+    ConfiguredTarget libTarget = getConfiguredTarget("//examples/apple_skylark:lib");
+    ObjcProvider libObjcProvider = libTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
+    assertThat(libObjcProvider.define().toList()).contains("mock_define");
   }
 
   @Test
@@ -924,7 +923,7 @@
     Iterable<String> foundLinkopts =
         skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).get(ObjcProvider.LINKOPT).toList();
     Iterable<String> foundDefines =
-        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).get(ObjcProvider.DEFINE).toList();
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).define().toList();
     boolean usesSwift =
         skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).is(ObjcProvider.Flag.USES_SWIFT);
 
@@ -963,7 +962,40 @@
   }
 
   @Test
-  public void testSkylarkCanCreateObjcProviderWithPathFragments() throws Exception {
+  public void testSkylarkCanCreateObjcProviderWithDefines() throws Exception {
+    ConfiguredTarget skylarkTarget =
+        createObjcProviderSkylarkTarget(
+            "   define = depset(['def1', 'def2', 'def3'])",
+            "   created_provider = apple_common.new_objc_provider\\",
+            "(define=define)",
+            "   return [created_provider]");
+
+    Iterable<String> foundDefines =
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).define().toList();
+
+    assertThat(foundDefines).containsExactly("def1", "def2", "def3");
+  }
+
+  @Test
+  public void testSkylarkCanCreateObjcProviderWithHeaders() throws Exception {
+    ConfiguredTarget skylarkTarget =
+        createObjcProviderSkylarkTarget(
+            "   hdr1 = ctx.actions.declare_file('hdr1')",
+            "   hdr2 = ctx.actions.declare_file('hdr2')",
+            "   ctx.actions.run_shell(outputs=[hdr1, hdr2], command='echo')",
+            "   header = depset([hdr1, hdr2])",
+            "   created_provider = apple_common.new_objc_provider\\",
+            "(header=header)",
+            "   return [created_provider]");
+
+    Iterable<Artifact> foundHeaders =
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).header().toList();
+
+    assertThat(ActionsTestUtil.baseArtifactNames(foundHeaders)).containsExactly("hdr1", "hdr2");
+  }
+
+  @Test
+  public void testSkylarkCanCreateObjcProviderWithIncludePathFragments() throws Exception {
     ConfiguredTarget skylarkTarget =
         createObjcProviderSkylarkTarget(
             "   includes = depset(['path1', 'path_dir/path2', 'path_dir1/path_dir2/path3'])",
@@ -972,7 +1004,61 @@
             "   return [created_provider]");
 
     Iterable<PathFragment> foundIncludes =
-        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).get(ObjcProvider.INCLUDE).toList();
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).include();
+
+    assertThat(foundIncludes)
+        .containsExactly(
+            PathFragment.create("path1"),
+            PathFragment.create("path_dir/path2"),
+            PathFragment.create("path_dir1/path_dir2/path3"));
+  }
+
+  @Test
+  public void testSkylarkCanCreateObjcProviderWithFrameworkIncludes() throws Exception {
+    ConfiguredTarget skylarkTarget =
+        createObjcProviderSkylarkTarget(
+            "   includes = depset(['path1/foo.framework', 'path_dir/path2/bar.framework'])",
+            "   created_provider = apple_common.new_objc_provider\\",
+            "(framework_search_paths=includes)",
+            "   return [created_provider]");
+
+    Iterable<PathFragment> foundIncludes =
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).frameworkInclude();
+
+    assertThat(foundIncludes)
+        .containsExactly(PathFragment.create("path1"), PathFragment.create("path_dir/path2"));
+  }
+
+  @Test
+  public void testSkylarkCanCreateObjcProviderWithSystemIncludes() throws Exception {
+    ConfiguredTarget skylarkTarget =
+        createObjcProviderSkylarkTarget(
+            "   includes = depset(['path1', 'path_dir/path2', 'path_dir1/path_dir2/path3'])",
+            "   created_provider = apple_common.new_objc_provider\\",
+            "(include_system=includes)",
+            "   return [created_provider]");
+
+    Iterable<PathFragment> foundIncludes =
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).systemInclude();
+
+    assertThat(foundIncludes)
+        .containsExactly(
+            PathFragment.create("path1"),
+            PathFragment.create("path_dir/path2"),
+            PathFragment.create("path_dir1/path_dir2/path3"));
+  }
+
+  @Test
+  public void testSkylarkCanCreateObjcProviderWithQuoteIncludes() throws Exception {
+    ConfiguredTarget skylarkTarget =
+        createObjcProviderSkylarkTarget(
+            "   includes = depset(['path1', 'path_dir/path2', 'path_dir1/path_dir2/path3'])",
+            "   created_provider = apple_common.new_objc_provider\\",
+            "(iquote=includes)",
+            "   return [created_provider]");
+
+    Iterable<PathFragment> foundIncludes =
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).quoteInclude();
 
     assertThat(foundIncludes)
         .containsExactly(
@@ -994,21 +1080,37 @@
             "   return [created_provider]");
 
     ObjcProvider skylarkProvider = skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
-    ObjcProvider skylarkProviderDirectDepender =
-        objcProviderBuilder().addTransitiveAndPropagate(skylarkProvider).build();
-    ObjcProvider skylarkProviderIndirectDepender =
-        objcProviderBuilder().addTransitiveAndPropagate(skylarkProviderDirectDepender).build();
+    assertThat(skylarkProvider.include())
+        .containsExactly(PathFragment.create("path1"), PathFragment.create("path2"));
+    assertThat(skylarkProvider.getStrictDependencyIncludes())
+        .containsExactly(PathFragment.create("path1"));
 
-    assertThat(skylarkProvider.get(ObjcProvider.INCLUDE).toList())
-        .containsExactly(PathFragment.create("path1"), PathFragment.create("path2"));
-    assertThat(skylarkProviderDirectDepender.get(ObjcProvider.INCLUDE).toList())
-        .containsExactly(PathFragment.create("path1"), PathFragment.create("path2"));
-    assertThat(skylarkProviderIndirectDepender.get(ObjcProvider.INCLUDE).toList())
+    scratch.file(
+        "examples/objc_skylark2/BUILD",
+        "objc_library(",
+        "   name = 'direct_dep',",
+        "   deps = ['//examples/objc_skylark:my_target']",
+        ")",
+        "objc_library(",
+        "   name = 'indirect_dep',",
+        "   deps = [':direct_dep']",
+        ")");
+
+    ObjcProvider skylarkProviderDirectDepender =
+        getConfiguredTarget("//examples/objc_skylark2:direct_dep")
+            .get(ObjcProvider.SKYLARK_CONSTRUCTOR);
+    assertThat(skylarkProviderDirectDepender.include())
+        .containsExactly(PathFragment.create("path2"));
+
+    ObjcProvider skylarkProviderIndirectDepender =
+        getConfiguredTarget("//examples/objc_skylark2:indirect_dep")
+            .get(ObjcProvider.SKYLARK_CONSTRUCTOR);
+    assertThat(skylarkProviderIndirectDepender.include())
         .containsExactly(PathFragment.create("path2"));
   }
 
   @Test
-  public void testSkylarkStrictDepsWhitelist() throws Exception {
+  public void testSkylarkStrictDepsDoesNotSupportDefine() throws Exception {
     AssertionError e =
         assertThrows(
             AssertionError.class,
@@ -1026,6 +1128,24 @@
   }
 
   @Test
+  public void testSkylarkStrictDepsDoesNotSupportLinkopt() throws Exception {
+    AssertionError e =
+        assertThrows(
+            AssertionError.class,
+            () ->
+                createObjcProviderSkylarkTarget(
+                    "   strict_linkopts = depset(['opt1'])",
+                    "   strict_provider = apple_common.new_objc_provider\\",
+                    "(linkopt=strict_linkopts)",
+                    "   created_provider = apple_common.new_objc_provider\\",
+                    "(direct_dep_providers=[strict_provider])",
+                    "   return [created_provider]"));
+    assertThat(e)
+        .hasMessageThat()
+        .contains(String.format(AppleSkylarkCommon.BAD_DIRECT_DEPENDENCY_KEY_ERROR, "linkopt"));
+  }
+
+  @Test
   public void testSkylarkCanCreateObjcProviderFromObjcProvider() throws Exception {
     ConfiguredTarget skylarkTarget =
         createObjcProviderSkylarkTarget(
@@ -1036,7 +1156,7 @@
             "   return [created_provider]");
 
     Iterable<String> foundStrings =
-        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).get(ObjcProvider.DEFINE).toList();
+        skylarkTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR).define().toList();
 
     assertThat(foundStrings).containsExactly("define_from_dep", "define_from_impl");
   }