CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_7/src/FWCore/RootAutoLibraryLoader/src/RootAutoLibraryLoader.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 //
00003 // Package:     LibraryLoader
00004 // Class  :     RootAutoLibraryLoader
00005 //
00006 // Implementation:
00007 //     <Notes on implementation>
00008 //
00009 // Original Author:
00010 //         Created:  Wed Nov 30 14:55:01 EST 2005
00011 //
00012 
00013 // system include files
00014 #include <string>
00015 #include <iostream>
00016 #include <map>
00017 #include "TROOT.h"
00018 #include "TInterpreter.h"
00019 #include "G__ci.h"
00020 
00021 // user include files
00022 #include "FWCore/RootAutoLibraryLoader/interface/RootAutoLibraryLoader.h"
00023 #include "FWCore/RootAutoLibraryLoader/src/stdNamespaceAdder.h"
00024 
00025 #include "FWCore/PluginManager/interface/PluginManager.h"
00026 #include "FWCore/PluginManager/interface/ProblemTracker.h"
00027 #include "FWCore/PluginManager/interface/PluginCapabilities.h"
00028 #include "FWCore/Utilities/interface/DictionaryTools.h"
00029 #include "FWCore/Utilities/interface/Exception.h"
00030 
00031 #include "Cintex/Cintex.h"
00032 #include "TClass.h"
00033 
00034 // We cannot use the MessageLogger here because this is also used by standalones that do not have the logger.
00035 
00036 //
00037 // constants, enums and typedefs
00038 //
00039 namespace {
00040   //Based on http://root.cern.ch/lxr/source/cintex/src/CINTSourceFile.h
00041   // If a Cint dictionary is accidently loaded as a side effect of loading a CMS
00042   // library Cint must have a file name assigned to that dictionary else Cint may crash
00043   class RootLoadFileSentry {
00044   public:
00045     RootLoadFileSentry() {
00046        G__setfilecontext("{CMS auto library loader}", &oldIFile_);
00047     }
00048 
00049     ~RootLoadFileSentry() {
00050       G__input_file* ifile = G__get_ifile();
00051       if (ifile) {
00052         *ifile = oldIFile_;
00053       }
00054     }
00055 
00056   private:
00057       G__input_file oldIFile_;
00058   };
00059 }
00060 
00061 //
00062 // static data member definitions
00063 //
00064 //hold onto the previous autolibrary loader
00065 typedef int (*CallbackPtr)(char*, char*);
00066 static CallbackPtr gPrevious = nullptr;
00067 static char const* kDummyLibName = "*dummy";
00068 
00069 //This is actually defined within ROOT's v6_struct.cxx file but is not declared static
00070 // I want to use it so that if the autoloading is already turned on, I can call the previously declared routine
00071 extern CallbackPtr G__p_class_autoloading;
00072 
00073 namespace ROOT {
00074   namespace Cintex {
00075     std::string CintName(std::string const&);
00076   }
00077 }
00078 
00079 namespace edm {
00080    namespace {
00081 
00082       std::map<std::string, std::string>&
00083       cintToReflexSpecialCasesMap() {
00084          static std::map<std::string, std::string> s_map;
00085          return s_map;
00086       }
00087 
00088       void
00089       addWrapperOfVectorOfBuiltin(std::map<std::string, std::string>& iMap, char const* iBuiltin) {
00090          static std::string sReflexPrefix("edm::Wrapper<std::vector<");
00091          static std::string sReflexPostfix("> >");
00092 
00093          //Wrapper<vector<float, allocator<float> > >
00094          static std::string sCintPrefix("Wrapper<vector<");
00095          static std::string sCintMiddle(",allocator<");
00096          static std::string sCintPostfix("> > >");
00097 
00098          std::string type(iBuiltin);
00099          iMap.insert(make_pair(sCintPrefix + type + sCintMiddle + type + sCintPostfix,
00100                                sReflexPrefix + type + sReflexPostfix));
00101       }
00102 
00103       std::string
00104       classNameForRoot(std::string const& classname) {
00105         // Converts the name to the name known by CINT (e.g. strips out "std::")
00106         return ROOT::Cintex::CintName(classname);
00107       }
00108 
00109       bool
00110       isDictionaryLoaded(std::string const& rootclassname) {
00111         // This checks if the class name is known to the interpreter.
00112         // In this context, this will be true if and only if the dictionary has been loaded (and converted to CINT).
00113         // This code is independent of the identity of the interpreter.
00114         ClassInfo_t* info = gInterpreter->ClassInfo_Factory(rootclassname.c_str());
00115         return gInterpreter->ClassInfo_IsValid(info);
00116       }
00117 
00118       bool loadLibraryForClass(char const* classname) {
00119         //std::cout << "loadLibaryForClass" << std::endl;
00120         if(nullptr == classname) {
00121           return false;
00122         }
00123         //std::cout << "asking to find " << classname << std::endl;
00124         std::string const& cPrefix = dictionaryPlugInPrefix();
00125         //std::cout << "asking to find " << cPrefix + classname << std::endl;
00126         std::string rootclassname = classNameForRoot(classname);
00127         try {
00128           //give ROOT a name for the file we are loading
00129           RootLoadFileSentry sentry;
00130           if(edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + classname)) {
00131             if(!isDictionaryLoaded(rootclassname)) {
00132               //would be nice to issue a warning here.  Not sure the remainder of this comment is correct.
00133               // this message happens too often (too many false positives) to be useful plus ROOT will complain about a missing dictionary
00134               //std::cerr << "Warning: ROOT knows about type '" << classname << "' but has no dictionary for it." << std::endl;
00135               return false;
00136             }
00137           } else {
00138             //see if adding a std namespace helps
00139             std::string name = root::stdNamespaceAdder(classname);
00140             //std::cout << "see if std helps" << std::endl;
00141             if (not edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + name)) {
00142               // Too many false positives on built-in types here.
00143               return false;
00144             }
00145             if(!isDictionaryLoaded(rootclassname)) {
00146               //would be nice to issue a warning here
00147               return false;
00148             }
00149           }
00150         } catch(cms::Exception& e) {
00151           //would be nice to issue a warning here
00152           return false;
00153         }
00154         //std::cout << "loaded " << classname << std::endl;
00155         return true;
00156       }
00157 
00158       //Based on code in ROOT's TCint.cxx file
00159 
00160       int ALL_AutoLoadCallback(char* c, char* l) {
00161         //NOTE: if the library (i.e. 'l') is an empty string this means we are dealing with a namespace
00162         // These checks appear to avoid a crash of ROOT during shutdown of the application
00163         if(nullptr == c || nullptr == l || l[0] == 0) {
00164           return 0;
00165         }
00166         ULong_t varp = G__getgvp();
00167         G__setgvp((long)G__PVOID);
00168         int result = loadLibraryForClass(c) ? 1:0;
00169         G__setgvp(varp);
00170         //NOTE: the check for the library is done since we can have a failure
00171         // if a CMS library has an incomplete set of reflex dictionaries where
00172         // the remaining dictionaries can be found by Cint.  If the library with
00173         // the reflex dictionaries is loaded first, then the Cint library then any
00174         // requests for a Type from the reflex library will fail because for
00175         // some reason the loading of the Cint library causes reflex to forget about
00176         // what types it already loaded from the reflex library.  This problem was
00177         // seen for libDataFormatsMath and libMathCore.  I do not print an error message
00178         // since the dictionaries are actually loaded so things work fine.
00179         if(!result && 0 != strcmp(l, kDummyLibName) && gPrevious) {
00180           result = gPrevious(c, l);
00181         }
00182         return result;
00183       }
00184 
00185       //Cint requires that we register the type and library containing the type
00186       // before the autoloading will work
00187         struct CompareFirst {
00188           bool operator()(std::pair<std::string, std::string> const& iLHS,
00189                           std::pair<std::string, std::string> const& iRHS) const{
00190             return iLHS.first > iRHS.first;
00191           }
00192         };
00193 
00194       void registerTypes() {
00195         edmplugin::PluginManager* db =  edmplugin::PluginManager::get();
00196 
00197         typedef edmplugin::PluginManager::CategoryToInfos CatToInfos;
00198 
00199         CatToInfos::const_iterator itFound = db->categoryToInfos().find("Capability");
00200 
00201         if(itFound == db->categoryToInfos().end()) {
00202           return;
00203         }
00204 
00205         //in order to determine if a name is from a class or a namespace, we will order
00206         // all the classes in descending order so that embedded classes will be seen before
00207         // their containing classes, that way we can say the containing class is a namespace
00208         // before finding out it is actually a class
00209         typedef std::vector<std::pair<std::string, std::string> > ClassAndLibraries;
00210         ClassAndLibraries classes;
00211         classes.reserve(1000);
00212         std::string lastClass;
00213 
00214         //find where special cases come from
00215         std::map<std::string, std::string> specialsToLib;
00216         std::map<std::string, std::string> const& specials = cintToReflexSpecialCasesMap();
00217         for(auto const& special : specials) {
00218           specialsToLib[classNameForRoot(special.second)];
00219         }
00220         std::string const& cPrefix = dictionaryPlugInPrefix();
00221         for(auto const& info : itFound->second) {
00222           if (lastClass == info.name_) {
00223             continue;
00224           }
00225           lastClass = info.name_;
00226           if(cPrefix == lastClass.substr(0, cPrefix.size())) {
00227             std::string className = classNameForRoot(lastClass.c_str() + cPrefix.size());
00228             classes.emplace_back(className, info.loadable_.string());
00229             std::map<std::string, std::string>::iterator iFound = specialsToLib.find(className);
00230             if(iFound != specialsToLib.end()) {
00231               // std::cout << "Found " << lastClass << " : " << className << std::endl;
00232               iFound->second = info.loadable_.string();
00233             }
00234           }
00235         }
00236         //sort_all(classes, std::greater<std::string>());
00237         //sort_all(classes, CompareFirst());
00238         //the values are already sorted by less, so just need to reverse to get greater
00239         for(ClassAndLibraries::reverse_iterator itClass = classes.rbegin(), itClassEnd = classes.rend();
00240             itClass != itClassEnd;
00241             ++itClass) {
00242 
00243           std::string const& className = itClass->first;
00244           std::string const& libraryName = itClass->second;
00245           //need to register namespaces and figure out if we have an embedded class
00246           static std::string const toFind(":<");
00247           std::string::size_type pos = 0;
00248           while(std::string::npos != (pos = className.find_first_of(toFind, pos))) {
00249             if (className[pos] == '<') {break;}
00250             if (className.size() <= pos + 1 || className[pos + 1] != ':') {break;}
00251             //should check to see if this is a class or not
00252             G__set_class_autoloading_table(const_cast<char*>(className.substr(0, pos).c_str()), const_cast<char*>(""));
00253             //std::cout << "namespace " << className.substr(0, pos).c_str() << std::endl;
00254             pos += 2;
00255           }
00256           G__set_class_autoloading_table(const_cast<char*>(className.c_str()), const_cast<char*>(libraryName.c_str()));
00257           //std::cout << "class " << className.c_str() << std::endl;
00258         }
00259 
00260         //now handle the special cases
00261         for(auto const& special : specials) {
00262           std::string const& name = special.second;
00263           std::string rootname = classNameForRoot(name);
00264           //std::cout << "registering special " << itSpecial->first << " " << name << " " << std::endl << "    " << specialsToLib[rootname] << std::endl;
00265           //force loading of specials
00266           if(specialsToLib[rootname].size()) {
00267             //std::cout << "&&&&& found special case " << itSpecial->first << std::endl;
00268             if(!isDictionaryLoaded(rootname) and
00269                 (not edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + name))) {
00270               std::cout << "failed to load plugin for " << cPrefix + name << std::endl;
00271               continue;
00272             } else {
00273               //need to construct the Class ourselves
00274               if(!isDictionaryLoaded(rootname)) {
00275                 std::cout << "dictionary did not build " << name << std::endl;
00276                 continue;
00277               }
00278               TClass* namedClass = TClass::GetClass(rootname.c_str());
00279               if(nullptr == namedClass) {
00280                 std::cout << "failed to get TClass for " << name << std::endl;
00281                 continue;
00282               }
00283               namedClass->Clone(special.first.c_str());
00284               std::string magictypedef("namespace edm { typedef ");
00285               magictypedef += rootname + " " + special.first + "; }";
00286               // std::cout << "Magic typedef " << magictypedef << std::endl;
00287               gROOT->ProcessLine(magictypedef.c_str());
00288             }
00289           }
00290         }
00291       }
00292    }
00293 
00294    //
00295    // constructors and destructor
00296    //
00297    RootAutoLibraryLoader::RootAutoLibraryLoader() :
00298 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00299      classNameAttemptingToLoad_(nullptr),
00300      isInitializingCintex_(true) {
00301 #else
00302      classNameAttemptingToLoad_(nullptr) {
00303 #endif
00304       AssertHandler h;
00305       gROOT->AddClassGenerator(this);
00306       ROOT::Cintex::Cintex::Enable();
00307 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00308       isInitializingCintex_ =false;
00309 #endif
00310       //set the special cases
00311       std::map<std::string, std::string>& specials = cintToReflexSpecialCasesMap();
00312       if(specials.empty()) {
00313          addWrapperOfVectorOfBuiltin(specials,"bool");
00314 
00315          addWrapperOfVectorOfBuiltin(specials,"char");
00316          addWrapperOfVectorOfBuiltin(specials,"unsigned char");
00317          addWrapperOfVectorOfBuiltin(specials,"signed char");
00318          addWrapperOfVectorOfBuiltin(specials,"short");
00319          addWrapperOfVectorOfBuiltin(specials,"unsigned short");
00320          addWrapperOfVectorOfBuiltin(specials,"int");
00321          addWrapperOfVectorOfBuiltin(specials,"unsigned int");
00322          addWrapperOfVectorOfBuiltin(specials,"long");
00323          addWrapperOfVectorOfBuiltin(specials,"unsigned long");
00324          addWrapperOfVectorOfBuiltin(specials,"long long");
00325          addWrapperOfVectorOfBuiltin(specials,"unsigned long long");
00326 
00327          addWrapperOfVectorOfBuiltin(specials,"float");
00328          addWrapperOfVectorOfBuiltin(specials,"double");
00329       }
00330       //std::cout << "my loader" << std::endl;
00331       //remember if the callback was already set so we can chain together our results
00332       gPrevious = G__p_class_autoloading;
00333       G__set_class_autoloading_callback(&ALL_AutoLoadCallback);
00334       registerTypes();
00335    }
00336 
00337    //
00338    // member functions
00339    //
00340 
00341    TClass*
00342    RootAutoLibraryLoader::GetClass(char const* classname, Bool_t load) {
00343       TClass* returnValue = nullptr;
00344       if(classNameAttemptingToLoad_ != nullptr && !strcmp(classname, classNameAttemptingToLoad_)) {
00345          // We can try to see if the class name contains "basic_string<char>".
00346          // If so, we replace "basic_string<char>" with "string" and try again.
00347          std::string className(classname);
00348          std::string::size_type idx = className.find("basic_string<char>");
00349          if (idx != std::string::npos) {
00350             className.replace(idx, 18, std::string("string"));
00351             //if basic_string<char> was the last argument to a templated class
00352             // then there would be an extra space to separate the two '>'
00353             if(className.size() > idx + 6 && className[idx + 6] == ' ') {
00354               className.replace(idx + 6, 1, "");
00355             }
00356             classNameAttemptingToLoad_ = className.c_str();
00357             returnValue = gROOT->GetClass(className.c_str(), kTRUE);
00358             classNameAttemptingToLoad_ = classname;
00359             return returnValue;
00360          }
00361          //NOTE: As of ROOT 5.27.06 this warning generates false positives for HepMC classes because
00362          // ROOT has special handling for them built into class.rules
00363          //std::cerr << "WARNING[RootAutoLibraryLoader]: ROOT failed to create CINT dictionary for " << classname << std::endl;
00364          return nullptr;
00365       }
00366       //std::cout << "looking for " << classname << " load " << (load? "T":"F") << std::endl;
00367       if (load) {
00368         //std::cout << " going to call loadLibraryForClass" << std::endl;
00369         //[ROOT 5.28] When Cintex is in its 'Enable' method it will register callbacks to build
00370         // TClasses. During this phase we do not want to actually force TClasses to have to 
00371         // come into existence.
00372 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00373         if (not isInitializingCintex_ and loadLibraryForClass(classname)) {
00374 #else
00375          if (loadLibraryForClass(classname)) {
00376 #endif
00377           //use this to check for infinite recursion attempt
00378           classNameAttemptingToLoad_ = classname;
00379           // This next call will create the TClass object for the class.
00380           // It will also attempt to load the dictionary for the class
00381           // if the second argument is kTRUE. This is the default, so it
00382           // need not be explicitly specified.
00383           returnValue = gROOT->GetClass(classname, kTRUE);
00384           classNameAttemptingToLoad_ = nullptr;
00385         }
00386       }
00387       return returnValue;
00388    }
00389 
00390    TClass*
00391    RootAutoLibraryLoader::GetClass(type_info const& typeinfo, Bool_t load) {
00392      //std::cout << "looking for type " << typeinfo.name() << std::endl;
00393       TClass* returnValue = nullptr;
00394       if(load) {
00395          return GetClass(typeinfo.name(), load);
00396       }
00397       return returnValue;
00398    }
00399 
00400    void
00401    RootAutoLibraryLoader::enable() {
00402       //static BareRootProductGetter s_getter;
00403       //static EDProductGetter::Operate s_op(&s_getter);
00404       static RootAutoLibraryLoader s_loader;
00405    }
00406 
00407    void
00408    RootAutoLibraryLoader::loadAll() {
00409      // std::cout << "LoadAllDictionaries" << std::endl;
00410      enable();
00411 
00412      edmplugin::PluginManager* db =  edmplugin::PluginManager::get();
00413 
00414      typedef edmplugin::PluginManager::CategoryToInfos CatToInfos;
00415 
00416      CatToInfos::const_iterator itFound = db->categoryToInfos().find("Capability");
00417 
00418      if(itFound == db->categoryToInfos().end()) {
00419        return;
00420      }
00421      std::string lastClass;
00422 
00423      //give ROOT a name for the file we are loading
00424      RootLoadFileSentry sentry;
00425 
00426      for(auto const& info : itFound->second) {
00427        if (lastClass == info.name_) {
00428          continue;
00429        }
00430 
00431        lastClass = info.name_;
00432        edmplugin::PluginCapabilities::get()->load(lastClass);
00433        //NOTE: since we have the library already, we could be more efficient if we just load it ourselves
00434      }
00435    }
00436 }
00437 
00438 //ClassImp(RootAutoLibraryLoader)