Add support for CPATH and friends.

This moves the existing code for CPATH into the driver and adds the environment lookup and path splitting there.
The paths are then passed down to cc1 with -I options (CPATH), added after the normal user-specified include dirs.
Language specific paths are passed via -LANG-isystem and the actual filtering is performed in the frontend.

I tried to match GCC's behavior as close as possible

Fixes PR8971.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@140341 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index ba9469a..ad61f6a 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -623,8 +623,16 @@
   HelpText<"Make the next included directory (-I or -F) an indexer header map">;
 def iquote : JoinedOrSeparate<"-iquote">, MetaVarName<"<directory>">,
   HelpText<"Add directory to QUOTE include search path">;
+def c_isystem : JoinedOrSeparate<"-c-isystem">, MetaVarName<"<directory>">,
+  HelpText<"Add directory to the C SYSTEM include search path">;
 def cxx_isystem : JoinedOrSeparate<"-cxx-isystem">, MetaVarName<"<directory>">,
   HelpText<"Add directory to the C++ SYSTEM include search path">;
+def objc_isystem : JoinedOrSeparate<"-objc-isystem">,
+  MetaVarName<"<directory>">,
+  HelpText<"Add directory to the ObjC SYSTEM include search path">;
+def objcxx_isystem : JoinedOrSeparate<"-objcxx-isystem">,
+  MetaVarName<"<directory>">,
+  HelpText<"Add directory to the ObjC++ SYSTEM include search path">;
 def isystem : JoinedOrSeparate<"-isystem">, MetaVarName<"<directory>">,
   HelpText<"Add directory to SYSTEM include search path">;
 def iwithsysroot : JoinedOrSeparate<"-iwithsysroot">,MetaVarName<"<directory>">,
diff --git a/include/clang/Frontend/HeaderSearchOptions.h b/include/clang/Frontend/HeaderSearchOptions.h
index e0f80e3..39d3136 100644
--- a/include/clang/Frontend/HeaderSearchOptions.h
+++ b/include/clang/Frontend/HeaderSearchOptions.h
@@ -26,7 +26,10 @@
     IndexHeaderMap, ///< Like Angled, but marks header maps used when
                        ///  building frameworks.
     System,         ///< Like Angled, but marks system directories.
+    CSystem,        ///< Like System, but only used for C.
     CXXSystem,      ///< Like System, but only used for C++.
+    ObjCSystem,     ///< Like System, but only used for ObjC.
+    ObjCXXSystem,   ///< Like System, but only used for ObjC++.
     After           ///< Like System, but searched after the system directories.
   };
 }
@@ -59,18 +62,6 @@
   /// User specified include entries.
   std::vector<Entry> UserEntries;
 
-  /// A (system-path) delimited list of include paths to be added from the
-  /// environment following the user specified includes (but prior to builtin
-  /// and standard includes). This is parsed in the same manner as the CPATH
-  /// environment variable for gcc.
-  std::string EnvIncPath;
-
-  /// Per-language environmental include paths, see \see EnvIncPath.
-  std::string CEnvIncPath;
-  std::string ObjCEnvIncPath;
-  std::string CXXEnvIncPath;
-  std::string ObjCXXEnvIncPath;
-
   /// The directory which holds the compiler resource files (builtin includes,
   /// etc.).
   std::string ResourceDir;
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 840e4c2..c3c07f4 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -183,6 +183,38 @@
   CmdArgs.push_back(Args.MakeArgString(ProfileRT));
 }
 
