[ClangImporter] Rewrite glibc paths in VFS overlay
diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp
index 241f9ce..f8efc39 100644
--- a/lib/ClangImporter/ClangImporter.cpp
+++ b/lib/ClangImporter/ClangImporter.cpp
@@ -43,6 +43,7 @@
 #include "clang/Basic/Module.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/Version.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/Utils.h"
@@ -63,6 +64,7 @@
 #include "llvm/Support/Path.h"
 #include <algorithm>
 #include <memory>
+#include <regex>
 
 using namespace swift;
 using namespace importer;
@@ -405,6 +407,84 @@
 
 #define SHIMS_INCLUDE_FLAG "-isystem"
 
+
+void generateGlibcMap(StringRef glibcModuleMapPathIn,
+                      StringRef sysroot,
+                      const llvm::Triple &triple,
+                      clang::vfs::InMemoryFileSystem *imf) {
+
+  auto glibcBuffer = llvm::MemoryBuffer::getFile(glibcModuleMapPathIn);
+  if (!glibcBuffer){
+    llvm::errs() << "unable to read glibc.modulemap '"
+      << glibcModuleMapPathIn << "'";
+    return;
+  }
+
+  SmallString<128> includePath = sysroot;
+  if (triple.isOSHaiku()) {
+    llvm::sys::path::append(includePath,
+                            "system", "develop", "headers", "posix");
+  } else if (triple.isOSFuchsia()){
+    llvm::sys::path::append(includePath, "include");
+  } else {
+    llvm::sys::path::append(includePath, "usr", "include");
+  }
+
+  // Attempt to detect if the target sysroot is storing headers in a multiarch
+  // fashion like Debian or Ubuntu
+  SmallString<128> archIncludePath = includePath;
+  if (triple.isOSLinux() && !triple.isAndroid()) {
+    // The multiarch folder *should* match the triple passed in.
+    SmallString<128> triplePath = includePath;
+    llvm::sys::path::append(triplePath, triple.getTriple());
+    if (llvm::sys::fs::exists(triplePath)){
+      llvm::sys::path::append(archIncludePath, triple.getTriple());
+    } else {
+      // Attempt to match to folder manually. Scan the lib folder looking for
+      // a directory that parses as triple, parses as Linux and and has a
+      // matching arch as our target.
+      std::error_code EC;
+      llvm::sys::fs::directory_iterator DI(includePath, EC);
+      llvm::sys::fs::directory_iterator DE;
+      for (; !EC && DI != DE; DI = DI.increment(EC)) {
+        StringRef path = DI->path();
+        if (llvm::sys::fs::is_directory(path)) {
+          StringRef filename = llvm::sys::path::filename(path);
+          if (filename.count('-') > 2) {
+            // This folder smells like a triple. Parse it as one.
+            llvm::Triple folderTriple(filename);
+            if (folderTriple.isOSLinux() &&
+                folderTriple.getArch() == triple.getArch()) {
+              // This folder matches both as Linux and matches our arch so it's
+              // a likely canidate.
+              llvm::sys::path::append(archIncludePath, filename);
+              break;
+            }
+          }
+        }
+      }
+      if (EC) {
+        llvm::errs() << "Failed to enumerate include path '" << includePath <<
+          "' - " << EC.message();
+      }
+    }
+  }
+
+  std::string contents = glibcBuffer.get()->getBuffer();
+  contents = std::regex_replace(contents,
+                                std::regex("\\<system-include\\>"),
+                                std::string(includePath.str()));
+  contents = std::regex_replace(contents,
+                                std::regex("\\<system-arch-include\\>"),
+                                std::string(archIncludePath.str()));
+
+  // Replace the original glibc.modulemap with a modified one in the VFS overlay
+  // using the same path.
+  imf->addFile(glibcModuleMapPathIn, 0,
+               llvm::MemoryBuffer::getMemBuffer(StringRef(contents)));
+}
+
+
 static StringRef
 getMinVersionOptNameForDarwinTriple(const llvm::Triple &triple) {
   switch(getDarwinPlatformKind(triple)) {
@@ -429,7 +509,8 @@
 static void
 getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
                              ASTContext &ctx,
-                             const ClangImporterOptions &importerOpts) {
+                             const ClangImporterOptions &importerOpts,
+                             clang::vfs::InMemoryFileSystem *imf) {
   const auto &LangOpts = ctx.LangOpts;
   const llvm::Triple &triple = LangOpts.Target;
   SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
@@ -533,6 +614,18 @@
       });
     }
   } else {
+    // Ideally we should turn this on for all Glibc targets that are actually
+    // using Glibc or a libc that respects that flag. This will cause some
+    // source breakage however (specifically with strerror_r()) on Linux
+    // without a workaround.
+    if (triple.isOSFuchsia()) {
+      // Many of the modern libc features are hidden behind feature macros like
+      // _GNU_SOURCE or _XOPEN_SOURCE.
+      invocationArgStrs.insert(invocationArgStrs.end(), {
+        "-D_GNU_SOURCE",
+      });
+    }
+
     // The module map used for Glibc depends on the target we're compiling for,
     // and is not included in the resource directory with the other implicit
     // module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
@@ -553,6 +646,14 @@
     // `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
     // a Swift compiler not built for Linux targets.
     if (llvm::sys::fs::exists(GlibcModuleMapPath)) {
+      // Inject VFS replacement for the glibc.modulemap where the paths are
+      // adjusted to match the current target SDK root.
+      StringRef sysroot = searchPathOpts.SDKPath;
+      if (sysroot.empty()){
+        sysroot = "/";
+      }
+      generateGlibcMap(GlibcModuleMapPath, sysroot, triple, imf);
+
       invocationArgStrs.push_back(
         (Twine("-fmodule-map-file=") + GlibcModuleMapPath).str());
     } else {
@@ -844,9 +945,17 @@
   // "clang" for argv[0]
   invocationArgStrs.push_back("clang");
 
+  // Create an in-memory VFS overlay so that a rewritten Glibc.modulemap with
+  // updated sysroot paths can be subbed in based on the target's paths.
+  llvm::IntrusiveRefCntPtr<clang::vfs::OverlayFileSystem>
+    ovs(new clang::vfs::OverlayFileSystem(clang::vfs::getRealFileSystem()));
+  llvm::IntrusiveRefCntPtr<clang::vfs::InMemoryFileSystem>
+    imf(new clang::vfs::InMemoryFileSystem);
+  ovs->pushOverlay(imf);
+
   switch (importerOpts.Mode) {
   case ClangImporterOptions::Modes::Normal:
-    getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts);
+    getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts, imf.get());
     break;
   case ClangImporterOptions::Modes::EmbedBitcode:
     getEmbedBitcodeInvocationArguments(invocationArgStrs, ctx, importerOpts);
@@ -896,7 +1005,7 @@
 
   // Create a new Clang compiler invocation.
   importer->Impl.Invocation =
-      clang::createInvocationFromCommandLine(invocationArgs, clangDiags);
+      clang::createInvocationFromCommandLine(invocationArgs, clangDiags, ovs);
   if (!importer->Impl.Invocation)
     return nullptr;
 
diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt
index 460a362..a412f71 100644
--- a/stdlib/public/Platform/CMakeLists.txt
+++ b/stdlib/public/Platform/CMakeLists.txt
@@ -47,32 +47,6 @@
     set(arch_subdir "${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${arch}")
     set(module_dir "${SWIFTLIB_DIR}/${arch_subdir}")
 
-    if("${sdk}" STREQUAL "HAIKU")
-      # Haiku doesn't have /usr/include, instead the headers are located
-      # at /system/develop/headers.
-      set(GLIBC_INCLUDE_PATH "/system/develop/headers/posix")
-      set(GLIBC_ARCH_INCLUDE_PATH "/system/develop/headers/posix")
-      set(GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH "/system/develop/headers/")
-      set(GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH "/system/develop/headers/")
-    else()
-      # Determine the location of glibc headers based on the target.
-      set(GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH "/usr/include")
-      set(GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH}")
-
-      # Some SDKs place their headers in architecture-specific subfolders.
-      if(("${sdk}" STREQUAL "LINUX" OR "${sdk}" STREQUAL "FREEBSD") AND CMAKE_LIBRARY_ARCHITECTURE)
-        set(GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH}/${CMAKE_LIBRARY_ARCHITECTURE}")
-      endif()
-
-      set(GLIBC_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH}")
-      set(GLIBC_ARCH_INCLUDE_PATH "${GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH}")
-
-      if(NOT "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}" STREQUAL "/")
-        set(GLIBC_INCLUDE_PATH "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}${GLIBC_INCLUDE_PATH}")
-        set(GLIBC_ARCH_INCLUDE_PATH "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}${GLIBC_ARCH_INCLUDE_PATH}")
-      endif()
-    endif()
-
     set(glibc_modulemap_source "glibc.modulemap.gyb")
     set(glibc_modulemap_out "${module_dir}/glibc.modulemap")
 
@@ -85,37 +59,10 @@
         OUTPUT "${glibc_modulemap_out}"
         FLAGS
             "-DCMAKE_SDK=${sdk}"
-            "-DGLIBC_INCLUDE_PATH=${GLIBC_INCLUDE_PATH}"
-            "-DGLIBC_ARCH_INCLUDE_PATH=${GLIBC_ARCH_INCLUDE_PATH}")
+            "-DGLIBC_INCLUDE_PATH=\"<system-include>\""
+            "-DGLIBC_ARCH_INCLUDE_PATH=\"<system-arch-include>\"")
 
     list(APPEND glibc_modulemap_target_list ${glibc_modulemap_target})
