The Gaudi Framework  v30r3 (a5ef0a68)
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
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 {
48  using experimental::string_view;
49 }
50 #endif
51 
52 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
53 
54 namespace
55 {
56  std::mutex registrySingletonMutex;
57 }
58 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard(::registrySingletonMutex );
59 
60 #include <algorithm>
61 
62 namespace
63 {
64  struct OldStyleCnv {
66  void operator()( const char c )
67  {
68  switch ( c ) {
69  case '<':
70  case '>':
71  case ',':
72  case '(':
73  case ')':
74  case ':':
75  case '.':
76  name.push_back( '_' );
77  break;
78  case '&':
79  name.push_back( 'r' );
80  break;
81  case '*':
82  name.push_back( 'p' );
83  break;
84  case ' ':
85  break;
86  default:
87  name.push_back( c );
88  break;
89  }
90  }
91  };
93  std::string old_style_name( const std::string& name )
94  {
95  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
96  }
97 }
98 
99 namespace Gaudi
100 {
101  namespace PluginService
102  {
103  GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2
104  {
105  namespace Details
106  {
108  {
109  int status;
111  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
112  if ( !realname ) return id;
113 #if _GLIBCXX_USE_CXX11_ABI
114  return std::regex_replace(
115  realname.get(),
116  std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
117  "std::string" );
118 #else
119  return std::string{realname.get()};
120 #endif
121  }
122  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
123 
124  Registry& Registry::instance()
125  {
127  static Registry r;
128  return r;
129  }
130 
131  void reportBadAnyCast( const std::type_info& factory_type, const std::string& id )
132  {
133  if ( logger().level() <= Logger::Debug ) {
135  const auto& info = Registry::instance().getInfo( id );
136  msg << "bad any_cast: requested factory " << id << " of type " << demangle( factory_type ) << ", got ";
137  if ( info.is_set() )
138  msg << demangle( info.factory.type() ) << " from " << info.library;
139  else
140  msg << "nothing";
141  logger().debug( msg.str() );
142  }
143  }
144 
145  Registry::Properties::mapped_type Registry::FactoryInfo::getprop( const Properties::key_type& name ) const
146  {
147  auto p = properties.find( name );
148  return ( p != end( properties ) ) ? p->second : Properties::mapped_type{};
149  }
150 
151  Registry::Registry() {}
152 
153  void Registry::initialize()
154  {
156 #if defined( _WIN32 )
157  const char* envVar = "PATH";
158  const char sep = ';';
159 #elif defined( __APPLE__ )
160  const char* envVar = "DYLD_LIBRARY_PATH";
161  const char sep = ':';
162 #else
163  const char* envVar = "LD_LIBRARY_PATH";
164  const char sep = ':';
165 #endif
166 
167  std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"};
168  std::smatch m;
169 
170  std::string_view search_path = std::getenv( envVar );
171  if ( !search_path.empty() ) {
172  logger().debug( std::string( "searching factories in " ) + envVar );
173 
174  std::string_view::size_type start_pos = 0, end_pos = 0;
175  while ( start_pos != std::string_view::npos ) {
176  // correctly handle begin of string or path separator
177  if ( start_pos ) ++start_pos;
178 
179  end_pos = search_path.find( sep, start_pos );
180  fs::path dirName =
181 #if __GNUC__ >= 7
182  search_path.substr( start_pos, end_pos - start_pos );
183 #else
184  std::string{search_path.substr( start_pos, end_pos - start_pos )};
185 #endif
186  start_pos = end_pos;
187 
188  logger().debug( " looking into " + dirName.string() );
189  // look for files called "*.components" in the directory
190  if ( is_directory( dirName ) ) {
191  for ( auto& p : fs::directory_iterator( dirName ) ) {
192  if ( p.path().extension() == ".components" && is_regular_file( p.path() ) ) {
193  // read the file
194  const auto& fullPath = p.path().string();
195  logger().debug( " reading " + p.path().filename().string() );
196  std::ifstream factories{fullPath};
198  int factoriesCount = 0;
199  int lineCount = 0;
200  while ( !factories.eof() ) {
201  ++lineCount;
202  std::getline( factories, line );
203  if ( regex_match( line, m, line_format ) ) {
204  if ( m[1] == "v2" ) { // ignore non "v2" and "empty" lines
205  const std::string lib{m[2]};
206  const std::string fact{m[3]};
207  m_factories.emplace( fact, FactoryInfo{lib, {}, {{"ClassName", fact}}} );
208 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
209  // add an alias for the factory using the Reflex convention
210  std::string old_name = old_style_name( fact );
211  if ( fact != old_name ) {
212  m_factories.emplace( old_name,
213  FactoryInfo{lib, {}, {{"ReflexName", "true"}, {"ClassName", fact}}} );
214  }
215 #endif
216  ++factoriesCount;
217  }
218  } else {
219  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
220  }
221  }
222  if ( logger().level() <= Logger::Debug ) {
223  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
224  }
225  }
226  }
227  }
228  }
229  }
230  }
231 
232  const Registry::FactoryMap& Registry::factories() const
233  {
234  std::call_once( m_initialized, &Registry::initialize, const_cast<Registry*>( this ) );
235  return m_factories;
236  }
237 
238  Registry::FactoryMap& Registry::factories()
239  {
240  std::call_once( m_initialized, &Registry::initialize, const_cast<Registry*>( this ) );
241  return m_factories;
242  }
243 
244  Registry::FactoryInfo& Registry::add( const KeyType& id, FactoryInfo info )
245  {
247  FactoryMap& facts = factories();
248 
249 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
250  // add an alias for the factory using the Reflex convention
251  const auto old_name = old_style_name( id );
252  if ( id != old_name ) {
253  auto new_info = info;
254 
255  new_info.properties["ReflexName"] = "true";
256 
257  add( old_name, new_info );
258  }
259 #endif
260 
261  auto entry = facts.find( id );
262  if ( entry == facts.end() ) {
263  // this factory was not known yet
264  entry = facts.emplace( id, std::move( info ) ).first;
265  } else {
266  // do not replace an existing factory with a new one
267  if ( !entry->second.is_set() ) entry->second = std::move( info );
268  }
269  return entry->second;
270  }
271 
272  const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const
273  {
275  static const FactoryInfo unknown = {"unknown"};
276  const FactoryMap& facts = factories();
277  auto f = facts.find( id );
278 
279  if ( f != facts.end() ) {
280  if ( load && !f->second.is_set() ) {
281  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
282  logger().warning( "cannot load " + f->second.library + " for factory " + id );
283  char* dlmsg = dlerror();
284  if ( dlmsg ) logger().warning( dlmsg );
285  return unknown;
286  }
287  f = facts.find( id ); // ensure that the iterator is valid
288  }
289  return f->second;
290  } else {
291  return unknown;
292  }
293  }
294 
295  Registry& Registry::addProperty( const KeyType& id, const KeyType& k, const std::string& v )
296  {
298  FactoryMap& facts = factories();
299  auto f = facts.find( id );
300 
301  if ( f != facts.end() ) f->second.properties[k] = v;
302  return *this;
303  }
304 
305  std::set<Registry::KeyType> Registry::loadedFactoryNames() const
306  {
309  for ( const auto& f : factories() ) {
310  if ( f.second.is_set() ) l.insert( f.first );
311  }
312  return l;
313  }
314 
315  void Logger::report( Level lvl, const std::string& msg )
316  {
317  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
318  if ( lvl >= level() ) {
319  std::cerr << levels[lvl] << msg << std::endl;
320  }
321  }
322 
323  static auto s_logger = std::make_unique<Logger>();
324  Logger& logger() { return *s_logger; }
325  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
326 
327  // This chunk of code was taken from GaudiKernel (genconf) DsoUtils.h
329  {
330 #ifdef _GNU_SOURCE
331  Dl_info info;
332  if ( dladdr( fptr, &info ) == 0 ) return "";
333 
334  auto pos = std::strrchr( info.dli_fname, '/' );
335  if ( pos )
336  ++pos;
337  else
338  return info.dli_fname;
339  return pos;
340 #else
341  return "";
342 #endif
343  }
344  } // namespace Details
345 
346  void SetDebug( int debugLevel )
347  {
348  using namespace Details;
349  Logger& l = logger();
350  if ( debugLevel > 1 )
351  l.setLevel( Logger::Debug );
352  else if ( debugLevel > 0 )
353  l.setLevel( Logger::Info );
354  else
355  l.setLevel( Logger::Warning );
356  }
357 
358  int Debug()
359  {
360  using namespace Details;
361  switch ( logger().level() ) {
362  case Logger::Debug:
363  return 2;
364  case Logger::Info:
365  return 1;
366  default:
367  return 0;
368  }
369  }
370  }
371  }
372 } // namespace Gaudi::PluginService
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:94
#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:440
STL class.
STL class.
virtual Out operator()(const vector_of_const_< In > &inputs) const =0
T begin(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.