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/IOSandbox.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
39#define DEBUG_TYPE "file-search"
47 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
48 SeenFileEntries(64), NextFileUID(0) {
52 this->FS = llvm::vfs::getRealFileSystem();
58 assert(statCache &&
"No stat cache provided?");
59 StatCache = std::move(statCache);
70 return llvm::errorCodeToError(
73 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
74 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
76 StringRef DirName = llvm::sys::path::parent_path(Filename);
81 return FileMgr.getDirectoryRef(DirName, CacheFailure);
84DirectoryEntry *&FileManager::getRealDirEntry(
const llvm::vfs::Status &Status) {
85 assert(Status.isDirectory() &&
"The directory should exist!");
90 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
95 UDE =
new (DirsAlloc.Allocate()) DirectoryEntry();
102void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
103 StringRef DirName = llvm::sys::path::parent_path(Path);
107 auto &NamedDirEnt = *SeenDirEntries.insert(
108 {DirName, std::errc::no_such_file_or_directory}).first;
114 if (NamedDirEnt.second)
118 llvm::vfs::Status Status;
120 getStatValue(DirName, Status,
false,
nullptr );
124 auto *UDE =
new (DirsAlloc.Allocate()) DirectoryEntry();
125 NamedDirEnt.second = *UDE;
126 VirtualDirectoryEntries.push_back(UDE);
129 DirectoryEntry *&UDE = getRealDirEntry(Status);
130 NamedDirEnt.second = *UDE;
134 addAncestorsAsVirtualDirs(DirName);
142 if (DirName.size() > 1 &&
143 DirName != llvm::sys::path::root_path(DirName) &&
144 llvm::sys::path::is_separator(DirName.back()))
145 DirName = DirName.drop_back();
146 std::optional<std::string> DirNameStr;
147 if (is_style_windows(llvm::sys::path::Style::native)) {
150 if (DirName.size() > 1 && DirName.back() ==
':' &&
151 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
152 DirNameStr = DirName.str() +
'.';
153 DirName = *DirNameStr;
161 auto SeenDirInsertResult =
162 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
163 if (!SeenDirInsertResult.second) {
164 if (SeenDirInsertResult.first->second)
166 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
171 auto &NamedDirEnt = *SeenDirInsertResult.first;
172 assert(!NamedDirEnt.second &&
"should be newly-created");
176 StringRef InterndDirName = NamedDirEnt.first();
179 llvm::vfs::Status Status;
180 auto statError = getStatValue(InterndDirName, Status,
false,
185 NamedDirEnt.second = statError;
187 SeenDirEntries.erase(DirName);
188 return llvm::errorCodeToError(statError);
193 NamedDirEnt.second = *UDE;
205 auto SeenFileInsertResult =
206 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
207 if (!SeenFileInsertResult.second) {
208 if (!SeenFileInsertResult.first->second)
209 return llvm::errorCodeToError(
210 SeenFileInsertResult.first->second.getError());
215 ++NumFileCacheMisses;
216 auto *NamedFileEnt = &*SeenFileInsertResult.first;
217 assert(!NamedFileEnt->second &&
"should be newly-created");
221 StringRef InterndFileName = NamedFileEnt->first();
230 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
232 NamedFileEnt->second = Err;
234 SeenFileEntries.erase(Filename);
236 return llvm::errorCodeToError(Err);
244 std::unique_ptr<llvm::vfs::File> F;
245 llvm::vfs::Status Status;
246 auto statError = getStatValue(InterndFileName, Status,
true,
247 openFile ? &F :
nullptr, IsText);
251 NamedFileEnt->second = statError;
253 SeenFileEntries.erase(Filename);
255 return llvm::errorCodeToError(statError);
258 assert((openFile || !F) &&
"undesired open file");
262 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
263 bool ReusingEntry = UFE !=
nullptr;
265 UFE =
new (FilesAlloc.Allocate())
FileEntry();
267 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
311 "filename redirected to a non-canonical filename?");
313 "filename from getStatValue() refers to wrong file");
326 UFE->Size = Status.getSize();
327 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
329 UFE->UID = NextFileUID++;
330 UFE->UniqueID = Status.getUniqueID();
331 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
333 Status.getType() == llvm::sys::fs::file_type::character_file;
334 UFE->File = std::move(F);
337 if (
auto PathName = UFE->File->getName())
338 fillRealPathName(UFE, *PathName);
339 }
else if (!openFile) {
341 fillRealPathName(UFE, InterndFileName);
351 auto ContentOrError = [] {
352 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
353 return llvm::MemoryBuffer::getSTDIN();
357 return llvm::errorCodeToError(ContentOrError.getError());
359 auto Content = std::move(*ContentOrError);
361 Content->getBufferSize(), 0);
363 FE.Content = std::move(Content);
364 FE.IsNamedPipe =
true;
369 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
370 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
371 RFS->setUsageTrackingActive(Active);
376 time_t ModificationTime) {
380 auto &NamedFileEnt = *SeenFileEntries.insert(
381 {Filename, std::errc::no_such_file_or_directory}).first;
382 if (NamedFileEnt.second) {
390 ++NumFileCacheMisses;
391 addAncestorsAsVirtualDirs(Filename);
401 *
this, Filename.empty() ?
"." : Filename,
true));
403 "The directory of a virtual file should already be in the cache.");
406 llvm::vfs::Status Status;
407 const char *InterndFileName = NamedFileEnt.first().data();
408 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
409 Status = llvm::vfs::Status(
410 Status.getName(), Status.getUniqueID(),
411 llvm::sys::toTimePoint(ModificationTime),
412 Status.getUser(), Status.getGroup(), Size,
413 Status.getType(), Status.getPermissions());
415 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
430 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
431 RealFE->UniqueID = Status.getUniqueID();
432 RealFE->IsNamedPipe =
433 Status.getType() == llvm::sys::fs::file_type::fifo_file;
434 fillRealPathName(RealFE, Status.getName());
439 UFE =
new (FilesAlloc.Allocate())
FileEntry();
440 VirtualFileEntries.push_back(UFE);
445 UFE->ModTime = ModificationTime;
446 UFE->Dir = &DirInfo->getDirEntry();
447 UFE->UID = NextFileUID++;
454 llvm::vfs::Status Status;
455 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
458 if (!SeenBypassFileEntries)
459 SeenBypassFileEntries = std::make_unique<
460 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
463 auto Insertion = SeenBypassFileEntries->insert(
464 {VF.
getName(), std::errc::no_such_file_or_directory});
465 if (!Insertion.second)
470 BypassFileEntries.push_back(BFE);
472 BFE->Size = Status.getSize();
474 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
475 BFE->UID = NextFileUID++;
483 StringRef pathRef(Path.data(), Path.size());
485 if (FileSystemOpts.WorkingDir.empty()
486 || llvm::sys::path::is_absolute(pathRef))
490 llvm::sys::path::append(NewPath, pathRef);
498 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
499 FS->makeAbsolute(Path);
513 llvm::sys::path::remove_dots(AbsPath,
true);
514 UFE->RealPathName = std::string(AbsPath);
517llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
519 bool RequiresNullTerminator,
520 std::optional<int64_t> MaybeLimit,
bool IsText) {
524 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
526 uint64_t FileSize = Entry->
getSize();
529 FileSize = *MaybeLimit;
536 StringRef Filename = FE.
getName();
539 auto Result = Entry->File->getBuffer(Filename, FileSize,
540 RequiresNullTerminator, isVolatile);
546 return getBufferForFileImpl(Filename, FileSize, isVolatile,
547 RequiresNullTerminator, IsText);
550llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
551FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
552 bool isVolatile,
bool RequiresNullTerminator,
555 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
560 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
569std::error_code FileManager::getStatValue(StringRef Path,
570 llvm::vfs::Status &Status,
572 std::unique_ptr<llvm::vfs::File> *F,
584 StatCache.get(), *FS, IsText);
589 llvm::vfs::Status &
Result) {
593 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
597 return std::error_code();
603 UIDToFiles.resize(NextFileUID);
605 for (
const auto &Entry : SeenFileEntries) {
627 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
628 CanonicalNames.find(Entry);
629 if (Known != CanonicalNames.end())
630 return Known->second;
634 StringRef CanonicalName(Name);
638 if (!FS->getRealPath(Name, RealPathBuf)) {
639 if (is_style_windows(llvm::sys::path::Style::native)) {
643 if (!FS->makeAbsolute(AbsPathBuf)) {
644 if (llvm::sys::path::root_name(RealPathBuf) ==
645 llvm::sys::path::root_name(AbsPathBuf)) {
646 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
651 llvm::sys::path::remove_dots(AbsPathBuf,
true);
652 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
656 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
660 CanonicalNames.insert({Entry, CanonicalName});
661 return CanonicalName;
665 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
666 NumDirLookups +=
Other.NumDirLookups;
667 NumFileLookups +=
Other.NumFileLookups;
668 NumDirCacheMisses +=
Other.NumDirCacheMisses;
669 NumFileCacheMisses +=
Other.NumFileCacheMisses;
673 llvm::errs() <<
"\n*** File Manager Stats:\n";
674 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
675 << UniqueRealDirs.size() <<
" real dirs found.\n";
676 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
677 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
678 llvm::errs() << NumDirLookups <<
" dir lookups, "
679 << NumDirCacheMisses <<
" dir cache misses.\n";
680 llvm::errs() << NumFileLookups <<
" file lookups, "
681 << NumFileCacheMisses <<
" file cache misses.\n";
684 if (
auto *
T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
685 llvm::errs() <<
"\n*** Virtual File System Stats:\n"
686 <<
T->NumStatusCalls <<
" status() calls\n"
687 <<
T->NumOpenFileForReadCalls <<
" openFileForRead() calls\n"
688 <<
T->NumDirBeginCalls <<
" dir_begin() calls\n"
689 <<
T->NumGetRealPathCalls <<
" getRealPath() calls\n"
690 <<
T->NumExistsCalls <<
" exists() calls\n"
691 <<
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,...
static bool fixupRelativePath(const FileSystemOptions &FileSystemOpts, SmallVectorImpl< char > &Path)
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.
bool isa(CodeGen::Address addr)
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
const FunctionProtoType * T
U cast(CodeGen::Address addr)
@ Other
Other implicit parameter.
Type stored in the StringMap.