Re: [PATCH] D13430: [VFS] Add an in-memory file system implementation.
This revision was automatically updated to reflect the committed changes. Closed by commit rL249316: [VFS] Add working directories to every virtual file system. (authored by d0k). Changed prior to commit: http://reviews.llvm.org/D13430?vs=36509&id=36515#toc Repository: rL LLVM http://reviews.llvm.org/D13430 Files: cfe/trunk/include/clang/Basic/VirtualFileSystem.h cfe/trunk/lib/Basic/VirtualFileSystem.cpp cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp Index: cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp === --- cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp +++ cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp @@ -43,6 +43,12 @@ openFileForRead(const Twine &Path) override { llvm_unreachable("unimplemented"); } + llvm::ErrorOr getCurrentWorkingDirectory() const override { +return std::string(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +return std::error_code(); + } struct DirIterImpl : public clang::vfs::detail::DirIterImpl { std::map &FilesAndDirs; Index: cfe/trunk/include/clang/Basic/VirtualFileSystem.h === --- cfe/trunk/include/clang/Basic/VirtualFileSystem.h +++ cfe/trunk/include/clang/Basic/VirtualFileSystem.h @@ -199,6 +199,25 @@ /// \note The 'end' iterator is directory_iterator(). virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) = 0; + + /// Set the working directory. This will affect all following operations on + /// this file system and may propagate down for nested file systems. + virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0; + /// Get the working directory of this file system. + virtual llvm::ErrorOr getCurrentWorkingDirectory() const = 0; + + /// Make \a Path an absolute path. + /// + /// Makes \a Path absolute using the current directory if it is not already. + /// An empty \a Path will result in the current directory. + /// + /// /absolute/path => /absolute/path + /// relative/../path => /relative/../path + /// + /// \param Path A path that is modified to be an absolute path. + /// \returns success if \a path has been made absolute, otherwise a + /// platform-specific error_code. + std::error_code makeAbsolute(SmallVectorImpl &Path) const; }; /// \brief Gets an \p vfs::FileSystem for the 'real' file system, as seen by @@ -230,6 +249,8 @@ llvm::ErrorOr> openFileForRead(const Twine &Path) override; directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + llvm::ErrorOr getCurrentWorkingDirectory() const override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; typedef FileSystemList::reverse_iterator iterator; @@ -248,6 +269,7 @@ /// An in-memory file system. class InMemoryFileSystem : public FileSystem { std::unique_ptr Root; + std::string WorkingDirectory; public: InMemoryFileSystem(); @@ -260,6 +282,13 @@ llvm::ErrorOr> openFileForRead(const Twine &Path) override; directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + llvm::ErrorOr getCurrentWorkingDirectory() const override { +return WorkingDirectory; + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +WorkingDirectory = Path.str(); +return std::error_code(); + } }; /// \brief Get a globally unique ID for a virtual file or directory. Index: cfe/trunk/lib/Basic/VirtualFileSystem.cpp === --- cfe/trunk/lib/Basic/VirtualFileSystem.cpp +++ cfe/trunk/lib/Basic/VirtualFileSystem.cpp @@ -89,6 +89,14 @@ return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); } +std::error_code FileSystem::makeAbsolute(SmallVectorImpl &Path) const { + auto WorkingDir = getCurrentWorkingDirectory(); + if (!WorkingDir) +return WorkingDir.getError(); + + return llvm::sys::fs::make_absolute(WorkingDir.get(), Path); +} + //===---===/ // RealFileSystem implementation //===---===/ @@ -160,6 +168,9 @@ ErrorOr status(const Twine &Path) override; ErrorOr> openFileForRead(const Twine &Path) override; directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + + llvm::ErrorOr getCurrentWorkingDirectory() const override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; }; } // end anonymous namespace @@ -178,6 +189,28 @@ return std::unique_ptr(new RealFile(FD, Name.str())); } +llvm::ErrorOr RealFileSystem::getCurrentWorkingDirectory() const { + SmallString<256> Dir; + if (std::error_code EC = llvm::sys::fs::current_path(Dir)) +return EC; + return Dir.str().st
Re: [PATCH] D13430: [VFS] Add an in-memory file system implementation.
klimek accepted this revision. klimek added a comment. This revision is now accepted and ready to land. lg Comment at: lib/Basic/VirtualFileSystem.cpp:1263-1265 @@ -957,5 +1262,5 @@ if (!F->useExternalName(UseExternalNames)) { // Provide a file wrapper that returns the external name when asked. class NamedFileAdaptor : public File { std::unique_ptr InnerFile; Optional: I'd put this into an anonymous namespace. But I'm also fine with waiting how others think about this :) http://reviews.llvm.org/D13430 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D13430: [VFS] Add an in-memory file system implementation.
bkramer marked 10 inline comments as done. Comment at: include/clang/Basic/VirtualFileSystem.h:286-288 @@ +285,5 @@ + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +WorkingDirectory = Path.str(); +return std::error_code(); + } klimek wrote: > Return errc::success? There is no errc::success, the preferred way is to just use error_code(). Fixed the comment. Comment at: lib/Basic/VirtualFileSystem.cpp:80 @@ -79,1 +79,3 @@ +// FIXME: This is copypasted from LLVM's file system implementation. +std::error_code FileSystem::make_absolute(SmallVectorImpl &path) const { klimek wrote: > What's the proposed fix? :) Moving it to LLVM. That's done now :) Comment at: lib/Basic/VirtualFileSystem.cpp:312-316 @@ -229,4 +311,7 @@ void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr FS) { FSList.push_back(FS); + // Synchronize added file systems by duplicating the working directory from + // the first one in the list. + FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get()); } klimek wrote: > If I read this correctly, it gets and sets the pwd from FS for the first one? > That seems superfluous. > (perhaps change the constructor to not use pushOverlay) Good catch! Comment at: lib/Basic/VirtualFileSystem.cpp:476 @@ +475,3 @@ + std::error_code close() override { return std::error_code(); } + void setName(StringRef Name) override { Parent.getStatus().setName(Name); } +}; klimek wrote: > Is this used in the File interface? It seems - strange... is this really a > "mv" implementation? Gone it is. Comment at: lib/Basic/VirtualFileSystem.cpp:546-550 @@ +545,7 @@ +// End of the path, create a new file. +Status Stat(Path, "", getNextVirtualUniqueID(), +llvm::sys::TimeValue(ModificationTime), 0, 0, +Buffer->getBufferSize(), +llvm::sys::fs::file_type::regular_file, +llvm::sys::fs::all_all); +Dir->addChild(Name, llvm::make_unique( klimek wrote: > I think we'll want to get some of these into the interface. But that's fine > in a follow-up patch. Add a FIXME though. FIXME added. Comment at: lib/Basic/VirtualFileSystem.cpp:800 @@ -455,1 +799,3 @@ + std::string WorkingDirectory; + klimek wrote: > Doesn't the YAML FS work on the same pwd as the current process? Changed the methods to forward to the inner FS of the yaml overlay. http://reviews.llvm.org/D13430 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D13430: [VFS] Add an in-memory file system implementation.
bkramer updated this revision to Diff 36509. bkramer added a comment. Rebased and addressed review comments. http://reviews.llvm.org/D13430 Files: include/clang/Basic/VirtualFileSystem.h lib/Basic/VirtualFileSystem.cpp unittests/Basic/VirtualFileSystemTest.cpp unittests/Tooling/RewriterTestContext.h Index: unittests/Tooling/RewriterTestContext.h === --- unittests/Tooling/RewriterTestContext.h +++ unittests/Tooling/RewriterTestContext.h @@ -34,25 +34,30 @@ /// methods, which help with writing tests that change files. class RewriterTestContext { public: - RewriterTestContext() - : DiagOpts(new DiagnosticOptions()), -Diagnostics(IntrusiveRefCntPtr(new DiagnosticIDs), -&*DiagOpts), -DiagnosticPrinter(llvm::outs(), &*DiagOpts), -Files((FileSystemOptions())), -Sources(Diagnostics, Files), -Rewrite(Sources, Options) { + RewriterTestContext() + : DiagOpts(new DiagnosticOptions()), + Diagnostics(IntrusiveRefCntPtr(new DiagnosticIDs), + &*DiagOpts), + DiagnosticPrinter(llvm::outs(), &*DiagOpts), + InMemoryFileSystem(new vfs::InMemoryFileSystem), + OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())), + Files(FileSystemOptions(), OverlayFileSystem), + Sources(Diagnostics, Files), Rewrite(Sources, Options) { Diagnostics.setClient(&DiagnosticPrinter, false); +// FIXME: To make these tests truly in-memory, we need to overlay the +// builtin headers. +OverlayFileSystem->pushOverlay(InMemoryFileSystem); } ~RewriterTestContext() {} FileID createInMemoryFile(StringRef Name, StringRef Content) { std::unique_ptr Source = llvm::MemoryBuffer::getMemBuffer(Content); -const FileEntry *Entry = - Files.getVirtualFile(Name, Source->getBufferSize(), 0); -Sources.overrideFileContents(Entry, std::move(Source)); +InMemoryFileSystem->addFile(Name, 0, std::move(Source)); + +const FileEntry *Entry = Files.getFile(Name); assert(Entry != nullptr); return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); } @@ -109,6 +114,8 @@ IntrusiveRefCntPtr DiagOpts; DiagnosticsEngine Diagnostics; TextDiagnosticPrinter DiagnosticPrinter; + IntrusiveRefCntPtr InMemoryFileSystem; + IntrusiveRefCntPtr OverlayFileSystem; FileManager Files; SourceManager Sources; LangOptions Options; Index: unittests/Basic/VirtualFileSystemTest.cpp === --- unittests/Basic/VirtualFileSystemTest.cpp +++ unittests/Basic/VirtualFileSystemTest.cpp @@ -43,6 +43,12 @@ openFileForRead(const Twine &Path) override { llvm_unreachable("unimplemented"); } + llvm::ErrorOr getCurrentWorkingDirectory() const override { +return std::string(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +return std::error_code(); + } struct DirIterImpl : public clang::vfs::detail::DirIterImpl { std::map &FilesAndDirs; @@ -515,6 +521,73 @@ } } +class InMemoryFileSystemTest : public ::testing::Test { +protected: + clang::vfs::InMemoryFileSystem FS; +}; + +TEST_F(InMemoryFileSystemTest, IsEmpty) { + auto Stat = FS.status("/a"); + ASSERT_EQ(errc::no_such_file_or_directory, Stat.getError()) << FS.toString(); + Stat = FS.status("/"); + ASSERT_EQ(errc::no_such_file_or_directory, Stat.getError()) << FS.toString(); +} + +TEST_F(InMemoryFileSystemTest, WindowsPath) { + FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); + auto Stat = FS.status("c:"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + Stat = FS.status("c:/windows/system128/foo.cpp"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); + Stat = FS.status("d:/windows/foo.cpp"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); +} + +TEST_F(InMemoryFileSystemTest, OverlayFile) { + FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); + auto Stat = FS.status("/"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + Stat = FS.status("/a"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); + ASSERT_EQ("/a", Stat->getName()); +} + +TEST_F(InMemoryFileSystemTest, OpenFileForRead) { + FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); + auto File = FS.openFileForRead("/a"); + ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); + File = FS.openFileForRead("/a"); // Open again. + ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); + File = FS.openFileForRead("/"); + ASSERT_EQ(errc::invalid_argument, File.getError()) << FS.toString(); + File = FS.openFileForRead("/b"); + ASSERT_EQ(errc::no_such_file_or_directory, File.getE
Re: [PATCH] D13430: [VFS] Add an in-memory file system implementation.
klimek added inline comments. Comment at: include/clang/Basic/VirtualFileSystem.h:286-288 @@ +285,5 @@ + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +WorkingDirectory = Path.str(); +return std::error_code(); + } Return errc::success? Comment at: lib/Basic/VirtualFileSystem.cpp:80 @@ -79,1 +79,3 @@ +// FIXME: This is copypasted from LLVM's file system implementation. +std::error_code FileSystem::make_absolute(SmallVectorImpl &path) const { What's the proposed fix? :) Comment at: lib/Basic/VirtualFileSystem.cpp:312-316 @@ -229,4 +311,7 @@ void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr FS) { FSList.push_back(FS); + // Synchronize added file systems by duplicating the working directory from + // the first one in the list. + FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get()); } If I read this correctly, it gets and sets the pwd from FS for the first one? That seems superfluous. (perhaps change the constructor to not use pushOverlay) Comment at: lib/Basic/VirtualFileSystem.cpp:437 @@ +436,3 @@ + virtual ~InMemoryNode() {} + Status &getStatus() { return Stat; } + InMemoryNodeKind getKind() const { return Kind; } Do we want to make this const? Comment at: lib/Basic/VirtualFileSystem.cpp:439 @@ +438,3 @@ + InMemoryNodeKind getKind() const { return Kind; } + virtual std::string toString(unsigned Indent) = 0; +}; This probably wants to be const. Comment at: lib/Basic/VirtualFileSystem.cpp:461 @@ +460,3 @@ +class InMemoryFileAdaptor : public File { + InMemoryFile &Parent; + I think parent is a misleading name here (parent implies a hierarchy, but this is a simple adapter). Perhaps "Node". Comment at: lib/Basic/VirtualFileSystem.cpp:476 @@ +475,3 @@ + std::error_code close() override { return std::error_code(); } + void setName(StringRef Name) override { Parent.getStatus().setName(Name); } +}; Is this used in the File interface? It seems - strange... is this really a "mv" implementation? Comment at: lib/Basic/VirtualFileSystem.cpp:546-550 @@ +545,7 @@ +// End of the path, create a new file. +Status Stat(Path, "", getNextVirtualUniqueID(), +llvm::sys::TimeValue(ModificationTime), 0, 0, +Buffer->getBufferSize(), +llvm::sys::fs::file_type::regular_file, +llvm::sys::fs::all_all); +Dir->addChild(Name, llvm::make_unique( I think we'll want to get some of these into the interface. But that's fine in a follow-up patch. Add a FIXME though. Comment at: lib/Basic/VirtualFileSystem.cpp:800 @@ -455,1 +799,3 @@ + std::string WorkingDirectory; + Doesn't the YAML FS work on the same pwd as the current process? Comment at: unittests/Tooling/RewriterTestContext.h:42-45 @@ +41,6 @@ + DiagnosticPrinter(llvm::outs(), &*DiagOpts), + InMemoryFileSystem(new vfs::InMemoryFileSystem), + OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())), + Files(FileSystemOptions(), OverlayFileSystem), + Sources(Diagnostics, Files), Rewrite(Sources, Options) { Perhaps add a FIXME: To make these tests truly in-memory, we need to overlay the builtin headers. http://reviews.llvm.org/D13430 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D13430: [VFS] Add an in-memory file system implementation.
bkramer created this revision. bkramer added a reviewer: klimek. bkramer added a subscriber: cfe-commits. Herald added a subscriber: klimek. This is a simple file system tree of memory buffers that can be filled by a client. In conjunction with an OverlayFS it can be used to make virtual files accessible right next to physical files. This can be used as a replacement for the virtual file handling in FileManager and which I intend to remove eventually. [VFS] Add working directories to every virtual file system. For RealFileSystem this is getcwd()/chdir(), the synthetic file systems can make up one for themselves. OverlayFileSystem now synchronizes the working directories when a new FS is added to the overlay or the overlay working directory is set. This allows purely artificial file systems that have zero ties to the underlying disks. http://reviews.llvm.org/D13430 Files: include/clang/Basic/VirtualFileSystem.h lib/Basic/VirtualFileSystem.cpp unittests/Basic/VirtualFileSystemTest.cpp unittests/Tooling/RewriterTestContext.h Index: unittests/Tooling/RewriterTestContext.h === --- unittests/Tooling/RewriterTestContext.h +++ unittests/Tooling/RewriterTestContext.h @@ -34,25 +34,28 @@ /// methods, which help with writing tests that change files. class RewriterTestContext { public: - RewriterTestContext() - : DiagOpts(new DiagnosticOptions()), -Diagnostics(IntrusiveRefCntPtr(new DiagnosticIDs), -&*DiagOpts), -DiagnosticPrinter(llvm::outs(), &*DiagOpts), -Files((FileSystemOptions())), -Sources(Diagnostics, Files), -Rewrite(Sources, Options) { + RewriterTestContext() + : DiagOpts(new DiagnosticOptions()), + Diagnostics(IntrusiveRefCntPtr(new DiagnosticIDs), + &*DiagOpts), + DiagnosticPrinter(llvm::outs(), &*DiagOpts), + InMemoryFileSystem(new vfs::InMemoryFileSystem), + OverlayFileSystem( + new vfs::OverlayFileSystem(vfs::getRealFileSystem())), + Files(FileSystemOptions(), OverlayFileSystem), + Sources(Diagnostics, Files), Rewrite(Sources, Options) { Diagnostics.setClient(&DiagnosticPrinter, false); +OverlayFileSystem->pushOverlay(InMemoryFileSystem); } ~RewriterTestContext() {} FileID createInMemoryFile(StringRef Name, StringRef Content) { std::unique_ptr Source = llvm::MemoryBuffer::getMemBuffer(Content); -const FileEntry *Entry = - Files.getVirtualFile(Name, Source->getBufferSize(), 0); -Sources.overrideFileContents(Entry, std::move(Source)); +InMemoryFileSystem->addFile(Name, 0, std::move(Source)); + +const FileEntry *Entry = Files.getFile(Name); assert(Entry != nullptr); return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); } @@ -109,6 +112,8 @@ IntrusiveRefCntPtr DiagOpts; DiagnosticsEngine Diagnostics; TextDiagnosticPrinter DiagnosticPrinter; + IntrusiveRefCntPtr InMemoryFileSystem; + IntrusiveRefCntPtr OverlayFileSystem; FileManager Files; SourceManager Sources; LangOptions Options; Index: unittests/Basic/VirtualFileSystemTest.cpp === --- unittests/Basic/VirtualFileSystemTest.cpp +++ unittests/Basic/VirtualFileSystemTest.cpp @@ -43,6 +43,12 @@ openFileForRead(const Twine &Path) override { llvm_unreachable("unimplemented"); } + llvm::ErrorOr getCurrentWorkingDirectory() const override { +return std::string(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +return std::error_code(); + } struct DirIterImpl : public clang::vfs::detail::DirIterImpl { std::map &FilesAndDirs; @@ -515,6 +521,73 @@ } } +class InMemoryFileSystemTest : public ::testing::Test { +protected: + clang::vfs::InMemoryFileSystem FS; +}; + +TEST_F(InMemoryFileSystemTest, IsEmpty) { + auto Stat = FS.status("/a"); + ASSERT_EQ(errc::no_such_file_or_directory, Stat.getError()) << FS.toString(); + Stat = FS.status("/"); + ASSERT_EQ(errc::no_such_file_or_directory, Stat.getError()) << FS.toString(); +} + +TEST_F(InMemoryFileSystemTest, WindowsPath) { + FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); + auto Stat = FS.status("c:"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + Stat = FS.status("c:/windows/system128/foo.cpp"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); + Stat = FS.status("d:/windows/foo.cpp"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); +} + +TEST_F(InMemoryFileSystemTest, OverlayFile) { + FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); + auto Stat = FS.status("/"); + ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); + Stat = FS.status("/a"); + ASSERT_FA