Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
rootcling_impl.cxx
Go to the documentation of this file.
1// Authors: Axel Naumann, Philippe Canal, Danilo Piparo
2
3/*************************************************************************
4 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "rootcling_impl.h"
12#include "rootclingCommandLineOptionsHelp.h"
13
14#include "RConfigure.h"
15#include <ROOT/RConfig.hxx>
17#include "snprintf.h"
18
19#include <iostream>
20#include <iomanip>
21#include <memory>
22#include <vector>
23#include <algorithm>
24#include <cstdio>
25
26#include <errno.h>
27#include <string>
28#include <list>
29#include <sstream>
30#include <map>
31#include <fstream>
32#include <sys/stat.h>
33#include <unordered_map>
34#include <unordered_set>
35#include <numeric>
36
37
38#ifdef _WIN32
39#ifdef system
40#undef system
41#endif
42#undef UNICODE
43#include <windows.h>
44#include <Tlhelp32.h> // for MAX_MODULE_NAME32
45#include <process.h>
46#define PATH_MAX _MAX_PATH
47#ifdef interface
48// prevent error coming from clang/AST/Attrs.inc
49#undef interface
50#endif
51#endif
52
53#ifdef __APPLE__
54#include <mach-o/dyld.h>
55#endif
56
57#ifdef R__FBSD
58#include <sys/param.h>
59#include <sys/user.h>
60#include <sys/types.h>
61#include <libutil.h>
62#include <libprocstat.h>
63#endif // R__FBSD
64
65#if !defined(R__WIN32)
66#include <limits.h>
67#include <unistd.h>
68#endif
69
70
71#include "cling/Interpreter/Interpreter.h"
72#include "cling/Interpreter/InterpreterCallbacks.h"
73#include "cling/Interpreter/LookupHelper.h"
74#include "cling/Interpreter/Value.h"
75#include "clang/AST/CXXInheritance.h"
76#include "clang/Basic/Diagnostic.h"
77#include "clang/Frontend/CompilerInstance.h"
78#include "clang/Frontend/FrontendActions.h"
79#include "clang/Frontend/FrontendDiagnostic.h"
80#include "clang/Lex/HeaderSearch.h"
81#include "clang/Lex/Preprocessor.h"
82#include "clang/Lex/ModuleMap.h"
83#include "clang/Lex/Pragma.h"
84#include "clang/Sema/Sema.h"
85#include "clang/Serialization/ASTWriter.h"
86#include "cling/Utils/AST.h"
87
88#include "llvm/ADT/StringRef.h"
89
90#include "llvm/Support/CommandLine.h"
91#include "llvm/Support/Path.h"
92#include "llvm/Support/PrettyStackTrace.h"
93#include "llvm/Support/Signals.h"
94
95#include "RtypesCore.h"
96#include "TModuleGenerator.h"
97#include "TClassEdit.h"
98#include "TClingUtils.h"
99#include "RStl.h"
100#include "XMLReader.h"
101#include "LinkdefReader.h"
102#include "DictSelectionReader.h"
103#include "SelectionRules.h"
104#include "Scanner.h"
105#include "strlcpy.h"
106
107#include "OptionParser.h"
108
109#ifdef WIN32
110const std::string gLibraryExtension(".dll");
111#else
112const std::string gLibraryExtension(".so"); // no dylib for the moment
113#endif
115
116#ifdef __APPLE__
117#include <mach-o/dyld.h>
118#endif
119
120#if defined(R__WIN32)
121#include "cygpath.h"
122#define strcasecmp _stricmp
123#define strncasecmp _strnicmp
124#else
125#include <unistd.h>
126#endif
127
128bool gBuildingROOT = false;
130
131#define rootclingStringify(s) rootclingStringifyx(s)
132#define rootclingStringifyx(s) #s
133
134// Maybe too ugly? let's see how it performs.
135using HeadersDeclsMap_t = std::map<std::string, std::list<std::string>>;
136
137using namespace ROOT;
138
139using std::string, std::map, std::ifstream, std::ofstream, std::endl, std::ios, std::vector;
140
141namespace genreflex {
142 bool verbose = false;
143}
144
145////////////////////////////////////////////////////////////////////////////////
146
147static llvm::cl::OptionCategory gRootclingOptions("rootcling common options");
148
149 // FIXME: We should remove after removal of r flag.
150static llvm::cl::opt<bool>
152 llvm::cl::desc("Deprecated. Similar to -f but it ignores the dictionary generation. \
153When -r is present rootcling becomes a tool to generate rootmaps (and capability files)."),
154 llvm::cl::Hidden,
155 llvm::cl::cat(gRootclingOptions));
156
157////////////////////////////////////////////////////////////////////////////////
158
159void SetRootSys();
160
162 // rootcling's libCore needs "our" ROOTSYS:
163 SetRootSys();
164};
165
166////////////////////////////////////////////////////////////////////////////////
167
168void EmitStreamerInfo(const char *normName)
169{
172}
173static void EmitTypedefs(const std::vector<const clang::TypedefNameDecl *> &tdvec)
174{
176 return;
177 for (const auto td : tdvec)
178 gDriverConfig->fAddTypedefToROOTFile(td->getQualifiedNameAsString().c_str());
179}
180static void EmitEnums(const std::vector<const clang::EnumDecl *> &enumvec)
181{
183 return;
184 for (const auto en : enumvec) {
185 // Enums within tag decls are processed as part of the tag.
186 if (clang::isa<clang::TranslationUnitDecl>(en->getDeclContext())
187 || clang::isa<clang::LinkageSpecDecl>(en->getDeclContext())
188 || clang::isa<clang::NamespaceDecl>(en->getDeclContext()))
189 gDriverConfig->fAddEnumToROOTFile(en->getQualifiedNameAsString().c_str());
190 }
191}
192
193////////////////////////////////////////////////////////////////////////////////
194/// Returns the executable path name, used e.g. by SetRootSys().
195
196const char *GetExePath()
197{
198 static std::string exepath;
199 if (exepath == "") {
200#ifdef __APPLE__
201 exepath = _dyld_get_image_name(0);
202#endif
203#if defined(__linux) || defined(__linux__)
204 char linkname[PATH_MAX]; // /proc/<pid>/exe
205 char buf[PATH_MAX]; // exe path name
206 pid_t pid;
207
208 // get our pid and build the name of the link in /proc
209 pid = getpid();
210 snprintf(linkname, PATH_MAX, "/proc/%i/exe", pid);
211 int ret = readlink(linkname, buf, 1024);
212 if (ret > 0 && ret < 1024) {
213 buf[ret] = 0;
214 exepath = buf;
215 }
216#endif
217#if defined(R__FBSD)
218 procstat* ps = procstat_open_sysctl(); //
219 kinfo_proc* kp = kinfo_getproc(getpid());
220
221 if (kp!=NULL) {
222 char path_str[PATH_MAX] = "";
223 procstat_getpathname(ps, kp, path_str, sizeof(path_str));
224 exepath = path_str;
225 }
226
227 free(kp);
228 procstat_close(ps);
229#endif
230#ifdef _WIN32
231 char *buf = new char[MAX_MODULE_NAME32 + 1];
232 ::GetModuleFileName(NULL, buf, MAX_MODULE_NAME32 + 1);
233 char *p = buf;
234 while ((p = strchr(p, '\\')))
235 * (p++) = '/';
236 exepath = buf;
237 delete[] buf;
238#endif
239 }
240 return exepath.c_str();
241}
242
243////////////////////////////////////////////////////////////////////////////////
244
245bool Namespace__HasMethod(const clang::NamespaceDecl *cl, const char *name,
246 const cling::Interpreter &interp)
247{
249}
250
251////////////////////////////////////////////////////////////////////////////////
252
253static void AnnotateFieldDecl(clang::FieldDecl &decl,
254 const std::list<VariableSelectionRule> &fieldSelRules)
255{
256 using namespace ROOT::TMetaUtils;
257 // See if in the VariableSelectionRules there are attributes and names with
258 // which we can annotate.
259 // We may look for a smarter algorithm.
260
261 // Nothing to do then ...
262 if (fieldSelRules.empty()) return;
263
264 clang::ASTContext &C = decl.getASTContext();
265
266 const std::string declName(decl.getNameAsString());
267 std::string varName;
268 for (std::list<VariableSelectionRule>::const_iterator it = fieldSelRules.begin();
269 it != fieldSelRules.end(); ++it) {
270 if (! it->GetAttributeValue(propNames::name, varName)) continue;
271 if (declName == varName) { // we have the rule!
272 // Let's extract the attributes
273 BaseSelectionRule::AttributesMap_t attrMap(it->GetAttributes());
274 BaseSelectionRule::AttributesMap_t::iterator iter;
275 std::string userDefinedProperty;
276 for (iter = attrMap.begin(); iter != attrMap.end(); ++iter) {
277 const std::string &name = iter->first;
278 const std::string &value = iter->second;
279
280 if (name == propNames::name) continue;
281
282 /* This test is here since in ROOT5, when using genreflex,
283 * for pods, iotype is ignored */
284
285 if (name == propNames::iotype &&
286 (decl.getType()->isArrayType() || decl.getType()->isPointerType())) {
287 const char *msg = "Data member \"%s\" is an array or a pointer. "
288 "It is not possible to assign to it the iotype \"%s\". "
289 "This transformation is possible only with data members "
290 "which are not pointers or arrays.\n";
291 ROOT::TMetaUtils::Error("AnnotateFieldDecl",
292 msg, varName.c_str(), value.c_str());
293 continue;
294 }
295
296
297 // These lines are here to use the root pcms. Indeed we need to annotate the AST
298 // before persisting the ProtoClasses in the root pcms.
299 // BEGIN ROOT PCMS
300 if (name == propNames::comment) {
301 decl.addAttr(clang::AnnotateAttr::CreateImplicit(C, value));
302 }
303 // END ROOT PCMS
304
305 if ((name == propNames::transient && value == "true") ||
306 (name == propNames::persistent && value == "false")) { // special case
307 userDefinedProperty = propNames::comment + propNames::separator + "!";
308 // This next line is here to use the root pcms. Indeed we need to annotate the AST
309 // before persisting the ProtoClasses in the root pcms.
310 // BEGIN ROOT PCMS
311 decl.addAttr(clang::AnnotateAttr::CreateImplicit(C, "!"));
312 // END ROOT PCMS
313 // The rest of the lines are not changed to leave in place the system which
314 // works with bulk header parsing on library load.
315 } else {
316 userDefinedProperty = name + propNames::separator + value;
317 }
318 ROOT::TMetaUtils::Info(nullptr, "%s %s\n", varName.c_str(), userDefinedProperty.c_str());
319 decl.addAttr(clang::AnnotateAttr::CreateImplicit(C, userDefinedProperty));
320
321 }
322 }
323 }
324}
325
326////////////////////////////////////////////////////////////////////////////////
327
328void AnnotateDecl(clang::CXXRecordDecl &CXXRD,
329 const RScanner::DeclsSelRulesMap_t &declSelRulesMap,
330 cling::Interpreter &interpreter,
331 bool isGenreflex)
332{
333 // In order to store the meaningful for the IO comments we have to transform
334 // the comment into annotation of the given decl.
335 // This works only with comments in the headers, so no selection rules in an
336 // xml file.
337
338 using namespace clang;
339 SourceLocation commentSLoc;
340 llvm::StringRef comment;
341
342 ASTContext &C = CXXRD.getASTContext();
343
344 // Fetch the selection rule associated to this class
345 clang::Decl *declBaseClassPtr = static_cast<clang::Decl *>(&CXXRD);
346 auto declSelRulePair = declSelRulesMap.find(declBaseClassPtr->getCanonicalDecl());
347 if (declSelRulePair == declSelRulesMap.end()){
348 const std::string thisClassName(CXXRD.getName());
349 ROOT::TMetaUtils::Error("AnnotateDecl","Cannot find class %s in the list of selected classes.\n",thisClassName.c_str());
350 return;
351 }
352 const BaseSelectionRule *thisClassBaseSelectionRule = declSelRulePair->second;
353 // If the rule is there
354 if (thisClassBaseSelectionRule) {
355 // Fetch and loop over Class attributes
356 // if the name of the attribute is not "name", add attr to the ast.
357 BaseSelectionRule::AttributesMap_t::iterator iter;
358 std::string userDefinedProperty;
359 for (auto const & attr : thisClassBaseSelectionRule->GetAttributes()) {
360 const std::string &name = attr.first;
362 const std::string &value = attr.second;
363 userDefinedProperty = name + ROOT::TMetaUtils::propNames::separator + value;
364 if (genreflex::verbose) std::cout << " * " << userDefinedProperty << std::endl;
365 CXXRD.addAttr(AnnotateAttr::CreateImplicit(C, userDefinedProperty));
366 }
367 }
368
369 // See if the rule is a class selection rule (FIX dynamic_cast)
370 const ClassSelectionRule *thisClassSelectionRule = reinterpret_cast<const ClassSelectionRule *>(thisClassBaseSelectionRule);
371
372 for (CXXRecordDecl::decl_iterator I = CXXRD.decls_begin(),
373 E = CXXRD.decls_end(); I != E; ++I) {
374
375 // CXXMethodDecl,FieldDecl and VarDecl inherit from NamedDecl
376 // See: http://clang.llvm.org/doxygen/classclang_1_1DeclaratorDecl.html
377 if (!(*I)->isImplicit()
378 && (isa<CXXMethodDecl>(*I) || isa<FieldDecl>(*I) || isa<VarDecl>(*I))) {
379
380 // For now we allow only a special macro (ClassDef) to have meaningful comments
381 bool isClassDefMacro = TMetaUtils::HasClassDefMacro(*I, interpreter);
382 if (isClassDefMacro) {
383 while (isa<NamedDecl>(*I) && cast<NamedDecl>(*I)->getName() != "DeclFileLine") {
384 ++I;
385 }
386 }
387
388 comment = ROOT::TMetaUtils::GetComment(**I, &commentSLoc);
389 if (comment.size()) {
390 // The ClassDef annotation is for the class itself
391 if (isClassDefMacro) {
392 CXXRD.addAttr(AnnotateAttr::CreateImplicit(C, comment.str()));
393 } else if (!isGenreflex) {
394 // Here we check if we are in presence of a selection file so that
395 // the comment does not ends up as a decoration in the AST,
396 // Nevertheless, w/o PCMS this has no effect, since the headers
397 // are parsed at runtime and the information in the AST dumped by
398 // rootcling is not relevant.
399 (*I)->addAttr(AnnotateAttr::CreateImplicit(C, comment.str()));
400 }
401 }
402 // Match decls with sel rules if we are in presence of a selection file
403 // and the cast was successful
404 if (isGenreflex && thisClassSelectionRule != nullptr) {
405 const std::list<VariableSelectionRule> &fieldSelRules = thisClassSelectionRule->GetFieldSelectionRules();
406
407 // This check is here to avoid asserts in debug mode (LLVMDEV env variable set)
408 if (FieldDecl *fieldDecl = dyn_cast<FieldDecl>(*I)) {
409 AnnotateFieldDecl(*fieldDecl, fieldSelRules);
410 }
411 } // End presence of XML selection file
412 }
413 }
414}
415
416////////////////////////////////////////////////////////////////////////////////
417
418size_t GetFullArrayLength(const clang::ConstantArrayType *arrayType)
419{
420 if (!arrayType)
421 return 0;
422 llvm::APInt len = arrayType->getSize();
423 while (const clang::ConstantArrayType *subArrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual())) {
424 len *= subArrayType->getSize();
425 arrayType = subArrayType;
426 }
427 return len.getLimitedValue();
428}
429
430////////////////////////////////////////////////////////////////////////////////
431
432bool InheritsFromTObject(const clang::RecordDecl *cl,
433 const cling::Interpreter &interp)
434{
435 static const clang::CXXRecordDecl *TObject_decl
436 = ROOT::TMetaUtils::ScopeSearch("TObject", interp, true /*diag*/, nullptr);
437
438 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(cl);
439 return ROOT::TMetaUtils::IsBase(clxx, TObject_decl, nullptr, interp);
440}
441
442////////////////////////////////////////////////////////////////////////////////
443
444bool InheritsFromTSelector(const clang::RecordDecl *cl,
445 const cling::Interpreter &interp)
446{
447 static const clang::CXXRecordDecl *TObject_decl
448 = ROOT::TMetaUtils::ScopeSearch("TSelector", interp, false /*diag*/, nullptr);
449
450 return ROOT::TMetaUtils::IsBase(llvm::dyn_cast<clang::CXXRecordDecl>(cl), TObject_decl, nullptr, interp);
451}
452
453////////////////////////////////////////////////////////////////////////////////
454
455bool IsSelectionXml(const char *filename)
456{
457 size_t len = strlen(filename);
458 size_t xmllen = 4; /* strlen(".xml"); */
459 if (strlen(filename) >= xmllen) {
460 return (0 == strcasecmp(filename + (len - xmllen), ".xml"));
461 } else {
462 return false;
463 }
464}
465
466////////////////////////////////////////////////////////////////////////////////
467
468bool IsLinkdefFile(const clang::PresumedLoc& PLoc)
469{
470 return ROOT::TMetaUtils::IsLinkdefFile(PLoc.getFilename());
471}
472
473////////////////////////////////////////////////////////////////////////////////
474
475bool IsSelectionFile(const char *filename)
476{
478}
479
480////////////////////////////////////////////////////////////////////////////////
481/// Set the ROOTSYS env var based on the executable location.
482
484{
485 const char *exepath = GetExePath();
486 if (exepath && *exepath) {
487#if !defined(_WIN32)
488 char *ep = new char[PATH_MAX];
489 if (!realpath(exepath, ep)) {
490 fprintf(stderr, "rootcling: error getting realpath of rootcling!");
491 strlcpy(ep, exepath, PATH_MAX);
492 }
493#else
494 int nche = strlen(exepath) + 1;
495 char *ep = new char[nche];
496 strlcpy(ep, exepath, nche);
497#endif
498 char *s;
499
500 if ((s = strrchr(ep, '/'))) {
501 // $ROOTSYS/bin/rootcling
502 int removesubdirs = 2;
503 if (!strncmp(s + 1, "rootcling_stage1.exe", 20)) {
504 // $ROOTSYS/bin/rootcling_stage1.exe
505 removesubdirs = 2;
506 gBuildingROOT = true;
507 } else if (!strncmp(s + 1, "rootcling_stage1", 16)) {
508 // $ROOTSYS/core/rootcling_stage1/src/rootcling_stage1
509 removesubdirs = 4;
510 gBuildingROOT = true;
511 }
512 for (int i = 1; s && i < removesubdirs; ++i) {
513 *s = 0;
514 s = strrchr(ep, '/');
515 }
516 if (s) *s = 0;
517 } else {
518 // There was no slashes at all let now change ROOTSYS
519 delete [] ep;
520 return;
521 }
522
523 if (!gBuildingROOT) {
524 delete [] ep;
525 return; // don't mess with user's ROOTSYS.
526 }
527
528 int ncha = strlen(ep) + 10;
529 char *env = new char[ncha];
530 snprintf(env, ncha, "ROOTSYS=%s", ep);
531
532 if (gDriverConfig) {
533 // After the putenv below, gRootDir might point to the old ROOTSYS
534 // entry, i.e. to deleted memory. Update it.
535 const char** pRootDir = gDriverConfig->fPRootDir;
536 if (pRootDir) {
537 *pRootDir = env + 8;
538 }
539 }
540
541 putenv(env);
542 // intentionally not call delete [] env, while GLIBC keep use pointer
543 delete [] ep;
544 }
545}
546
547////////////////////////////////////////////////////////////////////////////////
548/// Check whether the `#pragma` line contains expectedTokens (0-terminated array).
549
550bool ParsePragmaLine(const std::string &line,
551 const char *expectedTokens[],
552 size_t *end = nullptr)
553{
554 if (end) *end = 0;
555 if (line[0] != '#') return false;
556 size_t pos = 1;
557 for (const char **iToken = expectedTokens; *iToken; ++iToken) {
558 while (isspace(line[pos])) ++pos;
559 size_t lenToken = strlen(*iToken);
560 if (line.compare(pos, lenToken, *iToken)) {
561 if (end) *end = pos;
562 return false;
563 }
564 pos += lenToken;
565 }
566 if (end) *end = pos;
567 return true;
568}
569
570
571map<string, string> gAutoloads;
573
574////////////////////////////////////////////////////////////////////////////////
575
576void RecordDeclCallback(const clang::RecordDecl* recordDecl)
577{
578 std::string need;
579 if (recordDecl->hasOwningModule()) {
580 clang::Module *M = recordDecl->getOwningModule()->getTopLevelModule();
581 need = "lib" + M->Name + gLibraryExtension;
582 } else {
583 std::string qual_name;
584 RScanner::GetDeclQualName(recordDecl, qual_name);
585
586 need = gAutoloads[qual_name];
587 }
588
589 if (need.length() && gLibsNeeded.find(need) == string::npos) {
590 gLibsNeeded += " " + need;
591 }
592}
593
594////////////////////////////////////////////////////////////////////////////////
595
596void CheckClassNameForRootMap(const std::string &classname, map<string, string> &autoloads)
597{
598 if (classname.find(':') == std::string::npos) return;
599
600 // We have a namespace and we have to check it first
601 int slen = classname.size();
602 for (int k = 0; k < slen; ++k) {
603 if (classname[k] == ':') {
604 if (k + 1 >= slen || classname[k + 1] != ':') {
605 // we expected another ':'
606 break;
607 }
608 if (k) {
609 string base = classname.substr(0, k);
610 if (base == "std") {
611 // std is not declared but is also ignored by CINT!
612 break;
613 } else {
614 autoloads[base] = ""; // We never load namespaces on their own.
615 }
616 ++k;
617 }
618 } else if (classname[k] == '<') {
619 // We do not want to look at the namespace inside the template parameters!
620 break;
621 }
622 }
623}
624
625////////////////////////////////////////////////////////////////////////////////
626/// Parse the rootmap and add entries to the autoload map
627
628void ParseRootMapFile(ifstream &file, map<string, string> &autoloads)
629{
630 std::string classname;
631 std::string line;
632 while (file >> line) {
633
634 if (line.find("Library.") != 0) continue;
635
636 int pos = line.find(":", 8);
637 classname = line.substr(8, pos - 8);
638
639 ROOT::TMetaUtils::ReplaceAll(classname, "@@", "::");
640 ROOT::TMetaUtils::ReplaceAll(classname, "-", " ");
641
642 getline(file, line, '\n');
643 while (line[0] == ' ') line.replace(0, 1, "");
644
645 CheckClassNameForRootMap(classname, autoloads);
646
647 if (classname == "ROOT::TImpProxy") {
648 // Do not register the ROOT::TImpProxy so that they can be instantiated.
649 continue;
650 }
651 autoloads[classname] = line;
652 }
653
654}
655
656////////////////////////////////////////////////////////////////////////////////
657/// Parse the rootmap and add entries to the autoload map, using the new format
658
659void ParseRootMapFileNewFormat(ifstream &file, map<string, string> &autoloads)
660{
661 std::string keyname;
662 std::string libs;
663 std::string line;
664
665 // For "class ", "namespace " and "typedef " respectively
666 const std::unordered_map<char, unsigned int> keyLenMap = {{'c', 6}, {'n', 10}, {'t', 8}};
667
668 while (getline(file, line, '\n')) {
669 if (line == "{ decls }") {
670 while (getline(file, line, '\n')) {
671 if (line[0] == '[') break;
672 }
673 }
674 const char firstChar = line[0];
675 if (firstChar == '[') {
676 // new section
677 libs = line.substr(1, line.find(']') - 1);
678 while (libs[0] == ' ') libs.replace(0, 1, "");
679 } else if (0 != keyLenMap.count(firstChar)) {
680 unsigned int keyLen = keyLenMap.at(firstChar);
681 keyname = line.substr(keyLen, line.length() - keyLen);
682 CheckClassNameForRootMap(keyname, autoloads);
683 autoloads[keyname] = libs;
684 }
685 }
686
687}
688
689////////////////////////////////////////////////////////////////////////////////
690/// Fill the map of libraries to be loaded in presence of a class
691/// Transparently support the old and new rootmap file format
692
693void LoadLibraryMap(const std::string &fileListName, map<string, string> &autoloads)
694{
695 std::ifstream filelist(fileListName.c_str());
696
697 std::string filename;
698 std::string line;
699
700 while (filelist >> filename) {
701
702 if (llvm::sys::fs::is_directory(filename)) continue;
703
704 ifstream file(filename.c_str());
705
706 // Check which format is this
707 file >> line;
708 bool new_format = (line[0] == '[' || line[0] == '{') ;
709 file.clear();
710 file.seekg(0, std::ios::beg);
711
712 // Now act
713 if (new_format) {
714 ParseRootMapFileNewFormat(file, autoloads);
715 } else {
716 ParseRootMapFile(file, autoloads);
717 }
718
719 file.close();
720
721 } // end loop on files
722 filelist.close();
723}
724
725////////////////////////////////////////////////////////////////////////////////
726/// Check if the specified operator (what) has been properly declared if the user has
727/// requested a custom version.
728
729bool CheckInputOperator(const char *what,
730 const char *proto,
731 const string &fullname,
732 const clang::RecordDecl *cl,
733 cling::Interpreter &interp)
734{
735
736 const clang::FunctionDecl *method
737 = ROOT::TMetaUtils::GetFuncWithProto(llvm::dyn_cast<clang::Decl>(cl->getDeclContext()), what, proto, interp,
738 false /*diags*/);
739 if (!method) {
740 // This intended to find the global scope.
741 clang::TranslationUnitDecl *TU =
742 cl->getASTContext().getTranslationUnitDecl();
743 method = ROOT::TMetaUtils::GetFuncWithProto(TU, what, proto, interp,
744 false /*diags*/);
745 }
746 bool has_input_error = false;
747 if (method != nullptr && (method->getAccess() == clang::AS_public || method->getAccess() == clang::AS_none)) {
748 std::string filename = ROOT::TMetaUtils::GetFileName(*method, interp);
749 if (strstr(filename.c_str(), "TBuffer.h") != nullptr ||
750 strstr(filename.c_str(), "Rtypes.h") != nullptr) {
751
752 has_input_error = true;
753 }
754 } else {
755 has_input_error = true;
756 }
757 if (has_input_error) {
758 // We don't want to generate duplicated error messages in several dictionaries (when generating temporaries)
759 const char *maybeconst = "";
760 const char *mayberef = "&";
761 if (what[strlen(what) - 1] == '<') {
762 maybeconst = "const ";
763 mayberef = "";
764 }
766 "in this version of ROOT, the option '!' used in a linkdef file\n"
767 " implies the actual existence of customized operators.\n"
768 " The following declaration is now required:\n"
769 " TBuffer &%s(TBuffer &,%s%s *%s);\n", what, maybeconst, fullname.c_str(), mayberef);
770 }
771 return has_input_error;
772
773}
774
775////////////////////////////////////////////////////////////////////////////////
776/// Check if the operator>> has been properly declared if the user has
777/// requested a custom version.
778
779bool CheckInputOperator(const clang::RecordDecl *cl, cling::Interpreter &interp)
780{
781 string fullname;
783 int ncha = fullname.length() + 13;
784 char *proto = new char[ncha];
785 snprintf(proto, ncha, "TBuffer&,%s*&", fullname.c_str());
786
787 ROOT::TMetaUtils::Info(nullptr, "Class %s: Do not generate operator>>()\n",
788 fullname.c_str());
789
790 // We do want to call both CheckInputOperator all the times.
791 bool has_input_error = CheckInputOperator("operator>>", proto, fullname, cl, interp);
792 has_input_error = CheckInputOperator("operator<<", proto, fullname, cl, interp) || has_input_error;
793
794 delete [] proto;
795
796 return has_input_error;
797}
798
799////////////////////////////////////////////////////////////////////////////////
800/// Return false if the class does not have ClassDef even-though it should.
801
802bool CheckClassDef(const clang::RecordDecl &cl, const cling::Interpreter &interp)
803{
804
805 // Detect if the class has a ClassDef
806 bool hasClassDef = ROOT::TMetaUtils::ClassInfo__HasMethod(&cl, "Class_Version", interp);
807
808 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(&cl);
809 if (!clxx) {
810 return false;
811 }
812 bool isAbstract = clxx->isAbstract();
813
814 if (!isAbstract && InheritsFromTObject(clxx, interp) && !InheritsFromTSelector(clxx, interp) && !hasClassDef) {
815 std::string qualName;
817 const char *qualName_c = qualName.c_str();
818 ROOT::TMetaUtils::Warning(qualName_c, "The data members of %s will not be stored, "
819 "because it inherits from TObject but does not "
820 "have its own ClassDef.\n",
821 qualName_c);
822 }
823
824 return true;
825}
826
827////////////////////////////////////////////////////////////////////////////////
828/// Return the name of the data member so that it can be used
829/// by non-const operation (so it includes a const_cast if necessary).
830
831string GetNonConstMemberName(const clang::FieldDecl &m, const string &prefix = "")
832{
833 if (m.getType().isConstQualified()) {
834 string ret = "const_cast< ";
835 string type_name;
836 ROOT::TMetaUtils::GetQualifiedName(type_name, m.getType(), m);
837 if (type_name.substr(0,6)=="const ") {
838 ret += type_name.c_str()+6;
839 } else {
840 ret += type_name;
841 }
842 ret += " &>( ";
843 ret += prefix;
844 ret += m.getName().str();
845 ret += " )";
846 return ret;
847 } else {
848 return prefix + m.getName().str();
849 }
850}
851
852////////////////////////////////////////////////////////////////////////////////
853/// Create Streamer code for an STL container. Returns 1 if data member
854/// was an STL container and if Streamer code has been created, 0 otherwise.
855
856int STLContainerStreamer(const clang::FieldDecl &m,
857 int rwmode,
858 const cling::Interpreter &interp,
859 const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt,
860 std::ostream &dictStream)
861{
863 std::string mTypename;
864 ROOT::TMetaUtils::GetQualifiedName(mTypename, m.getType(), m);
865
866 const clang::CXXRecordDecl *clxx = llvm::dyn_cast_or_null<clang::CXXRecordDecl>(ROOT::TMetaUtils::GetUnderlyingRecordDecl(m.getType()));
867
868 if (stltype == ROOT::kNotSTL) {
869 return 0;
870 }
871 // fprintf(stderr,"Add %s (%d) which is also %s\n",
872 // m.Type()->Name(), stltype, m.Type()->TrueName() );
873 clang::QualType utype(ROOT::TMetaUtils::GetUnderlyingType(m.getType()), 0);
874 Internal::RStl::Instance().GenerateTClassFor(utype, interp, normCtxt);
875
876 if (!clxx || clxx->getTemplateSpecializationKind() == clang::TSK_Undeclared) return 0;
877
878 const clang::ClassTemplateSpecializationDecl *tmplt_specialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl> (clxx);
879 if (!tmplt_specialization) return 0;
880
881 string stlType(ROOT::TMetaUtils::ShortTypeName(mTypename.c_str()));
882 string stlName;
883 stlName = ROOT::TMetaUtils::ShortTypeName(m.getName().str().c_str());
884
885 string fulName1, fulName2;
886 const char *tcl1 = nullptr, *tcl2 = nullptr;
887 const clang::TemplateArgument &arg0(tmplt_specialization->getTemplateArgs().get(0));
888 clang::QualType ti = arg0.getAsType();
889
890 if (ROOT::TMetaUtils::ElementStreamer(dictStream, m, ti, nullptr, rwmode, interp)) {
891 tcl1 = "R__tcl1";
892 fulName1 = ti.getAsString(); // Should we be passing a context?
893 }
894 if (stltype == kSTLmap || stltype == kSTLmultimap) {
895 const clang::TemplateArgument &arg1(tmplt_specialization->getTemplateArgs().get(1));
896 clang::QualType tmplti = arg1.getAsType();
897 if (ROOT::TMetaUtils::ElementStreamer(dictStream, m, tmplti, nullptr, rwmode, interp)) {
898 tcl2 = "R__tcl2";
899 fulName2 = tmplti.getAsString(); // Should we be passing a context?
900 }
901 }
902
903 int isArr = 0;
904 int len = 1;
905 int pa = 0;
906 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(m.getType().getTypePtr());
907 if (arrayType) {
908 isArr = 1;
909 len = GetFullArrayLength(arrayType);
910 pa = 1;
911 while (arrayType) {
912 if (arrayType->getArrayElementTypeNoTypeQual()->isPointerType()) {
913 pa = 3;
914 break;
915 }
916 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
917 }
918 } else if (m.getType()->isPointerType()) {
919 pa = 2;
920 }
921 if (rwmode == 0) {
922 // create read code
923 dictStream << " {" << std::endl;
924 if (isArr) {
925 dictStream << " for (Int_t R__l = 0; R__l < " << len << "; R__l++) {" << std::endl;
926 }
927
928 switch (pa) {
929 case 0: //No pointer && No array
930 dictStream << " " << stlType.c_str() << " &R__stl = " << stlName.c_str() << ";" << std::endl;
931 break;
932 case 1: //No pointer && array
933 dictStream << " " << stlType.c_str() << " &R__stl = " << stlName.c_str() << "[R__l];" << std::endl;
934 break;
935 case 2: //pointer && No array
936 dictStream << " delete *" << stlName.c_str() << ";" << std::endl
937 << " *" << stlName.c_str() << " = new " << stlType.c_str() << ";" << std::endl
938 << " " << stlType.c_str() << " &R__stl = **" << stlName.c_str() << ";" << std::endl;
939 break;
940 case 3: //pointer && array
941 dictStream << " delete " << stlName.c_str() << "[R__l];" << std::endl
942 << " " << stlName.c_str() << "[R__l] = new " << stlType.c_str() << ";" << std::endl
943 << " " << stlType.c_str() << " &R__stl = *" << stlName.c_str() << "[R__l];" << std::endl;
944 break;
945 }
946
947 dictStream << " R__stl.clear();" << std::endl;
948
949 if (tcl1) {
950 dictStream << " TClass *R__tcl1 = TBuffer::GetClass(typeid(" << fulName1.c_str() << "));" << std::endl
951 << " if (R__tcl1==0) {" << std::endl
952 << " Error(\"" << stlName.c_str() << " streamer\",\"Missing the TClass object for "
953 << fulName1.c_str() << "!\");" << std::endl
954 << " return;" << std::endl
955 << " }" << std::endl;
956 }
957 if (tcl2) {
958 dictStream << " TClass *R__tcl2 = TBuffer::GetClass(typeid(" << fulName2.c_str() << "));" << std::endl
959 << " if (R__tcl2==0) {" << std::endl
960 << " Error(\"" << stlName.c_str() << " streamer\",\"Missing the TClass object for "
961 << fulName2.c_str() << "!\");" << std::endl
962 << " return;" << std::endl
963 << " }" << std::endl;
964 }
965
966 dictStream << " int R__i, R__n;" << std::endl
967 << " R__b >> R__n;" << std::endl;
968
969 if (stltype == kSTLvector) {
970 dictStream << " R__stl.reserve(R__n);" << std::endl;
971 }
972 dictStream << " for (R__i = 0; R__i < R__n; R__i++) {" << std::endl;
973
974 ROOT::TMetaUtils::ElementStreamer(dictStream, m, arg0.getAsType(), "R__t", rwmode, interp, tcl1);
975 if (stltype == kSTLmap || stltype == kSTLmultimap) { //Second Arg
976 const clang::TemplateArgument &arg1(tmplt_specialization->getTemplateArgs().get(1));
977 ROOT::TMetaUtils::ElementStreamer(dictStream, m, arg1.getAsType(), "R__t2", rwmode, interp, tcl2);
978 }
979
980 /* Need to go from
981 type R__t;
982 R__t.Stream;
983 vec.push_back(R__t);
984 to
985 vec.push_back(type());
986 R__t_p = &(vec.last());
987 *R__t_p->Stream;
988
989 */
990 switch (stltype) {
991
992 case kSTLmap:
993 case kSTLmultimap:
994 case kSTLunorderedmap:
996 std::string keyName(ti.getAsString());
997 dictStream << " typedef " << keyName << " Value_t;" << std::endl
998 << " std::pair<Value_t const, " << tmplt_specialization->getTemplateArgs().get(1).getAsType().getAsString() << " > R__t3(R__t,R__t2);" << std::endl
999 << " R__stl.insert(R__t3);" << std::endl;
1000 //fprintf(fp, " R__stl.insert(%s::value_type(R__t,R__t2));\n",stlType.c_str());
1001 break;
1002 }
1003 case kSTLset:
1004 case kSTLunorderedset:
1006 case kSTLmultiset:
1007 dictStream << " R__stl.insert(R__t);" << std::endl;
1008 break;
1009 case kSTLvector:
1010 case kSTLlist:
1011 case kSTLdeque:
1012 dictStream << " R__stl.push_back(R__t);" << std::endl;
1013 break;
1014 case kSTLforwardlist:
1015 dictStream << " R__stl.push_front(R__t);" << std::endl;
1016 break;
1017 default:
1018 assert(0);
1019 }
1020 dictStream << " }" << std::endl
1021 << " }" << std::endl;
1022 if (isArr) dictStream << " }" << std::endl;
1023
1024 } else {
1025
1026 // create write code
1027 if (isArr) {
1028 dictStream << " for (Int_t R__l = 0; R__l < " << len << "; R__l++) {" << std::endl;
1029 }
1030 dictStream << " {" << std::endl;
1031 switch (pa) {
1032 case 0: //No pointer && No array
1033 dictStream << " " << stlType.c_str() << " &R__stl = " << stlName.c_str() << ";" << std::endl;
1034 break;
1035 case 1: //No pointer && array
1036 dictStream << " " << stlType.c_str() << " &R__stl = " << stlName.c_str() << "[R__l];" << std::endl;
1037 break;
1038 case 2: //pointer && No array
1039 dictStream << " " << stlType.c_str() << " &R__stl = **" << stlName.c_str() << ";" << std::endl;
1040 break;
1041 case 3: //pointer && array
1042 dictStream << " " << stlType.c_str() << " &R__stl = *" << stlName.c_str() << "[R__l];" << std::endl;
1043 break;
1044 }
1045
1046 dictStream << " int R__n=int(R__stl.size());" << std::endl
1047 << " R__b << R__n;" << std::endl
1048 << " if(R__n) {" << std::endl;
1049
1050 if (tcl1) {
1051 dictStream << " TClass *R__tcl1 = TBuffer::GetClass(typeid(" << fulName1.c_str() << "));" << std::endl
1052 << " if (R__tcl1==0) {" << std::endl
1053 << " Error(\"" << stlName.c_str() << " streamer\",\"Missing the TClass object for "
1054 << fulName1.c_str() << "!\");" << std::endl
1055 << " return;" << std::endl
1056 << " }" << std::endl;
1057 }
1058 if (tcl2) {
1059 dictStream << " TClass *R__tcl2 = TBuffer::GetClass(typeid(" << fulName2.c_str() << "));" << std::endl
1060 << " if (R__tcl2==0) {" << std::endl
1061 << " Error(\"" << stlName.c_str() << "streamer\",\"Missing the TClass object for " << fulName2.c_str() << "!\");" << std::endl
1062 << " return;" << std::endl
1063 << " }" << std::endl;
1064 }
1065
1066 dictStream << " " << stlType.c_str() << "::iterator R__k;" << std::endl
1067 << " for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {" << std::endl;
1068 if (stltype == kSTLmap || stltype == kSTLmultimap) {
1069 const clang::TemplateArgument &arg1(tmplt_specialization->getTemplateArgs().get(1));
1070 clang::QualType tmplti = arg1.getAsType();
1071 ROOT::TMetaUtils::ElementStreamer(dictStream, m, ti, "((*R__k).first )", rwmode, interp, tcl1);
1072 ROOT::TMetaUtils::ElementStreamer(dictStream, m, tmplti, "((*R__k).second)", rwmode, interp, tcl2);
1073 } else {
1074 ROOT::TMetaUtils::ElementStreamer(dictStream, m, ti, "(*R__k)" , rwmode, interp, tcl1);
1075 }
1076
1077 dictStream << " }" << std::endl
1078 << " }" << std::endl
1079 << " }" << std::endl;
1080 if (isArr) dictStream << " }" << std::endl;
1081 }
1082 return 1;
1083}
1084
1085////////////////////////////////////////////////////////////////////////////////
1086/// Create Streamer code for a standard string object. Returns 1 if data
1087/// member was a standard string and if Streamer code has been created,
1088/// 0 otherwise.
1089
1090int STLStringStreamer(const clang::FieldDecl &m, int rwmode, std::ostream &dictStream)
1091{
1092 std::string mTypenameStr;
1093 ROOT::TMetaUtils::GetQualifiedName(mTypenameStr, m.getType(), m);
1094 // Note: here we could to a direct type comparison!
1095 const char *mTypeName = ROOT::TMetaUtils::ShortTypeName(mTypenameStr.c_str());
1096 if (!strcmp(mTypeName, "string")) {
1097
1098 std::string fieldname = m.getName().str();
1099 if (rwmode == 0) {
1100 // create read mode
1101 if (m.getType()->isConstantArrayType()) {
1102 if (m.getType().getTypePtr()->getArrayElementTypeNoTypeQual()->isPointerType()) {
1103 dictStream << "// Array of pointer to std::string are not supported (" << fieldname << "\n";
1104 } else {
1105 std::stringstream fullIdx;
1106 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(m.getType().getTypePtr());
1107 int dim = 0;
1108 while (arrayType) {
1109 dictStream << " for (int R__i" << dim << "=0; R__i" << dim << "<"
1110 << arrayType->getSize().getLimitedValue() << "; ++R__i" << dim << " )" << std::endl;
1111 fullIdx << "[R__i" << dim << "]";
1112 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
1113 ++dim;
1114 }
1115 dictStream << " { TString R__str; R__str.Streamer(R__b); "
1116 << fieldname << fullIdx.str() << " = R__str.Data();}" << std::endl;
1117 }
1118 } else {
1119 dictStream << " { TString R__str; R__str.Streamer(R__b); ";
1120 if (m.getType()->isPointerType())
1121 dictStream << "if (*" << fieldname << ") delete *" << fieldname << "; (*"
1122 << fieldname << " = new string(R__str.Data())); }" << std::endl;
1123 else
1124 dictStream << fieldname << " = R__str.Data(); }" << std::endl;
1125 }
1126 } else {
1127 // create write mode
1128 if (m.getType()->isPointerType())
1129 dictStream << " { TString R__str; if (*" << fieldname << ") R__str = (*"
1130 << fieldname << ")->c_str(); R__str.Streamer(R__b);}" << std::endl;
1131 else if (m.getType()->isConstantArrayType()) {
1132 std::stringstream fullIdx;
1133 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(m.getType().getTypePtr());
1134 int dim = 0;
1135 while (arrayType) {
1136 dictStream << " for (int R__i" << dim << "=0; R__i" << dim << "<"
1137 << arrayType->getSize().getLimitedValue() << "; ++R__i" << dim << " )" << std::endl;
1138 fullIdx << "[R__i" << dim << "]";
1139 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
1140 ++dim;
1141 }
1142 dictStream << " { TString R__str(" << fieldname << fullIdx.str() << ".c_str()); R__str.Streamer(R__b);}" << std::endl;
1143 } else
1144 dictStream << " { TString R__str = " << fieldname << ".c_str(); R__str.Streamer(R__b);}" << std::endl;
1145 }
1146 return 1;
1147 }
1148 return 0;
1149}
1150
1151////////////////////////////////////////////////////////////////////////////////
1152
1153bool isPointerToPointer(const clang::FieldDecl &m)
1154{
1155 if (m.getType()->isPointerType()) {
1156 if (m.getType()->getPointeeType()->isPointerType()) {
1157 return true;
1158 }
1159 }
1160 return false;
1161}
1162
1163////////////////////////////////////////////////////////////////////////////////
1164/// Write "[0]" for all but the 1st dimension.
1165
1166void WriteArrayDimensions(const clang::QualType &type, std::ostream &dictStream)
1167{
1168 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(type.getTypePtr());
1169 if (arrayType) {
1170 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
1171 while (arrayType) {
1172 dictStream << "[0]";
1173 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
1174 }
1175 }
1176}
1177
1178////////////////////////////////////////////////////////////////////////////////
1179/// Write the code to set the class name and the initialization object.
1180
1181void WriteClassFunctions(const clang::CXXRecordDecl *cl, std::ostream &dictStream, bool autoLoad = false)
1182{
1183 bool add_template_keyword = ROOT::TMetaUtils::NeedTemplateKeyword(cl);
1184
1185 string fullname;
1186 string clsname;
1187 string nsname;
1188 int enclSpaceNesting = 0;
1189
1190 if (ROOT::TMetaUtils::GetNameWithinNamespace(fullname, clsname, nsname, cl)) {
1191 enclSpaceNesting = ROOT::TMetaUtils::WriteNamespaceHeader(dictStream, cl);
1192 }
1193
1194 if (autoLoad)
1195 dictStream << "#include \"TInterpreter.h\"\n";
1196
1197 dictStream << "//_______________________________________"
1198 << "_______________________________________" << std::endl;
1199 if (add_template_keyword) dictStream << "template <> ";
1200 dictStream << "atomic_TClass_ptr " << clsname << "::fgIsA(nullptr); // static to hold class pointer" << std::endl
1201 << std::endl
1202
1203 << "//_______________________________________"
1204 << "_______________________________________" << std::endl;
1205 if (add_template_keyword) dictStream << "template <> ";
1206 dictStream << "const char *" << clsname << "::Class_Name()" << std::endl << "{" << std::endl
1207 << " return \"" << fullname << "\";" << std::endl << "}" << std::endl << std::endl;
1208
1209 dictStream << "//_______________________________________"
1210 << "_______________________________________" << std::endl;
1211 if (add_template_keyword) dictStream << "template <> ";
1212 dictStream << "const char *" << clsname << "::ImplFileName()" << std::endl << "{" << std::endl
1213 << " return ::ROOT::GenerateInitInstanceLocal((const ::" << fullname
1214 << "*)nullptr)->GetImplFileName();" << std::endl << "}" << std::endl << std::endl
1215
1216 << "//_______________________________________"
1217 << "_______________________________________" << std::endl;
1218 if (add_template_keyword) dictStream << "template <> ";
1219 dictStream << "int " << clsname << "::ImplFileLine()" << std::endl << "{" << std::endl
1220 << " return ::ROOT::GenerateInitInstanceLocal((const ::" << fullname
1221 << "*)nullptr)->GetImplFileLine();" << std::endl << "}" << std::endl << std::endl
1222
1223 << "//_______________________________________"
1224 << "_______________________________________" << std::endl;
1225 if (add_template_keyword) dictStream << "template <> ";
1226 dictStream << "TClass *" << clsname << "::Dictionary()" << std::endl << "{" << std::endl;
1227
1228 // Trigger autoloading if dictionary is split
1229 if (autoLoad)
1230 dictStream << " gInterpreter->AutoLoad(\"" << fullname << "\");\n";
1231 dictStream << " fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::" << fullname
1232 << "*)nullptr)->GetClass();" << std::endl
1233 << " return fgIsA;\n"
1234 << "}" << std::endl << std::endl
1235
1236 << "//_______________________________________"
1237 << "_______________________________________" << std::endl;
1238 if (add_template_keyword) dictStream << "template <> ";
1239 dictStream << "TClass *" << clsname << "::Class()" << std::endl << "{" << std::endl;
1240 if (autoLoad) {
1241 dictStream << " Dictionary();\n";
1242 } else {
1243 dictStream << " if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::";
1244 dictStream << fullname << "*)nullptr)->GetClass(); }" << std::endl;
1245 }
1246 dictStream << " return fgIsA;" << std::endl
1247 << "}" << std::endl << std::endl;
1248
1249 while (enclSpaceNesting) {
1250 dictStream << "} // namespace " << nsname << std::endl;
1251 --enclSpaceNesting;
1252 }
1253}
1254
1255////////////////////////////////////////////////////////////////////////////////
1256/// Write the code to initialize the namespace name and the initialization object.
1257
1258void WriteNamespaceInit(const clang::NamespaceDecl *cl,
1259 cling::Interpreter &interp,
1260 std::ostream &dictStream)
1261{
1262 if (cl->isAnonymousNamespace()) {
1263 // Don't write a GenerateInitInstance for the anonymous namespaces.
1264 return;
1265 }
1266
1267 // coverity[fun_call_w_exception] - that's just fine.
1268 string classname = ROOT::TMetaUtils::GetQualifiedName(*cl).c_str();
1269 string mappedname;
1270 TMetaUtils::GetCppName(mappedname, classname.c_str());
1271
1272 int nesting = 0;
1273 // We should probably unwind the namespace to properly nest it.
1274 if (classname != "ROOT") {
1275 nesting = ROOT::TMetaUtils::WriteNamespaceHeader(dictStream,cl);
1276 }
1277
1278 dictStream << " namespace ROOTDict {" << std::endl;
1279
1280#if !defined(R__AIX)
1281 dictStream << " inline ::ROOT::TGenericClassInfo *GenerateInitInstance();" << std::endl;
1282#endif
1283
1284 if (!Namespace__HasMethod(cl, "Dictionary", interp))
1285 dictStream << " static TClass *" << mappedname.c_str() << "_Dictionary();" << std::endl;
1286 dictStream << std::endl
1287
1288 << " // Function generating the singleton type initializer" << std::endl
1289
1290#if !defined(R__AIX)
1291 << " inline ::ROOT::TGenericClassInfo *GenerateInitInstance()" << std::endl
1292 << " {" << std::endl
1293#else
1294 << " ::ROOT::TGenericClassInfo *GenerateInitInstance()" << std::endl
1295 << " {" << std::endl
1296#endif
1297
1298 << " static ::ROOT::TGenericClassInfo " << std::endl
1299
1300 << " instance(\"" << classname.c_str() << "\", ";
1301
1302 if (Namespace__HasMethod(cl, "Class_Version", interp)) {
1303 dictStream << "::" << classname.c_str() << "::Class_Version(), ";
1304 } else {
1305 dictStream << "0 /*version*/, ";
1306 }
1307
1308 std::string filename = ROOT::TMetaUtils::GetFileName(*cl, interp);
1309 for (unsigned int i = 0; i < filename.length(); i++) {
1310 if (filename[i] == '\\') filename[i] = '/';
1311 }
1312 dictStream << "\"" << filename << "\", " << ROOT::TMetaUtils::GetLineNumber(cl) << "," << std::endl
1313 << " ::ROOT::Internal::DefineBehavior((void*)nullptr,(void*)nullptr)," << std::endl
1314 << " ";
1315
1316 if (Namespace__HasMethod(cl, "Dictionary", interp)) {
1317 dictStream << "&::" << classname.c_str() << "::Dictionary, ";
1318 } else {
1319 dictStream << "&" << mappedname.c_str() << "_Dictionary, ";
1320 }
1321
1322 dictStream << 0 << ");" << std::endl
1323
1324 << " return &instance;" << std::endl
1325 << " }" << std::endl
1326 << " // Insure that the inline function is _not_ optimized away by the compiler\n"
1327 << " ::ROOT::TGenericClassInfo *(*_R__UNIQUE_DICT_(InitFunctionKeeper))() = &GenerateInitInstance; " << std::endl
1328 << " // Static variable to force the class initialization" << std::endl
1329 // must be one long line otherwise R__UseDummy does not work
1330 << " static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstance();"
1331 << " R__UseDummy(_R__UNIQUE_DICT_(Init));" << std::endl;
1332
1333 if (!Namespace__HasMethod(cl, "Dictionary", interp)) {
1334 dictStream << std::endl << " // Dictionary for non-ClassDef classes" << std::endl
1335 << " static TClass *" << mappedname.c_str() << "_Dictionary() {" << std::endl
1336 << " return GenerateInitInstance()->GetClass();" << std::endl
1337 << " }" << std::endl << std::endl;
1338 }
1339
1340 dictStream << " }" << std::endl;
1341 while (nesting--) {
1342 dictStream << "}" << std::endl;
1343 }
1344 dictStream << std::endl;
1345}
1346
1347////////////////////////////////////////////////////////////////////////////////
1348/// GrabIndex returns a static string (so use it or copy it immediately, do not
1349/// call GrabIndex twice in the same expression) containing the size of the
1350/// array data member.
1351/// In case of error, or if the size is not specified, GrabIndex returns 0.
1352
1353llvm::StringRef GrabIndex(const cling::Interpreter& interp, const clang::FieldDecl &member, int printError)
1354{
1355 int error;
1356 llvm::StringRef where;
1357
1358 llvm::StringRef index = ROOT::TMetaUtils::DataMemberInfo__ValidArrayIndex(interp, member, &error, &where);
1359 if (index.size() == 0 && printError) {
1360 const char *errorstring;
1361 switch (error) {
1363 errorstring = "is not an integer";
1364 break;
1366 errorstring = "has not been defined before the array";
1367 break;
1369 errorstring = "is a private member of a parent class";
1370 break;
1372 errorstring = "is not known";
1373 break;
1374 default:
1375 errorstring = "UNKNOWN ERROR!!!!";
1376 }
1377
1378 if (where.size() == 0) {
1379 ROOT::TMetaUtils::Error(nullptr, "*** Datamember %s::%s: no size indication!\n",
1380 member.getParent()->getName().str().c_str(), member.getName().str().c_str());
1381 } else {
1382 ROOT::TMetaUtils::Error(nullptr, "*** Datamember %s::%s: size of array (%s) %s!\n",
1383 member.getParent()->getName().str().c_str(), member.getName().str().c_str(), where.str().c_str(), errorstring);
1384 }
1385 }
1386 return index;
1387}
1388
1389////////////////////////////////////////////////////////////////////////////////
1390
1392 const cling::Interpreter &interp,
1393 const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt,
1394 std::ostream &dictStream)
1395{
1396 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl());
1397 if (clxx == nullptr) return;
1398
1399 bool add_template_keyword = ROOT::TMetaUtils::NeedTemplateKeyword(clxx);
1400
1401 string fullname;
1402 string clsname;
1403 string nsname;
1404 int enclSpaceNesting = 0;
1405
1406 if (ROOT::TMetaUtils::GetNameWithinNamespace(fullname, clsname, nsname, clxx)) {
1407 enclSpaceNesting = ROOT::TMetaUtils::WriteNamespaceHeader(dictStream, cl);
1408 }
1409
1410 dictStream << "//_______________________________________"
1411 << "_______________________________________" << std::endl;
1412 if (add_template_keyword) dictStream << "template <> ";
1413 dictStream << "void " << clsname << "::Streamer(TBuffer &R__b)" << std::endl << "{" << std::endl
1414 << " // Stream an object of class " << fullname << "." << std::endl << std::endl;
1415
1416 // In case of VersionID<=0 write dummy streamer only calling
1417 // its base class Streamer(s). If no base class(es) let Streamer
1418 // print error message, i.e. this Streamer should never have been called.
1419 int version = ROOT::TMetaUtils::GetClassVersion(clxx, interp);
1420 if (version <= 0) {
1421 // We also need to look at the base classes.
1422 int basestreamer = 0;
1423 for (clang::CXXRecordDecl::base_class_const_iterator iter = clxx->bases_begin(), end = clxx->bases_end();
1424 iter != end;
1425 ++iter) {
1426 if (ROOT::TMetaUtils::ClassInfo__HasMethod(iter->getType()->getAsCXXRecordDecl(), "Streamer", interp)) {
1427 string base_fullname;
1428 ROOT::TMetaUtils::GetQualifiedName(base_fullname, * iter->getType()->getAsCXXRecordDecl());
1429
1430 if (strstr(base_fullname.c_str(), "::")) {
1431 // there is a namespace involved, trigger MS VC bug workaround
1432 dictStream << " //This works around a msvc bug and should be harmless on other platforms" << std::endl
1433 << " typedef " << base_fullname << " baseClass" << basestreamer << ";" << std::endl
1434 << " baseClass" << basestreamer << "::Streamer(R__b);" << std::endl;
1435 } else {
1436 dictStream << " " << base_fullname << "::Streamer(R__b);" << std::endl;
1437 }
1438 basestreamer++;
1439 }
1440 }
1441 if (!basestreamer) {
1442 dictStream << " ::Error(\"" << fullname << "::Streamer\", \"version id <=0 in ClassDef,"
1443 " dummy Streamer() called\"); if (R__b.IsReading()) { }" << std::endl;
1444 }
1445 dictStream << "}" << std::endl << std::endl;
1446 while (enclSpaceNesting) {
1447 dictStream << "} // namespace " << nsname.c_str() << std::endl;
1448 --enclSpaceNesting;
1449 }
1450 return;
1451 }
1452
1453 // loop twice: first time write reading code, second time writing code
1454 string classname = fullname;
1455 if (strstr(fullname.c_str(), "::")) {
1456 // there is a namespace involved, trigger MS VC bug workaround
1457 dictStream << " //This works around a msvc bug and should be harmless on other platforms" << std::endl
1458 << " typedef ::" << fullname << " thisClass;" << std::endl;
1459 classname = "thisClass";
1460 }
1461 for (int i = 0; i < 2; i++) {
1462
1463 int decli = 0;
1464
1465 if (i == 0) {
1466 dictStream << " UInt_t R__s, R__c;" << std::endl;
1467 dictStream << " if (R__b.IsReading()) {" << std::endl;
1468 dictStream << " Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }" << std::endl;
1469 } else {
1470 dictStream << " R__b.CheckByteCount(R__s, R__c, " << classname.c_str() << "::IsA());" << std::endl;
1471 dictStream << " } else {" << std::endl;
1472 dictStream << " R__c = R__b.WriteVersion(" << classname.c_str() << "::IsA(), kTRUE);" << std::endl;
1473 }
1474
1475 // Stream base class(es) when they have the Streamer() method
1476 int base = 0;
1477 for (clang::CXXRecordDecl::base_class_const_iterator iter = clxx->bases_begin(), end = clxx->bases_end();
1478 iter != end;
1479 ++iter) {
1480 if (ROOT::TMetaUtils::ClassInfo__HasMethod(iter->getType()->getAsCXXRecordDecl(), "Streamer", interp)) {
1481 string base_fullname;
1482 ROOT::TMetaUtils::GetQualifiedName(base_fullname, * iter->getType()->getAsCXXRecordDecl());
1483
1484 if (strstr(base_fullname.c_str(), "::")) {
1485 // there is a namespace involved, trigger MS VC bug workaround
1486 dictStream << " //This works around a msvc bug and should be harmless on other platforms" << std::endl
1487 << " typedef " << base_fullname << " baseClass" << base << ";" << std::endl
1488 << " baseClass" << base << "::Streamer(R__b);" << std::endl;
1489 ++base;
1490 } else {
1491 dictStream << " " << base_fullname << "::Streamer(R__b);" << std::endl;
1492 }
1493 }
1494 }
1495 // Stream data members
1496 // Loop over the non static data member.
1497 for (clang::RecordDecl::field_iterator field_iter = clxx->field_begin(), end = clxx->field_end();
1498 field_iter != end;
1499 ++field_iter) {
1500 const char *comment = ROOT::TMetaUtils::GetComment(**field_iter).data();
1501
1502 clang::QualType type = field_iter->getType();
1503 std::string type_name = type.getAsString(clxx->getASTContext().getPrintingPolicy());
1504
1505 const clang::Type *underling_type = ROOT::TMetaUtils::GetUnderlyingType(type);
1506
1507 // we skip:
1508 // - static members
1509 // - members with an ! as first character in the title (comment) field
1510
1511 //special case for Float16_t
1512 int isFloat16 = 0;
1513 if (strstr(type_name.c_str(), "Float16_t")) isFloat16 = 1;
1514
1515 //special case for Double32_t
1516 int isDouble32 = 0;
1517 if (strstr(type_name.c_str(), "Double32_t")) isDouble32 = 1;
1518
1519 // No need to test for static, there are not in this list.
1520 if (strncmp(comment, "!", 1)) {
1521
1522 // fundamental type: short, int, long, etc....
1523 if (underling_type->isFundamentalType() || underling_type->isEnumeralType()) {
1524 if (type.getTypePtr()->isConstantArrayType() &&
1525 type.getTypePtr()->getArrayElementTypeNoTypeQual()->isPointerType()) {
1526 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(type.getTypePtr());
1527 int s = GetFullArrayLength(arrayType);
1528
1529 if (!decli) {
1530 dictStream << " int R__i;" << std::endl;
1531 decli = 1;
1532 }
1533 dictStream << " for (R__i = 0; R__i < " << s << "; R__i++)" << std::endl;
1534 if (i == 0) {
1535 ROOT::TMetaUtils::Error(nullptr, "*** Datamember %s::%s: array of pointers to fundamental type (need manual intervention)\n", fullname.c_str(), field_iter->getName().str().c_str());
1536 dictStream << " ;//R__b.ReadArray(" << field_iter->getName().str() << ");" << std::endl;
1537 } else {
1538 dictStream << " ;//R__b.WriteArray(" << field_iter->getName().str() << ", __COUNTER__);" << std::endl;
1539 }
1540 } else if (type.getTypePtr()->isPointerType()) {
1541 llvm::StringRef indexvar = GrabIndex(interp, **field_iter, i == 0);
1542 if (indexvar.size() == 0) {
1543 if (i == 0) {
1544 ROOT::TMetaUtils::Error(nullptr, "*** Datamember %s::%s: pointer to fundamental type (need manual intervention)\n", fullname.c_str(), field_iter->getName().str().c_str());
1545 dictStream << " //R__b.ReadArray(" << field_iter->getName().str() << ");" << std::endl;
1546 } else {
1547 dictStream << " //R__b.WriteArray(" << field_iter->getName().str() << ", __COUNTER__);" << std::endl;
1548 }
1549 } else {
1550 if (i == 0) {
1551 dictStream << " delete [] " << field_iter->getName().str() << ";" << std::endl
1552 << " " << GetNonConstMemberName(**field_iter) << " = new "
1553 << ROOT::TMetaUtils::ShortTypeName(**field_iter) << "[" << indexvar.str() << "];" << std::endl;
1554 if (isFloat16) {
1555 dictStream << " R__b.ReadFastArrayFloat16(" << GetNonConstMemberName(**field_iter)
1556 << "," << indexvar.str() << ");" << std::endl;
1557 } else if (isDouble32) {
1558 dictStream << " R__b.ReadFastArrayDouble32(" << GetNonConstMemberName(**field_iter)
1559 << "," << indexvar.str() << ");" << std::endl;
1560 } else {
1561 dictStream << " R__b.ReadFastArray(" << GetNonConstMemberName(**field_iter)
1562 << "," << indexvar.str() << ");" << std::endl;
1563 }
1564 } else {
1565 if (isFloat16) {
1566 dictStream << " R__b.WriteFastArrayFloat16("
1567 << field_iter->getName().str() << "," << indexvar.str() << ");" << std::endl;
1568 } else if (isDouble32) {
1569 dictStream << " R__b.WriteFastArrayDouble32("
1570 << field_iter->getName().str() << "," << indexvar.str() << ");" << std::endl;
1571 } else {
1572 dictStream << " R__b.WriteFastArray("
1573 << field_iter->getName().str() << "," << indexvar.str() << ");" << std::endl;
1574 }
1575 }
1576 }
1577 } else if (type.getTypePtr()->isArrayType()) {
1578 if (i == 0) {
1579 if (type.getTypePtr()->getArrayElementTypeNoTypeQual()->isArrayType()) { // if (m.ArrayDim() > 1) {
1580 if (underling_type->isEnumeralType())
1581 dictStream << " R__b.ReadStaticArray((Int_t*)" << field_iter->getName().str() << ");" << std::endl;
1582 else {
1583 if (isFloat16) {
1584 dictStream << " R__b.ReadStaticArrayFloat16((" << ROOT::TMetaUtils::TrueName(**field_iter)
1585 << "*)" << field_iter->getName().str() << ");" << std::endl;
1586 } else if (isDouble32) {
1587 dictStream << " R__b.ReadStaticArrayDouble32((" << ROOT::TMetaUtils::TrueName(**field_iter)
1588 << "*)" << field_iter->getName().str() << ");" << std::endl;
1589 } else {
1590 dictStream << " R__b.ReadStaticArray((" << ROOT::TMetaUtils::TrueName(**field_iter)
1591 << "*)" << field_iter->getName().str() << ");" << std::endl;
1592 }
1593 }
1594 } else {
1595 if (underling_type->isEnumeralType()) {
1596 dictStream << " R__b.ReadStaticArray((Int_t*)" << field_iter->getName().str() << ");" << std::endl;
1597 } else {
1598 if (isFloat16) {
1599 dictStream << " R__b.ReadStaticArrayFloat16(" << field_iter->getName().str() << ");" << std::endl;
1600 } else if (isDouble32) {
1601 dictStream << " R__b.ReadStaticArrayDouble32(" << field_iter->getName().str() << ");" << std::endl;
1602 } else {
1603 dictStream << " R__b.ReadStaticArray((" << ROOT::TMetaUtils::TrueName(**field_iter)
1604 << "*)" << field_iter->getName().str() << ");" << std::endl;
1605 }
1606 }
1607 }
1608 } else {
1609 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(type.getTypePtr());
1610 int s = GetFullArrayLength(arrayType);
1611
1612 if (type.getTypePtr()->getArrayElementTypeNoTypeQual()->isArrayType()) {// if (m.ArrayDim() > 1) {
1613 if (underling_type->isEnumeralType())
1614 dictStream << " R__b.WriteArray((Int_t*)" << field_iter->getName().str() << ", "
1615 << s << ");" << std::endl;
1616 else if (isFloat16) {
1617 dictStream << " R__b.WriteArrayFloat16((" << ROOT::TMetaUtils::TrueName(**field_iter)
1618 << "*)" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1619 } else if (isDouble32) {
1620 dictStream << " R__b.WriteArrayDouble32((" << ROOT::TMetaUtils::TrueName(**field_iter)
1621 << "*)" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1622 } else {
1623 dictStream << " R__b.WriteArray((" << ROOT::TMetaUtils::TrueName(**field_iter)
1624 << "*)" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1625 }
1626 } else {
1627 if (underling_type->isEnumeralType())
1628 dictStream << " R__b.WriteArray((Int_t*)" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1629 else if (isFloat16) {
1630 dictStream << " R__b.WriteArrayFloat16(" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1631 } else if (isDouble32) {
1632 dictStream << " R__b.WriteArrayDouble32(" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1633 } else {
1634 dictStream << " R__b.WriteArray(" << field_iter->getName().str() << ", " << s << ");" << std::endl;
1635 }
1636 }
1637 }
1638 } else if (underling_type->isEnumeralType()) {
1639 if (i == 0) {
1640 dictStream << " void *ptr_" << field_iter->getName().str() << " = (void*)&" << field_iter->getName().str() << ";\n";
1641 dictStream << " R__b >> *reinterpret_cast<Int_t*>(ptr_" << field_iter->getName().str() << ");" << std::endl;
1642 } else
1643 dictStream << " R__b << (Int_t)" << field_iter->getName().str() << ";" << std::endl;
1644 } else {
1645 if (isFloat16) {
1646 if (i == 0)
1647 dictStream << " {float R_Dummy; R__b >> R_Dummy; " << GetNonConstMemberName(**field_iter)
1648 << "=Float16_t(R_Dummy);}" << std::endl;
1649 else
1650 dictStream << " R__b << float(" << GetNonConstMemberName(**field_iter) << ");" << std::endl;
1651 } else if (isDouble32) {
1652 if (i == 0)
1653 dictStream << " {float R_Dummy; R__b >> R_Dummy; " << GetNonConstMemberName(**field_iter)
1654 << "=Double32_t(R_Dummy);}" << std::endl;
1655 else
1656 dictStream << " R__b << float(" << GetNonConstMemberName(**field_iter) << ");" << std::endl;
1657 } else {
1658 if (i == 0)
1659 dictStream << " R__b >> " << GetNonConstMemberName(**field_iter) << ";" << std::endl;
1660 else
1661 dictStream << " R__b << " << GetNonConstMemberName(**field_iter) << ";" << std::endl;
1662 }
1663 }
1664 } else {
1665 // we have an object...
1666
1667 // check if object is a standard string
1668 if (STLStringStreamer(**field_iter, i, dictStream))
1669 continue;
1670
1671 // check if object is an STL container
1672 if (STLContainerStreamer(**field_iter, i, interp, normCtxt, dictStream))
1673 continue;
1674
1675 // handle any other type of objects
1676 if (type.getTypePtr()->isConstantArrayType() &&
1677 type.getTypePtr()->getArrayElementTypeNoTypeQual()->isPointerType()) {
1678 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(type.getTypePtr());
1679 int s = GetFullArrayLength(arrayType);
1680
1681 if (!decli) {
1682 dictStream << " int R__i;" << std::endl;
1683 decli = 1;
1684 }
1685 dictStream << " for (R__i = 0; R__i < " << s << "; R__i++)" << std::endl;
1686 if (i == 0)
1687 dictStream << " R__b >> " << GetNonConstMemberName(**field_iter);
1688 else {
1689 if (ROOT::TMetaUtils::IsBase(**field_iter, "TObject", interp) && ROOT::TMetaUtils::IsBase(**field_iter, "TArray", interp))
1690 dictStream << " R__b << (TObject*)" << field_iter->getName().str();
1691 else
1692 dictStream << " R__b << " << GetNonConstMemberName(**field_iter);
1693 }
1694 WriteArrayDimensions(field_iter->getType(), dictStream);
1695 dictStream << "[R__i];" << std::endl;
1696 } else if (type.getTypePtr()->isPointerType()) {
1697 // This is always good. However, in case of a pointer
1698 // to an object that is guaranteed to be there and not
1699 // being referenced by other objects we could use
1700 // xx->Streamer(b);
1701 // Optimize this with control statement in title.
1702 if (isPointerToPointer(**field_iter)) {
1703 if (i == 0) {
1704 ROOT::TMetaUtils::Error(nullptr, "*** Datamember %s::%s: pointer to pointer (need manual intervention)\n", fullname.c_str(), field_iter->getName().str().c_str());
1705 dictStream << " //R__b.ReadArray(" << field_iter->getName().str() << ");" << std::endl;
1706 } else {
1707 dictStream << " //R__b.WriteArray(" << field_iter->getName().str() << ", __COUNTER__);";
1708 }
1709 } else {
1710 if (ROOT::TMetaUtils::GetQualifiedName(*ROOT::TMetaUtils::GetUnderlyingType(field_iter->getType()), **field_iter) == "TClonesArray") {
1711 dictStream << " " << field_iter->getName().str() << "->Streamer(R__b);" << std::endl;
1712 } else {
1713 if (i == 0) {
1714 // The following:
1715 // if (strncmp(m.Title(),"->",2) != 0) fprintf(fp, " delete %s;\n", GetNonConstMemberName(**field_iter).c_str());
1716 // could be used to prevent a memory leak since the next statement could possibly create a new object.
1717 // In the TStreamerInfo based I/O we made the previous statement conditional on TStreamerInfo::CanDelete
1718 // to allow the user to prevent some inadvisable deletions. So we should be offering this flexibility
1719 // here to and should not (technically) rely on TStreamerInfo for it, so for now we leave it as is.
1720 // Note that the leak should happen from here only if the object is stored in an unsplit object
1721 // and either the user request an old branch or the streamer has been customized.
1722 dictStream << " R__b >> " << GetNonConstMemberName(**field_iter) << ";" << std::endl;
1723 } else {
1724 if (ROOT::TMetaUtils::IsBase(**field_iter, "TObject", interp) && ROOT::TMetaUtils::IsBase(**field_iter, "TArray", interp))
1725 dictStream << " R__b << (TObject*)" << field_iter->getName().str() << ";" << std::endl;
1726 else
1727 dictStream << " R__b << " << GetNonConstMemberName(**field_iter) << ";" << std::endl;
1728 }
1729 }
1730 }
1731 } else if (const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(type.getTypePtr())) {
1732 int s = GetFullArrayLength(arrayType);
1733
1734 if (!decli) {
1735 dictStream << " int R__i;" << std::endl;
1736 decli = 1;
1737 }
1738 dictStream << " for (R__i = 0; R__i < " << s << "; R__i++)" << std::endl;
1739 std::string mTypeNameStr;
1740 ROOT::TMetaUtils::GetQualifiedName(mTypeNameStr, field_iter->getType(), **field_iter);
1741 const char *mTypeName = mTypeNameStr.c_str();
1742 const char *constwd = "const ";
1743 if (strncmp(constwd, mTypeName, strlen(constwd)) == 0) {
1744 mTypeName += strlen(constwd);
1745 dictStream << " const_cast< " << mTypeName << " &>(" << field_iter->getName().str();
1746 WriteArrayDimensions(field_iter->getType(), dictStream);
1747 dictStream << "[R__i]).Streamer(R__b);" << std::endl;
1748 } else {
1749 dictStream << " " << GetNonConstMemberName(**field_iter);
1750 WriteArrayDimensions(field_iter->getType(), dictStream);
1751 dictStream << "[R__i].Streamer(R__b);" << std::endl;
1752 }
1753 } else {
1754 if (ROOT::TMetaUtils::ClassInfo__HasMethod(ROOT::TMetaUtils::GetUnderlyingRecordDecl(field_iter->getType()), "Streamer", interp))
1755 dictStream << " " << GetNonConstMemberName(**field_iter) << ".Streamer(R__b);" << std::endl;
1756 else {
1757 dictStream << " R__b.StreamObject(&(" << field_iter->getName().str() << "),typeid("
1758 << field_iter->getName().str() << "));" << std::endl; //R__t.Streamer(R__b);\n");
1759 //VP if (i == 0)
1760 //VP Error(0, "*** Datamember %s::%s: object has no Streamer() method (need manual intervention)\n",
1761 //VP fullname, field_iter->getName().str());
1762 //VP fprintf(fp, " //%s.Streamer(R__b);\n", m.Name());
1763 }
1764 }
1765 }
1766 }
1767 }
1768 }
1769 dictStream << " R__b.SetByteCount(R__c, kTRUE);" << std::endl
1770 << " }" << std::endl
1771 << "}" << std::endl << std::endl;
1772
1773 while (enclSpaceNesting) {
1774 dictStream << "} // namespace " << nsname.c_str() << std::endl;
1775 --enclSpaceNesting;
1776 }
1777}
1778
1779////////////////////////////////////////////////////////////////////////////////
1780
1782 const cling::Interpreter &interp,
1783 const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt,
1784 std::ostream &dictStream)
1785{
1786 // Write Streamer() method suitable for automatic schema evolution.
1787
1788 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl());
1789 if (clxx == nullptr) return;
1790
1791 bool add_template_keyword = ROOT::TMetaUtils::NeedTemplateKeyword(clxx);
1792
1793 // We also need to look at the base classes.
1794 for (clang::CXXRecordDecl::base_class_const_iterator iter = clxx->bases_begin(), end = clxx->bases_end();
1795 iter != end;
1796 ++iter) {
1797 int k = ROOT::TMetaUtils::IsSTLContainer(*iter);
1798 if (k != 0) {
1799 Internal::RStl::Instance().GenerateTClassFor(iter->getType(), interp, normCtxt);
1800 }
1801 }
1802
1803 string fullname;
1804 string clsname;
1805 string nsname;
1806 int enclSpaceNesting = 0;
1807
1808 if (ROOT::TMetaUtils::GetNameWithinNamespace(fullname, clsname, nsname, clxx)) {
1809 enclSpaceNesting = ROOT::TMetaUtils::WriteNamespaceHeader(dictStream, cl);
1810 }
1811
1812 dictStream << "//_______________________________________"
1813 << "_______________________________________" << std::endl;
1814 if (add_template_keyword) dictStream << "template <> ";
1815 dictStream << "void " << clsname << "::Streamer(TBuffer &R__b)" << std::endl
1816 << "{" << std::endl
1817 << " // Stream an object of class " << fullname << "." << std::endl << std::endl
1818 << " if (R__b.IsReading()) {" << std::endl
1819 << " R__b.ReadClassBuffer(" << fullname << "::Class(),this);" << std::endl
1820 << " } else {" << std::endl
1821 << " R__b.WriteClassBuffer(" << fullname << "::Class(),this);" << std::endl
1822 << " }" << std::endl
1823 << "}" << std::endl << std::endl;
1824
1825 while (enclSpaceNesting) {
1826 dictStream << "} // namespace " << nsname << std::endl;
1827 --enclSpaceNesting;
1828 }
1829}
1830
1831////////////////////////////////////////////////////////////////////////////////
1832
1834 const cling::Interpreter &interp,
1835 const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt,
1836 std::ostream &dictStream,
1837 bool isAutoStreamer)
1838{
1839 if (isAutoStreamer) {
1840 WriteAutoStreamer(cl, interp, normCtxt, dictStream);
1841 } else {
1842 WriteStreamer(cl, interp, normCtxt, dictStream);
1843 }
1844}
1845
1846////////////////////////////////////////////////////////////////////////////////
1847
1848void GenerateLinkdef(llvm::cl::list<std::string> &InputFiles,
1849 std::string &code_for_parser)
1850{
1851 code_for_parser += "#ifdef __CINT__\n\n";
1852 code_for_parser += "#pragma link off all globals;\n";
1853 code_for_parser += "#pragma link off all classes;\n";
1854 code_for_parser += "#pragma link off all functions;\n\n";
1855
1856 for (std::string& arg : InputFiles) {
1857 char trail[3];
1858 int nostr = 0, noinp = 0, bcnt = 0, l = arg.length() - 1;
1859 for (int j = 0; j < 3; j++) {
1860 if (arg[l] == '-') {
1861 arg[l] = '\0';
1862 nostr = 1;
1863 l--;
1864 }
1865 if (arg[l] == '!') {
1866 arg[l] = '\0';
1867 noinp = 1;
1868 l--;
1869 }
1870 if (arg[l] == '+') {
1871 arg[l] = '\0';
1872 bcnt = 1;
1873 l--;
1874 }
1875 }
1876 if (nostr || noinp) {
1877 trail[0] = 0;
1878 if (nostr) strlcat(trail, "-", 3);
1879 if (noinp) strlcat(trail, "!", 3);
1880 }
1881 if (bcnt) {
1882 strlcpy(trail, "+", 3);
1883 if (nostr)
1884 ROOT::TMetaUtils::Error(nullptr, "option + mutual exclusive with -\n");
1885 }
1886 llvm::SmallString<256> filestem = llvm::sys::path::filename(arg);
1887 llvm::sys::path::replace_extension(filestem, "");
1888
1889 code_for_parser += "#pragma link C++ class ";
1890 code_for_parser += filestem.str().str();
1891 if (nostr || noinp || bcnt)
1892 code_for_parser += trail;
1893 code_for_parser += ";\n";
1894 }
1895
1896 code_for_parser += "\n#endif\n";
1897}
1898
1899////////////////////////////////////////////////////////////////////////////////
1900/// Find file name in path specified via -I statements to Cling.
1901/// Return false if the file can not be found.
1902/// If the file is found, set pname to the full path name and return true.
1903
1904bool Which(cling::Interpreter &interp, const char *fname, string &pname)
1905{
1906 FILE *fp = nullptr;
1907
1908#ifdef WIN32
1909 static const char *fopenopts = "rb";
1910#else
1911 static const char *fopenopts = "r";
1912#endif
1913
1914 pname = fname;
1915 fp = fopen(pname.c_str(), fopenopts);
1916 if (fp) {
1917 fclose(fp);
1918 return true;
1919 }
1920
1921 llvm::SmallVector<std::string, 10> includePaths;//Why 10? Hell if I know.
1922 //false - no system header, false - with flags.
1923 interp.GetIncludePaths(includePaths, false, false);
1924
1925 const size_t nPaths = includePaths.size();
1926 for (size_t i = 0; i < nPaths; i += 1 /* 2 */) {
1927
1928 pname = includePaths[i].c_str() + gPathSeparator + fname;
1929
1930 fp = fopen(pname.c_str(), fopenopts);
1931 if (fp) {
1932 fclose(fp);
1933 return true;
1934 }
1935 }
1936 pname = "";
1937 return false;
1938}
1939
1940////////////////////////////////////////////////////////////////////////////////
1941/// If the argument starts with MODULE/inc, strip it
1942/// to make it the name we can use in `#includes`.
1943
1944const char *CopyArg(const char *original)
1945{
1946 if (!gBuildingROOT)
1947 return original;
1948
1949 if (IsSelectionFile(original))
1950 return original;
1951
1952 const char *inc = strstr(original, "\\inc\\");
1953 if (!inc)
1954 inc = strstr(original, "/inc/");
1955 if (inc && strlen(inc) > 5)
1956 return inc + 5;
1957 return original;
1958}
1959
1960////////////////////////////////////////////////////////////////////////////////
1961/// Copy the command line argument, stripping MODULE/inc if
1962/// necessary.
1963
1964void StrcpyArg(string &dest, const char *original)
1965{
1966 dest = CopyArg(original);
1967}
1968
1969////////////////////////////////////////////////////////////////////////////////
1970/// Write the extra header injected into the module:
1971/// umbrella header if (umbrella) else content header.
1972
1973static bool InjectModuleUtilHeader(const char *argv0,
1974 TModuleGenerator &modGen,
1975 cling::Interpreter &interp,
1976 bool umbrella)
1977{
1978 std::ostringstream out;
1979 if (umbrella) {
1980 // This will duplicate the -D,-U from clingArgs - but as they are surrounded
1981 // by #ifndef there is no problem here.
1982 modGen.WriteUmbrellaHeader(out);
1983 if (interp.declare(out.str()) != cling::Interpreter::kSuccess) {
1984 const std::string &hdrName
1985 = umbrella ? modGen.GetUmbrellaName() : modGen.GetContentName();
1986 ROOT::TMetaUtils::Error(nullptr, "%s: compilation failure (%s)\n", argv0,
1987 hdrName.c_str());
1988 return false;
1989 }
1990 } else {
1991 modGen.WriteContentHeader(out);
1992 }
1993 return true;
1994}
1995
1996////////////////////////////////////////////////////////////////////////////////
1997/// Write the AST of the given CompilerInstance to the given File while
1998/// respecting the given isysroot.
1999/// If module is not a null pointer, we only write the given module to the
2000/// given file and not the whole AST.
2001/// Returns true if the AST was successfully written.
2002static bool WriteAST(llvm::StringRef fileName, clang::CompilerInstance *compilerInstance,
2003 llvm::StringRef iSysRoot,
2004 clang::Module *module = nullptr)
2005{
2006 // From PCHGenerator and friends:
2007 llvm::SmallVector<char, 128> buffer;
2008 llvm::BitstreamWriter stream(buffer);
2009 clang::ASTWriter writer(stream, buffer, compilerInstance->getModuleCache(), /*Extensions=*/{});
2010 std::unique_ptr<llvm::raw_ostream> out =
2011 compilerInstance->createOutputFile(fileName, /*Binary=*/true,
2012 /*RemoveFileOnSignal=*/false,
2013 /*useTemporary=*/false,
2014 /*CreateMissingDirectories*/ false);
2015 if (!out) {
2016 ROOT::TMetaUtils::Error("WriteAST", "Couldn't open output stream to '%s'!\n", fileName.data());
2017 return false;
2018 }
2019
2020 compilerInstance->getFrontendOpts().RelocatablePCH = true;
2021
2022 writer.WriteAST(compilerInstance->getSema(), fileName.str(), module, iSysRoot);
2023
2024 // Write the generated bitstream to "Out".
2025 out->write(&buffer.front(), buffer.size());
2026
2027 // Make sure it hits disk now.
2028 out->flush();
2029
2030 return true;
2031}
2032
2033////////////////////////////////////////////////////////////////////////////////
2034/// Generates a PCH from the given ModuleGenerator and CompilerInstance.
2035/// Returns true iff the PCH was successfully generated.
2036static bool GenerateAllDict(TModuleGenerator &modGen, clang::CompilerInstance *compilerInstance,
2037 const std::string &currentDirectory)
2038{
2039 assert(modGen.IsPCH() && "modGen must be in PCH mode");
2040
2041 std::string iSysRoot("/DUMMY_SYSROOT/include/");
2042 if (gBuildingROOT) iSysRoot = (currentDirectory + "/");
2043 return WriteAST(modGen.GetModuleFileName(), compilerInstance, iSysRoot);
2044}
2045
2046////////////////////////////////////////////////////////////////////////////////
2047/// Includes all given headers in the interpreter. Returns true when we could
2048/// include the headers and otherwise false on an error when including.
2049static bool IncludeHeaders(const std::vector<std::string> &headers, cling::Interpreter &interpreter)
2050{
2051 // If no headers are given, this is a no-op.
2052 if (headers.empty())
2053 return true;
2054
2055 // Turn every header name into an include and parse it in the interpreter.
2056 std::stringstream includes;
2057 for (const std::string &header : headers) {
2058 includes << "#include \"" << header << "\"\n";
2059 }
2060 std::string includeListStr = includes.str();
2061 auto result = interpreter.declare(includeListStr);
2062 return result == cling::Interpreter::CompilationResult::kSuccess;
2063}
2064
2065
2066////////////////////////////////////////////////////////////////////////////////
2067
2068void AddPlatformDefines(std::vector<std::string> &clingArgs)
2069{
2070 char platformDefines[64] = {0};
2071#ifdef __INTEL_COMPILER
2072 snprintf(platformDefines, 64, "-DG__INTEL_COMPILER=%ld", (long)__INTEL_COMPILER);
2073 clingArgs.push_back(platformDefines);
2074#endif
2075#ifdef __xlC__
2076 snprintf(platformDefines, 64, "-DG__xlC=%ld", (long)__xlC__);
2077 clingArgs.push_back(platformDefines);
2078#endif
2079#ifdef __GNUC__
2080 snprintf(platformDefines, 64, "-DG__GNUC=%ld", (long)__GNUC__);
2081 snprintf(platformDefines, 64, "-DG__GNUC_VER=%ld", (long)__GNUC__ * 1000 + __GNUC_MINOR__);
2082 clingArgs.push_back(platformDefines);
2083#endif
2084#ifdef __GNUC_MINOR__
2085 snprintf(platformDefines, 64, "-DG__GNUC_MINOR=%ld", (long)__GNUC_MINOR__);
2086 clingArgs.push_back(platformDefines);
2087#endif
2088#ifdef __HP_aCC
2089 snprintf(platformDefines, 64, "-DG__HP_aCC=%ld", (long)__HP_aCC);
2090 clingArgs.push_back(platformDefines);
2091#endif
2092#ifdef __sun
2093 snprintf(platformDefines, 64, "-DG__sun=%ld", (long)__sun);
2094 clingArgs.push_back(platformDefines);
2095#endif
2096#ifdef __SUNPRO_CC
2097 snprintf(platformDefines, 64, "-DG__SUNPRO_CC=%ld", (long)__SUNPRO_CC);
2098 clingArgs.push_back(platformDefines);
2099#endif
2100#ifdef _STLPORT_VERSION
2101 // stlport version, used on e.g. SUN
2102 snprintf(platformDefines, 64, "-DG__STLPORT_VERSION=%ld", (long)_STLPORT_VERSION);
2103 clingArgs.push_back(platformDefines);
2104#endif
2105#ifdef __ia64__
2106 snprintf(platformDefines, 64, "-DG__ia64=%ld", (long)__ia64__);
2107 clingArgs.push_back(platformDefines);
2108#endif
2109#ifdef __x86_64__
2110 snprintf(platformDefines, 64, "-DG__x86_64=%ld", (long)__x86_64__);
2111 clingArgs.push_back(platformDefines);
2112#endif
2113#ifdef __i386__
2114 snprintf(platformDefines, 64, "-DG__i386=%ld", (long)__i386__);
2115 clingArgs.push_back(platformDefines);
2116#endif
2117#ifdef __arm__
2118 snprintf(platformDefines, 64, "-DG__arm=%ld", (long)__arm__);
2119 clingArgs.push_back(platformDefines);
2120#endif
2121#ifdef _WIN32
2122 snprintf(platformDefines, 64, "-DG__WIN32=%ld", (long)_WIN32);
2123 clingArgs.push_back(platformDefines);
2124#else
2125# ifdef WIN32
2126 snprintf(platformDefines, 64, "-DG__WIN32=%ld", (long)WIN32);
2127 clingArgs.push_back(platformDefines);
2128# endif
2129#endif
2130#ifdef _WIN64
2131 snprintf(platformDefines, 64, "-DG__WIN64=%ld", (long)_WIN64);
2132 clingArgs.push_back(platformDefines);
2133#endif
2134#ifdef _MSC_VER
2135 snprintf(platformDefines, 64, "-DG__MSC_VER=%ld", (long)_MSC_VER);
2136 clingArgs.push_back(platformDefines);
2137 snprintf(platformDefines, 64, "-DG__VISUAL=%ld", (long)_MSC_VER);
2138 clingArgs.push_back(platformDefines);
2139#endif
2140}
2141
2142////////////////////////////////////////////////////////////////////////////////
2143/// Extract the filename from a fullpath
2144
2145std::string ExtractFileName(const std::string &path)
2146{
2147 return llvm::sys::path::filename(path).str();
2148}
2149
2150////////////////////////////////////////////////////////////////////////////////
2151/// Extract the path from a fullpath finding the last \ or /
2152/// according to the content in gPathSeparator
2153
2154void ExtractFilePath(const std::string &path, std::string &dirname)
2155{
2156 const size_t pos = path.find_last_of(gPathSeparator);
2157 if (std::string::npos != pos) {
2158 dirname.assign(path.begin(), path.begin() + pos + 1);
2159 } else {
2160 dirname.assign("");
2161 }
2162}
2163
2164////////////////////////////////////////////////////////////////////////////////
2165/// Check if file has a path
2166
2167bool HasPath(const std::string &name)
2168{
2169 std::string dictLocation;
2170 ExtractFilePath(name, dictLocation);
2171 return !dictLocation.empty();
2172}
2173
2174////////////////////////////////////////////////////////////////////////////////
2175
2176void AdjustRootMapNames(std::string &rootmapFileName,
2177 std::string &rootmapLibName)
2178{
2179 // If the rootmap file name does not exist, create one following the libname
2180 // I.E. put into the directory of the lib the rootmap and within the rootmap the normalised path to the lib
2181 if (rootmapFileName.empty()) {
2182 size_t libExtensionPos = rootmapLibName.find_last_of(gLibraryExtension) - gLibraryExtension.size() + 1;
2183 rootmapFileName = rootmapLibName.substr(0, libExtensionPos) + ".rootmap";
2184 size_t libCleanNamePos = rootmapLibName.find_last_of(gPathSeparator) + 1;
2185 rootmapLibName = rootmapLibName.substr(libCleanNamePos, std::string::npos);
2186 ROOT::TMetaUtils::Info(nullptr, "Rootmap file name %s built from rootmap lib name %s",
2187 rootmapLibName.c_str(),
2188 rootmapFileName.c_str());
2189 }
2190}
2191
2192////////////////////////////////////////////////////////////////////////////////
2193/// Extract the proper autoload key for nested classes
2194/// The routine does not erase the name, just updates it
2195
2196void GetMostExternalEnclosingClassName(const clang::DeclContext &theContext,
2197 std::string &ctxtName,
2198 const cling::Interpreter &interpreter,
2199 bool treatParent = true)
2200{
2201 const clang::DeclContext *outerCtxt = treatParent ? theContext.getParent() : &theContext;
2202 // If the context has no outer context, we are finished
2203 if (!outerCtxt) return;
2204 // If the context is a class, we update the name
2205 if (const clang::RecordDecl *thisRcdDecl = llvm::dyn_cast<clang::RecordDecl>(outerCtxt)) {
2206 ROOT::TMetaUtils::GetNormalizedName(ctxtName, thisRcdDecl, interpreter);
2207 }
2208 // We recurse
2209 GetMostExternalEnclosingClassName(*outerCtxt, ctxtName, interpreter);
2210}
2211
2212////////////////////////////////////////////////////////////////////////////////
2213
2214void GetMostExternalEnclosingClassNameFromDecl(const clang::Decl &theDecl,
2215 std::string &ctxtName,
2216 const cling::Interpreter &interpreter)
2217{
2218 const clang::DeclContext *theContext = theDecl.getDeclContext();
2219 GetMostExternalEnclosingClassName(*theContext, ctxtName, interpreter, false);
2220}
2221
2222////////////////////////////////////////////////////////////////////////////////
2223template<class COLL>
2224int ExtractAutoloadKeys(std::list<std::string> &names,
2225 const COLL &decls,
2226 const cling::Interpreter &interp)
2227{
2228 if (!decls.empty()) {
2229 std::string autoLoadKey;
2230 for (auto & d : decls) {
2231 autoLoadKey = "";
2232 GetMostExternalEnclosingClassNameFromDecl(*d, autoLoadKey, interp);
2233 // If there is an outer class, it is already considered
2234 if (autoLoadKey.empty()) {
2235 names.push_back(d->getQualifiedNameAsString());
2236 }
2237 }
2238 }
2239 return 0;
2240}
2241
2242////////////////////////////////////////////////////////////////////////////////
2243/// Generate a rootmap file in the new format, like
2244/// { decls }
2245/// `namespace A { namespace B { template <typename T> class myTemplate; } }`
2246/// [libGpad.so libGraf.so libHist.so libMathCore.so]
2247/// class TAttCanvas
2248/// class TButton
2249/// (header1.h header2.h .. headerN.h)
2250/// class TMyClass
2251
2252int CreateNewRootMapFile(const std::string &rootmapFileName,
2253 const std::string &rootmapLibName,
2254 const std::list<std::string> &classesDefsList,
2255 const std::list<std::string> &classesNames,
2256 const std::list<std::string> &nsNames,
2257 const std::list<std::string> &tdNames,
2258 const std::list<std::string> &enNames,
2259 const std::list<std::string> &varNames,
2260 const HeadersDeclsMap_t &headersClassesMap,
2261 const std::unordered_set<std::string> headersToIgnore)
2262{
2263 // Create the rootmap file from the selected classes and namespaces
2264 std::ofstream rootmapFile(rootmapFileName.c_str());
2265 if (!rootmapFile) {
2266 ROOT::TMetaUtils::Error(nullptr, "Opening new rootmap file %s\n", rootmapFileName.c_str());
2267 return 1;
2268 }
2269
2270 // Keep track of the classes keys
2271 // This is done to avoid duplications of keys with typedefs
2272 std::unordered_set<std::string> classesKeys;
2273
2274
2275 // Add the "section"
2276 if (!classesNames.empty() || !nsNames.empty() || !tdNames.empty() ||
2277 !enNames.empty() || !varNames.empty()) {
2278
2279 // Add the template definitions
2280 if (!classesDefsList.empty()) {
2281 rootmapFile << "{ decls }\n";
2282 for (auto & classDef : classesDefsList) {
2283 rootmapFile << classDef << std::endl;
2284 }
2285 rootmapFile << "\n";
2286 }
2287 rootmapFile << "[ " << rootmapLibName << " ]\n";
2288
2289 // Loop on selected classes and insert them in the rootmap
2290 if (!classesNames.empty()) {
2291 rootmapFile << "# List of selected classes\n";
2292 for (auto & className : classesNames) {
2293 rootmapFile << "class " << className << std::endl;
2294 classesKeys.insert(className);
2295 }
2296 // And headers
2297 std::unordered_set<std::string> treatedHeaders;
2298 for (auto & className : classesNames) {
2299 // Don't treat templates
2300 if (className.find("<") != std::string::npos) continue;
2301 if (headersClassesMap.count(className)) {
2302 auto &headers = headersClassesMap.at(className);
2303 if (!headers.empty()){
2304 auto &header = headers.front();
2305 if (treatedHeaders.insert(header).second &&
2306 headersToIgnore.find(header) == headersToIgnore.end() &&
2308 rootmapFile << "header " << header << std::endl;
2309 }
2310 }
2311 }
2312 }
2313 }
2314
2315 // Same for namespaces
2316 if (!nsNames.empty()) {
2317 rootmapFile << "# List of selected namespaces\n";
2318 for (auto & nsName : nsNames) {
2319 rootmapFile << "namespace " << nsName << std::endl;
2320 }
2321 }
2322
2323 // And typedefs. These are used just to trigger the autoload mechanism
2324 if (!tdNames.empty()) {
2325 rootmapFile << "# List of selected typedefs and outer classes\n";
2326 for (const auto & autoloadKey : tdNames)
2327 if (classesKeys.insert(autoloadKey).second)
2328 rootmapFile << "typedef " << autoloadKey << std::endl;
2329 }
2330
2331 // And Enums. There is no incomplete type for an enum but we can nevertheless
2332 // have the key for the cases where the root typesystem is interrogated.
2333 if (!enNames.empty()){
2334 rootmapFile << "# List of selected enums and outer classes\n";
2335 for (const auto & autoloadKey : enNames)
2336 if (classesKeys.insert(autoloadKey).second)
2337 rootmapFile << "enum " << autoloadKey << std::endl;
2338 }
2339
2340 // And variables.
2341 if (!varNames.empty()){
2342 rootmapFile << "# List of selected vars\n";
2343 for (const auto & autoloadKey : varNames)
2344 if (classesKeys.insert(autoloadKey).second)
2345 rootmapFile << "var " << autoloadKey << std::endl;
2346 }
2347
2348 }
2349
2350 return 0;
2351
2352}
2353
2354////////////////////////////////////////////////////////////////////////////////
2355/// Performance is not critical here.
2356
2357std::pair<std::string,std::string> GetExternalNamespaceAndContainedEntities(const std::string line)
2358{
2359 auto nsPattern = '{'; auto nsPatternLength = 1;
2360 auto foundNsPos = line.find_last_of(nsPattern);
2361 if (foundNsPos == std::string::npos) return {"",""};
2362 foundNsPos+=nsPatternLength;
2363 auto extNs = line.substr(0,foundNsPos);
2364
2365 auto nsEndPattern = '}';
2366 auto foundEndNsPos = line.find(nsEndPattern);
2367 auto contained = line.substr(foundNsPos, foundEndNsPos-foundNsPos);
2368
2369 return {extNs, contained};
2370
2371
2372}
2373
2374////////////////////////////////////////////////////////////////////////////////
2375/// If two identical namespaces are there, just declare one only
2376/// Example:
2377/// namespace A { namespace B { fwd1; }}
2378/// namespace A { namespace B { fwd2; }}
2379/// get a namespace A { namespace B { fwd1; fwd2; }} line
2380
2381std::list<std::string> CollapseIdenticalNamespaces(const std::list<std::string>& fwdDeclarationsList)
2382{
2383 // Temp data structure holding the namespaces and the entities therewith
2384 // contained
2385 std::map<std::string, std::string> nsEntitiesMap;
2386 std::list<std::string> optFwdDeclList;
2387 for (auto const & fwdDecl : fwdDeclarationsList){
2388 // Check if the decl(s) are contained in a ns and which one
2389 auto extNsAndEntities = GetExternalNamespaceAndContainedEntities(fwdDecl);
2390 if (extNsAndEntities.first.empty()) {
2391 // no namespace found. Just put this on top
2392 optFwdDeclList.push_front(fwdDecl);
2393 };
2394 auto currentVal = nsEntitiesMap[extNsAndEntities.first];
2395 nsEntitiesMap[extNsAndEntities.first] = currentVal +=extNsAndEntities.second;
2396 }
2397
2398 // Now fill the new, optimised list
2399 std::string optFwdDecl;
2400 for (auto const & extNsAndEntities : nsEntitiesMap) {
2401 optFwdDecl = extNsAndEntities.first;
2402 optFwdDecl += extNsAndEntities.second;
2403 for (int i = 0; i < std::count(optFwdDecl.begin(), optFwdDecl.end(), '{'); ++i ){
2404 optFwdDecl += " }";
2405 }
2406 optFwdDeclList.push_front(optFwdDecl);
2407 }
2408
2409 return optFwdDeclList;
2410
2411}
2412
2413////////////////////////////////////////////////////////////////////////////////
2414/// Separate multiline strings
2415
2416bool ProcessAndAppendIfNotThere(const std::string &el,
2417 std::list<std::string> &el_list,
2418 std::unordered_set<std::string> &el_set)
2419{
2420 std::stringstream elStream(el);
2421 std::string tmp;
2422 bool added = false;
2423 while (getline(elStream, tmp, '\n')) {
2424 // Add if not there
2425 if (el_set.insert(tmp).second && !tmp.empty()) {
2426 el_list.push_back(tmp);
2427 added = true;
2428 }
2429 }
2430
2431 return added;
2432}
2433
2434////////////////////////////////////////////////////////////////////////////////
2435
2437 std::list<std::string> &classesList,
2438 std::list<std::string> &classesListForRootmap,
2439 std::list<std::string> &fwdDeclarationsList,
2440 const cling::Interpreter &interpreter)
2441{
2442 // Loop on selected classes. If they don't have the attribute "rootmap"
2443 // set to "false", store them in the list of classes for the rootmap
2444 // Returns 0 in case of success and 1 in case of issues.
2445
2446 // An unordered_set to keep track of the existing classes.
2447 // We want to avoid duplicates there as they may hint to a serious corruption
2448 std::unordered_set<std::string> classesSet;
2449 std::unordered_set<std::string> outerMostClassesSet;
2450
2451 std::string attrName, attrValue;
2452 bool isClassSelected;
2453 std::unordered_set<std::string> availableFwdDecls;
2454 std::string fwdDeclaration;
2455 for (auto const & selVar : scan.fSelectedVariables) {
2456 fwdDeclaration = "";
2457 int retCode = ROOT::TMetaUtils::AST2SourceTools::EncloseInNamespaces(*selVar, fwdDeclaration);
2458 if (retCode == 0) ProcessAndAppendIfNotThere(fwdDeclaration, fwdDeclarationsList, availableFwdDecls);
2459 }
2460
2461 for (auto const & selEnum : scan.fSelectedEnums) {
2462 fwdDeclaration = "";
2463 int retCode = ROOT::TMetaUtils::AST2SourceTools::EncloseInNamespaces(*selEnum, fwdDeclaration);
2464 if (retCode == 0) ProcessAndAppendIfNotThere(fwdDeclaration, fwdDeclarationsList, availableFwdDecls);
2465 }
2466
2467 // Loop on selected classes and put them in a list
2468 for (auto const & selClass : scan.fSelectedClasses) {
2469 isClassSelected = true;
2470 const clang::RecordDecl *rDecl = selClass.GetRecordDecl();
2471 std::string normalizedName;
2472 normalizedName = selClass.GetNormalizedName();
2473 if (!normalizedName.empty() &&
2474 !classesSet.insert(normalizedName).second &&
2475 outerMostClassesSet.count(normalizedName) == 0) {
2476 std::cerr << "FATAL: A class with normalized name " << normalizedName
2477 << " was already selected. This means that two different instances of"
2478 << " clang::RecordDecl had the same name, which is not possible."
2479 << " This can be a hint of a serious problem in the class selection."
2480 << " In addition, the generated dictionary would not even compile.\n";
2481 return 1;
2482 }
2483 classesList.push_back(normalizedName);
2484 // Allow to autoload with the name of the class as it was specified in the
2485 // selection xml or linkdef
2486 const char *reqName(selClass.GetRequestedName());
2487
2488 // Get always the containing namespace, put it in the list if not there
2489 fwdDeclaration = "";
2490 int retCode = ROOT::TMetaUtils::AST2SourceTools::EncloseInNamespaces(*rDecl, fwdDeclaration);
2491 if (retCode == 0) ProcessAndAppendIfNotThere(fwdDeclaration, fwdDeclarationsList, availableFwdDecls);
2492
2493 // Get template definition and put it in if not there
2494 if (llvm::isa<clang::ClassTemplateSpecializationDecl>(rDecl)) {
2495 fwdDeclaration = "";
2496 retCode = ROOT::TMetaUtils::AST2SourceTools::FwdDeclFromRcdDecl(*rDecl, interpreter, fwdDeclaration);
2497 if (retCode == 0) {
2498 std::string fwdDeclarationTemplateSpec;
2499 retCode = ROOT::TMetaUtils::AST2SourceTools::FwdDeclIfTmplSpec(*rDecl, interpreter, fwdDeclarationTemplateSpec, normalizedName);
2500 fwdDeclaration += '\n' + fwdDeclarationTemplateSpec;
2501 }
2502 if (retCode == 0)
2503 ProcessAndAppendIfNotThere(fwdDeclaration, fwdDeclarationsList, availableFwdDecls);
2504 }
2505
2506
2507 // Loop on attributes, if rootmap=false, don't put it in the list!
2508 for (auto ait = rDecl->attr_begin(); ait != rDecl->attr_end(); ++ait) {
2509 if (0 == ROOT::TMetaUtils::extractPropertyNameVal(*ait, attrName, attrValue) &&
2510 attrName == "rootmap" &&
2511 attrValue == "false") {
2512 attrName = attrValue = "";
2513 isClassSelected = false;
2514 break;
2515 }
2516 }
2517 if (isClassSelected) {
2518 // Now, check if this is an internal class. If yes, we check the name of the outermost one
2519 // This is because of ROOT-6517. On the other hand, we exclude from this treatment
2520 // classes which are template instances which are nested in classes. For example:
2521 // class A{
2522 // class B{};
2523 // };
2524 // selection: <class name="A::B" />
2525 // Will result in a rootmap entry like "class A"
2526 // On the other hand, taking
2527 // class A{
2528 // public:
2529 // template <class T> class B{};
2530 // };
2531 // selection: <class name="A::B<int>" />
2532 // Would result in an entry like "class A::B<int>"
2533 std::string outerMostClassName;
2534 GetMostExternalEnclosingClassName(*rDecl, outerMostClassName, interpreter);
2535 if (!outerMostClassName.empty() &&
2536 !llvm::isa<clang::ClassTemplateSpecializationDecl>(rDecl) &&
2537 classesSet.insert(outerMostClassName).second &&
2538 outerMostClassesSet.insert(outerMostClassName).second) {
2539 classesListForRootmap.push_back(outerMostClassName);
2540 } else {
2541 classesListForRootmap.push_back(normalizedName);
2542 if (reqName && reqName[0] && reqName != normalizedName) {
2543 classesListForRootmap.push_back(reqName);
2544 }
2545
2546 // Also register typeinfo::name(), unless we have pseudo-strong typedefs.
2547 // GetDemangledTypeInfo() checks for Double32_t etc already and returns an empty string.
2548 std::string demangledName = selClass.GetDemangledTypeInfo();
2549 if (!demangledName.empty()) {
2550 // See the operations in TCling::AutoLoad(type_info)
2553
2554 if (demangledName != normalizedName && (!reqName || demangledName != reqName)) {
2555 // if demangledName != other name
2556 classesListForRootmap.push_back(demangledName);
2557 }
2558 }
2559 }
2560 }
2561 }
2562 classesListForRootmap.sort();
2563
2564 // Disable for the moment
2565 // fwdDeclarationsList = CollapseIdenticalNamespaces(fwdDeclarationsList);
2566
2567 return 0;
2568}
2569
2570////////////////////////////////////////////////////////////////////////////////
2571/// Loop on selected classes and put them in a list
2572
2573void ExtractSelectedNamespaces(RScanner &scan, std::list<std::string> &nsList)
2574{
2575 for (RScanner::NamespaceColl_t::const_iterator selNsIter = scan.fSelectedNamespaces.begin();
2576 selNsIter != scan.fSelectedNamespaces.end(); ++selNsIter) {
2577 nsList.push_back(ROOT::TMetaUtils::GetQualifiedName(* selNsIter->GetNamespaceDecl()));
2578 }
2579}
2580
2581////////////////////////////////////////////////////////////////////////////////
2582/// We need annotations even in the PCH: // !, // || etc.
2583
2584void AnnotateAllDeclsForPCH(cling::Interpreter &interp,
2585 RScanner &scan)
2586{
2587 auto const & declSelRulesMap = scan.GetDeclsSelRulesMap();
2588 for (auto const & selClass : scan.fSelectedClasses) {
2589 // Very important: here we decide if we want to attach attributes to the decl.
2590 if (clang::CXXRecordDecl *CXXRD =
2591 llvm::dyn_cast<clang::CXXRecordDecl>(const_cast<clang::RecordDecl *>(selClass.GetRecordDecl()))) {
2592 AnnotateDecl(*CXXRD, declSelRulesMap, interp, false);
2593 }
2594 }
2595}
2596
2597////////////////////////////////////////////////////////////////////////////////
2598
2599int CheckClassesForInterpreterOnlyDicts(cling::Interpreter &interp,
2600 RScanner &scan)
2601{
2602 for (auto const & selClass : scan.fSelectedClasses) {
2603 if (!selClass.GetRecordDecl()->isCompleteDefinition() || selClass.RequestOnlyTClass()) {
2604 continue;
2605 }
2606 const clang::CXXRecordDecl *cxxdecl = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2607 if (cxxdecl && ROOT::TMetaUtils::ClassInfo__HasMethod(selClass, "Class_Name", interp)) {
2608 ROOT::TMetaUtils::Error("CheckClassesForInterpreterOnlyDicts",
2609 "Interactivity only dictionaries are not supported for classes with ClassDef\n");
2610 return 1;
2611 }
2612 }
2613 return 0;
2614}
2615
2616////////////////////////////////////////////////////////////////////////////////
2617/// Make up for skipping RegisterModule, now that dictionary parsing
2618/// is done and these headers cannot be selected anymore.
2619
2620int FinalizeStreamerInfoWriting(cling::Interpreter &interp, bool writeEmptyRootPCM=false)
2621{
2623 return 0;
2624
2625 if (interp.parseForModule("#include \"TStreamerInfo.h\"\n"
2626 "#include \"TFile.h\"\n"
2627 "#include \"TObjArray.h\"\n"
2628 "#include \"TVirtualArray.h\"\n"
2629 "#include \"TStreamerElement.h\"\n"
2630 "#include \"TProtoClass.h\"\n"
2631 "#include \"TBaseClass.h\"\n"
2632 "#include \"TListOfDataMembers.h\"\n"
2633 "#include \"TListOfEnums.h\"\n"
2634 "#include \"TListOfEnumsWithLock.h\"\n"
2635 "#include \"TDataMember.h\"\n"
2636 "#include \"TEnum.h\"\n"
2637 "#include \"TEnumConstant.h\"\n"
2638 "#include \"TDictAttributeMap.h\"\n"
2639 "#include \"TMessageHandler.h\"\n"
2640 "#include \"TArray.h\"\n"
2641 "#include \"TRefArray.h\"\n"
2642 "#include \"root_std_complex.h\"\n")
2643 != cling::Interpreter::kSuccess)
2644 return 1;
2645 if (!gDriverConfig->fCloseStreamerInfoROOTFile(writeEmptyRootPCM)) {
2646 return 1;
2647 }
2648 return 0;
2649}
2650
2651////////////////////////////////////////////////////////////////////////////////
2652
2653int GenerateFullDict(std::ostream &dictStream,
2654 cling::Interpreter &interp,
2655 RScanner &scan,
2656 const ROOT::TMetaUtils::RConstructorTypes &ctorTypes,
2657 bool isSplit,
2658 bool isGenreflex,
2659 bool isSelXML,
2660 bool writeEmptyRootPCM)
2661{
2662 ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper());
2663
2664 bool needsCollectionProxy = false;
2665
2666 //
2667 // We will loop over all the classes several times.
2668 // In order we will call
2669 //
2670 // WriteClassInit (code to create the TGenericClassInfo)
2671 // check for constructor and operator input
2672 // WriteClassFunctions (declared in ClassDef)
2673 // WriteClassCode (Streamer,ShowMembers,Auxiliary functions)
2674 //
2675
2676
2677 //
2678 // Loop over all classes and create Streamer() & Showmembers() methods
2679 //
2680
2681 // SELECTION LOOP
2682 for (auto const & ns : scan.fSelectedNamespaces) {
2684 WriteNamespaceInit(ns, interp, dictStream);
2685 }
2686 auto nsName = ns.GetNamespaceDecl()->getQualifiedNameAsString();
2687 if (nsName.find("(anonymous)") == std::string::npos)
2688 EmitStreamerInfo(nsName.c_str());
2689 }
2690
2691 for (auto const & selClass : scan.fSelectedClasses) {
2692 if (!selClass.GetRecordDecl()->isCompleteDefinition()) {
2693 ROOT::TMetaUtils::Error(nullptr, "A dictionary has been requested for %s but there is no declaration!\n", ROOT::TMetaUtils::GetQualifiedName(selClass).c_str());
2694 continue;
2695 }
2696 if (selClass.RequestOnlyTClass()) {
2697 // fprintf(stderr,"rootcling: Skipping class %s\n",R__GetQualifiedName(* selClass.GetRecordDecl()).c_str());
2698 // For now delay those for later.
2699 continue;
2700 }
2701
2702 // Very important: here we decide if we want to attach attributes to the decl.
2703
2704 if (clang::CXXRecordDecl *CXXRD =
2705 llvm::dyn_cast<clang::CXXRecordDecl>(const_cast<clang::RecordDecl *>(selClass.GetRecordDecl()))) {
2706 AnnotateDecl(*CXXRD, scan.GetDeclsSelRulesMap() , interp, isSelXML);
2707 }
2708
2709 const clang::CXXRecordDecl *CRD = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2710
2711 if (CRD) {
2712 ROOT::TMetaUtils::Info(nullptr, "Generating code for class %s\n", selClass.GetNormalizedName());
2713 if (TMetaUtils::IsStdClass(*CRD) && 0 != TClassEdit::STLKind(CRD->getName().str() /* unqualified name without template argument */)) {
2714 // Register the collections
2715 // coverity[fun_call_w_exception] - that's just fine.
2716 Internal::RStl::Instance().GenerateTClassFor(selClass.GetNormalizedName(), CRD, interp, normCtxt);
2717 } else if (CRD->getName() == "RVec") {
2718 static const clang::DeclContext *vecOpsDC = nullptr;
2719 if (!vecOpsDC)
2720 vecOpsDC = llvm::dyn_cast<clang::DeclContext>(
2721 interp.getLookupHelper().findScope("ROOT::VecOps", cling::LookupHelper::NoDiagnostics));
2722 if (vecOpsDC && vecOpsDC->Equals(CRD->getDeclContext())) {
2723 // Register the collections
2724 // coverity[fun_call_w_exception] - that's just fine.
2725 Internal::RStl::Instance().GenerateTClassFor(selClass.GetNormalizedName(), CRD, interp, normCtxt);
2726 }
2727 } else {
2729 ROOT::TMetaUtils::WriteClassInit(dictStream, selClass, CRD, interp, normCtxt, ctorTypes,
2730 needsCollectionProxy);
2731 }
2732 EmitStreamerInfo(selClass.GetNormalizedName());
2733 }
2734 }
2735 }
2736
2737 //
2738 // Write all TBuffer &operator>>(...), Class_Name(), Dictionary(), etc.
2739 // first to allow template specialisation to occur before template
2740 // instantiation (STK)
2741 //
2742 // SELECTION LOOP
2743 for (auto const & selClass : scan.fSelectedClasses) {
2744
2745 if (!selClass.GetRecordDecl()->isCompleteDefinition() || selClass.RequestOnlyTClass()) {
2746 // For now delay those for later.
2747 continue;
2748 }
2749 const clang::CXXRecordDecl *cxxdecl = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2750 if (cxxdecl && ROOT::TMetaUtils::ClassInfo__HasMethod(selClass, "Class_Name", interp) && !gOptIgnoreExistingDict) {
2751 WriteClassFunctions(cxxdecl, dictStream, isSplit);
2752 }
2753 }
2754
2755 // LINKDEF SELECTION LOOP
2756 // Loop to get the shadow class for the class marked 'RequestOnlyTClass' (but not the
2757 // STL class which is done via Internal::RStl::Instance().WriteClassInit(0);
2758 // and the ClassInit
2759
2760 for (auto const & selClass : scan.fSelectedClasses) {
2761 if (!selClass.GetRecordDecl()->isCompleteDefinition() || !selClass.RequestOnlyTClass()) {
2762 continue;
2763 }
2764
2765 const clang::CXXRecordDecl *CRD = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2766
2767 if (!ROOT::TMetaUtils::IsSTLContainer(selClass)) {
2769 ROOT::TMetaUtils::WriteClassInit(dictStream, selClass, CRD, interp, normCtxt, ctorTypes,
2770 needsCollectionProxy);
2771 }
2772 EmitStreamerInfo(selClass.GetNormalizedName());
2773 }
2774 }
2775 // Loop to write all the ClassCode
2777 for (auto const &selClass : scan.fSelectedClasses) {
2778 // The "isGenreflex" parameter allows the distinction between
2779 // genreflex and rootcling only for the treatment of collections which
2780 // are data members. To preserve the behaviour of the original
2781 // genreflex and rootcling tools, if the selection is performed with
2782 // genreflex, data members with collection type do not trigger the
2783 // selection of the collection type
2785 selClass,
2786 interp,
2787 normCtxt,
2788 dictStream,
2789 ctorTypes,
2790 isGenreflex);
2791 }
2792
2793 // Loop on the registered collections internally
2794 // coverity[fun_call_w_exception] - that's just fine.
2795 ROOT::Internal::RStl::Instance().WriteClassInit(dictStream, interp, normCtxt, ctorTypes, needsCollectionProxy,
2797 }
2798
2802 // Make up for skipping RegisterModule, now that dictionary parsing
2803 // is done and these headers cannot be selected anymore.
2804 int finRetCode = FinalizeStreamerInfoWriting(interp, writeEmptyRootPCM);
2805 if (finRetCode != 0) return finRetCode;
2806 }
2807
2808 return 0;
2809}
2810
2811////////////////////////////////////////////////////////////////////////////////
2812
2813void CreateDictHeader(std::ostream &dictStream, const std::string &main_dictname)
2814{
2815 dictStream << "// Do NOT change. Changes will be lost next time file is generated\n\n"
2816 << "#define R__DICTIONARY_FILENAME " << main_dictname << std::endl
2817
2818 // We do not want deprecation warnings to fire in dictionaries
2819 << "#define R__NO_DEPRECATION" << std::endl
2820
2821 // Now that CINT is not longer there to write the header file,
2822 // write one and include in there a few things for backward
2823 // compatibility.
2824 << "\n/*******************************************************************/\n"
2825 << "#include <stddef.h>\n"
2826 << "#include <stdio.h>\n"
2827 << "#include <stdlib.h>\n"
2828 << "#include <string.h>\n"
2829 << "#include <assert.h>\n"
2830 << "#define G__DICTIONARY\n"
2831 << "#include \"ROOT/RConfig.hxx\"\n"
2832 << "#include \"TClass.h\"\n"
2833 << "#include \"TDictAttributeMap.h\"\n"
2834 << "#include \"TInterpreter.h\"\n"
2835 << "#include \"TROOT.h\"\n"
2836 << "#include \"TBuffer.h\"\n"
2837 << "#include \"TMemberInspector.h\"\n"
2838 << "#include \"TInterpreter.h\"\n"
2839 << "#include \"TVirtualMutex.h\"\n"
2840 << "#include \"TError.h\"\n\n"
2841 << "#ifndef G__ROOT\n"
2842 << "#define G__ROOT\n"
2843 << "#endif\n\n"
2844 << "#include \"RtypesImp.h\"\n"
2845 << "#include \"TIsAProxy.h\"\n"
2846 << "#include \"TFileMergeInfo.h\"\n"
2847 << "#include <algorithm>\n"
2848 << "#include \"TCollectionProxyInfo.h\"\n"
2849 << "/*******************************************************************/\n\n"
2850 << "#include \"TDataMember.h\"\n\n"; // To set their transiency
2851}
2852
2853////////////////////////////////////////////////////////////////////////////////
2854
2855void AddNamespaceSTDdeclaration(std::ostream &dictStream)
2856{
2857 dictStream << "// The generated code does not explicitly qualify STL entities\n"
2858 << "namespace std {} using namespace std;\n\n";
2859}
2860
2861////////////////////////////////////////////////////////////////////////////////
2862
2863void GenerateNecessaryIncludes(std::ostream &dictStream,
2864 const std::string &includeForSource,
2865 const std::string &extraIncludes)
2866{
2867 dictStream << "// Header files passed as explicit arguments\n"
2868 << includeForSource << std::endl
2869 << "// Header files passed via #pragma extra_include\n"
2870 << extraIncludes << std::endl;
2871}
2872
2873//______________________________________________________________________________
2874
2875// cross-compiling for iOS and iOS simulator (assumes host is Intel Mac OS X)
2876#if defined(R__IOSSIM) || defined(R__IOS)
2877#ifdef __x86_64__
2878#undef __x86_64__
2879#endif
2880#ifdef __i386__
2881#undef __i386__
2882#endif
2883#ifdef R__IOSSIM
2884#define __i386__ 1
2885#endif
2886#ifdef R__IOS
2887#define __arm__ 1
2888#endif
2889#endif
2890
2891////////////////////////////////////////////////////////////////////////////////
2892/// Little helper class to bookkeep the files names which we want to make
2893/// temporary.
2894
2896public:
2897 //______________________________________________
2898 tempFileNamesCatalog(): m_size(0), m_emptyString("") {};
2899
2900 std::string getTmpFileName(const std::string &filename) {
2901 return filename + "_tmp_" + std::to_string(getpid());
2902 }
2903 /////////////////////////////////////////////////////////////////////////////
2904 /// Adds the name and the associated temp name to the catalog.
2905 /// Changes the name into the temp name
2906
2907 void addFileName(std::string &nameStr) {
2908 if (nameStr.empty()) return;
2909
2910 std::string tmpNameStr(getTmpFileName(nameStr));
2911
2912 // For brevity
2913 const char *name(nameStr.c_str());
2914 const char *tmpName(tmpNameStr.c_str());
2915
2916 m_names.push_back(nameStr);
2917 m_tempNames.push_back(tmpNameStr);
2918 ROOT::TMetaUtils::Info(nullptr, "File %s added to the tmp catalog.\n", name);
2919
2920 // This is to allow update of existing files
2921 if (0 == std::rename(name , tmpName)) {
2922 ROOT::TMetaUtils::Info(nullptr, "File %s existing. Preserved as %s.\n", name, tmpName);
2923 }
2924
2925 // To change the name to its tmp version
2926 nameStr = tmpNameStr;
2927
2928 m_size++;
2929
2930 }
2931
2932 /////////////////////////////////////////////////////////////////////////////
2933
2934 int clean() {
2935 int retval = 0;
2936 // rename the temp files into the normal ones
2937 for (unsigned int i = 0; i < m_size; ++i) {
2938 const char *tmpName = m_tempNames[i].c_str();
2939 // Check if the file exists
2940 std::ifstream ifile(tmpName);
2941 if (!ifile)
2942 ROOT::TMetaUtils::Error(nullptr, "Cannot find %s!\n", tmpName);
2943 // Make sure the file is closed, mostly for Windows FS, also when
2944 // accessing it from a Linux VM via a shared folder
2945 if (ifile.is_open())
2946 ifile.close();
2947 if (0 != std::remove(tmpName)) {
2948 ROOT::TMetaUtils::Error(nullptr, "Removing %s!\n", tmpName);
2949 retval++;
2950 }
2951 }
2952 return retval;
2953 }
2954
2955 /////////////////////////////////////////////////////////////////////////////
2956
2957 int commit() {
2958 int retval = 0;
2959 // rename the temp files into the normal ones
2960 for (unsigned int i = 0; i < m_size; ++i) {
2961 const char *tmpName = m_tempNames[i].c_str();
2962 const char *name = m_names[i].c_str();
2963 // Check if the file exists
2964 std::ifstream ifile(tmpName);
2965 if (!ifile)
2966 ROOT::TMetaUtils::Error(nullptr, "Cannot find %s!\n", tmpName);
2967 // Make sure the file is closed, mostly for Windows FS, also when
2968 // accessing it from a Linux VM via a shared folder
2969 if (ifile.is_open())
2970 ifile.close();
2971#ifdef WIN32
2972 // Sometimes files cannot be renamed on Windows if they don't have
2973 // been released by the system. So just copy them and try to delete
2974 // the old one afterwards.
2975 if (0 != std::rename(tmpName , name)) {
2976 if (llvm::sys::fs::copy_file(tmpName , name)) {
2977 llvm::sys::fs::remove(tmpName);
2978 }
2979 }
2980#else
2981 if (0 != std::rename(tmpName , name)) {
2982 ROOT::TMetaUtils::Error(nullptr, "Renaming %s into %s!\n", tmpName, name);
2983 retval++;
2984 }
2985#endif
2986 }
2987 return retval;
2988 }
2989
2990 /////////////////////////////////////////////////////////////////////////////
2991
2992 const std::string &getFileName(const std::string &tmpFileName) {
2993 size_t i = std::distance(m_tempNames.begin(),
2994 find(m_tempNames.begin(), m_tempNames.end(), tmpFileName));
2995 if (i == m_tempNames.size()) return m_emptyString;
2996 return m_names[i];
2997 }
2998
2999 /////////////////////////////////////////////////////////////////////////////
3000
3001 void dump() {
3002 std::cout << "Restoring files in temporary file catalog:\n";
3003 for (unsigned int i = 0; i < m_size; ++i) {
3004 std::cout << m_tempNames[i] << " --> " << m_names[i] << std::endl;
3005 }
3006 }
3007
3008private:
3009 unsigned int m_size;
3010 const std::string m_emptyString;
3011 std::vector<std::string> m_names;
3012 std::vector<std::string> m_tempNames;
3013};
3014
3015////////////////////////////////////////////////////////////////////////////////
3016/// Transform name of dictionary
3017
3018std::ostream *CreateStreamPtrForSplitDict(const std::string &dictpathname,
3019 tempFileNamesCatalog &tmpCatalog)
3020{
3021 std::string splitDictName(tmpCatalog.getFileName(dictpathname));
3022 const size_t dotPos = splitDictName.find_last_of(".");
3023 splitDictName.insert(dotPos, "_classdef");
3024 tmpCatalog.addFileName(splitDictName);
3025 return new std::ofstream(splitDictName.c_str());
3026}
3027
3028////////////////////////////////////////////////////////////////////////////////
3029/// Transform -W statements in diagnostic pragmas for cling reacting on "-Wno-"
3030/// For example
3031/// -Wno-deprecated-declarations --> `#pragma clang diagnostic ignored "-Wdeprecated-declarations"`
3032
3033static void CheckForMinusW(std::string arg,
3034 std::list<std::string> &diagnosticPragmas)
3035{
3036 static const std::string pattern("-Wno-");
3037
3038 if (arg.find(pattern) != 0)
3039 return;
3040
3041 ROOT::TMetaUtils::ReplaceAll(arg, pattern, "#pragma clang diagnostic ignored \"-W");
3042 arg += "\"";
3043 diagnosticPragmas.push_back(arg);
3044}
3045
3046////////////////////////////////////////////////////////////////////////////////
3047
3049 cling::Interpreter &interp)
3050{
3051 using namespace ROOT::TMetaUtils::AST2SourceTools;
3052 std::string fwdDecl;
3053 std::string initStr("{");
3054 auto &fwdDeclnArgsToSkipColl = normCtxt.GetTemplNargsToKeepMap();
3055 for (auto & strigNargsToKeepPair : fwdDeclnArgsToSkipColl) {
3056 auto &clTemplDecl = *strigNargsToKeepPair.first;
3057 FwdDeclFromTmplDecl(clTemplDecl , interp, fwdDecl);
3058 initStr += "{\"" +
3059 fwdDecl + "\", "
3060 + std::to_string(strigNargsToKeepPair.second)
3061 + "},";
3062 }
3063 if (!fwdDeclnArgsToSkipColl.empty())
3064 initStr.pop_back();
3065 initStr += "}";
3066 return initStr;
3067}
3068
3069////////////////////////////////////////////////////////////////////////////////
3070/// Get the pointee type if possible
3071
3072clang::QualType GetPointeeTypeIfPossible(const clang::QualType &qt)
3073{
3074 if (qt.isNull()) return qt;
3075 clang::QualType thisQt(qt);
3076 while (thisQt->isPointerType() ||
3077 thisQt->isReferenceType()) {
3078 thisQt = thisQt->getPointeeType();
3079 }
3080 return thisQt;
3081
3082}
3083
3084////////////////////////////////////////////////////////////////////////////////
3085/// Extract the list of headers necessary for the Decl
3086
3087std::list<std::string> RecordDecl2Headers(const clang::CXXRecordDecl &rcd,
3088 const cling::Interpreter &interp,
3089 std::set<const clang::CXXRecordDecl *> &visitedDecls)
3090{
3091 std::list<std::string> headers;
3092
3093 // We push a new transaction because we could deserialize decls here
3094 cling::Interpreter::PushTransactionRAII RAII(&interp);
3095
3096 // Avoid infinite recursion
3097 if (!visitedDecls.insert(rcd.getCanonicalDecl()).second)
3098 return headers;
3099
3100 // If this is a template
3101 if (const clang::ClassTemplateSpecializationDecl *tsd = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd)) {
3102
3103 // Loop on the template args
3104 for (auto & tArg : tsd->getTemplateArgs().asArray()) {
3105 if (clang::TemplateArgument::ArgKind::Type != tArg.getKind()) continue;
3106 auto tArgQualType = GetPointeeTypeIfPossible(tArg.getAsType());
3107 if (tArgQualType.isNull()) continue;
3108 if (const clang::CXXRecordDecl *tArgCxxRcd = tArgQualType->getAsCXXRecordDecl()) {
3109 headers.splice(headers.end(), RecordDecl2Headers(*tArgCxxRcd, interp, visitedDecls));
3110 }
3111 }
3112
3113 if (!ROOT::TMetaUtils::IsStdClass(rcd) && rcd.hasDefinition()) {
3114
3115 // Loop on base classes - with a newer llvm, range based possible
3116 for (auto baseIt = tsd->bases_begin(); baseIt != tsd->bases_end(); baseIt++) {
3117 auto baseQualType = GetPointeeTypeIfPossible(baseIt->getType());
3118 if (baseQualType.isNull()) continue;
3119 if (const clang::CXXRecordDecl *baseRcdPtr = baseQualType->getAsCXXRecordDecl()) {
3120 headers.splice(headers.end(), RecordDecl2Headers(*baseRcdPtr, interp, visitedDecls));
3121 }
3122 }
3123
3124 // Loop on the data members - with a newer llvm, range based possible
3125 for (auto declIt = tsd->decls_begin(); declIt != tsd->decls_end(); ++declIt) {
3126 if (const clang::FieldDecl *fieldDecl = llvm::dyn_cast<clang::FieldDecl>(*declIt)) {
3127 auto fieldQualType = GetPointeeTypeIfPossible(fieldDecl->getType());
3128 if (fieldQualType.isNull()) continue ;
3129 if (const clang::CXXRecordDecl *fieldCxxRcd = fieldQualType->getAsCXXRecordDecl()) {
3130 if (fieldCxxRcd->hasDefinition())
3131 headers.splice(headers.end(), RecordDecl2Headers(*fieldCxxRcd, interp, visitedDecls));
3132 }
3133 }
3134 }
3135
3136 // Loop on methods
3137 for (auto methodIt = tsd->method_begin(); methodIt != tsd->method_end(); ++methodIt) {
3138 // Check arguments
3139 for (auto & fPar : methodIt->parameters()) {
3140 auto fParQualType = GetPointeeTypeIfPossible(fPar->getOriginalType());
3141 if (fParQualType.isNull()) continue;
3142 if (const clang::CXXRecordDecl *fParCxxRcd = fParQualType->getAsCXXRecordDecl()) {
3143 if (fParCxxRcd->hasDefinition())
3144 headers.splice(headers.end(), RecordDecl2Headers(*fParCxxRcd, interp, visitedDecls));
3145 }
3146 }
3147 // Check return value
3148 auto retQualType = GetPointeeTypeIfPossible(methodIt->getReturnType());
3149 if (retQualType.isNull()) continue;
3150 if (const clang::CXXRecordDecl *retCxxRcd = retQualType->getAsCXXRecordDecl()) {
3151 if (retCxxRcd->hasDefinition())
3152 headers.splice(headers.end(), RecordDecl2Headers(*retCxxRcd, interp, visitedDecls));
3153 }
3154 }
3155 }
3156
3157 } // End template instance
3158
3159 std::string header = ROOT::TMetaUtils::GetFileName(rcd, interp);
3160 headers.emplace_back(header);
3161 headers.reverse();
3162 return headers;
3163
3164}
3165
3166////////////////////////////////////////////////////////////////////////////////
3167/// Check if the class good for being an autoparse key.
3168/// We exclude from this set stl containers of pods/strings
3169/// TODO: we may use also __gnu_cxx::
3170bool IsGoodForAutoParseMap(const clang::RecordDecl& rcd){
3171
3172 // If it's not an std class, we just pick it up.
3173 if (auto dclCtxt= rcd.getDeclContext()){
3174 if (! dclCtxt->isStdNamespace()){
3175 return true;
3176 }
3177 } else {
3178 return true;
3179 }
3180
3181 // Now, we have a stl class. We now check if it's a template. If not, we
3182 // do not take it: bitset, string and so on.
3183 auto clAsTmplSpecDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd);
3184 if (!clAsTmplSpecDecl) return false;
3185
3186 // Now we have a template in the stl. Let's see what the arguments are.
3187 // If they are not a POD or something which is good for autoparsing, we keep
3188 // them.
3189 auto& astCtxt = rcd.getASTContext();
3190 auto& templInstArgs = clAsTmplSpecDecl->getTemplateInstantiationArgs();
3191 for (auto&& arg : templInstArgs.asArray()){
3192
3193 auto argKind = arg.getKind();
3194 if (argKind != clang::TemplateArgument::Type){
3195 if (argKind == clang::TemplateArgument::Integral) continue;
3196 else return true;
3197 }
3198
3199 auto argQualType = arg.getAsType();
3200 auto isPOD = argQualType.isPODType(astCtxt);
3201 // This is a POD, we can inspect the next arg
3202 if (isPOD) continue;
3203
3204 auto argType = argQualType.getTypePtr();
3205 if (auto recType = llvm::dyn_cast<clang::RecordType>(argType)){
3206 auto isArgGoodForAutoParseMap = IsGoodForAutoParseMap(*recType->getDecl());
3207 // The arg is a class but good for the map
3208 if (isArgGoodForAutoParseMap) continue;
3209 } else {
3210 // The class is not a POD nor a class we can skip
3211 return true;
3212 }
3213 }
3214
3215 return false;
3216}
3217
3218////////////////////////////////////////////////////////////////////////////////
3219
3221 const RScanner::TypedefColl_t tDefDecls,
3222 const RScanner::FunctionColl_t funcDecls,
3223 const RScanner::VariableColl_t varDecls,
3224 const RScanner::EnumColl_t enumDecls,
3225 HeadersDeclsMap_t &headersClassesMap,
3226 HeadersDeclsMap_t &headersDeclsMap,
3227 const cling::Interpreter &interp)
3228{
3229 std::set<const clang::CXXRecordDecl *> visitedDecls;
3230 std::unordered_set<std::string> buffer;
3231 std::string autoParseKey;
3232
3233 // Add some manip of headers
3234 for (auto & annotatedRcd : annotatedRcds) {
3235 if (const clang::CXXRecordDecl *cxxRcd =
3236 llvm::dyn_cast_or_null<clang::CXXRecordDecl>(annotatedRcd.GetRecordDecl())) {
3237 autoParseKey = "";
3238 visitedDecls.clear();
3239 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3240 // remove duplicates, also if not subsequent
3241 buffer.clear();
3242 headers.remove_if([&buffer](const std::string & s) {
3243 return !buffer.insert(s).second;
3244 });
3245 GetMostExternalEnclosingClassName(*cxxRcd, autoParseKey, interp);
3246 if (autoParseKey.empty()) autoParseKey = annotatedRcd.GetNormalizedName();
3247 if (IsGoodForAutoParseMap(*cxxRcd)){
3248 headersDeclsMap[autoParseKey] = headers;
3249 headersDeclsMap[annotatedRcd.GetRequestedName()] = headers;
3250 } else {
3251 ROOT::TMetaUtils::Info(nullptr, "Class %s is not included in the set of autoparse keys.\n", autoParseKey.c_str());
3252 }
3253
3254 // Propagate to the classes map only if this is not a template.
3255 // The header is then used as autoload key and we want to avoid duplicates.
3256 if (!llvm::isa<clang::ClassTemplateSpecializationDecl>(cxxRcd)){
3257 headersClassesMap[autoParseKey] = headersDeclsMap[autoParseKey];
3258 headersClassesMap[annotatedRcd.GetRequestedName()] = headersDeclsMap[annotatedRcd.GetRequestedName()];
3259 }
3260 }
3261 }
3262
3263 // The same for the typedefs:
3264 for (auto & tDef : tDefDecls) {
3265 if (clang::CXXRecordDecl *cxxRcd = tDef->getUnderlyingType()->getAsCXXRecordDecl()) {
3266 autoParseKey = "";
3267 visitedDecls.clear();
3268 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3269 headers.push_back(ROOT::TMetaUtils::GetFileName(*tDef, interp));
3270 // remove duplicates, also if not subsequent
3271 buffer.clear();
3272 headers.remove_if([&buffer](const std::string & s) {
3273 return !buffer.insert(s).second;
3274 });
3275 GetMostExternalEnclosingClassNameFromDecl(*tDef, autoParseKey, interp);
3276 if (autoParseKey.empty()) autoParseKey = tDef->getQualifiedNameAsString();
3277 headersDeclsMap[autoParseKey] = headers;
3278 }
3279 }
3280
3281 // The same for the functions:
3282 for (auto & func : funcDecls) {
3283 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*func, interp)};
3284 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*func)] = headers;
3285 }
3286
3287 // The same for the variables:
3288 for (auto & var : varDecls) {
3289 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*var, interp)};
3290 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*var)] = headers;
3291 }
3292
3293 // The same for the enums:
3294 for (auto & en : enumDecls) {
3295 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*en, interp)};
3296 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*en)] = headers;
3297 }
3298}
3299
3300////////////////////////////////////////////////////////////////////////////////
3301/// Generate the fwd declarations of the selected entities
3302
3303static std::string GenerateFwdDeclString(const RScanner &scan,
3304 const cling::Interpreter &interp)
3305{
3306 std::string newFwdDeclString;
3307
3308 using namespace ROOT::TMetaUtils::AST2SourceTools;
3309
3310 std::string fwdDeclString;
3311 std::string buffer;
3312 std::unordered_set<std::string> fwdDecls;
3313
3314 // Classes
3315/*
3316 for (auto const & annRcd : scan.fSelectedClasses) {
3317 const auto rcdDeclPtr = annRcd.GetRecordDecl();
3318
3319 int retCode = FwdDeclFromRcdDecl(*rcdDeclPtr, interp, buffer);
3320 if (-1 == retCode) {
3321 ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3322 "Error generating fwd decl for class %s\n",
3323 annRcd.GetNormalizedName());
3324 return emptyString;
3325 }
3326 if (retCode == 0 && fwdDecls.insert(buffer).second)
3327 fwdDeclString += "\"" + buffer + "\"\n";
3328 }
3329*/
3330 // Build the input for a transaction containing all of the selected declarations
3331 // Cling will produce the fwd declaration payload.
3332
3333 std::vector<const clang::Decl *> selectedDecls(scan.fSelectedClasses.size());
3334
3335 // Pick only RecordDecls
3336 std::transform (scan.fSelectedClasses.begin(),
3337 scan.fSelectedClasses.end(),
3338 selectedDecls.begin(),
3339 [](const ROOT::TMetaUtils::AnnotatedRecordDecl& rcd){return rcd.GetRecordDecl();});
3340
3341 for (auto* TD: scan.fSelectedTypedefs)
3342 selectedDecls.push_back(TD);
3343
3344// for (auto* VAR: scan.fSelectedVariables)
3345// selectedDecls.push_back(VAR);
3346
3347 std::string fwdDeclLogs;
3348
3349 // The "R\"DICTFWDDCLS(\n" ")DICTFWDDCLS\"" pieces have been moved to
3350 // TModuleGenerator to be able to make the diagnostics more telling in presence
3351 // of an issue ROOT-6752.
3352 fwdDeclString += Decls2FwdDecls(selectedDecls,IsLinkdefFile,interp, genreflex::verbose ? &fwdDeclLogs : nullptr);
3353
3354 if (genreflex::verbose && !fwdDeclLogs.empty())
3355 std::cout << "Logs from forward decl printer: \n"
3356 << fwdDeclLogs;
3357
3358 // Functions
3359// for (auto const& fcnDeclPtr : scan.fSelectedFunctions){
3360// int retCode = FwdDeclFromFcnDecl(*fcnDeclPtr, interp, buffer);
3361// newFwdDeclString += Decl2FwdDecl(*fcnDeclPtr,interp);
3362// if (-1 == retCode){
3363// ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3364// "Error generating fwd decl for function %s\n",
3365// fcnDeclPtr->getNameAsString().c_str());
3366// return emptyString;
3367// }
3368// if (retCode == 0 && fwdDecls.insert(buffer).second)
3369// fwdDeclString+="\""+buffer+"\"\n";
3370// }
3371
3372 if (fwdDeclString.empty()) fwdDeclString = "";
3373 return fwdDeclString;
3374}
3375
3376////////////////////////////////////////////////////////////////////////////////
3377/// Generate a string for the dictionary from the headers-classes map.
3378
3379const std::string GenerateStringFromHeadersForClasses(const HeadersDeclsMap_t &headersClassesMap,
3380 const std::string &detectedUmbrella,
3381 bool payLoadOnly = false)
3382{
3383 std::string headerName;
3384
3386 std::cout << "Class-headers Mapping:\n";
3387 std::string headersClassesMapString = "";
3388 for (auto const & classHeaders : headersClassesMap) {
3390 std::cout << " o " << classHeaders.first << " --> ";
3391 headersClassesMapString += "\"";
3392 headersClassesMapString += classHeaders.first + "\"";
3393 for (auto const & header : classHeaders.second) {
3394 headerName = (detectedUmbrella == header || payLoadOnly) ? "payloadCode" : "\"" + header + "\"";
3395 headersClassesMapString += ", " + headerName;
3397 std::cout << ", " << headerName;
3398 if (payLoadOnly)
3399 break;
3400 }
3402 std::cout << std::endl;
3403 headersClassesMapString += ", \"@\",\n";
3404 }
3405 headersClassesMapString += "nullptr";
3406 return headersClassesMapString;
3407}
3408
3409////////////////////////////////////////////////////////////////////////////////
3410
3411bool IsImplementationName(const std::string &filename)
3412{
3414}
3415
3416////////////////////////////////////////////////////////////////////////////////
3417/// Check if the argument is a sane cling argument. Performing the following checks:
3418/// 1) It does not start with "--" and is not the --param option.
3419
3420bool IsCorrectClingArgument(const std::string& argument)
3421{
3422 if (ROOT::TMetaUtils::BeginsWith(argument,"--") && !ROOT::TMetaUtils::BeginsWith(argument,"--param")) return false;
3423 return true;
3424}
3425
3426////////////////////////////////////////////////////////////////////////////////
3427bool NeedsSelection(const char* name)
3428{
3429 static const std::vector<std::string> namePrfxes {
3430 "array<",
3431 "unique_ptr<"};
3432 auto pos = find_if(namePrfxes.begin(),
3433 namePrfxes.end(),
3434 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3435 return namePrfxes.end() == pos;
3436}
3437
3438////////////////////////////////////////////////////////////////////////////////
3439
3441{
3442 static const std::vector<std::string> uclNamePrfxes {
3443 "chrono:",
3444 "ratio<",
3445 "shared_ptr<"};
3446 static const std::set<std::string> unsupportedClassesNormNames{
3447 "regex",
3448 "thread"};
3449 if ( unsupportedClassesNormNames.count(name) == 1) return false;
3450 auto pos = find_if(uclNamePrfxes.begin(),
3451 uclNamePrfxes.end(),
3452 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3453 return uclNamePrfxes.end() == pos;
3454}
3455
3456////////////////////////////////////////////////////////////////////////////////
3457/// Check if the list of selected classes contains any class which is not
3458/// supported. Return the number of unsupported classes in the selection.
3459
3461{
3462 int nerrors = 0;
3463 for (auto&& aRcd : annotatedRcds){
3464 auto clName = aRcd.GetNormalizedName();
3465 if (!IsSupportedClassName(clName)){
3466 std::cerr << "Error: Class " << clName << " has been selected but "
3467 << "currently the support for its I/O is not yet available. Note that "
3468 << clName << ", even if not selected, will be available for "
3469 << "interpreted code.\n";
3470 nerrors++;
3471 }
3472 if (!NeedsSelection(clName)){
3473 std::cerr << "Error: It is not necessary to explicitly select class "
3474 << clName << ". I/O is supported for it transparently.\n";
3475 nerrors++;
3476 }
3477 }
3478 return nerrors;
3479}
3480
3481////////////////////////////////////////////////////////////////////////////////
3482
3483class TRootClingCallbacks : public cling::InterpreterCallbacks {
3484private:
3485 std::list<std::string>& fFilesIncludedByLinkdef;
3486 bool isLocked = false;
3487public:
3488 TRootClingCallbacks(cling::Interpreter* interp, std::list<std::string>& filesIncludedByLinkdef):
3489 InterpreterCallbacks(interp),
3490 fFilesIncludedByLinkdef(filesIncludedByLinkdef){};
3491
3493
3494 void InclusionDirective(clang::SourceLocation /*HashLoc*/, const clang::Token & /*IncludeTok*/,
3495 llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange /*FilenameRange*/,
3496 clang::OptionalFileEntryRef /*File*/, llvm::StringRef /*SearchPath*/,
3497 llvm::StringRef /*RelativePath*/, const clang::Module * /*Imported*/,
3498 clang::SrcMgr::CharacteristicKind /*FileType*/) override
3499 {
3500 if (isLocked) return;
3501 if (IsAngled) return;
3502 auto& PP = m_Interpreter->getCI()->getPreprocessor();
3503 auto curLexer = PP.getCurrentFileLexer();
3504 if (!curLexer) return;
3505 auto fileEntry = curLexer->getFileEntry();
3506 if (!fileEntry) return;
3507 auto thisFileName = fileEntry->getName();
3508 auto fileNameAsString = FileName.str();
3509 auto isThisLinkdef = ROOT::TMetaUtils::IsLinkdefFile(thisFileName.data());
3510 if (isThisLinkdef) {
3511 auto isTheIncludedLinkdef = ROOT::TMetaUtils::IsLinkdefFile(fileNameAsString.c_str());
3512 if (isTheIncludedLinkdef) {
3513 fFilesIncludedByLinkdef.clear();
3514 isLocked = true;
3515 } else {
3516 fFilesIncludedByLinkdef.emplace_back(fileNameAsString.c_str());
3517 }
3518 }
3519 }
3520
3521 // rootcling pre-includes things such as Rtypes.h. This means that ACLiC can
3522 // call rootcling asking it to create a module for a file with no #includes
3523 // but relying on things from Rtypes.h such as the ClassDef macro.
3524 //
3525 // When rootcling starts building a module, it becomes resilient to the
3526 // outside environment and pre-included files have no effect. This hook
3527 // informs rootcling when a new submodule is being built so that it can
3528 // make Core.Rtypes.h visible.
3529 void EnteredSubmodule(clang::Module* M,
3530 clang::SourceLocation ImportLoc,
3531 bool ForPragma) override {
3532 assert(M);
3533 using namespace clang;
3534 if (llvm::StringRef(M->Name).endswith("ACLiC_dict")) {
3535 Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
3536 HeaderSearch& HS = PP.getHeaderSearchInfo();
3537 // FIXME: Reduce to Core.Rtypes.h.
3538 Module* CoreModule = HS.lookupModule("Core", SourceLocation(),
3539 /*AllowSearch*/false);
3540 assert(M && "Must have module Core");
3541 PP.makeModuleVisible(CoreModule, ImportLoc);
3542 }
3543 }
3544};
3545
3546static llvm::cl::list<std::string>
3547gOptModuleByproducts("mByproduct", llvm::cl::ZeroOrMore,
3548 llvm::cl::Hidden,
3549 llvm::cl::desc("The list of the expected implicit modules build as part of building the current module."),
3550 llvm::cl::cat(gRootclingOptions));
3551// Really llvm::cl::Required, will be changed in RootClingMain below.
3552static llvm::cl::opt<std::string>
3553gOptDictionaryFileName(llvm::cl::Positional,
3554 llvm::cl::desc("<output dictionary file>"),
3555 llvm::cl::cat(gRootclingOptions));
3556
3557////////////////////////////////////////////////////////////////////////////////
3558/// Custom diag client for clang that verifies that each implicitly build module
3559/// is a system module. If not, it will let the current rootcling invocation
3560/// fail with an error. All other diags beside module build remarks will be
3561/// forwarded to the passed child diag client.
3562///
3563/// The reason why we need this is that if we built implicitly a C++ module
3564/// that belongs to a ROOT dictionary, then we will miss information generated
3565/// by rootcling in this file (e.g. the source code comments to annotation
3566/// attributes transformation will be missing in the module file).
3567class CheckModuleBuildClient : public clang::DiagnosticConsumer {
3568 clang::DiagnosticConsumer *fChild;
3570 clang::ModuleMap &fMap;
3571
3572public:
3573 CheckModuleBuildClient(clang::DiagnosticConsumer *Child, bool OwnsChild, clang::ModuleMap &Map)
3574 : fChild(Child), fOwnsChild(OwnsChild), fMap(Map)
3575 {
3576 }
3577
3579 {
3580 if (fOwnsChild)
3581 delete fChild;
3582 }
3583
3584 virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
3585 {
3586 using namespace clang::diag;
3587
3588 // This method catches the module_build remark from clang and checks if
3589 // the implicitly built module is a system module or not. We only support
3590 // building system modules implicitly.
3591
3592 std::string moduleName;
3593 const clang::Module *module = nullptr;
3594
3595 // Extract the module from the diag argument with index 0.
3596 const auto &ID = Info.getID();
3597 if (ID == remark_module_build || ID == remark_module_build_done) {
3598 moduleName = Info.getArgStdStr(0);
3599 module = fMap.findModule(moduleName);
3600 // We should never be able to build a module without having it in the
3601 // modulemap. Still, let's print a warning that we at least tell the
3602 // user that this could lead to problems.
3603 if (!module) {
3605 "Couldn't find module %s in the available modulemaps. This"
3606 "prevents us from correctly diagnosing wrongly built modules.\n",
3607 moduleName.c_str());
3608 }
3609 }
3610
3611 // A dictionary module could build implicitly a set of implicit modules.
3612 // For example, the Core module builds libc.pcm and std.pcm implicitly.
3613 // Those modules do not require I/O information and it is okay to build
3614 // them as part of another module.
3615 // However, we can build a module which requires I/O implictly which is
3616 // an error because rootcling is not able to generate the corresponding
3617 // dictionary.
3618 // If we build a I/O requiring module implicitly we should display
3619 // an error unless the -mByproduct was specified.
3620 bool isByproductModule
3621 = module && std::find(gOptModuleByproducts.begin(), gOptModuleByproducts.end(), moduleName) != gOptModuleByproducts.end();
3622 if (!isByproductModule)
3623 fChild->HandleDiagnostic(DiagLevel, Info);
3624
3625 if (ID == remark_module_build && !isByproductModule) {
3627 "Building module '%s' implicitly. If '%s' requires a \n"
3628 "dictionary please specify build dependency: '%s' depends on '%s'.\n"
3629 "Otherwise, specify '-mByproduct %s' to disable this diagnostic.\n",
3630 moduleName.c_str(), moduleName.c_str(), gOptDictionaryFileName.c_str(),
3631 moduleName.c_str(), moduleName.c_str());
3632 }
3633 }
3634
3635 // All methods below just forward to the child and the default method.
3636 virtual void clear() override
3637 {
3638 fChild->clear();
3639 DiagnosticConsumer::clear();
3640 }
3641
3642 virtual void BeginSourceFile(const clang::LangOptions &LangOpts, const clang::Preprocessor *PP) override
3643 {
3644 fChild->BeginSourceFile(LangOpts, PP);
3645 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
3646 }
3647
3648 virtual void EndSourceFile() override
3649 {
3650 fChild->EndSourceFile();
3651 DiagnosticConsumer::EndSourceFile();
3652 }
3653
3654 virtual void finish() override
3655 {
3656 fChild->finish();
3657 DiagnosticConsumer::finish();
3658 }
3659
3660 virtual bool IncludeInDiagnosticCounts() const override { return fChild->IncludeInDiagnosticCounts(); }
3661};
3662
3664#if defined(_WIN32) && defined(_MSC_VER)
3665 // Suppress error dialogs to avoid hangs on build nodes.
3666 // One can use an environment variable (Cling_GuiOnAssert) to enable
3667 // the error dialogs.
3668 const char *EnablePopups = getenv("Cling_GuiOnAssert");
3669 if (EnablePopups == nullptr || EnablePopups[0] == '0') {
3670 ::_set_error_mode(_OUT_TO_STDERR);
3671 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3672 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
3673 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3674 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
3675 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3676 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
3677 }
3678#endif
3679}
3680
3681static llvm::cl::opt<bool> gOptForce("f", llvm::cl::desc("Overwrite <file>s."),
3682 llvm::cl::cat(gRootclingOptions));
3683static llvm::cl::opt<bool> gOptRootBuild("rootbuild", llvm::cl::desc("If we are building ROOT."),
3684 llvm::cl::Hidden,
3685 llvm::cl::cat(gRootclingOptions));
3694static llvm::cl::opt<VerboseLevel>
3695gOptVerboseLevel(llvm::cl::desc("Choose verbosity level:"),
3696 llvm::cl::values(clEnumVal(v, "Show errors."),
3697 clEnumVal(v0, "Show only fatal errors."),
3698 clEnumVal(v1, "Show errors (the same as -v)."),
3699 clEnumVal(v2, "Show warnings (default)."),
3700 clEnumVal(v3, "Show notes."),
3701 clEnumVal(v4, "Show information.")),
3702 llvm::cl::init(v2),
3703 llvm::cl::cat(gRootclingOptions));
3704
3705static llvm::cl::opt<bool>
3706gOptCint("cint", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3707 llvm::cl::Hidden,
3708 llvm::cl::cat(gRootclingOptions));
3709static llvm::cl::opt<bool>
3710gOptReflex("reflex", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3711 llvm::cl::Hidden,
3712 llvm::cl::cat(gRootclingOptions));
3713static llvm::cl::opt<bool>
3714gOptGccXml("gccxml", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3715 llvm::cl::Hidden,
3716 llvm::cl::cat(gRootclingOptions));
3717static llvm::cl::opt<std::string>
3718gOptLibListPrefix("lib-list-prefix",
3719 llvm::cl::desc("An ACLiC feature which exports the list of dependent libraries."),
3720 llvm::cl::Hidden,
3721 llvm::cl::cat(gRootclingOptions));
3722static llvm::cl::opt<bool>
3723gOptGeneratePCH("generate-pch",
3724 llvm::cl::desc("Generates a pch file from a predefined set of headers. See makepch.py."),
3725 llvm::cl::Hidden,
3726 llvm::cl::cat(gRootclingOptions));
3727static llvm::cl::opt<bool>
3728gOptC("c", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3729 llvm::cl::cat(gRootclingOptions));
3730static llvm::cl::opt<bool>
3731gOptP("p", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3732 llvm::cl::cat(gRootclingOptions));
3733static llvm::cl::list<std::string>
3734gOptRootmapLibNames("rml", llvm::cl::ZeroOrMore,
3735 llvm::cl::desc("Generate rootmap file."),
3736 llvm::cl::cat(gRootclingOptions));
3737static llvm::cl::opt<std::string>
3739 llvm::cl::desc("Generate a rootmap file with the specified name."),
3740 llvm::cl::cat(gRootclingOptions));
3741static llvm::cl::opt<bool>
3742gOptCxxModule("cxxmodule",
3743 llvm::cl::desc("Generate a C++ module."),
3744 llvm::cl::cat(gRootclingOptions));
3745static llvm::cl::list<std::string>
3746gOptModuleMapFiles("moduleMapFile",
3747 llvm::cl::desc("Specify a C++ modulemap file."),
3748 llvm::cl::cat(gRootclingOptions));
3749// FIXME: Figure out how to combine the code of -umbrellaHeader and inlineInputHeader
3750static llvm::cl::opt<bool>
3751gOptUmbrellaInput("umbrellaHeader",
3752 llvm::cl::desc("A single header including all headers instead of specifying them on the command line."),
3753 llvm::cl::cat(gRootclingOptions));
3754static llvm::cl::opt<bool>
3755gOptMultiDict("multiDict",
3756 llvm::cl::desc("If this library has multiple separate LinkDef files."),
3757 llvm::cl::cat(gRootclingOptions));
3758static llvm::cl::opt<bool>
3759gOptNoGlobalUsingStd("noGlobalUsingStd",
3760 llvm::cl::desc("Do not declare {using namespace std} in dictionary global scope."),
3761 llvm::cl::cat(gRootclingOptions));
3762static llvm::cl::opt<bool>
3763gOptInterpreterOnly("interpreteronly",
3764 llvm::cl::desc("Generate minimal dictionary for interactivity (without IO information)."),
3765 llvm::cl::cat(gRootclingOptions));
3766static llvm::cl::opt<bool>
3768 llvm::cl::desc("Split the dictionary into two parts: one containing the IO (ClassDef)\
3769information and another the interactivity support."),
3770 llvm::cl::cat(gRootclingOptions));
3771static llvm::cl::opt<bool>
3772gOptNoDictSelection("noDictSelection",
3773 llvm::cl::Hidden,
3774 llvm::cl::desc("Do not run the selection rules. Useful when in -onepcm mode."),
3775 llvm::cl::cat(gRootclingOptions));
3776static llvm::cl::opt<std::string>
3778 llvm::cl::desc("The path to the library of the built dictionary."),
3779 llvm::cl::cat(gRootclingOptions));
3780static llvm::cl::list<std::string>
3782 llvm::cl::desc("The list of dependent modules of the dictionary."),
3783 llvm::cl::cat(gRootclingOptions));
3784static llvm::cl::list<std::string>
3785gOptExcludePaths("excludePath", llvm::cl::ZeroOrMore,
3786 llvm::cl::desc("Do not store the <path> in the dictionary."),
3787 llvm::cl::cat(gRootclingOptions));
3788// FIXME: This does not seem to work. We have one use of -inlineInputHeader in
3789// ROOT and it does not produce the expected result.
3790static llvm::cl::opt<bool>
3791gOptInlineInput("inlineInputHeader",
3792 llvm::cl::desc("Does not generate #include <header> but expands the header content."),
3793 llvm::cl::cat(gRootclingOptions));
3794// FIXME: This is totally the wrong concept. We should not expose an interface
3795// to be able to tell which component is in the pch and which needs extra
3796// scaffolding for interactive use. Moreover, some of the ROOT components are
3797// partially in the pch and this option makes it impossible to express that.
3798// We should be able to get the list of headers in the pch early and scan
3799// through them.
3800static llvm::cl::opt<bool>
3801gOptWriteEmptyRootPCM("writeEmptyRootPCM",
3802 llvm::cl::Hidden,
3803 llvm::cl::desc("Does not include the header files as it assumes they exist in the pch."),
3804 llvm::cl::cat(gRootclingOptions));
3805static llvm::cl::opt<bool>
3807 llvm::cl::desc("Check the selection syntax only."),
3808 llvm::cl::cat(gRootclingOptions));
3809static llvm::cl::opt<bool>
3810gOptFailOnWarnings("failOnWarnings",
3811 llvm::cl::desc("Fail if there are warnings."),
3812 llvm::cl::cat(gRootclingOptions));
3813static llvm::cl::opt<bool>
3814gOptNoIncludePaths("noIncludePaths",
3815 llvm::cl::desc("Do not store include paths but rely on the env variable ROOT_INCLUDE_PATH."),
3816 llvm::cl::cat(gRootclingOptions));
3817static llvm::cl::opt<std::string>
3818gOptISysRoot("isysroot", llvm::cl::Prefix, llvm::cl::Hidden,
3819 llvm::cl::desc("Specify an isysroot."),
3820 llvm::cl::cat(gRootclingOptions),
3821 llvm::cl::init("-"));
3822static llvm::cl::list<std::string>
3823gOptIncludePaths("I", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3824 llvm::cl::desc("Specify an include path."),
3825 llvm::cl::cat(gRootclingOptions));
3826static llvm::cl::list<std::string>
3827gOptCompDefaultIncludePaths("compilerI", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3828 llvm::cl::desc("Specify a compiler default include path, to suppress unneeded `-isystem` arguments."),
3829 llvm::cl::cat(gRootclingOptions));
3830static llvm::cl::list<std::string>
3831gOptSysIncludePaths("isystem", llvm::cl::ZeroOrMore,
3832 llvm::cl::desc("Specify a system include path."),
3833 llvm::cl::cat(gRootclingOptions));
3834static llvm::cl::list<std::string>
3835gOptPPDefines("D", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3836 llvm::cl::desc("Specify defined macros."),
3837 llvm::cl::cat(gRootclingOptions));
3838static llvm::cl::list<std::string>
3839gOptPPUndefines("U", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3840 llvm::cl::desc("Specify undefined macros."),
3841 llvm::cl::cat(gRootclingOptions));
3842static llvm::cl::list<std::string>
3843gOptWDiags("W", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3844 llvm::cl::desc("Specify compiler diagnostics options."),
3845 llvm::cl::cat(gRootclingOptions));
3846// Really OneOrMore, will be changed in RootClingMain below.
3847static llvm::cl::list<std::string>
3848gOptDictionaryHeaderFiles(llvm::cl::Positional, llvm::cl::ZeroOrMore,
3849 llvm::cl::desc("<list of dictionary header files> <LinkDef file>"),
3850 llvm::cl::cat(gRootclingOptions));
3851static llvm::cl::list<std::string>
3852gOptSink(llvm::cl::ZeroOrMore, llvm::cl::Sink,
3853 llvm::cl::desc("Consumes all unrecognized options."),
3854 llvm::cl::cat(gRootclingOptions));
3855
3856static llvm::cl::SubCommand
3857gBareClingSubcommand("bare-cling", "Call directly cling and exit.");
3858
3859static llvm::cl::list<std::string>
3860gOptBareClingSink(llvm::cl::OneOrMore, llvm::cl::Sink,
3861 llvm::cl::desc("Consumes options and sends them to cling."),
3862 llvm::cl::cat(gRootclingOptions), llvm::cl::sub(gBareClingSubcommand));
3863
3864////////////////////////////////////////////////////////////////////////////////
3865/// Returns true iff a given module (and its submodules) contains all headers
3866/// needed by the given ModuleGenerator.
3867/// The names of all header files that are needed by the ModuleGenerator but are
3868/// not in the given module will be inserted into the MissingHeader variable.
3869/// Returns true iff the PCH was successfully generated.
3870static bool ModuleContainsHeaders(TModuleGenerator &modGen, clang::HeaderSearch &headerSearch,
3871 clang::Module *module, std::vector<std::array<std::string, 2>> &missingHeaders)
3872{
3873 // Now we collect all header files from the previously collected modules.
3874 std::vector<clang::Module::Header> moduleHeaders;
3876 [&moduleHeaders](const clang::Module::Header &h) { moduleHeaders.push_back(h); });
3877
3878 bool foundAllHeaders = true;
3879
3880 auto isHeaderInModule = [&moduleHeaders](const std::string &header) {
3881 for (const clang::Module::Header &moduleHeader : moduleHeaders)
3882 if (header == moduleHeader.NameAsWritten)
3883 return true;
3884 return false;
3885 };
3886
3887 // Go through the list of headers that are required by the ModuleGenerator
3888 // and check for each header if it's in one of the modules we loaded.
3889 // If not, make sure we fail at the end and mark the header as missing.
3890 for (const std::string &header : modGen.GetHeaders()) {
3891 if (isHeaderInModule(header))
3892 continue;
3893
3894 clang::ModuleMap::KnownHeader SuggestedModule;
3895 clang::ConstSearchDirIterator *CurDir = nullptr;
3896 if (auto FE = headerSearch.LookupFile(
3897 header, clang::SourceLocation(),
3898 /*isAngled*/ false,
3899 /*FromDir*/ 0, CurDir,
3900 clang::ArrayRef<std::pair<const clang::FileEntry *, const clang::DirectoryEntry *>>(),
3901 /*SearchPath*/ 0,
3902 /*RelativePath*/ 0,
3903 /*RequestingModule*/ 0, &SuggestedModule,
3904 /*IsMapped*/ 0,
3905 /*IsFrameworkFound*/ nullptr,
3906 /*SkipCache*/ false,
3907 /*BuildSystemModule*/ false,
3908 /*OpenFile*/ false,
3909 /*CacheFail*/ false)) {
3910 if (auto OtherModule = SuggestedModule.getModule()) {
3911 std::string OtherModuleName;
3912 auto TLM = OtherModule->getTopLevelModuleName();
3913 if (!TLM.empty())
3914 OtherModuleName = TLM.str();
3915 else
3916 OtherModuleName = OtherModule->Name;
3917
3918 // Don't complain about headers that are actually in by-products:
3919 if (std::find(gOptModuleByproducts.begin(), gOptModuleByproducts.end(), OtherModuleName)
3920 != gOptModuleByproducts.end())
3921 continue;
3922
3923 missingHeaders.push_back({header, OtherModuleName});
3924 }
3925 } else {
3926 missingHeaders.push_back({header, {}});
3927 }
3928 foundAllHeaders = false;
3929 }
3930 return foundAllHeaders;
3931}
3932
3933////////////////////////////////////////////////////////////////////////////////
3934/// Check moduleName validity from modulemap. Check if this module is defined or not.
3935static bool CheckModuleValid(TModuleGenerator &modGen, const std::string &resourceDir, cling::Interpreter &interpreter,
3936 llvm::StringRef LinkdefPath, const std::string &moduleName)
3937{
3938 clang::CompilerInstance *CI = interpreter.getCI();
3939 clang::HeaderSearch &headerSearch = CI->getPreprocessor().getHeaderSearchInfo();
3940 headerSearch.loadTopLevelSystemModules();
3941
3942 // Actually lookup the module on the computed module name.
3943 clang::Module *module = headerSearch.lookupModule(llvm::StringRef(moduleName));
3944
3945 // Inform the user and abort if we can't find a module with a given name.
3946 if (!module) {
3947 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't find module with name '%s' in modulemap!\n",
3948 moduleName.c_str());
3949 return false;
3950 }
3951
3952 // Check if the loaded module covers all headers that were specified
3953 // by the user on the command line. This is an integrity check to
3954 // ensure that our used module map is not containing extraneous headers.
3955 std::vector<std::array<std::string, 2>> missingHdrMod;
3956 if (!ModuleContainsHeaders(modGen, headerSearch, module, missingHdrMod)) {
3957 // FIXME: Upgrade this to an error once modules are stable.
3958 std::stringstream msgStream;
3959 msgStream << "after creating module \"" << module->Name << "\" ";
3960 if (!module->PresumedModuleMapFile.empty())
3961 msgStream << "using modulemap \"" << module->PresumedModuleMapFile << "\" ";
3962 msgStream << "the following headers are not part of that module:\n";
3963 for (auto &H : missingHdrMod) {
3964 msgStream << " " << H[0];
3965 if (!H[1].empty())
3966 msgStream << " (already part of module \"" << H[1] << "\")";
3967 msgStream << "\n";
3968 }
3969 std::string warningMessage = msgStream.str();
3970
3971 bool maybeUmbrella = modGen.GetHeaders().size() == 1;
3972 // We may have an umbrella and forgot to add the flag. Downgrade the
3973 // warning into an information message.
3974 // FIXME: We should open the umbrella, extract the set of header files
3975 // and check if they exist in the modulemap.
3976 // FIXME: We should also check if the header files are specified in the
3977 // modulemap file as they appeared in the rootcling invocation, i.e.
3978 // if we passed rootcling ... -I/some/path somedir/some/header, the
3979 // modulemap should contain module M { header "somedir/some/header" }
3980 // This way we will make sure the module is properly activated.
3981 if (!gOptUmbrellaInput && maybeUmbrella) {
3982 ROOT::TMetaUtils::Info("CheckModuleValid, %s. You can silence this message by adding %s to the invocation.",
3983 warningMessage.c_str(),
3984 gOptUmbrellaInput.ArgStr.data());
3985 return true;
3986 }
3987
3988 ROOT::TMetaUtils::Warning("CheckModuleValid", warningMessage.c_str());
3989 // We include the missing headers to fix the module for the user.
3990 std::vector<std::string> missingHeaders;
3991 std::transform(missingHdrMod.begin(), missingHdrMod.end(), missingHeaders.begin(),
3992 [](const std::array<std::string, 2>& HdrMod) { return HdrMod[0];});
3993 if (!IncludeHeaders(missingHeaders, interpreter)) {
3994 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't include missing module headers for module '%s'!\n",
3995 module->Name.c_str());
3996 }
3997 }
3998
3999 return true;
4000}
4001
4002static llvm::StringRef GetModuleNameFromRdictName(llvm::StringRef rdictName)
4003{
4004 // Try to get the module name in the modulemap based on the filepath.
4005 llvm::StringRef moduleName = llvm::sys::path::filename(rdictName);
4006 moduleName.consume_front("lib");
4007 moduleName.consume_back(".pcm");
4008 moduleName.consume_back("_rdict");
4009 return moduleName;
4010}
4011
4012////////////////////////////////////////////////////////////////////////////////
4013
4014int RootClingMain(int argc,
4015 char **argv,
4016 bool isGenreflex = false)
4017{
4018 // Set number of required arguments. We cannot do this globally since it
4019 // would interfere with LLVM's option parsing.
4020 gOptDictionaryFileName.setNumOccurrencesFlag(llvm::cl::Required);
4021 gOptDictionaryHeaderFiles.setNumOccurrencesFlag(llvm::cl::OneOrMore);
4022
4023 // Copied from cling driver.
4024 // FIXME: Uncomment once we fix ROOT's teardown order.
4025 //llvm::llvm_shutdown_obj shutdownTrigger;
4026
4027 const char *executableFileName = argv[0];
4028
4029 llvm::sys::PrintStackTraceOnErrorSignal(executableFileName);
4030 llvm::PrettyStackTraceProgram X(argc, argv);
4032
4033#if defined(R__WIN32) && !defined(R__WINGCC)
4034 // FIXME: This is terrible hack allocating and changing the argument set.
4035 // We should remove it and use standard llvm facilities to convert the paths.
4036 // cygwin's make is presenting us some cygwin paths even though
4037 // we are windows native. Convert them as good as we can.
4038 for (int iic = 1 /* ignore binary file name in argv[0] */; iic < argc; ++iic) {
4039 std::string iiarg(argv[iic]);
4040 if (FromCygToNativePath(iiarg)) {
4041 size_t len = iiarg.length();
4042 // yes, we leak.
4043 char *argviic = new char[len + 1];
4044 strlcpy(argviic, iiarg.c_str(), len + 1);
4045 argv[iic] = argviic;
4046 }
4047 }
4048#endif
4049
4050 // Hide options from llvm which we got from static initialization of libCling.
4051 llvm::cl::HideUnrelatedOptions(/*keep*/gRootclingOptions);
4052
4053 // Define Options aliasses
4054 auto &opts = llvm::cl::getRegisteredOptions();
4055 llvm::cl::Option* optHelp = opts["help"];
4056 llvm::cl::alias optHelpAlias1("h",
4057 llvm::cl::desc("Alias for -help"),
4058 llvm::cl::aliasopt(*optHelp));
4059 llvm::cl::alias optHelpAlias2("?",
4060 llvm::cl::desc("Alias for -help"),
4061 llvm::cl::aliasopt(*optHelp));
4062
4063 llvm::cl::ParseCommandLineOptions(argc, argv, "rootcling");
4064
4065 std::string llvmResourceDir = std::string(gDriverConfig->fTROOT__GetEtcDir()) + "/cling";
4067 std::vector<const char *> clingArgsC;
4068 clingArgsC.push_back(executableFileName);
4069 // Help cling finds its runtime (RuntimeUniverse.h and such).
4070 clingArgsC.push_back("-I");
4071 clingArgsC.push_back(gDriverConfig->fTROOT__GetEtcDir());
4072
4073 //clingArgsC.push_back("-resource-dir");
4074 //clingArgsC.push_back(llvmResourceDir.c_str());
4075
4076 for (const std::string& Opt : gOptBareClingSink)
4077 clingArgsC.push_back(Opt.c_str());
4078
4079 auto interp = std::make_unique<cling::Interpreter>(clingArgsC.size(),
4080 &clingArgsC[0],
4081 llvmResourceDir.c_str());
4082 // FIXME: Diagnose when we have misspelled a flag. Currently we show no
4083 // diagnostic and report exit as success.
4084 return interp->getDiagnostics().hasFatalErrorOccurred();
4085 }
4086
4087 std::string dictname;
4088
4090 if (gOptRootBuild) {
4091 // running rootcling as part of the ROOT build for ROOT libraries.
4092 gBuildingROOT = true;
4093 }
4094 }
4095
4096 if (!gOptModuleMapFiles.empty() && !gOptCxxModule) {
4097 ROOT::TMetaUtils::Error("", "Option %s can be used only when option %s is specified.\n",
4098 gOptModuleMapFiles.ArgStr.str().c_str(),
4099 gOptCxxModule.ArgStr.str().c_str());
4100 std::cout << "\n";
4101 llvm::cl::PrintHelpMessage();
4102 return 1;
4103 }
4104
4105 // Set the default verbosity
4107 if (gOptVerboseLevel == v4)
4108 genreflex::verbose = true;
4109
4110 if (gOptReflex)
4111 isGenreflex = true;
4112
4113 if (!gOptLibListPrefix.empty()) {
4114 string filein = gOptLibListPrefix + ".in";
4115 FILE *fp;
4116 if ((fp = fopen(filein.c_str(), "r")) == nullptr) {
4117 ROOT::TMetaUtils::Error(nullptr, "%s: The input list file %s does not exist\n", executableFileName, filein.c_str());
4118 return 1;
4119 }
4120 fclose(fp);
4121 }
4122
4124 FILE *fp;
4125 if (!gOptIgnoreExistingDict && (fp = fopen(gOptDictionaryFileName.c_str(), "r")) != nullptr) {
4126 fclose(fp);
4127 if (!gOptForce) {
4128 ROOT::TMetaUtils::Error(nullptr, "%s: output file %s already exists\n", executableFileName, gOptDictionaryFileName.c_str());
4129 return 1;
4130 }
4131 }
4132
4133 // remove possible pathname to get the dictionary name
4134 if (gOptDictionaryFileName.size() > (PATH_MAX - 1)) {
4135 ROOT::TMetaUtils::Error(nullptr, "rootcling: dictionary name too long (more than %d characters): %s\n",
4136 (PATH_MAX - 1), gOptDictionaryFileName.c_str());
4137 return 1;
4138 }
4139
4140 dictname = llvm::sys::path::filename(gOptDictionaryFileName).str();
4141 }
4142
4143 if (gOptForce && dictname.empty()) {
4144 ROOT::TMetaUtils::Error(nullptr, "Inconsistent set of arguments detected: overwrite of dictionary file forced but no filename specified.\n");
4145 llvm::cl::PrintHelpMessage();
4146 return 1;
4147 }
4148
4149 std::vector<std::string> clingArgs;
4150 clingArgs.push_back(executableFileName);
4151 clingArgs.push_back("-iquote.");
4152
4153 bool dictSelection = !gOptNoDictSelection;
4154
4155 // Collect the diagnostic pragmas linked to the usage of -W
4156 // Workaround for ROOT-5656
4157 std::list<std::string> diagnosticPragmas = {"#pragma clang diagnostic ignored \"-Wdeprecated-declarations\""};
4158
4159 if (gOptFailOnWarnings) {
4160 using namespace ROOT::TMetaUtils;
4161 // If warnings are disabled with the current verbosity settings, lower
4162 // it so that the user sees the warning that caused the failure.
4163 if (GetErrorIgnoreLevel() > kWarning)
4164 GetErrorIgnoreLevel() = kWarning;
4165 GetWarningsAreErrors() = true;
4166 }
4167
4168 if (gOptISysRoot != "-") {
4169 if (gOptISysRoot.empty()) {
4170 ROOT::TMetaUtils::Error("", "isysroot specified without a value.\n");
4171 return 1;
4172 }
4173 clingArgs.push_back(gOptISysRoot.ArgStr.str());
4174 clingArgs.push_back(gOptISysRoot.ValueStr.str());
4175 }
4176
4177 // Check if we have a multi dict request but no target library
4178 if (gOptMultiDict && gOptSharedLibFileName.empty()) {
4179 ROOT::TMetaUtils::Error("", "Multidict requested but no target library. Please specify one with the -s argument.\n");
4180 return 1;
4181 }
4182
4183 for (const std::string &PPDefine : gOptPPDefines)
4184 clingArgs.push_back(std::string("-D") + PPDefine);
4185
4186 for (const std::string &PPUndefine : gOptPPUndefines)
4187 clingArgs.push_back(std::string("-U") + PPUndefine);
4188
4189 for (const std::string &IncludePath : gOptIncludePaths)
4190 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(IncludePath));
4191
4192 for (const std::string &IncludePath : gOptSysIncludePaths) {
4193 // Prevent mentioning compiler default include directories as -isystem
4194 // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129)
4195 if (std::find(gOptCompDefaultIncludePaths.begin(), gOptCompDefaultIncludePaths.end(), IncludePath)
4196 == gOptCompDefaultIncludePaths.end()) {
4197 clingArgs.push_back("-isystem");
4198 clingArgs.push_back(llvm::sys::path::convert_to_slash(IncludePath));
4199 }
4200 }
4201
4202 for (const std::string &WDiag : gOptWDiags) {
4203 const std::string FullWDiag = std::string("-W") + WDiag;
4204 // Suppress warning when compiling the dictionary, eg. gcc G__xxx.cxx
4205 CheckForMinusW(FullWDiag, diagnosticPragmas);
4206 // Suppress warning when compiling the input headers by cling.
4207 clingArgs.push_back(FullWDiag);
4208 }
4209
4210 std::string includeDir = llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetIncludeDir());
4211 clingArgs.push_back(std::string("-I") + includeDir);
4212
4213 std::vector<std::string> pcmArgs;
4214 for (size_t parg = 0, n = clingArgs.size(); parg < n; ++parg) {
4215 auto thisArg = clingArgs[parg];
4216 auto isInclude = ROOT::TMetaUtils::BeginsWith(thisArg,"-I");
4217 if (thisArg == "-c" ||
4218 (gOptNoIncludePaths && isInclude)) continue;
4219 // We now check if the include directories are not excluded
4220 if (isInclude) {
4221 unsigned int offset = 2; // -I is two characters. Now account for spaces
4222 char c = thisArg[offset];
4223 while (c == ' ') c = thisArg[++offset];
4224 auto excludePathsEnd = gOptExcludePaths.end();
4225 auto excludePathPos = std::find_if(gOptExcludePaths.begin(),
4226 excludePathsEnd,
4227 [&](const std::string& path){
4228 return ROOT::TMetaUtils::BeginsWith(&thisArg[offset], path);});
4229 if (excludePathsEnd != excludePathPos) continue;
4230 }
4231 pcmArgs.push_back(thisArg);
4232 }
4233
4234 // cling-only arguments
4235 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetEtcDir()));
4236 // We do not want __ROOTCLING__ in the pch!
4237 if (!gOptGeneratePCH) {
4238 clingArgs.push_back("-D__ROOTCLING__");
4239 }
4240#ifdef R__MACOSX
4241 clingArgs.push_back("-DSYSTEM_TYPE_macosx");
4242#elif defined(R__WIN32)
4243 clingArgs.push_back("-DSYSTEM_TYPE_winnt");
4244
4245 // Prevent the following #error: The C++ Standard Library forbids macroizing keywords.
4246 clingArgs.push_back("-D_XKEYCHECK_H");
4247 // Tell windows.h not to #define min and max, it clashes with numerical_limits.
4248 clingArgs.push_back("-DNOMINMAX");
4249#else // assume UNIX
4250 clingArgs.push_back("-DSYSTEM_TYPE_unix");
4251#endif
4252
4253 clingArgs.push_back("-fsyntax-only");
4254#ifndef R__WIN32
4255 clingArgs.push_back("-fPIC");
4256#endif
4257 clingArgs.push_back("-Xclang");
4258 clingArgs.push_back("-fmodules-embed-all-files");
4259 clingArgs.push_back("-Xclang");
4260 clingArgs.push_back("-main-file-name");
4261 clingArgs.push_back("-Xclang");
4262 clingArgs.push_back((dictname + ".h").c_str());
4263
4265
4266 // FIXME: This line is from TModuleGenerator, but we can't reuse this code
4267 // at this point because TModuleGenerator needs a CompilerInstance (and we
4268 // currently create the arguments for creating said CompilerInstance).
4269 bool isPCH = (gOptDictionaryFileName.getValue() == "allDict.cxx");
4270 std::string outputFile;
4271 // Data is in 'outputFile', therefore in the same scope.
4272 llvm::StringRef moduleName;
4273 std::string vfsArg;
4274 // Adding -fmodules to the args will break lexing with __CINT__ defined,
4275 // and we actually do lex with __CINT__ and reuse this variable later,
4276 // we have to copy it now.
4277 auto clingArgsInterpreter = clingArgs;
4278
4279 if (gOptSharedLibFileName.empty()) {
4281 }
4282
4283 if (!isPCH && gOptCxxModule) {
4284 // We just pass -fmodules, the CIFactory will do the rest and configure
4285 // clang correctly once it sees this flag.
4286 clingArgsInterpreter.push_back("-fmodules");
4287 clingArgsInterpreter.push_back("-fno-implicit-module-maps");
4288
4289 for (const std::string &modulemap : gOptModuleMapFiles)
4290 clingArgsInterpreter.push_back("-fmodule-map-file=" + modulemap);
4291
4292 clingArgsInterpreter.push_back("-fmodule-map-file=" +
4293 std::string(gDriverConfig->fTROOT__GetIncludeDir()) +
4294 "/ROOT.modulemap");
4295 std::string ModuleMapCWD = ROOT::FoundationUtils::GetCurrentDir() + "/module.modulemap";
4296 if (llvm::sys::fs::exists(ModuleMapCWD))
4297 clingArgsInterpreter.push_back("-fmodule-map-file=" + ModuleMapCWD);
4298
4299 // Specify the module name that we can lookup the module in the modulemap.
4300 outputFile = llvm::sys::path::stem(gOptSharedLibFileName).str();
4301 // Try to get the module name in the modulemap based on the filepath.
4302 moduleName = GetModuleNameFromRdictName(outputFile);
4303
4304#ifdef _MSC_VER
4305 clingArgsInterpreter.push_back("-Xclang");
4306 clingArgsInterpreter.push_back("-fmodule-feature");
4307 clingArgsInterpreter.push_back("-Xclang");
4308 clingArgsInterpreter.push_back("msvc" + std::string(rootclingStringify(_MSC_VER)));
4309#endif
4310 clingArgsInterpreter.push_back("-fmodule-name=" + moduleName.str());
4311
4312 std::string moduleCachePath = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4313 // FIXME: This is a horrible workaround to fix the incremental builds.
4314 // The enumerated modules are built by clang impicitly based on #include of
4315 // a header which is contained within that module. The build system has
4316 // no way to track dependencies on them and trigger a rebuild.
4317 // A possible solution can be to disable completely the implicit build of
4318 // modules and each module to be built by rootcling. We need to teach
4319 // rootcling how to build modules with no IO support.
4320 if (moduleName == "Core") {
4322 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_intrinsics.pcm").str().c_str());
4323 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_stddef_max_align_t.pcm").str().c_str());
4324 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime.pcm").str().c_str());
4325 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime_Extra.pcm").str().c_str());
4326#ifdef R__WIN32
4327 remove((moduleCachePath + llvm::sys::path::get_separator() + "vcruntime.pcm").str().c_str());
4328 remove((moduleCachePath + llvm::sys::path::get_separator() + "services.pcm").str().c_str());
4329#endif
4330
4331#ifdef R__MACOSX
4332 remove((moduleCachePath + llvm::sys::path::get_separator() + "Darwin.pcm").str().c_str());
4333#else
4334 remove((moduleCachePath + llvm::sys::path::get_separator() + "libc.pcm").str().c_str());
4335#endif
4336 remove((moduleCachePath + llvm::sys::path::get_separator() + "std.pcm").str().c_str());
4337 remove((moduleCachePath + llvm::sys::path::get_separator() + "boost.pcm").str().c_str());
4338 remove((moduleCachePath + llvm::sys::path::get_separator() + "tinyxml2.pcm").str().c_str());
4339 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Config.pcm").str().c_str());
4340 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Rtypes.pcm").str().c_str());
4341 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_C.pcm").str().c_str());
4342 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_Stage1_NoRTTI.pcm").str().c_str());
4343 } else if (moduleName == "MathCore") {
4344 remove((moduleCachePath + llvm::sys::path::get_separator() + "Vc.pcm").str().c_str());
4345 }
4346
4347 // Set the C++ modules output directory to the directory where we generate
4348 // the shared library.
4349 clingArgsInterpreter.push_back("-fmodules-cache-path=" + moduleCachePath);
4350 }
4351
4352 if (gOptVerboseLevel == v4)
4353 clingArgsInterpreter.push_back("-v");
4354
4355 // Convert arguments to a C array and check if they are sane
4356 std::vector<const char *> clingArgsC;
4357 for (auto const &clingArg : clingArgsInterpreter) {
4358 if (!IsCorrectClingArgument(clingArg)){
4359 std::cerr << "Argument \""<< clingArg << "\" is not a supported cling argument. "
4360 << "This could be mistyped rootcling argument. Please check the commandline.\n";
4361 return 1;
4362 }
4363 clingArgsC.push_back(clingArg.c_str());
4364 }
4365
4366
4367 std::unique_ptr<cling::Interpreter> owningInterpPtr;
4368 cling::Interpreter* interpPtr = nullptr;
4369
4370 std::list<std::string> filesIncludedByLinkdef;
4372#ifdef R__FAST_MATH
4373 // Same setting as in TCling.cxx.
4374 clingArgsC.push_back("-ffast-math");
4375#endif
4376
4377 owningInterpPtr.reset(new cling::Interpreter(clingArgsC.size(), &clingArgsC[0],
4378 llvmResourceDir.c_str()));
4379 interpPtr = owningInterpPtr.get();
4380 } else {
4381 // Pass the interpreter arguments to TCling's interpreter:
4382 clingArgsC.push_back("-resource-dir");
4383 clingArgsC.push_back(llvmResourceDir.c_str());
4384 clingArgsC.push_back(nullptr); // signal end of array
4385 const char ** &extraArgs = *gDriverConfig->fTROOT__GetExtraInterpreterArgs();
4386 extraArgs = &clingArgsC[1]; // skip binary name
4388 if (!isGenreflex && !gOptGeneratePCH) {
4389 std::unique_ptr<TRootClingCallbacks> callBacks (new TRootClingCallbacks(interpPtr, filesIncludedByLinkdef));
4390 interpPtr->setCallbacks(std::move(callBacks));
4391 }
4392 }
4393 cling::Interpreter &interp = *interpPtr;
4394 clang::CompilerInstance *CI = interp.getCI();
4395 // FIXME: Remove this once we switch cling to use the driver. This would handle -fmodules-embed-all-files for us.
4396 CI->getFrontendOpts().ModulesEmbedAllFiles = true;
4397 CI->getSourceManager().setAllFilesAreTransient(true);
4398
4399 clang::Preprocessor &PP = CI->getPreprocessor();
4400 clang::HeaderSearch &headerSearch = PP.getHeaderSearchInfo();
4401 clang::ModuleMap &moduleMap = headerSearch.getModuleMap();
4402 auto &diags = interp.getDiagnostics();
4403
4404 // Manually enable the module build remarks. We don't enable them via the
4405 // normal clang command line arg because otherwise we would get remarks for
4406 // building STL/libc when starting the interpreter in rootcling_stage1.
4407 // We can't prevent these diags in any other way because we can only attach
4408 // our own diag client now after the interpreter has already started.
4409 diags.setSeverity(clang::diag::remark_module_build, clang::diag::Severity::Remark, clang::SourceLocation());
4410
4411 // Attach our own diag client that listens to the module_build remarks from
4412 // clang to check that we don't build dictionary C++ modules implicitly.
4413 auto recordingClient = new CheckModuleBuildClient(diags.getClient(), diags.ownsClient(), moduleMap);
4414 diags.setClient(recordingClient, true);
4415
4417 ROOT::TMetaUtils::Info(nullptr, "\n");
4418 ROOT::TMetaUtils::Info(nullptr, "==== INTERPRETER CONFIGURATION ====\n");
4419 ROOT::TMetaUtils::Info(nullptr, "== Include paths\n");
4420 interp.DumpIncludePath();
4421 printf("\n\n");
4422 fflush(stdout);
4423
4424 ROOT::TMetaUtils::Info(nullptr, "== Included files\n");
4425 interp.printIncludedFiles(llvm::outs());
4426 llvm::outs() << "\n\n";
4427 llvm::outs().flush();
4428
4429 ROOT::TMetaUtils::Info(nullptr, "== Language Options\n");
4430 const clang::LangOptions& LangOpts
4431 = interp.getCI()->getASTContext().getLangOpts();
4432#define LANGOPT(Name, Bits, Default, Description) \
4433 ROOT::TMetaUtils::Info(nullptr, "%s = %d // %s\n", #Name, (int)LangOpts.Name, Description);
4434#define ENUM_LANGOPT(Name, Type, Bits, Default, Description)
4435#include "clang/Basic/LangOptions.def"
4436 ROOT::TMetaUtils::Info(nullptr, "==== END interpreter configuration ====\n\n");
4437 }
4438
4439 interp.getOptions().ErrorOut = true;
4440 interp.enableRawInput(true);
4441
4442 if (gOptCxxModule) {
4443 for (llvm::StringRef DepMod : gOptModuleDependencies) {
4444 if (DepMod.endswith("_rdict.pcm")) {
4445 ROOT::TMetaUtils::Warning(nullptr, "'%s' value is deprecated. Please use [<fullpath>]%s.pcm\n",
4446 DepMod.data(),
4447 GetModuleNameFromRdictName(DepMod).str().data());
4448 }
4449 DepMod = GetModuleNameFromRdictName(DepMod);
4450 // We might deserialize.
4451 cling::Interpreter::PushTransactionRAII RAII(&interp);
4452 if (!interp.loadModule(DepMod.str(), /*complain*/false)) {
4453 ROOT::TMetaUtils::Error(nullptr, "Module '%s' failed to load.\n",
4454 DepMod.data());
4455 }
4456 }
4457 }
4458
4459 if (!isGenreflex) { // rootcling
4460 // ROOTCINT uses to define a few header implicitly, we need to do it explicitly.
4461 if (interp.declare("#include <assert.h>\n"
4462 "#include \"Rtypes.h\"\n"
4463 "#include \"TObject.h\"") != cling::Interpreter::kSuccess
4464 ) {
4465 // There was an error.
4466 ROOT::TMetaUtils::Error(nullptr, "Error loading the default rootcling header files.\n");
4467 return 1;
4468 }
4469 }
4470
4471 if (interp.declare("#include <string>\n" // For the list of 'opaque' typedef to also include string.
4472 "#include <RtypesCore.h>\n" // For initializing TNormalizedCtxt.
4473 "namespace std {} using namespace std;") != cling::Interpreter::kSuccess) {
4474 ROOT::TMetaUtils::Error(nullptr, "Error loading the default header files.\n");
4475 return 1;
4476 }
4477
4478 // We are now ready (enough is loaded) to init the list of opaque typedefs.
4479 ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper());
4480 ROOT::TMetaUtils::TClingLookupHelper helper(interp, normCtxt, nullptr, nullptr, nullptr);
4481 TClassEdit::Init(&helper);
4482
4483 // flags used only for the pragma parser:
4484 clingArgs.push_back("-D__CINT__");
4485 clingArgs.push_back("-D__MAKECINT__");
4486
4487 AddPlatformDefines(clingArgs);
4488
4489 std::string currentDirectory = ROOT::FoundationUtils::GetCurrentDir();
4490
4491 std::string interpPragmaSource;
4492 std::string includeForSource;
4493 std::string interpreterDeclarations;
4494 std::string linkdef;
4495
4496 for (size_t i = 0, e = gOptDictionaryHeaderFiles.size(); i < e; ++i) {
4497 const std::string& optHeaderFileName = gOptDictionaryHeaderFiles[i];
4498 bool isSelectionFile = IsSelectionFile(optHeaderFileName.c_str());
4499
4500 if (isSelectionFile) {
4501 if (i == e - 1) {
4502 linkdef = optHeaderFileName;
4503 } else { // if the linkdef was not last, issue an error.
4504 ROOT::TMetaUtils::Error(nullptr, "%s: %s must be last file on command line\n",
4505 executableFileName, optHeaderFileName.c_str());
4506 return 1;
4507 }
4508 }
4509
4510 // coverity[tainted_data] The OS should already limit the argument size, so we are safe here
4511 std::string fullheader(optHeaderFileName);
4512 // Strip any trailing + which is only used by GeneratedLinkdef.h which currently
4513 // use directly argv.
4514 if (fullheader[fullheader.length() - 1] == '+') {
4515 fullheader.erase(fullheader.length() - 1);
4516 }
4517 std::string header(
4518 isSelectionFile ? fullheader : ROOT::FoundationUtils::MakePathRelative(fullheader, currentDirectory, gBuildingROOT));
4519
4520 interpPragmaSource += std::string("#include \"") + header + "\"\n";
4521 if (!isSelectionFile) {
4522 // In order to not have to add the equivalent to -I${PWD} to the
4523 // command line, include the complete file name, even if it is a
4524 // full pathname, when we write it down in the dictionary.
4525 // Note: have -I${PWD} means in that (at least in the case of
4526 // ACLiC) we inadvertently pick local file that have the same
4527 // name as system header (e.g. new or list) and -iquote has not
4528 // equivalent on some platforms.
4529 includeForSource += std::string("#include \"") + fullheader + "\"\n";
4530 pcmArgs.push_back(header);
4531 } else if (!IsSelectionXml(optHeaderFileName.c_str())) {
4532 interpreterDeclarations += std::string("#include \"") + header + "\"\n";
4533 }
4534 }
4535
4536 if (gOptUmbrellaInput) {
4537 bool hasSelectionFile = !linkdef.empty();
4538 unsigned expectedHeaderFilesSize = 1 + hasSelectionFile;
4539 if (gOptDictionaryHeaderFiles.size() > expectedHeaderFilesSize)
4540 ROOT::TMetaUtils::Error(nullptr, "Option %s used but more than one header file specified.\n",
4541 gOptUmbrellaInput.ArgStr.data());
4542 }
4543
4544 // We have a multiDict request. This implies generating a pcm which is of the form
4545 // dictName_libname_rdict.pcm
4546 if (gOptMultiDict) {
4547
4548 std::string newName = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4549 if (!newName.empty())
4550 newName += gPathSeparator;
4551 newName += llvm::sys::path::stem(gOptSharedLibFileName);
4552 newName += "_";
4553 newName += llvm::sys::path::stem(gOptDictionaryFileName);
4554 newName += llvm::sys::path::extension(gOptSharedLibFileName);
4555 gOptSharedLibFileName = newName;
4556 }
4557
4558 // Until the module are actually enabled in ROOT, we need to register
4559 // the 'current' directory to make it relocatable (i.e. have a way
4560 // to find the headers).
4562 string incCurDir = "-I";
4563 incCurDir += currentDirectory;
4564 pcmArgs.push_back(incCurDir);
4565 }
4566
4567 // Add the diagnostic pragmas distilled from the -Wno-xyz
4568 {
4569 std::stringstream res;
4570 const char* delim="\n";
4571 std::copy(diagnosticPragmas.begin(),
4572 diagnosticPragmas.end(),
4573 std::ostream_iterator<std::string>(res, delim));
4574 if (interp.declare(res.str()) != cling::Interpreter::kSuccess) {
4575 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -Wno-xyz flags as pragmas:\n%s", res.str().c_str());
4576 return 1;
4577 }
4578 }
4579
4580 class IgnoringPragmaHandler: public clang::PragmaNamespace {
4581 public:
4582 IgnoringPragmaHandler(const char* pragma):
4583 clang::PragmaNamespace(pragma) {}
4584 void HandlePragma(clang::Preprocessor &PP,
4585 clang::PragmaIntroducer Introducer,
4586 clang::Token &tok) override {
4587 PP.DiscardUntilEndOfDirective();
4588 }
4589 };
4590
4591 // Ignore these #pragmas to suppress "unknown pragma" warnings.
4592 // See LinkdefReader.cxx.
4593 PP.AddPragmaHandler(new IgnoringPragmaHandler("link"));
4594 PP.AddPragmaHandler(new IgnoringPragmaHandler("extra_include"));
4595 PP.AddPragmaHandler(new IgnoringPragmaHandler("read"));
4596 PP.AddPragmaHandler(new IgnoringPragmaHandler("create"));
4597
4598 if (!interpreterDeclarations.empty() &&
4599 interp.declare(interpreterDeclarations) != cling::Interpreter::kSuccess) {
4600 ROOT::TMetaUtils::Error(nullptr, "%s: Linkdef compilation failure\n", executableFileName);
4601 return 1;
4602 }
4603
4604
4605 TModuleGenerator modGen(interp.getCI(),
4609
4610 if (!gDriverConfig->fBuildingROOTStage1 && !filesIncludedByLinkdef.empty()) {
4611 pcmArgs.push_back(linkdef);
4612 }
4613
4614 modGen.ParseArgs(pcmArgs);
4615
4617 // Forward the -I, -D, -U
4618 for (const std::string & inclPath : modGen.GetIncludePaths()) {
4619 interp.AddIncludePath(inclPath);
4620 }
4621 std::stringstream definesUndefinesStr;
4622 modGen.WritePPDefines(definesUndefinesStr);
4623 modGen.WritePPUndefines(definesUndefinesStr);
4624 if (!definesUndefinesStr.str().empty()) {
4625 if (interp.declare(definesUndefinesStr.str()) != cling::Interpreter::kSuccess) {
4626 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -D, -U flags as preprocessor directives:\n%s", definesUndefinesStr.str().c_str());
4627 return 1;
4628 }
4629 }
4630 }
4631
4632 if (!InjectModuleUtilHeader(executableFileName, modGen, interp, true)
4633 || !InjectModuleUtilHeader(executableFileName, modGen, interp, false)) {
4634 return 1;
4635 }
4636
4637 if (linkdef.empty()) {
4638 // Generate autolinkdef
4639 GenerateLinkdef(gOptDictionaryHeaderFiles, interpPragmaSource);
4640 }
4641
4642 // Check if code goes to stdout or rootcling file
4643 std::ofstream fileout;
4644 string main_dictname(gOptDictionaryFileName.getValue());
4645 std::ostream *splitDictStream = nullptr;
4646 std::unique_ptr<std::ostream> splitDeleter(nullptr);
4647 // Store the temp files
4648 tempFileNamesCatalog tmpCatalog;
4650 if (!gOptDictionaryFileName.empty()) {
4651 tmpCatalog.addFileName(gOptDictionaryFileName.getValue());
4652 fileout.open(gOptDictionaryFileName.c_str());
4653 if (!fileout) {
4654 ROOT::TMetaUtils::Error(nullptr, "rootcling: failed to open %s in main\n",
4655 gOptDictionaryFileName.c_str());
4656 return 1;
4657 }
4658 }
4659 }
4660
4661 std::ostream &dictStream = (!gOptIgnoreExistingDict && !gOptDictionaryFileName.empty()) ? fileout : std::cout;
4662 bool isACLiC = gOptDictionaryFileName.getValue().find("_ACLiC_dict") != std::string::npos;
4663
4665 // Now generate a second stream for the split dictionary if it is necessary
4666 if (gOptSplit) {
4667 splitDictStream = CreateStreamPtrForSplitDict(gOptDictionaryFileName.getValue(), tmpCatalog);
4668 splitDeleter.reset(splitDictStream);
4669 } else {
4670 splitDictStream = &dictStream;
4671 }
4672
4673 size_t dh = main_dictname.rfind('.');
4674 if (dh != std::string::npos) {
4675 main_dictname.erase(dh);
4676 }
4677 // Need to replace all the characters not allowed in a symbol ...
4678 std::string main_dictname_copy(main_dictname);
4679 TMetaUtils::GetCppName(main_dictname, main_dictname_copy.c_str());
4680
4681 CreateDictHeader(dictStream, main_dictname);
4682 if (gOptSplit)
4683 CreateDictHeader(*splitDictStream, main_dictname);
4684
4685 if (!gOptNoGlobalUsingStd) {
4686 // ACLiC'ed macros might rely on `using namespace std` in front of user headers
4687 if (isACLiC) {
4688 AddNamespaceSTDdeclaration(dictStream);
4689 if (gOptSplit) {
4690 AddNamespaceSTDdeclaration(*splitDictStream);
4691 }
4692 }
4693 }
4694 }
4695
4696 //---------------------------------------------------------------------------
4697 // Parse the linkdef or selection.xml file.
4698 /////////////////////////////////////////////////////////////////////////////
4699
4700 string linkdefFilename;
4701 if (linkdef.empty()) {
4702 linkdefFilename = "in memory";
4703 } else {
4704 bool found = Which(interp, linkdef.c_str(), linkdefFilename);
4705 if (!found) {
4706 ROOT::TMetaUtils::Error(nullptr, "%s: cannot open linkdef file %s\n", executableFileName, linkdef.c_str());
4707 return 1;
4708 }
4709 }
4710
4711 // Exclude string not to re-generate the dictionary
4712 std::vector<std::pair<std::string, std::string>> namesForExclusion;
4713 if (!gBuildingROOT) {
4714 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::name, "std::string"));
4715 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::pattern, "ROOT::Meta::Selection*"));
4716 }
4717
4718 SelectionRules selectionRules(interp, normCtxt, namesForExclusion);
4719
4720 std::string extraIncludes;
4721
4722 ROOT::TMetaUtils::RConstructorTypes constructorTypes;
4723
4724 // Select using DictSelection
4725 const unsigned int selRulesInitialSize = selectionRules.Size();
4726 if (dictSelection && !gOptGeneratePCH)
4727 ROOT::Internal::DictSelectionReader dictSelReader(interp, selectionRules, CI->getASTContext(), normCtxt);
4728
4729 bool dictSelRulesPresent = selectionRules.Size() > selRulesInitialSize;
4730
4731 bool isSelXML = IsSelectionXml(linkdefFilename.c_str());
4732
4733 int rootclingRetCode(0);
4734
4735 if (linkdef.empty() || ROOT::TMetaUtils::IsLinkdefFile(linkdefFilename.c_str())) {
4736 if (ROOT::TMetaUtils::IsLinkdefFile(linkdefFilename.c_str())) {
4737 std::ifstream file(linkdefFilename.c_str());
4738 if (file.is_open()) {
4739 ROOT::TMetaUtils::Info(nullptr, "Using linkdef file: %s\n", linkdefFilename.c_str());
4740 file.close();
4741 } else {
4742 ROOT::TMetaUtils::Error(nullptr, "Linkdef file %s couldn't be opened!\n", linkdefFilename.c_str());
4743 }
4744
4746 }
4747 // If there is no linkdef file, we added the 'default' #pragma to
4748 // interpPragmaSource and we still need to process it.
4749
4750 LinkdefReader ldefr(interp, constructorTypes);
4751 clingArgs.push_back("-Ietc/cling/cint"); // For multiset and multimap
4752
4753 if (!ldefr.Parse(selectionRules, interpPragmaSource, clingArgs,
4754 llvmResourceDir.c_str())) {
4755 ROOT::TMetaUtils::Error(nullptr, "Parsing #pragma failed %s\n", linkdefFilename.c_str());
4756 rootclingRetCode += 1;
4757 } else {
4758 ROOT::TMetaUtils::Info(nullptr, "#pragma successfully parsed.\n");
4759 }
4760
4761 if (!ldefr.LoadIncludes(extraIncludes)) {
4762 ROOT::TMetaUtils::Error(nullptr, "Error loading the #pragma extra_include.\n");
4763 return 1;
4764 }
4765
4766 } else if (isSelXML) {
4767
4769
4770 std::ifstream file(linkdefFilename.c_str());
4771 if (file.is_open()) {
4772 ROOT::TMetaUtils::Info(nullptr, "Selection XML file\n");
4773
4774 XMLReader xmlr(interp);
4775 if (!xmlr.Parse(linkdefFilename.c_str(), selectionRules)) {
4776 ROOT::TMetaUtils::Error(nullptr, "Parsing XML file %s\n", linkdefFilename.c_str());
4777 return 1; // Return here to propagate the failure up to the build system
4778 } else {
4779 ROOT::TMetaUtils::Info(nullptr, "XML file successfully parsed\n");
4780 }
4781 file.close();
4782 } else {
4783 ROOT::TMetaUtils::Error(nullptr, "XML file %s couldn't be opened!\n", linkdefFilename.c_str());
4784 }
4785
4786 } else {
4787
4788 ROOT::TMetaUtils::Error(nullptr, "Unrecognized selection file: %s\n", linkdefFilename.c_str());
4789
4790 }
4791
4792 // Speed up the operations with rules
4793 selectionRules.FillCache();
4794 selectionRules.Optimize();
4795
4796 if (isGenreflex){
4797 if (0 != selectionRules.CheckDuplicates()){
4798 return 1;
4799 }
4800 }
4801
4802 // If we want to validate the selection only, we just quit.
4804 return 0;
4805
4806 //---------------------------------------------------------------------------
4807 // Write schema evolution related headers and declarations
4808 /////////////////////////////////////////////////////////////////////////////
4809
4810 if ((!ROOT::gReadRules.empty() || !ROOT::gReadRawRules.empty()) && !gOptIgnoreExistingDict) {
4811 dictStream << "#include \"TBuffer.h\"\n"
4812 << "#include \"TVirtualObject.h\"\n"
4813 << "#include <vector>\n"
4814 << "#include \"TSchemaHelper.h\"\n\n";
4815
4816 std::list<std::string> includes;
4817 GetRuleIncludes(includes);
4818 for (auto & incFile : includes) {
4819 dictStream << "#include <" << incFile << ">" << std::endl;
4820 }
4821 dictStream << std::endl;
4822 }
4823
4824 selectionRules.SearchNames(interp);
4825
4826 int scannerVerbLevel = 0;
4827 {
4828 using namespace ROOT::TMetaUtils;
4829 scannerVerbLevel = GetErrorIgnoreLevel() == kInfo; // 1 if true, 0 if false
4830 if (isGenreflex){
4831 scannerVerbLevel = GetErrorIgnoreLevel() < kWarning;
4832 }
4833 }
4834
4835 // Select the type of scan
4836 auto scanType = RScanner::EScanType::kNormal;
4837 if (gOptGeneratePCH)
4839 if (dictSelection)
4841
4842 RScanner scan(selectionRules,
4843 scanType,
4844 interp,
4845 normCtxt,
4846 scannerVerbLevel);
4847
4848 // If needed initialize the autoloading hook
4849 if (!gOptLibListPrefix.empty()) {
4852 }
4853
4854 scan.Scan(CI->getASTContext());
4855
4856 bool has_input_error = false;
4857
4859 selectionRules.PrintSelectionRules();
4860
4862 !gOptGeneratePCH &&
4863 !dictSelRulesPresent &&
4864 !selectionRules.AreAllSelectionRulesUsed()) {
4865 ROOT::TMetaUtils::Warning(nullptr, "Not all selection rules are used!\n");
4866 }
4867
4868 if (!gOptGeneratePCH){
4869 rootclingRetCode += CheckForUnsupportedClasses(scan.fSelectedClasses);
4870 if (rootclingRetCode) return rootclingRetCode;
4871 }
4872
4873 // SELECTION LOOP
4874 // Check for error in the class layout before doing anything else.
4875 for (auto const & annRcd : scan.fSelectedClasses) {
4876 if (ROOT::TMetaUtils::ClassInfo__HasMethod(annRcd, "Streamer", interp)) {
4877 if (annRcd.RequestNoInputOperator()) {
4878 int version = ROOT::TMetaUtils::GetClassVersion(annRcd, interp);
4879 if (version != 0) {
4880 // Only Check for input operator is the object is I/O has
4881 // been requested.
4882 has_input_error |= CheckInputOperator(annRcd, interp);
4883 }
4884 }
4885 }
4886 has_input_error |= !CheckClassDef(*annRcd, interp);
4887 }
4888
4889 if (has_input_error) {
4890 // Be a little bit makefile friendly and remove the dictionary in case of error.
4891 // We could add an option -k to keep the file even in case of error.
4892 exit(1);
4893 }
4894
4895 //---------------------------------------------------------------------------
4896 // Write all the necessary #include
4897 /////////////////////////////////////////////////////////////////////////////
4899 for (auto &&includedFromLinkdef : filesIncludedByLinkdef) {
4900 includeForSource += "#include \"" + includedFromLinkdef + "\"\n";
4901 }
4902 }
4903
4904 if (!gOptGeneratePCH) {
4906 GenerateNecessaryIncludes(dictStream, includeForSource, extraIncludes);
4907 if (gOptSplit) {
4908 GenerateNecessaryIncludes(*splitDictStream, includeForSource, extraIncludes);
4909 }
4910 }
4911 if (!gOptNoGlobalUsingStd) {
4912 // ACLiC'ed macros might have relied on `using namespace std` in front of user headers
4913 if (!isACLiC) {
4914 AddNamespaceSTDdeclaration(dictStream);
4915 if (gOptSplit) {
4916 AddNamespaceSTDdeclaration(*splitDictStream);
4917 }
4918 }
4919 }
4922 }
4923
4924 // The order of addition to the list of constructor type
4925 // is significant. The list is sorted by with the highest
4926 // priority first.
4927 if (!gOptInterpreterOnly) {
4928 constructorTypes.emplace_back("TRootIOCtor", interp);
4929 constructorTypes.emplace_back("__void__", interp); // ROOT-7723
4930 constructorTypes.emplace_back("", interp);
4931 }
4932 }
4934 AddNamespaceSTDdeclaration(dictStream);
4935
4936 if (gOptSplit && splitDictStream) {
4937 AddNamespaceSTDdeclaration(*splitDictStream);
4938 }
4939 }
4940
4941 if (gOptGeneratePCH) {
4942 AnnotateAllDeclsForPCH(interp, scan);
4943 } else if (gOptInterpreterOnly) {
4944 rootclingRetCode += CheckClassesForInterpreterOnlyDicts(interp, scan);
4945 // generate an empty pcm nevertheless for consistency
4946 // Negate as true is 1 and true is returned in case of success.
4948 rootclingRetCode += FinalizeStreamerInfoWriting(interp);
4949 }
4950 } else {
4951 rootclingRetCode += GenerateFullDict(*splitDictStream,
4952 interp,
4953 scan,
4954 constructorTypes,
4955 gOptSplit,
4956 isGenreflex,
4957 isSelXML,
4959 }
4960
4961 if (rootclingRetCode != 0) {
4962 return rootclingRetCode;
4963 }
4964
4965 // Now we have done all our looping and thus all the possible
4966 // annotation, let's write the pcms.
4967 HeadersDeclsMap_t headersClassesMap;
4968 HeadersDeclsMap_t headersDeclsMap;
4970 const std::string fwdDeclnArgsToKeepString(GetFwdDeclnArgsToKeepString(normCtxt, interp));
4971
4973 scan.fSelectedTypedefs,
4974 scan.fSelectedFunctions,
4975 scan.fSelectedVariables,
4976 scan.fSelectedEnums,
4977 headersClassesMap,
4978 headersDeclsMap,
4979 interp);
4980
4981 std::string detectedUmbrella;
4982 for (auto & arg : pcmArgs) {
4984 detectedUmbrella = arg;
4985 break;
4986 }
4987 }
4988
4990 headersDeclsMap.clear();
4991 }
4992
4993
4994 std::string headersClassesMapString = "\"\"";
4995 std::string fwdDeclsString = "\"\"";
4996 if (!gOptCxxModule) {
4997 headersClassesMapString = GenerateStringFromHeadersForClasses(headersDeclsMap,
4998 detectedUmbrella,
4999 true);
5002 fwdDeclsString = GenerateFwdDeclString(scan, interp);
5003 }
5004 }
5005 modGen.WriteRegistrationSource(dictStream, fwdDeclnArgsToKeepString, headersClassesMapString, fwdDeclsString,
5006 extraIncludes, gOptCxxModule);
5007 // If we just want to inline the input header, we don't need
5008 // to generate any files.
5009 if (!gOptInlineInput) {
5010 // Write the module/PCH depending on what mode we are on
5011 if (modGen.IsPCH()) {
5012 if (!GenerateAllDict(modGen, CI, currentDirectory)) return 1;
5013 } else if (gOptCxxModule) {
5014 if (!CheckModuleValid(modGen, llvmResourceDir, interp, linkdefFilename, moduleName.str()))
5015 return 1;
5016 }
5017 }
5018 }
5019
5020
5021 if (!gOptLibListPrefix.empty()) {
5022 string liblist_filename = gOptLibListPrefix + ".out";
5023
5024 ofstream outputfile(liblist_filename.c_str(), ios::out);
5025 if (!outputfile) {
5026 ROOT::TMetaUtils::Error(nullptr, "%s: Unable to open output lib file %s\n",
5027 executableFileName, liblist_filename.c_str());
5028 } else {
5029 const size_t endStr = gLibsNeeded.find_last_not_of(" \t");
5030 outputfile << gLibsNeeded.substr(0, endStr + 1) << endl;
5031 // Add explicit delimiter
5032 outputfile << "# Now the list of classes\n";
5033 // SELECTION LOOP
5034 for (auto const & annRcd : scan.fSelectedClasses) {
5035 // Shouldn't it be GetLong64_Name( cl_input.GetNormalizedName() )
5036 // or maybe we should be normalizing to turn directly all long long into Long64_t
5037 outputfile << annRcd.GetNormalizedName() << endl;
5038 }
5039 }
5040 }
5041
5042 // Check for errors in module generation
5043 rootclingRetCode += modGen.GetErrorCount();
5044 if (0 != rootclingRetCode) return rootclingRetCode;
5045
5046 // Create the rootmap file
5047 std::string rootmapLibName = std::accumulate(gOptRootmapLibNames.begin(),
5048 gOptRootmapLibNames.end(),
5049 std::string(),
5050 [](const std::string & a, const std::string & b) -> std::string {
5051 if (a.empty()) return b;
5052 else return a + " " + b;
5053 });
5054
5055 bool rootMapNeeded = !gOptRootMapFileName.empty() || !rootmapLibName.empty();
5056
5057 std::list<std::string> classesNames;
5058 std::list<std::string> classesNamesForRootmap;
5059 std::list<std::string> classesDefsList;
5060
5061 rootclingRetCode = ExtractClassesListAndDeclLines(scan,
5062 classesNames,
5063 classesNamesForRootmap,
5064 classesDefsList,
5065 interp);
5066
5067 std::list<std::string> enumNames;
5068 rootclingRetCode += ExtractAutoloadKeys(enumNames,
5069 scan.fSelectedEnums,
5070 interp);
5071
5072 std::list<std::string> varNames;
5073 rootclingRetCode += ExtractAutoloadKeys(varNames,
5074 scan.fSelectedVariables,
5075 interp);
5076
5077 if (0 != rootclingRetCode) return rootclingRetCode;
5078
5079 // Create the rootmapfile if needed
5080 if (rootMapNeeded) {
5081
5082 std::list<std::string> nsNames;
5083
5084 ExtractSelectedNamespaces(scan, nsNames);
5085
5087 rootmapLibName);
5088
5089 ROOT::TMetaUtils::Info(nullptr, "Rootmap file name %s and lib name(s) \"%s\"\n",
5090 gOptRootMapFileName.c_str(),
5091 rootmapLibName.c_str());
5092
5093 tmpCatalog.addFileName(gOptRootMapFileName);
5094 std::unordered_set<std::string> headersToIgnore;
5095 if (gOptInlineInput)
5096 for (const std::string& optHeaderFileName : gOptDictionaryHeaderFiles)
5097 headersToIgnore.insert(optHeaderFileName.c_str());
5098
5099 std::list<std::string> typedefsRootmapLines;
5100 rootclingRetCode += ExtractAutoloadKeys(typedefsRootmapLines,
5101 scan.fSelectedTypedefs,
5102 interp);
5103
5104 rootclingRetCode += CreateNewRootMapFile(gOptRootMapFileName,
5105 rootmapLibName,
5106 classesDefsList,
5107 classesNamesForRootmap,
5108 nsNames,
5109 typedefsRootmapLines,
5110 enumNames,
5111 varNames,
5112 headersClassesMap,
5113 headersToIgnore);
5114
5115 if (0 != rootclingRetCode) return 1;
5116 }
5117
5119 tmpCatalog.dump();
5120
5121 // Manually call end of translation unit because we never call the
5122 // appropriate deconstructors in the interpreter. This writes out the C++
5123 // module file that we currently generate.
5124 {
5125 cling::Interpreter::PushTransactionRAII RAII(&interp);
5126 CI->getSema().getASTConsumer().HandleTranslationUnit(CI->getSema().getASTContext());
5127 }
5128
5129 // Add the warnings
5130 rootclingRetCode += ROOT::TMetaUtils::GetNumberOfErrors();
5131
5132 // make sure the file is closed before committing
5133 fileout.close();
5134
5135 // Before returning, rename the files if no errors occurred
5136 // otherwise clean them to avoid remnants (see ROOT-10015)
5137 if(rootclingRetCode == 0) {
5138 rootclingRetCode += tmpCatalog.commit();
5139 } else {
5140 tmpCatalog.clean();
5141 }
5142
5143 return rootclingRetCode;
5144
5145}
5146
5147namespace genreflex {
5148
5149////////////////////////////////////////////////////////////////////////////////
5150/// Loop on arguments: stop at the first which starts with -
5151
5152 unsigned int checkHeadersNames(std::vector<std::string> &headersNames)
5153 {
5154 unsigned int numberOfHeaders = 0;
5155 for (std::vector<std::string>::iterator it = headersNames.begin();
5156 it != headersNames.end(); ++it) {
5157 const std::string headername(*it);
5158 if (ROOT::TMetaUtils::IsHeaderName(headername)) {
5159 numberOfHeaders++;
5160 } else {
5162 "*** genreflex: %s is not a valid header name (.h and .hpp extensions expected)!\n",
5163 headername.c_str());
5164 }
5165 }
5166 return numberOfHeaders;
5167 }
5168
5169////////////////////////////////////////////////////////////////////////////////
5170/// Extract the arguments from the command line
5171
5172 unsigned int extractArgs(int argc, char **argv, std::vector<std::string> &args)
5173 {
5174 // loop on argv, spot strings which are not preceded by something
5175 unsigned int argvCounter = 0;
5176 for (int i = 1; i < argc; ++i) {
5177 if (!ROOT::TMetaUtils::BeginsWith(argv[i - 1], "-") && // so, if preceding element starts with -, this is a value for an option
5178 !ROOT::TMetaUtils::BeginsWith(argv[i], "-")) { // and the element itself is not an option
5179 args.push_back(argv[i]);
5180 argvCounter++;
5181 } else if (argvCounter) {
5182 argv[i - argvCounter] = argv[i];
5183 }
5184 }
5185
5186 // Some debug
5187 if (genreflex::verbose) {
5188 int i = 0;
5189 std::cout << "Args: \n";
5190 for (std::vector<std::string>::iterator it = args.begin();
5191 it < args.end(); ++it) {
5192 std::cout << i << ") " << *it << std::endl;
5193 ++i;
5194 }
5195
5196 }
5197
5198 return argvCounter;
5199 }
5200
5201////////////////////////////////////////////////////////////////////////////////
5202
5203 void changeExtension(std::string &filename, const std::string &newExtension)
5204 {
5205 size_t result = filename.find_last_of('.');
5206 if (std::string::npos != result) {
5207 filename.erase(result);
5208 filename.append(newExtension);
5209 }
5210
5211 }
5212
5213////////////////////////////////////////////////////////////////////////////////
5214/// The caller is responsible for deleting the string!
5215
5216 char *string2charptr(const std::string &str)
5217 {
5218 const unsigned int size(str.size());
5219 char *a = new char[size + 1];
5220 a[size] = 0;
5221 memcpy(a, str.c_str(), size);
5222 return a;
5223 }
5224
5225////////////////////////////////////////////////////////////////////////////////
5226/// Replace the extension with "_rflx.cpp"
5227
5228 void header2outputName(std::string &fileName)
5229 {
5230 changeExtension(fileName, "_rflx.cpp");
5231 }
5232
5233////////////////////////////////////////////////////////////////////////////////
5234/// Get a proper name for the output file
5235
5236 void headers2outputsNames(const std::vector<std::string> &headersNames,
5237 std::vector<std::string> &ofilesnames)
5238 {
5239 ofilesnames.reserve(headersNames.size());
5240
5241 for (std::vector<std::string>::const_iterator it = headersNames.begin();
5242 it != headersNames.end(); ++it) {
5243 std::string ofilename(*it);
5244 header2outputName(ofilename);
5245 ofilesnames.push_back(ofilename);
5246 }
5247 }
5248
5249////////////////////////////////////////////////////////////////////////////////
5250
5251 void AddToArgVector(std::vector<char *> &argvVector,
5252 const std::vector<std::string> &argsToBeAdded,
5253 const std::string &optName = "")
5254 {
5255 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5256 it != argsToBeAdded.end(); ++it) {
5257 argvVector.push_back(string2charptr(optName + *it));
5258 }
5259 }
5260
5261////////////////////////////////////////////////////////////////////////////////
5262
5263 void AddToArgVectorSplit(std::vector<char *> &argvVector,
5264 const std::vector<std::string> &argsToBeAdded,
5265 const std::string &optName = "")
5266 {
5267 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5268 it != argsToBeAdded.end(); ++it) {
5269 if (optName.length()) {
5270 argvVector.push_back(string2charptr(optName));
5271 }
5272 argvVector.push_back(string2charptr(*it));
5273 }
5274 }
5275
5276////////////////////////////////////////////////////////////////////////////////
5277
5278 int invokeRootCling(const std::string &verbosity,
5279 const std::string &selectionFileName,
5280 const std::string &targetLibName,
5281 bool multiDict,
5282 const std::vector<std::string> &pcmsNames,
5283 const std::vector<std::string> &includes,
5284 const std::vector<std::string> &preprocDefines,
5285 const std::vector<std::string> &preprocUndefines,
5286 const std::vector<std::string> &warnings,
5287 const std::string &rootmapFileName,
5288 const std::string &rootmapLibName,
5289 bool interpreteronly,
5290 bool doSplit,
5291 bool isCxxmodule,
5292 bool writeEmptyRootPCM,
5293 bool selSyntaxOnly,
5294 bool noIncludePaths,
5295 bool noGlobalUsingStd,
5296 const std::vector<std::string> &headersNames,
5297 bool failOnWarnings,
5298 const std::string &ofilename)
5299 {
5300 // Prepare and invoke the commandline to invoke rootcling
5301
5302 std::vector<char *> argvVector;
5303
5304 argvVector.push_back(string2charptr("rootcling"));
5305 argvVector.push_back(string2charptr(verbosity));
5306 argvVector.push_back(string2charptr("-f"));
5307 argvVector.push_back(string2charptr(ofilename));
5308
5309 if (isCxxmodule)
5310 argvVector.push_back(string2charptr("-cxxmodule"));
5311
5312 // Extract the path to the dictionary
5313 std::string dictLocation;
5314 ExtractFilePath(ofilename, dictLocation);
5315
5316 // Rootmaps
5317
5318 // Prepare the correct rootmap libname if not already set.
5319 std::string newRootmapLibName(rootmapLibName);
5320 if (!rootmapFileName.empty() && newRootmapLibName.empty()) {
5321 if (headersNames.size() != 1) {
5323 "*** genreflex: No rootmap lib and several header specified!\n");
5324 }
5325 std::string cleanHeaderName = ExtractFileName(headersNames[0]);
5326 newRootmapLibName = "lib";
5327 newRootmapLibName += cleanHeaderName;
5328 changeExtension(newRootmapLibName, gLibraryExtension);
5329 }
5330
5331 // Prepend to the rootmap the designed directory of the dictionary
5332 // if no path is specified for the rootmap itself
5333 std::string newRootmapFileName(rootmapFileName);
5334 if (!newRootmapFileName.empty() && !HasPath(newRootmapFileName)) {
5335 newRootmapFileName = dictLocation + newRootmapFileName;
5336 }
5337
5338
5339 // RootMap filename
5340 if (!newRootmapFileName.empty()) {
5341 argvVector.push_back(string2charptr("-rmf"));
5342 argvVector.push_back(string2charptr(newRootmapFileName));
5343 }
5344
5345 // RootMap Lib filename
5346 if (!newRootmapLibName.empty()) {
5347 argvVector.push_back(string2charptr("-rml"));
5348 argvVector.push_back(string2charptr(newRootmapLibName));
5349 }
5350
5351 // Interpreter only dictionaries
5352 if (interpreteronly)
5353 argvVector.push_back(string2charptr("-interpreteronly"));
5354
5355 // Split dictionaries
5356 if (doSplit)
5357 argvVector.push_back(string2charptr("-split"));
5358
5359 // Targetlib
5360 if (!targetLibName.empty()) {
5361 argvVector.push_back(string2charptr("-s"));
5362 argvVector.push_back(string2charptr(targetLibName));
5363 }
5364
5365 // Multidict support
5366 if (multiDict)
5367 argvVector.push_back(string2charptr("-multiDict"));
5368
5369 // Don't declare "using namespace std"
5370 if (noGlobalUsingStd)
5371 argvVector.push_back(string2charptr("-noGlobalUsingStd"));
5372
5373
5374 AddToArgVectorSplit(argvVector, pcmsNames, "-m");
5375
5376 // Inline the input header
5377 argvVector.push_back(string2charptr("-inlineInputHeader"));
5378
5379 // Write empty root pcms
5380 if (writeEmptyRootPCM)
5381 argvVector.push_back(string2charptr("-writeEmptyRootPCM"));
5382
5383 // Just test the syntax of the selection file
5384 if (selSyntaxOnly)
5385 argvVector.push_back(string2charptr("-selSyntaxOnly"));
5386
5387 // No include paths
5388 if (noIncludePaths)
5389 argvVector.push_back(string2charptr("-noIncludePaths"));
5390
5391 // Fail on warnings
5392 if (failOnWarnings)
5393 argvVector.push_back(string2charptr("-failOnWarnings"));
5394
5395 // Clingargs
5396 AddToArgVector(argvVector, includes, "-I");
5397 AddToArgVector(argvVector, preprocDefines, "-D");
5398 AddToArgVector(argvVector, preprocUndefines, "-U");
5399 AddToArgVector(argvVector, warnings, "-W");
5400
5401 AddToArgVector(argvVector, headersNames);
5402
5403 if (!selectionFileName.empty()) {
5404 argvVector.push_back(string2charptr(selectionFileName));
5405 }
5406
5407 const int argc = argvVector.size();
5408
5409 // Output commandline for rootcling
5410 if (genreflex::verbose) {
5411 std::cout << "Rootcling commandline:\n";
5412 for (int i = 0; i < argc; i++)
5413 std::cout << i << ") " << argvVector[i] << std::endl;
5414 }
5415
5416 char **argv = & (argvVector[0]);
5417 int rootclingReturnCode = RootClingMain(argc,
5418 argv,
5419 /*isGenReflex=*/true);
5420
5421 for (int i = 0; i < argc; i++)
5422 delete [] argvVector[i];
5423
5424 return rootclingReturnCode;
5425
5426 }
5427
5428////////////////////////////////////////////////////////////////////////////////
5429/// Get the right ofilenames and invoke several times rootcling
5430/// One invokation per header
5431
5432 int invokeManyRootCling(const std::string &verbosity,
5433 const std::string &selectionFileName,
5434 const std::string &targetLibName,
5435 bool multiDict,
5436 const std::vector<std::string> &pcmsNames,
5437 const std::vector<std::string> &includes,
5438 const std::vector<std::string> &preprocDefines,
5439 const std::vector<std::string> &preprocUndefines,
5440 const std::vector<std::string> &warnings,
5441 const std::string &rootmapFileName,
5442 const std::string &rootmapLibName,
5443 bool interpreteronly,
5444 bool doSplit,
5445 bool isCxxmodule,
5446 bool writeEmptyRootPCM,
5447 bool selSyntaxOnly,
5448 bool noIncludePaths,
5449 bool noGlobalUsingStd,
5450 const std::vector<std::string> &headersNames,
5451 bool failOnWarnings,
5452 const std::string &outputDirName_const = "")
5453 {
5454 std::string outputDirName(outputDirName_const);
5455
5456 std::vector<std::string> ofilesNames;
5457 headers2outputsNames(headersNames, ofilesNames);
5458
5459 if (!outputDirName.empty() && !ROOT::TMetaUtils::EndsWith(outputDirName, gPathSeparator)) {
5460 outputDirName += gPathSeparator;
5461 }
5462
5463 std::vector<std::string> namesSingleton(1);
5464 for (unsigned int i = 0; i < headersNames.size(); ++i) {
5465 namesSingleton[0] = headersNames[i];
5466 std::string ofilenameFullPath(ofilesNames[i]);
5467 if (llvm::sys::path::parent_path(ofilenameFullPath) == "")
5468 ofilenameFullPath = outputDirName + ofilenameFullPath;
5469 int returnCode = invokeRootCling(verbosity,
5470 selectionFileName,
5471 targetLibName,
5472 multiDict,
5473 pcmsNames,
5474 includes,
5475 preprocDefines,
5476 preprocUndefines,
5477 warnings,
5478 rootmapFileName,
5479 rootmapLibName,
5480 interpreteronly,
5481 doSplit,
5482 isCxxmodule,
5483 writeEmptyRootPCM,
5484 selSyntaxOnly,
5485 noIncludePaths,
5486 noGlobalUsingStd,
5487 namesSingleton,
5488 failOnWarnings,
5489 ofilenameFullPath);
5490 if (returnCode != 0)
5491 return returnCode;
5492 }
5493
5494 return 0;
5495 }
5496
5497
5498} // end genreflex namespace
5499
5500////////////////////////////////////////////////////////////////////////////////
5501/// Extract from options multiple values with the same option
5502
5503int extractMultipleOptions(std::vector<ROOT::option::Option> &options,
5504 int oIndex,
5505 std::vector<std::string> &values)
5506{
5507 int nValues = 0;
5508 if (options[oIndex]) {
5509 const int nVals = options[oIndex].count();
5510 values.reserve(nVals);
5511 int optionIndex = 0;
5512 for (ROOT::option::Option *opt = options[oIndex]; opt; opt = opt->next()) {
5513 if (genreflex::verbose) std::cout << "Extracting multiple args: "
5514 << optionIndex << "/" << nVals << " "
5515 << opt->arg << std::endl;
5516 optionIndex++;
5517 values.push_back(opt->arg);
5518 nValues++;
5519 }
5520 }
5521 return nValues;
5522}
5523
5524////////////////////////////////////////////////////////////////////////////////
5525
5526void RiseWarningIfPresent(std::vector<ROOT::option::Option> &options,
5527 int optionIndex,
5528 const char *descriptor)
5529{