]> granicus.if.org Git - clang/commitdiff
[libclang] Introduce libclang APIs for creating a buffer with a JSON virtual file...
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 25 Feb 2014 03:59:23 +0000 (03:59 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 25 Feb 2014 03:59:23 +0000 (03:59 +0000)
The current API only supports adding 'virtual file path' -> 'real file path' mappings.

rdar://15986708

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@202105 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang-c/BuildSystem.h
include/clang-c/CXErrorCode.h [new file with mode: 0644]
include/clang-c/Index.h
tools/libclang/BuildSystem.cpp
tools/libclang/libclang.exports
unittests/libclang/LibclangTest.cpp

index f8eff4257df7a295c00347916ce4a6713e6f44a2..ab581fdccedd78d263d0d6204e6abfd0b5e26bab 100644 (file)
@@ -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
@@ -32,6 +33,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);
+
 /**
  * @}
  */
diff --git a/include/clang-c/CXErrorCode.h b/include/clang-c/CXErrorCode.h
new file mode 100644 (file)
index 0000000..a026c95
--- /dev/null
@@ -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
+
index eece5fb975696521441f0f83d7cb3d6a11bb76e9..9c37ac745bb5f66f5f95c87808dbedf1434923d3 100644 (file)
@@ -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)                       \
@@ -73,43 +74,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.
index caf8377174d3018785b4fe44c9876bcea8cd10f5..311319ab966f7c7f15f287e4ac8dc7b2c6004b1d 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #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;
+}
index 0fd63d311344bec12c863ec351a106ce282def1e..5fbd9a246854b6436cd69cef0ced0e97981887ad 100644 (file)
@@ -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
index 4278d10a1f80366978841189426baf9171ab24be..f855ecdd3c67cb597978dd8e4605a56f88432725 100644 (file)
@@ -28,3 +28,114 @@ TEST(libclang, clang_createTranslationUnit2_InvalidArgs) {
             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");
+  }
+}