#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"
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())
} // 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<uint64_t>::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) {}
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 {
// 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<detail::InMemoryDirectory>(Dir->addChild(
Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
continue;
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 {