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) {
715 ParseRootMapFileNewFormat(file, autoloads);
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 isSelXML,
2661 bool writeEmptyRootPCM)
2662{
2663 ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper());
2664
2665 bool needsCollectionProxy = false;
2666
2667 //
2668 // We will loop over all the classes several times.
2669 // In order we will call
2670 //
2671 // WriteClassInit (code to create the TGenericClassInfo)
2672 // check for constructor and operator input
2673 // WriteClassFunctions (declared in ClassDef)
2674 // WriteClassCode (Streamer,ShowMembers,Auxiliary functions)
2675 //
2676
2677
2678 //
2679 // Loop over all classes and create Streamer() & Showmembers() methods
2680 //
2681
2682 // SELECTION LOOP
2683 for (auto const & ns : scan.fSelectedNamespaces) {
2685 WriteNamespaceInit(ns, interp, dictStream);
2686 }
2687 auto nsName = ns.GetNamespaceDecl()->getQualifiedNameAsString();
2688 if (nsName.find("(anonymous)") == std::string::npos)
2689 EmitStreamerInfo(nsName.c_str());
2690 }
2691
2692 for (auto const & selClass : scan.fSelectedClasses) {
2693 if (!selClass.GetRecordDecl()->isCompleteDefinition()) {
2694 ROOT::TMetaUtils::Error(nullptr, "A dictionary has been requested for %s but there is no declaration!\n", ROOT::TMetaUtils::GetQualifiedName(selClass).c_str());
2695 continue;
2696 }
2697 if (selClass.RequestOnlyTClass()) {
2698 // fprintf(stderr,"rootcling: Skipping class %s\n",R__GetQualifiedName(* selClass.GetRecordDecl()).c_str());
2699 // For now delay those for later.
2700 continue;
2701 }
2702
2703 // Very important: here we decide if we want to attach attributes to the decl.
2704
2705 if (clang::CXXRecordDecl *CXXRD =
2706 llvm::dyn_cast<clang::CXXRecordDecl>(const_cast<clang::RecordDecl *>(selClass.GetRecordDecl()))) {
2707 AnnotateDecl(*CXXRD, scan.GetDeclsSelRulesMap() , interp, isSelXML);
2708 }
2709
2710 const clang::CXXRecordDecl *CRD = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2711
2712 if (CRD) {
2713 ROOT::TMetaUtils::Info(nullptr, "Generating code for class %s\n", selClass.GetNormalizedName());
2714 if (TMetaUtils::IsStdClass(*CRD) && 0 != TClassEdit::STLKind(CRD->getName().str() /* unqualified name without template argument */)) {
2715 // Register the collections
2716 // coverity[fun_call_w_exception] - that's just fine.
2717 Internal::RStl::Instance().GenerateTClassFor(selClass.GetNormalizedName(), CRD, interp, normCtxt);
2718 } else if (CRD->getName() == "RVec") {
2719 static const clang::DeclContext *vecOpsDC = nullptr;
2720 if (!vecOpsDC)
2721 vecOpsDC = llvm::dyn_cast<clang::DeclContext>(
2722 interp.getLookupHelper().findScope("ROOT::VecOps", cling::LookupHelper::NoDiagnostics));
2723 if (vecOpsDC && vecOpsDC->Equals(CRD->getDeclContext())) {
2724 // Register the collections
2725 // coverity[fun_call_w_exception] - that's just fine.
2726 Internal::RStl::Instance().GenerateTClassFor(selClass.GetNormalizedName(), CRD, interp, normCtxt);
2727 }
2728 } else {
2730 ROOT::TMetaUtils::WriteClassInit(dictStream, selClass, CRD, interp, normCtxt, ctorTypes,
2731 needsCollectionProxy);
2732 }
2733 EmitStreamerInfo(selClass.GetNormalizedName());
2734 }
2735 }
2736 }
2737
2738 //
2739 // Write all TBuffer &operator>>(...), Class_Name(), Dictionary(), etc.
2740 // first to allow template specialisation to occur before template
2741 // instantiation (STK)
2742 //
2743 // SELECTION LOOP
2744 for (auto const & selClass : scan.fSelectedClasses) {
2745
2746 if (!selClass.GetRecordDecl()->isCompleteDefinition() || selClass.RequestOnlyTClass()) {
2747 // For now delay those for later.
2748 continue;
2749 }
2750 const clang::CXXRecordDecl *cxxdecl = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2751 if (cxxdecl && ROOT::TMetaUtils::ClassInfo__HasMethod(selClass, "Class_Name", interp) && !gOptIgnoreExistingDict) {
2752 WriteClassFunctions(cxxdecl, dictStream, isSplit);
2753 }
2754 }
2755
2756 // LINKDEF SELECTION LOOP
2757 // Loop to get the shadow class for the class marked 'RequestOnlyTClass' (but not the
2758 // STL class which is done via Internal::RStl::Instance().WriteClassInit(0);
2759 // and the ClassInit
2760
2761 for (auto const & selClass : scan.fSelectedClasses) {
2762 if (!selClass.GetRecordDecl()->isCompleteDefinition() || !selClass.RequestOnlyTClass()) {
2763 continue;
2764 }
2765
2766 const clang::CXXRecordDecl *CRD = llvm::dyn_cast<clang::CXXRecordDecl>(selClass.GetRecordDecl());
2767
2768 if (!ROOT::TMetaUtils::IsSTLContainer(selClass)) {
2770 ROOT::TMetaUtils::WriteClassInit(dictStream, selClass, CRD, interp, normCtxt, ctorTypes,
2771 needsCollectionProxy);
2772 }
2773 EmitStreamerInfo(selClass.GetNormalizedName());
2774 }
2775 }
2776 // Loop to write all the ClassCode
2778 for (auto const &selClass : scan.fSelectedClasses) {
2779 // The "isGenreflex" parameter allows the distinction between
2780 // genreflex and rootcling only for the treatment of collections which
2781 // are data members. To preserve the behaviour of the original
2782 // genreflex and rootcling tools, if the selection is performed with
2783 // genreflex, data members with collection type do not trigger the
2784 // selection of the collection type
2786 selClass,
2787 interp,
2788 normCtxt,
2789 dictStream,
2790 ctorTypes,
2791 isGenreflex);
2792 }
2793
2794 // Loop on the registered collections internally
2795 // coverity[fun_call_w_exception] - that's just fine.
2796 ROOT::Internal::RStl::Instance().WriteClassInit(dictStream, interp, normCtxt, ctorTypes, needsCollectionProxy,
2798 }
2799
2803 // Make up for skipping RegisterModule, now that dictionary parsing
2804 // is done and these headers cannot be selected anymore.
2805 int finRetCode = FinalizeStreamerInfoWriting(interp, writeEmptyRootPCM);
2806 if (finRetCode != 0) return finRetCode;
2807 }
2808
2809 return 0;
2810}
2811
2812////////////////////////////////////////////////////////////////////////////////
2813
2814void CreateDictHeader(std::ostream &dictStream, const std::string &main_dictname)
2815{
2816 dictStream << "// Do NOT change. Changes will be lost next time file is generated\n\n"
2817 << "#define R__DICTIONARY_FILENAME " << main_dictname << std::endl
2818
2819 // We do not want deprecation warnings to fire in dictionaries
2820 << "#define R__NO_DEPRECATION" << std::endl
2821
2822 // Now that CINT is not longer there to write the header file,
2823 // write one and include in there a few things for backward
2824 // compatibility.
2825 << "\n/*******************************************************************/\n"
2826 << "#include <stddef.h>\n"
2827 << "#include <stdio.h>\n"
2828 << "#include <stdlib.h>\n"
2829 << "#include <string.h>\n"
2830 << "#include <assert.h>\n"
2831 << "#define G__DICTIONARY\n"
2832 << "#include \"ROOT/RConfig.hxx\"\n"
2833 << "#include \"TClass.h\"\n"
2834 << "#include \"TDictAttributeMap.h\"\n"
2835 << "#include \"TInterpreter.h\"\n"
2836 << "#include \"TROOT.h\"\n"
2837 << "#include \"TBuffer.h\"\n"
2838 << "#include \"TMemberInspector.h\"\n"
2839 << "#include \"TInterpreter.h\"\n"
2840 << "#include \"TVirtualMutex.h\"\n"
2841 << "#include \"TError.h\"\n\n"
2842 << "#ifndef G__ROOT\n"
2843 << "#define G__ROOT\n"
2844 << "#endif\n\n"
2845 << "#include \"RtypesImp.h\"\n"
2846 << "#include \"TIsAProxy.h\"\n"
2847 << "#include \"TFileMergeInfo.h\"\n"
2848 << "#include <algorithm>\n"
2849 << "#include \"TCollectionProxyInfo.h\"\n"
2850 << "/*******************************************************************/\n\n"
2851 << "#include \"TDataMember.h\"\n\n"; // To set their transiency
2852}
2853
2854////////////////////////////////////////////////////////////////////////////////
2855
2856void AddNamespaceSTDdeclaration(std::ostream &dictStream)
2857{
2858 dictStream << "// The generated code does not explicitly qualify STL entities\n"
2859 << "namespace std {} using namespace std;\n\n";
2860}
2861
2862////////////////////////////////////////////////////////////////////////////////
2863
2864void GenerateNecessaryIncludes(std::ostream &dictStream,
2865 const std::string &includeForSource,
2866 const std::string &extraIncludes)
2867{
2868 dictStream << "// Header files passed as explicit arguments\n"
2869 << includeForSource << std::endl
2870 << "// Header files passed via #pragma extra_include\n"
2871 << extraIncludes << std::endl;
2872}
2873
2874//______________________________________________________________________________
2875
2876// cross-compiling for iOS and iOS simulator (assumes host is Intel Mac OS X)
2877#if defined(R__IOSSIM) || defined(R__IOS)
2878#ifdef __x86_64__
2879#undef __x86_64__
2880#endif
2881#ifdef __i386__
2882#undef __i386__
2883#endif
2884#ifdef R__IOSSIM
2885#define __i386__ 1
2886#endif
2887#ifdef R__IOS
2888#define __arm__ 1
2889#endif
2890#endif
2891
2892////////////////////////////////////////////////////////////////////////////////
2893/// Little helper class to bookkeep the files names which we want to make
2894/// temporary.
2895
2897public:
2898 //______________________________________________
2899 tempFileNamesCatalog(): m_size(0), m_emptyString("") {};
2900
2901 std::string getTmpFileName(const std::string &filename) {
2902 return filename + "_tmp_" + std::to_string(getpid());
2903 }
2904 /////////////////////////////////////////////////////////////////////////////
2905 /// Adds the name and the associated temp name to the catalog.
2906 /// Changes the name into the temp name
2907
2908 void addFileName(std::string &nameStr) {
2909 if (nameStr.empty()) return;
2910
2911 std::string tmpNameStr(getTmpFileName(nameStr));
2912
2913 // For brevity
2914 const char *name(nameStr.c_str());
2915 const char *tmpName(tmpNameStr.c_str());
2916
2917 m_names.push_back(nameStr);
2918 m_tempNames.push_back(tmpNameStr);
2919 ROOT::TMetaUtils::Info(nullptr, "File %s added to the tmp catalog.\n", name);
2920
2921 // This is to allow update of existing files
2922 if (0 == std::rename(name , tmpName)) {
2923 ROOT::TMetaUtils::Info(nullptr, "File %s existing. Preserved as %s.\n", name, tmpName);
2924 }
2925
2926 // To change the name to its tmp version
2927 nameStr = tmpNameStr;
2928
2929 m_size++;
2930
2931 }
2932
2933 /////////////////////////////////////////////////////////////////////////////
2934
2935 int clean() {
2936 int retval = 0;
2937 // rename the temp files into the normal ones
2938 for (unsigned int i = 0; i < m_size; ++i) {
2939 const char *tmpName = m_tempNames[i].c_str();
2940 // Check if the file exists
2941 std::ifstream ifile(tmpName);
2942 if (!ifile)
2943 ROOT::TMetaUtils::Error(nullptr, "Cannot find %s!\n", tmpName);
2944 // Make sure the file is closed, mostly for Windows FS, also when
2945 // accessing it from a Linux VM via a shared folder
2946 if (ifile.is_open())
2947 ifile.close();
2948 if (0 != std::remove(tmpName)) {
2949 ROOT::TMetaUtils::Error(nullptr, "Removing %s!\n", tmpName);
2950 retval++;
2951 }
2952 }
2953 return retval;
2954 }
2955
2956 /////////////////////////////////////////////////////////////////////////////
2957
2958 int commit() {
2959 int retval = 0;
2960 // rename the temp files into the normal ones
2961 for (unsigned int i = 0; i < m_size; ++i) {
2962 const char *tmpName = m_tempNames[i].c_str();
2963 const char *name = m_names[i].c_str();
2964 // Check if the file exists
2965 std::ifstream ifile(tmpName);
2966 if (!ifile)
2967 ROOT::TMetaUtils::Error(nullptr, "Cannot find %s!\n", tmpName);
2968 // Make sure the file is closed, mostly for Windows FS, also when
2969 // accessing it from a Linux VM via a shared folder
2970 if (ifile.is_open())
2971 ifile.close();
2972#ifdef WIN32
2973 // Sometimes files cannot be renamed on Windows if they don't have
2974 // been released by the system. So just copy them and try to delete
2975 // the old one afterwards.
2976 if (0 != std::rename(tmpName , name)) {
2977 if (llvm::sys::fs::copy_file(tmpName , name)) {
2978 llvm::sys::fs::remove(tmpName);
2979 }
2980 }
2981#else
2982 if (0 != std::rename(tmpName , name)) {
2983 ROOT::TMetaUtils::Error(nullptr, "Renaming %s into %s!\n", tmpName, name);
2984 retval++;
2985 }
2986#endif
2987 }
2988 return retval;
2989 }
2990
2991 /////////////////////////////////////////////////////////////////////////////
2992
2993 const std::string &getFileName(const std::string &tmpFileName) {
2994 size_t i = std::distance(m_tempNames.begin(),
2995 find(m_tempNames.begin(), m_tempNames.end(), tmpFileName));
2996 if (i == m_tempNames.size()) return m_emptyString;
2997 return m_names[i];
2998 }
2999
3000 /////////////////////////////////////////////////////////////////////////////
3001
3002 void dump() {
3003 std::cout << "Restoring files in temporary file catalog:\n";
3004 for (unsigned int i = 0; i < m_size; ++i) {
3005 std::cout << m_tempNames[i] << " --> " << m_names[i] << std::endl;
3006 }
3007 }
3008
3009private:
3010 unsigned int m_size;
3011 const std::string m_emptyString;
3012 std::vector<std::string> m_names;
3013 std::vector<std::string> m_tempNames;
3014};
3015
3016////////////////////////////////////////////////////////////////////////////////
3017/// Transform name of dictionary
3018
3019std::ostream *CreateStreamPtrForSplitDict(const std::string &dictpathname,
3020 tempFileNamesCatalog &tmpCatalog)
3021{
3022 std::string splitDictName(tmpCatalog.getFileName(dictpathname));
3023 const size_t dotPos = splitDictName.find_last_of(".");
3024 splitDictName.insert(dotPos, "_classdef");
3025 tmpCatalog.addFileName(splitDictName);
3026 return new std::ofstream(splitDictName.c_str());
3027}
3028
3029////////////////////////////////////////////////////////////////////////////////
3030/// Transform -W statements in diagnostic pragmas for cling reacting on "-Wno-"
3031/// For example
3032/// -Wno-deprecated-declarations --> `#pragma clang diagnostic ignored "-Wdeprecated-declarations"`
3033
3034static void CheckForMinusW(std::string arg,
3035 std::list<std::string> &diagnosticPragmas)
3036{
3037 static const std::string pattern("-Wno-");
3038
3039 if (arg.find(pattern) != 0)
3040 return;
3041
3042 ROOT::TMetaUtils::ReplaceAll(arg, pattern, "#pragma clang diagnostic ignored \"-W");
3043 arg += "\"";
3044 diagnosticPragmas.push_back(arg);
3045}
3046
3047////////////////////////////////////////////////////////////////////////////////
3048
3050 cling::Interpreter &interp)
3051{
3052 using namespace ROOT::TMetaUtils::AST2SourceTools;
3053 std::string fwdDecl;
3054 std::string initStr("{");
3055 auto &fwdDeclnArgsToSkipColl = normCtxt.GetTemplNargsToKeepMap();
3056 for (auto & strigNargsToKeepPair : fwdDeclnArgsToSkipColl) {
3057 auto &clTemplDecl = *strigNargsToKeepPair.first;
3058 FwdDeclFromTmplDecl(clTemplDecl , interp, fwdDecl);
3059 initStr += "{\"" +
3060 fwdDecl + "\", "
3061 + std::to_string(strigNargsToKeepPair.second)
3062 + "},";
3063 }
3064 if (!fwdDeclnArgsToSkipColl.empty())
3065 initStr.pop_back();
3066 initStr += "}";
3067 return initStr;
3068}
3069
3070////////////////////////////////////////////////////////////////////////////////
3071/// Get the pointee type if possible
3072
3073clang::QualType GetPointeeTypeIfPossible(const clang::QualType &qt)
3074{
3075 if (qt.isNull()) return qt;
3076 clang::QualType thisQt(qt);
3077 while (thisQt->isPointerType() ||
3078 thisQt->isReferenceType()) {
3079 thisQt = thisQt->getPointeeType();
3080 }
3081 return thisQt;
3082
3083}
3084
3085////////////////////////////////////////////////////////////////////////////////
3086/// Extract the list of headers necessary for the Decl
3087
3088std::list<std::string> RecordDecl2Headers(const clang::CXXRecordDecl &rcd,
3089 const cling::Interpreter &interp,
3090 std::set<const clang::CXXRecordDecl *> &visitedDecls)
3091{
3092 std::list<std::string> headers;
3093
3094 // We push a new transaction because we could deserialize decls here
3095 cling::Interpreter::PushTransactionRAII RAII(&interp);
3096
3097 // Avoid infinite recursion
3098 if (!visitedDecls.insert(rcd.getCanonicalDecl()).second)
3099 return headers;
3100
3101 // If this is a template
3102 if (const clang::ClassTemplateSpecializationDecl *tsd = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd)) {
3103
3104 // Loop on the template args
3105 for (auto & tArg : tsd->getTemplateArgs().asArray()) {
3106 if (clang::TemplateArgument::ArgKind::Type != tArg.getKind()) continue;
3107 auto tArgQualType = GetPointeeTypeIfPossible(tArg.getAsType());
3108 if (tArgQualType.isNull()) continue;
3109 if (const clang::CXXRecordDecl *tArgCxxRcd = tArgQualType->getAsCXXRecordDecl()) {
3110 headers.splice(headers.end(), RecordDecl2Headers(*tArgCxxRcd, interp, visitedDecls));
3111 }
3112 }
3113
3114 if (!ROOT::TMetaUtils::IsStdClass(rcd) && rcd.hasDefinition()) {
3115
3116 // Loop on base classes - with a newer llvm, range based possible
3117 for (auto baseIt = tsd->bases_begin(); baseIt != tsd->bases_end(); baseIt++) {
3118 auto baseQualType = GetPointeeTypeIfPossible(baseIt->getType());
3119 if (baseQualType.isNull()) continue;
3120 if (const clang::CXXRecordDecl *baseRcdPtr = baseQualType->getAsCXXRecordDecl()) {
3121 headers.splice(headers.end(), RecordDecl2Headers(*baseRcdPtr, interp, visitedDecls));
3122 }
3123 }
3124
3125 // Loop on the data members - with a newer llvm, range based possible
3126 for (auto declIt = tsd->decls_begin(); declIt != tsd->decls_end(); ++declIt) {
3127 if (const clang::FieldDecl *fieldDecl = llvm::dyn_cast<clang::FieldDecl>(*declIt)) {
3128 auto fieldQualType = GetPointeeTypeIfPossible(fieldDecl->getType());
3129 if (fieldQualType.isNull()) continue ;
3130 if (const clang::CXXRecordDecl *fieldCxxRcd = fieldQualType->getAsCXXRecordDecl()) {
3131 if (fieldCxxRcd->hasDefinition())
3132 headers.splice(headers.end(), RecordDecl2Headers(*fieldCxxRcd, interp, visitedDecls));
3133 }
3134 }
3135 }
3136
3137 // Loop on methods
3138 for (auto methodIt = tsd->method_begin(); methodIt != tsd->method_end(); ++methodIt) {
3139 // Check arguments
3140 for (auto & fPar : methodIt->parameters()) {
3141 auto fParQualType = GetPointeeTypeIfPossible(fPar->getOriginalType());
3142 if (fParQualType.isNull()) continue;
3143 if (const clang::CXXRecordDecl *fParCxxRcd = fParQualType->getAsCXXRecordDecl()) {
3144 if (fParCxxRcd->hasDefinition())
3145 headers.splice(headers.end(), RecordDecl2Headers(*fParCxxRcd, interp, visitedDecls));
3146 }
3147 }
3148 // Check return value
3149 auto retQualType = GetPointeeTypeIfPossible(methodIt->getReturnType());
3150 if (retQualType.isNull()) continue;
3151 if (const clang::CXXRecordDecl *retCxxRcd = retQualType->getAsCXXRecordDecl()) {
3152 if (retCxxRcd->hasDefinition())
3153 headers.splice(headers.end(), RecordDecl2Headers(*retCxxRcd, interp, visitedDecls));
3154 }
3155 }
3156 }
3157
3158 } // End template instance
3159
3160 std::string header = ROOT::TMetaUtils::GetFileName(rcd, interp);
3161 headers.emplace_back(header);
3162 headers.reverse();
3163 return headers;
3164
3165}
3166
3167////////////////////////////////////////////////////////////////////////////////
3168/// Check if the class good for being an autoparse key.
3169/// We exclude from this set stl containers of pods/strings
3170/// TODO: we may use also __gnu_cxx::
3171bool IsGoodForAutoParseMap(const clang::RecordDecl& rcd){
3172
3173 // If it's not an std class, we just pick it up.
3174 if (auto dclCtxt= rcd.getDeclContext()){
3175 if (! dclCtxt->isStdNamespace()){
3176 return true;
3177 }
3178 } else {
3179 return true;
3180 }
3181
3182 // Now, we have a stl class. We now check if it's a template. If not, we
3183 // do not take it: bitset, string and so on.
3184 auto clAsTmplSpecDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&rcd);
3185 if (!clAsTmplSpecDecl) return false;
3186
3187 // Now we have a template in the stl. Let's see what the arguments are.
3188 // If they are not a POD or something which is good for autoparsing, we keep
3189 // them.
3190 auto& astCtxt = rcd.getASTContext();
3191 auto& templInstArgs = clAsTmplSpecDecl->getTemplateInstantiationArgs();
3192 for (auto&& arg : templInstArgs.asArray()){
3193
3194 auto argKind = arg.getKind();
3195 if (argKind != clang::TemplateArgument::Type){
3196 if (argKind == clang::TemplateArgument::Integral) continue;
3197 else return true;
3198 }
3199
3200 auto argQualType = arg.getAsType();
3201 auto isPOD = argQualType.isPODType(astCtxt);
3202 // This is a POD, we can inspect the next arg
3203 if (isPOD) continue;
3204
3205 auto argType = argQualType.getTypePtr();
3206 if (auto recType = llvm::dyn_cast<clang::RecordType>(argType)){
3207 auto isArgGoodForAutoParseMap = IsGoodForAutoParseMap(*recType->getDecl());
3208 // The arg is a class but good for the map
3209 if (isArgGoodForAutoParseMap) continue;
3210 } else {
3211 // The class is not a POD nor a class we can skip
3212 return true;
3213 }
3214 }
3215
3216 return false;
3217}
3218
3219////////////////////////////////////////////////////////////////////////////////
3220
3222 const RScanner::TypedefColl_t tDefDecls,
3223 const RScanner::FunctionColl_t funcDecls,
3224 const RScanner::VariableColl_t varDecls,
3225 const RScanner::EnumColl_t enumDecls,
3226 HeadersDeclsMap_t &headersClassesMap,
3227 HeadersDeclsMap_t &headersDeclsMap,
3228 const cling::Interpreter &interp)
3229{
3230 std::set<const clang::CXXRecordDecl *> visitedDecls;
3231 std::unordered_set<std::string> buffer;
3232 std::string autoParseKey;
3233
3234 // Add some manip of headers
3235 for (auto & annotatedRcd : annotatedRcds) {
3236 if (const clang::CXXRecordDecl *cxxRcd =
3237 llvm::dyn_cast_or_null<clang::CXXRecordDecl>(annotatedRcd.GetRecordDecl())) {
3238 autoParseKey = "";
3239 visitedDecls.clear();
3240 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3241 // remove duplicates, also if not subsequent
3242 buffer.clear();
3243 headers.remove_if([&buffer](const std::string & s) {
3244 return !buffer.insert(s).second;
3245 });
3246 GetMostExternalEnclosingClassName(*cxxRcd, autoParseKey, interp);
3247 if (autoParseKey.empty()) autoParseKey = annotatedRcd.GetNormalizedName();
3248 if (IsGoodForAutoParseMap(*cxxRcd)){
3249 headersDeclsMap[autoParseKey] = headers;
3250 headersDeclsMap[annotatedRcd.GetRequestedName()] = headers;
3251 } else {
3252 ROOT::TMetaUtils::Info(nullptr, "Class %s is not included in the set of autoparse keys.\n", autoParseKey.c_str());
3253 }
3254
3255 // Propagate to the classes map only if this is not a template.
3256 // The header is then used as autoload key and we want to avoid duplicates.
3257 if (!llvm::isa<clang::ClassTemplateSpecializationDecl>(cxxRcd)){
3258 headersClassesMap[autoParseKey] = headersDeclsMap[autoParseKey];
3259 headersClassesMap[annotatedRcd.GetRequestedName()] = headersDeclsMap[annotatedRcd.GetRequestedName()];
3260 }
3261 }
3262 }
3263
3264 // The same for the typedefs:
3265 for (auto & tDef : tDefDecls) {
3266 if (clang::CXXRecordDecl *cxxRcd = tDef->getUnderlyingType()->getAsCXXRecordDecl()) {
3267 autoParseKey = "";
3268 visitedDecls.clear();
3269 std::list<std::string> headers(RecordDecl2Headers(*cxxRcd, interp, visitedDecls));
3270 headers.push_back(ROOT::TMetaUtils::GetFileName(*tDef, interp));
3271 // remove duplicates, also if not subsequent
3272 buffer.clear();
3273 headers.remove_if([&buffer](const std::string & s) {
3274 return !buffer.insert(s).second;
3275 });
3276 GetMostExternalEnclosingClassNameFromDecl(*tDef, autoParseKey, interp);
3277 if (autoParseKey.empty()) autoParseKey = tDef->getQualifiedNameAsString();
3278 headersDeclsMap[autoParseKey] = headers;
3279 }
3280 }
3281
3282 // The same for the functions:
3283 for (auto & func : funcDecls) {
3284 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*func, interp)};
3285 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*func)] = headers;
3286 }
3287
3288 // The same for the variables:
3289 for (auto & var : varDecls) {
3290 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*var, interp)};
3291 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*var)] = headers;
3292 }
3293
3294 // The same for the enums:
3295 for (auto & en : enumDecls) {
3296 std::list<std::string> headers = {ROOT::TMetaUtils::GetFileName(*en, interp)};
3297 headersDeclsMap[ROOT::TMetaUtils::GetQualifiedName(*en)] = headers;
3298 }
3299}
3300
3301////////////////////////////////////////////////////////////////////////////////
3302/// Generate the fwd declarations of the selected entities
3303
3304static std::string GenerateFwdDeclString(const RScanner &scan,
3305 const cling::Interpreter &interp)
3306{
3307 std::string newFwdDeclString;
3308
3309 using namespace ROOT::TMetaUtils::AST2SourceTools;
3310
3311 std::string fwdDeclString;
3312 std::string buffer;
3313 std::unordered_set<std::string> fwdDecls;
3314
3315 // Classes
3316/*
3317 for (auto const & annRcd : scan.fSelectedClasses) {
3318 const auto rcdDeclPtr = annRcd.GetRecordDecl();
3319
3320 int retCode = FwdDeclFromRcdDecl(*rcdDeclPtr, interp, buffer);
3321 if (-1 == retCode) {
3322 ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3323 "Error generating fwd decl for class %s\n",
3324 annRcd.GetNormalizedName());
3325 return emptyString;
3326 }
3327 if (retCode == 0 && fwdDecls.insert(buffer).second)
3328 fwdDeclString += "\"" + buffer + "\"\n";
3329 }
3330*/
3331 // Build the input for a transaction containing all of the selected declarations
3332 // Cling will produce the fwd declaration payload.
3333
3334 std::vector<const clang::Decl *> selectedDecls(scan.fSelectedClasses.size());
3335
3336 // Pick only RecordDecls
3337 std::transform (scan.fSelectedClasses.begin(),
3338 scan.fSelectedClasses.end(),
3339 selectedDecls.begin(),
3340 [](const ROOT::TMetaUtils::AnnotatedRecordDecl& rcd){return rcd.GetRecordDecl();});
3341
3342 for (auto* TD: scan.fSelectedTypedefs)
3343 selectedDecls.push_back(TD);
3344
3345// for (auto* VAR: scan.fSelectedVariables)
3346// selectedDecls.push_back(VAR);
3347
3348 std::string fwdDeclLogs;
3349
3350 // The "R\"DICTFWDDCLS(\n" ")DICTFWDDCLS\"" pieces have been moved to
3351 // TModuleGenerator to be able to make the diagnostics more telling in presence
3352 // of an issue ROOT-6752.
3353 fwdDeclString += Decls2FwdDecls(selectedDecls,IsLinkdefFile,interp, genreflex::verbose ? &fwdDeclLogs : nullptr);
3354
3355 if (genreflex::verbose && !fwdDeclLogs.empty())
3356 std::cout << "Logs from forward decl printer: \n"
3357 << fwdDeclLogs;
3358
3359 // Functions
3360// for (auto const& fcnDeclPtr : scan.fSelectedFunctions){
3361// int retCode = FwdDeclFromFcnDecl(*fcnDeclPtr, interp, buffer);
3362// newFwdDeclString += Decl2FwdDecl(*fcnDeclPtr,interp);
3363// if (-1 == retCode){
3364// ROOT::TMetaUtils::Error("GenerateFwdDeclString",
3365// "Error generating fwd decl for function %s\n",
3366// fcnDeclPtr->getNameAsString().c_str());
3367// return emptyString;
3368// }
3369// if (retCode == 0 && fwdDecls.insert(buffer).second)
3370// fwdDeclString+="\""+buffer+"\"\n";
3371// }
3372
3373 if (fwdDeclString.empty()) fwdDeclString = "";
3374 return fwdDeclString;
3375}
3376
3377////////////////////////////////////////////////////////////////////////////////
3378/// Generate a string for the dictionary from the headers-classes map.
3379
3380const std::string GenerateStringFromHeadersForClasses(const HeadersDeclsMap_t &headersClassesMap,
3381 const std::string &detectedUmbrella,
3382 bool payLoadOnly = false)
3383{
3384 std::string headerName;
3385
3387 std::cout << "Class-headers Mapping:\n";
3388 std::string headersClassesMapString = "";
3389 for (auto const & classHeaders : headersClassesMap) {
3391 std::cout << " o " << classHeaders.first << " --> ";
3392 headersClassesMapString += "\"";
3393 headersClassesMapString += classHeaders.first + "\"";
3394 for (auto const & header : classHeaders.second) {
3395 headerName = (detectedUmbrella == header || payLoadOnly) ? "payloadCode" : "\"" + header + "\"";
3396 headersClassesMapString += ", " + headerName;
3398 std::cout << ", " << headerName;
3399 if (payLoadOnly)
3400 break;
3401 }
3403 std::cout << std::endl;
3404 headersClassesMapString += ", \"@\",\n";
3405 }
3406 headersClassesMapString += "nullptr";
3407 return headersClassesMapString;
3408}
3409
3410////////////////////////////////////////////////////////////////////////////////
3411
3412bool IsImplementationName(const std::string &filename)
3413{
3415}
3416
3417////////////////////////////////////////////////////////////////////////////////
3418/// Check if the argument is a sane cling argument. Performing the following checks:
3419/// 1) It does not start with "--" and is not the --param option.
3420
3421bool IsCorrectClingArgument(const std::string& argument)
3422{
3423 if (ROOT::TMetaUtils::BeginsWith(argument,"--") && !ROOT::TMetaUtils::BeginsWith(argument,"--param")) return false;
3424 return true;
3425}
3426
3427////////////////////////////////////////////////////////////////////////////////
3428bool NeedsSelection(const char* name)
3429{
3430 static const std::vector<std::string> namePrfxes {
3431 "array<",
3432 "unique_ptr<"};
3433 auto pos = find_if(namePrfxes.begin(),
3434 namePrfxes.end(),
3435 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3436 return namePrfxes.end() == pos;
3437}
3438
3439////////////////////////////////////////////////////////////////////////////////
3440
3442{
3443 static const std::vector<std::string> uclNamePrfxes {
3444 "chrono:",
3445 "ratio<",
3446 "shared_ptr<"};
3447 static const std::set<std::string> unsupportedClassesNormNames{
3448 "regex",
3449 "thread"};
3450 if ( unsupportedClassesNormNames.count(name) == 1) return false;
3451 auto pos = find_if(uclNamePrfxes.begin(),
3452 uclNamePrfxes.end(),
3453 [&](const std::string& str){return ROOT::TMetaUtils::BeginsWith(name,str);});
3454 return uclNamePrfxes.end() == pos;
3455}
3456
3457////////////////////////////////////////////////////////////////////////////////
3458/// Check if the list of selected classes contains any class which is not
3459/// supported. Return the number of unsupported classes in the selection.
3460
3462{
3463 int nerrors = 0;
3464 for (auto&& aRcd : annotatedRcds){
3465 auto clName = aRcd.GetNormalizedName();
3466 if (!IsSupportedClassName(clName)){
3467 std::cerr << "Error: Class " << clName << " has been selected but "
3468 << "currently the support for its I/O is not yet available. Note that "
3469 << clName << ", even if not selected, will be available for "
3470 << "interpreted code.\n";
3471 nerrors++;
3472 }
3473 if (!NeedsSelection(clName)){
3474 std::cerr << "Error: It is not necessary to explicitly select class "
3475 << clName << ". I/O is supported for it transparently.\n";
3476 nerrors++;
3477 }
3478 }
3479 return nerrors;
3480}
3481
3482////////////////////////////////////////////////////////////////////////////////
3483
3484class TRootClingCallbacks : public cling::InterpreterCallbacks {
3485private:
3486 std::list<std::string>& fFilesIncludedByLinkdef;
3487 bool isLocked = false;
3488public:
3489 TRootClingCallbacks(cling::Interpreter* interp, std::list<std::string>& filesIncludedByLinkdef):
3490 InterpreterCallbacks(interp),
3491 fFilesIncludedByLinkdef(filesIncludedByLinkdef){};
3492
3494
3495 void InclusionDirective(clang::SourceLocation /*HashLoc*/, const clang::Token & /*IncludeTok*/,
3496 llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange /*FilenameRange*/,
3497 clang::OptionalFileEntryRef /*File*/, llvm::StringRef /*SearchPath*/,
3498 llvm::StringRef /*RelativePath*/, const clang::Module * /*Imported*/,
3499 clang::SrcMgr::CharacteristicKind /*FileType*/) override
3500 {
3501 if (isLocked) return;
3502 if (IsAngled) return;
3503 auto& PP = m_Interpreter->getCI()->getPreprocessor();
3504 auto curLexer = PP.getCurrentFileLexer();
3505 if (!curLexer) return;
3506 auto fileEntry = curLexer->getFileEntry();
3507 if (!fileEntry) return;
3508 auto thisFileName = fileEntry->getName();
3509 auto fileNameAsString = FileName.str();
3510 auto isThisLinkdef = ROOT::TMetaUtils::IsLinkdefFile(thisFileName.data());
3511 if (isThisLinkdef) {
3512 auto isTheIncludedLinkdef = ROOT::TMetaUtils::IsLinkdefFile(fileNameAsString.c_str());
3513 if (isTheIncludedLinkdef) {
3514 fFilesIncludedByLinkdef.clear();
3515 isLocked = true;
3516 } else {
3517 fFilesIncludedByLinkdef.emplace_back(fileNameAsString.c_str());
3518 }
3519 }
3520 }
3521
3522 // rootcling pre-includes things such as Rtypes.h. This means that ACLiC can
3523 // call rootcling asking it to create a module for a file with no #includes
3524 // but relying on things from Rtypes.h such as the ClassDef macro.
3525 //
3526 // When rootcling starts building a module, it becomes resilient to the
3527 // outside environment and pre-included files have no effect. This hook
3528 // informs rootcling when a new submodule is being built so that it can
3529 // make Core.Rtypes.h visible.
3530 void EnteredSubmodule(clang::Module* M,
3531 clang::SourceLocation ImportLoc,
3532 bool ForPragma) override {
3533 assert(M);
3534 using namespace clang;
3535 if (llvm::StringRef(M->Name).endswith("ACLiC_dict")) {
3536 Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
3537 HeaderSearch& HS = PP.getHeaderSearchInfo();
3538 // FIXME: Reduce to Core.Rtypes.h.
3539 Module* CoreModule = HS.lookupModule("Core", SourceLocation(),
3540 /*AllowSearch*/false);
3541 assert(M && "Must have module Core");
3542 PP.makeModuleVisible(CoreModule, ImportLoc);
3543 }
3544 }
3545};
3546
3547static llvm::cl::list<std::string>
3548gOptModuleByproducts("mByproduct", llvm::cl::ZeroOrMore,
3549 llvm::cl::Hidden,
3550 llvm::cl::desc("The list of the expected implicit modules build as part of building the current module."),
3551 llvm::cl::cat(gRootclingOptions));
3552// Really llvm::cl::Required, will be changed in RootClingMain below.
3553static llvm::cl::opt<std::string>
3554gOptDictionaryFileName(llvm::cl::Positional,
3555 llvm::cl::desc("<output dictionary file>"),
3556 llvm::cl::cat(gRootclingOptions));
3557
3558////////////////////////////////////////////////////////////////////////////////
3559/// Custom diag client for clang that verifies that each implicitly build module
3560/// is a system module. If not, it will let the current rootcling invocation
3561/// fail with an error. All other diags beside module build remarks will be
3562/// forwarded to the passed child diag client.
3563///
3564/// The reason why we need this is that if we built implicitly a C++ module
3565/// that belongs to a ROOT dictionary, then we will miss information generated
3566/// by rootcling in this file (e.g. the source code comments to annotation
3567/// attributes transformation will be missing in the module file).
3568class CheckModuleBuildClient : public clang::DiagnosticConsumer {
3569 clang::DiagnosticConsumer *fChild;
3571 clang::ModuleMap &fMap;
3572
3573public:
3574 CheckModuleBuildClient(clang::DiagnosticConsumer *Child, bool OwnsChild, clang::ModuleMap &Map)
3575 : fChild(Child), fOwnsChild(OwnsChild), fMap(Map)
3576 {
3577 }
3578
3580 {
3581 if (fOwnsChild)
3582 delete fChild;
3583 }
3584
3585 virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
3586 {
3587 using namespace clang::diag;
3588
3589 // This method catches the module_build remark from clang and checks if
3590 // the implicitly built module is a system module or not. We only support
3591 // building system modules implicitly.
3592
3593 std::string moduleName;
3594 const clang::Module *module = nullptr;
3595
3596 // Extract the module from the diag argument with index 0.
3597 const auto &ID = Info.getID();
3598 if (ID == remark_module_build || ID == remark_module_build_done) {
3599 moduleName = Info.getArgStdStr(0);
3600 module = fMap.findModule(moduleName);
3601 // We should never be able to build a module without having it in the
3602 // modulemap. Still, let's print a warning that we at least tell the
3603 // user that this could lead to problems.
3604 if (!module) {
3606 "Couldn't find module %s in the available modulemaps. This"
3607 "prevents us from correctly diagnosing wrongly built modules.\n",
3608 moduleName.c_str());
3609 }
3610 }
3611
3612 // A dictionary module could build implicitly a set of implicit modules.
3613 // For example, the Core module builds libc.pcm and std.pcm implicitly.
3614 // Those modules do not require I/O information and it is okay to build
3615 // them as part of another module.
3616 // However, we can build a module which requires I/O implictly which is
3617 // an error because rootcling is not able to generate the corresponding
3618 // dictionary.
3619 // If we build a I/O requiring module implicitly we should display
3620 // an error unless the -mByproduct was specified.
3621 bool isByproductModule
3622 = module && std::find(gOptModuleByproducts.begin(), gOptModuleByproducts.end(), moduleName) != gOptModuleByproducts.end();
3623 if (!isByproductModule)
3624 fChild->HandleDiagnostic(DiagLevel, Info);
3625
3626 if (ID == remark_module_build && !isByproductModule) {
3628 "Building module '%s' implicitly. If '%s' requires a \n"
3629 "dictionary please specify build dependency: '%s' depends on '%s'.\n"
3630 "Otherwise, specify '-mByproduct %s' to disable this diagnostic.\n",
3631 moduleName.c_str(), moduleName.c_str(), gOptDictionaryFileName.c_str(),
3632 moduleName.c_str(), moduleName.c_str());
3633 }
3634 }
3635
3636 // All methods below just forward to the child and the default method.
3637 virtual void clear() override
3638 {
3639 fChild->clear();
3640 DiagnosticConsumer::clear();
3641 }
3642
3643 virtual void BeginSourceFile(const clang::LangOptions &LangOpts, const clang::Preprocessor *PP) override
3644 {
3645 fChild->BeginSourceFile(LangOpts, PP);
3646 DiagnosticConsumer::BeginSourceFile(LangOpts, PP);
3647 }
3648
3649 virtual void EndSourceFile() override
3650 {
3651 fChild->EndSourceFile();
3652 DiagnosticConsumer::EndSourceFile();
3653 }
3654
3655 virtual void finish() override
3656 {
3657 fChild->finish();
3658 DiagnosticConsumer::finish();
3659 }
3660
3661 virtual bool IncludeInDiagnosticCounts() const override { return fChild->IncludeInDiagnosticCounts(); }
3662};
3663
3665#if defined(_WIN32) && defined(_MSC_VER)
3666 // Suppress error dialogs to avoid hangs on build nodes.
3667 // One can use an environment variable (Cling_GuiOnAssert) to enable
3668 // the error dialogs.
3669 const char *EnablePopups = getenv("Cling_GuiOnAssert");
3670 if (EnablePopups == nullptr || EnablePopups[0] == '0') {
3671 ::_set_error_mode(_OUT_TO_STDERR);
3672 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3673 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
3674 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3675 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
3676 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
3677 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
3678 }
3679#endif
3680}
3681
3682static llvm::cl::opt<bool> gOptForce("f", llvm::cl::desc("Overwrite <file>s."),
3683 llvm::cl::cat(gRootclingOptions));
3684static llvm::cl::opt<bool> gOptRootBuild("rootbuild", llvm::cl::desc("If we are building ROOT."),
3685 llvm::cl::Hidden,
3686 llvm::cl::cat(gRootclingOptions));
3695static llvm::cl::opt<VerboseLevel>
3696gOptVerboseLevel(llvm::cl::desc("Choose verbosity level:"),
3697 llvm::cl::values(clEnumVal(v, "Show errors."),
3698 clEnumVal(v0, "Show only fatal errors."),
3699 clEnumVal(v1, "Show errors (the same as -v)."),
3700 clEnumVal(v2, "Show warnings (default)."),
3701 clEnumVal(v3, "Show notes."),
3702 clEnumVal(v4, "Show information.")),
3703 llvm::cl::init(v2),
3704 llvm::cl::cat(gRootclingOptions));
3705
3706static llvm::cl::opt<bool>
3707gOptCint("cint", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3708 llvm::cl::Hidden,
3709 llvm::cl::cat(gRootclingOptions));
3710static llvm::cl::opt<bool>
3711gOptReflex("reflex", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3712 llvm::cl::Hidden,
3713 llvm::cl::cat(gRootclingOptions));
3714static llvm::cl::opt<bool>
3715gOptGccXml("gccxml", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3716 llvm::cl::Hidden,
3717 llvm::cl::cat(gRootclingOptions));
3718static llvm::cl::opt<std::string>
3719gOptLibListPrefix("lib-list-prefix",
3720 llvm::cl::desc("An ACLiC feature which exports the list of dependent libraries."),
3721 llvm::cl::Hidden,
3722 llvm::cl::cat(gRootclingOptions));
3723static llvm::cl::opt<bool>
3724gOptGeneratePCH("generate-pch",
3725 llvm::cl::desc("Generates a pch file from a predefined set of headers. See makepch.py."),
3726 llvm::cl::Hidden,
3727 llvm::cl::cat(gRootclingOptions));
3728static llvm::cl::opt<bool>
3729gOptC("c", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3730 llvm::cl::cat(gRootclingOptions));
3731static llvm::cl::opt<bool>
3732gOptP("p", llvm::cl::desc("Deprecated, legacy flag which is ignored."),
3733 llvm::cl::cat(gRootclingOptions));
3734static llvm::cl::list<std::string>
3735gOptRootmapLibNames("rml", llvm::cl::ZeroOrMore,
3736 llvm::cl::desc("Generate rootmap file."),
3737 llvm::cl::cat(gRootclingOptions));
3738static llvm::cl::opt<std::string>
3740 llvm::cl::desc("Generate a rootmap file with the specified name."),
3741 llvm::cl::cat(gRootclingOptions));
3742static llvm::cl::opt<bool>
3743gOptCxxModule("cxxmodule",
3744 llvm::cl::desc("Generate a C++ module."),
3745 llvm::cl::cat(gRootclingOptions));
3746static llvm::cl::list<std::string>
3747gOptModuleMapFiles("moduleMapFile",
3748 llvm::cl::desc("Specify a C++ modulemap file."),
3749 llvm::cl::cat(gRootclingOptions));
3750// FIXME: Figure out how to combine the code of -umbrellaHeader and inlineInputHeader
3751static llvm::cl::opt<bool>
3752gOptUmbrellaInput("umbrellaHeader",
3753 llvm::cl::desc("A single header including all headers instead of specifying them on the command line."),
3754 llvm::cl::cat(gRootclingOptions));
3755static llvm::cl::opt<bool>
3756gOptMultiDict("multiDict",
3757 llvm::cl::desc("If this library has multiple separate LinkDef files."),
3758 llvm::cl::cat(gRootclingOptions));
3759static llvm::cl::opt<bool>
3760gOptNoGlobalUsingStd("noGlobalUsingStd",
3761 llvm::cl::desc("Do not declare {using namespace std} in dictionary global scope."),
3762 llvm::cl::cat(gRootclingOptions));
3763static llvm::cl::opt<bool>
3764gOptInterpreterOnly("interpreteronly",
3765 llvm::cl::desc("Generate minimal dictionary for interactivity (without IO information)."),
3766 llvm::cl::cat(gRootclingOptions));
3767static llvm::cl::opt<bool>
3769 llvm::cl::desc("Split the dictionary into two parts: one containing the IO (ClassDef)\
3770information and another the interactivity support."),
3771 llvm::cl::cat(gRootclingOptions));
3772static llvm::cl::opt<bool>
3773gOptNoDictSelection("noDictSelection",
3774 llvm::cl::Hidden,
3775 llvm::cl::desc("Do not run the selection rules. Useful when in -onepcm mode."),
3776 llvm::cl::cat(gRootclingOptions));
3777static llvm::cl::opt<std::string>
3779 llvm::cl::desc("The path to the library of the built dictionary."),
3780 llvm::cl::cat(gRootclingOptions));
3781static llvm::cl::list<std::string>
3783 llvm::cl::desc("The list of dependent modules of the dictionary."),
3784 llvm::cl::cat(gRootclingOptions));
3785static llvm::cl::list<std::string>
3786gOptExcludePaths("excludePath", llvm::cl::ZeroOrMore,
3787 llvm::cl::desc("Do not store the <path> in the dictionary."),
3788 llvm::cl::cat(gRootclingOptions));
3789// FIXME: This does not seem to work. We have one use of -inlineInputHeader in
3790// ROOT and it does not produce the expected result.
3791static llvm::cl::opt<bool>
3792gOptInlineInput("inlineInputHeader",
3793 llvm::cl::desc("Does not generate #include <header> but expands the header content."),
3794 llvm::cl::cat(gRootclingOptions));
3795// FIXME: This is totally the wrong concept. We should not expose an interface
3796// to be able to tell which component is in the pch and which needs extra
3797// scaffolding for interactive use. Moreover, some of the ROOT components are
3798// partially in the pch and this option makes it impossible to express that.
3799// We should be able to get the list of headers in the pch early and scan
3800// through them.
3801static llvm::cl::opt<bool>
3802gOptWriteEmptyRootPCM("writeEmptyRootPCM",
3803 llvm::cl::Hidden,
3804 llvm::cl::desc("Does not include the header files as it assumes they exist in the pch."),
3805 llvm::cl::cat(gRootclingOptions));
3806static llvm::cl::opt<bool>
3808 llvm::cl::desc("Check the selection syntax only."),
3809 llvm::cl::cat(gRootclingOptions));
3810static llvm::cl::opt<bool>
3811gOptFailOnWarnings("failOnWarnings",
3812 llvm::cl::desc("Fail if there are warnings."),
3813 llvm::cl::cat(gRootclingOptions));
3814static llvm::cl::opt<bool>
3815gOptNoIncludePaths("noIncludePaths",
3816 llvm::cl::desc("Do not store include paths but rely on the env variable ROOT_INCLUDE_PATH."),
3817 llvm::cl::cat(gRootclingOptions));
3818static llvm::cl::opt<std::string>
3819gOptISysRoot("isysroot", llvm::cl::Prefix, llvm::cl::Hidden,
3820 llvm::cl::desc("Specify an isysroot."),
3821 llvm::cl::cat(gRootclingOptions),
3822 llvm::cl::init("-"));
3823static llvm::cl::list<std::string>
3824gOptIncludePaths("I", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3825 llvm::cl::desc("Specify an include path."),
3826 llvm::cl::cat(gRootclingOptions));
3827static llvm::cl::list<std::string>
3828gOptCompDefaultIncludePaths("compilerI", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3829 llvm::cl::desc("Specify a compiler default include path, to suppress unneeded `-isystem` arguments."),
3830 llvm::cl::cat(gRootclingOptions));
3831static llvm::cl::list<std::string>
3832gOptSysIncludePaths("isystem", llvm::cl::ZeroOrMore,
3833 llvm::cl::desc("Specify a system include path."),
3834 llvm::cl::cat(gRootclingOptions));
3835static llvm::cl::list<std::string>
3836gOptPPDefines("D", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3837 llvm::cl::desc("Specify defined macros."),
3838 llvm::cl::cat(gRootclingOptions));
3839static llvm::cl::list<std::string>
3840gOptPPUndefines("U", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3841 llvm::cl::desc("Specify undefined macros."),
3842 llvm::cl::cat(gRootclingOptions));
3843static llvm::cl::list<std::string>
3844gOptWDiags("W", llvm::cl::Prefix, llvm::cl::ZeroOrMore,
3845 llvm::cl::desc("Specify compiler diagnostics options."),
3846 llvm::cl::cat(gRootclingOptions));
3847// Really OneOrMore, will be changed in RootClingMain below.
3848static llvm::cl::list<std::string>
3849gOptDictionaryHeaderFiles(llvm::cl::Positional, llvm::cl::ZeroOrMore,
3850 llvm::cl::desc("<list of dictionary header files> <LinkDef file>"),
3851 llvm::cl::cat(gRootclingOptions));
3852static llvm::cl::list<std::string>
3853gOptSink(llvm::cl::ZeroOrMore, llvm::cl::Sink,
3854 llvm::cl::desc("Consumes all unrecognized options."),
3855 llvm::cl::cat(gRootclingOptions));
3856
3857static llvm::cl::SubCommand
3858gBareClingSubcommand("bare-cling", "Call directly cling and exit.");
3859
3860static llvm::cl::list<std::string>
3861gOptBareClingSink(llvm::cl::OneOrMore, llvm::cl::Sink,
3862 llvm::cl::desc("Consumes options and sends them to cling."),
3863 llvm::cl::cat(gRootclingOptions), llvm::cl::sub(gBareClingSubcommand));
3864
3865////////////////////////////////////////////////////////////////////////////////
3866/// Returns true iff a given module (and its submodules) contains all headers
3867/// needed by the given ModuleGenerator.
3868/// The names of all header files that are needed by the ModuleGenerator but are
3869/// not in the given module will be inserted into the MissingHeader variable.
3870/// Returns true iff the PCH was successfully generated.
3871static bool ModuleContainsHeaders(TModuleGenerator &modGen, clang::HeaderSearch &headerSearch,
3872 clang::Module *module, std::vector<std::array<std::string, 2>> &missingHeaders)
3873{
3874 // Now we collect all header files from the previously collected modules.
3875 std::vector<clang::Module::Header> moduleHeaders;
3877 [&moduleHeaders](const clang::Module::Header &h) { moduleHeaders.push_back(h); });
3878
3879 bool foundAllHeaders = true;
3880
3881 auto isHeaderInModule = [&moduleHeaders](const std::string &header) {
3882 for (const clang::Module::Header &moduleHeader : moduleHeaders)
3883 if (header == moduleHeader.NameAsWritten)
3884 return true;
3885 return false;
3886 };
3887
3888 // Go through the list of headers that are required by the ModuleGenerator
3889 // and check for each header if it's in one of the modules we loaded.
3890 // If not, make sure we fail at the end and mark the header as missing.
3891 for (const std::string &header : modGen.GetHeaders()) {
3892 if (isHeaderInModule(header))
3893 continue;
3894
3895 clang::ModuleMap::KnownHeader SuggestedModule;
3896 clang::ConstSearchDirIterator *CurDir = nullptr;
3897 if (auto FE = headerSearch.LookupFile(
3898 header, clang::SourceLocation(),
3899 /*isAngled*/ false,
3900 /*FromDir*/ 0, CurDir,
3901 clang::ArrayRef<std::pair<const clang::FileEntry *, const clang::DirectoryEntry *>>(),
3902 /*SearchPath*/ 0,
3903 /*RelativePath*/ 0,
3904 /*RequestingModule*/ 0, &SuggestedModule,
3905 /*IsMapped*/ 0,
3906 /*IsFrameworkFound*/ nullptr,
3907 /*SkipCache*/ false,
3908 /*BuildSystemModule*/ false,
3909 /*OpenFile*/ false,
3910 /*CacheFail*/ false)) {
3911 if (auto OtherModule = SuggestedModule.getModule()) {
3912 std::string OtherModuleName;
3913 auto TLM = OtherModule->getTopLevelModuleName();
3914 if (!TLM.empty())
3915 OtherModuleName = TLM.str();
3916 else
3917 OtherModuleName = OtherModule->Name;
3918
3919 // Don't complain about headers that are actually in by-products:
3920 if (std::find(gOptModuleByproducts.begin(), gOptModuleByproducts.end(), OtherModuleName)
3921 != gOptModuleByproducts.end())
3922 continue;
3923
3924 missingHeaders.push_back({header, OtherModuleName});
3925 }
3926 } else {
3927 missingHeaders.push_back({header, {}});
3928 }
3929 foundAllHeaders = false;
3930 }
3931 return foundAllHeaders;
3932}
3933
3934////////////////////////////////////////////////////////////////////////////////
3935/// Check moduleName validity from modulemap. Check if this module is defined or not.
3936static bool CheckModuleValid(TModuleGenerator &modGen, const std::string &resourceDir, cling::Interpreter &interpreter,
3937 llvm::StringRef LinkdefPath, const std::string &moduleName)
3938{
3939 clang::CompilerInstance *CI = interpreter.getCI();
3940 clang::HeaderSearch &headerSearch = CI->getPreprocessor().getHeaderSearchInfo();
3941 headerSearch.loadTopLevelSystemModules();
3942
3943 // Actually lookup the module on the computed module name.
3944 clang::Module *module = headerSearch.lookupModule(llvm::StringRef(moduleName));
3945
3946 // Inform the user and abort if we can't find a module with a given name.
3947 if (!module) {
3948 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't find module with name '%s' in modulemap!\n",
3949 moduleName.c_str());
3950 return false;
3951 }
3952
3953 // Check if the loaded module covers all headers that were specified
3954 // by the user on the command line. This is an integrity check to
3955 // ensure that our used module map is not containing extraneous headers.
3956 std::vector<std::array<std::string, 2>> missingHdrMod;
3957 if (!ModuleContainsHeaders(modGen, headerSearch, module, missingHdrMod)) {
3958 // FIXME: Upgrade this to an error once modules are stable.
3959 std::stringstream msgStream;
3960 msgStream << "after creating module \"" << module->Name << "\" ";
3961 if (!module->PresumedModuleMapFile.empty())
3962 msgStream << "using modulemap \"" << module->PresumedModuleMapFile << "\" ";
3963 msgStream << "the following headers are not part of that module:\n";
3964 for (auto &H : missingHdrMod) {
3965 msgStream << " " << H[0];
3966 if (!H[1].empty())
3967 msgStream << " (already part of module \"" << H[1] << "\")";
3968 msgStream << "\n";
3969 }
3970 std::string warningMessage = msgStream.str();
3971
3972 bool maybeUmbrella = modGen.GetHeaders().size() == 1;
3973 // We may have an umbrella and forgot to add the flag. Downgrade the
3974 // warning into an information message.
3975 // FIXME: We should open the umbrella, extract the set of header files
3976 // and check if they exist in the modulemap.
3977 // FIXME: We should also check if the header files are specified in the
3978 // modulemap file as they appeared in the rootcling invocation, i.e.
3979 // if we passed rootcling ... -I/some/path somedir/some/header, the
3980 // modulemap should contain module M { header "somedir/some/header" }
3981 // This way we will make sure the module is properly activated.
3982 if (!gOptUmbrellaInput && maybeUmbrella) {
3983 ROOT::TMetaUtils::Info("CheckModuleValid, %s. You can silence this message by adding %s to the invocation.",
3984 warningMessage.c_str(),
3985 gOptUmbrellaInput.ArgStr.data());
3986 return true;
3987 }
3988
3989 ROOT::TMetaUtils::Warning("CheckModuleValid", warningMessage.c_str());
3990 // We include the missing headers to fix the module for the user.
3991 std::vector<std::string> missingHeaders;
3992 std::transform(missingHdrMod.begin(), missingHdrMod.end(), missingHeaders.begin(),
3993 [](const std::array<std::string, 2>& HdrMod) { return HdrMod[0];});
3994 if (!IncludeHeaders(missingHeaders, interpreter)) {
3995 ROOT::TMetaUtils::Error("CheckModuleValid", "Couldn't include missing module headers for module '%s'!\n",
3996 module->Name.c_str());
3997 }
3998 }
3999
4000 return true;
4001}
4002
4003static llvm::StringRef GetModuleNameFromRdictName(llvm::StringRef rdictName)
4004{
4005 // Try to get the module name in the modulemap based on the filepath.
4006 llvm::StringRef moduleName = llvm::sys::path::filename(rdictName);
4007 moduleName.consume_front("lib");
4008 moduleName.consume_back(".pcm");
4009 moduleName.consume_back("_rdict");
4010 return moduleName;
4011}
4012
4013////////////////////////////////////////////////////////////////////////////////
4014
4015int RootClingMain(int argc,
4016 char **argv,
4017 bool isGenreflex = false)
4018{
4019 // Set number of required arguments. We cannot do this globally since it
4020 // would interfere with LLVM's option parsing.
4021 gOptDictionaryFileName.setNumOccurrencesFlag(llvm::cl::Required);
4022 gOptDictionaryHeaderFiles.setNumOccurrencesFlag(llvm::cl::OneOrMore);
4023
4024 // Copied from cling driver.
4025 // FIXME: Uncomment once we fix ROOT's teardown order.
4026 //llvm::llvm_shutdown_obj shutdownTrigger;
4027
4028 const char *executableFileName = argv[0];
4029
4030 llvm::sys::PrintStackTraceOnErrorSignal(executableFileName);
4031 llvm::PrettyStackTraceProgram X(argc, argv);
4033
4034#if defined(R__WIN32) && !defined(R__WINGCC)
4035 // FIXME: This is terrible hack allocating and changing the argument set.
4036 // We should remove it and use standard llvm facilities to convert the paths.
4037 // cygwin's make is presenting us some cygwin paths even though
4038 // we are windows native. Convert them as good as we can.
4039 for (int iic = 1 /* ignore binary file name in argv[0] */; iic < argc; ++iic) {
4040 std::string iiarg(argv[iic]);
4041 if (FromCygToNativePath(iiarg)) {
4042 size_t len = iiarg.length();
4043 // yes, we leak.
4044 char *argviic = new char[len + 1];
4045 strlcpy(argviic, iiarg.c_str(), len + 1);
4046 argv[iic] = argviic;
4047 }
4048 }
4049#endif
4050
4051 // Hide options from llvm which we got from static initialization of libCling.
4052 llvm::cl::HideUnrelatedOptions(/*keep*/gRootclingOptions);
4053
4054 // Define Options aliasses
4055 auto &opts = llvm::cl::getRegisteredOptions();
4056 llvm::cl::Option* optHelp = opts["help"];
4057 llvm::cl::alias optHelpAlias1("h",
4058 llvm::cl::desc("Alias for -help"),
4059 llvm::cl::aliasopt(*optHelp));
4060 llvm::cl::alias optHelpAlias2("?",
4061 llvm::cl::desc("Alias for -help"),
4062 llvm::cl::aliasopt(*optHelp));
4063
4064 llvm::cl::ParseCommandLineOptions(argc, argv, "rootcling");
4065
4066 std::string llvmResourceDir = std::string(gDriverConfig->fTROOT__GetEtcDir()) + "/cling";
4068 std::vector<const char *> clingArgsC;
4069 clingArgsC.push_back(executableFileName);
4070 // Help cling finds its runtime (RuntimeUniverse.h and such).
4071 clingArgsC.push_back("-I");
4072 clingArgsC.push_back(gDriverConfig->fTROOT__GetEtcDir());
4073
4074 //clingArgsC.push_back("-resource-dir");
4075 //clingArgsC.push_back(llvmResourceDir.c_str());
4076
4077 for (const std::string& Opt : gOptBareClingSink)
4078 clingArgsC.push_back(Opt.c_str());
4079
4080 auto interp = std::make_unique<cling::Interpreter>(clingArgsC.size(),
4081 &clingArgsC[0],
4082 llvmResourceDir.c_str());
4083 // FIXME: Diagnose when we have misspelled a flag. Currently we show no
4084 // diagnostic and report exit as success.
4085 return interp->getDiagnostics().hasFatalErrorOccurred();
4086 }
4087
4088 std::string dictname;
4089
4091 if (gOptRootBuild) {
4092 // running rootcling as part of the ROOT build for ROOT libraries.
4093 gBuildingROOT = true;
4094 }
4095 }
4096
4097 if (!gOptModuleMapFiles.empty() && !gOptCxxModule) {
4098 ROOT::TMetaUtils::Error("", "Option %s can be used only when option %s is specified.\n",
4099 gOptModuleMapFiles.ArgStr.str().c_str(),
4100 gOptCxxModule.ArgStr.str().c_str());
4101 std::cout << "\n";
4102 llvm::cl::PrintHelpMessage();
4103 return 1;
4104 }
4105
4106 // Set the default verbosity
4108 if (gOptVerboseLevel == v4)
4109 genreflex::verbose = true;
4110
4111 if (gOptReflex)
4112 isGenreflex = true;
4113
4114 if (!gOptLibListPrefix.empty()) {
4115 string filein = gOptLibListPrefix + ".in";
4116 FILE *fp;
4117 if ((fp = fopen(filein.c_str(), "r")) == nullptr) {
4118 ROOT::TMetaUtils::Error(nullptr, "%s: The input list file %s does not exist\n", executableFileName, filein.c_str());
4119 return 1;
4120 }
4121 fclose(fp);
4122 }
4123
4125 FILE *fp;
4126 if (!gOptIgnoreExistingDict && (fp = fopen(gOptDictionaryFileName.c_str(), "r")) != nullptr) {
4127 fclose(fp);
4128 if (!gOptForce) {
4129 ROOT::TMetaUtils::Error(nullptr, "%s: output file %s already exists\n", executableFileName, gOptDictionaryFileName.c_str());
4130 return 1;
4131 }
4132 }
4133
4134 // remove possible pathname to get the dictionary name
4135 if (gOptDictionaryFileName.size() > (PATH_MAX - 1)) {
4136 ROOT::TMetaUtils::Error(nullptr, "rootcling: dictionary name too long (more than %d characters): %s\n",
4137 (PATH_MAX - 1), gOptDictionaryFileName.c_str());
4138 return 1;
4139 }
4140
4141 dictname = llvm::sys::path::filename(gOptDictionaryFileName).str();
4142 }
4143
4144 if (gOptForce && dictname.empty()) {
4145 ROOT::TMetaUtils::Error(nullptr, "Inconsistent set of arguments detected: overwrite of dictionary file forced but no filename specified.\n");
4146 llvm::cl::PrintHelpMessage();
4147 return 1;
4148 }
4149
4150 std::vector<std::string> clingArgs;
4151 clingArgs.push_back(executableFileName);
4152 clingArgs.push_back("-iquote.");
4153
4154 bool dictSelection = !gOptNoDictSelection;
4155
4156 // Collect the diagnostic pragmas linked to the usage of -W
4157 // Workaround for ROOT-5656
4158 std::list<std::string> diagnosticPragmas = {"#pragma clang diagnostic ignored \"-Wdeprecated-declarations\""};
4159
4160 if (gOptFailOnWarnings) {
4161 using namespace ROOT::TMetaUtils;
4162 // If warnings are disabled with the current verbosity settings, lower
4163 // it so that the user sees the warning that caused the failure.
4164 if (GetErrorIgnoreLevel() > kWarning)
4165 GetErrorIgnoreLevel() = kWarning;
4166 GetWarningsAreErrors() = true;
4167 }
4168
4169 if (gOptISysRoot != "-") {
4170 if (gOptISysRoot.empty()) {
4171 ROOT::TMetaUtils::Error("", "isysroot specified without a value.\n");
4172 return 1;
4173 }
4174 clingArgs.push_back(gOptISysRoot.ArgStr.str());
4175 clingArgs.push_back(gOptISysRoot.ValueStr.str());
4176 }
4177
4178 // Check if we have a multi dict request but no target library
4179 if (gOptMultiDict && gOptSharedLibFileName.empty()) {
4180 ROOT::TMetaUtils::Error("", "Multidict requested but no target library. Please specify one with the -s argument.\n");
4181 return 1;
4182 }
4183
4184 for (const std::string &PPDefine : gOptPPDefines)
4185 clingArgs.push_back(std::string("-D") + PPDefine);
4186
4187 for (const std::string &PPUndefine : gOptPPUndefines)
4188 clingArgs.push_back(std::string("-U") + PPUndefine);
4189
4190 for (const std::string &IncludePath : gOptIncludePaths)
4191 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(IncludePath));
4192
4193 for (const std::string &IncludePath : gOptSysIncludePaths) {
4194 // Prevent mentioning compiler default include directories as -isystem
4195 // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129)
4196 if (std::find(gOptCompDefaultIncludePaths.begin(), gOptCompDefaultIncludePaths.end(), IncludePath)
4197 == gOptCompDefaultIncludePaths.end()) {
4198 clingArgs.push_back("-isystem");
4199 clingArgs.push_back(llvm::sys::path::convert_to_slash(IncludePath));
4200 }
4201 }
4202
4203 for (const std::string &WDiag : gOptWDiags) {
4204 const std::string FullWDiag = std::string("-W") + WDiag;
4205 // Suppress warning when compiling the dictionary, eg. gcc G__xxx.cxx
4206 CheckForMinusW(FullWDiag, diagnosticPragmas);
4207 // Suppress warning when compiling the input headers by cling.
4208 clingArgs.push_back(FullWDiag);
4209 }
4210
4211 std::string includeDir = llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetIncludeDir());
4212 clingArgs.push_back(std::string("-I") + includeDir);
4213
4214 std::vector<std::string> pcmArgs;
4215 for (size_t parg = 0, n = clingArgs.size(); parg < n; ++parg) {
4216 auto thisArg = clingArgs[parg];
4217 auto isInclude = ROOT::TMetaUtils::BeginsWith(thisArg,"-I");
4218 if (thisArg == "-c" ||
4219 (gOptNoIncludePaths && isInclude)) continue;
4220 // We now check if the include directories are not excluded
4221 if (isInclude) {
4222 unsigned int offset = 2; // -I is two characters. Now account for spaces
4223 char c = thisArg[offset];
4224 while (c == ' ') c = thisArg[++offset];
4225 auto excludePathsEnd = gOptExcludePaths.end();
4226 auto excludePathPos = std::find_if(gOptExcludePaths.begin(),
4227 excludePathsEnd,
4228 [&](const std::string& path){
4229 return ROOT::TMetaUtils::BeginsWith(&thisArg[offset], path);});
4230 if (excludePathsEnd != excludePathPos) continue;
4231 }
4232 pcmArgs.push_back(thisArg);
4233 }
4234
4235 // cling-only arguments
4236 clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(gDriverConfig->fTROOT__GetEtcDir()));
4237 // We do not want __ROOTCLING__ in the pch!
4238 if (!gOptGeneratePCH) {
4239 clingArgs.push_back("-D__ROOTCLING__");
4240 }
4241#ifdef R__MACOSX
4242 clingArgs.push_back("-DSYSTEM_TYPE_macosx");
4243#elif defined(R__WIN32)
4244 clingArgs.push_back("-DSYSTEM_TYPE_winnt");
4245
4246 // Prevent the following #error: The C++ Standard Library forbids macroizing keywords.
4247 clingArgs.push_back("-D_XKEYCHECK_H");
4248 // Tell windows.h not to #define min and max, it clashes with numerical_limits.
4249 clingArgs.push_back("-DNOMINMAX");
4250#else // assume UNIX
4251 clingArgs.push_back("-DSYSTEM_TYPE_unix");
4252#endif
4253
4254 clingArgs.push_back("-fsyntax-only");
4255#ifndef R__WIN32
4256 clingArgs.push_back("-fPIC");
4257#endif
4258 clingArgs.push_back("-Xclang");
4259 clingArgs.push_back("-fmodules-embed-all-files");
4260 clingArgs.push_back("-Xclang");
4261 clingArgs.push_back("-main-file-name");
4262 clingArgs.push_back("-Xclang");
4263 clingArgs.push_back((dictname + ".h").c_str());
4264
4266
4267 // FIXME: This line is from TModuleGenerator, but we can't reuse this code
4268 // at this point because TModuleGenerator needs a CompilerInstance (and we
4269 // currently create the arguments for creating said CompilerInstance).
4270 bool isPCH = (gOptDictionaryFileName.getValue() == "allDict.cxx");
4271 std::string outputFile;
4272 // Data is in 'outputFile', therefore in the same scope.
4273 llvm::StringRef moduleName;
4274 std::string vfsArg;
4275 // Adding -fmodules to the args will break lexing with __CINT__ defined,
4276 // and we actually do lex with __CINT__ and reuse this variable later,
4277 // we have to copy it now.
4278 auto clingArgsInterpreter = clingArgs;
4279
4280 if (gOptSharedLibFileName.empty()) {
4282 }
4283
4284 if (!isPCH && gOptCxxModule) {
4285 // We just pass -fmodules, the CIFactory will do the rest and configure
4286 // clang correctly once it sees this flag.
4287 clingArgsInterpreter.push_back("-fmodules");
4288 clingArgsInterpreter.push_back("-fno-implicit-module-maps");
4289
4290 for (const std::string &modulemap : gOptModuleMapFiles)
4291 clingArgsInterpreter.push_back("-fmodule-map-file=" + modulemap);
4292
4293 clingArgsInterpreter.push_back("-fmodule-map-file=" +
4294 std::string(gDriverConfig->fTROOT__GetIncludeDir()) +
4295 "/ROOT.modulemap");
4296 std::string ModuleMapCWD = ROOT::FoundationUtils::GetCurrentDir() + "/module.modulemap";
4297 if (llvm::sys::fs::exists(ModuleMapCWD))
4298 clingArgsInterpreter.push_back("-fmodule-map-file=" + ModuleMapCWD);
4299
4300 // Specify the module name that we can lookup the module in the modulemap.
4301 outputFile = llvm::sys::path::stem(gOptSharedLibFileName).str();
4302 // Try to get the module name in the modulemap based on the filepath.
4303 moduleName = GetModuleNameFromRdictName(outputFile);
4304
4305#ifdef _MSC_VER
4306 clingArgsInterpreter.push_back("-Xclang");
4307 clingArgsInterpreter.push_back("-fmodule-feature");
4308 clingArgsInterpreter.push_back("-Xclang");
4309 clingArgsInterpreter.push_back("msvc" + std::string(rootclingStringify(_MSC_VER)));
4310#endif
4311 clingArgsInterpreter.push_back("-fmodule-name=" + moduleName.str());
4312
4313 std::string moduleCachePath = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4314 // FIXME: This is a horrible workaround to fix the incremental builds.
4315 // The enumerated modules are built by clang impicitly based on #include of
4316 // a header which is contained within that module. The build system has
4317 // no way to track dependencies on them and trigger a rebuild.
4318 // A possible solution can be to disable completely the implicit build of
4319 // modules and each module to be built by rootcling. We need to teach
4320 // rootcling how to build modules with no IO support.
4321 if (moduleName == "Core") {
4323 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_intrinsics.pcm").str().c_str());
4324 remove((moduleCachePath + llvm::sys::path::get_separator() + "_Builtin_stddef_max_align_t.pcm").str().c_str());
4325 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime.pcm").str().c_str());
4326 remove((moduleCachePath + llvm::sys::path::get_separator() + "Cling_Runtime_Extra.pcm").str().c_str());
4327#ifdef R__WIN32
4328 remove((moduleCachePath + llvm::sys::path::get_separator() + "vcruntime.pcm").str().c_str());
4329 remove((moduleCachePath + llvm::sys::path::get_separator() + "services.pcm").str().c_str());
4330#endif
4331
4332#ifdef R__MACOSX
4333 remove((moduleCachePath + llvm::sys::path::get_separator() + "Darwin.pcm").str().c_str());
4334#else
4335 remove((moduleCachePath + llvm::sys::path::get_separator() + "libc.pcm").str().c_str());
4336#endif
4337 remove((moduleCachePath + llvm::sys::path::get_separator() + "std.pcm").str().c_str());
4338 remove((moduleCachePath + llvm::sys::path::get_separator() + "boost.pcm").str().c_str());
4339 remove((moduleCachePath + llvm::sys::path::get_separator() + "tinyxml2.pcm").str().c_str());
4340 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Config.pcm").str().c_str());
4341 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Rtypes.pcm").str().c_str());
4342 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_C.pcm").str().c_str());
4343 remove((moduleCachePath + llvm::sys::path::get_separator() + "ROOT_Foundation_Stage1_NoRTTI.pcm").str().c_str());
4344 } else if (moduleName == "MathCore") {
4345 remove((moduleCachePath + llvm::sys::path::get_separator() + "Vc.pcm").str().c_str());
4346 }
4347
4348 // Set the C++ modules output directory to the directory where we generate
4349 // the shared library.
4350 clingArgsInterpreter.push_back("-fmodules-cache-path=" + moduleCachePath);
4351 }
4352
4353 if (gOptVerboseLevel == v4)
4354 clingArgsInterpreter.push_back("-v");
4355
4356 // Convert arguments to a C array and check if they are sane
4357 std::vector<const char *> clingArgsC;
4358 for (auto const &clingArg : clingArgsInterpreter) {
4359 if (!IsCorrectClingArgument(clingArg)){
4360 std::cerr << "Argument \""<< clingArg << "\" is not a supported cling argument. "
4361 << "This could be mistyped rootcling argument. Please check the commandline.\n";
4362 return 1;
4363 }
4364 clingArgsC.push_back(clingArg.c_str());
4365 }
4366
4367
4368 std::unique_ptr<cling::Interpreter> owningInterpPtr;
4369 cling::Interpreter* interpPtr = nullptr;
4370
4371 std::list<std::string> filesIncludedByLinkdef;
4373#ifdef R__FAST_MATH
4374 // Same setting as in TCling.cxx.
4375 clingArgsC.push_back("-ffast-math");
4376#endif
4377
4378 owningInterpPtr.reset(new cling::Interpreter(clingArgsC.size(), &clingArgsC[0],
4379 llvmResourceDir.c_str()));
4380 interpPtr = owningInterpPtr.get();
4381 } else {
4382 // Pass the interpreter arguments to TCling's interpreter:
4383 clingArgsC.push_back("-resource-dir");
4384 clingArgsC.push_back(llvmResourceDir.c_str());
4385 clingArgsC.push_back(nullptr); // signal end of array
4386 const char ** &extraArgs = *gDriverConfig->fTROOT__GetExtraInterpreterArgs();
4387 extraArgs = &clingArgsC[1]; // skip binary name
4389 if (!isGenreflex && !gOptGeneratePCH) {
4390 std::unique_ptr<TRootClingCallbacks> callBacks (new TRootClingCallbacks(interpPtr, filesIncludedByLinkdef));
4391 interpPtr->setCallbacks(std::move(callBacks));
4392 }
4393 }
4394 cling::Interpreter &interp = *interpPtr;
4395 clang::CompilerInstance *CI = interp.getCI();
4396 // FIXME: Remove this once we switch cling to use the driver. This would handle -fmodules-embed-all-files for us.
4397 CI->getFrontendOpts().ModulesEmbedAllFiles = true;
4398 CI->getSourceManager().setAllFilesAreTransient(true);
4399
4400 clang::Preprocessor &PP = CI->getPreprocessor();
4401 clang::HeaderSearch &headerSearch = PP.getHeaderSearchInfo();
4402 clang::ModuleMap &moduleMap = headerSearch.getModuleMap();
4403 auto &diags = interp.getDiagnostics();
4404
4405 // Manually enable the module build remarks. We don't enable them via the
4406 // normal clang command line arg because otherwise we would get remarks for
4407 // building STL/libc when starting the interpreter in rootcling_stage1.
4408 // We can't prevent these diags in any other way because we can only attach
4409 // our own diag client now after the interpreter has already started.
4410 diags.setSeverity(clang::diag::remark_module_build, clang::diag::Severity::Remark, clang::SourceLocation());
4411
4412 // Attach our own diag client that listens to the module_build remarks from
4413 // clang to check that we don't build dictionary C++ modules implicitly.
4414 auto recordingClient = new CheckModuleBuildClient(diags.getClient(), diags.ownsClient(), moduleMap);
4415 diags.setClient(recordingClient, true);
4416
4418 ROOT::TMetaUtils::Info(nullptr, "\n");
4419 ROOT::TMetaUtils::Info(nullptr, "==== INTERPRETER CONFIGURATION ====\n");
4420 ROOT::TMetaUtils::Info(nullptr, "== Include paths\n");
4421 interp.DumpIncludePath();
4422 printf("\n\n");
4423 fflush(stdout);
4424
4425 ROOT::TMetaUtils::Info(nullptr, "== Included files\n");
4426 interp.printIncludedFiles(llvm::outs());
4427 llvm::outs() << "\n\n";
4428 llvm::outs().flush();
4429
4430 ROOT::TMetaUtils::Info(nullptr, "== Language Options\n");
4431 const clang::LangOptions& LangOpts
4432 = interp.getCI()->getASTContext().getLangOpts();
4433#define LANGOPT(Name, Bits, Default, Description) \
4434 ROOT::TMetaUtils::Info(nullptr, "%s = %d // %s\n", #Name, (int)LangOpts.Name, Description);
4435#define ENUM_LANGOPT(Name, Type, Bits, Default, Description)
4436#include "clang/Basic/LangOptions.def"
4437 ROOT::TMetaUtils::Info(nullptr, "==== END interpreter configuration ====\n\n");
4438 }
4439
4440 interp.getOptions().ErrorOut = true;
4441 interp.enableRawInput(true);
4442
4443 if (gOptCxxModule) {
4444 for (llvm::StringRef DepMod : gOptModuleDependencies) {
4445 if (DepMod.endswith("_rdict.pcm")) {
4446 ROOT::TMetaUtils::Warning(nullptr, "'%s' value is deprecated. Please use [<fullpath>]%s.pcm\n",
4447 DepMod.data(),
4448 GetModuleNameFromRdictName(DepMod).str().data());
4449 }
4450 DepMod = GetModuleNameFromRdictName(DepMod);
4451 // We might deserialize.
4452 cling::Interpreter::PushTransactionRAII RAII(&interp);
4453 if (!interp.loadModule(DepMod.str(), /*complain*/false)) {
4454 ROOT::TMetaUtils::Error(nullptr, "Module '%s' failed to load.\n",
4455 DepMod.data());
4456 }
4457 }
4458 }
4459
4460 if (!isGenreflex) { // rootcling
4461 // ROOTCINT uses to define a few header implicitly, we need to do it explicitly.
4462 if (interp.declare("#include <assert.h>\n"
4463 "#include \"Rtypes.h\"\n"
4464 "#include \"TObject.h\"") != cling::Interpreter::kSuccess
4465 ) {
4466 // There was an error.
4467 ROOT::TMetaUtils::Error(nullptr, "Error loading the default rootcling header files.\n");
4468 return 1;
4469 }
4470 }
4471
4472 if (interp.declare("#include <string>\n" // For the list of 'opaque' typedef to also include string.
4473 "#include <RtypesCore.h>\n" // For initializing TNormalizedCtxt.
4474 "namespace std {} using namespace std;") != cling::Interpreter::kSuccess) {
4475 ROOT::TMetaUtils::Error(nullptr, "Error loading the default header files.\n");
4476 return 1;
4477 }
4478
4479 // We are now ready (enough is loaded) to init the list of opaque typedefs.
4480 ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper());
4481 ROOT::TMetaUtils::TClingLookupHelper helper(interp, normCtxt, nullptr, nullptr, nullptr);
4482 TClassEdit::Init(&helper);
4483
4484 // flags used only for the pragma parser:
4485 clingArgs.push_back("-D__CINT__");
4486 clingArgs.push_back("-D__MAKECINT__");
4487
4488 AddPlatformDefines(clingArgs);
4489
4490 std::string currentDirectory = ROOT::FoundationUtils::GetCurrentDir();
4491
4492 std::string interpPragmaSource;
4493 std::string includeForSource;
4494 std::string interpreterDeclarations;
4495 std::string linkdef;
4496
4497 for (size_t i = 0, e = gOptDictionaryHeaderFiles.size(); i < e; ++i) {
4498 const std::string& optHeaderFileName = gOptDictionaryHeaderFiles[i];
4499 bool isSelectionFile = IsSelectionFile(optHeaderFileName.c_str());
4500
4501 if (isSelectionFile) {
4502 if (i == e - 1) {
4503 linkdef = optHeaderFileName;
4504 } else { // if the linkdef was not last, issue an error.
4505 ROOT::TMetaUtils::Error(nullptr, "%s: %s must be last file on command line\n",
4506 executableFileName, optHeaderFileName.c_str());
4507 return 1;
4508 }
4509 }
4510
4511 // coverity[tainted_data] The OS should already limit the argument size, so we are safe here
4512 std::string fullheader(optHeaderFileName);
4513 // Strip any trailing + which is only used by GeneratedLinkdef.h which currently
4514 // use directly argv.
4515 if (fullheader[fullheader.length() - 1] == '+') {
4516 fullheader.erase(fullheader.length() - 1);
4517 }
4518 std::string header(
4519 isSelectionFile ? fullheader : ROOT::FoundationUtils::MakePathRelative(fullheader, currentDirectory, gBuildingROOT));
4520
4521 interpPragmaSource += std::string("#include \"") + header + "\"\n";
4522 if (!isSelectionFile) {
4523 // In order to not have to add the equivalent to -I${PWD} to the
4524 // command line, include the complete file name, even if it is a
4525 // full pathname, when we write it down in the dictionary.
4526 // Note: have -I${PWD} means in that (at least in the case of
4527 // ACLiC) we inadvertently pick local file that have the same
4528 // name as system header (e.g. new or list) and -iquote has not
4529 // equivalent on some platforms.
4530 includeForSource += std::string("#include \"") + fullheader + "\"\n";
4531 pcmArgs.push_back(header);
4532 } else if (!IsSelectionXml(optHeaderFileName.c_str())) {
4533 interpreterDeclarations += std::string("#include \"") + header + "\"\n";
4534 }
4535 }
4536
4537 if (gOptUmbrellaInput) {
4538 bool hasSelectionFile = !linkdef.empty();
4539 unsigned expectedHeaderFilesSize = 1 + hasSelectionFile;
4540 if (gOptDictionaryHeaderFiles.size() > expectedHeaderFilesSize)
4541 ROOT::TMetaUtils::Error(nullptr, "Option %s used but more than one header file specified.\n",
4542 gOptUmbrellaInput.ArgStr.data());
4543 }
4544
4545 // We have a multiDict request. This implies generating a pcm which is of the form
4546 // dictName_libname_rdict.pcm
4547 if (gOptMultiDict) {
4548
4549 std::string newName = llvm::sys::path::parent_path(gOptSharedLibFileName).str();
4550 if (!newName.empty())
4551 newName += gPathSeparator;
4552 newName += llvm::sys::path::stem(gOptSharedLibFileName);
4553 newName += "_";
4554 newName += llvm::sys::path::stem(gOptDictionaryFileName);
4555 newName += llvm::sys::path::extension(gOptSharedLibFileName);
4556 gOptSharedLibFileName = newName;
4557 }
4558
4559 // Until the module are actually enabled in ROOT, we need to register
4560 // the 'current' directory to make it relocatable (i.e. have a way
4561 // to find the headers).
4563 string incCurDir = "-I";
4564 incCurDir += currentDirectory;
4565 pcmArgs.push_back(incCurDir);
4566 }
4567
4568 // Add the diagnostic pragmas distilled from the -Wno-xyz
4569 {
4570 std::stringstream res;
4571 const char* delim="\n";
4572 std::copy(diagnosticPragmas.begin(),
4573 diagnosticPragmas.end(),
4574 std::ostream_iterator<std::string>(res, delim));
4575 if (interp.declare(res.str()) != cling::Interpreter::kSuccess) {
4576 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -Wno-xyz flags as pragmas:\n%s", res.str().c_str());
4577 return 1;
4578 }
4579 }
4580
4581 class IgnoringPragmaHandler: public clang::PragmaNamespace {
4582 public:
4583 IgnoringPragmaHandler(const char* pragma):
4584 clang::PragmaNamespace(pragma) {}
4585 void HandlePragma(clang::Preprocessor &PP,
4586 clang::PragmaIntroducer Introducer,
4587 clang::Token &tok) override {
4588 PP.DiscardUntilEndOfDirective();
4589 }
4590 };
4591
4592 // Ignore these #pragmas to suppress "unknown pragma" warnings.
4593 // See LinkdefReader.cxx.
4594 PP.AddPragmaHandler(new IgnoringPragmaHandler("link"));
4595 PP.AddPragmaHandler(new IgnoringPragmaHandler("extra_include"));
4596 PP.AddPragmaHandler(new IgnoringPragmaHandler("read"));
4597 PP.AddPragmaHandler(new IgnoringPragmaHandler("create"));
4598
4599 if (!interpreterDeclarations.empty() &&
4600 interp.declare(interpreterDeclarations) != cling::Interpreter::kSuccess) {
4601 ROOT::TMetaUtils::Error(nullptr, "%s: Linkdef compilation failure\n", executableFileName);
4602 return 1;
4603 }
4604
4605
4606 TModuleGenerator modGen(interp.getCI(),
4610
4611 if (!gDriverConfig->fBuildingROOTStage1 && !filesIncludedByLinkdef.empty()) {
4612 pcmArgs.push_back(linkdef);
4613 }
4614
4615 modGen.ParseArgs(pcmArgs);
4616
4618 // Forward the -I, -D, -U
4619 for (const std::string & inclPath : modGen.GetIncludePaths()) {
4620 interp.AddIncludePath(inclPath);
4621 }
4622 std::stringstream definesUndefinesStr;
4623 modGen.WritePPDefines(definesUndefinesStr);
4624 modGen.WritePPUndefines(definesUndefinesStr);
4625 if (!definesUndefinesStr.str().empty()) {
4626 if (interp.declare(definesUndefinesStr.str()) != cling::Interpreter::kSuccess) {
4627 ROOT::TMetaUtils::Error(nullptr, "Failed to parse -D, -U flags as preprocessor directives:\n%s", definesUndefinesStr.str().c_str());
4628 return 1;
4629 }
4630 }
4631 }
4632
4633 if (!InjectModuleUtilHeader(executableFileName, modGen, interp, true)
4634 || !InjectModuleUtilHeader(executableFileName, modGen, interp, false)) {
4635 return 1;
4636 }
4637
4638 if (linkdef.empty()) {
4639 // Generate autolinkdef
4640 GenerateLinkdef(gOptDictionaryHeaderFiles, interpPragmaSource);
4641 }
4642
4643 // Check if code goes to stdout or rootcling file
4644 std::ofstream fileout;
4645 string main_dictname(gOptDictionaryFileName.getValue());
4646 std::ostream *splitDictStream = nullptr;
4647 std::unique_ptr<std::ostream> splitDeleter(nullptr);
4648 // Store the temp files
4649 tempFileNamesCatalog tmpCatalog;
4651 if (!gOptDictionaryFileName.empty()) {
4652 tmpCatalog.addFileName(gOptDictionaryFileName.getValue());
4653 fileout.open(gOptDictionaryFileName.c_str());
4654 if (!fileout) {
4655 ROOT::TMetaUtils::Error(nullptr, "rootcling: failed to open %s in main\n",
4656 gOptDictionaryFileName.c_str());
4657 return 1;
4658 }
4659 }
4660 }
4661
4662 std::ostream &dictStream = (!gOptIgnoreExistingDict && !gOptDictionaryFileName.empty()) ? fileout : std::cout;
4663 bool isACLiC = gOptDictionaryFileName.getValue().find("_ACLiC_dict") != std::string::npos;
4664
4666 // Now generate a second stream for the split dictionary if it is necessary
4667 if (gOptSplit) {
4668 splitDictStream = CreateStreamPtrForSplitDict(gOptDictionaryFileName.getValue(), tmpCatalog);
4669 splitDeleter.reset(splitDictStream);
4670 } else {
4671 splitDictStream = &dictStream;
4672 }
4673
4674 size_t dh = main_dictname.rfind('.');
4675 if (dh != std::string::npos) {
4676 main_dictname.erase(dh);
4677 }
4678 // Need to replace all the characters not allowed in a symbol ...
4679 std::string main_dictname_copy(main_dictname);
4680 TMetaUtils::GetCppName(main_dictname, main_dictname_copy.c_str());
4681
4682 CreateDictHeader(dictStream, main_dictname);
4683 if (gOptSplit)
4684 CreateDictHeader(*splitDictStream, main_dictname);
4685
4686 if (!gOptNoGlobalUsingStd) {
4687 // ACLiC'ed macros might rely on `using namespace std` in front of user headers
4688 if (isACLiC) {
4689 AddNamespaceSTDdeclaration(dictStream);
4690 if (gOptSplit) {
4691 AddNamespaceSTDdeclaration(*splitDictStream);
4692 }
4693 }
4694 }
4695 }
4696
4697 //---------------------------------------------------------------------------
4698 // Parse the linkdef or selection.xml file.
4699 /////////////////////////////////////////////////////////////////////////////
4700
4701 string linkdefFilename;
4702 if (linkdef.empty()) {
4703 linkdefFilename = "in memory";
4704 } else {
4705 bool found = Which(interp, linkdef.c_str(), linkdefFilename);
4706 if (!found) {
4707 ROOT::TMetaUtils::Error(nullptr, "%s: cannot open linkdef file %s\n", executableFileName, linkdef.c_str());
4708 return 1;
4709 }
4710 }
4711
4712 // Exclude string not to re-generate the dictionary
4713 std::vector<std::pair<std::string, std::string>> namesForExclusion;
4714 if (!gBuildingROOT) {
4715 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::name, "std::string"));
4716 namesForExclusion.push_back(std::make_pair(ROOT::TMetaUtils::propNames::pattern, "ROOT::Meta::Selection*"));
4717 }
4718
4719 SelectionRules selectionRules(interp, normCtxt, namesForExclusion);
4720
4721 std::string extraIncludes;
4722
4723 ROOT::TMetaUtils::RConstructorTypes constructorTypes;
4724
4725 // Select using DictSelection
4726 const unsigned int selRulesInitialSize = selectionRules.Size();
4727 if (dictSelection && !gOptGeneratePCH)
4728 ROOT::Internal::DictSelectionReader dictSelReader(interp, selectionRules, CI->getASTContext(), normCtxt);
4729
4730 bool dictSelRulesPresent = selectionRules.Size() > selRulesInitialSize;
4731
4732 bool isSelXML = IsSelectionXml(linkdefFilename.c_str());
4733
4734 int rootclingRetCode(0);
4735
4736 if (linkdef.empty() || ROOT::TMetaUtils::IsLinkdefFile(linkdefFilename.c_str())) {
4737 if (ROOT::TMetaUtils::IsLinkdefFile(linkdefFilename.c_str())) {
4738 std::ifstream file(linkdefFilename.c_str());
4739 if (file.is_open()) {
4740 ROOT::TMetaUtils::Info(nullptr, "Using linkdef file: %s\n", linkdefFilename.c_str());
4741 file.close();
4742 } else {
4743 ROOT::TMetaUtils::Error(nullptr, "Linkdef file %s couldn't be opened!\n", linkdefFilename.c_str());
4744 }
4745
4747 }
4748 // If there is no linkdef file, we added the 'default' #pragma to
4749 // interpPragmaSource and we still need to process it.
4750
4751 LinkdefReader ldefr(interp, constructorTypes);
4752 clingArgs.push_back("-Ietc/cling/cint"); // For multiset and multimap
4753
4754 if (!ldefr.Parse(selectionRules, interpPragmaSource, clingArgs,
4755 llvmResourceDir.c_str())) {
4756 ROOT::TMetaUtils::Error(nullptr, "Parsing #pragma failed %s\n", linkdefFilename.c_str());
4757 rootclingRetCode += 1;
4758 } else {
4759 ROOT::TMetaUtils::Info(nullptr, "#pragma successfully parsed.\n");
4760 }
4761
4762 if (!ldefr.LoadIncludes(extraIncludes)) {
4763 ROOT::TMetaUtils::Error(nullptr, "Error loading the #pragma extra_include.\n");
4764 return 1;
4765 }
4766
4767 } else if (isSelXML) {
4768
4770
4771 std::ifstream file(linkdefFilename.c_str());
4772 if (file.is_open()) {
4773 ROOT::TMetaUtils::Info(nullptr, "Selection XML file\n");
4774
4775 XMLReader xmlr(interp);
4776 if (!xmlr.Parse(linkdefFilename.c_str(), selectionRules)) {
4777 ROOT::TMetaUtils::Error(nullptr, "Parsing XML file %s\n", linkdefFilename.c_str());
4778 return 1; // Return here to propagate the failure up to the build system
4779 } else {
4780 ROOT::TMetaUtils::Info(nullptr, "XML file successfully parsed\n");
4781 }
4782 file.close();
4783 } else {
4784 ROOT::TMetaUtils::Error(nullptr, "XML file %s couldn't be opened!\n", linkdefFilename.c_str());
4785 }
4786
4787 } else {
4788
4789 ROOT::TMetaUtils::Error(nullptr, "Unrecognized selection file: %s\n", linkdefFilename.c_str());
4790
4791 }
4792
4793 // Speed up the operations with rules
4794 selectionRules.FillCache();
4795 selectionRules.Optimize();
4796
4797 if (isGenreflex){
4798 if (0 != selectionRules.CheckDuplicates()){
4799 return 1;
4800 }
4801 }
4802
4803 // If we want to validate the selection only, we just quit.
4805 return 0;
4806
4807 //---------------------------------------------------------------------------
4808 // Write schema evolution related headers and declarations
4809 /////////////////////////////////////////////////////////////////////////////
4810
4811 if ((!ROOT::gReadRules.empty() || !ROOT::gReadRawRules.empty()) && !gOptIgnoreExistingDict) {
4812 dictStream << "#include \"TBuffer.h\"\n"
4813 << "#include \"TVirtualObject.h\"\n"
4814 << "#include <vector>\n"
4815 << "#include \"TSchemaHelper.h\"\n\n";
4816
4817 std::list<std::string> includes;
4818 GetRuleIncludes(includes);
4819 for (auto & incFile : includes) {
4820 dictStream << "#include <" << incFile << ">" << std::endl;
4821 }
4822 dictStream << std::endl;
4823 }
4824
4825 selectionRules.SearchNames(interp);
4826
4827 int scannerVerbLevel = 0;
4828 {
4829 using namespace ROOT::TMetaUtils;
4830 scannerVerbLevel = GetErrorIgnoreLevel() == kInfo; // 1 if true, 0 if false
4831 if (isGenreflex){
4832 scannerVerbLevel = GetErrorIgnoreLevel() < kWarning;
4833 }
4834 }
4835
4836 // Select the type of scan
4837 auto scanType = RScanner::EScanType::kNormal;
4838 if (gOptGeneratePCH)
4840 if (dictSelection)
4842
4843 RScanner scan(selectionRules,
4844 scanType,
4845 interp,
4846 normCtxt,
4847 scannerVerbLevel);
4848
4849 // If needed initialize the autoloading hook
4850 if (!gOptLibListPrefix.empty()) {
4853 }
4854
4855 scan.Scan(CI->getASTContext());
4856
4857 bool has_input_error = false;
4858
4860 selectionRules.PrintSelectionRules();
4861
4863 !gOptGeneratePCH &&
4864 !dictSelRulesPresent &&
4865 !selectionRules.AreAllSelectionRulesUsed()) {
4866 ROOT::TMetaUtils::Warning(nullptr, "Not all selection rules are used!\n");
4867 }
4868
4869 if (!gOptGeneratePCH){
4870 rootclingRetCode += CheckForUnsupportedClasses(scan.fSelectedClasses);
4871 if (rootclingRetCode) return rootclingRetCode;
4872 }
4873
4874 // SELECTION LOOP
4875 // Check for error in the class layout before doing anything else.
4876 for (auto const & annRcd : scan.fSelectedClasses) {
4877 if (ROOT::TMetaUtils::ClassInfo__HasMethod(annRcd, "Streamer", interp)) {
4878 if (annRcd.RequestNoInputOperator()) {
4879 int version = ROOT::TMetaUtils::GetClassVersion(annRcd, interp);
4880 if (version != 0) {
4881 // Only Check for input operator is the object is I/O has
4882 // been requested.
4883 has_input_error |= CheckInputOperator(annRcd, interp);
4884 }
4885 }
4886 }
4887 has_input_error |= !CheckClassDef(*annRcd, interp);
4888 }
4889
4890 if (has_input_error) {
4891 // Be a little bit makefile friendly and remove the dictionary in case of error.
4892 // We could add an option -k to keep the file even in case of error.
4893 exit(1);
4894 }
4895
4896 //---------------------------------------------------------------------------
4897 // Write all the necessary #include
4898 /////////////////////////////////////////////////////////////////////////////
4900 for (auto &&includedFromLinkdef : filesIncludedByLinkdef) {
4901 includeForSource += "#include \"" + includedFromLinkdef + "\"\n";
4902 }
4903 }
4904
4905 if (!gOptGeneratePCH) {
4907 GenerateNecessaryIncludes(dictStream, includeForSource, extraIncludes);
4908 if (gOptSplit) {
4909 GenerateNecessaryIncludes(*splitDictStream, includeForSource, extraIncludes);
4910 }
4911 }
4912 if (!gOptNoGlobalUsingStd) {
4913 // ACLiC'ed macros might have relied on `using namespace std` in front of user headers
4914 if (!isACLiC) {
4915 AddNamespaceSTDdeclaration(dictStream);
4916 if (gOptSplit) {
4917 AddNamespaceSTDdeclaration(*splitDictStream);
4918 }
4919 }
4920 }
4923 }
4924
4925 // The order of addition to the list of constructor type
4926 // is significant. The list is sorted by with the highest
4927 // priority first.
4928 if (!gOptInterpreterOnly) {
4929 constructorTypes.emplace_back("TRootIOCtor", interp);
4930 constructorTypes.emplace_back("__void__", interp); // ROOT-7723
4931 constructorTypes.emplace_back("", interp);
4932 }
4933 }
4935 AddNamespaceSTDdeclaration(dictStream);
4936
4937 if (gOptSplit && splitDictStream) {
4938 AddNamespaceSTDdeclaration(*splitDictStream);
4939 }
4940 }
4941
4942 if (gOptGeneratePCH) {
4943 AnnotateAllDeclsForPCH(interp, scan);
4944 } else if (gOptInterpreterOnly) {
4945 rootclingRetCode += CheckClassesForInterpreterOnlyDicts(interp, scan);
4946 // generate an empty pcm nevertheless for consistency
4947 // Negate as true is 1 and true is returned in case of success.
4949 rootclingRetCode += FinalizeStreamerInfoWriting(interp);
4950 }
4951 } else {
4952 rootclingRetCode += GenerateFullDict(*splitDictStream,
4953 interp,
4954 scan,
4955 constructorTypes,
4956 gOptSplit,
4957 isGenreflex,
4958 isSelXML,
4960 }
4961
4962 if (rootclingRetCode != 0) {
4963 return rootclingRetCode;
4964 }
4965
4966 // Now we have done all our looping and thus all the possible
4967 // annotation, let's write the pcms.
4968 HeadersDeclsMap_t headersClassesMap;
4969 HeadersDeclsMap_t headersDeclsMap;
4971 const std::string fwdDeclnArgsToKeepString(GetFwdDeclnArgsToKeepString(normCtxt, interp));
4972
4974 scan.fSelectedTypedefs,
4975 scan.fSelectedFunctions,
4976 scan.fSelectedVariables,
4977 scan.fSelectedEnums,
4978 headersClassesMap,
4979 headersDeclsMap,
4980 interp);
4981
4982 std::string detectedUmbrella;
4983 for (auto & arg : pcmArgs) {
4985 detectedUmbrella = arg;
4986 break;
4987 }
4988 }
4989
4991 headersDeclsMap.clear();
4992 }
4993
4994
4995 std::string headersClassesMapString = "\"\"";
4996 std::string fwdDeclsString = "\"\"";
4997 if (!gOptCxxModule) {
4998 headersClassesMapString = GenerateStringFromHeadersForClasses(headersDeclsMap,
4999 detectedUmbrella,
5000 true);
5003 fwdDeclsString = GenerateFwdDeclString(scan, interp);
5004 }
5005 }
5006 modGen.WriteRegistrationSource(dictStream, fwdDeclnArgsToKeepString, headersClassesMapString, fwdDeclsString,
5007 extraIncludes, gOptCxxModule);
5008 // If we just want to inline the input header, we don't need
5009 // to generate any files.
5010 if (!gOptInlineInput) {
5011 // Write the module/PCH depending on what mode we are on
5012 if (modGen.IsPCH()) {
5013 if (!GenerateAllDict(modGen, CI, currentDirectory)) return 1;
5014 } else if (gOptCxxModule) {
5015 if (!CheckModuleValid(modGen, llvmResourceDir, interp, linkdefFilename, moduleName.str()))
5016 return 1;
5017 }
5018 }
5019 }
5020
5021
5022 if (!gOptLibListPrefix.empty()) {
5023 string liblist_filename = gOptLibListPrefix + ".out";
5024
5025 ofstream outputfile(liblist_filename.c_str(), ios::out);
5026 if (!outputfile) {
5027 ROOT::TMetaUtils::Error(nullptr, "%s: Unable to open output lib file %s\n",
5028 executableFileName, liblist_filename.c_str());
5029 } else {
5030 const size_t endStr = gLibsNeeded.find_last_not_of(" \t");
5031 outputfile << gLibsNeeded.substr(0, endStr + 1) << endl;
5032 // Add explicit delimiter
5033 outputfile << "# Now the list of classes\n";
5034 // SELECTION LOOP
5035 for (auto const & annRcd : scan.fSelectedClasses) {
5036 // Shouldn't it be GetLong64_Name( cl_input.GetNormalizedName() )
5037 // or maybe we should be normalizing to turn directly all long long into Long64_t
5038 outputfile << annRcd.GetNormalizedName() << endl;
5039 }
5040 }
5041 }
5042
5043 // Check for errors in module generation
5044 rootclingRetCode += modGen.GetErrorCount();
5045 if (0 != rootclingRetCode) return rootclingRetCode;
5046
5047 // Create the rootmap file
5048 std::string rootmapLibName = std::accumulate(gOptRootmapLibNames.begin(),
5049 gOptRootmapLibNames.end(),
5050 std::string(),
5051 [](const std::string & a, const std::string & b) -> std::string {
5052 if (a.empty()) return b;
5053 else return a + " " + b;
5054 });
5055
5056 bool rootMapNeeded = !gOptRootMapFileName.empty() || !rootmapLibName.empty();
5057
5058 std::list<std::string> classesNames;
5059 std::list<std::string> classesNamesForRootmap;
5060 std::list<std::string> classesDefsList;
5061
5062 rootclingRetCode = ExtractClassesListAndDeclLines(scan,
5063 classesNames,
5064 classesNamesForRootmap,
5065 classesDefsList,
5066 interp);
5067
5068 std::list<std::string> enumNames;
5069 rootclingRetCode += ExtractAutoloadKeys(enumNames,
5070 scan.fSelectedEnums,
5071 interp);
5072
5073 std::list<std::string> varNames;
5074 rootclingRetCode += ExtractAutoloadKeys(varNames,
5075 scan.fSelectedVariables,
5076 interp);
5077
5078 if (0 != rootclingRetCode) return rootclingRetCode;
5079
5080 // Create the rootmapfile if needed
5081 if (rootMapNeeded) {
5082
5083 std::list<std::string> nsNames;
5084
5085 ExtractSelectedNamespaces(scan, nsNames);
5086
5088 rootmapLibName);
5089
5090 ROOT::TMetaUtils::Info(nullptr, "Rootmap file name %s and lib name(s) \"%s\"\n",
5091 gOptRootMapFileName.c_str(),
5092 rootmapLibName.c_str());
5093
5094 tmpCatalog.addFileName(gOptRootMapFileName);
5095 std::unordered_set<std::string> headersToIgnore;
5096 if (gOptInlineInput)
5097 for (const std::string& optHeaderFileName : gOptDictionaryHeaderFiles)
5098 headersToIgnore.insert(optHeaderFileName.c_str());
5099
5100 std::list<std::string> typedefsRootmapLines;
5101 rootclingRetCode += ExtractAutoloadKeys(typedefsRootmapLines,
5102 scan.fSelectedTypedefs,
5103 interp);
5104
5105 rootclingRetCode += CreateNewRootMapFile(gOptRootMapFileName,
5106 rootmapLibName,
5107 classesDefsList,
5108 classesNamesForRootmap,
5109 nsNames,
5110 typedefsRootmapLines,
5111 enumNames,
5112 varNames,
5113 headersClassesMap,
5114 headersToIgnore);
5115
5116 if (0 != rootclingRetCode) return 1;
5117 }
5118
5120 tmpCatalog.dump();
5121
5122 // Manually call end of translation unit because we never call the
5123 // appropriate deconstructors in the interpreter. This writes out the C++
5124 // module file that we currently generate.
5125 {
5126 cling::Interpreter::PushTransactionRAII RAII(&interp);
5127 CI->getSema().getASTConsumer().HandleTranslationUnit(CI->getSema().getASTContext());
5128 }
5129
5130 // Add the warnings
5131 rootclingRetCode += ROOT::TMetaUtils::GetNumberOfErrors();
5132
5133 // make sure the file is closed before committing
5134 fileout.close();
5135
5136 // Before returning, rename the files if no errors occurred
5137 // otherwise clean them to avoid remnants (see ROOT-10015)
5138 if(rootclingRetCode == 0) {
5139 rootclingRetCode += tmpCatalog.commit();
5140 } else {
5141 tmpCatalog.clean();
5142 }
5143
5144 return rootclingRetCode;
5145
5146}
5147
5148namespace genreflex {
5149
5150////////////////////////////////////////////////////////////////////////////////
5151/// Loop on arguments: stop at the first which starts with -
5152
5153 unsigned int checkHeadersNames(std::vector<std::string> &headersNames)
5154 {
5155 unsigned int numberOfHeaders = 0;
5156 for (std::vector<std::string>::iterator it = headersNames.begin();
5157 it != headersNames.end(); ++it) {
5158 const std::string headername(*it);
5159 if (ROOT::TMetaUtils::IsHeaderName(headername)) {
5160 numberOfHeaders++;
5161 } else {
5163 "*** genreflex: %s is not a valid header name (.h and .hpp extensions expected)!\n",
5164 headername.c_str());
5165 }
5166 }
5167 return numberOfHeaders;
5168 }
5169
5170////////////////////////////////////////////////////////////////////////////////
5171/// Extract the arguments from the command line
5172
5173 unsigned int extractArgs(int argc, char **argv, std::vector<std::string> &args)
5174 {
5175 // loop on argv, spot strings which are not preceded by something
5176 unsigned int argvCounter = 0;
5177 for (int i = 1; i < argc; ++i) {
5178 if (!ROOT::TMetaUtils::BeginsWith(argv[i - 1], "-") && // so, if preceding element starts with -, this is a value for an option
5179 !ROOT::TMetaUtils::BeginsWith(argv[i], "-")) { // and the element itself is not an option
5180 args.push_back(argv[i]);
5181 argvCounter++;
5182 } else if (argvCounter) {
5183 argv[i - argvCounter] = argv[i];
5184 }
5185 }
5186
5187 // Some debug
5188 if (genreflex::verbose) {
5189 int i = 0;
5190 std::cout << "Args: \n";
5191 for (std::vector<std::string>::iterator it = args.begin();
5192 it < args.end(); ++it) {
5193 std::cout << i << ") " << *it << std::endl;
5194 ++i;
5195 }
5196
5197 }
5198
5199 return argvCounter;
5200 }
5201
5202////////////////////////////////////////////////////////////////////////////////
5203
5204 void changeExtension(std::string &filename, const std::string &newExtension)
5205 {
5206 size_t result = filename.find_last_of('.');
5207 if (std::string::npos != result) {
5208 filename.erase(result);
5209 filename.append(newExtension);
5210 }
5211
5212 }
5213
5214////////////////////////////////////////////////////////////////////////////////
5215/// The caller is responsible for deleting the string!
5216
5217 char *string2charptr(const std::string &str)
5218 {
5219 const unsigned int size(str.size());
5220 char *a = new char[size + 1];
5221 a[size] = 0;
5222 memcpy(a, str.c_str(), size);
5223 return a;
5224 }
5225
5226////////////////////////////////////////////////////////////////////////////////
5227/// Replace the extension with "_rflx.cpp"
5228
5229 void header2outputName(std::string &fileName)
5230 {
5231 changeExtension(fileName, "_rflx.cpp");
5232 }
5233
5234////////////////////////////////////////////////////////////////////////////////
5235/// Get a proper name for the output file
5236
5237 void headers2outputsNames(const std::vector<std::string> &headersNames,
5238 std::vector<std::string> &ofilesnames)
5239 {
5240 ofilesnames.reserve(headersNames.size());
5241
5242 for (std::vector<std::string>::const_iterator it = headersNames.begin();
5243 it != headersNames.end(); ++it) {
5244 std::string ofilename(*it);
5245 header2outputName(ofilename);
5246 ofilesnames.push_back(ofilename);
5247 }
5248 }
5249
5250////////////////////////////////////////////////////////////////////////////////
5251
5252 void AddToArgVector(std::vector<char *> &argvVector,
5253 const std::vector<std::string> &argsToBeAdded,
5254 const std::string &optName = "")
5255 {
5256 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5257 it != argsToBeAdded.end(); ++it) {
5258 argvVector.push_back(string2charptr(optName + *it));
5259 }
5260 }
5261
5262////////////////////////////////////////////////////////////////////////////////
5263
5264 void AddToArgVectorSplit(std::vector<char *> &argvVector,
5265 const std::vector<std::string> &argsToBeAdded,
5266 const std::string &optName = "")
5267 {
5268 for (std::vector<std::string>::const_iterator it = argsToBeAdded.begin();
5269 it != argsToBeAdded.end(); ++it) {
5270 if (optName.length()) {
5271 argvVector.push_back(string2charptr(optName));
5272 }
5273 argvVector.push_back(string2charptr(*it));
5274 }
5275 }
5276
5277////////////////////////////////////////////////////////////////////////////////
5278
5279 int invokeRootCling(const std::string &verbosity,
5280 const std::string &selectionFileName,
5281 const std::string &targetLibName,
5282 bool multiDict,
5283 const std::vector<std::string> &pcmsNames,
5284 const std::vector<std::string> &includes,
5285 const std::vector<std::string> &preprocDefines,
5286 const std::vector<std::string> &preprocUndefines,
5287 const std::vector<std::string> &warnings,
5288 const std::string &rootmapFileName,
5289 const std::string &rootmapLibName,
5290 bool interpreteronly,
5291 bool doSplit,
5292 bool isCxxmodule,
5293 bool writeEmptyRootPCM,
5294 bool selSyntaxOnly,
5295 bool noIncludePaths,
5296 bool noGlobalUsingStd,
5297 const std::vector<std::string> &headersNames,
5298 bool failOnWarnings,
5299 const std::string &ofilename)
5300 {
5301 // Prepare and invoke the commandline to invoke rootcling
5302
5303 std::vector<char *> argvVector;
5304
5305 argvVector.push_back(string2charptr("rootcling"));
5306 argvVector.push_back(string2charptr(verbosity));
5307 argvVector.push_back(string2charptr("-f"));
5308 argvVector.push_back(string2charptr(ofilename));
5309
5310 if (isCxxmodule)
5311 argvVector.push_back(string2charptr("-cxxmodule"));
5312
5313 // Extract the path to the dictionary
5314 std::string dictLocation;
5315 ExtractFilePath(ofilename, dictLocation);
5316
5317 // Rootmaps
5318
5319 // Prepare the correct rootmap libname if not already set.
5320 std::string newRootmapLibName(rootmapLibName);
5321 if (!rootmapFileName.empty() && newRootmapLibName.empty()) {
5322 if (headersNames.size() != 1) {
5324 "*** genreflex: No rootmap lib and several header specified!\n");
5325 }
5326 std::string cleanHeaderName = ExtractFileName(headersNames[0]);
5327 newRootmapLibName = "lib";
5328 newRootmapLibName += cleanHeaderName;
5329 changeExtension(newRootmapLibName, gLibraryExtension);
5330 }
5331
5332 // Prepend to the rootmap the designed directory of the dictionary
5333 // if no path is specified for the rootmap itself
5334 std::string newRootmapFileName(rootmapFileName);
5335 if (!newRootmapFileName.empty() && !HasPath(newRootmapFileName)) {
5336 newRootmapFileName = dictLocation + newRootmapFileName;
5337 }
5338
5339
5340 // RootMap filename
5341 if (!newRootmapFileName.empty()) {
5342 argvVector.push_back(string2charptr("-rmf"));
5343 argvVector.push_back(string2charptr(newRootmapFileName));
5344 }
5345
5346 // RootMap Lib filename
5347 if (!newRootmapLibName.empty()) {
5348 argvVector.push_back(string2charptr("-rml"));
5349 argvVector.push_back(string2charptr(newRootmapLibName));
5350 }
5351
5352 // Interpreter only dictionaries
5353 if (interpreteronly)
5354 argvVector.push_back(string2charptr("-interpreteronly"));
5355
5356 // Split dictionaries
5357 if (doSplit)
5358 argvVector.push_back(string2charptr("-split"));
5359
5360 // Targetlib
5361 if (!targetLibName.empty()) {
5362 argvVector.push_back(string2charptr("-s"));
5363 argvVector.push_back(string2charptr(targetLibName));
5364 }
5365
5366 // Multidict support
5367 if (multiDict)
5368 argvVector.push_back(string2charptr("-multiDict"));
5369
5370 // Don't declare "using namespace std"
5371 if (noGlobalUsingStd)
5372 argvVector.push_back(string2charptr("-noGlobalUsingStd"));
5373
5374
5375 AddToArgVectorSplit(argvVector, pcmsNames, "-m");
5376
5377 // Inline the input header
5378 argvVector.push_back(string2charptr("-inlineInputHeader"));
5379
5380 // Write empty root pcms
5381 if (writeEmptyRootPCM)
5382 argvVector.push_back(string2charptr("-writeEmptyRootPCM"));
5383
5384 // Just test the syntax of the selection file
5385 if (selSyntaxOnly)
5386 argvVector.push_back(string2charptr("-selSyntaxOnly"));
5387
5388 // No include paths
5389 if (noIncludePaths)
5390 argvVector.push_back(string2charptr("-noIncludePaths"));
5391
5392 // Fail on warnings
5393 if (failOnWarnings)
5394 argvVector.push_back(string2charptr("-failOnWarnings"));
5395
5396 // Clingargs
5397 AddToArgVector(argvVector, includes, "-I");
5398 AddToArgVector(argvVector, preprocDefines, "-D");
5399 AddToArgVector(argvVector, preprocUndefines, "-U");
5400 AddToArgVector(argvVector, warnings, "-W");
5401
5402 AddToArgVector(argvVector, headersNames);
5403
5404 if (!selectionFileName.empty()) {
5405 argvVector.push_back(string2charptr(selectionFileName));
5406 }
5407
5408 const int argc = argvVector.size();
5409
5410 // Output commandline for rootcling
5411 if (genreflex::verbose) {
5412 std::cout << "Rootcling commandline:\n";
5413 for (int i = 0; i < argc; i++)
5414 std::cout << i << ") " << argvVector[i] << std::endl;
5415 }
5416
5417 char **argv = & (argvVector[0]);
5418 int rootclingReturnCode = RootClingMain(argc,
5419 argv,
5420 /*isGenReflex=*/true);
5421
5422 for (int i = 0; i < argc; i++)
5423 delete [] argvVector[i];
5424
5425 return rootclingReturnCode;
5426
5427 }
5428
5429////////////////////////////////////////////////////////////////////////////////
5430/// Get the right ofilenames and invoke several times rootcling
5431/// One invokation per header
5432
5433 int invokeManyRootCling(const std::string &verbosity,
5434 const std::string &selectionFileName,
5435 const std::string &targetLibName,
5436 bool multiDict,
5437 const std::vector<std::string> &pcmsNames,
5438 const std::vector<std::string> &includes,
5439 const std::vector<std::string> &preprocDefines,
5440 const std::vector<std::string> &preprocUndefines,
5441 const std::vector<std::string> &warnings,
5442 const std::string &rootmapFileName,
5443 const std::string &rootmapLibName,
5444 bool interpreteronly,
5445 bool doSplit,
5446 bool isCxxmodule,
5447 bool writeEmptyRootPCM,
5448 bool selSyntaxOnly,
5449 bool noIncludePaths,
5450 bool noGlobalUsingStd,
5451 const std::vector<std::string> &headersNames,
5452 bool failOnWarnings,
5453 const std::string &outputDirName_const = "")
5454 {
5455 std::string outputDirName(outputDirName_const);
5456
5457 std::vector<std::string> ofilesNames;
5458 headers2outputsNames(headersNames, ofilesNames);
5459
5460 if (!outputDirName.empty() && !ROOT::TMetaUtils::EndsWith(outputDirName, gPathSeparator)) {
5461 outputDirName += gPathSeparator;
5462 }
5463
5464 std::vector<std::string> namesSingleton(1);
5465 for (unsigned int i = 0; i < headersNames.size(); ++i) {
5466 namesSingleton[0] = headersNames[i];
5467 std::string ofilenameFullPath(ofilesNames[i]);
5468 if (llvm::sys::path::parent_path(ofilenameFullPath) == "")
5469 ofilenameFullPath = outputDirName + ofilenameFullPath;
5470 int returnCode = invokeRootCling(verbosity,
5471 selectionFileName,
5472 targetLibName,
5473 multiDict,
5474 pcmsNames,
5475 includes,
5476 preprocDefines,
5477 preprocUndefines,
5478 warnings,
5479 rootmapFileName,
5480 rootmapLibName,
5481 interpreteronly,
5482 doSplit,
5483 isCxxmodule,
5484 writeEmptyRootPCM,
5485 selSyntaxOnly,
5486 noIncludePaths,
5487 noGlobalUsingStd,
5488 namesSingleton,
5489 failOnWarnings,
5490 ofilenameFullPath);
5491 if (returnCode != 0)
5492 return returnCode;
5493 }
5494
5495 return 0;
5496 }
5497
5498
5499} // end genreflex namespace
5500
5501////////////////////////////////////////////////////////////////////////////////
5502/// Extract from options multiple values with the same option
5503
5504int extractMultipleOptions(std::vector<ROOT::option::Option> &options,
5505 int oIndex,
5506 std::vector<std::string> &values)
5507{
5508 int nValues = 0;
5509 if (options[oIndex]) {
5510 const int nVals = options[oIndex].count();
5511 values.reserve(nVals);
5512 int optionIndex = 0;
5513 for (ROOT::option::Option *opt = options[oIndex]; opt; opt = opt->next()) {
5514 if (genreflex::verbose) std::cout << "Extracting multiple args: "
5515 << optionIndex << "/" << nVals << " "
5516 << opt->arg << std::endl;
5517 optionIndex++;
5518 values.push_back(opt->arg);
5519 nValues++;
5520 }
5521 }
5522 return nValues;
5523}
5524
5525////////////////////////////////////////////////////////////////////////////////
5526
5527void RiseWarningIfPresent(std::vector<ROOT::option::Option> &options,
5528 int optionIndex,
5529 const char *descriptor)
5