pluginmanager.cpp 54.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
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
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26 27 28 29 30 31 32
#include "pluginmanager.h"
#include "pluginmanager_p.h"
#include "pluginspec.h"
#include "pluginspec_p.h"
#include "optionsparser.h"
#include "iplugin.h"

33
#include <QCoreApplication>
34 35 36
#include <QEventLoop>
#include <QDateTime>
#include <QDir>
37
#include <QFile>
38
#include <QLibrary>
39
#include <QLibraryInfo>
40 41 42 43 44
#include <QMetaProperty>
#include <QSettings>
#include <QTextStream>
#include <QTime>
#include <QWriteLocker>
hjk's avatar
hjk committed
45
#include <QDebug>
46
#include <QTimer>
47
#include <QSysInfo>
48

49
#include <utils/algorithm.h>
50
#include <utils/executeondestruction.h>
51
#include <utils/hostosinfo.h>
52
#include <utils/mimetypes/mimedatabase.h>
53
#include <utils/qtcassert.h>
54
#include <utils/synchronousprocess.h>
55

con's avatar
con committed
56
#ifdef WITH_TESTS
57
#include <utils/hostosinfo.h>
con's avatar
con committed
58 59 60
#include <QTest>
#endif

61 62
#include <functional>

63 64
Q_LOGGING_CATEGORY(pluginLog, "qtc.extensionsystem")

65 66 67
const char C_IGNORED_PLUGINS[] = "Plugins/Ignored";
const char C_FORCEENABLED_PLUGINS[] = "Plugins/ForceEnabled";
const int DELAYED_INITIALIZE_INTERVAL = 20; // ms
68

con's avatar
con committed
69 70 71 72
enum { debugLeaks = 0 };

/*!
    \namespace ExtensionSystem
73 74
    \brief The ExtensionSystem namespace provides classes that belong to the
           core plugin system.
con's avatar
con committed
75

76
    The basic extension system contains the plugin manager and its supporting classes,
con's avatar
con committed
77 78 79 80 81 82 83 84 85 86 87 88
    and the IPlugin interface that must be implemented by plugin providers.
*/

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

/*!
    \class ExtensionSystem::PluginManager
    \mainclass

89 90
    \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
91 92 93

    The plugin manager is used for the following tasks:
    \list
94 95
    \li Manage plugins and their state
    \li Manipulate a 'common object pool'
con's avatar
con committed
96 97 98
    \endlist

    \section1 Plugins
99
    Plugins consist of an XML descriptor file, and of a library that contains a Qt plugin
100 101
    that must derive from the IPlugin class and has an IID of
    \c "org.qt-project.Qt.QtCreatorPlugin".
con's avatar
con committed
102 103 104
    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.

105 106
    Usually, the application creates a PluginManager instance and initiates the
    loading.
con's avatar
con committed
107
    \code
108
        // 'plugins' and subdirs will be searched for plugins
109
        PluginManager::setPluginPaths(QStringList("plugins"));
110
        PluginManager::loadPlugins(); // try to load all the plugins
con's avatar
con committed
111
    \endcode
112 113
    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
114 115 116 117 118 119
    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
120
    via the getObjects() and getObject() functions.
con's avatar
con committed
121 122 123 124 125 126 127 128

    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
129
        // Plugin A provides a "MimeTypeHandler" extension point
con's avatar
con committed
130 131
        // in plugin B:
        MyMimeTypeHandler *handler = new MyMimeTypeHandler();
132
        PluginManager::instance()->addObject(handler);
133
        // In plugin A:
con's avatar
con committed
134
        QList<MimeTypeHandler *> mimeHandlers =
135
            PluginManager::getObjects<MimeTypeHandler>();
con's avatar
con committed
136 137
    \endcode

138 139 140 141 142 143

    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
144
    invokable functions of the "provider" object in the object pool.
145 146

    The \c{ExtensionSystem::invoke} function template encapsulates
Orgad Shaneh's avatar
Orgad Shaneh committed
147
    {ExtensionSystem::Invoker} construction for the common case where
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    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;

172
            QObject *target = PluginManager::getObjectByClassName("PluginA::SomeProvider");
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

            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

199
    \note The type of the parameters passed to the \c{invoke()} calls
200 201 202 203 204
    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.

205
    \note The object pool manipulating functions are thread-safe.
con's avatar
con committed
206 207 208 209
*/

