diff --git a/cmake/modules/RootNewMacros.cmake b/cmake/modules/RootNewMacros.cmake index 4b1a5090ef5e7df148b0391dd3be70593f9cd7c1..03f4838e6a7ada933f2cfecb48f303fcd6d927f2 100644 --- a/cmake/modules/RootNewMacros.cmake +++ b/cmake/modules/RootNewMacros.cmake @@ -358,24 +358,27 @@ function(ROOT_GENERATE_DICTIONARY dictionary) #---Set the library output directory----------------------- ROOT_GET_LIBRARY_OUTPUT_DIR(library_output_dir) if(ARG_MODULE) + set(cpp_module) set(library_name ${libprefix}${ARG_MODULE}${libsuffix}) + set(newargs -s ${library_output_dir}/${library_name}) + set(master_pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_rdict.pcm) if(ARG_MULTIDICT) - set(newargs -s ${library_output_dir}/${library_name} -multiDict) + set(newargs ${newargs} -multiDict) set(pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_${dictionary}_rdict.pcm) set(rootmap_name ${library_output_dir}/${libprefix}${deduced_arg_module}.rootmap) else() - set(newargs -s ${library_output_dir}/${library_name}) - set(pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_rdict.pcm) - set(rootmap_name ${library_output_dir}/${libprefix}${ARG_MODULE}.rootmap) set(cpp_module ${ARG_MODULE}) - if(runtime_cxxmodules) - set(cpp_module_file ${library_output_dir}/${cpp_module}.pcm) + set(pcm_name ${master_pcm_name}) + set(rootmap_name ${library_output_dir}/${libprefix}${ARG_MODULE}.rootmap) + endif(ARG_MULTIDICT) + + if(runtime_cxxmodules AND cpp_module) + set(cpp_module_file ${library_output_dir}/${cpp_module}.pcm) if (APPLE) if (${cpp_module} MATCHES "(GCocoa|GQuartz)") set(cpp_module_file) endif() endif(APPLE) - endif() endif() else() set(library_name ${libprefix}${deduced_arg_module}${libsuffix}) @@ -445,6 +448,14 @@ function(ROOT_GENERATE_DICTIONARY dictionary) #---roottest compability add_custom_target(${dictname} DEPENDS ${dictionary}.cxx ${pcm_name} ${rootmap_name} ${cpp_module_file}) + if (runtime_cxxmodules AND ARG_MULTIDICT) + set (main_pcm_target "G__${ARG_MODULE}") + if (NOT TARGET ${main_pcm_target}) + message(FATAL_ERROR "Target ${main_pcm_target} not found! Please move ${main_pcm_target} before specifying MULTIDICT.") + endif() + add_dependencies(${main_pcm_target} ${dictname}) + endif() + if(NOT ARG_NOINSTALL AND NOT CMAKE_ROOTTEST_DICT AND DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) set_property(GLOBAL APPEND PROPERTY ROOT_DICTIONARY_TARGETS ${dictname}) set_property(GLOBAL APPEND PROPERTY ROOT_DICTIONARY_FILES ${CMAKE_CURRENT_BINARY_DIR}/${dictionary}.cxx) diff --git a/core/dictgen/src/LinkdefReader.cxx b/core/dictgen/src/LinkdefReader.cxx index 1d389a06ae3079a11c38d32b5855b14413a545fe..49f2ece2f37b0dfe83c5e37701c61d24d7037ae2 100644 --- a/core/dictgen/src/LinkdefReader.cxx +++ b/core/dictgen/src/LinkdefReader.cxx @@ -1014,7 +1014,8 @@ bool LinkdefReader::Parse(SelectionRules &sr, llvm::StringRef code, const std::v // Extract all #pragmas std::unique_ptr<llvm::MemoryBuffer> memBuf = llvm::MemoryBuffer::getMemBuffer(code, "CLING #pragma extraction"); clang::CompilerInstance *pragmaCI = cling::CIFactory::createCI(std::move(memBuf), parserArgsC.size(), - &parserArgsC[0], llvmdir, nullptr, true /*OnlyLex*/); + &parserArgsC[0], llvmdir, nullptr /*Consumer*/, + {} /*ModuleFileExtension*/, true /*OnlyLex*/); clang::Preprocessor &PP = pragmaCI->getPreprocessor(); clang::DiagnosticConsumer &DClient = pragmaCI->getDiagnosticClient(); diff --git a/core/metacling/src/CMakeLists.txt b/core/metacling/src/CMakeLists.txt index 97a395136e2775b6f762f2b9071dfbae675dc004..c6015d49ceb696b8a58d5de08a0ca2fb0bbde445 100644 --- a/core/metacling/src/CMakeLists.txt +++ b/core/metacling/src/CMakeLists.txt @@ -21,8 +21,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLING_CXXFLAGS}") if(MSVC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingCallbacks.cxx COMPILE_FLAGS -GR-) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingRdictModuleFileExtension.cxx COMPILE_FLAGS -GR-) else() set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingCallbacks.cxx COMPILE_FLAGS -fno-rtti) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingRdictModuleFileExtension.cxx COMPILE_FLAGS -fno-rtti) endif() # This to avoid warning coming from llvm/src/tools/clang/include/clang/Sema/Lookup.h:441 @@ -47,6 +49,7 @@ ROOT_OBJECT_LIBRARY(MetaCling TClingDeclInfo.cxx TClingMethodArgInfo.cxx TClingMethodInfo.cxx + TClingRdictModuleFileExtension.cxx TClingTypedefInfo.cxx TClingTypeInfo.cxx TClingValue.cxx diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index b915fb4bdd02d8623ed6d02660d1d531acea6579..715b234040b42938294a8cd5dcfdc355be1d3e85 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -25,6 +25,7 @@ clang/LLVM technology. #include "TClingDataMemberInfo.h" #include "TClingMethodArgInfo.h" #include "TClingMethodInfo.h" +#include "TClingRdictModuleFileExtension.h" #include "TClingTypedefInfo.h" #include "TClingTypeInfo.h" #include "TClingValue.h" @@ -1340,9 +1341,12 @@ TCling::TCling(const char *name, const char *title, const char* const argv[]) interpArgs.push_back(arg.c_str()); } + // Add the Rdict module file extension. + extensions.push_back(std::make_shared<TClingRdictModuleFileExtension>("")); + fInterpreter = new cling::Interpreter(interpArgs.size(), &(interpArgs[0]), - llvmResourceDir); + llvmResourceDir, extensions); if (!fromRootCling) { fInterpreter->installLazyFunctionCreator(llvmLazyFunctionCreator); @@ -1493,6 +1497,122 @@ static bool R__InitStreamerInfoFactory() return doneFactory; // avoid unused variable warning. } +//////////////////////////////////////////////////////////////////////////////// +/// Tries to load a PCM from TFile; returns true on success. +bool TCling::LoadPCMImpl(TFile *pcmFile) +{ + + auto listOfKeys = pcmFile->GetListOfKeys(); + + // This is an empty pcm + if (listOfKeys && ((listOfKeys->GetSize() == 0) || // Nothing here, or + ((listOfKeys->GetSize() == 1) && // only one, and + !strcmp(((TKey *)listOfKeys->At(0))->GetName(), "EMPTY") // name is EMPTY + ))) { + return kTRUE; + } + + TObjArray *protoClasses; + if (gDebug > 1) + ::Info("TCling::LoadPCM", "reading protoclasses for %s \n", pcmFile->GetName()); + + pcmFile->GetObject("__ProtoClasses", protoClasses); + + if (protoClasses) { + for (auto obj : *protoClasses) { + TProtoClass *proto = (TProtoClass *)obj; + TClassTable::Add(proto); + } + // Now that all TClass-es know how to set them up we can update + // existing TClasses, which might cause the creation of e.g. TBaseClass + // objects which in turn requires the creation of TClasses, that could + // come from the PCH, but maybe later in the loop. Instead of resolving + // a dependency graph the addition to the TClassTable above allows us + // to create these dependent TClasses as needed below. + for (auto proto : *protoClasses) { + if (TClass *existingCl = (TClass *)gROOT->GetListOfClasses()->FindObject(proto->GetName())) { + // We have an existing TClass object. It might be emulated + // or interpreted; we now have more information available. + // Make that available. + if (existingCl->GetState() != TClass::kHasTClassInit) { + DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName()); + if (!dict) { + ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s", proto->GetName()); + } else { + // This will replace the existing TClass. + TClass *ncl = (*dict)(); + if (ncl) + ncl->PostLoadCheck(); + } + } + } + } + + protoClasses->Clear(); // Ownership was transfered to TClassTable. + delete protoClasses; + } + + TObjArray *dataTypes; + pcmFile->GetObject("__Typedefs", dataTypes); + if (dataTypes) { + for (auto typedf : *dataTypes) + gROOT->GetListOfTypes()->Add(typedf); + dataTypes->Clear(); // Ownership was transfered to TListOfTypes. + delete dataTypes; + } + + TObjArray *enums; + pcmFile->GetObject("__Enums", enums); + if (enums) { + // Cache the pointers + auto listOfGlobals = gROOT->GetListOfGlobals(); + auto listOfEnums = dynamic_cast<THashList *>(gROOT->GetListOfEnums()); + // Loop on enums and then on enum constants + for (auto selEnum : *enums) { + const char *enumScope = selEnum->GetTitle(); + const char *enumName = selEnum->GetName(); + if (strcmp(enumScope, "") == 0) { + // This is a global enum and is added to the + // list of enums and its constants to the list of globals + if (!listOfEnums->THashList::FindObject(enumName)) { + ((TEnum *)selEnum)->SetClass(nullptr); + listOfEnums->Add(selEnum); + } + for (auto enumConstant : *static_cast<TEnum *>(selEnum)->GetConstants()) { + if (!listOfGlobals->FindObject(enumConstant)) { + listOfGlobals->Add(enumConstant); + } + } + } else { + // This enum is in a namespace. A TClass entry is bootstrapped if + // none exists yet and the enum is added to it + TClass *nsTClassEntry = TClass::GetClass(enumScope); + if (!nsTClassEntry) { + nsTClassEntry = new TClass(enumScope, 0, TClass::kNamespaceForMeta, true); + } + auto listOfEnums = nsTClassEntry->fEnums.load(); + if (!listOfEnums) { + if ((kIsClass | kIsStruct | kIsUnion) & nsTClassEntry->Property()) { + // For this case, the list will be immutable once constructed + // (i.e. in this case, by the end of this routine). + listOfEnums = nsTClassEntry->fEnums = new TListOfEnums(nsTClassEntry); + } else { + // namespaces can have enums added to them + listOfEnums = nsTClassEntry->fEnums = new TListOfEnumsWithLock(nsTClassEntry); + } + } + if (listOfEnums && !listOfEnums->THashList::FindObject(enumName)) { + ((TEnum *)selEnum)->SetClass(nsTClassEntry); + listOfEnums->Add(selEnum); + } + } + } + enums->Clear(); + delete enums; + } + + return kTRUE; +} //////////////////////////////////////////////////////////////////////////////// /// Tries to load a PCM; returns true on success. @@ -1505,7 +1625,7 @@ bool TCling::LoadPCM(const std::string &pcmFileNameFullPath) assert(!pcmFileNameFullPath.empty()); assert(llvm::sys::path::is_absolute(pcmFileNameFullPath)); if (!llvm::sys::fs::exists(pcmFileNameFullPath)) { - return false; + return false; } // Easier to work with the ROOT interfaces. @@ -1527,131 +1647,15 @@ bool TCling::LoadPCM(const std::string &pcmFileNameFullPath) TDirectory::TContext ctxt; - TFile *pcmFile = new TFile(pcmFileName+"?filetype=pcm","READ"); - - auto listOfKeys = pcmFile->GetListOfKeys(); - - // This is an empty pcm - if ( - listOfKeys && - ( - (listOfKeys->GetSize() == 0) || // Nothing here, or - ( - (listOfKeys->GetSize() == 1) && // only one, and - !strcmp(((TKey*)listOfKeys->At(0))->GetName(), "EMPTY") // name is EMPTY - ) - ) - ) { - delete pcmFile; - gDebug = oldDebug; - return kTRUE; - } - - TObjArray *protoClasses; - if (gDebug > 1) - ::Info("TCling::LoadPCM","reading protoclasses for %s \n",pcmFileName.Data()); - - pcmFile->GetObject("__ProtoClasses", protoClasses); - - if (protoClasses) { - for (auto obj : *protoClasses) { - TProtoClass * proto = (TProtoClass*)obj; - TClassTable::Add(proto); - } - // Now that all TClass-es know how to set them up we can update - // existing TClasses, which might cause the creation of e.g. TBaseClass - // objects which in turn requires the creation of TClasses, that could - // come from the PCH, but maybe later in the loop. Instead of resolving - // a dependency graph the addition to the TClassTable above allows us - // to create these dependent TClasses as needed below. - for (auto proto : *protoClasses) { - if (TClass* existingCl - = (TClass*)gROOT->GetListOfClasses()->FindObject(proto->GetName())) { - // We have an existing TClass object. It might be emulated - // or interpreted; we now have more information available. - // Make that available. - if (existingCl->GetState() != TClass::kHasTClassInit) { - DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName()); - if (!dict) { - ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s", - proto->GetName()); - } else { - // This will replace the existing TClass. - TClass *ncl = (*dict)(); - if (ncl) ncl->PostLoadCheck(); - - } - } - } - } + TFile *pcmFile = new TFile(pcmFileName + "?filetype=pcm", "READ"); - protoClasses->Clear(); // Ownership was transfered to TClassTable. - delete protoClasses; - } - - TObjArray *dataTypes; - pcmFile->GetObject("__Typedefs", dataTypes); - if (dataTypes) { - for (auto typedf: *dataTypes) - gROOT->GetListOfTypes()->Add(typedf); - dataTypes->Clear(); // Ownership was transfered to TListOfTypes. - delete dataTypes; - } - - TObjArray *enums; - pcmFile->GetObject("__Enums", enums); - if (enums) { - // Cache the pointers - auto listOfGlobals = gROOT->GetListOfGlobals(); - auto listOfEnums = dynamic_cast<THashList*>(gROOT->GetListOfEnums()); - // Loop on enums and then on enum constants - for (auto selEnum: *enums){ - const char* enumScope = selEnum->GetTitle(); - const char* enumName = selEnum->GetName(); - if (strcmp(enumScope,"") == 0){ - // This is a global enum and is added to the - // list of enums and its constants to the list of globals - if (!listOfEnums->THashList::FindObject(enumName)){ - ((TEnum*) selEnum)->SetClass(nullptr); - listOfEnums->Add(selEnum); - } - for (auto enumConstant: *static_cast<TEnum*>(selEnum)->GetConstants()){ - if (!listOfGlobals->FindObject(enumConstant)){ - listOfGlobals->Add(enumConstant); - } - } - } - else { - // This enum is in a namespace. A TClass entry is bootstrapped if - // none exists yet and the enum is added to it - TClass* nsTClassEntry = TClass::GetClass(enumScope); - if (!nsTClassEntry){ - nsTClassEntry = new TClass(enumScope,0,TClass::kNamespaceForMeta, true); - } - auto listOfEnums = nsTClassEntry->fEnums.load(); - if (!listOfEnums) { - if ( (kIsClass | kIsStruct | kIsUnion) & nsTClassEntry->Property() ) { - // For this case, the list will be immutable once constructed - // (i.e. in this case, by the end of this routine). - listOfEnums = nsTClassEntry->fEnums = new TListOfEnums(nsTClassEntry); - } else { - //namespaces can have enums added to them - listOfEnums = nsTClassEntry->fEnums = new TListOfEnumsWithLock(nsTClassEntry); - } - } - if (listOfEnums && !listOfEnums->THashList::FindObject(enumName)){ - ((TEnum*) selEnum)->SetClass(nsTClassEntry); - listOfEnums->Add(selEnum); - } - } - } - enums->Clear(); - delete enums; - } + bool result = LoadPCMImpl(pcmFile); delete pcmFile; gDebug = oldDebug; + return result; + } else { if (gDebug > 5) ::Info("TCling::LoadPCM", "Loading clang PCM %s", pcmFileName.Data()); diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index 92089c67459067bf9e7fbcde50a71457d8caaa2f..27755922448017e00c532a26524c57100808dae8 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -59,6 +59,7 @@ namespace cling { class TClingCallbacks; class TEnv; +class TFile; class THashTable; class TInterpreterValue; class TMethod; @@ -579,7 +580,9 @@ private: // Private Utility Functions and Classes void AddFriendToClass(clang::FunctionDecl*, clang::CXXRecordDecl*) const; bool LoadPCM(const std::string &pcmFileNameFullPath); - void InitRootmapFile(const char *name); + bool LoadPCMImpl(TFile *pcmFile); + + void InitRootmapFile(const char *name); int ReadRootmapFile(const char *rootmapfile, TUniqueString* uniqueString = nullptr); Bool_t HandleNewTransaction(const cling::Transaction &T); void UnloadClassMembers(TClass* cl, const clang::DeclContext* DC); diff --git a/core/metacling/src/TClingRdictModuleFileExtension.cxx b/core/metacling/src/TClingRdictModuleFileExtension.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6127849e56de3022064df6ae43278217d419eb2c --- /dev/null +++ b/core/metacling/src/TClingRdictModuleFileExtension.cxx @@ -0,0 +1,180 @@ +/// \file TClingRdictModuleFileExtension.cxx +/// +/// \brief The file contains facilities to work with C++ module files extensions +/// used to store rdict files. +/// +/// \author Vassil Vassilev <vvasilev@cern.ch> +/// +/// \date May, 2019 +/// +/************************************************************************* + * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#include "TClingRdictModuleFileExtension.h" + +#include "TClingUtils.h" + +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/Module.h" + +#include "llvm/ADT/Hashing.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +/// Rdict module extension block name. +const std::string ROOT_CLING_RDICT_BLOCK_NAME = "root.cling.rdict"; + +/// Rdict module extension major version number. +constexpr uint16_t ROOT_CLING_RDICT_VERSION_MAJOR = 1; + +/// Rdict module extension minor version number. +/// +/// When the format changes IN ANY WAY, this number should be incremented. +constexpr uint16_t ROOT_CLING_RDICT_VERSION_MINOR = 1; + + +TClingRdictModuleFileExtension::Writer::~Writer() {} + +void TClingRdictModuleFileExtension::Writer::writeExtensionContents(clang::Sema &SemaRef, llvm::BitstreamWriter &Stream) +{ + + const clang::LangOptions &Opts = SemaRef.getLangOpts(); + const clang::Preprocessor &PP = SemaRef.getPreprocessor(); + + const llvm::StringRef CachePath = PP.getHeaderSearchInfo().getHeaderSearchOpts().ModuleCachePath; + const std::string RdictsStart = "lib" + Opts.CurrentModule + "_"; + const std::string RdictsEnd = "_rdict.pcm"; + + using namespace llvm; + using namespace clang; + using namespace clang::serialization; + // Write an abbreviation for this record. + auto Abv = std::make_shared<BitCodeAbbrev>(); + Abv->Add(BitCodeAbbrevOp(FIRST_EXTENSION_RECORD_ID)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + auto Abbrev = Stream.EmitAbbrev(std::move(Abv)); + auto Abv1 = std::make_shared<BitCodeAbbrev>(); + Abv1->Add(BitCodeAbbrevOp(FIRST_EXTENSION_RECORD_ID + 1)); + Abv1->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + auto Abbrev1 = Stream.EmitAbbrev(std::move(Abv1)); + + // Write a dict files into the extension block. + std::error_code EC; + for (llvm::sys::fs::directory_iterator DirIt(CachePath, EC), DirEnd; DirIt != DirEnd && !EC; DirIt.increment(EC)) { + StringRef FileName(DirIt->path()); + if (!llvm::sys::fs::is_directory(FileName) && llvm::sys::path::filename(FileName).startswith(RdictsStart) && + FileName.endswith(RdictsEnd)) { + + uint64_t Record[] = {FIRST_EXTENSION_RECORD_ID}; + Stream.EmitRecordWithBlob(Abbrev, Record, llvm::sys::path::filename(FileName)); + + uint64_t Record1[] = {FIRST_EXTENSION_RECORD_ID + 1}; + Twine rdictFileName = Twine(FileName); + auto MBOrErr = MemoryBuffer::getFile(rdictFileName); + MemoryBuffer &MB = *MBOrErr.get(); + Stream.EmitRecordWithBlob(Abbrev1, Record1, MB.getBuffer()); + + llvm::sys::fs::remove(rdictFileName); + } + } +} + +static void WriteOnDisk(llvm::StringRef PathName, llvm::StringRef CurRdictName, llvm::StringRef CurRdictContent) +{ + llvm::SmallString<255> FullRdictName = PathName; + llvm::sys::path::append(FullRdictName, CurRdictName); + if (llvm::sys::fs::exists(FullRdictName)) { + // FIXME: Add diagnostics if we have a file on disk and we enter here... + return; + } + std::error_code EC; + // FIXME: Convert to new TMemFile = ...; + llvm::raw_fd_ostream os(FullRdictName.str(), EC, llvm::sys::fs::F_None); + os << CurRdictContent; + os.close(); + // Add this class as a friend of TCling. + // gCling->LoadPCM(FullRdictName); +} + +TClingRdictModuleFileExtension::Reader::Reader(clang::ModuleFileExtension *Ext, clang::ASTReader &Reader, + clang::serialization::ModuleFile &Mod, + const llvm::BitstreamCursor &InStream) + : ModuleFileExtensionReader(Ext), Stream(InStream) +{ + // Read the extension block. + llvm::SmallVector<uint64_t, 4> Record; + llvm::StringRef CurrentRdictName; + while (true) { + llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); + switch (Entry.Kind) { + case llvm::BitstreamEntry::SubBlock: + case llvm::BitstreamEntry::EndBlock: + case llvm::BitstreamEntry::Error: return; + + case llvm::BitstreamEntry::Record: break; + } + + Record.clear(); + llvm::StringRef Blob; + unsigned RecCode = Stream.readRecord(Entry.ID, Record, &Blob); + using namespace clang::serialization; + switch (RecCode) { + case FIRST_EXTENSION_RECORD_ID: { + CurrentRdictName = Blob; + break; + } + case FIRST_EXTENSION_RECORD_ID + 1: { + llvm::StringRef Path = llvm::sys::path::parent_path(Mod.FileName); + WriteOnDisk(Path, CurrentRdictName, Blob); + break; + } + } + } +} + +TClingRdictModuleFileExtension::Reader::~Reader() {} + +TClingRdictModuleFileExtension::~TClingRdictModuleFileExtension() {} + +clang::ModuleFileExtensionMetadata TClingRdictModuleFileExtension::getExtensionMetadata() const +{ + const std::string UserInfo = ""; + return {ROOT_CLING_RDICT_BLOCK_NAME, ROOT_CLING_RDICT_VERSION_MAJOR, ROOT_CLING_RDICT_VERSION_MINOR, UserInfo}; +} + +llvm::hash_code TClingRdictModuleFileExtension::hashExtension(llvm::hash_code Code) const +{ + Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_BLOCK_NAME); + Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_VERSION_MAJOR); + Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_VERSION_MINOR); + Code = llvm::hash_combine(Code, UserInfo); + + return Code; +} + +std::unique_ptr<clang::ModuleFileExtensionWriter> +TClingRdictModuleFileExtension::createExtensionWriter(clang::ASTWriter &) +{ + return std::unique_ptr<clang::ModuleFileExtensionWriter>(new Writer(this)); +} + +std::unique_ptr<clang::ModuleFileExtensionReader> +TClingRdictModuleFileExtension::createExtensionReader(const clang::ModuleFileExtensionMetadata &Metadata, + clang::ASTReader &Reader, clang::serialization::ModuleFile &Mod, + const llvm::BitstreamCursor &Stream) +{ + return std::unique_ptr<clang::ModuleFileExtensionReader>( + new TClingRdictModuleFileExtension::Reader(this, Reader, Mod, Stream)); +} diff --git a/core/metacling/src/TClingRdictModuleFileExtension.h b/core/metacling/src/TClingRdictModuleFileExtension.h new file mode 100644 index 0000000000000000000000000000000000000000..0248f17d71aa3493f56c961ce655febe13aba623 --- /dev/null +++ b/core/metacling/src/TClingRdictModuleFileExtension.h @@ -0,0 +1,64 @@ +/// \file TClingRdictModuleFileExtension.h +/// +/// \brief The file contains facilities to work with C++ module files extensions +/// used to store rdict files. +/// +/// \author Vassil Vassilev <vvasilev@cern.ch> +/// +/// \date May, 2019 +/// +/************************************************************************* + * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_CLING_RDICT_MODULE_FILE_EXTENSION_H +#define ROOT_CLING_RDICT_MODULE_FILE_EXTENSION_H + +#include "clang/Serialization/ModuleFileExtension.h" + +#include "llvm/Bitcode/BitstreamReader.h" + +/// A module file extension used for testing purposes. +class TClingRdictModuleFileExtension : public clang::ModuleFileExtension { + std::string UserInfo; + std::string RdictFileName; + + class Writer : public clang::ModuleFileExtensionWriter { + public: + Writer(ModuleFileExtension *Ext) : ModuleFileExtensionWriter(Ext) {} + ~Writer() override; + + void writeExtensionContents(clang::Sema &SemaRef, llvm::BitstreamWriter &Stream) override; + }; + + class Reader : public clang::ModuleFileExtensionReader { + llvm::BitstreamCursor Stream; + + public: + ~Reader() override; + + Reader(clang::ModuleFileExtension *Ext, clang::ASTReader &Reader, clang::serialization::ModuleFile &Mod, + const llvm::BitstreamCursor &InStream); + }; + +public: + TClingRdictModuleFileExtension(llvm::StringRef UserInfo) : UserInfo(UserInfo) {} + + ~TClingRdictModuleFileExtension() override; + + clang::ModuleFileExtensionMetadata getExtensionMetadata() const override; + + llvm::hash_code hashExtension(llvm::hash_code Code) const override; + + std::unique_ptr<clang::ModuleFileExtensionWriter> createExtensionWriter(clang::ASTWriter &Writer) override; + + std::unique_ptr<clang::ModuleFileExtensionReader> + createExtensionReader(const clang::ModuleFileExtensionMetadata &Metadata, clang::ASTReader &Reader, + clang::serialization::ModuleFile &Mod, const llvm::BitstreamCursor &Stream) override; +}; + +#endif diff --git a/interpreter/cling/include/cling/Interpreter/CIFactory.h b/interpreter/cling/include/cling/Interpreter/CIFactory.h index 236f3324d692af173cb5b8a84df173002d59978a..1a77e22b2edd5cf0d37e3e9b25ef14fd537bdb4c 100644 --- a/interpreter/cling/include/cling/Interpreter/CIFactory.h +++ b/interpreter/cling/include/cling/Interpreter/CIFactory.h @@ -20,8 +20,9 @@ namespace llvm { } namespace clang { - class DiagnosticsEngine; class ASTConsumer; + class DiagnosticsEngine; + class ModuleFileExtension; } namespace cling { @@ -29,16 +30,20 @@ namespace cling { namespace CIFactory { typedef std::unique_ptr<llvm::MemoryBuffer> MemBufPtr_t; + using ModuleFileExtensions = + std::vector<std::shared_ptr<clang::ModuleFileExtension>>; // TODO: Add overload that takes file not MemoryBuffer clang::CompilerInstance* createCI(llvm::StringRef Code, const InvocationOptions& Opts, - const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer); + const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer, + const ModuleFileExtensions& moduleExtensions); clang::CompilerInstance* createCI(MemBufPtr_t Buffer, int Argc, const char* const* Argv, const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer, + const ModuleFileExtensions& moduleExtensions, bool OnlyLex = false); } // namespace CIFactory } // namespace cling diff --git a/interpreter/cling/include/cling/Interpreter/Interpreter.h b/interpreter/cling/include/cling/Interpreter/Interpreter.h index fe94b7a8cad348bf5428998b096d58a2c0233ede..3dad5066c7431024f77d2b63d93d8e75553b8626 100644 --- a/interpreter/cling/include/cling/Interpreter/Interpreter.h +++ b/interpreter/cling/include/cling/Interpreter/Interpreter.h @@ -41,6 +41,7 @@ namespace clang { class GlobalDecl; class MacroInfo; class Module; + class ModuleFileExtension; class NamedDecl; class Parser; class Preprocessor; @@ -137,6 +138,10 @@ namespace cling { kNumExeResults }; + public: + using ModuleFileExtensions = + std::vector<std::shared_ptr<clang::ModuleFileExtension>>; + private: ///\brief Interpreter invocation options. @@ -303,8 +308,8 @@ namespace cling { ///\brief The target constructor to be called from both the delegating /// constructors. parentInterp might be nullptr. /// - Interpreter(int argc, const char* const *argv, - const char* llvmdir, bool noRuntime, + Interpreter(int argc, const char* const* argv, const char* llvmdir, + const ModuleFileExtensions& moduleExtensions, bool noRuntime, const Interpreter* parentInterp); public: @@ -315,9 +320,11 @@ namespace cling { ///\param[in] llvmdir - ??? ///\param[in] noRuntime - flag to control the presence of runtime universe /// - Interpreter(int argc, const char* const *argv, const char* llvmdir = 0, - bool noRuntime = false) : - Interpreter(argc, argv, llvmdir, noRuntime, nullptr) { } + Interpreter(int argc, const char* const* argv, const char* llvmdir = 0, + const ModuleFileExtensions& moduleExtensions = {}, + bool noRuntime = false) + : Interpreter(argc, argv, llvmdir, moduleExtensions, noRuntime, + nullptr) {} ///\brief Constructor for child Interpreter. ///\param[in] parentInterpreter - the parent interpreter of this interpreter @@ -326,8 +333,10 @@ namespace cling { ///\param[in] llvmdir - ??? ///\param[in] noRuntime - flag to control the presence of runtime universe /// - Interpreter(const Interpreter &parentInterpreter,int argc, const char* const *argv, - const char* llvmdir = 0, bool noRuntime = true); + Interpreter(const Interpreter& parentInterpreter, int argc, + const char* const* argv, const char* llvmdir = 0, + const ModuleFileExtensions& moduleExtensions = {}, + bool noRuntime = true); virtual ~Interpreter(); diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp index 80d1b1c52a7dd4b8ae32297f56de9e2ee7581402..951f14005d291e3cb4c7f76a5ef457935d8eec20 100644 --- a/interpreter/cling/lib/Interpreter/CIFactory.cpp +++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp @@ -780,8 +780,9 @@ static void stringifyPreprocSetting(PreprocessorOptions& PPOpts, static CompilerInstance* createCIImpl(std::unique_ptr<llvm::MemoryBuffer> Buffer, const CompilerOptions& COpts, const char* LLVMDir, - std::unique_ptr<clang::ASTConsumer> customConsumer, bool OnlyLex, - bool HasInput = false) { + std::unique_ptr<clang::ASTConsumer> customConsumer, + const CIFactory::ModuleFileExtensions& moduleExtensions, + bool OnlyLex, bool HasInput = false) { // Follow clang -v convention of printing version on first line if (COpts.Verbose) cling::log() << "cling version " << ClingStringify(CLING_VERSION) << '\n'; @@ -1043,6 +1044,12 @@ static void stringifyPreprocSetting(PreprocessorOptions& PPOpts, } FrontendOptions& FrontendOpts = Invocation.getFrontendOpts(); + + // Register the externally constructed extensions. + assert(FrontendOpts.ModuleFileExtensions.empty() && "Extensions exist!"); + for (auto& E : moduleExtensions) + FrontendOpts.ModuleFileExtensions.push_back(E); + FrontendOpts.DisableFree = true; // With modules, we now start adding prebuilt module paths to the CI. @@ -1240,17 +1247,20 @@ namespace cling { CompilerInstance* CIFactory::createCI(llvm::StringRef Code, const InvocationOptions& Opts, const char* LLVMDir, - std::unique_ptr<clang::ASTConsumer> consumer) { + std::unique_ptr<clang::ASTConsumer> consumer, + const ModuleFileExtensions& moduleExtensions) { return createCIImpl(llvm::MemoryBuffer::getMemBuffer(Code), Opts.CompilerOpts, - LLVMDir, std::move(consumer), false /*OnlyLex*/, + LLVMDir, std::move(consumer), moduleExtensions, + false /*OnlyLex*/, !Opts.IsInteractive()); } CompilerInstance* CIFactory::createCI( MemBufPtr_t Buffer, int argc, const char* const* argv, const char* LLVMDir, - std::unique_ptr<clang::ASTConsumer> consumer, bool OnlyLex) { + std::unique_ptr<clang::ASTConsumer> consumer, + const ModuleFileExtensions& moduleExtensions, bool OnlyLex /*false*/) { return createCIImpl(std::move(Buffer), CompilerOptions(argc, argv), LLVMDir, - std::move(consumer), OnlyLex); + std::move(consumer), moduleExtensions, OnlyLex); } } // namespace cling diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp index 02daa3d4ab5a34ad0c5a48b9e7dd37fba1ad815a..8cceb62fb96a7e587b1894995b0abc37cee9d1c5 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp +++ b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp @@ -234,12 +234,13 @@ static void HandlePlugins(CompilerInstance& CI, } namespace cling { - IncrementalParser::IncrementalParser(Interpreter* interp, const char* llvmdir) + IncrementalParser::IncrementalParser(Interpreter* interp, const char* llvmdir, + const ModuleFileExtensions& moduleExtensions) : m_Interpreter(interp) { std::unique_ptr<cling::DeclCollector> consumer; consumer.reset(m_Consumer = new cling::DeclCollector()); m_CI.reset(CIFactory::createCI("", interp->getOptions(), llvmdir, - std::move(consumer))); + std::move(consumer), moduleExtensions)); if (!m_CI) { cling::errs() << "Compiler instance could not be created.\n"; diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.h b/interpreter/cling/lib/Interpreter/IncrementalParser.h index 8322de0908e990c34db3ee76bde4f1932c70c6c0..c88ec82e6f1651ed366c8fe073b714e4228f8bc4 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalParser.h +++ b/interpreter/cling/lib/Interpreter/IncrementalParser.h @@ -33,6 +33,7 @@ namespace clang { class DiagnosticConsumer; class Decl; class FileID; + class ModuleFileExtension; class Parser; } @@ -100,6 +101,9 @@ namespace cling { /// std::unique_ptr<IncrementalCUDADeviceCompiler> m_CUDACompiler; + using ModuleFileExtensions = + std::vector<std::shared_ptr<clang::ModuleFileExtension>>; + public: enum EParseResult { kSuccess, @@ -108,7 +112,8 @@ namespace cling { }; typedef llvm::PointerIntPair<Transaction*, 2, EParseResult> ParseResultTransaction; - IncrementalParser(Interpreter* interp, const char* llvmdir); + IncrementalParser(Interpreter* interp, const char* llvmdir, + const ModuleFileExtensions& moduleExtensions); ~IncrementalParser(); ///\brief Whether the IncrementalParser is valid. diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index 535325c3fb6d529c7a0f878e56dff964423a43d9..568658a09616d3968062017e1b17d2083ec8293f 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -217,8 +217,9 @@ namespace cling { } Interpreter::Interpreter(int argc, const char* const *argv, - const char* llvmdir /*= 0*/, bool noRuntime, - const Interpreter* parentInterp) : + const char* llvmdir /*= 0*/, + const ModuleFileExtensions& moduleExtensions, + bool noRuntime, const Interpreter* parentInterp) : m_Opts(argc, argv), m_UniqueCounter(parentInterp ? parentInterp->m_UniqueCounter + 1 : 0), m_PrintDebug(false), m_DynamicLookupDeclared(false), @@ -230,7 +231,7 @@ namespace cling { m_LLVMContext.reset(new llvm::LLVMContext); m_DyLibManager.reset(new DynamicLibraryManager(getOptions())); - m_IncrParser.reset(new IncrementalParser(this, llvmdir)); + m_IncrParser.reset(new IncrementalParser(this, llvmdir, moduleExtensions)); if (!m_IncrParser->isValid(false)) return; @@ -399,8 +400,11 @@ namespace cling { /// Interpreter::Interpreter(const Interpreter &parentInterpreter, int argc, const char* const *argv, - const char* llvmdir /*= 0*/, bool noRuntime) : - Interpreter(argc, argv, llvmdir, noRuntime, &parentInterpreter) { + const char* llvmdir /*= 0*/, + const ModuleFileExtensions& moduleExtensions/*={}*/, + bool noRuntime /*= true*/) : + Interpreter(argc, argv, llvmdir, moduleExtensions, noRuntime, + &parentInterpreter) { // Do the "setup" of the connection between this interpreter and // its parent interpreter. if (CompilerInstance* CI = getCIOrNull()) { @@ -1674,7 +1678,8 @@ namespace cling { // FIXME: CIFactory appends extra 3 folders to the llvmdir. std::string llvmdir = getCI()->getHeaderSearchOpts().ResourceDir + "/../../../"; - cling::Interpreter fwdGen(1, &dummy, llvmdir.c_str(), true); + cling::Interpreter fwdGen(1, &dummy, llvmdir.c_str(), + /*moduleExtensions*/ {}, /*noRuntime=*/true); // Copy the same header search options to the new instance. Preprocessor& fwdGenPP = fwdGen.getCI()->getPreprocessor();