File indexing completed on 2025-01-18 09:14:59
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #define GAUDI_PLUGIN_SERVICE_V1
0015 #include <Gaudi/PluginService.h>
0016
0017 #include <dirent.h>
0018 #include <dlfcn.h>
0019
0020 #include <cstdlib>
0021 #include <fstream>
0022 #include <iostream>
0023 #include <memory>
0024 #include <regex>
0025
0026 #include <cxxabi.h>
0027 #include <sys/stat.h>
0028
0029 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
0030
0031 namespace {
0032 std::mutex registrySingletonMutex;
0033 }
0034 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
0035
0036 #include <algorithm>
0037
0038 namespace {
0039
0040
0041
0042 constexpr struct is_space_t {
0043 bool operator()( int i ) const { return std::isspace( i ); }
0044 } is_space{};
0045
0046
0047 static inline std::string& ltrim( std::string& s ) {
0048 s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) );
0049 return s;
0050 }
0051
0052
0053 static inline std::string& rtrim( std::string& s ) {
0054 s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() );
0055 return s;
0056 }
0057
0058 static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); }
0059 }
0060
0061 namespace {
0062
0063
0064
0065 inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc,
0066 const std::string& id ) {
0067 if ( dest.empty() ) {
0068 dest = value;
0069 } else if ( dest != value ) {
0070 Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc +
0071 ": " + dest + " != " + value );
0072 }
0073 }
0074
0075 struct OldStyleCnv {
0076 std::string name;
0077 void operator()( const char c ) {
0078 switch ( c ) {
0079 case '<':
0080 case '>':
0081 case ',':
0082 case '(':
0083 case ')':
0084 case ':':
0085 case '.':
0086 name.push_back( '_' );
0087 break;
0088 case '&':
0089 name.push_back( 'r' );
0090 break;
0091 case '*':
0092 name.push_back( 'p' );
0093 break;
0094 case ' ':
0095 break;
0096 default:
0097 name.push_back( c );
0098 break;
0099 }
0100 }
0101 };
0102
0103 std::string old_style_name( const std::string& name ) {
0104 return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
0105 }
0106 }
0107
0108 namespace Gaudi {
0109 namespace PluginService {
0110 GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 {
0111 Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {}
0112 Exception::~Exception() throw() {}
0113 const char* Exception::what() const throw() { return m_msg.c_str(); }
0114
0115 namespace Details {
0116 void* getCreator( const std::string& id, const std::string& type ) {
0117 return Registry::instance().get( id, type );
0118 }
0119
0120 std::string demangle( const std::string& id ) {
0121 int status;
0122 auto realname = std::unique_ptr<char, decltype( free )*>(
0123 abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
0124 if ( !realname ) return id;
0125 #if _GLIBCXX_USE_CXX11_ABI
0126 return std::regex_replace(
0127 realname.get(),
0128 std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
0129 "std::string" );
0130 #else
0131 return std::string{realname.get()};
0132 #endif
0133 }
0134 std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
0135
0136 Registry& Registry::instance() {
0137 SINGLETON_LOCK
0138 static Registry r;
0139 return r;
0140 }
0141
0142 Registry::Registry() : m_initialized( false ) {}
0143
0144 void Registry::initialize() {
0145 REG_SCOPE_LOCK
0146 if ( m_initialized ) return;
0147 m_initialized = true;
0148 #if defined( _WIN32 )
0149 const char* envVar = "PATH";
0150 const char sep = ';';
0151 #elif defined( __APPLE__ )
0152 const char* envVar = "DYLD_LIBRARY_PATH";
0153 const char sep = ':';
0154 #else
0155 const char* envVar = "LD_LIBRARY_PATH";
0156 const char sep = ':';
0157 #endif
0158 char* search_path = ::getenv( envVar );
0159 if ( search_path ) {
0160 logger().debug( std::string( "searching factories in " ) + envVar );
0161 std::string path( search_path );
0162 std::string::size_type pos = 0;
0163 std::string::size_type newpos = 0;
0164 while ( pos != std::string::npos ) {
0165 std::string dirName;
0166
0167 newpos = path.find( sep, pos );
0168 if ( newpos != std::string::npos ) {
0169 dirName = path.substr( pos, newpos - pos );
0170 pos = newpos + 1;
0171 } else {
0172 dirName = path.substr( pos );
0173 pos = newpos;
0174 }
0175 logger().debug( std::string( " looking into " ) + dirName );
0176
0177 DIR* dir = opendir( dirName.c_str() );
0178 if ( dir ) {
0179 struct dirent* entry;
0180 while ( ( entry = readdir( dir ) ) ) {
0181 std::string name( entry->d_name );
0182
0183 std::string::size_type extpos = name.find( ".components" );
0184 if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
0185 std::string fullPath = ( dirName + '/' + name );
0186 {
0187 struct stat buf;
0188 if ( 0 != ::stat( fullPath.c_str(), &buf ) )
0189 continue;
0190 else if ( !S_ISREG( buf.st_mode ) )
0191 continue;
0192 }
0193
0194 logger().debug( std::string( " reading " ) + name );
0195 std::ifstream factories{fullPath};
0196 std::string line;
0197 int factoriesCount = 0;
0198 int lineCount = 0;
0199 while ( !factories.eof() ) {
0200 ++lineCount;
0201 std::getline( factories, line );
0202 trim( line );
0203
0204 if ( line.empty() || line[0] == '#' ) continue;
0205
0206 if ( line.substr( 0, 4 ) == "v1::" )
0207 line = line.substr( 4 );
0208 else
0209 continue;
0210
0211 auto pos = line.find( ':' );
0212 if ( pos == std::string::npos ) {
0213 logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
0214 continue;
0215 }
0216 const std::string lib( line, 0, pos );
0217 const std::string fact( line, pos + 1 );
0218 m_factories.emplace( fact, FactoryInfo( lib ) );
0219 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0220
0221 std::string old_name = old_style_name( fact );
0222 if ( fact != old_name ) {
0223 FactoryInfo old_info( lib );
0224 old_info.properties["ReflexName"] = "true";
0225 m_factories.emplace( old_name, old_info );
0226 }
0227 #endif
0228 ++factoriesCount;
0229 }
0230 if ( logger().level() <= Logger::Debug ) {
0231 logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
0232 }
0233 }
0234 }
0235 closedir( dir );
0236 }
0237 }
0238 }
0239 }
0240
0241 Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
0242 const std::string& rtype, const std::string& className,
0243 const Properties& props ) {
0244 REG_SCOPE_LOCK
0245 FactoryMap& facts = factories();
0246 auto entry = facts.find( id );
0247 if ( entry == facts.end() ) {
0248
0249 entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
0250 } else {
0251
0252 if ( !entry->second.ptr ) entry->second.ptr = factory;
0253 factoryInfoSetHelper( entry->second.type, type, "type", id );
0254 factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
0255 factoryInfoSetHelper( entry->second.className, className, "class", id );
0256 }
0257 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0258
0259 std::string old_name = old_style_name( id );
0260 if ( id != old_name )
0261 add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
0262 #endif
0263 return entry->second;
0264 }
0265
0266 void* Registry::get( const std::string& id, const std::string& type ) const {
0267 REG_SCOPE_LOCK
0268 const FactoryMap& facts = factories();
0269 auto f = facts.find( id );
0270 if ( f != facts.end() ) {
0271 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0272 const Properties& props = f->second.properties;
0273 if ( props.find( "ReflexName" ) != props.end() )
0274 logger().warning( "requesting factory via old name '" + id +
0275 "'"
0276 "use '" +
0277 f->second.className + "' instead" );
0278 #endif
0279 if ( !f->second.ptr ) {
0280 if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
0281 logger().warning( "cannot load " + f->second.library + " for factory " + id );
0282 char* dlmsg = dlerror();
0283 if ( dlmsg ) logger().warning( dlmsg );
0284 return nullptr;
0285 }
0286 f = facts.find( id );
0287 }
0288 if ( f->second.type == type ) return f->second.ptr;
0289 logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
0290 " instead of " + demangle( type ) );
0291 }
0292 return nullptr;
0293 }
0294
0295 const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
0296 REG_SCOPE_LOCK
0297 static const FactoryInfo unknown( "unknown" );
0298 const FactoryMap& facts = factories();
0299 auto f = facts.find( id );
0300 return ( f != facts.end() ) ? f->second : unknown;
0301 }
0302
0303 Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
0304 REG_SCOPE_LOCK
0305 FactoryMap& facts = factories();
0306 auto f = facts.find( id );
0307 if ( f != facts.end() ) f->second.properties[k] = v;
0308 return *this;
0309 }
0310
0311 std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
0312 REG_SCOPE_LOCK
0313 std::set<KeyType> l;
0314 for ( const auto& f : factories() ) {
0315 if ( f.second.ptr ) l.insert( f.first );
0316 }
0317 return l;
0318 }
0319
0320 void Logger::report( Level lvl, const std::string& msg ) {
0321 static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
0322 if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
0323 }
0324
0325 static auto s_logger = std::make_unique<Logger>();
0326 Logger& logger() { return *s_logger; }
0327 void setLogger( Logger* logger ) { s_logger.reset( logger ); }
0328
0329 }
0330
0331 void SetDebug( int debugLevel ) {
0332 using namespace Details;
0333 Logger& l = logger();
0334 if ( debugLevel > 1 )
0335 l.setLevel( Logger::Debug );
0336 else if ( debugLevel > 0 )
0337 l.setLevel( Logger::Info );
0338 else
0339 l.setLevel( Logger::Warning );
0340 }
0341
0342 int Debug() {
0343 using namespace Details;
0344 switch ( logger().level() ) {
0345 case Logger::Debug:
0346 return 2;
0347 case Logger::Info:
0348 return 1;
0349 default:
0350 return 0;
0351 }
0352 }
0353 }
0354 }
0355 }