clang: lib/Basic/FileManager.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"
29#include
30#include
31#include
32#include
33#include
34#include
35#include
36#include
37
38using namespace clang;
39
40#define DEBUG_TYPE "file-search"
41
42
43
44
45
48 : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
49 SeenFileEntries(64), NextFileUID(0) {
50
51
52 if (!this->FS)
53 this->FS = llvm::vfs::getRealFileSystem();
54}
55
57
59 assert(statCache && "No stat cache provided?");
60 StatCache = std::move(statCache);
61}
62
64
65
66
69 bool CacheFailure) {
71 return llvm::errorCodeToError(
73
74 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
75 return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory));
76
77 StringRef DirName = llvm::sys::path::parent_path(Filename);
78
79 if (DirName.empty())
80 DirName = ".";
81
83}
84
85DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) {
86 assert(Status.isDirectory() && "The directory should exist!");
87
88
89
90
91 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
92
93 if (!UDE) {
94
95
97 }
98 return UDE;
99}
100
101
102
103void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
104 StringRef DirName = llvm::sys::path::parent_path(Path);
105 if (DirName.empty())
106 DirName = ".";
107
108 auto &NamedDirEnt = *SeenDirEntries.insert(
109 {DirName, std::errc::no_such_file_or_directory}).first;
110
111
112
113
114
115 if (NamedDirEnt.second)
116 return;
117
118
119 llvm::vfs::Status Status;
120 auto statError =
121 getStatValue(DirName, Status, false, nullptr );
122 if (statError) {
123
124
125 auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
126 NamedDirEnt.second = *UDE;
127 VirtualDirectoryEntries.push_back(UDE);
128 } else {
129
131 NamedDirEnt.second = *UDE;
132 }
133
134
135 addAncestorsAsVirtualDirs(DirName);
136}
137
140
141
142
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::optionalstd::string DirNameStr;
148 if (is_style_windows(llvm::sys::path::Style::native)) {
149
150
151 if (DirName.size() > 1 && DirName.back() == ':' &&
152 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
153 DirNameStr = DirName.str() + '.';
154 DirName = *DirNameStr;
155 }
156 }
157
158 ++NumDirLookups;
159
160
161
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());
168 }
169
170
171 ++NumDirCacheMisses;
172 auto &NamedDirEnt = *SeenDirInsertResult.first;
173 assert(!NamedDirEnt.second && "should be newly-created");
174
175
176
177 StringRef InterndDirName = NamedDirEnt.first();
178
179
180 llvm::vfs::Status Status;
181 auto statError = getStatValue(InterndDirName, Status, false,
182 nullptr );
183 if (statError) {
184
185 if (CacheFailure)
186 NamedDirEnt.second = statError;
187 else
188 SeenDirEntries.erase(DirName);
189 return llvm::errorCodeToError(statError);
190 }
191
192
194 NamedDirEnt.second = *UDE;
195
197}
198
199llvm::ErrorOr<const DirectoryEntry *>
200FileManager::getDirectory(StringRef DirName, bool CacheFailure) {
203 return &Result->getDirEntry();
204 return llvm::errorToErrorCode(Result.takeError());
205}
206
207llvm::ErrorOr<const FileEntry *>
208FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) {
211 return &Result->getFileEntry();
212 return llvm::errorToErrorCode(Result.takeError());
213}
214
216 bool openFile,
217 bool CacheFailure,
218 bool IsText) {
219 ++NumFileLookups;
220
221
222 auto SeenFileInsertResult =
223 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
224 if (!SeenFileInsertResult.second) {
225 if (!SeenFileInsertResult.first->second)
226 return llvm::errorCodeToError(
227 SeenFileInsertResult.first->second.getError());
228 return FileEntryRef(*SeenFileInsertResult.first);
229 }
230
231
232 ++NumFileCacheMisses;
233 auto *NamedFileEnt = &*SeenFileInsertResult.first;
234 assert(!NamedFileEnt->second && "should be newly-created");
235
236
237
238 StringRef InterndFileName = NamedFileEnt->first();
239
240
241
242
243
244
246 if (!DirInfoOrErr) {
247 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
248 if (CacheFailure)
249 NamedFileEnt->second = Err;
250 else
251 SeenFileEntries.erase(Filename);
252
253 return llvm::errorCodeToError(Err);
254 }
256
257
258
259
260
261 std::unique_ptrllvm::vfs::File F;
262 llvm::vfs::Status Status;
263 auto statError = getStatValue(InterndFileName, Status, true,
264 openFile ? &F : nullptr, IsText);
265 if (statError) {
266
267 if (CacheFailure)
268 NamedFileEnt->second = statError;
269 else
270 SeenFileEntries.erase(Filename);
271
272 return llvm::errorCodeToError(statError);
273 }
274
275 assert((openFile || !F) && "undesired open file");
276
277
278
279 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
280 bool ReusingEntry = UFE != nullptr;
281 if (!UFE)
282 UFE = new (FilesAlloc.Allocate()) FileEntry();
283
284 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
285
287 } else {
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323 auto &Redirection =
324 *SeenFileEntries
326 .first;
327 assert(isa<FileEntry *>(Redirection.second->V) &&
328 "filename redirected to a non-canonical filename?");
329 assert(cast<FileEntry *>(Redirection.second->V) == UFE &&
330 "filename from getStatValue() refers to wrong file");
331
332
333
335 }
336
338 if (ReusingEntry) {
339 return ReturnedRef;
340 }
341
342
343 UFE->Size = Status.getSize();
344 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
346 UFE->UID = NextFileUID++;
347 UFE->UniqueID = Status.getUniqueID();
348 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
349 UFE->File = std::move(F);
350
351 if (UFE->File) {
352 if (auto PathName = UFE->File->getName())
353 fillRealPathName(UFE, *PathName);
354 } else if (!openFile) {
355
356 fillRealPathName(UFE, InterndFileName);
357 }
358 return ReturnedRef;
359}
360
362
363 if (STDIN)
364 return *STDIN;
365
366 std::unique_ptrllvm::MemoryBuffer Content;
367 if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
368 Content = std::move(*ContentOrError);
369 else
370 return llvm::errorCodeToError(ContentOrError.getError());
371
373 Content->getBufferSize(), 0);
375 FE.Content = std::move(Content);
376 FE.IsNamedPipe = true;
377 return *STDIN;
378}
379
381 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
382 if (auto *RFS = dyn_castllvm::vfs::RedirectingFileSystem(&FileSys))
383 RFS->setUsageTrackingActive(Active);
384 });
385}
386
387const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size,
388 time_t ModificationTime) {
390}
391
393 time_t ModificationTime) {
394 ++NumFileLookups;
395
396
397 auto &NamedFileEnt = *SeenFileEntries.insert(
398 {Filename, std::errc::no_such_file_or_directory}).first;
399 if (NamedFileEnt.second) {
401 if (LLVM_LIKELY(isa<FileEntry *>(Value.V)))
403 return FileEntryRef(*cast<const FileEntryRef::MapEntry *>(Value.V));
404 }
405
406
407 ++NumFileCacheMisses;
408 addAncestorsAsVirtualDirs(Filename);
410
411
412
413
414
415
416
418 *this, Filename.empty() ? "." : Filename, true));
419 assert(DirInfo &&
420 "The directory of a virtual file should already be in the cache.");
421
422
423 llvm::vfs::Status Status;
424 const char *InterndFileName = NamedFileEnt.first().data();
425 if (!getStatValue(InterndFileName, Status, true, nullptr)) {
426 Status = llvm::vfs::Status(
427 Status.getName(), Status.getUniqueID(),
429 Status.getUser(), Status.getGroup(), Size,
430 Status.getType(), Status.getPermissions());
431
432 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
433 if (RealFE) {
434
435
436
437 if (RealFE->File)
438 RealFE->closeFile();
439
440
441
442
445 }
446
447 RealFE = new (FilesAlloc.Allocate()) FileEntry();
448 RealFE->UniqueID = Status.getUniqueID();
449 RealFE->IsNamedPipe =
450 Status.getType() == llvm::sys::fs::file_type::fifo_file;
451 fillRealPathName(RealFE, Status.getName());
452
453 UFE = RealFE;
454 } else {
455
456 UFE = new (FilesAlloc.Allocate()) FileEntry();
457 VirtualFileEntries.push_back(UFE);
458 }
459
461 UFE->Size = Size;
463 UFE->Dir = &DirInfo->getDirEntry();
464 UFE->UID = NextFileUID++;
465 UFE->File.reset();
467}
468
470
471 llvm::vfs::Status Status;
472 if (getStatValue(VF.getName(), Status, true, nullptr))
473 return std::nullopt;
474
475 if (!SeenBypassFileEntries)
476 SeenBypassFileEntries = std::make_unique<
477 llvm::StringMap<llvm::ErrorOrFileEntryRef::MapValue>>();
478
479
480 auto Insertion = SeenBypassFileEntries->insert(
481 {VF.getName(), std::errc::no_such_file_or_directory});
482 if (!Insertion.second)
484
485
487 BypassFileEntries.push_back(BFE);
489 BFE->Size = Status.getSize();
491 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
492 BFE->UID = NextFileUID++;
493
494
496}
497
499 StringRef pathRef(path.data(), path.size());
500
501 if (FileSystemOpts.WorkingDir.empty()
502 || llvm::sys::path::is_absolute(pathRef))
503 return false;
504
506 llvm::sys::path::append(NewPath, pathRef);
507 path = NewPath;
508 return true;
509}
510
513
514 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
515 FS->makeAbsolute(Path);
516 Changed = true;
517 }
518
519 return Changed;
520}
521
522void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
524
525
526
527
529 llvm::sys::path::remove_dots(AbsPath, true);
530 UFE->RealPathName = std::string(AbsPath);
531}
532
533llvm::ErrorOr<std::unique_ptrllvm::MemoryBuffer>
535 bool RequiresNullTerminator,
536 std::optional<int64_t> MaybeLimit, bool IsText) {
538
539 if (Entry->Content)
540 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
541
542 uint64_t FileSize = Entry->getSize();
543
544 if (MaybeLimit)
545 FileSize = *MaybeLimit;
546
547
548
550 FileSize = -1;
551
553
554 if (Entry->File) {
555 auto Result = Entry->File->getBuffer(Filename, FileSize,
556 RequiresNullTerminator, isVolatile);
559 }
560
561
562 return getBufferForFileImpl(Filename, FileSize, isVolatile,
563 RequiresNullTerminator, IsText);
564}
565
566llvm::ErrorOr<std::unique_ptrllvm::MemoryBuffer>
567FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
568 bool isVolatile, bool RequiresNullTerminator,
569 bool IsText) const {
570 if (FileSystemOpts.WorkingDir.empty())
571 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
572 isVolatile, IsText);
573
576 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
577 isVolatile, IsText);
578}
579
580
581
582
583
584
585std::error_code FileManager::getStatValue(StringRef Path,
586 llvm::vfs::Status &Status,
587 bool isFile,
588 std::unique_ptrllvm::vfs::File *F,
589 bool IsText) {
590
591
592 if (FileSystemOpts.WorkingDir.empty())
594 *FS, IsText);
595
598
600 StatCache.get(), *FS, IsText);
601}
602
603std::error_code
605 llvm::vfs::Status &Result) {
608
609 llvm::ErrorOrllvm::vfs::Status S = FS->status(FilePath.c_str());
610 if (!S)
611 return S.getError();
613 return std::error_code();
614}
615
618 UIDToFiles.clear();
619 UIDToFiles.resize(NextFileUID);
620
621 for (const auto &Entry : SeenFileEntries) {
622
623 if (!Entry.getValue() || !isa<FileEntry *>(Entry.getValue()->V))
624 continue;
626
627
629 if (!ExistingFE || FE.getName() < ExistingFE->getName())
630 ExistingFE = FE;
631 }
632}
633
636}
637
640}
641
643 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
644 CanonicalNames.find(Entry);
645 if (Known != CanonicalNames.end())
646 return Known->second;
647
648
649
650 StringRef CanonicalName(Name);
651
654 if (!FS->getRealPath(Name, RealPathBuf)) {
655 if (is_style_windows(llvm::sys::path::Style::native)) {
656
657
658 AbsPathBuf = Name;
659 if (!FS->makeAbsolute(AbsPathBuf)) {
660 if (llvm::sys::path::root_name(RealPathBuf) ==
661 llvm::sys::path::root_name(AbsPathBuf)) {
662 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
663 } else {
664
665
666
667 llvm::sys::path::remove_dots(AbsPathBuf, true);
668 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
669 }
670 }
671 } else {
672 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
673 }
674 }
675
676 CanonicalNames.insert({Entry, CanonicalName});
677 return CanonicalName;
678}
679
681 assert(&Other != this && "Collecting stats into the same FileManager");
682 NumDirLookups += Other.NumDirLookups;
683 NumFileLookups += Other.NumFileLookups;
684 NumDirCacheMisses += Other.NumDirCacheMisses;
685 NumFileCacheMisses += Other.NumFileCacheMisses;
686}
687
689 llvm::errs() << "\n*** File Manager Stats:\n";
690 llvm::errs() << UniqueRealFiles.size() << " real files found, "
691 << UniqueRealDirs.size() << " real dirs found.\n";
692 llvm::errs() << VirtualFileEntries.size() << " virtual files found, "
693 << VirtualDirectoryEntries.size() << " virtual dirs found.\n";
694 llvm::errs() << NumDirLookups << " dir lookups, "
695 << NumDirCacheMisses << " dir cache misses.\n";
696 llvm::errs() << NumFileLookups << " file lookups, "
697 << NumFileCacheMisses << " file cache misses.\n";
698
700 if (auto *T = dyn_cast_or_nullllvm::vfs::TracingFileSystem(&VFS))
701 llvm::errs() << "\n*** Virtual File System Stats:\n"
702 << T->NumStatusCalls << " status() calls\n"
703 << T->NumOpenFileForReadCalls << " openFileForRead() calls\n"
704 << T->NumDirBeginCalls << " dir_begin() calls\n"
705 << T->NumGetRealPathCalls << " getRealPath() calls\n"
706 << T->NumExistsCalls << " exists() calls\n"
707 << T->NumIsLocalCalls << " isLocal() calls\n";
708 });
709
710
711}
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
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.
off_t time_t ModificationTime
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.
bool makeAbsolutePath(SmallVectorImpl< char > &Path) const
Makes Path absolute taking into account FileSystemOptions and the working directory option.
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...
OptionalFileEntryRef getBypassFile(FileEntryRef VFE)
Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual file entry,...
LLVM_DEPRECATED("Functions returning DirectoryEntry are deprecated.", "getOptionalDirectoryRef()") llvm LLVM_DEPRECATED("Functions returning FileEntry are deprecated.", "getOptionalFileRef()") llvm llvm::Expected< FileEntryRef > getFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true, bool IsText=true)
Lookup, cache, and verify the specified directory (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.
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
const FunctionProtoType * T
@ Other
Other implicit parameter.
Type stored in the StringMap.