Path: blob/main/contrib/llvm-project/clang/lib/Frontend/PrecompiledPreamble.cpp
35234 views
//===--- PrecompiledPreamble.cpp - Build precompiled preambles --*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Helper class to build precompiled preamble.9//10//===----------------------------------------------------------------------===//1112#include "clang/Frontend/PrecompiledPreamble.h"13#include "clang/Basic/FileManager.h"14#include "clang/Basic/LangStandard.h"15#include "clang/Frontend/CompilerInstance.h"16#include "clang/Frontend/CompilerInvocation.h"17#include "clang/Frontend/FrontendActions.h"18#include "clang/Frontend/FrontendOptions.h"19#include "clang/Lex/HeaderSearch.h"20#include "clang/Lex/Lexer.h"21#include "clang/Lex/Preprocessor.h"22#include "clang/Lex/PreprocessorOptions.h"23#include "clang/Serialization/ASTWriter.h"24#include "llvm/ADT/SmallString.h"25#include "llvm/ADT/StringSet.h"26#include "llvm/ADT/iterator_range.h"27#include "llvm/Config/llvm-config.h"28#include "llvm/Support/CrashRecoveryContext.h"29#include "llvm/Support/FileSystem.h"30#include "llvm/Support/ManagedStatic.h"31#include "llvm/Support/Path.h"32#include "llvm/Support/Process.h"33#include "llvm/Support/VirtualFileSystem.h"34#include <limits>35#include <mutex>36#include <utility>3738using namespace clang;3940namespace {4142StringRef getInMemoryPreamblePath() {43#if defined(LLVM_ON_UNIX)44return "/__clang_tmp/___clang_inmemory_preamble___";45#elif defined(_WIN32)46return "C:\\__clang_tmp\\___clang_inmemory_preamble___";47#else48#warning "Unknown platform. Defaulting to UNIX-style paths for in-memory PCHs"49return "/__clang_tmp/___clang_inmemory_preamble___";50#endif51}5253IntrusiveRefCntPtr<llvm::vfs::FileSystem>54createVFSOverlayForPreamblePCH(StringRef PCHFilename,55std::unique_ptr<llvm::MemoryBuffer> PCHBuffer,56IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {57// We want only the PCH file from the real filesystem to be available,58// so we create an in-memory VFS with just that and overlay it on top.59IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> PCHFS(60new llvm::vfs::InMemoryFileSystem());61PCHFS->addFile(PCHFilename, 0, std::move(PCHBuffer));62IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> Overlay(63new llvm::vfs::OverlayFileSystem(VFS));64Overlay->pushOverlay(PCHFS);65return Overlay;66}6768class PreambleDependencyCollector : public DependencyCollector {69public:70// We want to collect all dependencies for correctness. Avoiding the real71// system dependencies (e.g. stl from /usr/lib) would probably be a good idea,72// but there is no way to distinguish between those and the ones that can be73// spuriously added by '-isystem' (e.g. to suppress warnings from those74// headers).75bool needSystemDependencies() override { return true; }76};7778// Collects files whose existence would invalidate the preamble.79// Collecting *all* of these would make validating it too slow though, so we80// just find all the candidates for 'file not found' diagnostics.81//82// A caveat that may be significant for generated files: we'll omit files under83// search path entries whose roots don't exist when the preamble is built.84// These are pruned by InitHeaderSearch and so we don't see the search path.85// It would be nice to include them but we don't want to duplicate all the rest86// of the InitHeaderSearch logic to reconstruct them.87class MissingFileCollector : public PPCallbacks {88llvm::StringSet<> &Out;89const HeaderSearch &Search;90const SourceManager &SM;9192public:93MissingFileCollector(llvm::StringSet<> &Out, const HeaderSearch &Search,94const SourceManager &SM)95: Out(Out), Search(Search), SM(SM) {}9697void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,98StringRef FileName, bool IsAngled,99CharSourceRange FilenameRange,100OptionalFileEntryRef File, StringRef SearchPath,101StringRef RelativePath, const Module *SuggestedModule,102bool ModuleImported,103SrcMgr::CharacteristicKind FileType) override {104// File is std::nullopt if it wasn't found.105// (We have some false negatives if PP recovered e.g. <foo> -> "foo")106if (File)107return;108109// If it's a rare absolute include, we know the full path already.110if (llvm::sys::path::is_absolute(FileName)) {111Out.insert(FileName);112return;113}114115// Reconstruct the filenames that would satisfy this directive...116llvm::SmallString<256> Buf;117auto NotFoundRelativeTo = [&](DirectoryEntryRef DE) {118Buf = DE.getName();119llvm::sys::path::append(Buf, FileName);120llvm::sys::path::remove_dots(Buf, /*remove_dot_dot=*/true);121Out.insert(Buf);122};123// ...relative to the including file.124if (!IsAngled) {125if (OptionalFileEntryRef IncludingFile =126SM.getFileEntryRefForID(SM.getFileID(IncludeTok.getLocation())))127if (IncludingFile->getDir())128NotFoundRelativeTo(IncludingFile->getDir());129}130// ...relative to the search paths.131for (const auto &Dir : llvm::make_range(132IsAngled ? Search.angled_dir_begin() : Search.search_dir_begin(),133Search.search_dir_end())) {134// No support for frameworks or header maps yet.135if (Dir.isNormalDir())136NotFoundRelativeTo(*Dir.getDirRef());137}138}139};140141/// Keeps a track of files to be deleted in destructor.142class TemporaryFiles {143public:144// A static instance to be used by all clients.145static TemporaryFiles &getInstance();146147private:148// Disallow constructing the class directly.149TemporaryFiles() = default;150// Disallow copy.151TemporaryFiles(const TemporaryFiles &) = delete;152153public:154~TemporaryFiles();155156/// Adds \p File to a set of tracked files.157void addFile(StringRef File);158159/// Remove \p File from disk and from the set of tracked files.160void removeFile(StringRef File);161162private:163std::mutex Mutex;164llvm::StringSet<> Files;165};166167TemporaryFiles &TemporaryFiles::getInstance() {168static TemporaryFiles Instance;169return Instance;170}171172TemporaryFiles::~TemporaryFiles() {173std::lock_guard<std::mutex> Guard(Mutex);174for (const auto &File : Files)175llvm::sys::fs::remove(File.getKey());176}177178void TemporaryFiles::addFile(StringRef File) {179std::lock_guard<std::mutex> Guard(Mutex);180auto IsInserted = Files.insert(File).second;181(void)IsInserted;182assert(IsInserted && "File has already been added");183}184185void TemporaryFiles::removeFile(StringRef File) {186std::lock_guard<std::mutex> Guard(Mutex);187auto WasPresent = Files.erase(File);188(void)WasPresent;189assert(WasPresent && "File was not tracked");190llvm::sys::fs::remove(File);191}192193// A temp file that would be deleted on destructor call. If destructor is not194// called for any reason, the file will be deleted at static objects'195// destruction.196// An assertion will fire if two TempPCHFiles are created with the same name,197// so it's not intended to be used outside preamble-handling.198class TempPCHFile {199public:200// A main method used to construct TempPCHFile.201static std::unique_ptr<TempPCHFile> create(StringRef StoragePath) {202// FIXME: This is a hack so that we can override the preamble file during203// crash-recovery testing, which is the only case where the preamble files204// are not necessarily cleaned up.205if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"))206return std::unique_ptr<TempPCHFile>(new TempPCHFile(TmpFile));207208llvm::SmallString<128> File;209// Using the versions of createTemporaryFile() and210// createUniqueFile() with a file descriptor guarantees211// that we would never get a race condition in a multi-threaded setting212// (i.e., multiple threads getting the same temporary path).213int FD;214std::error_code EC;215if (StoragePath.empty())216EC = llvm::sys::fs::createTemporaryFile("preamble", "pch", FD, File);217else {218llvm::SmallString<128> TempPath = StoragePath;219// Use the same filename model as fs::createTemporaryFile().220llvm::sys::path::append(TempPath, "preamble-%%%%%%.pch");221namespace fs = llvm::sys::fs;222// Use the same owner-only file permissions as fs::createTemporaryFile().223EC = fs::createUniqueFile(TempPath, FD, File, fs::OF_None,224fs::owner_read | fs::owner_write);225}226if (EC)227return nullptr;228// We only needed to make sure the file exists, close the file right away.229llvm::sys::Process::SafelyCloseFileDescriptor(FD);230return std::unique_ptr<TempPCHFile>(new TempPCHFile(File.str().str()));231}232233TempPCHFile &operator=(const TempPCHFile &) = delete;234TempPCHFile(const TempPCHFile &) = delete;235~TempPCHFile() { TemporaryFiles::getInstance().removeFile(FilePath); };236237/// A path where temporary file is stored.238llvm::StringRef getFilePath() const { return FilePath; };239240private:241TempPCHFile(std::string FilePath) : FilePath(std::move(FilePath)) {242TemporaryFiles::getInstance().addFile(this->FilePath);243}244245std::string FilePath;246};247248class PrecompilePreambleAction : public ASTFrontendAction {249public:250PrecompilePreambleAction(std::shared_ptr<PCHBuffer> Buffer, bool WritePCHFile,251PreambleCallbacks &Callbacks)252: Buffer(std::move(Buffer)), WritePCHFile(WritePCHFile),253Callbacks(Callbacks) {}254255std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,256StringRef InFile) override;257258bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; }259260void setEmittedPreamblePCH(ASTWriter &Writer) {261if (FileOS) {262*FileOS << Buffer->Data;263// Make sure it hits disk now.264FileOS.reset();265}266267this->HasEmittedPreamblePCH = true;268Callbacks.AfterPCHEmitted(Writer);269}270271bool BeginSourceFileAction(CompilerInstance &CI) override {272assert(CI.getLangOpts().CompilingPCH);273return ASTFrontendAction::BeginSourceFileAction(CI);274}275276bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); }277bool hasCodeCompletionSupport() const override { return false; }278bool hasASTFileSupport() const override { return false; }279TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; }280281private:282friend class PrecompilePreambleConsumer;283284bool HasEmittedPreamblePCH = false;285std::shared_ptr<PCHBuffer> Buffer;286bool WritePCHFile; // otherwise the PCH is written into the PCHBuffer only.287std::unique_ptr<llvm::raw_pwrite_stream> FileOS; // null if in-memory288PreambleCallbacks &Callbacks;289};290291class PrecompilePreambleConsumer : public PCHGenerator {292public:293PrecompilePreambleConsumer(PrecompilePreambleAction &Action, Preprocessor &PP,294InMemoryModuleCache &ModuleCache,295StringRef isysroot,296std::shared_ptr<PCHBuffer> Buffer)297: PCHGenerator(PP, ModuleCache, "", isysroot, std::move(Buffer),298ArrayRef<std::shared_ptr<ModuleFileExtension>>(),299/*AllowASTWithErrors=*/true),300Action(Action) {}301302bool HandleTopLevelDecl(DeclGroupRef DG) override {303Action.Callbacks.HandleTopLevelDecl(DG);304return true;305}306307void HandleTranslationUnit(ASTContext &Ctx) override {308PCHGenerator::HandleTranslationUnit(Ctx);309if (!hasEmittedPCH())310return;311Action.setEmittedPreamblePCH(getWriter());312}313314bool shouldSkipFunctionBody(Decl *D) override {315return Action.Callbacks.shouldSkipFunctionBody(D);316}317318private:319PrecompilePreambleAction &Action;320};321322std::unique_ptr<ASTConsumer>323PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,324StringRef InFile) {325std::string Sysroot;326if (!GeneratePCHAction::ComputeASTConsumerArguments(CI, Sysroot))327return nullptr;328329if (WritePCHFile) {330std::string OutputFile; // unused331FileOS = GeneratePCHAction::CreateOutputFile(CI, InFile, OutputFile);332if (!FileOS)333return nullptr;334}335336if (!CI.getFrontendOpts().RelocatablePCH)337Sysroot.clear();338339return std::make_unique<PrecompilePreambleConsumer>(340*this, CI.getPreprocessor(), CI.getModuleCache(), Sysroot, Buffer);341}342343template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {344if (!Val)345return false;346Output = std::move(*Val);347return true;348}349350} // namespace351352PreambleBounds clang::ComputePreambleBounds(const LangOptions &LangOpts,353const llvm::MemoryBufferRef &Buffer,354unsigned MaxLines) {355return Lexer::ComputePreamble(Buffer.getBuffer(), LangOpts, MaxLines);356}357358class PrecompiledPreamble::PCHStorage {359public:360static std::unique_ptr<PCHStorage> file(std::unique_ptr<TempPCHFile> File) {361assert(File);362std::unique_ptr<PCHStorage> S(new PCHStorage());363S->File = std::move(File);364return S;365}366static std::unique_ptr<PCHStorage> inMemory(std::shared_ptr<PCHBuffer> Buf) {367std::unique_ptr<PCHStorage> S(new PCHStorage());368S->Memory = std::move(Buf);369return S;370}371372enum class Kind { InMemory, TempFile };373Kind getKind() const {374if (Memory)375return Kind::InMemory;376if (File)377return Kind::TempFile;378llvm_unreachable("Neither Memory nor File?");379}380llvm::StringRef filePath() const {381assert(getKind() == Kind::TempFile);382return File->getFilePath();383}384llvm::StringRef memoryContents() const {385assert(getKind() == Kind::InMemory);386return StringRef(Memory->Data.data(), Memory->Data.size());387}388389// 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.392void shrink() {393if (!Memory)394return;395Memory->Data = decltype(Memory->Data)(Memory->Data);396}397398private:399PCHStorage() = default;400PCHStorage(const PCHStorage &) = delete;401PCHStorage &operator=(const PCHStorage &) = delete;402403std::shared_ptr<PCHBuffer> Memory;404std::unique_ptr<TempPCHFile> File;405};406407PrecompiledPreamble::~PrecompiledPreamble() = default;408PrecompiledPreamble::PrecompiledPreamble(PrecompiledPreamble &&) = default;409PrecompiledPreamble &410PrecompiledPreamble::operator=(PrecompiledPreamble &&) = default;411412llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(413const CompilerInvocation &Invocation,414const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,415DiagnosticsEngine &Diagnostics,416IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,417std::shared_ptr<PCHContainerOperations> PCHContainerOps, bool StoreInMemory,418StringRef StoragePath, PreambleCallbacks &Callbacks) {419assert(VFS && "VFS is null");420421auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);422FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts();423PreprocessorOptions &PreprocessorOpts =424PreambleInvocation->getPreprocessorOpts();425426std::shared_ptr<PCHBuffer> Buffer = std::make_shared<PCHBuffer>();427std::unique_ptr<PCHStorage> Storage;428if (StoreInMemory) {429Storage = PCHStorage::inMemory(Buffer);430} else {431// Create a temporary file for the precompiled preamble. In rare432// circumstances, this can fail.433std::unique_ptr<TempPCHFile> PreamblePCHFile =434TempPCHFile::create(StoragePath);435if (!PreamblePCHFile)436return BuildPreambleError::CouldntCreateTempFile;437Storage = PCHStorage::file(std::move(PreamblePCHFile));438}439440// Save the preamble text for later; we'll need to compare against it for441// subsequent reparses.442std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(),443MainFileBuffer->getBufferStart() +444Bounds.Size);445bool PreambleEndsAtStartOfLine = Bounds.PreambleEndsAtStartOfLine;446447// Tell the compiler invocation to generate a temporary precompiled header.448FrontendOpts.ProgramAction = frontend::GeneratePCH;449FrontendOpts.OutputFile = std::string(450StoreInMemory ? getInMemoryPreamblePath() : Storage->filePath());451PreprocessorOpts.PrecompiledPreambleBytes.first = 0;452PreprocessorOpts.PrecompiledPreambleBytes.second = false;453// Inform preprocessor to record conditional stack when building the preamble.454PreprocessorOpts.GeneratePreamble = true;455456// Create the compiler instance to use for building the precompiled preamble.457std::unique_ptr<CompilerInstance> Clang(458new CompilerInstance(std::move(PCHContainerOps)));459460// Recover resources if we crash before exiting this method.461llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(462Clang.get());463464Clang->setInvocation(std::move(PreambleInvocation));465Clang->setDiagnostics(&Diagnostics);466467// Create the target instance.468if (!Clang->createTarget())469return BuildPreambleError::CouldntCreateTargetInfo;470471if (Clang->getFrontendOpts().Inputs.size() != 1 ||472Clang->getFrontendOpts().Inputs[0].getKind().getFormat() !=473InputKind::Source ||474Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() ==475Language::LLVM_IR) {476return BuildPreambleError::BadInputs;477}478479// Clear out old caches and data.480Diagnostics.Reset();481ProcessWarningOptions(Diagnostics, Clang->getDiagnosticOpts());482483VFS =484createVFSFromCompilerInvocation(Clang->getInvocation(), Diagnostics, VFS);485486// Create a file manager object to provide access to and cache the filesystem.487Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS));488489// Create the source manager.490Clang->setSourceManager(491new SourceManager(Diagnostics, Clang->getFileManager()));492493auto PreambleDepCollector = std::make_shared<PreambleDependencyCollector>();494Clang->addDependencyCollector(PreambleDepCollector);495496Clang->getLangOpts().CompilingPCH = true;497498// Remap the main source file to the preamble buffer.499StringRef MainFilePath = FrontendOpts.Inputs[0].getFile();500auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy(501MainFileBuffer->getBuffer().slice(0, Bounds.Size), MainFilePath);502if (PreprocessorOpts.RetainRemappedFileBuffers) {503// MainFileBuffer will be deleted by unique_ptr after leaving the method.504PreprocessorOpts.addRemappedFile(MainFilePath, PreambleInputBuffer.get());505} else {506// In that case, remapped buffer will be deleted by CompilerInstance on507// BeginSourceFile, so we call release() to avoid double deletion.508PreprocessorOpts.addRemappedFile(MainFilePath,509PreambleInputBuffer.release());510}511512auto Act = std::make_unique<PrecompilePreambleAction>(513std::move(Buffer),514/*WritePCHFile=*/Storage->getKind() == PCHStorage::Kind::TempFile,515Callbacks);516if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0]))517return BuildPreambleError::BeginSourceFileFailed;518519// Performed after BeginSourceFile to ensure Clang->Preprocessor can be520// referenced in the callback.521Callbacks.BeforeExecute(*Clang);522523std::unique_ptr<PPCallbacks> DelegatedPPCallbacks =524Callbacks.createPPCallbacks();525if (DelegatedPPCallbacks)526Clang->getPreprocessor().addPPCallbacks(std::move(DelegatedPPCallbacks));527if (auto CommentHandler = Callbacks.getCommentHandler())528Clang->getPreprocessor().addCommentHandler(CommentHandler);529llvm::StringSet<> MissingFiles;530Clang->getPreprocessor().addPPCallbacks(531std::make_unique<MissingFileCollector>(532MissingFiles, Clang->getPreprocessor().getHeaderSearchInfo(),533Clang->getSourceManager()));534535if (llvm::Error Err = Act->Execute())536return errorToErrorCode(std::move(Err));537538// Run the callbacks.539Callbacks.AfterExecute(*Clang);540541Act->EndSourceFile();542543if (!Act->hasEmittedPreamblePCH())544return BuildPreambleError::CouldntEmitPCH;545Act.reset(); // Frees the PCH buffer, unless Storage keeps it in memory.546547// Keep track of all of the files that the source manager knows about,548// so we can verify whether they have changed or not.549llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble;550551SourceManager &SourceMgr = Clang->getSourceManager();552for (auto &Filename : PreambleDepCollector->getDependencies()) {553auto MaybeFile = Clang->getFileManager().getOptionalFileRef(Filename);554if (!MaybeFile ||555MaybeFile == SourceMgr.getFileEntryRefForID(SourceMgr.getMainFileID()))556continue;557auto File = *MaybeFile;558if (time_t ModTime = File.getModificationTime()) {559FilesInPreamble[File.getName()] =560PrecompiledPreamble::PreambleFileHash::createForFile(File.getSize(),561ModTime);562} else {563llvm::MemoryBufferRef Buffer =564SourceMgr.getMemoryBufferForFileOrFake(File);565FilesInPreamble[File.getName()] =566PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer);567}568}569570// Shrinking the storage requires extra temporary memory.571// Destroying clang first reduces peak memory usage.572CICleanup.unregister();573Clang.reset();574Storage->shrink();575return PrecompiledPreamble(576std::move(Storage), std::move(PreambleBytes), PreambleEndsAtStartOfLine,577std::move(FilesInPreamble), std::move(MissingFiles));578}579580PreambleBounds PrecompiledPreamble::getBounds() const {581return PreambleBounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);582}583584std::size_t PrecompiledPreamble::getSize() const {585switch (Storage->getKind()) {586case PCHStorage::Kind::InMemory:587return Storage->memoryContents().size();588case PCHStorage::Kind::TempFile: {589uint64_t Result;590if (llvm::sys::fs::file_size(Storage->filePath(), Result))591return 0;592593assert(Result <= std::numeric_limits<std::size_t>::max() &&594"file size did not fit into size_t");595return Result;596}597}598llvm_unreachable("Unhandled storage kind");599}600601bool PrecompiledPreamble::CanReuse(const CompilerInvocation &Invocation,602const llvm::MemoryBufferRef &MainFileBuffer,603PreambleBounds Bounds,604llvm::vfs::FileSystem &VFS) const {605606assert(607Bounds.Size <= MainFileBuffer.getBufferSize() &&608"Buffer is too large. Bounds were calculated from a different buffer?");609610auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);611PreprocessorOptions &PreprocessorOpts =612PreambleInvocation->getPreprocessorOpts();613614// We've previously computed a preamble. Check whether we have the same615// preamble now that we did before, and that there's enough space in616// the main-file buffer within the precompiled preamble to fit the617// new main file.618if (PreambleBytes.size() != Bounds.Size ||619PreambleEndsAtStartOfLine != Bounds.PreambleEndsAtStartOfLine ||620!std::equal(PreambleBytes.begin(), PreambleBytes.end(),621MainFileBuffer.getBuffer().begin()))622return false;623// The preamble has not changed. We may be able to re-use the precompiled624// preamble.625626// Check that none of the files used by the preamble have changed.627// First, make a record of those files that have been overridden via628// remapping or unsaved_files.629std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles;630llvm::StringSet<> OverriddenAbsPaths; // Either by buffers or files.631for (const auto &R : PreprocessorOpts.RemappedFiles) {632llvm::vfs::Status Status;633if (!moveOnNoError(VFS.status(R.second), Status)) {634// If we can't stat the file we're remapping to, assume that something635// horrible happened.636return false;637}638// If a mapped file was previously missing, then it has changed.639llvm::SmallString<128> MappedPath(R.first);640if (!VFS.makeAbsolute(MappedPath))641OverriddenAbsPaths.insert(MappedPath);642643OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile(644Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime()));645}646647// OverridenFileBuffers tracks only the files not found in VFS.648llvm::StringMap<PreambleFileHash> OverridenFileBuffers;649for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {650const PrecompiledPreamble::PreambleFileHash PreambleHash =651PreambleFileHash::createForMemoryBuffer(RB.second->getMemBufferRef());652llvm::vfs::Status Status;653if (moveOnNoError(VFS.status(RB.first), Status))654OverriddenFiles[Status.getUniqueID()] = PreambleHash;655else656OverridenFileBuffers[RB.first] = PreambleHash;657658llvm::SmallString<128> MappedPath(RB.first);659if (!VFS.makeAbsolute(MappedPath))660OverriddenAbsPaths.insert(MappedPath);661}662663// Check whether anything has changed.664for (const auto &F : FilesInPreamble) {665auto OverridenFileBuffer = OverridenFileBuffers.find(F.first());666if (OverridenFileBuffer != OverridenFileBuffers.end()) {667// The file's buffer was remapped and the file was not found in VFS.668// Check whether it matches up with the previous mapping.669if (OverridenFileBuffer->second != F.second)670return false;671continue;672}673674llvm::vfs::Status Status;675if (!moveOnNoError(VFS.status(F.first()), Status)) {676// If the file's buffer is not remapped and we can't stat it,677// assume that something horrible happened.678return false;679}680681std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden =682OverriddenFiles.find(Status.getUniqueID());683if (Overridden != OverriddenFiles.end()) {684// This file was remapped; check whether the newly-mapped file685// matches up with the previous mapping.686if (Overridden->second != F.second)687return false;688continue;689}690691// Neither the file's buffer nor the file itself was remapped;692// check whether it has changed on disk.693if (Status.getSize() != uint64_t(F.second.Size) ||694llvm::sys::toTimeT(Status.getLastModificationTime()) !=695F.second.ModTime)696return false;697}698for (const auto &F : MissingFiles) {699// A missing file may be "provided" by an override buffer or file.700if (OverriddenAbsPaths.count(F.getKey()))701return false;702// If a file previously recorded as missing exists as a regular file, then703// consider the preamble out-of-date.704if (auto Status = VFS.status(F.getKey())) {705if (Status->isRegularFile())706return false;707}708}709return true;710}711712void PrecompiledPreamble::AddImplicitPreamble(713CompilerInvocation &CI, IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,714llvm::MemoryBuffer *MainFileBuffer) const {715PreambleBounds Bounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);716configurePreamble(Bounds, CI, VFS, MainFileBuffer);717}718719void PrecompiledPreamble::OverridePreamble(720CompilerInvocation &CI, IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,721llvm::MemoryBuffer *MainFileBuffer) const {722auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *MainFileBuffer, 0);723configurePreamble(Bounds, CI, VFS, MainFileBuffer);724}725726PrecompiledPreamble::PrecompiledPreamble(727std::unique_ptr<PCHStorage> Storage, std::vector<char> PreambleBytes,728bool PreambleEndsAtStartOfLine,729llvm::StringMap<PreambleFileHash> FilesInPreamble,730llvm::StringSet<> MissingFiles)731: Storage(std::move(Storage)), FilesInPreamble(std::move(FilesInPreamble)),732MissingFiles(std::move(MissingFiles)),733PreambleBytes(std::move(PreambleBytes)),734PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {735assert(this->Storage != nullptr);736}737738PrecompiledPreamble::PreambleFileHash739PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size,740time_t ModTime) {741PreambleFileHash Result;742Result.Size = Size;743Result.ModTime = ModTime;744Result.MD5 = {};745return Result;746}747748PrecompiledPreamble::PreambleFileHash749PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(750const llvm::MemoryBufferRef &Buffer) {751PreambleFileHash Result;752Result.Size = Buffer.getBufferSize();753Result.ModTime = 0;754755llvm::MD5 MD5Ctx;756MD5Ctx.update(Buffer.getBuffer().data());757MD5Ctx.final(Result.MD5);758759return Result;760}761762void PrecompiledPreamble::configurePreamble(763PreambleBounds Bounds, CompilerInvocation &CI,764IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,765llvm::MemoryBuffer *MainFileBuffer) const {766assert(VFS);767768auto &PreprocessorOpts = CI.getPreprocessorOpts();769770// Remap main file to point to MainFileBuffer.771auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile();772PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);773774// Configure ImpicitPCHInclude.775PreprocessorOpts.PrecompiledPreambleBytes.first = Bounds.Size;776PreprocessorOpts.PrecompiledPreambleBytes.second =777Bounds.PreambleEndsAtStartOfLine;778PreprocessorOpts.DisablePCHOrModuleValidation =779DisableValidationForModuleKind::PCH;780781// Don't bother generating the long version of the predefines buffer.782// The preamble is going to overwrite it anyway.783PreprocessorOpts.UsePredefines = false;784785setupPreambleStorage(*Storage, PreprocessorOpts, VFS);786}787788void PrecompiledPreamble::setupPreambleStorage(789const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts,790IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS) {791if (Storage.getKind() == PCHStorage::Kind::TempFile) {792llvm::StringRef PCHPath = Storage.filePath();793PreprocessorOpts.ImplicitPCHInclude = PCHPath.str();794795// Make sure we can access the PCH file even if we're using a VFS796IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS =797llvm::vfs::getRealFileSystem();798if (VFS == RealFS || VFS->exists(PCHPath))799return;800auto Buf = RealFS->getBufferForFile(PCHPath);801if (!Buf) {802// We can't read the file even from RealFS, this is clearly an error,803// but we'll just leave the current VFS as is and let clang's code804// figure out what to do with missing PCH.805return;806}807808// We have a slight inconsistency here -- we're using the VFS to809// read files, but the PCH was generated in the real file system.810VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(*Buf), VFS);811} else {812assert(Storage.getKind() == PCHStorage::Kind::InMemory);813// For in-memory preamble, we have to provide a VFS overlay that makes it814// accessible.815StringRef PCHPath = getInMemoryPreamblePath();816PreprocessorOpts.ImplicitPCHInclude = std::string(PCHPath);817818auto Buf = llvm::MemoryBuffer::getMemBuffer(819Storage.memoryContents(), PCHPath, /*RequiresNullTerminator=*/false);820VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(Buf), VFS);821}822}823824void PreambleCallbacks::BeforeExecute(CompilerInstance &CI) {}825void PreambleCallbacks::AfterExecute(CompilerInstance &CI) {}826void PreambleCallbacks::AfterPCHEmitted(ASTWriter &Writer) {}827void PreambleCallbacks::HandleTopLevelDecl(DeclGroupRef DG) {}828std::unique_ptr<PPCallbacks> PreambleCallbacks::createPPCallbacks() {829return nullptr;830}831CommentHandler *PreambleCallbacks::getCommentHandler() { return nullptr; }832833static llvm::ManagedStatic<BuildPreambleErrorCategory> BuildPreambleErrCategory;834835std::error_code clang::make_error_code(BuildPreambleError Error) {836return std::error_code(static_cast<int>(Error), *BuildPreambleErrCategory);837}838839const char *BuildPreambleErrorCategory::name() const noexcept {840return "build-preamble.error";841}842843std::string BuildPreambleErrorCategory::message(int condition) const {844switch (static_cast<BuildPreambleError>(condition)) {845case BuildPreambleError::CouldntCreateTempFile:846return "Could not create temporary file for PCH";847case BuildPreambleError::CouldntCreateTargetInfo:848return "CreateTargetInfo() return null";849case BuildPreambleError::BeginSourceFileFailed:850return "BeginSourceFile() return an error";851case BuildPreambleError::CouldntEmitPCH:852return "Could not emit PCH";853case BuildPreambleError::BadInputs:854return "Command line arguments must contain exactly one source file";855}856llvm_unreachable("unexpected BuildPreambleError");857}858859860