Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
PluginServiceV2.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * (c) Copyright 2013 CERN *
3 * *
4 * This software is distributed under the terms of the GNU General Public *
5 * Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \*****************************************************************************/
11 
13 
14 #define GAUDI_PLUGIN_SERVICE_V2
15 #include <Gaudi/PluginService.h>
16 
17 #include <dirent.h>
18 #include <dlfcn.h>
19 
20 #include <cstdlib>
21 #include <fstream>
22 #include <iostream>
23 #include <memory>
24 #include <regex>
25 
26 #include <cxxabi.h>
27 #include <sys/stat.h>
28 
29 #ifdef _GNU_SOURCE
30 # include <cstring>
31 # include <dlfcn.h>
32 #endif
33 
34 #if __GNUC__ >= 8 && __cplusplus >= 201703
35 # include <filesystem>
36 namespace fs = std::filesystem;
37 #else
38 # include <experimental/filesystem>
39 namespace fs = std::experimental::filesystem;
40 #endif
41 
42 #if __cplusplus >= 201703
43 # include <string_view>
44 #else
45 # include <experimental/string_view>
46 namespace std {
47  using experimental::string_view;
48 }
49 #endif
50 
51 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
52 
53 namespace {
54  std::mutex registrySingletonMutex;
55 }
56 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
57 
58 #include <algorithm>
59 
60 namespace {
61  struct OldStyleCnv {
63  void operator()( const char c ) {
64  switch ( c ) {
65  case '<':
66  case '>':
67  case ',':
68  case '(':
69  case ')':
70  case ':':
71  case '.':
72  name.push_back( '_' );
73  break;
74  case '&':
75  name.push_back( 'r' );
76  break;
77  case '*':
78  name.push_back( 'p' );
79  break;
80  case ' ':
81  break;
82  default:
83  name.push_back( c );
84  break;
85  }
86  }
87  };
89  std::string old_style_name( const std::string& name ) {
90  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
91  }
92 } // namespace
93 
94 namespace Gaudi {
95  namespace PluginService {
96  GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 {
97  namespace Details {
99  int status;
101  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
102  if ( !realname ) return id;
103 #if _GLIBCXX_USE_CXX11_ABI
104  return std::regex_replace(
105  realname.get(),
106  std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
107  "std::string" );
108 #else
109  return std::string{realname.get()};
110 #endif
111  }
112  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
113 
114  Registry& Registry::instance() {
116  static Registry r;
117  return r;
118  }
119 
120  void reportBadAnyCast( const std::type_info& factory_type, const std::string& id ) {
121  if ( logger().level() <= Logger::Debug ) {
123  const auto& info = Registry::instance().getInfo( id );
124  msg << "bad any_cast: requested factory " << id << " of type " << demangle( factory_type ) << ", got ";
125  if ( info.is_set() )
126  msg << demangle( info.factory.type() ) << " from " << info.library;
127  else
128  msg << "nothing";
129  logger().debug( msg.str() );
130  }
131  }
132 
133  Registry::Properties::mapped_type Registry::FactoryInfo::getprop( const Properties::key_type& name ) const {
134  auto p = properties.find( name );
135  return ( p != end( properties ) ) ? p->second : Properties::mapped_type{};
136  }
137 
138  Registry::Registry() {}
139 
140  void Registry::initialize() {
142 #if defined( _WIN32 )
143  const char* envVar = "PATH";
144  const char sep = ';';
145 #elif defined( __APPLE__ )
146  const char* envVar = "DYLD_LIBRARY_PATH";
147  const char sep = ':';
148 #else
149  const char* envVar = "LD_LIBRARY_PATH";
150  const char sep = ':';
151 #endif
152 
153  std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"};
154  std::smatch m;
155 
156  std::string_view search_path = std::getenv( envVar );
157  if ( !search_path.empty() ) {
158  logger().debug( std::string( "searching factories in " ) + envVar );
159 
160  std::string_view::size_type start_pos = 0, end_pos = 0;
161  while ( start_pos != std::string_view::npos ) {
162  // correctly handle begin of string or path separator
163  if ( start_pos ) ++start_pos;
164 
165  end_pos = search_path.find( sep, start_pos );
166  fs::path dirName =
167 #if __GNUC__ >= 7
168  search_path.substr( start_pos, end_pos - start_pos );
169 #else
170  std::string{search_path.substr( start_pos, end_pos - start_pos )};
171 #endif
172  start_pos = end_pos;
173 
174  logger().debug( " looking into " + dirName.string() );
175  // look for files called "*.components" in the directory
176  if ( is_directory( dirName ) ) {
177  for ( auto& p : fs::directory_iterator( dirName ) ) {
178  if ( p.path().extension() == ".components" && is_regular_file( p.path() ) ) {
179  // read the file
180  const auto& fullPath = p.path().string();
181  logger().debug( " reading " + p.path().filename().string() );
182  std::ifstream factories{fullPath};
184  int factoriesCount = 0;
185  int lineCount = 0;
186  while ( !factories.eof() ) {
187  ++lineCount;
188  std::getline( factories, line );
189  if ( regex_match( line, m, line_format ) ) {
190  if ( m[1] == "v2" ) { // ignore non "v2" and "empty" lines
191  const std::string lib{m[2]};
192  const std::string fact{m[3]};
193  m_factories.emplace( fact, FactoryInfo{lib, {}, {{"ClassName", fact}}} );
194 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
195  // add an alias for the factory using the Reflex convention
196  std::string old_name = old_style_name( fact );
197  if ( fact != old_name ) {
198  m_factories.emplace( old_name,
199  FactoryInfo{lib, {}, {{"ReflexName", "true"}, {"ClassName", fact}}} );
200  }
201 #endif
202  ++factoriesCount;
203  }
204  } else {
205  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
206  }
207  }
208  if ( logger().level() <= Logger::Debug ) {
209  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
210  }
211  }
212  }
213  }
214  }
215  }
216  }
217 
218  const Registry::FactoryMap& Registry::factories() const {
219  std::call_once( m_initialized, &Registry::initialize, const_cast<Registry*>( this ) );
220  return m_factories;
221  }
222 
223  Registry::FactoryMap& Registry::factories() {
224  std::call_once( m_initialized, &Registry::initialize, this );
225  return m_factories;
226  }
227 
228  Registry::FactoryInfo& Registry::add( const KeyType& id, FactoryInfo info ) {
230  FactoryMap& facts = factories();
231 
232 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
233  // add an alias for the factory using the Reflex convention
234  const auto old_name = old_style_name( id );
235  if ( id != old_name ) {
236  auto new_info = info;
237 
238  new_info.properties["ReflexName"] = "true";
239 
240  add( old_name, new_info );
241  }
242 #endif
243 
244  auto entry = facts.find( id );
245  if ( entry == facts.end() ) {
246  // this factory was not known yet
247  entry = facts.emplace( id, std::move( info ) ).first;
248  } else {
249  // do not replace an existing factory with a new one
250  if ( !entry->second.is_set() ) entry->second = std::move( info );
251  }
252  return entry->second;
253  }
254 
255  const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const {
257  static const FactoryInfo unknown = {"unknown"};
258  const FactoryMap& facts = factories();
259  auto f = facts.find( id );
260 
261  if ( f != facts.end() ) {
262  if ( load && !f->second.is_set() ) {
263  const std::string library = f->second.library;
264  if ( !dlopen( library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
265  logger().warning( "cannot load " + library + " for factory " + id );
266  char* dlmsg = dlerror();
267  if ( dlmsg ) logger().warning( dlmsg );
268  return unknown;
269  }
270  f = facts.find( id ); // ensure that the iterator is valid
271  }
272  return f->second;
273  } else {
274  return unknown;
275  }
276  }
277 
278  Registry& Registry::addProperty( const KeyType& id, const KeyType& k, const std::string& v ) {
280  FactoryMap& facts = factories();
281  auto f = facts.find( id );
282 
283  if ( f != facts.end() ) f->second.properties[k] = v;
284  return *this;
285  }
286 
287  std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
290  for ( const auto& f : factories() ) {
291  if ( f.second.is_set() ) l.insert( f.first );
292  }
293  return l;
294  }
295 
296  void Logger::report( Level lvl, const std::string& msg ) {
297  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
298  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
299  }
300 
301  static auto s_logger = std::make_unique<Logger>();
302  Logger& logger() { return *s_logger; }
303  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
304 
305  // This chunk of code was taken from GaudiKernel (genconf) DsoUtils.h
306  std::string getDSONameFor( void* fptr ) {
307 #ifdef _GNU_SOURCE
308  Dl_info info;
309  if ( dladdr( fptr, &info ) == 0 ) return "";
310 
311  auto pos = std::strrchr( info.dli_fname, '/' );
312  if ( pos )
313  ++pos;
314  else
315  return info.dli_fname;
316  return pos;
317 #else
318  return "";
319 #endif
320  }
321  } // namespace Details
322 
323  void SetDebug( int debugLevel ) {
324  using namespace Details;
325  Logger& l = logger();
326  if ( debugLevel > 1 )
327  l.setLevel( Logger::Debug );
328  else if ( debugLevel > 0 )
329  l.setLevel( Logger::Info );
330  else
331  l.setLevel( Logger::Warning );
332  }
333 
334  int Debug() {
335  using namespace Details;
336  switch ( logger().level() ) {
337  case Logger::Debug:
338  return 2;
339  case Logger::Info:
340  return 1;
341  default:
342  return 0;
343  }
344  }
345  }
346  } // namespace PluginService
347 } // namespace Gaudi
T regex_match(T...args)
#define GAUDI_PLUGIN_SERVICE_V2_INLINE
T getline(T...args)
T to_string(T...args)
T endl(T...args)
STL namespace.
T end(T...args)
void SetDebug(int debugLevel)
T free(T...args)
T call_once(T...args)
std::string getDSONameFor(void *fptr)
void reportBadAnyCast(const std::type_info &factory_type, const std::string &id)
STL class.
T getenv(T...args)
T push_back(T...args)
T regex_replace(T...args)
constexpr double m
Definition: SystemOfUnits.h:92
#define REG_SCOPE_LOCK
T str(T...args)
std::string demangle(const std::type_info &id)
T strrchr(T...args)
T move(T...args)
dictionary l
Definition: gaudirun.py:517
STL class.
STL class.
virtual Out operator()(const vector_of_const_< In > &inputs) const =0
T begin(T...args)
T c_str(T...args)
T substr(T...args)
#define SINGLETON_LOCK
T for_each(T...args)
Helper functions to set/get the application return code.
Definition: __init__.py:1
STL class.