clang 22.0.0git
PrecompiledPreamble.cpp
Go to the documentation of this file.
1//===--- PrecompiledPreamble.cpp - Build precompiled preambles --*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Helper class to build precompiled preamble.
10//
11//===----------------------------------------------------------------------===//
12
21#include "clang/Lex/Lexer.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/ADT/StringSet.h"
27#include "llvm/ADT/iterator_range.h"
28#include "llvm/Config/llvm-config.h"
29#include "llvm/Support/CrashRecoveryContext.h"
30#include "llvm/Support/FileSystem.h"
31#include "llvm/Support/ManagedStatic.h"
32#include "llvm/Support/Path.h"
33#include "llvm/Support/Process.h"
34#include "llvm/Support/VirtualFileSystem.h"
35#include <limits>
36#include <mutex>
37#include <utility>
38
39using namespace clang;
40
41namespace {
42
43StringRef getInMemoryPreamblePath() {
44#if defined(LLVM_ON_UNIX)
45 return "/__clang_tmp/___clang_inmemory_preamble___";
46#elif defined(_WIN32)
47 return "C:\\__clang_tmp\\___clang_inmemory_preamble___";
48#else
49#warning "Unknown platform. Defaulting to UNIX-style paths for in-memory PCHs"
50 return "/__clang_tmp/___clang_inmemory_preamble___";
51#endif
52}
53
55createVFSOverlayForPreamblePCH(StringRef PCHFilename,
56 std::unique_ptr<llvm::MemoryBuffer> PCHBuffer,
58 // We want only the PCH file from the real filesystem to be available,
59 // so we create an in-memory VFS with just that and overlay it on top.
60 auto PCHFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
61 PCHFS->addFile(PCHFilename, 0, std::move(PCHBuffer));
62 auto Overlay = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(VFS);
63 Overlay->pushOverlay(PCHFS);
64 return Overlay;
65}
66
67class PreambleDependencyCollector : public DependencyCollector {
68public:
69 // We want to collect all dependencies for correctness. Avoiding the real
70 // system dependencies (e.g. stl from /usr/lib) would probably be a good idea,
71 // but there is no way to distinguish between those and the ones that can be
72 // spuriously added by '-isystem' (e.g. to suppress warnings from those
73 // headers).
74 bool needSystemDependencies() override { return true; }
75};
76
77// Collects files whose existence would invalidate the preamble.
78// Collecting *all* of these would make validating it too slow though, so we
79// just find all the candidates for 'file not found' diagnostics.
80//
81// A caveat that may be significant for generated files: we'll omit files under
82// search path entries whose roots don't exist when the preamble is built.
83// These are pruned by InitHeaderSearch and so we don't see the search path.
84// It would be nice to include them but we don't want to duplicate all the rest
85// of the InitHeaderSearch logic to reconstruct them.
86class MissingFileCollector : public PPCallbacks {
87 llvm::StringSet<> &Out;
88 const HeaderSearch &Search;
89 const SourceManager &SM;
90
91public:
92 MissingFileCollector(llvm::StringSet<> &Out, const HeaderSearch &Search,
93 const SourceManager &SM)
94 : Out(Out), Search(Search), SM(SM) {}
95
96 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
97 StringRef FileName, bool IsAngled,
98 CharSourceRange FilenameRange,
99 OptionalFileEntryRef File, StringRef SearchPath,
100 StringRef RelativePath, const Module *SuggestedModule,
101 bool ModuleImported,
103 // File is std::nullopt if it wasn't found.
104 // (We have some false negatives if PP recovered e.g. <foo> -> "foo")
105 if (File)
106 return;
107
108 // If it's a rare absolute include, we know the full path already.
109 if (llvm::sys::path::is_absolute(FileName)) {
110 Out.insert(FileName);
111 return;
112 }
113
114 // Reconstruct the filenames that would satisfy this directive...
115 llvm::SmallString<256> Buf;
116 auto NotFoundRelativeTo = [&](DirectoryEntryRef DE) {
117 Buf = DE.getName();
118 llvm::sys::path::append(Buf, FileName);
119 llvm::sys::path::remove_dots(Buf, /*remove_dot_dot=*/true);
120 Out.insert(Buf);
121 };
122 // ...relative to the including file.
123 if (!IsAngled) {
124 if (OptionalFileEntryRef IncludingFile =
125 SM.getFileEntryRefForID(SM.getFileID(IncludeTok.getLocation())))
126 if (IncludingFile->getDir())
127 NotFoundRelativeTo(IncludingFile->getDir());
128 }
129 // ...relative to the search paths.
130 for (const auto &Dir : llvm::make_range(
131 IsAngled ? Search.angled_dir_begin() : Search.search_dir_begin(),
132 Search.search_dir_end())) {
133 // No support for frameworks or header maps yet.
134 if (Dir.isNormalDir())
135 NotFoundRelativeTo(*Dir.getDirRef());
136 }
137 }
138};
139
140/// Keeps a track of files to be deleted in destructor.
141class TemporaryFiles {
142public:
143 // A static instance to be used by all clients.
144 static TemporaryFiles &getInstance();
145
146private:
147 // Disallow constructing the class directly.
148 TemporaryFiles() = default;
149 // Disallow copy.
150 TemporaryFiles(const TemporaryFiles &) = delete;
151
152public:
153 ~TemporaryFiles();
154
155 /// Adds \p File to a set of tracked files.
156 void addFile(StringRef File);
157
158 /// Remove \p File from disk and from the set of tracked files.
159 void removeFile(StringRef File);
160
161private:
162 std::mutex Mutex;
163 llvm::StringSet<> Files;
164};
165
166TemporaryFiles &TemporaryFiles::getInstance() {
167 static TemporaryFiles Instance;
168 return Instance;
169}
170
171TemporaryFiles::~TemporaryFiles() {
172 std::lock_guard<std::mutex> Guard(Mutex);
173 for (const auto &File : Files)
174 llvm::sys::fs::remove(File.getKey());
175}
176
177void TemporaryFiles::addFile(StringRef File) {
178 std::lock_guard<std::mutex> Guard(Mutex);
179 auto IsInserted = Files.insert(File).second;
180 (void)IsInserted;
181 assert(IsInserted && "File has already been added");
182}
183
184void TemporaryFiles::removeFile(StringRef File) {
185 std::lock_guard<std::mutex> Guard(Mutex);
186 auto WasPresent = Files.erase(File);
187 (void)WasPresent;
188 assert(WasPresent && "File was not tracked");
189 llvm::sys::fs::remove(File);
190}
191
192// A temp file that would be deleted on destructor call. If destructor is not
193// called for any reason, the file will be deleted at static objects'
194// destruction.
195// An assertion will fire if two TempPCHFiles are created with the same name,
196// so it's not intended to be used outside preamble-handling.
197class TempPCHFile {
198public:
199 // A main method used to construct TempPCHFile.
200 static std::unique_ptr<TempPCHFile> create(StringRef StoragePath) {
201 // FIXME: This is a hack so that we can override the preamble file during
202 // crash-recovery testing, which is the only case where the preamble files
203 // are not necessarily cleaned up.
204 if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"))
205 return std::unique_ptr<TempPCHFile>(new TempPCHFile(TmpFile));
206
207 llvm::SmallString<128> File;
208 // Using the versions of createTemporaryFile() and
209 // createUniqueFile() with a file descriptor guarantees
210 // that we would never get a race condition in a multi-threaded setting
211 // (i.e., multiple threads getting the same temporary path).
212 int FD;
213 std::error_code EC;
214 if (StoragePath.empty())
215 EC = llvm::sys::fs::createTemporaryFile("preamble", "pch", FD, File);
216 else {
217 llvm::SmallString<128> TempPath = StoragePath;
218 // Use the same filename model as fs::createTemporaryFile().
219 llvm::sys::path::append(TempPath, "preamble-%%%%%%.pch");
220 namespace fs = llvm::sys::fs;
221 // Use the same owner-only file permissions as fs::createTemporaryFile().
222 EC = fs::createUniqueFile(TempPath, FD, File, fs::OF_None,
223 fs::owner_read | fs::owner_write);
224 }
225 if (EC)
226 return nullptr;
227 // We only needed to make sure the file exists, close the file right away.
228 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
229 return std::unique_ptr<TempPCHFile>(new TempPCHFile(File.str().str()));
230 }
231
232 TempPCHFile &operator=(const TempPCHFile &) = delete;
233 TempPCHFile(const TempPCHFile &) = delete;
234 ~TempPCHFile() { TemporaryFiles::getInstance().removeFile(FilePath); };
235
236 /// A path where temporary file is stored.
237 llvm::StringRef getFilePath() const { return FilePath; };
238
239private:
240 TempPCHFile(std::string FilePath) : FilePath(std::move(FilePath)) {
241 TemporaryFiles::getInstance().addFile(this->FilePath);
242 }
243
244 std::string FilePath;
245};
246
247class PrecompilePreambleAction : public ASTFrontendAction {
248public:
249 PrecompilePreambleAction(std::shared_ptr<PCHBuffer> Buffer, bool WritePCHFile,
250 PreambleCallbacks &Callbacks)
251 : Buffer(std::move(Buffer)), WritePCHFile(WritePCHFile),
252 Callbacks(Callbacks) {}
253
254 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
255 StringRef InFile) override;
256
257 bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; }
258
259 void setEmittedPreamblePCH(ASTWriter &Writer) {
260 if (FileOS) {
261 *FileOS << Buffer->Data;
262 // Make sure it hits disk now.
263 FileOS.reset();
264 }
265
266 this->HasEmittedPreamblePCH = true;
267 Callbacks.AfterPCHEmitted(Writer);
268 }
269
270 bool BeginSourceFileAction(CompilerInstance &CI) override {
271 assert(CI.getLangOpts().CompilingPCH);
272 return ASTFrontendAction::BeginSourceFileAction(CI);
273 }
274
275 bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); }
276 bool hasCodeCompletionSupport() const override { return false; }
277 bool hasASTFileSupport() const override { return false; }
278 TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; }
279
280private:
281 friend class PrecompilePreambleConsumer;
282
283 bool HasEmittedPreamblePCH = false;
284 std::shared_ptr<PCHBuffer> Buffer;
285 bool WritePCHFile; // otherwise the PCH is written into the PCHBuffer only.
286 std::unique_ptr<llvm::raw_pwrite_stream> FileOS; // null if in-memory
287 PreambleCallbacks &Callbacks;
288};
289
290class PrecompilePreambleConsumer : public PCHGenerator {
291public:
292 PrecompilePreambleConsumer(PrecompilePreambleAction &Action, Preprocessor &PP,
293 ModuleCache &ModCache, StringRef isysroot,
294 std::shared_ptr<PCHBuffer> Buffer,
295 const CodeGenOptions &CodeGenOpts)
296 : PCHGenerator(PP, ModCache, "", isysroot, std::move(Buffer), CodeGenOpts,
297 ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
298 /*AllowASTWithErrors=*/true),
299 Action(Action) {}
300
301 bool HandleTopLevelDecl(DeclGroupRef DG) override {
302 Action.Callbacks.HandleTopLevelDecl(DG);
303 return true;
304 }
305
306 void HandleTranslationUnit(ASTContext &Ctx) override {
308 if (!hasEmittedPCH())
309 return;
310 Action.setEmittedPreamblePCH(getWriter());
311 }
312
313 bool shouldSkipFunctionBody(Decl *D) override {
314 return Action.Callbacks.shouldSkipFunctionBody(D);
315 }
316
317private:
318 PrecompilePreambleAction &Action;
319};
320
321std::unique_ptr<ASTConsumer>
322PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,
323 StringRef InFile) {
324 std::string Sysroot;
326 return nullptr;
327
328 if (WritePCHFile) {
329 std::string OutputFile; // unused
330 FileOS = GeneratePCHAction::CreateOutputFile(CI, InFile, OutputFile);
331 if (!FileOS)
332 return nullptr;
333 }
334
336 Sysroot.clear();
337
338 return std::make_unique<PrecompilePreambleConsumer>(
339 *this, CI.getPreprocessor(), CI.getModuleCache(), Sysroot, Buffer,
340 CI.getCodeGenOpts());
341}
342
343template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {
344 if (!Val)
345 return false;
346 Output = std::move(*Val);
347 return true;
348}
349
350} // namespace
351
353 const llvm::MemoryBufferRef &Buffer,
354 unsigned MaxLines) {
355 return Lexer::ComputePreamble(Buffer.getBuffer(), LangOpts, MaxLines);
356}
357
359public:
360 static std::unique_ptr<PCHStorage> file(std::unique_ptr<TempPCHFile> File) {
361 assert(File);
362 std::unique_ptr<PCHStorage> S(new PCHStorage());
363 S->File = std::move(File);
364 return S;
365 }
366 static std::unique_ptr<PCHStorage> inMemory(std::shared_ptr<PCHBuffer> Buf) {
367 std::unique_ptr<PCHStorage> S(new PCHStorage());
368 S->Memory = std::move(Buf);
369 return S;
370 }
371
372 enum class Kind { InMemory, TempFile };
373 Kind getKind() const {
374 if (Memory)
375 return Kind::InMemory;
376 if (File)
377 return Kind::TempFile;
378 llvm_unreachable("Neither Memory nor File?");
379 }
380 llvm::StringRef filePath() const {
381 assert(getKind() == Kind::TempFile);
382 return File->getFilePath();
383 }
384 llvm::StringRef memoryContents() const {
385 assert(getKind() == Kind::InMemory);
386 return StringRef(Memory->Data.data(), Memory->Data.size());
387 }
388
389 // Shrink in-memory buffers to fit.
390 // This incurs a copy, but preambles tend to be long-lived.
391 // Only safe to call once nothing can alias the buffer.
392 void shrink() {
393 if (!Memory)
394 return;
395 Memory->Data = decltype(Memory->Data)(Memory->Data);
396 }
397
398private:
399 PCHStorage() = default;
400 PCHStorage(const PCHStorage &) = delete;
401 PCHStorage &operator=(const PCHStorage &) = delete;
402
403 std::shared_ptr<PCHBuffer> Memory;
404 std::unique_ptr<TempPCHFile> File;
405};
406
411
412llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
413 const CompilerInvocation &Invocation,
414 const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
417 std::shared_ptr<PCHContainerOperations> PCHContainerOps, bool StoreInMemory,
418 StringRef StoragePath, PreambleCallbacks &Callbacks) {
419 assert(VFS && "VFS is null");
420
421 auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
422 FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts();
423 PreprocessorOptions &PreprocessorOpts =
424 PreambleInvocation->getPreprocessorOpts();
425
426 std::shared_ptr<PCHBuffer> Buffer = std::make_shared<PCHBuffer>();
427 std::unique_ptr<PCHStorage> Storage;
428 if (StoreInMemory) {
429 Storage = PCHStorage::inMemory(Buffer);
430 } else {
431 // Create a temporary file for the precompiled preamble. In rare
432 // circumstances, this can fail.
433 std::unique_ptr<TempPCHFile> PreamblePCHFile =
434 TempPCHFile::create(StoragePath);
435 if (!PreamblePCHFile)
437 Storage = PCHStorage::file(std::move(PreamblePCHFile));
438 }
439
440 // Save the preamble text for later; we'll need to compare against it for
441 // subsequent reparses.
442 std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(),
443 MainFileBuffer->getBufferStart() +
444 Bounds.Size);
445 bool PreambleEndsAtStartOfLine = Bounds.PreambleEndsAtStartOfLine;
446
447 // Tell the compiler invocation to generate a temporary precompiled header.
449 FrontendOpts.OutputFile = std::string(
450 StoreInMemory ? getInMemoryPreamblePath() : Storage->filePath());
451 PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
452 PreprocessorOpts.PrecompiledPreambleBytes.second = false;
453 // Inform preprocessor to record conditional stack when building the preamble.
454 PreprocessorOpts.GeneratePreamble = true;
455
456 // Create the compiler instance to use for building the precompiled preamble.
457 auto Clang = std::make_unique<CompilerInstance>(std::move(PreambleInvocation),
458 std::move(PCHContainerOps));
459
460 // Recover resources if we crash before exiting this method.
461 llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(
462 Clang.get());
463
464 Clang->setDiagnostics(Diagnostics);
465
466 // Create the target instance.
467 if (!Clang->createTarget())
469
470 if (Clang->getFrontendOpts().Inputs.size() != 1 ||
471 Clang->getFrontendOpts().Inputs[0].getKind().getFormat() !=
473 Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() ==
476 }
477
478 // Clear out old caches and data.
479 Diagnostics->Reset();
480 ProcessWarningOptions(*Diagnostics, Clang->getDiagnosticOpts(), *VFS);
481
482 // Create a file manager object to provide access to and cache the filesystem.
483 Clang->createVirtualFileSystem(VFS);
484 Clang->createFileManager();
485
486 // Create the source manager.
487 Clang->createSourceManager();
488
489 auto PreambleDepCollector = std::make_shared<PreambleDependencyCollector>();
490 Clang->addDependencyCollector(PreambleDepCollector);
491
492 Clang->getLangOpts().CompilingPCH = true;
493
494 // Remap the main source file to the preamble buffer.
495 StringRef MainFilePath = FrontendOpts.Inputs[0].getFile();
496 auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy(
497 MainFileBuffer->getBuffer().slice(0, Bounds.Size), MainFilePath);
498 if (PreprocessorOpts.RetainRemappedFileBuffers) {
499 // MainFileBuffer will be deleted by unique_ptr after leaving the method.
500 PreprocessorOpts.addRemappedFile(MainFilePath, PreambleInputBuffer.get());
501 } else {
502 // In that case, remapped buffer will be deleted by CompilerInstance on
503 // BeginSourceFile, so we call release() to avoid double deletion.
504 PreprocessorOpts.addRemappedFile(MainFilePath,
505 PreambleInputBuffer.release());
506 }
507
508 auto Act = std::make_unique<PrecompilePreambleAction>(
509 std::move(Buffer),
510 /*WritePCHFile=*/Storage->getKind() == PCHStorage::Kind::TempFile,
511 Callbacks);
512 if (!Act->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
514
515 // Performed after BeginSourceFile to ensure Clang->Preprocessor can be
516 // referenced in the callback.
517 Callbacks.BeforeExecute(*Clang);
518
519 std::unique_ptr<PPCallbacks> DelegatedPPCallbacks =
520 Callbacks.createPPCallbacks();
521 if (DelegatedPPCallbacks)
522 Clang->getPreprocessor().addPPCallbacks(std::move(DelegatedPPCallbacks));
523 if (auto CommentHandler = Callbacks.getCommentHandler())
524 Clang->getPreprocessor().addCommentHandler(CommentHandler);
525 llvm::StringSet<> MissingFiles;
526 Clang->getPreprocessor().addPPCallbacks(
527 std::make_unique<MissingFileCollector>(
528 MissingFiles, Clang->getPreprocessor().getHeaderSearchInfo(),
529 Clang->getSourceManager()));
530
531 if (llvm::Error Err = Act->Execute())
532 return errorToErrorCode(std::move(Err));
533
534 // Run the callbacks.
535 Callbacks.AfterExecute(*Clang);
536
537 Act->EndSourceFile();
538
539 if (!Act->hasEmittedPreamblePCH())
541 Act.reset(); // Frees the PCH buffer, unless Storage keeps it in memory.
542
543 // Keep track of all of the files that the source manager knows about,
544 // so we can verify whether they have changed or not.
545 llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble;
546
547 SourceManager &SourceMgr = Clang->getSourceManager();
548 for (auto &Filename : PreambleDepCollector->getDependencies()) {
549 auto MaybeFile = Clang->getFileManager().getOptionalFileRef(Filename);
550 if (!MaybeFile ||
551 MaybeFile == SourceMgr.getFileEntryRefForID(SourceMgr.getMainFileID()))
552 continue;
553 auto File = *MaybeFile;
554 if (time_t ModTime = File.getModificationTime()) {
555 FilesInPreamble[File.getName()] =
556 PrecompiledPreamble::PreambleFileHash::createForFile(File.getSize(),
557 ModTime);
558 } else {
559 llvm::MemoryBufferRef Buffer =
560 SourceMgr.getMemoryBufferForFileOrFake(File);
561 FilesInPreamble[File.getName()] =
562 PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer);
563 }
564 }
565
566 // Shrinking the storage requires extra temporary memory.
567 // Destroying clang first reduces peak memory usage.
568 CICleanup.unregister();
569 Clang.reset();
570 Storage->shrink();
571 return PrecompiledPreamble(
572 std::move(Storage), std::move(PreambleBytes), PreambleEndsAtStartOfLine,
573 std::move(FilesInPreamble), std::move(MissingFiles));
574}
575
577 return PreambleBounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);
578}
579
580std::size_t PrecompiledPreamble::getSize() const {
581 switch (Storage->getKind()) {
582 case PCHStorage::Kind::InMemory:
583 return Storage->memoryContents().size();
584 case PCHStorage::Kind::TempFile: {
585 uint64_t Result;
586 if (llvm::sys::fs::file_size(Storage->filePath(), Result))
587 return 0;
588
589 assert(Result <= std::numeric_limits<std::size_t>::max() &&
590 "file size did not fit into size_t");
591 return Result;
592 }
593 }
594 llvm_unreachable("Unhandled storage kind");
595}
596
598 const llvm::MemoryBufferRef &MainFileBuffer,
599 PreambleBounds Bounds,
600 llvm::vfs::FileSystem &VFS) const {
601
602 assert(
603 Bounds.Size <= MainFileBuffer.getBufferSize() &&
604 "Buffer is too large. Bounds were calculated from a different buffer?");
605
606 auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
607 PreprocessorOptions &PreprocessorOpts =
608 PreambleInvocation->getPreprocessorOpts();
609
610 // We've previously computed a preamble. Check whether we have the same
611 // preamble now that we did before, and that there's enough space in
612 // the main-file buffer within the precompiled preamble to fit the
613 // new main file.
614 if (PreambleBytes.size() != Bounds.Size ||
615 PreambleEndsAtStartOfLine != Bounds.PreambleEndsAtStartOfLine ||
616 !std::equal(PreambleBytes.begin(), PreambleBytes.end(),
617 MainFileBuffer.getBuffer().begin()))
618 return false;
619 // The preamble has not changed. We may be able to re-use the precompiled
620 // preamble.
621
622 // Check that none of the files used by the preamble have changed.
623 // First, make a record of those files that have been overridden via
624 // remapping or unsaved_files.
625 std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles;
626 llvm::StringSet<> OverriddenAbsPaths; // Either by buffers or files.
627 for (const auto &R : PreprocessorOpts.RemappedFiles) {
628 llvm::vfs::Status Status;
629 if (!moveOnNoError(VFS.status(R.second), Status)) {
630 // If we can't stat the file we're remapping to, assume that something
631 // horrible happened.
632 return false;
633 }
634 // If a mapped file was previously missing, then it has changed.
635 llvm::SmallString<128> MappedPath(R.first);
636 if (!VFS.makeAbsolute(MappedPath))
637 OverriddenAbsPaths.insert(MappedPath);
638
639 OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile(
640 Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime()));
641 }
642
643 // OverridenFileBuffers tracks only the files not found in VFS.
644 llvm::StringMap<PreambleFileHash> OverridenFileBuffers;
645 for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
646 const PrecompiledPreamble::PreambleFileHash PreambleHash =
647 PreambleFileHash::createForMemoryBuffer(RB.second->getMemBufferRef());
648 llvm::vfs::Status Status;
649 if (moveOnNoError(VFS.status(RB.first), Status))
650 OverriddenFiles[Status.getUniqueID()] = PreambleHash;
651 else
652 OverridenFileBuffers[RB.first] = PreambleHash;
653
654 llvm::SmallString<128> MappedPath(RB.first);
655 if (!VFS.makeAbsolute(MappedPath))
656 OverriddenAbsPaths.insert(MappedPath);
657 }
658
659 // Check whether anything has changed.
660 for (const auto &F : FilesInPreamble) {
661 auto OverridenFileBuffer = OverridenFileBuffers.find(F.first());
662 if (OverridenFileBuffer != OverridenFileBuffers.end()) {
663 // The file's buffer was remapped and the file was not found in VFS.
664 // Check whether it matches up with the previous mapping.
665 if (OverridenFileBuffer->second != F.second)
666 return false;
667 continue;
668 }
669
670 llvm::vfs::Status Status;
671 if (!moveOnNoError(VFS.status(F.first()), Status)) {
672 // If the file's buffer is not remapped and we can't stat it,
673 // assume that something horrible happened.
674 return false;
675 }
676
677 std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden =
678 OverriddenFiles.find(Status.getUniqueID());
679 if (Overridden != OverriddenFiles.end()) {
680 // This file was remapped; check whether the newly-mapped file
681 // matches up with the previous mapping.
682 if (Overridden->second != F.second)
683 return false;
684 continue;
685 }
686
687 // Neither the file's buffer nor the file itself was remapped;
688 // check whether it has changed on disk.
689 if (Status.getSize() != uint64_t(F.second.Size) ||
690 llvm::sys::toTimeT(Status.getLastModificationTime()) !=
691 F.second.ModTime)
692 return false;
693 }
694 for (const auto &F : MissingFiles) {
695 // A missing file may be "provided" by an override buffer or file.
696 if (OverriddenAbsPaths.count(F.getKey()))
697 return false;
698 // If a file previously recorded as missing exists as a regular file, then
699 // consider the preamble out-of-date.
700 if (auto Status = VFS.status(F.getKey())) {
701 if (Status->isRegularFile())
702 return false;
703 }
704 }
705 return true;
706}
707
710 llvm::MemoryBuffer *MainFileBuffer) const {
711 PreambleBounds Bounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);
712 configurePreamble(Bounds, CI, VFS, MainFileBuffer);
713}
714
717 llvm::MemoryBuffer *MainFileBuffer) const {
718 auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *MainFileBuffer, 0);
719 configurePreamble(Bounds, CI, VFS, MainFileBuffer);
720}
721
723 std::unique_ptr<PCHStorage> Storage, std::vector<char> PreambleBytes,
724 bool PreambleEndsAtStartOfLine,
725 llvm::StringMap<PreambleFileHash> FilesInPreamble,
726 llvm::StringSet<> MissingFiles)
727 : Storage(std::move(Storage)), FilesInPreamble(std::move(FilesInPreamble)),
728 MissingFiles(std::move(MissingFiles)),
729 PreambleBytes(std::move(PreambleBytes)),
730 PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {
731 assert(this->Storage != nullptr);
732}
733
734PrecompiledPreamble::PreambleFileHash
735PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size,
736 time_t ModTime) {
737 PreambleFileHash Result;
738 Result.Size = Size;
739 Result.ModTime = ModTime;
740 Result.MD5 = {};
741 return Result;
742}
743
744PrecompiledPreamble::PreambleFileHash
745PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(
746 const llvm::MemoryBufferRef &Buffer) {
747 PreambleFileHash Result;
748 Result.Size = Buffer.getBufferSize();
749 Result.ModTime = 0;
750
751 llvm::MD5 MD5Ctx;
752 MD5Ctx.update(Buffer.getBuffer().data());
753 MD5Ctx.final(Result.MD5);
754
755 return Result;
756}
757
758void PrecompiledPreamble::configurePreamble(
759 PreambleBounds Bounds, CompilerInvocation &CI,
760 IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
761 llvm::MemoryBuffer *MainFileBuffer) const {
762 assert(VFS);
763
764 auto &PreprocessorOpts = CI.getPreprocessorOpts();
765
766 // Remap main file to point to MainFileBuffer.
767 auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile();
768 PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);
769
770 // Configure ImpicitPCHInclude.
771 PreprocessorOpts.PrecompiledPreambleBytes.first = Bounds.Size;
772 PreprocessorOpts.PrecompiledPreambleBytes.second =
774 PreprocessorOpts.DisablePCHOrModuleValidation =
776
777 // Don't bother generating the long version of the predefines buffer.
778 // The preamble is going to overwrite it anyway.
779 PreprocessorOpts.UsePredefines = false;
780
781 setupPreambleStorage(*Storage, PreprocessorOpts, VFS);
782}
783
784void PrecompiledPreamble::setupPreambleStorage(
785 const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts,
786 IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS) {
787 if (Storage.getKind() == PCHStorage::Kind::TempFile) {
788 llvm::StringRef PCHPath = Storage.filePath();
789 PreprocessorOpts.ImplicitPCHInclude = PCHPath.str();
790
791 // Make sure we can access the PCH file even if we're using a VFS
792 IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS =
793 llvm::vfs::getRealFileSystem();
794 if (VFS == RealFS || VFS->exists(PCHPath))
795 return;
796 auto Buf = RealFS->getBufferForFile(PCHPath);
797 if (!Buf) {
798 // We can't read the file even from RealFS, this is clearly an error,
799 // but we'll just leave the current VFS as is and let clang's code
800 // figure out what to do with missing PCH.
801 return;
802 }
803
804 // We have a slight inconsistency here -- we're using the VFS to
805 // read files, but the PCH was generated in the real file system.
806 VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(*Buf), VFS);
807 } else {
808 assert(Storage.getKind() == PCHStorage::Kind::InMemory);
809 // For in-memory preamble, we have to provide a VFS overlay that makes it
810 // accessible.
811 StringRef PCHPath = getInMemoryPreamblePath();
812 PreprocessorOpts.ImplicitPCHInclude = std::string(PCHPath);
813
814 auto Buf = llvm::MemoryBuffer::getMemBuffer(
815 Storage.memoryContents(), PCHPath, /*RequiresNullTerminator=*/false);
816 VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(Buf), VFS);
817 }
818}
819
824std::unique_ptr<PPCallbacks> PreambleCallbacks::createPPCallbacks() {
825 return nullptr;
826}
828
829static llvm::ManagedStatic<BuildPreambleErrorCategory> BuildPreambleErrCategory;
830
832 return std::error_code(static_cast<int>(Error), *BuildPreambleErrCategory);
833}
834
835const char *BuildPreambleErrorCategory::name() const noexcept {
836 return "build-preamble.error";
837}
838
839std::string BuildPreambleErrorCategory::message(int condition) const {
840 switch (static_cast<BuildPreambleError>(condition)) {
842 return "Could not create temporary file for PCH";
844 return "CreateTargetInfo() return null";
846 return "BeginSourceFile() return an error";
848 return "Could not emit PCH";
850 return "Command line arguments must contain exactly one source file";
851 }
852 llvm_unreachable("unexpected BuildPreambleError");
853}
static bool moveOnNoError(llvm::ErrorOr< T > Val, T &Output)
Definition ASTUnit.cpp:144
static Decl::Kind getKind(const Decl *D)
Defines the clang::FileManager interface and associated types.
llvm::MachO::FileType FileType
Definition MachO.h:46
static llvm::ManagedStatic< BuildPreambleErrorCategory > BuildPreambleErrCategory
Defines the clang::Preprocessor interface.
static std::unique_ptr< PCHStorage > file(std::unique_ptr< TempPCHFile > File)
static std::unique_ptr< PCHStorage > inMemory(std::shared_ptr< PCHBuffer > Buf)
Writes an AST file containing the contents of a translation unit.
Definition ASTWriter.h:97
std::string message(int condition) const override
const char * name() const noexcept override
Abstract base class that describes a handler that will receive source ranges for each of the comments...
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
ModuleCache & getModuleCache() const
Preprocessor & getPreprocessor() const
Return the current preprocessor.
FrontendOptions & getFrontendOpts()
CodeGenOptions & getCodeGenOpts()
Helper class for holding the data necessary to invoke the compiler.
PreprocessorOptions & getPreprocessorOpts()
LangOptions & getLangOpts()
Mutable getters.
FrontendOptions & getFrontendOpts()
An interface for collecting the dependencies of a compilation.
Definition Utils.h:63
FrontendOptions - Options for controlling the behavior of the frontend.
std::string OutputFile
The output file, if any.
unsigned RelocatablePCH
When generating PCH files, instruct the AST writer to create relocatable PCH files.
SmallVector< FrontendInputFile, 0 > Inputs
The input files and their types.
frontend::ActionKind ProgramAction
The frontend action to perform.
static std::unique_ptr< llvm::raw_pwrite_stream > CreateOutputFile(CompilerInstance &CI, StringRef InFile, std::string &OutputFile)
Creates file to write the PCH into and returns a stream to write it into.
static bool ComputeASTConsumerArguments(CompilerInstance &CI, std::string &Sysroot)
Compute the AST consumer arguments that will be used to create the PCHGenerator instance returned by ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static PreambleBounds ComputePreamble(StringRef Buffer, const LangOptions &LangOpts, unsigned MaxLines=0)
Compute the preamble of the given file.
Definition Lexer.cpp:635
void HandleTranslationUnit(ASTContext &Ctx) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition PPCallbacks.h:37
A set of callbacks to gather useful information while building a preamble.
virtual void AfterPCHEmitted(ASTWriter &Writer)
Called after PCH has been emitted.
virtual void BeforeExecute(CompilerInstance &CI)
Called before FrontendAction::Execute.
virtual CommentHandler * getCommentHandler()
The returned CommentHandler will be added to the preprocessor if not null.
virtual void HandleTopLevelDecl(DeclGroupRef DG)
Called for each TopLevelDecl.
virtual std::unique_ptr< PPCallbacks > createPPCallbacks()
Creates wrapper class for PPCallbacks so we can also process information about includes that are insi...
virtual void AfterExecute(CompilerInstance &CI)
Called after FrontendAction::Execute(), but before FrontendAction::EndSourceFile().
virtual bool shouldSkipFunctionBody(Decl *D)
Determines which function bodies are parsed, by default skips everything.
A class holding a PCH and all information to check whether it is valid to reuse the PCH for the subse...
void OverridePreamble(CompilerInvocation &CI, IntrusiveRefCntPtr< llvm::vfs::FileSystem > &VFS, llvm::MemoryBuffer *MainFileBuffer) const
Configure CI to use this preamble.
static llvm::ErrorOr< PrecompiledPreamble > Build(const CompilerInvocation &Invocation, const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, IntrusiveRefCntPtr< DiagnosticsEngine > Diagnostics, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, bool StoreInMemory, StringRef StoragePath, PreambleCallbacks &Callbacks)
Try to build PrecompiledPreamble for Invocation.
PrecompiledPreamble & operator=(PrecompiledPreamble &&)
bool CanReuse(const CompilerInvocation &Invocation, const llvm::MemoryBufferRef &MainFileBuffer, PreambleBounds Bounds, llvm::vfs::FileSystem &VFS) const
Check whether PrecompiledPreamble can be reused for the new contents(MainFileBuffer) of the main file...
void AddImplicitPreamble(CompilerInvocation &CI, IntrusiveRefCntPtr< llvm::vfs::FileSystem > &VFS, llvm::MemoryBuffer *MainFileBuffer) const
Changes options inside CI to use PCH from this preamble.
std::size_t getSize() const
Returns the size, in bytes, that preamble takes on disk or in memory.
PreambleBounds getBounds() const
PreambleBounds used to build the preamble.
PrecompiledPreamble(PrecompiledPreamble &&)
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
std::vector< std::pair< std::string, std::string > > RemappedFiles
The set of file remappings, which take existing files on the system (the first part of each pair) and...
std::pair< unsigned, bool > PrecompiledPreambleBytes
If non-zero, the implicit PCH include is actually a precompiled preamble that covers this number of b...
bool RetainRemappedFileBuffers
Whether the compiler instance should retain (i.e., not free) the buffers associated with remapped fil...
std::string ImplicitPCHInclude
The implicit PCH included at the start of the translation unit, or empty.
void addRemappedFile(StringRef From, StringRef To)
bool GeneratePreamble
True indicates that a preamble is being generated.
std::vector< std::pair< std::string, llvm::MemoryBuffer * > > RemappedFileBuffers
The set of file-to-buffer remappings, which take existing files on the system (the first part of each...
This class handles loading and caching of source files into memory.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition Token.h:134
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
@ GeneratePCH
Generate pre-compiled header.
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions &DiagOpts, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
@ HeaderSearch
Remove unused header search paths including header maps.
The JSON file list parser is used to communicate input to InstallAPI.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
std::error_code make_error_code(BuildPreambleError Error)
@ Module
Module linkage, which indicates that the entity can be referred to from other translation units withi...
Definition Linkage.h:54
@ Result
The result type of a method or function.
Definition TypeBase.h:905
const FunctionProtoType * T
void ProcessWarningOptions(DiagnosticsEngine &Diags, const DiagnosticOptions &Opts, llvm::vfs::FileSystem &VFS, bool ReportDiags=true)
ProcessWarningOptions - Initialize the diagnostic client and process the warning options specified on...
Definition Warnings.cpp:46
TranslationUnitKind
Describes the kind of translation unit being processed.
@ TU_Prefix
The translation unit is a prefix to a translation unit, and is not complete.
@ PCH
Disable validation for a precompiled header and the modules it depends on.
PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts, const llvm::MemoryBufferRef &Buffer, unsigned MaxLines)
Runs lexer to compute suggested preamble bounds.
#define true
Definition stdbool.h:25
Describes the bounds (start, size) of the preamble and a flag required by PreprocessorOptions::Precom...
Definition Lexer.h:60
unsigned Size
Size of the preamble in bytes.
Definition Lexer.h:62
bool PreambleEndsAtStartOfLine
Whether the preamble ends at the start of a new line.
Definition Lexer.h:68