20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/Statistic.h"
22#include "llvm/Config/llvm-config.h"
23#include "llvm/Support/Error.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/VirtualFileSystem.h"
29#include "llvm/Support/raw_ostream.h"
40#define DEBUG_TYPE "file-search"
43 std::optional<std::string> &Storage) {
44 using namespace llvm::sys::path;
48 if (Path.size() > 1 && root_path(Path) != Path && is_separator(Path.back()))
49 Path = Path.drop_back();
54 if (is_style_windows(Style::native)) {
55 if (Path.size() > 1 && Path.back() ==
':' &&
56 Path.equals_insensitive(root_name(Path))) {
57 Storage = Path.str() +
".";
72 : FS(
std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
73 SeenFileEntries(64), NextFileUID(0) {
77 this->FS = llvm::vfs::getRealFileSystem();
82 this->FS = std::move(FS);
92llvm::ErrorOr<DirectoryEntryRef>
93FileManager::getDirectoryFromFile(StringRef Filename,
bool CacheFailure) {
97 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
100 StringRef DirName = llvm::sys::path::parent_path(Filename);
105 return getDirectoryRefImpl(DirName, CacheFailure);
108DirectoryEntry *&FileManager::getRealDirEntry(
const llvm::vfs::Status &Status) {
109 assert(Status.isDirectory() &&
"The directory should exist!");
126void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
127 StringRef DirName = llvm::sys::path::parent_path(Path);
134 std::optional<std::string>
Storage;
135 StringRef OriginalDirName = DirName;
138 auto &NamedDirEnt = *SeenDirEntries.insert(
139 {DirName, std::errc::no_such_file_or_directory}).first;
145 if (NamedDirEnt.second)
149 llvm::vfs::Status Status;
151 getStatValue(DirName, Status,
false,
nullptr );
155 auto *UDE =
new (DirsAlloc.Allocate()) DirectoryEntry();
156 NamedDirEnt.second = *UDE;
157 VirtualDirectoryEntries.push_back(UDE);
160 DirectoryEntry *&UDE = getRealDirEntry(Status);
161 NamedDirEnt.second = *UDE;
165 addAncestorsAsVirtualDirs(OriginalDirName);
168llvm::ErrorOr<DirectoryEntryRef>
169FileManager::getDirectoryRefImpl(StringRef DirName,
bool CacheFailure) {
170 std::optional<std::string> DirNameStr;
177 auto SeenDirInsertResult =
178 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
179 if (!SeenDirInsertResult.second) {
180 if (SeenDirInsertResult.first->second)
181 return DirectoryEntryRef(*SeenDirInsertResult.first);
182 return SeenDirInsertResult.first->second.getError();
187 auto &NamedDirEnt = *SeenDirInsertResult.first;
188 assert(!NamedDirEnt.second &&
"should be newly-created");
192 StringRef InterndDirName = NamedDirEnt.first();
195 llvm::vfs::Status Status;
196 auto statError = getStatValue(InterndDirName, Status,
false,
201 NamedDirEnt.second = statError;
203 SeenDirEntries.erase(DirName);
208 DirectoryEntry *&UDE = getRealDirEntry(Status);
209 NamedDirEnt.second = *UDE;
211 return DirectoryEntryRef(NamedDirEnt);
214llvm::ErrorOr<FileEntryRef> FileManager::getFileRefImpl(StringRef Filename,
221 auto SeenFileInsertResult =
222 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
223 if (!SeenFileInsertResult.second) {
224 if (!SeenFileInsertResult.first->second)
225 return SeenFileInsertResult.first->second.getError();
226 return FileEntryRef(*SeenFileInsertResult.first);
230 ++NumFileCacheMisses;
231 auto *NamedFileEnt = &*SeenFileInsertResult.first;
232 assert(!NamedFileEnt->second &&
"should be newly-created");
236 StringRef InterndFileName = NamedFileEnt->first();
243 auto DirInfoOrErr = getDirectoryFromFile(Filename, CacheFailure);
245 std::error_code Err = DirInfoOrErr.getError();
247 NamedFileEnt->second = Err;
249 SeenFileEntries.erase(Filename);
253 DirectoryEntryRef DirInfo = *DirInfoOrErr;
259 std::unique_ptr<llvm::vfs::File> F;
260 llvm::vfs::Status Status;
261 auto statError = getStatValue(InterndFileName, Status,
true,
262 openFile ? &F :
nullptr, IsText);
266 NamedFileEnt->second = statError;
268 SeenFileEntries.erase(Filename);
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) {
284 NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);
323 .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})
326 "filename redirected to a non-canonical filename?");
328 "filename from getStatValue() refers to wrong file");
332 NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo);
335 FileEntryRef ReturnedRef(*NamedFileEnt);
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;
348 Status.getType() == llvm::sys::fs::file_type::character_file;
349 UFE->File = std::move(F);
352 if (
auto PathName = UFE->File->getName())
353 fillRealPathName(UFE, *PathName);
354 }
else if (!openFile) {
356 fillRealPathName(UFE, InterndFileName);
366 auto ContentOrError = [] {
367 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
368 return llvm::MemoryBuffer::getSTDIN();
372 return llvm::createFileError(
"-", ContentOrError.getError());
374 auto Content = std::move(*ContentOrError);
376 Content->getBufferSize(), 0);
378 FE.Content = std::move(Content);
379 FE.IsNamedPipe =
true;
384 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
385 if (
auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
386 RFS->setUsageTrackingActive(Active);
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);
415 auto DirInfo = getDirectoryFromFile(Filename.empty() ?
"." : 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;
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++;
498 StringRef pathRef(Path.data(), Path.size());
500 if (FileSystemOpts.WorkingDir.empty()
501 || llvm::sys::path::is_absolute(pathRef))
505 llvm::sys::path::append(NewPath, pathRef);
506 Path = std::move(NewPath);
511 bool Canonicalize)
const {
514 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
515 FS->makeAbsolute(Path);
520 Changed |= llvm::sys::path::remove_dots(Path);
532 llvm::sys::path::remove_dots(AbsPath,
true);
533 UFE->RealPathName = std::string(AbsPath);
536llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
538 bool RequiresNullTerminator,
539 std::optional<int64_t> MaybeLimit,
bool IsText) {
543 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
545 uint64_t FileSize = Entry->
getSize();
548 FileSize = *MaybeLimit;
555 StringRef Filename = FE.
getName();
558 auto Result = Entry->File->getBuffer(Filename, FileSize,
559 RequiresNullTerminator, isVolatile);
565 return getBufferForFileImpl(Filename, FileSize, isVolatile,
566 RequiresNullTerminator, IsText);
569llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
570FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
571 bool isVolatile,
bool RequiresNullTerminator,
574 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
579 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
583std::error_code FileManager::getStatValue(StringRef Path,
584 llvm::vfs::Status &Status,
586 std::unique_ptr<llvm::vfs::File> *F,
598 bool isForDir = !isFile;
599 std::error_code RetCode;
601 if (isForDir || !F) {
604 llvm::ErrorOr<llvm::vfs::Status> StatusOrErr = FS->status(Path);
606 RetCode = StatusOrErr.getError();
608 Status = *StatusOrErr;
619 IsText ? FS->openFileForRead(Path) : FS->openFileForReadBinary(Path);
623 RetCode = OwnedFile.getError();
628 llvm::ErrorOr<llvm::vfs::Status> StatusOrErr = (*OwnedFile)->status();
630 Status = *StatusOrErr;
631 *F = std::move(*OwnedFile);
636 RetCode = StatusOrErr.getError();
647 if (Status.isDirectory() != isForDir) {
651 return std::make_error_code(Status.isDirectory()
652 ? std::errc::is_a_directory
653 : std::errc::not_a_directory);
656 return std::error_code();
668 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
669 CanonicalNames.find(Entry);
670 if (Known != CanonicalNames.end())
671 return Known->second;
675 StringRef CanonicalName(Name);
679 if (!FS->getRealPath(Name, RealPathBuf)) {
680 if (is_style_windows(llvm::sys::path::Style::native)) {
684 if (!FS->makeAbsolute(AbsPathBuf)) {
685 if (llvm::sys::path::root_name(RealPathBuf) ==
686 llvm::sys::path::root_name(AbsPathBuf)) {
687 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
692 llvm::sys::path::remove_dots(AbsPathBuf,
true);
693 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
697 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
701 CanonicalNames.insert({Entry, CanonicalName});
702 return CanonicalName;
706 assert(&
Other !=
this &&
"Collecting stats into the same FileManager");
707 NumDirLookups +=
Other.NumDirLookups;
708 NumFileLookups +=
Other.NumFileLookups;
709 NumDirCacheMisses +=
Other.NumDirCacheMisses;
710 NumFileCacheMisses +=
Other.NumFileCacheMisses;
714 llvm::errs() <<
"\n*** File Manager Stats:\n";
715 llvm::errs() << UniqueRealFiles.size() <<
" real files found, "
716 << UniqueRealDirs.size() <<
" real dirs found.\n";
717 llvm::errs() << VirtualFileEntries.size() <<
" virtual files found, "
718 << VirtualDirectoryEntries.size() <<
" virtual dirs found.\n";
719 llvm::errs() << NumDirLookups <<
" dir lookups, "
720 << NumDirCacheMisses <<
" dir cache misses.\n";
721 llvm::errs() << NumFileLookups <<
" file lookups, "
722 << NumFileCacheMisses <<
" file cache misses.\n";
725 if (
auto *
T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
726 llvm::errs() <<
"\n*** Virtual File System Stats:\n"
727 <<
T->NumStatusCalls <<
" status() calls\n"
728 <<
T->NumOpenFileForReadCalls <<
" openFileForRead() calls\n"
729 <<
T->NumDirBeginCalls <<
" dir_begin() calls\n"
730 <<
T->NumGetRealPathCalls <<
" getRealPath() calls\n"
731 <<
T->NumExistsCalls <<
" exists() calls\n"
732 <<
T->NumIsLocalCalls <<
" isLocal() calls\n";
static void normalizeCacheKey(StringRef &Path, std::optional< std::string > &Storage)
Defines the clang::FileManager interface and associated types.
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).
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.
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,...
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.
FileManager(const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
Construct a file manager, optionally with a custom VFS.
void setVirtualFileSystem(IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
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::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
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)
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.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
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.