main.cpp 24.4 KB
Newer Older
Kai Koehne's avatar
Kai Koehne committed
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
Kai Koehne's avatar
Kai Koehne committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
Kai Koehne's avatar
Kai Koehne committed
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
Kai Koehne's avatar
Kai Koehne committed
18
19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
Kai Koehne's avatar
Kai Koehne committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
Kai Koehne's avatar
Kai Koehne committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
Kai Koehne's avatar
Kai Koehne committed
30
31
32
**
**************************************************************************/

33
34
35
// WARNING: This code is shared with the qmlplugindump tool code in Qt.
//          Modifications to this file need to be applied there.

36
37
38
39
#include <QtDeclarative>
#include <private/qdeclarativemetatype_p.h>
#include <private/qdeclarativeopenmetaobject_p.h>
#include <QDeclarativeView>
40

41
#include <QApplication>
42

43
44
45
46
47
48
#include <QSet>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>
#include <private/qobject_p.h>
#include <private/qmetaobject_p.h>
49
50
51

#include <iostream>

52
53
#include "qmlstreamwriter.h"

54
#ifdef QT_SIMULATOR
55
#include <private/qsimulatorconnection_p.h>
56
#endif
57

58
59
60
#ifdef Q_OS_UNIX
#include <signal.h>
#endif
61

62
QString pluginImportPath;
Christian Kamm's avatar
Christian Kamm committed
63
bool verbose = false;
64

65
66
67
QString currentProperty;
QString inObjectInstantiation;

68
void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas)
69
70
71
72
{
    if (! meta || metas->contains(meta))
        return;

73
    // dynamic meta objects break things badly, so just ignore them
74
75
76
77
    const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
    if (!(mop->flags & DynamicMetaObject))
        metas->insert(meta);

78
    collectReachableMetaObjects(meta->superClass(), metas);
79
80
}

81
void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
82
83
84
85
86
{
    if (! object)
        return;

    const QMetaObject *meta = object->metaObject();
Christian Kamm's avatar
Christian Kamm committed
87
88
    if (verbose)
        qDebug() << "Processing object" << meta->className();
89
    collectReachableMetaObjects(meta, metas);
90
91
92
93

    for (int index = 0; index < meta->propertyCount(); ++index) {
        QMetaProperty prop = meta->property(index);
        if (QDeclarativeMetaType::isQObject(prop.userType())) {
Christian Kamm's avatar
Christian Kamm committed
94
95
            if (verbose)
                qDebug() << "  Processing property" << prop.name();
96
            currentProperty = QString("%1::%2").arg(meta->className(), prop.name());
97
98
99

            // if the property was not initialized during construction,
            // accessing a member of oo is going to cause a segmentation fault
100
            QObject *oo = QDeclarativeMetaType::toQObject(prop.read(object));
101
            if (oo && !metas->contains(oo->metaObject()))
102
                collectReachableMetaObjects(oo, metas);
103
            currentProperty.clear();
104
105
106
107
        }
    }
}

108
void collectReachableMetaObjects(const QDeclarativeType *ty, QSet<const QMetaObject *> *metas)
109
{
110
    collectReachableMetaObjects(ty->metaObject(), metas);
111
112
    if (ty->attachedPropertiesType())
        collectReachableMetaObjects(ty->attachedPropertiesType(), metas);
113
114
}

115
116
117
118
/* We want to add the MetaObject for 'Qt' to the list, this is a
   simple way to access it.
*/
class FriendlyQObject: public QObject
119
{
120
121
122
public:
    static const QMetaObject *qtMeta() { return &staticQtMetaObject; }
};
123

124
125
126
127
128
/* When we dump a QMetaObject, we want to list all the types it is exported as.
   To do this, we need to find the QDeclarativeTypes associated with this
   QMetaObject.
*/
static QHash<QByteArray, QSet<const QDeclarativeType *> > qmlTypesByCppName;
129

130
static QHash<QByteArray, QByteArray> cppToId;
131

