/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2012 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #if !defined(WIN32) # include #else # include #endif MTS_NAMESPACE_BEGIN // ----------------------------------------------------------------------- // Abstract plugin module implementation // ----------------------------------------------------------------------- namespace { typedef void *(*CreateInstanceFunc)(const Properties &props); typedef void *(*CreateUtilityFunc)(); typedef char *(*GetDescriptionFunc)(); } struct Plugin::PluginPrivate { #if defined(WIN32) HMODULE handle; #else void *handle; #endif const std::string shortName; const fs::path path; bool isUtility; GetDescriptionFunc getDescription; CreateInstanceFunc createInstance; CreateUtilityFunc createUtility; PluginPrivate(const std::string &sn, const fs::path &p) : shortName(sn), path(p) {} }; Plugin::Plugin(const std::string &shortName, const fs::path &path) : d(new PluginPrivate(shortName, path)) { #if defined(_WIN32) d->handle = LoadLibraryW(path.c_str()); if (!d->handle) { SLog(EError, "Error while loading plugin \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } #else d->handle = dlopen(path.string().c_str(), RTLD_LAZY | RTLD_LOCAL); if (!d->handle) { SLog(EError, "Error while loading plugin \"%s\": %s", d->path.string().c_str(), dlerror()); } #endif try { d->getDescription = (GetDescriptionFunc) getSymbol("GetDescription"); } catch (...) { #if defined(_WIN32) FreeLibrary(d->handle); #else dlclose(d->handle); #endif throw; } d->createInstance = NULL; d->createUtility = NULL; d->isUtility = false; if (hasSymbol("CreateUtility")) { d->createUtility = (CreateUtilityFunc) getSymbol("CreateUtility"); d->isUtility = true; } else { d->createInstance = (CreateInstanceFunc) getSymbol("CreateInstance"); } Statistics::getInstance()->logPlugin(shortName, getDescription()); /* New classes must be registered within the class hierarchy */ Class::staticInitialization(); } bool Plugin::hasSymbol(const std::string &sym) const { #if defined(_WIN32) void *ptr = GetProcAddress(d->handle, sym.c_str()); #else void *ptr = dlsym(d->handle, sym.c_str()); #endif return ptr != NULL; } void *Plugin::getSymbol(const std::string &sym) { #if defined(_WIN32) void *data = GetProcAddress(d->handle, sym.c_str()); if (!data) { SLog(EError, "Could not resolve symbol \"%s\" in \"%s\": %s", sym.c_str(), d->path.string().c_str(), lastErrorText().c_str()); } #else void *data = dlsym(d->handle, sym.c_str()); if (!data) { SLog(EError, "Could not resolve symbol \"%s\" in \"%s\": %s", sym.c_str(), d->path.string().c_str(), dlerror()); } #endif return data; } ConfigurableObject *Plugin::createInstance(const Properties &props) const { return (ConfigurableObject *) d->createInstance(props); } Utility *Plugin::createUtility() const { return (Utility *) d->createUtility(); } bool Plugin::isUtility() const { return d->isUtility; } std::string Plugin::getDescription() const { return d->getDescription(); } const fs::path& Plugin::getPath() const { return d->path; } const std::string& Plugin::getShortName() const { return d->shortName; } Plugin::~Plugin() { #if defined(_WIN32) FreeLibrary(d->handle); #else dlclose(d->handle); #endif } // ----------------------------------------------------------------------- // Plugin manager // ----------------------------------------------------------------------- ref PluginManager::m_instance = NULL; PluginManager::PluginManager() { m_mutex = new Mutex(); } PluginManager::~PluginManager() { /* Release the memory used by plugin modules */ for (std::map::iterator it = m_plugins.begin(); it != m_plugins.end(); ++it) { delete (*it).second; } } ConfigurableObject *PluginManager::createObject(const Class *classType, const Properties &props) { ConfigurableObject *object; { LockGuard lock(m_mutex); ensurePluginLoaded(props.getPluginName()); object = m_plugins[props.getPluginName()]->createInstance(props); } if (!object->getClass()->derivesFrom(classType)) Log(EError, "Type mismatch when loading plugin \"%s\": Expected " "an instance of \"%s\"", props.getPluginName().c_str(), classType->getName().c_str()); if (object->getClass()->isAbstract()) Log(EError, "Error when loading plugin \"%s\": Identifies itself as an abstract class", props.getPluginName().c_str()); return object; } ConfigurableObject *PluginManager::createObject(const Properties &props) { ConfigurableObject *object; { LockGuard lock(m_mutex); ensurePluginLoaded(props.getPluginName()); object = m_plugins[props.getPluginName()]->createInstance(props); } if (object->getClass()->isAbstract()) Log(EError, "Error when loading plugin \"%s\": Identifies itself as an abstract class", props.getPluginName().c_str()); return object; } std::vector PluginManager::getLoadedPlugins() const { std::vector list; LockGuard lock(m_mutex); for (std::map::const_iterator it = m_plugins.begin(); it != m_plugins.end(); ++it) { list.push_back((*it).first); } return list; } void PluginManager::ensurePluginLoaded(const std::string &name) { /* Plugin already loaded? */ if (m_plugins[name] != NULL) return; /* Build the full plugin file name */ fs::path shortName = fs::path("plugins") / name; #if defined(WIN32) shortName.replace_extension(".dll"); #elif defined(__OSX__) shortName.replace_extension(".dylib"); #else shortName.replace_extension(".so"); #endif const FileResolver *resolver = Thread::getThread()->getFileResolver(); fs::path path = resolver->resolve(shortName); if (fs::exists(path)) { Log(EInfo, "Loading plugin \"%s\" ..", shortName.string().c_str()); m_plugins[name] = new Plugin(shortName.string(), path); return; } /* Plugin not found! */ Log(EError, "Plugin \"%s\" not found!", name.c_str()); } void PluginManager::staticInitialization() { m_instance = new PluginManager(); } void PluginManager::staticShutdown() { m_instance = NULL; } Version::Version(const std::string &versionString) { std::vector tokens = tokenize(trim(versionString), "."); if (tokens.size() != 3) SLog(EError, "Unable to parse version string \"%s\"!", versionString.c_str()); char *end_ptr = NULL; m_major = strtol(tokens[0].c_str(), &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Unable to parse the major program version \"%i\"!", tokens[0].c_str()); m_minor = strtol(tokens[1].c_str(), &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Unable to parse the minor program version \"%i\"!", tokens[1].c_str()); m_release = strtol(tokens[2].c_str(), &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Unable to parse the release program version \"%i\"!", tokens[2].c_str()); } std::string Version::toString() const { return formatString("%i.%i.%i", m_major, m_minor, m_release); } std::string Version::toStringComplete() const { std::ostringstream oss; oss << m_major << "." << m_minor << "." << m_release << " ("; #if defined(__WINDOWS__) oss << "Windows, "; #elif defined(__LINUX__) oss << "Linux, "; #elif defined(__OSX__) oss << "Mac OS, "; #else oss << "Unknown, "; #endif #if defined(__64BIT__) oss << "64 bit)"; #else oss << "32 bit)"; #endif return oss.str(); } MTS_IMPLEMENT_CLASS(PluginManager, false, Object) MTS_NAMESPACE_END