qmljsplugindumper.cpp 22.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** 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
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30
31

#include "qmljsplugindumper.h"
32
#include "qmljsmodelmanagerinterface.h"
33
34

#include <qmljs/qmljsinterpreter.h>
35
36
//#include <projectexplorer/session.h>
//#include <coreplugin/messagemanager.h>
37
#include <utils/filesystemwatcher.h>
38
#include <utils/fileutils.h>
39

40
#include <QDir>
41

42
using namespace LanguageUtils;
43
44
using namespace QmlJS;

45
PluginDumper::PluginDumper(ModelManagerInterface *modelManager)
46
47
    : QObject(modelManager)
    , m_modelManager(modelManager)
48
    , m_pluginWatcher(0)
49
{
50
    qRegisterMetaType<QmlJS::ModelManagerInterface::ProjectInfo>("QmlJS::ModelManagerInterface::ProjectInfo");
51
52
53
54
55
56
57
58
59
60
61
}

Utils::FileSystemWatcher *PluginDumper::pluginWatcher()
{
    if (!m_pluginWatcher) {
        m_pluginWatcher = new Utils::FileSystemWatcher(this);
        m_pluginWatcher->setObjectName(QLatin1String("PluginDumperWatcher"));
        connect(m_pluginWatcher, SIGNAL(fileChanged(QString)),
                this, SLOT(pluginChanged(QString)));
    }
    return m_pluginWatcher;
62
63
}

64
65
66
67
68
69
70
void PluginDumper::loadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info)
{
    // move to the owning thread
    metaObject()->invokeMethod(this, "onLoadBuiltinTypes",
                               Q_ARG(QmlJS::ModelManagerInterface::ProjectInfo, info));
}

71
void PluginDumper::loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion)
72
73
74
75
76
{
    // move to the owning thread
    metaObject()->invokeMethod(this, "onLoadPluginTypes",
                               Q_ARG(QString, libraryPath),
                               Q_ARG(QString, importPath),
77
78
                               Q_ARG(QString, importUri),
                               Q_ARG(QString, importVersion));
79
80
}

81
82
83
84
85
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
void PluginDumper::scheduleRedumpPlugins()
{
    // move to the owning thread
    metaObject()->invokeMethod(this, "dumpAllPlugins", Qt::QueuedConnection);
}

void PluginDumper::scheduleMaybeRedumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info)
{
    // move to the owning thread
    metaObject()->invokeMethod(this, "dumpBuiltins", Qt::QueuedConnection,
                               Q_ARG(QmlJS::ModelManagerInterface::ProjectInfo, info));
}

void PluginDumper::onLoadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info, bool force)
{
    if (info.qmlDumpPath.isEmpty() || info.qtImportsPath.isEmpty())
        return;

    const QString importsPath = QDir::cleanPath(info.qtImportsPath);
    if (m_runningQmldumps.values().contains(importsPath))
        return;

    LibraryInfo builtinInfo;
    if (!force) {
        const Snapshot snapshot = m_modelManager->snapshot();
        builtinInfo = snapshot.libraryInfo(info.qtImportsPath);
        if (builtinInfo.isValid())
            return;
    }
    builtinInfo = LibraryInfo(LibraryInfo::Found);
    m_modelManager->updateLibraryInfo(info.qtImportsPath, builtinInfo);

    // prefer QTDIR/imports/builtins.qmltypes if available
    const QString builtinQmltypesPath = info.qtImportsPath + QLatin1String("/builtins.qmltypes");
    if (QFile::exists(builtinQmltypesPath)) {
116
        loadQmltypesFile(QStringList(builtinQmltypesPath), info.qtImportsPath, builtinInfo);
117
118
        return;
    }
119
120
121
122
123
124
125
    // QTDIR/imports/QtQuick1/builtins.qmltypes was used in developer builds of 5.0.0, 5.0.1
    const QString builtinQmltypesPath2 = info.qtImportsPath
            + QLatin1String("/QtQuick1/builtins.qmltypes");
    if (QFile::exists(builtinQmltypesPath2)) {
        loadQmltypesFile(QStringList(builtinQmltypesPath2), info.qtImportsPath, builtinInfo);
        return;
    }
126
127
128
129
130
131
132
133
134
135
136
137

    // run qmldump
    QProcess *process = new QProcess(this);
    process->setEnvironment(info.qmlDumpEnvironment.toStringList());
    connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int)));
    connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(qmlPluginTypeDumpError(QProcess::ProcessError)));
    QStringList args(QLatin1String("--builtins"));
    process->start(info.qmlDumpPath, args);
    m_runningQmldumps.insert(process, info.qtImportsPath);
    m_qtToInfo.insert(info.qtImportsPath, info);
}

