diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index bea171a7d7acdca94c05550aed6427918c7eaaf2..fc70a70d122d1c0f7a3cf47f6b29b8f019b52446 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -2,7 +2,7 @@ // Author: Fons Rademakers 15/09/95 /************************************************************************* - * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * @@ -3535,9 +3535,35 @@ int TSystem::CompileMacro(const char *filename, Option_t *opt, } if (gEnv) { TString fromConfig = gEnv->GetValue("ACLiC.IncludePaths",""); - rcling.Append(fromConfig).Append(" \""); + rcling.Append(fromConfig); } - rcling.Append(filename_fullpath).Append("\" \"").Append(linkdef).Append("\"");; + + // Create a modulemap + // FIXME: Merge the modulemap generation from cmake and here in rootcling. + if (useCxxModules) { + // TString moduleMapFileName = file_dirname + "/" + libname + ".modulemap"; + TString moduleName = libname + "_ACLiC_dict"; + if (moduleName.BeginsWith("lib")) + moduleName = moduleName.Remove(0, 3); + TString moduleMapName = moduleName + ".modulemap"; + TString moduleMapFullPath = build_loc + "/" + moduleMapName; + // A modulemap may exist from previous runs, overwrite it. + if (verboseLevel > 3 && !AccessPathName(moduleMapFullPath)) + ::Info("ACLiC", "File %s already exists!", moduleMapFullPath.Data()); + + std::ofstream moduleMapFile(moduleMapFullPath, std::ios::out); + moduleMapFile << "module \"" << moduleName << "\" {" << std::endl; + moduleMapFile << " header \"" << filename_fullpath << "\"" << std::endl; + moduleMapFile << " export *" << std::endl; + moduleMapFile << " link \"" << libname_ext << "\"" << std::endl; + moduleMapFile << "}" << std::endl; + moduleMapFile.close(); + gInterpreter->RegisterPrebuiltModulePath(build_loc.Data(), moduleMapName.Data()); + rcling.Append(" \"-fmodule-map-file=" + moduleMapFullPath + "\" "); + } + + rcling.Append(" \"").Append(filename_fullpath).Append("\" "); + rcling.Append("\"").Append(linkdef).Append("\""); // ======= Run rootcling if (withInfo) { diff --git a/core/dictgen/src/rootcling_impl.cxx b/core/dictgen/src/rootcling_impl.cxx index 42bd2386737634cd08d75f4cec8ac9d6d47db1e4..1bb533dd5d626e0a3afcedbbf41f8694dc79f53c 100644 --- a/core/dictgen/src/rootcling_impl.cxx +++ b/core/dictgen/src/rootcling_impl.cxx @@ -3664,6 +3664,29 @@ public: } } } + + // rootcling pre-includes things such as Rtypes.h. This means that ACLiC can + // call rootcling asking it to create a module for a file with no #includes + // but relying on things from Rtypes.h such as the ClassDef macro. + // + // When rootcling starts building a module, it becomes resilient to the + // outside environment and pre-included files have no effect. This hook + // informs rootcling when a new submodule is being built so that it can + // make Core.Rtypes.h visible. + virtual void EnteredSubmodule(clang::Module* M, + clang::SourceLocation ImportLoc, + bool ForPragma) { + assert(M); + using namespace clang; + if (llvm::StringRef(M->Name).endswith("ACLiC_dict")) { + Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor(); + HeaderSearch& HS = PP.getHeaderSearchInfo(); + // FIXME: Reduce to Core.Rtypes.h. + Module* CoreModule = HS.lookupModule("Core", /*AllowSearch*/false); + assert(M && "Must have module Core"); + PP.makeModuleVisible(CoreModule, ImportLoc); + } + } }; //////////////////////////////////////////////////////////////////////////////// @@ -3991,7 +4014,6 @@ int RootClingMain(int argc, bool selSyntaxOnly = false; bool noIncludePaths = false; bool cxxmodule = false; - bool isAclic = false; // Collect the diagnostic pragmas linked to the usage of -W // Workaround for ROOT-5656 @@ -4137,8 +4159,6 @@ int RootClingMain(int argc, } ic++; } - if (liblistPrefix.length()) - isAclic = true; // Check if we have a multi dict request but no target library if (multiDict && sharedLibraryPathName.empty()) { @@ -4895,14 +4915,14 @@ int RootClingMain(int argc, } modGen.WriteRegistrationSource(dictStream, fwdDeclnArgsToKeepString, headersClassesMapString, fwdDeclsString, - extraIncludes, cxxmodule && !isAclic); + extraIncludes, cxxmodule); // If we just want to inline the input header, we don't need // to generate any files. if (!inlineInputHeader) { // Write the module/PCH depending on what mode we are on if (modGen.IsPCH()) { if (!GenerateAllDict(modGen, CI, currentDirectory)) return 1; - } else if (cxxmodule && !isAclic) { + } else if (cxxmodule) { if (!CheckModuleValid(modGen, resourceDir, interp, linkdefFilename, moduleName.str())) return 1; } @@ -5017,7 +5037,6 @@ int RootClingMain(int argc, // Manually call end of translation unit because we never call the // appropriate deconstructors in the interpreter. This writes out the C++ // module file that we currently generate. - if (!isAclic) { cling::Interpreter::PushTransactionRAII RAII(&interp); CI->getSema().getASTConsumer().HandleTranslationUnit(CI->getSema().getASTContext()); diff --git a/core/meta/inc/TInterpreter.h b/core/meta/inc/TInterpreter.h index 87fd9f09fd9886d6900e39b1e0e5bb5b352c9eb3..92fa634d544ce69b45dbc2eef99d83247441a5f0 100644 --- a/core/meta/inc/TInterpreter.h +++ b/core/meta/inc/TInterpreter.h @@ -176,6 +176,8 @@ public: virtual Long_t ProcessLine(const char *line, EErrorCode *error = 0) = 0; virtual Long_t ProcessLineSynch(const char *line, EErrorCode *error = 0) = 0; virtual void PrintIntro() = 0; + virtual bool RegisterPrebuiltModulePath(const std::string& FullPath, + const std::string& ModuleMapName = "module.modulemap") const = 0; virtual void RegisterModule(const char* /*modulename*/, const char** /*headers*/, const char** /*includePaths*/, diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 18764c28cbf1e7f958a4cea00709046931ad3722..87fc2d2944541271e10264f0b855de457876c407 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -1044,35 +1044,6 @@ std::string TCling::ToString(const char* type, void* obj) return fInterpreter->toString(type, obj); } -//////////////////////////////////////////////////////////////////////////////// -///\returns true if the module map was loaded, false on error or if the map was -/// already loaded. -static bool RegisterPrebuiltModulePath(clang::Preprocessor& PP, - const std::string& FullPath) { - assert(llvm::sys::path::is_absolute(FullPath)); - FileManager& FM = PP.getFileManager(); - // FIXME: In a ROOT session we can add an include path (through .I /inc/path) - // We should look for modulemap files there too. - const DirectoryEntry *DE = FM.getDirectory(FullPath); - if (DE) { - HeaderSearch& HS = PP.getHeaderSearchInfo(); - const FileEntry *FE = HS.lookupModuleMapFile(DE, /*IsFramework*/ false); - const auto &ModPaths = HS.getHeaderSearchOpts().PrebuiltModulePaths; - bool pathExists = std::find(ModPaths.begin(), ModPaths.end(), FullPath) != ModPaths.end(); - if (!pathExists) - HS.getHeaderSearchOpts().AddPrebuiltModulePath(FullPath); - // FIXME: Calling IsLoaded is slow! Replace this with the appropriate - // call to the clang::ModuleMap class. - if (FE && !gCling->IsLoaded(FE->getName().data())) { - assert(!pathExists && "Prebuilt module path was added w/o loading a modulemap!"); - if (!HS.loadModuleMapFile(FE, /*IsSystem*/ false)) - return true; - Error("TCling::LoadModule", "Could not load modulemap in the current directory"); - } - } - return false; -} - //////////////////////////////////////////////////////////////////////////////// ///\returns true if the module was loaded. static bool LoadModule(const std::string &ModuleName, cling::Interpreter &interp, bool Complain = true) @@ -1084,10 +1055,9 @@ static bool LoadModule(const std::string &ModuleName, cling::Interpreter &interp // // Before failing, try loading the modulemap in the current folder and try // loading the requested module from it. - clang::Preprocessor& PP = interp.getCI()->getPreprocessor(); std::string currentDir = gSystem->WorkingDirectory(); assert(!currentDir.empty()); - RegisterPrebuiltModulePath(PP, currentDir); + gCling->RegisterPrebuiltModulePath(currentDir); return interp.loadModule(ModuleName, Complain); } @@ -1720,6 +1690,45 @@ namespace { } +//////////////////////////////////////////////////////////////////////////////// +///\returns true if the module map was loaded, false on error or if the map was +/// already loaded. +bool TCling::RegisterPrebuiltModulePath(const std::string &FullPath, + const std::string &ModuleMapName /*= "module.modulemap"*/) const +{ + assert(llvm::sys::path::is_absolute(FullPath)); + Preprocessor &PP = fInterpreter->getCI()->getPreprocessor(); + FileManager &FM = PP.getFileManager(); + // FIXME: In a ROOT session we can add an include path (through .I /inc/path) + // We should look for modulemap files there too. + const DirectoryEntry *DE = FM.getDirectory(FullPath); + if (DE) { + HeaderSearch &HS = PP.getHeaderSearchInfo(); + HeaderSearchOptions &HSOpts = HS.getHeaderSearchOpts(); + const auto &ModPaths = HSOpts.PrebuiltModulePaths; + bool pathExists = std::find(ModPaths.begin(), ModPaths.end(), FullPath) != ModPaths.end(); + if (!pathExists) + HSOpts.AddPrebuiltModulePath(FullPath); + // We cannot use HS.lookupModuleMapFile(DE, /*IsFramework*/ false); + // because its internal call to getFile has CacheFailure set to true. + // In our case, modulemaps can appear any time due to ACLiC. + // Code copied from HS.lookupModuleMapFile. + llvm::SmallString<256> ModuleMapFileName(DE->getName()); + llvm::sys::path::append(ModuleMapFileName, ModuleMapName); + const FileEntry *FE = FM.getFile(ModuleMapFileName, /*openFile*/ false, + /*CacheFailure*/ false); + + // FIXME: Calling IsLoaded is slow! Replace this with the appropriate + // call to the clang::ModuleMap class. + if (FE && !this->IsLoaded(FE->getName().data())) { + if (!HS.loadModuleMapFile(FE, /*IsSystem*/ false)) + return true; + Error("RegisterPrebuiltModulePath", "Could not load modulemap in %s", ModuleMapFileName.c_str()); + } + } + return false; +} + //////////////////////////////////////////////////////////////////////////////// /// List of dicts that have the PCM information already in the PCH. static const std::unordered_set<std::string> gIgnoredPCMNames = {"libCore", @@ -2026,13 +2035,17 @@ void TCling::RegisterModule(const char* modulename, // specifying the relevant include paths we should try loading the // modulemap next to the library location. clang::Preprocessor &PP = TheSema.getPreprocessor(); - // Can be nullptr in case of libCore. - if (dyLibName) - RegisterPrebuiltModulePath(PP, llvm::sys::path::parent_path(dyLibName)); + std::string ModuleMapName; + if (isACLiC) + ModuleMapName = ModuleName + ".modulemap"; + else + ModuleMapName = "module.modulemap"; + RegisterPrebuiltModulePath(llvm::sys::path::parent_path(dyLibName), + ModuleMapName); // FIXME: We should only complain for modules which we know to exist. For example, we should not complain about // modules such as GenVector32 because it needs to fall back to GenVector. - ModuleWasSuccessfullyLoaded = LoadModule(ModuleName, *fInterpreter, /*Complain=*/ !isACLiC); + ModuleWasSuccessfullyLoaded = LoadModule(ModuleName, *fInterpreter, /*Complain=*/true); if (!ModuleWasSuccessfullyLoaded) { // Only report if we found the module in the modulemap. clang::HeaderSearch &headerSearch = PP.getHeaderSearchInfo(); diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index f5f0984672c803ca65d41c12334705a6a01acf5d..901509b2e566f21ed4b5f0329040cf0f37c0f7e5 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -223,6 +223,8 @@ public: // Public Interface Long_t ProcessLineAsynch(const char* line, EErrorCode* error = 0); Long_t ProcessLineSynch(const char* line, EErrorCode* error = 0); void PrintIntro(); + bool RegisterPrebuiltModulePath(const std::string& FullPath, + const std::string& ModuleMapName = "module.modulemap") const; void RegisterModule(const char* modulename, const char** headers, const char** includePaths, diff --git a/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h b/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h index 5100fc7398ab8de3b0d77bbe28955856971c5987..0c1a7f0a9c399f9f85e5cac468aa9364f721e1c6 100644 --- a/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h +++ b/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h @@ -110,6 +110,9 @@ namespace cling { llvm::StringRef /*SearchPath*/, llvm::StringRef /*RelativePath*/, const clang::Module* /*Imported*/) {} + virtual void EnteredSubmodule(clang::Module* M, + clang::SourceLocation ImportLoc, + bool ForPragma) {} virtual bool FileNotFound(llvm::StringRef FileName, llvm::SmallVectorImpl<char>& RecoveryPath); diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp index 6c5d829c20fa34579eefe51c9b253c39dc293d35..113e742bf8ebb12c4abed4f1928e03b8b91fc585 100644 --- a/interpreter/cling/lib/Interpreter/CIFactory.cpp +++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp @@ -1236,6 +1236,13 @@ static void stringifyPreprocSetting(PreprocessorOptions& PPOpts, PP.getTargetInfo().getTriple()); } + for (const auto& Filename : FrontendOpts.ModuleMapFiles) { + if (auto* File = FM.getFile(Filename)) + PP.getHeaderSearchInfo().loadModuleMapFile(File, /*IsSystem*/ false); + else + CI->getDiagnostics().Report(diag::err_module_map_not_found) << Filename; + } + return CI.release(); // Passes over the ownership to the caller. } diff --git a/interpreter/cling/lib/Interpreter/InterpreterCallbacks.cpp b/interpreter/cling/lib/Interpreter/InterpreterCallbacks.cpp index 2106eca25709572394477e887e83c95cda7eb3fd..b4087935cfe16b08e9aa896512ec287ab686adc4 100644 --- a/interpreter/cling/lib/Interpreter/InterpreterCallbacks.cpp +++ b/interpreter/cling/lib/Interpreter/InterpreterCallbacks.cpp @@ -49,6 +49,12 @@ namespace cling { SearchPath, RelativePath, Imported); } + void EnteredSubmodule(clang::Module* M, + clang::SourceLocation ImportLoc, + bool ForPragma) override { + m_Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma); + } + virtual bool FileNotFound(llvm::StringRef FileName, llvm::SmallVectorImpl<char>& RecoveryPath) { if (m_Callbacks) diff --git a/interpreter/cling/lib/Interpreter/MultiplexInterpreterCallbacks.h b/interpreter/cling/lib/Interpreter/MultiplexInterpreterCallbacks.h index 7d2bfe0ea3203389c129cb6aa399ad319bfb56f6..61b9180ef9e7ed5d596699864ff252afed854e97 100644 --- a/interpreter/cling/lib/Interpreter/MultiplexInterpreterCallbacks.h +++ b/interpreter/cling/lib/Interpreter/MultiplexInterpreterCallbacks.h @@ -41,6 +41,12 @@ namespace cling { Imported); } + void EnteredSubmodule(clang::Module* M, clang::SourceLocation ImportLoc, + bool ForPragma) override { + for (auto&& cb : m_Callbacks) + cb->EnteredSubmodule(M, ImportLoc, ForPragma); + } + bool FileNotFound(llvm::StringRef FileName, llvm::SmallVectorImpl<char>& RecoveryPath) override { bool result = false; diff --git a/interpreter/llvm/src/tools/clang/include/clang/Lex/PPCallbacks.h b/interpreter/llvm/src/tools/clang/include/clang/Lex/PPCallbacks.h index 81c3bd7d14ec5624bbe31949361e10ce7faf6b63..6f4d95c35169943bb5e41d0508baac873558a42f 100644 --- a/interpreter/llvm/src/tools/clang/include/clang/Lex/PPCallbacks.h +++ b/interpreter/llvm/src/tools/clang/include/clang/Lex/PPCallbacks.h @@ -128,6 +128,29 @@ public: const Module *Imported) { } + /// Callback invoked whenever a submodule was entered. + /// + /// \param M The submodule we have entered. + /// + /// \param ImportLoc The location of import directive token. + /// + /// \param ForPragma If entering from pragma directive. + /// + virtual void EnteredSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) { } + + /// Callback invoked whenever a submodule was left. + /// + /// \param M The submodule we have left. + /// + /// \param ImportLoc The location of import directive token. + /// + /// \param ForPragma If entering from pragma directive. + /// + virtual void LeftSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) { } + + /// \brief Callback invoked whenever there was an explicit module-import /// syntax. /// @@ -365,6 +388,18 @@ public: Imported); } + void EnteredSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) override { + First->EnteredSubmodule(M, ImportLoc, ForPragma); + Second->EnteredSubmodule(M, ImportLoc, ForPragma); + } + + void LeftSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) override { + First->LeftSubmodule(M, ImportLoc, ForPragma); + Second->LeftSubmodule(M, ImportLoc, ForPragma); + } + void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, const Module *Imported) override { First->moduleImport(ImportLoc, Path, Imported); diff --git a/interpreter/llvm/src/tools/clang/lib/Lex/PPLexerChange.cpp b/interpreter/llvm/src/tools/clang/lib/Lex/PPLexerChange.cpp index 36d7028da6886ad26039df51cfe955e060337d26..d66060ab1057721aa8e38e546e445119a445806d 100644 --- a/interpreter/llvm/src/tools/clang/lib/Lex/PPLexerChange.cpp +++ b/interpreter/llvm/src/tools/clang/lib/Lex/PPLexerChange.cpp @@ -665,6 +665,8 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc, BuildingSubmoduleStack.push_back( BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState, PendingModuleMacroNames.size())); + if (Callbacks) + Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma); return; } @@ -709,6 +711,9 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc, BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState, PendingModuleMacroNames.size())); + if (Callbacks) + Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma); + // Switch to this submodule as the current submodule. CurSubmoduleState = &State; @@ -749,6 +754,10 @@ Module *Preprocessor::LeaveSubmodule(bool ForPragma) { // are tracking macro visibility, don't build any, and preserve the list // of pending names for the surrounding submodule. BuildingSubmoduleStack.pop_back(); + + if (Callbacks) + Callbacks->LeftSubmodule(LeavingMod, ImportLoc, ForPragma); + makeModuleVisible(LeavingMod, ImportLoc); return LeavingMod; } @@ -833,6 +842,9 @@ Module *Preprocessor::LeaveSubmodule(bool ForPragma) { BuildingSubmoduleStack.pop_back(); + if (Callbacks) + Callbacks->LeftSubmodule(LeavingMod, ImportLoc, ForPragma); + // A nested #include makes the included submodule visible. makeModuleVisible(LeavingMod, ImportLoc); return LeavingMod;