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);
87void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
88 StringRef DirName = llvm::sys::path::parent_path(Path);
92 auto &NamedDirEnt = *SeenDirEntries.insert(
93 {DirName, std::errc::no_such_file_or_directory}).first;
99 if (NamedDirEnt.second)
104 NamedDirEnt.second = *UDE;
105 VirtualDirectoryEntries.push_back(UDE);
108 addAncestorsAsVirtualDirs(DirName);
116 if (DirName.size() > 1 &&
117 DirName != llvm::sys::path::root_path(DirName) &&
118 llvm::sys::path::is_separator(DirName.back()))
119 DirName = DirName.substr(0, DirName.size()-1);
120 std::optional<std::string> DirNameStr;
121 if (is_style_windows(llvm::sys::path::Style::native)) {
124 if (DirName.size() > 1 && DirName.back() ==
':' &&
125 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
126 DirNameStr = DirName.str() +
'.';
127 DirName = *DirNameStr;
135 auto SeenDirInsertResult =
136 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
137 if (!SeenDirInsertResult.second) {
138 if (SeenDirInsertResult.first->second)
140 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
145 auto &NamedDirEnt = *SeenDirInsertResult.first;
146 assert(!NamedDirEnt.second &&
"should be newly-created");
150 StringRef InterndDirName = NamedDirEnt.first();
153 llvm::vfs::Status Status;
154 auto statError = getStatValue(InterndDirName, Status,
false,
159 NamedDirEnt.second = statError;
161 SeenDirEntries.erase(DirName);
162 return llvm::errorCodeToError(statError);
176 NamedDirEnt.second = *UDE;
181llvm::ErrorOr<const DirectoryEntry *>
185 return &
Result->getDirEntry();
186 return llvm::errorToErrorCode(
Result.takeError());
189llvm::ErrorOr<const FileEntry *>
193 return &
Result->getFileEntry();
194 return llvm::errorToErrorCode(
Result.takeError());
202 auto SeenFileInsertResult =
203 SeenFileEntries.insert({
Filename, std::errc::no_such_file_or_directory});
204 if (!SeenFileInsertResult.second) {
205 if (!SeenFileInsertResult.first->second)
206 return llvm::errorCodeToError(
207 SeenFileInsertResult.first->second.getError());
212 ++NumFileCacheMisses;
213 auto *NamedFileEnt = &*SeenFileInsertResult.first;
214 assert(!NamedFileEnt->second &&
"should be newly-created");
218 StringRef InterndFileName = NamedFileEnt->first();
227 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
229 NamedFileEnt->second = Err;
233 return llvm::errorCodeToError(Err);
241 std::unique_ptr<llvm::vfs::File> F;
242 llvm::vfs::Status Status;
243 auto statError = getStatValue(InterndFileName, Status,
true,
244 openFile ? &F :
nullptr);
248 NamedFileEnt->second = statError;
252 return llvm::errorCodeToError(statError);
255 assert((openFile || !F) &&
"undesired open file");
259 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
260 bool ReusingEntry = UFE !=
nullptr;
262 UFE =
new (FilesAlloc.Allocate())
FileEntry();
264 if (!Status.ExposesExternalVFSPath || Status.getName() ==
Filename) {
307 assert(Redirection.second->V.is<
FileEntry *>() &&
308 "filename redirected to a non-canonical filename?");
309 assert(Redirection.second->V.get<
FileEntry *>() == UFE &&
310 "filename from getStatValue() refers to wrong file");
323 UFE->Size = Status.getSize();
324 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
326 UFE->UID = NextFileUID++;
327 UFE->UniqueID = Status.getUniqueID();
328 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
329 UFE->File = std::move(F);
332 if (
auto PathName = UFE->File->getName())
333 fillRealPathName(UFE, *PathName);
334 }
else if (!openFile) {
336 fillRealPathName(UFE, InterndFileName);
346 std::unique_ptr<llvm::MemoryBuffer> Content;
347 if (
auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
348 Content = std::move(*ContentOrError);
350 return llvm::errorCodeToError(ContentOrError.getError());
353 Content->getBufferSize(), 0);
355 FE.Content = std::move(Content);
356 FE.IsNamedPipe =
true;
361 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
362 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
363 RFS->setUsageTrackingActive(Active);
368 time_t ModificationTime) {
373 time_t ModificationTime) {
377 auto &NamedFileEnt = *SeenFileEntries.insert(
378 {
Filename, std::errc::no_such_file_or_directory}).first;
379 if (NamedFileEnt.second) {
387 ++NumFileCacheMisses;
388 addAncestorsAsVirtualDirs(
Filename);
400 "The directory of a virtual file should already be in the cache.");
403 llvm::vfs::Status Status;
404 const char *InterndFileName = NamedFileEnt.first().data();
405 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
406 Status = llvm::vfs::Status(
407 Status.getName(), Status.getUniqueID(),
408 llvm::sys::toTimePoint(ModificationTime),
409 Status.getUser(), Status.getGroup(), Size,
410 Status.getType(), Status.getPermissions());
412 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
427 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
428 RealFE->UniqueID = Status.getUniqueID();
429 RealFE->IsNamedPipe =
430 Status.getType() == llvm::sys::fs::file_type::fifo_file;
431 fillRealPathName(RealFE, Status.getName());
436 UFE =
new (FilesAlloc.Allocate())
FileEntry();
437 VirtualFileEntries.push_back(UFE);
442 UFE->ModTime = ModificationTime;
443 UFE->Dir = &DirInfo->getDirEntry();
444 UFE->UID = NextFileUID++;
451 llvm::vfs::Status Status;
452 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
455 if (!SeenBypassFileEntries)
456 SeenBypassFileEntries = std::make_unique<
457 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
460 auto Insertion = SeenBypassFileEntries->insert(
461 {VF.
getName(), std::errc::no_such_file_or_directory});
462 if (!Insertion.second)
467 BypassFileEntries.push_back(BFE);
469 BFE->Size = Status.getSize();
471 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
472 BFE->UID = NextFileUID++;
479 StringRef pathRef(path.data(), path.size());
482 || llvm::sys::path::is_absolute(pathRef))
486 llvm::sys::path::append(NewPath, pathRef);
494 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
495 FS->makeAbsolute(Path);
509 llvm::sys::path::remove_dots(AbsPath,
true);
510 UFE->RealPathName = std::string(AbsPath);
513llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
515 bool RequiresNullTerminator) {
519 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
521 uint64_t FileSize = Entry->
getSize();
531 RequiresNullTerminator, isVolatile);
537 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
538 RequiresNullTerminator);
541llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
542FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
544 bool RequiresNullTerminator)
const {
546 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
551 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
561FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
562 bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
567 StatCache.get(), *FS);
573 StatCache.get(), *FS);
578 llvm::vfs::Status &
Result) {
582 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
586 return std::error_code();
592 UIDToFiles.resize(NextFileUID);
594 for (
const auto &Entry : SeenFileEntries) {
596 if (!Entry.getValue() || !Entry.getValue()->V.is<
FileEntry *>())
616 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
617 CanonicalNames.find(Entry);
618 if (Known != CanonicalNames.end())
619 return Known->second;
623 StringRef CanonicalName(Name);
627 if (!FS->getRealPath(Name, RealPathBuf)) {
628 if (is_style_windows(llvm::sys::path::Style::native)) {
632 if (!FS->makeAbsolute(AbsPathBuf)) {
633 if (llvm::sys::path::root_name(RealPathBuf) ==
634 llvm::sys::path::root_name(AbsPathBuf)) {
635 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
640 llvm::sys::path::remove_dots(AbsPathBuf,
true);
641 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
645 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
649 CanonicalNames.insert({Entry, CanonicalName});
650 return CanonicalName;
654 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
655 NumDirLookups +=
Other.NumDirLookups;
656 NumFileLookups +=
Other.NumFileLookups;
657 NumDirCacheMisses +=
Other.NumDirCacheMisses;
658 NumFileCacheMisses +=
Other.NumFileCacheMisses;
662 llvm::errs() <<
"\n*** File Manager Stats:\n";
663 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
664 << UniqueRealDirs.size() <<
" real dirs found.\n";
665 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
666 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
667 llvm::errs() << NumDirLookups <<
" dir lookups, "
668 << NumDirCacheMisses <<
" dir cache misses.\n";
669 llvm::errs() << NumFileLookups <<
" file lookups, "
670 << 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.
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::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
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.