200 lines · cpp
1//===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- 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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the ChainedIncludesSource class, which converts headers10// to chained PCHs in memory, mainly used for testing.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Basic/Builtins.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/Frontend/ASTUnit.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Frontend/TextDiagnosticPrinter.h"19#include "clang/Lex/Preprocessor.h"20#include "clang/Lex/PreprocessorOptions.h"21#include "clang/Parse/ParseAST.h"22#include "clang/Sema/MultiplexExternalSemaSource.h"23#include "clang/Serialization/ASTReader.h"24#include "clang/Serialization/ASTWriter.h"25#include "llvm/Support/MemoryBuffer.h"26 27using namespace clang;28 29namespace {30class ChainedIncludesSource : public ExternalSemaSource {31public:32 ChainedIncludesSource(std::vector<std::unique_ptr<CompilerInstance>> CIs)33 : CIs(std::move(CIs)) {}34 35protected:36 //===--------------------------------------------------------------------===//37 // ExternalASTSource interface.38 //===--------------------------------------------------------------------===//39 40 /// Return the amount of memory used by memory buffers, breaking down41 /// by heap-backed versus mmap'ed memory.42 void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override {43 for (unsigned i = 0, e = CIs.size(); i != e; ++i) {44 if (const ExternalASTSource *eSrc =45 CIs[i]->getASTContext().getExternalSource()) {46 eSrc->getMemoryBufferSizes(sizes);47 }48 }49 }50 51private:52 std::vector<std::unique_ptr<CompilerInstance>> CIs;53};54} // end anonymous namespace55 56static llvm::IntrusiveRefCntPtr<ASTReader>57createASTReader(CompilerInstance &CI, StringRef pchFile,58 SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &MemBufs,59 SmallVectorImpl<std::string> &bufNames,60 ASTDeserializationListener *deserialListener = nullptr) {61 Preprocessor &PP = CI.getPreprocessor();62 auto Reader = llvm::makeIntrusiveRefCnt<ASTReader>(63 PP, CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(),64 CI.getCodeGenOpts(),65 /*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(),66 /*isysroot=*/"", DisableValidationForModuleKind::PCH);67 for (unsigned ti = 0; ti < bufNames.size(); ++ti) {68 StringRef sr(bufNames[ti]);69 Reader->addInMemoryBuffer(sr, std::move(MemBufs[ti]));70 }71 Reader->setDeserializationListener(deserialListener);72 switch (Reader->ReadAST(pchFile, serialization::MK_PCH, SourceLocation(),73 ASTReader::ARR_None)) {74 case ASTReader::Success:75 // Set the predefines buffer as suggested by the PCH reader.76 PP.setPredefines(Reader->getSuggestedPredefines());77 return Reader;78 79 case ASTReader::Failure:80 case ASTReader::Missing:81 case ASTReader::OutOfDate:82 case ASTReader::VersionMismatch:83 case ASTReader::ConfigurationMismatch:84 case ASTReader::HadErrors:85 break;86 }87 return nullptr;88}89 90IntrusiveRefCntPtr<ExternalSemaSource>91clang::createChainedIncludesSource(CompilerInstance &CI,92 IntrusiveRefCntPtr<ASTReader> &OutReader) {93 94 std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes;95 assert(!includes.empty() && "No '-chain-include' in options!");96 97 std::vector<std::unique_ptr<CompilerInstance>> CIs;98 InputKind IK = CI.getFrontendOpts().Inputs[0].getKind();99 100 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs;101 SmallVector<std::string, 4> serialBufNames;102 103 for (unsigned i = 0, e = includes.size(); i != e; ++i) {104 bool firstInclude = (i == 0);105 std::unique_ptr<CompilerInvocation> CInvok;106 CInvok.reset(new CompilerInvocation(CI.getInvocation()));107 108 CInvok->getPreprocessorOpts().ChainedIncludes.clear();109 CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear();110 CInvok->getPreprocessorOpts().DisablePCHOrModuleValidation =111 DisableValidationForModuleKind::PCH;112 CInvok->getPreprocessorOpts().Includes.clear();113 CInvok->getPreprocessorOpts().MacroIncludes.clear();114 CInvok->getPreprocessorOpts().Macros.clear();115 116 CInvok->getFrontendOpts().Inputs.clear();117 FrontendInputFile InputFile(includes[i], IK);118 CInvok->getFrontendOpts().Inputs.push_back(InputFile);119 120 TextDiagnosticPrinter *DiagClient =121 new TextDiagnosticPrinter(llvm::errs(), CI.getDiagnosticOpts());122 auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(123 DiagnosticIDs::create(), CI.getDiagnosticOpts(), DiagClient);124 125 auto Clang = std::make_unique<CompilerInstance>(126 std::move(CInvok), CI.getPCHContainerOperations());127 Clang->createVirtualFileSystem();128 Clang->setDiagnostics(Diags);129 Clang->setTarget(TargetInfo::CreateTargetInfo(130 Clang->getDiagnostics(), Clang->getInvocation().getTargetOpts()));131 Clang->createFileManager();132 Clang->createSourceManager();133 Clang->createPreprocessor(TU_Prefix);134 Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(),135 &Clang->getPreprocessor());136 Clang->createASTContext();137 138 auto Buffer = std::make_shared<PCHBuffer>();139 ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions;140 auto consumer = std::make_unique<PCHGenerator>(141 Clang->getPreprocessor(), Clang->getModuleCache(), "-", /*isysroot=*/"",142 Buffer, Clang->getCodeGenOpts(), Extensions,143 /*AllowASTWithErrors=*/true);144 Clang->getASTContext().setASTMutationListener(145 consumer->GetASTMutationListener());146 Clang->setASTConsumer(std::move(consumer));147 Clang->createSema(TU_Prefix, nullptr);148 149 if (firstInclude) {150 Preprocessor &PP = Clang->getPreprocessor();151 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(),152 PP.getLangOpts());153 } else {154 assert(!SerialBufs.empty());155 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs;156 // TODO: Pass through the existing MemoryBuffer instances instead of157 // allocating new ones.158 for (auto &SB : SerialBufs)159 Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer()));160 std::string pchName = includes[i-1];161 llvm::raw_string_ostream os(pchName);162 os << ".pch" << i-1;163 serialBufNames.push_back(pchName);164 165 IntrusiveRefCntPtr<ASTReader> Reader;166 Reader = createASTReader(167 *Clang, pchName, Bufs, serialBufNames,168 Clang->getASTConsumer().GetASTDeserializationListener());169 if (!Reader)170 return nullptr;171 Clang->setASTReader(Reader);172 Clang->getASTContext().setExternalSource(Reader);173 }174 175 if (!Clang->InitializeSourceManager(InputFile))176 return nullptr;177 178 ParseAST(Clang->getSema());179 Clang->getDiagnosticClient().EndSourceFile();180 assert(Buffer->IsComplete && "serialization did not complete");181 auto &serialAST = Buffer->Data;182 SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy(183 StringRef(serialAST.data(), serialAST.size())));184 serialAST.clear();185 CIs.push_back(std::move(Clang));186 }187 188 assert(!SerialBufs.empty());189 std::string pchName = includes.back() + ".pch-final";190 serialBufNames.push_back(pchName);191 OutReader = createASTReader(CI, pchName, SerialBufs, serialBufNames);192 if (!OutReader)193 return nullptr;194 195 auto ChainedSrc =196 llvm::makeIntrusiveRefCnt<ChainedIncludesSource>(std::move(CIs));197 return llvm::makeIntrusiveRefCnt<MultiplexExternalSemaSource>(198 std::move(ChainedSrc), OutReader);199}200