Index: include/llvm/Support/VirtualFileSystem.h =================================================================== --- include/llvm/Support/VirtualFileSystem.h +++ include/llvm/Support/VirtualFileSystem.h @@ -258,6 +258,8 @@ getBufferForFile(const Twine &Name, int64_t FileSize = -1, bool RequiresNullTerminator = true, bool IsVolatile = false); + virtual llvm::ErrorOr getExternalPath(const Twine &Path); + /// Get a directory_iterator for \p Dir. /// \note The 'end' iterator is directory_iterator(). virtual directory_iterator dir_begin(const Twine &Dir, @@ -326,6 +328,7 @@ llvm::ErrorOr status(const Twine &Path) override; llvm::ErrorOr> openFileForRead(const Twine &Path) override; + llvm::ErrorOr getExternalPath(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; @@ -357,6 +360,9 @@ llvm::ErrorOr status(const Twine &Path) override { return FS->status(Path); } + llvm::ErrorOr getExternalPath(const Twine &Path) override { + return FS->getExternalPath(Path); + } llvm::ErrorOr> openFileForRead(const Twine &Path) override { return FS->openFileForRead(Path); Index: lib/Support/VirtualFileSystem.cpp =================================================================== --- lib/Support/VirtualFileSystem.cpp +++ lib/Support/VirtualFileSystem.cpp @@ -120,6 +120,10 @@ return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); } +ErrorOr FileSystem::getExternalPath(const Twine &path) { + return make_error_code(llvm::errc::not_supported); +} + std::error_code FileSystem::makeAbsolute(SmallVectorImpl &Path) const { if (llvm::sys::path::is_absolute(Path)) return {}; @@ -235,6 +239,7 @@ ErrorOr> openFileForRead(const Twine &Path) override; directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + llvm::ErrorOr getExternalPath(const Twine &Path) override; llvm::ErrorOr getCurrentWorkingDirectory() const override; std::error_code setCurrentWorkingDirectory(const Twine &Path) override; std::error_code isLocal(const Twine &Path, bool &Result) override; @@ -303,6 +308,10 @@ return llvm::sys::fs::real_path(Path, Output); } +llvm::ErrorOr RealFileSystem::getExternalPath(const Twine &Path) { + return Path.str(); +} + IntrusiveRefCntPtr vfs::getRealFileSystem() { static IntrusiveRefCntPtr FS = new RealFileSystem(); return FS; @@ -372,6 +381,13 @@ return make_error_code(llvm::errc::no_such_file_or_directory); } +ErrorOr OverlayFileSystem::getExternalPath(const Twine &Path) { + for (auto &FS : FSList) + if (FS->exists(Path)) + return FS->getExternalPath(Path); + return make_error_code(llvm::errc::not_supported); +} + llvm::ErrorOr OverlayFileSystem::getCurrentWorkingDirectory() const { // All file systems are synchronized, just take the first working directory. @@ -1182,6 +1198,7 @@ ErrorOr status(const Twine &Path) override; ErrorOr> openFileForRead(const Twine &Path) override; + ErrorOr getExternalPath(const Twine &Path) override; llvm::ErrorOr getCurrentWorkingDirectory() const override { return ExternalFS->getCurrentWorkingDirectory(); @@ -1890,6 +1907,23 @@ llvm::make_unique(std::move(*Result), S)); } +ErrorOr RedirectingFileSystem::getExternalPath(const Twine &Path) { + ErrorOr E = lookupPath(Path); + if (!E) { + if (IsFallthrough && + E.getError() == llvm::errc::no_such_file_or_directory) { + return Path.str(); + } + return E.getError(); + } + + auto *F = dyn_cast(*E); + if (!F) + return make_error_code(llvm::errc::not_supported); + + return ExternalFS->getExternalPath(F->getExternalContentsPath()); +} + IntrusiveRefCntPtr vfs::getVFSFromYAML(std::unique_ptr Buffer, SourceMgr::DiagHandlerTy DiagHandler, Index: unittests/Support/VirtualFileSystemTest.cpp =================================================================== --- unittests/Support/VirtualFileSystemTest.cpp +++ unittests/Support/VirtualFileSystemTest.cpp @@ -1775,3 +1775,44 @@ checkContents(FS->dir_begin("//root/foo", EC), {"//root/foo/a", "//root/foo/b"}); } + +TEST_F(VFSFromYAMLTest, ExternalPath) { + ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); + ScopedDir _a(TestDirectory + "/a"); + ScopedDir _b(TestDirectory + "/b"); + + IntrusiveRefCntPtr RealFS = vfs::getRealFileSystem(); + + std::string Root = StringRef(TestDirectory).str(); + // clang-format off + std::string Yaml = "{ 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'foo',\n" + " 'external-contents': '"+ Root +"/a'\n" + " },\n" + " {\n" + " 'type': 'file',\n" + " 'name': 'bar',\n" + " 'external-contents': '"+ Root +"/b'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}"; + // clang-format on + + IntrusiveRefCntPtr FS = getFromYAMLString(Yaml, RealFS); + ASSERT_TRUE(FS.get() != nullptr); + + auto A = FS->getExternalPath("/foo"); + ASSERT_TRUE(A); + ASSERT_EQ(Root + "/a", *A); + + auto B = FS->getExternalPath("/bar"); + ASSERT_TRUE(B); + ASSERT_EQ(Root + "/b", *B); +}