r202105 - [libclang] Introduce libclang APIs for creating a buffer with a JSON virtual file overlay description.

Argyrios Kyrtzidis akyrtzi at gmail.com
Mon Feb 24 19:59:24 PST 2014


Author: akirtzidis
Date: Mon Feb 24 21:59:23 2014
New Revision: 202105

URL: http://llvm.org/viewvc/llvm-project?rev=202105&view=rev
Log:
[libclang] Introduce libclang APIs for creating a buffer with a JSON virtual file overlay description.

The current API only supports adding 'virtual file path' -> 'real file path' mappings.

rdar://15986708

Added:
    cfe/trunk/include/clang-c/CXErrorCode.h
Modified:
    cfe/trunk/include/clang-c/BuildSystem.h
    cfe/trunk/include/clang-c/Index.h
    cfe/trunk/tools/libclang/BuildSystem.cpp
    cfe/trunk/tools/libclang/libclang.exports
    cfe/trunk/unittests/libclang/LibclangTest.cpp

Modified: cfe/trunk/include/clang-c/BuildSystem.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/BuildSystem.h?rev=202105&r1=202104&r2=202105&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/BuildSystem.h (original)
+++ cfe/trunk/include/clang-c/BuildSystem.h Mon Feb 24 21:59:23 2014
@@ -15,6 +15,7 @@
 #define CLANG_C_BUILD_SYSTEM_H
 
 #include "clang-c/Platform.h"
+#include "clang-c/CXErrorCode.h"
 #include "clang-c/CXString.h"
 
 #ifdef __cplusplus
@@ -33,6 +34,48 @@ extern "C" {
 CINDEX_LINKAGE unsigned long long clang_getBuildSessionTimestamp(void);
 
 /**
+ * \brief Object encapsulating information about overlaying virtual
+ * file/directories over the real file system.
+ */
+typedef struct CXVirtualFileOverlayImpl *CXVirtualFileOverlay;
+
+/**
+ * \brief Create a \c CXVirtualFileOverlay object.
+ * Must be disposed with \c clang_VirtualFileOverlay_dispose().
+ *
+ * \param options is reserved, always pass 0.
+ */
+CINDEX_LINKAGE CXVirtualFileOverlay
+clang_VirtualFileOverlay_create(unsigned options);
+
+/**
+ * \brief Map an absolute virtual file path to an absolute real one.
+ * The virtual path must be canonicalized (not contain "."/"..").
+ * \returns 0 for success, non-zero to indicate an error.
+ */
+CINDEX_LINKAGE enum CXErrorCode
+clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay,
+                                        const char *virtualPath,
+                                        const char *realPath);
+
+/**
+ * \brief Write out the \c CXVirtualFileOverlay object to a char buffer.
+ *
+ * \param options is reserved, always pass 0.
+ * \param out_buffer pointer to receive the CXString object, which should be
+ * disposed using \c clang_disposeString().
+ * \returns 0 for success, non-zero to indicate an error.
+ */
+CINDEX_LINKAGE enum CXErrorCode
+clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay, unsigned options,
+                                       CXString *out_buffer);
+
+/**
+ * \brief Dispose a \c CXVirtualFileOverlay object.
+ */
+CINDEX_LINKAGE void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay);
+
+/**
  * @}
  */
 

