Index: llvm/trunk/include/llvm/Support/FileCollector.h =================================================================== --- llvm/trunk/include/llvm/Support/FileCollector.h +++ llvm/trunk/include/llvm/Support/FileCollector.h @@ -37,6 +37,12 @@ /// removed after it was added to the mapping. std::error_code copyFiles(bool StopOnError = true); + /// Create a VFS that collects all the paths that might be looked at by the + /// file system accesses. + static IntrusiveRefCntPtr + createCollectorVFS(IntrusiveRefCntPtr BaseFS, + std::shared_ptr Collector); + private: void addFileImpl(StringRef SrcPath); Index: llvm/trunk/lib/Support/FileCollector.cpp =================================================================== --- llvm/trunk/lib/Support/FileCollector.cpp +++ llvm/trunk/lib/Support/FileCollector.cpp @@ -187,3 +187,82 @@ return {}; } + +namespace { + +class FileCollectorFileSystem : public vfs::FileSystem { +public: + explicit FileCollectorFileSystem(IntrusiveRefCntPtr FS, + std::shared_ptr Collector) + : FS(std::move(FS)), Collector(std::move(Collector)) {} + + llvm::ErrorOr status(const Twine &Path) override { + auto Result = FS->status(Path); + if (Result && Result->exists()) + Collector->addFile(Path); + return Result; + } + + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + auto Result = FS->openFileForRead(Path); + if (Result && *Result) + Collector->addFile(Path); + return Result; + } + + llvm::vfs::directory_iterator dir_begin(const llvm::Twine &Dir, + std::error_code &EC) override { + auto It = FS->dir_begin(Dir, EC); + if (EC) + return It; + // Collect everything that's listed in case the user needs it. + Collector->addFile(Dir); + for (; !EC && It != llvm::vfs::directory_iterator(); It.increment(EC)) { + if (It->type() == sys::fs::file_type::regular_file || + It->type() == sys::fs::file_type::directory_file || + It->type() == sys::fs::file_type::symlink_file) { + Collector->addFile(It->path()); + } + } + if (EC) + return It; + // Return a new iterator. + return FS->dir_begin(Dir, EC); + } + + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) const override { + auto EC = FS->getRealPath(Path, Output); + if (!EC) { + Collector->addFile(Path); + if (Output.size() > 0) + Collector->addFile(Output); + } + return EC; + } + + std::error_code isLocal(const Twine &Path, bool &Result) override { + return FS->isLocal(Path, Result); + } + + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + + std::error_code setCurrentWorkingDirectory(const llvm::Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + +private: + IntrusiveRefCntPtr FS; + std::shared_ptr Collector; +}; + +} // end anonymous namespace + +IntrusiveRefCntPtr +FileCollector::createCollectorVFS(IntrusiveRefCntPtr BaseFS, + std::shared_ptr Collector) { + return new FileCollectorFileSystem(std::move(BaseFS), std::move(Collector)); +} Index: llvm/trunk/unittests/Support/FileCollectorTest.cpp =================================================================== --- llvm/trunk/unittests/Support/FileCollectorTest.cpp +++ llvm/trunk/unittests/Support/FileCollectorTest.cpp @@ -179,6 +179,44 @@ ASSERT_TRUE(IsDirectory); } +TEST(FileCollectorTest, recordVFSAccesses) { + ScopedDir file_root("dir_root", true); + ScopedDir subdir(file_root + "/subdir"); + ScopedDir subdir2(file_root + "/subdir2"); + ScopedFile a(subdir2 + "/a"); + ScopedFile b(file_root + "/b"); + ScopedDir subdir3(file_root + "/subdir3"); + ScopedFile subdir3a(subdir3 + "/aa"); + ScopedDir subdir3b(subdir3 + "/subdirb"); + { + ScopedFile subdir3fileremoved(subdir3 + "/removed"); + } + + // Create file collector and add files. + ScopedDir root("copy_files_root", true); + std::string root_fs = root.Path.str(); + auto Collector = std::make_shared(root_fs, root_fs); + auto VFS = + FileCollector::createCollectorVFS(vfs::getRealFileSystem(), Collector); + VFS->status(a.Path); + EXPECT_TRUE(Collector->hasSeen(a.Path)); + + VFS->openFileForRead(b.Path); + EXPECT_TRUE(Collector->hasSeen(b.Path)); + + VFS->status(subdir.Path); + EXPECT_TRUE(Collector->hasSeen(subdir.Path)); + + std::error_code EC; + auto It = VFS->dir_begin(subdir3.Path, EC); + EXPECT_FALSE(EC); + EXPECT_TRUE(Collector->hasSeen(subdir3.Path)); + EXPECT_TRUE(Collector->hasSeen(subdir3a.Path)); + EXPECT_TRUE(Collector->hasSeen(subdir3b.Path)); + std::string RemovedFileName = (Twine(subdir3.Path) + "/removed").str(); + EXPECT_FALSE(Collector->hasSeen(RemovedFileName)); +} + #ifndef _WIN32 TEST(FileCollectorTest, Symlinks) { // Root where the original files live. @@ -239,4 +277,21 @@ EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath))); } } + +TEST(FileCollectorTest, recordVFSSymlinkAccesses) { + ScopedDir file_root("dir_root", true); + ScopedFile a(file_root + "/a"); + ScopedLink symlink(file_root + "/a", file_root + "/b"); + + // Create file collector and add files. + ScopedDir root("copy_files_root", true); + std::string root_fs = root.Path.str(); + auto Collector = std::make_shared(root_fs, root_fs); + auto VFS = + FileCollector::createCollectorVFS(vfs::getRealFileSystem(), Collector); + SmallString<256> Output; + VFS->getRealPath(symlink.Path, Output); + EXPECT_TRUE(Collector->hasSeen(a.Path)); + EXPECT_TRUE(Collector->hasSeen(symlink.Path)); +} #endif