Warning: That file was not part of the compilation database. It may have many parsing errors.

1//===- VirtualFileSystem.h - 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//
10/// \file
11/// Defines the virtual file system interface vfs::FileSystem.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
16#define LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
17
18#include "clang/Basic/LLVM.h"
19#include "llvm/ADT/IntrusiveRefCntPtr.h"
20#include "llvm/ADT/None.h"
21#include "llvm/ADT/Optional.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/ADT/Twine.h"
25#include "llvm/Support/Chrono.h"
26#include "llvm/Support/ErrorOr.h"
27#include "llvm/Support/FileSystem.h"
28#include "llvm/Support/SourceMgr.h"
29#include <cassert>
30#include <cstdint>
31#include <ctime>
32#include <memory>
33#include <stack>
34#include <string>
35#include <system_error>
36#include <utility>
37#include <vector>
38
39namespace llvm {
40
41class MemoryBuffer;
42
43} // namespace llvm
44
45namespace clang {
46namespace vfs {
47
48/// The result of a \p status operation.
49class Status {
50 std::string Name;
51 llvm::sys::fs::UniqueID UID;
52 llvm::sys::TimePoint<> MTime;
53 uint32_t User;
54 uint32_t Group;
55 uint64_t Size;
56 llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error;
57 llvm::sys::fs::perms Perms;
58
59public:
60 // FIXME: remove when files support multiple names
61 bool IsVFSMapped = false;
62
63 Status() = default;
64 Status(const llvm::sys::fs::file_status &Status);
65 Status(StringRef Name, llvm::sys::fs::UniqueID UID,
66 llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group,
67 uint64_t Size, llvm::sys::fs::file_type Type,
68 llvm::sys::fs::perms Perms);
69
70 /// Get a copy of a Status with a different name.
71 static Status copyWithNewName(const Status &In, StringRef NewName);
72 static Status copyWithNewName(const llvm::sys::fs::file_status &In,
73 StringRef NewName);
74
75 /// Returns the name that should be used for this file or directory.
76 StringRef getName() const { return Name; }
77
78 /// @name Status interface from llvm::sys::fs
79 /// @{
80 llvm::sys::fs::file_type getType() const { return Type; }
81 llvm::sys::fs::perms getPermissions() const { return Perms; }
82 llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; }
83 llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
84 uint32_t getUser() const { return User; }
85 uint32_t getGroup() const { return Group; }
86 uint64_t getSize() const { return Size; }
87 /// @}
88 /// @name Status queries
89 /// These are static queries in llvm::sys::fs.
90 /// @{
91 bool equivalent(const Status &Other) const;
92 bool isDirectory() const;
93 bool isRegularFile() const;
94 bool isOther() const;
95 bool isSymlink() const;
96 bool isStatusKnown() const;
97 bool exists() const;
98 /// @}
99};
100
101/// Represents an open file.
102class File {
103public:
104 /// Destroy the file after closing it (if open).
105 /// Sub-classes should generally call close() inside their destructors. We
106 /// cannot do that from the base class, since close is virtual.
107 virtual ~File();
108
109 /// Get the status of the file.
110 virtual llvm::ErrorOr<Status> status() = 0;
111
112 /// Get the name of the file
113 virtual llvm::ErrorOr<std::string> getName() {
114 if (auto Status = status())
115 return Status->getName().str();
116 else
117 return Status.getError();
118 }
119
120 /// Get the contents of the file as a \p MemoryBuffer.
121 virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
122 getBuffer(const Twine &Name, int64_t FileSize = -1,
123 bool RequiresNullTerminator = true, bool IsVolatile = false) = 0;
124
125 /// Closes the file.
126 virtual std::error_code close() = 0;
127};
128
129namespace detail {
130
131/// An interface for virtual file systems to provide an iterator over the
132/// (non-recursive) contents of a directory.
133struct DirIterImpl {
134 virtual ~DirIterImpl();
135
136 /// Sets \c CurrentEntry to the next entry in the directory on success,
137 /// or returns a system-defined \c error_code.
138 virtual std::error_code increment() = 0;
139
140 Status CurrentEntry;
141};
142
143} // namespace detail
144
145/// An input iterator over the entries in a virtual path, similar to
146/// llvm::sys::fs::directory_iterator.
147class directory_iterator {
148 std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy
149
150public:
151 directory_iterator(std::shared_ptr<detail::DirIterImpl> I)
152 : Impl(std::move(I)) {
153 assert(Impl.get() != nullptr && "requires non-null implementation");
154 if (!Impl->CurrentEntry.isStatusKnown())
155 Impl.reset(); // Normalize the end iterator to Impl == nullptr.
156 }
157
158 /// Construct an 'end' iterator.
159 directory_iterator() = default;
160
161 /// Equivalent to operator++, with an error code.
162 directory_iterator &increment(std::error_code &EC) {
163 assert(Impl && "attempting to increment past end");
164 EC = Impl->increment();
165 if (!Impl->CurrentEntry.isStatusKnown())
166 Impl.reset(); // Normalize the end iterator to Impl == nullptr.
167 return *this;
168 }
169
170 const Status &operator*() const { return Impl->CurrentEntry; }
171 const Status *operator->() const { return &Impl->CurrentEntry; }
172
173 bool operator==(const directory_iterator &RHS) const {
174 if (Impl && RHS.Impl)
175 return Impl->CurrentEntry.equivalent(RHS.Impl->CurrentEntry);
176 return !Impl && !RHS.Impl;
177 }
178 bool operator!=(const directory_iterator &RHS) const {
179 return !(*this == RHS);
180 }
181};
182
183class FileSystem;
184
185/// An input iterator over the recursive contents of a virtual path,
186/// similar to llvm::sys::fs::recursive_directory_iterator.
187class recursive_directory_iterator {
188 using IterState =
189 std::stack<directory_iterator, std::vector<directory_iterator>>;
190
191 FileSystem *FS;
192 std::shared_ptr<IterState> State; // Input iterator semantics on copy.
193
194public:
195 recursive_directory_iterator(FileSystem &FS, const Twine &Path,
196 std::error_code &EC);
197
198 /// Construct an 'end' iterator.
199 recursive_directory_iterator() = default;
200
201 /// Equivalent to operator++, with an error code.
202 recursive_directory_iterator &increment(std::error_code &EC);
203
204 const Status &operator*() const { return *State->top(); }
205 const Status *operator->() const { return &*State->top(); }
206
207 bool operator==(const recursive_directory_iterator &Other) const {
208 return State == Other.State; // identity
209 }
210 bool operator!=(const recursive_directory_iterator &RHS) const {
211 return !(*this == RHS);
212 }
213
214 /// Gets the current level. Starting path is at level 0.
215 int level() const {
216 assert(!State->empty() && "Cannot get level without any iteration state");
217 return State->size()-1;
218 }
219};
220
221/// The virtual file system interface.
222class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
223public:
224 virtual ~FileSystem();
225
226 /// Get the status of the entry at \p Path, if one exists.
227 virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
228
229 /// Get a \p File object for the file at \p Path, if one exists.
230 virtual llvm::ErrorOr<std::unique_ptr<File>>
231 openFileForRead(const Twine &Path) = 0;
232
233 /// This is a convenience method that opens a file, gets its content and then
234 /// closes the file.
235 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
236 getBufferForFile(const Twine &Name, int64_t FileSize = -1,
237 bool RequiresNullTerminator = true, bool IsVolatile = false);
238
239 /// Get a directory_iterator for \p Dir.
240 /// \note The 'end' iterator is directory_iterator().
241 virtual directory_iterator dir_begin(const Twine &Dir,
242 std::error_code &EC) = 0;
243
244 /// Set the working directory. This will affect all following operations on
245 /// this file system and may propagate down for nested file systems.
246 virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
247
248 /// Get the working directory of this file system.
249 virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
250
251 /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve
252 /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`.
253 /// This returns errc::operation_not_permitted if not implemented by subclass.
254 virtual std::error_code getRealPath(const Twine &Path,
255 SmallVectorImpl<char> &Output) const;
256
257 /// Check whether a file exists. Provided for convenience.
258 bool exists(const Twine &Path);
259
260 /// Make \a Path an absolute path.
261 ///
262 /// Makes \a Path absolute using the current directory if it is not already.
263 /// An empty \a Path will result in the current directory.
264 ///
265 /// /absolute/path => /absolute/path
266 /// relative/../path => <current-directory>/relative/../path
267 ///
268 /// \param Path A path that is modified to be an absolute path.
269 /// \returns success if \a path has been made absolute, otherwise a
270 /// platform-specific error_code.
271 std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
272};
273
274/// Gets an \p vfs::FileSystem for the 'real' file system, as seen by
275/// the operating system.
276IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
277
278/// A file system that allows overlaying one \p AbstractFileSystem on top
279/// of another.
280///
281/// Consists of a stack of >=1 \p FileSystem objects, which are treated as being
282/// one merged file system. When there is a directory that exists in more than
283/// one file system, the \p OverlayFileSystem contains a directory containing
284/// the union of their contents. The attributes (permissions, etc.) of the
285/// top-most (most recently added) directory are used. When there is a file
286/// that exists in more than one file system, the file in the top-most file
287/// system overrides the other(s).
288class OverlayFileSystem : public FileSystem {
289 using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>;
290
291 /// The stack of file systems, implemented as a list in order of
292 /// their addition.
293 FileSystemList FSList;
294
295public:
296 OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
297
298 /// Pushes a file system on top of the stack.
299 void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
300
301 llvm::ErrorOr<Status> status(const Twine &Path) override;
302 llvm::ErrorOr<std::unique_ptr<File>>
303 openFileForRead(const Twine &Path) override;
304 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
305 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
306 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
307 std::error_code getRealPath(const Twine &Path,
308 SmallVectorImpl<char> &Output) const override;
309
310 using iterator = FileSystemList::reverse_iterator;
311 using const_iterator = FileSystemList::const_reverse_iterator;
312
313 /// Get an iterator pointing to the most recently added file system.
314 iterator overlays_begin() { return FSList.rbegin(); }
315 const_iterator overlays_begin() const { return FSList.rbegin(); }
316
317 /// Get an iterator pointing one-past the least recently added file
318 /// system.
319 iterator overlays_end() { return FSList.rend(); }
320 const_iterator overlays_end() const { return FSList.rend(); }
321};
322
323namespace detail {
324
325class InMemoryDirectory;
326
327} // namespace detail
328
329/// An in-memory file system.
330class InMemoryFileSystem : public FileSystem {
331 std::unique_ptr<detail::InMemoryDirectory> Root;
332 std::string WorkingDirectory;
333 bool UseNormalizedPaths = true;
334
335public:
336 explicit InMemoryFileSystem(bool UseNormalizedPaths = true);
337 ~InMemoryFileSystem() override;
338
339 /// Add a file containing a buffer or a directory to the VFS with a
340 /// path. The VFS owns the buffer. If present, User, Group, Type
341 /// and Perms apply to the newly-created file or directory.
342 /// \return true if the file or directory was successfully added,
343 /// false if the file or directory already exists in the file system with
344 /// different contents.
345 bool addFile(const Twine &Path, time_t ModificationTime,
346 std::unique_ptr<llvm::MemoryBuffer> Buffer,
347 Optional<uint32_t> User = None, Optional<uint32_t> Group = None,
348 Optional<llvm::sys::fs::file_type> Type = None,
349 Optional<llvm::sys::fs::perms> Perms = None);
350
351 /// Add a buffer to the VFS with a path. The VFS does not own the buffer.
352 /// If present, User, Group, Type and Perms apply to the newly-created file
353 /// or directory.
354 /// \return true if the file or directory was successfully added,
355 /// false if the file or directory already exists in the file system with
356 /// different contents.
357 bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
358 llvm::MemoryBuffer *Buffer,
359 Optional<uint32_t> User = None,
360 Optional<uint32_t> Group = None,
361 Optional<llvm::sys::fs::file_type> Type = None,
362 Optional<llvm::sys::fs::perms> Perms = None);
363
364 std::string toString() const;
365
366 /// Return true if this file system normalizes . and .. in paths.
367 bool useNormalizedPaths() const { return UseNormalizedPaths; }
368
369 llvm::ErrorOr<Status> status(const Twine &Path) override;
370 llvm::ErrorOr<std::unique_ptr<File>>
371 openFileForRead(const Twine &Path) override;
372 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
373
374 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
375 return WorkingDirectory;
376 }
377 /// Canonicalizes \p Path by combining with the current working
378 /// directory and normalizing the path (e.g. remove dots). If the current
379 /// working directory is not set, this returns errc::operation_not_permitted.
380 ///
381 /// This doesn't resolve symlinks as they are not supported in in-memory file
382 /// system.
383 std::error_code getRealPath(const Twine &Path,
384 SmallVectorImpl<char> &Output) const override;
385
386 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
387};
388
389/// Get a globally unique ID for a virtual file or directory.
390llvm::sys::fs::UniqueID getNextVirtualUniqueID();
391
392/// Gets a \p FileSystem for a virtual file system described in YAML
393/// format.
394IntrusiveRefCntPtr<FileSystem>
395getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
396 llvm::SourceMgr::DiagHandlerTy DiagHandler,
397 StringRef YAMLFilePath,
398 void *DiagContext = nullptr,
399 IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
400
401struct YAMLVFSEntry {
402 template <typename T1, typename T2> YAMLVFSEntry(T1 &&VPath, T2 &&RPath)
403 : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
404 std::string VPath;
405 std::string RPath;
406};
407
408/// Collect all pairs of <virtual path, real path> entries from the
409/// \p YAMLFilePath. This is used by the module dependency collector to forward
410/// the entries into the reproducer output VFS YAML file.
411void collectVFSFromYAML(
412 std::unique_ptr<llvm::MemoryBuffer> Buffer,
413 llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
414 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
415 void *DiagContext = nullptr,
416 IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
417
418class YAMLVFSWriter {
419 std::vector<YAMLVFSEntry> Mappings;
420 Optional<bool> IsCaseSensitive;
421 Optional<bool> IsOverlayRelative;
422 Optional<bool> UseExternalNames;
423 Optional<bool> IgnoreNonExistentContents;
424 std::string OverlayDir;
425
426public:
427 YAMLVFSWriter() = default;
428
429 void addFileMapping(StringRef VirtualPath, StringRef RealPath);
430
431 void setCaseSensitivity(bool CaseSensitive) {
432 IsCaseSensitive = CaseSensitive;
433 }
434
435 void setUseExternalNames(bool UseExtNames) {
436 UseExternalNames = UseExtNames;
437 }
438
439 void setIgnoreNonExistentContents(bool IgnoreContents) {
440 IgnoreNonExistentContents = IgnoreContents;
441 }
442
443 void setOverlayDir(StringRef OverlayDirectory) {
444 IsOverlayRelative = true;
445 OverlayDir.assign(OverlayDirectory.str());
446 }
447
448 void write(llvm::raw_ostream &OS);
449};
450
451} // namespace vfs
452} // namespace clang
453
454#endif // LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
455

Warning: That file was not part of the compilation database. It may have many parsing errors.