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"
42 std::optional<std::string> &Storage) {
43 using namespace llvm::sys::path;
47 if (Path.size() > 1 && root_path(Path) != Path && is_separator(Path.back()))
48 Path = Path.drop_back();
53 if (is_style_windows(Style::native)) {
54 if (Path.size() > 1 && Path.back() ==
':' &&
55 Path.equals_insensitive(root_name(Path))) {
56 Storage = Path.str() +
".";
68 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
69 SeenFileEntries(64), NextFileUID(0) {
73 this->FS = llvm::vfs::getRealFileSystem();
79 assert(statCache &&
"No stat cache provided?");
80 StatCache = std::move(statCache);
91 return llvm::errorCodeToError(
94 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
95 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
97 StringRef DirName = llvm::sys::path::parent_path(Filename);
102 return FileMgr.getDirectoryRef(DirName, CacheFailure);
105DirectoryEntry *&FileManager::getRealDirEntry(
const llvm::vfs::Status &Status) {
106 assert(Status.isDirectory() &&
"The directory should exist!");
111 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
116 UDE =
new (DirsAlloc.Allocate()) DirectoryEntry();
123void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
124 StringRef DirName = llvm::sys::path::parent_path(Path);
131 std::optional<std::string>
Storage;
132 StringRef OriginalDirName = DirName;
135 auto &NamedDirEnt = *SeenDirEntries.insert(
136 {DirName, std::errc::no_such_file_or_directory}).first;
142 if (NamedDirEnt.second)
146 llvm::vfs::Status Status;
148 getStatValue(DirName, Status,
false,
nullptr );
152 auto *UDE =
new (DirsAlloc.Allocate()) DirectoryEntry();
153 NamedDirEnt.second = *UDE;
154 VirtualDirectoryEntries.push_back(UDE);
157 DirectoryEntry *&UDE = getRealDirEntry(Status);
158 NamedDirEnt.second = *UDE;
162 addAncestorsAsVirtualDirs(OriginalDirName);
167 std::optional<std::string> DirNameStr;
174 auto SeenDirInsertResult =
175 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
176 if (!SeenDirInsertResult.second) {
177 if (SeenDirInsertResult.first->second)
179 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
184 auto &NamedDirEnt = *SeenDirInsertResult.first;
185 assert(!NamedDirEnt.second &&
"should be newly-created");
189 StringRef InterndDirName = NamedDirEnt.first();
192 llvm::vfs::Status Status;
193 auto statError = getStatValue(InterndDirName, Status,
false,
198 NamedDirEnt.second = statError;
200 SeenDirEntries.erase(DirName);
201 return llvm::errorCodeToError(statError);
206 NamedDirEnt.second = *UDE;
218 auto SeenFileInsertResult =
219 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
220 if (!SeenFileInsertResult.second) {
221 if (!SeenFileInsertResult.first->second)
222 return llvm::errorCodeToError(
223 SeenFileInsertResult.first->second.getError());
228 ++NumFileCacheMisses;
229 auto *NamedFileEnt = &*SeenFileInsertResult.first;
230 assert(!NamedFileEnt->second &&
"should be newly-created");
234 StringRef InterndFileName = NamedFileEnt->first();
243 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
245 NamedFileEnt->second = Err;
247 SeenFileEntries.erase(Filename);
249 return llvm::errorCodeToError(Err);
257 std::unique_ptr<llvm::vfs::File> F;
258 llvm::vfs::Status Status;
259 auto statError = getStatValue(InterndFileName, Status,
true,
260 openFile ? &F :
nullptr, IsText);
264 NamedFileEnt->second = statError;
266 SeenFileEntries.erase(Filename);
268 return llvm::errorCodeToError(statError);
271 assert((openFile || !F) &&
"undesired open file");
275 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
276 bool ReusingEntry = UFE !=
nullptr;
278 UFE =
new (FilesAlloc.Allocate())
FileEntry();
280 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
324 "filename redirected to a non-canonical filename?");
326 "filename from getStatValue() refers to wrong file");
339 UFE->Size = Status.getSize();
340 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
342 UFE->UID = NextFileUID++;
343 UFE->UniqueID = Status.getUniqueID();
344 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
346 Status.getType() == llvm::sys::fs::file_type::character_file;
347 UFE->File = std::move(F);
350 if (
auto PathName = UFE->File->getName())
351 fillRealPathName(UFE, *PathName);
352 }
else if (!openFile) {
354 fillRealPathName(UFE, InterndFileName);
364 auto ContentOrError = [] {
365 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
366 return llvm::MemoryBuffer::getSTDIN();
370 return llvm::errorCodeToError(ContentOrError.getError());
372 auto Content = std::move(*ContentOrError);
374 Content->getBufferSize(), 0);
376 FE.Content = std::move(Content);
377 FE.IsNamedPipe =
true;
382 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
383 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
384 RFS->setUsageTrackingActive(Active);
389 time_t ModificationTime) {
393 auto &NamedFileEnt = *SeenFileEntries.insert(
394 {Filename, std::errc::no_such_file_or_directory}).first;
395 if (NamedFileEnt.second) {
403 ++NumFileCacheMisses;
404 addAncestorsAsVirtualDirs(Filename);
414 *
this, Filename.empty() ?
"." : Filename,
true));
416 "The directory of a virtual file should already be in the cache.");
419 llvm::vfs::Status Status;
420 const char *InterndFileName = NamedFileEnt.first().data();
421 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
422 Status = llvm::vfs::Status(
423 Status.getName(), Status.getUniqueID(),
424 llvm::sys::toTimePoint(ModificationTime),
425 Status.getUser(), Status.getGroup(), Size,
426 Status.getType(), Status.getPermissions());
428 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
443 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
444 RealFE->UniqueID = Status.getUniqueID();
445 RealFE->IsNamedPipe =
446 Status.getType() == llvm::sys::fs::file_type::fifo_file;
447 fillRealPathName(RealFE, Status.getName());
452 UFE =
new (FilesAlloc.Allocate())
FileEntry();
453 VirtualFileEntries.push_back(UFE);
458 UFE->ModTime = ModificationTime;
459 UFE->Dir = &DirInfo->getDirEntry();
460 UFE->UID = NextFileUID++;
467 llvm::vfs::Status Status;
468 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
471 if (!SeenBypassFileEntries)
472 SeenBypassFileEntries = std::make_unique<
473 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
476 auto Insertion = SeenBypassFileEntries->insert(
477 {VF.
getName(), std::errc::no_such_file_or_directory});
478 if (!Insertion.second)
483 BypassFileEntries.push_back(BFE);
485 BFE->Size = Status.getSize();
487 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
488 BFE->UID = NextFileUID++;
496 StringRef pathRef(Path.data(), Path.size());
498 if (FileSystemOpts.WorkingDir.empty()
499 || llvm::sys::path::is_absolute(pathRef))
503 llvm::sys::path::append(NewPath, pathRef);
504 Path = std::move(NewPath);
509 bool Canonicalize)
const {
512 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
513 FS->makeAbsolute(Path);
518 Changed |= llvm::sys::path::remove_dots(Path);
530 llvm::sys::path::remove_dots(AbsPath,
true);
531 UFE->RealPathName = std::string(AbsPath);
534llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
536 bool RequiresNullTerminator,
537 std::optional<int64_t> MaybeLimit,
bool IsText) {
541 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
543 uint64_t FileSize = Entry->
getSize();
546 FileSize = *MaybeLimit;
553 StringRef Filename = FE.
getName();
556 auto Result = Entry->File->getBuffer(Filename, FileSize,
557 RequiresNullTerminator, isVolatile);
563 return getBufferForFileImpl(Filename, FileSize, isVolatile,
564 RequiresNullTerminator, IsText);
567llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
568FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
569 bool isVolatile,
bool RequiresNullTerminator,
572 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
577 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
586std::error_code FileManager::getStatValue(StringRef Path,
587 llvm::vfs::Status &Status,
589 std::unique_ptr<llvm::vfs::File> *F,
601 StatCache.get(), *FS, IsText);
606 llvm::vfs::Status &
Result) {
610 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
614 return std::error_code();
620 UIDToFiles.resize(NextFileUID);
622 for (
const auto &Entry : SeenFileEntries) {
644 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
645 CanonicalNames.find(Entry);
646 if (Known != CanonicalNames.end())
647 return Known->second;
651 StringRef CanonicalName(Name);
655 if (!FS->getRealPath(Name, RealPathBuf)) {
656 if (is_style_windows(llvm::sys::path::Style::native)) {
660 if (!FS->makeAbsolute(AbsPathBuf)) {
661 if (llvm::sys::path::root_name(RealPathBuf) ==
662 llvm::sys::path::root_name(AbsPathBuf)) {
663 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
668 llvm::sys::path::remove_dots(AbsPathBuf,
true);
669 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
673 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
677 CanonicalNames.insert({Entry, CanonicalName});
678 return CanonicalName;
682 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
683 NumDirLookups +=
Other.NumDirLookups;
684 NumFileLookups +=
Other.NumFileLookups;
685 NumDirCacheMisses +=
Other.NumDirCacheMisses;
686 NumFileCacheMisses +=
Other.NumFileCacheMisses;
690 llvm::errs() <<
"\n*** File Manager Stats:\n";
691 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
692 << UniqueRealDirs.size() <<
" real dirs found.\n";
693 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
694 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
695 llvm::errs() << NumDirLookups <<
" dir lookups, "
696 << NumDirCacheMisses <<
" dir cache misses.\n";
697 llvm::errs() << NumFileLookups <<
" file lookups, "
698 << NumFileCacheMisses <<
" file cache misses.\n";
701 if (
auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
702 llvm::errs() <<
"\n*** Virtual File System Stats:\n"
703 << T->NumStatusCalls <<
" status() calls\n"
704 << T->NumOpenFileForReadCalls <<
" openFileForRead() calls\n"
705 << T->NumDirBeginCalls <<
" dir_begin() calls\n"
706 << T->NumGetRealPathCalls <<
" getRealPath() calls\n"
707 << T->NumExistsCalls <<
" exists() calls\n"
708 << 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.
static void normalizeCacheKey(StringRef &Path, std::optional< std::string > &Storage)
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.
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...
bool makeAbsolutePath(SmallVectorImpl< char > &Path, bool Canonicalize=false) const
Makes Path absolute taking into account FileSystemOptions and the working directory option,...
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.
U cast(CodeGen::Address addr)
@ Other
Other implicit parameter.
Type stored in the StringMap.