Logo ROOT  
Reference Guide
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
2924 if (0 != std::remove(tmpName)) {
2925 ROOT::TMetaUtils::Error(nullptr, "Removing %s!\n", tmpName);
2926 retval++;
2927 }
2928 }
2929 return retval;
2930 }
2931
2932 /////////////////////////////////////////////////////////////////////////////
2933
2934 int commit() {
2935 int retval = 0;
2936 // rename the temp files into the normal ones
2937 for (unsigned int i = 0; i < m_size; ++i) {
2938 const char *tmpName = m_tempNames[i].c_str();
2939 const char *name = m_names[i].c_str();
2940 // Check if the file exists
2941 std::ifstream ifile(tmpName);
2942 if (!ifile)
2943 ROOT::TMetaUtils::Error(nullptr, "Cannot find %s!\n", tmpName);
2944#ifdef WIN32
2945 // Sometimes files cannot be renamed on Windows if they don't have
2946 // been released by the system. So just copy them and try to delete
2947 // the old one afterwards.
2948 if (ifile.is_open())
2949 ifile.close();
2950 if (0 != std::rename(tmpName , name)) {
2951 if (llvm::sys::fs::copy_file(tmpName , name)) {
2952 llvm::sys::fs::remove(tmpName);
2953 }
2954 }
2955#else
2956 if (0 != std::rename(tmpName , name)) {
2957 ROOT::TMetaUtils::Error(nullptr, "Renaming %s into %s!\n", tmpName, name);
2958 retval++;
2959 }
2960#endif
2961 }
2962 return retval;
2963 }
2964
2965 /////////////////////////////////////////////////////////////////////////////
2966
2967 const std::string &getFileName(const std::string &tmpFileName) {
2968 size_t i = std::distance(m_tempNames.begin(),
2969 find(m_tempNames.begin(), m_tempNames.end(), tmpFileName));
2970 if (i == m_tempNames.size()) return m_emptyString;
2971 return m_names[i];
2972 }
2973
2974 /////////////////////////////////////////////////////////////////////////////
2975
2976 void dump() {
2977 std::cout << "Restoring files in temporary file catalog:\n";
2978 for (unsigned int i = 0; i < m_size; ++i) {
2979 std::cout << m_tempNames[i] << " --> " << m_names[i] << std::endl;
2980 }
2981 }
2982
2983private:
2984 unsigned int m_size;
2985 const std::string m_emptyString;
2986 std::vector<std::string> m_names;
2987 std::vector<std::string> m_tempNames;
2988};
2989
2990////////////////////////////////////////////////////////////////////////////////
2991/// Transform name of dictionary
2992
2993std::ostream *CreateStreamPtrForSplitDict(const std::string &dictpathname,
2994 tempFileNamesCatalog &tmpCatalog)
2995{
2996 std::string splitDictName(tmpCatalog.getFileName(dictpathname));
2997 const size_t dotPos = splitDictName.find_last_of(".");
2998 splitDictName.insert(dotPos, "_classdef");
2999 tmpCatalog.addFileName(splitDictName);
3000 return new std::ofstream(splitDictName.c_str());
3001}
3002
3003////////////////////////////////////////////////////////////////////////////////
3004/// Transform -W statements in diagnostic pragmas for cling reacting on "-Wno-"
3005/// For example
3006/// -Wno-deprecated-declarations --> `#pragma clang diagnostic ignored "-Wdeprecated-declarations"`
3007
3008static void CheckForMinusW(std::string arg,
3009 std::list<std::string> &diagnosticPragmas)
3010{
3011 static const std::string pattern("-Wno-");
3012
3013 if (arg.find(pattern) != 0)
3014 return;
3015 if (arg == "-Wno-noexcept-type") {
3016 // GCC7 warning not supported by clang 3.9
3017 return;
3018 }
3019
3020 ROOT::TMetaUtils::ReplaceAll(arg, pattern, "#pragma clang diagnostic ignored \"-W");
3021 arg += "\"";
3022 diagnosticPragmas.push_back(arg);
3023}
3024
3025////////////////////////////////////////////////////////////////////////////////
3026
3028 cling::Interpreter &interp)
3029{
3030 using namespace ROOT::TMetaUtils::AST2SourceTools;
3031 std::string fwdDecl;
3032 std::string initStr("{");
3033 auto &fwdDeclnArgsToSkipColl = normCtxt.GetTemplNargsToKeepMap();
3034 for (auto & strigNargsToKeepPair : fwdDeclnArgsToSkipColl) {
3035 auto &clTemplDecl = *strigNargsToKeepPair.first;
3036 FwdDeclFromTmplDecl(clTemplDecl , interp, fwdDecl);
3037 initStr += "{\"" +
3038 fwdDecl + "\", "
3039 + std::to_string(strigNargsToKeepPair.second)
3040 + "},";
3041 }
3042 if (!fwdDeclnArgsToSkipColl.empty())
3043 initStr.pop_back();
3044 initStr += "}";
3045 return initStr;
3046}
3047
3048////////////////////////////////////////////////////////////////////////////////
3049/// Get the pointee type if possible
3050
3051clang::QualType GetPointeeTypeIfPossible(const clang::QualType &qt)
3052{
3053 if (qt.isNull()) return qt;
3054 clang::QualType thisQt(qt);
3055 while (thisQt->isPointerType() ||
3056 thisQt->isReferenceType()) {
3057 thisQt = thisQt->getPointeeType();
3058 }
3059 return thisQt;
3060
3061}
3062
3063////////////////////////////////////////////////////////////////////////////////
3064/// Extract the list of headers necessary for the Decl
3065
3066std::list<std::string> RecordDecl2Headers(const clang::CXXRecordDecl &rcd,
3067 const cling::Interpreter &interp,
3068 std::set<const clang::CXXRecordDecl *> &visitedDecls)
3069{
3070 std::list<std::string> headers;
3071
3072 // We push a new transaction because we could deserialize decls here
3073 cling::Interpreter::PushTransactionRAII RAII(&interp);
3074
3075 // Avoid infinite recursion
3076 if (!visitedDecls.insert(rcd.getCanonicalDecl()).second)
3077 return headers;
3078
3079 // If this is a template
3080 if (const clang::ClassTemplateSpecializationDecl *tsd = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd)) {
3081
3082 // Loop on the template args
3083 for (auto & tArg : tsd->getTemplateArgs().asArray()) {
3084 if (clang::TemplateArgument::ArgKind::Type != tArg.getKind()) continue;
3085 auto tArgQualType = GetPointeeTypeIfPossible(tArg.getAsType());
3086 if (tArgQualType.isNull()) continue;
3087 if (const clang::CXXRecordDecl *tArgCxxRcd = tArgQualType->getAsCXXRecordDecl()) {
3088 headers.splice(headers.end(), RecordDecl2Headers(*tArgCxxRcd, interp, visitedDecls));
3089 }
3090 }
3091
3092 if (!ROOT::TMetaUtils::IsStdClass(rcd) && rcd.hasDefinition()) {
3093
3094 // Loop on base classes - with a newer llvm, range based possible
3095 for (auto baseIt = tsd->bases_begin(); baseIt != tsd->bases_end(); baseIt++) {
3096 auto baseQualType = GetPointeeTypeIfPossible(baseIt->getType());
3097 if (baseQualType.isNull()) continue;
3098 if (const clang::CXXRecordDecl *baseRcdPtr = baseQualType->getAsCXXRecordDecl()) {
3099 headers.splice(headers.end(), RecordDecl2Headers(*baseRcdPtr, interp, visitedDecls));
3100 }
3101 }
3102
3103 // Loop on the data members - with a newer llvm, range based possible
3104 for (auto declIt = tsd->decls_begin(); declIt != tsd->decls_end(); ++declIt) {
3105 if (const clang::FieldDecl *fieldDecl = llvm::dyn_cast<clang::FieldDecl>(*declIt)) {
3106 auto fieldQualType = GetPointeeTypeIfPossible(fieldDecl->getType());
3107 if (fieldQualType.isNull()) continue ;
3108 if (const clang::CXXRecordDecl *fieldCxxRcd = fieldQualType->getAsCXXRecordDecl()) {
3109 if (fieldCxxRcd->hasDefinition())
3110 headers.splice(headers.end(), RecordDecl2Headers(*fieldCxxRcd, interp, visitedDecls));
3111 }
3112 }
3113 }
3114
3115 // Loop on methods
3116 for (auto methodIt = tsd->method_begin(); methodIt != tsd->method_end(); ++methodIt) {
3117 // Check arguments
3118 for (auto & fPar : methodIt->parameters()) {
3119 auto fParQualType = GetPointeeTypeIfPossible(fPar->getOriginalType());
3120 if (fParQualType.isNull()) continue;
3121 if (const clang::CXXRecordDecl *fParCxxRcd = fParQualType->getAsCXXRecordDecl()) {
3122 if (fParCxxRcd->hasDefinition())
3123 headers.splice(headers.end(), RecordDecl2Headers(*fParCxxRcd, interp, visitedDecls));
3124 }
3125 }
3126 // Check return value
3127 auto retQualType = GetPointeeTypeIfPossible(methodIt->getReturnType());
3128 if (retQualType.isNull()) continue;
3129 if (const clang::CXXRecordDecl *retCxxRcd = retQualType->getAsCXXRecordDecl()) {
3130 if (retCxxRcd->hasDefinition())
3131 headers.splice(headers.end(), RecordDecl2Headers(*retCxxRcd, interp, visitedDecls));
3132 }
3133 }
3134 }
3135
3136 } // End template instance
3137
3138 std::string header = ROOT::TMetaUtils::GetFileName(rcd, interp);
3139 headers.emplace_back(header);
3140 headers.reverse();
3141 return headers;
3142
3143}
3144
3145////////////////////////////////////////////////////////////////////////////////
3146/// Check if the class good for being an autoparse key.
3147/// We exclude from this set stl containers of pods/strings
3148/// TODO: we may use also __gnu_cxx::
3149bool IsGoodForAutoParseMap(const clang::RecordDecl& rcd){
3150
3151 // If it's not an std class, we just pick it up.
3152 if (auto dclCtxt= rcd.getDeclContext()){
3153 if (! dclCtxt->isStdNamespace()){
3154 return true;
3155 }
3156 } else {
3157 return true;
3158 }
3159
3160 // Now, we have a stl class. We now check if it's a template. If not, we
3161 // do not take it: bitset, string and so on.
3162 auto clAsTmplSpecDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd);
3163 if (!clAsTmplSpecDecl) return false;
3164
3165 // Now we have a template in the stl. Let's see what the arguments are.
3166 // If they are not a POD or something which is good for autoparsing, we keep
3167 // them.
3168 auto& astCtxt = rcd.getASTContext();
3169 auto& templInstArgs = clAsTmplSpecDecl->getTemplateInstantiationArgs();
3170 for (auto&& arg : templInstArgs.asArray()){
3171
3172 auto argKind = arg.getKind();
3173 if (argKind != clang::TemplateArgument::Type){
3174 if (argKind == clang::TemplateArgument::Integral) continue;
3175 else return true;
3176 }
3177
3178 auto argQualType = arg.getAsType();
3179 auto isPOD = argQualType.isPODType(astCtxt);
3180 // This is a POD, we can inspect the next arg
3181 if (isPOD) continue;
3182
3183 auto argType = argQualType.getTypePtr();
3184 if (auto recType = llvm::dyn_cast<clang::RecordType>(argType)){
3185 auto isArgGoodForAutoParseMap = IsGoodForAutoParseMap(*recType->getDecl());
3186 // The arg is a class but good for the map
3187 if (isArgGoodForAutoParseMap) continue;
3188 } else {
3189 // The class is not a POD nor a class we can skip
3190 return true;
3191 }
3192 }
3193
3194 return false;
3195}
3196
3197////////////////////////////////////////////////////////////////////////////////
3198
3200 const RScanner::TypedefColl_t tDefDecls,
3201 const RScanner::FunctionColl_t funcDecls,
3202 const RScanner::VariableColl_t varDecls,
3203 const RScanner::EnumColl_t enumDecls,
3204 HeadersDeclsMap_t &headersClassesMap,
3205 HeadersDeclsMap_t &headersDeclsMap,
3206 const cling::Interpreter &interp)
3207{
3208 std::set<const clang::CXXRecordDecl *> visitedDecls;
3209 std::unordered_set<std::string> buffer;
3210 std::string autoParseKey;
3211
3212 // Add some manip of headers
3213 for (auto & annotatedRcd : annotatedRcds) {
3214 if (const clang::CXXRecordDecl *cxxRcd =
3215 llvm::dyn_cast_or_null<clang::CXXRecordDecl>(annotatedRcd.GetRecordDecl())) {
3216 autoParseKey = "";
3217 visitedDecls.clear();
3218 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3219 // remove duplicates, also if not subsequent
3220 buffer.clear();
3221 headers.remove_if([&buffer](const std::string & s) {
3222 return !buffer.insert(s).second;
3223 });
3224 GetMostExternalEnclosingClassName(*cxxRcd, autoParseKey, interp);
3225 if (autoParseKey.empty()) autoParseKey = annotatedRcd.GetNormalizedName();
3226 if (IsGoodForAutoParseMap(*cxxRcd)){
3227 headersDeclsMap[autoParseKey] = headers;
3228 headersDeclsMap[annotatedRcd.GetRequestedName()] = headers;
3229 } else {
3230 ROOT::TMetaUtils::Info(nullptr, "Class %s is not included in the set of autoparse keys.\n", autoParseKey.c_str());
3231 }
3232
3233 // Propagate to the classes map only if this is not a template.
3234 // The header is then used as autoload key and we want to avoid duplicates.
3235 if (!llvm::isa<clang::ClassTemplateSpecializationDecl>(cxxRcd)){
3236 headersClassesMap[autoParseKey] = headersDeclsMap[autoParseKey];
3237 headersClassesMap[annotatedRcd.GetRequestedName()] = headersDeclsMap[annotatedRcd.GetRequestedName()];
3238 }
3239 }
3240 }
3241
3242 // The same for the typedefs:
3243 for (auto & tDef : tDefDecls) {
3244 if (clang::CXXRecordDecl *cxxRcd = tDef->getUnderlyingType()->getAsCXXRecordDecl()) {
3245 autoParseKey = "";
3246 visitedDecls.clear();
3247 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3248 headers.push_back(ROOT::TMetaUtils::GetFileName(*tDef, interp));
3249 // remove duplicates, also if not subsequent
3250 buffer.clear();
3251 headers.remove_if([&buffer](const std::string & s) {
3252 return !buffer.insert(s).second;
3253 });
3254 GetMostExternalEnclosingClassNameFromDecl(*tDef, autoParseKey, interp);
3255 if (autoParseKey.empty()) autoParseKey = tDef->getQualifiedNameAsString();
3256 headersDeclsMap[autoParseKey] = headers;
3257 }
3258 }
3259
3260 // The same for the functions:
3261 for (auto & func : funcDecls) {
3262 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*func, interp)};
3263 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*func)] = headers;
3264 }
3265
3266 // The same for the variables:
3267 for (auto & var : varDecls) {
3268 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*var, interp)};
3269 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*var)] = headers;
3270 }
3271
3272 // The same for the enums:
3273 for (auto & en : enumDecls) {
3274 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*en, interp)};
3275 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*en)] = headers;
3276 }
3277}
3278
3279////////////////////////////////////////////////////////////////////////////////
3280/// Generate the fwd declarations of the selected entities
3281
3282static std::string GenerateFwdDeclString(const RScanner &scan,
3283 const cling::Interpreter &interp)
3284{
3285 std::string newFwdDeclString;
3286
3287 using namespace ROOT::TMetaUtils::AST2SourceTools;
3288
3289 std::string fwdDeclString;
3290 std::string buffer;
3291 std::unordered_set<std::string> fwdDecls;
3292
3293 // Classes
3294/*
3295 for (auto const & annRcd : scan.fSelectedClasses) {
3296 const auto rcdDeclPtr = annRcd.GetRecordDecl();
3297
3298 int retCode = FwdDeclFromRcdDecl(*rcdDeclPtr, interp, buffer);
3299 if (-1 == retCode) {
3300 ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3301 "Error generating fwd decl for class %s\n",
3302 annRcd.GetNormalizedName());
3303 return emptyString;
3304 }
3305 if (retCode == 0 && fwdDecls.insert(buffer).second)
3306 fwdDeclString += "\"" + buffer + "\"\n";
3307 }
3308*/
3309 // Build the input for a transaction containing all of the selected declarations
3310 // Cling will produce the fwd declaration payload.
3311
3312 std::vector<const clang::Decl *> selectedDecls(scan.fSelectedClasses.size());
3313
3314 // Pick only RecordDecls
3315 std::transform (scan.fSelectedClasses.begin(),
3316 scan.fSelectedClasses.end(),
3317 selectedDecls.begin(),
3318 [](const ROOT::TMetaUtils::AnnotatedRecordDecl& rcd){return rcd.GetRecordDecl();});
3319
3320 for (auto* TD: scan.fSelectedTypedefs)
3321 selectedDecls.push_back(TD);
3322
3323// for (auto* VAR: scan.fSelectedVariables)
3324// selectedDecls.push_back(VAR);
3325
3326 std::string fwdDeclLogs;
3327
3328 // The "R\"DICTFWDDCLS(\n" ")DICTFWDDCLS\"" pieces have been moved to
3329 // TModuleGenerator to be able to make the diagnostics more telling in presence
3330 // of an issue ROOT-6752.
3331 fwdDeclString += Decls2FwdDecls(selectedDecls,IsLinkdefFile,interp, genreflex::verbose ? &fwdDeclLogs : nullptr);
3332
3333 if (genreflex::verbose && !fwdDeclLogs.empty())
3334 std::cout << "Logs from forward decl printer: \n"
3335 << fwdDeclLogs;
3336
3337 // Functions
3338// for (auto const& fcnDeclPtr : scan.fSelectedFunctions){
3339// int retCode = FwdDeclFromFcnDecl(*fcnDeclPtr, interp, buffer);
3340// newFwdDeclString += Decl2FwdDecl(*fcnDeclPtr,interp);
3341// if (-1 == retCode){
3342// ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3343// "Error generating fwd decl for function %s\n",
3344// fcnDeclPtr->getNameAsString().c_str());
3345// return emptyString;
3346// }
3347// if (retCode == 0 && fwdDecls.insert(buffer).second)
3348// fwdDeclString+="\""+buffer+"\"\n";
3349// }
3350
3351 if (fwdDeclString.empty()) fwdDeclString = "";
3352 return fwdDeclString;
3353}
3354
3355////////////////////////////////////////////////////////////////////////////////
3356/// Generate a string for the dictionary from the headers-classes map.
3357
3358const std::string GenerateStringFromHeadersForClasses(const HeadersDeclsMap_t &headersClassesMap,
3359 const std::string &detectedUmbrella,
3360 bool payLoadOnly = false)
3361{
3362 std::string headerName;
3363
3365 std::cout << "Class-headers Mapping:\n";
3366 std::string headersClassesMapString = "";
3367 for (auto const & classHeaders : headersClassesMap) {
3369 std::cout << " o " << classHeaders.first << " --> ";
3370 headersClassesMapString += "\"";
3371 headersClassesMapString += classHeaders.first + "\"";
3372 for (auto const & header : classHeaders.second) {
3373 headerName = (detectedUmbrella == header || payLoadOnly) ? "payloadCode" : "\"" + header + "\"";
3374 headersClassesMapString += ", " + headerName;
3376 std::cout << ", " << headerName;
3377 if (payLoadOnly)
3378 break;
3379 }
3381 std::cout << std::endl;
3382 headersClassesMapString += ", \"@\",\n";
3383 }
3384 headersClassesMapString += "nullptr";
3385 return headersClassesMapString;
3386}
3387
3388////////////////////////////////////////////////////////////////////////////////
3389
3390bool IsImplementationName(const std::string &filename)
3391{
3392 return !ROOT::TMetaUtils::IsHeaderName(filename);
3393}
3394
3395////////////////////////////////////////////////////////////////////////////////
3396/// Check if the argument is a sane cling argument. Performing the following checks:
3397/// 1) It does not start with "--" and is not the --param option.
3398
3399bool IsCorrectClingArgument(const std::string& argument)
3400{
3401 if (ROOT::TMetaUtils::BeginsWith(argument,"--") && !ROOT::TMetaUtils::BeginsWith(argument,"--param")) return false;
3402 return true;
3403}
3404
3405////////////////////////////////////////////////////////////////////////////////
3406bool NeedsSelection(const char* name)
3407{
3408 static const std::vector<std::string> namePrfxes {
3409 "array<",
3410 "unique_ptr<"};
3411 auto pos = find_if(namePrfxes.begin(),
3412 namePrfxes.end(),
3413 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3414 return namePrfxes.end() == pos;
3415}
3416
3417////////////////////////////////////////////////////////////////////////////////
3418
3420{
3421 static const std::vector<std::string> uclNamePrfxes {
3422 "chrono:",
3423 "ratio<",
3424 "shared_ptr<"};
3425 static const std::set<std::string> unsupportedClassesNormNames{
3426 "regex",
3427 "thread"};
3428 if ( unsupportedClassesNormNames.count(name) == 1) return false;
3429 auto pos = find_if(uclNamePrfxes.begin(),
3430 uclNamePrfxes.end(),
3431 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3432 return uclNamePrfxes.end() == pos;
3433}
3434
3435////////////////////////////////////////////////////////////////////////////////
3436/// Check if the list of selected classes contains any class which is not
3437/// supported. Return the number of unsupported classes in the selection.
3438
3440{
3441 int nerrors = 0;
3442 for (auto&& aRcd : annotatedRcds){
3443 auto clName = aRcd.GetNormalizedName();
3444 if (!IsSupportedClassName(clName)){
3445 std::cerr << "Error: Class " << clName << " has been selected but "
3446 << "currently the support for its I/O is not yet available. Note that "
3447 << clName << ", even if not selected, will be available for "
3448 << "interpreted code.\n";
3449 nerrors++;
3450 }
3451 if (!NeedsSelection(clName)){
3452 std::cerr << "Error: It is not necessary to explicitly select class "
3453 << clName << ". I/O is supported for it transparently.\n";
3454 nerrors++;
3455 }
3456 }
3457 return nerrors;
3458}
3459
3460////////////////////////////////////////////////////////////////////////////////
3461
3462class TRootClingCallbacks : public cling::InterpreterCallbacks {
3463private:
3464 std::list<std::string>& fFilesIncludedByLinkdef;
3465 bool isLocked = false;
3466public:
3467 TRootClingCallbacks(cling::Interpreter* interp, std::list<std::string>& filesIncludedByLinkdef):
3468 InterpreterCallbacks(interp),
3469 fFilesIncludedByLinkdef(filesIncludedByLinkdef){};
3470
3472
3473 virtual void InclusionDirective(clang::SourceLocation /*HashLoc*/, const clang::Token & /*IncludeTok*/,
3474 llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange /*FilenameRange*/,
3475 const clang::FileEntry * /*File*/, llvm::StringRef /*SearchPath*/,
3476 llvm::StringRef /*RelativePath*/, const clang::Module * /*Imported*/,
3477 clang::SrcMgr::CharacteristicKind /*FileType*/)
3478 {
3479 if (isLocked) return;
3480 if (IsAngled) return;
3481 auto& PP = m_Interpreter->getCI()->getPreprocessor();
3482 auto curLexer = PP.getCurrentFileLexer();
3483 if (!curLexer) return;
3484 auto fileEntry = curLexer->getFileEntry();
3485 if (!fileEntry) return;
3486 auto thisFileName = fileEntry->getName();
3487 auto fileNameAsString = FileName.str();
3488 auto isThisLinkdef = ROOT::TMetaUtils::IsLinkdefFile(thisFileName.data());
3489 if (isThisLinkdef) {
3490 auto isTheIncludedLinkdef = ROOT::TMetaUtils::IsLinkdefFile(fileNameAsString.c_str());
3491 if (isTheIncludedLinkdef) {
3492 fFilesIncludedByLinkdef.clear();
3493 isLocked = true;
3494 } else {
3495 fFilesIncludedByLinkdef.emplace_back(fileNameAsString.c_str());
3496 }
3497 }
3498 }
3499
3500 // rootcling pre-includes things such as Rtypes.h. This means that ACLiC can
3501 // call rootcling asking it to create a module for a file with no #includes
3502 // but relying on things from Rtypes.h such as the ClassDef macro.
3503 //
3504 // When rootcling starts building a module, it becomes resilient to the
3505 // outside environment and pre-included files have no effect. This hook
3506 // informs rootcling when a new submodule is being built so that it can
3507 // make Core.Rtypes.h visible.
3508 virtual void EnteredSubmodule(clang::Module* M,
3509 clang::SourceLocation ImportLoc,
3510 bool ForPragma) {
3511 assert(M);
3512 using namespace clang;
3513 if (llvm::StringRef(M->Name).endswith("ACLiC_dict")) {
3514 Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
3515 HeaderSearch& HS = PP.getHeaderSearchInfo();
3516 // FIXME: Reduce to Core.Rtypes.h.
3517 Module* CoreModule = HS.lookupModule("Core", /*AllowSearch*/false);
3518 assert(M && "Must have module Core");
3519 PP.makeModuleVisible(CoreModule, ImportLoc);
3520 }
3521 }
3522};
3523
3524static llvm::cl::list<std::string>
3525gOptModuleByproducts("mByproduct", llvm::cl::ZeroOrMore,
3526 llvm::cl::Hidden,
3527 llvm::cl::desc("The list of the expected implicit modules build as part of building the current module."),
3528 llvm::cl::cat(gRootclingOptions));
3529// Really llvm::cl::Required, will be changed in RootClingMain below.
3530static llvm::cl::opt<std::string>
3531gOptDictionaryFileName(llvm::cl::Positional,
3532 llvm::cl::desc("<output dictionary file>"),
3533 llvm::cl::cat(gRootclingOptions));
3534
3535////////////////////////////////////////////////////////////////////////////////
3536/// Custom diag client for clang that verifies that each implicitly build module
3537/// is a system module. If not, it will let the current rootcling invocation
3538/// fail with an error. All other diags beside module build remarks will be
3539/// forwarded to the passed child diag client.
3540///
3541/// The reason why we need this is that if we built implicitly a C++ module
3542/// that belongs to a ROOT dictionary, then we will miss information generated
3543/// by rootcling in this file (e.g. the source code comments to annotation
3544/// attributes transformation will be missing in the module file).
3545class CheckModuleBuildClient : public clang::DiagnosticConsumer {
3546 clang::DiagnosticConsumer *fChild;
3548 clang::ModuleMap &fMap;
3549
3550public:
3551 CheckModuleBuildClient(clang::DiagnosticConsumer *Child, bool OwnsChild, clang::ModuleMap &Map)
3552 : fChild(Child), fOwnsChild(OwnsChild), fMap(Map)
3553 {
3554 }
3555
3557 {
3558 if (fOwnsChild)
3559 delete fChild;
3560 }
3561
3562 virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
3563 {
3564 using namespace clang::diag;
3565
3566 // This method catches the module_build remark from clang and checks if
3567 // the implicitly built module is a system module or not. We only support
3568 // building system modules implicitly.
3569
3570 std::string moduleName;
3571 const clang::Module *module = nullptr;
3572
3573 // Extract the module from the diag argument with index 0.
3574 const auto &ID = Info.getID();
3575 if (ID == remark_module_build || ID == remark_module_build_done) {
3576 moduleName = Info.getArgStdStr(0);
3577 module = fMap.findModule(moduleName);
3578 // We should never be able to build a module without having it in the
3579 // modulemap. Still, let's print a warning that we at least tell the
3580 // user that this could lead to problems.
3581 if (!module) {
3583 "Couldn't find module %s in the available modulemaps. This"
3584 "prevents us from correctly diagnosing wrongly built modules.\n",
3585 moduleName.c_str());
3586 }
3587 }
3588
3589 // A dictionary module could build implicitly a set of implicit modules.
3590 // For example, the Core module builds libc.pcm and std.pcm implicitly.
3591 // Those modules do not require I/O information and it is okay to build
3592 // them as part of another module.
3593 // However, we can build a module which requires I/O implictly which is
3594 // an error because rootcling is not able to generate the corresponding
3595 // dictionary.
3596 // If we build a I/O requiring module implicitly we should display
3597 // an error unless the -mByproduct was specified.
3598 bool isByproductModule
3599 = module && std::find(gOptModuleByproducts.begin(), gOptModuleByproducts.end(), moduleName) != gOptModuleByproducts.end();
3600 if (!isByproductModule)
3601 fChild->HandleDiagnostic(DiagLevel, Info);
3602
3603 if (ID == remark_module_build && !isByproductModule) {
3605 "Building module '%s' implicitly. If '%s' requires a \n"
3606 "dictionary please specify build dependency: '%s' depends on '%s'.\n"
3607 "Otherwise, specify '-mByproduct %s' to disable this diagnostic.\n",
3608 moduleName.c_str(), moduleName.c_str(), gOptDictionaryFileName.c_str(),
3609 moduleName.c_str(), moduleName.c_str());
3610 }
3611 }
3612
3613 // All methods below just forward to the child and the default method.
3614 virtual void clear() override
3615 {
3616 fChild->clear();
3617 DiagnosticConsumer::clear();
3618 }
3619
3620 virtual void BeginSourceFile(const clang::LangOptions &LangOpts, const clang::Preprocessor *PP) override
3621 {
3622 fChild->BeginSourceFile(LangOpts, PP);
3623 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
3624 }
3625
3626 virtual void EndSourceFile() override
3627 {
3628 fChild->EndSourceFile();
3629 DiagnosticConsumer::EndSourceFile();
3630 }
3631
3632 virtual void finish() override
3633 {
3634 fChild->finish();
3635 DiagnosticConsumer::finish();
3636 }
3637
3638 virtual bool IncludeInDiagnosticCounts() const override { return fChild->IncludeInDiagnosticCounts(); }
3639};
3640
3642#if defined(_WIN32) && defined(_MSC_VER)
3643 // Suppress error dialogs to avoid hangs on build nodes.
3644 // One can use an environment variable (Cling_GuiOnAssert) to enable
3645 // the error dialogs.
3646 const char *EnablePopups = getenv("Cling_GuiOnAssert");
3647 if (EnablePopups == nullptr || EnablePopups[0] == '0') {
3648 ::_set_error_mode(_OUT_TO_STDERR);
3649 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3650 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
3651 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3652 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
3653 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3654 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
3655 }
3656#endif
3657}
3658
3659static llvm::cl::opt<bool> gOptForce("f", llvm::cl::desc("Overwrite <file>s."),
3660 llvm::cl::cat(gRootclingOptions));
3661static llvm::cl::opt<bool> gOptRootBuild("rootbuild", llvm::cl::desc("If we are building ROOT."),
3662 llvm::cl::Hidden,
3663 llvm::cl::cat(gRootclingOptions));
3672static llvm::cl::opt<VerboseLevel>
3673gOptVerboseLevel(llvm::cl::desc("Choose verbosity level:"),
3674 llvm::cl::values(clEnumVal(v, "Show errors."),
3675 clEnumVal(v0, "Show only fatal errors."),
3676 clEnumVal(v1, "Show errors (the same as -v)."),
3677 clEnumVal(v2, "Show warnings (default)."),
3678 clEnumVal(v3, "Show notes."),
3679 clEnumVal(v4, "Show information.")),
3680 llvm::cl::init(v2),
3681 llvm::cl::cat(gRootclingOptions));
3682
3683static llvm::cl::opt<bool>
3684gOptCint("cint", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3685 llvm::cl::Hidden,
3686 llvm::cl::cat(gRootclingOptions));
3687static llvm::cl::opt<bool>
3688gOptReflex("reflex", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3689 llvm::cl::Hidden,
3690 llvm::cl::cat(gRootclingOptions));
3691static llvm::cl::opt<bool>
3692gOptGccXml("gccxml", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3693 llvm::cl::Hidden,
3694 llvm::cl::cat(gRootclingOptions));
3695static llvm::cl::opt<std::string>
3696gOptLibListPrefix("lib-list-prefix",
3697 llvm::cl::desc("An ACLiC feature which exports the list of dependent libraries."),
3698 llvm::cl::Hidden,
3699 llvm::cl::cat(gRootclingOptions));
3700static llvm::cl::opt<bool>
3701gOptGeneratePCH("generate-pch",
3702 llvm::cl::desc("Generates a pch file from a predefined set of headers. See makepch.py."),
3703 llvm::cl::Hidden,
3704 llvm::cl::cat(gRootclingOptions));
3705static llvm::cl::opt<bool>
3706gOptC("c", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3707 llvm::cl::cat(gRootclingOptions));
3708static llvm::cl::opt<bool>
3709gOptP("p", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3710 llvm::cl::cat(gRootclingOptions));
3711static llvm::cl::list<std::string>
3712gOptRootmapLibNames("rml", llvm::cl::ZeroOrMore,
3713 llvm::cl::desc("Generate rootmap file."),
3714 llvm::cl::cat(gRootclingOptions));
3715static llvm::cl::opt<std::string>
3717 llvm::cl::desc("Generate a rootmap file with the specified name."),
3718 llvm::cl::cat(gRootclingOptions));
3719static llvm::cl::opt<bool>
3720gOptCxxModule("cxxmodule",
3721 llvm::cl::desc("Generate a C++ module."),
3722 llvm::cl::cat(gRootclingOptions));
3723static llvm::cl::list<std::string>
3724gOptModuleMapFiles("moduleMapFile",
3725 llvm::cl::desc("Specify a C++ modulemap file."),
3726 llvm::cl::cat(gRootclingOptions));
3727// FIXME: Figure out how to combine the code of -umbrellaHeader and inlineInputHeader
3728static llvm::cl::opt<bool>
3729gOptUmbrellaInput("umbrellaHeader",
3730 llvm::cl::desc("A single header including all headers instead of specifying them on the command line."),
3731 llvm::cl::cat(gRootclingOptions));
3732static llvm::cl::opt<bool>
3733gOptMultiDict("multiDict",
3734 llvm::cl::desc("If this library has multiple separate LinkDef files."),
3735 llvm::cl::cat(gRootclingOptions));
3736static llvm::cl::opt<bool>
3737gOptNoGlobalUsingStd("noGlobalUsingStd",
3738 llvm::cl::desc("Do not declare {using namespace std} in dictionary global scope."),
3739 llvm::cl::cat(gRootclingOptions));
3740static llvm::cl::opt<bool>
3741gOptInterpreterOnly("interpreteronly",
3742 llvm::cl::desc("Generate minimal dictionary for interactivity (without IO information)."),
3743 llvm::cl::cat(gRootclingOptions));
3744static llvm::cl::opt<bool>
3746 llvm::cl::desc("Split the dictionary into two parts: one containing the IO (ClassDef)\
3747information and another the interactivity support."),
3748 llvm::cl::cat(gRootclingOptions));
3749static llvm::cl::opt<bool>
3750gOptNoDictSelection("noDictSelection",
3751 llvm::cl::Hidden,
3752 llvm::cl::desc("Do not run the selection rules. Useful when in -onepcm mode."),
3753 llvm::cl::cat(gRootclingOptions));
3754static llvm::cl::opt<std::string>
3756 llvm::cl::desc("The path to the library of the built dictionary."),
3757 llvm::cl::cat(gRootclingOptions));
3758static llvm::cl::list<std::string>
3760 llvm::cl::desc("The list of dependent modules of the dictionary."),
3761 llvm::cl::cat(gRootclingOptions));
3762static llvm::cl::list<std::string>
3763gOptExcludePaths("excludePath", llvm::cl::ZeroOrMore,
3764 llvm::cl::desc("Do not store the <path> in the dictionary."),
3765 llvm::cl::cat(gRootclingOptions));
3766// FIXME: This does not seem to work. We have one use of -inlineInputHeader in
3767// ROOT and it does not produce the expected result.
3768static llvm::cl::opt<bool>
3769gOptInlineInput("inlineInputHeader",
3770 llvm::cl::desc("Does not generate #include <header> but expands the header content."),
3771 llvm::cl::cat(gRootclingOptions));
3772// FIXME: This is totally the wrong concept. We should not expose an interface
3773// to be able to tell which component is in the pch and which needs extra
3774// scaffolding for interactive use. Moreover, some of the ROOT components are
3775// partially in the pch and this option makes it impossible to express that.
3776// We should be able to get the list of headers in the pch early and scan
3777// through them.
3778static llvm::cl::opt<bool>
3779gOptWriteEmptyRootPCM("writeEmptyRootPCM",
3780 llvm::cl::Hidden,
3781 llvm::cl::desc("Does not include the header files as it assumes they exist in the pch."),
3782 llvm::cl::cat(gRootclingOptions));
3783static llvm::cl::opt<bool>
3785 llvm::cl::desc("Check the selection syntax only."),
3786 llvm::cl::cat(gRootclingOptions));
3787static llvm::cl::opt<bool>
3788gOptFailOnWarnings("failOnWarnings",
3789 llvm::cl::desc("Fail if there are warnings."),
3790 llvm::cl::cat(gRootclingOptions));
3791static llvm::cl::opt<bool>
3792gOptNoIncludePaths("noIncludePaths",
3793 llvm::cl::desc("Do not store include paths but rely on the env variable ROOT_INCLUDE_PATH."),
3794 llvm::cl::cat(gRootclingOptions));
3795static llvm::cl::opt<std::string>
3796gOptISysRoot("isysroot", llvm::cl::Prefix, llvm::cl::Hidden,
3797 llvm::cl::desc("Specify an isysroot."),
3798 llvm::cl::cat(gRootclingOptions),
3799 llvm::cl::init("-"));
3800static llvm::cl::list<std::string>
3801gOptIncludePaths("I", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3802 llvm::cl::desc("Specify an include path."),
3803 llvm::cl::cat(gRootclingOptions));
3804static llvm::cl::list<std::string>
3805gOptCompDefaultIncludePaths("compilerI", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3806 llvm::cl::desc("Specify a compiler default include path, to suppress unneeded `-isystem` arguments."),
3807 llvm::cl::cat(gRootclingOptions));
3808static llvm::cl::list<std::string>
3809gOptSysIncludePaths("isystem", llvm::cl::ZeroOrMore,
3810 llvm::cl::desc("Specify a system include path."),
3811 llvm::cl::cat(gRootclingOptions));
3812static llvm::cl::list<std::string>
3813gOptPPDefines("D", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3814 llvm::cl::desc("Specify defined macros."),
3815 llvm::cl::cat(gRootclingOptions));
3816static llvm::cl::list<std::string>
3817gOptPPUndefines("U", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3818 llvm::cl::desc("Specify undefined macros."),
3819 llvm::cl::cat(gRootclingOptions));
3820static llvm::cl::list<std::string>
3821gOptWDiags("W", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3822 llvm::cl::desc("Specify compiler diagnostics options."),
3823 llvm::cl::cat(gRootclingOptions));
3824// Really OneOrMore, will be changed in RootClingMain below.
3825static llvm::cl::list<std::string>
3826gOptDictionaryHeaderFiles(llvm::cl::Positional, llvm::cl::ZeroOrMore,
3827 llvm::cl::desc("<list of dictionary header files> <LinkDef file>"),
3828 llvm::cl::cat(gRootclingOptions));
3829static llvm::cl::list<std::string>
3830gOptSink(llvm::cl::ZeroOrMore, llvm::cl::Sink,
3831 llvm::cl::desc("Consumes all unrecognized options."),
3832 llvm::cl::cat(gRootclingOptions));
3833
3834static llvm::cl::SubCommand
3835gBareClingSubcommand("bare-cling", "Call directly cling and exit.");
3836
3837static llvm::cl::list<std::string>
3838gOptBareClingSink(llvm::cl::OneOrMore, llvm::cl::Sink,
3839 llvm::cl::desc("Consumes options and sends them to cling."),
3840 llvm::cl::cat(gRootclingOptions), llvm::cl::sub(gBareClingSubcommand));
3841
3842////////////////////////////////////////////////////////////////////////////////
3843/// Returns true iff a given module (and its submodules) contains all headers
3844/// needed by the given ModuleGenerator.
3845/// The names of all header files that are needed by the ModuleGenerator but are
3846/// not in the given module will be inserted into the MissingHeader variable.
3847/// Returns true iff the PCH was successfully generated.
3848static bool ModuleContainsHeaders(TModuleGenerator &modGen, clang::Module *module,
3849 std::vector<std::string> &missingHeaders)
3850{
3851 // Now we collect all header files from the previously collected modules.
3852 std::vector<clang::Module::Header> moduleHeaders;
3854 [&moduleHeaders](const clang::Module::Header &h) { moduleHeaders.push_back(h); });
3855
3856 bool foundAllHeaders = true;
3857
3858 // Go through the list of headers that are required by the ModuleGenerator
3859 // and check for each header if it's in one of the modules we loaded.
3860 // If not, make sure we fail at the end and mark the header as missing.
3861 for (const std::string &header : modGen.GetHeaders()) {
3862 bool headerFound = false;
3863 for (const clang::Module::Header &moduleHeader : moduleHeaders) {
3864 if (header == moduleHeader.NameAsWritten) {
3865 headerFound = true;
3866 break;
3867 }
3868 }
3869 if (!headerFound) {
3870 missingHeaders.push_back(header);
3871 foundAllHeaders = false;
3872 }
3873 }
3874 return foundAllHeaders;
3875}
3876
3877////////////////////////////////////////////////////////////////////////////////
3878/// Check moduleName validity from modulemap. Check if this module is defined or not.
3879static bool CheckModuleValid(TModuleGenerator &modGen, const std::string &resourceDir, cling::Interpreter &interpreter,
3880 llvm::StringRef LinkdefPath, const std::string &moduleName)
3881{
3882 clang::CompilerInstance *CI = interpreter.getCI();
3883 clang::HeaderSearch &headerSearch = CI->getPreprocessor().getHeaderSearchInfo();
3884 headerSearch.loadTopLevelSystemModules();
3885
3886 // Actually lookup the module on the computed module name.
3887 clang::Module *module = headerSearch.lookupModule(llvm::StringRef(moduleName));
3888
3889 // Inform the user and abort if we can't find a module with a given name.
3890 if (!module) {
3891 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't find module with name '%s' in modulemap!\n",
3892 moduleName.c_str());
3893 return false;
3894 }
3895
3896 // Check if the loaded module covers all headers that were specified
3897 // by the user on the command line. This is an integrity check to
3898 // ensure that our used module map is
3899 std::vector<std::string> missingHeaders;
3900 if (!ModuleContainsHeaders(modGen, module, missingHeaders)) {
3901 // FIXME: Upgrade this to an error once modules are stable.
3902 std::stringstream msgStream;
3903 msgStream << "warning: Couldn't find in "
3904 << module->PresumedModuleMapFile
3905 << " the following specified headers in "
3906 << "the module " << module->Name << ":\n";
3907 for (auto &H : missingHeaders) {
3908 msgStream << " " << H << "\n";
3909 }
3910 std::string warningMessage = msgStream.str();
3911
3912 bool maybeUmbrella = modGen.GetHeaders().size() == 1;
3913 // We may have an umbrella and forgot to add the flag. Downgrade the
3914 // warning into an information message.
3915 // FIXME: We should open the umbrella, extract the set of header files
3916 // and check if they exist in the modulemap.
3917 // FIXME: We should also check if the header files are specified in the
3918 // modulemap file as they appeared in the rootcling invocation, i.e.
3919 // if we passed rootcling ... -I/some/path somedir/some/header, the
3920 // modulemap should contain module M { header "somedir/some/header" }
3921 // This way we will make sure the module is properly activated.
3922 if (!gOptUmbrellaInput && maybeUmbrella) {
3923 ROOT::TMetaUtils::Info("CheckModuleValid, %s. You can silence this message by adding %s to the invocation.",
3924 warningMessage.c_str(),
3925 gOptUmbrellaInput.ArgStr.data());
3926 return true;
3927 }
3928
3929 ROOT::TMetaUtils::Warning("CheckModuleValid", warningMessage.c_str());
3930 // We include the missing headers to fix the module for the user.
3931 if (!IncludeHeaders(missingHeaders, interpreter)) {
3932 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't include missing module headers for module '%s'!\n",
3933 module->Name.c_str());
3934 }
3935 }
3936
3937 return true;
3938}
3939
3940static llvm::StringRef GetModuleNameFromRdictName(llvm::StringRef rdictName)
3941{
3942 // Try to get the module name in the modulemap based on the filepath.
3943 llvm::StringRef moduleName = llvm::sys::path::filename(rdictName);
3944 moduleName.consume_front("lib");
3945 moduleName.consume_back(".pcm");
3946 moduleName.consume_back("_rdict");
3947 return moduleName;
3948}
3949
3950////////////////////////////////////////////////////////////////////////////////
3951
3952int RootClingMain(int argc,
3953 char **argv,
3954 bool isGenreflex = false)
3955{
3956 // Define Options aliasses
3957 auto &opts = llvm::cl::getRegisteredOptions();
3958 auto &optHelp = *opts["help"];
3959 llvm::cl::alias optHelpAlias1("h",
3960 llvm::cl::desc("Alias for -help"),
3961 llvm::cl::aliasopt(optHelp));
3962 llvm::cl::alias optHelpAlias2("?",
3963 llvm::cl::desc("Alias for -help"),
3964 llvm::cl::aliasopt(optHelp));
3965
3966 // Set number of required arguments. We cannot do this globally since it
3967 // would interfere with LLVM's option parsing.
3968 gOptDictionaryFileName.setNumOccurrencesFlag(llvm::cl::Required);
3969 gOptDictionaryHeaderFiles.setNumOccurrencesFlag(llvm::cl::OneOrMore);
3970
3971 // Copied from cling driver.
3972 // FIXME: Uncomment once we fix ROOT's teardown order.
3973 //llvm::llvm_shutdown_obj shutdownTrigger;
3974
3975 const char *executableFileName = argv[0];
3976
3977 llvm::sys::PrintStackTraceOnErrorSignal(executableFileName);
3978 llvm::PrettyStackTraceProgram X(argc, argv);
3980
3981#if defined(R__WIN32) && !defined(R__WINGCC)
3982 // FIXME: This is terrible hack allocating and changing the argument set.
3983 // We should remove it and use standard llvm facilities to convert the paths.
3984 // cygwin's make is presenting us some cygwin paths even though
3985 // we are windows native. Convert them as good as we can.
3986 for (int iic = 1 /* ignore binary file name in argv[0] */; iic < argc; ++iic) {
3987 std::string iiarg(argv[iic]);
3988 if (FromCygToNativePath(iiarg)) {
3989 size_t len = iiarg.length();
3990 // yes, we leak.
3991 char *argviic = new char[len + 1];
3992 strlcpy(argviic, iiarg.c_str(), len + 1);
3993 argv[iic] = argviic;
3994 }
3995 }
3996#endif
3997
3998 // Hide options from llvm which we got from static initialization of libCling.
3999 llvm::cl::HideUnrelatedOptions(/*keep*/gRootclingOptions);
4000
4001 llvm::cl::ParseCommandLineOptions(argc, argv, "rootcling");
4002
4003 std::string llvmResourceDir = std::string(gDriverConfig->fTROOT__GetEtcDir()) + "/cling";
4005 std::vector<const char *> clingArgsC;
4006 clingArgsC.push_back(executableFileName);
4007 // Help cling finds its runtime (RuntimeUniverse.h and such).
4008 clingArgsC.push_back("-I");
4009 clingArgsC.push_back(gDriverConfig->fTROOT__GetEtcDir());
4010
4011 //clingArgsC.push_back("-resource-dir");
4012 //clingArgsC.push_back(llvmResourceDir.c_str());
4013
4014 for (const std::string& Opt : gOptBareClingSink)
4015 clingArgsC.push_back(Opt.c_str());
4016
4017 auto interp = llvm::make_unique<cling::Interpreter>(clingArgsC.size(),
4018 &clingArgsC[0],
4019 llvmResourceDir.c_str());
4020 // FIXME: Diagnose when we have misspelled a flag. Currently we show no
4021 // diagnostic and report exit as success.
4022 return interp->getDiagnostics().hasFatalErrorOccurred();
4023 }
4024
4025 std::string dictname;
4026
4028 if (gOptRootBuild) {
4029 // running rootcling as part of the ROOT build for ROOT libraries.
4030 gBuildingROOT = true;
4031 }
4032 }
4033
4034 if (!gOptModuleMapFiles.empty() && !gOptCxxModule) {
4035 ROOT::TMetaUtils::Error("", "Option %s can be used only when option %s is specified.\n",
4036 gOptModuleMapFiles.ArgStr.str().c_str(),
4037 gOptCxxModule.ArgStr.str().c_str());
4038 std::cout << "\n";
4039 llvm::cl::PrintHelpMessage();
4040 return 1;
4041 }
4042
4043 // Set the default verbosity
4045 if (gOptVerboseLevel == v4)
4046 genreflex::verbose = true;
4047
4048 if (gOptReflex)
4049 isGenreflex = true;
4050
4051 if (!gOptLibListPrefix.empty()) {
4052 string filein = gOptLibListPrefix + ".in";
4053 FILE *fp;
4054 if ((fp = fopen(filein.c_str(), "r")) == nullptr) {
4055 ROOT::TMetaUtils::Error(nullptr, "%s: The input list file %s does not exist\n", executableFileName, filein.c_str());
4056 return 1;
4057 }
4058 fclose(fp);
4059 }
4060
4062 FILE *fp;
4063 if (!gOptIgnoreExistingDict && (fp = fopen(gOptDictionaryFileName.c_str(), "r")) != nullptr) {
4064 fclose(fp);
4065 if (!gOptForce) {
4066 ROOT::TMetaUtils::Error(nullptr, "%s: output file %s already exists\n", executableFileName, gOptDictionaryFileName.c_str());
4067 return 1;
4068 }
4069 }
4070
4071 // remove possible pathname to get the dictionary name
4072 if (gOptDictionaryFileName.size() > (PATH_MAX - 1)) {
4073 ROOT::TMetaUtils::Error(nullptr, "rootcling: dictionary name too long (more than %d characters): %s\n",
4074 (PATH_MAX - 1), gOptDictionaryFileName.c_str());
4075 return 1;
4076 }
4077
4078 dictname = llvm::sys::path::filename(gOptDictionaryFileName);
4079 }
4080
4081 if (gOptForce && dictname.empty()) {
4082 ROOT::TMetaUtils::Error(nullptr, "Inconsistent set of arguments detected: overwrite of dictionary file forced but no filename specified.\n");
4083 llvm::cl::PrintHelpMessage();
4084 return 1;
4085 }
4086
4087 std::vector<std::string> clingArgs;
4088 clingArgs.push_back(executableFileName);
4089 clingArgs.push_back("-iquote.");
4090
4091 bool dictSelection = !gOptNoDictSelection;
4092
4093 // Collect the diagnostic pragmas linked to the usage of -W
4094 // Workaround for ROOT-5656
4095 std::list<std::string> diagnosticPragmas = {"#pragma clang diagnostic ignored \"-Wdeprecated-declarations\""};
4096
4097 if (gOptFailOnWarnings) {
4098 using namespace ROOT::TMetaUtils;
4099 // If warnings are disabled with the current verbosity settings, lower
4100 // it so that the user sees the warning that caused the failure.
4103 GetWarningsAreErrors() = true;
4104 }
4105
4106 if (gOptISysRoot != "-") {
4107 if (gOptISysRoot.empty()) {
4108 ROOT::TMetaUtils::Error("", "isysroot specified without a value.\n");
4109 return 1;
4110 }
4111 clingArgs.push_back(gOptISysRoot.ArgStr);
4112 clingArgs.push_back(gOptISysRoot.ValueStr);
4113 }
4114
4115 // Check if we have a multi dict request but no target library
4116 if (gOptMultiDict && gOptSharedLibFileName.empty()) {
4117 ROOT::TMetaUtils::Error("", "Multidict requested but no target library. Please specify one with the -s argument.\n");
4118 return 1;
4119 }
4120
4121 for (const std::string &PPDefine : gOptPPDefines)
4122 clingArgs.push_back(std::string("-D") + PPDefine);
4123
4124 for (const std::string &PPUndefine : gOptPPUndefines)
4125 clingArgs.push_back(std::string("-U") + PPUndefine);
4126
4127 for (const std::string &IncludePath : gOptIncludePaths)
4128 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(IncludePath));
4129
4130 for (const std::string &IncludePath : gOptSysIncludePaths) {
4131 // Prevent mentioning compiler default include directories as -isystem
4132 // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129)
4133 if (std::find(gOptCompDefaultIncludePaths.begin(), gOptCompDefaultIncludePaths.end(), IncludePath)
4134 == gOptCompDefaultIncludePaths.end()) {
4135 clingArgs.push_back("-isystem");
4136 clingArgs.push_back(llvm::sys::path::convert_to_slash(IncludePath));
4137 }
4138 }
4139
4140 for (const std::string &WDiag : gOptWDiags) {
4141 const std::string FullWDiag = std::string("-W") + WDiag;
4142 // Suppress warning when compiling the dictionary, eg. gcc G__xxx.cxx
4143 CheckForMinusW(FullWDiag, diagnosticPragmas);
4144 // Suppress warning when compiling the input headers by cling.
4145 clingArgs.push_back(FullWDiag);
4146 }
4147
4148 std::string includeDir = llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetIncludeDir());
4149 clingArgs.push_back(std::string("-I") + includeDir);
4150
4151 std::vector<std::string> pcmArgs;
4152 for (size_t parg = 0, n = clingArgs.size(); parg < n; ++parg) {
4153 auto thisArg = clingArgs[parg];
4154 auto isInclude = ROOT::TMetaUtils::BeginsWith(thisArg,"-I");
4155 if (thisArg == "-c" ||
4156 (gOptNoIncludePaths && isInclude)) continue;
4157 // We now check if the include directories are not excluded
4158 if (isInclude) {
4159 unsigned int offset = 2; // -I is two characters. Now account for spaces
4160 char c = thisArg[offset];
4161 while (c == ' ') c = thisArg[++offset];
4162 auto excludePathsEnd = gOptExcludePaths.end();
4163 auto excludePathPos = std::find_if(gOptExcludePaths.begin(),
4164 excludePathsEnd,
4165 [&](const std::string& path){
4166 return ROOT::TMetaUtils::BeginsWith(&thisArg[offset], path);});
4167 if (excludePathsEnd != excludePathPos) continue;
4168 }
4169 pcmArgs.push_back(thisArg);
4170 }
4171
4172 // cling-only arguments
4173 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetEtcDir()));
4174 // We do not want __ROOTCLING__ in the pch!
4175 if (!gOptGeneratePCH) {
4176 clingArgs.push_back("-D__ROOTCLING__");
4177 }
4178#ifdef R__MACOSX
4179 clingArgs.push_back("-DSYSTEM_TYPE_macosx");
4180#elif defined(R__WIN32)
4181 clingArgs.push_back("-DSYSTEM_TYPE_winnt");
4182
4183 // Prevent the following #error: The C++ Standard Library forbids macroizing keywords.
4184 clingArgs.push_back("-D_XKEYCHECK_H");
4185 // Tell windows.h not to #define min and max, it clashes with numerical_limits.
4186 clingArgs.push_back("-DNOMINMAX");
4187#else // assume UNIX
4188 clingArgs.push_back("-DSYSTEM_TYPE_unix");
4189#endif
4190
4191 clingArgs.push_back("-fsyntax-only");
4192#ifndef R__WIN32
4193 clingArgs.push_back("-fPIC");
4194#endif
4195 clingArgs.push_back("-Xclang");
4196 clingArgs.push_back("-fmodules-embed-all-files");
4197 clingArgs.push_back("-Xclang");
4198 clingArgs.push_back("-main-file-name");
4199 clingArgs.push_back("-Xclang");
4200 clingArgs.push_back((dictname + ".h").c_str());
4201
4203
4204 // FIXME: This line is from TModuleGenerator, but we can't reuse this code
4205 // at this point because TModuleGenerator needs a CompilerInstance (and we
4206 // currently create the arguments for creating said CompilerInstance).
4207 bool isPCH = (gOptDictionaryFileName.getValue() == "allDict.cxx");
4208 std::string outputFile;
4209 // Data is in 'outputFile', therefore in the same scope.
4210 llvm::StringRef moduleName;
4211 std::string vfsArg;
4212 // Adding -fmodules to the args will break lexing with __CINT__ defined,
4213 // and we actually do lex with __CINT__ and reuse this variable later,
4214 // we have to copy it now.
4215 auto clingArgsInterpreter = clingArgs;
4216
4217 if (gOptSharedLibFileName.empty()) {
4219 }
4220
4221 if (!isPCH && gOptCxxModule) {
4222 // We just pass -fmodules, the CIFactory will do the rest and configure
4223 // clang correctly once it sees this flag.
4224 clingArgsInterpreter.push_back("-fmodules");
4225 clingArgsInterpreter.push_back("-fno-implicit-module-maps");
4226
4227 for (const std::string &modulemap : gOptModuleMapFiles)
4228 clingArgsInterpreter.push_back("-fmodule-map-file=" + modulemap);
4229
4230 clingArgsInterpreter.push_back("-fmodule-map-file=" +
4231 std::string(gDriverConfig->fTROOT__GetIncludeDir()) +
4232 "/module.modulemap");
4233 std::string ModuleMapCWD = ROOT::FoundationUtils::GetCurrentDir() + "/module.modulemap";
4234 if (llvm::sys::fs::exists(ModuleMapCWD))
4235 clingArgsInterpreter.push_back("-fmodule-map-file=" + ModuleMapCWD);
4236
4237 // Specify the module name that we can lookup the module in the modulemap.
4238 outputFile = llvm::sys::path::stem(gOptSharedLibFileName).str();
4239 // Try to get the module name in the modulemap based on the filepath.
4240 moduleName = GetModuleNameFromRdictName(outputFile);
4241
4242 clingArgsInterpreter.push_back("-fmodule-name");
4243 clingArgsInterpreter.push_back(moduleName.str());
4244
4245 std::string moduleCachePath = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4246 // FIXME: This is a horrible workaround to fix the incremental builds.
4247 // The enumerated modules are built by clang impicitly based on #include of
4248 // a header which is contained within that module. The build system has
4249 // no way to track dependencies on them and trigger a rebuild.
4250 // A possible solution can be to disable completely the implicit build of
4251 // modules and each module to be built by rootcling. We need to teach
4252 // rootcling how to build modules with no IO support.
4253 if (moduleName == "Core") {
4255 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_intrinsics.pcm").str().c_str());
4256 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_stddef_max_align_t.pcm").str().c_str());
4257 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime.pcm").str().c_str());
4258 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime_Extra.pcm").str().c_str());
4259#ifdef R__WIN32
4260 remove((moduleCachePath + llvm::sys::path::get_separator() + "vcruntime.pcm").str().c_str());
4261 remove((moduleCachePath + llvm::sys::path::get_separator() + "services.pcm").str().c_str());
4262#endif
4263
4264#ifdef R__MACOSX
4265 remove((moduleCachePath + llvm::sys::path::get_separator() + "Darwin.pcm").str().c_str());
4266#else
4267 remove((moduleCachePath + llvm::sys::path::get_separator() + "libc.pcm").str().c_str());
4268#endif
4269 remove((moduleCachePath + llvm::sys::path::get_separator() + "std.pcm").str().c_str());
4270 remove((moduleCachePath + llvm::sys::path::get_separator() + "boost.pcm").str().c_str());
4271 remove((moduleCachePath + llvm::sys::path::get_separator() + "tinyxml2.pcm").str().c_str());
4272 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Config.pcm").str().c_str());
4273 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Rtypes.pcm").str().c_str());
4274 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_C.pcm").str().c_str());
4275 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_Stage1_NoRTTI.pcm").str().c_str());
4276 } else if (moduleName == "MathCore") {
4277 remove((moduleCachePath + llvm::sys::path::get_separator() + "Vc.pcm").str().c_str());
4278 }
4279
4280 // Set the C++ modules output directory to the directory where we generate
4281 // the shared library.
4282 clingArgsInterpreter.push_back("-fmodules-cache-path=" + moduleCachePath);
4283 }
4284
4285 if (gOptVerboseLevel == v4)
4286 clingArgsInterpreter.push_back("-v");
4287
4288 // Convert arguments to a C array and check if they are sane
4289 std::vector<const char *> clingArgsC;
4290 for (auto const &clingArg : clingArgsInterpreter) {
4291 if (!IsCorrectClingArgument(clingArg)){
4292 std::cerr << "Argument \""<< clingArg << "\" is not a supported cling argument. "
4293 << "This could be mistyped rootcling argument. Please check the commandline.\n";
4294 return 1;
4295 }
4296 clingArgsC.push_back(clingArg.c_str());
4297 }
4298
4299
4300 std::unique_ptr<cling::Interpreter> owningInterpPtr;
4301 cling::Interpreter* interpPtr = nullptr;
4302
4303 std::list<std::string> filesIncludedByLinkdef;
4305#ifdef R__FAST_MATH
4306 // Same setting as in TCling.cxx.
4307 clingArgsC.push_back("-ffast-math");
4308#endif
4309
4310 owningInterpPtr.reset(new cling::Interpreter(clingArgsC.size(), &clingArgsC[0],
4311 llvmResourceDir.c_str()));
4312 interpPtr = owningInterpPtr.get();
4313 } else {
4314 // Pass the interpreter arguments to TCling's interpreter:
4315 clingArgsC.push_back("-resource-dir");
4316 clingArgsC.push_back(llvmResourceDir.c_str());
4317 clingArgsC.push_back(nullptr); // signal end of array
4318 const char ** &extraArgs = *gDriverConfig->fTROOT__GetExtraInterpreterArgs();
4319 extraArgs = &clingArgsC[1]; // skip binary name
4321 if (!isGenreflex && !gOptGeneratePCH) {
4322 std::unique_ptr<TRootClingCallbacks> callBacks (new TRootClingCallbacks(interpPtr, filesIncludedByLinkdef));
4323 interpPtr->setCallbacks(std::move(callBacks));
4324 }
4325 }
4326 cling::Interpreter &interp = *interpPtr;
4327 clang::CompilerInstance *CI = interp.getCI();
4328 // FIXME: Remove this once we switch cling to use the driver. This would handle -fmodules-embed-all-files for us.
4329 CI->getFrontendOpts().ModulesEmbedAllFiles = true;
4330 CI->getSourceManager().setAllFilesAreTransient(true);
4331
4332 clang::Preprocessor &PP = CI->getPreprocessor();
4333 clang::HeaderSearch &headerSearch = PP.getHeaderSearchInfo();
4334 clang::ModuleMap &moduleMap = headerSearch.getModuleMap();
4335 auto &diags = interp.getDiagnostics();
4336
4337 // Manually enable the module build remarks. We don't enable them via the
4338 // normal clang command line arg because otherwise we would get remarks for
4339 // building STL/libc when starting the interpreter in rootcling_stage1.
4340 // We can't prevent these diags in any other way because we can only attach
4341 // our own diag client now after the interpreter has already started.
4342 diags.setSeverity(clang::diag::remark_module_build, clang::diag::Severity::Remark, clang::SourceLocation());
4343
4344 // Attach our own diag client that listens to the module_build remarks from
4345 // clang to check that we don't build dictionary C++ modules implicitly.
4346 auto recordingClient = new CheckModuleBuildClient(diags.getClient(), diags.ownsClient(), moduleMap);
4347 diags.setClient(recordingClient, true);
4348
4350 ROOT::TMetaUtils::Info(nullptr, "\n");
4351 ROOT::TMetaUtils::Info(nullptr, "==== INTERPRETER CONFIGURATION ====\n");
4352 ROOT::TMetaUtils::Info(nullptr, "== Include paths\n");
4353 interp.DumpIncludePath();
4354 printf("\n\n");
4355 fflush(stdout);
4356
4357 ROOT::TMetaUtils::Info(nullptr, "== Included files\n");
4358 interp.printIncludedFiles(llvm::outs());
4359 llvm::outs() << "\n\n";
4360 llvm::outs().flush();
4361
4362 ROOT::TMetaUtils::Info(nullptr, "== Language Options\n");
4363 const clang::LangOptions& LangOpts
4364 = interp.getCI()->getASTContext().getLangOpts();
4365#define LANGOPT(Name, Bits, Default, Description) \
4366 ROOT::TMetaUtils::Info(0, "%s = %d // %s\n", #Name, (int)LangOpts.Name, Description);
4367#define ENUM_LANGOPT(Name, Type, Bits, Default, Description)
4368#include "clang/Basic/LangOptions.def"
4369 ROOT::TMetaUtils::Info(nullptr, "==== END interpreter configuration ====\n\n");
4370 }
4371
4372 interp.getOptions().ErrorOut = true;
4373 interp.enableRawInput(true);
4374
4375 if (gOptCxxModule) {
4376 for (llvm::StringRef DepMod : gOptModuleDependencies) {
4377 if (DepMod.endswith("_rdict.pcm")) {
4378 ROOT::TMetaUtils::Warning(nullptr, "'%s' value is deprecated. Please use [<fullpath>]%s.pcm\n",
4379 DepMod.data(),
4380 GetModuleNameFromRdictName(DepMod).str().data());
4381 }
4382 DepMod = GetModuleNameFromRdictName(DepMod);
4383 // We might deserialize.
4384 cling::Interpreter::PushTransactionRAII RAII(&interp);
4385 if (!interp.loadModule(DepMod, /*complain*/false)) {
4386 ROOT::TMetaUtils::Error(nullptr, "Module '%s' failed to load.\n",
4387 DepMod.data());
4388 }
4389 }
4390 }
4391
4392 if (!isGenreflex) { // rootcling
4393 // ROOTCINT uses to define a few header implicitly, we need to do it explicitly.
4394 if (interp.declare("#include <assert.h>\n"
4395 "#include \"Rtypes.h\"\n"
4396 "#include \"TObject.h\"") != cling::Interpreter::kSuccess
4397 ) {
4398 // There was an error.
4399 ROOT::TMetaUtils::Error(nullptr, "Error loading the default rootcling header files.\n");
4400 return 1;
4401 }
4402 }
4403
4404 if (interp.declare("#include <string>\n" // For the list of 'opaque' typedef to also include string.
4405 "#include <RtypesCore.h>\n" // For initializing TNormalizedCtxt.
4406 "namespace std {} using namespace std;") != cling::Interpreter::kSuccess) {
4407 ROOT::TMetaUtils::Error(nullptr, "Error loading the default header files.\n");
4408 return 1;
4409 }
4410
4411 // We are now ready (enough is loaded) to init the list of opaque typedefs.
4412 ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper());
4413 ROOT::TMetaUtils::TClingLookupHelper helper(interp, normCtxt, nullptr, nullptr, nullptr);
4414 TClassEdit::Init(&helper);
4415
4416 // flags used only for the pragma parser:
4417 clingArgs.push_back("-D__CINT__");
4418 clingArgs.push_back("-D__MAKECINT__");
4419
4420 AddPlatformDefines(clingArgs);
4421
4422 std::string currentDirectory = ROOT::FoundationUtils::GetCurrentDir();
4423
4424 std::string interpPragmaSource;
4425 std::string includeForSource;
4426 std::string interpreterDeclarations;
4427 std::string linkdef;
4428
4429 for (size_t i = 0, e = gOptDictionaryHeaderFiles.size(); i < e; ++i) {
4430 const std::string& optHeaderFileName = gOptDictionaryHeaderFiles[i];
4431 bool isSelectionFile = IsSelectionFile(optHeaderFileName.c_str());
4432
4433 if (isSelectionFile) {
4434 if (i == e - 1) {
4435 linkdef = optHeaderFileName;
4436 } else { // if the linkdef was not last, issue an error.
4437 ROOT::TMetaUtils::Error(nullptr, "%s: %s must be last file on command line\n",
4438 executableFileName, optHeaderFileName.c_str());
4439 return 1;
4440 }
4441 }
4442
4443 // coverity[tainted_data] The OS should already limit the argument size, so we are safe here
4444 std::string fullheader(optHeaderFileName);
4445 // Strip any trailing + which is only used by GeneratedLinkdef.h which currently
4446 // use directly argv.
4447 if (fullheader[fullheader.length() - 1] == '+') {
4448 fullheader.erase(fullheader.length() - 1);
4449 }
4450 std::string header(
4451 isSelectionFile ? fullheader : ROOT::FoundationUtils::MakePathRelative(fullheader, currentDirectory, gBuildingROOT));
4452
4453 interpPragmaSource += std::string("#include \"") + header + "\"\n";
4454 if (!isSelectionFile) {
4455 // In order to not have to add the equivalent to -I${PWD} to the
4456 // command line, include the complete file name, even if it is a
4457 // full pathname, when we write it down in the dictionary.
4458 // Note: have -I${PWD} means in that (at least in the case of
4459 // ACLiC) we inadvertently pick local file that have the same
4460 // name as system header (e.g. new or list) and -iquote has not
4461 // equivalent on some platforms.
4462 includeForSource += std::string("#include \"") + fullheader + "\"\n";
4463 pcmArgs.push_back(header);
4464 } else if (!IsSelectionXml(optHeaderFileName.c_str())) {
4465 interpreterDeclarations += std::string("#include \"") + header + "\"\n";
4466 }
4467 }
4468
4469 if (gOptUmbrellaInput) {
4470 bool hasSelectionFile = !linkdef.empty();
4471 unsigned expectedHeaderFilesSize = 1 + hasSelectionFile;
4472 if (gOptDictionaryHeaderFiles.size() > expectedHeaderFilesSize)
4473 ROOT::TMetaUtils::Error(nullptr, "Option %s used but more than one header file specified.\n",
4474 gOptUmbrellaInput.ArgStr.data());
4475 }
4476
4477 // We have a multiDict request. This implies generating a pcm which is of the form
4478 // dictName_libname_rdict.pcm
4479 if (gOptMultiDict) {
4480
4481 std::string newName = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4482 if (!newName.empty())
4483 newName += gPathSeparator;
4484 newName += llvm::sys::path::stem(gOptSharedLibFileName);
4485 newName += "_";
4486 newName += llvm::sys::path::stem(gOptDictionaryFileName);
4488 gOptSharedLibFileName = newName;
4489 }
4490
4491 // Until the module are actually enabled in ROOT, we need to register
4492 // the 'current' directory to make it relocatable (i.e. have a way
4493 // to find the headers).
4495 string incCurDir = "-I";
4496 incCurDir += currentDirectory;
4497 pcmArgs.push_back(incCurDir);
4498 }
4499
4500 // Add the diagnostic pragmas distilled from the -Wno-xyz
4501 {
4502 std::stringstream res;
4503 const char* delim="\n";
4504 std::copy(diagnosticPragmas.begin(),
4505 diagnosticPragmas.end(),
4506 std::ostream_iterator<std::string>(res, delim));
4507 if (interp.declare(res.str()) != cling::Interpreter::kSuccess) {
4508 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -Wno-xyz flags as pragmas:\n%s", res.str().c_str());
4509 return 1;
4510 }
4511 }
4512
4513 class IgnoringPragmaHandler: public clang::PragmaNamespace {
4514 public:
4515 IgnoringPragmaHandler(const char* pragma):
4516 clang::PragmaNamespace(pragma) {}
4517 void HandlePragma(clang::Preprocessor &PP,
4518 clang::PragmaIntroducer Introducer,
4519 clang::Token &tok) {
4520 PP.DiscardUntilEndOfDirective();
4521 }
4522 };
4523
4524 // Ignore these #pragmas to suppress "unknown pragma" warnings.
4525 // See LinkdefReader.cxx.
4526 PP.AddPragmaHandler(new IgnoringPragmaHandler("link"));
4527 PP.AddPragmaHandler(new IgnoringPragmaHandler("extra_include"));
4528 PP.AddPragmaHandler(new IgnoringPragmaHandler("read"));
4529 PP.AddPragmaHandler(new IgnoringPragmaHandler("create"));
4530
4531 if (!interpreterDeclarations.empty() &&
4532 interp.declare(interpreterDeclarations) != cling::Interpreter::kSuccess) {
4533 ROOT::TMetaUtils::Error(nullptr, "%s: Linkdef compilation failure\n", executableFileName);
4534 return 1;
4535 }
4536
4537
4538 TModuleGenerator modGen(interp.getCI(),
4542
4543 if (!gDriverConfig->fBuildingROOTStage1 && !filesIncludedByLinkdef.empty()) {
4544 pcmArgs.push_back(linkdef);
4545 }
4546
4547 modGen.ParseArgs(pcmArgs);
4548
4550 // Forward the -I, -D, -U
4551 for (const std::string & inclPath : modGen.GetIncludePaths()) {
4552 interp.AddIncludePath(inclPath);
4553 }
4554 std::stringstream definesUndefinesStr;
4555 modGen.WritePPDefines(definesUndefinesStr);
4556 modGen.WritePPUndefines(definesUndefinesStr);
4557 if (!definesUndefinesStr.str().empty()) {
4558 if (interp.declare(definesUndefinesStr.str()) != cling::Interpreter::kSuccess) {
4559 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -D, -U flags as preprocessor directives:\n%s", definesUndefinesStr.str().c_str());
4560 return 1;
4561 }
4562 }
4563 }
4564
4565 if (!InjectModuleUtilHeader(executableFileName, modGen, interp, true)
4566 || !InjectModuleUtilHeader(executableFileName, modGen, interp, false)) {
4567 return 1;
4568 }
4569
4570 if (linkdef.empty()) {
4571 // Generate autolinkdef
4572 GenerateLinkdef(gOptDictionaryHeaderFiles, interpPragmaSource);
4573 }
4574
4575 // Check if code goes to stdout or rootcling file
4576 std::ofstream fileout;
4577 string main_dictname(gOptDictionaryFileName.getValue());
4578 std::ostream *splitDictStream = nullptr;
4579 std::unique_ptr<std::ostream> splitDeleter(nullptr);
4580 // Store the temp files
4581 tempFileNamesCatalog tmpCatalog;
4583 if (!gOptDictionaryFileName.empty()) {
4584 tmpCatalog.addFileName(gOptDictionaryFileName.getValue());
4585 fileout.open(gOptDictionaryFileName.c_str());
4586 if (!fileout) {
4587 ROOT::TMetaUtils::Error(nullptr, "rootcling: failed to open %s in main\n",
4588 gOptDictionaryFileName.c_str());
4589 return 1;
4590 }
4591 }
4592 }
4593
4594 std::ostream &dictStream = (!gOptIgnoreExistingDict && !gOptDictionaryFileName.empty()) ? fileout : std::cout;
4595 bool isACLiC = gOptDictionaryFileName.getValue().find("_ACLiC_dict") != std::string::npos;
4596
4598 // Now generate a second stream for the split dictionary if it is necessary
4599 if (gOptSplit) {
4600 splitDictStream = CreateStreamPtrForSplitDict(gOptDictionaryFileName.getValue(), tmpCatalog);
4601 splitDeleter.reset(splitDictStream);
4602 } else {
4603 splitDictStream = &dictStream;
4604 }
4605
4606 size_t dh = main_dictname.rfind('.');
4607 if (dh != std::string::npos) {
4608 main_dictname.erase(dh);
4609 }
4610 // Need to replace all the characters not allowed in a symbol ...
4611 std::string main_dictname_copy(main_dictname);
4612 TMetaUtils::GetCppName(main_dictname, main_dictname_copy.c_str());
4613
4614 CreateDictHeader(dictStream, main_dictname);
4615 if (gOptSplit)
4616 CreateDictHeader(*splitDictStream, main_dictname);
4617
4618 if (!gOptNoGlobalUsingStd) {
4619 // ACLiC'ed macros might rely on `using namespace std` in front of user headers
4620 if (isACLiC) {
4621 AddNamespaceSTDdeclaration(dictStream);
4622 if (gOptSplit) {
4623 AddNamespaceSTDdeclaration(*splitDictStream);
4624 }
4625 }
4626 }
4627 }
4628
4629 //---------------------------------------------------------------------------
4630 // Parse the linkdef or selection.xml file.
4631 /////////////////////////////////////////////////////////////////////////////
4632
4633 string linkdefFilename;
4634 if (linkdef.empty()) {
4635 linkdefFilename = "in memory";
4636 } else {
4637 bool found = Which(interp, linkdef.c_str(), linkdefFilename);
4638 if (!found) {
4639 ROOT::TMetaUtils::Error(nullptr, "%s: cannot open linkdef file %s\n", executableFileName, linkdef.c_str());
4640 return 1;
4641 }
4642 }
4643
4644 // Exclude string not to re-generate the dictionary
4645 std::vector<std::pair<std::string, std::string>> namesForExclusion;
4646 if (!gBuildingROOT) {
4647 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::name, "std::string"));
4648 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::pattern, "ROOT::Meta::Selection*"));
4649 }
4650
4651 SelectionRules selectionRules(interp, normCtxt, namesForExclusion);
4652
4653 std::string extraIncludes;
4654
4655 ROOT::TMetaUtils::RConstructorTypes constructorTypes;
4656
4657 // Select using DictSelection
4658 const unsigned int selRulesInitialSize = selectionRules.Size();
4659 if (dictSelection && !gOptGeneratePCH)
4660 ROOT::Internal::DictSelectionReader dictSelReader(interp, selectionRules, CI->getASTContext(), normCtxt);
4661
4662 bool dictSelRulesPresent = selectionRules.Size() > selRulesInitialSize;
4663
4664 bool isSelXML = IsSelectionXml(linkdefFilename.c_str());
4665
4666 int rootclingRetCode(0);
4667
4668 if (linkdef.empty()) {
4669 // There is no linkdef file, we added the 'default' #pragma to
4670 // interpPragmaSource.
4671
4672 LinkdefReader ldefr(interp, constructorTypes);
4673 clingArgs.push_back("-Ietc/cling/cint"); // For multiset and multimap
4674
4675 if (!ldefr.Parse(selectionRules, interpPragmaSource, clingArgs,
4676 llvmResourceDir.c_str())) {
4677 ROOT::TMetaUtils::Error(nullptr, "Parsing #pragma failed %s\n", linkdefFilename.c_str());
4678 rootclingRetCode += 1;
4679 } else {
4680 ROOT::TMetaUtils::Info(nullptr, "#pragma successfully parsed.\n");
4681 }
4682
4683 if (!ldefr.LoadIncludes(extraIncludes)) {
4684 ROOT::TMetaUtils::Error(nullptr, "Error loading the #pragma extra_include.\n");
4685 return 1;
4686 }
4687
4688 } else if (isSelXML) {
4689
4691
4692 std::ifstream file(linkdefFilename.c_str());
4693 if (file.is_open()) {
4694 ROOT::TMetaUtils::Info(nullptr, "Selection XML file\n");
4695
4696 XMLReader xmlr(interp);
4697 if (!xmlr.Parse(linkdefFilename.c_str(), selectionRules)) {
4698 ROOT::TMetaUtils::Error(nullptr, "Parsing XML file %s\n", linkdefFilename.c_str());
4699 return 1; // Return here to propagate the failure up to the build system
4700 } else {
4701 ROOT::TMetaUtils::Info(nullptr, "XML file successfully parsed\n");
4702 }
4703 file.close();
4704 } else {
4705 ROOT::TMetaUtils::Error(nullptr, "XML file %s couldn't be opened!\n", linkdefFilename.c_str());
4706 }
4707
4708 } else if (ROOT::TMetaUtils::IsLinkdefFile(linkdefFilename.c_str())) {
4709
4710 std::ifstream file(linkdefFilename.c_str());
4711 if (file.is_open()) {
4712 ROOT::TMetaUtils::Info(nullptr, "Using linkdef file: %s\n", linkdefFilename.c_str());
4713 file.close();
4714 } else {
4715 ROOT::TMetaUtils::Error(nullptr, "Linkdef file %s couldn't be opened!\n", linkdefFilename.c_str());
4716 }
4717
4719
4720 LinkdefReader ldefr(interp, constructorTypes);
4721 clingArgs.push_back("-Ietc/cling/cint"); // For multiset and multimap
4722
4723 if (!ldefr.Parse(selectionRules, interpPragmaSource, clingArgs,
4724 llvmResourceDir.c_str())) {
4725 ROOT::TMetaUtils::Error(nullptr, "Parsing Linkdef file %s\n", linkdefFilename.c_str());
4726 rootclingRetCode += 1;
4727 } else {
4728 ROOT::TMetaUtils::Info(nullptr, "Linkdef file successfully parsed.\n");
4729 }
4730
4731 if (! ldefr.LoadIncludes(extraIncludes)) {
4732 ROOT::TMetaUtils::Error(nullptr, "Error loading the #pragma extra_include.\n");
4733 return 1;
4734 }
4735
4736 } else {
4737
4738 ROOT::TMetaUtils::Error(nullptr, "Unrecognized selection file: %s\n", linkdefFilename.c_str());
4739
4740 }
4741
4742 // Speed up the operations with rules
4743 selectionRules.FillCache();
4744 selectionRules.Optimize();
4745
4746 if (isGenreflex){
4747 if (0 != selectionRules.CheckDuplicates()){
4748 return 1;
4749 }
4750 }
4751
4752 // If we want to validate the selection only, we just quit.
4754 return 0;
4755
4756 //---------------------------------------------------------------------------
4757 // Write schema evolution related headers and declarations
4758 /////////////////////////////////////////////////////////////////////////////
4759
4760 if ((!ROOT::gReadRules.empty() || !ROOT::gReadRawRules.empty()) && !gOptIgnoreExistingDict) {
4761 dictStream << "#include \"TBuffer.h\"\n"
4762 << "#include \"TVirtualObject.h\"\n"
4763 << "#include <vector>\n"
4764 << "#include \"TSchemaHelper.h\"\n\n";
4765
4766 std::list<std::string> includes;
4767 GetRuleIncludes(includes);
4768 for (auto & incFile : includes) {
4769 dictStream << "#include <" << incFile << ">" << std::endl;
4770 }
4771 dictStream << std::endl;
4772 }
4773
4774 selectionRules.SearchNames(interp);
4775
4776 int scannerVerbLevel = 0;
4777 {
4778 using namespace ROOT::TMetaUtils;
4779 scannerVerbLevel = GetErrorIgnoreLevel() == kInfo; // 1 if true, 0 if false
4780 if (isGenreflex){
4781 scannerVerbLevel = GetErrorIgnoreLevel() < kWarning;
4782 }
4783 }
4784
4785 // Select the type of scan
4786 auto scanType = RScanner::EScanType::kNormal;
4787 if (gOptGeneratePCH)
4789 if (dictSelection)
4791
4792 RScanner scan(selectionRules,
4793 scanType,
4794 interp,
4795 normCtxt,
4796 scannerVerbLevel);
4797
4798 // If needed initialize the autoloading hook
4799 if (!gOptLibListPrefix.empty()) {
4802 }
4803
4804 scan.Scan(CI->getASTContext());
4805
4806 bool has_input_error = false;
4807
4809 selectionRules.PrintSelectionRules();
4810
4812 !gOptGeneratePCH &&
4813 !dictSelRulesPresent &&
4814 !selectionRules.AreAllSelectionRulesUsed()) {
4815 ROOT::TMetaUtils::Warning(nullptr, "Not all selection rules are used!\n");
4816 }
4817
4818 if (!gOptGeneratePCH){
4819 rootclingRetCode += CheckForUnsupportedClasses(scan.fSelectedClasses);
4820 if (rootclingRetCode) return rootclingRetCode;
4821 }
4822
4823 // SELECTION LOOP
4824 // Check for error in the class layout before doing anything else.
4825 for (auto const & annRcd : scan.fSelectedClasses) {
4826 if (ROOT::TMetaUtils::ClassInfo__HasMethod(annRcd, "Streamer", interp)) {
4827 if (annRcd.RequestNoInputOperator()) {
4828 int version = ROOT::TMetaUtils::GetClassVersion(annRcd, interp);
4829 if (version != 0) {
4830 // Only Check for input operator is the object is I/O has
4831 // been requested.
4832 has_input_error |= CheckInputOperator(annRcd, interp);
4833 }
4834 }
4835 }
4836 has_input_error |= !CheckClassDef(*annRcd, interp);
4837 }
4838
4839 if (has_input_error) {
4840 // Be a little bit makefile friendly and remove the dictionary in case of error.
4841 // We could add an option -k to keep the file even in case of error.
4842 exit(1);
4843 }
4844
4845 //---------------------------------------------------------------------------
4846 // Write all the necessary #include
4847 /////////////////////////////////////////////////////////////////////////////
4849 for (auto &&includedFromLinkdef : filesIncludedByLinkdef) {
4850 includeForSource += "#include \"" + includedFromLinkdef + "\"\n";
4851 }
4852 }
4853
4854 if (!gOptGeneratePCH) {
4856 GenerateNecessaryIncludes(dictStream, includeForSource, extraIncludes);
4857 if (gOptSplit) {
4858 GenerateNecessaryIncludes(*splitDictStream, includeForSource, extraIncludes);
4859 }
4860 }
4861 if (!gOptNoGlobalUsingStd) {
4862 // ACLiC'ed macros might have relied on `using namespace std` in front of user headers
4863 if (!isACLiC) {
4864 AddNamespaceSTDdeclaration(dictStream);
4865 if (gOptSplit) {
4866 AddNamespaceSTDdeclaration(*splitDictStream);
4867 }
4868 }
4869 }
4872 }
4873
4874 // The order of addition to the list of constructor type
4875 // is significant. The list is sorted by with the highest
4876 // priority first.
4877 if (!gOptInterpreterOnly) {
4878 constructorTypes.emplace_back("TRootIOCtor", interp);
4879 constructorTypes.emplace_back("__void__", interp); // ROOT-7723
4880 constructorTypes.emplace_back("", interp);
4881 }
4882 }
4884 AddNamespaceSTDdeclaration(dictStream);
4885
4886 if (gOptSplit && splitDictStream) {
4887 AddNamespaceSTDdeclaration(*splitDictStream);
4888 }
4889 }
4890
4891 if (gOptGeneratePCH) {
4892 AnnotateAllDeclsForPCH(interp, scan);
4893 } else if (gOptInterpreterOnly) {
4894 rootclingRetCode += CheckClassesForInterpreterOnlyDicts(interp, scan);
4895 // generate an empty pcm nevertheless for consistency
4896 // Negate as true is 1 and true is returned in case of success.
4898 rootclingRetCode += FinalizeStreamerInfoWriting(interp);
4899 }
4900 } else {
4901 rootclingRetCode += GenerateFullDict(*splitDictStream,
4902 interp,
4903 scan,
4904 constructorTypes,
4905 gOptSplit,
4906 isGenreflex,
4908 }
4909
4910 if (rootclingRetCode != 0) {
4911 return rootclingRetCode;
4912 }
4913
4914 // Now we have done all our looping and thus all the possible
4915 // annotation, let's write the pcms.
4916 HeadersDeclsMap_t headersClassesMap;
4917 HeadersDeclsMap_t headersDeclsMap;
4919 const std::string fwdDeclnArgsToKeepString(GetFwdDeclnArgsToKeepString(normCtxt, interp));
4920
4922 scan.fSelectedTypedefs,
4923 scan.fSelectedFunctions,
4924 scan.fSelectedVariables,
4925 scan.fSelectedEnums,
4926 headersClassesMap,
4927 headersDeclsMap,
4928 interp);
4929
4930 std::string detectedUmbrella;
4931 for (auto & arg : pcmArgs) {
4933 detectedUmbrella = arg;
4934 break;
4935 }
4936 }
4937
4939 headersDeclsMap.clear();
4940 }
4941
4942
4943 std::string headersClassesMapString = "\"\"";
4944 std::string fwdDeclsString = "\"\"";
4945 if (!gOptCxxModule) {
4946 headersClassesMapString = GenerateStringFromHeadersForClasses(headersDeclsMap,
4947 detectedUmbrella,
4948 true);
4951 fwdDeclsString = GenerateFwdDeclString(scan, interp);
4952 }
4953 }
4954 modGen.WriteRegistrationSource(dictStream, fwdDeclnArgsToKeepString, headersClassesMapString, fwdDeclsString,
4955 extraIncludes, gOptCxxModule);
4956 // If we just want to inline the input header, we don't need
4957 // to generate any files.
4958 if (!gOptInlineInput) {
4959 // Write the module/PCH depending on what mode we are on
4960 if (modGen.IsPCH()) {
4961 if (!GenerateAllDict(modGen, CI, currentDirectory)) return 1;
4962 } else if (gOptCxxModule) {
4963 if (!CheckModuleValid(modGen, llvmResourceDir, interp, linkdefFilename, moduleName.str()))
4964 return 1;
4965 }
4966 }
4967 }
4968
4969
4970 if (!gOptLibListPrefix.empty()) {
4971 string liblist_filename = gOptLibListPrefix + ".out";
4972
4973 ofstream outputfile(liblist_filename.c_str(), ios::out);
4974 if (!outputfile) {
4975 ROOT::TMetaUtils::Error(nullptr, "%s: Unable to open output lib file %s\n",
4976 executableFileName, liblist_filename.c_str());
4977 } else {
4978 const size_t endStr = gLibsNeeded.find_last_not_of(" \t");
4979 outputfile << gLibsNeeded.substr(0, endStr + 1) << endl;
4980 // Add explicit delimiter
4981 outputfile << "# Now the list of classes\n";
4982 // SELECTION LOOP
4983 for (auto const & annRcd : scan.fSelectedClasses) {
4984 // Shouldn't it be GetLong64_Name( cl_input.GetNormalizedName() )
4985 // or maybe we should be normalizing to turn directly all long long into Long64_t
4986 outputfile << annRcd.GetNormalizedName() << endl;
4987 }
4988 }
4989 }
4990
4991 // Check for errors in module generation
4992 rootclingRetCode += modGen.GetErrorCount();
4993 if (0 != rootclingRetCode) return rootclingRetCode;
4994
4995 // Create the rootmap file
4996 std::string rootmapLibName = std::accumulate(gOptRootmapLibNames.begin(),
4997 gOptRootmapLibNames.end(),
4998 std::string(),
4999 [](const std::string & a, const std::string & b) -> std::string {
5000 if (a.empty()) return b;
5001 else return a + " " + b;
5002 });
5003
5004 bool rootMapNeeded = !gOptRootMapFileName.empty() || !rootmapLibName.empty();
5005
5006 std::list<std::string> classesNames;
5007 std::list<std::string> classesNamesForRootmap;
5008 std::list<std::string> classesDefsList;
5009
5010 rootclingRetCode = ExtractClassesListAndDeclLines(scan,
5011 classesNames,
5012 classesNamesForRootmap,
5013 classesDefsList,
5014 interp);
5015
5016 std::list<std::string> enumNames;
5017 rootclingRetCode += ExtractAutoloadKeys(enumNames,
5018 scan.fSelectedEnums,
5019 interp);
5020
5021 std::list<std::string> varNames;
5022 rootclingRetCode += ExtractAutoloadKeys(varNames,
5023 scan.fSelectedVariables,
5024 interp);
5025
5026 if (0 != rootclingRetCode) return rootclingRetCode;
5027
5028 // Create the rootmapfile if needed
5029 if (rootMapNeeded) {
5030
5031 std::list<std::string> nsNames;
5032
5033 ExtractSelectedNamespaces(scan, nsNames);
5034
5036 rootmapLibName);
5037
5038 ROOT::TMetaUtils::Info(nullptr, "Rootmap file name %s and lib name(s) \"%s\"\n",
5039 gOptRootMapFileName.c_str(),
5040 rootmapLibName.c_str());
5041
5042 tmpCatalog.addFileName(gOptRootMapFileName);
5043 std::unordered_set<std::string> headersToIgnore;
5044 if (gOptInlineInput)
5045 for (const std::string& optHeaderFileName : gOptDictionaryHeaderFiles)
5046 headersToIgnore.insert(optHeaderFileName.c_str());
5047
5048 std::list<std::string> typedefsRootmapLines;
5049 rootclingRetCode += ExtractAutoloadKeys(typedefsRootmapLines,
5050 scan.fSelectedTypedefs,
5051 interp);
5052
5053 rootclingRetCode += CreateNewRootMapFile(gOptRootMapFileName,
5054 rootmapLibName,
5055 classesDefsList,
5056 classesNamesForRootmap,
5057 nsNames,
5058 typedefsRootmapLines,
5059 enumNames,
5060 varNames,
5061 headersClassesMap,
5062 headersToIgnore);
5063
5064 if (0 != rootclingRetCode) return 1;
5065 }
5066
5068 tmpCatalog.dump();
5069
5070 // Manually call end of translation unit because we never call the
5071 // appropriate deconstructors in the interpreter. This writes out the C++
5072 // module file that we currently generate.
5073 {
5074 cling::Interpreter::PushTransactionRAII RAII(&interp);
5075 CI->getSema().getASTConsumer().HandleTranslationUnit(CI->getSema().getASTContext());
5076 }
5077
5078 // Add the warnings
5079 rootclingRetCode += ROOT::TMetaUtils::GetNumberOfErrors();
5080
5081 // make sure the file is closed before committing
5082 fileout.close();
5083
5084 // Before returning, rename the files if no errors occurred
5085 // otherwise clean them to avoid remnants (see ROOT-10015)
5086 if(rootclingRetCode == 0) {
5087 rootclingRetCode += tmpCatalog.commit();
5088 } else {
5089 tmpCatalog.clean();
5090 }
5091
5092 return rootclingRetCode;
5093
5094}
5095
5096namespace genreflex {
5097
5098////////////////////////////////////////////////////////////////////////////////
5099/// Loop on arguments: stop at the first which starts with -
5100
5101 unsigned int checkHeadersNames(std::vector<std::string> &headersNames)
5102 {
5103 unsigned int numberOfHeaders = 0;
5104 for (std::vector<std::string>::iterator it = headersNames.begin();
5105 it != headersNames.end(); ++it) {
5106 const std::string headername(*it);
5107 if (ROOT::TMetaUtils::IsHeaderName(headername)) {
5108 numberOfHeaders++;
5109 } else {
5111 "*** genreflex: %s is not a valid header name (.h and .hpp extensions expected)!\n",
5112 headername.c_str());
5113 }
5114 }
5115 return numberOfHeaders;
5116 }
5117
5118////////////////////////////////////////////////////////////////////////////////
5119/// Extract the arguments from the command line
5120
5121 unsigned int extractArgs(int argc, char **argv, std::vector<std::string> &args)
5122 {
5123 // loop on argv, spot strings which are not preceded by something
5124 unsigned int argvCounter = 0;
5125 for (int i = 1; i < argc; ++i) {
5126 if (!ROOT::TMetaUtils::BeginsWith(argv[i - 1], "-") && // so, if preceding element starts with -, this is a value for an option
5127 !ROOT::TMetaUtils::BeginsWith(argv[i], "-")) { // and the element itself is not an option
5128 args.push_back(argv[i]);
5129 argvCounter++;
5130 } else if (argvCounter) {
5131 argv[i - argvCounter] = argv[i];
5132 }
5133 }
5134
5135 // Some debug
5136 if (genreflex::verbose) {
5137 int i = 0;
5138 std::cout << "Args: \n";
5139 for (std::vector<std::string>::iterator it = args.begin();
5140 it < args.end(); ++it) {
5141 std::cout << i << ") " << *it << std::endl;
5142 ++i;
5143 }
5144
5145 }
5146
5147 return argvCounter;
5148 }
5149
5150////////////////////////////////////////////////////////////////////////////////
5151
5152 void changeExtension(std::string &filename, const std::string &newExtension)
5153 {
5154 size_t result = filename.find_last_of('.');
5155 if (std::string::npos != result) {
5156 filename.erase(result);
5157 filename.append(newExtension);
5158 }
5159
5160 }
5161
5162////////////////////////////////////////////////////////////////////////////////
5163/// The caller is responsible for deleting the string!
5164
5165 char *string2charptr(const std::string &str)
5166 {
5167 const unsigned int size(str.size());
5168 char *a = new char[size + 1];
5169 a[size] = 0;
5170 memcpy(a, str.c_str(), size);
5171 return a;
5172 }
5173
5174////////////////////////////////////////////////////////////////////////////////
5175/// Replace the extension with "_rflx.cpp"
5176
5177 void header2outputName(std::string &fileName)
5178 {
5179 changeExtension(fileName, "_rflx.cpp");
5180 }
5181
5182////////////////////////////////////////////////////////////////////////////////
5183/// Get a proper name for the output file
5184
5185 void headers2outputsNames(const std::vector<std::string> &headersNames,
5186 std::vector<std::string> &ofilesnames)
5187 {
5188 ofilesnames.reserve(headersNames.size());
5189
5190 for (std::vector<std::string>::const_iterator it = headersNames.begin();
5191 it != headersNames.end(); ++it) {
5192 std::string ofilename(*it);
5193 header2outputName(ofilename);
5194 ofilesnames.push_back(ofilename);
5195 }
5196 }
5197
5198////////////////////////////////////////////////////////////////////////////////
5199
5200 void AddToArgVector(std::vector<char *> &argvVector,
5201 const std::vector<std::string> &argsToBeAdded,
5202 const std::string &optName = "")
5203 {
5204 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5205 it != argsToBeAdded.end(); ++it) {
5206 argvVector.push_back(string2charptr(optName + *it));
5207 }
5208 }
5209
5210////////////////////////////////////////////////////////////////////////////////
5211
5212 void AddToArgVectorSplit(std::vector<char *> &argvVector,
5213 const std::vector<std::string> &argsToBeAdded,
5214 const std::string &optName = "")
5215 {
5216 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5217 it != argsToBeAdded.end(); ++it) {
5218 if (optName.length()) {
5219 argvVector.push_back(string2charptr(optName));
5220 }
5221 argvVector.push_back(string2charptr(*it));
5222 }
5223 }
5224
5225////////////////////////////////////////////////////////////////////////////////
5226
5227 int invokeRootCling(const std::string &verbosity,
5228 const std::string &selectionFileName,
5229 const std::string &targetLibName,
5230 bool multiDict,
5231 const std::vector<std::string> &pcmsNames,
5232 const std::vector<std::string> &includes,
5233 const std::vector<std::string> &preprocDefines,
5234 const std::vector<std::string> &preprocUndefines,
5235 const std::vector<std::string> &warnings,
5236 const std::string &rootmapFileName,
5237 const std::string &rootmapLibName,
5238 bool interpreteronly,
5239 bool doSplit,
5240 bool isCxxmodule,
5241 bool writeEmptyRootPCM,
5242 bool selSyntaxOnly,
5243 bool noIncludePaths,
5244 bool noGlobalUsingStd,
5245 const std::vector<std::string> &headersNames,
5246 bool failOnWarnings,
5247 const std::string &ofilename)
5248 {
5249 // Prepare and invoke the commandline to invoke rootcling
5250
5251 std::vector<char *> argvVector;
5252
5253 argvVector.push_back(string2charptr("rootcling"));
5254 argvVector.push_back(string2charptr(verbosity));
5255 argvVector.push_back(string2charptr("-f"));
5256 argvVector.push_back(string2charptr(ofilename));
5257
5258 if (isCxxmodule)
5259 argvVector.push_back(string2charptr("-cxxmodule"));
5260
5261 // Extract the path to the dictionary
5262 std::string dictLocation;
5263 ExtractFilePath(ofilename, dictLocation);
5264
5265 // Rootmaps
5266
5267 // Prepare the correct rootmap libname if not already set.
5268 std::string newRootmapLibName(rootmapLibName);
5269 if (!rootmapFileName.empty() && newRootmapLibName.empty()) {
5270 if (headersNames.size() != 1) {
5272 "*** genreflex: No rootmap lib and several header specified!\n");
5273 }
5274 std::string cleanHeaderName = ExtractFileName(headersNames[0]);
5275 newRootmapLibName = "lib";
5276 newRootmapLibName += cleanHeaderName;
5277 changeExtension(newRootmapLibName, gLibraryExtension);
5278 }
5279
5280 // Prepend to the rootmap the designed directory of the dictionary
5281 // if no path is specified for the rootmap itself
5282 std::string newRootmapFileName(rootmapFileName);
5283 if (!newRootmapFileName.empty() && !HasPath(newRootmapFileName)) {
5284 newRootmapFileName = dictLocation + newRootmapFileName;
5285 }
5286
5287
5288 // RootMap filename
5289 if (!newRootmapFileName.empty()) {
5290 argvVector.push_back(string2charptr("-rmf"));
5291 argvVector.push_back(string2charptr(newRootmapFileName));
5292 }
5293
5294 // RootMap Lib filename
5295 if (!newRootmapLibName.empty()) {
5296 argvVector.push_back(string2charptr("-rml"));
5297 argvVector.push_back(string2charptr(newRootmapLibName));
5298 }
5299
5300 // Interpreter only dictionaries
5301 if (interpreteronly)
5302 argvVector.push_back(string2charptr("-interpreteronly"));
5303
5304 // Split dictionaries
5305 if (doSplit)
5306 argvVector.push_back(string2charptr("-split"));
5307
5308 // Targetlib
5309 if (!targetLibName.empty()) {
5310 argvVector.push_back(string2charptr("-s"));
5311 argvVector.push_back(string2charptr(targetLibName));
5312 }
5313
5314 // Multidict support
5315 if (multiDict)
5316 argvVector.push_back(string2charptr("-multiDict"));
5317
5318 // Don't declare "using namespace std"
5319 if (noGlobalUsingStd)
5320 argvVector.push_back(string2charptr("-noGlobalUsingStd"));
5321
5322
5323 AddToArgVectorSplit(argvVector, pcmsNames, "-m");
5324
5325 // Inline the input header
5326 argvVector.push_back(string2charptr("-inlineInputHeader"));
5327
5328 // Write empty root pcms
5329 if (writeEmptyRootPCM)
5330 argvVector.push_back(string2charptr("-writeEmptyRootPCM"));
5331
5332 // Just test the syntax of the selection file
5333 if (selSyntaxOnly)
5334 argvVector.push_back(string2charptr("-selSyntaxOnly"));
5335
5336 // No include paths
5337 if (noIncludePaths)
5338 argvVector.push_back(string2charptr("-noIncludePaths"));
5339
5340 // Fail on warnings
5341 if (failOnWarnings)
5342 argvVector.push_back(string2charptr("-failOnWarnings"));
5343
5344 // Clingargs
5345 AddToArgVector(argvVector, includes, "-I");
5346 AddToArgVector(argvVector, preprocDefines, "-D");
5347 AddToArgVector(argvVector, preprocUndefines, "-U");
5348 AddToArgVector(argvVector, warnings, "-W");
5349
5350 AddToArgVector(argvVector, headersNames);
5351
5352 if (!selectionFileName.empty()) {
5353 argvVector.push_back(string2charptr(selectionFileName));
5354 }
5355
5356 const int argc = argvVector.size();
5357
5358 // Output commandline for rootcling
5359 if (genreflex::verbose) {
5360 std::cout << "Rootcling commandline:\n";
5361 for (int i = 0; i < argc; i++)
5362 std::cout << i << ") " << argvVector[i] << std::endl;
5363 }
5364
5365 char **argv = & (argvVector[0]);
5366 int rootclingReturnCode = RootClingMain(argc,
5367 argv,
5368 /*isGenReflex=*/true);
5369
5370 for (int i = 0; i < argc; i++)
5371 delete [] argvVector[i];
5372
5373 return rootclingReturnCode;
5374
5375 }
5376
5377////////////////////////////////////////////////////////////////////////////////
5378/// Get the right ofilenames and invoke several times rootcling
5379/// One invokation per header
5380
5381 int invokeManyRootCling(const std::string &verbosity,
5382 const std::string &selectionFileName,
5383 const std::string &targetLibName,
5384 bool multiDict,
5385 const std::vector<std::string> &pcmsNames,
5386 const std::vector<std::string> &includes,
5387 const std::vector<std::string> &preprocDefines,
5388 const std::vector<std::string> &preprocUndefines,
5389 const std::vector<std::string> &warnings,
5390 const std::string &rootmapFileName,
5391 const std::string &rootmapLibName,
5392 bool interpreteronly,
5393 bool doSplit,
5394 bool isCxxmodule,
5395 bool writeEmptyRootPCM,
5396 bool selSyntaxOnly,
5397 bool noIncludePaths,
5398 bool noGlobalUsingStd,
5399 const std::vector<std::string> &headersNames,
5400 bool failOnWarnings,
5401 const std::string &outputDirName_const = "")
5402 {
5403 std::string outputDirName(outputDirName_const);
5404
5405 std::vector<std::string> ofilesNames;
5406 headers2outputsNames(headersNames, ofilesNames);
5407
5408 if (!outputDirName.empty() && !ROOT::TMetaUtils::EndsWith(outputDirName, gPathSeparator)) {
5409 outputDirName += gPathSeparator;
5410 }
5411
5412 std::vector<std::string> namesSingleton(1);
5413 for (unsigned int i = 0; i < headersNames.size(); ++i) {
5414 namesSingleton[0] = headersNames[i];
5415 std::string ofilenameFullPath(ofilesNames[i]);
5416 if (llvm::sys::path::parent_path(ofilenameFullPath) == "")
5417 ofilenameFullPath = outputDirName + ofilenameFullPath;
5418 int returnCode = invokeRootCling(verbosity,
5419 selectionFileName,
5420 targetLibName,
5421 multiDict,
5422 pcmsNames,
5423 includes,
5424 preprocDefines,
5425 preprocUndefines,
5426 warnings,
5427 rootmapFileName,
5428 rootmapLibName,
5429 interpreteronly,
5430 doSplit,
5431 isCxxmodule,
5432 writeEmptyRootPCM,
5433 selSyntaxOnly,
5434 noIncludePaths,
5435 noGlobalUsingStd,
5436 namesSingleton,
5437 failOnWarnings,
5438 ofilenameFullPath);
5439 if (returnCode != 0)
5440 return returnCode;
5441 }
5442
5443 return 0;
5444 }
5445
5446
5447} // end genreflex namespace
5448
5449////////////////////////////////////////////////////////////////////////////////
5450/// Extract from options multiple values with the same option
5451
5452int extractMultipleOptions(std::vector<ROOT::option::Option> &options,
5453 int oIndex,
5454 std::vector<std::string> &values)
5455{
5456 int nValues = 0;
5457 if (options[oIndex]) {
5458 const int nVals = options[oIndex].count();
5459 values.reserve(nVals);
5460 int optionIndex = 0;
5461 for (ROOT::option::Option *opt = options[oIndex]; opt; opt = opt->next()) {
5462 if (genreflex::verbose) std::cout << "Extracting multiple args: "
5463 << optionIndex << "/" << nVals << " "
5464 << opt->arg << std::endl;
5465 optionIndex++;
5466 values.push_back(opt->arg);
5467 nValues++;
5468 }
5469 }
5470 return nValues;
5471}
5472
5473////////////////////////////////////////////////////////////////////////////////
5474
5475void RiseWarningIfPresent(std::vector<ROOT::option::Option> &options,
5476 int optionIndex,
5477 const char *descriptor)
5478{
5479 if (options[optionIndex]) {
5481 "*** genereflex: %s is not supported anymore.\n",
5482 descriptor);
5483 }
5484}
5485
5486////////////////////////////////////////////////////////////////////////////////
5487
5488bool IsGoodLibraryName(const std::string &name)
5489{
5490
5491
5493#ifdef __APPLE__
5494 isGood |= ROOT::TMetaUtils::EndsWith(name, ".dylib");
5495#endif
5496 return isGood;
5497}
5498
5499////////////////////////////////////////////////////////////////////////////////
5500/// Translate the arguments of genreflex into rootcling ones and forward them
5501/// to the RootCling function.
5502/// These are two typical genreflex and rootcling commandlines
5503/// 1) genreflex header1.h [header2.h ...] [options] [preprocessor options]
5504/// 2) rootcling [-v] [-v0-4] [-f] [out.cxx] [-s sharedlib.so] [-m pcmfilename]
5505/// header1.h[{+,-}][!] ..headerN.h[{+,-}][!] [{LinkDef.h,selectionRules.xml}]
5506/// The rules with which the arguments are translated are (1st column genreflex):
5507/// --debug -v4
5508/// --quiet -v0
5509