@@ -2235,6 +2235,64 @@ static bool GenerateAllDict(TModuleGenerator &modGen, clang::CompilerInstance *c
22352235 return WriteAST (modGen.GetModuleFileName (), compilerInstance, iSysRoot);
22362236}
22372237
2238+ static void
2239+ foreachHeaderInModule (const clang::Module &module , const std::function<void (const clang::Module::Header &)> &closure)
2240+ {
2241+ // Iterates over all headers in a module and calls the closure on each.
2242+
2243+ // FIXME: We currently have to hardcode '4' to do this. Maybe we
2244+ // will have a nicer way to do this in the future.
2245+ // NOTE: This is on purpose '4', not '5' which is the size of the
2246+ // vector. The last element is the list of excluded headers which we
2247+ // obviously don't want to check here.
2248+ const std::size_t publicHeaderIndex = 4 ;
2249+
2250+ // Integrity check in case this array changes its size at some point.
2251+ const std::size_t maxArrayLength = ((sizeof module .Headers ) / (sizeof *module .Headers ));
2252+ static_assert (publicHeaderIndex + 1 == maxArrayLength,
2253+ " 'Headers' has changed it's size, we need to update publicHeaderIndex" );
2254+
2255+ for (std::size_t i = 0 ; i < publicHeaderIndex; i++) {
2256+ auto &headerList = module .Headers [i];
2257+ for (const clang::Module::Header &moduleHeader : headerList) {
2258+ closure (moduleHeader);
2259+ }
2260+ }
2261+ }
2262+
2263+ // //////////////////////////////////////////////////////////////////////////////
2264+ // / Includes all headers in the given module from this interpreter.
2265+ static void IncludeModuleHeaders (TModuleGenerator &modGen, clang::Module *module , cling::Interpreter &interpreter)
2266+ {
2267+ // Make a list of modules and submodules that we can check for headers.
2268+ // We use a SetVector to prevent an infinite loop in unlikely case the
2269+ // modules somehow are messed up and don't form a tree...
2270+ llvm::SetVector<clang::Module *> modules;
2271+ modules.insert (module );
2272+ for (size_t i = 0 ; i < modules.size (); ++i) {
2273+ clang::Module *M = modules[i];
2274+ for (clang::Module *subModule : M->submodules ())
2275+ modules.insert (subModule);
2276+ }
2277+ // List of includes the interpreter has to parse.
2278+ std::stringstream includes;
2279+ // Now we include all header files from the previously collected modules.
2280+ // FIXME: This might hide that we have a missing include if one of the
2281+ // previous headers already includes this. We should reuse the clang way
2282+ // of doing this and 'import' each header in its own submodule, then let
2283+ // the visibility of the decls handle this situation nicely.
2284+ for (clang::Module *module : modules) {
2285+ foreachHeaderInModule (*module , [&includes](const clang::Module::Header &h) {
2286+ includes << " #include \" " << h.NameAsWritten << " \"\n " ;
2287+ });
2288+ }
2289+ std::string includeListStr = includes.str ();
2290+ auto result = interpreter.declare (includeListStr);
2291+ if (result != cling::Interpreter::CompilationResult::kSuccess ) {
2292+ ROOT::TMetaUtils::Error (" IncludeModuleHeaders" , " Couldn't include headers:\n %s!\n " , includeListStr.c_str ());
2293+ }
2294+ }
2295+
22382296// //////////////////////////////////////////////////////////////////////////////
22392297// / Returns true iff a given module (and its submodules) contains all headers
22402298// / needed by the given ModuleGenerator.
@@ -2256,18 +2314,8 @@ static bool ModuleContainsHeaders(TModuleGenerator &modGen, clang::Module *modul
22562314 // Now we collect all header files from the previously collected modules.
22572315 std::set<std::string> moduleHeaders;
22582316 for (clang::Module *module : modules) {
2259- // Iterate over all header types in a module.
2260- // FIXME: We currently have to hardcode '4' to do this. Maybe we
2261- // will have a nicer way to do this in the future.
2262- // NOTE: This is on purpose '4', not '5' which is the size of the
2263- // vector. The last element is the list of excluded headers which we
2264- // obviously don't want to check here.
2265- for (int i = 0 ; i < 4 ; i++) {
2266- auto &headerList = module ->Headers [i];
2267- for (const clang::Module::Header &moduleHeader : headerList) {
2268- moduleHeaders.insert (moduleHeader.NameAsWritten );
2269- }
2270- }
2317+ foreachHeaderInModule (
2318+ *module , [&moduleHeaders](const clang::Module::Header &h) { moduleHeaders.insert (h.NameAsWritten ); });
22712319 }
22722320
22732321 // Go through the list of headers that are required by the ModuleGenerator
@@ -2286,48 +2334,15 @@ static bool ModuleContainsHeaders(TModuleGenerator &modGen, clang::Module *modul
22862334// //////////////////////////////////////////////////////////////////////////////
22872335// / Generates a module from the given ModuleGenerator and CompilerInstance.
22882336// / Returns true iff the PCM was succesfully generated.
2289- static bool GenerateModule (TModuleGenerator &modGen, clang::CompilerInstance *CI)
2337+ static bool GenerateModule (TModuleGenerator &modGen, const std::string &resourceDir, cling::Interpreter &interpreter,
2338+ StringRef LinkdefPath, const std::string &moduleName)
22902339{
2291- assert (!modGen.IsPCH () && " modGen must not be in PCH mode" );
2292-
2293- std::string outputFile = modGen.GetModuleFileName ();
2294- std::string includeDir = gDriverConfig ->fTROOT__GetIncludeDir ();
2340+ clang::CompilerInstance *CI = interpreter.getCI ();
22952341 clang::HeaderSearch &headerSearch = CI->getPreprocessor ().getHeaderSearchInfo ();
2296- auto &fileMgr = headerSearch.getFileMgr ();
2297-
2298- // Load the modulemap from the ROOT include directory.
2299- clang::ModuleMap &moduleMap = headerSearch.getModuleMap ();
2300- std::string moduleMapPath = includeDir + " /module.modulemap" ;
2301- auto moduleFile = fileMgr.getFile (moduleMapPath);
2302-
2303- // Check if we actually found the modulemap file...
2304- if (!moduleFile) {
2305- ROOT::TMetaUtils::Error (" GenerateModule" , " Couldn't find ROOT modulemap in"
2306- " %s! Ensure that cxxmodules=On is set in CMake.\n " ,
2307- moduleMapPath.c_str ());
2308- return false ;
2309- }
2310-
2311- moduleMap.parseModuleMapFile (moduleFile, false , fileMgr.getDirectory (includeDir));
2312-
2313- // Try to get the module name in the modulemap based on the filepath.
2314- std::string moduleName = llvm::sys::path::filename (outputFile);
2315- // For module "libCore.so" we have the file name "libCore_rdict.pcm".
2316- // We replace this suffix with ".so" to get the name in the modulefile.
2317- if (StringRef (moduleName).endswith (" _rdict.pcm" )) {
2318- auto lengthWithoutSuffix = moduleName.size () - strlen (" _rdict.pcm" );
2319- moduleName = moduleName.substr (0 , lengthWithoutSuffix) + " .so" ;
2320- }
2342+ headerSearch.loadTopLevelSystemModules ();
23212343
23222344 // Actually lookup the module on the computed module name.
2323- clang::Module *module = moduleMap.findModule (moduleName);
2324-
2325- // Inform the user and abort if we can't find a module with a given name.
2326- if (!module ) {
2327- ROOT::TMetaUtils::Error (" GenerateModule" , " Couldn't find module with name '%s' in modulemap!\n " ,
2328- moduleName.c_str ());
2329- return false ;
2330- }
2345+ clang::Module *module = headerSearch.lookupModule (StringRef (moduleName));
23312346
23322347 // Check if the loaded module covers all headers that were specified
23332348 // by the user on the command line. This is an integrity check to
@@ -2345,7 +2360,15 @@ static bool GenerateModule(TModuleGenerator &modGen, clang::CompilerInstance *CI
23452360 ROOT::TMetaUtils::Warning (" GenerateModule" , warningMessage.c_str ());
23462361 }
23472362
2348- return WriteAST (outputFile, CI, " " , module );
2363+ // Inform the user and abort if we can't find a module with a given name.
2364+ if (!module ) {
2365+ ROOT::TMetaUtils::Error (" GenerateModule" , " Couldn't find module with name '%s' in modulemap!\n " ,
2366+ moduleName.c_str ());
2367+ return false ;
2368+ }
2369+
2370+ IncludeModuleHeaders (modGen, module , interpreter);
2371+ return true ;
23492372}
23502373
23512374// //////////////////////////////////////////////////////////////////////////////
@@ -4236,9 +4259,48 @@ int RootClingMain(int argc,
42364259
42374260 ROOT::TMetaUtils::SetPathsForRelocatability (clingArgs);
42384261
4262+ // FIXME: This line is from TModuleGenerator, but we can't reuse this code
4263+ // at this point because TModuleGenerator needs a CompilerInstance (and we
4264+ // currently create the arguments for creating said CompilerInstance).
4265+ bool isPCH = (dictpathname == " allDict.cxx" );
4266+ std::string outputFile;
4267+ // Data is in 'outputFile', therefore in the same scope.
4268+ StringRef moduleName;
4269+ // Adding -fmodules to the args will break lexing with __CINT__ defined,
4270+ // and we actually do lex with __CINT__ and reuse this variable later,
4271+ // we have to copy it now.
4272+ auto clingArgsInterpreter = clingArgs;
4273+ if (!isPCH && getenv (" ROOT_MODULES" )) {
4274+ // We just pass -fmodules, the CIFactory will do the rest and configure
4275+ // clang correctly once it sees this flag.
4276+ clingArgsInterpreter.push_back (" -fmodules" );
4277+
4278+ // Specify the module name that we can lookup the module in the modulemap.
4279+ outputFile = llvm::sys::path::stem (sharedLibraryPathName).str ();
4280+ // Try to get the module name in the modulemap based on the filepath.
4281+ moduleName = llvm::sys::path::filename (outputFile);
4282+ moduleName.consume_front (" lib" );
4283+ moduleName.consume_back (" _rdict.pcm" );
4284+
4285+ clingArgsInterpreter.push_back (" -fmodule-name" );
4286+ clingArgsInterpreter.push_back (moduleName.str ());
4287+
4288+ // Set the C++ modules output directory to the directory where we generate
4289+ // the shared library.
4290+ clingArgsInterpreter.push_back (" -fmodules-cache-path=" +
4291+ llvm::sys::path::parent_path (sharedLibraryPathName).str ());
4292+
4293+ // If the user has passed ROOT_MODULES with the value 'DEBUG', then we
4294+ // enable remarks in clang for building on-demand modules. This is useful
4295+ // to figure out when and why on-demand modules are built by clang.
4296+ if (llvm::StringRef (getenv (" ROOT_MODULES" )) == " DEBUG" ) {
4297+ clingArgsInterpreter.push_back (" -Rmodule-build" );
4298+ }
4299+ }
4300+
42394301 // Convert arguments to a C array and check if they are sane
42404302 std::vector<const char *> clingArgsC;
4241- for (auto const & clingArg : clingArgs ) {
4303+ for (auto const &clingArg : clingArgsInterpreter ) {
42424304 if (!IsCorrectClingArgument (clingArg)){
42434305 std::cerr << " Argument \" " << clingArg << " \" is not a supported cling argument. "
42444306 << " This could be mistyped rootcling argument. Please check the commandline.\n " ;
@@ -4879,7 +4941,8 @@ int RootClingMain(int argc,
48794941 if (modGen.IsPCH ()) {
48804942 if (!GenerateAllDict (modGen, CI, currentDirectory)) return 1 ;
48814943 } else if (getenv (" ROOT_MODULES" )) {
4882- if (!GenerateModule (modGen, CI)) return 1 ;
4944+ if (!GenerateModule (modGen, resourceDir, interp, linkdefFilename, moduleName.str ()))
4945+ return 1 ;
48834946 }
48844947 }
48854948 }
@@ -4989,6 +5052,15 @@ int RootClingMain(int argc,
49895052 if (genreflex::verbose)
49905053 tmpCatalog.dump ();
49915054
5055+ // Manually call end of translation unit because we never call the
5056+ // appropriate deconstructors in the interpreter. This writes out the C++
5057+ // module file that we currently generate.
5058+ {
5059+ cling::Interpreter::PushTransactionRAII RAII (&interp);
5060+ CI->getSema ().getASTConsumer ().HandleTranslationUnit (CI->getSema ().getASTContext ());
5061+ CI->clearOutputFiles (CI->getDiagnostics ().hasErrorOccurred ());
5062+ }
5063+
49925064 // Before returning, rename the files
49935065 rootclingRetCode += tmpCatalog.commit ();
49945066
0 commit comments