pluginmanager.cpp 45.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30 31 32 33 34 35
#include "pluginmanager.h"
#include "pluginmanager_p.h"
#include "pluginspec.h"
#include "pluginspec_p.h"
#include "optionsparser.h"
#include "iplugin.h"
36
#include "plugincollection.h"
con's avatar
con committed
37

38 39 40
#include <QEventLoop>
#include <QDateTime>
#include <QDir>
41
#include <QFile>
42 43 44 45 46
#include <QMetaProperty>
#include <QSettings>
#include <QTextStream>
#include <QTime>
#include <QWriteLocker>
hjk's avatar
hjk committed
47
#include <QDebug>
48
#include <QTimer>
49
#include <QSysInfo>
50

con's avatar
con committed
51 52 53 54
#ifdef WITH_TESTS
#include <QTest>
#endif

55 56
static const char C_IGNORED_PLUGINS[] = "Plugins/Ignored";
static const char C_FORCEENABLED_PLUGINS[] = "Plugins/ForceEnabled";
57
static const int DELAYED_INITIALIZE_INTERVAL = 20; // ms
58

59
typedef QList<ExtensionSystem::PluginSpec *> PluginSpecSet;
con's avatar
con committed
60 61 62 63 64

enum { debugLeaks = 0 };

/*!
    \namespace ExtensionSystem
65 66
    \brief The ExtensionSystem namespace provides classes that belong to the
           core plugin system.
con's avatar
con committed
67

68
    The basic extension system contains the plugin manager and its supporting classes,
con's avatar
con committed
69 70 71 72 73 74 75 76 77 78 79 80
    and the IPlugin interface that must be implemented by plugin providers.
*/

/*!
    \namespace ExtensionSystem::Internal
    \internal
*/

/*!
    \class ExtensionSystem::PluginManager
    \mainclass

81 82
    \brief The PluginManager class implements the core plugin system that
    manages the plugins, their life cycle, and their registered objects.
con's avatar
con committed
83 84 85

    The plugin manager is used for the following tasks:
    \list
86 87
    \li Manage plugins and their state
    \li Manipulate a 'common object pool'
con's avatar
con committed
88 89 90
    \endlist

    \section1 Plugins
91
    Plugins consist of an XML descriptor file, and of a library that contains a Qt plugin
con's avatar
con committed
92 93 94 95
    (declared via Q_EXPORT_PLUGIN) that must derive from the IPlugin class.
    The plugin manager is used to set a list of file system directories to search for
    plugins, retrieve information about the state of these plugins, and to load them.

96 97
    Usually, the application creates a PluginManager instance and initiates the
    loading.
con's avatar
con committed
98
    \code
99 100 101
        // 'plugins' and subdirs will be searched for plugins
        ExtensionSystem::PluginManager::setPluginPaths(QStringList() << "plugins");
        ExtensionSystem::PluginManager::loadPlugins(); // try to load all the plugins
con's avatar
con committed
102
    \endcode
103 104
    Additionally, it is possible to directly access the plugin specifications
    (the information in the descriptor file), the plugin instances (via PluginSpec),
con's avatar
con committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
    and their state.

    \section1 Object Pool
    Plugins (and everybody else) can add objects to a common 'pool' that is located in
    the plugin manager. Objects in the pool must derive from QObject, there are no other
    prerequisites. All objects of a specified type can be retrieved from the object pool
    via the getObjects() and getObject() methods. They are aware of Aggregation::Aggregate, i.e.
    these methods use the Aggregation::query methods instead of a qobject_cast to determine
    the matching objects.

    Whenever the state of the object pool changes a corresponding signal is emitted by the plugin manager.

    A common usecase for the object pool is that a plugin (or the application) provides
    an "extension point" for other plugins, which is a class / interface that can
    be implemented and added to the object pool. The plugin that provides the
    extension point looks for implementations of the class / interface in the object pool.
    \code
122
        // Plugin A provides a "MimeTypeHandler" extension point
con's avatar
con committed
123 124 125
        // in plugin B:
        MyMimeTypeHandler *handler = new MyMimeTypeHandler();
        ExtensionSystem::PluginManager::instance()->addObject(handler);
126
        // In plugin A:
con's avatar
con committed
127
        QList<MimeTypeHandler *> mimeHandlers =
128
            ExtensionSystem::PluginManager::getObjects<MimeTypeHandler>();
con's avatar
con committed
129 130
    \endcode

131 132 133 134 135 136 137 138 139

    The \c{ExtensionSystem::Invoker} class template provides "syntactic sugar"
    for using "soft" extension points that may or may not be provided by an
    object in the pool. This approach does neither require the "user" plugin being
    linked against the "provider" plugin nor a common shared
    header file. The exposed interface is implicitly given by the
    invokable methods of the "provider" object in the object pool.

    The \c{ExtensionSystem::invoke} function template encapsulates
Orgad Shaneh's avatar
Orgad Shaneh committed
140
    {ExtensionSystem::Invoker} construction for the common case where
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    the success of the call is not checked.

    \code
        // In the "provide" plugin A:
        namespace PluginA {
        class SomeProvider : public QObject
        {
            Q_OBJECT

        public:
            Q_INVOKABLE QString doit(const QString &msg, int n) {
            {
                qDebug() << "I AM DOING IT " << msg;
                return QString::number(n);
            }
        };
        } // namespace PluginA


        // In the "user" plugin B:
        int someFuntionUsingPluginA()
        {
            using namespace ExtensionSystem;

165
            QObject *target = PluginManager::getObjectByClassName("PluginA::SomeProvider");
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

            if (target) {
                // Some random argument.
                QString msg = "REALLY.";

                // Plain function call, no return value.
                invoke<void>(target, "doit", msg, 2);

                // Plain function with no return value.
                qDebug() << "Result: " << invoke<QString>(target, "doit", msg, 21);

                // Record success of function call with return value.
                Invoker<QString> in1(target, "doit", msg, 21);
                qDebug() << "Success: (expected)" << in1.wasSuccessful();

                // Try to invoke a non-existing function.
                Invoker<QString> in2(target, "doitWrong", msg, 22);
                qDebug() << "Success (not expected):" << in2.wasSuccessful();

            } else {

                // We have to cope with plugin A's absence.
            }
        };
    \endcode

192
    \note The type of the parameters passed to the \c{invoke()} calls
193 194 195 196 197
    is deduced from the parameters themselves and must match the type of
    the arguments of the called functions \e{exactly}. No conversion or even
    integer promotions are applicable, so to invoke a function with a \c{long}
    parameter explicitly use \c{long(43)} or such.

198
    \note The object pool manipulating functions are thread-safe.
con's avatar
con committed
199 200 201 202
*/

