This patch implements Duncan's suggestion from D94811. It allows us to extract the working directory logic from the virtual file system implementation. More specifically, it enables handling of purely virtual working directories in the RedirectingFileSystem. Currently, setting a working directory that does not exist in the RedirectingFileSystem's "underlying filesystem", disables falthrough to avoid inconsistencies.
Details
Diff Detail
Unit Tests
Event Timeline
This looks like the direction I was suggesting; may be in a follow up patch one of us can change RealFileSystem to be based on this.
What's missing right now is that the APIs taking paths need to be intercepted, so the paths can be converted to absolute before going to the underlying FS. I'd also prefer if we could put all or most of the guts in a base class that isn't templated, both to reduce code duplication and perhaps sink some of it into the .cpp file.
llvm/include/llvm/Support/VirtualFileSystem.h | ||
---|---|---|
771 | Please name this BaseT to document a little better that it's a base class. | |
786 | I think this should turn Path into an absolute path. I'd suggest something like: SmallString<128> CanonicalizedPath; Path.toVector(CanonicalizedPath); sys::fs::make_absolute(WorkingDirectory, CanonicalizedPath); sys::path::remove_dots(CanonicalizedPath); WorkingDirectory = CanonicalizedPath.str(); remove_dots is important to avoid cd ../x followed by cd ../y from endlessly growing the WD into something like /path/to/../x/../y. Also, see below, maybe these guts don't need to be templated and can go in the .cpp. | |
796 | I think you also need to override every API that takes a (potentially relative) path:
I'd suggest splitting out WorkingDirectoryFileSystemBase, which would not inherit from anything, as a non-templated place to put a couple of helpers: class WorkingDirectoryFileSystemBase { public: // Can be public or protected; doesn't matter since WorkingDirectoryFileSystem // inherits privately. Definition should probably go in the .cpp. Twine getAdjustedPathTwine(const Twine &Path, SmallVectorImpl<char> &Storage) { if (sys::path::is_absolute(Path)) return Path; Path.toVector(Storage); sys::fs::make_absolute(WorkingDirectory, Storage); return Storage; } ErrorOr<Status> status(const Twine &Path) { SmallString<128> Storage; return getWorkingDirectoryUnderlyingFS().status( getAdjustedPathTwine(Path, Storage)); } // etc. protected: const FileSystem &getWorkingDirectoryUnderlyingFS() const = 0; FileSystem &getWorkingDirectoryUnderlyingFS() { return const_cast<FileSystem &>( const_cast<const WorkingDirectoryFileSystemBase *>(this)-> getWorkingDirectoryUnderlyingFS()); } private: std::string WorkingDirectory; } and then you're left with: template <class BaseT> class WorkingDirectoryFileSystem : WorkingDirectoryFileSystemBase, public BaseT { public: std::error_code setCurrentWorkingDirectory(const Twine &Path) override { return WorkingDirectoryFileSystemBase::setCurrentWorkingDirectory(Path); } std::error_code makeAbsolute(SmallVectorImpl<char> &Path) override { return WorkingDirectoryFileSystemBase::makeAbsolute(Path); } ErrorOr<Status> status(const Twine &Path) override { return WorkingDirectoryFileSystemBase::status(Path); } // etc. private: const FileSystem &getWorkingDirectoryUnderlyingFS() const final { return static_cast<BaseT *>(this); } }; or there are other ways to layer it, like passing static_cast<BaseT *> down to WorkingDirectoryFileSystemBase as a parameter, or pulling the calls to getAdjustedPathTwine() up to the templated class. | |
llvm/unittests/Support/VirtualFileSystemTest.cpp | ||
907–910 | You can probably skip assigning EC here, make this two one-line statements, unless you're actually going to check what the error is. | |
962 | I think we also to test that the WorkingDirFS will correctly convert relative paths to absolute paths in the other APIs, like status(). |
Please name this BaseT to document a little better that it's a base class.