ComponentLoader.cpp
Go to the documentation of this file.
00001 #include "ComponentLoader.hpp"
00002 #include <rtt/TaskContext.hpp>
00003 #include <rtt/Logger.hpp>
00004 #include <boost/filesystem.hpp>
00005 #include <boost/version.hpp>
00006 #include <rtt/os/StartStopManager.hpp>
00007 #include <rtt/plugin/PluginLoader.hpp>
00008 #include <rtt/types/TypekitRepository.hpp>
00009 
00010 #ifdef HAS_ROSLIB
00011 #include <rospack/rospack.h>
00012 #endif
00013 
00014 #ifndef _WIN32
00015 # include <dlfcn.h>
00016 #endif
00017 
00018 using namespace RTT;
00019 using namespace RTT::detail;
00020 using namespace std;
00021 using namespace boost::filesystem;
00022 
00023 namespace RTT
00024 {
00025     // We have our own copy of the Factories object to store all
00026     // loaded component types. This is pointer is only shared with the DeploymentComponent.
00027     FactoryMap* ComponentFactories::Factories = 0;
00028 }
00029 
00030 // chose the file extension and debug postfix applicable to the O/S
00031 #ifdef  __APPLE__
00032 static const std::string SO_EXT(".dylib");
00033 static const std::string SO_POSTFIX("");
00034 #else
00035 # ifdef _WIN32
00036 static const std::string SO_EXT(".dll");
00037 #  ifdef _DEBUG
00038 static const std::string SO_POSTFIX("d");
00039 #  else
00040 static const std::string SO_POSTFIX("");
00041 #  endif // _DEBUG
00042 # else
00043 static const std::string SO_EXT(".so");
00044 static const std::string SO_POSTFIX("");
00045 # endif
00046 #endif
00047 
00048 // The full library suffix must be enforced by the UseOrocos macros
00049 static const std::string FULL_COMPONENT_SUFFIX(string("-") + string(OROCOS_TARGET_NAME) + SO_POSTFIX + SO_EXT);
00050 
00051 // choose how the PATH looks like
00052 # ifdef _WIN32
00053 static const std::string delimiters(";");
00054 static const char default_delimiter(';');
00055 # else
00056 static const std::string delimiters(":;");
00057 static const char default_delimiter(':');
00058 # endif
00059 
00068 static bool isExtensionVersion(const std::string& ext)
00069 {
00070     bool isExtensionVersion = false;
00071 
00072         if (!ext.empty() && ('.' == ext[0]))
00073         {
00074         std::istringstream      iss;
00075         int                                     i;
00076 
00077                 iss.str(ext.substr((size_t)1));     // take all after the '.'
00078         iss >> std::dec >> std::noskipws >> i;
00079         isExtensionVersion = !iss.fail() && iss.eof();
00080     }
00081 
00082     return isExtensionVersion;
00083 }
00084 
00085 /* Is this a dynamic library that we should load from within a directory scan?
00086 
00087    Versioned libraries are not loaded, to prevent loading both libfoo.so and
00088    libfoo.so.1 (which is typically a symlink to libfoo.so, and so loading
00089    the same library twice).
00090 
00091    Libraries are either (NB x.y.z is version, and could also be x or x.y)
00092 
00093    Linux
00094    libfoo.so          = load this
00095    libfoo.so.x.y.z    = don't load this
00096 
00097    Windows
00098    libfoo.dll         = load this
00099 
00100    Mac OS X
00101    libfoo.dylib       = load this
00102    libfoo.x.y.z.dylib = don't load this
00103 
00104    All the above also apply without the "lib" prefix.
00105 */
00106 static bool isLoadableLibrary(const path& filename)
00107 {
00108     bool isLoadable = false;
00109 
00110 #if     defined(__APPLE__)
00111         std::string     ext;
00112 #if BOOST_VERSION >= 104600
00113         ext = filename.extension().string();
00114 #else
00115         ext = filename.extension();
00116 #endif
00117     // ends in SO_EXT?
00118         if (0 == ext.compare(SO_EXT))
00119         {
00120                 // Ends in SO_EXT and so must not be a link for us to load
00121                 // Links are of the form abc.x.dylib or abc.x.y.dylib or abc.x.y.z.dylib,
00122                 // where x,y,z are positive numbers
00123                 path    name    = filename.stem();          // drop SO_EXT
00124                 path    ext     = name.extension();
00125                 isLoadable =
00126                         // wasn't just SO_EXT
00127                         !name.empty() &&
00128                         // and if there is and extension then it is not a number
00129                         (ext.empty() || !isExtensionVersion(ext.string()));
00130     }
00131     // else is not loadable
00132 
00133 #else
00134     // Linux or Windows
00135 
00136     // must end in SO_EXT and have a non-extension part
00137     isLoadable =
00138         (filename.extension() == SO_EXT) &&
00139         !filename.stem().empty();
00140 #endif
00141 
00142     return isLoadable;
00143 }
00144 
00145 namespace RTT {
00146     extern char const* default_comp_path_build;
00147 }
00148 
00149 namespace {
00153     int loadComponents()
00154     {
00155         std::string default_comp_path = ::default_comp_path_build;
00156 
00157         char* paths = getenv("RTT_COMPONENT_PATH");
00158         string component_paths;
00159         if (paths) {
00160             component_paths = paths;
00161             // prepend the default search path.
00162             if ( !default_comp_path.empty() )
00163                 component_paths = component_paths + default_delimiter + default_comp_path;
00164             log(Info) <<"RTT_COMPONENT_PATH was set to: " << paths << " . Searching in: "<< component_paths<< endlog();
00165         } else {
00166             component_paths = default_comp_path;
00167             log(Info) <<"No RTT_COMPONENT_PATH set. Using default: " << component_paths <<endlog();
00168         }
00169         // we set the component path such that we can search for sub-directories/projects lateron
00170         ComponentLoader::Instance()->setComponentPath(component_paths);
00171         return 0;
00172     }
00173 
00174     os::InitFunction component_loader( &loadComponents );
00175 
00176     void unloadComponents()
00177     {
00178         ComponentLoader::Release();
00179     }
00180 
00181     os::CleanupFunction component_unloader( &unloadComponents );
00182 }
00183 
00184 static boost::shared_ptr<ComponentLoader> instance2;
00185 
00186 namespace {
00187 
00188     // copied from RTT::PluginLoader
00189 static vector<string> splitPaths(string const& str)
00190 {
00191     vector<string> paths;
00192 
00193     // Skip delimiters at beginning.
00194     string::size_type lastPos = str.find_first_not_of(delimiters, 0);
00195     // Find first "non-delimiter".
00196     string::size_type pos = str.find_first_of(delimiters, lastPos);
00197 
00198     while (string::npos != pos || string::npos != lastPos)
00199     {
00200         // Found a token, add it to the vector.
00201         if ( !str.substr(lastPos, pos - lastPos).empty() )
00202             paths.push_back(str.substr(lastPos, pos - lastPos));
00203         // Skip delimiters.  Note the "not_of"
00204         lastPos = str.find_first_not_of(delimiters, pos);
00205         // Find next "non-delimiter"
00206         pos = str.find_first_of(delimiters, lastPos);
00207     }
00208     if ( paths.empty() )
00209         paths.push_back(".");
00210     return paths;
00211 }
00212 
00219 static string makeShortFilename(string const& str) {
00220     string ret = str;
00221     if (str.substr(0,3) == "lib")
00222         ret = str.substr(3);
00223     if (ret.rfind(FULL_COMPONENT_SUFFIX) != string::npos)
00224         ret = ret.substr(0, ret.rfind(FULL_COMPONENT_SUFFIX) );
00225     return ret;
00226 }
00227 
00228 }
00229 
00230 static bool hasEnding(string const &fullString, string const &ending)
00231 {
00232     if (fullString.length() > ending.length()) {
00233         return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
00234     } else {
00235         return false;
00236     }
00237 }
00238 
00239 boost::shared_ptr<ComponentLoader> ComponentLoader::Instance() {
00240     if (!instance2)
00241         instance2.reset( new ComponentLoader() );
00242     return instance2;
00243 }
00244 
00245 void ComponentLoader::Release() {
00246     instance2.reset();
00247 }
00248 
00249 // This is the dumb import function that takes an existing directory and
00250 // imports components and plugins from it.
00251 bool ComponentLoader::import( std::string const& path_list )
00252 {
00253     if (path_list.empty() ) {
00254         log(Error) << "import paths: No paths were given for loading ( path_list = '' )."<<endlog();
00255         return false;
00256     }
00257 
00258     // we return false if nothing was found here, or an error happened during loading of a library.
00259     vector<string> paths;
00260     paths = splitPaths( path_list ); // import package or path list.
00261 
00262     bool all_good = true, found = false;
00263     // perform search in paths:
00264     for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
00265     {
00266         // Scan path/* (non recursive) for components
00267         path p = path(*it);
00268         if (is_directory(p))
00269         {
00270             log(Info) << "Importing directory " << p.string() << " ..."<<endlog();
00271             for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
00272             {
00273                 log(Debug) << "Scanning file " << itr->path().string() << " ...";
00274                 if (is_regular_file(itr->status()) && isLoadableLibrary(itr->path()) ) {
00275                     found = true;
00276                     std::string libname;
00277 #if BOOST_VERSION >= 104600
00278                     libname = itr->path().filename().string();
00279 #else
00280                     libname = itr->path().filename();
00281 #endif
00282                     if(!isCompatibleComponent(libname))
00283                     {
00284                         log(Debug) << "not a compatible component: ignored."<<endlog();
00285                     }
00286                     else
00287                     {
00288                         found = true;
00289                         all_good = loadInProcess( itr->path().string(), makeShortFilename(libname ),  true) && all_good;
00290                     }
00291                 } else {
00292                     if (!is_regular_file(itr->status()))
00293                         log(Debug) << "not a regular file: ignored."<<endlog();
00294                     else
00295                         log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
00296                 }
00297             }
00298             log(Debug) << "Looking for plugins or typekits in directory " << p.string() << " ..."<<endlog();
00299             try {
00300                 found = PluginLoader::Instance()->loadTypekits( p.string() ) || found;
00301                 found = PluginLoader::Instance()->loadPlugins( p.string() ) || found;
00302             } catch (std::exception& e) {
00303                 all_good = false;
00304                 log(Error) << e.what() <<endlog();
00305             }
00306         }
00307         else {
00308             // If the path is not complete (not absolute), look it up in the search directories:
00309             log(Debug) << "No such directory: " << p<< endlog();
00310         }
00311 #if 0
00312         // Repeat for path/OROCOS_TARGET: (already done in other import function)
00313         p = path(*it) / OROCOS_TARGET_NAME;
00314         if (is_directory(p))
00315         {
00316             log(Info) << "Importing component libraries from directory " << p.string() << " ..."<<endlog();
00317             for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
00318             {
00319                 log(Debug) << "Scanning file " << itr->path().string() << " ...";
00320                 if (is_regular_file(itr->status()) && isLoadableLibrary(itr->path()) ) {
00321                     found = true;
00322 #if BOOST_VERSION >= 104600
00323                     all_good = loadInProcess( itr->path().string(), makeShortFilename(itr->path().filename().string() ),  true) && all_good;
00324 #else
00325                     all_good = loadInProcess( itr->path().string(), makeShortFilename(itr->path().filename() ),  true) && all_good;
00326 #endif
00327                 }else {
00328                     if (!is_regular_file(itr->status()))
00329                         log(Debug) << "not a regular file: ignored."<<endlog();
00330                     else
00331                         log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
00332                 }
00333             }
00334             log(Info) << "Importing plugins and typekits from directory " << p.string() << " ..."<<endlog();
00335             try {
00336                 found = PluginLoader::Instance()->loadTypekits( p.string() ) || found;
00337                 found = PluginLoader::Instance()->loadPlugins( p.string() ) || found;
00338             } catch (std::exception& e) {
00339                 all_good = false;
00340                 log(Error) << e.what() <<endlog();
00341             }
00342         }
00343 #endif
00344     }
00345     if (!all_good)
00346         throw std::runtime_error("Some found plugins could not be loaded !");
00347     return found;
00348 }
00349 
00350 // this is the smart import function that tries to guess where 'package' lives in path_list or
00351 // the search path.
00352 bool ComponentLoader::import( std::string const& package, std::string const& path_list )
00353 {
00354     // check first for exact match to *file*:
00355     path arg( package );
00356     if (is_regular_file(arg) && isLoadableLibrary(arg)) {
00357 #if BOOST_VERSION >= 104600
00358             return loadInProcess(arg.string(), makeShortFilename( arg.filename().string() ), true);
00359 #else
00360             return loadInProcess(arg.string(), makeShortFilename( arg.filename() ), true);
00361 #endif
00362     }
00363 
00364     // check for absolute path:
00365     if ( arg.is_complete() ) {
00366         // plain import
00367         bool ret = import(package);
00368         // if not yet given, test for target subdir:
00369         if ( arg.parent_path().leaf() != OROCOS_TARGET_NAME )
00370             ret = import( (arg / OROCOS_TARGET_NAME).string() ) || ret;
00371         // if something found, return true:
00372         if (ret)
00373             return true;
00374         // both failed:
00375         log(Error) << "Could not import absolute path '"<<package << "': nothing found."<<endlog();
00376         return false;
00377     }
00378 
00379     if ( isImported(package) ) {
00380         log(Info) <<"Component package '"<< package <<"' already imported." <<endlog();
00381         return true;
00382     }
00383 
00384     // from here on: it's a package name or a path.
00385     // package names must be found to return true. 
00386     // path will be scanned and will always return true, but will warn when invalid.
00387     if ( importRosPackage(package) )
00388         return true;
00389 
00390     // Try the RTT_COMPONENT_PATH:
00391     return importInstalledPackage(package, path_list);
00392 }
00393 
00394 bool ComponentLoader::importRosPackage(std::string const& package)
00395 {
00396     // check for rospack
00397 #ifdef HAS_ROSLIB
00398     using namespace rospack;
00399     try {
00400         bool found = false;
00401         Rospack rpack;
00402         rpack.setQuiet(true);
00403         char* rpp = getenv("ROS_PACKAGE_PATH");
00404         vector<string> paths;
00405         if (rpp)
00406             paths = splitPaths(rpp);
00407         string ppath;
00408         rpack.crawl(paths,false);
00409         rpack.find(package, ppath);
00410         if ( !ppath.empty() ) {
00411             path rospath = path(ppath) / "lib" / "orocos";
00412             path rospath_target = rospath / OROCOS_TARGET_NAME;
00413             // + add all dependencies to paths:
00414             vector<string> rospackresult;
00415             rpack.setQuiet(false);
00416             bool valid = rpack.deps(package, false, rospackresult); // false: also indirect deps.
00417             if (!valid) {
00418                 log(Error) <<"The ROS package '"<< package <<"' in '"<< ppath << "' caused trouble. Bailing out."<<endlog();
00419                 return false;
00420             }
00421                 
00422             for(vector<string>::iterator it = rospackresult.begin(); it != rospackresult.end(); ++it) {
00423                 if ( isImported(*it) ) {
00424                     log(Debug) <<"Package dependency '"<< *it <<"' already imported." <<endlog();
00425                     continue;
00426                 }
00427                 if ( rpack.find( *it, ppath ) == false )
00428                     throw *it;
00429                 path deppath = path(ppath) / "lib" / "orocos";
00430                 path deppath_target = path(deppath) / OROCOS_TARGET_NAME;
00431                 // if orocos directory exists and we could import it, mark it as loaded.
00432                 if ( is_directory( deppath_target ) ) {
00433                     log(Debug) << "Ignoring files under " << deppath.string() << " since " << deppath_target.string() << " was found."<<endlog();
00434                     found = true;
00435                     if ( import( deppath_target.string() ) ) {
00436                         loadedPackages.push_back( *it );
00437                     }
00438                 }
00439                 else if ( is_directory( deppath ) ) {
00440                     found = true;
00441                     if ( import( deppath.string() ) ) {
00442                         loadedPackages.push_back( *it );
00443                     }
00444                 }
00445             }
00446             // now that all deps are done, import the package itself:
00447             if ( is_directory( rospath_target ) ) {
00448                 log(Debug) << "Ignoring files under " << rospath.string() << " since " << rospath_target.string() << " was found."<<endlog();
00449                 found = true;
00450                 if ( import( rospath_target.string() ) ) {
00451                     loadedPackages.push_back( package );
00452                 }
00453             } else if ( is_directory( rospath ) ) {
00454                 found = true;
00455                 if ( import( rospath.string() ) ) {
00456                     loadedPackages.push_back( package );
00457                 }
00458             }
00459             // since it was a ROS package, we exit here.
00460             if (!found) {
00461                 log(Debug) <<"The ROS package '"<< package <<"' in '"<< ppath << "' nor its dependencies contained a lib/orocos directory. I'll look in the RTT_COMPONENT_PATH next."<<endlog();
00462             }
00463             return found;
00464         } else
00465             log(Info) << "Not a ros package: " << package << endlog();
00466     } catch(std::string arg) {
00467         log(Info) << "While processing the dependencies of " << package << ": not a ros package: " << arg << endlog();
00468     }
00469 #endif
00470     return false;
00471 }
00472 
00473 bool ComponentLoader::importInstalledPackage(std::string const& package, std::string const& path_list)
00474 {
00475     string paths;
00476     string trypaths;
00477     vector<string> tryouts;
00478     if (path_list.empty())
00479         paths = component_path + default_delimiter + ".";
00480     else
00481         paths = path_list;
00482 
00483     bool path_found = false;
00484 
00485     // if ros did not find anything, split the paths above.
00486     // set vpaths from (default) search paths.
00487     vector<string> vpaths;
00488     vpaths = splitPaths(paths);
00489     trypaths = paths; // store for error reporting below.
00490     paths.clear();
00491     // Detect absolute/relative import:
00492     path p( package );
00493     if (is_directory( p )) {
00494         path_found = true;
00495         // search in dir + dir/TARGET
00496         paths += p.string() + default_delimiter + (p / OROCOS_TARGET_NAME).string() + default_delimiter;
00497         if ( p.is_complete() ) {
00498             // 2.2.x: path may be absolute or relative to search path.
00499             //log(Warning) << "You supplied an absolute directory to the import directive. Use 'path' to set absolute directories and 'import' only for packages (sub directories)."<<endlog();
00500             //log(Warning) << "Please modify your XML file or script. I'm importing it now for the sake of backwards compatibility."<<endlog();
00501         } // else: we allow to import a subdirectory of '.'.
00502     }
00503     // append '/package' or 'target/package' to each plugin path in order to search all of them:
00504     for(vector<string>::iterator it = vpaths.begin(); it != vpaths.end(); ++it) {
00505         p = *it;
00506         p = p / package;
00507         // we only search in existing directories:
00508         if (is_directory( p )) {
00509             path_found = true;
00510             paths += p.string() + default_delimiter ;
00511         } else
00512             tryouts.push_back( p.string() );
00513         p = *it;
00514         p = p / OROCOS_TARGET_NAME / package;
00515         // we only search in existing directories:
00516         if (is_directory( p )) {
00517             path_found = true;
00518             paths += p.string() + default_delimiter ;
00519         } else
00520             tryouts.push_back( p.string() );
00521     }
00522     if ( path_found )
00523         paths.erase( paths.size() - 1 ); // remove trailing delimiter ';'
00524 
00525     // when at least one directory exists:
00526     if (path_found) {
00527         if ( import(paths) ) {
00528             loadedPackages.push_back( package );
00529             return true;
00530         } else {
00531             log(Error) << "Failed to import components, types or plugins from package or directory '"<< package <<"' found in:"<< endlog();
00532             log(Error) << paths << endlog();
00533             return false;
00534         }
00535     }
00536     log(Error) << "No such package or directory found in search path: " << package << ". Search path is: "<< endlog();
00537     log(Error) << trypaths << endlog();
00538     for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
00539         log(Error) << *it << endlog();
00540     return false;
00541 }
00542 
00543 bool ComponentLoader::reloadLibrary(std::string const& name)
00544 {
00545     path arg = name;
00546     // check for direct match:
00547 #if BOOST_VERSION >= 104600
00548     if (is_regular_file( arg ) && reloadInProcess( arg.string(), makeShortFilename( arg.filename().string() ) ) )
00549 #else
00550     if (is_regular_file( arg ) && reloadInProcess( arg.string(), makeShortFilename( arg.filename() ) ) )
00551 #endif
00552         return true;
00553     // bail out if not an absolute path
00554     return false;
00555 }
00556 
00557 bool ComponentLoader::loadLibrary( std::string const& name )
00558 {
00559     path arg = name;
00560     // check for direct match:
00561 #if BOOST_VERSION >= 104600
00562     if (is_regular_file( arg ) && loadInProcess( arg.string(), makeShortFilename( arg.filename().string() ), true ) )
00563 #else
00564     if (is_regular_file( arg ) && loadInProcess( arg.string(), makeShortFilename( arg.filename() ), true ) )
00565 #endif
00566         return true;
00567     // bail out if absolute path
00568     if ( arg.is_complete() )
00569         return false;
00570 
00571     // search for relative match
00572     vector<string> paths = splitPaths( component_path );
00573     vector<string> tryouts( paths.size() * 4 );
00574     tryouts.clear();
00575     path dir = arg.parent_path();
00576 #if BOOST_VERSION >= 104600
00577     string file = arg.filename().string();
00578 #else
00579     string file = arg.filename();
00580 #endif
00581 
00582     for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
00583     {
00584         path p = path(*it) / dir / (file + FULL_COMPONENT_SUFFIX);
00585         tryouts.push_back( p.string() );
00586         if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00587             return true;
00588         p = path(*it) / dir / ("lib" + file + FULL_COMPONENT_SUFFIX);
00589         tryouts.push_back( p.string() );
00590         if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00591             return true;
00592         p = path(*it) / OROCOS_TARGET_NAME / dir / (file + FULL_COMPONENT_SUFFIX);
00593         tryouts.push_back( p.string() );
00594         if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00595             return true;
00596         p = path(*it) / OROCOS_TARGET_NAME / dir / ("lib" + file + FULL_COMPONENT_SUFFIX);
00597         tryouts.push_back( p.string() );
00598         if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00599             return true;
00600     }
00601     log(Debug) << "No such library found in path: " << name << ". Tried:"<< endlog();
00602     for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
00603         log(Debug) << *it << endlog();
00604     return false;
00605 }
00606 
00607 bool ComponentLoader::isImported(string type_name)
00608 {
00609     if (ComponentFactories::Instance().find(type_name) != ComponentFactories::Instance().end() )
00610         return true;
00611     if (find(loadedPackages.begin(), loadedPackages.end(), type_name) != loadedPackages.end())
00612         return true;
00613     // hack: in current versions, ocl is loaded most of the times by default because it does not reside in a package subdir
00614     // once ocl is in the 'ocl' package directory, this code becomes obsolete:
00615     if ( type_name == "ocl" && TypekitRepository::hasTypekit("OCLTypekit")) {
00616         return true;
00617     }
00618     return false;
00619 }
00620 
00621 bool ComponentLoader::reloadInProcess(string file, string libname)
00622 {
00623     path p(file);
00624 
00625     // check if the library is already loaded
00626     // NOTE if this library has been loaded, you can unload and reload it to apply changes (may be you have updated the dynamic library)
00627     // anyway it is safe to do this only if there isn't any instance whose type was loaded from this library
00628 
00629     std::vector<LoadedLib>::iterator lib = loadedLibs.begin();
00630     while (lib != loadedLibs.end()) {
00631         // We only reload if it's exactly the same file.
00632         if ( lib->filename == file) {
00633             log(Info) <<"Component library "<< lib->filename <<" already loaded... " ;
00634 
00635             bool can_unload = true;
00636             CompList::iterator cit;
00637             for( std::vector<std::string>::iterator ctype = lib->components_type.begin();  ctype != lib->components_type.end() && can_unload; ++ctype) {
00638                 for ( cit = comps.begin(); cit != comps.end(); ++cit) {
00639                     if( (*ctype) == cit->second.type ) {
00640                         // the type of an allocated component was loaded from this library. it might be unsafe to reload the library
00641                         log(Info) << "can NOT reload library because of the instance " << cit->second.type  <<"::"<<cit->second.instance->getName()  <<endlog();
00642                         can_unload = false;
00643                     }
00644                 }
00645             }
00646             if( can_unload ) {
00647                 log(Info) << "try to RELOAD"<<endlog();
00648                 dlclose(lib->handle);
00649                 // remove the library info from the vector
00650                 std::vector<LoadedLib>::iterator lib_un = lib;
00651                 loadedLibs.erase(lib_un);
00652                 return loadInProcess(file, libname, true);
00653             }
00654             else
00655                 return false;
00656         }
00657         else lib++;
00658     }
00659     log(Error) << "Can't reload Component library "<< file << " since it was not loaded or is not a component library." <<endlog();
00660     return false;
00661 }
00662 
00663 // loads a single component in the current process.
00664 bool ComponentLoader::loadInProcess(string file, string libname, bool log_error) {
00665     path p(file);
00666     char* error;
00667     void* handle;
00668     bool success=false;
00669 
00670     // Last chance to validate component compatibility
00671     if(!isCompatibleComponent(file))
00672     {
00673         if(log_error)
00674             log(Error) << "Could not load library '"<< p.string() <<"': incompatible." <<endlog();
00675         return false;
00676     }
00677 
00678     handle = dlopen ( p.string().c_str(), RTLD_NOW);
00679 
00680     if (!handle) {
00681         if ( log_error ) {
00682             log(Error) << "Could not load library '"<< p.string() <<"':"<<endlog();
00683             log(Error) << dlerror() << endlog();
00684         }
00685         return false;
00686     }
00687 
00688     //------------- if you get here, the library has been loaded -------------
00689     log(Debug)<<"Succesfully loaded "<<libname<<endlog();
00690     LoadedLib loading_lib(file, libname, handle);
00691     dlerror();    /* Clear any existing error */
00692 
00693     // Lookup Component factories (multi component case):
00694     FactoryMap* (*getfactory)(void) = 0;
00695     vector<string> (*getcomponenttypes)(void) = 0;
00696     FactoryMap* fmap = 0;
00697     getfactory = (FactoryMap*(*)(void))( dlsym(handle, "getComponentFactoryMap") );
00698     if ((error = dlerror()) == NULL) {
00699         // symbol found, register factories...
00700         fmap = (*getfactory)();
00701         ComponentFactories::Instance().insert( fmap->begin(), fmap->end() );
00702         log(Info) << "Loaded multi component library '"<< file <<"'"<<endlog();
00703         getcomponenttypes = (vector<string>(*)(void))(dlsym(handle, "getComponentTypeNames"));
00704         if ((error = dlerror()) == NULL) {
00705             log(Debug) << "Components:";
00706             vector<string> ctypes = getcomponenttypes();
00707             for (vector<string>::iterator it = ctypes.begin(); it != ctypes.end(); ++it)
00708                 log(Debug) <<" "<< *it;
00709             log(Debug) << endlog();
00710         }
00711         loadedLibs.push_back(loading_lib);
00712         success = true;
00713     }
00714 
00715     // Lookup createComponent (single component case):
00716     dlerror();    /* Clear any existing error */
00717 
00718     RTT::TaskContext* (*factory)(std::string) = 0;
00719     std::string(*tname)(void) = 0;
00720     factory = (RTT::TaskContext*(*)(std::string))(dlsym(handle, "createComponent") );
00721     string create_error;
00722     error = dlerror();
00723     if (error) create_error = error;
00724     tname = (std::string(*)(void))(dlsym(handle, "getComponentType") );
00725     string gettype_error;
00726     error = dlerror();
00727     if (error) gettype_error = error;
00728     if ( factory && tname ) {
00729         std::string cname = (*tname)();
00730         if ( ComponentFactories::Instance().count(cname) == 1 ) {
00731             log(Warning) << "Component type name "<<cname<<" already used: overriding."<<endlog();
00732         }
00733         ComponentFactories::Instance()[cname] = factory;
00734         log(Info) << "Loaded component type '"<< cname <<"'"<<endlog();
00735         loading_lib.components_type.push_back( cname );
00736         loadedLibs.push_back(loading_lib);
00737         success = true;
00738     }
00739 
00740     if (success) return true;
00741 
00742     log(Error) <<"Unloading "<< loading_lib.filename  <<": not a valid component library:" <<endlog();
00743     if (!create_error.empty())
00744         log(Error) << "   " << create_error << endlog();
00745     if (!gettype_error.empty())
00746         log(Error) << "   " << gettype_error << endlog();
00747     dlclose(handle);
00748     return false;
00749 }
00750 
00751 std::vector<std::string> ComponentLoader::listComponentTypes() const {
00752     vector<string> names;
00753     FactoryMap::iterator it;
00754     for( it = ComponentFactories::Instance().begin(); it != ComponentFactories::Instance().end(); ++it) {
00755         names.push_back( it->first );
00756     }
00757     return names;
00758 }
00759 
00760 std::string ComponentLoader::getComponentPath() const {
00761     string ret = component_path;
00762     // append default delimiter if not present. such that it can be combined with a new path.
00763     if ( ret.length() && ret[ ret.length() -1 ] != default_delimiter )
00764         ret += default_delimiter;
00765     return ret;
00766 }
00767 
00768 void ComponentLoader::setComponentPath( std::string const& newpath ) {
00769     component_path = newpath;
00770 }
00771 
00772 
00773 RTT::TaskContext *ComponentLoader::loadComponent(const std::string & name, const std::string & type)
00774 {
00775     TaskContext* instance = 0;
00776     RTT::TaskContext* (*factory)(std::string name) = 0;
00777     log(Debug) << "Trying to create component "<< name <<" of type "<< type << endlog();
00778 
00779     // First: try loading from imported libraries. (see: import).
00780     if ( ComponentFactories::Instance().count(type) == 1 ) {
00781         factory = ComponentFactories::Instance()[ type ];
00782         if (factory == 0 ) {
00783             log(Error) <<"Found empty factory for Component type "<<type<<endlog();
00784             return 0;
00785         }
00786     }
00787 
00788     if ( factory ) {
00789         log(Debug) <<"Found factory for Component type "<<type<<endlog();
00790     } else {
00791         log(Error) << "Unable to create Orocos Component '"<<type<<"': unknown component type." <<endlog();
00792         return 0;
00793     }
00794 
00795     comps[name].type = type;
00796 
00797     try {
00798         comps[name].instance = instance = (*factory)(name);
00799     } catch(...) {
00800         log(Error) <<"The constructor of component type "<<type<<" threw an exception!"<<endlog();
00801     }
00802 
00803     if ( instance == 0 ) {
00804         log(Error) <<"Failed to load component with name "<<name<<": refused to be created."<<endlog();
00805     }
00806     return instance;
00807 }
00808 
00809 bool ComponentLoader::unloadComponent( RTT::TaskContext* tc ) {
00810     if (!tc)
00811         return false;
00812     CompList::iterator it;
00813     it = comps.find( tc->getName() );
00814 
00815     if ( it != comps.end() ) {
00816         delete tc;
00817         comps.erase(it);
00818         return true;
00819     }
00820     log(Error) <<"Refusing to unload a component I didn't load myself."<<endlog();
00821     return false;
00822 }
00823 
00824 std::vector<std::string> ComponentLoader::listComponents() const
00825 {
00826     vector<string> names( comps.size() );
00827     for(map<string,ComponentData>::const_iterator it = comps.begin(); it != comps.end(); ++it)
00828         names.push_back( it->first );
00829     return names;
00830 }
00831 
00832 bool ComponentLoader::isCompatibleComponent(std::string const& filepath)
00833 {
00834     path p(filepath);
00835 
00836 #if BOOST_VERSION >= 104600
00837     string libname = p.filename().string();
00838 #else
00839     string libname = p.filename();
00840 #endif
00841 
00842     //log(Debug) << "Validating compatility of component file '" + libname + "'" <<endlog();
00843 
00844 #ifdef _WIN32
00845     // On WIN32 target:
00846     // - look if the library name ends with "win32.dll" on release mode
00847     // - look if the library name ends with "win32d.dll" on debug mode
00848     if(!hasEnding(libname, FULL_COMPONENT_SUFFIX))
00849     {
00850         //log(Debug) << "Component file '" + libname + "' is incompatible because it doesn't have the suffix " << FULL_COMPONENT_SUFFIX << endlog();
00851         return false;
00852     }
00853 #endif // _WIN32
00854 
00855     // There's no validation on other targets
00856 
00857     //log(Debug) << "Component file '" + libname + "' is valid." <<endlog();
00858 
00859     return true;
00860 }
00861 
00862 const FactoryMap& ComponentLoader::getFactories() const
00863 {
00864   return ComponentFactories::Instance();
00865 }
00866 
00867 void ComponentLoader::addFactory(std::string const& name, ComponentLoaderSignature factory)
00868 {
00869   ComponentFactories::Instance()[name] = factory;
00870 }


rtt
Author(s): RTT Developers
autogenerated on Mon Oct 6 2014 03:13:34