138
139
140
141
static QString makeAbsolute(const QString &path, const QString &base)
{
    if (QFileInfo(path).isAbsolute())
        return path;
142
    return QString::fromLatin1("%1/%3").arg(base, path);
143
144
}

145
void PluginDumper::onLoadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion)
146
147
148
149
150
{
    const QString canonicalLibraryPath = QDir::cleanPath(libraryPath);
    if (m_runningQmldumps.values().contains(canonicalLibraryPath))
        return;
    const Snapshot snapshot = m_modelManager->snapshot();
151
    const LibraryInfo libraryInfo = snapshot.libraryInfo(canonicalLibraryPath);
152
    if (libraryInfo.pluginTypeInfoStatus() != LibraryInfo::NoTypeInfo)
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        return;

    // avoid inserting the same plugin twice
    int index;
    for (index = 0; index < m_plugins.size(); ++index) {
        if (m_plugins.at(index).qmldirPath == libraryPath)
            break;
    }
    if (index == m_plugins.size())
        m_plugins.append(Plugin());

    Plugin &plugin = m_plugins[index];
    plugin.qmldirPath = canonicalLibraryPath;
    plugin.importPath = importPath;
    plugin.importUri = importUri;
168
    plugin.importVersion = importVersion;
169

170
171
172
    // add default qmltypes file if it exists
    const QLatin1String defaultQmltypesFileName("plugins.qmltypes");
    const QString defaultQmltypesPath = makeAbsolute(defaultQmltypesFileName, canonicalLibraryPath);
173
    if (!plugin.typeInfoPaths.contains(defaultQmltypesPath) && QFile::exists(defaultQmltypesPath))
174
175
176
        plugin.typeInfoPaths += defaultQmltypesPath;

    // add typeinfo files listed in qmldir
177
178
179
180
181
    foreach (const QmlDirParser::TypeInfo &typeInfo, libraryInfo.typeInfos()) {
        QString pathNow = makeAbsolute(typeInfo.fileName, canonicalLibraryPath);
        if (!plugin.typeInfoPaths.contains(pathNow) && QFile::exists(pathNow))
            plugin.typeInfoPaths += pathNow;
    }
182

183
    // watch plugin libraries
184
185
    foreach (const QmlDirParser::Plugin &plugin, snapshot.libraryInfo(canonicalLibraryPath).plugins()) {
        const QString pluginLibrary = resolvePlugin(canonicalLibraryPath, plugin.path, plugin.name);
186
187
188
189
190
        if (!pluginLibrary.isEmpty()) {
            if (!pluginWatcher()->watchesFile(pluginLibrary))
                pluginWatcher()->addFile(pluginLibrary, Utils::FileSystemWatcher::WatchModifiedDate);
            m_libraryToPluginIndex.insert(pluginLibrary, index);
        }
191
192
    }

193
194
195
196
197
    // watch library qmltypes file
    if (!plugin.typeInfoPaths.isEmpty()) {
        foreach (const QString &path, plugin.typeInfoPaths) {
            if (!QFile::exists(path))
                continue;
198
199
200
201
            if (!pluginWatcher()->watchesFile(path))
                pluginWatcher()->addFile(path, Utils::FileSystemWatcher::WatchModifiedDate);
            m_libraryToPluginIndex.insert(path, index);
        }
202
203
    }

204
205
206
    dump(plugin);
}

207
void PluginDumper::dumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info)
208
{
209
210
211
212
213
214
215
216
217
    // if the builtin types were generated with a different qmldump, regenerate!
    if (m_qtToInfo.contains(info.qtImportsPath)) {
        QmlJS::ModelManagerInterface::ProjectInfo oldInfo = m_qtToInfo.value(info.qtImportsPath);
        if (oldInfo.qmlDumpPath != info.qmlDumpPath
                || oldInfo.qmlDumpEnvironment != info.qmlDumpEnvironment) {
            m_qtToInfo.remove(info.qtImportsPath);
            onLoadBuiltinTypes(info, true);
        }
    }
218
219
220
221
}