132
133
/* Takes a C++ type name, such as Qt::LayoutDirection or QString and
   maps it to how it should appear in the description file.
134

135
136
137
138
139
140
141
   These names need to be unique globally, so we don't change the C++ symbol's
   name much. It is mostly used to for explicit translations such as
   QString->string and translations for extended QML objects.
*/
QByteArray convertToId(const QByteArray &cppName)
{
    return cppToId.value(cppName, cppName);
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
QByteArray convertToId(const QMetaObject *mo)
{
    QByteArray className(mo->className());
    if (!className.isEmpty())
        return convertToId(className);

    // likely a metaobject generated for an extended qml object
    if (mo->superClass()) {
        className = convertToId(mo->superClass());
        className.append("_extended");
        return className;
    }

    static QHash<const QMetaObject *, QByteArray> generatedNames;
    className = generatedNames.value(mo);
    if (!className.isEmpty())
        return className;

    qWarning() << "Found a QMetaObject without a className, generating a random name";
    className = QByteArray("error-unknown-name-");
    className.append(QByteArray::number(generatedNames.size()));
    generatedNames.insert(mo, className);
    return className;
}

169
QSet<const QMetaObject *> collectReachableMetaObjects(const QList<QDeclarativeType *> &skip = QList<QDeclarativeType *>())
170
{
171
172
173
174
175
176
177
178
179
180
    QSet<const QMetaObject *> metas;
    metas.insert(FriendlyQObject::qtMeta());

    QHash<QByteArray, QSet<QByteArray> > extensions;
    foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
        qmlTypesByCppName[ty->metaObject()->className()].insert(ty);
        if (ty->isExtendedType()) {
            extensions[ty->typeName()].insert(ty->metaObject()->className());
        }
        collectReachableMetaObjects(ty, &metas);
181
182
    }

183
184
185
186
187
188
189
190
    // Adjust exports of the base object if there are extensions.
    // For each export of a base object there can be a single extension object overriding it.
    // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export
    //          of QGraphicsWidget.
    foreach (const QByteArray &baseCpp, extensions.keys()) {
        QSet<const QDeclarativeType *> baseExports = qmlTypesByCppName.value(baseCpp);

        const QSet<QByteArray> extensionCppNames = extensions.value(baseCpp);
191
        foreach (const QByteArray &extensionCppName, extensionCppNames) {
192
193
194
195
196
197
198
199
            const QSet<const QDeclarativeType *> extensionExports = qmlTypesByCppName.value(extensionCppName);

            // remove extension exports from base imports
            // unfortunately the QDeclarativeType pointers don't match, so can't use QSet::substract
            QSet<const QDeclarativeType *> newBaseExports;
            foreach (const QDeclarativeType *baseExport, baseExports) {
                bool match = false;
                foreach (const QDeclarativeType *extensionExport, extensionExports) {
200
                    if (baseExport->qmlTypeName() == extensionExport->qmlTypeName()
201
202
203
204
205
206
207
208
                            && baseExport->majorVersion() == extensionExport->majorVersion()
                            && baseExport->minorVersion() == extensionExport->minorVersion()) {
                        match = true;
                        break;
                    }
                }
                if (!match)
                    newBaseExports.insert(baseExport);
209
            }
210
            baseExports = newBaseExports;
211
        }
212
        qmlTypesByCppName[baseCpp] = baseExports;
213
214
    }

215
216
    // find even more QMetaObjects by instantiating QML types and running
    // over the instances
217
218
219
    foreach (QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
        if (skip.contains(ty))
            continue;
220
221
        if (ty->isExtendedType())
            continue;
Christian Kamm's avatar
Christian Kamm committed
222
223
224
225
        if (!ty->isCreatable())
            continue;
        if (ty->typeName() == "QDeclarativeComponent")
            continue;
226

227
228
        QByteArray tyName = ty->qmlTypeName();
        tyName = tyName.mid(tyName.lastIndexOf('/') + 1);
Christian Kamm's avatar
Christian Kamm committed
229
230
        if (tyName.isEmpty())
            continue;
231

232
        inObjectInstantiation = tyName;
233
        QObject *object = ty->create();
234
235
        inObjectInstantiation.clear();

236
237
238
        if (object)
            collectReachableMetaObjects(object, &metas);
        else
239
            qWarning() << "Could not create" << tyName;
240
    }
241

242
243
    return metas;
}
244
245


