JDevlieghere created this revision.
JDevlieghere added reviewers: zturner, labath, clayborg.
JDevlieghere added a project: LLDB.

This patch adds support for virtual file systems by delegating relevant 
operations. By default the real file system is used so this should be NFC for 
all intents and purposes.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D53532

Files:
  include/lldb/Utility/FileSpec.h
  source/API/SBFileSpec.cpp
  source/Utility/FileSpec.cpp
  unittests/Utility/FileSpecTest.cpp

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<vfs::Status> status() override { return S; }
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  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<std::string, vfs::Status> FilesAndDirs;
+
+  static int getNextFSID() {
+    static int Count = 0;
+    return Count++;
+  }
+
+public:
+  DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
+
+  ErrorOr<vfs::Status> status(const Twine &Path) override {
+    std::map<std::string, vfs::Status>::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<std::unique_ptr<vfs::File>>
+  openFileForRead(const Twine &Path) override {
+    auto S = status(Path);
+    if (S)
+      return std::unique_ptr<vfs::File>(new DummyFile{*S});
+    return S.getError();
+  }
+  llvm::ErrorOr<std::string> 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<char> &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<std::string, vfs::Status> &FilesAndDirs;
+    std::map<std::string, vfs::Status>::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<std::string, vfs::Status> &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<DirIterImpl>(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<std::vector<std::string> *>(baton);
+  visited->push_back(spec.GetPath());
+  return FileSpec::eEnumerateDirectoryResultNext;
+}
+
+TEST(FileSpecTest, VirtualFileSystem) {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  ErrorOr<vfs::Status> 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<std::string> 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());
+}
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<char> &path) {
+void FileSpec::Resolve(llvm::SmallVectorImpl<char> &path,
+                       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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<llvm::vfs::FileSystem> 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<llvm::vfs::FileSystem> 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<llvm::vfs::FileSystem> 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<llvm::vfs::Status> 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<llvm::vfs::Status> 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<llvm::vfs::FileSystem> 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<fs::basic_file_status> Status = Item.status();
+    llvm::ErrorOr<llvm::vfs::Status> 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: 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: 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 <stddef.h> // for size_t
 #include <stdint.h> // for uint32_t, uint64_t
@@ -63,7 +64,8 @@
 public:
   using Style = llvm::sys::path::Style;
 
-  FileSpec();
+  FileSpec(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> =
+               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::FileSystem> =
+                        llvm::vfs::getRealFileSystem());
 
   explicit FileSpec(llvm::StringRef path, bool resolve_path,
-                    const llvm::Triple &Triple);
+                    const llvm::Triple &Triple,
+                    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> =
+                        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<char> &path);
+  static void Resolve(llvm::SmallVectorImpl<char> &path,
+                      llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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<llvm::vfs::FileSystem> fs =
+                         llvm::vfs::getRealFileSystem());
 
   typedef std::function<EnumerateDirectoryResult(
       llvm::sys::fs::file_type file_type, const FileSpec &spec)>
@@ -572,6 +581,7 @@
   //------------------------------------------------------------------
   // Member variables
   //------------------------------------------------------------------
+  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to