mitsuba/src/libcore/plugin.cpp

302 lines
8.1 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/core/plugin.h>
#include <mitsuba/core/statistics.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/core/lock.h>
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/cobject.h>
#include <mitsuba/core/version.h>
#if !defined(WIN32)
# include <dlfcn.h>
#else
# include <windows.h>
#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> PluginManager::m_instance = NULL;
PluginManager::PluginManager() {
m_mutex = new Mutex();
}
PluginManager::~PluginManager() {
/* Release the memory used by plugin modules */
for (std::map<std::string, Plugin *>::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<std::string> PluginManager::getLoadedPlugins() const {
std::vector<std::string> list;
LockGuard lock(m_mutex);
for (std::map<std::string, Plugin *>::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<std::string> 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