/*!
    \fn void PluginManager::objectAdded(QObject *obj)
210
    Signals that \a obj has been added to the object pool.
con's avatar
con committed
211 212 213 214
*/

/*!
    \fn void PluginManager::aboutToRemoveObject(QObject *obj)
215
    Signals that \a obj will be removed from the object pool.
con's avatar
con committed
216 217 218 219
*/

/*!
    \fn void PluginManager::pluginsChanged()
220
    Signals that the list of available plugins has changed.
con's avatar
con committed
221 222 223 224 225

    \sa plugins()
*/

/*!
226 227
    \fn T *PluginManager::getObject()

228 229
    Retrieves the object of a given type from the object pool.

230
    This function uses \c qobject_cast to determine the type of an object.
con's avatar
con committed
231
    If there are more than one object of the given type in
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    the object pool, this function will arbitrarily choose one of them.

    \sa addObject()
*/

/*!
    \fn T *PluginManager::getObject(Predicate predicate)

    Retrieves the object of a given type from the object pool that matches
    the \a predicate.

    This function uses \c qobject_cast to determine the type of an object.
    The predicate must be a function taking T * and returning a bool.
    If there is more than one object matching the type and predicate,
    this function will arbitrarily choose one of them.
con's avatar
con committed
247 248 249 250 251

    \sa addObject()
*/

/*!
252
    \fn QList<T *> PluginManager::getObjects()
253 254 255

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

256
    This function uses \c qobject_cast to determine the type of an object.
con's avatar
con committed
257 258 259 260

    \sa addObject()
*/

261 262 263 264 265 266 267 268 269 270 271 272 273 274
/*!
    \fn QList<T *> PluginManager::getObjects(Predicate predicate)

    Retrieves all objects of a given type from the object pool that
    match the \a predicate.

    This function uses \c qobject_cast to determine the type of an object.
    The predicate should be a unary function taking a T* parameter and
    returning a bool.

    \sa addObject()
*/


275
using namespace Utils;
276 277 278
using namespace ExtensionSystem::Internal;

namespace ExtensionSystem {
con's avatar
con committed
279

hjk's avatar
hjk committed
280 281 282
static Internal::PluginManagerPrivate *d = 0;
static PluginManager *m_instance = 0;

con's avatar
con committed
283
/*!
284
    Gets the unique plugin manager instance.
con's avatar
con committed
285 286 287 288 289 290 291
*/
PluginManager *PluginManager::instance()
{
    return m_instance;
}

/*!
292
    Creates a plugin manager. Should be done only once per application.
con's avatar
con committed
293 294 295 296
*/
PluginManager::PluginManager()
{
    m_instance = this;
hjk's avatar
hjk committed
297
    d = new PluginManagerPrivate(this);
con's avatar
con committed
298 299 300 301 302 303 304 305 306 307 308 309
}

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

/*!
310 311 312
    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
313 314 315 316 317 318 319 320 321 322 323
    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
324
    d->addObject(obj);
con's avatar
con committed
325 326 327 328 329 330 331 332
}

/*!
    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
333
    d->removeObject(obj);
con's avatar
con committed
334 335 336
}

/*!
337 338 339 340
    Retrieves the list of all objects in the pool, unfiltered.

    Usually, clients do not need to call this function.

con's avatar
con committed
341 342 343
    \sa PluginManager::getObject()
    \sa PluginManager::getObjects()
*/
344
QList<QObject *> PluginManager::allObjects()
con's avatar
con committed
345
{
hjk's avatar
hjk committed
346 347 348 349 350 351
    return d->allObjects;
}

QReadWriteLock *PluginManager::listLock()
{
    return &d->m_lock;
con's avatar
con committed
352 353 354 355 356 357 358 359 360 361 362 363
}

/*!
    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()
{
364
    d->loadPlugins();
con's avatar
con committed
365 366
}

367 368 369 370
/*!
    Returns true if any plugin has errors even though it is enabled.
    Most useful to call after loadPlugins().
*/
371
bool PluginManager::hasError()
372
{
Eike Ziller's avatar
Eike Ziller committed
373
    return Utils::anyOf(plugins(), [](PluginSpec *spec) {
374
        // only show errors on startup if plugin is enabled.
Eike Ziller's avatar
Eike Ziller committed
375 376
        return spec->hasError() && spec->isEffectivelyEnabled();
    });
377 378
}

379 380 381 382 383
/*!
    Returns all plugins that require \a spec to be loaded. Recurses into dependencies.
 */
QSet<PluginSpec *> PluginManager::pluginsRequiringPlugin(PluginSpec *spec)
{
Eike Ziller's avatar
Eike Ziller committed
384 385 386 387 388
    QSet<PluginSpec *> dependingPlugins({spec});
    // recursively add plugins that depend on plugins that.... that depend on spec
    foreach (PluginSpec *spec, d->loadQueue()) {
        if (spec->requiresAny(dependingPlugins))
            dependingPlugins.insert(spec);
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    }
    dependingPlugins.remove(spec);
    return dependingPlugins;
}

/*!
    Returns all plugins that \a spec requires to be loaded. Recurses into dependencies.
 */
QSet<PluginSpec *> PluginManager::pluginsRequiredByPlugin(PluginSpec *spec)
{
    QSet<PluginSpec *> recursiveDependencies;
    recursiveDependencies.insert(spec);
    QList<PluginSpec *> queue;
    queue.append(spec);
    while (!queue.isEmpty()) {
        PluginSpec *checkSpec = queue.takeFirst();
        QHashIterator<PluginDependency, PluginSpec *> depIt(checkSpec->dependencySpecs());
        while (depIt.hasNext()) {
            depIt.next();
            if (depIt.key().type != PluginDependency::Required)
                continue;
            PluginSpec *depSpec = depIt.value();
            if (!recursiveDependencies.contains(depSpec)) {
                recursiveDependencies.insert(depSpec);
                queue.append(depSpec);
            }
        }
    }
    recursiveDependencies.remove(spec);
    return recursiveDependencies;
}

421 422 423 424 425 426 427 428
/*!
    Shuts down and deletes all plugins.
*/
void PluginManager::shutdown()
{
    d->shutdown();
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
static QString filled(const QString &s, int min)
{
    return s + QString(qMax(0, min - s.size()), ' ');
}

QString PluginManager::systemInformation() const
{
    QString result;
    const QString qtdiagBinary = HostOsInfo::withExecutableSuffix(
                QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qtdiag");
    SynchronousProcess qtdiagProc;
    const SynchronousProcessResponse response = qtdiagProc.runBlocking(qtdiagBinary, QStringList());
    if (response.result == SynchronousProcessResponse::Finished)
        result += response.allOutput() + "\n";
    result += "Plugin information:\n\n";
    auto longestSpec = std::max_element(plugins().cbegin(), plugins().cend(),
                                        [](const PluginSpec *left, const PluginSpec *right) {
                                            return left->name().size() < right->name().size();
                                        });
    int size = (*longestSpec)->name().size();
    for (const PluginSpec *spec : plugins()) {
450
        result += QLatin1String(spec->isEffectivelyEnabled() ? "+ " : "  ") + filled(spec->name(), size) +
451 452 453 454 455
                  " " + spec->version() + "\n";
    }
    return result;
}

con's avatar
con committed
456 457 458 459 460
/*!
    The list of paths were the plugin manager searches for plugins.

    \sa setPluginPaths()
*/
461
QStringList PluginManager::pluginPaths()
con's avatar
con committed
462
{
hjk's avatar
hjk committed
463
    return d->pluginPaths;
con's avatar
con committed
464 465 466 467 468 469 470 471 472 473 474 475
}

/*!
    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
476
    d->setPluginPaths(paths);
con's avatar
con committed
477 478 479
}

/*!
480
    The IID that valid plugins must have.
con's avatar
con committed
481

482
    \sa setPluginIID()
con's avatar
con committed
483
*/
484
QString PluginManager::pluginIID()
con's avatar
con committed
485
{
486
    return d->pluginIID;
con's avatar
con committed
487 488 489
}

/*!
490 491 492
    Sets the IID that valid plugins must have. Only plugins with this IID are loaded, others are
    silently ignored.

con's avatar
con committed
493
    At the moment this must be called before setPluginPaths() is called.
494
    // ### TODO let this + setPluginPaths read the plugin meta data lazyly whenever loadPlugins() or plugins() is called.
con's avatar
con committed
495
*/
496
void PluginManager::setPluginIID(const QString &iid)
con's avatar
con committed
497
{
498
    d->pluginIID = iid;
con's avatar
con committed
499 500
}

501
/*!
502 503
    Defines the user specific settings to use for information about enabled and
    disabled plugins.
504 505
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
506
void PluginManager::setSettings(QSettings *settings)
507
{
hjk's avatar
hjk committed
508
    d->setSettings(settings);
509 510
}

511
/*!
512 513
    Defines the global (user-independent) settings to use for information about
    default disabled plugins.
514 515 516 517
    Needs to be set before the plugin search path is set with setPluginPaths().
*/
void PluginManager::setGlobalSettings(QSettings *settings)
{
hjk's avatar
hjk committed
518
    d->setGlobalSettings(settings);
519 520 521
}

/*!
522 523
    Returns the user specific settings used for information about enabled and
    disabled plugins.
524
*/
525
QSettings *PluginManager::settings()
526
{
hjk's avatar
hjk committed
527
    return d->settings;
528 529
}

530 531 532
/*!
    Returns the global (user-independent) settings used for information about default disabled plugins.
*/
533
QSettings *PluginManager::globalSettings()
534
{
hjk's avatar
hjk committed
535
    return d->globalSettings;
536 537 538 539
}

void PluginManager::writeSettings()
{
hjk's avatar
hjk committed
540
    d->writeSettings();
541 542
}

con's avatar
con committed
543
/*!
544
    The arguments left over after parsing (that were neither startup nor plugin
con's avatar
con committed
545 546
    arguments). Typically, this will be the list of files to open.
*/
547
QStringList PluginManager::arguments()
con's avatar
con committed
548
{
hjk's avatar
hjk committed
549
    return d->arguments;
con's avatar
con committed
550 551 552 553 554 555 556 557 558 559 560
}

/*!
    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()
*/
561
const QList<PluginSpec *> PluginManager::plugins()
con's avatar
con committed
562
{
hjk's avatar
hjk committed
563
    return d->pluginSpecs;
con's avatar
con committed
564 565
}

566
QHash<QString, QList<PluginSpec *> > PluginManager::pluginCollections()
567
{
hjk's avatar
hjk committed
568
    return d->pluginCategories;
569 570
}

571
static const char argumentKeywordC[] = ":arguments";
572
static const char pwdKeywordC[] = ":pwd";
573 574

/*!
575
    Serializes plugin options and arguments for sending in a single string
576 577 578 579 580 581
    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()
*/
582
QString PluginManager::serializedArguments()
583 584 585 586 587 588 589 590 591 592
{
    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;
hjk's avatar
hjk committed
593
            rc +=  ps->arguments().join(separator);
594 595
        }
    }
596 597 598
    if (!rc.isEmpty())
        rc += separator;
    rc += QLatin1String(pwdKeywordC) + separator + QDir::currentPath();
hjk's avatar
hjk committed
599
    if (!d->arguments.isEmpty()) {
600 601 602
        if (!rc.isEmpty())
            rc += separator;
        rc += QLatin1String(argumentKeywordC);
603 604
        foreach (const QString &argument, d->arguments)
            rc += separator + argument;
605 606 607 608 609 610 611 612 613 614 615 616 617
    }
    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();
618
    QStringList::const_iterator it = std::find(in.constBegin(), inEnd, key);
619 620 621 622 623 624 625 626 627 628 629
    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.
630 631 632

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

635
void PluginManager::remoteArguments(const QString &serializedArgument, QObject *socket)
636 637 638 639
{
    if (serializedArgument.isEmpty())
        return;
    QStringList serializedArguments = serializedArgument.split(QLatin1Char('|'));
640 641
    const QStringList pwdValue = subList(serializedArguments, QLatin1String(pwdKeywordC));
    const QString workingDirectory = pwdValue.isEmpty() ? QString() : pwdValue.first();
642 643 644 645
    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());
646 647
            QObject *socketParent = ps->plugin()->remoteCommand(pluginOptions, workingDirectory,
                                                                arguments);
648 649 650 651
            if (socketParent && socket) {
                socket->setParent(socketParent);
                socket = 0;
            }
652 653
        }
    }
654 655
    if (socket)
        delete socket;
656 657
}

con's avatar
con committed
658 659 660 661 662 663 664
/*!
    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.
665

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

con's avatar
con committed
671 672 673 674 675 676 677
    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
678
    OptionsParser options(args, appOptions, foundAppOptions, errorString, d);
con's avatar
con committed
679 680 681 682 683 684 685
    return options.parse();
}



static inline void indent(QTextStream &str, int indent)
{
Eike Ziller's avatar
Eike Ziller committed
686
    str << QString(indent, ' ');
con's avatar
con committed
687 688 689 690 691 692 693 694 695 696 697 698 699
}

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();
    }
hjk's avatar
hjk committed
700 701 702 703 704 705
    if (remainingIndent >= 1) {
        indent(str, remainingIndent);
    } else {
        str << '\n';
        indent(str, descriptionIndentation);
    }
con's avatar
con committed
706 707 708 709
    str << description << '\n';
}

/*!
710
    Formats the startup options of the plugin manager for command line help.
con's avatar
con committed
711 712 713 714
*/

void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
{
715
    formatOption(str, QLatin1String(OptionsParser::LOAD_OPTION),
716
                 QLatin1String("plugin"), QLatin1String("Load <plugin> and all plugins that it requires"),
717
                 optionIndentation, descriptionIndentation);
718 719 720
    formatOption(str, QLatin1String(OptionsParser::LOAD_OPTION) + QLatin1String(" all"),
                 QString(), QLatin1String("Load all available plugins"),
                 optionIndentation, descriptionIndentation);
con's avatar
con committed
721
    formatOption(str, QLatin1String(OptionsParser::NO_LOAD_OPTION),
722
                 QLatin1String("plugin"), QLatin1String("Do not load <plugin> and all plugins that require it"),
con's avatar
con committed
723
                 optionIndentation, descriptionIndentation);
724 725 726 727 728
    formatOption(str, QLatin1String(OptionsParser::NO_LOAD_OPTION) + QLatin1String(" all"),
                 QString(), QString::fromLatin1("Do not load any plugin (useful when "
                                                "followed by one or more \"%1\" arguments)")
                 .arg(QLatin1String(OptionsParser::LOAD_OPTION)),
                 optionIndentation, descriptionIndentation);
729 730 731
    formatOption(str, QLatin1String(OptionsParser::PROFILE_OPTION),
                 QString(), QLatin1String("Profile plugin loading"),
                 optionIndentation, descriptionIndentation);
Eike Ziller's avatar
Eike Ziller committed
732
#ifdef WITH_TESTS
733
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION)
734
                 + QLatin1String(" <plugin>[,testfunction[:testdata]]..."), QString(),
735 736
                 QLatin1String("Run plugin's tests (by default a separate settings path is used)"),
                 optionIndentation, descriptionIndentation);
737 738
    formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION) + QLatin1String(" all"),
                 QString(), QLatin1String("Run tests from all plugins"),
Eike Ziller's avatar
Eike Ziller committed
739
                 optionIndentation, descriptionIndentation);
740 741 742
    formatOption(str, QString::fromLatin1(OptionsParser::NOTEST_OPTION),
                 QLatin1String("plugin"), QLatin1String("Exclude all of the plugin's tests from the test run"),
                 optionIndentation, descriptionIndentation);
Eike Ziller's avatar
Eike Ziller committed
743
#endif
con's avatar
con committed
744 745 746
}

/*!
747
    Formats the plugin options of the plugin specs for command line help.
con's avatar
con committed
748 749
*/

750
void PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation)
con's avatar
con committed
751 752
{
    // Check plugins for options
hjk's avatar
hjk committed
753 754
    foreach (PluginSpec *ps, d->pluginSpecs) {
        const PluginSpec::PluginArgumentDescriptions pargs = ps->argumentDescriptions();
con's avatar
con committed
755
        if (!pargs.empty()) {
hjk's avatar
hjk committed
756 757 758
            str << "\nPlugin: " <<  ps->name() << '\n';
            foreach (PluginArgumentDescription pad, pargs)
                formatOption(str, pad.name, pad.parameter, pad.description, optionIndentation, descriptionIndentation);
con's avatar
con committed
759 760 761 762 763
        }
    }
}

