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