When generating Xcode projects, remove -I options from copts and instead add the -I include paths as non-propagated header search paths. If the paths are relative, prepend $(WORKSPACE_ROOT) to them.

--
MOS_MIGRATED_REVID=102994196
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 6c2f99c..6e08eaf 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
@@ -45,6 +45,8 @@
 import com.google.common.base.Joiner;
 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;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -101,6 +103,14 @@
 
   private static final String FRAMEWORK_SUFFIX = ".framework";
 
+  private static final Predicate<String> INCLUDE_DIR_OPTION_IN_COPTS =
+      new Predicate<String>() {
+        @Override
+        public boolean apply(String copt) {
+          return copt.startsWith("-I") && copt.length() > 2;
+        }
+      };
+
   /**
    * Iterable wrapper providing strong type safety for arguments to binary linking.
    */
@@ -744,15 +754,30 @@
     for (CompilationArtifacts artifacts : common.getCompilationArtifacts().asSet()) {
       xcodeProviderBuilder.setCompilationArtifacts(artifacts);
     }
+
+    // The include directory options ("-I") are parsed out of copts. The include directories are
+    // added as non-propagated header search paths local to the associated Xcode target.
+    Iterable<String> copts = Iterables.concat(
+        objcConfiguration.getCopts(), attributes.copts(), attributes.optionsCopts());
+    Iterable<String> includeDirOptions = Iterables.filter(copts, INCLUDE_DIR_OPTION_IN_COPTS);
+    Iterable<String> coptsWithoutIncludeDirs = Iterables.filter(
+        copts, Predicates.not(INCLUDE_DIR_OPTION_IN_COPTS));
+    ImmutableList.Builder<PathFragment> nonPropagatedHeaderSearchPaths =
+        new ImmutableList.Builder<>();
+    for (String includeDirOption : includeDirOptions) {
+      nonPropagatedHeaderSearchPaths.add(new PathFragment(includeDirOption.substring(2)));
+    }
+
     xcodeProviderBuilder
         .addHeaders(attributes.hdrs())
         .addUserHeaderSearchPaths(ObjcCommon.userHeaderSearchPaths(ruleContext.getConfiguration()))
         .addHeaderSearchPaths("$(WORKSPACE_ROOT)", attributes.headerSearchPaths())
         .addHeaderSearchPaths("$(SDKROOT)/usr/include", attributes.sdkIncludes())
+        .addNonPropagatedHeaderSearchPaths(
+            "$(WORKSPACE_ROOT)", nonPropagatedHeaderSearchPaths.build())
         .addCompilationModeCopts(objcConfiguration.getCoptsForCompilationMode())
-        .addCopts(objcConfiguration.getCopts())
-        .addCopts(attributes.copts())
-        .addCopts(attributes.optionsCopts());
+        .addCopts(coptsWithoutIncludeDirs);
+
     return this;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
index d753115..04be142 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
@@ -224,6 +224,10 @@
           <a href="#sh-tokenization">Bourne shell tokenization</a>.
           These flags will only apply to this target, and not those upon which
           it depends, or those which depend on it.
+          <p>
+          Note that for the generated Xcode project, directory paths specified using "-I" flags in
+          copts are parsed out, prepended with "$(WORKSPACE_ROOT)/" if they are relative paths, and
+          added to the header search paths for the associated Xcode target.
           <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
           .add(attr("copts", STRING_LIST))
           .build();
@@ -568,6 +572,11 @@
           genfiles and bin roots (e.g. <code>blaze-genfiles/pkg/includedir</code>
           and <code>blaze-out/pkg/includedir</code>) are included in addition to the
           actual client root.
+          <p>
+          Unlike <a href="#objc_library.copts">COPTS</a>, these flags are added for this rule
+          and every rule that depends on it. (Note: not the rules it depends upon!) Be
+          very careful, since this may have far-reaching effects.  When in doubt, add
+          "-I" flags to <a href="#objc_library.copts">COPTS</a> instead.
           <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
           .add(attr("includes", Type.STRING_LIST))
           /* <!-- #BLAZE_RULE($objc_compile_dependency_rule).ATTRIBUTE(sdk_includes) -->
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
index 5fd9612..c763052 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
@@ -133,6 +133,15 @@
     }
 
     /**
+     * Adds non-propagated header search paths for this target. Each relative path is interpreted
+     * relative to the given root, such as {@code "$(WORKSPACE_ROOT)"}.
+     */
+    public Builder addNonPropagatedHeaderSearchPaths(String root, Iterable<PathFragment> paths) {
+      this.nonPropagatedHeaderSearchPaths.addAll(rootEach(root, paths));
+      return this;
+    }
+
+    /**
      * Sets the Info.plist for the bundle represented by this provider.
      */
     public Builder setBundleInfoplist(Artifact bundleInfoplist) {
@@ -656,7 +665,8 @@
 
   /**
    * Prepends the given path to each path in {@code paths}. Empty paths are
-   * transformed to the value of {@code variable} rather than {@code variable + "/."}
+   * transformed to the value of {@code variable} rather than {@code variable + "/."}. Absolute
+   * paths are returned without modifications.
    */
   @VisibleForTesting
   static Iterable<String> rootEach(final String prefix, Iterable<PathFragment> paths) {
@@ -669,6 +679,8 @@
       public String apply(PathFragment input) {
         if (input.getSafePathString().equals(".")) {
           return prefix;
+        } else if (input.isAbsolute()) {
+          return input.getSafePathString();
         } else {
           return prefix + "/" + input.getSafePathString();
         }