blob: 1ba4a29c1c4ee15668b6c0da5d4e7206fb7ed07d [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 Kramer4527fb22014-03-02 17:08:31 +000022#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000023#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000024
25using namespace clang;
26using namespace clang::vfs;
27using namespace llvm;
28using llvm::sys::fs::file_status;
29using llvm::sys::fs::file_type;
30using llvm::sys::fs::perms;
31using llvm::sys::fs::UniqueID;
32
33Status::Status(const file_status &Status)
34 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
35 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Ben Langmuir5de00f32014-05-23 18:15:47 +000036 Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000037
Benjamin Kramer268b51a2015-10-05 13:15:33 +000038Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
39 uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
40 perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000041 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
Ben Langmuir5de00f32014-05-23 18:15:47 +000042 Type(Type), Perms(Perms), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000043
Benjamin Kramer268b51a2015-10-05 13:15:33 +000044Status Status::copyWithNewName(const Status &In, StringRef NewName) {
45 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
46 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
47 In.getPermissions());
48}
49
50Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
51 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
52 In.getUser(), In.getGroup(), In.getSize(), In.type(),
53 In.permissions());
54}
55
Ben Langmuirc8130a72014-02-20 21:59:23 +000056bool Status::equivalent(const Status &Other) const {
57 return getUniqueID() == Other.getUniqueID();
58}
59bool Status::isDirectory() const {
60 return Type == file_type::directory_file;
61}
62bool Status::isRegularFile() const {
63 return Type == file_type::regular_file;
64}
65bool Status::isOther() const {
66 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
67}
68bool Status::isSymlink() const {
69 return Type == file_type::symlink_file;
70}
71bool Status::isStatusKnown() const {
72 return Type != file_type::status_error;
73}
74bool Status::exists() const {
75 return isStatusKnown() && Type != file_type::file_not_found;
76}
77
78File::~File() {}
79
80FileSystem::~FileSystem() {}
81
Benjamin Kramera8857962014-10-26 22:44:13 +000082ErrorOr<std::unique_ptr<MemoryBuffer>>
83FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
84 bool RequiresNullTerminator, bool IsVolatile) {
85 auto F = openFileForRead(Name);
86 if (!F)
87 return F.getError();
Ben Langmuirc8130a72014-02-20 21:59:23 +000088
Benjamin Kramera8857962014-10-26 22:44:13 +000089 return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000090}
91
Benjamin Kramer7708b2a2015-10-05 13:55:20 +000092std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
93 auto WorkingDir = getCurrentWorkingDirectory();
94 if (!WorkingDir)
95 return WorkingDir.getError();
96
97 return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
98}
99
Ben Langmuirc8130a72014-02-20 21:59:23 +0000100//===-----------------------------------------------------------------------===/
101// RealFileSystem implementation
102//===-----------------------------------------------------------------------===/
103
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000104namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000105/// \brief Wrapper around a raw file descriptor.
106class RealFile : public File {
107 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +0000108 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000109 friend class RealFileSystem;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000110 RealFile(int FD, StringRef NewName)
111 : FD(FD), S(NewName, {}, {}, {}, {}, {},
112 llvm::sys::fs::file_type::status_error, {}) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000113 assert(FD >= 0 && "Invalid or inactive file descriptor");
114 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000115
Ben Langmuirc8130a72014-02-20 21:59:23 +0000116public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000117 ~RealFile() override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000118 ErrorOr<Status> status() override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000119 ErrorOr<std::unique_ptr<MemoryBuffer>>
120 getBuffer(const Twine &Name, int64_t FileSize = -1,
121 bool RequiresNullTerminator = true,
122 bool IsVolatile = false) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000123 std::error_code close() override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000124};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000125} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000126RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000127
128ErrorOr<Status> RealFile::status() {
129 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000130 if (!S.isStatusKnown()) {
131 file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000132 if (std::error_code EC = sys::fs::status(FD, RealStatus))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000133 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000134 S = Status::copyWithNewName(RealStatus, S.getName());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000135 }
136 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000137}
138
Benjamin Kramera8857962014-10-26 22:44:13 +0000139ErrorOr<std::unique_ptr<MemoryBuffer>>
140RealFile::getBuffer(const Twine &Name, int64_t FileSize,
141 bool RequiresNullTerminator, bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000142 assert(FD != -1 && "cannot get buffer for closed file");
Benjamin Kramera8857962014-10-26 22:44:13 +0000143 return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
144 IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000145}
146
147// FIXME: This is terrible, we need this for ::close.
148#if !defined(_MSC_VER) && !defined(__MINGW32__)
149#include <unistd.h>
150#include <sys/uio.h>
151#else
152#include <io.h>
153#ifndef S_ISFIFO
154#define S_ISFIFO(x) (0)
155#endif
156#endif
Rafael Espindola8e650d72014-06-12 20:37:59 +0000157std::error_code RealFile::close() {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000158 if (::close(FD))
Rafael Espindola8e650d72014-06-12 20:37:59 +0000159 return std::error_code(errno, std::generic_category());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000160 FD = -1;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000161 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000162}
163
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000164namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000165/// \brief The file system according to your operating system.
166class RealFileSystem : public FileSystem {
167public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000168 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000169 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000170 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000171
172 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
173 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000174};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000175} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000176
177ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
178 sys::fs::file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000179 if (std::error_code EC = sys::fs::status(Path, RealStatus))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000180 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000181 return Status::copyWithNewName(RealStatus, Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000182}
183
Benjamin Kramera8857962014-10-26 22:44:13 +0000184ErrorOr<std::unique_ptr<File>>
185RealFileSystem::openFileForRead(const Twine &Name) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000186 int FD;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000187 if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000188 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000189 return std::unique_ptr<File>(new RealFile(FD, Name.str()));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000190}
191
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000192llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
193 SmallString<256> Dir;
194 if (std::error_code EC = llvm::sys::fs::current_path(Dir))
195 return EC;
196 return Dir.str().str();
197}
198
199std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
200 // FIXME: chdir is thread hostile; on the other hand, creating the same
201 // behavior as chdir is complex: chdir resolves the path once, thus
202 // guaranteeing that all subsequent relative path operations work
203 // on the same path the original chdir resulted in. This makes a
204 // difference for example on network filesystems, where symlinks might be
205 // switched during runtime of the tool. Fixing this depends on having a
206 // file system abstraction that allows openat() style interactions.
207 SmallString<256> Storage;
208 StringRef Dir = Path.toNullTerminatedStringRef(Storage);
209 if (int Err = ::chdir(Dir.data()))
210 return std::error_code(Err, std::generic_category());
211 return std::error_code();
212}
213
Ben Langmuirc8130a72014-02-20 21:59:23 +0000214IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
215 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
216 return FS;
217}
218
Ben Langmuir740812b2014-06-24 19:37:16 +0000219namespace {
220class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
221 std::string Path;
222 llvm::sys::fs::directory_iterator Iter;
223public:
224 RealFSDirIter(const Twine &_Path, std::error_code &EC)
225 : Path(_Path.str()), Iter(Path, EC) {
226 if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
227 llvm::sys::fs::file_status S;
228 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000229 if (!EC)
230 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000231 }
232 }
233
234 std::error_code increment() override {
235 std::error_code EC;
236 Iter.increment(EC);
237 if (EC) {
238 return EC;
239 } else if (Iter == llvm::sys::fs::directory_iterator()) {
240 CurrentEntry = Status();
241 } else {
242 llvm::sys::fs::file_status S;
243 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000244 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000245 }
246 return EC;
247 }
248};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000249}
Ben Langmuir740812b2014-06-24 19:37:16 +0000250
251directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
252 std::error_code &EC) {
253 return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
254}
255
Ben Langmuirc8130a72014-02-20 21:59:23 +0000256//===-----------------------------------------------------------------------===/
257// OverlayFileSystem implementation
258//===-----------------------------------------------------------------------===/
259OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000260 FSList.push_back(BaseFS);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000261}
262
263void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
264 FSList.push_back(FS);
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000265 // Synchronize added file systems by duplicating the working directory from
266 // the first one in the list.
267 FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000268}
269
270ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
271 // FIXME: handle symlinks that cross file systems
272 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
273 ErrorOr<Status> Status = (*I)->status(Path);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000274 if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000275 return Status;
276 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000277 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000278}
279
Benjamin Kramera8857962014-10-26 22:44:13 +0000280ErrorOr<std::unique_ptr<File>>
281OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000282 // FIXME: handle symlinks that cross file systems
283 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
Benjamin Kramera8857962014-10-26 22:44:13 +0000284 auto Result = (*I)->openFileForRead(Path);
285 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
286 return Result;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000287 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000288 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000289}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000290
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000291llvm::ErrorOr<std::string>
292OverlayFileSystem::getCurrentWorkingDirectory() const {
293 // All file systems are synchronized, just take the first working directory.
294 return FSList.front()->getCurrentWorkingDirectory();
295}
296std::error_code
297OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
298 for (auto &FS : FSList)
299 if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
300 return EC;
301 return std::error_code();
302}
303
Ben Langmuir740812b2014-06-24 19:37:16 +0000304clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
305
306namespace {
307class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
308 OverlayFileSystem &Overlays;
309 std::string Path;
310 OverlayFileSystem::iterator CurrentFS;
311 directory_iterator CurrentDirIter;
312 llvm::StringSet<> SeenNames;
313
314 std::error_code incrementFS() {
315 assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
316 ++CurrentFS;
317 for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
318 std::error_code EC;
319 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
320 if (EC && EC != errc::no_such_file_or_directory)
321 return EC;
322 if (CurrentDirIter != directory_iterator())
323 break; // found
324 }
325 return std::error_code();
326 }
327
328 std::error_code incrementDirIter(bool IsFirstTime) {
329 assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
330 "incrementing past end");
331 std::error_code EC;
332 if (!IsFirstTime)
333 CurrentDirIter.increment(EC);
334 if (!EC && CurrentDirIter == directory_iterator())
335 EC = incrementFS();
336 return EC;
337 }
338
339 std::error_code incrementImpl(bool IsFirstTime) {
340 while (true) {
341 std::error_code EC = incrementDirIter(IsFirstTime);
342 if (EC || CurrentDirIter == directory_iterator()) {
343 CurrentEntry = Status();
344 return EC;
345 }
346 CurrentEntry = *CurrentDirIter;
347 StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
David Blaikie61b86d42014-11-19 02:56:13 +0000348 if (SeenNames.insert(Name).second)
Ben Langmuir740812b2014-06-24 19:37:16 +0000349 return EC; // name not seen before
350 }
351 llvm_unreachable("returned above");
352 }
353
354public:
355 OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
356 std::error_code &EC)
357 : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
358 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
359 EC = incrementImpl(true);
360 }
361
362 std::error_code increment() override { return incrementImpl(false); }
363};
364} // end anonymous namespace
365
366directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
367 std::error_code &EC) {
368 return directory_iterator(
369 std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
370}
371
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000372namespace clang {
373namespace vfs {
374namespace detail {
375
376enum InMemoryNodeKind { IME_File, IME_Directory };
377
378/// The in memory file system is a tree of Nodes. Every node can either be a
379/// file or a directory.
380class InMemoryNode {
381 Status Stat;
382 InMemoryNodeKind Kind;
383
384public:
385 InMemoryNode(Status Stat, InMemoryNodeKind Kind)
386 : Stat(std::move(Stat)), Kind(Kind) {}
387 virtual ~InMemoryNode() {}
388 const Status &getStatus() const { return Stat; }
389 InMemoryNodeKind getKind() const { return Kind; }
390 virtual std::string toString(unsigned Indent) const = 0;
391};
392
393namespace {
394class InMemoryFile : public InMemoryNode {
395 std::unique_ptr<llvm::MemoryBuffer> Buffer;
396
397public:
398 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
399 : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
400
401 llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
402 std::string toString(unsigned Indent) const override {
403 return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
404 }
405 static bool classof(const InMemoryNode *N) {
406 return N->getKind() == IME_File;
407 }
408};
409
410/// Adapt a InMemoryFile for VFS' File interface.
411class InMemoryFileAdaptor : public File {
412 InMemoryFile &Node;
413
414public:
415 explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
416
417 llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
418 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
419 getBuffer(const Twine &Name, int64_t FileSize = -1,
420 bool RequiresNullTerminator = true,
421 bool IsVolatile = false) override {
422 llvm::MemoryBuffer *Buf = Node.getBuffer();
423 return llvm::MemoryBuffer::getMemBuffer(
424 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
425 }
426 std::error_code close() override { return std::error_code(); }
427};
428} // end anonymous namespace
429
430class InMemoryDirectory : public InMemoryNode {
431 std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
432
433public:
434 InMemoryDirectory(Status Stat)
435 : InMemoryNode(std::move(Stat), IME_Directory) {}
436 InMemoryNode *getChild(StringRef Name) {
437 auto I = Entries.find(Name);
438 if (I != Entries.end())
439 return I->second.get();
440 return nullptr;
441 }
442 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
443 return Entries.insert(make_pair(Name, std::move(Child)))
444 .first->second.get();
445 }
446
447 typedef decltype(Entries)::const_iterator const_iterator;
448 const_iterator begin() const { return Entries.begin(); }
449 const_iterator end() const { return Entries.end(); }
450
451 std::string toString(unsigned Indent) const override {
452 std::string Result =
453 (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
454 for (const auto &Entry : Entries) {
455 Result += Entry.second->toString(Indent + 2);
456 }
457 return Result;
458 }
459 static bool classof(const InMemoryNode *N) {
460 return N->getKind() == IME_Directory;
461 }
462};
463}
464
465InMemoryFileSystem::InMemoryFileSystem()
466 : Root(new detail::InMemoryDirectory(
467 Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
468 0, 0, 0, llvm::sys::fs::file_type::directory_file,
469 llvm::sys::fs::perms::all_all))) {}
470
471InMemoryFileSystem::~InMemoryFileSystem() {}
472
473StringRef InMemoryFileSystem::toString() const {
474 return Root->toString(/*Indent=*/0);
475}
476
477void InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
478 std::unique_ptr<llvm::MemoryBuffer> Buffer) {
479 SmallString<128> Path;
480 P.toVector(Path);
481
482 // Fix up relative paths. This just prepends the current working directory.
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000483 std::error_code EC = makeAbsolute(Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000484 assert(!EC);
485 (void)EC;
486
487 detail::InMemoryDirectory *Dir = Root.get();
488 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
489 while (true) {
490 StringRef Name = *I;
491 detail::InMemoryNode *Node = Dir->getChild(Name);
492 ++I;
493 if (!Node) {
494 if (I == E) {
495 // End of the path, create a new file.
496 // FIXME: expose the status details in the interface.
497 Status Stat(Path, getNextVirtualUniqueID(),
498 llvm::sys::TimeValue(ModificationTime), 0, 0,
499 Buffer->getBufferSize(),
500 llvm::sys::fs::file_type::regular_file,
501 llvm::sys::fs::all_all);
502 Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
503 std::move(Stat), std::move(Buffer)));
504 return;
505 }
506
507 // Create a new directory. Use the path up to here.
508 // FIXME: expose the status details in the interface.
509 Status Stat(
510 StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
511 getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime), 0,
512 0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file,
513 llvm::sys::fs::all_all);
514 Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
515 Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
516 continue;
517 }
518
519 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node))
520 Dir = NewDir;
521 }
522}
523
524static ErrorOr<detail::InMemoryNode *>
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000525lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
526 const Twine &P) {
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000527 SmallString<128> Path;
528 P.toVector(Path);
529
530 // Fix up relative paths. This just prepends the current working directory.
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000531 std::error_code EC = FS.makeAbsolute(Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000532 assert(!EC);
533 (void)EC;
534
535 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
536 while (true) {
537 detail::InMemoryNode *Node = Dir->getChild(*I);
538 ++I;
539 if (!Node)
540 return errc::no_such_file_or_directory;
541
542 // Return the file if it's at the end of the path.
543 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
544 if (I == E)
545 return File;
546 return errc::no_such_file_or_directory;
547 }
548
549 // Traverse directories.
550 Dir = cast<detail::InMemoryDirectory>(Node);
551 if (I == E)
552 return Dir;
553 }
554}
555
556llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000557 auto Node = lookupInMemoryNode(*this, Root.get(), Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000558 if (Node)
559 return (*Node)->getStatus();
560 return Node.getError();
561}
562
563llvm::ErrorOr<std::unique_ptr<File>>
564InMemoryFileSystem::openFileForRead(const Twine &Path) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000565 auto Node = lookupInMemoryNode(*this, Root.get(), Path);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000566 if (!Node)
567 return Node.getError();
568
569 // When we have a file provide a heap-allocated wrapper for the memory buffer
570 // to match the ownership semantics for File.
571 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
572 return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
573
574 // FIXME: errc::not_a_file?
575 return make_error_code(llvm::errc::invalid_argument);
576}
577
578namespace {
579/// Adaptor from InMemoryDir::iterator to directory_iterator.
580class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
581 detail::InMemoryDirectory::const_iterator I;
582 detail::InMemoryDirectory::const_iterator E;
583
584public:
585 InMemoryDirIterator() {}
586 explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
587 : I(Dir.begin()), E(Dir.end()) {
588 if (I != E)
589 CurrentEntry = I->second->getStatus();
590 }
591
592 std::error_code increment() override {
593 ++I;
594 // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
595 // the rest.
596 CurrentEntry = I != E ? I->second->getStatus() : Status();
597 return std::error_code();
598 }
599};
600} // end anonymous namespace
601
602directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
603 std::error_code &EC) {
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000604 auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000605 if (!Node) {
606 EC = Node.getError();
607 return directory_iterator(std::make_shared<InMemoryDirIterator>());
608 }
609
610 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
611 return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
612
613 EC = make_error_code(llvm::errc::not_a_directory);
614 return directory_iterator(std::make_shared<InMemoryDirIterator>());
615}
616}
617}
618
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000619//===-----------------------------------------------------------------------===/
620// VFSFromYAML implementation
621//===-----------------------------------------------------------------------===/
622
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000623namespace {
624
625enum EntryKind {
626 EK_Directory,
627 EK_File
628};
629
630/// \brief A single file or directory in the VFS.
631class Entry {
632 EntryKind Kind;
633 std::string Name;
634
635public:
636 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000637 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
638 StringRef getName() const { return Name; }
639 EntryKind getKind() const { return Kind; }
640};
641
642class DirectoryEntry : public Entry {
643 std::vector<Entry *> Contents;
644 Status S;
645
646public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000647 ~DirectoryEntry() override;
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000648 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
649 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000650 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000651 Status getStatus() { return S; }
652 typedef std::vector<Entry *>::iterator iterator;
653 iterator contents_begin() { return Contents.begin(); }
654 iterator contents_end() { return Contents.end(); }
655 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
656};
657
658class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000659public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000660 enum NameKind {
661 NK_NotSet,
662 NK_External,
663 NK_Virtual
664 };
665private:
666 std::string ExternalContentsPath;
667 NameKind UseName;
668public:
669 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
670 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
671 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000672 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000673 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000674 bool useExternalName(bool GlobalUseExternalName) const {
675 return UseName == NK_NotSet ? GlobalUseExternalName
676 : (UseName == NK_External);
677 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000678 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
679};
680
Ben Langmuir740812b2014-06-24 19:37:16 +0000681class VFSFromYAML;
682
683class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
684 std::string Dir;
685 VFSFromYAML &FS;
686 DirectoryEntry::iterator Current, End;
687public:
688 VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
689 DirectoryEntry::iterator Begin,
690 DirectoryEntry::iterator End, std::error_code &EC);
691 std::error_code increment() override;
692};
693
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000694/// \brief A virtual file system parsed from a YAML file.
695///
696/// Currently, this class allows creating virtual directories and mapping
697/// virtual file paths to existing external files, available in \c ExternalFS.
698///
699/// The basic structure of the parsed file is:
700/// \verbatim
701/// {
702/// 'version': <version number>,
703/// <optional configuration>
704/// 'roots': [
705/// <directory entries>
706/// ]
707/// }
708/// \endverbatim
709///
710/// All configuration options are optional.
711/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000712/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000713///
714/// Virtual directories are represented as
715/// \verbatim
716/// {
717/// 'type': 'directory',
718/// 'name': <string>,
719/// 'contents': [ <file or directory entries> ]
720/// }
721/// \endverbatim
722///
723/// The default attributes for virtual directories are:
724/// \verbatim
725/// MTime = now() when created
726/// Perms = 0777
727/// User = Group = 0
728/// Size = 0
729/// UniqueID = unspecified unique value
730/// \endverbatim
731///
732/// Re-mapped files are represented as
733/// \verbatim
734/// {
735/// 'type': 'file',
736/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000737/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000738/// 'external-contents': <path to external file>)
739/// }
740/// \endverbatim
741///
742/// and inherit their attributes from the external contents.
743///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000744/// In both cases, the 'name' field may contain multiple path components (e.g.
745/// /path/to/file). However, any directory that contains more than one child
746/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000747class VFSFromYAML : public vfs::FileSystem {
748 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
749 /// \brief The file system to use for external references.
750 IntrusiveRefCntPtr<FileSystem> ExternalFS;
751
752 /// @name Configuration
753 /// @{
754
755 /// \brief Whether to perform case-sensitive comparisons.
756 ///
757 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000758 bool CaseSensitive;
759
760 /// \brief Whether to use to use the value of 'external-contents' for the
761 /// names of files. This global value is overridable on a per-file basis.
762 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000763 /// @}
764
765 friend class VFSFromYAMLParser;
766
767private:
768 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000769 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000770
771 /// \brief Looks up \p Path in \c Roots.
772 ErrorOr<Entry *> lookupPath(const Twine &Path);
773
774 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
775 /// recursing into the contents of \p From if it is a directory.
776 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
777 sys::path::const_iterator End, Entry *From);
778
Ben Langmuir740812b2014-06-24 19:37:16 +0000779 /// \brief Get the status of a given an \c Entry.
780 ErrorOr<Status> status(const Twine &Path, Entry *E);
781
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000782public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000783 ~VFSFromYAML() override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000784
785 /// \brief Parses \p Buffer, which is expected to be in YAML format and
786 /// returns a virtual file system representing its contents.
Rafael Espindola04ab21d72014-08-17 22:12:58 +0000787 static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000788 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000789 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000790 IntrusiveRefCntPtr<FileSystem> ExternalFS);
791
Craig Toppera798a9d2014-03-02 09:32:10 +0000792 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000793 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000794
Benjamin Kramer7708b2a2015-10-05 13:55:20 +0000795 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
796 return ExternalFS->getCurrentWorkingDirectory();
797 }
798 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
799 return ExternalFS->setCurrentWorkingDirectory(Path);
800 }
801
Ben Langmuir740812b2014-06-24 19:37:16 +0000802 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
803 ErrorOr<Entry *> E = lookupPath(Dir);
804 if (!E) {
805 EC = E.getError();
806 return directory_iterator();
807 }
808 ErrorOr<Status> S = status(Dir, *E);
809 if (!S) {
810 EC = S.getError();
811 return directory_iterator();
812 }
813 if (!S->isDirectory()) {
814 EC = std::error_code(static_cast<int>(errc::not_a_directory),
815 std::system_category());
816 return directory_iterator();
817 }
818
819 DirectoryEntry *D = cast<DirectoryEntry>(*E);
820 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
821 *this, D->contents_begin(), D->contents_end(), EC));
822 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000823};
824
825/// \brief A helper class to hold the common YAML parsing state.
826class VFSFromYAMLParser {
827 yaml::Stream &Stream;
828
829 void error(yaml::Node *N, const Twine &Msg) {
830 Stream.printError(N, Msg);
831 }
832
833 // false on error
834 bool parseScalarString(yaml::Node *N, StringRef &Result,
835 SmallVectorImpl<char> &Storage) {
836 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
837 if (!S) {
838 error(N, "expected string");
839 return false;
840 }
841 Result = S->getValue(Storage);
842 return true;
843 }
844
845 // false on error
846 bool parseScalarBool(yaml::Node *N, bool &Result) {
847 SmallString<5> Storage;
848 StringRef Value;
849 if (!parseScalarString(N, Value, Storage))
850 return false;
851
852 if (Value.equals_lower("true") || Value.equals_lower("on") ||
853 Value.equals_lower("yes") || Value == "1") {
854 Result = true;
855 return true;
856 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
857 Value.equals_lower("no") || Value == "0") {
858 Result = false;
859 return true;
860 }
861
862 error(N, "expected boolean value");
863 return false;
864 }
865
866 struct KeyStatus {
867 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
868 bool Required;
869 bool Seen;
870 };
871 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
872
873 // false on error
874 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
875 DenseMap<StringRef, KeyStatus> &Keys) {
876 if (!Keys.count(Key)) {
877 error(KeyNode, "unknown key");
878 return false;
879 }
880 KeyStatus &S = Keys[Key];
881 if (S.Seen) {
882 error(KeyNode, Twine("duplicate key '") + Key + "'");
883 return false;
884 }
885 S.Seen = true;
886 return true;
887 }
888
889 // false on error
890 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
891 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
892 E = Keys.end();
893 I != E; ++I) {
894 if (I->second.Required && !I->second.Seen) {
895 error(Obj, Twine("missing key '") + I->first + "'");
896 return false;
897 }
898 }
899 return true;
900 }
901
902 Entry *parseEntry(yaml::Node *N) {
903 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
904 if (!M) {
905 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000906 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000907 }
908
909 KeyStatusPair Fields[] = {
910 KeyStatusPair("name", true),
911 KeyStatusPair("type", true),
912 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000913 KeyStatusPair("external-contents", false),
914 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000915 };
916
917 DenseMap<StringRef, KeyStatus> Keys(
918 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
919
920 bool HasContents = false; // external or otherwise
921 std::vector<Entry *> EntryArrayContents;
922 std::string ExternalContentsPath;
923 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000924 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000925 EntryKind Kind;
926
927 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
928 ++I) {
929 StringRef Key;
930 // Reuse the buffer for key and value, since we don't look at key after
931 // parsing value.
932 SmallString<256> Buffer;
933 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000934 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000935
936 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000937 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000938
939 StringRef Value;
940 if (Key == "name") {
941 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000942 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000943 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000944 } else if (Key == "type") {
945 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000946 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000947 if (Value == "file")
948 Kind = EK_File;
949 else if (Value == "directory")
950 Kind = EK_Directory;
951 else {
952 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000953 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000954 }
955 } else if (Key == "contents") {
956 if (HasContents) {
957 error(I->getKey(),
958 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000959 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000960 }
961 HasContents = true;
962 yaml::SequenceNode *Contents =
963 dyn_cast<yaml::SequenceNode>(I->getValue());
964 if (!Contents) {
965 // FIXME: this is only for directories, what about files?
966 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000967 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000968 }
969
970 for (yaml::SequenceNode::iterator I = Contents->begin(),
971 E = Contents->end();
972 I != E; ++I) {
973 if (Entry *E = parseEntry(&*I))
974 EntryArrayContents.push_back(E);
975 else
Craig Topperf1186c52014-05-08 06:41:40 +0000976 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000977 }
978 } else if (Key == "external-contents") {
979 if (HasContents) {
980 error(I->getKey(),
981 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000982 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000983 }
984 HasContents = true;
985 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000986 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000987 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000988 } else if (Key == "use-external-name") {
989 bool Val;
990 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000991 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000992 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000993 } else {
994 llvm_unreachable("key missing from Keys");
995 }
996 }
997
998 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000999 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001000
1001 // check for missing keys
1002 if (!HasContents) {
1003 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +00001004 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001005 }
1006 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +00001007 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001008
Ben Langmuirb59cf672014-02-27 00:25:12 +00001009 // check invalid configuration
1010 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
1011 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +00001012 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001013 }
1014
Ben Langmuir93853232014-03-05 21:32:20 +00001015 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001016 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +00001017 size_t RootPathLen = sys::path::root_path(Trimmed).size();
1018 while (Trimmed.size() > RootPathLen &&
1019 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001020 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1021 // Get the last component
1022 StringRef LastComponent = sys::path::filename(Trimmed);
1023
Craig Topperf1186c52014-05-08 06:41:40 +00001024 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001025 switch (Kind) {
1026 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +00001027 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +00001028 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001029 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001030 case EK_Directory:
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001031 Result = new DirectoryEntry(
1032 LastComponent, std::move(EntryArrayContents),
1033 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1034 file_type::directory_file, sys::fs::all_all));
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001035 break;
1036 }
1037
1038 StringRef Parent = sys::path::parent_path(Trimmed);
1039 if (Parent.empty())
1040 return Result;
1041
1042 // if 'name' contains multiple components, create implicit directory entries
1043 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1044 E = sys::path::rend(Parent);
1045 I != E; ++I) {
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001046 Result = new DirectoryEntry(
1047 *I, llvm::makeArrayRef(Result),
1048 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1049 file_type::directory_file, sys::fs::all_all));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001050 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +00001051 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001052 }
1053
1054public:
1055 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
1056
1057 // false on error
1058 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
1059 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1060 if (!Top) {
1061 error(Root, "expected mapping node");
1062 return false;
1063 }
1064
1065 KeyStatusPair Fields[] = {
1066 KeyStatusPair("version", true),
1067 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +00001068 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001069 KeyStatusPair("roots", true),
1070 };
1071
1072 DenseMap<StringRef, KeyStatus> Keys(
1073 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
1074
1075 // Parse configuration and 'roots'
1076 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1077 ++I) {
1078 SmallString<10> KeyBuffer;
1079 StringRef Key;
1080 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1081 return false;
1082
1083 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1084 return false;
1085
1086 if (Key == "roots") {
1087 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1088 if (!Roots) {
1089 error(I->getValue(), "expected array");
1090 return false;
1091 }
1092
1093 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1094 I != E; ++I) {
1095 if (Entry *E = parseEntry(&*I))
1096 FS->Roots.push_back(E);
1097 else
1098 return false;
1099 }
1100 } else if (Key == "version") {
1101 StringRef VersionString;
1102 SmallString<4> Storage;
1103 if (!parseScalarString(I->getValue(), VersionString, Storage))
1104 return false;
1105 int Version;
1106 if (VersionString.getAsInteger<int>(10, Version)) {
1107 error(I->getValue(), "expected integer");
1108 return false;
1109 }
1110 if (Version < 0) {
1111 error(I->getValue(), "invalid version number");
1112 return false;
1113 }
1114 if (Version != 0) {
1115 error(I->getValue(), "version mismatch, expected 0");
1116 return false;
1117 }
1118 } else if (Key == "case-sensitive") {
1119 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1120 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001121 } else if (Key == "use-external-names") {
1122 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1123 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001124 } else {
1125 llvm_unreachable("key missing from Keys");
1126 }
1127 }
1128
1129 if (Stream.failed())
1130 return false;
1131
1132 if (!checkMissingKeys(Top, Keys))
1133 return false;
1134 return true;
1135 }
1136};
1137} // end of anonymous namespace
1138
1139Entry::~Entry() {}
1140DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
1141
1142VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
1143
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001144VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001145 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +00001146 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001147 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1148
1149 SourceMgr SM;
Rafael Espindola85d78922014-08-27 19:03:27 +00001150 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001151
Ben Langmuir97882e72014-02-24 20:56:37 +00001152 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001153 yaml::document_iterator DI = Stream.begin();
1154 yaml::Node *Root = DI->getRoot();
1155 if (DI == Stream.end() || !Root) {
1156 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +00001157 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001158 }
1159
1160 VFSFromYAMLParser P(Stream);
1161
Ahmed Charlesb8984322014-03-07 20:03:18 +00001162 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001163 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +00001164 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001165
Ahmed Charles9a16beb2014-03-07 19:33:25 +00001166 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001167}
1168
1169ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001170 SmallString<256> Path;
1171 Path_.toVector(Path);
1172
1173 // Handle relative paths
Benjamin Kramer7708b2a2015-10-05 13:55:20 +00001174 if (std::error_code EC = makeAbsolute(Path))
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001175 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001176
1177 if (Path.empty())
Rafael Espindola71de0b62014-06-13 17:20:50 +00001178 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001179
1180 sys::path::const_iterator Start = sys::path::begin(Path);
1181 sys::path::const_iterator End = sys::path::end(Path);
1182 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
1183 I != E; ++I) {
1184 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +00001185 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001186 return Result;
1187 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001188 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001189}
1190
1191ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
1192 sys::path::const_iterator End,
1193 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001194 if (Start->equals("."))
1195 ++Start;
1196
1197 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001198 if (CaseSensitive ? !Start->equals(From->getName())
1199 : !Start->equals_lower(From->getName()))
1200 // failure to match
Rafael Espindola71de0b62014-06-13 17:20:50 +00001201 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001202
1203 ++Start;
1204
1205 if (Start == End) {
1206 // Match!
1207 return From;
1208 }
1209
1210 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
1211 if (!DE)
Rafael Espindola71de0b62014-06-13 17:20:50 +00001212 return make_error_code(llvm::errc::not_a_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001213
1214 for (DirectoryEntry::iterator I = DE->contents_begin(),
1215 E = DE->contents_end();
1216 I != E; ++I) {
1217 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +00001218 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001219 return Result;
1220 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001221 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001222}
1223
Ben Langmuir740812b2014-06-24 19:37:16 +00001224ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
1225 assert(E != nullptr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001226 std::string PathStr(Path.str());
Ben Langmuir740812b2014-06-24 19:37:16 +00001227 if (FileEntry *F = dyn_cast<FileEntry>(E)) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001228 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +00001229 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +00001230 if (S && !F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001231 *S = Status::copyWithNewName(*S, PathStr);
Ben Langmuir5de00f32014-05-23 18:15:47 +00001232 if (S)
1233 S->IsVFSMapped = true;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001234 return S;
1235 } else { // directory
Ben Langmuir740812b2014-06-24 19:37:16 +00001236 DirectoryEntry *DE = cast<DirectoryEntry>(E);
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001237 return Status::copyWithNewName(DE->getStatus(), PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001238 }
1239}
1240
Ben Langmuir740812b2014-06-24 19:37:16 +00001241ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
1242 ErrorOr<Entry *> Result = lookupPath(Path);
1243 if (!Result)
1244 return Result.getError();
1245 return status(Path, *Result);
1246}
1247
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001248namespace {
1249/// Provide a file wrapper that returns the external name when asked.
1250class NamedFileAdaptor : public File {
1251 std::unique_ptr<File> InnerFile;
1252 std::string NewName;
1253
1254public:
1255 NamedFileAdaptor(std::unique_ptr<File> InnerFile, std::string NewName)
1256 : InnerFile(std::move(InnerFile)), NewName(std::move(NewName)) {}
1257
1258 llvm::ErrorOr<Status> status() override {
1259 auto InnerStatus = InnerFile->status();
1260 if (InnerStatus)
1261 return Status::copyWithNewName(*InnerStatus, NewName);
1262 return InnerStatus.getError();
1263 }
1264 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1265 getBuffer(const Twine &Name, int64_t FileSize = -1,
1266 bool RequiresNullTerminator = true,
1267 bool IsVolatile = false) override {
1268 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1269 IsVolatile);
1270 }
1271 std::error_code close() override { return InnerFile->close(); }
1272};
1273} // end anonymous namespace
1274
Benjamin Kramera8857962014-10-26 22:44:13 +00001275ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001276 ErrorOr<Entry *> E = lookupPath(Path);
1277 if (!E)
1278 return E.getError();
1279
1280 FileEntry *F = dyn_cast<FileEntry>(*E);
1281 if (!F) // FIXME: errc::not_a_file?
Rafael Espindola71de0b62014-06-13 17:20:50 +00001282 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001283
Benjamin Kramera8857962014-10-26 22:44:13 +00001284 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1285 if (!Result)
1286 return Result;
Ben Langmuird066d4c2014-02-28 21:16:07 +00001287
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001288 if (!F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001289 return std::unique_ptr<File>(
1290 new NamedFileAdaptor(std::move(*Result), Path.str()));
Ben Langmuird066d4c2014-02-28 21:16:07 +00001291
Benjamin Kramera8857962014-10-26 22:44:13 +00001292 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001293}
1294
1295IntrusiveRefCntPtr<FileSystem>
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001296vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1297 SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001298 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001299 return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext,
1300 ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001301}
1302
1303UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +00001304 static std::atomic<unsigned> UID;
1305 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001306 // The following assumes that uint64_t max will never collide with a real
1307 // dev_t value from the OS.
1308 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1309}
Justin Bogner9c785292014-05-20 21:43:27 +00001310
1311#ifndef NDEBUG
1312static bool pathHasTraversal(StringRef Path) {
1313 using namespace llvm::sys;
1314 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1315 if (Comp == "." || Comp == "..")
1316 return true;
1317 return false;
1318}
1319#endif
1320
1321void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1322 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1323 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1324 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1325 Mappings.emplace_back(VirtualPath, RealPath);
1326}
1327
Justin Bogner44fa450342014-05-21 22:46:51 +00001328namespace {
1329class JSONWriter {
1330 llvm::raw_ostream &OS;
1331 SmallVector<StringRef, 16> DirStack;
1332 inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1333 inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1334 bool containedIn(StringRef Parent, StringRef Path);
1335 StringRef containedPart(StringRef Parent, StringRef Path);
1336 void startDirectory(StringRef Path);
1337 void endDirectory();
1338 void writeEntry(StringRef VPath, StringRef RPath);
1339
1340public:
1341 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1342 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1343};
Alexander Kornienkoab9db512015-06-22 23:07:51 +00001344}
Justin Bogner9c785292014-05-20 21:43:27 +00001345
Justin Bogner44fa450342014-05-21 22:46:51 +00001346bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
Justin Bogner1c078f22014-05-20 22:12:58 +00001347 using namespace llvm::sys;
1348 // Compare each path component.
1349 auto IParent = path::begin(Parent), EParent = path::end(Parent);
1350 for (auto IChild = path::begin(Path), EChild = path::end(Path);
1351 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1352 if (*IParent != *IChild)
1353 return false;
1354 }
1355 // Have we exhausted the parent path?
1356 return IParent == EParent;
Justin Bogner9c785292014-05-20 21:43:27 +00001357}
1358
Justin Bogner44fa450342014-05-21 22:46:51 +00001359StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1360 assert(!Parent.empty());
Justin Bogner9c785292014-05-20 21:43:27 +00001361 assert(containedIn(Parent, Path));
Justin Bogner9c785292014-05-20 21:43:27 +00001362 return Path.slice(Parent.size() + 1, StringRef::npos);
1363}
1364
Justin Bogner44fa450342014-05-21 22:46:51 +00001365void JSONWriter::startDirectory(StringRef Path) {
1366 StringRef Name =
1367 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1368 DirStack.push_back(Path);
1369 unsigned Indent = getDirIndent();
1370 OS.indent(Indent) << "{\n";
1371 OS.indent(Indent + 2) << "'type': 'directory',\n";
1372 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1373 OS.indent(Indent + 2) << "'contents': [\n";
1374}
1375
1376void JSONWriter::endDirectory() {
1377 unsigned Indent = getDirIndent();
1378 OS.indent(Indent + 2) << "]\n";
1379 OS.indent(Indent) << "}";
1380
1381 DirStack.pop_back();
1382}
1383
1384void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1385 unsigned Indent = getFileIndent();
1386 OS.indent(Indent) << "{\n";
1387 OS.indent(Indent + 2) << "'type': 'file',\n";
1388 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1389 OS.indent(Indent + 2) << "'external-contents': \""
1390 << llvm::yaml::escape(RPath) << "\"\n";
1391 OS.indent(Indent) << "}";
1392}
1393
1394void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1395 Optional<bool> IsCaseSensitive) {
1396 using namespace llvm::sys;
1397
1398 OS << "{\n"
1399 " 'version': 0,\n";
1400 if (IsCaseSensitive.hasValue())
1401 OS << " 'case-sensitive': '"
1402 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1403 OS << " 'roots': [\n";
1404
Justin Bogner73466402014-07-15 01:24:35 +00001405 if (!Entries.empty()) {
1406 const YAMLVFSEntry &Entry = Entries.front();
1407 startDirectory(path::parent_path(Entry.VPath));
Justin Bogner44fa450342014-05-21 22:46:51 +00001408 writeEntry(path::filename(Entry.VPath), Entry.RPath);
Justin Bogner44fa450342014-05-21 22:46:51 +00001409
Justin Bogner73466402014-07-15 01:24:35 +00001410 for (const auto &Entry : Entries.slice(1)) {
1411 StringRef Dir = path::parent_path(Entry.VPath);
1412 if (Dir == DirStack.back())
1413 OS << ",\n";
1414 else {
1415 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1416 OS << "\n";
1417 endDirectory();
1418 }
1419 OS << ",\n";
1420 startDirectory(Dir);
1421 }
1422 writeEntry(path::filename(Entry.VPath), Entry.RPath);
1423 }
1424
1425 while (!DirStack.empty()) {
1426 OS << "\n";
1427 endDirectory();
1428 }
Justin Bogner44fa450342014-05-21 22:46:51 +00001429 OS << "\n";
Justin Bogner44fa450342014-05-21 22:46:51 +00001430 }
1431
Justin Bogner73466402014-07-15 01:24:35 +00001432 OS << " ]\n"
Justin Bogner44fa450342014-05-21 22:46:51 +00001433 << "}\n";
1434}
1435
Justin Bogner9c785292014-05-20 21:43:27 +00001436void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1437 std::sort(Mappings.begin(), Mappings.end(),
Justin Bogner44fa450342014-05-21 22:46:51 +00001438 [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
Justin Bogner9c785292014-05-20 21:43:27 +00001439 return LHS.VPath < RHS.VPath;
1440 });
1441
Justin Bogner44fa450342014-05-21 22:46:51 +00001442 JSONWriter(OS).write(Mappings, IsCaseSensitive);
Justin Bogner9c785292014-05-20 21:43:27 +00001443}
Ben Langmuir740812b2014-06-24 19:37:16 +00001444
1445VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
1446 VFSFromYAML &FS,
1447 DirectoryEntry::iterator Begin,
1448 DirectoryEntry::iterator End,
1449 std::error_code &EC)
1450 : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1451 if (Current != End) {
1452 SmallString<128> PathStr(Dir);
1453 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001454 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001455 if (S)
1456 CurrentEntry = *S;
1457 else
1458 EC = S.getError();
1459 }
1460}
1461
1462std::error_code VFSFromYamlDirIterImpl::increment() {
1463 assert(Current != End && "cannot iterate past end");
1464 if (++Current != End) {
1465 SmallString<128> PathStr(Dir);
1466 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001467 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001468 if (!S)
1469 return S.getError();
1470 CurrentEntry = *S;
1471 } else {
1472 CurrentEntry = Status();
1473 }
1474 return std::error_code();
1475}
Ben Langmuir7c9f6c82014-06-25 20:25:40 +00001476
1477vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1478 const Twine &Path,
1479 std::error_code &EC)
1480 : FS(&FS_) {
1481 directory_iterator I = FS->dir_begin(Path, EC);
1482 if (!EC && I != directory_iterator()) {
1483 State = std::make_shared<IterState>();
1484 State->push(I);
1485 }
1486}
1487
1488vfs::recursive_directory_iterator &
1489recursive_directory_iterator::increment(std::error_code &EC) {
1490 assert(FS && State && !State->empty() && "incrementing past end");
1491 assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1492 vfs::directory_iterator End;
1493 if (State->top()->isDirectory()) {
1494 vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1495 if (EC)
1496 return *this;
1497 if (I != End) {
1498 State->push(I);
1499 return *this;
1500 }
1501 }
1502
1503 while (!State->empty() && State->top().increment(EC) == End)
1504 State->pop();
1505
1506 if (State->empty())
1507 State.reset(); // end iterator
1508
1509 return *this;
Rafael Espindola2d2b4202014-07-06 17:43:24 +00001510}