169c0d7b19ec63e33b148557227d241471937f13
[profile/ivi/qtdeclarative.git] / tools / qmlplugindump / main.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtDeclarative/qdeclarativeengine.h>
43 #include <QtDeclarative/private/qdeclarativemetatype_p.h>
44 #include <QtDeclarative/private/qdeclarativeopenmetaobject_p.h>
45 #include <QtQuick/private/qquickevents_p_p.h>
46 #include <QtQuick/private/qquickpincharea_p.h>
47
48 #ifdef QT_WIDGETS_LIB
49 #include <QtWidgets/QApplication>
50 #endif
51
52 #include <QtGui/QGuiApplication>
53 #include <QtCore/QDir>
54 #include <QtCore/QFileInfo>
55 #include <QtCore/QSet>
56 #include <QtCore/QStringList>
57 #include <QtCore/QTimer>
58 #include <QtCore/QMetaObject>
59 #include <QtCore/QMetaProperty>
60 #include <QtCore/QDebug>
61 #include <QtCore/private/qobject_p.h>
62 #include <QtCore/private/qmetaobject_p.h>
63
64 #include <iostream>
65
66 #include "qmlstreamwriter.h"
67
68 #ifdef QT_SIMULATOR
69 #include <QtGui/private/qsimulatorconnection_p.h>
70 #endif
71
72 #ifdef Q_OS_UNIX
73 #include <signal.h>
74 #endif
75
76 QString pluginImportPath;
77 bool verbose = false;
78
79 QString currentProperty;
80 QString inObjectInstantiation;
81
82 void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, bool extended = false)
83 {
84     if (! meta || metas->contains(meta))
85         return;
86
87     // dynamic meta objects can break things badly (like QDeclarative1VisualDataModelParts)
88     // but extended types are usually fine (like QDeclarative1GraphicsWidget)
89     const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
90     if (extended || !(mop->flags & DynamicMetaObject))
91         metas->insert(meta);
92
93     collectReachableMetaObjects(meta->superClass(), metas);
94 }
95
96 void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
97 {
98     if (! object)
99         return;
100
101     const QMetaObject *meta = object->metaObject();
102     if (verbose)
103         qDebug() << "Processing object" << meta->className();
104     collectReachableMetaObjects(meta, metas);
105
106     for (int index = 0; index < meta->propertyCount(); ++index) {
107         QMetaProperty prop = meta->property(index);
108         if (QDeclarativeMetaType::isQObject(prop.userType())) {
109             if (verbose)
110                 qDebug() << "  Processing property" << prop.name();
111             currentProperty = QString("%1::%2").arg(meta->className(), prop.name());
112
113             // if the property was not initialized during construction,
114             // accessing a member of oo is going to cause a segmentation fault
115             QObject *oo = QDeclarativeMetaType::toQObject(prop.read(object));
116             if (oo && !metas->contains(oo->metaObject()))
117                 collectReachableMetaObjects(oo, metas);
118             currentProperty.clear();
119         }
120     }
121 }
122
123 void collectReachableMetaObjects(const QDeclarativeType *ty, QSet<const QMetaObject *> *metas)
124 {
125     collectReachableMetaObjects(ty->metaObject(), metas, ty->isExtendedType());
126     if (ty->attachedPropertiesType())
127         collectReachableMetaObjects(ty->attachedPropertiesType(), metas);
128 }
129
130 /* We want to add the MetaObject for 'Qt' to the list, this is a
131    simple way to access it.
132 */
133 class FriendlyQObject: public QObject
134 {
135 public:
136     static const QMetaObject *qtMeta() { return &staticQtMetaObject; }
137 };
138
139 /* When we dump a QMetaObject, we want to list all the types it is exported as.
140    To do this, we need to find the QDeclarativeTypes associated with this
141    QMetaObject.
142 */
143 static QHash<QByteArray, QSet<const QDeclarativeType *> > qmlTypesByCppName;
144
145 static QHash<QByteArray, QByteArray> cppToId;
146
147 /* Takes a C++ type name, such as Qt::LayoutDirection or QString and
148    maps it to how it should appear in the description file.
149
150    These names need to be unique globally, so we don't change the C++ symbol's
151    name much. It is mostly used to for explicit translations such as
152    QString->string and translations for extended QML objects.
153 */
154 QByteArray convertToId(const QByteArray &cppName)
155 {
156     return cppToId.value(cppName, cppName);
157 }
158
159 QByteArray convertToId(const QMetaObject *mo)
160 {
161     QByteArray className(mo->className());
162     if (!className.isEmpty())
163         return convertToId(className);
164
165     // likely a metaobject generated for an extended qml object
166     if (mo->superClass()) {
167         className = convertToId(mo->superClass());
168         className.append("_extended");
169         return className;
170     }
171
172     static QHash<const QMetaObject *, QByteArray> generatedNames;
173     className = generatedNames.value(mo);
174     if (!className.isEmpty())
175         return className;
176
177     qWarning() << "Found a QMetaObject without a className, generating a random name";
178     className = QByteArray("error-unknown-name-");
179     className.append(QByteArray::number(generatedNames.size()));
180     generatedNames.insert(mo, className);
181     return className;
182 }
183
184 /* All exported module APIs are collected into this list */
185 class ModuleApi {
186 public:
187     QString uri;
188     int majorVersion;
189     int minorVersion;
190     QByteArray objectId;
191 };
192 QList<ModuleApi> moduleApis;
193
194 QSet<const QMetaObject *> collectReachableMetaObjects(QDeclarativeEngine *engine, const QList<QDeclarativeType *> &skip = QList<QDeclarativeType *>())
195 {
196     QSet<const QMetaObject *> metas;
197     metas.insert(FriendlyQObject::qtMeta());
198
199     QHash<QByteArray, QSet<QByteArray> > extensions;
200     foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
201         qmlTypesByCppName[ty->metaObject()->className()].insert(ty);
202         if (ty->isExtendedType()) {
203             extensions[ty->typeName()].insert(ty->metaObject()->className());
204         }
205         collectReachableMetaObjects(ty, &metas);
206     }
207
208     // Adjust exports of the base object if there are extensions.
209     // For each export of a base object there can be a single extension object overriding it.
210     // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export
211     //          of QGraphicsWidget.
212     foreach (const QByteArray &baseCpp, extensions.keys()) {
213         QSet<const QDeclarativeType *> baseExports = qmlTypesByCppName.value(baseCpp);
214
215         const QSet<QByteArray> extensionCppNames = extensions.value(baseCpp);
216         foreach (const QByteArray &extensionCppName, extensionCppNames) {
217             const QSet<const QDeclarativeType *> extensionExports = qmlTypesByCppName.value(extensionCppName);
218
219             // remove extension exports from base imports
220             // unfortunately the QDeclarativeType pointers don't match, so can't use QSet::substract
221             QSet<const QDeclarativeType *> newBaseExports;
222             foreach (const QDeclarativeType *baseExport, baseExports) {
223                 bool match = false;
224                 foreach (const QDeclarativeType *extensionExport, extensionExports) {
225                     if (baseExport->qmlTypeName() == extensionExport->qmlTypeName()
226                             && baseExport->majorVersion() == extensionExport->majorVersion()
227                             && baseExport->minorVersion() == extensionExport->minorVersion()) {
228                         match = true;
229                         break;
230                     }
231                 }
232                 if (!match)
233                     newBaseExports.insert(baseExport);
234             }
235             baseExports = newBaseExports;
236         }
237         qmlTypesByCppName[baseCpp] = baseExports;
238     }
239
240     // find even more QMetaObjects by instantiating QML types and running
241     // over the instances
242     foreach (QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
243         if (skip.contains(ty))
244             continue;
245         if (ty->isExtendedType())
246             continue;
247         if (!ty->isCreatable())
248             continue;
249         if (ty->typeName() == "QDeclarativeComponent")
250             continue;
251
252         QString tyName = ty->qmlTypeName();
253         tyName = tyName.mid(tyName.lastIndexOf(QLatin1Char('/')) + 1);
254         if (tyName.isEmpty())
255             continue;
256
257         inObjectInstantiation = tyName;
258         QObject *object = ty->create();
259         inObjectInstantiation.clear();
260
261         if (object)
262             collectReachableMetaObjects(object, &metas);
263         else
264             qWarning() << "Could not create" << tyName;
265     }
266
267     // extract exported module api
268     QHashIterator<QString, QList<QDeclarativeMetaType::ModuleApi> > moduleApiIt(QDeclarativeMetaType::moduleApis());
269     while (moduleApiIt.hasNext()) {
270         moduleApiIt.next();
271         foreach (const QDeclarativeMetaType::ModuleApi &api, moduleApiIt.value()) {
272             ModuleApi moduleApi;
273             moduleApi.uri = moduleApiIt.key();
274             moduleApi.majorVersion = api.major;
275             moduleApi.minorVersion = api.minor;
276
277             if (api.qobject) {
278                 if (QObject *object = (*api.qobject)(engine, engine)) {
279                     collectReachableMetaObjects(object, &metas);
280                     moduleApi.objectId = convertToId(object->metaObject()->className());
281                     delete object;
282                 }
283             } else if (api.script) {
284                 qWarning() << "Can't dump the module api in " << moduleApi.uri << ". QJSValue based module API is not supported.";
285 //                QJSValue value = (*api.script)(engine, engine);
286 //                IdToObjectHash jsObjects;
287 //                collectReachableJSObjects(value, &jsObjects, &metas);
288             }
289
290             moduleApis += moduleApi;
291         }
292     }
293
294     return metas;
295 }
296
297
298 class Dumper
299 {
300     QmlStreamWriter *qml;
301     QString relocatableModuleUri;
302
303 public:
304     Dumper(QmlStreamWriter *qml) : qml(qml) {}
305
306     void setRelocatableModuleUri(const QString &uri)
307     {
308         relocatableModuleUri = uri;
309     }
310
311     void dump(const QMetaObject *meta)
312     {
313         qml->writeStartObject("Component");
314
315         QByteArray id = convertToId(meta);
316         qml->writeScriptBinding(QLatin1String("name"), enquote(id));
317
318         for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
319             QMetaClassInfo classInfo = meta->classInfo(index);
320             if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
321                 qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
322                 break;
323             }
324         }
325
326         if (meta->superClass())
327             qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
328
329         QSet<const QDeclarativeType *> qmlTypes = qmlTypesByCppName.value(meta->className());
330         if (!qmlTypes.isEmpty()) {
331             QHash<QString, const QDeclarativeType *> exports;
332
333             foreach (const QDeclarativeType *qmlTy, qmlTypes) {
334                 QString qmlTyName = qmlTy->qmlTypeName();
335                 if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
336                     qmlTyName.remove(0, relocatableModuleUri.size() + 1);
337                 }
338                 if (qmlTyName.startsWith("./")) {
339                     qmlTyName.remove(0, 2);
340                 }
341                 if (qmlTyName.startsWith("/")) {
342                     qmlTyName.remove(0, 1);
343                 }
344                 const QString exportString = enquote(
345                             QString("%1 %2.%3").arg(
346                                 qmlTyName,
347                                 QString::number(qmlTy->majorVersion()),
348                                 QString::number(qmlTy->minorVersion())));
349                 exports.insert(exportString, qmlTy);
350             }
351
352             // ensure exports are sorted and don't change order when the plugin is dumped again
353             QStringList exportStrings = exports.keys();
354             qSort(exportStrings);
355             qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
356
357             // write meta object revisions unless they're all zero
358             QStringList metaObjectRevisions;
359             bool shouldWriteMetaObjectRevisions = false;
360             foreach (const QString &exportString, exportStrings) {
361                 int metaObjectRevision = exports[exportString]->metaObjectRevision();
362                 if (metaObjectRevision != 0)
363                     shouldWriteMetaObjectRevisions = true;
364                 metaObjectRevisions += QString::number(metaObjectRevision);
365             }
366             if (shouldWriteMetaObjectRevisions)
367                 qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
368
369             if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
370                 // Can happen when a type is registered that returns itself as attachedPropertiesType()
371                 // because there is no creatable type to attach to.
372                 if (attachedType != meta) {
373                     qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
374                                                 convertToId(attachedType)));
375                 }
376             }
377         }
378
379         for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
380             dump(meta->enumerator(index));
381
382         QSet<QString> implicitSignals;
383         for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
384             const QMetaProperty &property = meta->property(index);
385             dump(property);
386             implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name())));
387         }
388
389         if (meta == &QObject::staticMetaObject) {
390             // for QObject, hide deleteLater() and onDestroyed
391             for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) {
392                 QMetaMethod method = meta->method(index);
393                 const char *signature(method.signature());
394                 if (signature == QLatin1String("destroyed(QObject*)")
395                         || signature == QLatin1String("destroyed()")
396                         || signature == QLatin1String("deleteLater()"))
397                     continue;
398                 dump(method, implicitSignals);
399             }
400
401             // and add toString(), destroy() and destroy(int)
402             qml->writeStartObject(QLatin1String("Method"));
403             qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("toString")));
404             qml->writeEndObject();
405             qml->writeStartObject(QLatin1String("Method"));
406             qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
407             qml->writeEndObject();
408             qml->writeStartObject(QLatin1String("Method"));
409             qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
410             qml->writeStartObject(QLatin1String("Parameter"));
411             qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("delay")));
412             qml->writeScriptBinding(QLatin1String("type"), enquote(QLatin1String("int")));
413             qml->writeEndObject();
414             qml->writeEndObject();
415         } else {
416             for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
417                 dump(meta->method(index), implicitSignals);
418         }
419
420         qml->writeEndObject();
421     }
422
423     void dump(const ModuleApi &api)
424     {
425         qml->writeStartObject(QLatin1String("ModuleApi"));
426         if (api.uri != relocatableModuleUri)
427             qml->writeScriptBinding(QLatin1String("uri"), enquote(api.uri));
428         qml->writeScriptBinding(QLatin1String("version"), QString("%1.%2").arg(
429                                     QString::number(api.majorVersion),
430                                     QString::number(api.minorVersion)));
431         qml->writeScriptBinding(QLatin1String("name"), enquote(api.objectId));
432         qml->writeEndObject();
433     }
434
435     void writeEasingCurve()
436     {
437         qml->writeStartObject(QLatin1String("Component"));
438         qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve")));
439         qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QDeclarativeEasingValueType")));
440         qml->writeEndObject();
441     }
442
443 private:
444     static QString enquote(const QString &string)
445     {
446         return QString("\"%1\"").arg(string);
447     }
448
449     /* Removes pointer and list annotations from a type name, returning
450        what was removed in isList and isPointer
451     */
452     static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer)
453     {
454         static QByteArray declListPrefix = "QDeclarativeListProperty<";
455
456         if (typeName->endsWith('*')) {
457             *isPointer = true;
458             typeName->truncate(typeName->length() - 1);
459             removePointerAndList(typeName, isList, isPointer);
460         } else if (typeName->startsWith(declListPrefix)) {
461             *isList = true;
462             typeName->truncate(typeName->length() - 1); // get rid of the suffix '>'
463             *typeName = typeName->mid(declListPrefix.size());
464             removePointerAndList(typeName, isList, isPointer);
465         }
466
467         *typeName = convertToId(*typeName);
468     }
469
470     void writeTypeProperties(QByteArray typeName, bool isWritable)
471     {
472         bool isList = false, isPointer = false;
473         removePointerAndList(&typeName, &isList, &isPointer);
474
475         qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
476         if (isList)
477             qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true"));
478         if (!isWritable)
479             qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
480         if (isPointer)
481             qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true"));
482     }
483
484     void dump(const QMetaProperty &prop)
485     {
486         qml->writeStartObject("Property");
487
488         qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
489 #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
490         if (int revision = prop.revision())
491             qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
492 #endif
493         writeTypeProperties(prop.typeName(), prop.isWritable());
494
495         qml->writeEndObject();
496     }
497
498     void dump(const QMetaMethod &meth, const QSet<QString> &implicitSignals)
499     {
500         if (meth.methodType() == QMetaMethod::Signal) {
501             if (meth.access() != QMetaMethod::Protected)
502                 return; // nothing to do.
503         } else if (meth.access() != QMetaMethod::Public) {
504             return; // nothing to do.
505         }
506
507         QByteArray name = meth.signature();
508         int lparenIndex = name.indexOf('(');
509         if (lparenIndex == -1) {
510             return; // invalid signature
511         }
512         name = name.left(lparenIndex);
513         const QString typeName = convertToId(meth.typeName());
514
515         if (implicitSignals.contains(name)
516                 && !meth.revision()
517                 && meth.methodType() == QMetaMethod::Signal
518                 && meth.parameterNames().isEmpty()
519                 && typeName.isEmpty()) {
520             // don't mention implicit signals
521             return;
522         }
523
524         if (meth.methodType() == QMetaMethod::Signal)
525             qml->writeStartObject(QLatin1String("Signal"));
526         else
527             qml->writeStartObject(QLatin1String("Method"));
528
529         qml->writeScriptBinding(QLatin1String("name"), enquote(name));
530
531 #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
532         if (int revision = meth.revision())
533             qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
534 #endif
535
536         if (! typeName.isEmpty())
537             qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
538
539         for (int i = 0; i < meth.parameterTypes().size(); ++i) {
540             QByteArray argName = meth.parameterNames().at(i);
541
542             qml->writeStartObject(QLatin1String("Parameter"));
543             if (! argName.isEmpty())
544                 qml->writeScriptBinding(QLatin1String("name"), enquote(argName));
545             writeTypeProperties(meth.parameterTypes().at(i), true);
546             qml->writeEndObject();
547         }
548
549         qml->writeEndObject();
550     }
551
552     void dump(const QMetaEnum &e)
553     {
554         qml->writeStartObject(QLatin1String("Enum"));
555         qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));
556
557         QList<QPair<QString, QString> > namesValues;
558         for (int index = 0; index < e.keyCount(); ++index) {
559             namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
560         }
561
562         qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues);
563         qml->writeEndObject();
564     }
565 };
566
567
568 enum ExitCode {
569     EXIT_INVALIDARGUMENTS = 1,
570     EXIT_SEGV = 2,
571     EXIT_IMPORTERROR = 3
572 };
573
574 #ifdef Q_OS_UNIX
575 void sigSegvHandler(int) {
576     fprintf(stderr, "Error: SEGV\n");
577     if (!currentProperty.isEmpty())
578         fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData());
579     if (!inObjectInstantiation.isEmpty())
580         fprintf(stderr, "While instantiating the object '%s'.\n", inObjectInstantiation.toLatin1().constData());
581     exit(EXIT_SEGV);
582 }
583 #endif
584
585 void printUsage(const QString &appName)
586 {
587     qWarning() << qPrintable(QString(
588                                  "Usage: %1 [-v] [-notrelocatable] module.uri version [module/import/path]\n"
589                                  "       %1 [-v] -path path/to/qmldir/directory [version]\n"
590                                  "       %1 [-v] -builtins\n"
591                                  "Example: %1 Qt.labs.particles 4.7 /home/user/dev/qt-install/imports").arg(
592                                  appName));
593 }
594
595 int main(int argc, char *argv[])
596 {
597 #ifdef Q_OS_UNIX
598     // qmldump may crash, but we don't want any crash handlers to pop up
599     // therefore we intercept the segfault and just exit() ourselves
600     struct sigaction sigAction;
601
602     sigemptyset(&sigAction.sa_mask);
603     sigAction.sa_handler = &sigSegvHandler;
604     sigAction.sa_flags   = 0;
605
606     sigaction(SIGSEGV, &sigAction, 0);
607 #endif
608
609 #ifdef QT_SIMULATOR
610     // Running this application would bring up the Qt Simulator (since it links QtGui), avoid that!
611     QtSimulatorPrivate::SimulatorConnection::createStubInstance();
612 #endif
613
614 #ifdef QT_WIDGETS_LIB
615     QApplication app(argc, argv);
616 #else
617     QGuiApplication app(argc, argv);
618 #endif
619     const QStringList args = app.arguments();
620     const QString appName = QFileInfo(app.applicationFilePath()).baseName();
621     if (args.size() < 2) {
622         printUsage(appName);
623         return EXIT_INVALIDARGUMENTS;
624     }
625
626     QString pluginImportUri;
627     QString pluginImportVersion;
628     bool relocatable = true;
629     enum Action { Uri, Path, Builtins };
630     Action action = Uri;
631     {
632         QStringList positionalArgs;
633         foreach (const QString &arg, args) {
634             if (!arg.startsWith(QLatin1Char('-'))) {
635                 positionalArgs.append(arg);
636                 continue;
637             }
638
639             if (arg == QLatin1String("--notrelocatable")
640                     || arg == QLatin1String("-notrelocatable")) {
641                 relocatable = false;
642             } else if (arg == QLatin1String("--path")
643                        || arg == QLatin1String("-path")) {
644                 action = Path;
645             } else if (arg == QLatin1String("--builtins")
646                        || arg == QLatin1String("-builtins")) {
647                 action = Builtins;
648             } else if (arg == QLatin1String("-v")) {
649                 verbose = true;
650             } else {
651                 qWarning() << "Invalid argument: " << arg;
652                 return EXIT_INVALIDARGUMENTS;
653             }
654         }
655
656         if (action == Uri) {
657             if (positionalArgs.size() != 3 && positionalArgs.size() != 4) {
658                 qWarning() << "Incorrect number of positional arguments";
659                 return EXIT_INVALIDARGUMENTS;
660             }
661             pluginImportUri = positionalArgs[1];
662             pluginImportVersion = positionalArgs[2];
663             if (positionalArgs.size() >= 4)
664                 pluginImportPath = positionalArgs[3];
665         } else if (action == Path) {
666             if (positionalArgs.size() != 2 && positionalArgs.size() != 3) {
667                 qWarning() << "Incorrect number of positional arguments";
668                 return EXIT_INVALIDARGUMENTS;
669             }
670             pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]);
671             if (positionalArgs.size() == 3)
672                 pluginImportVersion = positionalArgs[2];
673         } else if (action == Builtins) {
674             if (positionalArgs.size() != 1) {
675                 qWarning() << "Incorrect number of positional arguments";
676                 return EXIT_INVALIDARGUMENTS;
677             }
678         }
679     }
680
681     QDeclarativeEngine engine;
682     if (!pluginImportPath.isEmpty()) {
683         QDir cur = QDir::current();
684         cur.cd(pluginImportPath);
685         pluginImportPath = cur.absolutePath();
686         QDir::setCurrent(pluginImportPath);
687         engine.addImportPath(pluginImportPath);
688     }
689
690 #ifdef QT_WIDGETS_LIB
691     // load the QtQuick 1 plugin
692     {
693         QByteArray code("import QtQuick 1.0\nQtObject {}");
694         QDeclarativeComponent c(&engine);
695         c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/loadqtquick1.qml"));
696         c.create();
697         if (!c.errors().isEmpty()) {
698             foreach (const QDeclarativeError &error, c.errors())
699                 qWarning() << error.toString();
700             return EXIT_IMPORTERROR;
701         }
702     }
703 #endif
704
705     // load the QtQuick 2 plugin
706     {
707         QByteArray code("import QtQuick 2.0\nQtObject {}");
708         QDeclarativeComponent c(&engine);
709         c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/loadqtquick2.qml"));
710         c.create();
711         if (!c.errors().isEmpty()) {
712             foreach (const QDeclarativeError &error, c.errors())
713                 qWarning() << error.toString();
714             return EXIT_IMPORTERROR;
715         }
716     }
717
718     // find all QMetaObjects reachable from the builtin module
719     QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(&engine);
720     QList<QDeclarativeType *> defaultTypes = QDeclarativeMetaType::qmlTypes();
721
722     // add some otherwise unreachable QMetaObjects
723     defaultReachable.insert(&QQuickMouseEvent::staticMetaObject);
724     // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported
725
726     // this will hold the meta objects we want to dump information of
727     QSet<const QMetaObject *> metas;
728
729     if (action == Builtins) {
730         metas = defaultReachable;
731     } else {
732         // find a valid QtQuick import
733         QByteArray importCode;
734         QDeclarativeType *qtObjectType = QDeclarativeMetaType::qmlType(&QObject::staticMetaObject);
735         if (!qtObjectType) {
736             qWarning() << "Could not find QtObject type";
737             importCode = QByteArray("import QtQuick 2.0\n");
738         } else {
739             QString module = qtObjectType->qmlTypeName();
740             module = module.mid(0, module.lastIndexOf(QLatin1Char('/')));
741             importCode = QString("import %1 %2.%3\n").arg(module,
742                                                           QString::number(qtObjectType->majorVersion()),
743                                                           QString::number(qtObjectType->minorVersion())).toUtf8();
744         }
745
746         // find all QMetaObjects reachable when the specified module is imported
747         if (action != Path) {
748             importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toAscii();
749         } else {
750             // pluginImportVersion can be empty
751             importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toAscii();
752         }
753
754         // create a component with these imports to make sure the imports are valid
755         // and to populate the declarative meta type system
756         {
757             QByteArray code = importCode;
758             code += "QtObject {}";
759             QDeclarativeComponent c(&engine);
760
761             c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml"));
762             c.create();
763             if (!c.errors().isEmpty()) {
764                 foreach (const QDeclarativeError &error, c.errors())
765                     qWarning() << error.toString();
766                 return EXIT_IMPORTERROR;
767             }
768         }
769
770         QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, defaultTypes);
771         candidates.subtract(defaultReachable);
772
773         // Also eliminate meta objects with the same classname.
774         // This is required because extended objects seem not to share
775         // a single meta object instance.
776         QSet<QByteArray> defaultReachableNames;
777         foreach (const QMetaObject *mo, defaultReachable)
778             defaultReachableNames.insert(QByteArray(mo->className()));
779         foreach (const QMetaObject *mo, candidates) {
780             if (!defaultReachableNames.contains(mo->className()))
781                 metas.insert(mo);
782         }
783     }
784
785     // setup static rewrites of type names
786     cppToId.insert("QString", "string");
787     cppToId.insert("QDeclarativeEasingValueType::Type", "Type");
788
789     // start dumping data
790     QByteArray bytes;
791     QmlStreamWriter qml(&bytes);
792
793     qml.writeStartDocument();
794     qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 1);
795     qml.write("\n"
796               "// This file describes the plugin-supplied types contained in the library.\n"
797               "// It is used for QML tooling purposes only.\n"
798               "\n");
799     qml.writeStartObject("Module");
800
801     // put the metaobjects into a map so they are always dumped in the same order
802     QMap<QString, const QMetaObject *> nameToMeta;
803     foreach (const QMetaObject *meta, metas)
804         nameToMeta.insert(convertToId(meta), meta);
805
806     Dumper dumper(&qml);
807     if (relocatable)
808         dumper.setRelocatableModuleUri(pluginImportUri);
809     foreach (const QMetaObject *meta, nameToMeta) {
810         dumper.dump(meta);
811     }
812
813     // define QEasingCurve as an extension of QDeclarativeEasingValueType, this way
814     // properties using the QEasingCurve type get useful type information.
815     if (pluginImportUri.isEmpty())
816         dumper.writeEasingCurve();
817
818     // write out module api elements
819     foreach (const ModuleApi &api, moduleApis) {
820         dumper.dump(api);
821     }
822
823     qml.writeEndObject();
824     qml.writeEndDocument();
825
826     std::cout << bytes.constData() << std::flush;
827
828     // workaround to avoid crashes on exit
829     QTimer timer;
830     timer.setSingleShot(true);
831     timer.setInterval(0);
832     QObject::connect(&timer, SIGNAL(timeout()), &app, SLOT(quit()));
833     timer.start();
834
835     return app.exec();
836 }