/*!
764
    Formats the version of the plugin specs for command line help.
con's avatar
con committed
765
*/
766
void PluginManager::formatPluginVersions(QTextStream &str)
con's avatar
con committed
767
{
hjk's avatar
hjk committed
768
    foreach (PluginSpec *ps, d->pluginSpecs)
con's avatar
con committed
769 770 771
        str << "  " << ps->name() << ' ' << ps->version() << ' ' << ps->description() <<  '\n';
}

772 773 774
/*!
 * \internal
 */
775
bool PluginManager::testRunRequested()
con's avatar
con committed
776
{
hjk's avatar
hjk committed
777
    return !d->testSpecs.isEmpty();
con's avatar
con committed
778 779
}

780
/*!
781 782
    Creates a profiling entry showing the elapsed time if profiling is
    activated.
783 784 785 786
*/

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

790 791 792 793 794 795

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

con's avatar
con committed
799 800 801 802 803 804 805 806 807 808
//============PluginManagerPrivate===========

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

809 810 811 812 813 814 815 816 817 818 819 820
/*!
    \internal
*/
void PluginManagerPrivate::setSettings(QSettings *s)
{
    if (settings)
        delete settings;
    settings = s;
    if (settings)
        settings->setParent(this);
}

821 822 823 824 825 826 827 828 829 830 831 832
/*!
    \internal
*/
void PluginManagerPrivate::setGlobalSettings(QSettings *s)
{
    if (globalSettings)
        delete globalSettings;
    globalSettings = s;
    if (globalSettings)
        globalSettings->setParent(this);
}

con's avatar
con committed
833 834 835 836 837 838 839 840
/*!
    \internal
*/
PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
{
    return spec->d;
}

841 842 843 844 845 846 847 848 849 850 851
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()) {
852
        m_isInitializationDone = true;
853 854
        delete delayedInitializeTimer;
        delayedInitializeTimer = 0;
855
        profilingSummary();
Tobias Hunger's avatar
Tobias Hunger committed
856
        emit q->initializationDone();
857 858
#ifdef WITH_TESTS
        if (q->testRunRequested())
Nikolai Kosjar's avatar
Nikolai Kosjar committed
859
            startTests();
860
#endif
861 862 863 864 865
    } else {
        delayedInitializeTimer->start();
    }
}

con's avatar
con committed
866 867 868
/*!
    \internal
*/
869
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
870 871
    delayedInitializeTimer(0),
    shutdownEventLoop(0),
872
    m_profileElapsedMS(0),
873
    m_profilingVerbosity(0),
874
    settings(0),
875
    globalSettings(0),
876
    q(pluginManager)
con's avatar
con committed
877 878 879
{
}

880

con's avatar
con committed
881 882 883 884 885 886 887 888
/*!
    \internal
*/
PluginManagerPrivate::~PluginManagerPrivate()
{
    qDeleteAll(pluginSpecs);
}

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

905 906
    settings->setValue(QLatin1String(C_IGNORED_PLUGINS), tempDisabledPlugins);
    settings->setValue(QLatin1String(C_FORCEENABLED_PLUGINS), tempForceEnabledPlugins);
907 908
}

909 910 911
/*!
    \internal
*/
912
void PluginManagerPrivate::readSettings()
913
{
914
    if (globalSettings) {
915
        defaultDisabledPlugins = globalSettings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList();
916 917
        defaultEnabledPlugins = globalSettings->value(QLatin1String(C_FORCEENABLED_PLUGINS)).toStringList();
    }
918 919 920 921
    if (settings) {
        disabledPlugins = settings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList();
        forceEnabledPlugins = settings->value(QLatin1String(C_FORCEENABLED_PLUGINS)).toStringList();
    }
922 923
}

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

