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