246
247
248
249
class Dumper
{
    QmlStreamWriter *qml;
    QString relocatableModuleUri;
250

251
252
public:
    Dumper(QmlStreamWriter *qml) : qml(qml) {}
253

254
255
256
257
    void setRelocatableModuleUri(const QString &uri)
    {
        relocatableModuleUri = uri;
    }
258

259
260
261
    void dump(const QMetaObject *meta)
    {
        qml->writeStartObject("Component");
262

263
        QByteArray id = convertToId(meta);
264
        qml->writeScriptBinding(QLatin1String("name"), enquote(id));
265

266
267
268
269
270
271
272
        for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
            QMetaClassInfo classInfo = meta->classInfo(index);
            if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
                qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
                break;
            }
        }
273

274
        if (meta->superClass())
275
            qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
276
277
278
279
280
281
282

        QSet<const QDeclarativeType *> qmlTypes = qmlTypesByCppName.value(meta->className());
        if (!qmlTypes.isEmpty()) {
            QStringList exports;

            foreach (const QDeclarativeType *qmlTy, qmlTypes) {
                QString qmlTyName = qmlTy->qmlTypeName();
283
284
285
                // some qmltype names are missing the actual names, ignore that import
                if (qmlTyName.endsWith('/'))
                    continue;
286
                if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
287
288
                    qmlTyName.remove(0, relocatableModuleUri.size() + 1);
                }
289
290
291
                if (qmlTyName.startsWith("./")) {
                    qmlTyName.remove(0, 2);
                }
292
293
294
295
296
                exports += enquote(QString("%1 %2.%3").arg(
                                       qmlTyName,
                                       QString::number(qmlTy->majorVersion()),
                                       QString::number(qmlTy->minorVersion())));
            }
297

298
299
300
            // ensure exports are sorted and don't change order when the plugin is dumped again
            exports.removeDuplicates();
            qSort(exports);
301

302
            qml->writeArrayBinding(QLatin1String("exports"), exports);
303
304
305

            if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
                qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
306
                                            convertToId(attachedType)));
307
            }
308
        }
309

310
311
        for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
            dump(meta->enumerator(index));
312

313
314
        for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index)
            dump(meta->property(index));
315

316
317
        for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
            dump(meta->method(index));
318

319
        qml->writeEndObject();
320
321
    }

322
323
324
325
326
327
328
    void writeEasingCurve()
    {
        qml->writeStartObject("Component");
        qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve")));
        qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QDeclarativeEasingValueType")));
        qml->writeEndObject();
    }
329

330
331
332
333
334
private:
    static QString enquote(const QString &string)
    {
        return QString("\"%1\"").arg(string);
    }
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    /* Removes pointer and list annotations from a type name, returning
       what was removed in isList and isPointer
    */
    static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer)
    {
        static QByteArray declListPrefix = "QDeclarativeListProperty<";

        if (typeName->endsWith('*')) {
            *isPointer = true;
            typeName->truncate(typeName->length() - 1);
            removePointerAndList(typeName, isList, isPointer);
        } else if (typeName->startsWith(declListPrefix)) {
            *isList = true;
            typeName->truncate(typeName->length() - 1); // get rid of the suffix '>'
            *typeName = typeName->mid(declListPrefix.size());
            removePointerAndList(typeName, isList, isPointer);
        }
353

354
355
        *typeName = convertToId(*typeName);
    }
356

357
358
359
360
361
362
363
364
365
366
367
368
369
    void writeTypeProperties(QByteArray typeName, bool isWritable)
    {
        bool isList = false, isPointer = false;
        removePointerAndList(&typeName, &isList, &isPointer);

        qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
        if (isList)
            qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true"));
        if (!isWritable)
            qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
        if (isPointer)
            qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true"));
    }
370

