]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/include/clang/Basic/VirtualFileSystem.h
MFC r345703:
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / include / clang / Basic / VirtualFileSystem.h
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
39 namespace llvm {
40
41 class MemoryBuffer;
42
43 } // namespace llvm
44
45 namespace clang {
46 namespace vfs {
47
48 /// The result of a \p status operation.
49 class 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
59 public:
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.
102 class File {
103 public:
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
129 namespace detail {
130
131 /// An interface for virtual file systems to provide an iterator over the
132 /// (non-recursive) contents of a directory.
133 struct 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.
147 class directory_iterator {
148   std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy
149
150 public:
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
183 class FileSystem;
184
185 /// An input iterator over the recursive contents of a virtual path,
186 /// similar to llvm::sys::fs::recursive_directory_iterator.
187 class 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
194 public:
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.
222 class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
223 public:
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.
276 IntrusiveRefCntPtr<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).
288 class 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
295 public:
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
323 namespace detail {
324
325 class InMemoryDirectory;
326
327 } // namespace detail
328
329 /// An in-memory file system.
330 class InMemoryFileSystem : public FileSystem {
331   std::unique_ptr<detail::InMemoryDirectory> Root;
332   std::string WorkingDirectory;
333   bool UseNormalizedPaths = true;
334
335 public:
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.
390 llvm::sys::fs::UniqueID getNextVirtualUniqueID();
391
392 /// Gets a \p FileSystem for a virtual file system described in YAML
393 /// format.
394 IntrusiveRefCntPtr<FileSystem>
395 getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
396                llvm::SourceMgr::DiagHandlerTy DiagHandler,
397                StringRef YAMLFilePath,
398                void *DiagContext = nullptr,
399                IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
400
401 struct 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.
411 void 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
418 class 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
426 public:
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