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