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