Index: include/lldb/Utility/FileSpec.h =================================================================== --- include/lldb/Utility/FileSpec.h +++ include/lldb/Utility/FileSpec.h @@ -23,6 +23,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" #include // for size_t #include // for uint32_t, uint64_t @@ -63,7 +64,8 @@ public: using Style = llvm::sys::path::Style; - FileSpec(); + FileSpec(llvm::IntrusiveRefCntPtr = + llvm::vfs::getRealFileSystem()); //------------------------------------------------------------------ /// Constructor with path. @@ -83,10 +85,14 @@ /// @see FileSpec::SetFile (const char *path, bool resolve) //------------------------------------------------------------------ explicit FileSpec(llvm::StringRef path, bool resolve_path, - Style style = Style::native); + Style style = Style::native, + llvm::IntrusiveRefCntPtr = + llvm::vfs::getRealFileSystem()); explicit FileSpec(llvm::StringRef path, bool resolve_path, - const llvm::Triple &Triple); + const llvm::Triple &Triple, + llvm::IntrusiveRefCntPtr = + llvm::vfs::getRealFileSystem()); //------------------------------------------------------------------ /// Copy constructor @@ -520,7 +526,8 @@ /// Input path to be resolved, in the form of a llvm::SmallString or /// similar. //------------------------------------------------------------------ - static void Resolve(llvm::SmallVectorImpl &path); + static void Resolve(llvm::SmallVectorImpl &path, + llvm::IntrusiveRefCntPtr fs); FileSpec CopyByAppendingPathComponent(llvm::StringRef component) const; FileSpec CopyByRemovingLastPathComponent() const; @@ -553,11 +560,13 @@ typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType)( void *baton, llvm::sys::fs::file_type file_type, const FileSpec &spec); - static void EnumerateDirectory(llvm::StringRef dir_path, - bool find_directories, bool find_files, - bool find_other, - EnumerateDirectoryCallbackType callback, - void *callback_baton); + static void + EnumerateDirectory(llvm::StringRef dir_path, bool find_directories, + bool find_files, bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton, + llvm::IntrusiveRefCntPtr fs = + llvm::vfs::getRealFileSystem()); typedef std::function @@ -572,6 +581,7 @@ //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ + llvm::IntrusiveRefCntPtr m_fs; ConstString m_directory; ///< The uniqued directory path ConstString m_filename; ///< The uniqued filename path mutable bool m_is_resolved = false; ///< True if this path has been resolved. Index: source/API/SBFileSpec.cpp =================================================================== --- source/API/SBFileSpec.cpp +++ source/API/SBFileSpec.cpp @@ -18,6 +18,7 @@ #include "lldb/Utility/Stream.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/VirtualFileSystem.h" using namespace lldb; using namespace lldb_private; @@ -67,7 +68,7 @@ int SBFileSpec::ResolvePath(const char *src_path, char *dst_path, size_t dst_len) { llvm::SmallString<64> result(src_path); - lldb_private::FileSpec::Resolve(result); + lldb_private::FileSpec::Resolve(result, llvm::vfs::getRealFileSystem()); ::snprintf(dst_path, dst_len, "%s", result.c_str()); return std::min(dst_len - 1, result.size()); } Index: source/Utility/FileSpec.cpp =================================================================== --- source/Utility/FileSpec.cpp +++ source/Utility/FileSpec.cpp @@ -67,7 +67,8 @@ } // end anonymous namespace -void FileSpec::Resolve(llvm::SmallVectorImpl &path) { +void FileSpec::Resolve(llvm::SmallVectorImpl &path, + llvm::IntrusiveRefCntPtr fs) { if (path.empty()) return; @@ -78,39 +79,43 @@ // Save a copy of the original path that's passed in llvm::SmallString<128> original_path(path.begin(), path.end()); - llvm::sys::fs::make_absolute(path); - if (!llvm::sys::fs::exists(path)) { + fs->makeAbsolute(path); + if (!fs->exists(path)) { path.clear(); path.append(original_path.begin(), original_path.end()); } } -FileSpec::FileSpec() : m_style(GetNativeStyle()) {} +FileSpec::FileSpec(llvm::IntrusiveRefCntPtr fs) + : m_fs(fs), m_style(GetNativeStyle()) {} //------------------------------------------------------------------ // Default constructor that can take an optional full path to a file on disk. //------------------------------------------------------------------ -FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, Style style) - : m_style(style) { +FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, Style style, + llvm::IntrusiveRefCntPtr fs) + : m_fs(fs), m_style(style) { SetFile(path, resolve_path, style); } FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, - const llvm::Triple &Triple) + const llvm::Triple &Triple, + llvm::IntrusiveRefCntPtr fs) : FileSpec{path, resolve_path, - Triple.isOSWindows() ? Style::windows : Style::posix} {} + Triple.isOSWindows() ? Style::windows : Style::posix, fs} {} //------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------ FileSpec::FileSpec(const FileSpec &rhs) - : m_directory(rhs.m_directory), m_filename(rhs.m_filename), - m_is_resolved(rhs.m_is_resolved), m_style(rhs.m_style) {} + : m_fs(llvm::vfs::getRealFileSystem()), m_directory(rhs.m_directory), + m_filename(rhs.m_filename), m_is_resolved(rhs.m_is_resolved), + m_style(rhs.m_style) {} //------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------ -FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() { +FileSpec::FileSpec(const FileSpec *rhs) : m_fs(), m_directory(), m_filename() { if (rhs) *this = *rhs; } @@ -248,7 +253,7 @@ llvm::SmallString<64> resolved(pathname); if (resolve) { - FileSpec::Resolve(resolved); + FileSpec::Resolve(resolved, m_fs); m_is_resolved = true; } @@ -456,7 +461,7 @@ //------------------------------------------------------------------ // Returns true if the file exists. //------------------------------------------------------------------ -bool FileSpec::Exists() const { return llvm::sys::fs::exists(GetPath()); } +bool FileSpec::Exists() const { return m_fs->exists(GetPath()); } bool FileSpec::Readable() const { return GetPermissions() & llvm::sys::fs::perms::all_read; @@ -508,21 +513,19 @@ } uint64_t FileSpec::GetByteSize() const { - uint64_t Size = 0; - if (llvm::sys::fs::file_size(GetPath(), Size)) + llvm::ErrorOr status = m_fs->status(GetPath()); + if (!status) return 0; - return Size; + return status->getSize(); } FileSpec::Style FileSpec::GetPathStyle() const { return m_style; } uint32_t FileSpec::GetPermissions() const { - namespace fs = llvm::sys::fs; - fs::file_status st; - if (fs::status(GetPath(), st, false)) - return fs::perms::perms_not_known; - - return st.permissions(); + llvm::ErrorOr status = m_fs->status(GetPath()); + if (!status) + return llvm::sys::fs::perms::perms_not_known; + return status->getPermissions(); } //------------------------------------------------------------------ @@ -602,29 +605,27 @@ return m_filename.MemorySize() + m_directory.MemorySize(); } -void FileSpec::EnumerateDirectory(llvm::StringRef dir_path, - bool find_directories, bool find_files, - bool find_other, - EnumerateDirectoryCallbackType callback, - void *callback_baton) { - namespace fs = llvm::sys::fs; +void FileSpec::EnumerateDirectory( + llvm::StringRef dir_path, bool find_directories, bool find_files, + bool find_other, EnumerateDirectoryCallbackType callback, + void *callback_baton, llvm::IntrusiveRefCntPtr fs) { std::error_code EC; - fs::recursive_directory_iterator Iter(dir_path, EC); - fs::recursive_directory_iterator End; + llvm::vfs::recursive_directory_iterator Iter(*fs, dir_path, EC); + llvm::vfs::recursive_directory_iterator End; for (; Iter != End && !EC; Iter.increment(EC)) { const auto &Item = *Iter; - llvm::ErrorOr Status = Item.status(); + llvm::ErrorOr Status = fs->status(Item.path()); if (!Status) break; - if (!find_files && fs::is_regular_file(*Status)) + if (!find_files && Status->isRegularFile()) continue; - if (!find_directories && fs::is_directory(*Status)) + if (!find_directories && Status->isDirectory()) continue; - if (!find_other && fs::is_other(*Status)) + if (!find_other && Status->isOther()) continue; FileSpec Spec(Item.path(), false); - auto Result = callback(callback_baton, Status->type(), Spec); + auto Result = callback(callback_baton, Status->getType(), Spec); if (Result == eEnumerateDirectoryResultQuit) return; if (Result == eEnumerateDirectoryResultNext) { Index: unittests/Utility/FileSpecTest.cpp =================================================================== --- unittests/Utility/FileSpecTest.cpp +++ unittests/Utility/FileSpecTest.cpp @@ -10,8 +10,11 @@ #include "gtest/gtest.h" #include "lldb/Utility/FileSpec.h" +#include "llvm/Support/Errc.h" using namespace lldb_private; +using namespace llvm; +using llvm::sys::fs::UniqueID; TEST(FileSpecTest, FileAndDirectoryComponents) { FileSpec fs_posix("/foo/bar", false, FileSpec::Style::posix); @@ -370,3 +373,180 @@ EXPECT_FALSE(fs_windows.RemoveLastPathComponent()); EXPECT_STREQ("C:", fs_windows.GetCString()); } + +// Copied verbatim from unittests/Support/VirtualFileSystemTest.cpp +namespace { +struct DummyFile : public vfs::File { + vfs::Status S; + explicit DummyFile(vfs::Status S) : S(S) {} + llvm::ErrorOr status() override { return S; } + llvm::ErrorOr> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + llvm_unreachable("unimplemented"); + } + std::error_code close() override { return std::error_code(); } +}; + +class DummyFileSystem : public vfs::FileSystem { + int FSID; // used to produce UniqueIDs + int FileID; // used to produce UniqueIDs + std::map FilesAndDirs; + + static int getNextFSID() { + static int Count = 0; + return Count++; + } + +public: + DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} + + ErrorOr status(const Twine &Path) override { + std::map::iterator I = + FilesAndDirs.find(Path.str()); + if (I == FilesAndDirs.end()) + return make_error_code(llvm::errc::no_such_file_or_directory); + return I->second; + } + ErrorOr> + openFileForRead(const Twine &Path) override { + auto S = status(Path); + if (S) + return std::unique_ptr(new DummyFile{*S}); + return S.getError(); + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return std::string(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return std::error_code(); + } + // Map any symlink to "/symlink". + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) const override { + auto I = FilesAndDirs.find(Path.str()); + if (I == FilesAndDirs.end()) + return make_error_code(llvm::errc::no_such_file_or_directory); + if (I->second.isSymlink()) { + Output.clear(); + Twine("/symlink").toVector(Output); + return std::error_code(); + } + Output.clear(); + Path.toVector(Output); + return std::error_code(); + } + + struct DirIterImpl : public llvm::vfs::detail::DirIterImpl { + std::map &FilesAndDirs; + std::map::iterator I; + std::string Path; + bool isInPath(StringRef S) { + if (Path.size() < S.size() && S.find(Path) == 0) { + auto LastSep = S.find_last_of('/'); + if (LastSep == Path.size() || LastSep == Path.size() - 1) + return true; + } + return false; + } + DirIterImpl(std::map &FilesAndDirs, + const Twine &_Path) + : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), + Path(_Path.str()) { + for (; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = + vfs::directory_entry(I->second.getName(), I->second.getType()); + break; + } + } + } + std::error_code increment() override { + ++I; + for (; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = + vfs::directory_entry(I->second.getName(), I->second.getType()); + break; + } + } + if (I == FilesAndDirs.end()) + CurrentEntry = vfs::directory_entry(); + return std::error_code(); + } + }; + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return vfs::directory_iterator( + std::make_shared(FilesAndDirs, Dir)); + } + + void addEntry(StringRef Path, const vfs::Status &Status) { + FilesAndDirs[Path] = Status; + } + + void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 1024, + sys::fs::file_type::regular_file, Perms); + addEntry(Path, S); + } + + void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::directory_file, Perms); + addEntry(Path, S); + } + + void addSymlink(StringRef Path) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::symlink_file, sys::fs::all_all); + addEntry(Path, S); + } +}; +} // namespace + +FileSpec::EnumerateDirectoryResult +VFSCallback(void *baton, llvm::sys::fs::file_type file_type, + const FileSpec &spec) { + auto visited = static_cast *>(baton); + visited->push_back(spec.GetPath()); + return FileSpec::eEnumerateDirectoryResultNext; +} + +TEST(FileSpecTest, VirtualFileSystem) { + IntrusiveRefCntPtr D(new DummyFileSystem()); + ErrorOr Status((std::error_code())); + D->addRegularFile("/foo"); + D->addDirectory("/bar"); + D->addSymlink("/baz"); + + FileSpec foo_posix("/foo", false, FileSpec::Style::posix, D); + EXPECT_TRUE(foo_posix.Exists()); + + constexpr bool find_directories = true; + constexpr bool find_files = true; + constexpr bool find_other = true; + + std::vector visited; + FileSpec::EnumerateDirectory("/", find_directories, find_files, find_other, + VFSCallback, &visited, D); + + EXPECT_EQ(visited.size(), (size_t)3); + EXPECT_TRUE(std::find(visited.begin(), visited.end(), "/foo") != + visited.end()); + EXPECT_TRUE(std::find(visited.begin(), visited.end(), "/bar") != + visited.end()); + EXPECT_TRUE(std::find(visited.begin(), visited.end(), "/baz") != + visited.end()); + + D->addRegularFile("C:\\foo"); + FileSpec foo_windows("C:\\foo", false, FileSpec::Style::windows, D); + EXPECT_TRUE(foo_windows.Exists()); + + FileSpec bogus("bogus", false, FileSpec::Style::posix, D); + EXPECT_FALSE(bogus.Exists()); +}