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