1 //===--- GeneratePCH.cpp - Sema Consumer for PCH Generation -----*- 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 PCHGenerator, which as a SemaConsumer that generates 10 // a PCH file. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/AST/ASTContext.h" 15 #include "clang/Frontend/FrontendDiagnostic.h" 16 #include "clang/Lex/HeaderSearch.h" 17 #include "clang/Lex/HeaderSearchOptions.h" 18 #include "clang/Lex/Preprocessor.h" 19 #include "clang/Sema/SemaConsumer.h" 20 #include "clang/Serialization/ASTWriter.h" 21 #include "llvm/Bitstream/BitstreamWriter.h" 22 23 using namespace clang; 24 25 PCHGenerator::PCHGenerator( 26 Preprocessor &PP, InMemoryModuleCache &ModuleCache, StringRef OutputFile, 27 StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer, 28 ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, 29 bool AllowASTWithErrors, bool IncludeTimestamps, 30 bool BuildingImplicitModule, bool ShouldCacheASTInMemory, 31 bool GeneratingReducedBMI) 32 : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), 33 SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data), 34 Writer(Stream, this->Buffer->Data, ModuleCache, Extensions, 35 IncludeTimestamps, BuildingImplicitModule, GeneratingReducedBMI), 36 AllowASTWithErrors(AllowASTWithErrors), 37 ShouldCacheASTInMemory(ShouldCacheASTInMemory) { 38 this->Buffer->IsComplete = false; 39 } 40 41 PCHGenerator::~PCHGenerator() { 42 } 43 44 Module *PCHGenerator::getEmittingModule(ASTContext &) { 45 Module *M = nullptr; 46 47 if (PP.getLangOpts().isCompilingModule()) { 48 M = PP.getHeaderSearchInfo().lookupModule(PP.getLangOpts().CurrentModule, 49 SourceLocation(), 50 /*AllowSearch*/ false); 51 if (!M) 52 assert(PP.getDiagnostics().hasErrorOccurred() && 53 "emitting module but current module doesn't exist"); 54 } 55 56 return M; 57 } 58 59 void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { 60 // Don't create a PCH if there were fatal failures during module loading. 61 if (PP.getModuleLoader().HadFatalFailure) 62 return; 63 64 bool hasErrors = PP.getDiagnostics().hasErrorOccurred(); 65 if (hasErrors && !AllowASTWithErrors) 66 return; 67 68 Module *Module = getEmittingModule(Ctx); 69 70 // Errors that do not prevent the PCH from being written should not cause the 71 // overall compilation to fail either. 72 if (AllowASTWithErrors) 73 PP.getDiagnostics().getClient()->clear(); 74 75 // Emit the PCH file to the Buffer. 76 assert(SemaPtr && "No Sema?"); 77 Buffer->Signature = Writer.WriteAST(*SemaPtr, OutputFile, Module, isysroot, 78 ShouldCacheASTInMemory); 79 80 Buffer->IsComplete = true; 81 } 82 83 ASTMutationListener *PCHGenerator::GetASTMutationListener() { 84 return &Writer; 85 } 86 87 ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() { 88 return &Writer; 89 } 90 91 CXX20ModulesGenerator::CXX20ModulesGenerator(Preprocessor &PP, 92 InMemoryModuleCache &ModuleCache, 93 StringRef OutputFile, 94 bool GeneratingReducedBMI) 95 : PCHGenerator( 96 PP, ModuleCache, OutputFile, llvm::StringRef(), 97 std::make_shared<PCHBuffer>(), 98 /*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(), 99 /*AllowASTWithErrors*/ false, /*IncludeTimestamps=*/false, 100 /*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false, 101 GeneratingReducedBMI) {} 102 103 Module *CXX20ModulesGenerator::getEmittingModule(ASTContext &Ctx) { 104 Module *M = Ctx.getCurrentNamedModule(); 105 assert(M && M->isNamedModuleUnit() && 106 "CXX20ModulesGenerator should only be used with C++20 Named modules."); 107 return M; 108 } 109 110 void CXX20ModulesGenerator::HandleTranslationUnit(ASTContext &Ctx) { 111 // FIMXE: We'd better to wrap such options to a new class ASTWriterOptions 112 // since this is not about searching header really. 113 HeaderSearchOptions &HSOpts = 114 getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts(); 115 HSOpts.ModulesSkipDiagnosticOptions = true; 116 HSOpts.ModulesSkipHeaderSearchPaths = true; 117 HSOpts.ModulesSkipPragmaDiagnosticMappings = true; 118 119 PCHGenerator::HandleTranslationUnit(Ctx); 120 121 if (!isComplete()) 122 return; 123 124 std::error_code EC; 125 auto OS = std::make_unique<llvm::raw_fd_ostream>(getOutputFile(), EC); 126 if (EC) { 127 getDiagnostics().Report(diag::err_fe_unable_to_open_output) 128 << getOutputFile() << EC.message() << "\n"; 129 return; 130 } 131 132 *OS << getBufferPtr()->Data; 133 OS->flush(); 134 } 135