371
372
373
374
375
    void dump(const QMetaProperty &prop)
    {
        qml->writeStartObject("Property");

        qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
376
377
378
379
#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
        if (int revision = prop.revision())
            qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
#endif
380
381
382
        writeTypeProperties(prop.typeName(), prop.isWritable());

        qml->writeEndObject();
383
384
    }

385
386
387
388
389
390
391
    void dump(const QMetaMethod &meth)
    {
        if (meth.methodType() == QMetaMethod::Signal) {
            if (meth.access() != QMetaMethod::Protected)
                return; // nothing to do.
        } else if (meth.access() != QMetaMethod::Public) {
            return; // nothing to do.
392
393
        }

394
395
396
397
398
399
400
401
402
403
404
        QByteArray name = meth.signature();
        int lparenIndex = name.indexOf('(');
        if (lparenIndex == -1) {
            return; // invalid signature
        }
        name = name.left(lparenIndex);

        if (meth.methodType() == QMetaMethod::Signal)
            qml->writeStartObject(QLatin1String("Signal"));
        else
            qml->writeStartObject(QLatin1String("Method"));
405

406
        qml->writeScriptBinding(QLatin1String("name"), enquote(name));
407

408
409
410
411
412
#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
        if (int revision = meth.revision())
            qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
#endif

413
414
415
        const QString typeName = convertToId(meth.typeName());
        if (! typeName.isEmpty())
            qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
416

417
418
        for (int i = 0; i < meth.parameterTypes().size(); ++i) {
            QByteArray argName = meth.parameterNames().at(i);
419

420
421
422
423
424
425
426
427
428
429
430
            qml->writeStartObject(QLatin1String("Parameter"));
            if (! argName.isEmpty())
                qml->writeScriptBinding(QLatin1String("name"), enquote(argName));
            writeTypeProperties(meth.parameterTypes().at(i), true);
            qml->writeEndObject();
        }

        qml->writeEndObject();
    }

    void dump(const QMetaEnum &e)
431
    {
432
433
434
435
436
437
438
439
440
441
        qml->writeStartObject(QLatin1String("Enum"));
        qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));

        QList<QPair<QString, QString> > namesValues;
        for (int index = 0; index < e.keyCount(); ++index) {
            namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
        }

        qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues);
        qml->writeEndObject();
442
    }
443
};
444
445


446
447
448
449
450
451
enum ExitCode {
    EXIT_INVALIDARGUMENTS = 1,
    EXIT_SEGV = 2,
    EXIT_IMPORTERROR = 3
};

452
453
#ifdef Q_OS_UNIX
void sigSegvHandler(int) {
454
    fprintf(stderr, "Error: SEGV\n");
455
456
    if (!currentProperty.isEmpty())
        fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData());
457
458
    if (!inObjectInstantiation.isEmpty())
        fprintf(stderr, "While instantiating the object '%s'.\n", inObjectInstantiation.toLatin1().constData());
459
    exit(EXIT_SEGV);
460
461
462
}
#endif

463
464
465
void printUsage(const QString &appName)
{
    qWarning() << qPrintable(QString(
Christian Kamm's avatar
Christian Kamm committed
466
467
468
                                 "Usage: %1 [-v] [-notrelocatable] module.uri version [module/import/path]\n"
                                 "       %1 [-v] -path path/to/qmldir/directory [version]\n"
                                 "       %1 [-v] -builtins\n"
469
470
471
472
                                 "Example: %1 Qt.labs.particles 4.7 /home/user/dev/qt-install/imports").arg(
                                 appName));
}

