diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h --- a/llvm/include/llvm/Support/VirtualFileSystem.h +++ b/llvm/include/llvm/Support/VirtualFileSystem.h @@ -734,25 +734,6 @@ enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File }; enum NameKind { NK_NotSet, NK_External, NK_Virtual }; - // TODO: Simplify RedirectingFileSystem to remove redirection completely. - // Remove RedirectKind, Redirection, and all non-redirect-only paths. Require - // any old uses to move to using OverlayFileSystem instead (see the new - // getVFSFromYAMLs API). - - /// The type of redirection to perform. - enum class RedirectKind { - /// Lookup the redirected path first (ie. the one specified in - /// 'external-contents') and if that fails "fallthrough" to a lookup of the - /// originally provided path. - Fallthrough, - /// Lookup the provided path first and if that fails, "fallback" to a - /// lookup of the redirected path. - Fallback, - /// Only lookup the redirected path, do not lookup the originally provided - /// path. - RedirectOnly - }; - /// A single file or directory in the VFS. class Entry { EntryKind Kind; @@ -930,9 +911,6 @@ /// names of files. This global value is overridable on a per-file basis. bool UseExternalNames = true; - /// Determines the lookups to perform, as well as their order. See - /// \c RedirectKind for details. - RedirectKind Redirection = RedirectKind::Fallthrough; /// @} RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS); @@ -945,18 +923,17 @@ llvm::sys::path::const_iterator End, Entry *From) const; - /// Get the status for a path with the provided \c LookupResult. - ErrorOr status(const Twine &CanonicalPath, const Twine &OriginalPath, - const LookupResult &Result); - public: /// Looks up \p Path in \c Roots and returns a LookupResult giving the /// matched entry and, if the entry was a FileEntry or DirectoryRemapEntry, /// the path it redirects to in the external file system. - ErrorOr lookupPath(StringRef Path) const; + ErrorOr lookupPath(StringRef CanonicalPath) const; - /// Parses \p Buffer, which is expected to be in YAML format and - /// returns a virtual file system representing its contents. + /// Parses \p Buffer, which is expected to be in the YAML format described + /// in \c RedirectingFileSystem and returns a virtual file system + /// representing its contents. + /// + /// \see getVFSFromYAMLs static std::unique_ptr create(std::unique_ptr Buffer, SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, @@ -984,12 +961,6 @@ directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; - /// Sets the redirection kind to \c Fallthrough if true or \c RedirectOnly - /// otherwise. Will removed in the future, use \c setRedirection instead. - void setFallthrough(bool Fallthrough); - - void setRedirection(RedirectingFileSystem::RedirectKind Kind); - std::vector getRoots() const; void printEntry(raw_ostream &OS, Entry *E, unsigned IndentLevel = 0) const; diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -1162,8 +1162,6 @@ // RedirectingFileSystem implementation //===-----------------------------------------------------------------------===/ -namespace { - static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) { // Detect the path style in use by checking the first separator. llvm::sys::path::Style style = llvm::sys::path::Style::native; @@ -1188,17 +1186,6 @@ return result; } -/// Whether the error and entry specify a file/directory that was not found. -static bool isFileNotFound(std::error_code EC, - RedirectingFileSystem::Entry *E = nullptr) { - if (E && !isa(E)) - return false; - return EC == llvm::errc::no_such_file_or_directory; -} - -} // anonymous namespace - - RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr FS) : ExternalFS(std::move(FS)) { if (ExternalFS) @@ -1301,14 +1288,15 @@ std::error_code RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { - // Don't change the working directory if the path doesn't exist. - if (!exists(Path)) - return errc::no_such_file_or_directory; - SmallString<128> AbsolutePath; Path.toVector(AbsolutePath); if (std::error_code EC = makeAbsolute(AbsolutePath)) return EC; + + // Don't change the working directory if the path doesn't exist. + if (!exists(AbsolutePath)) + return errc::no_such_file_or_directory; + WorkingDirectory = std::string(AbsolutePath.str()); return {}; } @@ -1319,7 +1307,7 @@ Path_.toVector(Path); if (std::error_code EC = makeCanonical(Path)) - return {}; + return EC; return ExternalFS->isLocal(Path, Result); } @@ -1363,115 +1351,51 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, std::error_code &EC) { - SmallString<256> Path; - Dir.toVector(Path); - - EC = makeCanonical(Path); + SmallString<256> CanonicalPath; + Dir.toVector(CanonicalPath); + EC = makeCanonical(CanonicalPath); if (EC) return {}; - ErrorOr Result = lookupPath(Path); + ErrorOr Result = + lookupPath(CanonicalPath); if (!Result) { - if (Redirection != RedirectKind::RedirectOnly && - isFileNotFound(Result.getError())) - return ExternalFS->dir_begin(Path, EC); - EC = Result.getError(); return {}; } - // Use status to make sure the path exists and refers to a directory. - ErrorOr S = status(Path, Dir, *Result); - if (!S) { - if (Redirection != RedirectKind::RedirectOnly && - isFileNotFound(S.getError(), Result->E)) - return ExternalFS->dir_begin(Dir, EC); + // Create the appropriate directory iterator based on whether we found a + // DirectoryRemapEntry or DirectoryEntry. + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) { + // DirectoryEntry - just make an iterator for it + auto DE = cast(Result->E); + return directory_iterator(std::make_shared( + CanonicalPath, DE->contents_begin(), DE->contents_end(), EC)); + } + // Have a DirectoryRemapEntry - need to check that the external directory + // actually exists and then possibly remap names to the virtual path depending + // on 'use-external-names'. + ErrorOr S = ExternalFS->status(*ExtRedirect); + if (!S) { EC = S.getError(); return {}; } - if (!S->isDirectory()) { EC = errc::not_a_directory; return {}; } - // Create the appropriate directory iterator based on whether we found a - // DirectoryRemapEntry or DirectoryEntry. - directory_iterator RedirectIter; - std::error_code RedirectEC; - if (auto ExtRedirect = Result->getExternalRedirect()) { - auto RE = cast(Result->E); - RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC); - - if (!RE->useExternalName(UseExternalNames)) { - // Update the paths in the results to use the virtual directory's path. - RedirectIter = - directory_iterator(std::make_shared( - std::string(Path), RedirectIter)); - } - } else { - auto DE = cast(Result->E); + auto RE = cast(Result->E); + directory_iterator RedirectIter = ExternalFS->dir_begin(*ExtRedirect, EC); + if (!RE->useExternalName(UseExternalNames)) { + // Update the paths in the results to use the virtual directory's path. RedirectIter = - directory_iterator(std::make_shared( - Path, DE->contents_begin(), DE->contents_end(), RedirectEC)); - } - - if (RedirectEC) { - if (RedirectEC != errc::no_such_file_or_directory) { - EC = RedirectEC; - return {}; - } - RedirectIter = {}; - } - - if (Redirection == RedirectKind::RedirectOnly) { - EC = RedirectEC; - return RedirectIter; - } - - std::error_code ExternalEC; - directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC); - if (ExternalEC) { - if (ExternalEC != errc::no_such_file_or_directory) { - EC = ExternalEC; - return {}; - } - ExternalIter = {}; + directory_iterator(std::make_shared( + std::string(CanonicalPath), RedirectIter)); } - - SmallVector Iters; - switch (Redirection) { - case RedirectKind::Fallthrough: - Iters.push_back(ExternalIter); - Iters.push_back(RedirectIter); - break; - case RedirectKind::Fallback: - Iters.push_back(RedirectIter); - Iters.push_back(ExternalIter); - break; - default: - llvm_unreachable("unhandled RedirectKind"); - } - - directory_iterator Combined{ - std::make_shared(Iters, EC)}; - if (EC) - return {}; - return Combined; -} - -void RedirectingFileSystem::setFallthrough(bool Fallthrough) { - if (Fallthrough) { - Redirection = RedirectingFileSystem::RedirectKind::Fallthrough; - } else { - Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly; - } -} - -void RedirectingFileSystem::setRedirection( - RedirectingFileSystem::RedirectKind Kind) { - Redirection = Kind; + return RedirectIter; } std::vector RedirectingFileSystem::getRoots() const { @@ -1556,19 +1480,6 @@ std::unique_ptr FS; RedirectKind Redirection; - - // TODO: Remove after simplifying RedirectingFileSystem - static RedirectingFileSystem::RedirectKind - toRFSKind(RedirectKind Redirection) { - switch (Redirection) { - case RedirectKind::Fallthrough: - return RedirectingFileSystem::RedirectKind::Fallthrough; - case RedirectKind::Fallback: - return RedirectingFileSystem::RedirectKind::Fallback; - case RedirectKind::RedirectOnly: - return RedirectingFileSystem::RedirectKind::RedirectOnly; - } - } }; } // namespace @@ -2087,14 +1998,8 @@ if (ShouldFallthrough) { Result.Redirection = YAMLParseResult::RedirectKind::Fallthrough; - // TODO: Remove after simplifying RedirectingFileSystem - Result.FS->Redirection = - RedirectingFileSystem::RedirectKind::Fallthrough; } else { Result.Redirection = YAMLParseResult::RedirectKind::RedirectOnly; - // TODO: Remove after simplifying RedirectingFileSystem - Result.FS->Redirection = - RedirectingFileSystem::RedirectKind::RedirectOnly; } } else if (Key == "redirecting-with") { if (Keys["fallthrough"].Seen) { @@ -2105,8 +2010,6 @@ if (auto Kind = parseRedirectKind(I.getValue())) { Result.Redirection = *Kind; - // TODO: Remove after simplifying RedirectingFileSystem - Result.FS->Redirection = YAMLParseResult::toRFSKind(*Kind); } else { error(I.getValue(), "expected valid redirect kind"); return None; @@ -2229,9 +2132,9 @@ } ErrorOr -RedirectingFileSystem::lookupPath(StringRef Path) const { - sys::path::const_iterator Start = sys::path::begin(Path); - sys::path::const_iterator End = sys::path::end(Path); +RedirectingFileSystem::lookupPath(StringRef CanonicalPath) const { + sys::path::const_iterator Start = sys::path::begin(CanonicalPath); + sys::path::const_iterator End = sys::path::end(CanonicalPath); for (const auto &Root : Roots) { ErrorOr Result = lookupPathImpl(Start, End, Root.get()); @@ -2282,82 +2185,37 @@ return make_error_code(llvm::errc::no_such_file_or_directory); } -static Status getRedirectedFileStatus(const Twine &OriginalPath, - bool UseExternalNames, - Status ExternalStatus) { - Status S = ExternalStatus; - if (!UseExternalNames) - S = Status::copyWithNewName(S, OriginalPath); - S.IsVFSMapped = true; - return S; -} - -ErrorOr RedirectingFileSystem::status( - const Twine &CanonicalPath, const Twine &OriginalPath, - const RedirectingFileSystem::LookupResult &Result) { - if (Optional ExtRedirect = Result.getExternalRedirect()) { - SmallString<256> CanonicalRemappedPath((*ExtRedirect).str()); - if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) - return EC; - - ErrorOr S = ExternalFS->status(CanonicalRemappedPath); - if (!S) - return S; - S = Status::copyWithNewName(*S, *ExtRedirect); - auto *RE = cast(Result.E); - return getRedirectedFileStatus(OriginalPath, - RE->useExternalName(UseExternalNames), *S); - } - - auto *DE = cast(Result.E); - return Status::copyWithNewName(DE->getStatus(), CanonicalPath); -} - -ErrorOr -RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath, - const Twine &OriginalPath) const { - if (auto Result = ExternalFS->status(CanonicalPath)) { - return Result.get().copyWithNewName(Result.get(), OriginalPath); - } else { - return Result.getError(); - } -} - ErrorOr RedirectingFileSystem::status(const Twine &OriginalPath) { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - ErrorOr S = getExternalStatus(CanonicalPath, OriginalPath); - if (S) - return S; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return getExternalStatus(CanonicalPath, OriginalPath); + if (!Result) return Result.getError(); - } - ErrorOr S = status(CanonicalPath, OriginalPath, *Result); - if (!S && Redirection == RedirectKind::Fallthrough && - isFileNotFound(S.getError(), Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return getExternalStatus(CanonicalPath, OriginalPath); + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) { + auto *DE = cast(Result->E); + Status S = DE->getStatus(); + if (!UseExternalNames) + S = Status::copyWithNewName(S, OriginalPath); + else + S = Status::copyWithNewName(S, CanonicalPath); + S.IsVFSMapped = true; + return S; } + ErrorOr S = ExternalFS->status(*ExtRedirect); + if (!S) + return S; + + auto *RE = cast(Result->E); + if (!RE->useExternalName(UseExternalNames)) + S = Status::copyWithNewName(*S, OriginalPath); + S->IsVFSMapped = true; return S; } @@ -2404,65 +2262,32 @@ RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); - if (F) - return F; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); + if (!Result) return Result.getError(); - } - if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file? + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) return make_error_code(llvm::errc::invalid_argument); - StringRef ExtRedirect = *Result->getExternalRedirect(); - SmallString<256> CanonicalRemappedPath(ExtRedirect.str()); - if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) - return EC; - auto *RE = cast(Result->E); - - auto ExternalFile = File::getWithPath( - ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect); - if (!ExternalFile) { - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(ExternalFile.getError(), Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); - } + ErrorOr> ExternalFile = + ExternalFS->openFileForRead(*ExtRedirect); + if (!RE->useExternalName(UseExternalNames)) + ExternalFile = File::getWithPath(std::move(ExternalFile), OriginalPath); + if (!ExternalFile) return ExternalFile; - } - auto ExternalStatus = (*ExternalFile)->status(); + ErrorOr ExternalStatus = (*ExternalFile)->status(); if (!ExternalStatus) return ExternalStatus.getError(); - - // Otherwise, the file was successfully remapped. Mark it as such. Also - // replace the underlying path if the external name is being used. - Status S = getRedirectedFileStatus( - OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus); - return std::unique_ptr( - std::make_unique(std::move(*ExternalFile), S)); + ExternalStatus->IsVFSMapped = true; + return std::make_unique(std::move(*ExternalFile), + *ExternalStatus); } std::error_code @@ -2470,48 +2295,20 @@ SmallVectorImpl &Output) const { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output); - if (!EC) - return EC; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return ExternalFS->getRealPath(CanonicalPath, Output); + if (!Result) return Result.getError(); - } - // If we found FileEntry or DirectoryRemapEntry, look up the mapped - // path in the external file system. - if (auto ExtRedirect = Result->getExternalRedirect()) { - auto P = ExternalFS->getRealPath(*ExtRedirect, Output); - if (P && Redirection == RedirectKind::Fallthrough && - isFileNotFound(P, Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return ExternalFS->getRealPath(CanonicalPath, Output); - } - return P; - } + if (Optional ExternalRedirect = Result->getExternalRedirect()) + return ExternalFS->getRealPath(*ExternalRedirect, Output); - // If we found a DirectoryEntry, still fallthrough to the original path if - // allowed, because directories don't have a single external contents path. - if (Redirection == RedirectKind::Fallthrough) - return ExternalFS->getRealPath(CanonicalPath, Output); - return llvm::errc::invalid_argument; + // Otherwise we have a DirectoryEntry, just use its path + Output.assign(CanonicalPath); + return {}; } std::unique_ptr @@ -2606,9 +2403,6 @@ return createFileError(Buffer.getBufferIdentifier(), llvm::errc::invalid_argument); - // TODO: Remove after simplifying RedirectingFileSystem - Result->FS->setRedirection( - RedirectingFileSystem::RedirectKind::RedirectOnly); switch (Result->Redirection) { case YAMLParseResult::RedirectKind::Fallthrough: // Simple case - add on the FS diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -1832,30 +1832,21 @@ EXPECT_EQ(0, NumDiagnostics); } -TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) { +TEST_F(VFSFromYAMLTest, RelativeVFSPathMiss) { IntrusiveRefCntPtr BaseFS( new vfs::InMemoryFileSystem); BaseFS->addFile("//root/foo/a", 0, MemoryBuffer::getMemBuffer("contents of a")); ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo")); + auto RemappedFS = vfs::RedirectingFileSystem::create( {}, /*UseExternalNames=*/false, *BaseFS); - auto OpenedF = RemappedFS->openFileForRead("a"); + auto OpenedF = BaseFS->openFileForRead("a"); ASSERT_FALSE(OpenedF.getError()); - llvm::ErrorOr Name = (*OpenedF)->getName(); - ASSERT_FALSE(Name.getError()); - EXPECT_EQ("a", Name.get()); - auto OpenedS = (*OpenedF)->status(); - ASSERT_FALSE(OpenedS.getError()); - EXPECT_EQ("a", OpenedS->getName()); - EXPECT_FALSE(OpenedS->IsVFSMapped); - - auto DirectS = RemappedFS->status("a"); - ASSERT_FALSE(DirectS.getError()); - EXPECT_EQ("a", DirectS->getName()); - EXPECT_FALSE(DirectS->IsVFSMapped); + OpenedF = RemappedFS->openFileForRead("a"); + ASSERT_TRUE(OpenedF.getError()); EXPECT_EQ(0, NumDiagnostics); } @@ -3181,6 +3172,77 @@ Output); } +TEST(RedirectingFileSystemTest, Basic) { + auto Real = makeIntrusiveRefCnt(); + Real->addRegularFile("/real/f"); + + SmallVector> Mappings = { + {"/vfs/a", "/real/f"}}; + auto FS = vfs::RedirectingFileSystem::create(Mappings, true, *Real); + ASSERT_TRUE(FS); + + EXPECT_PATHS(*FS, "/vfs/a", "/real/f", "/real/f"); + + // Should not check ExternalFS + auto S = FS->status("/real/f"); + ASSERT_TRUE(S.getError()); + + FS = vfs::RedirectingFileSystem::create(Mappings, false, *Real); + ASSERT_TRUE(FS); + + // Returning the original path for realpath doesn't make sense, so expect the + // external here + EXPECT_PATHS(*FS, "/vfs/a", "/vfs/a", "/real/f"); +} + +// Check that a mapped directory and its parents all return a status with the +// correct path +TEST(RedirectingFileSystemTest, DirectoryStatus) { + auto Real = makeIntrusiveRefCnt(); + Real->addDirectory("/real"); + + SmallVector> Mappings = { + {"/a/b/c", "/real"}}; + auto FS = vfs::RedirectingFileSystem::create(Mappings, true, *Real); + ASSERT_TRUE(FS); + + auto S = FS->status("/a"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a", S->getName()); + + S = FS->status("/a/b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a/b", S->getName()); + + S = FS->status("/a/b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/real", S->getName()); + + FS->setCurrentWorkingDirectory("/a"); + + S = FS->status("b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a/b", S->getName()); + + S = FS->status("b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/real", S->getName()); + + FS = vfs::RedirectingFileSystem::create(Mappings, false, *Real); + ASSERT_TRUE(FS); + + // External names is false, use the passed in path instead + FS->setCurrentWorkingDirectory("/a"); + + S = FS->status("b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("b", S->getName()); + + S = FS->status("b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("b/c", S->getName()); +} + static std::unique_ptr getVFSOrNull(ArrayRef YAMLOverlays, IntrusiveRefCntPtr ExternalFS) {