diff options
Diffstat (limited to 'llvm/lib/Support/VirtualFileSystem.cpp')
-rw-r--r-- | llvm/lib/Support/VirtualFileSystem.cpp | 114 |
1 files changed, 99 insertions, 15 deletions
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index cf7fe967f01..c9920197fba 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -993,16 +993,44 @@ public: static bool classof(const Entry *E) { return E->getKind() == EK_File; } }; +// FIXME: reuse implementation common with OverlayFSDirIterImpl as these +// iterators are conceptually similar. class VFSFromYamlDirIterImpl : public llvm::vfs::detail::DirIterImpl { std::string Dir; RedirectingDirectoryEntry::iterator Current, End; - std::error_code incrementImpl(); + // To handle 'fallthrough' mode we need to iterate at first through + // RedirectingDirectoryEntry and then through ExternalFS. These operations are + // done sequentially, we just need to keep a track of what kind of iteration + // we are currently performing. + + /// Flag telling if we should iterate through ExternalFS or stop at the last + /// RedirectingDirectoryEntry::iterator. + bool IterateExternalFS; + /// Flag telling if we have switched to iterating through ExternalFS. + bool IsExternalFSCurrent = false; + FileSystem &ExternalFS; + directory_iterator ExternalDirIter; + llvm::StringSet<> SeenNames; + + /// To combine multiple iterations, different methods are responsible for + /// different iteration steps. + /// @{ + + /// Responsible for dispatching between RedirectingDirectoryEntry iteration + /// and ExternalFS iteration. + std::error_code incrementImpl(bool IsFirstTime); + /// Responsible for RedirectingDirectoryEntry iteration. + std::error_code incrementContent(bool IsFirstTime); + /// Responsible for ExternalFS iteration. + std::error_code incrementExternal(); + /// @} public: VFSFromYamlDirIterImpl(const Twine &Path, RedirectingDirectoryEntry::iterator Begin, RedirectingDirectoryEntry::iterator End, + bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC); std::error_code increment() override; @@ -1028,6 +1056,7 @@ public: /// 'case-sensitive': <boolean, default=true> /// 'use-external-names': <boolean, default=true> /// 'overlay-relative': <boolean, default=false> +/// 'fallthrough': <boolean, default=true> /// /// Virtual directories are represented as /// \verbatim @@ -1091,6 +1120,10 @@ class RedirectingFileSystem : public vfs::FileSystem { /// Whether to use to use the value of 'external-contents' for the /// names of files. This global value is overridable on a per-file basis. bool UseExternalNames = true; + + /// Whether to attempt a file lookup in external file system after it wasn't + /// found in VFS. + bool IsFallthrough = true; /// @} /// Virtual file paths and external files could be canonicalized without "..", @@ -1141,6 +1174,8 @@ public: ErrorOr<Entry *> E = lookupPath(Dir); if (!E) { EC = E.getError(); + if (IsFallthrough && EC == errc::no_such_file_or_directory) + return ExternalFS->dir_begin(Dir, EC); return {}; } ErrorOr<Status> S = status(Dir, *E); @@ -1156,7 +1191,8 @@ public: auto *D = cast<RedirectingDirectoryEntry>(*E); return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>( - Dir, D->contents_begin(), D->contents_end(), EC)); + Dir, D->contents_begin(), D->contents_end(), + /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); } void setExternalContentsPrefixDir(StringRef PrefixDir) { @@ -1538,6 +1574,7 @@ public: KeyStatusPair("case-sensitive", false), KeyStatusPair("use-external-names", false), KeyStatusPair("overlay-relative", false), + KeyStatusPair("fallthrough", false), KeyStatusPair("roots", true), }; @@ -1595,6 +1632,9 @@ public: } else if (Key == "use-external-names") { if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) return false; + } else if (Key == "fallthrough") { + if (!parseScalarBool(I.getValue(), FS->IsFallthrough)) + return false; } else { llvm_unreachable("key missing from Keys"); } @@ -1760,8 +1800,13 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) { ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) { ErrorOr<Entry *> Result = lookupPath(Path); - if (!Result) + if (!Result) { + if (IsFallthrough && + Result.getError() == llvm::errc::no_such_file_or_directory) { + return ExternalFS->status(Path); + } return Result.getError(); + } return status(Path, *Result); } @@ -1793,8 +1838,13 @@ public: ErrorOr<std::unique_ptr<File>> RedirectingFileSystem::openFileForRead(const Twine &Path) { ErrorOr<Entry *> E = lookupPath(Path); - if (!E) + if (!E) { + if (IsFallthrough && + E.getError() == llvm::errc::no_such_file_or_directory) { + return ExternalFS->openFileForRead(Path); + } return E.getError(); + } auto *F = dyn_cast<RedirectingFileEntry>(*E); if (!F) // FIXME: errc::not_a_file? @@ -2035,18 +2085,42 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) { VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( const Twine &_Path, RedirectingDirectoryEntry::iterator Begin, - RedirectingDirectoryEntry::iterator End, std::error_code &EC) - : Dir(_Path.str()), Current(Begin), End(End) { - EC = incrementImpl(); + RedirectingDirectoryEntry::iterator End, bool IterateExternalFS, + FileSystem &ExternalFS, std::error_code &EC) + : Dir(_Path.str()), Current(Begin), End(End), + IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) { + EC = incrementImpl(/*IsFirstTime=*/true); } std::error_code VFSFromYamlDirIterImpl::increment() { - assert(Current != End && "cannot iterate past end"); - ++Current; - return incrementImpl(); + return incrementImpl(/*IsFirstTime=*/false); +} + +std::error_code VFSFromYamlDirIterImpl::incrementExternal() { + assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) && + "incrementing past end"); + std::error_code EC; + if (IsExternalFSCurrent) { + ExternalDirIter.increment(EC); + } else if (IterateExternalFS) { + ExternalDirIter = ExternalFS.dir_begin(Dir, EC); + IsExternalFSCurrent = true; + if (EC && EC != errc::no_such_file_or_directory) + return EC; + EC = {}; + } + if (EC || ExternalDirIter == directory_iterator()) { + CurrentEntry = directory_entry(); + } else { + CurrentEntry = *ExternalDirIter; + } + return EC; } -std::error_code VFSFromYamlDirIterImpl::incrementImpl() { +std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { + assert(IsFirstTime || Current != End && "cannot iterate past end"); + if (!IsFirstTime) + ++Current; while (Current != End) { SmallString<128> PathStr(Dir); llvm::sys::path::append(PathStr, (*Current)->getName()); @@ -2060,12 +2134,22 @@ std::error_code VFSFromYamlDirIterImpl::incrementImpl() { break; } CurrentEntry = directory_entry(PathStr.str(), Type); - break; + return {}; } + return incrementExternal(); +} - if (Current == End) - CurrentEntry = directory_entry(); - return {}; +std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) { + while (true) { + std::error_code EC = IsExternalFSCurrent ? incrementExternal() + : incrementContent(IsFirstTime); + if (EC || CurrentEntry.path().empty()) + return EC; + StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); + if (SeenNames.insert(Name).second) + return EC; // name not seen before + } + llvm_unreachable("returned above"); } vfs::recursive_directory_iterator::recursive_directory_iterator( |