r201618 - Initial implementation of virtual file system

Aaron Ballman aaron at aaronballman.com
Wed Feb 19 06:30:20 PST 2014


On Tue, Feb 18, 2014 at 7:10 PM, Ben Langmuir <blangmuir at apple.com> wrote:
> Author: benlangmuir
> Date: Tue Feb 18 18:10:30 2014
> New Revision: 201618
>
> URL: http://llvm.org/viewvc/llvm-project?rev=201618&view=rev
> Log:
> Initial implementation of virtual file system
>
> This adds the minimum virtual file system support to start migrating
> FileManager onto the VFS.
>
> Originally discussed here:
> http://lists.cs.uiuc.edu/pipermail/cfe-dev/2014-February/035188.html
>
> Differential Revision: http://llvm-reviews.chandlerc.com/D2745
>
> Added:
>     cfe/trunk/include/clang/Basic/VirtualFileSystem.h
>     cfe/trunk/lib/Basic/VirtualFileSystem.cpp
> Modified:
>     cfe/trunk/include/clang/Basic/FileManager.h
>     cfe/trunk/include/clang/Basic/FileSystemStatCache.h
>     cfe/trunk/include/clang/Frontend/CompilerInstance.h
>     cfe/trunk/lib/Basic/CMakeLists.txt
>     cfe/trunk/lib/Basic/FileManager.cpp
>     cfe/trunk/lib/Basic/FileSystemStatCache.cpp
>     cfe/trunk/lib/Frontend/ASTUnit.cpp
>     cfe/trunk/lib/Frontend/CacheTokens.cpp
>     cfe/trunk/lib/Frontend/ChainedIncludesSource.cpp
>     cfe/trunk/lib/Frontend/CompilerInstance.cpp
>     cfe/trunk/lib/Frontend/FrontendAction.cpp
>     cfe/trunk/lib/Lex/PTHLexer.cpp
>     cfe/trunk/lib/Serialization/ModuleManager.cpp
>     cfe/trunk/unittests/Basic/FileManagerTest.cpp
>
> Modified: cfe/trunk/include/clang/Basic/FileManager.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/FileManager.h?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/FileManager.h (original)
> +++ cfe/trunk/include/clang/Basic/FileManager.h Tue Feb 18 18:10:30 2014
> @@ -17,6 +17,7 @@
>
>  #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/OwningPtr.h"
> @@ -24,7 +25,6 @@
>  #include "llvm/ADT/StringMap.h"
>  #include "llvm/ADT/StringRef.h"
>  #include "llvm/Support/Allocator.h"
> -#include "llvm/Support/FileSystem.h"
>  // FIXME: Enhance libsystem to support inode and other fields in stat.
>  #include <sys/types.h>
>
> @@ -55,7 +55,7 @@ public:
>  /// \brief Cached information about one file (either on disk
>  /// or in the virtual file system).
>  ///
> -/// If the 'FD' member is valid, then this FileEntry has an open file
> +/// 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.
> @@ -67,31 +67,33 @@ class FileEntry {
>    bool IsNamedPipe;
>    bool InPCH;
>
> -  /// FD - The file descriptor for the file entry if it is opened and owned
> -  /// by the FileEntry.  If not, this is set to -1.
> -  mutable int FD;
> +  /// \brief The open file, if it is owned by the \p FileEntry.
> +  mutable OwningPtr<vfs::File> File;
>    friend class FileManager;
>
> +  void closeFile() const {
> +    File.reset(0); // rely on destructor to close File
> +  }
> +
>  public:
>    FileEntry(llvm::sys::fs::UniqueID UniqueID, bool IsNamedPipe, bool InPCH)
> -      : Name(0), UniqueID(UniqueID), IsNamedPipe(IsNamedPipe), InPCH(InPCH),
> -        FD(-1) {}
> +      : Name(0), UniqueID(UniqueID), IsNamedPipe(IsNamedPipe), InPCH(InPCH)
> +  {}
>    // Add a default constructor for use with llvm::StringMap
>    FileEntry()
> -      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false), FD(-1) {}
> +      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false)
> +  {}
>
>    FileEntry(const FileEntry &FE) {
>      memcpy(this, &FE, sizeof(FE));
> -    assert(FD == -1 && "Cannot copy a file-owning FileEntry");
> +    assert(!File && "Cannot copy a file-owning FileEntry");
>    }
>
>    void operator=(const FileEntry &FE) {
>      memcpy(this, &FE, sizeof(FE));
> -    assert(FD == -1 && "Cannot assign a file-owning FileEntry");
> +    assert(!File && "Cannot assign a file-owning FileEntry");
>    }
>
> -  ~FileEntry();
> -
>    const char *getName() const { return Name; }
>    off_t getSize() const { return Size; }
>    unsigned getUID() const { return UID; }
> @@ -119,6 +121,7 @@ struct FileData;
>  /// as a single file.
>  ///
>  class FileManager : public RefCountedBase<FileManager> {
> +  IntrusiveRefCntPtr<vfs::FileSystem> FS;
>    FileSystemOptions FileSystemOpts;
>
>    class UniqueDirContainer;
> @@ -172,14 +175,15 @@ class FileManager : public RefCountedBas
>    OwningPtr<FileSystemStatCache> StatCache;
>
>    bool getStatValue(const char *Path, FileData &Data, bool isFile,
> -                    int *FileDescriptor);
> +                    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);
>
>  public:
> -  FileManager(const FileSystemOptions &FileSystemOpts);
> +  FileManager(const FileSystemOptions &FileSystemOpts,
> +              llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS = 0);
>    ~FileManager();
>
>    /// \brief Installs the provided FileSystemStatCache object within
> @@ -248,7 +252,7 @@ public:
>    ///
>    /// \returns false on success, true on error.
>    bool getNoncachedStatValue(StringRef Path,
> -                             llvm::sys::fs::file_status &Result);
> +                             vfs::Status &Result);
>
>    /// \brief Remove the real file \p Entry from the cache.
>    void invalidateCache(const FileEntry *Entry);
>
> Modified: cfe/trunk/include/clang/Basic/FileSystemStatCache.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/FileSystemStatCache.h?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/FileSystemStatCache.h (original)
> +++ cfe/trunk/include/clang/Basic/FileSystemStatCache.h Tue Feb 18 18:10:30 2014
> @@ -24,6 +24,11 @@
>
>  namespace clang {
>
> +namespace vfs {
> +class File;
> +class FileSystem;
> +}
> +
>  struct FileData {
>    uint64_t Size;
>    time_t ModTime;
> @@ -57,10 +62,11 @@ public:
>    /// If isFile is true, then this lookup should only return success for files
>    /// (not directories).  If it is false this lookup should only return
>    /// success for directories (not files).  On a successful file lookup, the
> -  /// implementation can optionally fill in FileDescriptor with a valid
> -  /// descriptor and the client guarantees that it will close it.
> +  /// implementation can optionally fill in \p F with a valid \p File object and
> +  /// the client guarantees that it will close it.
>    static bool get(const char *Path, FileData &Data, bool isFile,
> -                  int *FileDescriptor, FileSystemStatCache *Cache);
> +                  vfs::File **F, FileSystemStatCache *Cache,
> +                  vfs::FileSystem &FS);
>
>    /// \brief Sets the next stat call cache in the chain of stat caches.
>    /// Takes ownership of the given stat cache.
> @@ -78,17 +84,16 @@ public:
>
>  protected:
>    virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
> -                               int *FileDescriptor) = 0;
> +                               vfs::File **F, vfs::FileSystem &FS) = 0;
>
>    LookupResult statChained(const char *Path, FileData &Data, bool isFile,
> -                           int *FileDescriptor) {
> +                           vfs::File **F, vfs::FileSystem &FS) {
>      if (FileSystemStatCache *Next = getNextStatCache())
> -      return Next->getStat(Path, Data, isFile, FileDescriptor);
> +      return Next->getStat(Path, Data, isFile, F, FS);
>
>      // If we hit the end of the list of stat caches to try, just compute and
>      // return it without a cache.
> -    return get(Path, Data, isFile, FileDescriptor, 0) ? CacheMissing
> -                                                      : CacheExists;
> +    return get(Path, Data, isFile, F, 0, FS) ? CacheMissing : CacheExists;
>    }
>  };
>
> @@ -107,7 +112,7 @@ public:
>    iterator end() const { return StatCalls.end(); }
>
>    virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
> -                               int *FileDescriptor);
> +                               vfs::File **F, vfs::FileSystem &FS);
>  };
>
>  } // end namespace clang
>
> Added: cfe/trunk/include/clang/Basic/VirtualFileSystem.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/VirtualFileSystem.h?rev=201618&view=auto
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/VirtualFileSystem.h (added)
> +++ cfe/trunk/include/clang/Basic/VirtualFileSystem.h Tue Feb 18 18:10:30 2014
> @@ -0,0 +1,127 @@
> +//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +/// \file
> +/// \brief Defines the virtual file system interface vfs::FileSystem.
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
> +#define LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
> +
> +#include "llvm/ADT/IntrusiveRefCntPtr.h"
> +#include "llvm/Support/FileSystem.h"
> +#include "llvm/Support/ErrorOr.h"
> +
> +namespace llvm {
> +template <typename T> class OwningPtr;
> +class MemoryBuffer;
> +}
> +
> +namespace clang {
> +namespace vfs {
> +
> +/// \brief The result of a \p status operation.
> +class Status {
> +  std::string Name;
> +  std::string ExternalName;
> +  llvm::sys::fs::UniqueID UID;
> +  llvm::sys::TimeValue MTime;
> +  uint32_t User;
> +  uint32_t Group;
> +  uint64_t Size;
> +  llvm::sys::fs::file_type Type;
> +  llvm::sys::fs::perms Perms;
> +
> +public:
> +  Status() : Type(llvm::sys::fs::file_type::status_error) {}
> +  Status(const llvm::sys::fs::file_status &Status);
> +  Status(llvm::StringRef Name, llvm::StringRef RealName,
> +         llvm::sys::fs::UniqueID UID, llvm::sys::TimeValue MTime,
> +         uint32_t User, uint32_t Group, uint64_t Size,
> +         llvm::sys::fs::file_type Type, llvm::sys::fs::perms Perms);
> +
> +  /// \brief Returns the name this status was looked up by.
> +  llvm::StringRef getName() const { return Name; }
> +
> +  /// \brief Returns the name to use outside the compiler.
> +  ///
> +  /// For example, in diagnostics or debug info we should use this name.
> +  llvm::StringRef getExternalName() const { return ExternalName; }
> +
> +  void setName(llvm::StringRef N) { Name = N; }
> +  void setExternalName(llvm::StringRef N) { ExternalName = N; }
> +
> +  /// @name Status interface from llvm::sys::fs
> +  /// @{
> +  llvm::sys::fs::file_type getType() const { return Type; }
> +  llvm::sys::fs::perms getPermissions() const { return Perms; }
> +  llvm::sys::TimeValue getLastModificationTime() const { return MTime; }
> +  llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
> +  uint32_t getUser() const { return User; }
> +  uint32_t getGroup() const { return Group; }
> +  uint64_t getSize() const { return Size; }
> +  void setType(llvm::sys::fs::file_type v) { Type = v; }
> +  void setPermissions(llvm::sys::fs::perms p) { Perms = p; }
> +  /// @}
> +  /// @name Status queries
> +  /// These are static queries in llvm::sys::fs.
> +  /// @{
> +  bool equivalent(const Status &Other) const;
> +  bool isDirectory() const;
> +  bool isRegularFile() const;
> +  bool isOther() const;
> +  bool isSymlink() const;
> +  bool isStatusKnown() const;
> +  bool exists() const;
> +  /// @}
> +};
> +
> +/// \brief Represents an open file.
> +class File {
> +public:
> +  /// \brief Destroy the file after closing it (if open).
> +  /// Sub-classes should generally call close() inside their destructors.  We
> +  /// cannot do that from the base class, since close is virtual.
> +  virtual ~File();
> +  /// \brief Get the status of the file.
> +  virtual llvm::ErrorOr<Status> status() = 0;
> +  /// \brief Get the contents of the file as a \p MemoryBuffer.
> +  virtual llvm::error_code
> +  getBuffer(const llvm::Twine &Name,
> +            llvm::OwningPtr<llvm::MemoryBuffer> &Result, int64_t FileSize = -1,
> +            bool RequiresNullTerminator = true) = 0;
> +  /// \brief Closes the file.
> +  virtual llvm::error_code close() = 0;
> +};
> +
> +/// \brief The virtual file system interface.
> +class FileSystem : public llvm::RefCountedBase<FileSystem> {
> +public:
> +  virtual ~FileSystem();
> +
> +  /// \brief Get the status of the entry at \p Path, if one exists.
> +  virtual llvm::ErrorOr<Status> status(const llvm::Twine &Path) = 0;
> +  /// \brief Get a \p File object for the file at \p Path, if one exists.
> +  virtual llvm::error_code openFileForRead(const llvm::Twine &Path,
> +                                           llvm::OwningPtr<File> &Result) = 0;
> +
> +  /// This is a convenience method that opens a file, gets its content and then
> +  /// closes the file.
> +  llvm::error_code getBufferForFile(const llvm::Twine &Name,
> +                                    llvm::OwningPtr<llvm::MemoryBuffer> &Result,
> +                                    int64_t FileSize = -1,
> +                                    bool RequiresNullTerminator = true);
> +};
> +
> +/// \brief Gets an \p vfs::FileSystem for the 'real' file system, as seen by
> +/// the operating system.
> +llvm::IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
> +
> +} // end namespace vfs
> +} // end namespace clang
> +#endif // LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
>
> Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
> +++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Tue Feb 18 18:10:30 2014
> @@ -75,6 +75,9 @@ class CompilerInstance : public ModuleLo
>    /// The target being compiled for.
>    IntrusiveRefCntPtr<TargetInfo> Target;
>
> +  /// The virtual file system.
> +  IntrusiveRefCntPtr<vfs::FileSystem> VirtualFileSystem;
> +
>    /// The file manager.
>    IntrusiveRefCntPtr<FileManager> FileMgr;
>
> @@ -314,6 +317,23 @@ public:
>    void setTarget(TargetInfo *Value);
>
>    /// }
> +  /// @name Virtual File System
> +  /// {
> +
> +  bool hasVirtualFileSystem() const { return VirtualFileSystem != 0; }
> +
> +  vfs::FileSystem &getVirtualFileSystem() const {
> +    assert(hasVirtualFileSystem() &&
> +           "Compiler instance has no virtual file system");
> +    return *VirtualFileSystem;
> +  }
> +
> +  /// \brief Replace the current virtual file system.
> +  void setVirtualFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> FS) {
> +    VirtualFileSystem = FS;
> +  }
> +
> +  /// }
>    /// @name File Manager
>    /// {
>
> @@ -527,6 +547,10 @@ public:
>                      bool ShouldOwnClient = true,
>                      const CodeGenOptions *CodeGenOpts = 0);
>
> +  /// Create a virtual file system and replace any existing one with it.
> +  /// The default is to use the real file system.
> +  void createVirtualFileSystem();
> +
>    /// Create the file manager and replace any existing one with it.
>    void createFileManager();
>
>
> Modified: cfe/trunk/lib/Basic/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/CMakeLists.txt?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Basic/CMakeLists.txt (original)
> +++ cfe/trunk/lib/Basic/CMakeLists.txt Tue Feb 18 18:10:30 2014
> @@ -23,6 +23,7 @@ add_clang_library(clangBasic
>    TokenKinds.cpp
>    Version.cpp
>    VersionTuple.cpp
> +  VirtualFileSystem.cpp
>    )
>
>  # Determine Subversion revision.
>
> Modified: cfe/trunk/lib/Basic/FileManager.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/FileManager.cpp?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Basic/FileManager.cpp (original)
> +++ cfe/trunk/lib/Basic/FileManager.cpp Tue Feb 18 18:10:30 2014
> @@ -30,19 +30,6 @@
>  #include <set>
>  #include <string>
>
> -// FIXME: This is terrible, we need this for ::close.
> -#if !defined(_MSC_VER) && !defined(__MINGW32__)
> -#include <unistd.h>
> -#include <sys/uio.h>
> -#else
> -#include <io.h>
> -#ifndef S_ISFIFO
> -#define S_ISFIFO(x) (0)
> -#endif
> -#endif
> -#if defined(LLVM_ON_UNIX)
> -#include <limits.h>
> -#endif
>  using namespace clang;
>
>  // FIXME: Enhance libsystem to support inode and other fields.
> @@ -57,12 +44,6 @@ using namespace clang;
>  #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
>
>
> -FileEntry::~FileEntry() {
> -  // If this FileEntry owns an open file descriptor that never got used, close
> -  // it.
> -  if (FD != -1) ::close(FD);
> -}
> -
>  class FileManager::UniqueDirContainer {
>    /// UniqueDirs - Cache from ID's to existing directories/files.
>    std::map<llvm::sys::fs::UniqueID, DirectoryEntry> UniqueDirs;
> @@ -101,13 +82,19 @@ public:
>  // Common logic.
>  //===----------------------------------------------------------------------===//
>
> -FileManager::FileManager(const FileSystemOptions &FSO)
> -  : FileSystemOpts(FSO),
> +FileManager::FileManager(const FileSystemOptions &FSO,
> +                         IntrusiveRefCntPtr<vfs::FileSystem> FS)
> +  : FS(FS), FileSystemOpts(FSO),
>      UniqueRealDirs(*new UniqueDirContainer()),
>      UniqueRealFiles(*new UniqueFileContainer()),
>      SeenDirEntries(64), SeenFileEntries(64), NextFileUID(0) {
>    NumDirLookups = NumFileLookups = 0;
>    NumDirCacheMisses = NumFileCacheMisses = 0;
> +
> +  // If the caller doesn't provide a virtual file system, just grab the real
> +  // file system.
> +  if (!FS)
> +    this->FS = vfs::getRealFileSystem();
>  }
>
>  FileManager::~FileManager() {
> @@ -309,10 +296,9 @@ const FileEntry *FileManager::getFile(St
>    // FIXME: This will reduce the # syscalls.
>
>    // Nope, there isn't.  Check to see if the file exists.
> -  int FileDescriptor = -1;
> +  vfs::File *F = 0;
>    FileData Data;
> -  if (getStatValue(InterndFileName, Data, true,
> -                   openFile ? &FileDescriptor : 0)) {
> +  if (getStatValue(InterndFileName, Data, true, openFile ? &F : 0)) {
>      // There's no real file at the given path.
>      if (!CacheFailure)
>        SeenFileEntries.erase(Filename);
> @@ -320,10 +306,7 @@ const FileEntry *FileManager::getFile(St
>      return 0;
>    }
>
> -  if (FileDescriptor != -1 && !openFile) {
> -    close(FileDescriptor);
> -    FileDescriptor = -1;
> -  }
> +  assert(openFile || !F && "undesired open file");
>
>    // It exists.  See if we have already opened a file with the same inode.
>    // This occurs when one dir is symlinked to another, for example.
> @@ -333,8 +316,8 @@ const FileEntry *FileManager::getFile(St
>    NamedFileEnt.setValue(&UFE);
>    if (UFE.getName()) { // Already have an entry with this inode, return it.
>      // If the stat process opened the file, close it to avoid a FD leak.
> -    if (FileDescriptor != -1)
> -      close(FileDescriptor);
> +    if (F)
> +      delete F;
>
>      return &UFE;
>    }
> @@ -347,7 +330,7 @@ const FileEntry *FileManager::getFile(St
>    UFE.ModTime = Data.ModTime;
>    UFE.Dir     = DirInfo;
>    UFE.UID     = NextFileUID++;
> -  UFE.FD      = FileDescriptor;
> +  UFE.File.reset(F);
>    return &UFE;
>  }
>
> @@ -393,10 +376,8 @@ FileManager::getVirtualFile(StringRef Fi
>      // If we had already opened this file, close it now so we don't
>      // leak the descriptor. We're not going to use the file
>      // descriptor anyway, since this is a virtual file.
> -    if (UFE->FD != -1) {
> -      close(UFE->FD);
> -      UFE->FD = -1;
> -    }
> +    if (UFE->File)
> +      UFE->closeFile();
>
>      // If we already have an entry with this inode, return it.
>      if (UFE->getName())
> @@ -414,7 +395,7 @@ FileManager::getVirtualFile(StringRef Fi
>    UFE->ModTime = ModificationTime;
>    UFE->Dir     = DirInfo;
>    UFE->UID     = NextFileUID++;
> -  UFE->FD      = -1;
> +  UFE->File.reset();
>    return UFE;
>  }
>
> @@ -444,20 +425,18 @@ getBufferForFile(const FileEntry *Entry,
>
>    const char *Filename = Entry->getName();
>    // If the file is already open, use the open file descriptor.
> -  if (Entry->FD != -1) {
> -    ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, FileSize);
> +  if (Entry->File) {
> +    ec = Entry->File->getBuffer(Filename, Result, FileSize);
>      if (ErrorStr)
>        *ErrorStr = ec.message();
> -
> -    close(Entry->FD);
> -    Entry->FD = -1;
> +    Entry->closeFile();
>      return Result.take();
>    }
>
>    // Otherwise, open the file.
>
>    if (FileSystemOpts.WorkingDir.empty()) {
> -    ec = llvm::MemoryBuffer::getFile(Filename, Result, FileSize);
> +    ec = FS->getBufferForFile(Filename, Result, FileSize);
>      if (ec && ErrorStr)
>        *ErrorStr = ec.message();
>      return Result.take();
> @@ -465,7 +444,7 @@ getBufferForFile(const FileEntry *Entry,
>
>    SmallString<128> FilePath(Entry->getName());
>    FixupRelativePath(FilePath);
> -  ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, FileSize);
> +  ec = FS->getBufferForFile(FilePath.str(), Result, FileSize);
>    if (ec && ErrorStr)
>      *ErrorStr = ec.message();
>    return Result.take();
> @@ -476,7 +455,7 @@ getBufferForFile(StringRef Filename, std
>    OwningPtr<llvm::MemoryBuffer> Result;
>    llvm::error_code ec;
>    if (FileSystemOpts.WorkingDir.empty()) {
> -    ec = llvm::MemoryBuffer::getFile(Filename, Result);
> +    ec = FS->getBufferForFile(Filename, Result);
>      if (ec && ErrorStr)
>        *ErrorStr = ec.message();
>      return Result.take();
> @@ -484,7 +463,7 @@ getBufferForFile(StringRef Filename, std
>
>    SmallString<128> FilePath(Filename);
>    FixupRelativePath(FilePath);
> -  ec = llvm::MemoryBuffer::getFile(FilePath.c_str(), Result);
> +  ec = FS->getBufferForFile(FilePath.c_str(), Result);
>    if (ec && ErrorStr)
>      *ErrorStr = ec.message();
>    return Result.take();
> @@ -496,26 +475,29 @@ getBufferForFile(StringRef Filename, std
>  /// false if it's an existent real file.  If FileDescriptor is NULL,
>  /// do directory look-up instead of file look-up.
>  bool FileManager::getStatValue(const char *Path, FileData &Data, bool isFile,
> -                               int *FileDescriptor) {
> +                               vfs::File **F) {
>    // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
>    // absolute!
>    if (FileSystemOpts.WorkingDir.empty())
> -    return FileSystemStatCache::get(Path, Data, isFile, FileDescriptor,
> -                                    StatCache.get());
> +    return FileSystemStatCache::get(Path, Data, isFile, F,StatCache.get(), *FS);
>
>    SmallString<128> FilePath(Path);
>    FixupRelativePath(FilePath);
>
> -  return FileSystemStatCache::get(FilePath.c_str(), Data, isFile,
> -                                  FileDescriptor, StatCache.get());
> +  return FileSystemStatCache::get(FilePath.c_str(), Data, isFile, F,
> +                                  StatCache.get(), *FS);
>  }
>
>  bool FileManager::getNoncachedStatValue(StringRef Path,
> -                                        llvm::sys::fs::file_status &Result) {
> +                                        vfs::Status &Result) {
>    SmallString<128> FilePath(Path);
>    FixupRelativePath(FilePath);
>
> -  return llvm::sys::fs::status(FilePath.c_str(), Result);
> +  llvm::ErrorOr<vfs::Status> S = FS->status(FilePath.c_str());
> +  if (!S)
> +    return true;
> +  Result = *S;
> +  return false;
>  }
>
>  void FileManager::invalidateCache(const FileEntry *Entry) {
>
> Modified: cfe/trunk/lib/Basic/FileSystemStatCache.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/FileSystemStatCache.cpp?rev=201618&r1=201617&r2=201618&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Basic/FileSystemStatCache.cpp (original)
> +++ cfe/trunk/lib/Basic/FileSystemStatCache.cpp Tue Feb 18 18:10:30 2014
> @@ -12,7 +12,7 @@
>  //===----------------------------------------------------------------------===//
>
>  #include "clang/Basic/FileSystemStatCache.h"
> -#include "llvm/Support/FileSystem.h"
> +#include "clang/Basic/VirtualFileSystem.h"
>  #include "llvm/Support/Path.h"
>
>  // FIXME: This is terrible, we need this for ::close.
> @@ -30,13 +30,13 @@ using namespace clang;
>
>  void FileSystemStatCache::anchor() { }
>
> -static void copyStatusToFileData(const llvm::sys::fs::file_status &Status,
> +static void copyStatusToFileData(const vfs::Status &Status,
>                                   FileData &Data) {
>    Data.Size = Status.getSize();
>    Data.ModTime = Status.getLastModificationTime().toEpochTime();
>    Data.UniqueID = Status.getUniqueID();
> -  Data.IsDirectory = is_directory(Status);
> -  Data.IsNamedPipe = Status.type() == llvm::sys::fs::file_type::fifo_file;
> +  Data.IsDirectory = Status.isDirectory();
> +  Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
>    Data.InPCH = false;
>  }
>
> @@ -50,22 +50,23 @@ static void copyStatusToFileData(const l
>  /// implementation can optionally fill in FileDescriptor with a valid
>  /// descriptor and the client guarantees that it will close it.
>  bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
> -                              int *FileDescriptor, FileSystemStatCache *Cache) {
> +                              vfs::File **F, FileSystemStatCache *Cache,
> +                              vfs::FileSystem &FS) {
>    LookupResult R;
>    bool isForDir = !isFile;
>
>    // If we have a cache, use it to resolve the stat query.
>    if (Cache)
> -    R = Cache->getStat(Path, Data, isFile, FileDescriptor);
> -  else if (isForDir || !FileDescriptor) {
> +    R = Cache->getStat(Path, Data, isFile, F, FS);
> +  else if (isForDir || !F) {
>      // If this is a directory or a file descriptor is not needed and we have
>      // no cache, just go to the file system.
> -    llvm::sys::fs::file_status Status;
> -    if (llvm::sys::fs::status(Path, Status)) {
> +    llvm::ErrorOr<vfs::Status> Status = FS.status(Path);
> +    if (!Status) {
>        R = CacheMissing;
>      } else {
>        R = CacheExists;
> -      copyStatusToFileData(Status, Data);
> +      copyStatusToFileData(*Status, Data);
>      }
>    } else {
>      // Otherwise, we have to go to the filesystem.  We can always just use
> @@ -75,7 +76,8 @@ bool FileSystemStatCache::get(const char
>      //
>      // Because of this, check to see if the file exists with 'open'.  If the
>      // open succeeds, use fstat to get the stat info.
> -    llvm::error_code EC = llvm::sys::fs::openFileForRead(Path, *FileDescriptor);
> +    llvm::OwningPtr<vfs::File> OwnedFile;
> +    llvm::error_code EC = FS.openFileForRead(Path, OwnedFile);
>
>      if (EC) {
>        // If the open fails, our "stat" fails.
> @@ -84,16 +86,16 @@ bool FileSystemStatCache::get(const char
>        // Otherwise, the open succeeded.  Do an fstat to get the information
>        // about the file.  We'll end up returning the open file descriptor to the
>        // client to do what they please with it.
> -      llvm::sys::fs::file_status Status;
> -      if (!llvm::sys::fs::status(*FileDescriptor, Status)) {
> +      llvm::ErrorOr<vfs::Status> Status = OwnedFile->status();
> +      if (Status) {
>          R = CacheExists;
> -        copyStatusToFileData(Status, Data);
> +        copyStatusToFileData(*Status, Data);
> +        *F = OwnedFile.take();
>        } else {
>          // fstat rarely fails.  If it does, claim the initial open didn't
>          // succeed.
>          R = CacheMissing;
> -        ::close(*FileDescriptor);
> -        *FileDescriptor = -1;
> +        *F = 0;
>        }
>      }
>    }
> @@ -105,9 +107,9 @@ bool FileSystemStatCache::get(const char
>    // demands.
>    if (Data.IsDirectory != isForDir) {
>      // If not, close the file if opened.
> -    if (FileDescriptor && *FileDescriptor != -1) {
> -      ::close(*FileDescriptor);
> -      *FileDescriptor = -1;
> +    if (F && *F) {
> +      (*F)->close();
> +      *F = 0;
>      }
>
>      return true;
> @@ -118,8 +120,8 @@ bool FileSystemStatCache::get(const char
>
>  MemorizeStatCalls::LookupResult
>  MemorizeStatCalls::getStat(const char *Path, FileData &Data, bool isFile,
> -                           int *FileDescriptor) {
> -  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
> +                           vfs::File **F, vfs::FileSystem &FS) {
> +  LookupResult Result = statChained(Path, Data, isFile, F, FS);
>
>    // Do not cache failed stats, it is easy to construct common inconsistent
>    // situations if we do, and they are not important for PCH performance (which
>
> Added: cfe/trunk/lib/Basic/VirtualFileSystem.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/VirtualFileSystem.cpp?rev=201618&view=auto
> ==============================================================================
> --- cfe/trunk/lib/Basic/VirtualFileSystem.cpp (added)
> +++ cfe/trunk/lib/Basic/VirtualFileSystem.cpp Tue Feb 18 18:10:30 2014
> @@ -0,0 +1,162 @@
> +//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +// This file implements the VirtualFileSystem interface.
> +//===----------------------------------------------------------------------===//
> +
> +#include "clang/Basic/VirtualFileSystem.h"
> +#include "llvm/ADT/OwningPtr.h"
> +#include "llvm/Support/MemoryBuffer.h"
> +#include "llvm/Support/SourceMgr.h"
> +#include "llvm/Support/Path.h"
> +
> +using namespace clang;
> +using namespace clang::vfs;
> +using namespace llvm;
> +using llvm::sys::fs::file_status;
> +using llvm::sys::fs::file_type;
> +using llvm::sys::fs::perms;
> +using llvm::sys::fs::UniqueID;
> +
> +Status::Status(const file_status &Status)
> +    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
> +      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
> +      Type(Status.type()), Perms(Status.permissions()) {}
> +
> +Status::Status(StringRef Name, StringRef ExternalName,
> +               UniqueID UID, sys::TimeValue MTime,
> +               uint32_t User, uint32_t Group, uint64_t Size,
> +               file_type Type, perms Perms)
> +    : Name(Name), ExternalName(ExternalName), UID(UID), MTime(MTime),
> +      User(User), Group(Group), Size(Size), Type(Type), Perms(Perms) {}
> +
> +bool Status::equivalent(const Status &Other) const {
> +  return getUniqueID() == Other.getUniqueID();
> +}
> +bool Status::isDirectory() const {
> +  return Type == file_type::directory_file;
> +}
> +bool Status::isRegularFile() const {
> +  return Type == file_type::regular_file;
> +}
> +bool Status::isOther() const {
> +  return Type == exists() && !isRegularFile() && !isDirectory() && !isSymlink();

I'm not certain I understand what Type == exists() is meant to test.
Is this a typo?

FWIW, it yields warnings in MSVC, so this should be resolved: Warning
1 warning C4805: '==' : unsafe mix of type 'int' and type 'bool' in
operation

~Aaron



More information about the cfe-commits mailing list