21#include "llvm/ADT/SmallString.h"
22#include "llvm/ADT/Statistic.h"
23#include "llvm/Config/llvm-config.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include "llvm/Support/Path.h"
27#include "llvm/Support/raw_ostream.h"
38#define DEBUG_TYPE "file-search"
46 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
47 SeenFileEntries(64), NextFileUID(0) {
51 this->FS = llvm::vfs::getRealFileSystem();
57 assert(statCache &&
"No stat cache provided?");
58 StatCache = std::move(statCache);
69 return llvm::errorCodeToError(
73 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
75 StringRef DirName = llvm::sys::path::parent_path(
Filename);
83DirectoryEntry *&FileManager::getRealDirEntry(
const llvm::vfs::Status &Status) {
84 assert(Status.isDirectory() &&
"The directory should exist!");
101void FileManager::addAncestorsAsVirtualDirs(StringRef
Path) {
102 StringRef DirName = llvm::sys::path::parent_path(
Path);
106 auto &NamedDirEnt = *SeenDirEntries.insert(
107 {DirName, std::errc::no_such_file_or_directory}).first;
113 if (NamedDirEnt.second)
117 llvm::vfs::Status Status;
119 getStatValue(DirName, Status,
false,
nullptr );
124 NamedDirEnt.second = *UDE;
125 VirtualDirectoryEntries.push_back(UDE);
129 NamedDirEnt.second = *UDE;
133 addAncestorsAsVirtualDirs(DirName);
141 if (DirName.size() > 1 &&
142 DirName != llvm::sys::path::root_path(DirName) &&
143 llvm::sys::path::is_separator(DirName.back()))
144 DirName = DirName.drop_back();
145 std::optional<std::string> DirNameStr;
146 if (is_style_windows(llvm::sys::path::Style::native)) {
149 if (DirName.size() > 1 && DirName.back() ==
':' &&
150 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
151 DirNameStr = DirName.str() +
'.';
152 DirName = *DirNameStr;
160 auto SeenDirInsertResult =
161 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
162 if (!SeenDirInsertResult.second) {
163 if (SeenDirInsertResult.first->second)
165 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
170 auto &NamedDirEnt = *SeenDirInsertResult.first;
171 assert(!NamedDirEnt.second &&
"should be newly-created");
175 StringRef InterndDirName = NamedDirEnt.first();
178 llvm::vfs::Status Status;
179 auto statError = getStatValue(InterndDirName, Status,
false,
184 NamedDirEnt.second = statError;
186 SeenDirEntries.erase(DirName);
187 return llvm::errorCodeToError(statError);
192 NamedDirEnt.second = *UDE;
204 auto SeenFileInsertResult =
205 SeenFileEntries.insert({
Filename, std::errc::no_such_file_or_directory});
206 if (!SeenFileInsertResult.second) {
207 if (!SeenFileInsertResult.first->second)
208 return llvm::errorCodeToError(
209 SeenFileInsertResult.first->second.getError());
214 ++NumFileCacheMisses;
215 auto *NamedFileEnt = &*SeenFileInsertResult.first;
216 assert(!NamedFileEnt->second &&
"should be newly-created");
220 StringRef InterndFileName = NamedFileEnt->first();
229 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
231 NamedFileEnt->second = Err;
235 return llvm::errorCodeToError(Err);
243 std::unique_ptr<llvm::vfs::File> F;
244 llvm::vfs::Status Status;
245 auto statError = getStatValue(InterndFileName, Status,
true,
246 openFile ? &F :
nullptr, IsText);
250 NamedFileEnt->second = statError;
254 return llvm::errorCodeToError(statError);
257 assert((openFile || !F) &&
"undesired open file");
261 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
262 bool ReusingEntry = UFE !=
nullptr;
264 UFE =
new (FilesAlloc.Allocate())
FileEntry();
266 if (!Status.ExposesExternalVFSPath || Status.getName() ==
Filename) {
309 assert(isa<FileEntry *>(Redirection.second->V) &&
310 "filename redirected to a non-canonical filename?");
311 assert(cast<FileEntry *>(Redirection.second->V) == UFE &&
312 "filename from getStatValue() refers to wrong file");
325 UFE->Size = Status.getSize();
326 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
328 UFE->UID = NextFileUID++;
329 UFE->UniqueID = Status.getUniqueID();
330 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
332 Status.getType() == llvm::sys::fs::file_type::character_file;
333 UFE->File = std::move(F);
336 if (
auto PathName = UFE->File->getName())
337 fillRealPathName(UFE, *PathName);
338 }
else if (!openFile) {
340 fillRealPathName(UFE, InterndFileName);
350 std::unique_ptr<llvm::MemoryBuffer> Content;
351 if (
auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
352 Content = std::move(*ContentOrError);
354 return llvm::errorCodeToError(ContentOrError.getError());
357 Content->getBufferSize(), 0);
359 FE.Content = std::move(Content);
360 FE.IsNamedPipe =
true;
365 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
366 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
367 RFS->setUsageTrackingActive(Active);
372 time_t ModificationTime) {
376 auto &NamedFileEnt = *SeenFileEntries.insert(
377 {
Filename, std::errc::no_such_file_or_directory}).first;
378 if (NamedFileEnt.second) {
380 if (LLVM_LIKELY(isa<FileEntry *>(
Value.V)))
386 ++NumFileCacheMisses;
387 addAncestorsAsVirtualDirs(
Filename);
399 "The directory of a virtual file should already be in the cache.");
402 llvm::vfs::Status Status;
403 const char *InterndFileName = NamedFileEnt.first().data();
404 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
405 Status = llvm::vfs::Status(
406 Status.getName(), Status.getUniqueID(),
407 llvm::sys::toTimePoint(ModificationTime),
408 Status.getUser(), Status.getGroup(), Size,
409 Status.getType(), Status.getPermissions());
411 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
426 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
427 RealFE->UniqueID = Status.getUniqueID();
428 RealFE->IsNamedPipe =
429 Status.getType() == llvm::sys::fs::file_type::fifo_file;
430 fillRealPathName(RealFE, Status.getName());
435 UFE =
new (FilesAlloc.Allocate())
FileEntry();
436 VirtualFileEntries.push_back(UFE);
441 UFE->ModTime = ModificationTime;
442 UFE->Dir = &DirInfo->getDirEntry();
443 UFE->UID = NextFileUID++;
450 llvm::vfs::Status Status;
451 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
454 if (!SeenBypassFileEntries)
455 SeenBypassFileEntries = std::make_unique<
456 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
459 auto Insertion = SeenBypassFileEntries->insert(
460 {VF.
getName(), std::errc::no_such_file_or_directory});
461 if (!Insertion.second)
466 BypassFileEntries.push_back(BFE);
468 BFE->Size = Status.getSize();
470 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
471 BFE->UID = NextFileUID++;
478 StringRef pathRef(path.data(), path.size());
481 || llvm::sys::path::is_absolute(pathRef))
485 llvm::sys::path::append(NewPath, pathRef);
493 if (!llvm::sys::path::is_absolute(StringRef(
Path.data(),
Path.size()))) {
494 FS->makeAbsolute(
Path);
508 llvm::sys::path::remove_dots(AbsPath,
true);
509 UFE->RealPathName = std::string(AbsPath);
512llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
514 bool RequiresNullTerminator,
515 std::optional<int64_t> MaybeLimit,
bool IsText) {
519 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
521 uint64_t FileSize = Entry->
getSize();
524 FileSize = *MaybeLimit;
535 RequiresNullTerminator, isVolatile);
541 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
542 RequiresNullTerminator, IsText);
545llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
546FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
547 bool isVolatile,
bool RequiresNullTerminator,
550 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
555 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
564std::error_code FileManager::getStatValue(StringRef
Path,
565 llvm::vfs::Status &Status,
567 std::unique_ptr<llvm::vfs::File> *F,
579 StatCache.get(), *FS, IsText);
584 llvm::vfs::Status &
Result) {
588 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
592 return std::error_code();
598 UIDToFiles.resize(NextFileUID);
600 for (
const auto &Entry : SeenFileEntries) {
602 if (!Entry.getValue() || !isa<FileEntry *>(Entry.getValue()->V))
622 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
623 CanonicalNames.find(Entry);
624 if (Known != CanonicalNames.end())
625 return Known->second;
629 StringRef CanonicalName(Name);
633 if (!FS->getRealPath(Name, RealPathBuf)) {
634 if (is_style_windows(llvm::sys::path::Style::native)) {
638 if (!FS->makeAbsolute(AbsPathBuf)) {
639 if (llvm::sys::path::root_name(RealPathBuf) ==
640 llvm::sys::path::root_name(AbsPathBuf)) {
641 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
646 llvm::sys::path::remove_dots(AbsPathBuf,
true);
647 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
651 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
655 CanonicalNames.insert({Entry, CanonicalName});
656 return CanonicalName;
660 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
661 NumDirLookups +=
Other.NumDirLookups;
662 NumFileLookups +=
Other.NumFileLookups;
663 NumDirCacheMisses +=
Other.NumDirCacheMisses;
664 NumFileCacheMisses +=
Other.NumFileCacheMisses;
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";
679 if (
auto *
T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
680 llvm::errs() <<
"\n*** Virtual File System Stats:\n"
681 <<
T->NumStatusCalls <<
" status() calls\n"
682 <<
T->NumOpenFileForReadCalls <<
" openFileForRead() calls\n"
683 <<
T->NumDirBeginCalls <<
" dir_begin() calls\n"
684 <<
T->NumGetRealPathCalls <<
" getRealPath() calls\n"
685 <<
T->NumExistsCalls <<
" exists() calls\n"
686 <<
T->NumIsLocalCalls <<
" isLocal() calls\n";
static llvm::Expected< DirectoryEntryRef > getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, bool CacheFailure)
Retrieve the directory that the given file name resides in.
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...
StringRef getName() const
const DirectoryEntry & getDirEntry() const
Cached information about one directory (either on disk or in the virtual file system).
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
const FileEntry & getFileEntry() const
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).
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 AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
void trackVFSUsage(bool Active)
Enable or disable tracking of VFS usage.
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true, std::optional< int64_t > MaybeLimit=std::nullopt, bool IsText=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
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::Expected< FileEntryRef > getSTDIN()
Get the FileEntryRef for stdin, returning an error if stdin cannot be read.
StringRef getCanonicalName(DirectoryEntryRef Dir)
Retrieve the canonical name for a given directory.
void GetUniqueIDMapping(SmallVectorImpl< OptionalFileEntryRef > &UIDToFiles) const
Produce an array mapping from the unique IDs assigned to each file to the corresponding FileEntryRef.
bool makeAbsolutePath(SmallVectorImpl< char > &Path) const
Makes Path absolute taking into account FileSystemOptions and the working directory option.
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...
bool FixupRelativePath(SmallVectorImpl< char > &path) const
If path is not absolute and FileSystemOptions set the working directory, the path is modified to be r...
OptionalFileEntryRef getBypassFile(FileEntryRef VFE)
Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual file entry,...
llvm::Expected< FileEntryRef > getFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true, bool IsText=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, bool IsText=true)
Get the 'stat' information for the specified path, using the cache to accelerate it if possible.
The JSON file list parser is used to communicate input to InstallAPI.
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
const FunctionProtoType * T
@ Other
Other implicit parameter.
Type stored in the StringMap.