2014-02-21 05:59:23 +08:00
|
|
|
//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// This file implements the VirtualFileSystem interface.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Basic/VirtualFileSystem.h"
|
2015-10-13 00:16:39 +08:00
|
|
|
#include "clang/Basic/FileManager.h"
|
2014-02-22 07:39:37 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2014-06-25 03:37:16 +08:00
|
|
|
#include "llvm/ADT/StringSet.h"
|
2015-01-14 19:29:14 +08:00
|
|
|
#include "llvm/ADT/iterator_range.h"
|
2016-05-27 22:27:13 +08:00
|
|
|
#include "llvm/Config/llvm-config.h"
|
2016-05-07 07:21:57 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2014-06-14 01:20:50 +08:00
|
|
|
#include "llvm/Support/Errc.h"
|
2014-02-21 05:59:23 +08:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
2016-03-04 13:26:14 +08:00
|
|
|
#include "llvm/Support/Process.h"
|
2014-02-22 07:39:37 +08:00
|
|
|
#include "llvm/Support/YAMLParser.h"
|
2014-03-03 01:08:31 +08:00
|
|
|
#include <atomic>
|
2014-03-09 19:36:40 +08:00
|
|
|
#include <memory>
|
2016-05-27 22:27:13 +08:00
|
|
|
#include <utility>
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
using namespace clang::vfs;
|
|
|
|
using namespace llvm;
|
|
|
|
using llvm::sys::fs::file_status;
|
|
|
|
using llvm::sys::fs::file_type;
|
|
|
|
using llvm::sys::fs::perms;
|
|
|
|
using llvm::sys::fs::UniqueID;
|
|
|
|
|
|
|
|
Status::Status(const file_status &Status)
|
|
|
|
: UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
|
|
|
|
User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
|
2014-05-24 02:15:47 +08:00
|
|
|
Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2016-11-09 18:52:22 +08:00
|
|
|
Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime,
|
2015-10-05 21:15:33 +08:00
|
|
|
uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
|
|
|
|
perms Perms)
|
2014-02-27 08:25:12 +08:00
|
|
|
: Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
|
2014-05-24 02:15:47 +08:00
|
|
|
Type(Type), Perms(Perms), IsVFSMapped(false) {}
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2015-10-05 21:15:33 +08:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
bool Status::equivalent(const Status &Other) const {
|
2017-07-20 19:57:02 +08:00
|
|
|
assert(isStatusKnown() && Other.isStatusKnown());
|
2014-02-21 05:59:23 +08:00
|
|
|
return getUniqueID() == Other.getUniqueID();
|
|
|
|
}
|
|
|
|
bool Status::isDirectory() const {
|
|
|
|
return Type == file_type::directory_file;
|
|
|
|
}
|
|
|
|
bool Status::isRegularFile() const {
|
|
|
|
return Type == file_type::regular_file;
|
|
|
|
}
|
|
|
|
bool Status::isOther() const {
|
|
|
|
return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
|
|
|
|
}
|
|
|
|
bool Status::isSymlink() const {
|
|
|
|
return Type == file_type::symlink_file;
|
|
|
|
}
|
|
|
|
bool Status::isStatusKnown() const {
|
|
|
|
return Type != file_type::status_error;
|
|
|
|
}
|
|
|
|
bool Status::exists() const {
|
|
|
|
return isStatusKnown() && Type != file_type::file_not_found;
|
|
|
|
}
|
|
|
|
|
2015-10-20 21:23:58 +08:00
|
|
|
File::~File() {}
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2015-10-20 21:23:58 +08:00
|
|
|
FileSystem::~FileSystem() {}
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>>
|
|
|
|
FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
|
|
|
|
bool RequiresNullTerminator, bool IsVolatile) {
|
|
|
|
auto F = openFileForRead(Name);
|
|
|
|
if (!F)
|
|
|
|
return F.getError();
|
|
|
|
|
|
|
|
return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:20 +08:00
|
|
|
std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
|
2016-03-27 02:55:13 +08:00
|
|
|
if (llvm::sys::path::is_absolute(Path))
|
|
|
|
return std::error_code();
|
|
|
|
|
2015-10-05 21:55:20 +08:00
|
|
|
auto WorkingDir = getCurrentWorkingDirectory();
|
|
|
|
if (!WorkingDir)
|
|
|
|
return WorkingDir.getError();
|
|
|
|
|
|
|
|
return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
|
|
|
|
}
|
|
|
|
|
2015-10-07 23:48:01 +08:00
|
|
|
bool FileSystem::exists(const Twine &Path) {
|
|
|
|
auto Status = status(Path);
|
|
|
|
return Status && Status->exists();
|
|
|
|
}
|
|
|
|
|
2016-03-17 10:20:43 +08:00
|
|
|
#ifndef NDEBUG
|
|
|
|
static bool isTraversalComponent(StringRef Component) {
|
|
|
|
return Component.equals("..") || Component.equals(".");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool pathHasTraversal(StringRef Path) {
|
|
|
|
using namespace llvm::sys;
|
|
|
|
for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
|
|
|
|
if (isTraversalComponent(Comp))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
//===-----------------------------------------------------------------------===/
|
|
|
|
// RealFileSystem implementation
|
|
|
|
//===-----------------------------------------------------------------------===/
|
|
|
|
|
2014-03-02 01:21:22 +08:00
|
|
|
namespace {
|
2014-02-21 05:59:23 +08:00
|
|
|
/// \brief Wrapper around a raw file descriptor.
|
|
|
|
class RealFile : public File {
|
|
|
|
int FD;
|
2014-03-01 05:16:07 +08:00
|
|
|
Status S;
|
2016-06-14 04:40:21 +08:00
|
|
|
std::string RealName;
|
2014-02-21 05:59:23 +08:00
|
|
|
friend class RealFileSystem;
|
2016-06-14 04:40:21 +08:00
|
|
|
RealFile(int FD, StringRef NewName, StringRef NewRealPathName)
|
2015-10-05 21:15:33 +08:00
|
|
|
: FD(FD), S(NewName, {}, {}, {}, {}, {},
|
2016-06-14 04:40:21 +08:00
|
|
|
llvm::sys::fs::file_type::status_error, {}),
|
|
|
|
RealName(NewRealPathName.str()) {
|
2014-02-21 05:59:23 +08:00
|
|
|
assert(FD >= 0 && "Invalid or inactive file descriptor");
|
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
public:
|
2015-04-11 10:00:23 +08:00
|
|
|
~RealFile() override;
|
2014-03-02 17:32:10 +08:00
|
|
|
ErrorOr<Status> status() override;
|
2016-06-14 04:40:21 +08:00
|
|
|
ErrorOr<std::string> getName() override;
|
2015-10-06 05:20:19 +08:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
|
|
|
|
int64_t FileSize,
|
|
|
|
bool RequiresNullTerminator,
|
|
|
|
bool IsVolatile) override;
|
2014-06-13 04:37:59 +08:00
|
|
|
std::error_code close() override;
|
2014-02-21 05:59:23 +08:00
|
|
|
};
|
2014-03-02 01:21:22 +08:00
|
|
|
} // end anonymous namespace
|
2014-02-22 07:39:37 +08:00
|
|
|
RealFile::~RealFile() { close(); }
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
ErrorOr<Status> RealFile::status() {
|
|
|
|
assert(FD != -1 && "cannot stat closed file");
|
2014-03-01 05:16:07 +08:00
|
|
|
if (!S.isStatusKnown()) {
|
|
|
|
file_status RealStatus;
|
2014-06-13 04:37:59 +08:00
|
|
|
if (std::error_code EC = sys::fs::status(FD, RealStatus))
|
2014-03-01 05:16:07 +08:00
|
|
|
return EC;
|
2015-10-05 21:15:33 +08:00
|
|
|
S = Status::copyWithNewName(RealStatus, S.getName());
|
2014-03-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
return S;
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2016-06-14 04:40:21 +08:00
|
|
|
ErrorOr<std::string> RealFile::getName() {
|
|
|
|
return RealName.empty() ? S.getName().str() : RealName;
|
|
|
|
}
|
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>>
|
|
|
|
RealFile::getBuffer(const Twine &Name, int64_t FileSize,
|
|
|
|
bool RequiresNullTerminator, bool IsVolatile) {
|
2014-02-21 05:59:23 +08:00
|
|
|
assert(FD != -1 && "cannot get buffer for closed file");
|
2014-10-27 06:44:13 +08:00
|
|
|
return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
|
|
|
|
IsVolatile);
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2014-06-13 04:37:59 +08:00
|
|
|
std::error_code RealFile::close() {
|
2016-03-04 13:26:14 +08:00
|
|
|
std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD);
|
2014-02-21 05:59:23 +08:00
|
|
|
FD = -1;
|
2016-03-04 13:26:14 +08:00
|
|
|
return EC;
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2014-03-02 01:21:22 +08:00
|
|
|
namespace {
|
2014-02-21 05:59:23 +08:00
|
|
|
/// \brief The file system according to your operating system.
|
|
|
|
class RealFileSystem : public FileSystem {
|
|
|
|
public:
|
2014-03-02 17:32:10 +08:00
|
|
|
ErrorOr<Status> status(const Twine &Path) override;
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
|
2014-06-25 03:37:16 +08:00
|
|
|
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
|
2015-10-05 21:55:20 +08:00
|
|
|
|
|
|
|
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
|
|
|
|
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
|
2014-02-21 05:59:23 +08:00
|
|
|
};
|
2014-03-02 01:21:22 +08:00
|
|
|
} // end anonymous namespace
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
|
|
|
|
sys::fs::file_status RealStatus;
|
2014-06-13 04:37:59 +08:00
|
|
|
if (std::error_code EC = sys::fs::status(Path, RealStatus))
|
2014-02-21 05:59:23 +08:00
|
|
|
return EC;
|
2015-10-05 21:15:33 +08:00
|
|
|
return Status::copyWithNewName(RealStatus, Path.str());
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<File>>
|
|
|
|
RealFileSystem::openFileForRead(const Twine &Name) {
|
2014-02-21 05:59:23 +08:00
|
|
|
int FD;
|
2016-06-14 04:40:21 +08:00
|
|
|
SmallString<256> RealName;
|
|
|
|
if (std::error_code EC = sys::fs::openFileForRead(Name, FD, &RealName))
|
2014-02-21 05:59:23 +08:00
|
|
|
return EC;
|
2016-06-14 04:40:21 +08:00
|
|
|
return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:20 +08:00
|
|
|
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.
|
2017-01-24 19:14:29 +08:00
|
|
|
return llvm::sys::fs::set_current_path(Path);
|
2015-10-05 21:55:20 +08:00
|
|
|
}
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
|
|
|
|
static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
|
|
|
|
return FS;
|
|
|
|
}
|
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
namespace {
|
|
|
|
class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
|
|
|
|
llvm::sys::fs::directory_iterator Iter;
|
|
|
|
public:
|
2017-03-11 05:23:29 +08:00
|
|
|
RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
|
2014-06-25 03:37:16 +08:00
|
|
|
if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
|
|
|
|
llvm::sys::fs::file_status S;
|
2017-10-11 06:19:46 +08:00
|
|
|
EC = llvm::sys::fs::status(Iter->path(), S, true);
|
2017-03-14 08:14:40 +08:00
|
|
|
CurrentEntry = Status::copyWithNewName(S, Iter->path());
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code increment() override {
|
|
|
|
std::error_code EC;
|
|
|
|
Iter.increment(EC);
|
|
|
|
if (EC) {
|
|
|
|
return EC;
|
|
|
|
} else if (Iter == llvm::sys::fs::directory_iterator()) {
|
|
|
|
CurrentEntry = Status();
|
|
|
|
} else {
|
|
|
|
llvm::sys::fs::file_status S;
|
2017-10-11 06:19:46 +08:00
|
|
|
EC = llvm::sys::fs::status(Iter->path(), S, true);
|
2015-10-05 21:15:33 +08:00
|
|
|
CurrentEntry = Status::copyWithNewName(S, Iter->path());
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
};
|
2015-06-23 07:07:51 +08:00
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
|
|
|
|
std::error_code &EC) {
|
|
|
|
return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
|
|
|
|
}
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
//===-----------------------------------------------------------------------===/
|
|
|
|
// OverlayFileSystem implementation
|
|
|
|
//===-----------------------------------------------------------------------===/
|
|
|
|
OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
|
2016-06-13 04:05:23 +08:00
|
|
|
FSList.push_back(std::move(BaseFS));
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
|
|
|
|
FSList.push_back(FS);
|
2015-10-05 21:55:20 +08:00
|
|
|
// Synchronize added file systems by duplicating the working directory from
|
|
|
|
// the first one in the list.
|
|
|
|
FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
|
|
|
|
// FIXME: handle symlinks that cross file systems
|
|
|
|
for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
|
|
|
|
ErrorOr<Status> Status = (*I)->status(Path);
|
2014-06-14 01:20:50 +08:00
|
|
|
if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
|
2014-02-21 05:59:23 +08:00
|
|
|
return Status;
|
|
|
|
}
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<File>>
|
|
|
|
OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
|
2014-02-21 05:59:23 +08:00
|
|
|
// FIXME: handle symlinks that cross file systems
|
|
|
|
for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
|
2014-10-27 06:44:13 +08:00
|
|
|
auto Result = (*I)->openFileForRead(Path);
|
|
|
|
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
|
|
|
return Result;
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2015-10-05 21:55:20 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-10-20 21:23:58 +08:00
|
|
|
clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
|
|
|
|
OverlayFileSystem &Overlays;
|
|
|
|
std::string Path;
|
|
|
|
OverlayFileSystem::iterator CurrentFS;
|
|
|
|
directory_iterator CurrentDirIter;
|
|
|
|
llvm::StringSet<> SeenNames;
|
|
|
|
|
|
|
|
std::error_code incrementFS() {
|
|
|
|
assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
|
|
|
|
++CurrentFS;
|
|
|
|
for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
|
|
|
|
std::error_code EC;
|
|
|
|
CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
|
|
|
|
if (EC && EC != errc::no_such_file_or_directory)
|
|
|
|
return EC;
|
|
|
|
if (CurrentDirIter != directory_iterator())
|
|
|
|
break; // found
|
|
|
|
}
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code incrementDirIter(bool IsFirstTime) {
|
|
|
|
assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
|
|
|
|
"incrementing past end");
|
|
|
|
std::error_code EC;
|
|
|
|
if (!IsFirstTime)
|
|
|
|
CurrentDirIter.increment(EC);
|
|
|
|
if (!EC && CurrentDirIter == directory_iterator())
|
|
|
|
EC = incrementFS();
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code incrementImpl(bool IsFirstTime) {
|
|
|
|
while (true) {
|
|
|
|
std::error_code EC = incrementDirIter(IsFirstTime);
|
|
|
|
if (EC || CurrentDirIter == directory_iterator()) {
|
|
|
|
CurrentEntry = Status();
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
CurrentEntry = *CurrentDirIter;
|
|
|
|
StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
|
2014-11-19 10:56:13 +08:00
|
|
|
if (SeenNames.insert(Name).second)
|
2014-06-25 03:37:16 +08:00
|
|
|
return EC; // name not seen before
|
|
|
|
}
|
|
|
|
llvm_unreachable("returned above");
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
|
|
|
|
std::error_code &EC)
|
|
|
|
: Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
|
|
|
|
CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
|
|
|
|
EC = incrementImpl(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code increment() override { return incrementImpl(false); }
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
|
|
|
|
std::error_code &EC) {
|
|
|
|
return directory_iterator(
|
|
|
|
std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
|
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:14 +08:00
|
|
|
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) {}
|
2015-10-20 21:23:58 +08:00
|
|
|
virtual ~InMemoryNode() {}
|
2015-10-05 21:55:14 +08:00
|
|
|
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>>
|
2015-10-06 05:20:19 +08:00
|
|
|
getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
|
|
|
|
bool IsVolatile) override {
|
2015-10-05 21:55:14 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
|
2015-10-05 21:55:14 +08:00
|
|
|
: Root(new detail::InMemoryDirectory(
|
2016-11-09 18:52:22 +08:00
|
|
|
Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0,
|
|
|
|
0, llvm::sys::fs::file_type::directory_file,
|
2015-10-13 00:16:39 +08:00
|
|
|
llvm::sys::fs::perms::all_all))),
|
|
|
|
UseNormalizedPaths(UseNormalizedPaths) {}
|
2015-10-05 21:55:14 +08:00
|
|
|
|
2015-10-20 21:23:58 +08:00
|
|
|
InMemoryFileSystem::~InMemoryFileSystem() {}
|
2015-10-05 21:55:14 +08:00
|
|
|
|
2015-10-09 21:03:22 +08:00
|
|
|
std::string InMemoryFileSystem::toString() const {
|
2015-10-05 21:55:14 +08:00
|
|
|
return Root->toString(/*Indent=*/0);
|
|
|
|
}
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
|
2017-11-10 00:01:16 +08:00
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buffer,
|
|
|
|
Optional<uint32_t> User,
|
|
|
|
Optional<uint32_t> Group,
|
|
|
|
Optional<llvm::sys::fs::file_type> Type,
|
|
|
|
Optional<llvm::sys::fs::perms> Perms) {
|
2015-10-05 21:55:14 +08:00
|
|
|
SmallString<128> Path;
|
|
|
|
P.toVector(Path);
|
|
|
|
|
|
|
|
// Fix up relative paths. This just prepends the current working directory.
|
2015-10-05 21:55:20 +08:00
|
|
|
std::error_code EC = makeAbsolute(Path);
|
2015-10-05 21:55:14 +08:00
|
|
|
assert(!EC);
|
|
|
|
(void)EC;
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
if (useNormalizedPaths())
|
2015-11-10 03:12:18 +08:00
|
|
|
llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
|
2015-10-12 21:30:38 +08:00
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
if (Path.empty())
|
|
|
|
return false;
|
2015-10-12 21:30:38 +08:00
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
detail::InMemoryDirectory *Dir = Root.get();
|
2017-11-10 00:01:16 +08:00
|
|
|
auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
|
|
|
|
const auto ResolvedUser = User.getValueOr(0);
|
|
|
|
const auto ResolvedGroup = Group.getValueOr(0);
|
|
|
|
const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file);
|
|
|
|
const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all);
|
|
|
|
// Any intermediate directories we create should be accessible by
|
|
|
|
// the owner, even if Perms says otherwise for the final path.
|
|
|
|
const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
|
2015-10-05 21:55:14 +08:00
|
|
|
while (true) {
|
|
|
|
StringRef Name = *I;
|
|
|
|
detail::InMemoryNode *Node = Dir->getChild(Name);
|
|
|
|
++I;
|
|
|
|
if (!Node) {
|
|
|
|
if (I == E) {
|
2017-11-17 03:34:08 +08:00
|
|
|
// End of the path, create a new file or directory.
|
2015-10-06 22:45:16 +08:00
|
|
|
Status Stat(P.str(), getNextVirtualUniqueID(),
|
2017-11-10 00:01:16 +08:00
|
|
|
llvm::sys::toTimePoint(ModificationTime), ResolvedUser,
|
|
|
|
ResolvedGroup, Buffer->getBufferSize(), ResolvedType,
|
|
|
|
ResolvedPerms);
|
2017-11-17 03:34:08 +08:00
|
|
|
std::unique_ptr<detail::InMemoryNode> Child;
|
|
|
|
if (ResolvedType == sys::fs::file_type::directory_file) {
|
|
|
|
Child.reset(new detail::InMemoryDirectory(std::move(Stat)));
|
|
|
|
} else {
|
|
|
|
Child.reset(new detail::InMemoryFile(std::move(Stat),
|
|
|
|
std::move(Buffer)));
|
|
|
|
}
|
|
|
|
Dir->addChild(Name, std::move(Child));
|
2015-10-13 00:16:39 +08:00
|
|
|
return true;
|
2015-10-05 21:55:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new directory. Use the path up to here.
|
|
|
|
Status Stat(
|
|
|
|
StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
|
2017-11-10 00:01:16 +08:00
|
|
|
getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime),
|
|
|
|
ResolvedUser, ResolvedGroup, Buffer->getBufferSize(),
|
|
|
|
sys::fs::file_type::directory_file, NewDirectoryPerms);
|
2015-10-05 21:55:14 +08:00
|
|
|
Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
|
|
|
|
Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
|
2015-10-05 21:55:14 +08:00
|
|
|
Dir = NewDir;
|
2015-10-13 00:16:39 +08:00
|
|
|
} 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();
|
|
|
|
}
|
2015-10-05 21:55:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
|
2017-11-10 00:01:16 +08:00
|
|
|
llvm::MemoryBuffer *Buffer,
|
|
|
|
Optional<uint32_t> User,
|
|
|
|
Optional<uint32_t> Group,
|
|
|
|
Optional<llvm::sys::fs::file_type> Type,
|
|
|
|
Optional<llvm::sys::fs::perms> Perms) {
|
2015-10-06 18:04:08 +08:00
|
|
|
return addFile(P, ModificationTime,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(
|
2017-11-10 00:01:16 +08:00
|
|
|
Buffer->getBuffer(), Buffer->getBufferIdentifier()),
|
|
|
|
std::move(User), std::move(Group), std::move(Type),
|
|
|
|
std::move(Perms));
|
2015-10-06 18:04:08 +08:00
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:14 +08:00
|
|
|
static ErrorOr<detail::InMemoryNode *>
|
2015-10-05 21:55:20 +08:00
|
|
|
lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
|
|
|
|
const Twine &P) {
|
2015-10-05 21:55:14 +08:00
|
|
|
SmallString<128> Path;
|
|
|
|
P.toVector(Path);
|
|
|
|
|
|
|
|
// Fix up relative paths. This just prepends the current working directory.
|
2015-10-05 21:55:20 +08:00
|
|
|
std::error_code EC = FS.makeAbsolute(Path);
|
2015-10-05 21:55:14 +08:00
|
|
|
assert(!EC);
|
|
|
|
(void)EC;
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
if (FS.useNormalizedPaths())
|
2015-11-10 03:12:18 +08:00
|
|
|
llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
|
2015-10-12 21:30:38 +08:00
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
if (Path.empty())
|
2015-10-09 21:03:22 +08:00
|
|
|
return Dir;
|
|
|
|
|
2015-10-13 00:16:39 +08:00
|
|
|
auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
|
2015-10-05 21:55:14 +08:00
|
|
|
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) {
|
2015-10-05 21:55:20 +08:00
|
|
|
auto Node = lookupInMemoryNode(*this, Root.get(), Path);
|
2015-10-05 21:55:14 +08:00
|
|
|
if (Node)
|
|
|
|
return (*Node)->getStatus();
|
|
|
|
return Node.getError();
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::ErrorOr<std::unique_ptr<File>>
|
|
|
|
InMemoryFileSystem::openFileForRead(const Twine &Path) {
|
2015-10-05 21:55:20 +08:00
|
|
|
auto Node = lookupInMemoryNode(*this, Root.get(), Path);
|
2015-10-05 21:55:14 +08:00
|
|
|
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) {
|
2015-10-05 21:55:20 +08:00
|
|
|
auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
|
2015-10-05 21:55:14 +08:00
|
|
|
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>());
|
|
|
|
}
|
2016-01-10 00:33:16 +08:00
|
|
|
|
|
|
|
std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
|
|
|
|
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())
|
|
|
|
WorkingDirectory = Path.str();
|
|
|
|
return std::error_code();
|
|
|
|
}
|
2015-10-05 21:55:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
//===-----------------------------------------------------------------------===/
|
2015-10-07 18:05:44 +08:00
|
|
|
// RedirectingFileSystem implementation
|
2014-02-22 07:39:37 +08:00
|
|
|
//===-----------------------------------------------------------------------===/
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
enum EntryKind {
|
|
|
|
EK_Directory,
|
|
|
|
EK_File
|
|
|
|
};
|
|
|
|
|
|
|
|
/// \brief A single file or directory in the VFS.
|
|
|
|
class Entry {
|
|
|
|
EntryKind Kind;
|
|
|
|
std::string Name;
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual ~Entry();
|
|
|
|
Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
|
|
|
|
StringRef getName() const { return Name; }
|
|
|
|
EntryKind getKind() const { return Kind; }
|
|
|
|
};
|
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
class RedirectingDirectoryEntry : public Entry {
|
2015-10-07 18:05:44 +08:00
|
|
|
std::vector<std::unique_ptr<Entry>> Contents;
|
2014-02-22 07:39:37 +08:00
|
|
|
Status S;
|
|
|
|
|
|
|
|
public:
|
2015-10-09 21:28:13 +08:00
|
|
|
RedirectingDirectoryEntry(StringRef Name,
|
|
|
|
std::vector<std::unique_ptr<Entry>> Contents,
|
|
|
|
Status S)
|
2014-02-25 12:34:14 +08:00
|
|
|
: Entry(EK_Directory, Name), Contents(std::move(Contents)),
|
2014-02-22 07:39:37 +08:00
|
|
|
S(std::move(S)) {}
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
RedirectingDirectoryEntry(StringRef Name, Status S)
|
|
|
|
: Entry(EK_Directory, Name), S(std::move(S)) {}
|
2014-02-22 07:39:37 +08:00
|
|
|
Status getStatus() { return S; }
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
void addContent(std::unique_ptr<Entry> Content) {
|
|
|
|
Contents.push_back(std::move(Content));
|
|
|
|
}
|
|
|
|
Entry *getLastContent() const { return Contents.back().get(); }
|
2015-10-07 18:05:44 +08:00
|
|
|
typedef decltype(Contents)::iterator iterator;
|
2014-02-22 07:39:37 +08:00
|
|
|
iterator contents_begin() { return Contents.begin(); }
|
|
|
|
iterator contents_end() { return Contents.end(); }
|
|
|
|
static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
|
|
|
|
};
|
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
class RedirectingFileEntry : public Entry {
|
2014-02-27 08:25:12 +08:00
|
|
|
public:
|
|
|
|
enum NameKind {
|
|
|
|
NK_NotSet,
|
|
|
|
NK_External,
|
|
|
|
NK_Virtual
|
|
|
|
};
|
|
|
|
private:
|
2014-02-22 07:39:37 +08:00
|
|
|
std::string ExternalContentsPath;
|
2014-02-27 08:25:12 +08:00
|
|
|
NameKind UseName;
|
2014-02-22 07:39:37 +08:00
|
|
|
public:
|
2015-10-09 21:28:13 +08:00
|
|
|
RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
|
|
|
|
NameKind UseName)
|
2014-02-27 08:25:12 +08:00
|
|
|
: Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
|
|
|
|
UseName(UseName) {}
|
2014-02-22 07:39:37 +08:00
|
|
|
StringRef getExternalContentsPath() const { return ExternalContentsPath; }
|
2014-02-27 08:25:12 +08:00
|
|
|
/// \brief whether to use the external path as the name for this file.
|
2014-03-01 05:16:07 +08:00
|
|
|
bool useExternalName(bool GlobalUseExternalName) const {
|
|
|
|
return UseName == NK_NotSet ? GlobalUseExternalName
|
|
|
|
: (UseName == NK_External);
|
|
|
|
}
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
NameKind getUseName() const { return UseName; }
|
2014-02-22 07:39:37 +08:00
|
|
|
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
|
|
|
|
};
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
class RedirectingFileSystem;
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
|
|
|
|
std::string Dir;
|
2015-10-07 18:05:44 +08:00
|
|
|
RedirectingFileSystem &FS;
|
2015-10-09 21:28:13 +08:00
|
|
|
RedirectingDirectoryEntry::iterator Current, End;
|
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
public:
|
2015-10-07 18:05:44 +08:00
|
|
|
VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
|
2015-10-09 21:28:13 +08:00
|
|
|
RedirectingDirectoryEntry::iterator Begin,
|
|
|
|
RedirectingDirectoryEntry::iterator End,
|
|
|
|
std::error_code &EC);
|
2014-06-25 03:37:16 +08:00
|
|
|
std::error_code increment() override;
|
|
|
|
};
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
/// \brief 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>
|
2014-02-27 08:25:12 +08:00
|
|
|
/// 'use-external-names': <boolean, default=true>
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
/// 'overlay-relative': <boolean, default=false>
|
2016-08-12 09:50:53 +08:00
|
|
|
/// 'ignore-non-existent-contents': <boolean, default=true>
|
2014-02-22 07:39:37 +08:00
|
|
|
///
|
|
|
|
/// 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>,
|
2014-02-27 08:25:12 +08:00
|
|
|
/// 'use-external-name': <boolean> # Optional
|
2014-02-22 07:39:37 +08:00
|
|
|
/// 'external-contents': <path to external file>)
|
|
|
|
/// }
|
|
|
|
/// \endverbatim
|
|
|
|
///
|
|
|
|
/// and inherit their attributes from the external contents.
|
|
|
|
///
|
2014-02-25 12:34:14 +08:00
|
|
|
/// 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.
|
2015-10-07 18:05:44 +08:00
|
|
|
class RedirectingFileSystem : public vfs::FileSystem {
|
|
|
|
/// The root(s) of the virtual file system.
|
|
|
|
std::vector<std::unique_ptr<Entry>> Roots;
|
2014-02-22 07:39:37 +08:00
|
|
|
/// \brief The file system to use for external references.
|
|
|
|
IntrusiveRefCntPtr<FileSystem> ExternalFS;
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
/// 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;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
/// @name Configuration
|
|
|
|
/// @{
|
|
|
|
|
|
|
|
/// \brief Whether to perform case-sensitive comparisons.
|
|
|
|
///
|
|
|
|
/// Currently, case-insensitive matching only works correctly with ASCII.
|
2016-04-14 03:28:16 +08:00
|
|
|
bool CaseSensitive = true;
|
2014-02-27 08:25:12 +08:00
|
|
|
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
/// IsRelativeOverlay marks whether a IsExternalContentsPrefixDir path must
|
|
|
|
/// be prefixed in every 'external-contents' when reading from YAML files.
|
|
|
|
bool IsRelativeOverlay = false;
|
|
|
|
|
2014-02-27 08:25:12 +08:00
|
|
|
/// \brief 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.
|
2016-04-14 03:28:16 +08:00
|
|
|
bool UseExternalNames = true;
|
2016-08-12 09:50:53 +08:00
|
|
|
|
|
|
|
/// \brief Whether an invalid path obtained via 'external-contents' should
|
|
|
|
/// cause iteration on the VFS to stop. If 'true', the VFS should ignore
|
|
|
|
/// the entry and continue with the next. Allows YAML files to be shared
|
|
|
|
/// across multiple compiler invocations regardless of prior existent
|
|
|
|
/// paths in 'external-contents'. This global value is overridable on a
|
|
|
|
/// per-file basis.
|
|
|
|
bool IgnoreNonExistentContents = true;
|
2014-02-22 07:39:37 +08:00
|
|
|
/// @}
|
|
|
|
|
2016-03-17 10:20:43 +08:00
|
|
|
/// 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 LLVM_ON_WIN32
|
|
|
|
false;
|
|
|
|
#else
|
|
|
|
true;
|
|
|
|
#endif
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
friend class RedirectingFileSystemParser;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
private:
|
2015-10-07 18:05:44 +08:00
|
|
|
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
|
2016-05-27 22:27:13 +08:00
|
|
|
: ExternalFS(std::move(ExternalFS)) {}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
/// \brief 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(sys::path::const_iterator Start,
|
|
|
|
sys::path::const_iterator End, Entry *From);
|
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
/// \brief Get the status of a given an \c Entry.
|
|
|
|
ErrorOr<Status> status(const Twine &Path, Entry *E);
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
public:
|
2016-12-22 15:06:03 +08:00
|
|
|
/// \brief Looks up \p Path in \c Roots.
|
|
|
|
ErrorOr<Entry *> lookupPath(const Twine &Path);
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
/// \brief Parses \p Buffer, which is expected to be in YAML format and
|
|
|
|
/// returns a virtual file system representing its contents.
|
2015-10-07 18:05:44 +08:00
|
|
|
static RedirectingFileSystem *
|
|
|
|
create(std::unique_ptr<MemoryBuffer> Buffer,
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
|
|
|
|
void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-03-02 17:32:10 +08:00
|
|
|
ErrorOr<Status> status(const Twine &Path) override;
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
|
2014-06-25 03:37:16 +08:00
|
|
|
|
2015-10-05 21:55:20 +08:00
|
|
|
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
|
|
|
|
return ExternalFS->getCurrentWorkingDirectory();
|
|
|
|
}
|
|
|
|
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
|
|
|
|
return ExternalFS->setCurrentWorkingDirectory(Path);
|
|
|
|
}
|
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
|
|
|
|
ErrorOr<Entry *> E = lookupPath(Dir);
|
|
|
|
if (!E) {
|
|
|
|
EC = E.getError();
|
|
|
|
return directory_iterator();
|
|
|
|
}
|
|
|
|
ErrorOr<Status> S = status(Dir, *E);
|
|
|
|
if (!S) {
|
|
|
|
EC = S.getError();
|
|
|
|
return directory_iterator();
|
|
|
|
}
|
|
|
|
if (!S->isDirectory()) {
|
|
|
|
EC = std::error_code(static_cast<int>(errc::not_a_directory),
|
|
|
|
std::system_category());
|
|
|
|
return directory_iterator();
|
|
|
|
}
|
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
auto *D = cast<RedirectingDirectoryEntry>(*E);
|
2014-06-25 03:37:16 +08:00
|
|
|
return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
|
|
|
|
*this, D->contents_begin(), D->contents_end(), EC));
|
|
|
|
}
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
|
|
|
|
void setExternalContentsPrefixDir(StringRef PrefixDir) {
|
|
|
|
ExternalContentsPrefixDir = PrefixDir.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef getExternalContentsPrefixDir() const {
|
|
|
|
return ExternalContentsPrefixDir;
|
|
|
|
}
|
|
|
|
|
2016-08-12 09:50:53 +08:00
|
|
|
bool ignoreNonExistentContents() const {
|
|
|
|
return IgnoreNonExistentContents;
|
|
|
|
}
|
|
|
|
|
2016-05-07 07:21:57 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
|
|
LLVM_DUMP_METHOD void dump() const {
|
|
|
|
for (const std::unique_ptr<Entry> &Root : Roots)
|
|
|
|
dumpEntry(Root.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const {
|
|
|
|
StringRef Name = E->getName();
|
|
|
|
for (int i = 0, e = NumSpaces; i < e; ++i)
|
|
|
|
dbgs() << " ";
|
|
|
|
dbgs() << "'" << Name.str().c_str() << "'" << "\n";
|
|
|
|
|
|
|
|
if (E->getKind() == EK_Directory) {
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(E);
|
|
|
|
assert(DE && "Should be a directory");
|
|
|
|
|
|
|
|
for (std::unique_ptr<Entry> &SubEntry :
|
|
|
|
llvm::make_range(DE->contents_begin(), DE->contents_end()))
|
|
|
|
dumpEntry(SubEntry.get(), NumSpaces+2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/// \brief A helper class to hold the common YAML parsing state.
|
2015-10-07 18:05:44 +08:00
|
|
|
class RedirectingFileSystemParser {
|
2014-02-22 07:39:37 +08:00
|
|
|
yaml::Stream &Stream;
|
|
|
|
|
|
|
|
void error(yaml::Node *N, const Twine &Msg) {
|
|
|
|
Stream.printError(N, Msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// false on error
|
|
|
|
bool parseScalarString(yaml::Node *N, StringRef &Result,
|
|
|
|
SmallVectorImpl<char> &Storage) {
|
|
|
|
yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
|
|
|
|
if (!S) {
|
|
|
|
error(N, "expected string");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Result = S->getValue(Storage);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// false on error
|
|
|
|
bool parseScalarBool(yaml::Node *N, bool &Result) {
|
|
|
|
SmallString<5> Storage;
|
|
|
|
StringRef Value;
|
|
|
|
if (!parseScalarString(N, Value, Storage))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Value.equals_lower("true") || Value.equals_lower("on") ||
|
|
|
|
Value.equals_lower("yes") || Value == "1") {
|
|
|
|
Result = true;
|
|
|
|
return true;
|
|
|
|
} else if (Value.equals_lower("false") || Value.equals_lower("off") ||
|
|
|
|
Value.equals_lower("no") || Value == "0") {
|
|
|
|
Result = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
error(N, "expected boolean value");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct KeyStatus {
|
|
|
|
KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
|
|
|
|
bool Required;
|
|
|
|
bool Seen;
|
|
|
|
};
|
|
|
|
typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
|
|
|
|
|
|
|
|
// false on error
|
|
|
|
bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
|
|
|
|
DenseMap<StringRef, KeyStatus> &Keys) {
|
|
|
|
if (!Keys.count(Key)) {
|
|
|
|
error(KeyNode, "unknown key");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
KeyStatus &S = Keys[Key];
|
|
|
|
if (S.Seen) {
|
|
|
|
error(KeyNode, Twine("duplicate key '") + Key + "'");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
S.Seen = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// false on error
|
|
|
|
bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
|
|
|
|
for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
|
|
|
|
E = Keys.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
if (I->second.Required && !I->second.Seen) {
|
|
|
|
error(Obj, Twine("missing key '") + I->first + "'");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
|
|
|
|
Entry *ParentEntry = nullptr) {
|
|
|
|
if (!ParentEntry) { // Look for a existent root
|
|
|
|
for (const std::unique_ptr<Entry> &Root : FS->Roots) {
|
|
|
|
if (Name.equals(Root->getName())) {
|
|
|
|
ParentEntry = Root.get();
|
|
|
|
return ParentEntry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // Advance to the next component
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
|
|
|
|
for (std::unique_ptr<Entry> &Content :
|
|
|
|
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
|
|
|
|
auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get());
|
|
|
|
if (DirContent && Name.equals(Content->getName()))
|
|
|
|
return DirContent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ... or create a new one
|
|
|
|
std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>(
|
2016-11-09 18:52:22 +08:00
|
|
|
Name,
|
|
|
|
Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
|
|
|
|
0, 0, 0, file_type::directory_file, sys::fs::all_all));
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
|
|
|
|
if (!ParentEntry) { // Add a new root to the overlay
|
|
|
|
FS->Roots.push_back(std::move(E));
|
|
|
|
ParentEntry = FS->Roots.back().get();
|
|
|
|
return ParentEntry;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
|
|
|
|
DE->addContent(std::move(E));
|
|
|
|
return DE->getLastContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE,
|
|
|
|
Entry *NewParentE = nullptr) {
|
|
|
|
StringRef Name = SrcE->getName();
|
|
|
|
switch (SrcE->getKind()) {
|
|
|
|
case EK_Directory: {
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
|
|
|
|
assert(DE && "Must be a directory");
|
|
|
|
// Empty directories could be present in the YAML as a way to
|
|
|
|
// describe a file for a current directory after some of its subdir
|
|
|
|
// is parsed. This only leads to redundant walks, ignore it.
|
|
|
|
if (!Name.empty())
|
|
|
|
NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
|
|
|
|
for (std::unique_ptr<Entry> &SubEntry :
|
|
|
|
llvm::make_range(DE->contents_begin(), DE->contents_end()))
|
|
|
|
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EK_File: {
|
|
|
|
auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
|
|
|
|
assert(FE && "Must be a file");
|
|
|
|
assert(NewParentE && "Parent entry must exist");
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE);
|
|
|
|
DE->addContent(llvm::make_unique<RedirectingFileEntry>(
|
|
|
|
Name, FE->getExternalContentsPath(), FE->getUseName()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-17 10:20:43 +08:00
|
|
|
std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS) {
|
2014-02-22 07:39:37 +08:00
|
|
|
yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
|
|
|
|
if (!M) {
|
|
|
|
error(N, "expected mapping node for file or directory entry");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
KeyStatusPair Fields[] = {
|
|
|
|
KeyStatusPair("name", true),
|
|
|
|
KeyStatusPair("type", true),
|
|
|
|
KeyStatusPair("contents", false),
|
2014-02-27 08:25:12 +08:00
|
|
|
KeyStatusPair("external-contents", false),
|
|
|
|
KeyStatusPair("use-external-name", false),
|
2014-02-22 07:39:37 +08:00
|
|
|
};
|
|
|
|
|
2015-11-30 11:11:10 +08:00
|
|
|
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
bool HasContents = false; // external or otherwise
|
2015-10-07 18:05:44 +08:00
|
|
|
std::vector<std::unique_ptr<Entry>> EntryArrayContents;
|
2014-02-22 07:39:37 +08:00
|
|
|
std::string ExternalContentsPath;
|
|
|
|
std::string Name;
|
2015-10-09 21:28:13 +08:00
|
|
|
auto UseExternalName = RedirectingFileEntry::NK_NotSet;
|
2014-02-22 07:39:37 +08:00
|
|
|
EntryKind Kind;
|
|
|
|
|
|
|
|
for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
|
|
|
|
++I) {
|
|
|
|
StringRef Key;
|
|
|
|
// Reuse the buffer for key and value, since we don't look at key after
|
|
|
|
// parsing value.
|
|
|
|
SmallString<256> Buffer;
|
|
|
|
if (!parseScalarString(I->getKey(), Key, Buffer))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
StringRef Value;
|
|
|
|
if (Key == "name") {
|
|
|
|
if (!parseScalarString(I->getValue(), Value, Buffer))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2016-03-17 10:20:43 +08:00
|
|
|
|
|
|
|
if (FS->UseCanonicalizedPaths) {
|
|
|
|
SmallString<256> Path(Value);
|
|
|
|
// Guarantee that old YAML files containing paths with ".." and "."
|
|
|
|
// are properly canonicalized before read into the VFS.
|
|
|
|
Path = sys::path::remove_leading_dotslash(Path);
|
|
|
|
sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
|
|
|
|
Name = Path.str();
|
|
|
|
} else {
|
|
|
|
Name = Value;
|
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
} else if (Key == "type") {
|
|
|
|
if (!parseScalarString(I->getValue(), Value, Buffer))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
if (Value == "file")
|
|
|
|
Kind = EK_File;
|
|
|
|
else if (Value == "directory")
|
|
|
|
Kind = EK_Directory;
|
|
|
|
else {
|
|
|
|
error(I->getValue(), "unknown value for 'type'");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
} else if (Key == "contents") {
|
|
|
|
if (HasContents) {
|
|
|
|
error(I->getKey(),
|
|
|
|
"entry already has 'contents' or 'external-contents'");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
HasContents = true;
|
|
|
|
yaml::SequenceNode *Contents =
|
|
|
|
dyn_cast<yaml::SequenceNode>(I->getValue());
|
|
|
|
if (!Contents) {
|
|
|
|
// FIXME: this is only for directories, what about files?
|
|
|
|
error(I->getValue(), "expected array");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (yaml::SequenceNode::iterator I = Contents->begin(),
|
|
|
|
E = Contents->end();
|
|
|
|
I != E; ++I) {
|
2016-03-17 10:20:43 +08:00
|
|
|
if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
|
2015-10-07 18:05:44 +08:00
|
|
|
EntryArrayContents.push_back(std::move(E));
|
2014-02-22 07:39:37 +08:00
|
|
|
else
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
} else if (Key == "external-contents") {
|
|
|
|
if (HasContents) {
|
|
|
|
error(I->getKey(),
|
|
|
|
"entry already has 'contents' or 'external-contents'");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
HasContents = true;
|
|
|
|
if (!parseScalarString(I->getValue(), Value, Buffer))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
|
|
|
|
SmallString<256> FullPath;
|
|
|
|
if (FS->IsRelativeOverlay) {
|
|
|
|
FullPath = FS->getExternalContentsPrefixDir();
|
|
|
|
assert(!FullPath.empty() &&
|
|
|
|
"External contents prefix directory must exist");
|
|
|
|
llvm::sys::path::append(FullPath, Value);
|
|
|
|
} else {
|
|
|
|
FullPath = Value;
|
|
|
|
}
|
|
|
|
|
2016-03-17 10:20:43 +08:00
|
|
|
if (FS->UseCanonicalizedPaths) {
|
|
|
|
// Guarantee that old YAML files containing paths with ".." and "."
|
|
|
|
// are properly canonicalized before read into the VFS.
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
FullPath = sys::path::remove_leading_dotslash(FullPath);
|
|
|
|
sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true);
|
2016-03-17 10:20:43 +08:00
|
|
|
}
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
ExternalContentsPath = FullPath.str();
|
2014-02-27 08:25:12 +08:00
|
|
|
} else if (Key == "use-external-name") {
|
|
|
|
bool Val;
|
|
|
|
if (!parseScalarBool(I->getValue(), Val))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2015-10-09 21:28:13 +08:00
|
|
|
UseExternalName = Val ? RedirectingFileEntry::NK_External
|
|
|
|
: RedirectingFileEntry::NK_Virtual;
|
2014-02-22 07:39:37 +08:00
|
|
|
} else {
|
|
|
|
llvm_unreachable("key missing from Keys");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Stream.failed())
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// check for missing keys
|
|
|
|
if (!HasContents) {
|
|
|
|
error(N, "missing key 'contents' or 'external-contents'");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
if (!checkMissingKeys(N, Keys))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-27 08:25:12 +08:00
|
|
|
// check invalid configuration
|
2015-10-09 21:28:13 +08:00
|
|
|
if (Kind == EK_Directory &&
|
|
|
|
UseExternalName != RedirectingFileEntry::NK_NotSet) {
|
2014-02-27 08:25:12 +08:00
|
|
|
error(N, "'use-external-name' is not supported for directories");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-27 08:25:12 +08:00
|
|
|
}
|
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
// Remove trailing slash(es), being careful not to remove the root path
|
2014-02-25 12:34:14 +08:00
|
|
|
StringRef Trimmed(Name);
|
2014-03-06 05:32:20 +08:00
|
|
|
size_t RootPathLen = sys::path::root_path(Trimmed).size();
|
|
|
|
while (Trimmed.size() > RootPathLen &&
|
|
|
|
sys::path::is_separator(Trimmed.back()))
|
2014-02-25 12:34:14 +08:00
|
|
|
Trimmed = Trimmed.slice(0, Trimmed.size()-1);
|
|
|
|
// Get the last component
|
|
|
|
StringRef LastComponent = sys::path::filename(Trimmed);
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
std::unique_ptr<Entry> Result;
|
2014-02-22 07:39:37 +08:00
|
|
|
switch (Kind) {
|
|
|
|
case EK_File:
|
2015-10-09 21:28:13 +08:00
|
|
|
Result = llvm::make_unique<RedirectingFileEntry>(
|
2015-10-07 18:05:44 +08:00
|
|
|
LastComponent, std::move(ExternalContentsPath), UseExternalName);
|
2014-02-25 12:34:14 +08:00
|
|
|
break;
|
2014-02-22 07:39:37 +08:00
|
|
|
case EK_Directory:
|
2015-10-09 21:28:13 +08:00
|
|
|
Result = llvm::make_unique<RedirectingDirectoryEntry>(
|
2015-10-05 21:15:33 +08:00
|
|
|
LastComponent, std::move(EntryArrayContents),
|
2016-11-09 18:52:22 +08:00
|
|
|
Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
|
|
|
|
0, 0, 0, file_type::directory_file, sys::fs::all_all));
|
2014-02-25 12:34:14 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef Parent = sys::path::parent_path(Trimmed);
|
|
|
|
if (Parent.empty())
|
|
|
|
return Result;
|
|
|
|
|
|
|
|
// if 'name' contains multiple components, create implicit directory entries
|
|
|
|
for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
|
|
|
|
E = sys::path::rend(Parent);
|
|
|
|
I != E; ++I) {
|
2015-10-07 18:05:44 +08:00
|
|
|
std::vector<std::unique_ptr<Entry>> Entries;
|
|
|
|
Entries.push_back(std::move(Result));
|
2015-10-09 21:28:13 +08:00
|
|
|
Result = llvm::make_unique<RedirectingDirectoryEntry>(
|
2015-10-07 18:05:44 +08:00
|
|
|
*I, std::move(Entries),
|
2016-11-09 18:52:22 +08:00
|
|
|
Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
|
|
|
|
0, 0, 0, file_type::directory_file, sys::fs::all_all));
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
2014-02-25 12:34:14 +08:00
|
|
|
return Result;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2015-10-07 18:05:44 +08:00
|
|
|
RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// false on error
|
2015-10-07 18:05:44 +08:00
|
|
|
bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
|
2014-02-22 07:39:37 +08:00
|
|
|
yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
|
|
|
|
if (!Top) {
|
|
|
|
error(Root, "expected mapping node");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyStatusPair Fields[] = {
|
|
|
|
KeyStatusPair("version", true),
|
|
|
|
KeyStatusPair("case-sensitive", false),
|
2014-02-27 08:25:12 +08:00
|
|
|
KeyStatusPair("use-external-names", false),
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
KeyStatusPair("overlay-relative", false),
|
2016-08-12 09:50:53 +08:00
|
|
|
KeyStatusPair("ignore-non-existent-contents", false),
|
2014-02-22 07:39:37 +08:00
|
|
|
KeyStatusPair("roots", true),
|
|
|
|
};
|
|
|
|
|
2015-11-30 11:11:10 +08:00
|
|
|
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
std::vector<std::unique_ptr<Entry>> RootEntries;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// Parse configuration and 'roots'
|
|
|
|
for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
|
|
|
|
++I) {
|
|
|
|
SmallString<10> KeyBuffer;
|
|
|
|
StringRef Key;
|
|
|
|
if (!parseScalarString(I->getKey(), Key, KeyBuffer))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Key == "roots") {
|
|
|
|
yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
|
|
|
|
if (!Roots) {
|
|
|
|
error(I->getValue(), "expected array");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
|
|
|
|
I != E; ++I) {
|
2016-03-17 10:20:43 +08:00
|
|
|
if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
RootEntries.push_back(std::move(E));
|
2014-02-22 07:39:37 +08:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (Key == "version") {
|
|
|
|
StringRef VersionString;
|
|
|
|
SmallString<4> Storage;
|
|
|
|
if (!parseScalarString(I->getValue(), VersionString, Storage))
|
|
|
|
return false;
|
|
|
|
int Version;
|
|
|
|
if (VersionString.getAsInteger<int>(10, Version)) {
|
|
|
|
error(I->getValue(), "expected integer");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Version < 0) {
|
|
|
|
error(I->getValue(), "invalid version number");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Version != 0) {
|
|
|
|
error(I->getValue(), "version mismatch, expected 0");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (Key == "case-sensitive") {
|
|
|
|
if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
|
|
|
|
return false;
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
} else if (Key == "overlay-relative") {
|
|
|
|
if (!parseScalarBool(I->getValue(), FS->IsRelativeOverlay))
|
|
|
|
return false;
|
2014-02-27 08:25:12 +08:00
|
|
|
} else if (Key == "use-external-names") {
|
|
|
|
if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
|
|
|
|
return false;
|
2016-08-12 09:50:53 +08:00
|
|
|
} else if (Key == "ignore-non-existent-contents") {
|
|
|
|
if (!parseScalarBool(I->getValue(), FS->IgnoreNonExistentContents))
|
|
|
|
return false;
|
2014-02-22 07:39:37 +08:00
|
|
|
} else {
|
|
|
|
llvm_unreachable("key missing from Keys");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Stream.failed())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!checkMissingKeys(Top, Keys))
|
|
|
|
return false;
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
|
|
|
|
// Now that we sucessefully parsed the YAML file, canonicalize the internal
|
|
|
|
// representation to a proper directory tree so that we can search faster
|
|
|
|
// inside the VFS.
|
|
|
|
for (std::unique_ptr<Entry> &E : RootEntries)
|
|
|
|
uniqueOverlayTree(FS, E.get());
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
Entry::~Entry() = default;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
RedirectingFileSystem *
|
|
|
|
RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
|
|
|
|
SourceMgr::DiagHandlerTy DiagHandler,
|
|
|
|
StringRef YAMLFilePath, void *DiagContext,
|
|
|
|
IntrusiveRefCntPtr<FileSystem> ExternalFS) {
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
SourceMgr SM;
|
2014-08-28 03:03:27 +08:00
|
|
|
yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
SM.setDiagHandler(DiagHandler, DiagContext);
|
2014-02-22 07:39:37 +08:00
|
|
|
yaml::document_iterator DI = Stream.begin();
|
|
|
|
yaml::Node *Root = DI->getRoot();
|
|
|
|
if (DI == Stream.end() || !Root) {
|
|
|
|
SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
RedirectingFileSystemParser P(Stream);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
std::unique_ptr<RedirectingFileSystem> FS(
|
2016-06-13 04:05:23 +08:00
|
|
|
new RedirectingFileSystem(std::move(ExternalFS)));
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
|
|
|
|
if (!YAMLFilePath.empty()) {
|
|
|
|
// Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
|
|
|
|
// to each 'external-contents' path.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
// -ivfsoverlay dummy.cache/vfs/vfs.yaml
|
|
|
|
// yields:
|
|
|
|
// FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
|
|
|
|
//
|
|
|
|
SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
|
|
|
|
std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
|
|
|
|
assert(!EC && "Overlay dir final path must be absolute");
|
|
|
|
(void)EC;
|
|
|
|
FS->setExternalContentsPrefixDir(OverlayAbsDir);
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
if (!P.parse(Root, FS.get()))
|
2014-05-08 14:41:40 +08:00
|
|
|
return nullptr;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-03-08 03:33:25 +08:00
|
|
|
return FS.release();
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
|
2014-03-05 06:34:50 +08:00
|
|
|
SmallString<256> Path;
|
|
|
|
Path_.toVector(Path);
|
|
|
|
|
|
|
|
// Handle relative paths
|
2015-10-05 21:55:20 +08:00
|
|
|
if (std::error_code EC = makeAbsolute(Path))
|
2014-03-05 06:34:50 +08:00
|
|
|
return EC;
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2016-03-17 10:20:43 +08:00
|
|
|
// Canonicalize path by removing ".", "..", "./", etc components. This is
|
|
|
|
// a VFS request, do bot bother about symlinks in the path components
|
|
|
|
// but canonicalize in order to perform the correct entry search.
|
|
|
|
if (UseCanonicalizedPaths) {
|
|
|
|
Path = sys::path::remove_leading_dotslash(Path);
|
|
|
|
sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
if (Path.empty())
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::invalid_argument);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
sys::path::const_iterator Start = sys::path::begin(Path);
|
|
|
|
sys::path::const_iterator End = sys::path::end(Path);
|
2015-10-07 18:05:44 +08:00
|
|
|
for (const std::unique_ptr<Entry> &Root : Roots) {
|
|
|
|
ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
|
2014-06-14 01:20:50 +08:00
|
|
|
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
2014-02-22 07:39:37 +08:00
|
|
|
return Result;
|
|
|
|
}
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
ErrorOr<Entry *>
|
|
|
|
RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
|
|
|
|
sys::path::const_iterator End, Entry *From) {
|
2016-03-17 10:20:43 +08:00
|
|
|
#ifndef LLVM_ON_WIN32
|
|
|
|
assert(!isTraversalComponent(*Start) &&
|
|
|
|
!isTraversalComponent(From->getName()) &&
|
|
|
|
"Paths should not contain traversal components");
|
|
|
|
#else
|
|
|
|
// FIXME: this is here to support windows, remove it once canonicalized
|
|
|
|
// paths become globally default.
|
2016-02-24 01:06:50 +08:00
|
|
|
if (Start->equals("."))
|
|
|
|
++Start;
|
2016-03-17 10:20:43 +08:00
|
|
|
#endif
|
2014-03-05 06:34:50 +08:00
|
|
|
|
2016-03-31 07:54:00 +08:00
|
|
|
StringRef FromName = From->getName();
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2016-03-31 07:54:00 +08:00
|
|
|
// Forward the search to the next component in case this is an empty one.
|
|
|
|
if (!FromName.empty()) {
|
|
|
|
if (CaseSensitive ? !Start->equals(FromName)
|
|
|
|
: !Start->equals_lower(FromName))
|
|
|
|
// failure to match
|
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2016-03-31 07:54:00 +08:00
|
|
|
++Start;
|
|
|
|
|
|
|
|
if (Start == End) {
|
|
|
|
// Match!
|
|
|
|
return From;
|
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
|
2014-02-22 07:39:37 +08:00
|
|
|
if (!DE)
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::not_a_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
for (const std::unique_ptr<Entry> &DirEntry :
|
|
|
|
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
|
|
|
|
ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
|
2014-06-14 01:20:50 +08:00
|
|
|
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
|
2014-02-22 07:39:37 +08:00
|
|
|
return Result;
|
|
|
|
}
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2015-12-11 07:41:39 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
|
2014-06-25 03:37:16 +08:00
|
|
|
assert(E != nullptr);
|
2015-10-09 21:28:13 +08:00
|
|
|
if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {
|
2014-02-22 07:39:37 +08:00
|
|
|
ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
|
2014-02-27 08:25:12 +08:00
|
|
|
assert(!S || S->getName() == F->getExternalContentsPath());
|
2014-05-24 02:15:47 +08:00
|
|
|
if (S)
|
2015-12-11 07:41:39 +08:00
|
|
|
return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
|
|
|
|
*S);
|
2014-02-22 07:39:37 +08:00
|
|
|
return S;
|
|
|
|
} else { // directory
|
2015-10-09 21:28:13 +08:00
|
|
|
auto *DE = cast<RedirectingDirectoryEntry>(E);
|
2015-12-11 07:41:39 +08:00
|
|
|
return Status::copyWithNewName(DE->getStatus(), Path.str());
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
|
2014-06-25 03:37:16 +08:00
|
|
|
ErrorOr<Entry *> Result = lookupPath(Path);
|
|
|
|
if (!Result)
|
|
|
|
return Result.getError();
|
|
|
|
return status(Path, *Result);
|
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:09 +08:00
|
|
|
namespace {
|
2015-12-11 07:41:39 +08:00
|
|
|
/// Provide a file wrapper with an overriden status.
|
|
|
|
class FileWithFixedStatus : public File {
|
2015-10-05 21:55:09 +08:00
|
|
|
std::unique_ptr<File> InnerFile;
|
2015-12-11 07:41:39 +08:00
|
|
|
Status S;
|
2015-10-05 21:55:09 +08:00
|
|
|
|
|
|
|
public:
|
2015-12-11 07:41:39 +08:00
|
|
|
FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
|
2016-05-27 22:27:13 +08:00
|
|
|
: InnerFile(std::move(InnerFile)), S(std::move(S)) {}
|
2015-12-11 07:41:39 +08:00
|
|
|
|
|
|
|
ErrorOr<Status> status() override { return S; }
|
|
|
|
ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
|
2015-10-06 05:20:19 +08:00
|
|
|
getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
|
|
|
|
bool IsVolatile) override {
|
2015-10-05 21:55:09 +08:00
|
|
|
return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
|
|
|
|
IsVolatile);
|
|
|
|
}
|
|
|
|
std::error_code close() override { return InnerFile->close(); }
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2015-10-07 18:05:44 +08:00
|
|
|
ErrorOr<std::unique_ptr<File>>
|
|
|
|
RedirectingFileSystem::openFileForRead(const Twine &Path) {
|
2014-02-22 07:39:37 +08:00
|
|
|
ErrorOr<Entry *> E = lookupPath(Path);
|
|
|
|
if (!E)
|
|
|
|
return E.getError();
|
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
auto *F = dyn_cast<RedirectingFileEntry>(*E);
|
2014-02-22 07:39:37 +08:00
|
|
|
if (!F) // FIXME: errc::not_a_file?
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::invalid_argument);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
|
|
|
|
if (!Result)
|
|
|
|
return Result;
|
2014-03-01 05:16:07 +08:00
|
|
|
|
2015-12-11 07:41:39 +08:00
|
|
|
auto ExternalStatus = (*Result)->status();
|
|
|
|
if (!ExternalStatus)
|
|
|
|
return ExternalStatus.getError();
|
2014-03-01 05:16:07 +08:00
|
|
|
|
2015-12-11 07:41:39 +08:00
|
|
|
// 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));
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<FileSystem>
|
2014-08-18 06:12:58 +08:00
|
|
|
vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
SourceMgr::DiagHandlerTy DiagHandler,
|
|
|
|
StringRef YAMLFilePath,
|
|
|
|
void *DiagContext,
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<FileSystem> ExternalFS) {
|
2015-10-07 18:05:44 +08:00
|
|
|
return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
|
2016-06-13 04:05:23 +08:00
|
|
|
YAMLFilePath, DiagContext,
|
|
|
|
std::move(ExternalFS));
|
2014-02-22 07:39:37 +08:00
|
|
|
}
|
|
|
|
|
2016-12-22 15:06:03 +08:00
|
|
|
static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path,
|
|
|
|
SmallVectorImpl<YAMLVFSEntry> &Entries) {
|
|
|
|
auto Kind = SrcE->getKind();
|
|
|
|
if (Kind == EK_Directory) {
|
|
|
|
auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
|
|
|
|
assert(DE && "Must be a directory");
|
|
|
|
for (std::unique_ptr<Entry> &SubEntry :
|
|
|
|
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
|
|
|
|
Path.push_back(SubEntry->getName());
|
|
|
|
getVFSEntries(SubEntry.get(), Path, Entries);
|
|
|
|
Path.pop_back();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(Kind == EK_File && "Must be a EK_File");
|
|
|
|
auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
|
|
|
|
assert(FE && "Must be a file");
|
|
|
|
SmallString<128> VPath;
|
|
|
|
for (auto &Comp : Path)
|
|
|
|
llvm::sys::path::append(VPath, Comp);
|
|
|
|
Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
|
|
|
|
SourceMgr::DiagHandlerTy DiagHandler,
|
|
|
|
StringRef YAMLFilePath,
|
|
|
|
SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
|
|
|
|
void *DiagContext,
|
|
|
|
IntrusiveRefCntPtr<FileSystem> ExternalFS) {
|
|
|
|
RedirectingFileSystem *VFS = RedirectingFileSystem::create(
|
|
|
|
std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
|
|
|
|
std::move(ExternalFS));
|
|
|
|
ErrorOr<Entry *> RootE = VFS->lookupPath("/");
|
|
|
|
if (!RootE)
|
|
|
|
return;
|
|
|
|
SmallVector<StringRef, 8> Components;
|
|
|
|
Components.push_back("/");
|
|
|
|
getVFSEntries(*RootE, Components, CollectedEntries);
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
UniqueID vfs::getNextVirtualUniqueID() {
|
2014-03-03 01:08:31 +08:00
|
|
|
static std::atomic<unsigned> UID;
|
|
|
|
unsigned ID = ++UID;
|
2014-02-22 07:39:37 +08:00
|
|
|
// The following assumes that uint64_t max will never collide with a real
|
|
|
|
// dev_t value from the OS.
|
|
|
|
return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
|
|
|
|
}
|
2014-05-21 05:43:27 +08:00
|
|
|
|
|
|
|
void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
|
|
|
|
assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
|
|
|
|
assert(sys::path::is_absolute(RealPath) && "real path not absolute");
|
|
|
|
assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
|
|
|
|
Mappings.emplace_back(VirtualPath, RealPath);
|
|
|
|
}
|
|
|
|
|
2014-05-22 06:46:51 +08:00
|
|
|
namespace {
|
|
|
|
class JSONWriter {
|
|
|
|
llvm::raw_ostream &OS;
|
|
|
|
SmallVector<StringRef, 16> DirStack;
|
|
|
|
inline unsigned getDirIndent() { return 4 * DirStack.size(); }
|
|
|
|
inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
|
|
|
|
bool containedIn(StringRef Parent, StringRef Path);
|
|
|
|
StringRef containedPart(StringRef Parent, StringRef Path);
|
|
|
|
void startDirectory(StringRef Path);
|
|
|
|
void endDirectory();
|
|
|
|
void writeEntry(StringRef VPath, StringRef RPath);
|
2014-05-21 05:43:27 +08:00
|
|
|
|
2014-05-22 06:46:51 +08:00
|
|
|
public:
|
|
|
|
JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
|
2016-04-14 03:28:21 +08:00
|
|
|
void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames,
|
|
|
|
Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative,
|
2016-08-12 09:50:53 +08:00
|
|
|
Optional<bool> IgnoreNonExistentContents, StringRef OverlayDir);
|
2014-05-22 06:46:51 +08:00
|
|
|
};
|
2015-06-23 07:07:51 +08:00
|
|
|
}
|
2014-05-21 05:43:27 +08:00
|
|
|
|
2014-05-22 06:46:51 +08:00
|
|
|
bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
|
2014-05-21 06:12:58 +08:00
|
|
|
using namespace llvm::sys;
|
|
|
|
// Compare each path component.
|
|
|
|
auto IParent = path::begin(Parent), EParent = path::end(Parent);
|
|
|
|
for (auto IChild = path::begin(Path), EChild = path::end(Path);
|
|
|
|
IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
|
|
|
|
if (*IParent != *IChild)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Have we exhausted the parent path?
|
|
|
|
return IParent == EParent;
|
2014-05-21 05:43:27 +08:00
|
|
|
}
|
|
|
|
|
2014-05-22 06:46:51 +08:00
|
|
|
StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
|
|
|
|
assert(!Parent.empty());
|
2014-05-21 05:43:27 +08:00
|
|
|
assert(containedIn(Parent, Path));
|
|
|
|
return Path.slice(Parent.size() + 1, StringRef::npos);
|
|
|
|
}
|
|
|
|
|
2014-05-22 06:46:51 +08:00
|
|
|
void JSONWriter::startDirectory(StringRef Path) {
|
|
|
|
StringRef Name =
|
|
|
|
DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
|
|
|
|
DirStack.push_back(Path);
|
|
|
|
unsigned Indent = getDirIndent();
|
|
|
|
OS.indent(Indent) << "{\n";
|
|
|
|
OS.indent(Indent + 2) << "'type': 'directory',\n";
|
|
|
|
OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
|
|
|
|
OS.indent(Indent + 2) << "'contents': [\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSONWriter::endDirectory() {
|
|
|
|
unsigned Indent = getDirIndent();
|
|
|
|
OS.indent(Indent + 2) << "]\n";
|
|
|
|
OS.indent(Indent) << "}";
|
|
|
|
|
|
|
|
DirStack.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
|
|
|
|
unsigned Indent = getFileIndent();
|
|
|
|
OS.indent(Indent) << "{\n";
|
|
|
|
OS.indent(Indent + 2) << "'type': 'file',\n";
|
|
|
|
OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
|
|
|
|
OS.indent(Indent + 2) << "'external-contents': \""
|
|
|
|
<< llvm::yaml::escape(RPath) << "\"\n";
|
|
|
|
OS.indent(Indent) << "}";
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
|
2016-04-14 03:28:21 +08:00
|
|
|
Optional<bool> UseExternalNames,
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
Optional<bool> IsCaseSensitive,
|
|
|
|
Optional<bool> IsOverlayRelative,
|
2016-08-12 09:50:53 +08:00
|
|
|
Optional<bool> IgnoreNonExistentContents,
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
StringRef OverlayDir) {
|
2014-05-22 06:46:51 +08:00
|
|
|
using namespace llvm::sys;
|
2014-05-21 05:43:27 +08:00
|
|
|
|
|
|
|
OS << "{\n"
|
|
|
|
" 'version': 0,\n";
|
2014-05-22 06:46:51 +08:00
|
|
|
if (IsCaseSensitive.hasValue())
|
|
|
|
OS << " 'case-sensitive': '"
|
|
|
|
<< (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
|
2016-04-14 03:28:21 +08:00
|
|
|
if (UseExternalNames.hasValue())
|
|
|
|
OS << " 'use-external-names': '"
|
|
|
|
<< (UseExternalNames.getValue() ? "true" : "false") << "',\n";
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
bool UseOverlayRelative = false;
|
|
|
|
if (IsOverlayRelative.hasValue()) {
|
|
|
|
UseOverlayRelative = IsOverlayRelative.getValue();
|
|
|
|
OS << " 'overlay-relative': '"
|
|
|
|
<< (UseOverlayRelative ? "true" : "false") << "',\n";
|
|
|
|
}
|
2016-08-12 09:50:53 +08:00
|
|
|
if (IgnoreNonExistentContents.hasValue())
|
|
|
|
OS << " 'ignore-non-existent-contents': '"
|
|
|
|
<< (IgnoreNonExistentContents.getValue() ? "true" : "false") << "',\n";
|
2014-05-21 05:43:27 +08:00
|
|
|
OS << " 'roots': [\n";
|
2014-05-22 06:46:51 +08:00
|
|
|
|
2014-07-15 09:24:35 +08:00
|
|
|
if (!Entries.empty()) {
|
|
|
|
const YAMLVFSEntry &Entry = Entries.front();
|
|
|
|
startDirectory(path::parent_path(Entry.VPath));
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
|
|
|
|
StringRef RPath = Entry.RPath;
|
|
|
|
if (UseOverlayRelative) {
|
|
|
|
unsigned OverlayDirLen = OverlayDir.size();
|
|
|
|
assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
|
|
|
|
"Overlay dir must be contained in RPath");
|
|
|
|
RPath = RPath.slice(OverlayDirLen, RPath.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
writeEntry(path::filename(Entry.VPath), RPath);
|
2014-07-15 09:24:35 +08:00
|
|
|
|
|
|
|
for (const auto &Entry : Entries.slice(1)) {
|
|
|
|
StringRef Dir = path::parent_path(Entry.VPath);
|
|
|
|
if (Dir == DirStack.back())
|
|
|
|
OS << ",\n";
|
|
|
|
else {
|
|
|
|
while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
|
|
|
|
OS << "\n";
|
|
|
|
endDirectory();
|
|
|
|
}
|
|
|
|
OS << ",\n";
|
|
|
|
startDirectory(Dir);
|
2014-05-22 06:46:51 +08:00
|
|
|
}
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
StringRef RPath = Entry.RPath;
|
|
|
|
if (UseOverlayRelative) {
|
|
|
|
unsigned OverlayDirLen = OverlayDir.size();
|
|
|
|
assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
|
|
|
|
"Overlay dir must be contained in RPath");
|
|
|
|
RPath = RPath.slice(OverlayDirLen, RPath.size());
|
|
|
|
}
|
|
|
|
writeEntry(path::filename(Entry.VPath), RPath);
|
2014-05-22 06:46:51 +08:00
|
|
|
}
|
|
|
|
|
2014-07-15 09:24:35 +08:00
|
|
|
while (!DirStack.empty()) {
|
|
|
|
OS << "\n";
|
|
|
|
endDirectory();
|
|
|
|
}
|
2014-05-22 06:46:51 +08:00
|
|
|
OS << "\n";
|
|
|
|
}
|
|
|
|
|
2014-07-15 09:24:35 +08:00
|
|
|
OS << " ]\n"
|
2014-05-21 05:43:27 +08:00
|
|
|
<< "}\n";
|
|
|
|
}
|
2014-05-22 06:46:51 +08:00
|
|
|
|
|
|
|
void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
|
|
|
|
std::sort(Mappings.begin(), Mappings.end(),
|
|
|
|
[](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
|
|
|
|
return LHS.VPath < RHS.VPath;
|
|
|
|
});
|
|
|
|
|
2016-04-14 03:28:21 +08:00
|
|
|
JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
|
2016-08-12 09:50:53 +08:00
|
|
|
IsOverlayRelative, IgnoreNonExistentContents,
|
|
|
|
OverlayDir);
|
2014-05-22 06:46:51 +08:00
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
|
2015-10-09 21:28:13 +08:00
|
|
|
VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
|
|
|
|
const Twine &_Path, RedirectingFileSystem &FS,
|
|
|
|
RedirectingDirectoryEntry::iterator Begin,
|
|
|
|
RedirectingDirectoryEntry::iterator End, std::error_code &EC)
|
2014-06-25 03:37:16 +08:00
|
|
|
: Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
|
2016-08-13 02:18:24 +08:00
|
|
|
while (Current != End) {
|
2014-06-25 03:37:16 +08:00
|
|
|
SmallString<128> PathStr(Dir);
|
|
|
|
llvm::sys::path::append(PathStr, (*Current)->getName());
|
2015-03-18 18:17:07 +08:00
|
|
|
llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
|
2016-08-13 02:18:24 +08:00
|
|
|
if (S) {
|
2014-06-25 03:37:16 +08:00
|
|
|
CurrentEntry = *S;
|
2016-08-13 02:18:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Skip entries which do not map to a reliable external content.
|
|
|
|
if (FS.ignoreNonExistentContents() &&
|
|
|
|
S.getError() == llvm::errc::no_such_file_or_directory) {
|
|
|
|
++Current;
|
|
|
|
continue;
|
|
|
|
} else {
|
2014-06-25 03:37:16 +08:00
|
|
|
EC = S.getError();
|
2016-08-13 02:18:24 +08:00
|
|
|
break;
|
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code VFSFromYamlDirIterImpl::increment() {
|
|
|
|
assert(Current != End && "cannot iterate past end");
|
2016-08-13 02:18:24 +08:00
|
|
|
while (++Current != End) {
|
2014-06-25 03:37:16 +08:00
|
|
|
SmallString<128> PathStr(Dir);
|
|
|
|
llvm::sys::path::append(PathStr, (*Current)->getName());
|
2015-03-18 18:17:07 +08:00
|
|
|
llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
|
2016-08-13 02:18:24 +08:00
|
|
|
if (!S) {
|
|
|
|
// Skip entries which do not map to a reliable external content.
|
|
|
|
if (FS.ignoreNonExistentContents() &&
|
|
|
|
S.getError() == llvm::errc::no_such_file_or_directory) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return S.getError();
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
CurrentEntry = *S;
|
2016-08-13 02:18:24 +08:00
|
|
|
break;
|
2016-08-12 10:17:26 +08:00
|
|
|
}
|
2016-08-13 02:18:24 +08:00
|
|
|
|
|
|
|
if (Current == End)
|
|
|
|
CurrentEntry = Status();
|
2014-06-25 03:37:16 +08:00
|
|
|
return std::error_code();
|
|
|
|
}
|
2014-06-26 04:25:40 +08:00
|
|
|
|
|
|
|
vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
|
|
|
|
const Twine &Path,
|
|
|
|
std::error_code &EC)
|
|
|
|
: FS(&FS_) {
|
|
|
|
directory_iterator I = FS->dir_begin(Path, EC);
|
2017-03-14 08:14:40 +08:00
|
|
|
if (I != directory_iterator()) {
|
2014-06-26 04:25:40 +08:00
|
|
|
State = std::make_shared<IterState>();
|
|
|
|
State->push(I);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vfs::recursive_directory_iterator &
|
|
|
|
recursive_directory_iterator::increment(std::error_code &EC) {
|
|
|
|
assert(FS && State && !State->empty() && "incrementing past end");
|
|
|
|
assert(State->top()->isStatusKnown() && "non-canonical end iterator");
|
|
|
|
vfs::directory_iterator End;
|
|
|
|
if (State->top()->isDirectory()) {
|
|
|
|
vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
|
|
|
|
if (I != End) {
|
|
|
|
State->push(I);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!State->empty() && State->top().increment(EC) == End)
|
|
|
|
State->pop();
|
|
|
|
|
|
|
|
if (State->empty())
|
|
|
|
State.reset(); // end iterator
|
|
|
|
|
|
|
|
return *this;
|
2014-07-07 01:43:24 +08:00
|
|
|
}
|