|
24 | 24 | #include "llvm/Support/Chrono.h"
|
25 | 25 | #include "llvm/Support/ErrorOr.h"
|
26 | 26 | #include "llvm/Support/FileSystem.h"
|
| 27 | +#include "llvm/Support/Path.h" |
27 | 28 | #include "llvm/Support/SourceMgr.h"
|
28 | 29 | #include <cassert>
|
29 | 30 | #include <cstdint>
|
@@ -495,6 +496,230 @@ struct YAMLVFSEntry {
|
495 | 496 | std::string RPath;
|
496 | 497 | };
|
497 | 498 |
|
| 499 | +class VFSFromYamlDirIterImpl; |
| 500 | +class RedirectingFileSystemParser; |
| 501 | + |
| 502 | +/// A virtual file system parsed from a YAML file. |
| 503 | +/// |
| 504 | +/// Currently, this class allows creating virtual directories and mapping |
| 505 | +/// virtual file paths to existing external files, available in \c ExternalFS. |
| 506 | +/// |
| 507 | +/// The basic structure of the parsed file is: |
| 508 | +/// \verbatim |
| 509 | +/// { |
| 510 | +/// 'version': <version number>, |
| 511 | +/// <optional configuration> |
| 512 | +/// 'roots': [ |
| 513 | +/// <directory entries> |
| 514 | +/// ] |
| 515 | +/// } |
| 516 | +/// \endverbatim |
| 517 | +/// |
| 518 | +/// All configuration options are optional. |
| 519 | +/// 'case-sensitive': <boolean, default=true> |
| 520 | +/// 'use-external-names': <boolean, default=true> |
| 521 | +/// 'overlay-relative': <boolean, default=false> |
| 522 | +/// 'fallthrough': <boolean, default=true> |
| 523 | +/// |
| 524 | +/// Virtual directories are represented as |
| 525 | +/// \verbatim |
| 526 | +/// { |
| 527 | +/// 'type': 'directory', |
| 528 | +/// 'name': <string>, |
| 529 | +/// 'contents': [ <file or directory entries> ] |
| 530 | +/// } |
| 531 | +/// \endverbatim |
| 532 | +/// |
| 533 | +/// The default attributes for virtual directories are: |
| 534 | +/// \verbatim |
| 535 | +/// MTime = now() when created |
| 536 | +/// Perms = 0777 |
| 537 | +/// User = Group = 0 |
| 538 | +/// Size = 0 |
| 539 | +/// UniqueID = unspecified unique value |
| 540 | +/// \endverbatim |
| 541 | +/// |
| 542 | +/// Re-mapped files are represented as |
| 543 | +/// \verbatim |
| 544 | +/// { |
| 545 | +/// 'type': 'file', |
| 546 | +/// 'name': <string>, |
| 547 | +/// 'use-external-name': <boolean> # Optional |
| 548 | +/// 'external-contents': <path to external file> |
| 549 | +/// } |
| 550 | +/// \endverbatim |
| 551 | +/// |
| 552 | +/// and inherit their attributes from the external contents. |
| 553 | +/// |
| 554 | +/// In both cases, the 'name' field may contain multiple path components (e.g. |
| 555 | +/// /path/to/file). However, any directory that contains more than one child |
| 556 | +/// must be uniquely represented by a directory entry. |
| 557 | +class RedirectingFileSystem : public vfs::FileSystem { |
| 558 | +public: |
| 559 | + enum EntryKind { EK_Directory, EK_File }; |
| 560 | + |
| 561 | + /// A single file or directory in the VFS. |
| 562 | + class Entry { |
| 563 | + EntryKind Kind; |
| 564 | + std::string Name; |
| 565 | + |
| 566 | + public: |
| 567 | + Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} |
| 568 | + virtual ~Entry() = default; |
| 569 | + |
| 570 | + StringRef getName() const { return Name; } |
| 571 | + EntryKind getKind() const { return Kind; } |
| 572 | + }; |
| 573 | + |
| 574 | + class RedirectingDirectoryEntry : public Entry { |
| 575 | + std::vector<std::unique_ptr<Entry>> Contents; |
| 576 | + Status S; |
| 577 | + |
| 578 | + public: |
| 579 | + RedirectingDirectoryEntry(StringRef Name, |
| 580 | + std::vector<std::unique_ptr<Entry>> Contents, |
| 581 | + Status S) |
| 582 | + : Entry(EK_Directory, Name), Contents(std::move(Contents)), |
| 583 | + S(std::move(S)) {} |
| 584 | + RedirectingDirectoryEntry(StringRef Name, Status S) |
| 585 | + : Entry(EK_Directory, Name), S(std::move(S)) {} |
| 586 | + |
| 587 | + Status getStatus() { return S; } |
| 588 | + |
| 589 | + void addContent(std::unique_ptr<Entry> Content) { |
| 590 | + Contents.push_back(std::move(Content)); |
| 591 | + } |
| 592 | + |
| 593 | + Entry *getLastContent() const { return Contents.back().get(); } |
| 594 | + |
| 595 | + using iterator = decltype(Contents)::iterator; |
| 596 | + |
| 597 | + iterator contents_begin() { return Contents.begin(); } |
| 598 | + iterator contents_end() { return Contents.end(); } |
| 599 | + |
| 600 | + static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } |
| 601 | + }; |
| 602 | + |
| 603 | + class RedirectingFileEntry : public Entry { |
| 604 | + public: |
| 605 | + enum NameKind { NK_NotSet, NK_External, NK_Virtual }; |
| 606 | + |
| 607 | + private: |
| 608 | + std::string ExternalContentsPath; |
| 609 | + NameKind UseName; |
| 610 | + |
| 611 | + public: |
| 612 | + RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, |
| 613 | + NameKind UseName) |
| 614 | + : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), |
| 615 | + UseName(UseName) {} |
| 616 | + |
| 617 | + StringRef getExternalContentsPath() const { return ExternalContentsPath; } |
| 618 | + |
| 619 | + /// whether to use the external path as the name for this file. |
| 620 | + bool useExternalName(bool GlobalUseExternalName) const { |
| 621 | + return UseName == NK_NotSet ? GlobalUseExternalName |
| 622 | + : (UseName == NK_External); |
| 623 | + } |
| 624 | + |
| 625 | + NameKind getUseName() const { return UseName; } |
| 626 | + |
| 627 | + static bool classof(const Entry *E) { return E->getKind() == EK_File; } |
| 628 | + }; |
| 629 | + |
| 630 | +private: |
| 631 | + friend class VFSFromYamlDirIterImpl; |
| 632 | + friend class RedirectingFileSystemParser; |
| 633 | + |
| 634 | + /// The root(s) of the virtual file system. |
| 635 | + std::vector<std::unique_ptr<Entry>> Roots; |
| 636 | + |
| 637 | + /// The file system to use for external references. |
| 638 | + IntrusiveRefCntPtr<FileSystem> ExternalFS; |
| 639 | + |
| 640 | + /// If IsRelativeOverlay is set, this represents the directory |
| 641 | + /// path that should be prefixed to each 'external-contents' entry |
| 642 | + /// when reading from YAML files. |
| 643 | + std::string ExternalContentsPrefixDir; |
| 644 | + |
| 645 | + /// @name Configuration |
| 646 | + /// @{ |
| 647 | + |
| 648 | + /// Whether to perform case-sensitive comparisons. |
| 649 | + /// |
| 650 | + /// Currently, case-insensitive matching only works correctly with ASCII. |
| 651 | + bool CaseSensitive = true; |
| 652 | + |
| 653 | + /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must |
| 654 | + /// be prefixed in every 'external-contents' when reading from YAML files. |
| 655 | + bool IsRelativeOverlay = false; |
| 656 | + |
| 657 | + /// Whether to use to use the value of 'external-contents' for the |
| 658 | + /// names of files. This global value is overridable on a per-file basis. |
| 659 | + bool UseExternalNames = true; |
| 660 | + |
| 661 | + /// Whether to attempt a file lookup in external file system after it wasn't |
| 662 | + /// found in VFS. |
| 663 | + bool IsFallthrough = true; |
| 664 | + /// @} |
| 665 | + |
| 666 | + /// Virtual file paths and external files could be canonicalized without "..", |
| 667 | + /// "." and "./" in their paths. FIXME: some unittests currently fail on |
| 668 | + /// win32 when using remove_dots and remove_leading_dotslash on paths. |
| 669 | + bool UseCanonicalizedPaths = |
| 670 | +#ifdef _WIN32 |
| 671 | + false; |
| 672 | +#else |
| 673 | + true; |
| 674 | +#endif |
| 675 | + |
| 676 | + RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS) |
| 677 | + : ExternalFS(std::move(ExternalFS)) {} |
| 678 | + |
| 679 | + /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly |
| 680 | + /// recursing into the contents of \p From if it is a directory. |
| 681 | + ErrorOr<Entry *> lookupPath(llvm::sys::path::const_iterator Start, |
| 682 | + llvm::sys::path::const_iterator End, |
| 683 | + Entry *From) const; |
| 684 | + |
| 685 | + /// Get the status of a given an \c Entry. |
| 686 | + ErrorOr<Status> status(const Twine &Path, Entry *E); |
| 687 | + |
| 688 | +public: |
| 689 | + /// Looks up \p Path in \c Roots. |
| 690 | + ErrorOr<Entry *> lookupPath(const Twine &Path) const; |
| 691 | + |
| 692 | + /// Parses \p Buffer, which is expected to be in YAML format and |
| 693 | + /// returns a virtual file system representing its contents. |
| 694 | + static RedirectingFileSystem * |
| 695 | + create(std::unique_ptr<MemoryBuffer> Buffer, |
| 696 | + SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, |
| 697 | + void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS); |
| 698 | + |
| 699 | + ErrorOr<Status> status(const Twine &Path) override; |
| 700 | + ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; |
| 701 | + |
| 702 | + std::error_code getRealPath(const Twine &Path, |
| 703 | + SmallVectorImpl<char> &Output) const override; |
| 704 | + |
| 705 | + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; |
| 706 | + |
| 707 | + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; |
| 708 | + |
| 709 | + std::error_code isLocal(const Twine &Path, bool &Result) override; |
| 710 | + |
| 711 | + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; |
| 712 | + |
| 713 | + void setExternalContentsPrefixDir(StringRef PrefixDir); |
| 714 | + |
| 715 | + StringRef getExternalContentsPrefixDir() const; |
| 716 | + |
| 717 | +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| 718 | + LLVM_DUMP_METHOD void dump() const; |
| 719 | + LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const; |
| 720 | +#endif |
| 721 | +}; |
| 722 | + |
498 | 723 | /// Collect all pairs of <virtual path, real path> entries from the
|
499 | 724 | /// \p YAMLFilePath. This is used by the module dependency collector to forward
|
500 | 725 | /// the entries into the reproducer output VFS YAML file.
|
|
0 commit comments