Added: cfe/trunk/include/clang-c/CXErrorCode.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/CXErrorCode.h?rev=202105&view=auto
==============================================================================
--- cfe/trunk/include/clang-c/CXErrorCode.h (added)
+++ cfe/trunk/include/clang-c/CXErrorCode.h Mon Feb 24 21:59:23 2014
@@ -0,0 +1,64 @@
+/*===-- clang-c/CXErrorCode.h - C Index Error Codes  --------------*- C -*-===*\
+|*                                                                            *|
+|*                     The LLVM Compiler Infrastructure                       *|
+|*                                                                            *|
+|* This file is distributed under the University of Illinois Open Source      *|
+|* License. See LICENSE.TXT for details.                                      *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides the CXErrorCode enumerators.                          *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_C_CXERRORCODE_H
+#define CLANG_C_CXERRORCODE_H
+
+#include "clang-c/Platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Error codes returned by libclang routines.
+ *
+ * Zero (\c CXError_Success) is the only error code indicating success.  Other
+ * error codes, including not yet assigned non-zero values, indicate errors.
+ */
+enum CXErrorCode {
+  /**
+   * \brief No error.
+   */
+  CXError_Success = 0,
+
+  /**
+   * \brief A generic error code, no further details are available.
+   *
+   * Errors of this kind can get their own specific error codes in future
+   * libclang versions.
+   */
+  CXError_Failure = 1,
+
+  /**
+   * \brief libclang crashed while performing the requested operation.
+   */
+  CXError_Crashed = 2,
+
+  /**
+   * \brief The function detected that the arguments violate the function
+   * contract.
+   */
+  CXError_InvalidArguments = 3,
+
+  /**
+   * \brief An AST deserialization error has occurred.
+   */
+  CXError_ASTReadError = 4
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+

Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=202105&r1=202104&r2=202105&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Mon Feb 24 21:59:23 2014
@@ -19,6 +19,7 @@
 #include <time.h>
 
 #include "clang-c/Platform.h"
+#include "clang-c/CXErrorCode.h"
 #include "clang-c/CXString.h"
 #include "clang-c/BuildSystem.h"
 
@@ -31,7 +32,7 @@
  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
  */
 #define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 23
+#define CINDEX_VERSION_MINOR 24
 
 #define CINDEX_VERSION_ENCODE(major, minor) ( \
       ((major) * 10000)                       \
@@ -74,43 +75,6 @@ extern "C" {
  */
 
 /**
- * \brief Error codes returned by libclang routines.
- *
- * Zero (\c CXError_Success) is the only error code indicating success.  Other
- * error codes, including not yet assigned non-zero values, indicate errors.
- */
-enum CXErrorCode {
-  /**
-   * \brief No error.
-   */
-  CXError_Success = 0,
-
-  /**
-   * \brief A generic error code, no further details are available.
-   *
-   * Errors of this kind can get their own specific error codes in future
-   * libclang versions.
-   */
-  CXError_Failure = 1,
-
-  /**
-   * \brief libclang crashed while performing the requested operation.
-   */
-  CXError_Crashed = 2,
-
-  /**
-   * \brief The function detected that the arguments violate the function
-   * contract.
-   */
-  CXError_InvalidArguments = 3,
-
-  /**
-   * \brief An AST deserialization error has occurred.
-   */
-  CXError_ASTReadError = 4
-};
-
-/**
  * \brief An "index" that consists of a set of translation units that would
  * typically be linked together into an executable or library.
  */

Modified: cfe/trunk/tools/libclang/BuildSystem.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/BuildSystem.cpp?rev=202105&r1=202104&r2=202105&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/BuildSystem.cpp (original)
+++ cfe/trunk/tools/libclang/BuildSystem.cpp Mon Feb 24 21:59:23 2014
@@ -12,11 +12,184 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-c/BuildSystem.h"
+#include "CXString.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/TimeValue.h"
 
-extern "C" {
+using namespace clang;
+using namespace llvm::sys;
+
 unsigned long long clang_getBuildSessionTimestamp(void) {
   return llvm::sys::TimeValue::now().toEpochTime();
 }
-} // extern "C"
 
+struct CXVirtualFileOverlayImpl {
+  std::vector<std::pair<std::string, std::string> > Mappings;
+};
+
+CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) {
+  return new CXVirtualFileOverlayImpl();
+}
+
+enum CXErrorCode
+clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO,
+                                        const char *virtualPath,
+                                        const char *realPath) {
+  if (!VFO || !virtualPath || !realPath)
+    return CXError_InvalidArguments;
+  if (!path::is_absolute(virtualPath))
+    return CXError_InvalidArguments;
+  if (!path::is_absolute(realPath))
+    return CXError_InvalidArguments;
+
+  for (path::const_iterator
+         PI = path::begin(virtualPath),
+         PE = path::end(virtualPath); PI != PE; ++PI) {
+    StringRef Comp = *PI;
+    if (Comp == "." || Comp == "..")
+      return CXError_InvalidArguments;
+  }
+
+  VFO->Mappings.push_back(std::make_pair(virtualPath, realPath));
+  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;
+
+public:
+  JSONVFSPrinter(llvm::raw_ostream &OS) : OS(OS) {}
+
+  /// Entries must be sorted.
+  void print(ArrayRef<EntryTy> Entries) {
+    OS << "{\n"
+          "  'version': 0,\n"
+          "  '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";
+      OS.indent(Indent) << "'name': \"";
+      StringRef DirName = containedPart(ParentPath,
+                                        path::parent_path(Entry.VPath));
+      OS.write_escaped(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': \"";
+      OS.write_escaped(VName) << "\",\n";
+      OS.indent(Indent) << "'external-contents': \"";
+      OS.write_escaped(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, CXString *out_buffer) {
+  if (!VFO || !out_buffer)
+    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);
+  Printer.print(Entries);
+
+  *out_buffer = cxstring::createDup(OS.str());
+  return CXError_Success;
+}
+
+void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) {
+  delete VFO;
+}

Modified: cfe/trunk/tools/libclang/libclang.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/libclang.exports?rev=202105&r1=202104&r2=202105&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/libclang.exports (original)
+++ cfe/trunk/tools/libclang/libclang.exports Mon Feb 24 21:59:23 2014
@@ -284,3 +284,7 @@ clang_CompileCommand_getNumArgs
 clang_CompileCommand_getArg
 clang_visitChildren
 clang_visitChildrenWithBlock
+clang_VirtualFileOverlay_addFileMapping
+clang_VirtualFileOverlay_create
+clang_VirtualFileOverlay_dispose
+clang_VirtualFileOverlay_writeToBuffer

Modified: cfe/trunk/unittests/libclang/LibclangTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/libclang/LibclangTest.cpp?rev=202105&r1=202104&r2=202105&view=diff
==============================================================================
--- cfe/trunk/unittests/libclang/LibclangTest.cpp (original)
+++ cfe/trunk/unittests/libclang/LibclangTest.cpp Mon Feb 24 21:59:23 2014
@@ -28,3 +28,114 @@ TEST(libclang, clang_createTranslationUn
             clang_createTranslationUnit2(0, 0, &TU));
   EXPECT_EQ(0, TU);
 }
