From: Justin Bogner Date: Tue, 20 May 2014 21:43:27 +0000 (+0000) Subject: VirtualFileSystem: Add YAMLVFSWriter to generate VFS mapping files X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=99b5a85324c758f15a72fdf084240f3c2af8ce35;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209241 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/VirtualFileSystem.h b/include/clang/Basic/VirtualFileSystem.h index ec2ca7050a..e4582c8028 100644 --- a/include/clang/Basic/VirtualFileSystem.h +++ b/include/clang/Basic/VirtualFileSystem.h @@ -15,8 +15,10 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/SourceMgr.h" namespace llvm { @@ -166,6 +168,34 @@ getVFSFromYAML(llvm::MemoryBuffer *Buffer, void *DiagContext = nullptr, IntrusiveRefCntPtr ExternalFS = getRealFileSystem()); +class YAMLVFSWriter { + struct MapEntry { + template MapEntry(T1 &&VPath, T2 &&RPath) + : VPath(std::forward(VPath)), RPath(std::forward(RPath)) {} + std::string VPath; + std::string RPath; + }; + std::vector Mappings; + Optional IsCaseSensitive; + + llvm::ArrayRef printDirNodes(llvm::raw_ostream &OS, + llvm::ArrayRef Entries, + StringRef ParentPath, unsigned Indent); + llvm::ArrayRef printContents(llvm::raw_ostream &OS, + llvm::ArrayRef Entries, + unsigned Indent); + bool containedIn(StringRef Parent, StringRef Path); + StringRef containedPart(StringRef Parent, StringRef Path); + +public: + YAMLVFSWriter() {} + void addFileMapping(StringRef VirtualPath, StringRef RealPath); + void setCaseSensitivity(bool CaseSensitive) { + IsCaseSensitive = CaseSensitive; + } + void write(llvm::raw_ostream &OS); +}; + } // end namespace vfs } // end namespace clang #endif // LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index fae6a35a92..24454b0192 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -11,6 +11,7 @@ #include "clang/Basic/VirtualFileSystem.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/MemoryBuffer.h" @@ -843,3 +844,118 @@ UniqueID vfs::getNextVirtualUniqueID() { // dev_t value from the OS. return UniqueID(std::numeric_limits::max(), ID); } + +#ifndef NDEBUG +static bool pathHasTraversal(StringRef Path) { + using namespace llvm::sys; + for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) + if (Comp == "." || Comp == "..") + return true; + return false; +} +#endif + +void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { + assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); + assert(sys::path::is_absolute(RealPath) && "real path not absolute"); + assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); + Mappings.emplace_back(VirtualPath, RealPath); +} + +ArrayRef +YAMLVFSWriter::printDirNodes(llvm::raw_ostream &OS, ArrayRef Entries, + StringRef ParentPath, unsigned Indent) { + while (!Entries.empty()) { + const MapEntry &Entry = Entries.front(); + OS.indent(Indent) << "{\n"; + Indent += 2; + OS.indent(Indent) << "'type': 'directory',\n"; + StringRef DirName = + containedPart(ParentPath, sys::path::parent_path(Entry.VPath)); + OS.indent(Indent) + << "'name': \"" << llvm::yaml::escape(DirName) << "\",\n"; + OS.indent(Indent) << "'contents': [\n"; + Entries = printContents(OS, 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 +YAMLVFSWriter::printContents(llvm::raw_ostream &OS, ArrayRef Entries, + unsigned Indent) { + using namespace llvm::sys; + while (!Entries.empty()) { + const MapEntry &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(OS, Entries, ParentPath, Indent); + } + } + return Entries; +} + +bool YAMLVFSWriter::containedIn(StringRef Parent, StringRef Path) { + return Path.startswith(Parent); +} + +StringRef YAMLVFSWriter::containedPart(StringRef Parent, StringRef Path) { + assert(containedIn(Parent, Path)); + if (Parent.empty()) + return Path; + return Path.slice(Parent.size() + 1, StringRef::npos); +} + +void YAMLVFSWriter::write(llvm::raw_ostream &OS) { + std::sort(Mappings.begin(), Mappings.end(), + [](const MapEntry &LHS, const MapEntry &RHS) { + return LHS.VPath < RHS.VPath; + }); + + OS << "{\n" + " 'version': 0,\n"; + if (IsCaseSensitive.hasValue()) { + OS << " 'case-sensitive': '"; + if (IsCaseSensitive.getValue()) + OS << "true"; + else + OS << "false"; + OS << "',\n"; + } + OS << " 'roots': [\n"; + printDirNodes(OS, Mappings, "", 4); + OS << " ]\n" + << "}\n"; +} diff --git a/tools/libclang/BuildSystem.cpp b/tools/libclang/BuildSystem.cpp index e5caa5447b..e9423c3286 100644 --- a/tools/libclang/BuildSystem.cpp +++ b/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 @@ unsigned long long clang_getBuildSessionTimestamp(void) { return llvm::sys::TimeValue::now().toEpochTime(); } -struct CXVirtualFileOverlayImpl { - std::vector > Mappings; - Optional 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 @@ clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO, return CXError_InvalidArguments; } - VFO->Mappings.push_back(std::make_pair(virtualPath, realPath)); + unwrap(VFO)->addFileMapping(virtualPath, realPath); return CXError_Success; } @@ -65,124 +62,10 @@ clang_VirtualFileOverlay_setCaseSensitivity(CXVirtualFileOverlay VFO, 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 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 printDirNodes(ArrayRef 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 printContents(ArrayRef 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 @@ clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned, if (!VFO || !out_buffer_ptr || !out_buffer_size) return CXError_InvalidArguments; - llvm::SmallVector 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 @@ clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned, } void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) { - delete VFO; + delete unwrap(VFO); }