Add objc_include_scanning flag

Currently this flag will do nothing -- actual functionality will be
submitted in a separate CL.  I also made some changes in preparation
for the ability to support three modes of include processing: include
scanning, headering thinning, and no scanning.

RELNOTES: None
PiperOrigin-RevId: 268800639
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 e2bdfe5..a428530 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
@@ -232,6 +232,13 @@
           "c-compile",
           "c++-compile");
 
+  /** The kind of include processing to use. */
+  enum IncludeProcessingType {
+    HEADER_THINNING,
+    INCLUDE_SCANNING,
+    NO_PROCESSING;
+  }
+
   /** Returns the location of the xcrunwrapper tool. */
   public static final FilesToRunProvider xcrunwrapper(RuleContext ruleContext) {
     return ruleContext.getExecutablePrerequisite("$xcrunwrapper", Mode.HOST);
@@ -259,21 +266,24 @@
 
   private IncludeProcessing createIncludeProcessing(
       Iterable<Artifact> privateHdrs, ObjcProvider objcProvider, @Nullable Artifact pchHdr) {
-    if (isHeaderThinningEnabled()) {
-      Iterable<Artifact> potentialInputs = Iterables.concat(privateHdrs, objcProvider.get(HEADER));
-      if (!starlarkSemantics.incompatibleObjcFrameworkCleanup()) {
-        potentialInputs =
-            Iterables.concat(
-                potentialInputs,
-                objcProvider.get(STATIC_FRAMEWORK_FILE),
-                objcProvider.get(DYNAMIC_FRAMEWORK_FILE));
-      }
-      if (pchHdr != null) {
-        potentialInputs = Iterables.concat(potentialInputs, ImmutableList.of(pchHdr));
-      }
-      return new HeaderThinning(potentialInputs);
-    } else {
-      return NoProcessing.INSTANCE;
+    switch (includeProcessingType) {
+      case HEADER_THINNING:
+        Iterable<Artifact> potentialInputs =
+            Iterables.concat(privateHdrs, objcProvider.get(HEADER));
+        if (!starlarkSemantics.incompatibleObjcFrameworkCleanup()) {
+          potentialInputs =
+              Iterables.concat(
+                  potentialInputs,
+                  objcProvider.get(STATIC_FRAMEWORK_FILE),
+                  objcProvider.get(DYNAMIC_FRAMEWORK_FILE));
+        }
+        if (pchHdr != null) {
+          potentialInputs = Iterables.concat(potentialInputs, ImmutableList.of(pchHdr));
+        }
+        return new HeaderThinning(potentialInputs);
+      case INCLUDE_SCANNING:
+      default:
+        return NoProcessing.INSTANCE;
     }
   }
 
@@ -508,9 +518,9 @@
       ObjcProvider objcProvider, Collection<Artifact> privateHdrs, Artifact pchHdr) {
     return new ObjcCppSemantics(
         objcProvider,
+        includeProcessingType,
         createIncludeProcessing(privateHdrs, objcProvider, pchHdr),
         ruleContext.getFragment(ObjcConfiguration.class),
-        isHeaderThinningEnabled(),
         intermediateArtifacts,
         buildConfiguration,
         starlarkSemantics);
@@ -748,6 +758,7 @@
   private final CcToolchainProvider toolchain;
   private final boolean isTestRule;
   private final boolean usePch;
+  private final IncludeProcessingType includeProcessingType;
 
   /**
    * Creates a new compilation support for the given rule and build configuration.
@@ -792,6 +803,14 @@
     }
 
     this.toolchain = toolchain;
+
+    if (objcConfiguration.shouldScanIncludes()) {
+      includeProcessingType = IncludeProcessingType.INCLUDE_SCANNING;
+    } else if (isHeaderThinningEnabled()) {
+      includeProcessingType = IncludeProcessingType.HEADER_THINNING;
+    } else {
+      includeProcessingType = IncludeProcessingType.NO_PROCESSING;
+    }
   }
 
   /** Builder for {@link CompilationSupport} */
@@ -1810,7 +1829,8 @@
       CompilationArtifacts compilationArtifacts)
       throws RuleErrorException {
     // PIC is not used for Obj-C builds, if that changes this method will need to change
-    if (!isHeaderThinningEnabled() || ccCompilationOutputs.getObjectFiles(false).isEmpty()) {
+    if (includeProcessingType != IncludeProcessingType.HEADER_THINNING
+        || ccCompilationOutputs.getObjectFiles(false).isEmpty()) {
       return;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java
index 3321b72..f6e15e7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java
@@ -266,15 +266,26 @@
   public int objcHeaderThinningPartitionSize;
 
   @Option(
-    name = "objc_header_scanner_tool",
-    defaultValue = "@bazel_tools//tools/objc:header_scanner",
-    converter = LabelConverter.class,
-    documentationCategory = OptionDocumentationCategory.TOOLCHAIN,
-    effectTags = {OptionEffectTag.CHANGES_INPUTS},
-    help =
-        "Location of tool to scan Objective-C code for inclusions and output a .headers_list "
-            + "file."
-  )
+      name = "experimental_objc_include_scanning",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.BUILD_TIME_OPTIMIZATION,
+      effectTags = {
+        OptionEffectTag.LOADING_AND_ANALYSIS,
+        OptionEffectTag.EXECUTION,
+        OptionEffectTag.CHANGES_INPUTS
+      },
+      help = "Whether to perform include scanning for objective C/C++.")
+  public boolean scanIncludes;
+
+  @Option(
+      name = "objc_header_scanner_tool",
+      defaultValue = "@bazel_tools//tools/objc:header_scanner",
+      converter = LabelConverter.class,
+      documentationCategory = OptionDocumentationCategory.TOOLCHAIN,
+      effectTags = {OptionEffectTag.CHANGES_INPUTS},
+      help =
+          "Location of tool to scan Objective-C code for inclusions and output a .headers_list "
+              + "file.")
   public Label objcHeaderScannerTool;
 
   @Option(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java
index fd18583..9ba7762 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java
@@ -68,6 +68,7 @@
   private final HeaderDiscovery.DotdPruningMode dotdPruningPlan;
   private final boolean experimentalHeaderThinning;
   private final int objcHeaderThinningPartitionSize;
+  private final boolean shouldScanIncludes;
   private final Label objcHeaderScannerTool;
   private final Label appleSdk;
   private final boolean strictObjcModuleMaps;
@@ -100,6 +101,7 @@
             : HeaderDiscovery.DotdPruningMode.DO_NOT_USE;
     this.experimentalHeaderThinning = objcOptions.experimentalObjcHeaderThinning;
     this.objcHeaderThinningPartitionSize = objcOptions.objcHeaderThinningPartitionSize;
+    this.shouldScanIncludes = objcOptions.scanIncludes;
     this.objcHeaderScannerTool = objcOptions.objcHeaderScannerTool;
     this.appleSdk = objcOptions.appleSdk;
     this.strictObjcModuleMaps = objcOptions.strictObjcModuleMaps;
@@ -270,6 +272,11 @@
     return experimentalHeaderThinning;
   }
 
+  /** Returns true iff we should do "include scanning" during this build. */
+  public boolean shouldScanIncludes() {
+    return shouldScanIncludes;
+  }
+
   /** Returns the max number of source files to add to each header scanning action. */
   public int objcHeaderThinningPartitionSize() {
     return objcHeaderThinningPartitionSize;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationLoader.java
index babc7a3..8252241 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationLoader.java
@@ -42,6 +42,11 @@
           "Experimental Objective-C header thinning (--experimental_objc_header_thinning) requires "
               + "Objective-C dotd pruning (--objc_use_dotd_pruning).");
     }
+    if (objcOptions.experimentalObjcHeaderThinning && objcOptions.scanIncludes) {
+      throw new InvalidConfigurationException(
+          "Only one of header thinning (--experimental_objc_header_thinning) and include scanning "
+              + "(--objc_include_scanning) can be enabled.");
+    }
   }
 
   @Override
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 060a51a..75306af 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
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.rules.objc;
 
+import static com.google.devtools.build.lib.rules.objc.CompilationSupport.IncludeProcessingType.HEADER_THINNING;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.DYNAMIC_FRAMEWORK_FILE;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER;
 import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STATIC_FRAMEWORK_FILE;
@@ -31,6 +32,7 @@
 import com.google.devtools.build.lib.rules.cpp.CppSemantics;
 import com.google.devtools.build.lib.rules.cpp.HeaderDiscovery.DotdPruningMode;
 import com.google.devtools.build.lib.rules.cpp.IncludeProcessing;
+import com.google.devtools.build.lib.rules.objc.CompilationSupport.IncludeProcessingType;
 import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.util.FileTypeSet;
 
@@ -39,10 +41,10 @@
  */
 public class ObjcCppSemantics implements CppSemantics {
 
+  private final IncludeProcessingType includeProcessingType;
   private final IncludeProcessing includeProcessing;
   private final ObjcProvider objcProvider;
   private final ObjcConfiguration config;
-  private final boolean isHeaderThinningEnabled;
   private final IntermediateArtifacts intermediateArtifacts;
   private final BuildConfiguration buildConfiguration;
   private final StarlarkSemantics starlarkSemantics;
@@ -63,26 +65,25 @@
    *
    * @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 config the ObjcConfiguration for this build
-   * @param isHeaderThinningEnabled true if headers_list artifacts should be generated and added as
-   *     input to compiling actions
    * @param intermediateArtifacts used to create headers_list artifacts
    * @param buildConfiguration the build configuration for this build
    */
   public ObjcCppSemantics(
       ObjcProvider objcProvider,
+      IncludeProcessingType includeProcessingType,
       IncludeProcessing includeProcessing,
       ObjcConfiguration config,
-      boolean isHeaderThinningEnabled,
       IntermediateArtifacts intermediateArtifacts,
       BuildConfiguration buildConfiguration,
       StarlarkSemantics starlarkSemantics) {
     this.objcProvider = objcProvider;
+    this.includeProcessingType = includeProcessingType;
     this.includeProcessing = includeProcessing;
     this.config = config;
-    this.isHeaderThinningEnabled = isHeaderThinningEnabled;
     this.intermediateArtifacts = intermediateArtifacts;
     this.buildConfiguration = buildConfiguration;
     this.starlarkSemantics = starlarkSemantics;
@@ -105,7 +106,7 @@
           .addTransitiveMandatoryInputs(objcProvider.get(DYNAMIC_FRAMEWORK_FILE));
     }
 
-    if (isHeaderThinningEnabled) {
+    if (includeProcessingType == HEADER_THINNING) {
       Artifact sourceFile = actionBuilder.getSourceFile();
       if (!sourceFile.isTreeArtifact()
           && SOURCES_FOR_HEADER_THINNING.matches(sourceFile.getFilename())) {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationTest.java
new file mode 100644
index 0000000..5dd65ae
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcConfigurationTest.java
@@ -0,0 +1,43 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.objc;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
+
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test case for Objective-C configuration. */
+@RunWith(JUnit4.class)
+public class ObjcConfigurationTest extends BuildViewTestCase {
+  @Test
+  public void disallowBothHeaderThinningAndIncludeScanning() {
+    InvalidConfigurationException e =
+        assertThrows(
+            InvalidConfigurationException.class,
+            () ->
+                useConfiguration(
+                    "--experimental_objc_header_thinning", "--experimental_objc_include_scanning"));
+    assertThat(e)
+        .hasMessageThat()
+        .isEqualTo(
+            "Only one of header thinning (--experimental_objc_header_thinning) and include "
+                + "scanning (--objc_include_scanning) can be enabled.");
+  }
+}