-
-    # If this SDK is a target for a non-native host, create a native modulemap
-    # without a sysroot prefix. This is the one we'll install instead.
-    if(NOT "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_ARCH_${arch}_PATH}" STREQUAL "/")
-
-      set(glibc_sysroot_relative_modulemap_out "${module_dir}/sysroot-relative-modulemaps/glibc.modulemap")
-      handle_gyb_source_single(glibc_modulemap_native_target
-          SOURCE "${glibc_modulemap_source}"
-          OUTPUT "${glibc_sysroot_relative_modulemap_out}"
-          FLAGS
-              "-DCMAKE_SDK=${sdk}"
-              "-DGLIBC_INCLUDE_PATH=${GLIBC_SYSROOT_RELATIVE_INCLUDE_PATH}"
-              "-DGLIBC_ARCH_INCLUDE_PATH=${GLIBC_SYSROOT_RELATIVE_ARCH_INCLUDE_PATH}")
-
-      list(APPEND glibc_modulemap_target_list ${glibc_modulemap_native_target})
-      set(glibc_modulemap_out ${glibc_sysroot_relative_modulemap_out})
-    endif()
-
-    # FIXME: When SDK is a cross-compile target (SDK != Host), the generated
-    #        modulemap will be relative to the Host, with hardcoded paths.
-    #        It is not relocatable to the target platform itself.
-    #        This affects any cross-compiled targets that use glibc.modulemap
-
-    swift_install_in_component(sdk-overlay
-        FILES "${glibc_modulemap_out}"
-        DESTINATION "lib/swift/${arch_subdir}")
-
   endforeach()
 endforeach()
 add_custom_target(glibc_modulemap DEPENDS ${glibc_modulemap_target_list})