void PluginDumper::dumpAllPlugins()
{
222
    foreach (const Plugin &plugin, m_plugins) {
223
        dump(plugin);
224
    }
225
226
}

227
static QString noTypeinfoError(const QString &libraryPath)
228
{
Leena Miettinen's avatar
Leena Miettinen committed
229
    return PluginDumper::tr("QML module does not contain information about components contained in plugins.\n\n"
Christian Kamm's avatar
Christian Kamm committed
230
                            "Module path: %1\n"
231
232
                            "See \"Using QML Modules with Plugins\" in the documentation.").arg(
                libraryPath);
233
234
}

235
236
static QString qmldumpErrorMessage(const QString &libraryPath, const QString &error)
{
237
    return noTypeinfoError(libraryPath) + QLatin1String("\n\n") +
238
239
            PluginDumper::tr("Automatic type dump of QML module failed.\nErrors:\n%1").
            arg(error) + QLatin1Char('\n');
240
241
}

242
static QString qmldumpFailedMessage(const QString &libraryPath, const QString &error)
243
{
244
    QString firstLines =
hjk's avatar
hjk committed
245
            QStringList(error.split(QLatin1Char('\n')).mid(0, 10)).join(QLatin1Char('\n'));
246
247
    return noTypeinfoError(libraryPath) + QLatin1String("\n\n") +
            PluginDumper::tr("Automatic type dump of QML module failed.\n"
248
249
                             "First 10 lines or errors:\n"
                             "\n"
250
                             "%1"
251
252
                             "\n"
                             "Check 'General Messages' output pane for details."
253
                             ).arg(firstLines);
254
255
}

256
257
static void printParseWarnings(const QString &libraryPath, const QString &warning)
{
258
    ModelManagerInterface::writeWarning(
Leena Miettinen's avatar
Leena Miettinen committed
259
                PluginDumper::tr("Warnings while parsing QML type information of %1:\n"
260
                                 "%2").arg(libraryPath, warning));
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
288
289
290
291
292
293
294
295
296
static QString qmlPluginDumpErrorMessage(QProcess *process)
{
    QString errorMessage;
    const QString binary = QDir::toNativeSeparators(process->program());
    switch (process->error()) {
    case QProcess::FailedToStart:
        errorMessage = PluginDumper::tr("\"%1\" failed to start: %2").arg(binary, process->errorString());
        break;
    case QProcess::Crashed:
        errorMessage = PluginDumper::tr("\"%1\" crashed.").arg(binary);
        break;
    case QProcess::Timedout:
        errorMessage = PluginDumper::tr("\"%1\" timed out.").arg(binary);
        break;
    case QProcess::ReadError:
    case QProcess::WriteError:
        errorMessage = PluginDumper::tr("I/O error running \"%1\".").arg(binary);
        break;
    case QProcess::UnknownError:
        if (process->exitCode())
            errorMessage = PluginDumper::tr("\"%1\" returned exit code %2.").arg(binary).arg(process->exitCode());
        break;
    }
    errorMessage += QLatin1Char('\n') + PluginDumper::tr("Arguments: %1").arg(process->arguments().join(QLatin1Char(' ')));
    if (process->error() != QProcess::FailedToStart) {
        const QString stdErr = QString::fromLocal8Bit(process->readAllStandardError());
        if (!stdErr.isEmpty()) {
            errorMessage += QLatin1Char('\n');
            errorMessage += stdErr;
        }
    }
    return errorMessage;
}

297
298
299
300
301
302
303
304
void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
{
    QProcess *process = qobject_cast<QProcess *>(sender());
    if (!process)
        return;
    process->deleteLater();

    const QString libraryPath = m_runningQmldumps.take(process);
305
306
    if (libraryPath.isEmpty())
        return;
307
308
309
310
    const Snapshot snapshot = m_modelManager->snapshot();
    LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);

    if (exitCode != 0) {
311
        const QString errorMessages = qmlPluginDumpErrorMessage(process);
312
        ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
313
        libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
314
315
316
    }

    const QByteArray output = process->readAllStandardOutput();
317
    QString error;
318
    QString warning;
319
320
    CppQmlTypesLoader::BuiltinObjects objectsList;
    QList<ModuleApiInfo> moduleApis;
321
    CppQmlTypesLoader::parseQmlTypeDescriptions(output, &objectsList, &moduleApis, &error, &warning,
322
                                                QLatin1String("<dump of ") + libraryPath + QLatin1Char('>'));
323
324
    if (exitCode == 0) {
        if (!error.isEmpty()) {
325
326
            libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
                                                qmldumpErrorMessage(libraryPath, error));
327
            printParseWarnings(libraryPath, libraryInfo.pluginTypeInfoError());
328
        } else {
329
330
            libraryInfo.setMetaObjects(objectsList.values());
            libraryInfo.setModuleApis(moduleApis);
331
332
            libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
        }
333

334
335
        if (!warning.isEmpty())
            printParseWarnings(libraryPath, warning);
336
    }
