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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the ChainedIncludesSource class, which converts headers 10 // 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 27 using namespace clang; 28 29 namespace { 30 class ChainedIncludesSource : public ExternalSemaSource { 31 public: 32 ChainedIncludesSource(std::vector<std::unique_ptr<CompilerInstance>> CIs) 33 : CIs(std::move(CIs)) {} 34 35 protected: 36 //===--------------------------------------------------------------------===// 37 // ExternalASTSource interface. 38 //===--------------------------------------------------------------------===// 39 40 /// Return the amount of memory used by memory buffers, breaking down 41 /// 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 51 private: 52 std::vector<std::unique_ptr<CompilerInstance>> CIs; 53 }; 54 } // end anonymous namespace 55 56 static ASTReader * 57 createASTReader(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 std::unique_ptr<ASTReader> Reader; 63 Reader.reset(new ASTReader( 64 PP, CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(), 65 /*Extensions=*/{}, 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.release(); 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 90 IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource( 91 CompilerInstance &CI, IntrusiveRefCntPtr<ExternalSemaSource> &Reader) { 92 93 std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; 94 assert(!includes.empty() && "No '-chain-include' in options!"); 95 96 std::vector<std::unique_ptr<CompilerInstance>> CIs; 97 InputKind IK = CI.getFrontendOpts().Inputs[0].getKind(); 98 99 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs; 100 SmallVector<std::string, 4> serialBufNames; 101 102 for (unsigned i = 0, e = includes.size(); i != e; ++i) { 103 bool firstInclude = (i == 0); 104 std::unique_ptr<CompilerInvocation> CInvok; 105 CInvok.reset(new CompilerInvocation(CI.getInvocation())); 106 107 CInvok->getPreprocessorOpts().ChainedIncludes.clear(); 108 CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); 109 CInvok->getPreprocessorOpts().DisablePCHOrModuleValidation = 110 DisableValidationForModuleKind::PCH; 111 CInvok->getPreprocessorOpts().Includes.clear(); 112 CInvok->getPreprocessorOpts().MacroIncludes.clear(); 113 CInvok->getPreprocessorOpts().Macros.clear(); 114 115 CInvok->getFrontendOpts().Inputs.clear(); 116 FrontendInputFile InputFile(includes[i], IK); 117 CInvok->getFrontendOpts().Inputs.push_back(InputFile); 118 119 TextDiagnosticPrinter *DiagClient = 120 new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions()); 121 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 122 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 123 new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), DiagClient)); 124 125 std::unique_ptr<CompilerInstance> Clang( 126 new CompilerInstance(CI.getPCHContainerOperations())); 127 Clang->setInvocation(std::move(CInvok)); 128 Clang->setDiagnostics(Diags.get()); 129 Clang->setTarget(TargetInfo::CreateTargetInfo( 130 Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); 131 Clang->createFileManager(); 132 Clang->createSourceManager(Clang->getFileManager()); 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, Extensions, /*AllowASTWithErrors=*/true); 143 Clang->getASTContext().setASTMutationListener( 144 consumer->GetASTMutationListener()); 145 Clang->setASTConsumer(std::move(consumer)); 146 Clang->createSema(TU_Prefix, nullptr); 147 148 if (firstInclude) { 149 Preprocessor &PP = Clang->getPreprocessor(); 150 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), 151 PP.getLangOpts()); 152 } else { 153 assert(!SerialBufs.empty()); 154 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs; 155 // TODO: Pass through the existing MemoryBuffer instances instead of 156 // allocating new ones. 157 for (auto &SB : SerialBufs) 158 Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer())); 159 std::string pchName = includes[i-1]; 160 llvm::raw_string_ostream os(pchName); 161 os << ".pch" << i-1; 162 serialBufNames.push_back(os.str()); 163 164 IntrusiveRefCntPtr<ASTReader> Reader; 165 Reader = createASTReader( 166 *Clang, pchName, Bufs, serialBufNames, 167 Clang->getASTConsumer().GetASTDeserializationListener()); 168 if (!Reader) 169 return nullptr; 170 Clang->setASTReader(Reader); 171 Clang->getASTContext().setExternalSource(Reader); 172 } 173 174 if (!Clang->InitializeSourceManager(InputFile)) 175 return nullptr; 176 177 ParseAST(Clang->getSema()); 178 Clang->getDiagnosticClient().EndSourceFile(); 179 assert(Buffer->IsComplete && "serialization did not complete"); 180 auto &serialAST = Buffer->Data; 181 SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy( 182 StringRef(serialAST.data(), serialAST.size()))); 183 serialAST.clear(); 184 CIs.push_back(std::move(Clang)); 185 } 186 187 assert(!SerialBufs.empty()); 188 std::string pchName = includes.back() + ".pch-final"; 189 serialBufNames.push_back(pchName); 190 Reader = createASTReader(CI, pchName, SerialBufs, serialBufNames); 191 if (!Reader) 192 return nullptr; 193 194 auto ChainedSrc = 195 llvm::makeIntrusiveRefCnt<ChainedIncludesSource>(std::move(CIs)); 196 return llvm::makeIntrusiveRefCnt<MultiplexExternalSemaSource>( 197 ChainedSrc.get(), Reader.get()); 198 } 199