21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/SmallString.h"
23#include "llvm/ADT/Statistic.h"
24#include "llvm/Config/llvm-config.h"
25#include "llvm/Support/FileSystem.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
40#define DEBUG_TYPE "file-search"
45 "Number of directory cache misses.");
54 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
55 SeenFileEntries(64), NextFileUID(0) {
59 this->FS = llvm::vfs::getRealFileSystem();
65 assert(statCache &&
"No stat cache provided?");
66 StatCache = std::move(statCache);
77 return llvm::errorCodeToError(
81 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
83 StringRef DirName = llvm::sys::path::parent_path(
Filename);
93void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
94 StringRef DirName = llvm::sys::path::parent_path(Path);
98 auto &NamedDirEnt = *SeenDirEntries.insert(
99 {DirName, std::errc::no_such_file_or_directory}).first;
105 if (NamedDirEnt.second)
110 UDE->Name = NamedDirEnt.first();
111 NamedDirEnt.second = *UDE;
112 VirtualDirectoryEntries.push_back(UDE);
115 addAncestorsAsVirtualDirs(DirName);
123 if (DirName.size() > 1 &&
124 DirName != llvm::sys::path::root_path(DirName) &&
125 llvm::sys::path::is_separator(DirName.back()))
126 DirName = DirName.substr(0, DirName.size()-1);
127 std::optional<std::string> DirNameStr;
128 if (is_style_windows(llvm::sys::path::Style::native)) {
131 if (DirName.size() > 1 && DirName.back() ==
':' &&
132 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
133 DirNameStr = DirName.str() +
'.';
134 DirName = *DirNameStr;
142 auto SeenDirInsertResult =
143 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
144 if (!SeenDirInsertResult.second) {
145 if (SeenDirInsertResult.first->second)
147 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
152 auto &NamedDirEnt = *SeenDirInsertResult.first;
153 assert(!NamedDirEnt.second &&
"should be newly-created");
157 StringRef InterndDirName = NamedDirEnt.first();
160 llvm::vfs::Status Status;
161 auto statError = getStatValue(InterndDirName, Status,
false,
166 NamedDirEnt.second = statError;
168 SeenDirEntries.erase(DirName);
169 return llvm::errorCodeToError(statError);
182 UDE->Name = InterndDirName;
184 NamedDirEnt.second = *UDE;
189llvm::ErrorOr<const DirectoryEntry *>
193 return &
Result->getDirEntry();
194 return llvm::errorToErrorCode(
Result.takeError());
197llvm::ErrorOr<const FileEntry *>
201 return &
Result->getFileEntry();
202 return llvm::errorToErrorCode(
Result.takeError());
210 auto SeenFileInsertResult =
211 SeenFileEntries.insert({
Filename, std::errc::no_such_file_or_directory});
212 if (!SeenFileInsertResult.second) {
213 if (!SeenFileInsertResult.first->second)
214 return llvm::errorCodeToError(
215 SeenFileInsertResult.first->second.getError());
220 ++NumFileCacheMisses;
221 auto *NamedFileEnt = &*SeenFileInsertResult.first;
222 assert(!NamedFileEnt->second &&
"should be newly-created");
226 StringRef InterndFileName = NamedFileEnt->first();
235 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
237 NamedFileEnt->second = Err;
241 return llvm::errorCodeToError(Err);
249 std::unique_ptr<llvm::vfs::File> F;
250 llvm::vfs::Status Status;
251 auto statError = getStatValue(InterndFileName, Status,
true,
252 openFile ? &F :
nullptr);
256 NamedFileEnt->second = statError;
260 return llvm::errorCodeToError(statError);
263 assert((openFile || !F) &&
"undesired open file");
267 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
268 bool ReusingEntry = UFE !=
nullptr;
270 UFE =
new (FilesAlloc.Allocate())
FileEntry();
272 if (!Status.ExposesExternalVFSPath || Status.getName() ==
Filename) {
315 assert(Redirection.second->V.is<
FileEntry *>() &&
316 "filename redirected to a non-canonical filename?");
317 assert(Redirection.second->V.get<
FileEntry *>() == UFE &&
318 "filename from getStatValue() refers to wrong file");
336 if (&DirInfo.
getDirEntry() != UFE->Dir && Status.IsVFSMapped)
346 UFE->LastRef = ReturnedRef;
352 UFE->LastRef = ReturnedRef;
353 UFE->Size = Status.getSize();
354 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
356 UFE->UID = NextFileUID++;
357 UFE->UniqueID = Status.getUniqueID();
358 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
359 UFE->File = std::move(F);
362 if (
auto PathName = UFE->File->getName())
363 fillRealPathName(UFE, *PathName);
364 }
else if (!openFile) {
366 fillRealPathName(UFE, InterndFileName);
376 std::unique_ptr<llvm::MemoryBuffer> Content;
377 if (
auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
378 Content = std::move(*ContentOrError);
380 return llvm::errorCodeToError(ContentOrError.getError());
383 Content->getBufferSize(), 0);
385 FE.Content = std::move(Content);
386 FE.IsNamedPipe =
true;
391 time_t ModificationTime) {
396 time_t ModificationTime) {
400 auto &NamedFileEnt = *SeenFileEntries.insert(
401 {
Filename, std::errc::no_such_file_or_directory}).first;
402 if (NamedFileEnt.second) {
407 Value.V.get<
const void *>()));
411 ++NumFileCacheMisses;
412 addAncestorsAsVirtualDirs(
Filename);
424 "The directory of a virtual file should already be in the cache.");
427 llvm::vfs::Status Status;
428 const char *InterndFileName = NamedFileEnt.first().data();
429 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
430 Status = llvm::vfs::Status(
431 Status.getName(), Status.getUniqueID(),
432 llvm::sys::toTimePoint(ModificationTime),
433 Status.getUser(), Status.getGroup(), Size,
434 Status.getType(), Status.getPermissions());
436 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
451 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
452 RealFE->UniqueID = Status.getUniqueID();
453 RealFE->IsNamedPipe =
454 Status.getType() == llvm::sys::fs::file_type::fifo_file;
455 fillRealPathName(RealFE, Status.getName());
460 UFE =
new (FilesAlloc.Allocate())
FileEntry();
461 VirtualFileEntries.push_back(UFE);
467 UFE->ModTime = ModificationTime;
468 UFE->Dir = &DirInfo->getDirEntry();
469 UFE->UID = NextFileUID++;
476 llvm::vfs::Status Status;
477 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
480 if (!SeenBypassFileEntries)
481 SeenBypassFileEntries = std::make_unique<
482 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
485 auto Insertion = SeenBypassFileEntries->insert(
486 {VF.
getName(), std::errc::no_such_file_or_directory});
487 if (!Insertion.second)
492 BypassFileEntries.push_back(BFE);
495 BFE->Size = Status.getSize();
497 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
498 BFE->UID = NextFileUID++;
505 StringRef pathRef(path.data(), path.size());
508 || llvm::sys::path::is_absolute(pathRef))
512 llvm::sys::path::append(NewPath, pathRef);
520 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
521 FS->makeAbsolute(Path);
528void FileManager::fillRealPathName(
FileEntry *UFE, llvm::StringRef FileName) {
535 llvm::sys::path::remove_dots(AbsPath,
true);
536 UFE->RealPathName = std::string(AbsPath.str());
539llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
541 bool RequiresNullTerminator) {
544 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
546 uint64_t FileSize = Entry->
getSize();
556 RequiresNullTerminator, isVolatile);
562 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
563 RequiresNullTerminator);
566llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
567FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
569 bool RequiresNullTerminator) {
571 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
576 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
586FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
587 bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
592 StatCache.get(), *FS);
598 StatCache.get(), *FS);
603 llvm::vfs::Status &
Result) {
607 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
611 return std::error_code();
617 UIDToFiles.resize(NextFileUID);
620 for (llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>,
621 llvm::BumpPtrAllocator>::const_iterator
622 FE = SeenFileEntries.begin(),
623 FEEnd = SeenFileEntries.end();
625 if (llvm::ErrorOr<FileEntryRef::MapValue> Entry = FE->getValue()) {
626 if (
const auto *FE = Entry->V.dyn_cast<
FileEntry *>())
627 UIDToFiles[FE->getUID()] = FE;
631 for (
const auto &VFE : VirtualFileEntries)
632 UIDToFiles[VFE->getUID()] = VFE;
636 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
637 = CanonicalNames.find(Dir);
638 if (Known != CanonicalNames.end())
639 return Known->second;
641 StringRef CanonicalName(Dir->
getName());
644 if (!FS->getRealPath(Dir->
getName(), CanonicalNameBuf))
645 CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage);
647 CanonicalNames.insert({Dir, CanonicalName});
648 return CanonicalName;
652 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
653 = CanonicalNames.find(
File);
654 if (Known != CanonicalNames.end())
655 return Known->second;
657 StringRef CanonicalName(
File->getName());
660 if (!FS->getRealPath(
File->getName(), CanonicalNameBuf))
661 CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage);
663 CanonicalNames.insert({
File, CanonicalName});
664 return CanonicalName;
668 llvm::errs() <<
"\n*** File Manager Stats:\n";
669 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
670 << UniqueRealDirs.size() <<
" real dirs found.\n";
671 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
672 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
673 llvm::errs() << NumDirLookups <<
" dir lookups, "
674 << NumDirCacheMisses <<
" dir cache misses.\n";
675 llvm::errs() << NumFileLookups <<
" file lookups, "
676 << NumFileCacheMisses <<
" file cache misses.\n";
static llvm::Expected< DirectoryEntryRef > getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, bool CacheFailure)
Retrieve the directory that the given file name resides in.
ALWAYS_ENABLED_STATISTIC(NumDirLookups, "Number of directory lookups.")
Defines the clang::FileManager interface and associated types.
Defines the FileSystemStatCache interface.
A reference to a DirectoryEntry that includes the name of the directory as it was accessed by the Fil...
const DirectoryEntry & getDirEntry() const
Cached information about one directory (either on disk or in the virtual file system).
StringRef getName() const
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
const FileEntry & getFileEntry() const
llvm::StringMapEntry< llvm::ErrorOr< MapValue > > MapEntry
Type used in the StringMap.
StringRef getName() const
The name of this FileEntry.
DirectoryEntryRef getDir() const
Cached information about one file (either on disk or in the virtual file system).
StringRef getName() const
bool isNamedPipe() const
Check whether the file is a named pipe (and thus can't be opened by the native FileManager methods).
Implements support for file system lookup, file system caching, and directory search management.
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
std::error_code getNoncachedStatValue(StringRef Path, llvm::vfs::Status &Result)
Get the 'stat' information for the given Path.
FileManager(const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
Construct a file manager, optionally with a custom VFS.
llvm::ErrorOr< const DirectoryEntry * > getDirectory(StringRef DirName, bool CacheFailure=true)
Lookup, cache, and verify the specified directory (real or virtual).
llvm::Expected< FileEntryRef > getSTDIN()
Get the FileEntryRef for stdin, returning an error if stdin cannot be read.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const FileEntry *Entry, bool isVolatile=false, bool RequiresNullTerminator=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
bool makeAbsolutePath(SmallVectorImpl< char > &Path) const
Makes Path absolute taking into account FileSystemOptions and the working directory option.
const FileEntry * getVirtualFile(StringRef Filename, off_t Size, time_t ModificationTime)
llvm::Expected< DirectoryEntryRef > getDirectoryRef(StringRef DirName, bool CacheFailure=true)
Lookup, cache, and verify the specified directory (real or virtual).
void setStatCache(std::unique_ptr< FileSystemStatCache > statCache)
Installs the provided FileSystemStatCache object within the FileManager.
FileEntryRef getVirtualFileRef(StringRef Filename, off_t Size, time_t ModificationTime)
Retrieve a file entry for a "virtual" file that acts as if there were a file with the given name on d...
llvm::ErrorOr< const FileEntry * > getFile(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Lookup, cache, and verify the specified file (real or virtual).
bool FixupRelativePath(SmallVectorImpl< char > &path) const
If path is not absolute and FileSystemOptions set the working directory, the path is modified to be r...
void GetUniqueIDMapping(SmallVectorImpl< const FileEntry * > &UIDToFiles) const
Produce an array mapping from the unique IDs assigned to each file to the corresponding FileEntry poi...
OptionalFileEntryRef getBypassFile(FileEntryRef VFE)
Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual file entry,...
StringRef getCanonicalName(const DirectoryEntry *Dir)
Retrieve the canonical name for a given directory.
llvm::Expected< FileEntryRef > getFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Lookup, cache, and verify the specified file (real or virtual).
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
static std::error_code get(StringRef Path, llvm::vfs::Status &Status, bool isFile, std::unique_ptr< llvm::vfs::File > *F, FileSystemStatCache *Cache, llvm::vfs::FileSystem &FS)
Get the 'stat' information for the specified path, using the cache to accelerate it if possible.
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
Type stored in the StringMap.