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