Index: include/llvm/Support/VirtualFileSystem.h =================================================================== --- include/llvm/Support/VirtualFileSystem.h +++ include/llvm/Support/VirtualFileSystem.h @@ -24,6 +24,7 @@ #include "llvm/Support/Chrono.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" #include #include @@ -490,6 +491,229 @@ std::string RPath; }; +enum EntryKind { EK_Directory, EK_File }; + +/// A single file or directory in the VFS. +class Entry { + EntryKind Kind; + std::string Name; + +public: + Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} + virtual ~Entry() = default; + + StringRef getName() const { return Name; } + EntryKind getKind() const { return Kind; } +}; + +class RedirectingDirectoryEntry : public Entry { + std::vector> Contents; + Status S; + +public: + RedirectingDirectoryEntry(StringRef Name, + std::vector> Contents, + Status S) + : Entry(EK_Directory, Name), Contents(std::move(Contents)), + S(std::move(S)) {} + RedirectingDirectoryEntry(StringRef Name, Status S) + : Entry(EK_Directory, Name), S(std::move(S)) {} + + Status getStatus() { return S; } + + void addContent(std::unique_ptr Content) { + Contents.push_back(std::move(Content)); + } + + Entry *getLastContent() const { return Contents.back().get(); } + + using iterator = decltype(Contents)::iterator; + + iterator contents_begin() { return Contents.begin(); } + iterator contents_end() { return Contents.end(); } + + static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } +}; + +class RedirectingFileEntry : public Entry { +public: + enum NameKind { NK_NotSet, NK_External, NK_Virtual }; + +private: + std::string ExternalContentsPath; + NameKind UseName; + +public: + RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, + NameKind UseName) + : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), + UseName(UseName) {} + + StringRef getExternalContentsPath() const { return ExternalContentsPath; } + + /// whether to use the external path as the name for this file. + bool useExternalName(bool GlobalUseExternalName) const { + return UseName == NK_NotSet ? GlobalUseExternalName + : (UseName == NK_External); + } + + NameKind getUseName() const { return UseName; } + + static bool classof(const Entry *E) { return E->getKind() == EK_File; } +}; + +class VFSFromYamlDirIterImpl; +class RedirectingFileSystemParser; + +/// A virtual file system parsed from a YAML file. +/// +/// Currently, this class allows creating virtual directories and mapping +/// virtual file paths to existing external files, available in \c ExternalFS. +/// +/// The basic structure of the parsed file is: +/// \verbatim +/// { +/// 'version': , +/// +/// 'roots': [ +/// +/// ] +/// } +/// \endverbatim +/// +/// All configuration options are optional. +/// 'case-sensitive': +/// 'use-external-names': +/// 'overlay-relative': +/// 'fallthrough': +/// +/// Virtual directories are represented as +/// \verbatim +/// { +/// 'type': 'directory', +/// 'name': , +/// 'contents': [ ] +/// } +/// \endverbatim +/// +/// The default attributes for virtual directories are: +/// \verbatim +/// MTime = now() when created +/// Perms = 0777 +/// User = Group = 0 +/// Size = 0 +/// UniqueID = unspecified unique value +/// \endverbatim +/// +/// Re-mapped files are represented as +/// \verbatim +/// { +/// 'type': 'file', +/// 'name': , +/// 'use-external-name': # Optional +/// 'external-contents': +/// } +/// \endverbatim +/// +/// and inherit their attributes from the external contents. +/// +/// In both cases, the 'name' field may contain multiple path components (e.g. +/// /path/to/file). However, any directory that contains more than one child +/// must be uniquely represented by a directory entry. +class RedirectingFileSystem : public vfs::FileSystem { +protected: + friend class VFSFromYamlDirIterImpl; + friend class RedirectingFileSystemParser; + + /// The root(s) of the virtual file system. + std::vector> Roots; + + /// The file system to use for external references. + IntrusiveRefCntPtr ExternalFS; + + /// If IsRelativeOverlay is set, this represents the directory + /// path that should be prefixed to each 'external-contents' entry + /// when reading from YAML files. + std::string ExternalContentsPrefixDir; + + /// @name Configuration + /// @{ + + /// Whether to perform case-sensitive comparisons. + /// + /// Currently, case-insensitive matching only works correctly with ASCII. + bool CaseSensitive = true; + + /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must + /// be prefixed in every 'external-contents' when reading from YAML files. + bool IsRelativeOverlay = false; + + /// Whether to use to use the value of 'external-contents' for the + /// names of files. This global value is overridable on a per-file basis. + bool UseExternalNames = true; + + /// Whether to attempt a file lookup in external file system after it wasn't + /// found in VFS. + bool IsFallthrough = true; + /// @} + + /// Virtual file paths and external files could be canonicalized without "..", + /// "." and "./" in their paths. FIXME: some unittests currently fail on + /// win32 when using remove_dots and remove_leading_dotslash on paths. + bool UseCanonicalizedPaths = +#ifdef _WIN32 + false; +#else + true; +#endif + + RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS) + : ExternalFS(std::move(ExternalFS)) {} + + /// Looks up the path [Start, End) in \p From, possibly + /// recursing into the contents of \p From if it is a directory. + ErrorOr lookupPath(llvm::sys::path::const_iterator Start, + llvm::sys::path::const_iterator End, + Entry *From) const; + + /// Get the status of a given an \c Entry. + ErrorOr status(const Twine &Path, Entry *E); + +public: + /// Looks up \p Path in \c Roots. + ErrorOr lookupPath(const Twine &Path) const; + + /// Parses \p Buffer, which is expected to be in YAML format and + /// returns a virtual file system representing its contents. + static RedirectingFileSystem * + create(std::unique_ptr Buffer, + SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, + void *DiagContext, IntrusiveRefCntPtr ExternalFS); + + ErrorOr status(const Twine &Path) override; + ErrorOr> openFileForRead(const Twine &Path) override; + + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) const override; + + llvm::ErrorOr getCurrentWorkingDirectory() const override; + + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + + std::error_code isLocal(const Twine &Path, bool &Result) override; + + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + + void setExternalContentsPrefixDir(StringRef PrefixDir); + + StringRef getExternalContentsPrefixDir() const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const; + LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const; +#endif +}; + /// Collect all pairs of entries from the /// \p YAMLFilePath. This is used by the module dependency collector to forward /// the entries into the reproducer output VFS YAML file. @@ -523,6 +747,8 @@ OverlayDir.assign(OverlayDirectory.str()); } + const std::vector &getMappings() const { return Mappings; } + void write(llvm::raw_ostream &OS); }; Index: lib/Support/VirtualFileSystem.cpp =================================================================== --- lib/Support/VirtualFileSystem.cpp +++ lib/Support/VirtualFileSystem.cpp @@ -941,82 +941,10 @@ // RedirectingFileSystem implementation //===-----------------------------------------------------------------------===/ -namespace { - -enum EntryKind { EK_Directory, EK_File }; - -/// A single file or directory in the VFS. -class Entry { - EntryKind Kind; - std::string Name; - -public: - Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} - virtual ~Entry() = default; - - StringRef getName() const { return Name; } - EntryKind getKind() const { return Kind; } -}; - -class RedirectingDirectoryEntry : public Entry { - std::vector> Contents; - Status S; - -public: - RedirectingDirectoryEntry(StringRef Name, - std::vector> Contents, - Status S) - : Entry(EK_Directory, Name), Contents(std::move(Contents)), - S(std::move(S)) {} - RedirectingDirectoryEntry(StringRef Name, Status S) - : Entry(EK_Directory, Name), S(std::move(S)) {} - - Status getStatus() { return S; } - - void addContent(std::unique_ptr Content) { - Contents.push_back(std::move(Content)); - } - - Entry *getLastContent() const { return Contents.back().get(); } - - using iterator = decltype(Contents)::iterator; - - iterator contents_begin() { return Contents.begin(); } - iterator contents_end() { return Contents.end(); } - - static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } -}; - -class RedirectingFileEntry : public Entry { -public: - enum NameKind { NK_NotSet, NK_External, NK_Virtual }; - -private: - std::string ExternalContentsPath; - NameKind UseName; - -public: - RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, - NameKind UseName) - : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), - UseName(UseName) {} - - StringRef getExternalContentsPath() const { return ExternalContentsPath; } - - /// whether to use the external path as the name for this file. - bool useExternalName(bool GlobalUseExternalName) const { - return UseName == NK_NotSet ? GlobalUseExternalName - : (UseName == NK_External); - } - - NameKind getUseName() const { return UseName; } - - static bool classof(const Entry *E) { return E->getKind() == EK_File; } -}; - // FIXME: reuse implementation common with OverlayFSDirIterImpl as these // iterators are conceptually similar. -class VFSFromYamlDirIterImpl : public llvm::vfs::detail::DirIterImpl { +class llvm::vfs::VFSFromYamlDirIterImpl + : public llvm::vfs::detail::DirIterImpl { std::string Dir; RedirectingDirectoryEntry::iterator Current, End; @@ -1057,207 +985,82 @@ std::error_code increment() override; }; -/// A virtual file system parsed from a YAML file. -/// -/// Currently, this class allows creating virtual directories and mapping -/// virtual file paths to existing external files, available in \c ExternalFS. -/// -/// The basic structure of the parsed file is: -/// \verbatim -/// { -/// 'version': , -/// -/// 'roots': [ -/// -/// ] -/// } -/// \endverbatim -/// -/// All configuration options are optional. -/// 'case-sensitive': -/// 'use-external-names': -/// 'overlay-relative': -/// 'fallthrough': -/// -/// Virtual directories are represented as -/// \verbatim -/// { -/// 'type': 'directory', -/// 'name': , -/// 'contents': [ ] -/// } -/// \endverbatim -/// -/// The default attributes for virtual directories are: -/// \verbatim -/// MTime = now() when created -/// Perms = 0777 -/// User = Group = 0 -/// Size = 0 -/// UniqueID = unspecified unique value -/// \endverbatim -/// -/// Re-mapped files are represented as -/// \verbatim -/// { -/// 'type': 'file', -/// 'name': , -/// 'use-external-name': # Optional -/// 'external-contents': -/// } -/// \endverbatim -/// -/// and inherit their attributes from the external contents. -/// -/// In both cases, the 'name' field may contain multiple path components (e.g. -/// /path/to/file). However, any directory that contains more than one child -/// must be uniquely represented by a directory entry. -class RedirectingFileSystem : public vfs::FileSystem { - friend class RedirectingFileSystemParser; - - /// The root(s) of the virtual file system. - std::vector> Roots; - - /// The file system to use for external references. - IntrusiveRefCntPtr ExternalFS; - - /// If IsRelativeOverlay is set, this represents the directory - /// path that should be prefixed to each 'external-contents' entry - /// when reading from YAML files. - std::string ExternalContentsPrefixDir; - - /// @name Configuration - /// @{ - - /// Whether to perform case-sensitive comparisons. - /// - /// Currently, case-insensitive matching only works correctly with ASCII. - bool CaseSensitive = true; - - /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must - /// be prefixed in every 'external-contents' when reading from YAML files. - bool IsRelativeOverlay = false; - - /// Whether to use to use the value of 'external-contents' for the - /// names of files. This global value is overridable on a per-file basis. - bool UseExternalNames = true; - - /// Whether to attempt a file lookup in external file system after it wasn't - /// found in VFS. - bool IsFallthrough = true; - /// @} - - /// Virtual file paths and external files could be canonicalized without "..", - /// "." and "./" in their paths. FIXME: some unittests currently fail on - /// win32 when using remove_dots and remove_leading_dotslash on paths. - bool UseCanonicalizedPaths = -#ifdef _WIN32 - false; -#else - true; -#endif - -private: - RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS) - : ExternalFS(std::move(ExternalFS)) {} - - /// Looks up the path [Start, End) in \p From, possibly - /// recursing into the contents of \p From if it is a directory. - ErrorOr lookupPath(sys::path::const_iterator Start, - sys::path::const_iterator End, Entry *From) const; - - /// Get the status of a given an \c Entry. - ErrorOr status(const Twine &Path, Entry *E); - -public: - /// Looks up \p Path in \c Roots. - ErrorOr lookupPath(const Twine &Path) const; - - /// Parses \p Buffer, which is expected to be in YAML format and - /// returns a virtual file system representing its contents. - static RedirectingFileSystem * - create(std::unique_ptr Buffer, - SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, - void *DiagContext, IntrusiveRefCntPtr ExternalFS); +llvm::ErrorOr +RedirectingFileSystem::getCurrentWorkingDirectory() const { + return ExternalFS->getCurrentWorkingDirectory(); +} - ErrorOr status(const Twine &Path) override; - ErrorOr> openFileForRead(const Twine &Path) override; +std::error_code +RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { + return ExternalFS->setCurrentWorkingDirectory(Path); +} - std::error_code getRealPath(const Twine &Path, - SmallVectorImpl &Output) const override; +std::error_code RedirectingFileSystem::isLocal(const Twine &Path, + bool &Result) { + return ExternalFS->isLocal(Path, Result); +} - llvm::ErrorOr getCurrentWorkingDirectory() const override { - return ExternalFS->getCurrentWorkingDirectory(); +directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + ErrorOr E = lookupPath(Dir); + if (!E) { + EC = E.getError(); + if (IsFallthrough && EC == errc::no_such_file_or_directory) + return ExternalFS->dir_begin(Dir, EC); + return {}; } - - std::error_code setCurrentWorkingDirectory(const Twine &Path) override { - return ExternalFS->setCurrentWorkingDirectory(Path); + ErrorOr S = status(Dir, *E); + if (!S) { + EC = S.getError(); + return {}; } - - std::error_code isLocal(const Twine &Path, bool &Result) override { - return ExternalFS->isLocal(Path, Result); + if (!S->isDirectory()) { + EC = std::error_code(static_cast(errc::not_a_directory), + std::system_category()); + return {}; } - directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { - ErrorOr E = lookupPath(Dir); - if (!E) { - EC = E.getError(); - if (IsFallthrough && EC == errc::no_such_file_or_directory) - return ExternalFS->dir_begin(Dir, EC); - return {}; - } - ErrorOr S = status(Dir, *E); - if (!S) { - EC = S.getError(); - return {}; - } - if (!S->isDirectory()) { - EC = std::error_code(static_cast(errc::not_a_directory), - std::system_category()); - return {}; - } - - auto *D = cast(*E); - return directory_iterator(std::make_shared( - Dir, D->contents_begin(), D->contents_end(), - /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); - } + auto *D = cast(*E); + return directory_iterator(std::make_shared( + Dir, D->contents_begin(), D->contents_end(), + /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); +} - void setExternalContentsPrefixDir(StringRef PrefixDir) { - ExternalContentsPrefixDir = PrefixDir.str(); - } +void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { + ExternalContentsPrefixDir = PrefixDir.str(); +} - StringRef getExternalContentsPrefixDir() const { - return ExternalContentsPrefixDir; - } +StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const { + return ExternalContentsPrefixDir; +} #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - LLVM_DUMP_METHOD void dump() const { - for (const auto &Root : Roots) - dumpEntry(Root.get()); - } +LLVM_DUMP_METHOD void RedirectingFileSystem::dump() const { + for (const auto &Root : Roots) + dumpEntry(Root.get()); +} - LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const { - StringRef Name = E->getName(); - for (int i = 0, e = NumSpaces; i < e; ++i) - dbgs() << " "; - dbgs() << "'" << Name.str().c_str() << "'" - << "\n"; +LLVM_DUMP_METHOD void RedirectingFileSystem::dumpEntry(Entry *E, + int NumSpaces) const { + StringRef Name = E->getName(); + for (int i = 0, e = NumSpaces; i < e; ++i) + dbgs() << " "; + dbgs() << "'" << Name.str().c_str() << "'" + << "\n"; - if (E->getKind() == EK_Directory) { - auto *DE = dyn_cast(E); - assert(DE && "Should be a directory"); + if (E->getKind() == EK_Directory) { + auto *DE = dyn_cast(E); + assert(DE && "Should be a directory"); - for (std::unique_ptr &SubEntry : - llvm::make_range(DE->contents_begin(), DE->contents_end())) - dumpEntry(SubEntry.get(), NumSpaces + 2); - } + for (std::unique_ptr &SubEntry : + llvm::make_range(DE->contents_begin(), DE->contents_end())) + dumpEntry(SubEntry.get(), NumSpaces + 2); } +} #endif -}; /// A helper class to hold the common YAML parsing state. -class RedirectingFileSystemParser { +class llvm::vfs::RedirectingFileSystemParser { yaml::Stream &Stream; void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } @@ -1684,8 +1487,6 @@ } }; -} // namespace - RedirectingFileSystem * RedirectingFileSystem::create(std::unique_ptr Buffer, SourceMgr::DiagHandlerTy DiagHandler,