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