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