pluginmanager.cpp 42.6 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 41 42 43 44 45
#include <QEventLoop>
#include <QDateTime>
#include <QDir>
#include <QMetaProperty>
#include <QSettings>
#include <QTextStream>
#include <QTime>
#include <QWriteLocker>
hjk's avatar
hjk committed
46
#include <QDebug>
47
#include <QTimer>
48

con's avatar
con committed
49 50 51 52
#ifdef WITH_TESTS
#include <QTest>
#endif

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

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

enum { debugLeaks = 0 };

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

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

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

/*!
    \class ExtensionSystem::PluginManager
    \mainclass

79 80
    \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
81 82 83

    The plugin manager is used for the following tasks:
    \list
84 85
    \li Manage plugins and their state
    \li Manipulate a 'common object pool'
con's avatar
con committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    \endlist

    \section1 Plugins
    Plugins consist of an xml descriptor file, and of a library that contains a Qt plugin
    (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.

    Usually the application creates a PluginManager instance and initiates the loading.
    \code
        ExtensionSystem::PluginManager *manager = new ExtensionSystem::PluginManager();
        manager->setPluginPaths(QStringList() << "plugins"); // 'plugins' and subdirs will be searched for plugins
        manager->loadPlugins(); // try to load all the plugins
    \endcode
    Additionally it is possible to directly access to the plugin specifications
    (the information in the descriptor file), and the plugin instances (via PluginSpec),
    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
119
        // Plugin A provides a "MimeTypeHandler" extension point
con's avatar
con committed
120 121 122
        // in plugin B:
        MyMimeTypeHandler *handler = new MyMimeTypeHandler();
        ExtensionSystem::PluginManager::instance()->addObject(handler);
123
        // In plugin A:
con's avatar
con committed
124 125 126 127
        QList<MimeTypeHandler *> mimeHandlers =
            ExtensionSystem::PluginManager::instance()->getObjects<MimeTypeHandler>();
    \endcode

128 129 130 131 132 133 134 135 136

    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
137
    {ExtensionSystem::Invoker} construction for the common case where
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    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;

            QObject *target = PluginManager::instance()
                ->getObjectByClassName("PluginA::SomeProvider");

            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

190
    \note The type of the parameters passed to the \c{invoke()} calls
191 192 193 194 195
    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.

196
    \note The object pool manipulating functions are thread-safe.
con's avatar
con committed
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
*/

/*!
    \fn void PluginManager::objectAdded(QObject *obj)
    Signal that \a obj has been added to the object pool.
*/

/*!
    \fn void PluginManager::aboutToRemoveObject(QObject *obj)
    Signal that \a obj will be removed from the object pool.
*/

/*!
    \fn void PluginManager::pluginsChanged()
    Signal that the list of available plugins has changed.

    \sa plugins()
*/

/*!
217 218
    \fn T *PluginManager::getObject()

con's avatar
con committed
219 220 221 222 223 224 225 226 227 228 229
    Retrieve the object of a given type from the object pool.
    This method is aware of Aggregation::Aggregate, i.e. it uses
    the Aggregation::query methods instead of qobject_cast to
    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()
*/

/*!
230
    \fn QList<T *> PluginManager::getObjects()
con's avatar
con committed
231 232 233 234 235 236 237 238 239 240 241
    Retrieve all objects of a given type from the object pool.
    This method is aware of Aggregation::Aggregate, i.e. it uses
    the Aggregation::query methods instead of qobject_cast to
    determine the type of an object.

    \sa addObject()
*/

using namespace ExtensionSystem;
using namespace ExtensionSystem::Internal;

242 243 244 245 246
static bool lessThanByPluginName(const PluginSpec *one, const PluginSpec *two)
{
    return one->name() < two->name();
}

con's avatar
con committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
PluginManager *PluginManager::m_instance = 0;

/*!
    Get the unique plugin manager instance.
*/
PluginManager *PluginManager::instance()
{
    return m_instance;
}

/*!
    Create a plugin manager. Should be done only once per application.
*/
PluginManager::PluginManager()
    : d(new PluginManagerPrivate(this))
{
    m_instance = this;
}

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

/*!
    Add the given object \a obj to the object pool, so it can be retrieved again from the pool by type.
    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)
{
288
    m_instance->d->addObject(obj);
con's avatar
con committed
289 290 291 292 293 294 295 296
}

/*!
    Emits aboutToRemoveObject() and removes the object \a obj from the object pool.
    \sa PluginManager::addObject()
*/
void PluginManager::removeObject(QObject *obj)
{
297
    m_instance->d->removeObject(obj);
con's avatar
con committed
298 299 300 301 302 303 304 305
}

/*!
    Retrieve the list of all objects in the pool, unfiltered.
    Usually clients do not need to call this.
    \sa PluginManager::getObject()
    \sa PluginManager::getObjects()
*/
306
QList<QObject *> PluginManager::allObjects()
con's avatar
con committed
307
{
308
    return m_instance->d->allObjects;
con's avatar
con committed
309 310 311 312 313 314 315 316 317 318 319 320
}

/*!
    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()
{
321
    return m_instance->d->loadPlugins();
con's avatar
con committed
322 323
}

324 325 326 327
/*!
    Returns true if any plugin has errors even though it is enabled.
    Most useful to call after loadPlugins().
*/
328
bool PluginManager::hasError()
329 330 331
{
    foreach (PluginSpec *spec, plugins()) {
        // only show errors on startup if plugin is enabled.
332
        if (spec->hasError() && spec->isEnabledInSettings() && !spec->isDisabledIndirectly())
333 334 335 336 337
            return true;
    }
    return false;
}

338 339 340 341 342 343 344 345
/*!
    Shuts down and deletes all plugins.
*/
void PluginManager::shutdown()
{
    d->shutdown();
}

con's avatar
con committed
346 347 348 349 350
/*!
    The list of paths were the plugin manager searches for plugins.

    \sa setPluginPaths()
*/
351
QStringList PluginManager::pluginPaths()
con's avatar
con committed
352
{
353
    return m_instance->d->pluginPaths;
con's avatar
con committed
354 355 356 357 358 359 360 361 362 363 364 365
}

/*!
    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)
{
366
    m_instance->d->setPluginPaths(paths);
con's avatar
con committed
367 368 369 370 371 372 373 374
}

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

    \sa setFileExtension()
*/
375
QString PluginManager::fileExtension()
con's avatar
con committed
376
{
377
    return m_instance->d->extension;
con's avatar
con committed
378 379 380 381 382 383 384 385 386 387
}

/*!
    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)
{
388
    m_instance->d->extension = extension;
con's avatar
con committed
389 390
}

391 392 393 394
/*!
    Define the user specific settings to use for information about enabled/disabled plugins.
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
395
void PluginManager::setSettings(QSettings *settings)
396
{
397
    m_instance->d->setSettings(settings);
398 399
}

400 401 402 403 404 405
/*!
    Define the global (user-independent) settings to use for information about default disabled plugins.
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
void PluginManager::setGlobalSettings(QSettings *settings)
{
406
    m_instance->d->setGlobalSettings(settings);
407 408 409 410 411
}

/*!
    Returns the user specific settings used for information about enabled/disabled plugins.
*/
412
QSettings *PluginManager::settings()
413
{
414
    return m_instance->d->settings;
415 416
}

417 418 419
/*!
    Returns the global (user-independent) settings used for information about default disabled plugins.
*/
420
QSettings *PluginManager::globalSettings()
421
{
422
    return m_instance->d->globalSettings;
423 424 425 426
}

void PluginManager::writeSettings()
{
427
    m_instance->d->writeSettings();
428 429
}

con's avatar
con committed
430 431 432 433
/*!
    The arguments left over after parsing (Neither startup nor plugin
    arguments). Typically, this will be the list of files to open.
*/
434
QStringList PluginManager::arguments()
con's avatar
con committed
435
{
436
    return m_instance->d->arguments;
con's avatar
con committed
437 438 439 440 441 442 443 444 445 446 447
}

/*!
    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()
*/
448
QList<PluginSpec *> PluginManager::plugins()
con's avatar
con committed
449
{
450
    return m_instance->d->pluginSpecs;
con's avatar
con committed
451 452
}

453
QHash<QString, PluginCollection *> PluginManager::pluginCollections()
454
{
455
    return m_instance->d->pluginCategories;
456 457
}

458
static const char argumentKeywordC[] = ":arguments";
459 460 461 462 463 464 465 466 467

/*!
    Serialize plugin options and arguments for sending in a single string
    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()
*/
468
QString PluginManager::serializedArguments()
469 470 471 472 473 474 475 476 477 478 479 480 481
{
    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));
        }
    }
482
    if (!m_instance->d->arguments.isEmpty()) {
483 484 485 486 487
        if (!rc.isEmpty())
            rc += separator;
        rc += QLatin1String(argumentKeywordC);
        // If the argument appears to be a file, make it absolute
        // when sending to another instance.
488
        foreach (const QString &argument, m_instance->d->arguments) {
489 490
            rc += separator;
            const QFileInfo fi(argument);
491
            if (fi.exists() && fi.isRelative())
492
                rc += fi.absoluteFilePath();
493
            else
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
                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.
521 522 523

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

526
void PluginManager::remoteArguments(const QString &serializedArgument, QObject *socket)
527 528 529 530 531 532 533 534
{
    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());
535 536 537 538 539
            QObject *socketParent = ps->plugin()->remoteCommand(pluginOptions, arguments);
            if (socketParent && socket) {
                socket->setParent(socketParent);
                socket = 0;
            }
540 541
        }
    }
542 543
    if (socket)
        delete socket;
544 545
}

con's avatar
con committed
546 547 548 549 550 551 552
/*!
    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.
553

con's avatar
con committed
554 555 556 557
    \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.
558

con's avatar
con committed
559 560 561 562 563 564 565
    Returns if there was an error.
 */
bool PluginManager::parseOptions(const QStringList &args,
    const QMap<QString, bool> &appOptions,
    QMap<QString, QString> *foundAppOptions,
    QString *errorString)
{
566
    OptionsParser options(args, appOptions, foundAppOptions, errorString, m_instance->d);
con's avatar
con committed
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
    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();
    }
590
    indent(str, qMax(1, remainingIndent));
con's avatar
con committed
591 592 593 594 595 596 597 598 599
    str << description << '\n';
}

/*!
    Format the startup options of the plugin manager for command line help.
*/

void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
{
600 601 602
    formatOption(str, QLatin1String(OptionsParser::LOAD_OPTION),
                 QLatin1String("plugin"), QLatin1String("Load <plugin>"),
                 optionIndentation, descriptionIndentation);
con's avatar
con committed
603 604 605
    formatOption(str, QLatin1String(OptionsParser::NO_LOAD_OPTION),
                 QLatin1String("plugin"), QLatin1String("Do not load <plugin>"),
                 optionIndentation, descriptionIndentation);
606 607 608
    formatOption(str, QLatin1String(OptionsParser::PROFILE_OPTION),
                 QString(), QLatin1String("Profile plugin loading"),
                 optionIndentation, descriptionIndentation);
Eike Ziller's avatar
Eike Ziller committed
609
#ifdef WITH_TESTS
610
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION)
611
                 + QLatin1String(" <plugin>[,testfunction[:testdata]]..."), QString(),
612 613 614
                 QLatin1String("Run plugin's tests"), optionIndentation, descriptionIndentation);
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION) + QLatin1String(" all"),
                 QString(), QLatin1String("Run tests from all plugins"),
Eike Ziller's avatar
Eike Ziller committed
615 616
                 optionIndentation, descriptionIndentation);
#endif
con's avatar
con committed
617 618 619 620 621 622
}

/*!
    Format the plugin  options of the plugin specs for command line help.
*/

623
void PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
con's avatar
con committed
624 625 626
{
    typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions;
    // Check plugins for options
627 628
    const PluginSpecSet::const_iterator pcend = m_instance->d->pluginSpecs.constEnd();
    for (PluginSpecSet::const_iterator pit = m_instance->d->pluginSpecs.constBegin(); pit != pcend; ++pit) {
con's avatar
con committed
629 630 631 632 633 634 635 636 637 638 639 640 641
        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);
        }
    }
}

/*!
    Format the version of the plugin specs for command line help.
*/
642
void PluginManager::formatPluginVersions(QTextStream &str)
con's avatar
con committed
643
{
644 645
    const PluginSpecSet::const_iterator cend = m_instance->d->pluginSpecs.constEnd();
    for (PluginSpecSet::const_iterator it = m_instance->d->pluginSpecs.constBegin(); it != cend; ++it) {
con's avatar
con committed
646 647 648 649 650 651 652 653
        const PluginSpec *ps = *it;
        str << "  " << ps->name() << ' ' << ps->version() << ' ' << ps->description() <<  '\n';
    }
}

void PluginManager::startTests()
{
#ifdef WITH_TESTS
654 655
    foreach (const PluginManagerPrivate::TestSpec &testSpec, d->testSpecs) {
        const PluginSpec * const pluginSpec = testSpec.pluginSpec;
656 657
        if (!pluginSpec->plugin())
            continue;
658 659 660

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

Nikolai Kosjar's avatar
Nikolai Kosjar committed
663
        for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
Friedemann Kleint's avatar
Friedemann Kleint committed
664
#if QT_VERSION >= 0x050000
Nikolai Kosjar's avatar
Nikolai Kosjar committed
665
            const QByteArray signature = metaObject->method(i).methodSignature();
Friedemann Kleint's avatar
Friedemann Kleint committed
666
#else
Nikolai Kosjar's avatar
Nikolai Kosjar committed
667
            const QByteArray signature = metaObject->method(i).signature();
Friedemann Kleint's avatar
Friedemann Kleint committed
668 669 670
#endif
            if (signature.startsWith("test") && !signature.endsWith("_data()")) {
                const QString method = QString::fromLatin1(signature);
Nikolai Kosjar's avatar
Nikolai Kosjar committed
671 672
                const QString methodName = method.left(method.size() - 2);
                allTestFunctions.append(methodName);
con's avatar
con committed
673 674
            }
        }
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706

        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;
                const int index = testFunctionName.indexOf(QLatin1Char(':'));
                if (index != -1)
                    testFunctionName = testFunctionName.left(index);

                if (allTestFunctions.contains(testFunctionName)) {
                    // If the specified test data is invalid, the QTest framework will
                    // print a reasonable error message for us.
                    testFunctionsToExecute.append(userTestFunction);
                } else {
                    QTextStream out(stdout);
                    out << "Unknown test function \"" << testFunctionName
                        << "\" for plugin \"" << pluginSpec->name() << "\"." << endl
                        << "  Available test functions for plugin \"" << pluginSpec->name()
                        << "\" are:" << endl;
                    foreach (const QString &testFunction, allTestFunctions)
                        out << "    " << testFunction << endl;
                }
            }
        }

707
        // Don't run QTest::qExec without any test functions, that'd run
708
        // *all* slots as tests.
709 710 711 712 713 714 715 716
        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
717
    }
hjk's avatar
hjk committed
718
    if (!d->testSpecs.isEmpty())
719
        QTimer::singleShot(1, QCoreApplication::instance(), SLOT(quit()));
con's avatar
con committed
720 721 722
#endif
}

723 724 725
/*!
 * \internal
 */
726
bool PluginManager::testRunRequested()
con's avatar
con committed
727
{
728
    return !m_instance->d->testSpecs.isEmpty();
con's avatar
con committed
729 730
}

731 732 733
/*!
 * \internal
 */
734
QString PluginManager::testDataDirectory()
con's avatar
con committed
735
{
736
    QByteArray ba = qgetenv("QTCREATOR_TEST_DIR");
hjk's avatar
hjk committed
737
    QString s = QString::fromLocal8Bit(ba.constData(), ba.size());
con's avatar
con committed
738
    if (s.isEmpty()) {
739 740
        s = QLatin1String(IDE_TEST_DIR);
        s.append(QLatin1String("/tests"));
con's avatar
con committed
741 742 743 744 745
    }
    s = QDir::cleanPath(s);
    return s;
}

746 747 748 749 750 751
/*!
    Create a profiling entry showing the elapsed time if profiling is activated.
*/

void PluginManager::profilingReport(const char *what, const PluginSpec *spec)
{
752
    m_instance->d->profilingReport(what, spec);
753 754
}

755 756 757 758 759 760

/*!
    Returns a list of plugins in load order.
*/
QList<PluginSpec *> PluginManager::loadQueue()
{
761
    return m_instance->d->loadQueue();
762 763
}

con's avatar
con committed
764 765 766 767 768 769 770 771 772 773
//============PluginManagerPrivate===========

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

774 775 776 777 778 779 780 781 782 783 784 785
/*!
    \internal
*/
void PluginManagerPrivate::setSettings(QSettings *s)
{
    if (settings)
        delete settings;
    settings = s;
    if (settings)
        settings->setParent(this);
}

786 787 788 789 790 791 792 793 794 795 796 797
/*!
    \internal
*/
void PluginManagerPrivate::setGlobalSettings(QSettings *s)
{
    if (globalSettings)
        delete globalSettings;
    globalSettings = s;
    if (globalSettings)
        globalSettings->setParent(this);
}

con's avatar
con committed
798 799 800 801 802 803 804 805
/*!
    \internal
*/
PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
{
    return spec->d;
}

806 807 808 809 810 811 812 813 814 815 816 817 818
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;
819
        profilingSummary();
Tobias Hunger's avatar
Tobias Hunger committed
820
        emit q->initializationDone();
821 822 823 824 825
    } else {
        delayedInitializeTimer->start();
    }
}

con's avatar
con committed
826 827 828
/*!
    \internal
*/
829 830
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
    extension(QLatin1String("xml")),
831 832
    delayedInitializeTimer(0),
    shutdownEventLoop(0),
833
    m_profileElapsedMS(0),
834
    m_profilingVerbosity(0),
835
    settings(0),
836
    globalSettings(0),
837
    q(pluginManager)
con's avatar
con committed
838 839 840
{
}

841

con's avatar
con committed
842 843 844 845 846 847
/*!
    \internal
*/
PluginManagerPrivate::~PluginManagerPrivate()
{
    qDeleteAll(pluginSpecs);
848
    qDeleteAll(pluginCategories);
con's avatar
con committed
849 850
}

851 852 853
/*!
    \internal
*/
854 855
void PluginManagerPrivate::writeSettings()
{
856 857
    if (!settings)
        return;
con's avatar
con committed
858
    QStringList tempDisabledPlugins;
859
    QStringList tempForceEnabledPlugins;
860
    foreach (PluginSpec *spec, pluginSpecs) {
861
        if (!spec->isDisabledByDefault() && !spec->isEnabledInSettings())
con's avatar
con committed
862
            tempDisabledPlugins.append(spec->name());
863
        if (spec->isDisabledByDefault() && spec->isEnabledInSettings())
864
            tempForceEnabledPlugins.append(spec->name());
865 866
    }

867 868
    settings->setValue(QLatin1String(C_IGNORED_PLUGINS), tempDisabledPlugins);
    settings->setValue(QLatin1String(C_FORCEENABLED_PLUGINS), tempForceEnabledPlugins);
869 870
}

871 872 873
/*!
    \internal
*/
874
void PluginManagerPrivate::readSettings()
875
{
876
    if (globalSettings)
877 878 879 880 881
        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();
    }
882 883
}

884 885 886
/*!
    \internal
*/
con's avatar
con committed
887 888
void PluginManagerPrivate::stopAll()
{
889 890 891 892 893
    if (delayedInitializeTimer && delayedInitializeTimer->isActive()) {
        delayedInitializeTimer->stop();
        delete delayedInitializeTimer;
        delayedInitializeTimer = 0;
    }
con's avatar
con committed
894 895 896 897
    QList<PluginSpec *> queue = loadQueue();
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Stopped);
    }
898 899 900 901 902 903 904 905
}

/*!
    \internal
*/
void PluginManagerPrivate::deleteAll()
{
    QList<PluginSpec *> queue = loadQueue();
con's avatar
con committed
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
    QListIterator<PluginSpec *> it(queue);
    it.toBack();
    while (it.hasPrevious()) {
        loadPlugin(it.previous(), PluginSpec::Deleted);
    }
}

/*!
    \internal
*/
void PluginManagerPrivate::addObject(QObject *obj)
{
    {
        QWriteLocker lock(&(q->m_lock));
        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();

932 933 934 935 936 937 938
        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
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
        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);
    QWriteLocker lock(&(q->m_lock));
    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()) {
982 983 984 985
        PluginSpec *spec = it.previous();
        loadPlugin(spec, PluginSpec::Running);
        if (spec->state() == PluginSpec::Running)
            delayedInitializeQueue.append(spec);
con's avatar
con committed
986 987
    }
    emit q->pluginsChanged();
988 989 990 991 992 993 994

    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
995 996
}

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
/*!
    \internal
*/
void PluginManagerPrivate::shutdown()
{
    stopAll();
    if (!asynchronousPlugins.isEmpty()) {
        shutdownEventLoop = new QEventLoop;
        shutdownEventLoop->exec();
    }
    deleteAll();
1008
    if (!allObjects.isEmpty())
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
        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
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
/*!
    \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
1048
        spec->d->errorString = PluginManager::tr("Circular dependency detected:\n");
con's avatar
con committed
1049 1050
        int index = circularityCheckQueue.indexOf(spec);
        for (int i = index; i < circularityCheckQueue.size(); ++i) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1051
            spec->d->errorString.append(PluginManager::tr("%1(%2) depends on\n")
con's avatar
con committed
1052 1053
                .arg(circularityCheckQueue.at(i)->name()).arg(circularityCheckQueue.at(i)->version()));
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1054
        spec->d->errorString.append(PluginManager::tr("%1(%2)").arg(spec->name()).arg(spec->version()));
con's avatar
con committed
1055 1056 1057 1058 1059
        return false;
    }
    circularityCheckQueue.append(spec);
    // check if we have the dependencies
    if (spec->state() == PluginSpec::Invalid || spec->state() == PluginSpec::Read) {
1060
        queue.append(spec);
con's avatar
con committed
1061 1062
        return false;
    }
1063

con's avatar
con committed
1064 1065 1066 1067 1068
    // 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
1069
                PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
con's avatar
con committed
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
                    .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)
{
1084 1085 1086 1087
    if (spec->hasError() || spec->state() != destState-1)
        return;

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

1091 1092 1093
    switch (destState) {
    case PluginSpec::Running:
        profilingReport(">initializeExtensions", spec);
con's avatar
con committed
1094
        spec->d->initializeExtensions();
1095
        profilingReport("<initializeExtensions", spec);
con's avatar
con committed
1096
        return;
1097
    case PluginSpec::Deleted:
1098
        profilingReport(">delete", spec);
con's avatar
con committed
1099
        spec->d->kill();
1100
        profilingReport("<delete", spec);
con's avatar
con committed
1101
        return;
1102 1103
    default:
        break;
con's avatar
con committed
1104
    }
con's avatar
con committed
1105 1106 1107 1108 1109 1110 1111
    // check if dependencies have loaded without error
    QHashIterator<PluginDependency, PluginSpec *> it(spec->dependencySpecs());
    while (it.hasNext()) {
        it.next();
        if (it.key().type == PluginDependency::Optional)
            continue;
        PluginSpec *depSpec = it.value();
con's avatar
con committed
1112 1113 1114
        if (depSpec->state() != destState) {
            spec->d->hasError = true;
            spec->d->errorString =
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1115
                PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
con's avatar
con committed
1116 1117 1118 1119
                    .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
            return;
        }
    }
1120 1121 1122
    switch (destState) {
    case PluginSpec::Loaded:
        profilingReport(">loadLibrary", spec);
con's avatar
con committed
1123
        spec->d->loadLibrary();
1124 1125 1126 1127
        profilingReport("<loadLibrary", spec);
        break;
    case PluginSpec::Initialized:
        profilingReport(">initializePlugin", spec);
con's avatar
con committed
1128
        spec->d->initializePlugin();
1129 1130 1131 1132
        profilingReport("<initializePlugin", spec);
        break;
    case PluginSpec::Stopped:
        profilingReport(">stop", spec);
1133 1134 1135 1136 1137
        if (spec->d->stop() == IPlugin::AsynchronousShutdown) {
            asynchronousPlugins << spec;
            connect(spec->plugin(), SIGNAL(asynchronousShutdownFinished()),
                    this, SLOT(asyncShutdownFinished()));
        }
1138 1139 1140 1141 1142
        profilingReport("<stop", spec);
        break;
    default:
        break;
    }
con's avatar
con committed
1143 1144 1145 1146 1147 1148 1149 1150
}

/*!
    \internal
*/
void PluginManagerPrivate::setPluginPaths(const QStringList &paths)
{
    pluginPaths = paths;
1151
    readSettings();
con's avatar
con committed
1152 1153 1154 1155 1156 1157 1158 1159
    readPluginPaths();
}

/*!
    \internal
*/
void PluginManagerPrivate::readPluginPaths()
{
1160
    qDeleteAll(pluginCategories);
con's avatar
con committed
1161 1162
    qDeleteAll(pluginSpecs);
    pluginSpecs.clear();
1163
    pluginCategories.clear();
con's avatar
con committed
1164 1165 1166 1167 1168

    QStringList specFiles;
    QStringList searchPaths = pluginPaths;
    while (!searchPaths.isEmpty()) {
        const QDir dir(searchPaths.takeFirst());
1169 1170
        const QString pattern = QLatin1String("*.") + extension;
        const QFileInfoList files = dir.entryInfoList(QStringList(pattern), QDir::Files);
con's avatar
con committed
1171 1172 1173 1174 1175 1176
        foreach (const QFileInfo &file, files)
            specFiles << file.absoluteFilePath();
        const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
        foreach (const QFileInfo &subdir, dirs)
            searchPaths << subdir.absoluteFilePath();
    }
1177
    defaultCollection = new PluginCollection(QString());
1178
    pluginCategories.insert(QString(), defaultCollection);
1179

con's avatar
con committed
1180 1181 1182
    foreach (const QString &specFile, specFiles) {
        PluginSpec *spec = new PluginSpec;
        spec->d->read(specFile);
1183 1184 1185

        PluginCollection *collection = 0;
        // find correct plugin collection or create a new one
1186
        if (pluginCategories.contains(spec->category())) {
1187
            collection = pluginCategories.value(spec->category());
1188
        } else {
1189 1190 1191
            collection = new PluginCollection(spec->category());
            pluginCategories.insert(spec->category(), collection);
        }
1192 1193 1194 1195 1196
        if (defaultDisabledPlugins.contains(spec->name())) {
            spec->setDisabledByDefault(true);
            spec->setEnabled(false);
        }
        if (spec->isDisabledByDefault() && forceEnabledPlugins.contains(spec->name()))
1197
            spec->setEnabled(true);
1198
        if (!spec->isDisabledByDefault() && disabledPlugins.contains(spec->name()))
con's avatar
con committed
1199
            spec->setEnabled(false);
1200 1201

        collection->addPlugin(spec);
1202
        pluginSpecs.append(spec);
con's avatar
con committed
1203 1204
    }
    resolveDependencies();
1205 1206
    // ensure deterministic plugin load order by sorting
    qSort(pluginSpecs.begin(), pluginSpecs.end(), lessThanByPluginName);
con's avatar
con committed
1207 1208 1209 1210 1211 1212 1213 1214
    emit q->pluginsChanged();
}

void PluginManagerPrivate::resolveDependencies()
{
    foreach (PluginSpec *spec, pluginSpecs) {
        spec->d->resolveDependencies(pluginSpecs);
    }
1215 1216 1217 1218 1219

    // Reset disabledIndirectly flag
    foreach (PluginSpec *spec, loadQueue())
        spec->d->disabledIndirectly = false;

1220 1221 1222
    foreach (PluginSpec *spec, loadQueue()) {
        spec->d->disableIndirectlyIfDependencyDisabled();
    }
con's avatar
con committed
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
}

 // Look in argument descriptions of the specs for the option.
PluginSpec *PluginManagerPrivate::pluginForOption(const QString &option, bool *requiresArgument) const
{
    // Look in the plugins for an option
    typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions;

    *requiresArgument = false;
    const PluginSpecSet::const_iterator pcend = pluginSpecs.constEnd();
    for (PluginSpecSet::const_iterator pit = pluginSpecs.constBegin(); pit != pcend; ++pit) {
        PluginSpec *ps = *pit;
        const PluginArgumentDescriptions pargs = ps->argumentDescriptions();
        if (!pargs.empty()) {
            const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd();
            for (PluginArgumentDescriptions::const_iterator ait = pargs.constBegin(); ait != acend; ++ait) {
                if (ait->name == option) {
                    *requiresArgument = !ait->parameter.isEmpty();
                    return ps;
                }
            }
        }
    }
    return 0;
}

1249
void PluginManagerPrivate::disablePluginIndirectly(PluginSpec *spec)
1250
{
1251
    spec->d->disabledIndirectly = true;
1252 1253
}

con's avatar
con committed
1254 1255 1256 1257 1258 1259 1260 1261
PluginSpec *PluginManagerPrivate::pluginByName(const QString &name) const
{
    foreach (PluginSpec *spec, pluginSpecs)
        if (spec->name() == name)
            return spec;
    return 0;
}

1262 1263 1264 1265 1266 1267 1268
void PluginManagerPrivate::initProfiling()
{
    if (m_profileTimer.isNull()) {
        m_profileTimer.reset(new QTime);
        m_profileTimer->start();
        m_profileElapsedMS = 0;
        qDebug("Profiling started");
1269 1270
    } else {
        m_profilingVerbosity++;
1271 1272 1273 1274 1275 1276 1277 1278 1279
    }
}

void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *spec /* = 0 */)
{
    if (!m_profileTimer.isNull()) {
        const int absoluteElapsedMS = m_profileTimer->elapsed();
        const int elapsedMS = absoluteElapsedMS - m_profileElapsedMS;
        m_profileElapsedMS = absoluteElapsedMS;
1280 1281
        if (spec)
            m_profileTotal[spec] += elapsedMS;