/*!
    \fn void PluginManager::objectAdded(QObject *obj)
203
    Signals that \a obj has been added to the object pool.
con's avatar
con committed
204 205 206 207
*/

/*!
    \fn void PluginManager::aboutToRemoveObject(QObject *obj)
208
    Signals that \a obj will be removed from the object pool.
con's avatar
con committed
209 210 211 212
*/

/*!
    \fn void PluginManager::pluginsChanged()
213
    Signals that the list of available plugins has changed.
con's avatar
con committed
214 215 216 217 218

    \sa plugins()
*/

/*!
219 220
    \fn T *PluginManager::getObject()

221 222 223 224
    Retrieves the object of a given type from the object pool.

    This method is aware of Aggregation::Aggregate. That is, it uses
    the \c Aggregation::query methods instead of \c qobject_cast to
con's avatar
con committed
225 226 227 228 229 230 231 232
    determine the type of an object.
    If there are more than one object of the given type in
    the object pool, this method will choose an arbitrary one of them.

    \sa addObject()
*/

/*!
233
    \fn QList<T *> PluginManager::getObjects()
234 235 236 237 238

    Retrieves all objects of a given type from the object pool.

    This method is aware of Aggregation::Aggregate. That is, it uses
    the \c Aggregation::query methods instead of \c qobject_cast to
con's avatar
con committed
239 240 241 242 243 244 245 246
    determine the type of an object.

    \sa addObject()
*/

using namespace ExtensionSystem;
using namespace ExtensionSystem::Internal;

hjk's avatar
hjk committed
247 248 249
static Internal::PluginManagerPrivate *d = 0;
static PluginManager *m_instance = 0;

250 251 252 253 254
static bool lessThanByPluginName(const PluginSpec *one, const PluginSpec *two)
{
    return one->name() < two->name();
}

con's avatar
con committed
255
/*!
256
    Gets the unique plugin manager instance.
con's avatar
con committed
257 258 259 260 261 262 263
*/
PluginManager *PluginManager::instance()
{
    return m_instance;
}

/*!
264
    Creates a plugin manager. Should be done only once per application.
con's avatar
con committed
265 266 267 268
*/
PluginManager::PluginManager()
{
    m_instance = this;
hjk's avatar
hjk committed
269
    d = new PluginManagerPrivate(this);
con's avatar
con committed
270 271 272 273 274 275 276 277 278 279 280 281
}

/*!
    \internal
*/
PluginManager::~PluginManager()
{
    delete d;
    d = 0;
}

/*!
282 283 284
    Adds the object \a obj to the object pool, so it can be retrieved
    again from the pool by type.

con's avatar
con committed
285 286 287 288 289 290 291 292 293 294 295
    The plugin manager does not do any memory management - added objects
    must be removed from the pool and deleted manually by whoever is responsible for the object.

    Emits the objectAdded() signal.

    \sa PluginManager::removeObject()
    \sa PluginManager::getObject()
    \sa PluginManager::getObjects()
*/
void PluginManager::addObject(QObject *obj)
{
hjk's avatar
hjk committed
296
    d->addObject(obj);
con's avatar
con committed
297 298 299 300 301 302 303 304
}

/*!
    Emits aboutToRemoveObject() and removes the object \a obj from the object pool.
    \sa PluginManager::addObject()
*/
void PluginManager::removeObject(QObject *obj)
{
hjk's avatar
hjk committed
305
    d->removeObject(obj);
con's avatar
con committed
306 307 308
}

/*!
309 310 311 312
    Retrieves the list of all objects in the pool, unfiltered.

    Usually, clients do not need to call this function.

con's avatar
con committed
313 314 315
    \sa PluginManager::getObject()
    \sa PluginManager::getObjects()
*/
316
QList<QObject *> PluginManager::allObjects()
con's avatar
con committed
317
{
hjk's avatar
hjk committed
318 319 320 321 322 323
    return d->allObjects;
}

QReadWriteLock *PluginManager::listLock()
{
    return &d->m_lock;
con's avatar
con committed
324 325 326 327 328 329 330 331 332 333 334 335
}

/*!
    Tries to load all the plugins that were previously found when
    setting the plugin search paths. The plugin specs of the plugins
    can be used to retrieve error and state information about individual plugins.

    \sa setPluginPaths()
    \sa plugins()
*/
void PluginManager::loadPlugins()
{
hjk's avatar
hjk committed
336
    return d->loadPlugins();
con's avatar
con committed
337 338
}

339 340 341 342
/*!
    Returns true if any plugin has errors even though it is enabled.
    Most useful to call after loadPlugins().
*/
343
bool PluginManager::hasError()
344 345 346
{
    foreach (PluginSpec *spec, plugins()) {
        // only show errors on startup if plugin is enabled.
347
        if (spec->hasError() && spec->isEnabledInSettings() && !spec->isDisabledIndirectly())
348 349 350 351 352
            return true;
    }
    return false;
}

353 354 355 356 357 358 359 360
/*!
    Shuts down and deletes all plugins.
*/
void PluginManager::shutdown()
{
    d->shutdown();
}

con's avatar
con committed
361 362 363 364 365
/*!
    The list of paths were the plugin manager searches for plugins.

    \sa setPluginPaths()
*/
366
QStringList PluginManager::pluginPaths()
con's avatar
con committed
367
{
hjk's avatar
hjk committed
368
    return d->pluginPaths;
con's avatar
con committed
369 370 371 372 373 374 375 376 377 378 379 380
}

/*!
    Sets the plugin search paths, i.e. the file system paths where the plugin manager
    looks for plugin descriptions. All given \a paths and their sub directory trees
    are searched for plugin xml description files.

    \sa pluginPaths()
    \sa loadPlugins()
*/
void PluginManager::setPluginPaths(const QStringList &paths)
{
hjk's avatar
hjk committed
381
    d->setPluginPaths(paths);
con's avatar
con committed
382 383 384 385 386 387 388 389
}

/*!
    The file extension of plugin description files.
    The default is "xml".

    \sa setFileExtension()
*/
390
QString PluginManager::fileExtension()
con's avatar
con committed
391
{
hjk's avatar
hjk committed
392
    return d->extension;
con's avatar
con committed
393 394 395 396 397 398 399 400 401 402
}

/*!
    Sets the file extension of plugin description files.
    The default is "xml".
    At the moment this must be called before setPluginPaths() is called.
    // ### TODO let this + setPluginPaths read the plugin specs lazyly whenever loadPlugins() or plugins() is called.
*/
void PluginManager::setFileExtension(const QString &extension)
{
hjk's avatar
hjk committed
403
    d->extension = extension;
con's avatar
con committed
404 405
}

406
/*!
407 408
    Defines the user specific settings to use for information about enabled and
    disabled plugins.
409 410
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
411
void PluginManager::setSettings(QSettings *settings)
412
{
hjk's avatar
hjk committed
413
    d->setSettings(settings);
414 415
}

416
/*!
417 418
    Defines the global (user-independent) settings to use for information about
    default disabled plugins.
419 420 421 422
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
void PluginManager::setGlobalSettings(QSettings *settings)
{
hjk's avatar
hjk committed
423
    d->setGlobalSettings(settings);
424 425 426
}

/*!
427 428
    Returns the user specific settings used for information about enabled and
    disabled plugins.
429
*/
430
QSettings *PluginManager::settings()
431
{
hjk's avatar
hjk committed
432
    return d->settings;
433 434
}

435 436 437
/*!
    Returns the global (user-independent) settings used for information about default disabled plugins.
*/
438
QSettings *PluginManager::globalSettings()
439
{
hjk's avatar
hjk committed
440
    return d->globalSettings;
441 442 443 444
}

void PluginManager::writeSettings()
{
hjk's avatar
hjk committed
445
    d->writeSettings();
446 447
}

con's avatar
con committed
448
/*!
449
    The arguments left over after parsing (that were neither startup nor plugin
con's avatar
con committed
450 451
    arguments). Typically, this will be the list of files to open.
*/
452
QStringList PluginManager::arguments()
con's avatar
con committed
453
{
hjk's avatar
hjk committed
454
    return d->arguments;
con's avatar
con committed
455 456 457 458 459 460 461 462 463 464 465
}

/*!
    List of all plugin specifications that have been found in the plugin search paths.
    This list is valid directly after the setPluginPaths() call.
    The plugin specifications contain the information from the plugins' xml description files
    and the current state of the plugins. If a plugin's library has been already successfully loaded,
    the plugin specification has a reference to the created plugin instance as well.

    \sa setPluginPaths()
*/
466
QList<PluginSpec *> PluginManager::plugins()
con's avatar
con committed
467
{
hjk's avatar
hjk committed
468
    return d->pluginSpecs;
con's avatar
con committed
469 470
}

471
QHash<QString, PluginCollection *> PluginManager::pluginCollections()
472
{
hjk's avatar
hjk committed
473
    return d->pluginCategories;
474 475
}

476
static const char argumentKeywordC[] = ":arguments";
477 478

/*!
479
    Serializes plugin options and arguments for sending in a single string
480 481 482 483 484 485
    via QtSingleApplication:
    ":myplugin|-option1|-option2|:arguments|argument1|argument2",
    as a list of lists started by a keyword with a colon. Arguments are last.

    \sa setPluginPaths()
*/
486
QString PluginManager::serializedArguments()
487 488 489 490 491 492 493 494 495 496 497 498 499
{
    const QChar separator = QLatin1Char('|');
    QString rc;
    foreach (const PluginSpec *ps, plugins()) {
        if (!ps->arguments().isEmpty()) {
            if (!rc.isEmpty())
                rc += separator;
            rc += QLatin1Char(':');
            rc += ps->name();
            rc += separator;
            rc +=  ps->arguments().join(QString(separator));
        }
    }
hjk's avatar
hjk committed
500
    if (!d->arguments.isEmpty()) {
501 502 503 504 505
        if (!rc.isEmpty())
            rc += separator;
        rc += QLatin1String(argumentKeywordC);
        // If the argument appears to be a file, make it absolute
        // when sending to another instance.
hjk's avatar
hjk committed
506
        foreach (const QString &argument, d->arguments) {
507 508
            rc += separator;
            const QFileInfo fi(argument);
509
            if (fi.exists() && fi.isRelative())
510
                rc += fi.absoluteFilePath();
511
            else
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
                rc += argument;
        }
    }
    return rc;
}

/* Extract a sublist from the serialized arguments
 * indicated by a keyword starting with a colon indicator:
 * ":a,i1,i2,:b:i3,i4" with ":a" -> "i1,i2"
 */
static QStringList subList(const QStringList &in, const QString &key)
{
    QStringList rc;
    // Find keyword and copy arguments until end or next keyword
    const QStringList::const_iterator inEnd = in.constEnd();
    QStringList::const_iterator it = qFind(in.constBegin(), inEnd, key);
    if (it != inEnd) {
        const QChar nextIndicator = QLatin1Char(':');
        for (++it; it != inEnd && !it->startsWith(nextIndicator); ++it)
            rc.append(*it);
    }
    return rc;
}

/*!
    Parses the options encoded by serializedArguments() const
    and passes them on to the respective plugins along with the arguments.
539 540 541

    \a socket is passed for disconnecting the peer when the operation is done (for example,
    document is closed) for supporting the -block flag.
542 543
*/

544
void PluginManager::remoteArguments(const QString &serializedArgument, QObject *socket)
545 546 547 548 549 550 551 552
{
    if (serializedArgument.isEmpty())
        return;
    QStringList serializedArguments = serializedArgument.split(QLatin1Char('|'));
    const QStringList arguments = subList(serializedArguments, QLatin1String(argumentKeywordC));
    foreach (const PluginSpec *ps, plugins()) {
        if (ps->state() == PluginSpec::Running) {
            const QStringList pluginOptions = subList(serializedArguments, QLatin1Char(':') + ps->name());
553 554 555 556 557
            QObject *socketParent = ps->plugin()->remoteCommand(pluginOptions, arguments);
            if (socketParent && socket) {
                socket->setParent(socketParent);
                socket = 0;
            }
558 559
        }
    }
560 561
    if (socket)
        delete socket;
562 563
}

con's avatar
con committed
564 565 566 567 568 569 570
/*!
    Takes the list of command line options in \a args and parses them.
    The plugin manager itself might process some options itself directly (-noload <plugin>), and
    adds options that are registered by plugins to their plugin specs.
    The caller (the application) may register itself for options via the \a appOptions list, containing pairs
    of "option string" and a bool that indicates if the option requires an argument.
    Application options always override any plugin's options.
571

con's avatar
con committed
572 573 574 575
    \a foundAppOptions is set to pairs of ("option string", "argument") for any application options that were found.
    The command line options that were not processed can be retrieved via the arguments() method.
    If an error occurred (like missing argument for an option that requires one), \a errorString contains
    a descriptive message of the error.
576

con's avatar
con committed
577 578 579 580 581 582 583
    Returns if there was an error.
 */
bool PluginManager::parseOptions(const QStringList &args,
    const QMap<QString, bool> &appOptions,
    QMap<QString, QString> *foundAppOptions,
    QString *errorString)
{
hjk's avatar
hjk committed
584
    OptionsParser options(args, appOptions, foundAppOptions, errorString, d);
con's avatar
con committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    return options.parse();
}



static inline void indent(QTextStream &str, int indent)
{
    const QChar blank = QLatin1Char(' ');
    for (int i = 0 ; i < indent; i++)
        str << blank;
}

static inline void formatOption(QTextStream &str,
                                const QString &opt, const QString &parm, const QString &description,
                                int optionIndentation, int descriptionIndentation)
{
    int remainingIndent = descriptionIndentation - optionIndentation - opt.size();
    indent(str, optionIndentation);
    str << opt;
    if (!parm.isEmpty()) {
        str << " <" << parm << '>';
        remainingIndent -= 3 + parm.size();
    }
608
    indent(str, qMax(1, remainingIndent));
con's avatar
con committed
609 610 611 612
    str << description << '\n';
}

/*!
613
    Formats the startup options of the plugin manager for command line help.
con's avatar
con committed
614 615 616 617
*/

void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
{
618 619 620
    formatOption(str, QLatin1String(OptionsParser::LOAD_OPTION),
                 QLatin1String("plugin"), QLatin1String("Load <plugin>"),
                 optionIndentation, descriptionIndentation);
con's avatar
con committed
621 622 623
    formatOption(str, QLatin1String(OptionsParser::NO_LOAD_OPTION),
                 QLatin1String("plugin"), QLatin1String("Do not load <plugin>"),
                 optionIndentation, descriptionIndentation);
624 625 626
    formatOption(str, QLatin1String(OptionsParser::PROFILE_OPTION),
                 QString(), QLatin1String("Profile plugin loading"),
                 optionIndentation, descriptionIndentation);
Eike Ziller's avatar
Eike Ziller committed
627
#ifdef WITH_TESTS
628
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION)
629
                 + QLatin1String(" <plugin>[,testfunction[:testdata]]..."), QString(),
630 631
                 QLatin1String("Run plugin's tests (by default a separate settings path is used)"),
                 optionIndentation, descriptionIndentation);
632 633
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION) + QLatin1String(" all"),
                 QString(), QLatin1String("Run tests from all plugins"),
Eike Ziller's avatar
Eike Ziller committed
634 635
                 optionIndentation, descriptionIndentation);
#endif
con's avatar
con committed
636 637 638
}

/*!
639
    Formats the plugin options of the plugin specs for command line help.
con's avatar
con committed
640 641
*/

642
void PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
con's avatar
con committed
643 644 645
{
    typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions;
    // Check plugins for options
hjk's avatar
hjk committed
646 647
    const PluginSpecSet::const_iterator pcend = d->pluginSpecs.constEnd();
    for (PluginSpecSet::const_iterator pit = d->pluginSpecs.constBegin(); pit != pcend; ++pit) {
con's avatar
con committed
648 649 650 651 652 653 654 655 656 657 658
        const PluginArgumentDescriptions pargs = (*pit)->argumentDescriptions();
        if (!pargs.empty()) {
            str << "\nPlugin: " <<  (*pit)->name() << '\n';
            const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd();
            for (PluginArgumentDescriptions::const_iterator ait =pargs.constBegin(); ait != acend; ++ait)
                formatOption(str, ait->name, ait->parameter, ait->description, optionIndentation, descriptionIndentation);
        }
    }
}

/*!
659
    Formats the version of the plugin specs for command line help.
con's avatar
con committed
660
*/
661
void PluginManager::formatPluginVersions(QTextStream &str)
con's avatar
con committed
662
{
hjk's avatar
hjk committed
663 664
    const PluginSpecSet::const_iterator cend = d->pluginSpecs.constEnd();
    for (PluginSpecSet::const_iterator it = d->pluginSpecs.constBegin(); it != cend; ++it) {
con's avatar
con committed
665 666 667 668 669 670 671 672
        const PluginSpec *ps = *it;
        str << "  " << ps->name() << ' ' << ps->version() << ' ' << ps->description() <<  '\n';
    }
}

void PluginManager::startTests()
{
#ifdef WITH_TESTS
673 674
    foreach (const PluginManagerPrivate::TestSpec &testSpec, d->testSpecs) {
        const PluginSpec * const pluginSpec = testSpec.pluginSpec;
675 676
        if (!pluginSpec->plugin())
            continue;
677 678 679

        // Collect all test functions/methods of the plugin.
        QStringList allTestFunctions;
Nikolai Kosjar's avatar
Nikolai Kosjar committed
680
        const QMetaObject *metaObject = pluginSpec->plugin()->metaObject();
681

Nikolai Kosjar's avatar
Nikolai Kosjar committed
682
        for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
Friedemann Kleint's avatar
Friedemann Kleint committed
683
#if QT_VERSION >= 0x050000
Nikolai Kosjar's avatar
Nikolai Kosjar committed
684
            const QByteArray signature = metaObject->method(i).methodSignature();
Friedemann Kleint's avatar
Friedemann Kleint committed
685
#else
Nikolai Kosjar's avatar
Nikolai Kosjar committed
686
            const QByteArray signature = metaObject->method(i).signature();
Friedemann Kleint's avatar
Friedemann Kleint committed
687 688 689
#endif
            if (signature.startsWith("test") && !signature.endsWith("_data()")) {
                const QString method = QString::fromLatin1(signature);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
690 691
                const QString methodName = method.left(method.size() - 2);
                allTestFunctions.append(methodName);
con's avatar
con committed
692 693
            }
        }
694 695 696 697 698 699 700 701 702 703 704 705

        QStringList testFunctionsToExecute;

        // User did not specify any test functions, so add every test function.
        if (testSpec.testFunctions.isEmpty()) {
            testFunctionsToExecute = allTestFunctions;

        // User specified test functions. Add them if they are valid.
        } else {
            foreach (const QString &userTestFunction, testSpec.testFunctions) {
                // There might be a test data suffix like in "testfunction:testdata1".
                QString testFunctionName = userTestFunction;
706
                QString testDataSuffix;
707
                const int index = testFunctionName.indexOf(QLatin1Char(':'));
708 709
                if (index != -1) {
                    testDataSuffix = testFunctionName.mid(index);
710
                    testFunctionName = testFunctionName.left(index);
711
                }
712

713 714 715 716 717 718 719
                const QRegExp regExp(testFunctionName, Qt::CaseSensitive, QRegExp::Wildcard);
                QStringList matchingFunctions;
                foreach (const QString &testFunction, allTestFunctions) {
                    if (regExp.exactMatch(testFunction))
                        matchingFunctions.append(testFunction);
                }
                if (!matchingFunctions.isEmpty()) {
720 721
                    // If the specified test data is invalid, the QTest framework will
                    // print a reasonable error message for us.
722 723
                    foreach (const QString &matchingFunction, matchingFunctions)
                        testFunctionsToExecute.append(matchingFunction + testDataSuffix);
724 725
                } else {
                    QTextStream out(stdout);
726
                    out << "No test function matches \"" << testFunctionName
727 728 729 730 731 732 733 734 735
                        << "\" for plugin \"" << pluginSpec->name() << "\"." << endl
                        << "  Available test functions for plugin \"" << pluginSpec->name()
                        << "\" are:" << endl;
                    foreach (const QString &testFunction, allTestFunctions)
                        out << "    " << testFunction << endl;
                }
            }
        }

736
        // Don't run QTest::qExec without any test functions, that'd run
737
        // *all* slots as tests.
738 739 740 741 742 743 744 745
        if (!testFunctionsToExecute.isEmpty()) {
            // QTest::qExec() expects basically QCoreApplication::arguments(),
            QStringList qExecArguments = QStringList()
                << QLatin1String("arg0") // fake application name
                << QLatin1String("-maxwarnings") << QLatin1String("0"); // unlimit output
            qExecArguments << testFunctionsToExecute;
            QTest::qExec(pluginSpec->plugin(), qExecArguments);
        }
con's avatar
con committed
746
    }
hjk's avatar
hjk committed
747
    if (!d->testSpecs.isEmpty())
748
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
con's avatar
con committed
749 750 751
#endif
}

752 753 754
/*!
 * \internal
 */
755
bool PluginManager::testRunRequested()
con's avatar
con committed
756
{
hjk's avatar
hjk committed
757
    return !d->testSpecs.isEmpty();
con's avatar
con committed
758 759
}

760 761 762
/*!
 * \internal
 */
763
QString PluginManager::testDataDirectory()
con's avatar
con committed
764
{
765
    QByteArray ba = qgetenv("QTCREATOR_TEST_DIR");
hjk's avatar
hjk committed
766
    QString s = QString::fromLocal8Bit(ba.constData(), ba.size());
con's avatar
con committed
767
    if (s.isEmpty()) {
768 769
        s = QLatin1String(IDE_TEST_DIR);
        s.append(QLatin1String("/tests"));
con's avatar
con committed
770 771 772 773 774
    }
    s = QDir::cleanPath(s);
    return s;
}

775
/*!
776 777
    Creates a profiling entry showing the elapsed time if profiling is
    activated.
778 779 780 781
*/

void PluginManager::profilingReport(const char *what, const PluginSpec *spec)
{
hjk's avatar
hjk committed
782
    d->profilingReport(what, spec);
783 784
}

785 786 787 788 789 790

/*!
    Returns a list of plugins in load order.
*/
QList<PluginSpec *> PluginManager::loadQueue()
{
hjk's avatar
hjk committed
791
    return d->loadQueue();
792 793
}

con's avatar
con committed
794 795 796 797 798 799 800 801 802 803
//============PluginManagerPrivate===========

/*!
    \internal
*/
PluginSpec *PluginManagerPrivate::createSpec()
{
    return new PluginSpec();
}

804 805 806 807 808 809 810 811 812 813 814 815
/*!
    \internal
*/
void PluginManagerPrivate::setSettings(QSettings *s)
{
    if (settings)
        delete settings;
    settings = s;
    if (settings)
        settings->setParent(this);
}

816 817 818 819 820 821 822 823 824 825 826 827
/*!
    \internal
*/
void PluginManagerPrivate::setGlobalSettings(QSettings *s)
{
    if (globalSettings)
        delete globalSettings;
    globalSettings = s;
    if (globalSettings)
        globalSettings->setParent(this);
}

con's avatar
con committed
828 829 830 831 832 833 834 835
/*!
    \internal
*/
PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
{
    return spec->d;
}

836 837 838 839 840 841 842 843 844 845 846 847 848
void PluginManagerPrivate::nextDelayedInitialize()
{
    while (!delayedInitializeQueue.isEmpty()) {
        PluginSpec *spec = delayedInitializeQueue.takeFirst();
        profilingReport(">delayedInitialize", spec);
        bool delay = spec->d->delayedInitialize();
        profilingReport("<delayedInitialize", spec);
        if (delay)
            break; // do next delayedInitialize after a delay
    }
    if (delayedInitializeQueue.isEmpty()) {
        delete delayedInitializeTimer;
        delayedInitializeTimer = 0;
849
        profilingSummary();
Tobias Hunger's avatar
Tobias Hunger committed
850
        emit q->initializationDone();
851 852 853 854
#ifdef WITH_TESTS
        if (q->testRunRequested())
            q->startTests();
#endif
855 856 857 858 859
    } else {
        delayedInitializeTimer->start();
    }
}

con's avatar
con committed
860 861 862
/*!
    \internal
*/
863 864
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
    extension(QLatin1String("xml")),
865 866
    delayedInitializeTimer(0),
    shutdownEventLoop(0),
867
    m_profileElapsedMS(0),
868
    m_profilingVerbosity(0),
869
    settings(0),
870
    globalSettings(0),
871
    q(pluginManager)
con's avatar
con committed
872 873 874
{
}

875

con's avatar
con committed
876 877 878 879 880 881
/*!
    \internal
*/
PluginManagerPrivate::~PluginManagerPrivate()
{
    qDeleteAll(pluginSpecs);
882
    qDeleteAll(pluginCategories);
con's avatar
con committed
883 884
}

885 886 887
/*!
    \internal
*/
888 889
void PluginManagerPrivate::writeSettings()
{
890 891
    if (!settings)
        return;
con's avatar
con committed
892
    QStringList tempDisabledPlugins;
893
    QStringList tempForceEnabledPlugins;
894
    foreach (PluginSpec *spec, pluginSpecs) {
895
        if (!spec->isDisabledByDefault() && !spec->isEnabledInSettings())
con's avatar
con committed
896
            tempDisabledPlugins.append(spec->name());
897
        if (spec->isDisabledByDefault() && spec->isEnabledInSettings())
898
            tempForceEnabledPlugins.append(spec->name());
899 900
    }

901 902
    settings->setValue(QLatin1String(C_IGNORED_PLUGINS), tempDisabledPlugins);
    settings->setValue(QLatin1String(C_FORCEENABLED_PLUGINS), tempForceEnabledPlugins);
903 904
}

905 906 907
/*!
    \internal
*/
908
void PluginManagerPrivate::readSettings()
909
{
910
    if (globalSettings)
911 912 913 914 915
        defaultDisabledPlugins = globalSettings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList();
    if (settings) {
        disabledPlugins = settings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList();
        forceEnabledPlugins = settings->value(QLatin1String(C_FORCEENABLED_PLUGINS)).toStringList();
    }
916 917
}

918 919 920
/*!
    \internal
*/
con's avatar
con committed
921 922
void PluginManagerPrivate::stopAll()
{
923 924 925 926 927
    if (delayedInitializeTimer && delayedInitializeTimer->isActive()) {
        delayedInitializeTimer->stop();
        delete delayedInitializeTimer;
        delayedInitializeTimer = 0;
    }
con's avatar
con committed
928 929 930 931
    QList<PluginSpec *> queue = loadQueue();
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Stopped);
    }
932 933 934 935 936 937 938 939
}

/*!
    \internal
*/
void PluginManagerPrivate::deleteAll()
{
    QList<PluginSpec *> queue = loadQueue();
con's avatar
con committed
940 941 942 943 944 945 946 947 948 949 950 951 952
    QListIterator<PluginSpec *> it(queue);
    it.toBack();
    while (it.hasPrevious()) {
        loadPlugin(it.previous(), PluginSpec::Deleted);
    }
}

/*!
    \internal
*/
void PluginManagerPrivate::addObject(QObject *obj)
{
    {
hjk's avatar
hjk committed
953
        QWriteLocker lock(&m_lock);
con's avatar
con committed
954 955 956 957 958 959 960 961 962 963 964 965
        if (obj == 0) {
            qWarning() << "PluginManagerPrivate::addObject(): trying to add null object";
            return;
        }
        if (allObjects.contains(obj)) {
            qWarning() << "PluginManagerPrivate::addObject(): trying to add duplicate object";
            return;
        }

        if (debugLeaks)
            qDebug() << "PluginManagerPrivate::addObject" << obj << obj->objectName();

966 967 968 969 970 971 972
        if (m_profilingVerbosity && !m_profileTimer.isNull()) {
            // Report a timestamp when adding an object. Useful for profiling
            // its initialization time.
            const int absoluteElapsedMS = m_profileTimer->elapsed();
            qDebug("  %-43s %8dms", obj->metaObject()->className(), absoluteElapsedMS);
        }

con's avatar
con committed
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996
        allObjects.append(obj);
    }
    emit q->objectAdded(obj);
}