+static void AddIncludeDirectoryList(const ArgList &Args,
+                                    ArgStringList &CmdArgs,
+                                    const char *ArgName,
+                                    const char *DirList) {
+  if (!DirList)
+    return; // Nothing to do.
+
+  StringRef Dirs(DirList);
+  if (Dirs.empty()) // Empty string should not add '.'.
+    return;
+
+  StringRef::size_type Delim;
+  while ((Delim = Dirs.find(llvm::sys::PathSeparator)) != StringRef::npos) {
+    if (Delim == 0) { // Leading colon.
+      CmdArgs.push_back(ArgName);
+      CmdArgs.push_back(".");
+    } else {
+      CmdArgs.push_back(ArgName);
+      CmdArgs.push_back(Args.MakeArgString(Dirs.substr(0, Delim)));
+    }
+    Dirs = Dirs.substr(Delim + 1);
+  }
+
+  if (Dirs.empty()) { // Trailing colon.
+    CmdArgs.push_back(ArgName);
+    CmdArgs.push_back(".");
+  } else { // Add the last path.
+    CmdArgs.push_back(ArgName);
+    CmdArgs.push_back(Args.MakeArgString(Dirs));
+  }
+}
+
 void Clang::AddPreprocessingOptions(const Driver &D,
                                     const ArgList &Args,
                                     ArgStringList &CmdArgs,
@@ -391,6 +423,23 @@
   }
   
   Args.AddAllArgs(CmdArgs, options::OPT_fauto_module_import);
+
+  // Parse additional include paths from environment variables.
+  // CPATH - included following the user specified includes (but prior to
+  // builtin and standard includes).
+  AddIncludeDirectoryList(Args, CmdArgs, "-I", ::getenv("CPATH"));
+  // C_INCLUDE_PATH - system includes enabled when compiling C.
+  AddIncludeDirectoryList(Args, CmdArgs, "-c-isystem",
+                          ::getenv("C_INCLUDE_PATH"));
+  // CPLUS_INCLUDE_PATH - system includes enabled when compiling C++.
+  AddIncludeDirectoryList(Args, CmdArgs, "-cxx-isystem",
+                          ::getenv("CPLUS_INCLUDE_PATH"));
+  // OBJC_INCLUDE_PATH - system includes enabled when compiling ObjC.
+  AddIncludeDirectoryList(Args, CmdArgs, "-objc-isystem",
+                          ::getenv("OBJC_INCLUDE_PATH"));
+  // OBJCPLUS_INCLUDE_PATH - system includes enabled when compiling ObjC++.
+  AddIncludeDirectoryList(Args, CmdArgs, "-objcxx-isystem",
+                          ::getenv("OBJCPLUS_INCLUDE_PATH"));
 }
 
 /// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting.
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 7cb707b..e699261 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -534,9 +534,21 @@
         Res.push_back(E.IsFramework? "-F" : "-I");
         break;
         
+      case frontend::CSystem:
+        Res.push_back("-c-isystem");
+        break;
+
       case frontend::CXXSystem:
         Res.push_back("-cxx-isystem");
         break;
+
+      case frontend::ObjCSystem:
+        Res.push_back("-objc-isystem");
+        break;
+
+      case frontend::ObjCXXSystem:
+        Res.push_back("-objcxx-isystem");
+        break;
         
       case frontend::Angled:
         Res.push_back(E.IsFramework ? "-F" : "-I");
@@ -551,26 +563,6 @@
     Res.push_back(E.Path);
   }
 
