#pragma once #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif //===--- PrecompiledPreamble.h - Build precompiled preambles ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Helper class to build precompiled preamble. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_FRONTEND_PRECOMPILEDPREAMBLE_H #define LLVM_CLANG_FRONTEND_PRECOMPILEDPREAMBLE_H #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/AlignOf.h" #include "llvm/Support/MD5.h" #include #include #include #include namespace llvm { class MemoryBuffer; class MemoryBufferRef; namespace vfs { class FileSystem; } } // namespace llvm namespace clang { class CompilerInstance; class CompilerInvocation; class Decl; class DeclGroupRef; class PCHContainerOperations; /// Runs lexer to compute suggested preamble bounds. PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts, const llvm::MemoryBufferRef &Buffer, unsigned MaxLines); class PreambleCallbacks; /// A class holding a PCH and all information to check whether it is valid to /// reuse the PCH for the subsequent runs. Use BuildPreamble to create PCH and /// CanReusePreamble + AddImplicitPreamble to make use of it. class PrecompiledPreamble { class PCHStorage; struct PreambleFileHash; public: /// Try to build PrecompiledPreamble for \p Invocation. See /// BuildPreambleError for possible error codes. /// /// \param Invocation Original CompilerInvocation with options to compile the /// file. /// /// \param MainFileBuffer Buffer with the contents of the main file. /// /// \param Bounds Bounds of the preamble, result of calling /// ComputePreambleBounds. /// /// \param Diagnostics Diagnostics engine to be used while building the /// preamble. /// /// \param VFS An instance of vfs::FileSystem to be used for file /// accesses. /// /// \param PCHContainerOps An instance of PCHContainerOperations. /// /// \param StoreInMemory Store PCH in memory. If false, PCH will be stored in /// a temporary file. /// /// \param Callbacks A set of callbacks to be executed when building /// the preamble. static llvm::ErrorOr Build(const CompilerInvocation &Invocation, const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr VFS, std::shared_ptr PCHContainerOps, bool StoreInMemory, PreambleCallbacks &Callbacks); PrecompiledPreamble(PrecompiledPreamble &&) = default; PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default; /// PreambleBounds used to build the preamble. PreambleBounds getBounds() const; /// Returns the size, in bytes, that preamble takes on disk or in memory. /// For on-disk preambles returns 0 if filesystem operations fail. Intended to /// be used for logging and debugging purposes only. std::size_t getSize() const; /// Returned string is not null-terminated. llvm::StringRef getContents() const { return {PreambleBytes.data(), PreambleBytes.size()}; } /// Check whether PrecompiledPreamble can be reused for the new contents(\p /// MainFileBuffer) of the main file. bool CanReuse(const CompilerInvocation &Invocation, const llvm::MemoryBufferRef &MainFileBuffer, PreambleBounds Bounds, llvm::vfs::FileSystem &VFS) const; /// Changes options inside \p CI to use PCH from this preamble. Also remaps /// main file to \p MainFileBuffer and updates \p VFS to ensure the preamble /// is accessible. /// Requires that CanReuse() is true. /// For in-memory preambles, PrecompiledPreamble instance continues to own the /// MemoryBuffer with the Preamble after this method returns. The caller is /// responsible for making sure the PrecompiledPreamble instance outlives the /// compiler run and the AST that will be using the PCH. void AddImplicitPreamble(CompilerInvocation &CI, IntrusiveRefCntPtr &VFS, llvm::MemoryBuffer *MainFileBuffer) const; /// Configure \p CI to use this preamble. /// Like AddImplicitPreamble, but doesn't assume CanReuse() is true. /// If this preamble does not match the file, it may parse differently. void OverridePreamble(CompilerInvocation &CI, IntrusiveRefCntPtr &VFS, llvm::MemoryBuffer *MainFileBuffer) const; private: PrecompiledPreamble(PCHStorage Storage, std::vector PreambleBytes, bool PreambleEndsAtStartOfLine, llvm::StringMap FilesInPreamble, llvm::StringSet<> MissingFiles); /// A temp file that would be deleted on destructor call. If destructor is not /// called for any reason, the file will be deleted at static objects' /// destruction. /// An assertion will fire if two TempPCHFiles are created with the same name, /// so it's not intended to be used outside preamble-handling. class TempPCHFile { public: // A main method used to construct TempPCHFile. static llvm::ErrorOr CreateNewPreamblePCHFile(); private: TempPCHFile(std::string FilePath); public: TempPCHFile(TempPCHFile &&Other); TempPCHFile &operator=(TempPCHFile &&Other); TempPCHFile(const TempPCHFile &) = delete; ~TempPCHFile(); /// A path where temporary file is stored. llvm::StringRef getFilePath() const; private: void RemoveFileIfPresent(); private: llvm::Optional FilePath; }; class InMemoryPreamble { public: std::string Data; }; class PCHStorage { public: enum class Kind { Empty, InMemory, TempFile }; PCHStorage() = default; PCHStorage(TempPCHFile File); PCHStorage(InMemoryPreamble Memory); PCHStorage(const PCHStorage &) = delete; PCHStorage &operator=(const PCHStorage &) = delete; PCHStorage(PCHStorage &&Other); PCHStorage &operator=(PCHStorage &&Other); ~PCHStorage(); Kind getKind() const; TempPCHFile &asFile(); const TempPCHFile &asFile() const; InMemoryPreamble &asMemory(); const InMemoryPreamble &asMemory() const; private: void destroy(); void setEmpty(); private: Kind StorageKind = Kind::Empty; llvm::AlignedCharArrayUnion Storage = {}; }; /// Data used to determine if a file used in the preamble has been changed. struct PreambleFileHash { /// All files have size set. off_t Size = 0; /// Modification time is set for files that are on disk. For memory /// buffers it is zero. time_t ModTime = 0; /// Memory buffers have MD5 instead of modification time. We don't /// compute MD5 for on-disk files because we hope that modification time is /// enough to tell if the file was changed. llvm::MD5::MD5Result MD5 = {}; static PreambleFileHash createForFile(off_t Size, time_t ModTime); static PreambleFileHash createForMemoryBuffer(const llvm::MemoryBufferRef &Buffer); friend bool operator==(const PreambleFileHash &LHS, const PreambleFileHash &RHS) { return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime && LHS.MD5 == RHS.MD5; } friend bool operator!=(const PreambleFileHash &LHS, const PreambleFileHash &RHS) { return !(LHS == RHS); } }; /// Helper function to set up PCH for the preamble into \p CI and \p VFS to /// with the specified \p Bounds. void configurePreamble(PreambleBounds Bounds, CompilerInvocation &CI, IntrusiveRefCntPtr &VFS, llvm::MemoryBuffer *MainFileBuffer) const; /// Sets up the PreprocessorOptions and changes VFS, so that PCH stored in \p /// Storage is accessible to clang. This method is an implementation detail of /// AddImplicitPreamble. static void setupPreambleStorage(const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts, IntrusiveRefCntPtr &VFS); /// Manages the memory buffer or temporary file that stores the PCH. PCHStorage Storage; /// Keeps track of the files that were used when computing the /// preamble, with both their buffer size and their modification time. /// /// If any of the files have changed from one compile to the next, /// the preamble must be thrown away. llvm::StringMap FilesInPreamble; /// Files that were not found during preamble building. If any of these now /// exist then the preamble should not be reused. /// /// Storing *all* the missing files that could invalidate the preamble would /// make it too expensive to revalidate (when the include path has many /// entries, each #include will miss half of them on average). /// Instead, we track only files that could have satisfied an #include that /// was ultimately not found. llvm::StringSet<> MissingFiles; /// The contents of the file that was used to precompile the preamble. Only /// contains first PreambleBounds::Size bytes. Used to compare if the relevant /// part of the file has not changed, so that preamble can be reused. std::vector PreambleBytes; /// See PreambleBounds::PreambleEndsAtStartOfLine bool PreambleEndsAtStartOfLine; }; /// A set of callbacks to gather useful information while building a preamble. class PreambleCallbacks { public: virtual ~PreambleCallbacks() = default; /// Called before FrontendAction::Execute. /// Can be used to store references to various CompilerInstance fields /// (e.g. SourceManager) that may be interesting to the consumers of other /// callbacks. virtual void BeforeExecute(CompilerInstance &CI); /// Called after FrontendAction::Execute(), but before /// FrontendAction::EndSourceFile(). Can be used to transfer ownership of /// various CompilerInstance fields before they are destroyed. virtual void AfterExecute(CompilerInstance &CI); /// Called after PCH has been emitted. \p Writer may be used to retrieve /// information about AST, serialized in PCH. virtual void AfterPCHEmitted(ASTWriter &Writer); /// Called for each TopLevelDecl. /// NOTE: To allow more flexibility a custom ASTConsumer could probably be /// used instead, but having only this method allows a simpler API. virtual void HandleTopLevelDecl(DeclGroupRef DG); /// Creates wrapper class for PPCallbacks so we can also process information /// about includes that are inside of a preamble. Called after BeforeExecute. virtual std::unique_ptr createPPCallbacks(); /// The returned CommentHandler will be added to the preprocessor if not null. virtual CommentHandler *getCommentHandler(); /// Determines which function bodies are parsed, by default skips everything. /// Only used if FrontendOpts::SkipFunctionBodies is true. /// See ASTConsumer::shouldSkipFunctionBody. virtual bool shouldSkipFunctionBody(Decl *D) { return true; } }; enum class BuildPreambleError { CouldntCreateTempFile = 1, CouldntCreateTargetInfo, BeginSourceFileFailed, CouldntEmitPCH, BadInputs }; class BuildPreambleErrorCategory final : public std::error_category { public: const char *name() const noexcept override; std::string message(int condition) const override; }; std::error_code make_error_code(BuildPreambleError Error); } // namespace clang namespace std { template <> struct is_error_code_enum : std::true_type {}; } // namespace std #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif