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