-  if (!Opts.EnvIncPath.empty()) {
-    // FIXME: Provide an option for this, and move env detection to driver.
-    llvm::report_fatal_error("Not yet implemented!");
-  }
-  if (!Opts.CEnvIncPath.empty()) {
-    // FIXME: Provide an option for this, and move env detection to driver.
-    llvm::report_fatal_error("Not yet implemented!");
-  }
-  if (!Opts.ObjCEnvIncPath.empty()) {
-    // FIXME: Provide an option for this, and move env detection to driver.
-    llvm::report_fatal_error("Not yet implemented!");
-  }
-  if (!Opts.CXXEnvIncPath.empty()) {
-    // FIXME: Provide an option for this, and move env detection to driver.
-    llvm::report_fatal_error("Not yet implemented!");
-  }
-  if (!Opts.ObjCXXEnvIncPath.empty()) {
-    // FIXME: Provide an option for this, and move env detection to driver.
-    llvm::report_fatal_error("Not yet implemented!");
-  }
   if (!Opts.ResourceDir.empty()) {
     Res.push_back("-resource-dir");
     Res.push_back(Opts.ResourceDir);
@@ -1424,14 +1416,25 @@
   for (arg_iterator it = Args.filtered_begin(OPT_iquote),
          ie = Args.filtered_end(); it != ie; ++it)
     Opts.AddPath((*it)->getValue(Args), frontend::Quoted, true, false, false);
-  for (arg_iterator it = Args.filtered_begin(OPT_cxx_isystem, OPT_isystem,
+  for (arg_iterator it = Args.filtered_begin(OPT_isystem,
          OPT_iwithsysroot), ie = Args.filtered_end(); it != ie; ++it)
-    Opts.AddPath((*it)->getValue(Args),
-                 ((*it)->getOption().matches(OPT_cxx_isystem) ?
-                   frontend::CXXSystem : frontend::System),
-                 true, false, !(*it)->getOption().matches(OPT_iwithsysroot));
-  
-  // FIXME: Need options for the various environment variables!
+    Opts.AddPath((*it)->getValue(Args), frontend::System, true, false,
+                 !(*it)->getOption().matches(OPT_iwithsysroot));
+
+  // Add the paths for the various language specific isystem flags.
+  for (arg_iterator it = Args.filtered_begin(OPT_c_isystem),
+       ie = Args.filtered_end(); it != ie; ++it)
+    Opts.AddPath((*it)->getValue(Args), frontend::CSystem, true, false, true);
+  for (arg_iterator it = Args.filtered_begin(OPT_cxx_isystem),
+       ie = Args.filtered_end(); it != ie; ++it)
+    Opts.AddPath((*it)->getValue(Args), frontend::CXXSystem, true, false, true);
+  for (arg_iterator it = Args.filtered_begin(OPT_objc_isystem),
+       ie = Args.filtered_end(); it != ie; ++it)
+    Opts.AddPath((*it)->getValue(Args), frontend::ObjCSystem, true, false,true);
+  for (arg_iterator it = Args.filtered_begin(OPT_objcxx_isystem),
+       ie = Args.filtered_end(); it != ie; ++it)
+    Opts.AddPath((*it)->getValue(Args), frontend::ObjCXXSystem, true, false,
+                 true);
 }
 
 void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp
index 08af532..089ded1 100644
--- a/lib/Frontend/InitHeaderSearch.cpp
+++ b/lib/Frontend/InitHeaderSearch.cpp
@@ -81,10 +81,6 @@
   void AddMinGW64CXXPaths(StringRef Base,
                           StringRef Version);
 
-  /// AddDelimitedPaths - Add a list of paths delimited by the system PATH
-  /// separator. The processing follows that of the CPATH variable for gcc.
-  void AddDelimitedPaths(StringRef String);
-
   // AddDefaultCIncludePaths - Add paths that should always be searched.
   void AddDefaultCIncludePaths(const llvm::Triple &triple,
                                const HeaderSearchOptions &HSOpts);
@@ -167,26 +163,6 @@
                  << MappedPathStr << "\"\n";
 }
 
-
-void InitHeaderSearch::AddDelimitedPaths(StringRef at) {
-  if (at.empty()) // Empty string should not add '.' path.
-    return;
-
-  StringRef::size_type delim;
-  while ((delim = at.find(llvm::sys::PathSeparator)) != StringRef::npos) {
-    if (delim == 0)
-      AddPath(".", Angled, false, true, false);
-    else
-      AddPath(at.substr(0, delim), Angled, false, true, false);
-    at = at.substr(delim + 1);
-  }
-
-  if (at.empty())
-    AddPath(".", Angled, false, true, false);
-  else
-    AddPath(at, Angled, false, true, false);
-}
-
 void InitHeaderSearch::AddGnuCPlusPlusIncludePaths(StringRef Base,
                                                    StringRef ArchDir,
                                                    StringRef Dir32,
@@ -1094,7 +1070,11 @@
 
   for (path_iterator it = IncludePath.begin(), ie = IncludePath.end();
        it != ie; ++it) {
-    if (it->first == System || (Lang.CPlusPlus && it->first == CXXSystem))
+    if (it->first == System ||
+        (!Lang.ObjC1 && !Lang.CPlusPlus && it->first == CSystem)    ||
+        (!Lang.ObjC1 && Lang.CPlusPlus  && it->first == CXXSystem)  ||
+        (Lang.ObjC1  && !Lang.CPlusPlus && it->first == ObjCSystem) ||
+        (Lang.ObjC1  && Lang.CPlusPlus  && it->first == ObjCXXSystem))
       SearchList.push_back(it->second);
   }
 
@@ -1147,17 +1127,6 @@
                  E.IgnoreSysRoot);
   }
 
