14 #define GAUDI_PLUGIN_SERVICE_V2
17 #include <boost/algorithm/string.hpp>
37 #ifdef USE_BOOST_FILESYSTEM
38 # include <boost/filesystem.hpp>
39 namespace fs = boost::filesystem;
41 # include <filesystem>
42 namespace fs = std::filesystem;
43 #endif // USE_BOOST_FILESYSTEM
45 #if __cplusplus >= 201703 || (__clang__ && __APPLE__)
46 # include <string_view>
48 # include <experimental/string_view>
50 using experimental::string_view;
54 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
57 std::mutex registrySingletonMutex;
59 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
66 void operator()(
const char c ) {
75 name.push_back(
'_' );
78 name.push_back(
'r' );
81 name.push_back(
'p' );
92 std::string old_style_name(
const std::string& name ) {
93 return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
98 namespace PluginService {
103 auto realname = std::unique_ptr<char, decltype( free )*>(
104 abi::__cxa_demangle(
id.c_str(),
nullptr,
nullptr, &status ), free );
105 if ( !realname )
return id;
106 #if _GLIBCXX_USE_CXX11_ABI
107 return std::regex_replace(
109 std::regex{
"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
112 return std::string{realname.get()};
117 Registry& Registry::instance() {
125 std::stringstream msg;
126 const auto&
info = Registry::instance().getInfo(
id );
127 msg <<
"bad any_cast: requested factory " <<
id <<
" of type " <<
demangle( factory_type ) <<
", got ";
132 logger().debug( msg.str() );
136 Registry::Properties::mapped_type Registry::FactoryInfo::getprop(
const Properties::key_type& name )
const {
137 auto p = properties.find( name );
138 return ( p != end( properties ) ) ? p->second : Properties::mapped_type{};
141 Registry::Registry() {}
143 void Registry::initialize() {
145 #if defined( _WIN32 )
146 const std::string envVar =
"PATH";
147 const std::string sep =
";";
148 #elif defined( __APPLE__ )
149 const std::string envVar =
"DYLD_LIBRARY_PATH";
150 const std::string sep =
":";
152 const std::string envVar =
"LD_LIBRARY_PATH";
153 const std::string sep =
":";
156 std::regex line_format{
"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"};
159 std::string search_path;
160 const char* envPtr = std::getenv( envVar.c_str() );
161 if ( envPtr ) search_path = envPtr;
162 if ( search_path.empty() ) {
166 logger().debug(
"searching factories in " + envVar);
167 logger().debug(
"searching factories in " + search_path);
169 std::vector<std::string> directories;
170 boost::split(directories, search_path, boost::is_any_of(sep));
172 for(fs::path dirName: directories) {
173 if ( not is_directory( dirName ) ) {
176 logger().debug(
" looking into " + dirName.string() );
177 for (
auto& p : fs::directory_iterator( dirName ) ) {
179 if ( p.path().extension() ==
".components" && is_regular_file( p.path() ) ) {
181 const auto& fullPath = p.path().string();
182 logger().debug(
" reading " + p.path().filename().string() );
183 std::ifstream factories{fullPath};
185 int factoriesCount = 0;
187 while ( !factories.eof() ) {
189 std::getline( factories, line );
190 if ( regex_match( line, matches, line_format ) ) {
191 if ( matches[1] ==
"v2" ) {
192 const std::string lib{matches[2]};
193 const std::string fact{matches[3]};
194 m_factories.emplace( fact, FactoryInfo{lib, {}, {{
"ClassName", fact}}} );
195 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
197 std::string old_name = old_style_name( fact );
198 if ( fact != old_name ) {
199 m_factories.emplace( old_name,
200 FactoryInfo{lib, {}, {{
"ReflexName",
"true"}, {
"ClassName", fact}}} );
206 logger().debug(
"failed to parse line " + fullPath +
':' + std::to_string( lineCount ) );
210 logger().debug(
" found " + std::to_string( factoriesCount ) +
" factories" );
217 const Registry::FactoryMap& Registry::factories()
const {
218 std::call_once( m_initialized, &Registry::initialize,
const_cast<Registry*
>(
this ) );
222 Registry::FactoryMap& Registry::factories() {
223 std::call_once( m_initialized, &Registry::initialize,
this );
227 Registry::FactoryInfo& Registry::add(
const KeyType&
id, FactoryInfo
info ) {
229 FactoryMap& facts = factories();
231 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
233 const auto old_name = old_style_name(
id );
234 if (
id != old_name ) {
235 auto new_info =
info;
237 new_info.properties[
"ReflexName"] =
"true";
239 add( old_name, new_info );
243 auto entry = facts.find(
id );
244 if ( entry == facts.end() ) {
246 entry = facts.emplace(
id, std::move(
info ) ).first;
249 if ( !entry->second.is_set() ) entry->second = std::move(
info );
251 return entry->second;
254 const Registry::FactoryInfo& Registry::getInfo(
const KeyType&
id,
const bool load )
const {
256 static const FactoryInfo unknown = {
"unknown"};
257 const FactoryMap& facts = factories();
258 auto f = facts.find(
id );
260 if ( f != facts.end() ) {
261 if ( load && !f->second.is_set() ) {
262 const std::string library = f->second.library;
263 if ( !dlopen( library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
264 logger().warning(
"cannot load " + library +
" for factory " +
id );
265 char* dlmsg = dlerror();
266 if ( dlmsg )
logger().warning( dlmsg );
269 f = facts.find(
id );
277 Registry& Registry::addProperty(
const KeyType&
id,
const KeyType& k,
const std::string&
v ) {
279 FactoryMap& facts = factories();
280 auto f = facts.find(
id );
282 if ( f != facts.end() ) f->second.properties[k] =
v;
286 std::set<Registry::KeyType> Registry::loadedFactoryNames()
const {
289 for (
const auto& f : factories() ) {
290 if ( f.second.is_set() ) l.insert( f.first );
295 void Logger::report( Level lvl,
const std::string& msg ) {
296 static const char* levels[] = {
"DEBUG : ",
"INFO : ",
"WARNING: ",
"ERROR : "};
297 if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
300 static auto s_logger = std::make_unique<Logger>();
308 if ( dladdr( fptr, &
info ) == 0 )
return "";
310 auto pos = std::strrchr(
info.dli_fname,
'/' );
314 return info.dli_fname;
323 using namespace Details;
325 if ( debugLevel > 1 )
327 else if ( debugLevel > 0 )
328 l.setLevel( Logger::Info );
330 l.setLevel( Logger::Warning );
334 using namespace Details;
335 switch (
logger().level() ) {