1 //===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the ChainedIncludesSource class, which converts headers
10 // to chained PCHs in memory, mainly used for testing.
12 //===----------------------------------------------------------------------===//
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"
27 using namespace clang
;
30 class ChainedIncludesSource
: public ExternalSemaSource
{
32 ChainedIncludesSource(std::vector
<std::unique_ptr
<CompilerInstance
>> CIs
)
33 : CIs(std::move(CIs
)) {}
36 //===--------------------------------------------------------------------===//
37 // ExternalASTSource interface.
38 //===--------------------------------------------------------------------===//
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
);
52 std::vector
<std::unique_ptr
<CompilerInstance
>> CIs
;
54 } // end anonymous namespace
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(),
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
]));
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();
79 case ASTReader::Failure
:
80 case ASTReader::Missing
:
81 case ASTReader::OutOfDate
:
82 case ASTReader::VersionMismatch
:
83 case ASTReader::ConfigurationMismatch
:
84 case ASTReader::HadErrors
:
90 IntrusiveRefCntPtr
<ExternalSemaSource
> clang::createChainedIncludesSource(
91 CompilerInstance
&CI
, IntrusiveRefCntPtr
<ExternalSemaSource
> &Reader
) {
93 std::vector
<std::string
> &includes
= CI
.getPreprocessorOpts().ChainedIncludes
;
94 assert(!includes
.empty() && "No '-chain-include' in options!");
96 std::vector
<std::unique_ptr
<CompilerInstance
>> CIs
;
97 InputKind IK
= CI
.getFrontendOpts().Inputs
[0].getKind();
99 SmallVector
<std::unique_ptr
<llvm::MemoryBuffer
>, 4> SerialBufs
;
100 SmallVector
<std::string
, 4> serialBufNames
;
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()));
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();
115 CInvok
->getFrontendOpts().Inputs
.clear();
116 FrontendInputFile
InputFile(includes
[i
], IK
);
117 CInvok
->getFrontendOpts().Inputs
.push_back(InputFile
);
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
));
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();
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);
149 Preprocessor
&PP
= Clang
->getPreprocessor();
150 PP
.getBuiltinInfo().initializeBuiltins(PP
.getIdentifierTable(),
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
);
162 serialBufNames
.push_back(os
.str());
164 IntrusiveRefCntPtr
<ASTReader
> Reader
;
165 Reader
= createASTReader(
166 *Clang
, pchName
, Bufs
, serialBufNames
,
167 Clang
->getASTConsumer().GetASTDeserializationListener());
170 Clang
->setASTReader(Reader
);
171 Clang
->getASTContext().setExternalSource(Reader
);
174 if (!Clang
->InitializeSourceManager(InputFile
))
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())));
184 CIs
.push_back(std::move(Clang
));
187 assert(!SerialBufs
.empty());
188 std::string pchName
= includes
.back() + ".pch-final";
189 serialBufNames
.push_back(pchName
);
190 Reader
= createASTReader(CI
, pchName
, SerialBufs
, serialBufNames
);
195 llvm::makeIntrusiveRefCnt
<ChainedIncludesSource
>(std::move(CIs
));
196 return llvm::makeIntrusiveRefCnt
<MultiplexExternalSemaSource
>(
197 ChainedSrc
.get(), Reader
.get());