337
    libraryInfo.updateFingerprint();
338

339
    m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
340
341
342
343
344
345
346
347
348
349
}

void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
{
    QProcess *process = qobject_cast<QProcess *>(sender());
    if (!process)
        return;
    process->deleteLater();

    const QString libraryPath = m_runningQmldumps.take(process);
350
351
    if (libraryPath.isEmpty())
        return;
352

353
    const QString errorMessages = qmlPluginDumpErrorMessage(process);
354
    ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
355
356
357
    if (!libraryPath.isEmpty()) {
        const Snapshot snapshot = m_modelManager->snapshot();
        LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
358
        libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
359
        libraryInfo.updateFingerprint();
360
361
362
363
364
365
366
367
368
369
370
371
372
373
        m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
    }
}

void PluginDumper::pluginChanged(const QString &pluginLibrary)
{
    const int pluginIndex = m_libraryToPluginIndex.value(pluginLibrary, -1);
    if (pluginIndex == -1)
        return;

    const Plugin &plugin = m_plugins.at(pluginIndex);
    dump(plugin);
}

374
void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
375
376
377
                                    const QString &libraryPath,
                                    QmlJS::LibraryInfo libraryInfo)
{
378
379
380
    QStringList errors;
    QStringList warnings;
    QList<FakeMetaObject::ConstPtr> objects;
381
    QList<ModuleApiInfo> moduleApis;
382
383
384
385
386
387
388

    foreach (const QString &qmltypesFilePath, qmltypesFilePaths) {
        Utils::FileReader reader;
        if (!reader.fetch(qmltypesFilePath, QFile::Text)) {
            errors += reader.errorString();
            continue;
        }
389

390
391
        QString error;
        QString warning;
392
393
        CppQmlTypesLoader::BuiltinObjects newObjects;
        QList<ModuleApiInfo> newModuleApis;
394
        CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &newObjects, &newModuleApis, &error, &warning, qmltypesFilePath);
395
        if (!error.isEmpty()) {
396
            errors += tr("Failed to parse \"%1\".\nError: %2").arg(qmltypesFilePath, error);
397
398
399
400
        } else {
            objects += newObjects.values();
            moduleApis += newModuleApis;
        }
401
402
403
        if (!warning.isEmpty())
            warnings += warning;
    }
404

405
    libraryInfo.setMetaObjects(objects);
406
    libraryInfo.setModuleApis(moduleApis);
407
    if (errors.isEmpty()) {
408
409
        libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
    } else {
hjk's avatar
hjk committed
410
        printParseWarnings(libraryPath, errors.join(QLatin1Char('\n')));
411
        errors.prepend(tr("Errors while reading typeinfo files:"));
hjk's avatar
hjk committed
412
        libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1Char('\n')));
413
    }
414
415
416

    if (!warnings.isEmpty())
        printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
417

418
    libraryInfo.updateFingerprint();
419
420
421
    m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
}

422
423
void PluginDumper::dump(const Plugin &plugin)
{
Fawzi Mohamed's avatar
Fawzi Mohamed committed
424
425
426
427
    ModelManagerInterface::ProjectInfo info = m_modelManager->defaultProjectInfo();
    const Snapshot snapshot = m_modelManager->snapshot();
    LibraryInfo libraryInfo = snapshot.libraryInfo(plugin.qmldirPath);

428
429
    // if there are type infos, don't dump!
    if (!plugin.typeInfoPaths.isEmpty()) {
430
431
432
        if (!libraryInfo.isValid())
            return;

433
        loadQmltypesFile(plugin.typeInfoPaths, plugin.qmldirPath, libraryInfo);
434
435
436
        return;
    }

437
    if (!info.tryQmlDump || info.qmlDumpPath.isEmpty()) {
438
439
440
        if (!libraryInfo.isValid())
            return;

441
442
443
444
445
        QString errorMessage;
        if (!info.tryQmlDump) {
            errorMessage = noTypeinfoError(plugin.qmldirPath);
        } else {
            errorMessage = qmldumpErrorMessage(plugin.qmldirPath,
446
                    tr("Could not locate the helper application for dumping type information from C++ plugins.\n"
Friedemann Kleint's avatar
Friedemann Kleint committed
447
                       "Please build the qmldump application on the Qt version options page."));
448
449
450
        }

        libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, errorMessage);
451
        libraryInfo.updateFingerprint();
452
        m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo);
