diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h --- a/llvm/include/llvm/Support/VirtualFileSystem.h +++ b/llvm/include/llvm/Support/VirtualFileSystem.h @@ -306,6 +306,28 @@ /// \returns success if \a path has been made absolute, otherwise a /// platform-specific error_code. virtual std::error_code makeAbsolute(SmallVectorImpl &Path) const; + + enum class PrintType { Summary, Contents, RecursiveContents }; + void print(raw_ostream &OS, PrintType Type = PrintType::Contents, + unsigned IndentLevel = 0) const { + printImpl(OS, Type, IndentLevel); + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const; +#endif + +protected: + virtual void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "FileSystem\n"; + } + + void printIndent(raw_ostream &OS, unsigned IndentLevel) const { + for (unsigned i = 0; i < IndentLevel; ++i) + OS << " "; + } }; /// Gets an \p vfs::FileSystem for the 'real' file system, as seen by @@ -357,6 +379,8 @@ using const_iterator = FileSystemList::const_reverse_iterator; using reverse_iterator = FileSystemList::iterator; using const_reverse_iterator = FileSystemList::const_iterator; + using range = iterator_range; + using const_range = iterator_range; /// Get an iterator pointing to the most recently added file system. iterator overlays_begin() { return FSList.rbegin(); } @@ -373,6 +397,13 @@ /// Get an iterator pointing one-past the most recently added file system. reverse_iterator overlays_rend() { return FSList.end(); } const_reverse_iterator overlays_rend() const { return FSList.end(); } + + range overlays_range() { return llvm::reverse(FSList); } + const_range overlays_range() const { return llvm::reverse(FSList); } + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; }; /// By default, this delegates all calls to the underlying file system. This @@ -520,6 +551,10 @@ SmallVectorImpl &Output) const override; std::error_code isLocal(const Twine &Path, bool &Result) override; std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; }; /// Get a globally unique ID for a virtual file or directory. @@ -910,12 +945,11 @@ std::vector getRoots() const; - void print(raw_ostream &OS) const; - void printEntry(raw_ostream &OS, Entry *E, int NumSpaces = 0) const; + void printEntry(raw_ostream &OS, Entry *E, unsigned IndentLevel = 0) const; -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - LLVM_DUMP_METHOD void dump() const; -#endif +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; }; /// Collect all pairs of entries from the diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -151,6 +151,10 @@ return Status && Status->exists(); } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); } +#endif + #ifndef NDEBUG static bool isTraversalComponent(StringRef Component) { return Component.equals("..") || Component.equals("."); @@ -273,6 +277,10 @@ std::error_code getRealPath(const Twine &Path, SmallVectorImpl &Output) const override; +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; + private: // If this FS has its own working dir, use it to make Path absolute. // The returned twine is safe to use as long as both Storage and Path live. @@ -354,6 +362,17 @@ return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); } +void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "RealFileSystem using "; + if (WD) + OS << "own"; + else + OS << "process"; + OS << " CWD\n"; +} + IntrusiveRefCntPtr vfs::getRealFileSystem() { static IntrusiveRefCntPtr FS(new RealFileSystem(true)); return FS; @@ -459,6 +478,19 @@ return errc::no_such_file_or_directory; } +void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "OverlayFileSystem\n"; + if (Type == PrintType::Summary) + return; + + if (Type == PrintType::Contents) + Type = PrintType::Summary; + for (auto FS : overlays_range()) + FS->print(OS, Type, IndentLevel + 1); +} + llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default; namespace { @@ -1047,6 +1079,12 @@ return {}; } +void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "InMemoryFileSystem\n"; +} + } // namespace vfs } // namespace llvm @@ -1381,34 +1419,59 @@ return R; } -void RedirectingFileSystem::print(raw_ostream &OS) const { +void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "RedirectingFileSystem (UseExternalNames: " + << (UseExternalNames ? "true" : "false") << ")\n"; + if (Type == PrintType::Summary) + return; + for (const auto &Root : Roots) - printEntry(OS, Root.get()); + printEntry(OS, Root.get(), IndentLevel); + + printIndent(OS, IndentLevel); + OS << "ExternalFS:\n"; + ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type, + IndentLevel + 1); } void RedirectingFileSystem::printEntry(raw_ostream &OS, RedirectingFileSystem::Entry *E, - int NumSpaces) const { - StringRef Name = E->getName(); - for (int i = 0, e = NumSpaces; i < e; ++i) - OS << " "; - OS << "'" << Name.str().c_str() << "'" - << "\n"; + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "'" << E->getName() << "'"; - if (E->getKind() == RedirectingFileSystem::EK_Directory) { - auto *DE = dyn_cast(E); - assert(DE && "Should be a directory"); + switch (E->getKind()) { + case EK_Directory: { + auto *DE = cast(E); + OS << "\n"; for (std::unique_ptr &SubEntry : llvm::make_range(DE->contents_begin(), DE->contents_end())) - printEntry(OS, SubEntry.get(), NumSpaces + 2); + printEntry(OS, SubEntry.get(), IndentLevel + 1); + break; + } + case EK_DirectoryRemap: + case EK_File: { + auto *RE = cast(E); + OS << " -> '" << RE->getExternalContentsPath() << "'"; + switch (RE->getUseName()) { + case NK_NotSet: + break; + case NK_External: + OS << " (UseExternalName: true)"; + break; + case NK_Virtual: + OS << " (UseExternalName: false)"; + break; + } + OS << "\n"; + break; + } } } -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) -void RedirectingFileSystem::dump() const { print(dbgs()); } -#endif - /// A helper class to hold the common YAML parsing state. class llvm::vfs::RedirectingFileSystemParser { yaml::Stream &Stream; diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -171,6 +171,25 @@ sys::fs::file_type::symlink_file, sys::fs::all_all); addEntry(Path, S); } + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override { + printIndent(OS, IndentLevel); + OS << "DummyFileSystem ("; + switch (Type) { + case vfs::FileSystem::PrintType::Summary: + OS << "Summary"; + break; + case vfs::FileSystem::PrintType::Contents: + OS << "Contents"; + break; + case vfs::FileSystem::PrintType::RecursiveContents: + OS << "RecursiveContents"; + break; + } + OS << ")\n"; + } }; class ErrorDummyFileSystem : public DummyFileSystem { @@ -848,6 +867,36 @@ } } +TEST(OverlayFileSystemTest, PrintOutput) { + auto Dummy = makeIntrusiveRefCnt(); + auto Overlay1 = makeIntrusiveRefCnt(Dummy); + Overlay1->pushOverlay(Dummy); + auto Overlay2 = makeIntrusiveRefCnt(Overlay1); + Overlay2->pushOverlay(Dummy); + + SmallString<0> Output; + raw_svector_ostream OuputStream{Output}; + + Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Summary); + ASSERT_EQ("OverlayFileSystem\n", Output); + + Output.clear(); + Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Contents); + ASSERT_EQ("OverlayFileSystem\n" + " DummyFileSystem (Summary)\n" + " OverlayFileSystem\n", + Output); + + Output.clear(); + Overlay2->print(OuputStream, vfs::FileSystem::PrintType::RecursiveContents); + ASSERT_EQ("OverlayFileSystem\n" + " DummyFileSystem (RecursiveContents)\n" + " OverlayFileSystem\n" + " DummyFileSystem (RecursiveContents)\n" + " DummyFileSystem (RecursiveContents)\n", + Output); +} + TEST(ProxyFileSystemTest, Basic) { IntrusiveRefCntPtr Base( new vfs::InMemoryFileSystem()); @@ -2928,3 +2977,80 @@ EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); } + +TEST(RedirectingFileSystemTest, PrintOutput) { + auto Buffer = + MemoryBuffer::getMemBuffer("{\n" + " 'version': 0,\n" + " 'roots': [\n" + " {\n" + " 'type': 'directory-remap',\n" + " 'name': '/dremap',\n" + " 'external-contents': '/a',\n" + " }," + " {\n" + " 'type': 'directory',\n" + " 'name': '/vdir',\n" + " 'contents': [" + " {\n" + " 'type': 'directory-remap',\n" + " 'name': 'dremap',\n" + " 'external-contents': '/b'\n" + " 'use-external-name': 'true'\n" + " },\n" + " {\n" + " 'type': 'file',\n" + " 'name': 'vfile',\n" + " 'external-contents': '/c'\n" + " 'use-external-name': 'false'\n" + " }]\n" + " }]\n" + "}"); + + auto Dummy = makeIntrusiveRefCnt(); + auto Redirecting = vfs::RedirectingFileSystem::create( + std::move(Buffer), nullptr, "", nullptr, Dummy); + + SmallString<0> Output; + raw_svector_ostream OuputStream{Output}; + + Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary); + ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output); + + Output.clear(); + Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents); + ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" + "'/'\n" + " 'dremap' -> '/a'\n" + " 'vdir'\n" + " 'dremap' -> '/b' (UseExternalName: true)\n" + " 'vfile' -> '/c' (UseExternalName: false)\n" + "ExternalFS:\n" + " DummyFileSystem (Summary)\n", + Output); + + Output.clear(); + Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1); + ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n" + " '/'\n" + " 'dremap' -> '/a'\n" + " 'vdir'\n" + " 'dremap' -> '/b' (UseExternalName: true)\n" + " 'vfile' -> '/c' (UseExternalName: false)\n" + " ExternalFS:\n" + " DummyFileSystem (Summary)\n", + Output); + + Output.clear(); + Redirecting->print(OuputStream, + vfs::FileSystem::PrintType::RecursiveContents); + ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n" + "'/'\n" + " 'dremap' -> '/a'\n" + " 'vdir'\n" + " 'dremap' -> '/b' (UseExternalName: true)\n" + " 'vfile' -> '/c' (UseExternalName: false)\n" + "ExternalFS:\n" + " DummyFileSystem (RecursiveContents)\n", + Output); +}