-  // Add entries from CPATH and friends.
-  Init.AddDelimitedPaths(HSOpts.EnvIncPath);
-  if (Lang.CPlusPlus && Lang.ObjC1)
-    Init.AddDelimitedPaths(HSOpts.ObjCXXEnvIncPath);
-  else if (Lang.CPlusPlus)
-    Init.AddDelimitedPaths(HSOpts.CXXEnvIncPath);
-  else if (Lang.ObjC1)
-    Init.AddDelimitedPaths(HSOpts.ObjCEnvIncPath);
-  else
-    Init.AddDelimitedPaths(HSOpts.CEnvIncPath);
-
   if (HSOpts.UseStandardIncludes)
     Init.AddDefaultSystemIncludePaths(Lang, Triple, HSOpts);
 
diff --git a/test/Driver/cpath.c b/test/Driver/cpath.c
new file mode 100644
index 0000000..00ed879
--- /dev/null
+++ b/test/Driver/cpath.c
@@ -0,0 +1,20 @@
+// RUN: mkdir -p %T/test1 %T/test2
+
+// RUN: CPATH=%T/test1: %clang -x c -E -v %s 2>&1 | FileCheck %s -check-prefix=CPATH
+// CPATH: -I {{.*}}/test1 -I .
+// CPATH: search starts here
+// CPATH: test1
+
+// RUN: OBJC_INCLUDE_PATH=%T/test1  OBJCPLUS_INCLUDE_PATH=%T/test1  CPLUS_INCLUDE_PATH=%T/test1 C_INCLUDE_PATH=%T/test2 %clang -x c -E -v %s 2>&1 | FileCheck %s -check-prefix=C_INCLUDE_PATH
+// C_INCLUDE_PATH: -c-isystem {{.*}}/test2 -cxx-isystem {{.*}}/test1 -objc-isystem {{.*}}/test1 -objcxx-isystem {{.*}}/test1
+// C_INCLUDE_PATH: search starts here
+// C_INCLUDE_PATH-NOT: test1
+// C_INCLUDE_PATH: test2
+// C_INCLUDE_PATH-NOT: test1
+
+// RUN: OBJC_INCLUDE_PATH=%T/test1  OBJCPLUS_INCLUDE_PATH=%T/test2  CPLUS_INCLUDE_PATH=%T/test1 C_INCLUDE_PATH=%T/test1 %clang -x objective-c++ -E -v %s 2>&1 | FileCheck %s -check-prefix=OBJCPLUS_INCLUDE_PATH
+// OBJCPLUS_INCLUDE_PATH: -c-isystem {{.*}}/test1 -cxx-isystem {{.*}}/test1 -objc-isystem {{.*}}/test1 -objcxx-isystem {{.*}}/test2
+// OBJCPLUS_INCLUDE_PATH: search starts here
+// OBJCPLUS_INCLUDE_PATH-NOT: test1
+// OBJCPLUS_INCLUDE_PATH: test2
+// OBJCPLUS_INCLUDE_PATH-NOT: test1