Index: llvm/include/llvm/Support/VirtualFileSystem.h =================================================================== --- llvm/include/llvm/Support/VirtualFileSystem.h +++ llvm/include/llvm/Support/VirtualFileSystem.h @@ -20,6 +20,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -767,6 +768,37 @@ #endif }; +template class WorkingDirectoryFileSystem : public T { +public: + template + WorkingDirectoryFileSystem(ArgsT... Args) + : T(Args...), WorkingDirectory(), EC() { + llvm::ErrorOr WD = T::getCurrentWorkingDirectory(); + if (WD) + WorkingDirectory = *WD; + else + EC = WD.getError(); + }; + + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + if (!this->exists(Path)) + return llvm::errc::no_such_file_or_directory; + WorkingDirectory = Path.str(); + EC = {}; + return {}; + } + + llvm::ErrorOr getCurrentWorkingDirectory() const override { + if (EC) + return EC; + return WorkingDirectory; + } + +private: + std::string WorkingDirectory; + std::error_code EC; +}; + /// Collect all pairs of entries from the /// \p YAMLFilePath. This is used by the module dependency collector to forward /// the entries into the reproducer output VFS YAML file. Index: llvm/lib/Support/VirtualFileSystem.cpp =================================================================== --- llvm/lib/Support/VirtualFileSystem.cpp +++ llvm/lib/Support/VirtualFileSystem.cpp @@ -28,7 +28,6 @@ #include "llvm/Support/Chrono.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" -#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" Index: llvm/unittests/Support/VirtualFileSystemTest.cpp =================================================================== --- llvm/unittests/Support/VirtualFileSystemTest.cpp +++ llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -174,9 +174,20 @@ }; class ErrorDummyFileSystem : public DummyFileSystem { +public: + ErrorDummyFileSystem(bool InvalidInitialWorkingDirectory = false) + : InvalidInitialWorkingDirectory(InvalidInitialWorkingDirectory) {} std::error_code setCurrentWorkingDirectory(const Twine &Path) override { return llvm::errc::no_such_file_or_directory; } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + if (InvalidInitialWorkingDirectory) + return llvm::errc::no_such_file_or_directory; + return "//working/directory"; + } + +private: + bool InvalidInitialWorkingDirectory; }; /// Replace back-slashes by front-slashes. @@ -883,6 +894,72 @@ EXPECT_FALSE(Local); } +TEST(WorkingDirectoryFileSystemTest, Basic) { + IntrusiveRefCntPtr ErrorFS(new ErrorDummyFileSystem()); + ErrorFS->addDirectory("//root/"); + ErrorFS->addDirectory("//root/foo"); + ErrorFS->addRegularFile("//root/foo/a"); + ErrorFS->addRegularFile("//root/foo/b"); + + std::error_code EC; + + // The ErrorDummyFileSystem rejects every path. + EC = ErrorFS->setCurrentWorkingDirectory("//root/"); + ASSERT_TRUE(EC); + EC = ErrorFS->setCurrentWorkingDirectory("//root/foo"); + ASSERT_TRUE(EC); + + typedef llvm::vfs::WorkingDirectoryFileSystem + WorkingDirDummyFileSystem; + IntrusiveRefCntPtr WorkingDirFS( + new WorkingDirDummyFileSystem()); + WorkingDirFS->addDirectory("//root/"); + WorkingDirFS->addDirectory("//root/foo"); + WorkingDirFS->addRegularFile("//root/foo/a"); + WorkingDirFS->addRegularFile("//root/foo/b"); + + ASSERT_EQ("//working/directory", *WorkingDirFS->getCurrentWorkingDirectory()); + + // The WorkingDirectoryFileSystem accepts valid paths. + EC = WorkingDirFS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + ASSERT_EQ("//root/", *WorkingDirFS->getCurrentWorkingDirectory()); + + EC = WorkingDirFS->setCurrentWorkingDirectory("//root/foo"); + ASSERT_FALSE(EC); + ASSERT_EQ("//root/foo", *WorkingDirFS->getCurrentWorkingDirectory()); + ASSERT_TRUE(WorkingDirFS->exists("a")); + ASSERT_TRUE(WorkingDirFS->exists("b")); + + // The WorkingDirectoryFileSystem rejects invalid paths. + EC = WorkingDirFS->setCurrentWorkingDirectory("//root/bar"); + ASSERT_TRUE(EC); + ASSERT_EQ("//root/foo", *WorkingDirFS->getCurrentWorkingDirectory()); +} + +TEST(WorkingDirectoryFileSystemTest, InvalidInitialWorkingDirectory) { + typedef llvm::vfs::WorkingDirectoryFileSystem + WorkingDirDummyFileSystem; + IntrusiveRefCntPtr WorkingDirFS( + new WorkingDirDummyFileSystem(/*InvalidInitialWorkingDirectory*/ true)); + WorkingDirFS->addDirectory("//root/"); + + std::error_code EC; + + // If the wrapped FS returned an error code from getCurrentWorkingDirectory, + // WorkingDirectoryFileSystem will do the same until the working directory is + // changed. + llvm::ErrorOr WD = WorkingDirFS->getCurrentWorkingDirectory(); + ASSERT_FALSE(WD); + ASSERT_EQ(llvm::errc::no_such_file_or_directory, WD.getError()); + + // The error code is cleared after setting the working directory to a valid + // path. + EC = WorkingDirFS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + ASSERT_EQ("//root/", *WorkingDirFS->getCurrentWorkingDirectory()); +} + class InMemoryFileSystemTest : public ::testing::Test { protected: llvm::vfs::InMemoryFileSystem FS;