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) {
410 ++NumFileCacheMisses;
411 addAncestorsAsVirtualDirs(
Filename);
423 "The directory of a virtual file should already be in the cache.");
426 llvm::vfs::Status Status;
427 const char *InterndFileName = NamedFileEnt.first().data();
428 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
429 Status = llvm::vfs::Status(
430 Status.getName(), Status.getUniqueID(),
431 llvm::sys::toTimePoint(ModificationTime),
432 Status.getUser(), Status.getGroup(), Size,
433 Status.getType(), Status.getPermissions());
435 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
450 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
451 RealFE->UniqueID = Status.getUniqueID();
452 RealFE->IsNamedPipe =
453 Status.getType() == llvm::sys::fs::file_type::fifo_file;
454 fillRealPathName(RealFE, Status.getName());
459 UFE =
new (FilesAlloc.Allocate())
FileEntry();
460 VirtualFileEntries.push_back(UFE);
466 UFE->ModTime = ModificationTime;
467 UFE->Dir = &DirInfo->getDirEntry();
468 UFE->UID = NextFileUID++;
475 llvm::vfs::Status Status;
476 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
479 if (!SeenBypassFileEntries)
480 SeenBypassFileEntries = std::make_unique<
481 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
484 auto Insertion = SeenBypassFileEntries->insert(
485 {VF.
getName(), std::errc::no_such_file_or_directory});
486 if (!Insertion.second)
491 BypassFileEntries.push_back(BFE);
494 BFE->Size = Status.getSize();
496 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
497 BFE->UID = NextFileUID++;
504 StringRef pathRef(path.data(), path.size());
507 || llvm::sys::path::is_absolute(pathRef))
511 llvm::sys::path::append(NewPath, pathRef);
519 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
520 FS->makeAbsolute(Path);
527void FileManager::fillRealPathName(
FileEntry *UFE, llvm::StringRef FileName) {
534 llvm::sys::path::remove_dots(AbsPath,
true);
535 UFE->RealPathName = std::string(AbsPath.str());
538llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
540 bool RequiresNullTerminator) {
543 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
545 uint64_t FileSize = Entry->
getSize();
555 RequiresNullTerminator, isVolatile);
561 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
562 RequiresNullTerminator);
565llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
566FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
568 bool RequiresNullTerminator) {
570 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
575 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
585FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
586 bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
591 StatCache.get(), *FS);
597 StatCache.get(), *FS);
602 llvm::vfs::Status &
Result) {
606 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
610 return std::error_code();
616 UIDToFiles.resize(NextFileUID);
619 for (llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>,
620 llvm::BumpPtrAllocator>::const_iterator
621 FE = SeenFileEntries.begin(),
622 FEEnd = SeenFileEntries.end();
624 if (llvm::ErrorOr<FileEntryRef::MapValue> Entry = FE->getValue()) {
625 if (
const auto *FE = Entry->V.dyn_cast<
FileEntry *>())
626 UIDToFiles[FE->getUID()] = FE;
630 for (
const auto &VFE : VirtualFileEntries)
631 UIDToFiles[VFE->getUID()] = VFE;
635 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
636 = CanonicalNames.find(Dir);
637 if (Known != CanonicalNames.end())
638 return Known->second;
640 StringRef CanonicalName(Dir->
getName());
643 if (!FS->getRealPath(Dir->
getName(), CanonicalNameBuf))
644 CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage);
646 CanonicalNames.insert({Dir, CanonicalName});
647 return CanonicalName;
651 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
652 = CanonicalNames.find(
File);
653 if (Known != CanonicalNames.end())
654 return Known->second;
656 StringRef CanonicalName(
File->getName());
659 if (!FS->getRealPath(
File->getName(), CanonicalNameBuf))
660 CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage);
662 CanonicalNames.insert({
File, CanonicalName});
663 return CanonicalName;
667 llvm::errs() <<
"\n*** File Manager Stats:\n";
668 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
669 << UniqueRealDirs.size() <<
" real dirs found.\n";
670 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
671 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
672 llvm::errs() << NumDirLookups <<
" dir lookups, "
673 << NumDirCacheMisses <<
" dir cache misses.\n";
674 llvm::errs() << NumFileLookups <<
" file lookups, "
675 << 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.