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"
48 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
49 SeenFileEntries(64), NextFileUID(0) {
53 this->FS = llvm::vfs::getRealFileSystem();
59 assert(statCache &&
"No stat cache provided?");
60 StatCache = std::move(statCache);
71 return llvm::errorCodeToError(
75 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
77 StringRef DirName = llvm::sys::path::parent_path(
Filename);
85DirectoryEntry *&FileManager::getRealDirEntry(
const llvm::vfs::Status &Status) {
86 assert(Status.isDirectory() &&
"The directory should exist!");
103void FileManager::addAncestorsAsVirtualDirs(StringRef
Path) {
104 StringRef DirName = llvm::sys::path::parent_path(
Path);
108 auto &NamedDirEnt = *SeenDirEntries.insert(
109 {DirName, std::errc::no_such_file_or_directory}).first;
115 if (NamedDirEnt.second)
119 llvm::vfs::Status Status;
121 getStatValue(DirName, Status,
false,
nullptr );
126 NamedDirEnt.second = *UDE;
127 VirtualDirectoryEntries.push_back(UDE);
131 NamedDirEnt.second = *UDE;
135 addAncestorsAsVirtualDirs(DirName);
143 if (DirName.size() > 1 &&
144 DirName != llvm::sys::path::root_path(DirName) &&
145 llvm::sys::path::is_separator(DirName.back()))
146 DirName = DirName.substr(0, DirName.size()-1);
147 std::optional<std::string> DirNameStr;
148 if (is_style_windows(llvm::sys::path::Style::native)) {
151 if (DirName.size() > 1 && DirName.back() ==
':' &&
152 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
153 DirNameStr = DirName.str() +
'.';
154 DirName = *DirNameStr;
162 auto SeenDirInsertResult =
163 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
164 if (!SeenDirInsertResult.second) {
165 if (SeenDirInsertResult.first->second)
167 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
172 auto &NamedDirEnt = *SeenDirInsertResult.first;
173 assert(!NamedDirEnt.second &&
"should be newly-created");
177 StringRef InterndDirName = NamedDirEnt.first();
180 llvm::vfs::Status Status;
181 auto statError = getStatValue(InterndDirName, Status,
false,
186 NamedDirEnt.second = statError;
188 SeenDirEntries.erase(DirName);
189 return llvm::errorCodeToError(statError);
194 NamedDirEnt.second = *UDE;
199llvm::ErrorOr<const DirectoryEntry *>
203 return &
Result->getDirEntry();
204 return llvm::errorToErrorCode(
Result.takeError());
207llvm::ErrorOr<const FileEntry *>
211 return &
Result->getFileEntry();
212 return llvm::errorToErrorCode(
Result.takeError());
220 auto SeenFileInsertResult =
221 SeenFileEntries.insert({
Filename, std::errc::no_such_file_or_directory});
222 if (!SeenFileInsertResult.second) {
223 if (!SeenFileInsertResult.first->second)
224 return llvm::errorCodeToError(
225 SeenFileInsertResult.first->second.getError());
230 ++NumFileCacheMisses;
231 auto *NamedFileEnt = &*SeenFileInsertResult.first;
232 assert(!NamedFileEnt->second &&
"should be newly-created");
236 StringRef InterndFileName = NamedFileEnt->first();
245 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
247 NamedFileEnt->second = Err;
251 return llvm::errorCodeToError(Err);
259 std::unique_ptr<llvm::vfs::File> F;
260 llvm::vfs::Status Status;
261 auto statError = getStatValue(InterndFileName, Status,
true,
262 openFile ? &F :
nullptr);
266 NamedFileEnt->second = statError;
270 return llvm::errorCodeToError(statError);
273 assert((openFile || !F) &&
"undesired open file");
277 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
278 bool ReusingEntry = UFE !=
nullptr;
280 UFE =
new (FilesAlloc.Allocate())
FileEntry();
282 if (!Status.ExposesExternalVFSPath || Status.getName() ==
Filename) {
325 assert(Redirection.second->V.is<
FileEntry *>() &&
326 "filename redirected to a non-canonical filename?");
327 assert(Redirection.second->V.get<
FileEntry *>() == UFE &&
328 "filename from getStatValue() refers to wrong file");
341 UFE->Size = Status.getSize();
342 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
344 UFE->UID = NextFileUID++;
345 UFE->UniqueID = Status.getUniqueID();
346 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_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 std::unique_ptr<llvm::MemoryBuffer> Content;
365 if (
auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
366 Content = std::move(*ContentOrError);
368 return llvm::errorCodeToError(ContentOrError.getError());
371 Content->getBufferSize(), 0);
373 FE.Content = std::move(Content);
374 FE.IsNamedPipe =
true;
379 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
380 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
381 RFS->setUsageTrackingActive(Active);
386 time_t ModificationTime) {
391 time_t ModificationTime) {
395 auto &NamedFileEnt = *SeenFileEntries.insert(
396 {
Filename, std::errc::no_such_file_or_directory}).first;
397 if (NamedFileEnt.second) {
405 ++NumFileCacheMisses;
406 addAncestorsAsVirtualDirs(
Filename);
418 "The directory of a virtual file should already be in the cache.");
421 llvm::vfs::Status Status;
422 const char *InterndFileName = NamedFileEnt.first().data();
423 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
424 Status = llvm::vfs::Status(
425 Status.getName(), Status.getUniqueID(),
426 llvm::sys::toTimePoint(ModificationTime),
427 Status.getUser(), Status.getGroup(), Size,
428 Status.getType(), Status.getPermissions());
430 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
445 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
446 RealFE->UniqueID = Status.getUniqueID();
447 RealFE->IsNamedPipe =
448 Status.getType() == llvm::sys::fs::file_type::fifo_file;
449 fillRealPathName(RealFE, Status.getName());
454 UFE =
new (FilesAlloc.Allocate())
FileEntry();
455 VirtualFileEntries.push_back(UFE);
460 UFE->ModTime = ModificationTime;
461 UFE->Dir = &DirInfo->getDirEntry();
462 UFE->UID = NextFileUID++;
469 llvm::vfs::Status Status;
470 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
473 if (!SeenBypassFileEntries)
474 SeenBypassFileEntries = std::make_unique<
475 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
478 auto Insertion = SeenBypassFileEntries->insert(
479 {VF.
getName(), std::errc::no_such_file_or_directory});
480 if (!Insertion.second)
485 BypassFileEntries.push_back(BFE);
487 BFE->Size = Status.getSize();
489 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
490 BFE->UID = NextFileUID++;
497 StringRef pathRef(path.data(), path.size());
500 || llvm::sys::path::is_absolute(pathRef))
504 llvm::sys::path::append(NewPath, pathRef);
512 if (!llvm::sys::path::is_absolute(StringRef(
Path.data(),
Path.size()))) {
513 FS->makeAbsolute(
Path);
527 llvm::sys::path::remove_dots(AbsPath,
true);
528 UFE->RealPathName = std::string(AbsPath);
531llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
533 bool RequiresNullTerminator,
534 std::optional<int64_t> MaybeLimit) {
538 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
540 uint64_t FileSize = Entry->
getSize();
543 FileSize = *MaybeLimit;
554 RequiresNullTerminator, isVolatile);
560 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
561 RequiresNullTerminator);
564llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
565FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
567 bool RequiresNullTerminator)
const {
569 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
574 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
584FileManager::getStatValue(StringRef
Path, llvm::vfs::Status &Status,
585 bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
590 StatCache.get(), *FS);
596 StatCache.get(), *FS);
601 llvm::vfs::Status &
Result) {
605 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
609 return std::error_code();
615 UIDToFiles.resize(NextFileUID);
617 for (
const auto &Entry : SeenFileEntries) {
619 if (!Entry.getValue() || !Entry.getValue()->V.is<
FileEntry *>())
639 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
640 CanonicalNames.find(Entry);
641 if (Known != CanonicalNames.end())
642 return Known->second;
646 StringRef CanonicalName(Name);
650 if (!FS->getRealPath(Name, RealPathBuf)) {
651 if (is_style_windows(llvm::sys::path::Style::native)) {
655 if (!FS->makeAbsolute(AbsPathBuf)) {
656 if (llvm::sys::path::root_name(RealPathBuf) ==
657 llvm::sys::path::root_name(AbsPathBuf)) {
658 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
663 llvm::sys::path::remove_dots(AbsPathBuf,
true);
664 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
668 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
672 CanonicalNames.insert({Entry, CanonicalName});
673 return CanonicalName;
677 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
678 NumDirLookups +=
Other.NumDirLookups;
679 NumFileLookups +=
Other.NumFileLookups;
680 NumDirCacheMisses +=
Other.NumDirCacheMisses;
681 NumFileCacheMisses +=
Other.NumFileCacheMisses;
685 llvm::errs() <<
"\n*** File Manager Stats:\n";
686 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
687 << UniqueRealDirs.size() <<
" real dirs found.\n";
688 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
689 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
690 llvm::errs() << NumDirLookups <<
" dir lookups, "
691 << NumDirCacheMisses <<
" dir cache misses.\n";
692 llvm::errs() << NumFileLookups <<
" file lookups, "
693 << 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.
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
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).
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.
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(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true, std::optional< int64_t > MaybeLimit=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
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.
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...
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)
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.
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.
@ Other
Other implicit parameter.
Type stored in the StringMap.