diff options
Diffstat (limited to 'llvm')
| -rw-r--r-- | llvm/include/llvm/Support/VirtualFileSystem.h | 13 | ||||
| -rw-r--r-- | llvm/lib/Support/VirtualFileSystem.cpp | 45 | ||||
| -rw-r--r-- | llvm/unittests/Support/VirtualFileSystemTest.cpp | 175 | 
3 files changed, 217 insertions, 16 deletions
diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h index 627831ea7cb..c844d9d194f 100644 --- a/llvm/include/llvm/Support/VirtualFileSystem.h +++ b/llvm/include/llvm/Support/VirtualFileSystem.h @@ -647,9 +647,19 @@ private:    friend class VFSFromYamlDirIterImpl;    friend class RedirectingFileSystemParser; +  bool shouldUseExternalFS() const { +    return ExternalFSValidWD && IsFallthrough; +  } +    /// The root(s) of the virtual file system.    std::vector<std::unique_ptr<Entry>> Roots; +  /// The current working directory of the file system. +  std::string WorkingDirectory; + +  /// Whether the current working directory is valid for the external FS. +  bool ExternalFSValidWD = false; +    /// The file system to use for external references.    IntrusiveRefCntPtr<FileSystem> ExternalFS; @@ -689,8 +699,7 @@ private:        true;  #endif -  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS) -      : ExternalFS(std::move(ExternalFS)) {} +  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS);    /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly    /// recursing into the contents of \p From if it is a directory. diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index 5aa420cfc72..7c3d04817a3 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -989,6 +989,16 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {  // RedirectingFileSystem implementation  //===-----------------------------------------------------------------------===/ +RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS) +    : ExternalFS(std::move(FS)) { +  if (ExternalFS) +    if (auto ExternalWorkingDirectory = +            ExternalFS->getCurrentWorkingDirectory()) { +      WorkingDirectory = *ExternalWorkingDirectory; +      ExternalFSValidWD = true; +    } +} +  // FIXME: reuse implementation common with OverlayFSDirIterImpl as these  // iterators are conceptually similar.  class llvm::vfs::VFSFromYamlDirIterImpl @@ -1035,12 +1045,27 @@ public:  llvm::ErrorOr<std::string>  RedirectingFileSystem::getCurrentWorkingDirectory() const { -  return ExternalFS->getCurrentWorkingDirectory(); +  return WorkingDirectory;  }  std::error_code  RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { -  return ExternalFS->setCurrentWorkingDirectory(Path); +  // Don't change the working directory if the path doesn't exist. +  if (!exists(Path)) +    return errc::no_such_file_or_directory; + +  // Always change the external FS but ignore its result. +  if (ExternalFS) { +    auto EC = ExternalFS->setCurrentWorkingDirectory(Path); +    ExternalFSValidWD = !static_cast<bool>(EC); +  } + +  SmallString<128> AbsolutePath; +  Path.toVector(AbsolutePath); +  if (std::error_code EC = makeAbsolute(AbsolutePath)) +    return EC; +  WorkingDirectory = AbsolutePath.str(); +  return {};  }  std::error_code RedirectingFileSystem::isLocal(const Twine &Path, @@ -1053,7 +1078,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,    ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);    if (!E) {      EC = E.getError(); -    if (IsFallthrough && EC == errc::no_such_file_or_directory) +    if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)        return ExternalFS->dir_begin(Dir, EC);      return {};    } @@ -1071,7 +1096,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,    auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);    return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(        Dir, D->contents_begin(), D->contents_end(), -      /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); +      /*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));  }  void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { @@ -1572,7 +1597,7 @@ RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,    RedirectingFileSystemParser P(Stream);    std::unique_ptr<RedirectingFileSystem> FS( -      new RedirectingFileSystem(std::move(ExternalFS))); +      new RedirectingFileSystem(ExternalFS));    if (!YAMLFilePath.empty()) {      // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed @@ -1701,7 +1726,7 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,  ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {    ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);    if (!Result) { -    if (IsFallthrough && +    if (shouldUseExternalFS() &&          Result.getError() == llvm::errc::no_such_file_or_directory) {        return ExternalFS->status(Path);      } @@ -1739,7 +1764,7 @@ ErrorOr<std::unique_ptr<File>>  RedirectingFileSystem::openFileForRead(const Twine &Path) {    ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);    if (!E) { -    if (IsFallthrough && +    if (shouldUseExternalFS() &          E.getError() == llvm::errc::no_such_file_or_directory) {        return ExternalFS->openFileForRead(Path);      } @@ -1770,7 +1795,7 @@ RedirectingFileSystem::getRealPath(const Twine &Path,                                     SmallVectorImpl<char> &Output) const {    ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);    if (!Result) { -    if (IsFallthrough && +    if (shouldUseExternalFS() &&          Result.getError() == llvm::errc::no_such_file_or_directory) {        return ExternalFS->getRealPath(Path, Output);      } @@ -1783,8 +1808,8 @@ RedirectingFileSystem::getRealPath(const Twine &Path,    }    // Even if there is a directory entry, fall back to ExternalFS if allowed,    // because directories don't have a single external contents path. -  return IsFallthrough ? ExternalFS->getRealPath(Path, Output) -                       : llvm::errc::invalid_argument; +  return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output) +                               : llvm::errc::invalid_argument;  }  IntrusiveRefCntPtr<FileSystem> diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp index a867bc57e94..b81cb86f0b6 100644 --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -41,7 +41,9 @@ struct DummyFile : public vfs::File {  class DummyFileSystem : public vfs::FileSystem {    int FSID;   // used to produce UniqueIDs    int FileID; // used to produce UniqueIDs +  std::string WorkingDirectory;    std::map<std::string, vfs::Status> FilesAndDirs; +  typedef std::map<std::string, vfs::Status>::const_iterator const_iterator;    static int getNextFSID() {      static int Count = 0; @@ -52,8 +54,7 @@ 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()); +    auto I = findEntry(Path);      if (I == FilesAndDirs.end())        return make_error_code(llvm::errc::no_such_file_or_directory);      return I->second; @@ -66,15 +67,16 @@ public:      return S.getError();    }    llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { -    return std::string(); +    return WorkingDirectory;    }    std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +    WorkingDirectory = Path.str();      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()); +    auto I = findEntry(Path);      if (I == FilesAndDirs.end())        return make_error_code(llvm::errc::no_such_file_or_directory);      if (I->second.isSymlink()) { @@ -136,6 +138,14 @@ public:      FilesAndDirs[Path] = Status;    } +  const_iterator findEntry(const Twine &Path) const { +    SmallString<128> P; +    Path.toVector(P); +    std::error_code EC = makeAbsolute(P); +    assert(!EC); +    return FilesAndDirs.find(P.str()); +  } +    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, @@ -158,6 +168,12 @@ public:    }  }; +class ErrorDummyFileSystem : public DummyFileSystem { +  std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +    return llvm::errc::no_such_file_or_directory; +  } +}; +  /// Replace back-slashes by front-slashes.  std::string getPosixPath(std::string S) {    SmallString<128> Result; @@ -1994,3 +2010,154 @@ TEST_F(VFSFromYAMLTest, GetRealPath) {    EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),              errc::no_such_file_or_directory);  } + +TEST_F(VFSFromYAMLTest, WorkingDirectory) { +  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); +  Lower->addDirectory("//root/"); +  Lower->addDirectory("//root/foo"); +  Lower->addRegularFile("//root/foo/a"); +  Lower->addRegularFile("//root/foo/b"); +  IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( +      "{ 'use-external-names': false,\n" +      "  'roots': [\n" +      "{\n" +      "  'type': 'directory',\n" +      "  'name': '//root/',\n" +      "  'contents': [ {\n" +      "                  'type': 'file',\n" +      "                  'name': 'bar/a',\n" +      "                  'external-contents': '//root/foo/a'\n" +      "                }\n" +      "              ]\n" +      "}\n" +      "]\n" +      "}", +      Lower); +  ASSERT_TRUE(FS.get() != nullptr); +  std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar/"); +  ASSERT_FALSE(EC); + +  llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory(); +  ASSERT_TRUE(WorkingDir); +  EXPECT_EQ(*WorkingDir, "//root/bar/"); + +  llvm::ErrorOr<vfs::Status> Status = FS->status("./a"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->isStatusKnown()); +  EXPECT_FALSE(Status->isDirectory()); +  EXPECT_TRUE(Status->isRegularFile()); +  EXPECT_FALSE(Status->isSymlink()); +  EXPECT_FALSE(Status->isOther()); +  EXPECT_TRUE(Status->exists()); + +  EC = FS->setCurrentWorkingDirectory("bogus"); +  ASSERT_TRUE(EC); +  WorkingDir = FS->getCurrentWorkingDirectory(); +  ASSERT_TRUE(WorkingDir); +  EXPECT_EQ(*WorkingDir, "//root/bar/"); + +  EC = FS->setCurrentWorkingDirectory("//root/"); +  ASSERT_FALSE(EC); +  WorkingDir = FS->getCurrentWorkingDirectory(); +  ASSERT_TRUE(WorkingDir); +  EXPECT_EQ(*WorkingDir, "//root/"); + +  EC = FS->setCurrentWorkingDirectory("bar/"); +  ASSERT_FALSE(EC); +  WorkingDir = FS->getCurrentWorkingDirectory(); +  ASSERT_TRUE(WorkingDir); +  EXPECT_EQ(*WorkingDir, "//root/bar/"); +} + +TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) { +  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); +  Lower->addDirectory("//root/"); +  Lower->addDirectory("//root/foo"); +  Lower->addRegularFile("//root/foo/a"); +  Lower->addRegularFile("//root/foo/b"); +  Lower->addRegularFile("//root/c"); +  IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( +      "{ 'use-external-names': false,\n" +      "  'roots': [\n" +      "{\n" +      "  'type': 'directory',\n" +      "  'name': '//root/',\n" +      "  'contents': [ {\n" +      "                  'type': 'file',\n" +      "                  'name': 'bar/a',\n" +      "                  'external-contents': '//root/foo/a'\n" +      "                }\n" +      "              ]\n" +      "}\n" +      "]\n" +      "}", +      Lower); +  ASSERT_TRUE(FS.get() != nullptr); +  std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); +  ASSERT_FALSE(EC); +  ASSERT_TRUE(FS.get() != nullptr); + +  llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->exists()); + +  Status = FS->status("foo/a"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->exists()); + +  EC = FS->setCurrentWorkingDirectory("//root/bar/"); +  ASSERT_FALSE(EC); + +  Status = FS->status("./a"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->exists()); + +  Status = FS->status("./b"); +  ASSERT_TRUE(Status.getError()); + +  Status = FS->status("./c"); +  ASSERT_TRUE(Status.getError()); + +  EC = FS->setCurrentWorkingDirectory("//root/"); +  ASSERT_FALSE(EC); + +  Status = FS->status("c"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->exists()); +} + +TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) { +  IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem()); +  Lower->addDirectory("//root/"); +  Lower->addDirectory("//root/foo"); +  Lower->addRegularFile("//root/foo/a"); +  Lower->addRegularFile("//root/foo/b"); +  Lower->addRegularFile("//root/c"); +  IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( +      "{ 'use-external-names': false,\n" +      "  'roots': [\n" +      "{\n" +      "  'type': 'directory',\n" +      "  'name': '//root/',\n" +      "  'contents': [ {\n" +      "                  'type': 'file',\n" +      "                  'name': 'bar/a',\n" +      "                  'external-contents': '//root/foo/a'\n" +      "                }\n" +      "              ]\n" +      "}\n" +      "]\n" +      "}", +      Lower); +  ASSERT_TRUE(FS.get() != nullptr); +  std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); +  ASSERT_FALSE(EC); +  ASSERT_TRUE(FS.get() != nullptr); + +  llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a"); +  ASSERT_FALSE(Status.getError()); +  EXPECT_TRUE(Status->exists()); + +  Status = FS->status("foo/a"); +  ASSERT_TRUE(Status.getError()); +}  | 

