//===--- 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/MD5.h"
#include <cstddef>
#include <memory>
#include <system_error>
#include <type_traits>
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<PrecompiledPreamble>
Build(const CompilerInvocation &Invocation,
const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
DiagnosticsEngine &Diagnostics,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
bool StoreInMemory, PreambleCallbacks &Callbacks);
PrecompiledPreamble(PrecompiledPreamble &&);
PrecompiledPreamble &operator=(PrecompiledPreamble &&);
~PrecompiledPreamble();
/// 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<llvm::vfs::FileSystem> &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<llvm::vfs::FileSystem> &VFS,
llvm::MemoryBuffer *MainFileBuffer) const;
private:
PrecompiledPreamble(std::unique_ptr<PCHStorage> Storage,
std::vector<char> PreambleBytes,
bool PreambleEndsAtStartOfLine,
llvm::StringMap<PreambleFileHash> FilesInPreamble,
llvm::StringSet<> MissingFiles);
/// 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<llvm::vfs::FileSystem> &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<llvm::vfs::FileSystem> &VFS);
/// Manages the memory buffer or temporary file that stores the PCH.
std::unique_ptr<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<PreambleFileHash> 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<char> 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<PPCallbacks> 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<clang::BuildPreambleError> : std::true_type {};
} // namespace std
#endif