nathawes updated this revision to Diff 317157.
nathawes edited the summary of this revision.
nathawes added a comment.
Moved the change combining OverlayFSDirIterImpl and VFSFromYamlDirIterImpl in a
single implementation into a separate NFC patch
(https://reviews.llvm.org/D94857)
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D94844/new/
https://reviews.llvm.org/D94844
Files:
clang/test/VFS/Inputs/vfsoverlay-directory-relative.yaml
clang/test/VFS/Inputs/vfsoverlay-directory.yaml
clang/test/VFS/directory.c
llvm/include/llvm/Support/VirtualFileSystem.h
llvm/lib/Support/VirtualFileSystem.cpp
llvm/unittests/Support/VirtualFileSystemTest.cpp
Index: llvm/unittests/Support/VirtualFileSystemTest.cpp
===================================================================
--- llvm/unittests/Support/VirtualFileSystemTest.cpp
+++ llvm/unittests/Support/VirtualFileSystemTest.cpp
@@ -1328,6 +1328,7 @@
TEST_F(VFSFromYAMLTest, MappedFiles) {
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+ Lower->addDirectory("//root/foo/bar");
Lower->addRegularFile("//root/foo/bar/a");
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
"{ 'roots': [\n"
@@ -1343,6 +1344,17 @@
" 'type': 'file',\n"
" 'name': 'file2',\n"
" 'external-contents': '//root/foo/b'\n"
+ " },\n"
+ " {\n"
+ " 'type': 'directory',\n"
+ " 'name': 'mappeddir',\n"
+ " 'external-contents': '//root/foo/bar'\n"
+ " },\n"
+ " {\n"
+ " 'type': 'directory',\n"
+ " 'name': 'mappeddir2',\n"
+ " 'use-external-name': false,\n"
+ " 'external-contents': '//root/foo/bar'\n"
" }\n"
" ]\n"
"}\n"
@@ -1374,18 +1386,102 @@
EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
EXPECT_TRUE(OpenedS->IsVFSMapped);
- // directory
+ // virtual directory
S = O->status("//root/");
ASSERT_FALSE(S.getError());
EXPECT_TRUE(S->isDirectory());
EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
+ // mapped directory
+ S = O->status("//root/mappeddir");
+ ASSERT_FALSE(S.getError());
+ EXPECT_TRUE(S->isDirectory());
+ EXPECT_TRUE(S->IsVFSMapped);
+ EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));
+
+ SLower = O->status("//root/foo/bar");
+ EXPECT_EQ("//root/foo/bar", SLower->getName());
+ EXPECT_TRUE(S->equivalent(*SLower));
+ EXPECT_FALSE(SLower->IsVFSMapped);
+
+ // file in mapped directory
+ S = O->status("//root/mappeddir/a");
+ ASSERT_FALSE(S.getError());
+ ASSERT_FALSE(S->isDirectory());
+ ASSERT_TRUE(S->IsVFSMapped);
+ ASSERT_EQ("//root/foo/bar/a", S->getName());
+
+ // file in mapped directory, with use-external-name=false
+ S = O->status("//root/mappeddir2/a");
+ ASSERT_FALSE(S.getError());
+ ASSERT_FALSE(S->isDirectory());
+ ASSERT_TRUE(S->IsVFSMapped);
+ ASSERT_EQ("//root/mappeddir2/a", S->getName());
+
+ // file contents in mapped directory
+ OpenedF = O->openFileForRead("//root/mappeddir/a");
+ ASSERT_FALSE(OpenedF.getError());
+ OpenedS = (*OpenedF)->status();
+ ASSERT_FALSE(OpenedS.getError());
+ EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
+ EXPECT_TRUE(OpenedS->IsVFSMapped);
+
+ // file contents in mapped directory, with use-external-name=false
+ OpenedF = O->openFileForRead("//root/mappeddir2/a");
+ ASSERT_FALSE(OpenedF.getError());
+ OpenedS = (*OpenedF)->status();
+ ASSERT_FALSE(OpenedS.getError());
+ EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());
+ EXPECT_TRUE(OpenedS->IsVFSMapped);
+
// broken mapping
EXPECT_EQ(O->status("//root/file2").getError(),
llvm::errc::no_such_file_or_directory);
EXPECT_EQ(0, NumDiagnostics);
}
+TEST_F(VFSFromYAMLTest, MappedRoot) {
+ IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+ Lower->addDirectory("//root/foo/bar");
+ Lower->addRegularFile("//root/foo/bar/a");
+ IntrusiveRefCntPtr<vfs::FileSystem> FS =
+ getFromYAMLString("{ 'roots': [\n"
+ "{\n"
+ " 'type': 'directory',\n"
+ " 'name': '//mappedroot/',\n"
+ " 'external-contents': '//root/foo/bar'\n"
+ "}\n"
+ "]\n"
+ "}",
+ Lower);
+ ASSERT_TRUE(FS.get() != nullptr);
+
+ IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
+ new vfs::OverlayFileSystem(Lower));
+ O->pushOverlay(FS);
+
+ // file
+ ErrorOr<vfs::Status> S = O->status("//mappedroot/a");
+ ASSERT_FALSE(S.getError());
+ EXPECT_EQ("//root/foo/bar/a", S->getName());
+ EXPECT_TRUE(S->IsVFSMapped);
+
+ ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
+ EXPECT_EQ("//root/foo/bar/a", SLower->getName());
+ EXPECT_TRUE(S->equivalent(*SLower));
+ EXPECT_FALSE(SLower->IsVFSMapped);
+
+ // file after opening
+ auto OpenedF = O->openFileForRead("//mappedroot/a");
+ ASSERT_FALSE(OpenedF.getError());
+ auto OpenedS = (*OpenedF)->status();
+ ASSERT_FALSE(OpenedS.getError());
+ EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
+ EXPECT_TRUE(OpenedS->IsVFSMapped);
+
+ EXPECT_EQ(0, NumDiagnostics);
+}
+
TEST_F(VFSFromYAMLTest, CaseInsensitive) {
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
Lower->addRegularFile("//root/foo/bar/a");
@@ -1542,7 +1638,17 @@
EXPECT_EQ(nullptr, FS.get());
FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
EXPECT_EQ(nullptr, FS.get());
- EXPECT_EQ(24, NumDiagnostics);
+
+ // both 'external-contents' and 'contents' specified
+ Lower->addDirectory("//root/external/dir");
+ FS = getFromYAMLString(
+ "{ 'roots':[ \n"
+ "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
+ " 'external-contents': '//root/external/dir'}]}",
+ Lower);
+ EXPECT_EQ(nullptr, FS.get());
+
+ EXPECT_EQ(25, NumDiagnostics);
}
TEST_F(VFSFromYAMLTest, UseExternalName) {
Index: llvm/lib/Support/VirtualFileSystem.cpp
===================================================================
--- llvm/lib/Support/VirtualFileSystem.cpp
+++ llvm/lib/Support/VirtualFileSystem.cpp
@@ -1058,6 +1058,8 @@
sys::fs::file_type Type = sys::fs::file_type::type_unknown;
switch ((*Current)->getKind()) {
case RedirectingFileSystem::EK_Directory:
+ LLVM_FALLTHROUGH;
+ case RedirectingFileSystem::EK_RemapDirectory:
Type = sys::fs::file_type::directory_file;
break;
case RedirectingFileSystem::EK_File:
@@ -1145,15 +1147,20 @@
directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
std::error_code &EC) {
- ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);
+ SmallString<256> ExternalRedirect;
+ ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir, ExternalRedirect);
+
if (!E) {
EC = E.getError();
if (shouldFallBackToExternalFS(EC))
return ExternalFS->dir_begin(Dir, EC);
return {};
}
- ErrorOr<Status> S = status(Dir, *E);
+
+ ErrorOr<Status> S = status(Dir, *E, ExternalRedirect);
if (!S) {
+ if (shouldFallBackToExternalFS(S.getError(), *E))
+ return ExternalFS->dir_begin(Dir, EC);
EC = S.getError();
return {};
}
@@ -1163,9 +1170,14 @@
return {};
}
- auto *D = cast<RedirectingFileSystem::DirectoryEntry>(*E);
- auto DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
- Dir, D->contents_begin(), D->contents_end(), EC));
+ directory_iterator DirIter;
+ if (auto *D = cast<RedirectingFileSystem::DirectoryEntry>(*E)) {
+ DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
+ Dir, D->contents_begin(), D->contents_end(), EC));
+ } else {
+ assert((*E)->getKind() == EK_RemapDirectory);
+ DirIter = ExternalFS->dir_begin(ExternalRedirect, EC);
+ }
if (!shouldUseExternalFS())
return DirIter;
@@ -1354,6 +1366,15 @@
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
break;
}
+ case RedirectingFileSystem::EK_RemapDirectory: {
+ assert(NewParentE && "Parent entry must exist");
+ auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
+ DE->addContent(
+ std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
+ Name, DR->getExternalContentsPath(), DR->getUseName()));
+ break;
+ }
case RedirectingFileSystem::EK_File: {
assert(NewParentE && "Parent entry must exist");
auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
@@ -1389,7 +1410,7 @@
SmallString<256> ExternalContentsPath;
SmallString<256> Name;
yaml::Node *NameValueNode = nullptr;
- auto UseExternalName = RedirectingFileSystem::FileEntry::NK_NotSet;
+ auto UseExternalName = RedirectingFileSystem::NK_NotSet;
RedirectingFileSystem::EntryKind Kind;
for (auto &I : *M) {
@@ -1472,8 +1493,8 @@
bool Val;
if (!parseScalarBool(I.getValue(), Val))
return nullptr;
- UseExternalName = Val ? RedirectingFileSystem::FileEntry::NK_External
- : RedirectingFileSystem::FileEntry::NK_Virtual;
+ UseExternalName = Val ? RedirectingFileSystem::NK_External
+ : RedirectingFileSystem::NK_Virtual;
} else {
llvm_unreachable("key missing from Keys");
}
@@ -1482,6 +1503,11 @@
if (Stream.failed())
return nullptr;
+ if (Kind == RedirectingFileSystem::EK_Directory &&
+ !ExternalContentsPath.empty()) {
+ Kind = RedirectingFileSystem::EK_RemapDirectory;
+ }
+
// check for missing keys
if (!HasContents) {
error(N, "missing key 'contents' or 'external-contents'");
@@ -1492,8 +1518,10 @@
// check invalid configuration
if (Kind == RedirectingFileSystem::EK_Directory &&
- UseExternalName != RedirectingFileSystem::FileEntry::NK_NotSet) {
- error(N, "'use-external-name' is not supported for directories");
+ ExternalContentsPath.empty() &&
+ UseExternalName != RedirectingFileSystem::NK_NotSet) {
+ error(N, "'use-external-name' is not supported for directories without "
+ "'external-contents'");
return nullptr;
}
@@ -1529,6 +1557,10 @@
Result = std::make_unique<RedirectingFileSystem::FileEntry>(
LastComponent, std::move(ExternalContentsPath), UseExternalName);
break;
+ case RedirectingFileSystem::EK_RemapDirectory:
+ Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
+ LastComponent, std::move(ExternalContentsPath), UseExternalName);
+ break;
case RedirectingFileSystem::EK_Directory:
Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
LastComponent, std::move(EntryArrayContents),
@@ -1739,8 +1771,8 @@
// Add the file.
auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
llvm::sys::path::filename(From), To,
- UseExternalNames ? RedirectingFileSystem::FileEntry::NK_External
- : RedirectingFileSystem::FileEntry::NK_Virtual);
+ UseExternalNames ? RedirectingFileSystem::NK_External
+ : RedirectingFileSystem::NK_Virtual);
ToEntry = NewFile.get();
cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
std::move(NewFile));
@@ -1749,14 +1781,17 @@
return FS;
}
-bool RedirectingFileSystem::shouldFallBackToExternalFS(
- std::error_code Error) const {
+bool RedirectingFileSystem::shouldFallBackToExternalFS(std::error_code Error,
+ Entry *SrcEntry) const {
+ if (SrcEntry && SrcEntry->getKind() != EK_RemapDirectory)
+ return false;
return shouldUseExternalFS() &&
Error == llvm::errc::no_such_file_or_directory;
};
ErrorOr<RedirectingFileSystem::Entry *>
-RedirectingFileSystem::lookupPath(const Twine &Path_) const {
+RedirectingFileSystem::lookupPath(const Twine &Path_,
+ SmallVectorImpl<char> &ExtRedirect) const {
SmallString<256> Path;
Path_.toVector(Path);
@@ -1775,7 +1810,7 @@
sys::path::const_iterator End = sys::path::end(Path);
for (const auto &Root : Roots) {
ErrorOr<RedirectingFileSystem::Entry *> Result =
- lookupPath(Start, End, Root.get());
+ lookupPath(Start, End, Root.get(), ExtRedirect);
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
@@ -1785,14 +1820,27 @@
ErrorOr<RedirectingFileSystem::Entry *>
RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
sys::path::const_iterator End,
- RedirectingFileSystem::Entry *From) const {
+ RedirectingFileSystem::Entry *From,
+ SmallVectorImpl<char> &ExtRedirect) const {
assert(!isTraversalComponent(*Start) &&
!isTraversalComponent(From->getName()) &&
"Paths should not contain traversal components");
- StringRef FromName = From->getName();
+ // If the matched entry is a remapped directory, sets ExtRedirect to the
+ // directory's external contents path plus any remaining path components.
+ auto SetExtRedirect = [&](RedirectingFileSystem::Entry *E) {
+ auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(From);
+ if (!DRE)
+ return;
+ StringRef ExternalPath = DRE->getExternalContentsPath();
+ if (!ExternalPath.empty()) {
+ ExtRedirect.assign(ExternalPath.begin(), ExternalPath.end());
+ sys::path::append(ExtRedirect, Start, End);
+ }
+ };
// Forward the search to the next component in case this is an empty one.
+ StringRef FromName = From->getName();
if (!FromName.empty()) {
if (!pathComponentMatches(*Start, FromName))
return make_error_code(llvm::errc::no_such_file_or_directory);
@@ -1801,22 +1849,27 @@
if (Start == End) {
// Match!
+ SetExtRedirect(From);
return From;
}
}
- auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(From);
- if (!DE)
+ if (isa<RedirectingFileSystem::FileEntry>(From))
return make_error_code(llvm::errc::not_a_directory);
+ if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From)) {
+ SetExtRedirect(From);
+ return From;
+ }
+
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
ErrorOr<RedirectingFileSystem::Entry *> Result =
- lookupPath(Start, End, DirEntry.get());
+ lookupPath(Start, End, DirEntry.get(), ExtRedirect);
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
-
return make_error_code(llvm::errc::no_such_file_or_directory);
}
@@ -1830,29 +1883,41 @@
}
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
- RedirectingFileSystem::Entry *E) {
+ RedirectingFileSystem::Entry *E,
+ StringRef ExternalRedirect) {
assert(E != nullptr);
- if (auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(E)) {
- ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
- assert(!S || S->getName() == F->getExternalContentsPath());
- if (S)
- return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
- *S);
- return S;
- } else { // directory
- auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
- return Status::copyWithNewName(DE->getStatus(), Path);
+ if (auto *RE = dyn_cast<RedirectingFileSystem::RemapEntry>(E)) {
+ if (ExternalRedirect.empty()) {
+ assert(RE->getKind() == RedirectingFileSystem::EK_File);
+ ExternalRedirect = RE->getExternalContentsPath();
+ }
+
+ auto S = ExternalFS->status(ExternalRedirect);
+ if (!S)
+ return S;
+ return getRedirectedFileStatus(Path, RE->useExternalName(UseExternalNames),
+ *S);
}
+
+ assert(ExternalRedirect.empty());
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
+ return Status::copyWithNewName(DE->getStatus(), Path);
}
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
- ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
+ SmallString<256> ExternalRedirect;
+ ErrorOr<RedirectingFileSystem::Entry *> Result =
+ lookupPath(Path, ExternalRedirect);
if (!Result) {
if (shouldFallBackToExternalFS(Result.getError()))
return ExternalFS->status(Path);
return Result.getError();
}
- return status(Path, *Result);
+
+ auto S = status(Path, *Result, ExternalRedirect);
+ if (!S && shouldFallBackToExternalFS(S.getError(), *Result))
+ S = ExternalFS->status(Path);
+ return S;
}
namespace {
@@ -1882,28 +1947,38 @@
ErrorOr<std::unique_ptr<File>>
RedirectingFileSystem::openFileForRead(const Twine &Path) {
- ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
+ SmallString<256> ExternalRedirect;
+ ErrorOr<RedirectingFileSystem::Entry *> E =
+ lookupPath(Path, ExternalRedirect);
if (!E) {
if (shouldFallBackToExternalFS(E.getError()))
return ExternalFS->openFileForRead(Path);
return E.getError();
}
- auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(*E);
- if (!F) // FIXME: errc::not_a_file?
+ auto *RE = dyn_cast<RedirectingFileSystem::RemapEntry>(*E);
+ if (!RE) // FIXME: errc::not_a_file?
return make_error_code(llvm::errc::invalid_argument);
- auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
- if (!Result)
+ if (ExternalRedirect.empty()) {
+ assert(RE->getKind() == EK_File);
+ ExternalRedirect = RE->getExternalContentsPath();
+ }
+
+ auto Result = ExternalFS->openFileForRead(ExternalRedirect);
+ if (!Result) {
+ if (shouldFallBackToExternalFS(Result.getError(), RE))
+ Result = ExternalFS->openFileForRead(Path);
return Result;
+ }
auto ExternalStatus = (*Result)->status();
if (!ExternalStatus)
return ExternalStatus.getError();
// FIXME: Update the status with the name and VFSMapped.
- Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
- *ExternalStatus);
+ Status S = getRedirectedFileStatus(
+ Path, RE->useExternalName(UseExternalNames), *ExternalStatus);
return std::unique_ptr<File>(
std::make_unique<FileWithFixedStatus>(std::move(*Result), S));
}
@@ -1911,16 +1986,27 @@
std::error_code
RedirectingFileSystem::getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const {
- ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
+ SmallString<256> ExternalRedirect;
+ ErrorOr<RedirectingFileSystem::Entry *> Result =
+ lookupPath(Path, ExternalRedirect);
if (!Result) {
if (shouldFallBackToExternalFS(Result.getError()))
return ExternalFS->getRealPath(Path, Output);
return Result.getError();
}
- if (auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(*Result)) {
- return ExternalFS->getRealPath(F->getExternalContentsPath(), Output);
+ if (auto *RE = dyn_cast<RedirectingFileSystem::RemapEntry>(*Result)) {
+ if (ExternalRedirect.empty()) {
+ assert(RE->getKind() == EK_File);
+ ExternalRedirect = RE->getExternalContentsPath();
+ }
+ auto P = ExternalFS->getRealPath(ExternalRedirect, Output);
+ if (!P && shouldFallBackToExternalFS(P, RE)) {
+ return ExternalFS->getRealPath(Path, Output);
+ }
+ return P;
}
+
// Even if there is a directory entry, fall back to ExternalFS if allowed,
// because directories don't have a single external contents path.
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
@@ -1950,16 +2036,24 @@
getVFSEntries(SubEntry.get(), Path, Entries);
Path.pop_back();
}
- return;
+ } else if (Kind == RedirectingFileSystem::EK_RemapDirectory) {
+ auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
+ assert(DR && "Must be a remap directory");
+ SmallString<128> VPath;
+ for (auto &Comp : Path)
+ llvm::sys::path::append(VPath, Comp);
+ Entries.push_back(
+ YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
+ } else {
+ assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
+ auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
+ assert(FE && "Must be a file");
+ SmallString<128> VPath;
+ for (auto &Comp : Path)
+ llvm::sys::path::append(VPath, Comp);
+ Entries.push_back(
+ YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
}
-
- assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
- auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
- assert(FE && "Must be a file");
- SmallString<128> VPath;
- for (auto &Comp : Path)
- llvm::sys::path::append(VPath, Comp);
- Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
}
void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
@@ -1971,7 +2065,9 @@
std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
std::move(ExternalFS));
- ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/");
+ SmallString<256> ExternalRedirect;
+ ErrorOr<RedirectingFileSystem::Entry *> RootE =
+ VFS->lookupPath("/", ExternalRedirect);
if (!RootE)
return;
SmallVector<StringRef, 8> Components;
Index: llvm/include/llvm/Support/VirtualFileSystem.h
===================================================================
--- llvm/include/llvm/Support/VirtualFileSystem.h
+++ llvm/include/llvm/Support/VirtualFileSystem.h
@@ -521,8 +521,10 @@
/// A virtual file system parsed from a YAML file.
///
-/// Currently, this class allows creating virtual directories and mapping
-/// virtual file paths to existing external files, available in \c ExternalFS.
+/// Currently, this class allows creating virtual files and directories. Virtual
+/// files map to existing external files in \c ExternalFS, and virtual
+/// directories may either map to existing directories in \c ExternalFS or list
+/// their contents in the form of other virtual directories and/or files.
///
/// The basic structure of the parsed file is:
/// \verbatim
@@ -541,7 +543,7 @@
/// 'overlay-relative': <boolean, default=false>
/// 'fallthrough': <boolean, default=true>
///
-/// Virtual directories are represented as
+/// Virtual directories that list their contents are represented as
/// \verbatim
/// {
/// 'type': 'directory',
@@ -550,7 +552,7 @@
/// }
/// \endverbatim
///
-/// The default attributes for virtual directories are:
+/// The default attributes for such virtual directories are:
/// \verbatim
/// MTime = now() when created
/// Perms = 0777
@@ -559,6 +561,17 @@
/// UniqueID = unspecified unique value
/// \endverbatim
///
+/// Re-mapped directories are represented as
+/// /// \verbatim
+/// {
+/// 'type': 'directory',
+/// 'name': <string>,
+/// 'external-contents': <path to external directory>
+/// }
+/// \endverbatim
+///
+/// and inherit their attributes from the external directory.
+///
/// Re-mapped files are represented as
/// \verbatim
/// {
@@ -569,14 +582,15 @@
/// }
/// \endverbatim
///
-/// and inherit their attributes from the external contents.
+/// and similarly inherit their attributes from their external contents.
///
-/// In both cases, the 'name' field may contain multiple path components (e.g.
-/// /path/to/file). However, any directory that contains more than one child
-/// must be uniquely represented by a directory entry.
+/// For both files and directories, the 'name' field may contain multiple path
+/// components (e.g. /path/to/file). However, any directory that contains more
+/// than one child must be uniquely represented by a directory entry.
class RedirectingFileSystem : public vfs::FileSystem {
public:
- enum EntryKind { EK_Directory, EK_File };
+ enum EntryKind { EK_Directory, EK_RemapDirectory, EK_File };
+ enum NameKind { NK_NotSet, NK_External, NK_Virtual };
/// A single file or directory in the VFS.
class Entry {
@@ -591,6 +605,41 @@
EntryKind getKind() const { return Kind; }
};
+ /// A file or directory in the vfs that is mapped to a file or directory in
+ /// the external filesystem.
+ class RemapEntry : public Entry {
+ std::string ExternalContentsPath;
+ NameKind UseName;
+
+ protected:
+ RemapEntry(EntryKind K, StringRef Name, StringRef ExternalContentsPath,
+ NameKind UseName)
+ : Entry(K, Name), ExternalContentsPath(ExternalContentsPath),
+ UseName(UseName) {}
+
+ public:
+ NameKind getUseName() const { return UseName; }
+ StringRef getExternalContentsPath() const { return ExternalContentsPath; }
+
+ /// whether to use the external path as the name for this file or directory.
+ bool useExternalName(bool GlobalUseExternalName) const {
+ return UseName == NK_NotSet ? GlobalUseExternalName
+ : (UseName == NK_External);
+ }
+
+ static bool classof(const Entry *E) {
+ switch (E->getKind()) {
+ case EK_RemapDirectory:
+ LLVM_FALLTHROUGH;
+ case EK_File:
+ return true;
+ case EK_Directory:
+ return false;
+ }
+ }
+ };
+
+ /// A directory in the vfs with explicitly specified contents.
class DirectoryEntry : public Entry {
std::vector<std::unique_ptr<Entry>> Contents;
Status S;
@@ -622,28 +671,24 @@
static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
};
- class FileEntry : public Entry {
+ /// A directory in the vfs that maps to a directory in the external file
+ /// system.
+ class DirectoryRemapEntry : public RemapEntry {
public:
- enum NameKind { NK_NotSet, NK_External, NK_Virtual };
+ DirectoryRemapEntry(StringRef Name, StringRef ExternalContentsPath,
+ NameKind UseName)
+ : RemapEntry(EK_RemapDirectory, Name, ExternalContentsPath, UseName) {}
- private:
- std::string ExternalContentsPath;
- NameKind UseName;
+ static bool classof(const Entry *E) {
+ return E->getKind() == EK_RemapDirectory;
+ }
+ };
+ /// A file in the vfs that maps to a file in the external file system.
+ class FileEntry : public RemapEntry {
public:
FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
- : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
- UseName(UseName) {}
-
- StringRef getExternalContentsPath() const { return ExternalContentsPath; }
-
- /// whether to use the external path as the name for this file.
- bool useExternalName(bool GlobalUseExternalName) const {
- return UseName == NK_NotSet ? GlobalUseExternalName
- : (UseName == NK_External);
- }
-
- NameKind getUseName() const { return UseName; }
+ : RemapEntry(EK_File, Name, ExternalContentsPath, UseName) {}
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
};
@@ -657,8 +702,9 @@
}
/// Whether to fall back to the external file system when an operation fails
- /// with the given error code.
- bool shouldFallBackToExternalFS(std::error_code Error) const;
+ /// with the given error code on a path associated with the provided entry.
+ bool shouldFallBackToExternalFS(std::error_code Error,
+ Entry *SrcEntry = nullptr) const;
// In a RedirectingFileSystem, keys can be specified in Posix or Windows
// style (or even a mixture of both), so this comparison helper allows
@@ -719,15 +765,17 @@
/// Looks up the path <tt>[Start, End)</tt> in \p From, possibly
/// recursing into the contents of \p From if it is a directory.
ErrorOr<Entry *> lookupPath(llvm::sys::path::const_iterator Start,
- llvm::sys::path::const_iterator End,
- Entry *From) const;
+ llvm::sys::path::const_iterator End, Entry *From,
+ SmallVectorImpl<char> &ExternalRedirect) const;
/// Get the status of a given an \c Entry.
- ErrorOr<Status> status(const Twine &Path, Entry *E);
+ ErrorOr<Status> status(const Twine &Path, Entry *E,
+ StringRef ExternalRedirect);
public:
/// Looks up \p Path in \c Roots.
- ErrorOr<Entry *> lookupPath(const Twine &Path) const;
+ ErrorOr<Entry *> lookupPath(const Twine &Path,
+ SmallVectorImpl<char> &ExternalRedirect) const;
/// Parses \p Buffer, which is expected to be in YAML format and
/// returns a virtual file system representing its contents.
Index: clang/test/VFS/directory.c
===================================================================
--- /dev/null
+++ clang/test/VFS/directory.c
@@ -0,0 +1,48 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/Underlying
+// RUN: mkdir -p %t/Overlay
+// RUN: mkdir -p %t/Middle
+// RUN: echo '// B.h in Underlying' > %t/Underlying/B.h
+// RUN: echo '#ifdef NESTED' >> %t/Underlying/B.h
+// RUN: echo '#include "C.h"' >> %t/Underlying/B.h
+// RUN: echo '#endif' >> %t/Underlying/B.h
+// RUN: echo '// C.h in Underlying' > %t/Underlying/C.h
+// RUN: echo '// C.h in Middle' > %t/Middle/C.h
+// RUN: echo '// C.h in Overlay' > %t/Overlay/C.h
+
+// 1) Underlying -> Overlay (C.h found, B.h falling back to Underlying)
+// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}/Overlay@g" -e "s@OUT_DIR@%{/t:regex_replacement}/Underlying@g" %S/Inputs/vfsoverlay-directory.yaml > %t/vfs.yaml
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=DIRECT %s
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -fsyntax-only -DNESTED -E -C %s 2>&1 | FileCheck --check-prefix=DIRECT %s
+// RUN: sed -e "s@INPUT_DIR@Overlay@g" -e "s@OUT_DIR@%{/t:regex_replacement}/Underlying@g" %S/Inputs/vfsoverlay-directory-relative.yaml > %t/vfs-relative.yaml
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs-relative.yaml -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=DIRECT %s
+
+// DIRECT: {{^}}// B.h in Underlying
+// DIRECT: {{^}}// C.h in Overlay
+
+// 2) Underlying -> Middle -> Overlay (C.h found, B.h falling back to Underlying)
+// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}/Overlay@g" -e "s@OUT_DIR@%{/t:regex_replacement}/Middle@g" %S/Inputs/vfsoverlay-directory.yaml > %t/vfs.yaml
+// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}/Middle@g" -e "s@OUT_DIR@%{/t:regex_replacement}/Underlying@g" %S/Inputs/vfsoverlay-directory.yaml > %t/vfs2.yaml
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -ivfsoverlay %t/vfs2.yaml -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=DIRECT %s
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -ivfsoverlay %t/vfs2.yaml -DNESTED -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=DIRECT %s
+
+// Same as direct above
+
+// 3) Underlying -> Middle -> Overlay (C.h falling back to Middle, B.h falling back to Underlying)
+// RUN: rm -f %t/Overlay/C.h
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -ivfsoverlay %t/vfs2.yaml -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=FALLBACK %s
+
+// FALLBACK: {{^}}// B.h in Underlying
+// FALLBACK: {{^}}// C.h in Middle
+
+// 3) Underlying -> Middle -> Overlay (C.h falling back to Underlying, B.h falling back to Underlying)
+// RUN: rm -f %t/Middle/C.h
+// RUN: %clang_cc1 -Werror -I %t/Underlying -ivfsoverlay %t/vfs.yaml -ivfsoverlay %t/vfs2.yaml -fsyntax-only -E -C %s 2>&1 | FileCheck --check-prefix=FALLBACK2 %s
+
+// FALLBACK2: {{^}}// B.h in Underlying
+// FALLBACK2: {{^}}// C.h in Underlying
+
+#include "B.h"
+#ifndef NESTED
+#include "C.h"
+#endif
Index: clang/test/VFS/Inputs/vfsoverlay-directory.yaml
===================================================================
--- /dev/null
+++ clang/test/VFS/Inputs/vfsoverlay-directory.yaml
@@ -0,0 +1,10 @@
+{
+ 'version': 0,
+ 'fallthrough': true,
+ 'roots': [
+ { 'name': 'OUT_DIR',
+ 'type': 'directory',
+ 'external-contents': 'INPUT_DIR'
+ }
+ ]
+}
Index: clang/test/VFS/Inputs/vfsoverlay-directory-relative.yaml
===================================================================
--- /dev/null
+++ clang/test/VFS/Inputs/vfsoverlay-directory-relative.yaml
@@ -0,0 +1,11 @@
+{
+ 'version': 0,
+ 'fallthrough': true,
+ 'overlay-relative': true,
+ 'roots': [
+ { 'name': 'OUT_DIR',
+ 'type': 'directory',
+ 'external-contents': 'INPUT_DIR'
+ }
+ ]
+}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits