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 @@ -731,6 +731,11 @@ SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, void *DiagContext, IntrusiveRefCntPtr ExternalFS); + /// Redirect each of the remapped files from first to second. + static std::unique_ptr + create(ArrayRef> RemappedFiles, + bool UseExternalNames, FileSystem &ExternalFS); + ErrorOr status(const Twine &Path) override; ErrorOr> openFileForRead(const Twine &Path) override; 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 @@ -1272,7 +1272,8 @@ return true; } - RedirectingFileSystem::Entry * +public: + static RedirectingFileSystem::Entry * lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, RedirectingFileSystem::Entry *ParentEntry = nullptr) { if (!ParentEntry) { // Look for a existent root @@ -1314,6 +1315,7 @@ return DE->getLastContent(); } +private: void uniqueOverlayTree(RedirectingFileSystem *FS, RedirectingFileSystem::Entry *SrcE, RedirectingFileSystem::Entry *NewParentE = nullptr) { @@ -1682,6 +1684,61 @@ return FS; } +std::unique_ptr RedirectingFileSystem::create( + ArrayRef> RemappedFiles, + bool UseExternalNames, FileSystem &ExternalFS) { + std::unique_ptr FS( + new RedirectingFileSystem(&ExternalFS)); + FS->UseExternalNames = UseExternalNames; + + StringMap Entries; + + for (auto &Mapping : llvm::reverse(RemappedFiles)) { + SmallString<128> From = StringRef(Mapping.first); + SmallString<128> To = StringRef(Mapping.second); + { + auto EC = ExternalFS.makeAbsolute(From); + (void)EC; + assert(!EC && "Could not make absolute path"); + } + + // Check if we've already mapped this file. The first one we see (in the + // reverse iteration) wins. + RedirectingFileSystem::Entry *&ToEntry = Entries[From]; + if (ToEntry) + continue; + + // Add parent directories. + RedirectingFileSystem::Entry *Parent = nullptr; + StringRef FromDirectory = llvm::sys::path::parent_path(From); + for (auto I = llvm::sys::path::begin(FromDirectory), + E = llvm::sys::path::end(FromDirectory); + I != E; ++I) { + Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I, + Parent); + } + assert(Parent && "File without a directory?"); + { + auto EC = ExternalFS.makeAbsolute(To); + (void)EC; + assert(!EC && "Could not make absolute path"); + } + + // Add the file. + auto NewFile = + std::make_unique( + llvm::sys::path::filename(From), To, + UseExternalNames + ? RedirectingFileSystem::RedirectingFileEntry::NK_External + : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual); + ToEntry = NewFile.get(); + cast(Parent)->addContent( + std::move(NewFile)); + } + + return FS; +} + ErrorOr RedirectingFileSystem::lookupPath(const Twine &Path_) const { SmallString<256> Path; 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 @@ -2287,3 +2287,89 @@ EXPECT_FALSE(FS->exists(_b.path("b"))); EXPECT_FALSE(FS->exists(_c.path("c"))); } + +TEST(VFSFromRemappedFilesTest, Basic) { + IntrusiveRefCntPtr BaseFS = + new vfs::InMemoryFileSystem; + BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); + BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); + + std::vector> RemappedFiles = { + {"//root/a/a", "//root/b"}, + {"//root/a/b/c", "//root/c"}, + }; + auto RemappedFS = vfs::RedirectingFileSystem::create( + RemappedFiles, /*UseExternalNames=*/false, *BaseFS); + + auto StatA = RemappedFS->status("//root/a/a"); + auto StatB = RemappedFS->status("//root/a/b/c"); + ASSERT_TRUE(StatA); + ASSERT_TRUE(StatB); + EXPECT_EQ("//root/a/a", StatA->getName()); + EXPECT_EQ("//root/a/b/c", StatB->getName()); + + auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); + auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); + ASSERT_TRUE(BufferA); + ASSERT_TRUE(BufferB); + EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); + EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); +} + +TEST(VFSFromRemappedFilesTest, UseExternalNames) { + IntrusiveRefCntPtr BaseFS = + new vfs::InMemoryFileSystem; + BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); + BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); + + std::vector> RemappedFiles = { + {"//root/a/a", "//root/b"}, + {"//root/a/b/c", "//root/c"}, + }; + auto RemappedFS = vfs::RedirectingFileSystem::create( + RemappedFiles, /*UseExternalNames=*/true, *BaseFS); + + auto StatA = RemappedFS->status("//root/a/a"); + auto StatB = RemappedFS->status("//root/a/b/c"); + ASSERT_TRUE(StatA); + ASSERT_TRUE(StatB); + EXPECT_EQ("//root/b", StatA->getName()); + EXPECT_EQ("//root/c", StatB->getName()); + + auto BufferA = RemappedFS->getBufferForFile("//root/a/a"); + auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c"); + ASSERT_TRUE(BufferA); + ASSERT_TRUE(BufferB); + EXPECT_EQ("contents of b", (*BufferA)->getBuffer()); + EXPECT_EQ("contents of c", (*BufferB)->getBuffer()); +} + +TEST(VFSFromRemappedFilesTest, LastMappingWins) { + IntrusiveRefCntPtr BaseFS = + new vfs::InMemoryFileSystem; + BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b")); + BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c")); + + std::vector> RemappedFiles = { + {"//root/a", "//root/b"}, + {"//root/a", "//root/c"}, + }; + auto RemappedFSKeepName = vfs::RedirectingFileSystem::create( + RemappedFiles, /*UseExternalNames=*/false, *BaseFS); + auto RemappedFSExternalName = vfs::RedirectingFileSystem::create( + RemappedFiles, /*UseExternalNames=*/true, *BaseFS); + + auto StatKeepA = RemappedFSKeepName->status("//root/a"); + auto StatExternalA = RemappedFSExternalName->status("//root/a"); + ASSERT_TRUE(StatKeepA); + ASSERT_TRUE(StatExternalA); + EXPECT_EQ("//root/a", StatKeepA->getName()); + EXPECT_EQ("//root/c", StatExternalA->getName()); + + auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a"); + auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a"); + ASSERT_TRUE(BufferKeepA); + ASSERT_TRUE(BufferExternalA); + EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); + EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); +}