25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/ADT/StringSet.h"
27 #include "llvm/Support/CrashRecoveryContext.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/Mutex.h"
30 #include "llvm/Support/MutexGuard.h"
32 using namespace clang;
37 class TemporaryFiles {
40 static TemporaryFiles &getInstance();
44 TemporaryFiles() =
default;
46 TemporaryFiles(
const TemporaryFiles &) =
delete;
52 void addFile(StringRef File);
55 void removeFile(StringRef File);
58 llvm::sys::SmartMutex<false> Mutex;
59 llvm::StringSet<> Files;
62 TemporaryFiles &TemporaryFiles::getInstance() {
63 static TemporaryFiles Instance;
67 TemporaryFiles::~TemporaryFiles() {
68 llvm::MutexGuard Guard(Mutex);
69 for (
const auto &File : Files)
70 llvm::sys::fs::remove(File.getKey());
73 void TemporaryFiles::addFile(StringRef File) {
74 llvm::MutexGuard Guard(Mutex);
75 auto IsInserted = Files.insert(File).second;
77 assert(IsInserted &&
"File has already been added");
80 void TemporaryFiles::removeFile(StringRef File) {
81 llvm::MutexGuard Guard(Mutex);
82 auto WasPresent = Files.erase(File);
84 assert(WasPresent &&
"File was not tracked");
85 llvm::sys::fs::remove(File);
92 void MacroDefined(
const Token &MacroNameTok,
104 : Callbacks(Callbacks) {}
107 StringRef InFile)
override;
109 bool hasEmittedPreamblePCH()
const {
return HasEmittedPreamblePCH; }
111 void setEmittedPreamblePCH(
ASTWriter &Writer) {
112 this->HasEmittedPreamblePCH =
true;
113 Callbacks.AfterPCHEmitted(Writer);
116 bool shouldEraseOutputFiles()
override {
return !hasEmittedPreamblePCH(); }
117 bool hasCodeCompletionSupport()
const override {
return false; }
118 bool hasASTFileSupport()
const override {
return false; }
122 friend class PrecompilePreambleConsumer;
124 bool HasEmittedPreamblePCH =
false;
128 class PrecompilePreambleConsumer :
public PCHGenerator {
130 PrecompilePreambleConsumer(PrecompilePreambleAction &
Action,
132 std::unique_ptr<raw_ostream> Out)
136 Action(Action), Out(std::move(Out)) {}
139 Action.Callbacks.HandleTopLevelDecl(DG);
143 void HandleTranslationUnit(
ASTContext &Ctx)
override {
145 if (!hasEmittedPCH())
154 getPCH() = std::move(Empty);
156 Action.setEmittedPreamblePCH(getWriter());
160 PrecompilePreambleAction &
Action;
161 std::unique_ptr<raw_ostream> Out;
164 std::unique_ptr<ASTConsumer>
169 std::string OutputFile;
170 std::unique_ptr<raw_ostream> OS =
180 llvm::make_unique<PreambleMacroCallbacks>(Callbacks));
181 return llvm::make_unique<PrecompilePreambleConsumer>(
185 template <
class T>
bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {
188 Output = std::move(*Val);
195 llvm::MemoryBuffer *
Buffer,
205 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
207 assert(VFS &&
"VFS is null");
212 auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
215 PreambleInvocation->getPreprocessorOpts();
219 llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile =
220 PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile();
221 if (!PreamblePCHFile)
226 std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(),
227 MainFileBuffer->getBufferStart() +
234 FrontendOpts.OutputFile = PreamblePCHFile->getFilePath();
239 std::unique_ptr<CompilerInstance> Clang(
243 llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(
246 Clang->setInvocation(std::move(PreambleInvocation));
247 Clang->setDiagnostics(&Diagnostics);
251 Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
252 if (!Clang->hasTarget())
259 Clang->getTarget().adjust(Clang->getLangOpts());
261 assert(Clang->getFrontendOpts().Inputs.size() == 1 &&
262 "Invocation must have exactly one source file!");
263 assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() ==
265 "FIXME: AST inputs not yet supported here!");
266 assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() !=
268 "IR inputs not support here!");
280 Clang->setFileManager(
new FileManager(Clang->getFileSystemOpts(), VFS));
283 Clang->setSourceManager(
286 auto PreambleDepCollector = std::make_shared<DependencyCollector>();
287 Clang->addDependencyCollector(PreambleDepCollector);
290 StringRef MainFilePath = FrontendOpts.Inputs[0].getFile();
291 auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy(
292 MainFileBuffer->getBuffer().slice(0, Bounds.
Size), MainFilePath);
295 PreprocessorOpts.
addRemappedFile(MainFilePath, PreambleInputBuffer.get());
300 PreambleInputBuffer.release());
303 std::unique_ptr<PrecompilePreambleAction> Act;
304 Act.reset(
new PrecompilePreambleAction(Callbacks));
305 if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0]))
313 Act->EndSourceFile();
315 if (!Act->hasEmittedPreamblePCH())
320 llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble;
323 for (
auto &
Filename : PreambleDepCollector->getDependencies()) {
328 FilesInPreamble[File->
getName()] =
329 PrecompiledPreamble::PreambleFileHash::createForFile(File->
getSize(),
333 FilesInPreamble[File->
getName()] =
334 PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer);
339 std::move(*PreamblePCHFile), std::move(PreambleBytes),
340 PreambleEndsAtStartOfLine, std::move(FilesInPreamble));
344 return PreambleBounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);
348 const llvm::MemoryBuffer *MainFileBuffer,
353 Bounds.
Size <= MainFileBuffer->getBufferSize() &&
354 "Buffer is too large. Bounds were calculated from a different buffer?");
356 auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
358 PreambleInvocation->getPreprocessorOpts();
367 if (PreambleBytes.size() != Bounds.
Size ||
369 memcmp(PreambleBytes.data(), MainFileBuffer->getBufferStart(),
378 std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles;
379 for (
const auto &R : PreprocessorOpts.RemappedFiles) {
387 OverriddenFiles[Status.
getUniqueID()] = PreambleFileHash::createForFile(
391 for (
const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
397 PreambleFileHash::createForMemoryBuffer(RB.second);
401 for (
const auto &F : FilesInPreamble) {
403 if (!moveOnNoError(VFS->
status(F.first()), Status)) {
408 std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden =
410 if (Overridden != OverriddenFiles.end()) {
413 if (Overridden->second != F.second)
419 if (Status.
getSize() != uint64_t(F.second.Size) ||
433 PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine;
434 PreprocessorOpts.ImplicitPCHInclude = PCHFile.getFilePath();
435 PreprocessorOpts.DisablePCHValidation =
true;
439 PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);
443 TempPCHFile PCHFile, std::vector<char> PreambleBytes,
444 bool PreambleEndsAtStartOfLine,
445 llvm::StringMap<PreambleFileHash> FilesInPreamble)
446 : PCHFile(std::move(PCHFile)), FilesInPreamble(FilesInPreamble),
447 PreambleBytes(std::move(PreambleBytes)),
448 PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {}
450 llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
451 PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile() {
455 const char *TmpFile = ::getenv(
"CINDEXTEST_PREAMBLE_FILE");
457 return TempPCHFile::createFromCustomPath(TmpFile);
458 return TempPCHFile::createInSystemTempDir(
"preamble",
"pch");
461 llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
462 PrecompiledPreamble::TempPCHFile::createInSystemTempDir(
const Twine &Prefix,
465 auto EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, File);
468 return TempPCHFile(std::move(File).str());
471 llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
472 PrecompiledPreamble::TempPCHFile::createFromCustomPath(
const Twine &Path) {
473 return TempPCHFile(Path.str());
476 PrecompiledPreamble::TempPCHFile::TempPCHFile(std::string
FilePath)
477 : FilePath(std::move(FilePath)) {
478 TemporaryFiles::getInstance().addFile(*this->FilePath);
481 PrecompiledPreamble::TempPCHFile::TempPCHFile(TempPCHFile &&Other) {
482 FilePath = std::move(Other.FilePath);
483 Other.FilePath =
None;
486 PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::TempPCHFile::
487 operator=(TempPCHFile &&Other) {
488 RemoveFileIfPresent();
490 FilePath = std::move(Other.FilePath);
491 Other.FilePath =
None;
495 PrecompiledPreamble::TempPCHFile::~TempPCHFile() { RemoveFileIfPresent(); }
497 void PrecompiledPreamble::TempPCHFile::RemoveFileIfPresent() {
499 TemporaryFiles::getInstance().removeFile(*FilePath);
504 llvm::StringRef PrecompiledPreamble::TempPCHFile::getFilePath()
const {
505 assert(FilePath &&
"TempPCHFile doesn't have a FilePath. Had it been moved?");
509 PrecompiledPreamble::PreambleFileHash
510 PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size,
514 Result.ModTime = ModTime;
519 PrecompiledPreamble::PreambleFileHash
520 PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(
521 const llvm::MemoryBuffer *
Buffer) {
523 Result.Size = Buffer->getBufferSize();
527 MD5Ctx.update(Buffer->getBuffer().data());
528 MD5Ctx.final(Result.MD5);
544 return "build-preamble.error";
548 switch (static_cast<BuildPreambleError>(condition)) {
550 return "Preamble is empty";
552 return "Could not create temporary file for PCH";
554 return "CreateTargetInfo() return null";
556 return "Could not create VFS Overlay";
558 return "BeginSourceFile() return an error";
560 return "Could not emit PCH";
562 llvm_unreachable(
"unexpected BuildPreambleError");
A size of the preamble and a flag required by PreprocessorOptions::PrecompiledPreambleBytes.
Implements support for file system lookup, file system caching, and directory search management...
static std::pair< unsigned, bool > ComputePreamble(StringRef Buffer, const LangOptions &LangOpts, unsigned MaxLines=0)
Compute the preamble of the given file.
The translation unit is a prefix to a translation unit, and is not complete.
std::unique_ptr< llvm::MemoryBuffer > Buffer
static std::unique_ptr< raw_pwrite_stream > ComputeASTConsumerArguments(CompilerInstance &CI, StringRef InFile, std::string &Sysroot, std::string &OutputFile)
Compute the AST consumer arguments that will be used to create the PCHGenerator instance returned by ...
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
void addRemappedFile(StringRef From, StringRef To)
This interface provides a way to observe the actions of the preprocessor as it does its thing...
The virtual file system interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
void HandleTranslationUnit(ASTContext &Ctx) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
Token - This structure provides full information about a lexed token.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
unsigned RelocatablePCH
When generating PCH files, instruct the AST writer to create relocatable PCH files.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
std::error_code make_error_code(BuildPreambleError Error)
FrontendOptions & getFrontendOpts()
Concrete class used by the front-end to report problems and issues.
bool RetainRemappedFileBuffers
Whether the compiler instance should retain (i.e., not free) the buffers associated with remapped fil...
StringRef getName() const
llvm::sys::TimePoint getLastModificationTime() const
The result of a status operation.
void Reset()
Reset the state of the diagnostic object to its initial configuration.
Preprocessor & getPreprocessor() const
Return the current preprocessor.
FrontendOptions & getFrontendOpts()
bool PreambleEndsAtStartOfLine
Whether the preamble ends at the start of a new line.
A set of callbacks to gather useful information while building a preamble.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
PreambleBounds getBounds() const
PreambleBounds used to build the preamble.
llvm::sys::fs::UniqueID getUniqueID() const
IntrusiveRefCntPtr< vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
virtual void HandleMacroDefined(const Token &MacroNameTok, const MacroDirective *MD)
Called for each macro defined in the Preamble.
static TargetInfo * CreateTargetInfo(DiagnosticsEngine &Diags, const std::shared_ptr< TargetOptions > &Opts)
Construct a target for the given options.
PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts, llvm::MemoryBuffer *Buffer, unsigned MaxLines)
Runs lexer to compute suggested preamble bounds.
bool CanReuse(const CompilerInvocation &Invocation, const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, vfs::FileSystem *VFS) const
Check whether PrecompiledPreamble can be reused for the new contents(MainFileBuffer) of the main file...
Encapsulates changes to the "macros namespace" (the location where the macro name became active...
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
std::vector< FrontendInputFile > Inputs
The input files and their types.
Cached information about one file (either on disk or in the virtual file system). ...
void ProcessWarningOptions(DiagnosticsEngine &Diags, const DiagnosticOptions &Opts, bool ReportDiags=true)
ProcessWarningOptions - Initialize the diagnostic client and process the warning options specified on...
Abstract base class to use for AST consumer-based frontend actions.
virtual void AfterPCHEmitted(ASTWriter &Writer)
Called after PCH has been emitted.
llvm::MemoryBuffer * getMemoryBufferForFile(const FileEntry *File, bool *Invalid=nullptr)
Retrieve the memory buffer associated with the given file.
PrecompiledPreamble(PrecompiledPreamble &&)=default
FileID getMainFileID() const
Returns the FileID of the main source file.
std::string message(int condition) const override
const char * name() const noexceptoverride
PreprocessorOptions & getPreprocessorOpts()
Helper class for holding the data necessary to invoke the compiler.
Defines the virtual file system interface vfs::FileSystem.
FrontendOptions - Options for controlling the behavior of the frontend.
virtual void AfterExecute(CompilerInstance &CI)
Called after FrontendAction::Execute(), but before FrontendAction::EndSourceFile().
void AddImplicitPreamble(CompilerInvocation &CI, llvm::MemoryBuffer *MainFileBuffer) const
Changes options inside CI to use PCH from this preamble.
time_t getModificationTime() const
unsigned Size
Size of the preamble in bytes.
Generate pre-compiled header.
TranslationUnitKind
Describes the kind of translation unit being processed.
Writes an AST file containing the contents of a translation unit.
std::pair< unsigned, bool > PrecompiledPreambleBytes
If non-zero, the implicit PCH include is actually a precompiled preamble that covers this number of b...
Defines the clang::TargetInfo interface.
An abstract superclass that describes a custom extension to the module/precompiled header file format...
AST and semantic-analysis consumer that generates a precompiled header from the parsed source code...
static llvm::ErrorOr< PrecompiledPreamble > Build(const CompilerInvocation &Invocation, const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr< vfs::FileSystem > VFS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, PreambleCallbacks &Callbacks)
Try to build PrecompiledPreamble for Invocation.
virtual void HandleTopLevelDecl(DeclGroupRef DG)
Called for each TopLevelDecl.
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.