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 @@ -32,6 +32,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileSystem/UniqueID.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -655,6 +656,9 @@ Status getStatus(const Twine &RequestedName) const { return Status::copyWithNewName(Stat, RequestedName); } + + UniqueID getUniqueID() const { return Stat.getUniqueID(); } + InMemoryNode *getChild(StringRef Name) { auto I = Entries.find(Name); if (I != Entries.end()) @@ -698,10 +702,28 @@ } // namespace } // namespace detail +// The UniqueID of in-memory files is derived from path and content. +// This avoids difficulties in creating exactly equivalent in-memory FSes, +// as often needed in multithreaded programs. +static sys::fs::UniqueID getUniqueID(hash_code Hash) { + return sys::fs::UniqueID(std::numeric_limits::max(), + uint64_t(size_t(Hash))); +} +static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent, + llvm::StringRef Name, + llvm::StringRef Contents) { + return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents)); +} +static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent, + llvm::StringRef Name) { + return getUniqueID(llvm::hash_combine(Parent.getFile(), Name)); +} + InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths) : Root(new detail::InMemoryDirectory( - Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0, - 0, llvm::sys::fs::file_type::directory_file, + Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""), + llvm::sys::TimePoint<>(), 0, 0, 0, + llvm::sys::fs::file_type::directory_file, llvm::sys::fs::perms::all_all))), UseNormalizedPaths(UseNormalizedPaths) {} @@ -754,10 +776,14 @@ Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget)); else { // Create a new file or directory. - Status Stat(P.str(), getNextVirtualUniqueID(), - llvm::sys::toTimePoint(ModificationTime), ResolvedUser, - ResolvedGroup, Buffer->getBufferSize(), ResolvedType, - ResolvedPerms); + Status Stat( + P.str(), + (ResolvedType == sys::fs::file_type::directory_file) + ? getDirectoryID(Dir->getUniqueID(), Name) + : getFileID(Dir->getUniqueID(), Name, Buffer->getBuffer()), + llvm::sys::toTimePoint(ModificationTime), ResolvedUser, + ResolvedGroup, Buffer->getBufferSize(), ResolvedType, + ResolvedPerms); if (ResolvedType == sys::fs::file_type::directory_file) { Child.reset(new detail::InMemoryDirectory(std::move(Stat))); } else { @@ -772,9 +798,9 @@ // Create a new directory. Use the path up to here. Status Stat( StringRef(Path.str().begin(), Name.end() - Path.str().begin()), - getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), - ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file, - NewDirectoryPerms); + getDirectoryID(Dir->getUniqueID(), Name), + llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup, + 0, sys::fs::file_type::directory_file, NewDirectoryPerms); Dir = cast(Dir->addChild( Name, std::make_unique(std::move(Stat)))); continue; 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 @@ -1279,6 +1279,26 @@ EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); } +TEST_F(InMemoryFileSystemTest, UniqueID) { + ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); + ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text"))); + ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b")); + + EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID()); + EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID()); + EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID()); + EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID()); + EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID()); + EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID()); + + // Recreating the "same" FS yields the same UniqueIDs. + vfs::InMemoryFileSystem FS2; + ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text"))); + EXPECT_EQ(FS.status("/a/b")->getUniqueID(), + FS2.status("/a/b")->getUniqueID()); + EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID()); +} + // NOTE: in the tests below, we use '//root/' as our root directory, since it is // a legal *absolute* path on Windows as well as *nix. class VFSFromYAMLTest : public ::testing::Test {