473
474
int main(int argc, char *argv[])
{
475
476
477
#ifdef Q_OS_UNIX
    // qmldump may crash, but we don't want any crash handlers to pop up
    // therefore we intercept the segfault and just exit() ourselves
Christian Kamm's avatar
Christian Kamm committed
478
    struct sigaction sigAction;
479

Christian Kamm's avatar
Christian Kamm committed
480
481
482
    sigemptyset(&sigAction.sa_mask);
    sigAction.sa_handler = &sigSegvHandler;
    sigAction.sa_flags   = 0;
483

Christian Kamm's avatar
Christian Kamm committed
484
    sigaction(SIGSEGV, &sigAction, 0);
485
486
#endif

487
#ifdef QT_SIMULATOR
488
    // Running this application would bring up the Qt Simulator (since it links QtGui), avoid that!
489
490
    QtSimulatorPrivate::SimulatorConnection::createStubInstance();
#endif
491
    QApplication app(argc, argv);
492
493
    const QStringList args = app.arguments();
    const QString appName = QFileInfo(app.applicationFilePath()).baseName();
Christian Kamm's avatar
Christian Kamm committed
494
    if (args.size() < 2) {
495
        printUsage(appName);
496
        return EXIT_INVALIDARGUMENTS;
497
498
    }

499
500
    QString pluginImportUri;
    QString pluginImportVersion;
501
    bool relocatable = true;
Christian Kamm's avatar
Christian Kamm committed
502
503
504
    enum Action { Uri, Path, Builtins };
    Action action = Uri;
    {
505
506
        QStringList positionalArgs;
        foreach (const QString &arg, args) {
507
            if (!arg.startsWith(QLatin1Char('-'))) {
508
509
510
511
                positionalArgs.append(arg);
                continue;
            }

512
513
            if (arg == QLatin1String("--notrelocatable")
                    || arg == QLatin1String("-notrelocatable")) {
514
                relocatable = false;
515
516
            } else if (arg == QLatin1String("--path")
                       || arg == QLatin1String("-path")) {
Christian Kamm's avatar
Christian Kamm committed
517
518
519
520
521
522
                action = Path;
            } else if (arg == QLatin1String("--builtins")
                       || arg == QLatin1String("-builtins")) {
                action = Builtins;
            } else if (arg == QLatin1String("-v")) {
                verbose = true;
523
524
525
526
527
528
            } else {
                qWarning() << "Invalid argument: " << arg;
                return EXIT_INVALIDARGUMENTS;
            }
        }

Christian Kamm's avatar
Christian Kamm committed
529
        if (action == Uri) {
530
531
532
533
534
535
536
537
            if (positionalArgs.size() != 3 && positionalArgs.size() != 4) {
                qWarning() << "Incorrect number of positional arguments";
                return EXIT_INVALIDARGUMENTS;
            }
            pluginImportUri = positionalArgs[1];
            pluginImportVersion = positionalArgs[2];
            if (positionalArgs.size() >= 4)
                pluginImportPath = positionalArgs[3];
Christian Kamm's avatar
Christian Kamm committed
538
        } else if (action == Path) {
539
540
541
542
            if (positionalArgs.size() != 2 && positionalArgs.size() != 3) {
                qWarning() << "Incorrect number of positional arguments";
                return EXIT_INVALIDARGUMENTS;
            }
543
            pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]);
544
545
            if (positionalArgs.size() == 3)
                pluginImportVersion = positionalArgs[2];
Christian Kamm's avatar
Christian Kamm committed
546
547
548
549
550
        } else if (action == Builtins) {
            if (positionalArgs.size() != 1) {
                qWarning() << "Incorrect number of positional arguments";
                return EXIT_INVALIDARGUMENTS;
            }
551
        }
552
553
    }

554
555
    QDeclarativeView view;
    QDeclarativeEngine *engine = view.engine();
556
557
558
559
560
    if (!pluginImportPath.isEmpty()) {
        QDir cur = QDir::current();
        cur.cd(pluginImportPath);
        pluginImportPath = cur.absolutePath();
        QDir::setCurrent(pluginImportPath);
561
        engine->addImportPath(pluginImportPath);
562
    }
563

564
    // find all QMetaObjects reachable from the builtin module
565
566
    QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects();
    QList<QDeclarativeType *> defaultTypes = QDeclarativeMetaType::qmlTypes();
567

568
    // this will hold the meta objects we want to dump information of
569
570
    QSet<const QMetaObject *> metas;

