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.