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