Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / studio / src / extension_system / plugin_spec.cpp
blob53250cebeb68c2b5b4522d0b19a21bcdebca10aa
1 // Object Viewer Qt - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2011 Dzmitry KAMIAHIN (dnk-88) <dnk-88@tut.by>
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Winch Gate Property Limited
6 // Copyright (C) 2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 // Project includes
22 #include "plugin_spec.h"
23 #include "iplugin.h"
24 #include "iplugin_manager.h"
26 #include "nel/misc/app_context.h"
27 #include "nel/misc/debug.h"
29 // Qt includes
30 #include <QtCore/QList>
31 #include <QtCore/QFile>
32 #include <QtCore/QFileInfo>
33 #include <QtCore/QPluginLoader>
34 #include <QtCore/QCoreApplication>
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
40 #ifdef HAVE_OVQT_CONFIG_H
41 #include "ovqt_config.h"
42 #endif
44 namespace ExtensionSystem
46 const char *const PLUGIN_SPEC_NAME = "name";
47 const char *const PLUGIN_SPEC_VENDOR = "vendor";
48 const char *const PLUGIN_SPEC_VERSION = "version";
49 const char *const PLUGIN_SPEC_LIBRARY_NAME = "library-name";
50 const char *const PLUGIN_SPEC_DESCRIPTION = "description";
51 const char *const PLUGIN_SPEC_DEPENDENCIES = "dependencies";
52 const char *const PLUGIN_SPEC_DEPENDENCY = "dependency";
53 const char *const PLUGIN_SPEC_DEPENDENCY_NAME = "plugin-name";
54 const char *const PLUGIN_SPEC_DEPENDENCY_VERSION = "version";
56 PluginSpec::PluginSpec()
57 : m_location(""),
58 m_filePath(""),
59 m_fileName(""),
60 m_name(""),
61 m_version(""),
62 m_vendor(""),
63 m_description(""),
64 m_nameSpecFile(""),
65 m_suffix(""),
66 m_state(State::Invalid),
67 m_enabled(true),
68 m_enabledStartup(true),
69 m_hasError(false),
70 m_errorString(""),
71 m_plugin(0),
72 m_pluginManager(0)
74 // Compilation mode specific suffixes
75 #ifdef NL_OS_WINDOWS
76 # if defined(NL_DEBUG)
77 m_suffix = "_d.dll";
78 # elif defined(NL_RELEASE)
79 m_suffix = "_r.dll";
80 # else
81 # error "Unknown compilation mode, can't build suffix"
82 # endif
83 #elif defined (NL_OS_UNIX)
84 m_prefix = "lib";
85 m_suffix = ".so";
86 #else
87 # error "You must define the lib suffix for your platform"
88 #endif
89 loader = NULL;
92 PluginSpec::~PluginSpec()
94 delete loader;
95 loader = NULL;
98 QString PluginSpec::name() const
100 return m_name;
103 QString PluginSpec::version() const
105 return m_version;
108 QString PluginSpec::vendor() const
110 return m_vendor;
113 QString PluginSpec::description() const
115 return m_description;
118 QString PluginSpec::location() const
120 return m_location;
123 QString PluginSpec::filePath() const
125 return m_filePath;
128 QString PluginSpec::fileName() const
130 return m_fileName;
133 IPlugin *PluginSpec::plugin() const
135 return m_plugin;
138 int PluginSpec::state() const
140 return m_state;
143 bool PluginSpec::hasError() const
145 return m_hasError;
148 QString PluginSpec::errorString() const
150 return m_errorString;
153 QList<PluginSpec *> PluginSpec::dependencySpecs() const
155 return m_dependencySpecs;
158 bool PluginSpec::setFileName(const QString &fileName)
160 m_fileName = m_prefix + fileName + m_suffix;
161 m_filePath = m_location + "/" + m_fileName;
163 QFile file;
164 file.setFileName(m_filePath);
166 bool exists = file.exists();
168 #ifdef NL_OS_UNIX
170 #ifdef PLUGINS_DIR
171 if (!exists)
173 // if plugin can't be found in the same directory as spec file,
174 // looks for it in PLUGINS_DIR
175 m_filePath = QString("%1/%2").arg(PLUGINS_DIR).arg(m_fileName);
177 file.setFileName(m_filePath);
179 exists = file.exists();
181 #endif
183 #ifdef NL_LIB_PREFIX
184 if (!exists)
186 // if plugin can't be found in the same directory as spec file or PLUGINS_DIR,
187 // looks for it in NL_LIB_PREFIX
188 m_filePath = QString("%1/%2").arg(NL_LIB_PREFIX).arg(m_fileName);
190 file.setFileName(m_filePath);
192 exists = file.exists();
194 #endif
196 #endif
198 nlinfo(m_filePath.toUtf8().constData());
200 if (!exists)
201 return reportError(QCoreApplication::translate("PluginSpec", "File does not exist: %1").arg(file.fileName()));
202 if (!file.open(QIODevice::ReadOnly))
203 return reportError(QCoreApplication::translate("PluginSpec", "Could not open file for read: %1").arg(file.fileName()));
204 return true;
207 bool PluginSpec::setSpecFileName(const QString &specFileName)
209 m_nameSpecFile = specFileName;
211 QFile file(specFileName);
212 if (!file.exists())
213 return reportError(QCoreApplication::translate("PluginSpec", "Spec file does not exist: %1").arg(file.fileName()));
215 QFileInfo fileInfo(file);
216 m_location = fileInfo.absolutePath();
217 readSpec();
218 return true;
221 bool PluginSpec::readSpec()
223 QFile file(m_nameSpecFile);
224 if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
225 return reportError(QCoreApplication::translate("PluginSpec", "Could not open spec file for read: %1").arg(file.fileName()));
227 QXmlStreamReader reader(&file);
228 while (!reader.atEnd())
230 if (reader.isStartElement())
231 parseSpec(reader);
232 reader.readNext();
234 if (reader.hasError())
235 return reportError(QCoreApplication::translate("PluginSpec", "Error parsing file %1: %2, at line %3, column %4")
236 .arg(file.fileName())
237 .arg(reader.errorString())
238 .arg(reader.lineNumber())
239 .arg(reader.columnNumber()));
240 m_state = State::Read;
241 return true;
244 void PluginSpec::parseSpec(QXmlStreamReader &reader)
246 QString elemName = reader.name().toString();
247 reader.readNext();
248 if (reader.isCharacters())
250 QString elemText = reader.text().toString();
251 if (elemName == PLUGIN_SPEC_LIBRARY_NAME)
252 setFileName(elemText);
253 if (elemName == PLUGIN_SPEC_NAME)
254 m_name = elemText;
255 if (elemName == PLUGIN_SPEC_VERSION)
256 m_version = elemText;
257 if (elemName == PLUGIN_SPEC_VENDOR)
258 m_vendor = elemText;
259 if (elemName == PLUGIN_SPEC_DESCRIPTION)
260 m_description = elemText;
261 if (elemName == PLUGIN_SPEC_DEPENDENCIES)
262 parseDependency(reader);
266 void PluginSpec::parseDependency(QXmlStreamReader &reader)
268 QString elemName;
269 while (!reader.atEnd() && (elemName != PLUGIN_SPEC_DEPENDENCIES))
271 reader.readNext();
272 elemName = reader.name().toString();
273 if (reader.isStartElement() && (elemName == PLUGIN_SPEC_DEPENDENCY))
275 // Read name dependency plugin
276 QString dependencyName = reader.attributes().value(PLUGIN_SPEC_DEPENDENCY_NAME).toString();
277 if (dependencyName.isEmpty())
279 reader.raiseError(QCoreApplication::translate("CPluginSpec", "'%1' misses attribute '%2'")
280 .arg(PLUGIN_SPEC_DEPENDENCY)
281 .arg(PLUGIN_SPEC_DEPENDENCY_NAME));
282 return;
284 // TODO: Read version dependency plugin
285 QString dependencyVersion = reader.attributes().value(PLUGIN_SPEC_DEPENDENCY_VERSION).toString();
287 m_dependencies.push_back(dependencyName);
292 void PluginSpec::setEnabled(bool enabled)
294 m_enabled = enabled;
297 bool PluginSpec::isEnabled() const
299 return m_enabled;
302 bool PluginSpec::loadLibrary()
304 nlassert( loader == NULL );
306 if (m_hasError)
307 return false;
308 if (m_state != State::Resolved)
310 if (m_state == State::Loaded)
311 return true;
312 return reportError(QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved"));
315 loader = new QPluginLoader( m_filePath );
316 if (!loader->load())
317 return reportError(loader->errorString());
319 IPlugin *pluginObject = qobject_cast<IPlugin *>(loader->instance());
320 if (!pluginObject)
322 loader->unload();
323 delete loader;
324 loader = NULL;
325 return reportError(QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)"));
328 pluginObject->setNelContext(&NLMISC::INelContext::getInstance());
330 m_state = State::Loaded;
331 m_plugin = pluginObject;
332 return true;
335 bool PluginSpec::resolveDependencies(const QList<PluginSpec *> &specs)
337 if (m_hasError)
338 return false;
339 if (m_state != State::Read)
341 m_errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read");
342 m_hasError = true;
343 return false;
345 QList<PluginSpec *> resolvedDependencies;
346 Q_FOREACH(const QString &dependency, m_dependencies)
348 PluginSpec *found = 0;
350 Q_FOREACH(PluginSpec *spec, specs)
352 if (QString::compare(dependency, spec->name(), Qt::CaseInsensitive) == 0)
354 found = spec;
355 break;
358 if (!found)
360 m_hasError = true;
361 if (!m_errorString.isEmpty())
362 m_errorString.append(QLatin1Char('\n'));
363 m_errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1'")
364 .arg(dependency));
365 continue;
367 resolvedDependencies.append(found);
369 if (m_hasError)
370 return false;
372 m_dependencySpecs = resolvedDependencies;
373 m_state = State::Resolved;
374 return true;
377 bool PluginSpec::initializePlugin()
379 if (m_hasError)
380 return false;
381 if (m_state != State::Loaded)
383 if (m_state == State::Initialized)
384 return true;
385 return reportError(QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded)"));
387 if (!m_plugin)
388 return reportError(QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize"));
390 QString err;
391 if (!m_plugin->initialize(m_pluginManager, &err))
392 return reportError(QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err));
394 m_state = State::Initialized;
395 return true;
398 bool PluginSpec::initializeExtensions()
400 if (m_hasError)
401 return false;
402 if (m_state != State::Initialized)
404 if (m_state == State::Running)
405 return true;
406 return reportError(QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized"));
408 if (!m_plugin)
409 return reportError(QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized"));
411 m_plugin->extensionsInitialized();
412 m_state = State::Running;
413 return true;
416 void PluginSpec::stop()
418 if (!m_plugin)
419 return;
420 m_plugin->shutdown();
421 m_state = State::Stopped;
424 void PluginSpec::kill()
426 if (!m_plugin)
427 return;
429 bool b = loader->unload();
430 if( !b )
432 nlinfo( "Plugin %s couldn't be unloaded.", this->m_name.toAscii().data() );
435 //delete m_plugin;
436 m_plugin = NULL;
437 delete loader;
438 loader = NULL;
439 m_state = State::Deleted;
442 void PluginSpec::setEnabledStartup(bool enabled)
444 m_enabledStartup = enabled;
447 bool PluginSpec::isEnabledStartup() const
449 return m_enabledStartup;
452 bool PluginSpec::reportError(const QString &err)
454 m_errorString = err;
455 m_hasError = true;
456 return false;
459 } // namespace ExtensionSystem