Christian Kamm's avatar
Christian Kamm committed
571
    if (action == Builtins) {
572
573
        metas = defaultReachable;
    } else {
574
575
576
577
578
579
580
581
582
583
584
585
586
587
        // find a valid QtQuick import
        QByteArray importCode;
        QDeclarativeType *qtObjectType = QDeclarativeMetaType::qmlType(&QObject::staticMetaObject);
        if (!qtObjectType) {
            qWarning() << "Could not find QtObject type";
            importCode = QByteArray("import QtQuick 1.0\n");
        } else {
            QByteArray module = qtObjectType->qmlTypeName();
            module = module.mid(0, module.lastIndexOf('/'));
            importCode = QString("import %1 %2.%3\n").arg(module,
                                                          QString::number(qtObjectType->majorVersion()),
                                                          QString::number(qtObjectType->minorVersion())).toUtf8();
        }

588
        // find all QMetaObjects reachable when the specified module is imported
Christian Kamm's avatar
Christian Kamm committed
589
        if (action != Path) {
590
591
592
            importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toAscii();
        } else {
            // pluginImportVersion can be empty
593
            importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toAscii();
594
        }
595
596
597
598
599
600
601
602

        // create a component with these imports to make sure the imports are valid
        // and to populate the declarative meta type system
        {
            QByteArray code = importCode;
            code += "QtObject {}";
            QDeclarativeComponent c(engine);

603
            c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml"));
604
605
606
607
608
609
            c.create();
            if (!c.errors().isEmpty()) {
                foreach (const QDeclarativeError &error, c.errors())
                    qWarning() << error.toString();
                return EXIT_IMPORTERROR;
            }
610
        }
611

612
        QSet<const QMetaObject *> candidates = collectReachableMetaObjects(defaultTypes);
613
614
615
616
617
618
619
620
621
622
623
        candidates.subtract(defaultReachable);

        // Also eliminate meta objects with the same classname.
        // This is required because extended objects seem not to share
        // a single meta object instance.
        QSet<QByteArray> defaultReachableNames;
        foreach (const QMetaObject *mo, defaultReachable)
            defaultReachableNames.insert(QByteArray(mo->className()));
        foreach (const QMetaObject *mo, candidates) {
            if (!defaultReachableNames.contains(mo->className()))
                metas.insert(mo);
624
625
626
        }
    }

627
628
629
    // setup static rewrites of type names
    cppToId.insert("QString", "string");
    cppToId.insert("QDeclarativeEasingValueType::Type", "Type");
630

631
    // start dumping data
632
    QByteArray bytes;
633
    QmlStreamWriter qml(&bytes);
634

635
    qml.writeStartDocument();
636
    qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 1);
637
638
639
640
641
    qml.write("\n"
              "// This file describes the plugin-supplied types contained in the library.\n"
              "// It is used for QML tooling purposes only.\n"
              "\n");
    qml.writeStartObject("Module");
642

643
    // put the metaobjects into a map so they are always dumped in the same order
644
    QMap<QString, const QMetaObject *> nameToMeta;
645
    foreach (const QMetaObject *meta, metas)
646
        nameToMeta.insert(convertToId(meta), meta);
647
648

    Dumper dumper(&qml);
649
650
    if (relocatable)
        dumper.setRelocatableModuleUri(pluginImportUri);
651
    foreach (const QMetaObject *meta, nameToMeta) {
652
        dumper.dump(meta);
653
    }
654

655
656
657
658
    // define QEasingCurve as an extension of QDeclarativeEasingValueType, this way
    // properties using the QEasingCurve type get useful type information.
    if (pluginImportUri.isEmpty())
        dumper.writeEasingCurve();
659

660
661
    qml.writeEndObject();
    qml.writeEndDocument();
662

663
    std::cout << bytes.constData() << std::flush;
664

665
    // workaround to avoid crashes on exit
666
667
668
669
670
671
672
673
    QTimer timer;
    timer.setSingleShot(true);
    timer.setInterval(0);
    QObject::connect(&timer, SIGNAL(timeout()), &app, SLOT(quit()));
    timer.start();

    return app.exec();
}