blob: 5389de727a6efc3ab1fadae14e8aaf7577860aa0 [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"
Justin Bogner9c785292014-05-20 21:43:27 +000014#include "llvm/ADT/iterator_range.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000015#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringExtras.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000017#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000018#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000019#include "llvm/Support/YAMLParser.h"
Benjamin Kramer4527fb22014-03-02 17:08:31 +000020#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000021#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000022
23using namespace clang;
24using namespace clang::vfs;
25using namespace llvm;
26using llvm::sys::fs::file_status;
27using llvm::sys::fs::file_type;
28using llvm::sys::fs::perms;
29using llvm::sys::fs::UniqueID;
30
31Status::Status(const file_status &Status)
32 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
33 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Ben Langmuir5de00f32014-05-23 18:15:47 +000034 Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000035
36Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
37 sys::TimeValue MTime, uint32_t User, uint32_t Group,
38 uint64_t Size, file_type Type, perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000039 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
Ben Langmuir5de00f32014-05-23 18:15:47 +000040 Type(Type), Perms(Perms), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000041
42bool Status::equivalent(const Status &Other) const {
43 return getUniqueID() == Other.getUniqueID();
44}
45bool Status::isDirectory() const {
46 return Type == file_type::directory_file;
47}
48bool Status::isRegularFile() const {
49 return Type == file_type::regular_file;
50}
51bool Status::isOther() const {
52 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
53}
54bool Status::isSymlink() const {
55 return Type == file_type::symlink_file;
56}
57bool Status::isStatusKnown() const {
58 return Type != file_type::status_error;
59}
60bool Status::exists() const {
61 return isStatusKnown() && Type != file_type::file_not_found;
62}
63
64File::~File() {}
65
66FileSystem::~FileSystem() {}
67
Rafael Espindola8e650d72014-06-12 20:37:59 +000068std::error_code FileSystem::getBufferForFile(
69 const llvm::Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
70 int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) {
Ahmed Charlesb8984322014-03-07 20:03:18 +000071 std::unique_ptr<File> F;
Rafael Espindola8e650d72014-06-12 20:37:59 +000072 if (std::error_code EC = openFileForRead(Name, F))
Ben Langmuirc8130a72014-02-20 21:59:23 +000073 return EC;
74
Rafael Espindola8e650d72014-06-12 20:37:59 +000075 std::error_code EC =
76 F->getBuffer(Name, Result, FileSize, RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000077 return EC;
78}
79
80//===-----------------------------------------------------------------------===/
81// RealFileSystem implementation
82//===-----------------------------------------------------------------------===/
83
Benjamin Kramer3d6220d2014-03-01 17:21:22 +000084namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +000085/// \brief Wrapper around a raw file descriptor.
86class RealFile : public File {
87 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +000088 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +000089 friend class RealFileSystem;
90 RealFile(int FD) : FD(FD) {
91 assert(FD >= 0 && "Invalid or inactive file descriptor");
92 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000093
Ben Langmuirc8130a72014-02-20 21:59:23 +000094public:
95 ~RealFile();
Craig Toppera798a9d2014-03-02 09:32:10 +000096 ErrorOr<Status> status() override;
Rafael Espindola8e650d72014-06-12 20:37:59 +000097 std::error_code getBuffer(const Twine &Name,
98 std::unique_ptr<MemoryBuffer> &Result,
99 int64_t FileSize = -1,
100 bool RequiresNullTerminator = true,
101 bool IsVolatile = false) override;
102 std::error_code close() override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000103 void setName(StringRef Name) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000104};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000105} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000106RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000107
108ErrorOr<Status> RealFile::status() {
109 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000110 if (!S.isStatusKnown()) {
111 file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000112 if (std::error_code EC = sys::fs::status(FD, RealStatus))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000113 return EC;
114 Status NewS(RealStatus);
115 NewS.setName(S.getName());
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000116 S = std::move(NewS);
Ben Langmuird066d4c2014-02-28 21:16:07 +0000117 }
118 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000119}
120
Rafael Espindola8e650d72014-06-12 20:37:59 +0000121std::error_code RealFile::getBuffer(const Twine &Name,
122 std::unique_ptr<MemoryBuffer> &Result,
123 int64_t FileSize,
124 bool RequiresNullTerminator,
125 bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000126 assert(FD != -1 && "cannot get buffer for closed file");
127 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000128 RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000129}
130
131// FIXME: This is terrible, we need this for ::close.
132#if !defined(_MSC_VER) && !defined(__MINGW32__)
133#include <unistd.h>
134#include <sys/uio.h>
135#else
136#include <io.h>
137#ifndef S_ISFIFO
138#define S_ISFIFO(x) (0)
139#endif
140#endif
Rafael Espindola8e650d72014-06-12 20:37:59 +0000141std::error_code RealFile::close() {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000142 if (::close(FD))
Rafael Espindola8e650d72014-06-12 20:37:59 +0000143 return std::error_code(errno, std::generic_category());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000144 FD = -1;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000145 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000146}
147
Ben Langmuird066d4c2014-02-28 21:16:07 +0000148void RealFile::setName(StringRef Name) {
149 S.setName(Name);
150}
151
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000152namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000153/// \brief The file system according to your operating system.
154class RealFileSystem : public FileSystem {
155public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000156 ErrorOr<Status> status(const Twine &Path) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000157 std::error_code openFileForRead(const Twine &Path,
158 std::unique_ptr<File> &Result) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000159};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000160} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000161
162ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
163 sys::fs::file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000164 if (std::error_code EC = sys::fs::status(Path, RealStatus))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000165 return EC;
166 Status Result(RealStatus);
167 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000168 return Result;
169}
170
Rafael Espindola8e650d72014-06-12 20:37:59 +0000171std::error_code RealFileSystem::openFileForRead(const Twine &Name,
172 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000173 int FD;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000174 if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000175 return EC;
176 Result.reset(new RealFile(FD));
Ben Langmuird066d4c2014-02-28 21:16:07 +0000177 Result->setName(Name.str());
Rafael Espindola8e650d72014-06-12 20:37:59 +0000178 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000179}
180
181IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
182 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
183 return FS;
184}
185
186//===-----------------------------------------------------------------------===/
187// OverlayFileSystem implementation
188//===-----------------------------------------------------------------------===/
189OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
190 pushOverlay(BaseFS);
191}
192
193void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
194 FSList.push_back(FS);
195}
196
197ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
198 // FIXME: handle symlinks that cross file systems
199 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
200 ErrorOr<Status> Status = (*I)->status(Path);
Rafael Espindola96b03302014-06-11 19:05:55 +0000201 if (Status || Status.getError() != std::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000202 return Status;
203 }
Rafael Espindola790589c2014-06-12 03:53:36 +0000204 return std::make_error_code(std::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000205}
206
Rafael Espindola8e650d72014-06-12 20:37:59 +0000207std::error_code
208OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
209 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000210 // FIXME: handle symlinks that cross file systems
211 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
Rafael Espindola8e650d72014-06-12 20:37:59 +0000212 std::error_code EC = (*I)->openFileForRead(Path, Result);
Rafael Espindola96b03302014-06-11 19:05:55 +0000213 if (!EC || EC != std::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000214 return EC;
215 }
Rafael Espindola790589c2014-06-12 03:53:36 +0000216 return std::make_error_code(std::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000217}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000218
219//===-----------------------------------------------------------------------===/
220// VFSFromYAML implementation
221//===-----------------------------------------------------------------------===/
222
223// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
224// strings are literals and will outlive the map, and there is no reason to
225// store them.
226namespace llvm {
227 template<>
228 struct DenseMapInfo<StringRef> {
229 // This assumes that "" will never be a valid key.
230 static inline StringRef getEmptyKey() { return StringRef(""); }
231 static inline StringRef getTombstoneKey() { return StringRef(); }
232 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
233 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
234 };
235}
236
237namespace {
238
239enum EntryKind {
240 EK_Directory,
241 EK_File
242};
243
244/// \brief A single file or directory in the VFS.
245class Entry {
246 EntryKind Kind;
247 std::string Name;
248
249public:
250 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000251 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
252 StringRef getName() const { return Name; }
253 EntryKind getKind() const { return Kind; }
254};
255
256class DirectoryEntry : public Entry {
257 std::vector<Entry *> Contents;
258 Status S;
259
260public:
261 virtual ~DirectoryEntry();
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000262 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
263 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000264 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000265 Status getStatus() { return S; }
266 typedef std::vector<Entry *>::iterator iterator;
267 iterator contents_begin() { return Contents.begin(); }
268 iterator contents_end() { return Contents.end(); }
269 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
270};
271
272class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000273public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000274 enum NameKind {
275 NK_NotSet,
276 NK_External,
277 NK_Virtual
278 };
279private:
280 std::string ExternalContentsPath;
281 NameKind UseName;
282public:
283 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
284 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
285 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000286 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000287 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000288 bool useExternalName(bool GlobalUseExternalName) const {
289 return UseName == NK_NotSet ? GlobalUseExternalName
290 : (UseName == NK_External);
291 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000292 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
293};
294
295/// \brief A virtual file system parsed from a YAML file.
296///
297/// Currently, this class allows creating virtual directories and mapping
298/// virtual file paths to existing external files, available in \c ExternalFS.
299///
300/// The basic structure of the parsed file is:
301/// \verbatim
302/// {
303/// 'version': <version number>,
304/// <optional configuration>
305/// 'roots': [
306/// <directory entries>
307/// ]
308/// }
309/// \endverbatim
310///
311/// All configuration options are optional.
312/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000313/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000314///
315/// Virtual directories are represented as
316/// \verbatim
317/// {
318/// 'type': 'directory',
319/// 'name': <string>,
320/// 'contents': [ <file or directory entries> ]
321/// }
322/// \endverbatim
323///
324/// The default attributes for virtual directories are:
325/// \verbatim
326/// MTime = now() when created
327/// Perms = 0777
328/// User = Group = 0
329/// Size = 0
330/// UniqueID = unspecified unique value
331/// \endverbatim
332///
333/// Re-mapped files are represented as
334/// \verbatim
335/// {
336/// 'type': 'file',
337/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000338/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000339/// 'external-contents': <path to external file>)
340/// }
341/// \endverbatim
342///
343/// and inherit their attributes from the external contents.
344///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000345/// In both cases, the 'name' field may contain multiple path components (e.g.
346/// /path/to/file). However, any directory that contains more than one child
347/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000348class VFSFromYAML : public vfs::FileSystem {
349 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
350 /// \brief The file system to use for external references.
351 IntrusiveRefCntPtr<FileSystem> ExternalFS;
352
353 /// @name Configuration
354 /// @{
355
356 /// \brief Whether to perform case-sensitive comparisons.
357 ///
358 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000359 bool CaseSensitive;
360
361 /// \brief Whether to use to use the value of 'external-contents' for the
362 /// names of files. This global value is overridable on a per-file basis.
363 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000364 /// @}
365
366 friend class VFSFromYAMLParser;
367
368private:
369 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000370 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000371
372 /// \brief Looks up \p Path in \c Roots.
373 ErrorOr<Entry *> lookupPath(const Twine &Path);
374
375 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
376 /// recursing into the contents of \p From if it is a directory.
377 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
378 sys::path::const_iterator End, Entry *From);
379
380public:
381 ~VFSFromYAML();
382
383 /// \brief Parses \p Buffer, which is expected to be in YAML format and
384 /// returns a virtual file system representing its contents.
385 ///
386 /// Takes ownership of \p Buffer.
387 static VFSFromYAML *create(MemoryBuffer *Buffer,
388 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000389 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000390 IntrusiveRefCntPtr<FileSystem> ExternalFS);
391
Craig Toppera798a9d2014-03-02 09:32:10 +0000392 ErrorOr<Status> status(const Twine &Path) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000393 std::error_code openFileForRead(const Twine &Path,
394 std::unique_ptr<File> &Result) override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000395};
396
397/// \brief A helper class to hold the common YAML parsing state.
398class VFSFromYAMLParser {
399 yaml::Stream &Stream;
400
401 void error(yaml::Node *N, const Twine &Msg) {
402 Stream.printError(N, Msg);
403 }
404
405 // false on error
406 bool parseScalarString(yaml::Node *N, StringRef &Result,
407 SmallVectorImpl<char> &Storage) {
408 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
409 if (!S) {
410 error(N, "expected string");
411 return false;
412 }
413 Result = S->getValue(Storage);
414 return true;
415 }
416
417 // false on error
418 bool parseScalarBool(yaml::Node *N, bool &Result) {
419 SmallString<5> Storage;
420 StringRef Value;
421 if (!parseScalarString(N, Value, Storage))
422 return false;
423
424 if (Value.equals_lower("true") || Value.equals_lower("on") ||
425 Value.equals_lower("yes") || Value == "1") {
426 Result = true;
427 return true;
428 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
429 Value.equals_lower("no") || Value == "0") {
430 Result = false;
431 return true;
432 }
433
434 error(N, "expected boolean value");
435 return false;
436 }
437
438 struct KeyStatus {
439 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
440 bool Required;
441 bool Seen;
442 };
443 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
444
445 // false on error
446 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
447 DenseMap<StringRef, KeyStatus> &Keys) {
448 if (!Keys.count(Key)) {
449 error(KeyNode, "unknown key");
450 return false;
451 }
452 KeyStatus &S = Keys[Key];
453 if (S.Seen) {
454 error(KeyNode, Twine("duplicate key '") + Key + "'");
455 return false;
456 }
457 S.Seen = true;
458 return true;
459 }
460
461 // false on error
462 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
463 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
464 E = Keys.end();
465 I != E; ++I) {
466 if (I->second.Required && !I->second.Seen) {
467 error(Obj, Twine("missing key '") + I->first + "'");
468 return false;
469 }
470 }
471 return true;
472 }
473
474 Entry *parseEntry(yaml::Node *N) {
475 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
476 if (!M) {
477 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000478 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000479 }
480
481 KeyStatusPair Fields[] = {
482 KeyStatusPair("name", true),
483 KeyStatusPair("type", true),
484 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000485 KeyStatusPair("external-contents", false),
486 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000487 };
488
489 DenseMap<StringRef, KeyStatus> Keys(
490 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
491
492 bool HasContents = false; // external or otherwise
493 std::vector<Entry *> EntryArrayContents;
494 std::string ExternalContentsPath;
495 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000496 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000497 EntryKind Kind;
498
499 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
500 ++I) {
501 StringRef Key;
502 // Reuse the buffer for key and value, since we don't look at key after
503 // parsing value.
504 SmallString<256> Buffer;
505 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000506 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000507
508 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000509 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000510
511 StringRef Value;
512 if (Key == "name") {
513 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000514 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000515 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000516 } else if (Key == "type") {
517 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000518 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000519 if (Value == "file")
520 Kind = EK_File;
521 else if (Value == "directory")
522 Kind = EK_Directory;
523 else {
524 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000525 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000526 }
527 } else if (Key == "contents") {
528 if (HasContents) {
529 error(I->getKey(),
530 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000531 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000532 }
533 HasContents = true;
534 yaml::SequenceNode *Contents =
535 dyn_cast<yaml::SequenceNode>(I->getValue());
536 if (!Contents) {
537 // FIXME: this is only for directories, what about files?
538 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000539 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000540 }
541
542 for (yaml::SequenceNode::iterator I = Contents->begin(),
543 E = Contents->end();
544 I != E; ++I) {
545 if (Entry *E = parseEntry(&*I))
546 EntryArrayContents.push_back(E);
547 else
Craig Topperf1186c52014-05-08 06:41:40 +0000548 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000549 }
550 } else if (Key == "external-contents") {
551 if (HasContents) {
552 error(I->getKey(),
553 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000554 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000555 }
556 HasContents = true;
557 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000558 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000559 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000560 } else if (Key == "use-external-name") {
561 bool Val;
562 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000563 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000564 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000565 } else {
566 llvm_unreachable("key missing from Keys");
567 }
568 }
569
570 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000571 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000572
573 // check for missing keys
574 if (!HasContents) {
575 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000576 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000577 }
578 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000579 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000580
Ben Langmuirb59cf672014-02-27 00:25:12 +0000581 // check invalid configuration
582 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
583 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +0000584 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000585 }
586
Ben Langmuir93853232014-03-05 21:32:20 +0000587 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000588 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +0000589 size_t RootPathLen = sys::path::root_path(Trimmed).size();
590 while (Trimmed.size() > RootPathLen &&
591 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000592 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
593 // Get the last component
594 StringRef LastComponent = sys::path::filename(Trimmed);
595
Craig Topperf1186c52014-05-08 06:41:40 +0000596 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000597 switch (Kind) {
598 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000599 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000600 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000601 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000602 case EK_Directory:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000603 Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000604 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
605 0, file_type::directory_file, sys::fs::all_all));
606 break;
607 }
608
609 StringRef Parent = sys::path::parent_path(Trimmed);
610 if (Parent.empty())
611 return Result;
612
613 // if 'name' contains multiple components, create implicit directory entries
614 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
615 E = sys::path::rend(Parent);
616 I != E; ++I) {
Benjamin Kramer3f755aa2014-03-10 17:55:02 +0000617 Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000618 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
619 0, file_type::directory_file, sys::fs::all_all));
620 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000621 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000622 }
623
624public:
625 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
626
627 // false on error
628 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
629 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
630 if (!Top) {
631 error(Root, "expected mapping node");
632 return false;
633 }
634
635 KeyStatusPair Fields[] = {
636 KeyStatusPair("version", true),
637 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000638 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000639 KeyStatusPair("roots", true),
640 };
641
642 DenseMap<StringRef, KeyStatus> Keys(
643 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
644
645 // Parse configuration and 'roots'
646 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
647 ++I) {
648 SmallString<10> KeyBuffer;
649 StringRef Key;
650 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
651 return false;
652
653 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
654 return false;
655
656 if (Key == "roots") {
657 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
658 if (!Roots) {
659 error(I->getValue(), "expected array");
660 return false;
661 }
662
663 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
664 I != E; ++I) {
665 if (Entry *E = parseEntry(&*I))
666 FS->Roots.push_back(E);
667 else
668 return false;
669 }
670 } else if (Key == "version") {
671 StringRef VersionString;
672 SmallString<4> Storage;
673 if (!parseScalarString(I->getValue(), VersionString, Storage))
674 return false;
675 int Version;
676 if (VersionString.getAsInteger<int>(10, Version)) {
677 error(I->getValue(), "expected integer");
678 return false;
679 }
680 if (Version < 0) {
681 error(I->getValue(), "invalid version number");
682 return false;
683 }
684 if (Version != 0) {
685 error(I->getValue(), "version mismatch, expected 0");
686 return false;
687 }
688 } else if (Key == "case-sensitive") {
689 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
690 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000691 } else if (Key == "use-external-names") {
692 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
693 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000694 } else {
695 llvm_unreachable("key missing from Keys");
696 }
697 }
698
699 if (Stream.failed())
700 return false;
701
702 if (!checkMissingKeys(Top, Keys))
703 return false;
704 return true;
705 }
706};
707} // end of anonymous namespace
708
709Entry::~Entry() {}
710DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
711
712VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
713
714VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
715 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000716 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000717 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
718
719 SourceMgr SM;
720 yaml::Stream Stream(Buffer, SM);
721
Ben Langmuir97882e72014-02-24 20:56:37 +0000722 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000723 yaml::document_iterator DI = Stream.begin();
724 yaml::Node *Root = DI->getRoot();
725 if (DI == Stream.end() || !Root) {
726 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +0000727 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000728 }
729
730 VFSFromYAMLParser P(Stream);
731
Ahmed Charlesb8984322014-03-07 20:03:18 +0000732 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000733 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +0000734 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000735
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000736 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000737}
738
739ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000740 SmallString<256> Path;
741 Path_.toVector(Path);
742
743 // Handle relative paths
Rafael Espindola8e650d72014-06-12 20:37:59 +0000744 if (std::error_code EC = sys::fs::make_absolute(Path))
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000745 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000746
747 if (Path.empty())
Rafael Espindola790589c2014-06-12 03:53:36 +0000748 return std::make_error_code(std::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000749
750 sys::path::const_iterator Start = sys::path::begin(Path);
751 sys::path::const_iterator End = sys::path::end(Path);
752 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
753 I != E; ++I) {
754 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola96b03302014-06-11 19:05:55 +0000755 if (Result || Result.getError() != std::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000756 return Result;
757 }
Rafael Espindola790589c2014-06-12 03:53:36 +0000758 return std::make_error_code(std::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000759}
760
761ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
762 sys::path::const_iterator End,
763 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000764 if (Start->equals("."))
765 ++Start;
766
767 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000768 if (CaseSensitive ? !Start->equals(From->getName())
769 : !Start->equals_lower(From->getName()))
770 // failure to match
Rafael Espindola790589c2014-06-12 03:53:36 +0000771 return std::make_error_code(std::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000772
773 ++Start;
774
775 if (Start == End) {
776 // Match!
777 return From;
778 }
779
780 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
781 if (!DE)
Rafael Espindola790589c2014-06-12 03:53:36 +0000782 return std::make_error_code(std::errc::not_a_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000783
784 for (DirectoryEntry::iterator I = DE->contents_begin(),
785 E = DE->contents_end();
786 I != E; ++I) {
787 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola96b03302014-06-11 19:05:55 +0000788 if (Result || Result.getError() != std::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000789 return Result;
790 }
Rafael Espindola790589c2014-06-12 03:53:36 +0000791 return std::make_error_code(std::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000792}
793
794ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
795 ErrorOr<Entry *> Result = lookupPath(Path);
796 if (!Result)
797 return Result.getError();
798
799 std::string PathStr(Path.str());
800 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
801 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000802 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000803 if (S && !F->useExternalName(UseExternalNames))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000804 S->setName(PathStr);
Ben Langmuir5de00f32014-05-23 18:15:47 +0000805 if (S)
806 S->IsVFSMapped = true;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000807 return S;
808 } else { // directory
809 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
810 Status S = DE->getStatus();
811 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000812 return S;
813 }
814}
815
Rafael Espindola8e650d72014-06-12 20:37:59 +0000816std::error_code
817VFSFromYAML::openFileForRead(const Twine &Path,
818 std::unique_ptr<vfs::File> &Result) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000819 ErrorOr<Entry *> E = lookupPath(Path);
820 if (!E)
821 return E.getError();
822
823 FileEntry *F = dyn_cast<FileEntry>(*E);
824 if (!F) // FIXME: errc::not_a_file?
Rafael Espindola790589c2014-06-12 03:53:36 +0000825 return std::make_error_code(std::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000826
Rafael Espindola8e650d72014-06-12 20:37:59 +0000827 if (std::error_code EC =
828 ExternalFS->openFileForRead(F->getExternalContentsPath(), Result))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000829 return EC;
830
831 if (!F->useExternalName(UseExternalNames))
832 Result->setName(Path.str());
833
Rafael Espindola8e650d72014-06-12 20:37:59 +0000834 return std::error_code();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000835}
836
837IntrusiveRefCntPtr<FileSystem>
838vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000839 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000840 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000841 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000842}
843
844UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +0000845 static std::atomic<unsigned> UID;
846 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000847 // The following assumes that uint64_t max will never collide with a real
848 // dev_t value from the OS.
849 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
850}
Justin Bogner9c785292014-05-20 21:43:27 +0000851
852#ifndef NDEBUG
853static bool pathHasTraversal(StringRef Path) {
854 using namespace llvm::sys;
855 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
856 if (Comp == "." || Comp == "..")
857 return true;
858 return false;
859}
860#endif
861
862void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
863 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
864 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
865 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
866 Mappings.emplace_back(VirtualPath, RealPath);
867}
868
Justin Bogner44fa450342014-05-21 22:46:51 +0000869namespace {
870class JSONWriter {
871 llvm::raw_ostream &OS;
872 SmallVector<StringRef, 16> DirStack;
873 inline unsigned getDirIndent() { return 4 * DirStack.size(); }
874 inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
875 bool containedIn(StringRef Parent, StringRef Path);
876 StringRef containedPart(StringRef Parent, StringRef Path);
877 void startDirectory(StringRef Path);
878 void endDirectory();
879 void writeEntry(StringRef VPath, StringRef RPath);
880
881public:
882 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
883 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
884};
Justin Bogner9c785292014-05-20 21:43:27 +0000885}
886
Justin Bogner44fa450342014-05-21 22:46:51 +0000887bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
Justin Bogner1c078f22014-05-20 22:12:58 +0000888 using namespace llvm::sys;
889 // Compare each path component.
890 auto IParent = path::begin(Parent), EParent = path::end(Parent);
891 for (auto IChild = path::begin(Path), EChild = path::end(Path);
892 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
893 if (*IParent != *IChild)
894 return false;
895 }
896 // Have we exhausted the parent path?
897 return IParent == EParent;
Justin Bogner9c785292014-05-20 21:43:27 +0000898}
899
Justin Bogner44fa450342014-05-21 22:46:51 +0000900StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
901 assert(!Parent.empty());
Justin Bogner9c785292014-05-20 21:43:27 +0000902 assert(containedIn(Parent, Path));
Justin Bogner9c785292014-05-20 21:43:27 +0000903 return Path.slice(Parent.size() + 1, StringRef::npos);
904}
905
Justin Bogner44fa450342014-05-21 22:46:51 +0000906void JSONWriter::startDirectory(StringRef Path) {
907 StringRef Name =
908 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
909 DirStack.push_back(Path);
910 unsigned Indent = getDirIndent();
911 OS.indent(Indent) << "{\n";
912 OS.indent(Indent + 2) << "'type': 'directory',\n";
913 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
914 OS.indent(Indent + 2) << "'contents': [\n";
915}
916
917void JSONWriter::endDirectory() {
918 unsigned Indent = getDirIndent();
919 OS.indent(Indent + 2) << "]\n";
920 OS.indent(Indent) << "}";
921
922 DirStack.pop_back();
923}
924
925void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
926 unsigned Indent = getFileIndent();
927 OS.indent(Indent) << "{\n";
928 OS.indent(Indent + 2) << "'type': 'file',\n";
929 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
930 OS.indent(Indent + 2) << "'external-contents': \""
931 << llvm::yaml::escape(RPath) << "\"\n";
932 OS.indent(Indent) << "}";
933}
934
935void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
936 Optional<bool> IsCaseSensitive) {
937 using namespace llvm::sys;
938
939 OS << "{\n"
940 " 'version': 0,\n";
941 if (IsCaseSensitive.hasValue())
942 OS << " 'case-sensitive': '"
943 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
944 OS << " 'roots': [\n";
945
946 if (Entries.empty())
947 return;
948
949 const YAMLVFSEntry &Entry = Entries.front();
950 startDirectory(path::parent_path(Entry.VPath));
951 writeEntry(path::filename(Entry.VPath), Entry.RPath);
952
953 for (const auto &Entry : Entries.slice(1)) {
954 StringRef Dir = path::parent_path(Entry.VPath);
955 if (Dir == DirStack.back())
956 OS << ",\n";
957 else {
958 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
959 OS << "\n";
960 endDirectory();
961 }
962 OS << ",\n";
963 startDirectory(Dir);
964 }
965 writeEntry(path::filename(Entry.VPath), Entry.RPath);
966 }
967
968 while (!DirStack.empty()) {
969 OS << "\n";
970 endDirectory();
971 }
972
973 OS << "\n"
974 << " ]\n"
975 << "}\n";
976}
977
Justin Bogner9c785292014-05-20 21:43:27 +0000978void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
979 std::sort(Mappings.begin(), Mappings.end(),
Justin Bogner44fa450342014-05-21 22:46:51 +0000980 [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
Justin Bogner9c785292014-05-20 21:43:27 +0000981 return LHS.VPath < RHS.VPath;
982 });
983
Justin Bogner44fa450342014-05-21 22:46:51 +0000984 JSONWriter(OS).write(Mappings, IsCaseSensitive);
Justin Bogner9c785292014-05-20 21:43:27 +0000985}