/*!
    \internal
*/
void PluginManagerPrivate::removeObject(QObject *obj)
{
    if (obj == 0) {
        qWarning() << "PluginManagerPrivate::removeObject(): trying to remove null object";
        return;
    }

    if (!allObjects.contains(obj)) {
        qWarning() << "PluginManagerPrivate::removeObject(): object not in list:"
            << obj << obj->objectName();
        return;
    }
    if (debugLeaks)
        qDebug() << "PluginManagerPrivate::removeObject" << obj << obj->objectName();

    emit q->aboutToRemoveObject(obj);
hjk's avatar
hjk committed
997
    QWriteLocker lock(&m_lock);
con's avatar
con committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    allObjects.removeAll(obj);
}

/*!
    \internal
*/
void PluginManagerPrivate::loadPlugins()
{
    QList<PluginSpec *> queue = loadQueue();
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Loaded);
    }
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Initialized);
    }
    QListIterator<PluginSpec *> it(queue);
    it.toBack();
    while (it.hasPrevious()) {
1016 1017 1018 1019
        PluginSpec *spec = it.previous();
        loadPlugin(spec, PluginSpec::Running);
        if (spec->state() == PluginSpec::Running)
            delayedInitializeQueue.append(spec);
con's avatar
con committed
1020 1021
    }
    emit q->pluginsChanged();
1022 1023 1024 1025 1026 1027 1028

    delayedInitializeTimer = new QTimer;
    delayedInitializeTimer->setInterval(DELAYED_INITIALIZE_INTERVAL);
    delayedInitializeTimer->setSingleShot(true);
    connect(delayedInitializeTimer, SIGNAL(timeout()),
            this, SLOT(nextDelayedInitialize()));
    delayedInitializeTimer->start();
con's avatar
con committed
1029 1030
}

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
/*!
    \internal
*/
void PluginManagerPrivate::shutdown()
{
    stopAll();
    if (!asynchronousPlugins.isEmpty()) {
        shutdownEventLoop = new QEventLoop;
        shutdownEventLoop->exec();
    }
    deleteAll();
1042
    if (!allObjects.isEmpty())
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
        qDebug() << "There are" << allObjects.size() << "objects left in the plugin manager pool: " << allObjects;
}

/*!
    \internal
*/
void PluginManagerPrivate::asyncShutdownFinished()
{
    IPlugin *plugin = qobject_cast<IPlugin *>(sender());
    Q_ASSERT(plugin);
    asynchronousPlugins.removeAll(plugin->pluginSpec());
    if (asynchronousPlugins.isEmpty())
        shutdownEventLoop->exit();
}

con's avatar
con committed
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
/*!
    \internal
*/
QList<PluginSpec *> PluginManagerPrivate::loadQueue()
{
    QList<PluginSpec *> queue;
    foreach (PluginSpec *spec, pluginSpecs) {
        QList<PluginSpec *> circularityCheckQueue;
        loadQueue(spec, queue, circularityCheckQueue);
    }
    return queue;
}

/*!
    \internal
*/
bool PluginManagerPrivate::loadQueue(PluginSpec *spec, QList<PluginSpec *> &queue,
        QList<PluginSpec *> &circularityCheckQueue)
{
    if (queue.contains(spec))
        return true;
    // check for circular dependencies
    if (circularityCheckQueue.contains(spec)) {
        spec->d->hasError = true;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1082
        spec->d->errorString = PluginManager::tr("Circular dependency detected:\n");
con's avatar
con committed
1083 1084
        int index = circularityCheckQueue.indexOf(spec);
        for (int i = index; i < circularityCheckQueue.size(); ++i) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1085
            spec->d->errorString.append(PluginManager::tr("%1(%2) depends on\n")
con's avatar
con committed
1086 1087
                .arg(circularityCheckQueue.at(i)->name()).arg(circularityCheckQueue.at(i)->version()));
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1088
        spec->d->errorString.append(PluginManager::tr("%1(%2)").arg(spec->name()).arg(spec->version()));
con's avatar
con committed
1089 1090 1091 1092 1093
        return false;
    }
    circularityCheckQueue.append(spec);
    // check if we have the dependencies
    if (spec->state() == PluginSpec::Invalid || spec->state() == PluginSpec::Read) {
1094
        queue.append(spec);
con's avatar
con committed
1095 1096
        return false;
    }
1097

con's avatar
con committed
1098 1099 1100 1101 1102
    // add dependencies
    foreach (PluginSpec *depSpec, spec->dependencySpecs()) {
        if (!loadQueue(depSpec, queue, circularityCheckQueue)) {
            spec->d->hasError = true;
            spec->d->errorString =
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1103
                PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
con's avatar
con committed
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
                    .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
            return false;
        }
    }
    // add self
    queue.append(spec);
    return true;
}

/*!
    \internal
*/
void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState)
{
1118 1119 1120 1121
    if (spec->hasError() || spec->state() != destState-1)
        return;

    // don't load disabled plugins.
1122
    if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded)
con's avatar
con committed
1123
        return;
1124

1125 1126 1127
    switch (destState) {
    case PluginSpec::Running:
        profilingReport(">initializeExtensions", spec);
con's avatar
con committed
1128
        spec->d->initializeExtensions();
1129
        profilingReport("<initializeExtensions", spec);
con's avatar
con committed
1130
        return;
1131
    case PluginSpec::Deleted:
1132
        profilingReport(">delete", spec);
con's avatar
con committed
1133
        spec->d->kill();