Skip to content

Commit

Permalink
[VFS] Move RedirectingFileSystem interface into header (NFC)
Browse files Browse the repository at this point in the history
This moves the RedirectingFileSystem into the header so it can be
extended. This is needed in LLDB we need a way to obtain the external
path to deal with FILE* and file descriptor APIs.

Discussion on the mailing list:
http://lists.llvm.org/pipermail/llvm-dev/2018-November/127755.html

Differential revision: https://reviews.llvm.org/D54277

llvm-svn: 351265
  • Loading branch information
JDevlieghere committed Jan 15, 2019
1 parent 222474b commit 1a0ce65
Show file tree
Hide file tree
Showing 2 changed files with 391 additions and 339 deletions.
225 changes: 225 additions & 0 deletions llvm/include/llvm/Support/VirtualFileSystem.h
Expand Up @@ -24,6 +24,7 @@
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include <cassert>
#include <cstdint>
Expand Down Expand Up @@ -495,6 +496,230 @@ struct YAMLVFSEntry {
std::string RPath;
};

class VFSFromYamlDirIterImpl;
class RedirectingFileSystemParser;

/// 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.
///
/// The basic structure of the parsed file is:
/// \verbatim
/// {
/// 'version': <version number>,
/// <optional configuration>
/// 'roots': [
/// <directory entries>
/// ]
/// }
/// \endverbatim
///
/// All configuration options are optional.
/// 'case-sensitive': <boolean, default=true>
/// 'use-external-names': <boolean, default=true>
/// 'overlay-relative': <boolean, default=false>
/// 'fallthrough': <boolean, default=true>
///
/// Virtual directories are represented as
/// \verbatim
/// {
/// 'type': 'directory',
/// 'name': <string>,
/// 'contents': [ <file or directory entries> ]
/// }
/// \endverbatim
///
/// The default attributes for virtual directories are:
/// \verbatim
/// MTime = now() when created
/// Perms = 0777
/// User = Group = 0
/// Size = 0
/// UniqueID = unspecified unique value
/// \endverbatim
///
/// Re-mapped files are represented as
/// \verbatim
/// {
/// 'type': 'file',
/// 'name': <string>,
/// 'use-external-name': <boolean> # Optional
/// 'external-contents': <path to external file>
/// }
/// \endverbatim
///
/// and inherit their attributes from the 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.
class RedirectingFileSystem : public vfs::FileSystem {
public:
enum EntryKind { EK_Directory, EK_File };

/// A single file or directory in the VFS.
class Entry {
EntryKind Kind;
std::string Name;

public:
Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
virtual ~Entry() = default;

StringRef getName() const { return Name; }
EntryKind getKind() const { return Kind; }
};

class RedirectingDirectoryEntry : public Entry {
std::vector<std::unique_ptr<Entry>> Contents;
Status S;

public:
RedirectingDirectoryEntry(StringRef Name,
std::vector<std::unique_ptr<Entry>> Contents,
Status S)
: Entry(EK_Directory, Name), Contents(std::move(Contents)),
S(std::move(S)) {}
RedirectingDirectoryEntry(StringRef Name, Status S)
: Entry(EK_Directory, Name), S(std::move(S)) {}

Status getStatus() { return S; }

void addContent(std::unique_ptr<Entry> Content) {
Contents.push_back(std::move(Content));
}

Entry *getLastContent() const { return Contents.back().get(); }

using iterator = decltype(Contents)::iterator;

iterator contents_begin() { return Contents.begin(); }
iterator contents_end() { return Contents.end(); }

static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
};

class RedirectingFileEntry : public Entry {
public:
enum NameKind { NK_NotSet, NK_External, NK_Virtual };

private:
std::string ExternalContentsPath;
NameKind UseName;

public:
RedirectingFileEntry(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; }

static bool classof(const Entry *E) { return E->getKind() == EK_File; }
};

private:
friend class VFSFromYamlDirIterImpl;
friend class RedirectingFileSystemParser;

/// The root(s) of the virtual file system.
std::vector<std::unique_ptr<Entry>> Roots;

/// The file system to use for external references.
IntrusiveRefCntPtr<FileSystem> ExternalFS;

/// If IsRelativeOverlay is set, this represents the directory
/// path that should be prefixed to each 'external-contents' entry
/// when reading from YAML files.
std::string ExternalContentsPrefixDir;

/// @name Configuration
/// @{

/// Whether to perform case-sensitive comparisons.
///
/// Currently, case-insensitive matching only works correctly with ASCII.
bool CaseSensitive = true;

/// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must
/// be prefixed in every 'external-contents' when reading from YAML files.
bool IsRelativeOverlay = false;

/// Whether to use to use the value of 'external-contents' for the
/// names of files. This global value is overridable on a per-file basis.
bool UseExternalNames = true;

/// Whether to attempt a file lookup in external file system after it wasn't
/// found in VFS.
bool IsFallthrough = true;
/// @}

/// Virtual file paths and external files could be canonicalized without "..",
/// "." and "./" in their paths. FIXME: some unittests currently fail on
/// win32 when using remove_dots and remove_leading_dotslash on paths.
bool UseCanonicalizedPaths =
#ifdef _WIN32
false;
#else
true;
#endif

RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
: ExternalFS(std::move(ExternalFS)) {}

/// 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;

/// Get the status of a given an \c Entry.
ErrorOr<Status> status(const Twine &Path, Entry *E);

public:
/// Looks up \p Path in \c Roots.
ErrorOr<Entry *> lookupPath(const Twine &Path) const;

/// Parses \p Buffer, which is expected to be in YAML format and
/// returns a virtual file system representing its contents.
static RedirectingFileSystem *
create(std::unique_ptr<MemoryBuffer> Buffer,
SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS);

ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;

std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override;

llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;

std::error_code setCurrentWorkingDirectory(const Twine &Path) override;

std::error_code isLocal(const Twine &Path, bool &Result) override;

directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;

void setExternalContentsPrefixDir(StringRef PrefixDir);

StringRef getExternalContentsPrefixDir() const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const;
#endif
};

/// Collect all pairs of <virtual path, real path> entries from the
/// \p YAMLFilePath. This is used by the module dependency collector to forward
/// the entries into the reproducer output VFS YAML file.
Expand Down

0 comments on commit 1a0ce65

Please sign in to comment.