diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -12,6 +12,7 @@ #include "clang/AST/DeclGroup.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LangStandard.h" +#include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -26,6 +27,7 @@ #include "clang/Parse/ParseAST.h" #include "clang/Serialization/ASTDeserializationListener.h" #include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" #include "clang/Serialization/GlobalModuleIndex.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/BuryPointer.h" @@ -171,17 +173,81 @@ if (!FoundAllPlugins) return nullptr; - // If there are no registered plugins we don't need to wrap the consumer - if (FrontendPluginRegistry::begin() == FrontendPluginRegistry::end()) + bool NoPlugs = FrontendPluginRegistry::begin() == FrontendPluginRegistry::end(); + + InputKind IK = getCurrentFileKind(); + + // Do we want to emit a CMI on demand if the source contains an export. + // We do this if : C++20 modules, if the input is source, and we do not + // stop at the Preprocessor. + bool EmitCMI = CI.getLangOpts().CPlusPlusModules && + IK.getFormat() == InputKind::Format::Source && + !this->usesPreprocessorOnly() && + (CI.getFrontendOpts().ProgramAction == frontend::EmitObj || + CI.getFrontendOpts().ProgramAction == frontend::EmitAssembly || + CI.getFrontendOpts().ProgramAction == frontend::EmitBC || + CI.getFrontendOpts().ProgramAction == frontend::EmitLLVM || + CI.getFrontendOpts().ProgramAction == frontend::EmitLLVMOnly || + CI.getFrontendOpts().ProgramAction == frontend::EmitCodeGenOnly); + + // If there are no registered plugins and we do not need to emit a CMI, we + // do not need to wrap the consumer in a MultiplexConsumer. + if (!EmitCMI && NoPlugs) return Consumer; // If this is a code completion run, avoid invoking the plugin consumers + // ??? : is there a case that we want code completion and CMI emission? if (CI.hasCodeCompletionConsumer()) return Consumer; + // List of AST consumers for this source. + std::vector> Consumers; + + // First, add a pair of consumers that will write the AST as a CMI for this + // module. ??? : Should any plugins that precede the main consumer also be + // run before this. + if (EmitCMI) { + // Make a presumed output filename (this would be overwritten by the one + // derived from the module to CMI mapping determined from any export + // statement). We do not open this on the output stream, but provide it + // as a fallback. + std::string XOut + = llvm::sys::path::parent_path(CI.getFrontendOpts().OutputFile).str(); + std::string XIn = llvm::sys::path::filename(InFile).str(); + if (!XOut.empty()) { + XOut += llvm::sys::path::get_separator(); + XOut += XIn; + } else + XOut = XIn; + + SmallString<128> Path(XOut); + llvm::sys::path::replace_extension(Path, "pcm"); + XOut = std::string(Path.str()); + + std::unique_ptr OS; + std::string Sysroot; + auto Buffer = std::make_shared(); + + // Add a job to build the CMI from the AST. + // ??? : change the CTOR flags to note that this is a CXX20 module? + Consumers.push_back(std::make_unique( + CI.getPreprocessor(), CI.getModuleCache(), XOut, Sysroot, Buffer, + CI.getFrontendOpts().ModuleFileExtensions, + /*AllowASTWithErrors=*/false, + /*IncludeTimestamps=*/false, + /*ShouldCacheASTInMemory=*/false, + /*IsForCMI=*/true)); + + // This writes the CMI (if one is needed), but does not open the output + // file unless/until it is required. + Consumers.push_back(CI.getPCHContainerWriter() + .CreatePCHDeferredContainerGenerator( + CI, std::string(InFile), XOut, std::move(OS), Buffer)); + } + // Collect the list of plugins that go before the main action (in Consumers) // or after it (in AfterConsumers) - std::vector> Consumers; + std::vector> AfterConsumers; for (const FrontendPluginRegistry::entry &Plugin : FrontendPluginRegistry::entries()) {