mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
[VFS] Add support to RedirectingFileSystem for mapping a virtual directory to one in the external FS.
Previously file entries in the -ivfsoverlay yaml could map to a file in the external file system, but directories had to list their contents in the form of other file entries or directories. Allowing directory entries to map to a directory in the external file system makes it possible to present an external directory's contents in a different location and (in combination with the 'fallthrough' option) overlay one directory's contents on top of another. rdar://problem/72485443 Differential Revision: https://reviews.llvm.org/D94844
This commit is contained in:
parent
e420343cf4
commit
574038ba44
@ -521,8 +521,10 @@ class RedirectingFileSystemParser;
|
|||||||
|
|
||||||
/// A virtual file system parsed from a YAML file.
|
/// A virtual file system parsed from a YAML file.
|
||||||
///
|
///
|
||||||
/// Currently, this class allows creating virtual directories and mapping
|
/// Currently, this class allows creating virtual files and directories. Virtual
|
||||||
/// virtual file paths to existing external files, available in \c ExternalFS.
|
/// 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:
|
/// The basic structure of the parsed file is:
|
||||||
/// \verbatim
|
/// \verbatim
|
||||||
@ -541,7 +543,7 @@ class RedirectingFileSystemParser;
|
|||||||
/// 'overlay-relative': <boolean, default=false>
|
/// 'overlay-relative': <boolean, default=false>
|
||||||
/// 'fallthrough': <boolean, default=true>
|
/// 'fallthrough': <boolean, default=true>
|
||||||
///
|
///
|
||||||
/// Virtual directories are represented as
|
/// Virtual directories that list their contents are represented as
|
||||||
/// \verbatim
|
/// \verbatim
|
||||||
/// {
|
/// {
|
||||||
/// 'type': 'directory',
|
/// 'type': 'directory',
|
||||||
@ -550,7 +552,7 @@ class RedirectingFileSystemParser;
|
|||||||
/// }
|
/// }
|
||||||
/// \endverbatim
|
/// \endverbatim
|
||||||
///
|
///
|
||||||
/// The default attributes for virtual directories are:
|
/// The default attributes for such virtual directories are:
|
||||||
/// \verbatim
|
/// \verbatim
|
||||||
/// MTime = now() when created
|
/// MTime = now() when created
|
||||||
/// Perms = 0777
|
/// Perms = 0777
|
||||||
@ -559,24 +561,45 @@ class RedirectingFileSystemParser;
|
|||||||
/// UniqueID = unspecified unique value
|
/// UniqueID = unspecified unique value
|
||||||
/// \endverbatim
|
/// \endverbatim
|
||||||
///
|
///
|
||||||
|
/// When a path prefix matches such a directory, the next component in the path
|
||||||
|
/// is matched against the entries in the 'contents' array.
|
||||||
|
///
|
||||||
|
/// Re-mapped directories, on the other hand, are represented as
|
||||||
|
/// /// \verbatim
|
||||||
|
/// {
|
||||||
|
/// 'type': 'directory-remap',
|
||||||
|
/// 'name': <string>,
|
||||||
|
/// 'use-external-name': <boolean>, # Optional
|
||||||
|
/// 'external-contents': <path to external directory>
|
||||||
|
/// }
|
||||||
|
/// \endverbatim
|
||||||
|
///
|
||||||
|
/// and inherit their attributes from the external directory. When a path
|
||||||
|
/// prefix matches such an entry, the unmatched components are appended to the
|
||||||
|
/// 'external-contents' path, and the resulting path is looked up in the
|
||||||
|
/// external file system instead.
|
||||||
|
///
|
||||||
/// Re-mapped files are represented as
|
/// Re-mapped files are represented as
|
||||||
/// \verbatim
|
/// \verbatim
|
||||||
/// {
|
/// {
|
||||||
/// 'type': 'file',
|
/// 'type': 'file',
|
||||||
/// 'name': <string>,
|
/// 'name': <string>,
|
||||||
/// 'use-external-name': <boolean> # Optional
|
/// 'use-external-name': <boolean>, # Optional
|
||||||
/// 'external-contents': <path to external file>
|
/// 'external-contents': <path to external file>
|
||||||
/// }
|
/// }
|
||||||
/// \endverbatim
|
/// \endverbatim
|
||||||
///
|
///
|
||||||
/// and inherit their attributes from the external contents.
|
/// Their attributes and file contents are determined by looking up the file at
|
||||||
|
/// their 'external-contents' path in the external file system.
|
||||||
///
|
///
|
||||||
/// In both cases, the 'name' field may contain multiple path components (e.g.
|
/// For 'file', 'directory' and 'directory-remap' entries the 'name' field may
|
||||||
/// /path/to/file). However, any directory that contains more than one child
|
/// contain multiple path components (e.g. /path/to/file). However, any
|
||||||
/// must be uniquely represented by a directory entry.
|
/// directory in such a path that contains more than one child must be uniquely
|
||||||
|
/// represented by a 'directory' entry.
|
||||||
class RedirectingFileSystem : public vfs::FileSystem {
|
class RedirectingFileSystem : public vfs::FileSystem {
|
||||||
public:
|
public:
|
||||||
enum EntryKind { EK_Directory, EK_File };
|
enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File };
|
||||||
|
enum NameKind { NK_NotSet, NK_External, NK_Virtual };
|
||||||
|
|
||||||
/// A single file or directory in the VFS.
|
/// A single file or directory in the VFS.
|
||||||
class Entry {
|
class Entry {
|
||||||
@ -591,6 +614,7 @@ public:
|
|||||||
EntryKind getKind() const { return Kind; }
|
EntryKind getKind() const { return Kind; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A directory in the vfs with explicitly specified contents.
|
||||||
class DirectoryEntry : public Entry {
|
class DirectoryEntry : public Entry {
|
||||||
std::vector<std::unique_ptr<Entry>> Contents;
|
std::vector<std::unique_ptr<Entry>> Contents;
|
||||||
Status S;
|
Status S;
|
||||||
@ -622,22 +646,22 @@ public:
|
|||||||
static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
|
static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileEntry : public Entry {
|
/// A file or directory in the vfs that is mapped to a file or directory in
|
||||||
public:
|
/// the external filesystem.
|
||||||
enum NameKind { NK_NotSet, NK_External, NK_Virtual };
|
class RemapEntry : public Entry {
|
||||||
|
|
||||||
private:
|
|
||||||
std::string ExternalContentsPath;
|
std::string ExternalContentsPath;
|
||||||
NameKind UseName;
|
NameKind UseName;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
|
RemapEntry(EntryKind K, StringRef Name, StringRef ExternalContentsPath,
|
||||||
: Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
|
NameKind UseName)
|
||||||
|
: Entry(K, Name), ExternalContentsPath(ExternalContentsPath),
|
||||||
UseName(UseName) {}
|
UseName(UseName) {}
|
||||||
|
|
||||||
|
public:
|
||||||
StringRef getExternalContentsPath() const { return ExternalContentsPath; }
|
StringRef getExternalContentsPath() const { return ExternalContentsPath; }
|
||||||
|
|
||||||
/// whether to use the external path as the name for this file.
|
/// Whether to use the external path as the name for this file or directory.
|
||||||
bool useExternalName(bool GlobalUseExternalName) const {
|
bool useExternalName(bool GlobalUseExternalName) const {
|
||||||
return UseName == NK_NotSet ? GlobalUseExternalName
|
return UseName == NK_NotSet ? GlobalUseExternalName
|
||||||
: (UseName == NK_External);
|
: (UseName == NK_External);
|
||||||
@ -645,9 +669,67 @@ public:
|
|||||||
|
|
||||||
NameKind getUseName() const { return UseName; }
|
NameKind getUseName() const { return UseName; }
|
||||||
|
|
||||||
|
static bool classof(const Entry *E) {
|
||||||
|
switch (E->getKind()) {
|
||||||
|
case EK_DirectoryRemap:
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case EK_File:
|
||||||
|
return true;
|
||||||
|
case EK_Directory:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A directory in the vfs that maps to a directory in the external file
|
||||||
|
/// system.
|
||||||
|
class DirectoryRemapEntry : public RemapEntry {
|
||||||
|
public:
|
||||||
|
DirectoryRemapEntry(StringRef Name, StringRef ExternalContentsPath,
|
||||||
|
NameKind UseName)
|
||||||
|
: RemapEntry(EK_DirectoryRemap, Name, ExternalContentsPath, UseName) {}
|
||||||
|
|
||||||
|
static bool classof(const Entry *E) {
|
||||||
|
return E->getKind() == EK_DirectoryRemap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
: RemapEntry(EK_File, Name, ExternalContentsPath, UseName) {}
|
||||||
|
|
||||||
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
|
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Represents the result of a path lookup into the RedirectingFileSystem.
|
||||||
|
struct LookupResult {
|
||||||
|
/// The entry the looked-up path corresponds to.
|
||||||
|
Entry *E;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// When the found Entry is a DirectoryRemapEntry, stores the path in the
|
||||||
|
/// external file system that the looked-up path in the virtual file system
|
||||||
|
// corresponds to.
|
||||||
|
Optional<std::string> ExternalRedirect;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LookupResult(Entry *E, sys::path::const_iterator Start,
|
||||||
|
sys::path::const_iterator End);
|
||||||
|
|
||||||
|
/// If the found Entry maps the the input path to a path in the external
|
||||||
|
/// file system (i.e. it is a FileEntry or DirectoryRemapEntry), returns
|
||||||
|
/// that path.
|
||||||
|
Optional<StringRef> getExternalRedirect() const {
|
||||||
|
if (isa<DirectoryRemapEntry>(E))
|
||||||
|
return StringRef(*ExternalRedirect);
|
||||||
|
if (auto *FE = dyn_cast<FileEntry>(E))
|
||||||
|
return FE->getExternalContentsPath();
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class RedirectingFSDirIterImpl;
|
friend class RedirectingFSDirIterImpl;
|
||||||
friend class RedirectingFileSystemParser;
|
friend class RedirectingFileSystemParser;
|
||||||
@ -660,8 +742,8 @@ private:
|
|||||||
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
|
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
|
||||||
|
|
||||||
/// Whether to fall back to the external file system when an operation fails
|
/// Whether to fall back to the external file system when an operation fails
|
||||||
/// with the given error code.
|
/// with the given error code on a path associated with the provided Entry.
|
||||||
bool shouldFallBackToExternalFS(std::error_code EC) const;
|
bool shouldFallBackToExternalFS(std::error_code EC, Entry *E = nullptr) const;
|
||||||
|
|
||||||
// In a RedirectingFileSystem, keys can be specified in Posix or Windows
|
// In a RedirectingFileSystem, keys can be specified in Posix or Windows
|
||||||
// style (or even a mixture of both), so this comparison helper allows
|
// style (or even a mixture of both), so this comparison helper allows
|
||||||
@ -716,18 +798,22 @@ private:
|
|||||||
|
|
||||||
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS);
|
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS);
|
||||||
|
|
||||||
/// Looks up the path <tt>[Start, End)</tt> in \p From, possibly
|
/// Looks up the path <tt>[Start, End)</tt> in \p From, possibly recursing
|
||||||
/// recursing into the contents of \p From if it is a directory.
|
/// into the contents of \p From if it is a directory. Returns a LookupResult
|
||||||
ErrorOr<Entry *> lookupPath(llvm::sys::path::const_iterator Start,
|
/// giving the matched entry and, if that entry is a FileEntry or
|
||||||
llvm::sys::path::const_iterator End,
|
/// DirectoryRemapEntry, the path it redirects to in the external file system.
|
||||||
Entry *From) const;
|
ErrorOr<LookupResult> lookupPathImpl(llvm::sys::path::const_iterator Start,
|
||||||
|
llvm::sys::path::const_iterator End,
|
||||||
|
Entry *From) const;
|
||||||
|
|
||||||
/// Get the status of a given an \c Entry.
|
/// Get the status for a path with the provided \c LookupResult.
|
||||||
ErrorOr<Status> status(const Twine &Path, Entry *E);
|
ErrorOr<Status> status(const Twine &Path, const LookupResult &Result);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Looks up \p Path in \c Roots.
|
/// Looks up \p Path in \c Roots and returns a LookupResult giving the
|
||||||
ErrorOr<Entry *> lookupPath(StringRef Path) const;
|
/// matched entry and, if the entry was a FileEntry or DirectoryRemapEntry,
|
||||||
|
/// the path it redirects to in the external file system.
|
||||||
|
ErrorOr<LookupResult> lookupPath(StringRef Path) const;
|
||||||
|
|
||||||
/// Parses \p Buffer, which is expected to be in YAML format and
|
/// Parses \p Buffer, which is expected to be in YAML format and
|
||||||
/// returns a virtual file system representing its contents.
|
/// returns a virtual file system representing its contents.
|
||||||
|
@ -1011,14 +1011,20 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// Removes leading "./" as well as path components like ".." and ".".
|
static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
|
||||||
static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
|
// Detect the path style in use by checking the first separator.
|
||||||
// First detect the path style in use by checking the first separator.
|
|
||||||
llvm::sys::path::Style style = llvm::sys::path::Style::native;
|
llvm::sys::path::Style style = llvm::sys::path::Style::native;
|
||||||
const size_t n = Path.find_first_of("/\\");
|
const size_t n = Path.find_first_of("/\\");
|
||||||
if (n != static_cast<size_t>(-1))
|
if (n != static_cast<size_t>(-1))
|
||||||
style = (Path[n] == '/') ? llvm::sys::path::Style::posix
|
style = (Path[n] == '/') ? llvm::sys::path::Style::posix
|
||||||
: llvm::sys::path::Style::windows;
|
: llvm::sys::path::Style::windows;
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes leading "./" as well as path components like ".." and ".".
|
||||||
|
static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
|
||||||
|
// First detect the path style in use by checking the first separator.
|
||||||
|
llvm::sys::path::Style style = getExistingStyle(Path);
|
||||||
|
|
||||||
// Now remove the dots. Explicitly specifying the path style prevents the
|
// Now remove the dots. Explicitly specifying the path style prevents the
|
||||||
// direction of the slashes from changing.
|
// direction of the slashes from changing.
|
||||||
@ -1057,6 +1063,8 @@ class llvm::vfs::RedirectingFSDirIterImpl
|
|||||||
sys::fs::file_type Type = sys::fs::file_type::type_unknown;
|
sys::fs::file_type Type = sys::fs::file_type::type_unknown;
|
||||||
switch ((*Current)->getKind()) {
|
switch ((*Current)->getKind()) {
|
||||||
case RedirectingFileSystem::EK_Directory:
|
case RedirectingFileSystem::EK_Directory:
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case RedirectingFileSystem::EK_DirectoryRemap:
|
||||||
Type = sys::fs::file_type::directory_file;
|
Type = sys::fs::file_type::directory_file;
|
||||||
break;
|
break;
|
||||||
case RedirectingFileSystem::EK_File:
|
case RedirectingFileSystem::EK_File:
|
||||||
@ -1083,6 +1091,45 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Directory iterator implementation for \c RedirectingFileSystem's
|
||||||
|
/// directory remap entries that maps the paths reported by the external
|
||||||
|
/// file system's directory iterator back to the virtual directory's path.
|
||||||
|
class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
|
||||||
|
std::string Dir;
|
||||||
|
llvm::sys::path::Style DirStyle;
|
||||||
|
llvm::vfs::directory_iterator ExternalIter;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RedirectingFSDirRemapIterImpl(std::string DirPath,
|
||||||
|
llvm::vfs::directory_iterator ExtIter)
|
||||||
|
: Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
|
||||||
|
ExternalIter(ExtIter) {
|
||||||
|
if (ExternalIter != llvm::vfs::directory_iterator())
|
||||||
|
setCurrentEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCurrentEntry() {
|
||||||
|
StringRef ExternalPath = ExternalIter->path();
|
||||||
|
llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
|
||||||
|
StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
|
||||||
|
|
||||||
|
SmallString<128> NewPath(Dir);
|
||||||
|
llvm::sys::path::append(NewPath, DirStyle, File);
|
||||||
|
|
||||||
|
CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code increment() override {
|
||||||
|
std::error_code EC;
|
||||||
|
ExternalIter.increment(EC);
|
||||||
|
if (!EC && ExternalIter != llvm::vfs::directory_iterator())
|
||||||
|
setCurrentEntry();
|
||||||
|
else
|
||||||
|
CurrentEntry = directory_entry();
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
llvm::ErrorOr<std::string>
|
llvm::ErrorOr<std::string>
|
||||||
RedirectingFileSystem::getCurrentWorkingDirectory() const {
|
RedirectingFileSystem::getCurrentWorkingDirectory() const {
|
||||||
return WorkingDirectory;
|
return WorkingDirectory;
|
||||||
@ -1151,15 +1198,19 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
|
|||||||
if (EC)
|
if (EC)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
|
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
|
||||||
if (!E) {
|
if (!Result) {
|
||||||
EC = E.getError();
|
EC = Result.getError();
|
||||||
if (shouldFallBackToExternalFS(EC))
|
if (shouldFallBackToExternalFS(EC))
|
||||||
return ExternalFS->dir_begin(Path, EC);
|
return ExternalFS->dir_begin(Path, EC);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
ErrorOr<Status> S = status(Path, *E);
|
|
||||||
|
// Use status to make sure the path exists and refers to a directory.
|
||||||
|
ErrorOr<Status> S = status(Path, *Result);
|
||||||
if (!S) {
|
if (!S) {
|
||||||
|
if (shouldFallBackToExternalFS(S.getError(), Result->E))
|
||||||
|
return ExternalFS->dir_begin(Dir, EC);
|
||||||
EC = S.getError();
|
EC = S.getError();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -1169,9 +1220,24 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *D = cast<RedirectingFileSystem::DirectoryEntry>(*E);
|
// Create the appropriate directory iterator based on whether we found a
|
||||||
auto DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
|
// DirectoryRemapEntry or DirectoryEntry.
|
||||||
Path, D->contents_begin(), D->contents_end(), EC));
|
directory_iterator DirIter;
|
||||||
|
if (auto ExtRedirect = Result->getExternalRedirect()) {
|
||||||
|
auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
|
||||||
|
DirIter = ExternalFS->dir_begin(*ExtRedirect, EC);
|
||||||
|
|
||||||
|
if (!RE->useExternalName(UseExternalNames)) {
|
||||||
|
// Update the paths in the results to use the virtual directory's path.
|
||||||
|
DirIter =
|
||||||
|
directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
|
||||||
|
std::string(Path), DirIter));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto DE = cast<DirectoryEntry>(Result->E);
|
||||||
|
DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
|
||||||
|
Path, DE->contents_begin(), DE->contents_end(), EC));
|
||||||
|
}
|
||||||
|
|
||||||
if (!shouldUseExternalFS())
|
if (!shouldUseExternalFS())
|
||||||
return DirIter;
|
return DirIter;
|
||||||
@ -1360,6 +1426,15 @@ private:
|
|||||||
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
|
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case RedirectingFileSystem::EK_DirectoryRemap: {
|
||||||
|
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: {
|
case RedirectingFileSystem::EK_File: {
|
||||||
assert(NewParentE && "Parent entry must exist");
|
assert(NewParentE && "Parent entry must exist");
|
||||||
auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
|
auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
|
||||||
@ -1389,13 +1464,13 @@ private:
|
|||||||
|
|
||||||
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
|
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
|
||||||
|
|
||||||
bool HasContents = false; // external or otherwise
|
enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
|
||||||
std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
|
std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
|
||||||
EntryArrayContents;
|
EntryArrayContents;
|
||||||
SmallString<256> ExternalContentsPath;
|
SmallString<256> ExternalContentsPath;
|
||||||
SmallString<256> Name;
|
SmallString<256> Name;
|
||||||
yaml::Node *NameValueNode = nullptr;
|
yaml::Node *NameValueNode = nullptr;
|
||||||
auto UseExternalName = RedirectingFileSystem::FileEntry::NK_NotSet;
|
auto UseExternalName = RedirectingFileSystem::NK_NotSet;
|
||||||
RedirectingFileSystem::EntryKind Kind;
|
RedirectingFileSystem::EntryKind Kind;
|
||||||
|
|
||||||
for (auto &I : *M) {
|
for (auto &I : *M) {
|
||||||
@ -1425,17 +1500,19 @@ private:
|
|||||||
Kind = RedirectingFileSystem::EK_File;
|
Kind = RedirectingFileSystem::EK_File;
|
||||||
else if (Value == "directory")
|
else if (Value == "directory")
|
||||||
Kind = RedirectingFileSystem::EK_Directory;
|
Kind = RedirectingFileSystem::EK_Directory;
|
||||||
|
else if (Value == "directory-remap")
|
||||||
|
Kind = RedirectingFileSystem::EK_DirectoryRemap;
|
||||||
else {
|
else {
|
||||||
error(I.getValue(), "unknown value for 'type'");
|
error(I.getValue(), "unknown value for 'type'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else if (Key == "contents") {
|
} else if (Key == "contents") {
|
||||||
if (HasContents) {
|
if (ContentsField != CF_NotSet) {
|
||||||
error(I.getKey(),
|
error(I.getKey(),
|
||||||
"entry already has 'contents' or 'external-contents'");
|
"entry already has 'contents' or 'external-contents'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HasContents = true;
|
ContentsField = CF_List;
|
||||||
auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
|
auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
|
||||||
if (!Contents) {
|
if (!Contents) {
|
||||||
// FIXME: this is only for directories, what about files?
|
// FIXME: this is only for directories, what about files?
|
||||||
@ -1451,12 +1528,12 @@ private:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else if (Key == "external-contents") {
|
} else if (Key == "external-contents") {
|
||||||
if (HasContents) {
|
if (ContentsField != CF_NotSet) {
|
||||||
error(I.getKey(),
|
error(I.getKey(),
|
||||||
"entry already has 'contents' or 'external-contents'");
|
"entry already has 'contents' or 'external-contents'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HasContents = true;
|
ContentsField = CF_External;
|
||||||
if (!parseScalarString(I.getValue(), Value, Buffer))
|
if (!parseScalarString(I.getValue(), Value, Buffer))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -1478,8 +1555,8 @@ private:
|
|||||||
bool Val;
|
bool Val;
|
||||||
if (!parseScalarBool(I.getValue(), Val))
|
if (!parseScalarBool(I.getValue(), Val))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
UseExternalName = Val ? RedirectingFileSystem::FileEntry::NK_External
|
UseExternalName = Val ? RedirectingFileSystem::NK_External
|
||||||
: RedirectingFileSystem::FileEntry::NK_Virtual;
|
: RedirectingFileSystem::NK_Virtual;
|
||||||
} else {
|
} else {
|
||||||
llvm_unreachable("key missing from Keys");
|
llvm_unreachable("key missing from Keys");
|
||||||
}
|
}
|
||||||
@ -1489,7 +1566,7 @@ private:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// check for missing keys
|
// check for missing keys
|
||||||
if (!HasContents) {
|
if (ContentsField == CF_NotSet) {
|
||||||
error(N, "missing key 'contents' or 'external-contents'");
|
error(N, "missing key 'contents' or 'external-contents'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -1498,8 +1575,14 @@ private:
|
|||||||
|
|
||||||
// check invalid configuration
|
// check invalid configuration
|
||||||
if (Kind == RedirectingFileSystem::EK_Directory &&
|
if (Kind == RedirectingFileSystem::EK_Directory &&
|
||||||
UseExternalName != RedirectingFileSystem::FileEntry::NK_NotSet) {
|
UseExternalName != RedirectingFileSystem::NK_NotSet) {
|
||||||
error(N, "'use-external-name' is not supported for directories");
|
error(N, "'use-external-name' is not supported for 'directory' entries");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
|
||||||
|
ContentsField == CF_List) {
|
||||||
|
error(N, "'contents' is not supported for 'directory-remap' entries");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1535,6 +1618,10 @@ private:
|
|||||||
Result = std::make_unique<RedirectingFileSystem::FileEntry>(
|
Result = std::make_unique<RedirectingFileSystem::FileEntry>(
|
||||||
LastComponent, std::move(ExternalContentsPath), UseExternalName);
|
LastComponent, std::move(ExternalContentsPath), UseExternalName);
|
||||||
break;
|
break;
|
||||||
|
case RedirectingFileSystem::EK_DirectoryRemap:
|
||||||
|
Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
|
||||||
|
LastComponent, std::move(ExternalContentsPath), UseExternalName);
|
||||||
|
break;
|
||||||
case RedirectingFileSystem::EK_Directory:
|
case RedirectingFileSystem::EK_Directory:
|
||||||
Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
|
Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
|
||||||
LastComponent, std::move(EntryArrayContents),
|
LastComponent, std::move(EntryArrayContents),
|
||||||
@ -1745,8 +1832,8 @@ std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
|
|||||||
// Add the file.
|
// Add the file.
|
||||||
auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
|
auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
|
||||||
llvm::sys::path::filename(From), To,
|
llvm::sys::path::filename(From), To,
|
||||||
UseExternalNames ? RedirectingFileSystem::FileEntry::NK_External
|
UseExternalNames ? RedirectingFileSystem::NK_External
|
||||||
: RedirectingFileSystem::FileEntry::NK_Virtual);
|
: RedirectingFileSystem::NK_Virtual);
|
||||||
ToEntry = NewFile.get();
|
ToEntry = NewFile.get();
|
||||||
cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
|
cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
|
||||||
std::move(NewFile));
|
std::move(NewFile));
|
||||||
@ -1755,8 +1842,25 @@ std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
|
|||||||
return FS;
|
return FS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RedirectingFileSystem::LookupResult::LookupResult(
|
||||||
|
Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
|
||||||
|
: E(E) {
|
||||||
|
assert(E != nullptr);
|
||||||
|
// If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
|
||||||
|
// path of the directory it maps to in the external file system plus any
|
||||||
|
// remaining path components in the provided iterator.
|
||||||
|
if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
|
||||||
|
SmallString<256> Redirect(DRE->getExternalContentsPath());
|
||||||
|
sys::path::append(Redirect, Start, End,
|
||||||
|
getExistingStyle(DRE->getExternalContentsPath()));
|
||||||
|
ExternalRedirect = std::string(Redirect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool RedirectingFileSystem::shouldFallBackToExternalFS(
|
bool RedirectingFileSystem::shouldFallBackToExternalFS(
|
||||||
std::error_code EC) const {
|
std::error_code EC, RedirectingFileSystem::Entry *E) const {
|
||||||
|
if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
|
||||||
|
return false;
|
||||||
return shouldUseExternalFS() && EC == llvm::errc::no_such_file_or_directory;
|
return shouldUseExternalFS() && EC == llvm::errc::no_such_file_or_directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1774,23 +1878,23 @@ RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *>
|
ErrorOr<RedirectingFileSystem::LookupResult>
|
||||||
RedirectingFileSystem::lookupPath(StringRef Path) const {
|
RedirectingFileSystem::lookupPath(StringRef Path) const {
|
||||||
sys::path::const_iterator Start = sys::path::begin(Path);
|
sys::path::const_iterator Start = sys::path::begin(Path);
|
||||||
sys::path::const_iterator End = sys::path::end(Path);
|
sys::path::const_iterator End = sys::path::end(Path);
|
||||||
for (const auto &Root : Roots) {
|
for (const auto &Root : Roots) {
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> Result =
|
ErrorOr<RedirectingFileSystem::LookupResult> Result =
|
||||||
lookupPath(Start, End, Root.get());
|
lookupPathImpl(Start, End, Root.get());
|
||||||
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
return make_error_code(llvm::errc::no_such_file_or_directory);
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *>
|
ErrorOr<RedirectingFileSystem::LookupResult>
|
||||||
RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
|
RedirectingFileSystem::lookupPathImpl(
|
||||||
sys::path::const_iterator End,
|
sys::path::const_iterator Start, sys::path::const_iterator End,
|
||||||
RedirectingFileSystem::Entry *From) const {
|
RedirectingFileSystem::Entry *From) const {
|
||||||
assert(!isTraversalComponent(*Start) &&
|
assert(!isTraversalComponent(*Start) &&
|
||||||
!isTraversalComponent(From->getName()) &&
|
!isTraversalComponent(From->getName()) &&
|
||||||
"Paths should not contain traversal components");
|
"Paths should not contain traversal components");
|
||||||
@ -1806,18 +1910,21 @@ RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
|
|||||||
|
|
||||||
if (Start == End) {
|
if (Start == End) {
|
||||||
// Match!
|
// Match!
|
||||||
return From;
|
return LookupResult(From, Start, End);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(From);
|
if (isa<RedirectingFileSystem::FileEntry>(From))
|
||||||
if (!DE)
|
|
||||||
return make_error_code(llvm::errc::not_a_directory);
|
return make_error_code(llvm::errc::not_a_directory);
|
||||||
|
|
||||||
|
if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
|
||||||
|
return LookupResult(From, Start, End);
|
||||||
|
|
||||||
|
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
|
||||||
for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
|
for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
|
||||||
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
|
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> Result =
|
ErrorOr<RedirectingFileSystem::LookupResult> Result =
|
||||||
lookupPath(Start, End, DirEntry.get());
|
lookupPathImpl(Start, End, DirEntry.get());
|
||||||
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
@ -1834,20 +1941,19 @@ static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
|
|||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
|
ErrorOr<Status> RedirectingFileSystem::status(
|
||||||
RedirectingFileSystem::Entry *E) {
|
const Twine &Path, const RedirectingFileSystem::LookupResult &Result) {
|
||||||
assert(E != nullptr);
|
if (Optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
|
||||||
if (auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(E)) {
|
ErrorOr<Status> S = ExternalFS->status(*ExtRedirect);
|
||||||
ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
|
if (!S)
|
||||||
assert(!S || S->getName() == F->getExternalContentsPath());
|
return S;
|
||||||
if (S)
|
auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
|
||||||
return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
|
return getRedirectedFileStatus(Path, RE->useExternalName(UseExternalNames),
|
||||||
*S);
|
*S);
|
||||||
return S;
|
|
||||||
} else { // directory
|
|
||||||
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
|
|
||||||
return Status::copyWithNewName(DE->getStatus(), Path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
|
||||||
|
return Status::copyWithNewName(DE->getStatus(), Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
|
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
|
||||||
@ -1857,13 +1963,17 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
|
|||||||
if (std::error_code EC = makeCanonical(Path))
|
if (std::error_code EC = makeCanonical(Path))
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
|
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
|
||||||
if (!Result) {
|
if (!Result) {
|
||||||
if (shouldFallBackToExternalFS(Result.getError()))
|
if (shouldFallBackToExternalFS(Result.getError()))
|
||||||
return ExternalFS->status(Path);
|
return ExternalFS->status(Path);
|
||||||
return Result.getError();
|
return Result.getError();
|
||||||
}
|
}
|
||||||
return status(Path, *Result);
|
|
||||||
|
ErrorOr<Status> S = status(Path, *Result);
|
||||||
|
if (!S && shouldFallBackToExternalFS(S.getError(), Result->E))
|
||||||
|
S = ExternalFS->status(Path);
|
||||||
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -1899,30 +2009,35 @@ RedirectingFileSystem::openFileForRead(const Twine &Path_) {
|
|||||||
if (std::error_code EC = makeCanonical(Path))
|
if (std::error_code EC = makeCanonical(Path))
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
|
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
|
||||||
if (!E) {
|
if (!Result) {
|
||||||
if (shouldFallBackToExternalFS(E.getError()))
|
if (shouldFallBackToExternalFS(Result.getError()))
|
||||||
return ExternalFS->openFileForRead(Path);
|
return ExternalFS->openFileForRead(Path);
|
||||||
return E.getError();
|
return Result.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(*E);
|
if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
|
||||||
if (!F) // FIXME: errc::not_a_file?
|
|
||||||
return make_error_code(llvm::errc::invalid_argument);
|
return make_error_code(llvm::errc::invalid_argument);
|
||||||
|
|
||||||
auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
|
StringRef ExtRedirect = *Result->getExternalRedirect();
|
||||||
if (!Result)
|
auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
|
||||||
return Result;
|
|
||||||
|
|
||||||
auto ExternalStatus = (*Result)->status();
|
auto ExternalFile = ExternalFS->openFileForRead(ExtRedirect);
|
||||||
|
if (!ExternalFile) {
|
||||||
|
if (shouldFallBackToExternalFS(ExternalFile.getError(), Result->E))
|
||||||
|
return ExternalFS->openFileForRead(Path);
|
||||||
|
return ExternalFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ExternalStatus = (*ExternalFile)->status();
|
||||||
if (!ExternalStatus)
|
if (!ExternalStatus)
|
||||||
return ExternalStatus.getError();
|
return ExternalStatus.getError();
|
||||||
|
|
||||||
// FIXME: Update the status with the name and VFSMapped.
|
// FIXME: Update the status with the name and VFSMapped.
|
||||||
Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
|
Status S = getRedirectedFileStatus(
|
||||||
*ExternalStatus);
|
Path, RE->useExternalName(UseExternalNames), *ExternalStatus);
|
||||||
return std::unique_ptr<File>(
|
return std::unique_ptr<File>(
|
||||||
std::make_unique<FileWithFixedStatus>(std::move(*Result), S));
|
std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code
|
std::error_code
|
||||||
@ -1934,17 +2049,24 @@ RedirectingFileSystem::getRealPath(const Twine &Path_,
|
|||||||
if (std::error_code EC = makeCanonical(Path))
|
if (std::error_code EC = makeCanonical(Path))
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
|
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
|
||||||
if (!Result) {
|
if (!Result) {
|
||||||
if (shouldFallBackToExternalFS(Result.getError()))
|
if (shouldFallBackToExternalFS(Result.getError()))
|
||||||
return ExternalFS->getRealPath(Path, Output);
|
return ExternalFS->getRealPath(Path, Output);
|
||||||
return Result.getError();
|
return Result.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto *F = dyn_cast<RedirectingFileSystem::FileEntry>(*Result)) {
|
// If we found FileEntry or DirectoryRemapEntry, look up the mapped
|
||||||
return ExternalFS->getRealPath(F->getExternalContentsPath(), Output);
|
// path in the external file system.
|
||||||
|
if (auto ExtRedirect = Result->getExternalRedirect()) {
|
||||||
|
auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
|
||||||
|
if (!P && shouldFallBackToExternalFS(P, Result->E)) {
|
||||||
|
return ExternalFS->getRealPath(Path, Output);
|
||||||
|
}
|
||||||
|
return P;
|
||||||
}
|
}
|
||||||
// Even if there is a directory entry, fall back to ExternalFS if allowed,
|
|
||||||
|
// If we found a DirectoryEntry, still fall back to ExternalFS if allowed,
|
||||||
// because directories don't have a single external contents path.
|
// because directories don't have a single external contents path.
|
||||||
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
|
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
|
||||||
: llvm::errc::invalid_argument;
|
: llvm::errc::invalid_argument;
|
||||||
@ -1976,6 +2098,17 @@ static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
|
||||||
|
auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
|
||||||
|
assert(DR && "Must be a directory remap");
|
||||||
|
SmallString<128> VPath;
|
||||||
|
for (auto &Comp : Path)
|
||||||
|
llvm::sys::path::append(VPath, Comp);
|
||||||
|
Entries.push_back(
|
||||||
|
YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
|
assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
|
||||||
auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
|
auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
|
||||||
assert(FE && "Must be a file");
|
assert(FE && "Must be a file");
|
||||||
@ -1994,12 +2127,13 @@ void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
|
|||||||
std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
|
std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
|
||||||
std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
|
std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
|
||||||
std::move(ExternalFS));
|
std::move(ExternalFS));
|
||||||
ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/");
|
ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
|
||||||
if (!RootE)
|
VFS->lookupPath("/");
|
||||||
|
if (!RootResult)
|
||||||
return;
|
return;
|
||||||
SmallVector<StringRef, 8> Components;
|
SmallVector<StringRef, 8> Components;
|
||||||
Components.push_back("/");
|
Components.push_back("/");
|
||||||
getVFSEntries(*RootE, Components, CollectedEntries);
|
getVFSEntries(RootResult->E, Components, CollectedEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueID vfs::getNextVirtualUniqueID() {
|
UniqueID vfs::getNextVirtualUniqueID() {
|
||||||
|
@ -1328,6 +1328,7 @@ TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
|
|||||||
|
|
||||||
TEST_F(VFSFromYAMLTest, MappedFiles) {
|
TEST_F(VFSFromYAMLTest, MappedFiles) {
|
||||||
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
||||||
|
Lower->addDirectory("//root/foo/bar");
|
||||||
Lower->addRegularFile("//root/foo/bar/a");
|
Lower->addRegularFile("//root/foo/bar/a");
|
||||||
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
||||||
"{ 'roots': [\n"
|
"{ 'roots': [\n"
|
||||||
@ -1343,6 +1344,17 @@ TEST_F(VFSFromYAMLTest, MappedFiles) {
|
|||||||
" 'type': 'file',\n"
|
" 'type': 'file',\n"
|
||||||
" 'name': 'file2',\n"
|
" 'name': 'file2',\n"
|
||||||
" 'external-contents': '//root/foo/b'\n"
|
" 'external-contents': '//root/foo/b'\n"
|
||||||
|
" },\n"
|
||||||
|
" {\n"
|
||||||
|
" 'type': 'directory-remap',\n"
|
||||||
|
" 'name': 'mappeddir',\n"
|
||||||
|
" 'external-contents': '//root/foo/bar'\n"
|
||||||
|
" },\n"
|
||||||
|
" {\n"
|
||||||
|
" 'type': 'directory-remap',\n"
|
||||||
|
" 'name': 'mappeddir2',\n"
|
||||||
|
" 'use-external-name': false,\n"
|
||||||
|
" 'external-contents': '//root/foo/bar'\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" ]\n"
|
" ]\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
@ -1380,12 +1392,221 @@ TEST_F(VFSFromYAMLTest, MappedFiles) {
|
|||||||
EXPECT_TRUE(S->isDirectory());
|
EXPECT_TRUE(S->isDirectory());
|
||||||
EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
|
EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
|
||||||
|
|
||||||
|
// remapped 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 remapped 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 remapped 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 remapped 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 remapped 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
|
// broken mapping
|
||||||
EXPECT_EQ(O->status("//root/file2").getError(),
|
EXPECT_EQ(O->status("//root/file2").getError(),
|
||||||
llvm::errc::no_such_file_or_directory);
|
llvm::errc::no_such_file_or_directory);
|
||||||
EXPECT_EQ(0, NumDiagnostics);
|
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-remap',\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, RemappedDirectoryOverlay) {
|
||||||
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
||||||
|
Lower->addDirectory("//root/foo");
|
||||||
|
Lower->addRegularFile("//root/foo/a");
|
||||||
|
Lower->addDirectory("//root/bar");
|
||||||
|
Lower->addRegularFile("//root/bar/b");
|
||||||
|
Lower->addRegularFile("//root/bar/c");
|
||||||
|
IntrusiveRefCntPtr<vfs::FileSystem> FS =
|
||||||
|
getFromYAMLString("{ 'roots': [\n"
|
||||||
|
"{\n"
|
||||||
|
" 'type': 'directory',\n"
|
||||||
|
" 'name': '//root/',\n"
|
||||||
|
" 'contents': [ {\n"
|
||||||
|
" 'type': 'directory-remap',\n"
|
||||||
|
" 'name': 'bar',\n"
|
||||||
|
" 'external-contents': '//root/foo'\n"
|
||||||
|
" }\n"
|
||||||
|
" ]\n"
|
||||||
|
"}]}",
|
||||||
|
Lower);
|
||||||
|
ASSERT_TRUE(FS.get() != nullptr);
|
||||||
|
|
||||||
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
||||||
|
new vfs::OverlayFileSystem(Lower));
|
||||||
|
O->pushOverlay(FS);
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> S = O->status("//root/foo");
|
||||||
|
ASSERT_FALSE(S.getError());
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> SS = O->status("//root/bar");
|
||||||
|
ASSERT_FALSE(SS.getError());
|
||||||
|
EXPECT_TRUE(S->equivalent(*SS));
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
checkContents(O->dir_begin("//root/bar", EC),
|
||||||
|
{"//root/foo/a", "//root/bar/b", "//root/bar/c"});
|
||||||
|
|
||||||
|
Lower->addRegularFile("//root/foo/b");
|
||||||
|
checkContents(O->dir_begin("//root/bar", EC),
|
||||||
|
{"//root/foo/a", "//root/foo/b", "//root/bar/c"});
|
||||||
|
|
||||||
|
EXPECT_EQ(0, NumDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) {
|
||||||
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
||||||
|
Lower->addDirectory("//root/foo");
|
||||||
|
Lower->addRegularFile("//root/foo/a");
|
||||||
|
Lower->addDirectory("//root/bar");
|
||||||
|
Lower->addRegularFile("//root/bar/b");
|
||||||
|
Lower->addRegularFile("//root/bar/c");
|
||||||
|
IntrusiveRefCntPtr<vfs::FileSystem> FS =
|
||||||
|
getFromYAMLString("{ 'use-external-names': false,\n"
|
||||||
|
" 'roots': [\n"
|
||||||
|
"{\n"
|
||||||
|
" 'type': 'directory',\n"
|
||||||
|
" 'name': '//root/',\n"
|
||||||
|
" 'contents': [ {\n"
|
||||||
|
" 'type': 'directory-remap',\n"
|
||||||
|
" 'name': 'bar',\n"
|
||||||
|
" 'external-contents': '//root/foo'\n"
|
||||||
|
" }\n"
|
||||||
|
" ]\n"
|
||||||
|
"}]}",
|
||||||
|
Lower);
|
||||||
|
ASSERT_TRUE(FS.get() != nullptr);
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> S = FS->status("//root/foo");
|
||||||
|
ASSERT_FALSE(S.getError());
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> SS = FS->status("//root/bar");
|
||||||
|
ASSERT_FALSE(SS.getError());
|
||||||
|
EXPECT_TRUE(S->equivalent(*SS));
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
checkContents(FS->dir_begin("//root/bar", EC),
|
||||||
|
{"//root/bar/a", "//root/bar/b", "//root/bar/c"});
|
||||||
|
|
||||||
|
Lower->addRegularFile("//root/foo/b");
|
||||||
|
checkContents(FS->dir_begin("//root/bar", EC),
|
||||||
|
{"//root/bar/a", "//root/bar/b", "//root/bar/c"});
|
||||||
|
|
||||||
|
EXPECT_EQ(0, NumDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {
|
||||||
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
||||||
|
Lower->addDirectory("//root/foo");
|
||||||
|
Lower->addRegularFile("//root/foo/a");
|
||||||
|
Lower->addDirectory("//root/bar");
|
||||||
|
Lower->addRegularFile("//root/bar/b");
|
||||||
|
Lower->addRegularFile("//root/bar/c");
|
||||||
|
IntrusiveRefCntPtr<vfs::FileSystem> FS =
|
||||||
|
getFromYAMLString("{ 'fallthrough': false,\n"
|
||||||
|
" 'roots': [\n"
|
||||||
|
"{\n"
|
||||||
|
" 'type': 'directory',\n"
|
||||||
|
" 'name': '//root/',\n"
|
||||||
|
" 'contents': [ {\n"
|
||||||
|
" 'type': 'directory-remap',\n"
|
||||||
|
" 'name': 'bar',\n"
|
||||||
|
" 'external-contents': '//root/foo'\n"
|
||||||
|
" }\n"
|
||||||
|
" ]\n"
|
||||||
|
"}]}",
|
||||||
|
Lower);
|
||||||
|
ASSERT_TRUE(FS.get() != nullptr);
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> S = Lower->status("//root/foo");
|
||||||
|
ASSERT_FALSE(S.getError());
|
||||||
|
|
||||||
|
ErrorOr<vfs::Status> SS = FS->status("//root/bar");
|
||||||
|
ASSERT_FALSE(SS.getError());
|
||||||
|
EXPECT_TRUE(S->equivalent(*SS));
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"});
|
||||||
|
|
||||||
|
Lower->addRegularFile("//root/foo/b");
|
||||||
|
checkContents(FS->dir_begin("//root/bar", EC),
|
||||||
|
{"//root/foo/a", "//root/foo/b"});
|
||||||
|
|
||||||
|
EXPECT_EQ(0, NumDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(VFSFromYAMLTest, CaseInsensitive) {
|
TEST_F(VFSFromYAMLTest, CaseInsensitive) {
|
||||||
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
||||||
Lower->addRegularFile("//root/foo/bar/a");
|
Lower->addRegularFile("//root/foo/bar/a");
|
||||||
@ -1542,7 +1763,24 @@ TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
|
|||||||
EXPECT_EQ(nullptr, FS.get());
|
EXPECT_EQ(nullptr, FS.get());
|
||||||
FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
|
FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
|
||||||
EXPECT_EQ(nullptr, FS.get());
|
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());
|
||||||
|
|
||||||
|
// 'directory-remap' with 'contents'
|
||||||
|
FS = getFromYAMLString(
|
||||||
|
"{ 'roots':[ \n"
|
||||||
|
"{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
|
||||||
|
Lower);
|
||||||
|
EXPECT_EQ(nullptr, FS.get());
|
||||||
|
|
||||||
|
EXPECT_EQ(26, NumDiagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(VFSFromYAMLTest, UseExternalName) {
|
TEST_F(VFSFromYAMLTest, UseExternalName) {
|
||||||
|
Loading…
Reference in New Issue
Block a user