summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2015-12-30 13:34:49 +0000
committerdim <dim@FreeBSD.org>2015-12-30 13:34:49 +0000
commit63b24cc778504ffd19e4c30a730e574c346312ee (patch)
tree28726ef2038e86121e353aabf52297b35a48efa2 /contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
parent9b5bf5c4f53d65d6a48722d7410ed7cb15f5ba3a (diff)
parent3176e97f130184ece0e1a21352c8124cc83ff24a (diff)
downloadFreeBSD-src-63b24cc778504ffd19e4c30a730e574c346312ee.zip
FreeBSD-src-63b24cc778504ffd19e4c30a730e574c346312ee.tar.gz
Update clang to trunk r256633.
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp')
-rw-r--r--contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp631
1 files changed, 506 insertions, 125 deletions
diff --git a/contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp b/contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
index a36102c..cf5a8d6 100644
--- a/contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
+++ b/contrib/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
@@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Basic/FileManager.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
@@ -19,9 +20,17 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLParser.h"
+#include "llvm/Config/llvm-config.h"
#include <atomic>
#include <memory>
+// For chdir.
+#ifdef LLVM_ON_WIN32
+# include <direct.h>
+#else
+# include <unistd.h>
+#endif
+
using namespace clang;
using namespace clang::vfs;
using namespace llvm;
@@ -35,12 +44,24 @@ Status::Status(const file_status &Status)
User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
-Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
- sys::TimeValue MTime, uint32_t User, uint32_t Group,
- uint64_t Size, file_type Type, perms Perms)
+Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
+ uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
+ perms Perms)
: Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
Type(Type), Perms(Perms), IsVFSMapped(false) {}
+Status Status::copyWithNewName(const Status &In, StringRef NewName) {
+ return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
+ In.getUser(), In.getGroup(), In.getSize(), In.getType(),
+ In.getPermissions());
+}
+
+Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
+ return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
+ In.getUser(), In.getGroup(), In.getSize(), In.type(),
+ In.permissions());
+}
+
bool Status::equivalent(const Status &Other) const {
return getUniqueID() == Other.getUniqueID();
}
@@ -77,6 +98,19 @@ FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
}
+std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
+ auto WorkingDir = getCurrentWorkingDirectory();
+ if (!WorkingDir)
+ return WorkingDir.getError();
+
+ return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
+}
+
+bool FileSystem::exists(const Twine &Path) {
+ auto Status = status(Path);
+ return Status && Status->exists();
+}
+
//===-----------------------------------------------------------------------===/
// RealFileSystem implementation
//===-----------------------------------------------------------------------===/
@@ -87,19 +121,20 @@ class RealFile : public File {
int FD;
Status S;
friend class RealFileSystem;
- RealFile(int FD) : FD(FD) {
+ RealFile(int FD, StringRef NewName)
+ : FD(FD), S(NewName, {}, {}, {}, {}, {},
+ llvm::sys::fs::file_type::status_error, {}) {
assert(FD >= 0 && "Invalid or inactive file descriptor");
}
public:
~RealFile() override;
ErrorOr<Status> status() override;
- ErrorOr<std::unique_ptr<MemoryBuffer>>
- getBuffer(const Twine &Name, int64_t FileSize = -1,
- bool RequiresNullTerminator = true,
- bool IsVolatile = false) override;
+ ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
+ int64_t FileSize,
+ bool RequiresNullTerminator,
+ bool IsVolatile) override;
std::error_code close() override;
- void setName(StringRef Name) override;
};
} // end anonymous namespace
RealFile::~RealFile() { close(); }
@@ -110,9 +145,7 @@ ErrorOr<Status> RealFile::status() {
file_status RealStatus;
if (std::error_code EC = sys::fs::status(FD, RealStatus))
return EC;
- Status NewS(RealStatus);
- NewS.setName(S.getName());
- S = std::move(NewS);
+ S = Status::copyWithNewName(RealStatus, S.getName());
}
return S;
}
@@ -142,10 +175,6 @@ std::error_code RealFile::close() {
return std::error_code();
}
-void RealFile::setName(StringRef Name) {
- S.setName(Name);
-}
-
namespace {
/// \brief The file system according to your operating system.
class RealFileSystem : public FileSystem {
@@ -153,6 +182,9 @@ public:
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
};
} // end anonymous namespace
@@ -160,9 +192,7 @@ ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
sys::fs::file_status RealStatus;
if (std::error_code EC = sys::fs::status(Path, RealStatus))
return EC;
- Status Result(RealStatus);
- Result.setName(Path.str());
- return Result;
+ return Status::copyWithNewName(RealStatus, Path.str());
}
ErrorOr<std::unique_ptr<File>>
@@ -170,9 +200,29 @@ RealFileSystem::openFileForRead(const Twine &Name) {
int FD;
if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
return EC;
- std::unique_ptr<File> Result(new RealFile(FD));
- Result->setName(Name.str());
- return std::move(Result);
+ return std::unique_ptr<File>(new RealFile(FD, Name.str()));
+}
+
+llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
+ SmallString<256> Dir;
+ if (std::error_code EC = llvm::sys::fs::current_path(Dir))
+ return EC;
+ return Dir.str().str();
+}
+
+std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+ // FIXME: chdir is thread hostile; on the other hand, creating the same
+ // behavior as chdir is complex: chdir resolves the path once, thus
+ // guaranteeing that all subsequent relative path operations work
+ // on the same path the original chdir resulted in. This makes a
+ // difference for example on network filesystems, where symlinks might be
+ // switched during runtime of the tool. Fixing this depends on having a
+ // file system abstraction that allows openat() style interactions.
+ SmallString<256> Storage;
+ StringRef Dir = Path.toNullTerminatedStringRef(Storage);
+ if (int Err = ::chdir(Dir.data()))
+ return std::error_code(Err, std::generic_category());
+ return std::error_code();
}
IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
@@ -190,10 +240,8 @@ public:
if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
llvm::sys::fs::file_status S;
EC = Iter->status(S);
- if (!EC) {
- CurrentEntry = Status(S);
- CurrentEntry.setName(Iter->path());
- }
+ if (!EC)
+ CurrentEntry = Status::copyWithNewName(S, Iter->path());
}
}
@@ -207,8 +255,7 @@ public:
} else {
llvm::sys::fs::file_status S;
EC = Iter->status(S);
- CurrentEntry = Status(S);
- CurrentEntry.setName(Iter->path());
+ CurrentEntry = Status::copyWithNewName(S, Iter->path());
}
return EC;
}
@@ -224,11 +271,14 @@ directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
// OverlayFileSystem implementation
//===-----------------------------------------------------------------------===/
OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
- pushOverlay(BaseFS);
+ FSList.push_back(BaseFS);
}
void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
FSList.push_back(FS);
+ // Synchronize added file systems by duplicating the working directory from
+ // the first one in the list.
+ FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
}
ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
@@ -252,6 +302,19 @@ OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
return make_error_code(llvm::errc::no_such_file_or_directory);
}
+llvm::ErrorOr<std::string>
+OverlayFileSystem::getCurrentWorkingDirectory() const {
+ // All file systems are synchronized, just take the first working directory.
+ return FSList.front()->getCurrentWorkingDirectory();
+}
+std::error_code
+OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+ for (auto &FS : FSList)
+ if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
+ return EC;
+ return std::error_code();
+}
+
clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
namespace {
@@ -320,8 +383,286 @@ directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
}
+namespace clang {
+namespace vfs {
+namespace detail {
+
+enum InMemoryNodeKind { IME_File, IME_Directory };
+
+/// The in memory file system is a tree of Nodes. Every node can either be a
+/// file or a directory.
+class InMemoryNode {
+ Status Stat;
+ InMemoryNodeKind Kind;
+
+public:
+ InMemoryNode(Status Stat, InMemoryNodeKind Kind)
+ : Stat(std::move(Stat)), Kind(Kind) {}
+ virtual ~InMemoryNode() {}
+ const Status &getStatus() const { return Stat; }
+ InMemoryNodeKind getKind() const { return Kind; }
+ virtual std::string toString(unsigned Indent) const = 0;
+};
+
+namespace {
+class InMemoryFile : public InMemoryNode {
+ std::unique_ptr<llvm::MemoryBuffer> Buffer;
+
+public:
+ InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
+ : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
+
+ llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
+ std::string toString(unsigned Indent) const override {
+ return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
+ }
+ static bool classof(const InMemoryNode *N) {
+ return N->getKind() == IME_File;
+ }
+};
+
+/// Adapt a InMemoryFile for VFS' File interface.
+class InMemoryFileAdaptor : public File {
+ InMemoryFile &Node;
+
+public:
+ explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
+
+ llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
+ bool IsVolatile) override {
+ llvm::MemoryBuffer *Buf = Node.getBuffer();
+ return llvm::MemoryBuffer::getMemBuffer(
+ Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
+ }
+ std::error_code close() override { return std::error_code(); }
+};
+} // end anonymous namespace
+
+class InMemoryDirectory : public InMemoryNode {
+ std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
+
+public:
+ InMemoryDirectory(Status Stat)
+ : InMemoryNode(std::move(Stat), IME_Directory) {}
+ InMemoryNode *getChild(StringRef Name) {
+ auto I = Entries.find(Name);
+ if (I != Entries.end())
+ return I->second.get();
+ return nullptr;
+ }
+ InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
+ return Entries.insert(make_pair(Name, std::move(Child)))
+ .first->second.get();
+ }
+
+ typedef decltype(Entries)::const_iterator const_iterator;
+ const_iterator begin() const { return Entries.begin(); }
+ const_iterator end() const { return Entries.end(); }
+
+ std::string toString(unsigned Indent) const override {
+ std::string Result =
+ (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
+ for (const auto &Entry : Entries) {
+ Result += Entry.second->toString(Indent + 2);
+ }
+ return Result;
+ }
+ static bool classof(const InMemoryNode *N) {
+ return N->getKind() == IME_Directory;
+ }
+};
+}
+
+InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
+ : Root(new detail::InMemoryDirectory(
+ Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
+ 0, 0, 0, llvm::sys::fs::file_type::directory_file,
+ llvm::sys::fs::perms::all_all))),
+ UseNormalizedPaths(UseNormalizedPaths) {}
+
+InMemoryFileSystem::~InMemoryFileSystem() {}
+
+std::string InMemoryFileSystem::toString() const {
+ return Root->toString(/*Indent=*/0);
+}
+
+bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
+ std::unique_ptr<llvm::MemoryBuffer> Buffer) {
+ SmallString<128> Path;
+ P.toVector(Path);
+
+ // Fix up relative paths. This just prepends the current working directory.
+ std::error_code EC = makeAbsolute(Path);
+ assert(!EC);
+ (void)EC;
+
+ if (useNormalizedPaths())
+ llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
+
+ if (Path.empty())
+ return false;
+
+ detail::InMemoryDirectory *Dir = Root.get();
+ auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
+ while (true) {
+ StringRef Name = *I;
+ detail::InMemoryNode *Node = Dir->getChild(Name);
+ ++I;
+ if (!Node) {
+ if (I == E) {
+ // End of the path, create a new file.
+ // FIXME: expose the status details in the interface.
+ Status Stat(P.str(), getNextVirtualUniqueID(),
+ llvm::sys::TimeValue(ModificationTime, 0), 0, 0,
+ Buffer->getBufferSize(),
+ llvm::sys::fs::file_type::regular_file,
+ llvm::sys::fs::all_all);
+ Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
+ std::move(Stat), std::move(Buffer)));
+ return true;
+ }
+
+ // Create a new directory. Use the path up to here.
+ // FIXME: expose the status details in the interface.
+ Status Stat(
+ StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
+ getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime, 0),
+ 0, 0, Buffer->getBufferSize(),
+ llvm::sys::fs::file_type::directory_file, llvm::sys::fs::all_all);
+ Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
+ Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
+ continue;
+ }
+
+ if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
+ Dir = NewDir;
+ } else {
+ assert(isa<detail::InMemoryFile>(Node) &&
+ "Must be either file or directory!");
+
+ // Trying to insert a directory in place of a file.
+ if (I != E)
+ return false;
+
+ // Return false only if the new file is different from the existing one.
+ return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
+ Buffer->getBuffer();
+ }
+ }
+}
+
+bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
+ llvm::MemoryBuffer *Buffer) {
+ return addFile(P, ModificationTime,
+ llvm::MemoryBuffer::getMemBuffer(
+ Buffer->getBuffer(), Buffer->getBufferIdentifier()));
+}
+
+static ErrorOr<detail::InMemoryNode *>
+lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
+ const Twine &P) {
+ SmallString<128> Path;
+ P.toVector(Path);
+
+ // Fix up relative paths. This just prepends the current working directory.
+ std::error_code EC = FS.makeAbsolute(Path);
+ assert(!EC);
+ (void)EC;
+
+ if (FS.useNormalizedPaths())
+ llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
+
+ if (Path.empty())
+ return Dir;
+
+ auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
+ while (true) {
+ detail::InMemoryNode *Node = Dir->getChild(*I);
+ ++I;
+ if (!Node)
+ return errc::no_such_file_or_directory;
+
+ // Return the file if it's at the end of the path.
+ if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
+ if (I == E)
+ return File;
+ return errc::no_such_file_or_directory;
+ }
+
+ // Traverse directories.
+ Dir = cast<detail::InMemoryDirectory>(Node);
+ if (I == E)
+ return Dir;
+ }
+}
+
+llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
+ auto Node = lookupInMemoryNode(*this, Root.get(), Path);
+ if (Node)
+ return (*Node)->getStatus();
+ return Node.getError();
+}
+
+llvm::ErrorOr<std::unique_ptr<File>>
+InMemoryFileSystem::openFileForRead(const Twine &Path) {
+ auto Node = lookupInMemoryNode(*this, Root.get(), Path);
+ if (!Node)
+ return Node.getError();
+
+ // When we have a file provide a heap-allocated wrapper for the memory buffer
+ // to match the ownership semantics for File.
+ if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
+ return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
+
+ // FIXME: errc::not_a_file?
+ return make_error_code(llvm::errc::invalid_argument);
+}
+
+namespace {
+/// Adaptor from InMemoryDir::iterator to directory_iterator.
+class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
+ detail::InMemoryDirectory::const_iterator I;
+ detail::InMemoryDirectory::const_iterator E;
+
+public:
+ InMemoryDirIterator() {}
+ explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
+ : I(Dir.begin()), E(Dir.end()) {
+ if (I != E)
+ CurrentEntry = I->second->getStatus();
+ }
+
+ std::error_code increment() override {
+ ++I;
+ // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
+ // the rest.
+ CurrentEntry = I != E ? I->second->getStatus() : Status();
+ return std::error_code();
+ }
+};
+} // end anonymous namespace
+
+directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
+ std::error_code &EC) {
+ auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
+ if (!Node) {
+ EC = Node.getError();
+ return directory_iterator(std::make_shared<InMemoryDirIterator>());
+ }
+
+ if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
+ return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
+
+ EC = make_error_code(llvm::errc::not_a_directory);
+ return directory_iterator(std::make_shared<InMemoryDirIterator>());
+}
+}
+}
+
//===-----------------------------------------------------------------------===/
-// VFSFromYAML implementation
+// RedirectingFileSystem implementation
//===-----------------------------------------------------------------------===/
namespace {
@@ -343,23 +684,24 @@ public:
EntryKind getKind() const { return Kind; }
};
-class DirectoryEntry : public Entry {
- std::vector<Entry *> Contents;
+class RedirectingDirectoryEntry : public Entry {
+ std::vector<std::unique_ptr<Entry>> Contents;
Status S;
public:
- ~DirectoryEntry() override;
- DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
+ RedirectingDirectoryEntry(StringRef Name,
+ std::vector<std::unique_ptr<Entry>> Contents,
+ Status S)
: Entry(EK_Directory, Name), Contents(std::move(Contents)),
S(std::move(S)) {}
Status getStatus() { return S; }
- typedef std::vector<Entry *>::iterator iterator;
+ typedef decltype(Contents)::iterator 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 FileEntry : public Entry {
+class RedirectingFileEntry : public Entry {
public:
enum NameKind {
NK_NotSet,
@@ -370,7 +712,8 @@ private:
std::string ExternalContentsPath;
NameKind UseName;
public:
- FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
+ RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
+ NameKind UseName)
: Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
UseName(UseName) {}
StringRef getExternalContentsPath() const { return ExternalContentsPath; }
@@ -382,16 +725,18 @@ public:
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
};
-class VFSFromYAML;
+class RedirectingFileSystem;
class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
std::string Dir;
- VFSFromYAML &FS;
- DirectoryEntry::iterator Current, End;
+ RedirectingFileSystem &FS;
+ RedirectingDirectoryEntry::iterator Current, End;
+
public:
- VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
- DirectoryEntry::iterator Begin,
- DirectoryEntry::iterator End, std::error_code &EC);
+ VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
+ RedirectingDirectoryEntry::iterator Begin,
+ RedirectingDirectoryEntry::iterator End,
+ std::error_code &EC);
std::error_code increment() override;
};
@@ -448,8 +793,9 @@ public:
/// 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 VFSFromYAML : public vfs::FileSystem {
- std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
+class RedirectingFileSystem : public vfs::FileSystem {
+ /// The root(s) of the virtual file system.
+ std::vector<std::unique_ptr<Entry>> Roots;
/// \brief The file system to use for external references.
IntrusiveRefCntPtr<FileSystem> ExternalFS;
@@ -466,10 +812,10 @@ class VFSFromYAML : public vfs::FileSystem {
bool UseExternalNames;
/// @}
- friend class VFSFromYAMLParser;
+ friend class RedirectingFileSystemParser;
private:
- VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
+ RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
: ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
/// \brief Looks up \p Path in \c Roots.
@@ -484,18 +830,23 @@ private:
ErrorOr<Status> status(const Twine &Path, Entry *E);
public:
- ~VFSFromYAML() override;
-
/// \brief Parses \p Buffer, which is expected to be in YAML format and
/// returns a virtual file system representing its contents.
- static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer,
- SourceMgr::DiagHandlerTy DiagHandler,
- void *DiagContext,
- IntrusiveRefCntPtr<FileSystem> ExternalFS);
+ static RedirectingFileSystem *
+ create(std::unique_ptr<MemoryBuffer> Buffer,
+ SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
+ IntrusiveRefCntPtr<FileSystem> ExternalFS);
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+ return ExternalFS->getCurrentWorkingDirectory();
+ }
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+ return ExternalFS->setCurrentWorkingDirectory(Path);
+ }
+
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
ErrorOr<Entry *> E = lookupPath(Dir);
if (!E) {
@@ -513,14 +864,14 @@ public:
return directory_iterator();
}
- DirectoryEntry *D = cast<DirectoryEntry>(*E);
+ auto *D = cast<RedirectingDirectoryEntry>(*E);
return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
*this, D->contents_begin(), D->contents_end(), EC));
}
};
/// \brief A helper class to hold the common YAML parsing state.
-class VFSFromYAMLParser {
+class RedirectingFileSystemParser {
yaml::Stream &Stream;
void error(yaml::Node *N, const Twine &Msg) {
@@ -596,7 +947,7 @@ class VFSFromYAMLParser {
return true;
}
- Entry *parseEntry(yaml::Node *N) {
+ std::unique_ptr<Entry> parseEntry(yaml::Node *N) {
yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
if (!M) {
error(N, "expected mapping node for file or directory entry");
@@ -611,14 +962,13 @@ class VFSFromYAMLParser {
KeyStatusPair("use-external-name", false),
};
- DenseMap<StringRef, KeyStatus> Keys(
- &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
+ DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
bool HasContents = false; // external or otherwise
- std::vector<Entry *> EntryArrayContents;
+ std::vector<std::unique_ptr<Entry>> EntryArrayContents;
std::string ExternalContentsPath;
std::string Name;
- FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
+ auto UseExternalName = RedirectingFileEntry::NK_NotSet;
EntryKind Kind;
for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
@@ -667,8 +1017,8 @@ class VFSFromYAMLParser {
for (yaml::SequenceNode::iterator I = Contents->begin(),
E = Contents->end();
I != E; ++I) {
- if (Entry *E = parseEntry(&*I))
- EntryArrayContents.push_back(E);
+ if (std::unique_ptr<Entry> E = parseEntry(&*I))
+ EntryArrayContents.push_back(std::move(E));
else
return nullptr;
}
@@ -686,7 +1036,8 @@ class VFSFromYAMLParser {
bool Val;
if (!parseScalarBool(I->getValue(), Val))
return nullptr;
- UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
+ UseExternalName = Val ? RedirectingFileEntry::NK_External
+ : RedirectingFileEntry::NK_Virtual;
} else {
llvm_unreachable("key missing from Keys");
}
@@ -704,7 +1055,8 @@ class VFSFromYAMLParser {
return nullptr;
// check invalid configuration
- if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
+ if (Kind == EK_Directory &&
+ UseExternalName != RedirectingFileEntry::NK_NotSet) {
error(N, "'use-external-name' is not supported for directories");
return nullptr;
}
@@ -718,16 +1070,17 @@ class VFSFromYAMLParser {
// Get the last component
StringRef LastComponent = sys::path::filename(Trimmed);
- Entry *Result = nullptr;
+ std::unique_ptr<Entry> Result;
switch (Kind) {
case EK_File:
- Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
- UseExternalName);
+ Result = llvm::make_unique<RedirectingFileEntry>(
+ LastComponent, std::move(ExternalContentsPath), UseExternalName);
break;
case EK_Directory:
- Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
- Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
- 0, file_type::directory_file, sys::fs::all_all));
+ Result = llvm::make_unique<RedirectingDirectoryEntry>(
+ LastComponent, std::move(EntryArrayContents),
+ Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
+ file_type::directory_file, sys::fs::all_all));
break;
}
@@ -739,18 +1092,21 @@ class VFSFromYAMLParser {
for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
E = sys::path::rend(Parent);
I != E; ++I) {
- Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
- Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
- 0, file_type::directory_file, sys::fs::all_all));
+ std::vector<std::unique_ptr<Entry>> Entries;
+ Entries.push_back(std::move(Result));
+ Result = llvm::make_unique<RedirectingDirectoryEntry>(
+ *I, std::move(Entries),
+ Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
+ file_type::directory_file, sys::fs::all_all));
}
return Result;
}
public:
- VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
+ RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
// false on error
- bool parse(yaml::Node *Root, VFSFromYAML *FS) {
+ bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
if (!Top) {
error(Root, "expected mapping node");
@@ -764,8 +1120,7 @@ public:
KeyStatusPair("roots", true),
};
- DenseMap<StringRef, KeyStatus> Keys(
- &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
+ DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
// Parse configuration and 'roots'
for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
@@ -787,8 +1142,8 @@ public:
for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
I != E; ++I) {
- if (Entry *E = parseEntry(&*I))
- FS->Roots.push_back(E);
+ if (std::unique_ptr<Entry> E = parseEntry(&*I))
+ FS->Roots.push_back(std::move(E));
else
return false;
}
@@ -831,15 +1186,11 @@ public:
};
} // end of anonymous namespace
-Entry::~Entry() {}
-DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
+Entry::~Entry() = default;
-VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
-
-VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
- SourceMgr::DiagHandlerTy DiagHandler,
- void *DiagContext,
- IntrusiveRefCntPtr<FileSystem> ExternalFS) {
+RedirectingFileSystem *RedirectingFileSystem::create(
+ std::unique_ptr<MemoryBuffer> Buffer, SourceMgr::DiagHandlerTy DiagHandler,
+ void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS) {
SourceMgr SM;
yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
@@ -852,21 +1203,22 @@ VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
return nullptr;
}
- VFSFromYAMLParser P(Stream);
+ RedirectingFileSystemParser P(Stream);
- std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
+ std::unique_ptr<RedirectingFileSystem> FS(
+ new RedirectingFileSystem(ExternalFS));
if (!P.parse(Root, FS.get()))
return nullptr;
return FS.release();
}
-ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
+ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
SmallString<256> Path;
Path_.toVector(Path);
// Handle relative paths
- if (std::error_code EC = sys::fs::make_absolute(Path))
+ if (std::error_code EC = makeAbsolute(Path))
return EC;
if (Path.empty())
@@ -874,18 +1226,17 @@ ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
sys::path::const_iterator Start = sys::path::begin(Path);
sys::path::const_iterator End = sys::path::end(Path);
- for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
- I != E; ++I) {
- ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
+ for (const std::unique_ptr<Entry> &Root : Roots) {
+ ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
return make_error_code(llvm::errc::no_such_file_or_directory);
}
-ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
- sys::path::const_iterator End,
- Entry *From) {
+ErrorOr<Entry *>
+RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
+ sys::path::const_iterator End, Entry *From) {
if (Start->equals("."))
++Start;
@@ -902,52 +1253,78 @@ ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
return From;
}
- DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
+ auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
if (!DE)
return make_error_code(llvm::errc::not_a_directory);
- for (DirectoryEntry::iterator I = DE->contents_begin(),
- E = DE->contents_end();
- I != E; ++I) {
- ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
+ for (const std::unique_ptr<Entry> &DirEntry :
+ llvm::make_range(DE->contents_begin(), DE->contents_end())) {
+ ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
return make_error_code(llvm::errc::no_such_file_or_directory);
}
-ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
+static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
+ Status ExternalStatus) {
+ Status S = ExternalStatus;
+ if (!UseExternalNames)
+ S = Status::copyWithNewName(S, Path.str());
+ S.IsVFSMapped = true;
+ return S;
+}
+
+ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
assert(E != nullptr);
- std::string PathStr(Path.str());
- if (FileEntry *F = dyn_cast<FileEntry>(E)) {
+ if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {
ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
assert(!S || S->getName() == F->getExternalContentsPath());
- if (S && !F->useExternalName(UseExternalNames))
- S->setName(PathStr);
if (S)
- S->IsVFSMapped = true;
+ return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
+ *S);
return S;
} else { // directory
- DirectoryEntry *DE = cast<DirectoryEntry>(E);
- Status S = DE->getStatus();
- S.setName(PathStr);
- return S;
+ auto *DE = cast<RedirectingDirectoryEntry>(E);
+ return Status::copyWithNewName(DE->getStatus(), Path.str());
}
}
-ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
+ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
ErrorOr<Entry *> Result = lookupPath(Path);
if (!Result)
return Result.getError();
return status(Path, *Result);
}
-ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
+namespace {
+/// Provide a file wrapper with an overriden status.
+class FileWithFixedStatus : public File {
+ std::unique_ptr<File> InnerFile;
+ Status S;
+
+public:
+ FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
+ : InnerFile(std::move(InnerFile)), S(S) {}
+
+ ErrorOr<Status> status() override { return S; }
+ ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
+ bool IsVolatile) override {
+ return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
+ IsVolatile);
+ }
+ std::error_code close() override { return InnerFile->close(); }
+};
+} // end anonymous namespace
+
+ErrorOr<std::unique_ptr<File>>
+RedirectingFileSystem::openFileForRead(const Twine &Path) {
ErrorOr<Entry *> E = lookupPath(Path);
if (!E)
return E.getError();
- FileEntry *F = dyn_cast<FileEntry>(*E);
+ auto *F = dyn_cast<RedirectingFileEntry>(*E);
if (!F) // FIXME: errc::not_a_file?
return make_error_code(llvm::errc::invalid_argument);
@@ -955,18 +1332,23 @@ ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
if (!Result)
return Result;
- if (!F->useExternalName(UseExternalNames))
- (*Result)->setName(Path.str());
+ auto ExternalStatus = (*Result)->status();
+ if (!ExternalStatus)
+ return ExternalStatus.getError();
- return Result;
+ // FIXME: Update the status with the name and VFSMapped.
+ Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
+ *ExternalStatus);
+ return std::unique_ptr<File>(
+ llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));
}
IntrusiveRefCntPtr<FileSystem>
vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
IntrusiveRefCntPtr<FileSystem> ExternalFS) {
- return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext,
- ExternalFS);
+ return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
+ DiagContext, ExternalFS);
}
UniqueID vfs::getNextVirtualUniqueID() {
@@ -1111,11 +1493,10 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
JSONWriter(OS).write(Mappings, IsCaseSensitive);
}
-VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
- VFSFromYAML &FS,
- DirectoryEntry::iterator Begin,
- DirectoryEntry::iterator End,
- std::error_code &EC)
+VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
+ const Twine &_Path, RedirectingFileSystem &FS,
+ RedirectingDirectoryEntry::iterator Begin,
+ RedirectingDirectoryEntry::iterator End, std::error_code &EC)
: Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
if (Current != End) {
SmallString<128> PathStr(Dir);
OpenPOWER on IntegriCloud