+
+namespace {
+struct TestVFO {
+  const char *Contents;
+  CXVirtualFileOverlay VFO;
+
+  TestVFO(const char *Contents) : Contents(Contents) {
+    VFO = clang_VirtualFileOverlay_create(0);
+  }
+
+  void map(const char *VPath, const char *RPath) {
+    CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
+    EXPECT_EQ(Err, CXError_Success);
+  }
+
+  void mapError(const char *VPath, const char *RPath, CXErrorCode ExpErr) {
+    CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
+    EXPECT_EQ(Err, ExpErr);
+  }
+
+  ~TestVFO() {
+    if (!Contents)
+      return;
+    CXString Buf;
+    clang_VirtualFileOverlay_writeToBuffer(VFO, 0, &Buf);
+    EXPECT_STREQ(Contents, clang_getCString(Buf));
+    clang_disposeString(Buf);
+    clang_VirtualFileOverlay_dispose(VFO);
+  }
+};
+}
+
+TEST(libclang, VirtualFileOverlay) {
+  {
+    const char *contents =
+    "{\n"
+    "  'version': 0,\n"
+    "  'roots': [\n"
+    "    {\n"
+    "      'type': 'directory',\n"
+    "      'name': \"/path/virtual\",\n"
+    "      'contents': [\n"
+    "        {\n"
+    "          'type': 'file',\n"
+    "          'name': \"foo.h\",\n"
+    "          'external-contents': \"/real/foo.h\"\n"
+    "        }\n"
+    "      ]\n"
+    "    }\n"
+    "  ]\n"
+    "}\n";
+    TestVFO T(contents);
+    T.map("/path/virtual/foo.h", "/real/foo.h");
+  }
+  {
+    TestVFO T(NULL);
+    T.mapError("/path/./virtual/../foo.h", "/real/foo.h",
+               CXError_InvalidArguments);
+  }
+  {
+    const char *contents =
+    "{\n"
+    "  'version': 0,\n"
+    "  'roots': [\n"
+    "    {\n"
+    "      'type': 'directory',\n"
+    "      'name': \"/another/dir\",\n"
+    "      'contents': [\n"
+    "        {\n"
+    "          'type': 'file',\n"
+    "          'name': \"foo2.h\",\n"
+    "          'external-contents': \"/real/foo2.h\"\n"
+    "        }\n"
+    "      ]\n"
+    "    },\n"
+    "    {\n"
+    "      'type': 'directory',\n"
+    "      'name': \"/path/virtual/dir\",\n"
+    "      'contents': [\n"
+    "        {\n"
+    "          'type': 'file',\n"
+    "          'name': \"foo1.h\",\n"
+    "          'external-contents': \"/real/foo1.h\"\n"
+    "        },\n"
+    "        {\n"
+    "          'type': 'file',\n"
+    "          'name': \"foo3.h\",\n"
+    "          'external-contents': \"/real/foo3.h\"\n"
+    "        },\n"
+    "        {\n"
+    "          'type': 'directory',\n"
+    "          'name': \"in/subdir\",\n"
+    "          'contents': [\n"
+    "            {\n"
+    "              'type': 'file',\n"
+    "              'name': \"foo4.h\",\n"
+    "              'external-contents': \"/real/foo4.h\"\n"
+    "            }\n"
+    "          ]\n"
+    "        }\n"
+    "      ]\n"
+    "    }\n"
+    "  ]\n"
+    "}\n";
+    TestVFO T(contents);
+    T.map("/path/virtual/dir/foo1.h", "/real/foo1.h");
+    T.map("/another/dir/foo2.h", "/real/foo2.h");
+    T.map("/path/virtual/dir/foo3.h", "/real/foo3.h");
+    T.map("/path/virtual/dir/in/subdir/foo4.h", "/real/foo4.h");
+  }
+}





More information about the cfe-commits mailing list