/*!
    \internal
*/
void PluginManagerPrivate::deleteAll()
{
Eike Ziller's avatar
Eike Ziller committed
945 946 947
    Utils::reverseForeach(loadQueue(), [this](PluginSpec *spec) {
        loadPlugin(spec, PluginSpec::Deleted);
    });
con's avatar
con committed
948 949
}

950
#ifdef WITH_TESTS
951 952 953 954 955 956

typedef QMap<QObject *, QStringList> TestPlan; // Object -> selected test functions
typedef QMapIterator<QObject *, QStringList> TestPlanIterator;

static bool isTestFunction(const QMetaMethod &metaMethod)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
957
    static const QList<QByteArray> blackList = QList<QByteArray>()
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
        << "initTestCase()" << "cleanupTestCase()" << "init()" << "cleanup()";

    if (metaMethod.methodType() != QMetaMethod::Slot)
        return false;

    if (metaMethod.access() != QMetaMethod::Private)
        return false;

    const QByteArray signature = metaMethod.methodSignature();
    if (blackList.contains(signature))
        return false;

    if (!signature.startsWith("test"))
        return false;

    if (signature.endsWith("_data()"))
        return false;

    return true;
}

979 980
static QStringList testFunctions(const QMetaObject *metaObject)
{
981 982

    QStringList functions;
983 984

    for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
985 986 987
        const QMetaMethod metaMethod = metaObject->method(i);
        if (isTestFunction(metaMethod)) {
            const QByteArray signature = metaMethod.methodSignature();
988 989
            const QString method = QString::fromLatin1(signature);
            const QString methodName = method.left(method.size() - 2);
990
            functions.append(methodName);
991 992 993
        }
    }

994
    return functions;
995 996
}

997 998
static QStringList matchingTestFunctions(const QStringList &testFunctions,
                                         const QString &matchText)
999
{
1000 1001 1002 1003 1004 1005 1006 1007
    // There might be a test data suffix like in "testfunction:testdata1".
    QString testFunctionName = matchText;
    QString testDataSuffix;
    const int index = testFunctionName.indexOf(QLatin1Char(':'));
    if (index != -1) {
        testDataSuffix = testFunctionName.mid(index);
        testFunctionName = testFunctionName.left(index);
    }
1008

1009 1010 1011 1012
    const QRegExp regExp(testFunctionName, Qt::CaseSensitive, QRegExp::Wildcard);
    QStringList matchingFunctions;
    foreach (const QString &testFunction, testFunctions) {
        if (regExp.exactMatch(testFunction)) {
1013 1014
            // If the specified test data is invalid, the QTest framework will
            // print a reasonable error message for us.
1015 1016 1017 1018 1019 1020 1021 1022 1023
            matchingFunctions.append(testFunction + testDataSuffix);
        }
    }

    return matchingFunctions;
}

static QObject *objectWithClassName(const QList<QObject *> &objects, const QString &className)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
1024
    return Utils::findOr(objects, 0, [className] (QObject *object) -> bool {
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
        QString candidate = QString::fromUtf8(object->metaObject()->className());
        const int colonIndex = candidate.lastIndexOf(QLatin1Char(':'));
        if (colonIndex != -1 && colonIndex < candidate.size() - 1)
            candidate = candidate.mid(colonIndex + 1);
        return candidate == className;
    });
}

static int executeTestPlan(const TestPlan &testPlan)
{
    int failedTests = 0;

    TestPlanIterator it(testPlan);
    while (it.hasNext()) {
        it.next();
        QObject *testObject = it.key();
        QStringList functions = it.value();

        // Don't run QTest::qExec without any test functions, that'd run *all* slots as tests.
        if (functions.isEmpty())
            continue;

        functions.removeDuplicates();

        // QTest::qExec() expects basically QCoreApplication::arguments(),
        QStringList qExecArguments = QStringList()
                << QLatin1String("arg0") // fake application name
                << QLatin1String("-maxwarnings") << QLatin1String("0"); // unlimit output
        qExecArguments << functions;
1054
        // avoid being stuck in QTBUG-24925
1055
        if (!HostOsInfo::isWindowsHost())
1056
            qExecArguments << "-nocrashhandler";
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101