File indexing completed on 2025-07-12 07:55:12
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 logger().debug( std::string( "searching factories in " ) + envVar );
0160 std::string path = search_path ? std::string(search_path) : "/usr/lib64:/usr/lib:/usr/local/lib";
0161 std::string::size_type pos = 0;
0162 std::string::size_type newpos = 0;
0163 while ( pos != std::string::npos ) {
0164 std::string dirName;
0165
0166 newpos = path.find( sep, pos );
0167 if ( newpos != std::string::npos ) {
0168 dirName = path.substr( pos, newpos - pos );
0169 pos = newpos + 1;
0170 } else {
0171 dirName = path.substr( pos );
0172 pos = newpos;
0173 }
0174 logger().debug( std::string( " looking into " ) + dirName );
0175
0176 DIR* dir = opendir( dirName.c_str() );
0177 if ( dir ) {
0178 struct dirent* entry;
0179 while ( ( entry = readdir( dir ) ) ) {
0180 std::string name( entry->d_name );
0181
0182 std::string::size_type extpos = name.find( ".components" );
0183 if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
0184 std::string fullPath = ( dirName + '/' + name );
0185 {
0186 struct stat buf;
0187 if ( 0 != ::stat( fullPath.c_str(), &buf ) )
0188 continue;
0189 else if ( !S_ISREG( buf.st_mode ) )
0190 continue;
0191 }
0192
0193 logger().debug( std::string( " reading " ) + name );
0194 std::ifstream factories{fullPath};
0195 std::string line;
0196 int factoriesCount = 0;
0197 int lineCount = 0;
0198 while ( !factories.eof() ) {
0199 ++lineCount;
0200 std::getline( factories, line );
0201 trim( line );
0202
0203 if ( line.empty() || line[0] == '#' ) continue;
0204
0205 if ( line.substr( 0, 4 ) == "v1::" )
0206 line = line.substr( 4 );
0207 else
0208 continue;
0209
0210 auto pos = line.find( ':' );
0211 if ( pos == std::string::npos ) {
0212 logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
0213 continue;
0214 }
0215 const std::string lib( line, 0, pos );
0216 const std::string fact( line, pos + 1 );
0217 m_factories.emplace( fact, FactoryInfo( lib ) );
0218 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0219
0220 std::string old_name = old_style_name( fact );
0221 if ( fact != old_name ) {
0222 FactoryInfo old_info( lib );
0223 old_info.properties["ReflexName"] = "true";
0224 m_factories.emplace( old_name, old_info );
0225 }
0226 #endif
0227 ++factoriesCount;
0228 }
0229 if ( logger().level() <= Logger::Debug ) {
0230 logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
0231 }
0232 }
0233 }
0234 closedir( dir );
0235 }
0236 }
0237 }
0238
0239 Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
0240 const std::string& rtype, const std::string& className,
0241 const Properties& props ) {
0242 REG_SCOPE_LOCK
0243 FactoryMap& facts = factories();
0244 auto entry = facts.find( id );
0245 if ( entry == facts.end() ) {
0246
0247 entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
0248 } else {
0249
0250 if ( !entry->second.ptr ) entry->second.ptr = factory;
0251 factoryInfoSetHelper( entry->second.type, type, "type", id );
0252 factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
0253 factoryInfoSetHelper( entry->second.className, className, "class", id );
0254 }
0255 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0256
0257 std::string old_name = old_style_name( id );
0258 if ( id != old_name )
0259 add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
0260 #endif
0261 return entry->second;
0262 }
0263
0264 void* Registry::get( const std::string& id, const std::string& type ) const {
0265 REG_SCOPE_LOCK
0266 const FactoryMap& facts = factories();
0267 auto f = facts.find( id );
0268 if ( f != facts.end() ) {
0269 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0270 const Properties& props = f->second.properties;
0271 if ( props.find( "ReflexName" ) != props.end() )
0272 logger().warning( "requesting factory via old name '" + id +
0273 "'"
0274 "use '" +
0275 f->second.className + "' instead" );
0276 #endif
0277 if ( !f->second.ptr ) {
0278 if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
0279 logger().warning( "cannot load " + f->second.library + " for factory " + id );
0280 char* dlmsg = dlerror();
0281 if ( dlmsg ) logger().warning( dlmsg );
0282 return nullptr;
0283 }
0284 f = facts.find( id );
0285 }
0286 if ( f->second.type == type ) return f->second.ptr;
0287 logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
0288 " instead of " + demangle( type ) );
0289 }
0290 return nullptr;
0291 }
0292
0293 const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
0294 REG_SCOPE_LOCK
0295 static const FactoryInfo unknown( "unknown" );
0296 const FactoryMap& facts = factories();
0297 auto f = facts.find( id );
0298 return ( f != facts.end() ) ? f->second : unknown;
0299 }
0300
0301 Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
0302 REG_SCOPE_LOCK
0303 FactoryMap& facts = factories();
0304 auto f = facts.find( id );
0305 if ( f != facts.end() ) f->second.properties[k] = v;
0306 return *this;
0307 }
0308
0309 std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
0310 REG_SCOPE_LOCK
0311 std::set<KeyType> l;
0312 for ( const auto& f : factories() ) {
0313 if ( f.second.ptr ) l.insert( f.first );
0314 }
0315 return l;
0316 }
0317
0318 void Logger::report( Level lvl, const std::string& msg ) {
0319 static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
0320 if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
0321 }
0322
0323 static auto s_logger = std::make_unique<Logger>();
0324 Logger& logger() { return *s_logger; }
0325 void setLogger( Logger* logger ) { s_logger.reset( logger ); }
0326
0327 }
0328
0329 void SetDebug( int debugLevel ) {
0330 using namespace Details;
0331 Logger& l = logger();
0332 if ( debugLevel > 1 )
0333 l.setLevel( Logger::Debug );
0334 else if ( debugLevel > 0 )
0335 l.setLevel( Logger::Info );
0336 else
0337 l.setLevel( Logger::Warning );
0338 }
0339
0340 int Debug() {
0341 using namespace Details;
0342 switch ( logger().level() ) {
0343 case Logger::Debug:
0344 return 2;
0345 case Logger::Info:
0346 return 1;
0347 default:
0348 return 0;
0349 }
0350 }
0351 }
0352 }
0353 }