VirtualFileSystem: Add YAMLVFSWriter to generate VFS mapping files

This moves the logic to write a JSON VFS mapping from the C api into
VirtualFileSystem, so that we can use it internally.

No functional change.

llvm-svn: 209241
diff --git a/clang/tools/libclang/BuildSystem.cpp b/clang/tools/libclang/BuildSystem.cpp
index e5caa54..e9423c3 100644
--- a/clang/tools/libclang/BuildSystem.cpp
+++ b/clang/tools/libclang/BuildSystem.cpp
@@ -13,13 +13,12 @@
 
 #include "clang-c/BuildSystem.h"
 #include "CXString.h"
-#include "llvm/ADT/ArrayRef.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/Support/CBindingWrapping.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/TimeValue.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/YAMLParser.h"
 
 using namespace clang;
 using namespace llvm::sys;
@@ -28,13 +27,11 @@
   return llvm::sys::TimeValue::now().toEpochTime();
 }
 
-struct CXVirtualFileOverlayImpl {
-  std::vector<std::pair<std::string, std::string> > Mappings;
-  Optional<bool> IsCaseSensitive;
-};
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(clang::vfs::YAMLVFSWriter,
+                                   CXVirtualFileOverlay)
 
 CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
-  return new CXVirtualFileOverlayImpl();
+  return wrap(new clang::vfs::YAMLVFSWriter());
 }
 
 enum CXErrorCode
@@ -56,7 +53,7 @@
       return CXError_InvalidArguments;
   }
 
-  VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
+  unwrap(VFO)->addFileMapping(virtualPath, realPath);
   return CXError_Success;
 }
 
@@ -65,124 +62,10 @@
                                             int caseSensitive) {
   if (!VFO)
     return CXError_InvalidArguments;
-
-  VFO->IsCaseSensitive = caseSensitive;
+  unwrap(VFO)->setCaseSensitivity(caseSensitive);
   return CXError_Success;
 }
 
-namespace {
-struct EntryTy {
-  std::string VPath;
-  std::string RPath;
-
-  friend bool operator < (const EntryTy &LHS, const EntryTy &RHS) {
-    return LHS.VPath < RHS.VPath;
-  }
-};
-
-class JSONVFSPrinter {
-  llvm::raw_ostream &OS;
-  CXVirtualFileOverlay VFO;
-
-public:
-  JSONVFSPrinter(llvm::raw_ostream &OS, CXVirtualFileOverlay VFO)
-    : OS(OS), VFO(VFO) {}
-
-  /// Entries must be sorted.
-  void print(ArrayRef<EntryTy> Entries) {
-    OS << "{\n"
-          "  'version': 0,\n";
-    if (VFO->IsCaseSensitive.hasValue()) {
-      OS << "  'case-sensitive': '";
-      if (VFO->IsCaseSensitive.getValue())
-        OS << "true";
-      else
-        OS << "false";
-      OS << "',\n";
-    }
-    OS << "  'roots': [\n";
-    printDirNodes(Entries, "", 4);
-    OS << "  ]\n"
-          "}\n";
-  }
-
-private:
-  ArrayRef<EntryTy> printDirNodes(ArrayRef<EntryTy> Entries,
-                                  StringRef ParentPath,
-                                  unsigned Indent) {
-    while (!Entries.empty()) {
-      const EntryTy &Entry = Entries.front();
-      OS.indent(Indent) << "{\n";
-      Indent += 2;
-      OS.indent(Indent) << "'type': 'directory',\n";
-      StringRef DirName = containedPart(ParentPath,
-                                        path::parent_path(Entry.VPath));
-      OS.indent(Indent)
-          << "'name': \"" << llvm::yaml::escape(DirName) << "\",\n";
-      OS.indent(Indent) << "'contents': [\n";
-      Entries = printContents(Entries, Indent + 2);
-      OS.indent(Indent) << "]\n";
-      Indent -= 2;
-      OS.indent(Indent) << '}';
-      if (Entries.empty()) {
-        OS << '\n';
-        break;
-      }
-      StringRef NextVPath = Entries.front().VPath;
-      if (!containedIn(ParentPath, NextVPath)) {
-        OS << '\n';
-        break;
-      }
-      OS << ",\n";
-    }
-    return Entries;
-  }
-
-  ArrayRef<EntryTy> printContents(ArrayRef<EntryTy> Entries,
-                                  unsigned Indent) {
-    while (!Entries.empty()) {
-      const EntryTy &Entry = Entries.front();
-      Entries = Entries.slice(1);
-      StringRef ParentPath = path::parent_path(Entry.VPath);
-      StringRef VName = path::filename(Entry.VPath);
-      OS.indent(Indent) << "{\n";
-      Indent += 2;
-      OS.indent(Indent) << "'type': 'file',\n";
-      OS.indent(Indent) << "'name': \"" << llvm::yaml::escape(VName) << "\",\n";
-      OS.indent(Indent) << "'external-contents': \""
-                        << llvm::yaml::escape(Entry.RPath) << "\"\n";
-      Indent -= 2;
-      OS.indent(Indent) << '}';
-      if (Entries.empty()) {
-        OS << '\n';
-        break;
-      }
-      StringRef NextVPath = Entries.front().VPath;
-      if (!containedIn(ParentPath, NextVPath)) {
-        OS << '\n';
-        break;
-      }
-      OS << ",\n";
-      if (path::parent_path(NextVPath) != ParentPath) {
-        Entries = printDirNodes(Entries, ParentPath, Indent);
-      }
-    }
-    return Entries;
-  }
-
-  bool containedIn(StringRef Parent, StringRef Path) {
-    return Path.startswith(Parent);
-  }
-
-  StringRef containedPart(StringRef Parent, StringRef Path) {
-    assert(containedIn(Parent, Path));
-    if (Parent.empty())
-      return Path;
-    return Path.slice(Parent.size()+1, StringRef::npos);
-  }
-};
-}
-
 enum CXErrorCode
 clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned,
                                        char **out_buffer_ptr,
@@ -190,24 +73,9 @@
   if (!VFO || !out_buffer_ptr || !out_buffer_size)
     return CXError_InvalidArguments;
 
-  llvm::SmallVector<EntryTy, 16> Entries;
-  for (unsigned i = 0, e = VFO->Mappings.size(); i != e; ++i) {
-    EntryTy Entry;
-    Entry.VPath = VFO->Mappings[i].first;
-    Entry.RPath = VFO->Mappings[i].second;
-    Entries.push_back(Entry);
-  }
-
-  // FIXME: We should add options to determine if the paths are case sensitive
-  // or not. The following assumes that if paths are case-insensitive the caller
-  // did not mix cases in the virtual paths it provided.
-
-  std::sort(Entries.begin(), Entries.end());
-
   llvm::SmallString<256> Buf;
   llvm::raw_svector_ostream OS(Buf);
-  JSONVFSPrinter Printer(OS, VFO);
-  Printer.print(Entries);
+  unwrap(VFO)->write(OS);
 
   StringRef Data = OS.str();
   *out_buffer_ptr = (char*)malloc(Data.size());
@@ -217,7 +85,7 @@
 }
 
 void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) {
-  delete VFO;
+  delete unwrap(VFO);
 }