453
        return;
454
    }
455
456
457
458
459
460

    QProcess *process = new QProcess(this);
    process->setEnvironment(info.qmlDumpEnvironment.toStringList());
    connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int)));
    connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(qmlPluginTypeDumpError(QProcess::ProcessError)));
    QStringList args;
461
462
463
    if (plugin.importUri.isEmpty())
        return; // dumping with --path always fails
    if (info.qmlDumpHasRelocatableFlag)
464
        args << QLatin1String("-nonrelocatable");
465
466
467
    args << plugin.importUri;
    args << plugin.importVersion;
    args << plugin.importPath;
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
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
521
522
523
524
525
526
    process->start(info.qmlDumpPath, args);
    m_runningQmldumps.insert(process, plugin.qmldirPath);
}

/*!
  Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
  The \a prefix must contain the dot.

  \a qmldirPath is the location of the qmldir file.

  Adapted from QDeclarativeImportDatabase::resolvePlugin.
*/
QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
                                    const QString &baseName, const QStringList &suffixes,
                                    const QString &prefix)
{
    QStringList searchPaths;
    searchPaths.append(QLatin1String("."));

    bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
    if (!qmldirPluginPathIsRelative)
        searchPaths.prepend(qmldirPluginPath);

    foreach (const QString &pluginPath, searchPaths) {

        QString resolvedPath;

        if (pluginPath == QLatin1String(".")) {
            if (qmldirPluginPathIsRelative)
                resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
            else
                resolvedPath = qmldirPath.absolutePath();
        } else {
            resolvedPath = pluginPath;
        }

        QDir dir(resolvedPath);
        foreach (const QString &suffix, suffixes) {
            QString pluginFileName = prefix;

            pluginFileName += baseName;
            pluginFileName += suffix;

            QFileInfo fileInfo(dir, pluginFileName);

            if (fileInfo.exists())
                return fileInfo.absoluteFilePath();
        }
    }

    return QString();
}

/*!
  Returns the result of the merge of \a baseName with \a dir and the platform suffix.

  Adapted from QDeclarativeImportDatabase::resolvePlugin.

  \table
527
528
529
530
531
532
  \header \li Platform \li Valid suffixes
  \row \li Windows     \li \c .dll
  \row \li Unix/Linux  \li \c .so
  \row \li AIX  \li \c .a
  \row \li HP-UX       \li \c .sl, \c .so (HP-UXi)
  \row \li Mac OS X    \li \c .dylib, \c .bundle, \c .so
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
  \endtable

  Version number on unix are ignored.
*/
QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
                                    const QString &baseName)
{
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
    return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
                         QStringList()
                         << QLatin1String("d.dll") // try a qmake-style debug build first
                         << QLatin1String(".dll"));
#elif defined(Q_OS_DARWIN)
    return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
                         QStringList()
                         << QLatin1String("_debug.dylib") // try a qmake-style debug build first
                         << QLatin1String(".dylib")
                         << QLatin1String(".so")
                         << QLatin1String(".bundle"),
                         QLatin1String("lib"));
#else  // Generic Unix
    QStringList validSuffixList;

#  if defined(Q_OS_HPUX)
/*
    See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
    "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
    the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
 */
    validSuffixList << QLatin1String(".sl");
#   if defined __ia64
    validSuffixList << QLatin1String(".so");
#   endif
#  elif defined(Q_OS_AIX)
    validSuffixList << QLatin1String(".a") << QLatin1String(".so");
#  elif defined(Q_OS_UNIX)
    validSuffixList << QLatin1String(".so");
#  endif

    // Examples of valid library names:
    //  libfoo.so

    return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
#endif
}