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"
45 "Number of directory cache misses.");
54 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
55 SeenFileEntries(64), NextFileUID(0) {
59 this->FS = llvm::vfs::getRealFileSystem();
65 assert(statCache &&
"No stat cache provided?");
66 StatCache = std::move(statCache);
77 return llvm::errorCodeToError(
81 return llvm::errorCodeToError(
make_error_code(std::errc::is_a_directory));
83 StringRef DirName = llvm::sys::path::parent_path(
Filename);
93void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
94 StringRef DirName = llvm::sys::path::parent_path(Path);
98 auto &NamedDirEnt = *SeenDirEntries.insert(
99 {DirName, std::errc::no_such_file_or_directory}).first;
105 if (NamedDirEnt.second)
110 NamedDirEnt.second = *UDE;
111 VirtualDirectoryEntries.push_back(UDE);
114 addAncestorsAsVirtualDirs(DirName);
122 if (DirName.size() > 1 &&
123 DirName != llvm::sys::path::root_path(DirName) &&
124 llvm::sys::path::is_separator(DirName.back()))
125 DirName = DirName.substr(0, DirName.size()-1);
126 std::optional<std::string> DirNameStr;
127 if (is_style_windows(llvm::sys::path::Style::native)) {
130 if (DirName.size() > 1 && DirName.back() ==
':' &&
131 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
132 DirNameStr = DirName.str() +
'.';
133 DirName = *DirNameStr;
141 auto SeenDirInsertResult =
142 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
143 if (!SeenDirInsertResult.second) {
144 if (SeenDirInsertResult.first->second)
146 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
151 auto &NamedDirEnt = *SeenDirInsertResult.first;
152 assert(!NamedDirEnt.second &&
"should be newly-created");
156 StringRef InterndDirName = NamedDirEnt.first();
159 llvm::vfs::Status Status;
160 auto statError = getStatValue(InterndDirName, Status,
false,
165 NamedDirEnt.second = statError;
167 SeenDirEntries.erase(DirName);
168 return llvm::errorCodeToError(statError);
182 NamedDirEnt.second = *UDE;
187llvm::ErrorOr<const DirectoryEntry *>
191 return &
Result->getDirEntry();
192 return llvm::errorToErrorCode(
Result.takeError());
195llvm::ErrorOr<const FileEntry *>
199 return &
Result->getFileEntry();
200 return llvm::errorToErrorCode(
Result.takeError());
208 auto SeenFileInsertResult =
209 SeenFileEntries.insert({
Filename, std::errc::no_such_file_or_directory});
210 if (!SeenFileInsertResult.second) {
211 if (!SeenFileInsertResult.first->second)
212 return llvm::errorCodeToError(
213 SeenFileInsertResult.first->second.getError());
218 ++NumFileCacheMisses;
219 auto *NamedFileEnt = &*SeenFileInsertResult.first;
220 assert(!NamedFileEnt->second &&
"should be newly-created");
224 StringRef InterndFileName = NamedFileEnt->first();
233 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
235 NamedFileEnt->second = Err;
239 return llvm::errorCodeToError(Err);
247 std::unique_ptr<llvm::vfs::File> F;
248 llvm::vfs::Status Status;
249 auto statError = getStatValue(InterndFileName, Status,
true,
250 openFile ? &F :
nullptr);
254 NamedFileEnt->second = statError;
258 return llvm::errorCodeToError(statError);
261 assert((openFile || !F) &&
"undesired open file");
265 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
266 bool ReusingEntry = UFE !=
nullptr;
268 UFE =
new (FilesAlloc.Allocate())
FileEntry();
270 if (!Status.ExposesExternalVFSPath || Status.getName() ==
Filename) {
313 assert(Redirection.second->V.is<
FileEntry *>() &&
314 "filename redirected to a non-canonical filename?");
315 assert(Redirection.second->V.get<
FileEntry *>() == UFE &&
316 "filename from getStatValue() refers to wrong file");
329 UFE->Size = Status.getSize();
330 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
332 UFE->UID = NextFileUID++;
333 UFE->UniqueID = Status.getUniqueID();
334 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
335 UFE->File = std::move(F);
338 if (
auto PathName = UFE->File->getName())
339 fillRealPathName(UFE, *PathName);
340 }
else if (!openFile) {
342 fillRealPathName(UFE, InterndFileName);
352 std::unique_ptr<llvm::MemoryBuffer> Content;
353 if (
auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
354 Content = std::move(*ContentOrError);
356 return llvm::errorCodeToError(ContentOrError.getError());
359 Content->getBufferSize(), 0);
361 FE.Content = std::move(Content);
362 FE.IsNamedPipe =
true;
367 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
368 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
369 RFS->setUsageTrackingActive(Active);
374 time_t ModificationTime) {
379 time_t ModificationTime) {
383 auto &NamedFileEnt = *SeenFileEntries.insert(
384 {
Filename, std::errc::no_such_file_or_directory}).first;
385 if (NamedFileEnt.second) {
393 ++NumFileCacheMisses;
394 addAncestorsAsVirtualDirs(
Filename);
406 "The directory of a virtual file should already be in the cache.");
409 llvm::vfs::Status Status;
410 const char *InterndFileName = NamedFileEnt.first().data();
411 if (!getStatValue(InterndFileName, Status,
true,
nullptr)) {
412 Status = llvm::vfs::Status(
413 Status.getName(), Status.getUniqueID(),
414 llvm::sys::toTimePoint(ModificationTime),
415 Status.getUser(), Status.getGroup(), Size,
416 Status.getType(), Status.getPermissions());
418 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
433 RealFE =
new (FilesAlloc.Allocate())
FileEntry();
434 RealFE->UniqueID = Status.getUniqueID();
435 RealFE->IsNamedPipe =
436 Status.getType() == llvm::sys::fs::file_type::fifo_file;
437 fillRealPathName(RealFE, Status.getName());
442 UFE =
new (FilesAlloc.Allocate())
FileEntry();
443 VirtualFileEntries.push_back(UFE);
448 UFE->ModTime = ModificationTime;
449 UFE->Dir = &DirInfo->getDirEntry();
450 UFE->UID = NextFileUID++;
457 llvm::vfs::Status Status;
458 if (getStatValue(VF.
getName(), Status,
true,
nullptr))
461 if (!SeenBypassFileEntries)
462 SeenBypassFileEntries = std::make_unique<
463 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
466 auto Insertion = SeenBypassFileEntries->insert(
467 {VF.
getName(), std::errc::no_such_file_or_directory});
468 if (!Insertion.second)
473 BypassFileEntries.push_back(BFE);
475 BFE->Size = Status.getSize();
477 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
478 BFE->UID = NextFileUID++;
485 StringRef pathRef(path.data(), path.size());
488 || llvm::sys::path::is_absolute(pathRef))
492 llvm::sys::path::append(NewPath, pathRef);
500 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
501 FS->makeAbsolute(Path);
515 llvm::sys::path::remove_dots(AbsPath,
true);
516 UFE->RealPathName = std::string(AbsPath);
519llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
521 bool RequiresNullTerminator) {
525 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
527 uint64_t FileSize = Entry->
getSize();
537 RequiresNullTerminator, isVolatile);
543 return getBufferForFileImpl(
Filename, FileSize, isVolatile,
544 RequiresNullTerminator);
547llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
548FileManager::getBufferForFileImpl(StringRef
Filename, int64_t FileSize,
550 bool RequiresNullTerminator)
const {
552 return FS->getBufferForFile(
Filename, FileSize, RequiresNullTerminator,
557 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
567FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
568 bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
573 StatCache.get(), *FS);
579 StatCache.get(), *FS);
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() || !Entry.getValue()->V.is<
FileEntry *>())
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 llvm::errs() <<
"\n*** File Manager Stats:\n";
661 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
662 << UniqueRealDirs.size() <<
" real dirs found.\n";
663 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
664 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
665 llvm::errs() << NumDirLookups <<
" dir lookups, "
666 << NumDirCacheMisses <<
" dir cache misses.\n";
667 llvm::errs() << NumFileLookups <<
" file lookups, "
668 << 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.
ALWAYS_ENABLED_STATISTIC(NumDirLookups, "Number of directory lookups.")
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 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.
Type stored in the StringMap.