blob: 04383dbb878cda32e8603ecf44e5f18c0545b59a [file] [log] [blame]
Ben Langmuirc8130a72014-02-20 21:59:23 +00001//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9// This file implements the VirtualFileSystem interface.
10//===----------------------------------------------------------------------===//
11
12#include "clang/Basic/VirtualFileSystem.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000013#include "llvm/ADT/DenseMap.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000014#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringExtras.h"
Ben Langmuir740812b2014-06-24 19:37:16 +000016#include "llvm/ADT/StringSet.h"
Chandler Carruth0d9593d2015-01-14 11:29:14 +000017#include "llvm/ADT/iterator_range.h"
Rafael Espindola71de0b62014-06-13 17:20:50 +000018#include "llvm/Support/Errc.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000019#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000020#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000021#include "llvm/Support/YAMLParser.h"
Benjamin Kramerd6bbee72015-10-05 14:06:36 +000022#include "llvm/Config/llvm-config.h"
Benjamin Kramer4527fb22014-03-02 17:08:31 +000023#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000024#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000025
Benjamin Kramerd6bbee72015-10-05 14:06:36 +000026// For chdir.
27#ifdef LLVM_ON_WIN32
28# include <direct.h>
29#else
30# include <unistd.h>
31#endif
32
Ben Langmuirc8130a72014-02-20 21:59:23 +000033using namespace clang;
34using namespace clang::vfs;
35using namespace llvm;
36using llvm::sys::fs::file_status;
37using llvm::sys::fs::file_type;
38using llvm::sys::fs::perms;
39using llvm::sys::fs::UniqueID;
40
41Status::Status(const file_status &Status)
42 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
43 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Ben Langmuir5de00f32014-05-23 18:15:47 +000044 Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000045
Benjamin Kramer268b51a2015-10-05 13:15:33 +000046Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
47 uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
48 perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000049 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
Ben Langmuir5de00f32014-05-23 18:15:47 +000050 Type(Type), Perms(Perms), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000051
Benjamin Kramer268b51a2015-10-05 13:15:33 +000052Status Status::copyWithNewName(const Status &In, StringRef NewName) {
53 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
54 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
55 In.getPermissions());
56}
57
58Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
59 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
60 In.getUser(), In.getGroup(), In.getSize(), In.type(),
61 In.permissions());
62}
63
Ben Langmuirc8130a72014-02-20 21:59:23 +000064bool Status::equivalent(const Status &Other) const {
65 return getUniqueID() == Other.getUniqueID();
66}
67bool Status::isDirectory() const {
68 return Type == file_type::directory_file;
69}
70bool Status::isRegularFile() const {
71 return Type == file_type::regular_file;
72}
73bool Status::isOther() const {
74 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
75}
76bool Status::isSymlink() const {
77 return Type == file_type::symlink_file;
78}
79bool Status::isStatusKnown() const {
80 return Type != file_type::status_error;
81}
82bool Status::exists() const {
83 return isStatusKnown() && Type != file_type::file_not_found;
84}
85
86File::~File() {}
87
88FileSystem::~FileSystem() {}
89
Benjamin Kramera8857962014-10-26 22:44:13 +000090ErrorOr<std::unique_ptr<MemoryBuffer>>
91FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
92 bool RequiresNullTerminator, bool IsVolatile) {
93 auto F = openFileForRead(Name);
94 if (!F)
95 return F.getError();
Ben Langmuirc8130a72014-02-20 21:59:23 +000096
Benjamin Kramera8857962014-10-26 22:44:13 +000097 return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000098}
99
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000100std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
101 auto WorkingDir = getCurrentWorkingDirectory();
102 if (!WorkingDir)
103 return WorkingDir.getError();
104
105 return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
106}
107
Benjamin Kramerd45b2052015-10-07 15:48:01 +0000108bool FileSystem::exists(const Twine &Path) {
109 auto Status = status(Path);
110 return Status && Status->exists();
111}
112
Ben Langmuirc8130a72014-02-20 21:59:23 +0000113//===-----------------------------------------------------------------------===/
114// RealFileSystem implementation
115//===-----------------------------------------------------------------------===/
116
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000117namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000118/// \brief Wrapper around a raw file descriptor.
119class RealFile : public File {
120 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +0000121 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000122 friend class RealFileSystem;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000123 RealFile(int FD, StringRef NewName)
124 : FD(FD), S(NewName, {}, {}, {}, {}, {},
125 llvm::sys::fs::file_type::status_error, {}) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000126 assert(FD >= 0 && "Invalid or inactive file descriptor");
127 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000128
Ben Langmuirc8130a72014-02-20 21:59:23 +0000129public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000130 ~RealFile() override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000131 ErrorOr<Status> status() override;
Benjamin Kramer737501c2015-10-05 21:20:19 +0000132 ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
133 int64_t FileSize,
134 bool RequiresNullTerminator,
135 bool IsVolatile) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000136 std::error_code close() override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000137};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000138} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000139RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000140
141ErrorOr<Status> RealFile::status() {
142 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000143 if (!S.isStatusKnown()) {
144 file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000145 if (std::error_code EC = sys::fs::status(FD, RealStatus))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000146 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000147 S = Status::copyWithNewName(RealStatus, S.getName());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000148 }
149 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000150}
151
Benjamin Kramera8857962014-10-26 22:44:13 +0000152ErrorOr<std::unique_ptr<MemoryBuffer>>
153RealFile::getBuffer(const Twine &Name, int64_t FileSize,
154 bool RequiresNullTerminator, bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000155 assert(FD != -1 && "cannot get buffer for closed file");
Benjamin Kramera8857962014-10-26 22:44:13 +0000156 return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
157 IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000158}
159
160// FIXME: This is terrible, we need this for ::close.
161#if !defined(_MSC_VER) && !defined(__MINGW32__)
162#include <unistd.h>
163#include <sys/uio.h>
164#else
165#include <io.h>
166#ifndef S_ISFIFO
167#define S_ISFIFO(x) (0)
168#endif
169#endif
Rafael Espindola8e650d72014-06-12 20:37:59 +0000170std::error_code RealFile::close() {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000171 if (::close(FD))
Rafael Espindola8e650d72014-06-12 20:37:59 +0000172 return std::error_code(errno, std::generic_category());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000173 FD = -1;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000174 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000175}
176
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000177namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000178/// \brief The file system according to your operating system.
179class RealFileSystem : public FileSystem {
180public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000181 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000182 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000183 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000184
185 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
186 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000187};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000188} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000189
190ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
191 sys::fs::file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000192 if (std::error_code EC = sys::fs::status(Path, RealStatus))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000193 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000194 return Status::copyWithNewName(RealStatus, Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000195}
196
Benjamin Kramera8857962014-10-26 22:44:13 +0000197ErrorOr<std::unique_ptr<File>>
198RealFileSystem::openFileForRead(const Twine &Name) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000199 int FD;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000200 if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000201 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000202 return std::unique_ptr<File>(new RealFile(FD, Name.str()));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000203}
204
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000205llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
206 SmallString<256> Dir;
207 if (std::error_code EC = llvm::sys::fs::current_path(Dir))
208 return EC;
209 return Dir.str().str();
210}
211
212std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
213 // FIXME: chdir is thread hostile; on the other hand, creating the same
214 // behavior as chdir is complex: chdir resolves the path once, thus
215 // guaranteeing that all subsequent relative path operations work
216 // on the same path the original chdir resulted in. This makes a
217 // difference for example on network filesystems, where symlinks might be
218 // switched during runtime of the tool. Fixing this depends on having a
219 // file system abstraction that allows openat() style interactions.
220 SmallString<256> Storage;
221 StringRef Dir = Path.toNullTerminatedStringRef(Storage);
222 if (int Err = ::chdir(Dir.data()))
223 return std::error_code(Err, std::generic_category());
224 return std::error_code();
225}
226
Ben Langmuirc8130a72014-02-20 21:59:23 +0000227IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
228 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
229 return FS;
230}
231
Ben Langmuir740812b2014-06-24 19:37:16 +0000232namespace {
233class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
234 std::string Path;
235 llvm::sys::fs::directory_iterator Iter;
236public:
237 RealFSDirIter(const Twine &_Path, std::error_code &EC)
238 : Path(_Path.str()), Iter(Path, EC) {
239 if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
240 llvm::sys::fs::file_status S;
241 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000242 if (!EC)
243 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000244 }
245 }
246
247 std::error_code increment() override {
248 std::error_code EC;
249 Iter.increment(EC);
250 if (EC) {
251 return EC;
252 } else if (Iter == llvm::sys::fs::directory_iterator()) {
253 CurrentEntry = Status();
254 } else {
255 llvm::sys::fs::file_status S;
256 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000257 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000258 }
259 return EC;
260 }
261};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000262}
Ben Langmuir740812b2014-06-24 19:37:16 +0000263
264directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
265 std::error_code &EC) {
266 return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
267}
268
Ben Langmuirc8130a72014-02-20 21:59:23 +0000269//===-----------------------------------------------------------------------===/
270// OverlayFileSystem implementation
271//===-----------------------------------------------------------------------===/
272OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000273 FSList.push_back(BaseFS);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000274}
275
276void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
277 FSList.push_back(FS);
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000278 // Synchronize added file systems by duplicating the working directory from
279 // the first one in the list.
280 FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000281}
282
283ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
284 // FIXME: handle symlinks that cross file systems
285 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
286 ErrorOr<Status> Status = (*I)->status(Path);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000287 if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000288 return Status;
289 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000290 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000291}
292
Benjamin Kramera8857962014-10-26 22:44:13 +0000293ErrorOr<std::unique_ptr<File>>
294OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000295 // FIXME: handle symlinks that cross file systems
296 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
Benjamin Kramera8857962014-10-26 22:44:13 +0000297 auto Result = (*I)->openFileForRead(Path);
298 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
299 return Result;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000300 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000301 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000302}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000303
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000304llvm::ErrorOr<std::string>
305OverlayFileSystem::getCurrentWorkingDirectory() const {
306 // All file systems are synchronized, just take the first working directory.
307 return FSList.front()->getCurrentWorkingDirectory();
308}
309std::error_code
310OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
311 for (auto &FS : FSList)
312 if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
313 return EC;
314 return std::error_code();
315}
316
Ben Langmuir740812b2014-06-24 19:37:16 +0000317clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
318
319namespace {
320class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
321 OverlayFileSystem &Overlays;
322 std::string Path;
323 OverlayFileSystem::iterator CurrentFS;
324 directory_iterator CurrentDirIter;
325 llvm::StringSet<> SeenNames;
326
327 std::error_code incrementFS() {
328 assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
329 ++CurrentFS;
330 for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
331 std::error_code EC;
332 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
333 if (EC && EC != errc::no_such_file_or_directory)
334 return EC;
335 if (CurrentDirIter != directory_iterator())
336 break; // found
337 }
338 return std::error_code();
339 }
340
341 std::error_code incrementDirIter(bool IsFirstTime) {
342 assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
343 "incrementing past end");
344 std::error_code EC;
345 if (!IsFirstTime)
346 CurrentDirIter.increment(EC);
347 if (!EC && CurrentDirIter == directory_iterator())
348 EC = incrementFS();
349 return EC;
350 }
351
352 std::error_code incrementImpl(bool IsFirstTime) {
353 while (true) {
354 std::error_code EC = incrementDirIter(IsFirstTime);
355 if (EC || CurrentDirIter == directory_iterator()) {
356 CurrentEntry = Status();
357 return EC;
358 }
359 CurrentEntry = *CurrentDirIter;
360 StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
David Blaikie61b86d42014-11-19 02:56:13 +0000361 if (SeenNames.insert(Name).second)
Ben Langmuir740812b2014-06-24 19:37:16 +0000362 return EC; // name not seen before
363 }
364 llvm_unreachable("returned above");
365 }
366
367public:
368 OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
369 std::error_code &EC)
370 : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
371 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
372 EC = incrementImpl(true);
373 }
374
375 std::error_code increment() override { return incrementImpl(false); }
376};
377} // end anonymous namespace
378
379directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
380 std::error_code &EC) {
381 return directory_iterator(
382 std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
383}
384
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000385namespace clang {
386namespace vfs {
387namespace detail {
388
389enum InMemoryNodeKind { IME_File, IME_Directory };
390
391/// The in memory file system is a tree of Nodes. Every node can either be a
392/// file or a directory.
393class InMemoryNode {
394 Status Stat;
395 InMemoryNodeKind Kind;
396
397public:
398 InMemoryNode(Status Stat, InMemoryNodeKind Kind)
399 : Stat(std::move(Stat)), Kind(Kind) {}
400 virtual ~InMemoryNode() {}
401 const Status &getStatus() const { return Stat; }
402 InMemoryNodeKind getKind() const { return Kind; }
403 virtual std::string toString(unsigned Indent) const = 0;
404};
405
406namespace {
407class InMemoryFile : public InMemoryNode {
408 std::unique_ptr<llvm::MemoryBuffer> Buffer;
409
410public:
411 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
412 : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
413
414 llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
415 std::string toString(unsigned Indent) const override {
416 return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
417 }
418 static bool classof(const InMemoryNode *N) {
419 return N->getKind() == IME_File;
420 }
421};
422
423/// Adapt a InMemoryFile for VFS' File interface.
424class InMemoryFileAdaptor : public File {
425 InMemoryFile &Node;
426
427public:
428 explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
429
430 llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
431 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
Benjamin Kramer737501c2015-10-05 21:20:19 +0000432 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
433 bool IsVolatile) override {
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000434 llvm::MemoryBuffer *Buf = Node.getBuffer();
435 return llvm::MemoryBuffer::getMemBuffer(
436 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
437 }
438 std::error_code close() override { return std::error_code(); }
439};
440} // end anonymous namespace
441
442class InMemoryDirectory : public InMemoryNode {
443 std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
444
445public:
446 InMemoryDirectory(Status Stat)
447 : InMemoryNode(std::move(Stat), IME_Directory) {}
448 InMemoryNode *getChild(StringRef Name) {
449 auto I = Entries.find(Name);
450 if (I != Entries.end())
451 return I->second.get();
452 return nullptr;
453 }
454 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
455 return Entries.insert(make_pair(Name, std::move(Child)))
456 .first->second.get();
457 }
458
459 typedef decltype(Entries)::const_iterator const_iterator;
460 const_iterator begin() const { return Entries.begin(); }
461 const_iterator end() const { return Entries.end(); }
462
463 std::string toString(unsigned Indent) const override {
464 std::string Result =
465 (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
466 for (const auto &Entry : Entries) {
467 Result += Entry.second->toString(Indent + 2);
468 }
469 return Result;
470 }
471 static bool classof(const InMemoryNode *N) {
472 return N->getKind() == IME_Directory;
473 }
474};
475}
476
477InMemoryFileSystem::InMemoryFileSystem()
478 : Root(new detail::InMemoryDirectory(
479 Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
480 0, 0, 0, llvm::sys::fs::file_type::directory_file,
481 llvm::sys::fs::perms::all_all))) {}
482
483InMemoryFileSystem::~InMemoryFileSystem() {}
484
485StringRef InMemoryFileSystem::toString() const {
486 return Root->toString(/*Indent=*/0);
487}
488
489void InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
490 std::unique_ptr<llvm::MemoryBuffer> Buffer) {
491 SmallString<128> Path;
492 P.toVector(Path);
493
494 // Fix up relative paths. This just prepends the current working directory.
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000495 std::error_code EC = makeAbsolute(Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000496 assert(!EC);
497 (void)EC;
498
499 detail::InMemoryDirectory *Dir = Root.get();
500 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
501 while (true) {
502 StringRef Name = *I;
Benjamin Kramerd5e0b582015-10-07 08:32:50 +0000503 // Skip over ".".
504 // FIXME: Also handle "..".
505 if (Name == ".") {
506 ++I;
507 continue;
508 }
509
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000510 detail::InMemoryNode *Node = Dir->getChild(Name);
511 ++I;
512 if (!Node) {
513 if (I == E) {
514 // End of the path, create a new file.
515 // FIXME: expose the status details in the interface.
Benjamin Kramer1b8dbe32015-10-06 14:45:16 +0000516 Status Stat(P.str(), getNextVirtualUniqueID(),
Benjamin Kramer6b618052015-10-05 14:02:15 +0000517 llvm::sys::TimeValue(ModificationTime, 0), 0, 0,
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000518 Buffer->getBufferSize(),
519 llvm::sys::fs::file_type::regular_file,
520 llvm::sys::fs::all_all);
521 Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
522 std::move(Stat), std::move(Buffer)));
523 return;
524 }
525
526 // Create a new directory. Use the path up to here.
527 // FIXME: expose the status details in the interface.
528 Status Stat(
529 StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
Benjamin Kramer6b618052015-10-05 14:02:15 +0000530 getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime, 0),
531 0, 0, Buffer->getBufferSize(),
532 llvm::sys::fs::file_type::directory_file, llvm::sys::fs::all_all);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000533 Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
534 Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
535 continue;
536 }
537
538 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node))
539 Dir = NewDir;
540 }
541}
542
Benjamin Kramer2e2351a2015-10-06 10:04:08 +0000543void InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
544 llvm::MemoryBuffer *Buffer) {
545 return addFile(P, ModificationTime,
546 llvm::MemoryBuffer::getMemBuffer(
547 Buffer->getBuffer(), Buffer->getBufferIdentifier()));
548}
549
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000550static ErrorOr<detail::InMemoryNode *>
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000551lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
552 const Twine &P) {
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000553 SmallString<128> Path;
554 P.toVector(Path);
555
556 // Fix up relative paths. This just prepends the current working directory.
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000557 std::error_code EC = FS.makeAbsolute(Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000558 assert(!EC);
559 (void)EC;
560
561 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
562 while (true) {
Benjamin Kramer2e2351a2015-10-06 10:04:08 +0000563 // Skip over ".".
564 // FIXME: Also handle "..".
565 if (*I == ".") {
566 ++I;
567 if (I == E)
568 return Dir;
569 continue;
570 }
571
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000572 detail::InMemoryNode *Node = Dir->getChild(*I);
573 ++I;
574 if (!Node)
575 return errc::no_such_file_or_directory;
576
577 // Return the file if it's at the end of the path.
578 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
579 if (I == E)
580 return File;
581 return errc::no_such_file_or_directory;
582 }
583
584 // Traverse directories.
585 Dir = cast<detail::InMemoryDirectory>(Node);
586 if (I == E)
587 return Dir;
588 }
589}
590
591llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000592 auto Node = lookupInMemoryNode(*this, Root.get(), Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000593 if (Node)
594 return (*Node)->getStatus();
595 return Node.getError();
596}
597
598llvm::ErrorOr<std::unique_ptr<File>>
599InMemoryFileSystem::openFileForRead(const Twine &Path) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000600 auto Node = lookupInMemoryNode(*this, Root.get(), Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000601 if (!Node)
602 return Node.getError();
603
604 // When we have a file provide a heap-allocated wrapper for the memory buffer
605 // to match the ownership semantics for File.
606 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
607 return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
608
609 // FIXME: errc::not_a_file?
610 return make_error_code(llvm::errc::invalid_argument);
611}
612
613namespace {
614/// Adaptor from InMemoryDir::iterator to directory_iterator.
615class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
616 detail::InMemoryDirectory::const_iterator I;
617 detail::InMemoryDirectory::const_iterator E;
618
619public:
620 InMemoryDirIterator() {}
621 explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
622 : I(Dir.begin()), E(Dir.end()) {
623 if (I != E)
624 CurrentEntry = I->second->getStatus();
625 }
626
627 std::error_code increment() override {
628 ++I;
629 // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
630 // the rest.
631 CurrentEntry = I != E ? I->second->getStatus() : Status();
632 return std::error_code();
633 }
634};
635} // end anonymous namespace
636
637directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
638 std::error_code &EC) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000639 auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000640 if (!Node) {
641 EC = Node.getError();
642 return directory_iterator(std::make_shared<InMemoryDirIterator>());
643 }
644
645 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
646 return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
647
648 EC = make_error_code(llvm::errc::not_a_directory);
649 return directory_iterator(std::make_shared<InMemoryDirIterator>());
650}
651}
652}
653
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000654//===-----------------------------------------------------------------------===/
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000655// RedirectingFileSystem implementation
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000656//===-----------------------------------------------------------------------===/
657
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000658namespace {
659
660enum EntryKind {
661 EK_Directory,
662 EK_File
663};
664
665/// \brief A single file or directory in the VFS.
666class Entry {
667 EntryKind Kind;
668 std::string Name;
669
670public:
671 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000672 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
673 StringRef getName() const { return Name; }
674 EntryKind getKind() const { return Kind; }
675};
676
677class DirectoryEntry : public Entry {
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000678 std::vector<std::unique_ptr<Entry>> Contents;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000679 Status S;
680
681public:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000682 DirectoryEntry(StringRef Name, std::vector<std::unique_ptr<Entry>> Contents,
683 Status S)
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000684 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000685 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000686 Status getStatus() { return S; }
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000687 typedef decltype(Contents)::iterator iterator;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000688 iterator contents_begin() { return Contents.begin(); }
689 iterator contents_end() { return Contents.end(); }
690 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
691};
692
693class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000694public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000695 enum NameKind {
696 NK_NotSet,
697 NK_External,
698 NK_Virtual
699 };
700private:
701 std::string ExternalContentsPath;
702 NameKind UseName;
703public:
704 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
705 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
706 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000707 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000708 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000709 bool useExternalName(bool GlobalUseExternalName) const {
710 return UseName == NK_NotSet ? GlobalUseExternalName
711 : (UseName == NK_External);
712 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000713 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
714};
715
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000716class RedirectingFileSystem;
Ben Langmuir740812b2014-06-24 19:37:16 +0000717
718class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
719 std::string Dir;
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000720 RedirectingFileSystem &FS;
Ben Langmuir740812b2014-06-24 19:37:16 +0000721 DirectoryEntry::iterator Current, End;
722public:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000723 VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
Ben Langmuir740812b2014-06-24 19:37:16 +0000724 DirectoryEntry::iterator Begin,
725 DirectoryEntry::iterator End, std::error_code &EC);
726 std::error_code increment() override;
727};
728
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000729/// \brief A virtual file system parsed from a YAML file.
730///
731/// Currently, this class allows creating virtual directories and mapping
732/// virtual file paths to existing external files, available in \c ExternalFS.
733///
734/// The basic structure of the parsed file is:
735/// \verbatim
736/// {
737/// 'version': <version number>,
738/// <optional configuration>
739/// 'roots': [
740/// <directory entries>
741/// ]
742/// }
743/// \endverbatim
744///
745/// All configuration options are optional.
746/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000747/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000748///
749/// Virtual directories are represented as
750/// \verbatim
751/// {
752/// 'type': 'directory',
753/// 'name': <string>,
754/// 'contents': [ <file or directory entries> ]
755/// }
756/// \endverbatim
757///
758/// The default attributes for virtual directories are:
759/// \verbatim
760/// MTime = now() when created
761/// Perms = 0777
762/// User = Group = 0
763/// Size = 0
764/// UniqueID = unspecified unique value
765/// \endverbatim
766///
767/// Re-mapped files are represented as
768/// \verbatim
769/// {
770/// 'type': 'file',
771/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000772/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000773/// 'external-contents': <path to external file>)
774/// }
775/// \endverbatim
776///
777/// and inherit their attributes from the external contents.
778///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000779/// In both cases, the 'name' field may contain multiple path components (e.g.
780/// /path/to/file). However, any directory that contains more than one child
781/// must be uniquely represented by a directory entry.
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000782class RedirectingFileSystem : public vfs::FileSystem {
783 /// The root(s) of the virtual file system.
784 std::vector<std::unique_ptr<Entry>> Roots;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000785 /// \brief The file system to use for external references.
786 IntrusiveRefCntPtr<FileSystem> ExternalFS;
787
788 /// @name Configuration
789 /// @{
790
791 /// \brief Whether to perform case-sensitive comparisons.
792 ///
793 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000794 bool CaseSensitive;
795
796 /// \brief Whether to use to use the value of 'external-contents' for the
797 /// names of files. This global value is overridable on a per-file basis.
798 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000799 /// @}
800
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000801 friend class RedirectingFileSystemParser;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000802
803private:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000804 RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000805 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000806
807 /// \brief Looks up \p Path in \c Roots.
808 ErrorOr<Entry *> lookupPath(const Twine &Path);
809
810 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
811 /// recursing into the contents of \p From if it is a directory.
812 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
813 sys::path::const_iterator End, Entry *From);
814
Ben Langmuir740812b2014-06-24 19:37:16 +0000815 /// \brief Get the status of a given an \c Entry.
816 ErrorOr<Status> status(const Twine &Path, Entry *E);
817
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000818public:
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000819 /// \brief Parses \p Buffer, which is expected to be in YAML format and
820 /// returns a virtual file system representing its contents.
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000821 static RedirectingFileSystem *
822 create(std::unique_ptr<MemoryBuffer> Buffer,
823 SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
824 IntrusiveRefCntPtr<FileSystem> ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000825
Craig Toppera798a9d2014-03-02 09:32:10 +0000826 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000827 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000828
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000829 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
830 return ExternalFS->getCurrentWorkingDirectory();
831 }
832 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
833 return ExternalFS->setCurrentWorkingDirectory(Path);
834 }
835
Ben Langmuir740812b2014-06-24 19:37:16 +0000836 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
837 ErrorOr<Entry *> E = lookupPath(Dir);
838 if (!E) {
839 EC = E.getError();
840 return directory_iterator();
841 }
842 ErrorOr<Status> S = status(Dir, *E);
843 if (!S) {
844 EC = S.getError();
845 return directory_iterator();
846 }
847 if (!S->isDirectory()) {
848 EC = std::error_code(static_cast<int>(errc::not_a_directory),
849 std::system_category());
850 return directory_iterator();
851 }
852
853 DirectoryEntry *D = cast<DirectoryEntry>(*E);
854 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
855 *this, D->contents_begin(), D->contents_end(), EC));
856 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000857};
858
859/// \brief A helper class to hold the common YAML parsing state.
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000860class RedirectingFileSystemParser {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000861 yaml::Stream &Stream;
862
863 void error(yaml::Node *N, const Twine &Msg) {
864 Stream.printError(N, Msg);
865 }
866
867 // false on error
868 bool parseScalarString(yaml::Node *N, StringRef &Result,
869 SmallVectorImpl<char> &Storage) {
870 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
871 if (!S) {
872 error(N, "expected string");
873 return false;
874 }
875 Result = S->getValue(Storage);
876 return true;
877 }
878
879 // false on error
880 bool parseScalarBool(yaml::Node *N, bool &Result) {
881 SmallString<5> Storage;
882 StringRef Value;
883 if (!parseScalarString(N, Value, Storage))
884 return false;
885
886 if (Value.equals_lower("true") || Value.equals_lower("on") ||
887 Value.equals_lower("yes") || Value == "1") {
888 Result = true;
889 return true;
890 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
891 Value.equals_lower("no") || Value == "0") {
892 Result = false;
893 return true;
894 }
895
896 error(N, "expected boolean value");
897 return false;
898 }
899
900 struct KeyStatus {
901 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
902 bool Required;
903 bool Seen;
904 };
905 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
906
907 // false on error
908 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
909 DenseMap<StringRef, KeyStatus> &Keys) {
910 if (!Keys.count(Key)) {
911 error(KeyNode, "unknown key");
912 return false;
913 }
914 KeyStatus &S = Keys[Key];
915 if (S.Seen) {
916 error(KeyNode, Twine("duplicate key '") + Key + "'");
917 return false;
918 }
919 S.Seen = true;
920 return true;
921 }
922
923 // false on error
924 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
925 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
926 E = Keys.end();
927 I != E; ++I) {
928 if (I->second.Required && !I->second.Seen) {
929 error(Obj, Twine("missing key '") + I->first + "'");
930 return false;
931 }
932 }
933 return true;
934 }
935
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000936 std::unique_ptr<Entry> parseEntry(yaml::Node *N) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000937 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
938 if (!M) {
939 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000940 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000941 }
942
943 KeyStatusPair Fields[] = {
944 KeyStatusPair("name", true),
945 KeyStatusPair("type", true),
946 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000947 KeyStatusPair("external-contents", false),
948 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000949 };
950
951 DenseMap<StringRef, KeyStatus> Keys(
952 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
953
954 bool HasContents = false; // external or otherwise
Benjamin Kramerdadb58b2015-10-07 10:05:44 +0000955 std::vector<std::unique_ptr<Entry>> EntryArrayContents;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000956 std::string ExternalContentsPath;
957 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000958 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000959 EntryKind Kind;
960
961 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
962 ++I) {
963 StringRef Key;
964 // Reuse the buffer for key and value, since we don't look at key after
965 // parsing value.
966 SmallString<256> Buffer;
967 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000968 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000969
970 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000971 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000972
973 StringRef Value;
974 if (Key == "name") {
975 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000976 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000977 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000978 } else if (Key == "type") {
979 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000980 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000981 if (Value == "file")
982 Kind = EK_File;
983 else if (Value == "directory")
984 Kind = EK_Directory;
985 else {
986 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000987 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000988 }
989 } else if (Key == "contents") {
990 if (HasContents) {
991 error(I->getKey(),
992 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000993 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000994 }
995 HasContents = true;
996 yaml::SequenceNode *Contents =
997 dyn_cast<yaml::SequenceNode>(I->getValue());
998 if (!Contents) {
999 // FIXME: this is only for directories, what about files?
1000 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +00001001 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001002 }
1003
1004 for (yaml::SequenceNode::iterator I = Contents->begin(),
1005 E = Contents->end();
1006 I != E; ++I) {
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001007 if (std::unique_ptr<Entry> E = parseEntry(&*I))
1008 EntryArrayContents.push_back(std::move(E));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001009 else
Craig Topperf1186c52014-05-08 06:41:40 +00001010 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001011 }
1012 } else if (Key == "external-contents") {
1013 if (HasContents) {
1014 error(I->getKey(),
1015 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +00001016 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001017 }
1018 HasContents = true;
1019 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +00001020 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001021 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001022 } else if (Key == "use-external-name") {
1023 bool Val;
1024 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +00001025 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001026 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001027 } else {
1028 llvm_unreachable("key missing from Keys");
1029 }
1030 }
1031
1032 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +00001033 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001034
1035 // check for missing keys
1036 if (!HasContents) {
1037 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +00001038 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001039 }
1040 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +00001041 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001042
Ben Langmuirb59cf672014-02-27 00:25:12 +00001043 // check invalid configuration
1044 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
1045 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +00001046 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001047 }
1048
Ben Langmuir93853232014-03-05 21:32:20 +00001049 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001050 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +00001051 size_t RootPathLen = sys::path::root_path(Trimmed).size();
1052 while (Trimmed.size() > RootPathLen &&
1053 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001054 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1055 // Get the last component
1056 StringRef LastComponent = sys::path::filename(Trimmed);
1057
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001058 std::unique_ptr<Entry> Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001059 switch (Kind) {
1060 case EK_File:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001061 Result = llvm::make_unique<FileEntry>(
1062 LastComponent, std::move(ExternalContentsPath), UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001063 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001064 case EK_Directory:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001065 Result = llvm::make_unique<DirectoryEntry>(
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001066 LastComponent, std::move(EntryArrayContents),
1067 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1068 file_type::directory_file, sys::fs::all_all));
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001069 break;
1070 }
1071
1072 StringRef Parent = sys::path::parent_path(Trimmed);
1073 if (Parent.empty())
1074 return Result;
1075
1076 // if 'name' contains multiple components, create implicit directory entries
1077 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1078 E = sys::path::rend(Parent);
1079 I != E; ++I) {
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001080 std::vector<std::unique_ptr<Entry>> Entries;
1081 Entries.push_back(std::move(Result));
1082 Result = llvm::make_unique<DirectoryEntry>(
1083 *I, std::move(Entries),
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001084 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1085 file_type::directory_file, sys::fs::all_all));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001086 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001087 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001088 }
1089
1090public:
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001091 RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001092
1093 // false on error
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001094 bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001095 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1096 if (!Top) {
1097 error(Root, "expected mapping node");
1098 return false;
1099 }
1100
1101 KeyStatusPair Fields[] = {
1102 KeyStatusPair("version", true),
1103 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +00001104 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001105 KeyStatusPair("roots", true),
1106 };
1107
1108 DenseMap<StringRef, KeyStatus> Keys(
1109 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
1110
1111 // Parse configuration and 'roots'
1112 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1113 ++I) {
1114 SmallString<10> KeyBuffer;
1115 StringRef Key;
1116 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1117 return false;
1118
1119 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1120 return false;
1121
1122 if (Key == "roots") {
1123 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1124 if (!Roots) {
1125 error(I->getValue(), "expected array");
1126 return false;
1127 }
1128
1129 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1130 I != E; ++I) {
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001131 if (std::unique_ptr<Entry> E = parseEntry(&*I))
1132 FS->Roots.push_back(std::move(E));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001133 else
1134 return false;
1135 }
1136 } else if (Key == "version") {
1137 StringRef VersionString;
1138 SmallString<4> Storage;
1139 if (!parseScalarString(I->getValue(), VersionString, Storage))
1140 return false;
1141 int Version;
1142 if (VersionString.getAsInteger<int>(10, Version)) {
1143 error(I->getValue(), "expected integer");
1144 return false;
1145 }
1146 if (Version < 0) {
1147 error(I->getValue(), "invalid version number");
1148 return false;
1149 }
1150 if (Version != 0) {
1151 error(I->getValue(), "version mismatch, expected 0");
1152 return false;
1153 }
1154 } else if (Key == "case-sensitive") {
1155 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1156 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001157 } else if (Key == "use-external-names") {
1158 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1159 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001160 } else {
1161 llvm_unreachable("key missing from Keys");
1162 }
1163 }
1164
1165 if (Stream.failed())
1166 return false;
1167
1168 if (!checkMissingKeys(Top, Keys))
1169 return false;
1170 return true;
1171 }
1172};
1173} // end of anonymous namespace
1174
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001175Entry::~Entry() = default;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001176
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001177RedirectingFileSystem *RedirectingFileSystem::create(
1178 std::unique_ptr<MemoryBuffer> Buffer, SourceMgr::DiagHandlerTy DiagHandler,
1179 void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001180
1181 SourceMgr SM;
Rafael Espindola85d78922014-08-27 19:03:27 +00001182 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001183
Ben Langmuir97882e72014-02-24 20:56:37 +00001184 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001185 yaml::document_iterator DI = Stream.begin();
1186 yaml::Node *Root = DI->getRoot();
1187 if (DI == Stream.end() || !Root) {
1188 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +00001189 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001190 }
1191
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001192 RedirectingFileSystemParser P(Stream);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001193
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001194 std::unique_ptr<RedirectingFileSystem> FS(
1195 new RedirectingFileSystem(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001196 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +00001197 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001198
Ahmed Charles9a16beb2014-03-07 19:33:25 +00001199 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001200}
1201
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001202ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001203 SmallString<256> Path;
1204 Path_.toVector(Path);
1205
1206 // Handle relative paths
Benjamin Kramer7708b2a2015-10-05 13:55:20 +00001207 if (std::error_code EC = makeAbsolute(Path))
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001208 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001209
1210 if (Path.empty())
Rafael Espindola71de0b62014-06-13 17:20:50 +00001211 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001212
1213 sys::path::const_iterator Start = sys::path::begin(Path);
1214 sys::path::const_iterator End = sys::path::end(Path);
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001215 for (const std::unique_ptr<Entry> &Root : Roots) {
1216 ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
Rafael Espindola71de0b62014-06-13 17:20:50 +00001217 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001218 return Result;
1219 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001220 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001221}
1222
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001223ErrorOr<Entry *>
1224RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1225 sys::path::const_iterator End, Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001226 if (Start->equals("."))
1227 ++Start;
1228
1229 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001230 if (CaseSensitive ? !Start->equals(From->getName())
1231 : !Start->equals_lower(From->getName()))
1232 // failure to match
Rafael Espindola71de0b62014-06-13 17:20:50 +00001233 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001234
1235 ++Start;
1236
1237 if (Start == End) {
1238 // Match!
1239 return From;
1240 }
1241
1242 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
1243 if (!DE)
Rafael Espindola71de0b62014-06-13 17:20:50 +00001244 return make_error_code(llvm::errc::not_a_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001245
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001246 for (const std::unique_ptr<Entry> &DirEntry :
1247 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1248 ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
Rafael Espindola71de0b62014-06-13 17:20:50 +00001249 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001250 return Result;
1251 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001252 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001253}
1254
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001255ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
Ben Langmuir740812b2014-06-24 19:37:16 +00001256 assert(E != nullptr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001257 std::string PathStr(Path.str());
Ben Langmuir740812b2014-06-24 19:37:16 +00001258 if (FileEntry *F = dyn_cast<FileEntry>(E)) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001259 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +00001260 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +00001261 if (S && !F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001262 *S = Status::copyWithNewName(*S, PathStr);
Ben Langmuir5de00f32014-05-23 18:15:47 +00001263 if (S)
1264 S->IsVFSMapped = true;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001265 return S;
1266 } else { // directory
Ben Langmuir740812b2014-06-24 19:37:16 +00001267 DirectoryEntry *DE = cast<DirectoryEntry>(E);
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001268 return Status::copyWithNewName(DE->getStatus(), PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001269 }
1270}
1271
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001272ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
Ben Langmuir740812b2014-06-24 19:37:16 +00001273 ErrorOr<Entry *> Result = lookupPath(Path);
1274 if (!Result)
1275 return Result.getError();
1276 return status(Path, *Result);
1277}
1278
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001279namespace {
1280/// Provide a file wrapper that returns the external name when asked.
1281class NamedFileAdaptor : public File {
1282 std::unique_ptr<File> InnerFile;
1283 std::string NewName;
1284
1285public:
1286 NamedFileAdaptor(std::unique_ptr<File> InnerFile, std::string NewName)
1287 : InnerFile(std::move(InnerFile)), NewName(std::move(NewName)) {}
1288
1289 llvm::ErrorOr<Status> status() override {
1290 auto InnerStatus = InnerFile->status();
1291 if (InnerStatus)
1292 return Status::copyWithNewName(*InnerStatus, NewName);
1293 return InnerStatus.getError();
1294 }
1295 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
Benjamin Kramer737501c2015-10-05 21:20:19 +00001296 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1297 bool IsVolatile) override {
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001298 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1299 IsVolatile);
1300 }
1301 std::error_code close() override { return InnerFile->close(); }
1302};
1303} // end anonymous namespace
1304
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001305ErrorOr<std::unique_ptr<File>>
1306RedirectingFileSystem::openFileForRead(const Twine &Path) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001307 ErrorOr<Entry *> E = lookupPath(Path);
1308 if (!E)
1309 return E.getError();
1310
1311 FileEntry *F = dyn_cast<FileEntry>(*E);
1312 if (!F) // FIXME: errc::not_a_file?
Rafael Espindola71de0b62014-06-13 17:20:50 +00001313 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001314
Benjamin Kramera8857962014-10-26 22:44:13 +00001315 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1316 if (!Result)
1317 return Result;
Ben Langmuird066d4c2014-02-28 21:16:07 +00001318
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001319 if (!F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001320 return std::unique_ptr<File>(
1321 new NamedFileAdaptor(std::move(*Result), Path.str()));
Ben Langmuird066d4c2014-02-28 21:16:07 +00001322
Benjamin Kramera8857962014-10-26 22:44:13 +00001323 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001324}
1325
1326IntrusiveRefCntPtr<FileSystem>
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001327vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1328 SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001329 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001330 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1331 DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001332}
1333
1334UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +00001335 static std::atomic<unsigned> UID;
1336 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001337 // The following assumes that uint64_t max will never collide with a real
1338 // dev_t value from the OS.
1339 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1340}
Justin Bogner9c785292014-05-20 21:43:27 +00001341
1342#ifndef NDEBUG
1343static bool pathHasTraversal(StringRef Path) {
1344 using namespace llvm::sys;
1345 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1346 if (Comp == "." || Comp == "..")
1347 return true;
1348 return false;
1349}
1350#endif
1351
1352void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1353 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1354 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1355 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1356 Mappings.emplace_back(VirtualPath, RealPath);
1357}
1358
Justin Bogner44fa450342014-05-21 22:46:51 +00001359namespace {
1360class JSONWriter {
1361 llvm::raw_ostream &OS;
1362 SmallVector<StringRef, 16> DirStack;
1363 inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1364 inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1365 bool containedIn(StringRef Parent, StringRef Path);
1366 StringRef containedPart(StringRef Parent, StringRef Path);
1367 void startDirectory(StringRef Path);
1368 void endDirectory();
1369 void writeEntry(StringRef VPath, StringRef RPath);
1370
1371public:
1372 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1373 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1374};
Alexander Kornienkoab9db512015-06-22 23:07:51 +00001375}
Justin Bogner9c785292014-05-20 21:43:27 +00001376
Justin Bogner44fa450342014-05-21 22:46:51 +00001377bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
Justin Bogner1c078f22014-05-20 22:12:58 +00001378 using namespace llvm::sys;
1379 // Compare each path component.
1380 auto IParent = path::begin(Parent), EParent = path::end(Parent);
1381 for (auto IChild = path::begin(Path), EChild = path::end(Path);
1382 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1383 if (*IParent != *IChild)
1384 return false;
1385 }
1386 // Have we exhausted the parent path?
1387 return IParent == EParent;
Justin Bogner9c785292014-05-20 21:43:27 +00001388}
1389
Justin Bogner44fa450342014-05-21 22:46:51 +00001390StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1391 assert(!Parent.empty());
Justin Bogner9c785292014-05-20 21:43:27 +00001392 assert(containedIn(Parent, Path));
Justin Bogner9c785292014-05-20 21:43:27 +00001393 return Path.slice(Parent.size() + 1, StringRef::npos);
1394}
1395
Justin Bogner44fa450342014-05-21 22:46:51 +00001396void JSONWriter::startDirectory(StringRef Path) {
1397 StringRef Name =
1398 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1399 DirStack.push_back(Path);
1400 unsigned Indent = getDirIndent();
1401 OS.indent(Indent) << "{\n";
1402 OS.indent(Indent + 2) << "'type': 'directory',\n";
1403 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1404 OS.indent(Indent + 2) << "'contents': [\n";
1405}
1406
1407void JSONWriter::endDirectory() {
1408 unsigned Indent = getDirIndent();
1409 OS.indent(Indent + 2) << "]\n";
1410 OS.indent(Indent) << "}";
1411
1412 DirStack.pop_back();
1413}
1414
1415void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1416 unsigned Indent = getFileIndent();
1417 OS.indent(Indent) << "{\n";
1418 OS.indent(Indent + 2) << "'type': 'file',\n";
1419 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1420 OS.indent(Indent + 2) << "'external-contents': \""
1421 << llvm::yaml::escape(RPath) << "\"\n";
1422 OS.indent(Indent) << "}";
1423}
1424
1425void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1426 Optional<bool> IsCaseSensitive) {
1427 using namespace llvm::sys;
1428
1429 OS << "{\n"
1430 " 'version': 0,\n";
1431 if (IsCaseSensitive.hasValue())
1432 OS << " 'case-sensitive': '"
1433 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1434 OS << " 'roots': [\n";
1435
Justin Bogner73466402014-07-15 01:24:35 +00001436 if (!Entries.empty()) {
1437 const YAMLVFSEntry &Entry = Entries.front();
1438 startDirectory(path::parent_path(Entry.VPath));
Justin Bogner44fa450342014-05-21 22:46:51 +00001439 writeEntry(path::filename(Entry.VPath), Entry.RPath);
Justin Bogner44fa450342014-05-21 22:46:51 +00001440
Justin Bogner73466402014-07-15 01:24:35 +00001441 for (const auto &Entry : Entries.slice(1)) {
1442 StringRef Dir = path::parent_path(Entry.VPath);
1443 if (Dir == DirStack.back())
1444 OS << ",\n";
1445 else {
1446 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1447 OS << "\n";
1448 endDirectory();
1449 }
1450 OS << ",\n";
1451 startDirectory(Dir);
1452 }
1453 writeEntry(path::filename(Entry.VPath), Entry.RPath);
1454 }
1455
1456 while (!DirStack.empty()) {
1457 OS << "\n";
1458 endDirectory();
1459 }
Justin Bogner44fa450342014-05-21 22:46:51 +00001460 OS << "\n";
Justin Bogner44fa450342014-05-21 22:46:51 +00001461 }
1462
Justin Bogner73466402014-07-15 01:24:35 +00001463 OS << " ]\n"
Justin Bogner44fa450342014-05-21 22:46:51 +00001464 << "}\n";
1465}
1466
Justin Bogner9c785292014-05-20 21:43:27 +00001467void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1468 std::sort(Mappings.begin(), Mappings.end(),
Justin Bogner44fa450342014-05-21 22:46:51 +00001469 [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
Justin Bogner9c785292014-05-20 21:43:27 +00001470 return LHS.VPath < RHS.VPath;
1471 });
1472
Justin Bogner44fa450342014-05-21 22:46:51 +00001473 JSONWriter(OS).write(Mappings, IsCaseSensitive);
Justin Bogner9c785292014-05-20 21:43:27 +00001474}
Ben Langmuir740812b2014-06-24 19:37:16 +00001475
1476VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
Benjamin Kramerdadb58b2015-10-07 10:05:44 +00001477 RedirectingFileSystem &FS,
Ben Langmuir740812b2014-06-24 19:37:16 +00001478 DirectoryEntry::iterator Begin,
1479 DirectoryEntry::iterator End,
1480 std::error_code &EC)
1481 : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1482 if (Current != End) {
1483 SmallString<128> PathStr(Dir);
1484 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001485 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001486 if (S)
1487 CurrentEntry = *S;
1488 else
1489 EC = S.getError();
1490 }
1491}
1492
1493std::error_code VFSFromYamlDirIterImpl::increment() {
1494 assert(Current != End && "cannot iterate past end");
1495 if (++Current != End) {
1496 SmallString<128> PathStr(Dir);
1497 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001498 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001499 if (!S)
1500 return S.getError();
1501 CurrentEntry = *S;
1502 } else {
1503 CurrentEntry = Status();
1504 }
1505 return std::error_code();
1506}
Ben Langmuir7c9f6c82014-06-25 20:25:40 +00001507
1508vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1509 const Twine &Path,
1510 std::error_code &EC)
1511 : FS(&FS_) {
1512 directory_iterator I = FS->dir_begin(Path, EC);
1513 if (!EC && I != directory_iterator()) {
1514 State = std::make_shared<IterState>();
1515 State->push(I);
1516 }
1517}
1518
1519vfs::recursive_directory_iterator &
1520recursive_directory_iterator::increment(std::error_code &EC) {
1521 assert(FS && State && !State->empty() && "incrementing past end");
1522 assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1523 vfs::directory_iterator End;
1524 if (State->top()->isDirectory()) {
1525 vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1526 if (EC)
1527 return *this;
1528 if (I != End) {
1529 State->push(I);
1530 return *this;
1531 }
1532 }
1533
1534 while (!State->empty() && State->top().increment(EC) == End)
1535 State->pop();
1536
1537 if (State->empty())
1538 State.reset(); // end iterator
1539
1540 return *this;
Rafael Espindola2d2b4202014-07-06 17:43:24 +00001541}