// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the FileManager interface.
-//
+///
+/// \file
+/// \brief Defines the clang::FileManager interface and associated types.
+///
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_FILEMANAGER_H
-#define LLVM_CLANG_FILEMANAGER_H
+#ifndef LLVM_CLANG_BASIC_FILEMANAGER_H
+#define LLVM_CLANG_BASIC_FILEMANAGER_H
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/OwningPtr.h"
-#include "llvm/Bitcode/SerializationFwd.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
-#include "llvm/Config/config.h" // for mode_t
-#include <map>
-#include <set>
-#include <string>
+#include <memory>
// FIXME: Enhance libsystem to support inode and other fields in stat.
#include <sys/types.h>
-#include <sys/stat.h>
+#include <map>
+
+#ifdef _MSC_VER
+typedef unsigned short mode_t;
+#endif
+
+struct stat;
+
+namespace llvm {
+class MemoryBuffer;
+}
namespace clang {
class FileManager;
-
-/// DirectoryEntry - Cached information about one directory on the disk.
-///
+class FileSystemStatCache;
+
+/// \brief Cached information about one directory (either on disk or in
+/// the virtual file system).
class DirectoryEntry {
const char *Name; // Name of the directory.
friend class FileManager;
public:
- DirectoryEntry() : Name(0) {}
- const char *getName() const { return Name; }
+ DirectoryEntry() : Name(nullptr) {}
+ const char *getName() const { return Name; }
};
-/// FileEntry - Cached information about one file on the disk.
+/// \brief Cached information about one file (either on disk
+/// or in the virtual file system).
///
+/// If the 'File' member is valid, then this FileEntry has an open file
+/// descriptor for the file.
class FileEntry {
- const char *Name; // Name of the file.
+ std::string Name; // Name of the file.
off_t Size; // File size in bytes.
time_t ModTime; // Modification time of file.
const DirectoryEntry *Dir; // Directory file lives in.
unsigned UID; // A unique (small) ID for the file.
- dev_t Device; // ID for the device containing the file.
- ino_t Inode; // Inode number for the file.
- mode_t FileMode; // The file mode as returned by 'stat'.
+ llvm::sys::fs::UniqueID UniqueID;
+ bool IsNamedPipe;
+ bool InPCH;
+ bool IsValid; // Is this \c FileEntry initialized and valid?
+
+ /// \brief The open file, if it is owned by the \p FileEntry.
+ mutable std::unique_ptr<vfs::File> File;
friend class FileManager;
+
+ void closeFile() const {
+ File.reset(); // rely on destructor to close File
+ }
+
+ void operator=(const FileEntry &) LLVM_DELETED_FUNCTION;
+
public:
- FileEntry(dev_t device, ino_t inode, mode_t m)
- : Name(0), Device(device), Inode(inode), FileMode(m) {}
- // Add a default constructor for use with llvm::StringMap
- FileEntry() : Name(0), Device(0), Inode(0), FileMode(0) {}
-
- const char *getName() const { return Name; }
+ FileEntry()
+ : UniqueID(0, 0), IsNamedPipe(false), InPCH(false), IsValid(false)
+ {}
+
+ // FIXME: this is here to allow putting FileEntry in std::map. Once we have
+ // emplace, we shouldn't need a copy constructor anymore.
+ /// Intentionally does not copy fields that are not set in an uninitialized
+ /// \c FileEntry.
+ FileEntry(const FileEntry &FE) : UniqueID(FE.UniqueID),
+ IsNamedPipe(FE.IsNamedPipe), InPCH(FE.InPCH), IsValid(FE.IsValid) {
+ assert(!isValid() && "Cannot copy an initialized FileEntry");
+ }
+
+ const char *getName() const { return Name.c_str(); }
+ bool isValid() const { return IsValid; }
off_t getSize() const { return Size; }
unsigned getUID() const { return UID; }
- ino_t getInode() const { return Inode; }
- dev_t getDevice() const { return Device; }
+ const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; }
+ bool isInPCH() const { return InPCH; }
time_t getModificationTime() const { return ModTime; }
- mode_t getFileMode() const { return FileMode; }
-
- /// getDir - Return the directory the file lives in.
- ///
+
+ /// \brief Return the directory the file lives in.
const DirectoryEntry *getDir() const { return Dir; }
-
- bool operator<(const FileEntry& RHS) const {
- return Device < RHS.Device || (Device == RHS.Device && Inode < RHS.Inode);
- }
-};
-// FIXME: This is a lightweight shim that is used by FileManager to cache
-// 'stat' system calls. We will use it with PTH to identify if caching
-// stat calls in PTH files is a performance win.
-class StatSysCallCache {
-public:
- virtual ~StatSysCallCache() {}
- virtual int stat(const char *path, struct stat *buf) = 0;
+ bool operator<(const FileEntry &RHS) const { return UniqueID < RHS.UniqueID; }
+
+ /// \brief Check whether the file is a named pipe (and thus can't be opened by
+ /// the native FileManager methods).
+ bool isNamedPipe() const { return IsNamedPipe; }
};
-
-/// FileManager - Implements support for file system lookup, file system
-/// caching, and directory search management. This also handles more advanced
-/// properties, such as uniquing files based on "inode", so that a file with two
-/// names (e.g. symlinked) will be treated as a single file.
+
+struct FileData;
+
+/// \brief Implements support for file system lookup, file system caching,
+/// and directory search management.
///
-class FileManager {
+/// This also handles more advanced properties, such as uniquing files based
+/// on "inode", so that a file with two names (e.g. symlinked) will be treated
+/// as a single file.
+///
+class FileManager : public RefCountedBase<FileManager> {
+ IntrusiveRefCntPtr<vfs::FileSystem> FS;
+ FileSystemOptions FileSystemOpts;
+
+ /// \brief Cache for existing real directories.
+ std::map<llvm::sys::fs::UniqueID, DirectoryEntry> UniqueRealDirs;
- class UniqueDirContainer;
- class UniqueFileContainer;
+ /// \brief Cache for existing real files.
+ std::map<llvm::sys::fs::UniqueID, FileEntry> UniqueRealFiles;
- /// UniqueDirs/UniqueFiles - Cache for existing directories/files.
+ /// \brief The virtual directories that we have allocated.
///
- UniqueDirContainer &UniqueDirs;
- UniqueFileContainer &UniqueFiles;
+ /// For each virtual file (e.g. foo/bar/baz.cpp), we add all of its parent
+ /// directories (foo/ and foo/bar/) here.
+ SmallVector<DirectoryEntry*, 4> VirtualDirectoryEntries;
+ /// \brief The virtual files that we have allocated.
+ SmallVector<FileEntry*, 4> VirtualFileEntries;
- /// DirEntries/FileEntries - This is a cache of directory/file entries we have
- /// looked up. The actual Entry is owned by UniqueFiles/UniqueDirs above.
+ /// \brief A cache that maps paths to directory entries (either real or
+ /// virtual) we have looked up
///
- llvm::StringMap<DirectoryEntry*, llvm::BumpPtrAllocator> DirEntries;
- llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator> FileEntries;
-
- /// NextFileUID - Each FileEntry we create is assigned a unique ID #.
+ /// The actual Entries for real directories/files are
+ /// owned by UniqueRealDirs/UniqueRealFiles above, while the Entries
+ /// for virtual directories/files are owned by
+ /// VirtualDirectoryEntries/VirtualFileEntries above.
+ ///
+ llvm::StringMap<DirectoryEntry*, llvm::BumpPtrAllocator> SeenDirEntries;
+
+ /// \brief A cache that maps paths to file entries (either real or
+ /// virtual) we have looked up.
+ ///
+ /// \see SeenDirEntries
+ llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator> SeenFileEntries;
+
+ /// \brief The canonical names of directories.
+ llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;
+
+ /// \brief Storage for canonical names that we have computed.
+ llvm::BumpPtrAllocator CanonicalNameStorage;
+
+ /// \brief Each FileEntry we create is assigned a unique ID #.
///
unsigned NextFileUID;
-
+
// Statistics.
unsigned NumDirLookups, NumFileLookups;
unsigned NumDirCacheMisses, NumFileCacheMisses;
-
+
// Caching.
- llvm::OwningPtr<StatSysCallCache> StatCache;
+ std::unique_ptr<FileSystemStatCache> StatCache;
+
+ bool getStatValue(const char *Path, FileData &Data, bool isFile,
+ std::unique_ptr<vfs::File> *F);
+
+ /// Add all ancestors of the given path (pointing to either a file
+ /// or a directory) as virtual directories.
+ void addAncestorsAsVirtualDirs(StringRef Path);
- int stat_cached(const char* path, struct stat* buf) {
- return StatCache.get() ? StatCache->stat(path, buf) : stat(path, buf);
- }
-
public:
- FileManager();
+ FileManager(const FileSystemOptions &FileSystemOpts,
+ IntrusiveRefCntPtr<vfs::FileSystem> FS = nullptr);
~FileManager();
- /// setStatCache - Installs the provided StatSysCallCache object within
- /// the FileManager. Ownership of this object is transferred to the
- /// FileManager.
- void setStatCache(StatSysCallCache *statCache) { StatCache.reset(statCache); }
-
- /// getDirectory - Lookup, cache, and verify the specified directory. This
- /// returns null if the directory doesn't exist.
- ///
- const DirectoryEntry *getDirectory(const std::string &Filename) {
- return getDirectory(&Filename[0], &Filename[0] + Filename.size());
- }
- const DirectoryEntry *getDirectory(const char *FileStart,const char *FileEnd);
-
- /// getFile - Lookup, cache, and verify the specified file. This returns null
- /// if the file doesn't exist.
- ///
- const FileEntry *getFile(const std::string &Filename) {
- return getFile(&Filename[0], &Filename[0] + Filename.size());
+ /// \brief Installs the provided FileSystemStatCache object within
+ /// the FileManager.
+ ///
+ /// Ownership of this object is transferred to the FileManager.
+ ///
+ /// \param statCache the new stat cache to install. Ownership of this
+ /// object is transferred to the FileManager.
+ ///
+ /// \param AtBeginning whether this new stat cache must be installed at the
+ /// beginning of the chain of stat caches. Otherwise, it will be added to
+ /// the end of the chain.
+ void addStatCache(std::unique_ptr<FileSystemStatCache> statCache,
+ bool AtBeginning = false);
+
+ /// \brief Removes the specified FileSystemStatCache object from the manager.
+ void removeStatCache(FileSystemStatCache *statCache);
+
+ /// \brief Removes all FileSystemStatCache objects from the manager.
+ void clearStatCaches();
+
+ /// \brief Lookup, cache, and verify the specified directory (real or
+ /// virtual).
+ ///
+ /// This returns NULL if the directory doesn't exist.
+ ///
+ /// \param CacheFailure If true and the file does not exist, we'll cache
+ /// the failure to find this file.
+ const DirectoryEntry *getDirectory(StringRef DirName,
+ bool CacheFailure = true);
+
+ /// \brief Lookup, cache, and verify the specified file (real or
+ /// virtual).
+ ///
+ /// This returns NULL if the file doesn't exist.
+ ///
+ /// \param OpenFile if true and the file exists, it will be opened.
+ ///
+ /// \param CacheFailure If true and the file does not exist, we'll cache
+ /// the failure to find this file.
+ const FileEntry *getFile(StringRef Filename, bool OpenFile = false,
+ bool CacheFailure = true);
+
+ /// \brief Returns the current file system options
+ const FileSystemOptions &getFileSystemOptions() { return FileSystemOpts; }
+
+ IntrusiveRefCntPtr<vfs::FileSystem> getVirtualFileSystem() const {
+ return FS;
}
- const FileEntry *getFile(const char *FilenameStart,
- const char *FilenameEnd);
-
+
+ /// \brief Retrieve a file entry for a "virtual" file that acts as
+ /// if there were a file with the given name on disk.
+ ///
+ /// The file itself is not accessed.
+ const FileEntry *getVirtualFile(StringRef Filename, off_t Size,
+ time_t ModificationTime);
+
+ /// \brief Open the specified file as a MemoryBuffer, returning a new
+ /// MemoryBuffer if successful, otherwise returning null.
+ llvm::MemoryBuffer *getBufferForFile(const FileEntry *Entry,
+ std::string *ErrorStr = nullptr,
+ bool isVolatile = false,
+ bool ShouldCloseOpenFile = true);
+ llvm::MemoryBuffer *getBufferForFile(StringRef Filename,
+ std::string *ErrorStr = nullptr);
+
+ /// \brief Get the 'stat' information for the given \p Path.
+ ///
+ /// If the path is relative, it will be resolved against the WorkingDir of the
+ /// FileManager's FileSystemOptions.
+ ///
+ /// \returns false on success, true on error.
+ bool getNoncachedStatValue(StringRef Path,
+ vfs::Status &Result);
+
+ /// \brief Remove the real file \p Entry from the cache.
+ void invalidateCache(const FileEntry *Entry);
+
+ /// \brief If path is not absolute and FileSystemOptions set the working
+ /// directory, the path is modified to be relative to the given
+ /// working directory.
+ void FixupRelativePath(SmallVectorImpl<char> &path) const;
+
+ /// \brief Produce an array mapping from the unique IDs assigned to each
+ /// file to the corresponding FileEntry pointer.
+ void GetUniqueIDMapping(
+ SmallVectorImpl<const FileEntry *> &UIDToFiles) const;
+
+ /// \brief Modifies the size and modification time of a previously created
+ /// FileEntry. Use with caution.
+ static void modifyFileEntry(FileEntry *File, off_t Size,
+ time_t ModificationTime);
+
+ /// \brief Retrieve the canonical name for a given directory.
+ ///
+ /// This is a very expensive operation, despite its results being cached,
+ /// and should only be used when the physical layout of the file system is
+ /// required, which is (almost) never.
+ StringRef getCanonicalName(const DirectoryEntry *Dir);
+
void PrintStats() const;
};