Skip to content

Commit 3ef33e6

Browse files
committed
[VirtualFileSystem] Support directory entries in the YAMLVFSWriter
The current implementation of the JSONWriter does not support writing out directory entries. Earlier today I added a unit test to illustrate the problem. When an entry is added to the YAMLVFSWriter and the path is a directory, it will incorrectly emit the directory as a file, and any files inside that directory will not be found by the VFS. It's possible to partially work around the issue by only adding "leaf nodes" (files) to the YAMLVFSWriter. However, this doesn't work for representing empty directories. This is a problem for clients of the VFS that want to iterate over a directory. The directory not being there is not the same as the directory being empty. This is not just a hypothetical problem. The FileCollector for example does not differentiate between file and directory paths. I temporarily worked around the issue for LLDB by ignoring directories, but I suspect this will prove problematic sooner rather than later. This patch fixes the issue by extending the JSONWriter to support writing out directory entries. We store whether an entry should be emitted as a file or directory. Differential revision: https://reviews.llvm.org/D76670
1 parent 0fca766 commit 3ef33e6

File tree

3 files changed

+39
-22
lines changed

3 files changed

+39
-22
lines changed

Diff for: llvm/include/llvm/Support/VirtualFileSystem.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -506,10 +506,12 @@ getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
506506

507507
struct YAMLVFSEntry {
508508
template <typename T1, typename T2>
509-
YAMLVFSEntry(T1 &&VPath, T2 &&RPath)
510-
: VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
509+
YAMLVFSEntry(T1 &&VPath, T2 &&RPath, bool IsDirectory = false)
510+
: VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)),
511+
IsDirectory(IsDirectory) {}
511512
std::string VPath;
512513
std::string RPath;
514+
bool IsDirectory = false;
513515
};
514516

515517
class VFSFromYamlDirIterImpl;
@@ -771,10 +773,13 @@ class YAMLVFSWriter {
771773
Optional<bool> UseExternalNames;
772774
std::string OverlayDir;
773775

776+
void addEntry(StringRef VirtualPath, StringRef RealPath, bool IsDirectory);
777+
774778
public:
775779
YAMLVFSWriter() = default;
776780

777781
void addFileMapping(StringRef VirtualPath, StringRef RealPath);
782+
void addDirectoryMapping(StringRef VirtualPath, StringRef RealPath);
778783

779784
void setCaseSensitivity(bool CaseSensitive) {
780785
IsCaseSensitive = CaseSensitive;

Diff for: llvm/lib/Support/VirtualFileSystem.cpp

+26-8
Original file line numberDiff line numberDiff line change
@@ -1912,11 +1912,21 @@ UniqueID vfs::getNextVirtualUniqueID() {
19121912
return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
19131913
}
19141914

1915-
void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1915+
void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
1916+
bool IsDirectory) {
19161917
assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
19171918
assert(sys::path::is_absolute(RealPath) && "real path not absolute");
19181919
assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1919-
Mappings.emplace_back(VirtualPath, RealPath);
1920+
Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
1921+
}
1922+
1923+
void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1924+
addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
1925+
}
1926+
1927+
void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
1928+
StringRef RealPath) {
1929+
addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
19201930
}
19211931

19221932
namespace {
@@ -2017,7 +2027,10 @@ void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
20172027

20182028
if (!Entries.empty()) {
20192029
const YAMLVFSEntry &Entry = Entries.front();
2020-
startDirectory(path::parent_path(Entry.VPath));
2030+
bool first_entry_is_directory = Entry.IsDirectory;
2031+
StringRef Dir =
2032+
first_entry_is_directory ? Entry.VPath : path::parent_path(Entry.VPath);
2033+
startDirectory(Dir);
20212034

20222035
StringRef RPath = Entry.RPath;
20232036
if (UseOverlayRelative) {
@@ -2027,13 +2040,18 @@ void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
20272040
RPath = RPath.slice(OverlayDirLen, RPath.size());
20282041
}
20292042

2030-
writeEntry(path::filename(Entry.VPath), RPath);
2043+
if (!first_entry_is_directory)
2044+
writeEntry(path::filename(Entry.VPath), RPath);
20312045

20322046
for (const auto &Entry : Entries.slice(1)) {
2033-
StringRef Dir = path::parent_path(Entry.VPath);
2034-
if (Dir == DirStack.back())
2035-
OS << ",\n";
2036-
else {
2047+
StringRef Dir =
2048+
Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
2049+
if (Dir == DirStack.back()) {
2050+
if (!first_entry_is_directory) {
2051+
OS << ",\n";
2052+
first_entry_is_directory = false;
2053+
}
2054+
} else {
20372055
while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
20382056
OS << "\n";
20392057
endDirectory();

Diff for: llvm/unittests/Support/VirtualFileSystemTest.cpp

+6-12
Original file line numberDiff line numberDiff line change
@@ -2200,20 +2200,14 @@ TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {
22002200
ScopedDir _g(TestDirectory + "/g");
22012201
ScopedFile _h(TestDirectory + "/h", "");
22022202

2203-
// This test exposes a bug/shortcoming in the YAMLVFSWriter. Below we call
2204-
// addFileMapping for _a and _e, which causes _ab and _ef not to exists in
2205-
// the deserialized file system, because _a and _e got emitted as regular
2206-
// files. The counter example is _c, if we only call addFileMapping for _cd,
2207-
// things work as expected.
2208-
22092203
vfs::YAMLVFSWriter VFSWriter;
2210-
VFSWriter.addFileMapping(_a.Path, "//root/a");
2204+
VFSWriter.addDirectoryMapping(_a.Path, "//root/a");
22112205
VFSWriter.addFileMapping(_ab.Path, "//root/a/b");
22122206
VFSWriter.addFileMapping(_cd.Path, "//root/c/d");
2213-
VFSWriter.addFileMapping(_e.Path, "//root/e");
2214-
VFSWriter.addFileMapping(_ef.Path, "//root/e/f");
2207+
VFSWriter.addDirectoryMapping(_e.Path, "//root/e");
2208+
VFSWriter.addDirectoryMapping(_ef.Path, "//root/e/f");
22152209
VFSWriter.addFileMapping(_g.Path, "//root/g");
2216-
VFSWriter.addFileMapping(_h.Path, "//root/h");
2210+
VFSWriter.addDirectoryMapping(_h.Path, "//root/h");
22172211

22182212
std::string Buffer;
22192213
raw_string_ostream OS(Buffer);
@@ -2236,11 +2230,11 @@ TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {
22362230
ASSERT_TRUE(FS.get() != nullptr);
22372231

22382232
EXPECT_TRUE(FS->exists(_a.Path));
2239-
EXPECT_FALSE(FS->exists(_ab.Path)); // FIXME: See explanation above.
2233+
EXPECT_TRUE(FS->exists(_ab.Path));
22402234
EXPECT_TRUE(FS->exists(_c.Path));
22412235
EXPECT_TRUE(FS->exists(_cd.Path));
22422236
EXPECT_TRUE(FS->exists(_e.Path));
2243-
EXPECT_FALSE(FS->exists(_ef.Path)); // FIXME: See explanation above.
2237+
EXPECT_TRUE(FS->exists(_ef.Path));
22442238
EXPECT_TRUE(FS->exists(_g.Path));
22452239
EXPECT_TRUE(FS->exists(_h.Path));
22462240
}

0 commit comments

Comments
 (0)