37 #include <QtCore/QMetaProperty>
38 #include <QtCore/QDir>
39 #include <QtCore/QTextStream>
40 #include <QtCore/QWriteLocker>
42 #include <QMetaMethod>
159 using namespace ExtensionSystem;
160 using namespace ExtensionSystem::Internal;
182 PluginManager::PluginManager()
192 PluginManager::~PluginManager()
210 void PluginManager::addObject(QObject *obj)
220 void PluginManager::removeObject(QObject *obj)
246 void PluginManager::loadPlugins()
257 QStringList PluginManager::pluginPaths()
const
271 void PluginManager::setPluginPaths(
const QStringList &paths)
283 QString PluginManager::fileExtension()
const
295 void PluginManager::setFileExtension(
const QString &extension)
305 QStringList PluginManager::arguments()
const
331 QStringList PluginManager::parseOptions(QStringList pluginOptions, QStringList pluginTests, QStringList pluginNoLoad)
334 return options.parse(pluginOptions, pluginTests, pluginNoLoad);
337 static inline void indent(QTextStream &str,
int indent)
339 const QChar blank = QLatin1Char(
' ');
340 for (
int i = 0 ;
i < indent;
i++)
344 static inline void formatOption(QTextStream &str,
345 const QString &opt,
const QString &parm,
const QString &description,
346 int optionIndentation,
int descriptionIndentation)
348 int remainingIndent = descriptionIndentation - optionIndentation - opt.size();
349 indent(str, optionIndentation);
351 if (!parm.isEmpty()) {
352 str <<
" <" << parm << '>
';
353 remainingIndent -= 3 + parm.size();
355 indent(str, qMax(0, remainingIndent));
356 str << description << '\n';
365 void PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const
367 typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions;
368 // Check plugins for options
369 const PluginSpecSet::const_iterator pcend = d->pluginSpecs.constEnd();
370 for (PluginSpecSet::const_iterator pit = d->pluginSpecs.constBegin(); pit != pcend; ++pit) {
371 const PluginArgumentDescriptions pargs = (*pit)->argumentDescriptions();
372 if (!pargs.empty()) {
373 str << "\nPlugin: " << (*pit)->name() << '\n';
374 const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd();
375 for (PluginArgumentDescriptions::const_iterator ait =pargs.constBegin(); ait != acend; ++ait)
376 formatOption(str, ait->name, ait->parameter, ait->description, optionIndentation, descriptionIndentation);
387 void PluginManager::formatPluginVersions(QTextStream &str) const
389 const PluginSpecSet::const_iterator cend = d->pluginSpecs.constEnd();
390 for (PluginSpecSet::const_iterator it = d->pluginSpecs.constBegin(); it != cend; ++it) {
391 const PluginSpec *ps = *it;
392 str << " " << ps->name() << ' ' << ps->version() << ' ' << ps->description() << '\n';
396 void PluginManager::startTests()
401 foreach (PluginSpec *pluginSpec, d->testSpecs) {
402 const QMetaObject *mo = pluginSpec->plugin()->metaObject();
404 methods.append("arg0");
405 // We only want slots starting with "test"
406 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
407 if (QByteArray(mo->method(i).methodSignature()).startsWith("test")) {
408 QString method = QString::fromLatin1(mo->method(i).methodSignature());
409 methods.append(method.left(method.size()-2));
412 if (methods.length() > 1 && QTest::qExec(pluginSpec->plugin(), methods) != 0) {
414 qWarning() << "Tests failed for plugin:" << pluginSpec->name();
419 QCoreApplication::exit(1);
420 else if (runningTests())
421 QCoreApplication::exit(0);
429 bool PluginManager::runningTests() const
431 return !d->testSpecs.isEmpty();
438 QString PluginManager::testDataDirectory() const
440 QString s = GCS_TEST_DIR;
442 s = QDir::cleanPath(s);
446 //============PluginManagerPrivate===========
452 PluginSpec *PluginManagerPrivate::createSpec()
454 return new PluginSpec();
461 PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
470 PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager)
471 : extension("xml"), q(pluginManager)
479 PluginManagerPrivate::~PluginManagerPrivate()
482 qDeleteAll(pluginSpecs);
483 if (!allObjects.isEmpty()) {
484 qDebug() << "There are" << allObjects.size() << "objects left in the plugin manager pool: " << allObjects;
488 void PluginManagerPrivate::stopAll()
490 QList<PluginSpec *> queue = loadQueue();
491 foreach (PluginSpec *spec, queue) {
492 loadPlugin(spec, PluginSpec::Stopped);
494 QListIterator<PluginSpec *> it(queue);
496 while (it.hasPrevious()) {
497 loadPlugin(it.previous(), PluginSpec::Deleted);
505 void PluginManagerPrivate::addObject(QObject *obj)
508 QWriteLocker lock(&(q->m_lock));
509 if (obj == nullptr) {
510 qWarning() << "PluginManagerPrivate::addObject(): trying to add null object";
513 if (allObjects.contains(obj)) {
514 qWarning() << "PluginManagerPrivate::addObject(): trying to add duplicate object";
519 qDebug() << "PluginManagerPrivate::addObject" << obj << obj->objectName();
521 allObjects.append(obj);
523 emit q->objectAdded(obj);
530 void PluginManagerPrivate::removeObject(QObject *obj)
532 if (obj == nullptr) {
533 qWarning() << "PluginManagerPrivate::removeObject(): trying to remove null object";
537 if (!allObjects.contains(obj)) {
538 qWarning() << "PluginManagerPrivate::removeObject(): object not in list:"
539 << obj << obj->objectName();
543 qDebug() << "PluginManagerPrivate::removeObject" << obj << obj->objectName();
545 emit q->aboutToRemoveObject(obj);
546 QWriteLocker lock(&(q->m_lock));
547 allObjects.removeAll(obj);
554 void PluginManagerPrivate::loadPlugins()
556 QList<PluginSpec *> queue = loadQueue();
557 foreach (PluginSpec *spec, queue) {
558 emit q->splashMessages(QString(QObject::tr("Loading %1 plugin")).arg(spec->name()));
559 loadPlugin(spec, PluginSpec::Loaded);
560 if(spec->name() == "Core") {
561 QObject::connect(spec->plugin(),SIGNAL(splashMessages(QString)), q, SIGNAL(splashMessages(QString)));
562 QObject::connect(spec->plugin(),SIGNAL(showSplash()), q, SIGNAL(showSplash()));
563 QObject::connect(spec->plugin(),SIGNAL(hideSplash()), q, SIGNAL(hideSplash()));
567 foreach (PluginSpec *spec, queue) {
568 emit q->splashMessages(QString(QObject::tr("Initializing %1 plugin")).arg(spec->name()));
569 loadPlugin(spec, PluginSpec::Initialized);
571 QListIterator<PluginSpec *> it(queue);
573 while (it.hasPrevious()) {
574 loadPlugin(it.previous(), PluginSpec::Running);
576 emit q->pluginsChanged();
577 q->m_allPluginsLoaded=true;
578 emit q->pluginsLoadEnded();
585 QList<PluginSpec *> PluginManagerPrivate::loadQueue()
587 QList<PluginSpec *> queue;
588 foreach (PluginSpec *spec, pluginSpecs) {
589 QList<PluginSpec *> circularityCheckQueue;
590 loadQueue(spec, queue, circularityCheckQueue);
599 bool PluginManagerPrivate::loadQueue(PluginSpec *spec, QList<PluginSpec *> &queue,
600 QList<PluginSpec *> &circularityCheckQueue)
602 if (queue.contains(spec))
604 // check for circular dependencies
605 if (circularityCheckQueue.contains(spec)) {
606 spec->d->hasError = true;
607 spec->d->errorString = PluginManager::tr("Circular dependency detected:\n");
608 int index = circularityCheckQueue.indexOf(spec);
609 for (int i = index; i < circularityCheckQueue.size(); ++i) {
610 spec->d->errorString.append(PluginManager::tr("%1(%2) depends on\n")
611 .arg(circularityCheckQueue.at(i)->name()).arg(circularityCheckQueue.at(i)->version()));
613 spec->d->errorString.append(PluginManager::tr("%1(%2)").arg(spec->name()).arg(spec->version()));
616 circularityCheckQueue.append(spec);
617 // check if we have the dependencies
618 if (spec->state() == PluginSpec::Invalid || spec->state() == PluginSpec::Read) {
619 spec->d->hasError = true;
620 spec->d->errorString += "\n";
621 spec->d->errorString += PluginManager::tr("Cannot load plugin because dependencies are not resolved");
625 foreach (PluginSpec *depSpec, spec->dependencySpecs()) {
626 if (!loadQueue(depSpec, queue, circularityCheckQueue)) {
627 spec->d->hasError = true;
628 spec->d->errorString =
629 PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
630 .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
643 void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState)
645 if (spec->hasError())
647 if (destState == PluginSpec::Running) {
648 spec->d->initializeExtensions();
650 } else if (destState == PluginSpec::Deleted) {
654 foreach (PluginSpec *depSpec, spec->dependencySpecs()) {
655 if (depSpec->state() != destState) {
656 spec->d->hasError = true;
657 spec->d->errorString =
658 PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
659 .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
663 if (destState == PluginSpec::Loaded)
664 spec->d->loadLibrary();
665 else if (destState == PluginSpec::Initialized)
666 spec->d->initializePlugin();
667 else if (destState == PluginSpec::Stopped)
675 void PluginManagerPrivate::setPluginPaths(const QStringList &paths)
685 void PluginManagerPrivate::readPluginPaths()
687 qDeleteAll(pluginSpecs);
690 QStringList specFiles;
691 QStringList searchPaths = pluginPaths;
692 while (!searchPaths.isEmpty()) {
693 const QDir dir(searchPaths.takeFirst());
694 const QFileInfoList files = dir.entryInfoList(QStringList() << QString("*.%1").arg(extension), QDir::Files);
695 foreach (const QFileInfo &file, files)
696 specFiles << file.absoluteFilePath();
697 const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
698 foreach (const QFileInfo &subdir, dirs)
699 searchPaths << subdir.absoluteFilePath();
701 foreach (const QString &specFile, specFiles) {
702 PluginSpec *spec = new PluginSpec;
703 spec->d->read(specFile);
704 pluginSpecs.append(spec);
706 resolveDependencies();
707 // ensure deterministic plugin load order by sorting
708 std::sort(pluginSpecs.begin(), pluginSpecs.end(), lessThanByPluginName);
709 emit q->pluginsChanged();
712 void PluginManagerPrivate::resolveDependencies()
714 foreach (PluginSpec *spec, pluginSpecs) {
715 spec->d->resolveDependencies(pluginSpecs);
719 // Look in argument descriptions of the specs for the option.
720 PluginSpec *PluginManagerPrivate::pluginForOption(const QString &option, bool *requiresArgument) const
722 // Look in the plugins for an option
723 typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions;
725 *requiresArgument = false;
726 const PluginSpecSet::const_iterator pcend = pluginSpecs.constEnd();
727 for (PluginSpecSet::const_iterator pit = pluginSpecs.constBegin(); pit != pcend; ++pit) {
728 PluginSpec *ps = *pit;
729 const PluginArgumentDescriptions pargs = ps->argumentDescriptions();
730 if (!pargs.empty()) {
731 const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd();
732 for (PluginArgumentDescriptions::const_iterator ait = pargs.constBegin(); ait != acend; ++ait) {
733 if (ait->name == option) {
734 *requiresArgument = !ait->parameter.isEmpty();
743 PluginSpec *PluginManagerPrivate::pluginByName(const QString &name) const
745 foreach (PluginSpec *spec, pluginSpecs)
746 if (spec->name() == name)
void addObject(QObject *obj)
void setPluginPaths(const QStringList &paths)
Core plugin system that manages the plugins, their life cycle and their registered objects...
QList< ExtensionSystem::PluginSpec * > PluginSpecSet
Contains the information of the plugins xml description file and information about the plugin's curre...
void removeObject(QObject *obj)
QList< QObject * > allObjects
QList< PluginSpec * > pluginSpecs