1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtQml/qqmlengine.h>
30#include <QtQml/private/qqmlengine_p.h>
31#include <QtQml/private/qqmlmetatype_p.h>
32#include <QtQml/private/qqmlopenmetaobject_p.h>
33#include <QtQuick/private/qquickevents_p_p.h>
34#include <QtQuick/private/qquickpincharea_p.h>
35
36#ifdef QT_WIDGETS_LIB
37#include <QApplication>
38#endif // QT_WIDGETS_LIB
39
40#include <QtGui/QGuiApplication>
41#include <QtCore/QDir>
42#include <QtCore/QFileInfo>
43#include <QtCore/QSet>
44#include <QtCore/QStringList>
45#include <QtCore/QTimer>
46#include <QtCore/QMetaObject>
47#include <QtCore/QMetaProperty>
48#include <QtCore/QDebug>
49#include <QtCore/QJsonDocument>
50#include <QtCore/QJsonParseError>
51#include <QtCore/QJsonValue>
52#include <QtCore/QJsonArray>
53#include <QtCore/QJsonObject>
54#include <QtCore/QProcess>
55#include <QtCore/private/qobject_p.h>
56#include <QtCore/private/qmetaobject_p.h>
57
58#include <QRegularExpression>
59#include <iostream>
60#include <algorithm>
61
62#include "qmltypereader.h"
63#include "qmlstreamwriter.h"
64
65#ifdef QT_SIMULATOR
66#include <QtGui/private/qsimulatorconnection_p.h>
67#endif
68
69#ifdef Q_OS_WIN
70# if !defined(Q_CC_MINGW)
71# include <crtdbg.h>
72# endif
73#include <qt_windows.h>
74#endif
75
76namespace {
77
78const uint qtQmlMajorVersion = 2;
79const uint qtQmlMinorVersion = 0;
80const uint qtQuickMajorVersion = 2;
81const uint qtQuickMinorVersion = 0;
82
83const QString qtQuickQualifiedName = QString::fromLatin1(str: "QtQuick %1.%2")
84 .arg(a: qtQuickMajorVersion)
85 .arg(a: qtQuickMinorVersion);
86
87QString pluginImportPath;
88bool verbose = false;
89bool creatable = true;
90
91QString currentProperty;
92QString inObjectInstantiation;
93
94}
95
96static QString enquote(const QString &string)
97{
98 QString s = string;
99 return QString("\"%1\"").arg(a: s.replace(c: QLatin1Char('\\'), after: QLatin1String("\\\\"))
100 .replace(c: QLatin1Char('"'),after: QLatin1String("\\\"")));
101}
102
103struct QmlVersionInfo
104{
105 QString pluginImportUri;
106 int majorVersion;
107 int minorVersion;
108 bool strict;
109};
110
111static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) {
112 const QString &module = ty.module();
113 if (versionInfo.strict) {
114 return (versionInfo.pluginImportUri == module
115 && (ty.majorVersion() == versionInfo.majorVersion
116 || ty.majorVersion() == -1))
117 || module.isEmpty();
118 }
119 return module.isEmpty()
120 || versionInfo.pluginImportUri == module
121 || module.startsWith(s: versionInfo.pluginImportUri + QLatin1Char('.'));
122}
123
124void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false)
125{
126 auto ty = QQmlMetaType::qmlType(meta);
127 if (! meta || metas->contains(value: meta))
128 return;
129
130 if (matchingImportUri(ty, versionInfo: info)) {
131 if (!alreadyChangedModule) {
132 // dynamic meta objects can break things badly
133 // but extended types are usually fine
134 const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
135 if (extended || !(mop->flags & DynamicMetaObject))
136 metas->insert(value: meta);
137 } else if (!ty.module().isEmpty()) { // empty module (e.g. from an attached property) would cause a (false) match; do not warn about them
138 qWarning() << "Circular module dependency cannot be expressed in plugin.qmltypes file"
139 << "Object was:" << meta->className()
140 << ty.module() << info.pluginImportUri;
141 }
142 } else if (!ty.module().isEmpty()) {
143 alreadyChangedModule = true;
144 }
145
146 collectReachableMetaObjects(meta: meta->superClass(), metas, info, /*extended=*/ false, alreadyChangedModule);
147}
148
149void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info)
150{
151 if (! object)
152 return;
153
154 const QMetaObject *meta = object->metaObject();
155 if (verbose)
156 std::cerr << "Processing object " << qPrintable( meta->className() ) << std::endl;
157 collectReachableMetaObjects(meta, metas, info);
158
159 for (int index = 0; index < meta->propertyCount(); ++index) {
160 QMetaProperty prop = meta->property(index);
161 if (QQmlMetaType::isQObject(prop.userType())) {
162 if (verbose)
163 std::cerr << " Processing property " << qPrintable( prop.name() ) << std::endl;
164 currentProperty = QString("%1::%2").arg(args: meta->className(), args: prop.name());
165
166 // if the property was not initialized during construction,
167 // accessing a member of oo is going to cause a segmentation fault
168 QObject *oo = QQmlMetaType::toQObject(prop.read(obj: object));
169 if (oo && !metas->contains(value: oo->metaObject()))
170 collectReachableMetaObjects(object: oo, metas, info);
171 currentProperty.clear();
172 }
173 }
174}
175
176void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas, const QmlVersionInfo& info)
177{
178 collectReachableMetaObjects(meta: ty.baseMetaObject(), metas, info, extended: ty.isExtendedType());
179 if (ty.attachedPropertiesType(engine) && matchingImportUri(ty, versionInfo: info)) {
180 collectReachableMetaObjects(meta: ty.attachedPropertiesType(engine), metas, info);
181 }
182}
183
184/* We want to add the MetaObject for 'Qt' to the list, this is a
185 simple way to access it.
186*/
187class FriendlyQObject: public QObject
188{
189public:
190 static const QMetaObject *qtMeta() { return &staticQtMetaObject; }
191};
192
193/* When we dump a QMetaObject, we want to list all the types it is exported as.
194 To do this, we need to find the QQmlTypes associated with this
195 QMetaObject.
196*/
197static QHash<QByteArray, QSet<QQmlType> > qmlTypesByCppName;
198
199static QHash<QByteArray, QByteArray> cppToId;
200
201/* Takes a C++ type name, such as Qt::LayoutDirection or QString and
202 maps it to how it should appear in the description file.
203
204 These names need to be unique globally, so we don't change the C++ symbol's
205 name much. It is mostly used to for explicit translations such as
206 QString->string and translations for extended QML objects.
207*/
208QByteArray convertToId(const QByteArray &cppName)
209{
210 return cppToId.value(akey: cppName, adefaultValue: cppName);
211}
212
213QByteArray convertToId(const QMetaObject *mo)
214{
215 QByteArray className(mo->className());
216 if (!className.isEmpty())
217 return convertToId(cppName: className);
218
219 // likely a metaobject generated for an extended qml object
220 if (mo->superClass()) {
221 className = convertToId(mo: mo->superClass());
222 className.append(s: "_extended");
223 return className;
224 }
225
226 static QHash<const QMetaObject *, QByteArray> generatedNames;
227 className = generatedNames.value(akey: mo);
228 if (!className.isEmpty())
229 return className;
230
231 std::cerr << "Found a QMetaObject without a className, generating a random name" << std::endl;
232 className = QByteArray("error-unknown-name-");
233 className.append(a: QByteArray::number(generatedNames.size()));
234 generatedNames.insert(akey: mo, avalue: className);
235 return className;
236}
237
238
239// Collect all metaobjects for types registered with qmlRegisterType() without parameters
240void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas,
241 QMap<QString, QList<QQmlType>> &compositeTypes, const QmlVersionInfo &info) {
242 const auto qmlAllTypes = QQmlMetaType::qmlAllTypes();
243 for (const QQmlType &ty : qmlAllTypes) {
244 if (!metas.contains(value: ty.baseMetaObject())) {
245 if (!ty.isComposite()) {
246 collectReachableMetaObjects(engine, ty, metas: &metas, info);
247 } else if (matchingImportUri(ty, versionInfo: info)) {
248 compositeTypes[ty.elementName()].append(t: ty);
249 }
250 }
251 }
252}
253
254QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
255 QSet<const QMetaObject *> &noncreatables,
256 QSet<const QMetaObject *> &singletons,
257 QMap<QString, QList<QQmlType>> &compositeTypes,
258 const QmlVersionInfo &info,
259 const QList<QQmlType> &skip = QList<QQmlType>()
260 )
261{
262 QSet<const QMetaObject *> metas;
263 metas.insert(value: FriendlyQObject::qtMeta());
264
265 const auto qmlTypes = QQmlMetaType::qmlTypes();
266 for (const QQmlType &ty : qmlTypes) {
267 if (!matchingImportUri(ty,versionInfo: info))
268 continue;
269 if (!ty.isCreatable())
270 noncreatables.insert(value: ty.baseMetaObject());
271 if (ty.isSingleton())
272 singletons.insert(value: ty.baseMetaObject());
273 if (!ty.isComposite()) {
274 qmlTypesByCppName[ty.baseMetaObject()->className()].insert(value: ty);
275 collectReachableMetaObjects(engine: QQmlEnginePrivate::get(e: engine), ty, metas: &metas, info);
276 } else {
277 compositeTypes[ty.elementName()].append(t: ty);
278 }
279 }
280
281 if (creatable) {
282 // find even more QMetaObjects by instantiating QML types and running
283 // over the instances
284 for (const QQmlType &ty : qmlTypes) {
285 if (!matchingImportUri(ty, versionInfo: info))
286 continue;
287 if (skip.contains(t: ty))
288 continue;
289 if (ty.isExtendedType())
290 continue;
291 if (!ty.isCreatable())
292 continue;
293 if (ty.typeName() == "QQmlComponent")
294 continue;
295
296 QString tyName = ty.qmlTypeName();
297 tyName = tyName.mid(position: tyName.lastIndexOf(c: QLatin1Char('/')) + 1);
298 if (tyName.isEmpty())
299 continue;
300
301 inObjectInstantiation = tyName;
302 QObject *object = nullptr;
303
304 if (ty.isSingleton()) {
305 QQmlType::SingletonInstanceInfo *siinfo = ty.singletonInstanceInfo();
306 if (!siinfo) {
307 std::cerr << "Internal error, " << qPrintable(tyName)
308 << "(" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")"
309 << " is singleton, but has no singletonInstanceInfo" << std::endl;
310 continue;
311 }
312 if (ty.isQObjectSingleton()) {
313 if (verbose)
314 std::cerr << "Trying to get singleton for " << qPrintable(tyName)
315 << " (" << qPrintable( siinfo->typeName ) << ")" << std::endl;
316 collectReachableMetaObjects(object, metas: &metas, info);
317 object = QQmlEnginePrivate::get(e: engine)->singletonInstance<QObject*>(type: ty);
318 } else {
319 inObjectInstantiation.clear();
320 continue; // we don't handle QJSValue singleton types.
321 }
322 } else {
323 if (verbose)
324 std::cerr << "Trying to create object " << qPrintable( tyName )
325 << " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl;
326 object = ty.create();
327 }
328
329 inObjectInstantiation.clear();
330
331 if (object) {
332 if (verbose)
333 std::cerr << "Got " << qPrintable( tyName )
334 << " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl;
335 collectReachableMetaObjects(object, metas: &metas, info);
336 object->deleteLater();
337 } else {
338 std::cerr << "Could not create " << qPrintable(tyName) << std::endl;
339 }
340 }
341 }
342
343 collectReachableMetaObjectsWithoutQmlName(engine: QQmlEnginePrivate::get(e: engine), metas, compositeTypes, info);
344
345 return metas;
346}
347
348class KnownAttributes {
349 QHash<QByteArray, int> m_properties;
350 QHash<QByteArray, QHash<int, int> > m_methods;
351public:
352 bool knownMethod(const QByteArray &name, int nArgs, int revision)
353 {
354 if (m_methods.contains(akey: name)) {
355 QHash<int, int> overloads = m_methods.value(akey: name);
356 if (overloads.contains(akey: nArgs) && overloads.value(akey: nArgs) <= revision)
357 return true;
358 }
359 m_methods[name][nArgs] = revision;
360 return false;
361 }
362
363 bool knownProperty(const QByteArray &name, int revision)
364 {
365 if (m_properties.contains(akey: name) && m_properties.value(akey: name) <= revision)
366 return true;
367 m_properties[name] = revision;
368 return false;
369 }
370};
371
372class Dumper
373{
374 QmlStreamWriter *qml;
375 QString relocatableModuleUri;
376
377public:
378 Dumper(QmlStreamWriter *qml) : qml(qml) {}
379
380 void setRelocatableModuleUri(const QString &uri)
381 {
382 relocatableModuleUri = uri;
383 }
384
385 QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
386 {
387 const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri
388 : type.module();
389 const int majorVersion = type.majorVersion() >= 0 ? type.majorVersion()
390 : versionInfo.majorVersion;
391 const int minorVersion = type.minorVersion() >= 0 ? type.minorVersion()
392 : versionInfo.minorVersion;
393
394 const QString versionedElement = type.elementName()
395 + QString::fromLatin1(str: " %1.%2").arg(a: majorVersion).arg(a: minorVersion);
396
397 return enquote(string: (module == relocatableModuleUri)
398 ? versionedElement
399 : module + QLatin1Char('/') + versionedElement);
400 }
401
402 void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr)
403 {
404 QSet<QString> implicitSignals = dumpMetaProperties(meta, metaRevision: 0, knownAttributes);
405
406 if (meta == &QObject::staticMetaObject) {
407 // for QObject, hide deleteLater() and onDestroyed
408 for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) {
409 QMetaMethod method = meta->method(index);
410 QByteArray signature = method.methodSignature();
411 if (signature == QByteArrayLiteral("destroyed(QObject*)")
412 || signature == QByteArrayLiteral("destroyed()")
413 || signature == QByteArrayLiteral("deleteLater()"))
414 continue;
415 dump(meth: method, implicitSignals, knownAttributes);
416 }
417
418 // and add toString(), destroy() and destroy(int)
419 if (!knownAttributes || !knownAttributes->knownMethod(name: QByteArray("toString"), nArgs: 0, revision: 0)) {
420 qml->writeStartObject(component: QLatin1String("Method"));
421 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QLatin1String("toString")));
422 qml->writeEndObject();
423 }
424 if (!knownAttributes || !knownAttributes->knownMethod(name: QByteArray("destroy"), nArgs: 0, revision: 0)) {
425 qml->writeStartObject(component: QLatin1String("Method"));
426 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QLatin1String("destroy")));
427 qml->writeEndObject();
428 }
429 if (!knownAttributes || !knownAttributes->knownMethod(name: QByteArray("destroy"), nArgs: 1, revision: 0)) {
430 qml->writeStartObject(component: QLatin1String("Method"));
431 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QLatin1String("destroy")));
432 qml->writeStartObject(component: QLatin1String("Parameter"));
433 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QLatin1String("delay")));
434 qml->writeScriptBinding(name: QLatin1String("type"), rhs: enquote(string: QLatin1String("int")));
435 qml->writeEndObject();
436 qml->writeEndObject();
437 }
438 } else {
439 for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
440 dump(meth: meta->method(index), implicitSignals, knownAttributes);
441 }
442 }
443
444 QString getPrototypeNameForCompositeType(
445 const QMetaObject *metaObject, QList<const QMetaObject *> *objectsToMerge,
446 const QmlVersionInfo &versionInfo)
447 {
448 auto ty = QQmlMetaType::qmlType(metaObject);
449 QString prototypeName;
450 if (matchingImportUri(ty, versionInfo)) {
451 // dynamic meta objects can break things badly
452 // but extended types are usually fine
453 const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data);
454 if (!(mop->flags & DynamicMetaObject) && objectsToMerge
455 && !objectsToMerge->contains(t: metaObject))
456 objectsToMerge->append(t: metaObject);
457 const QMetaObject *superMetaObject = metaObject->superClass();
458 if (!superMetaObject) {
459 prototypeName = "QObject";
460 } else {
461 QQmlType superType = QQmlMetaType::qmlType(superMetaObject);
462 if (superType.isValid() && !superType.isComposite())
463 return convertToId(cppName: superMetaObject->className());
464 prototypeName = getPrototypeNameForCompositeType(
465 metaObject: superMetaObject, objectsToMerge, versionInfo);
466 }
467 } else {
468 prototypeName = convertToId(cppName: metaObject->className());
469 }
470 return prototypeName;
471 }
472
473 void dumpComposite(QQmlEngine *engine, const QList<QQmlType> &compositeType, const QmlVersionInfo &versionInfo)
474 {
475 for (const QQmlType &type : compositeType)
476 dumpCompositeItem(engine, compositeType: type, versionInfo);
477 }
478
479 void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, const QmlVersionInfo &versionInfo)
480 {
481 QQmlComponent e(engine, compositeType.sourceUrl());
482 if (!e.isReady()) {
483 std::cerr << "WARNING: skipping module " << compositeType.elementName().toStdString()
484 << std::endl << e.errorString().toStdString() << std::endl;
485 return;
486 }
487
488 QObject *object = e.create();
489
490 if (!object)
491 return;
492
493 qml->writeStartObject(component: "Component");
494
495 const QMetaObject *mainMeta = object->metaObject();
496
497 QList<const QMetaObject *> objectsToMerge;
498 KnownAttributes knownAttributes;
499 // Get C++ base class name for the composite type
500 QString prototypeName = getPrototypeNameForCompositeType(metaObject: mainMeta, objectsToMerge: &objectsToMerge,
501 versionInfo);
502 qml->writeScriptBinding(name: QLatin1String("prototype"), rhs: enquote(string: prototypeName));
503
504 QString qmlTyName = compositeType.qmlTypeName();
505 const QString exportString = getExportString(type: compositeType, versionInfo);
506
507 // TODO: why don't we simply output the compositeType.elementName() here?
508 // That would make more sense, but it would change the format quite a bit.
509 qml->writeScriptBinding(name: QLatin1String("name"), rhs: exportString);
510
511 qml->writeArrayBinding(name: QLatin1String("exports"), elements: QStringList() << exportString);
512 qml->writeArrayBinding(name: QLatin1String("exportMetaObjectRevisions"), elements: QStringList() << QString::number(compositeType.minorVersion()));
513 qml->writeBooleanBinding(name: QLatin1String("isComposite"), value: true);
514
515 if (compositeType.isSingleton()) {
516 qml->writeBooleanBinding(name: QLatin1String("isCreatable"), value: false);
517 qml->writeBooleanBinding(name: QLatin1String("isSingleton"), value: true);
518 }
519
520 for (int index = mainMeta->classInfoCount() - 1 ; index >= 0 ; --index) {
521 QMetaClassInfo classInfo = mainMeta->classInfo(index);
522 if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
523 qml->writeScriptBinding(name: QLatin1String("defaultProperty"), rhs: enquote(string: QLatin1String(classInfo.value())));
524 break;
525 }
526 }
527
528 for (const QMetaObject *meta : qAsConst(t&: objectsToMerge)) {
529 for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
530 dump(e: meta->enumerator(index));
531
532 writeMetaContent(meta, knownAttributes: &knownAttributes);
533 }
534
535 qml->writeEndObject();
536 }
537
538 QString getDefaultProperty(const QMetaObject *meta)
539 {
540 for (int index = meta->classInfoCount() - 1; index >= 0; --index) {
541 QMetaClassInfo classInfo = meta->classInfo(index);
542 if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
543 return QLatin1String(classInfo.value());
544 }
545 }
546 return QString();
547 }
548
549 struct QmlTypeInfo {
550 QmlTypeInfo() {}
551 QmlTypeInfo(const QString &exportString, int revision, const QMetaObject *extendedObject, QByteArray attachedTypeId)
552 : exportString(exportString), revision(revision), extendedObject(extendedObject), attachedTypeId(attachedTypeId) {}
553 QString exportString;
554 int revision = 0;
555 const QMetaObject *extendedObject = nullptr;
556 QByteArray attachedTypeId;
557 };
558
559 void dump(QQmlEnginePrivate *engine, const QMetaObject *meta, bool isUncreatable, bool isSingleton)
560 {
561 qml->writeStartObject(component: "Component");
562
563 QByteArray id = convertToId(mo: meta);
564 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: id));
565
566 // collect type information
567 QVector<QmlTypeInfo> typeInfo;
568 for (QQmlType type : qmlTypesByCppName.value(akey: meta->className())) {
569 const QMetaObject *extendedObject = type.extensionFunction() ? type.metaObject() : nullptr;
570 QByteArray attachedTypeId;
571 if (const QMetaObject *attachedType = type.attachedPropertiesType(engine)) {
572 // Can happen when a type is registered that returns itself as attachedPropertiesType()
573 // because there is no creatable type to attach to.
574 if (attachedType != meta)
575 attachedTypeId = convertToId(mo: attachedType);
576 }
577 const QString exportString = getExportString(type, versionInfo: { .pluginImportUri: QString(), .majorVersion: -1, .minorVersion: -1, .strict: false });
578 int metaObjectRevision = type.metaObjectRevision();
579 if (extendedObject) {
580 // emulate custom metaobjectrevision out of import
581 metaObjectRevision = type.majorVersion() * 100 + type.minorVersion();
582 }
583
584 QmlTypeInfo info = { exportString, metaObjectRevision, extendedObject, attachedTypeId };
585 typeInfo.append(t: info);
586 }
587
588 // sort to ensure stable output
589 std::sort(first: typeInfo.begin(), last: typeInfo.end(), comp: [](const QmlTypeInfo &i1, const QmlTypeInfo &i2) {
590 return i1.revision < i2.revision;
591 });
592
593 // determine default property
594 // TODO: support revisioning of default property
595 QString defaultProperty = getDefaultProperty(meta);
596 if (defaultProperty.isEmpty()) {
597 for (const QmlTypeInfo &iter : typeInfo) {
598 if (iter.extendedObject) {
599 defaultProperty = getDefaultProperty(meta: iter.extendedObject);
600 if (!defaultProperty.isEmpty())
601 break;
602 }
603 }
604 }
605 if (!defaultProperty.isEmpty())
606 qml->writeScriptBinding(name: QLatin1String("defaultProperty"), rhs: enquote(string: defaultProperty));
607
608 if (meta->superClass())
609 qml->writeScriptBinding(name: QLatin1String("prototype"), rhs: enquote(string: convertToId(mo: meta->superClass())));
610
611 if (!typeInfo.isEmpty()) {
612 QMap<QString, QString> exports; // sort exports
613 for (const QmlTypeInfo &iter : typeInfo)
614 exports.insert(akey: iter.exportString, avalue: QString::number(iter.revision));
615
616 QStringList exportStrings = exports.keys();
617 QStringList metaObjectRevisions = exports.values();
618 qml->writeArrayBinding(name: QLatin1String("exports"), elements: exportStrings);
619
620 if (isUncreatable)
621 qml->writeBooleanBinding(name: QLatin1String("isCreatable"), value: false);
622
623 if (isSingleton)
624 qml->writeBooleanBinding(name: QLatin1String("isSingleton"), value: true);
625
626 qml->writeArrayBinding(name: QLatin1String("exportMetaObjectRevisions"), elements: metaObjectRevisions);
627
628 for (const QmlTypeInfo &iter : typeInfo) {
629 if (!iter.attachedTypeId.isEmpty()) {
630 qml->writeScriptBinding(name: QLatin1String("attachedType"), rhs: enquote(string: iter.attachedTypeId));
631 break;
632 }
633 }
634 }
635
636 for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
637 dump(e: meta->enumerator(index));
638
639 writeMetaContent(meta);
640
641 // dump properties from extended metaobjects last
642 for (auto iter : typeInfo) {
643 if (iter.extendedObject)
644 dumpMetaProperties(meta: iter.extendedObject, metaRevision: iter.revision);
645 }
646
647 qml->writeEndObject();
648 }
649
650 void writeEasingCurve()
651 {
652 qml->writeStartObject(component: QLatin1String("Component"));
653 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QLatin1String("QEasingCurve")));
654 qml->writeScriptBinding(name: QLatin1String("prototype"), rhs: enquote(string: QLatin1String("QQmlEasingValueType")));
655 qml->writeEndObject();
656 }
657
658private:
659
660 /* Removes pointer and list annotations from a type name, returning
661 what was removed in isList and isPointer
662 */
663 static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer)
664 {
665 static QByteArray declListPrefix = "QQmlListProperty<";
666
667 if (typeName->endsWith(c: '*')) {
668 *isPointer = true;
669 typeName->truncate(pos: typeName->length() - 1);
670 removePointerAndList(typeName, isList, isPointer);
671 } else if (typeName->startsWith(a: declListPrefix)) {
672 *isList = true;
673 typeName->truncate(pos: typeName->length() - 1); // get rid of the suffix '>'
674 *typeName = typeName->mid(index: declListPrefix.size());
675 removePointerAndList(typeName, isList, isPointer);
676 }
677
678 *typeName = convertToId(cppName: *typeName);
679 }
680
681 void writeTypeProperties(QByteArray typeName, bool isWritable)
682 {
683 bool isList = false, isPointer = false;
684 removePointerAndList(typeName: &typeName, isList: &isList, isPointer: &isPointer);
685
686 qml->writeScriptBinding(name: QLatin1String("type"), rhs: enquote(string: typeName));
687 if (isList)
688 qml->writeScriptBinding(name: QLatin1String("isList"), rhs: QLatin1String("true"));
689 if (!isWritable)
690 qml->writeScriptBinding(name: QLatin1String("isReadonly"), rhs: QLatin1String("true"));
691 if (isPointer)
692 qml->writeScriptBinding(name: QLatin1String("isPointer"), rhs: QLatin1String("true"));
693 }
694
695 void dump(const QMetaProperty &prop, int metaRevision = -1, KnownAttributes *knownAttributes = nullptr)
696 {
697 int revision = metaRevision ? metaRevision : prop.revision();
698 QByteArray propName = prop.name();
699 if (knownAttributes && knownAttributes->knownProperty(name: propName, revision))
700 return;
701 qml->writeStartObject(component: "Property");
702 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QString::fromUtf8(str: prop.name())));
703 if (revision)
704 qml->writeScriptBinding(name: QLatin1String("revision"), rhs: QString::number(revision));
705 writeTypeProperties(typeName: prop.typeName(), isWritable: prop.isWritable());
706
707 qml->writeEndObject();
708 }
709
710 QSet<QString> dumpMetaProperties(const QMetaObject *meta, int metaRevision = -1, KnownAttributes *knownAttributes = nullptr)
711 {
712 QSet<QString> implicitSignals;
713 for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
714 const QMetaProperty &property = meta->property(index);
715 dump(prop: property, metaRevision, knownAttributes);
716 if (knownAttributes)
717 knownAttributes->knownMethod(name: QByteArray(property.name()).append(s: "Changed"),
718 nArgs: 0, revision: property.revision());
719 implicitSignals.insert(value: QString("%1Changed").arg(a: QString::fromUtf8(str: property.name())));
720 }
721 return implicitSignals;
722 }
723
724 void dump(const QMetaMethod &meth, const QSet<QString> &implicitSignals,
725 KnownAttributes *knownAttributes = nullptr)
726 {
727 if (meth.methodType() == QMetaMethod::Signal) {
728 if (meth.access() != QMetaMethod::Public)
729 return; // nothing to do.
730 } else if (meth.access() != QMetaMethod::Public) {
731 return; // nothing to do.
732 }
733
734 QByteArray name = meth.name();
735 const QString typeName = convertToId(cppName: meth.typeName());
736
737 if (implicitSignals.contains(value: name)
738 && !meth.revision()
739 && meth.methodType() == QMetaMethod::Signal
740 && meth.parameterNames().isEmpty()
741 && typeName == QLatin1String("void")) {
742 // don't mention implicit signals
743 return;
744 }
745
746 int revision = meth.revision();
747 if (knownAttributes && knownAttributes->knownMethod(name, nArgs: meth.parameterNames().size(), revision))
748 return;
749 if (meth.methodType() == QMetaMethod::Signal)
750 qml->writeStartObject(component: QLatin1String("Signal"));
751 else
752 qml->writeStartObject(component: QLatin1String("Method"));
753
754 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: name));
755
756 if (revision)
757 qml->writeScriptBinding(name: QLatin1String("revision"), rhs: QString::number(revision));
758
759 if (typeName != QLatin1String("void"))
760 qml->writeScriptBinding(name: QLatin1String("type"), rhs: enquote(string: typeName));
761
762 for (int i = 0; i < meth.parameterTypes().size(); ++i) {
763 QByteArray argName = meth.parameterNames().at(i);
764
765 qml->writeStartObject(component: QLatin1String("Parameter"));
766 if (! argName.isEmpty())
767 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: argName));
768 writeTypeProperties(typeName: meth.parameterTypes().at(i), isWritable: true);
769 qml->writeEndObject();
770 }
771
772 qml->writeEndObject();
773 }
774
775 void dump(const QMetaEnum &e)
776 {
777 qml->writeStartObject(component: QLatin1String("Enum"));
778 qml->writeScriptBinding(name: QLatin1String("name"), rhs: enquote(string: QString::fromUtf8(str: e.name())));
779
780 QList<QPair<QString, QString> > namesValues;
781 const int keyCount = e.keyCount();
782 namesValues.reserve(alloc: keyCount);
783 for (int index = 0; index < keyCount; ++index) {
784 namesValues.append(t: qMakePair(x: enquote(string: QString::fromUtf8(str: e.key(index))), y: QString::number(e.value(index))));
785 }
786
787 qml->writeScriptObjectLiteralBinding(name: QLatin1String("values"), keyValue: namesValues);
788 qml->writeEndObject();
789 }
790};
791
792enum ExitCode {
793 EXIT_INVALIDARGUMENTS = 1,
794 EXIT_SEGV = 2,
795 EXIT_IMPORTERROR = 3
796};
797
798void printUsage(const QString &appName)
799{
800 std::cerr << qPrintable(QString(
801 "Usage: %1 [-v] [-qapp] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies <dependencies.json>] [-merge <file-to-merge.qmltypes>] [-output <output-file.qmltypes>] [-noforceqtquick] module.uri version [module/import/path]\n"
802 " %1 [-v] [-qapp] [-noinstantiate] -path path/to/qmldir/directory [version]\n"
803 " %1 [-v] -builtins\n"
804 "Example: %1 Qt.labs.folderlistmodel 2.0 /home/user/dev/qt-install/imports").arg(
805 appName)) << std::endl;
806}
807
808static bool readDependenciesData(QString dependenciesFile, const QByteArray &fileData,
809 QStringList *dependencies, const QStringList &urisToSkip,
810 bool forceQtQuickDependency = true) {
811 if (verbose) {
812 std::cerr << "parsing "
813 << qPrintable( dependenciesFile ) << " skipping";
814 for (const QString &uriToSkip : urisToSkip)
815 std::cerr << ' ' << qPrintable(uriToSkip);
816 std::cerr << std::endl;
817 }
818 QJsonParseError parseError;
819 parseError.error = QJsonParseError::NoError;
820 QJsonDocument doc = QJsonDocument::fromJson(json: fileData, error: &parseError);
821 if (parseError.error != QJsonParseError::NoError) {
822 std::cerr << "Error parsing dependencies file " << dependenciesFile.toStdString()
823 << ":" << parseError.errorString().toStdString() << " at " << parseError.offset
824 << std::endl;
825 return false;
826 }
827 if (doc.isArray()) {
828 const QStringList requiredKeys = QStringList() << QStringLiteral("name")
829 << QStringLiteral("type")
830 << QStringLiteral("version");
831 const auto deps = doc.array();
832 for (const QJsonValue &dep : deps) {
833 if (dep.isObject()) {
834 QJsonObject obj = dep.toObject();
835 for (const QString &requiredKey : requiredKeys)
836 if (!obj.contains(key: requiredKey) || obj.value(key: requiredKey).isString())
837 continue;
838 if (obj.value(QStringLiteral("type")).toString() != QLatin1String("module"))
839 continue;
840 QString name = obj.value(key: (QStringLiteral("name"))).toString();
841 QString version = obj.value(QStringLiteral("version")).toString();
842 if (name.isEmpty() || urisToSkip.contains(str: name) || version.isEmpty())
843 continue;
844 if (name.contains(s: QLatin1String("Private"), cs: Qt::CaseInsensitive)) {
845 if (verbose)
846 std::cerr << "skipping private dependecy "
847 << qPrintable( name ) << " " << qPrintable(version) << std::endl;
848 continue;
849 }
850 if (verbose)
851 std::cerr << "appending dependency "
852 << qPrintable( name ) << " " << qPrintable(version) << std::endl;
853 dependencies->append(t: name + QLatin1Char(' ')+version);
854 }
855 }
856 } else {
857 std::cerr << "Error parsing dependencies file " << dependenciesFile.toStdString()
858 << ": expected an array" << std::endl;
859 return false;
860 }
861 // Workaround for avoiding conflicting types when no dependency has been found.
862 //
863 // qmlplugindump used to import QtQuick, so all types defined in QtQuick used to be skipped when dumping.
864 // Now that it imports only Qt, it is no longer the case: if no dependency is found all the types defined
865 // in QtQuick will be dumped, causing conflicts.
866 if (forceQtQuickDependency && dependencies->isEmpty())
867 dependencies->push_back(t: qtQuickQualifiedName);
868 return true;
869}
870
871static bool readDependenciesFile(const QString &dependenciesFile, QStringList *dependencies,
872 const QStringList &urisToSkip) {
873 if (!QFileInfo::exists(file: dependenciesFile)) {
874 std::cerr << "non existing dependencies file " << dependenciesFile.toStdString()
875 << std::endl;
876 return false;
877 }
878 QFile f(dependenciesFile);
879 if (!f.open(flags: QFileDevice::ReadOnly)) {
880 std::cerr << "non existing dependencies file " << dependenciesFile.toStdString()
881 << ", " << f.errorString().toStdString() << std::endl;
882 return false;
883 }
884 QByteArray fileData = f.readAll();
885 return readDependenciesData(dependenciesFile, fileData, dependencies, urisToSkip, forceQtQuickDependency: false);
886}
887
888static bool getDependencies(const QQmlEngine &engine, const QString &pluginImportUri,
889 const QString &pluginImportVersion, QStringList *dependencies,
890 bool forceQtQuickDependency)
891{
892 QString importScannerExe = QLatin1String("qmlimportscanner");
893 QFileInfo selfExe(QCoreApplication::applicationFilePath());
894 if (!selfExe.suffix().isEmpty())
895 importScannerExe += QLatin1String(".") + selfExe.suffix();
896 QString command = selfExe.absoluteDir().filePath(fileName: importScannerExe);
897
898 QStringList commandArgs = QStringList()
899 << QLatin1String("-qmlFiles")
900 << QLatin1String("-");
901 QStringList importPathList = engine.importPathList();
902 importPathList.removeOne(QStringLiteral("qrc:/qt-project.org/imports"));
903 for (const QString &path : importPathList)
904 commandArgs << QLatin1String("-importPath") << path;
905
906 QProcess importScanner;
907 importScanner.start(program: command, arguments: commandArgs, mode: QProcess::ReadWrite);
908 if (!importScanner.waitForStarted())
909 return false;
910
911 importScanner.write(data: "import ");
912 importScanner.write(data: pluginImportUri.toUtf8());
913 importScanner.write(data: " ");
914 importScanner.write(data: pluginImportVersion.toUtf8());
915 importScanner.write(data: "\nQtObject{}\n");
916 importScanner.closeWriteChannel();
917
918 if (!importScanner.waitForFinished()) {
919 std::cerr << "failure to start " << qPrintable(command);
920 for (const QString &arg : qAsConst(t&: commandArgs))
921 std::cerr << ' ' << qPrintable(arg);
922 std::cerr << std::endl;
923 return false;
924 }
925 QByteArray depencenciesData = importScanner.readAllStandardOutput();
926 if (!readDependenciesData(dependenciesFile: QLatin1String("<outputOfQmlimportscanner>"), fileData: depencenciesData,
927 dependencies, urisToSkip: QStringList(pluginImportUri), forceQtQuickDependency)) {
928 std::cerr << "failed to process output of qmlimportscanner" << std::endl;
929 if (importScanner.exitCode() != 0)
930 std::cerr << importScanner.readAllStandardError().toStdString();
931 return false;
932 }
933
934 QStringList aux;
935 for (const QString &str : qAsConst(t&: *dependencies)) {
936 if (!str.startsWith(s: "Qt.test.qtestroot"))
937 aux += str;
938 }
939 *dependencies = aux;
940
941 return true;
942}
943
944bool compactDependencies(QStringList *dependencies)
945{
946 if (dependencies->isEmpty())
947 return false;
948 dependencies->sort();
949 QStringList oldDep = dependencies->constFirst().split(sep: QLatin1Char(' '));
950 Q_ASSERT(oldDep.size() == 2);
951 int oldPos = 0;
952 for (int idep = 1; idep < dependencies->size(); ++idep) {
953 QString depStr = dependencies->at(i: idep);
954 const QStringList newDep = depStr.split(sep: QLatin1Char(' '));
955 Q_ASSERT(newDep.size() == 2);
956 if (newDep.constFirst() != oldDep.constFirst()) {
957 if (++oldPos != idep)
958 dependencies->replace(i: oldPos, t: depStr);
959 oldDep = newDep;
960 } else {
961 const QStringList v1 = oldDep.constLast().split(sep: QLatin1Char('.'));
962 const QStringList v2 = newDep.constLast().split(sep: QLatin1Char('.'));
963 Q_ASSERT(v1.size() == 2);
964 Q_ASSERT(v2.size() == 2);
965 bool ok;
966 int major1 = v1.first().toInt(ok: &ok);
967 Q_ASSERT(ok);
968 int major2 = v2.first().toInt(ok: &ok);
969 Q_ASSERT(ok);
970 if (major1 != major2) {
971 std::cerr << "Found a dependency on " << qPrintable(oldDep.constFirst())
972 << " with two major versions:" << qPrintable(oldDep.constLast())
973 << " and " << qPrintable(newDep.constLast())
974 << " which is unsupported, discarding smaller version" << std::endl;
975 if (major1 < major2)
976 dependencies->replace(i: oldPos, t: depStr);
977 } else {
978 int minor1 = v1.last().toInt(ok: &ok);
979 Q_ASSERT(ok);
980 int minor2 = v2.last().toInt(ok: &ok);
981 Q_ASSERT(ok);
982 if (minor1 < minor2)
983 dependencies->replace(i: oldPos, t: depStr);
984 }
985 }
986 }
987 if (++oldPos < dependencies->size()) {
988 *dependencies = dependencies->mid(pos: 0, alength: oldPos);
989 return true;
990 }
991 return false;
992}
993
994void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg)
995{
996 std::cerr << msg.toStdString() << std::endl;
997 // In case of QtFatalMsg the calling code will abort() when appropriate.
998}
999
1000QT_BEGIN_NAMESPACE
1001static bool operator<(const QQmlType &a, const QQmlType &b)
1002{
1003 return a.qmlTypeName() < b.qmlTypeName()
1004 || (a.qmlTypeName() == b.qmlTypeName()
1005 && ((a.majorVersion() < b.majorVersion())
1006 || (a.majorVersion() == b.majorVersion()
1007 && a.minorVersion() < b.minorVersion())));
1008}
1009QT_END_NAMESPACE
1010
1011int main(int argc, char *argv[])
1012{
1013#if defined(Q_OS_WIN) && !defined(Q_CC_MINGW)
1014 // we do not want windows popping up if the module loaded triggers an assert
1015 SetErrorMode(SEM_NOGPFAULTERRORBOX);
1016 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
1017 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
1018 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
1019#endif // Q_OS_WIN && !Q_CC_MINGW
1020 // The default message handler might not print to console on some systems. Enforce this.
1021 qInstallMessageHandler(printDebugMessage);
1022
1023#ifdef QT_SIMULATOR
1024 // Running this application would bring up the Qt Simulator (since it links Qt GUI), avoid that!
1025 QtSimulatorPrivate::SimulatorConnection::createStubInstance();
1026#endif
1027
1028 // don't require a window manager even though we're a QGuiApplication
1029 bool requireWindowManager = false;
1030 for (int index = 1; index < argc; ++index) {
1031 if (QString::fromLocal8Bit(str: argv[index]) == "--defaultplatform"
1032 || QString::fromLocal8Bit(str: argv[index]) == "-defaultplatform") {
1033 requireWindowManager = true;
1034 break;
1035 }
1036 }
1037
1038 if (!requireWindowManager && qEnvironmentVariableIsEmpty(varName: "QT_QPA_PLATFORM"))
1039 qputenv(varName: "QT_QPA_PLATFORM", QByteArrayLiteral("minimal"));
1040 else
1041 QCoreApplication::setAttribute(attribute: Qt::AA_ShareOpenGLContexts, on: true);
1042
1043 // Check which kind of application should be instantiated.
1044 bool useQApplication = false;
1045 for (int i = 0; i < argc; ++i) {
1046 QString arg = QLatin1String(argv[i]);
1047 if (arg == QLatin1String("--qapp") || arg == QLatin1String("-qapp"))
1048 useQApplication = true;
1049 }
1050
1051#ifdef QT_WIDGETS_LIB
1052 QScopedPointer<QCoreApplication> app(useQApplication
1053 ? new QApplication(argc, argv)
1054 : new QGuiApplication(argc, argv));
1055#else
1056 Q_UNUSED(useQApplication);
1057 QScopedPointer<QCoreApplication> app(new QGuiApplication(argc, argv));
1058#endif // QT_WIDGETS_LIB
1059
1060 QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
1061 QStringList args = app->arguments();
1062 const QString appName = QFileInfo(app->applicationFilePath()).baseName();
1063 if (args.size() < 2) {
1064 printUsage(appName);
1065 return EXIT_INVALIDARGUMENTS;
1066 }
1067
1068 QString outputFilename;
1069 QString pluginImportUri;
1070 QString pluginImportVersion;
1071 bool relocatable = true;
1072 QString dependenciesFile;
1073 QString mergeFile;
1074 bool forceQtQuickDependency = true;
1075 bool strict = false;
1076 enum Action { Uri, Path, Builtins };
1077 Action action = Uri;
1078 {
1079 QStringList positionalArgs;
1080
1081 for (int iArg = 0; iArg < args.size(); ++iArg) {
1082 const QString &arg = args.at(i: iArg);
1083 if (!arg.startsWith(c: QLatin1Char('-'))) {
1084 positionalArgs.append(t: arg);
1085 continue;
1086 }
1087 if (arg == QLatin1String("--dependencies")
1088 || arg == QLatin1String("-dependencies")) {
1089 if (++iArg == args.size()) {
1090 std::cerr << "missing dependencies file" << std::endl;
1091 return EXIT_INVALIDARGUMENTS;
1092 }
1093 dependenciesFile = args.at(i: iArg);
1094
1095 // Remove absolute path so that it does not show up in the
1096 // printed command line inside the plugins.qmltypes file.
1097 args[iArg] = QFileInfo(args.at(i: iArg)).fileName();
1098 } else if (arg == QLatin1String("--merge")
1099 || arg == QLatin1String("-merge")) {
1100 if (++iArg == args.size()) {
1101 std::cerr << "missing merge file" << std::endl;
1102 return EXIT_INVALIDARGUMENTS;
1103 }
1104 mergeFile = args.at(i: iArg);
1105 } else if (arg == QLatin1String("--notrelocatable")
1106 || arg == QLatin1String("-notrelocatable")
1107 || arg == QLatin1String("--nonrelocatable")
1108 || arg == QLatin1String("-nonrelocatable")) {
1109 relocatable = false;
1110 } else if (arg == QLatin1String("--relocatable")
1111 || arg == QLatin1String("-relocatable")) {
1112 relocatable = true;
1113 } else if (arg == QLatin1String("--noinstantiate")
1114 || arg == QLatin1String("-noinstantiate")) {
1115 creatable = false;
1116 } else if (arg == QLatin1String("--path")
1117 || arg == QLatin1String("-path")) {
1118 action = Path;
1119 } else if (arg == QLatin1String("--builtins")
1120 || arg == QLatin1String("-builtins")) {
1121 action = Builtins;
1122 } else if (arg == QLatin1String("-v")) {
1123 verbose = true;
1124 } else if (arg == QLatin1String("--noforceqtquick")
1125 || arg == QLatin1String("-noforceqtquick")){
1126 forceQtQuickDependency = false;
1127 } else if (arg == QLatin1String("--output")
1128 || arg == QLatin1String("-output")) {
1129 if (++iArg == args.size()) {
1130 std::cerr << "missing output file" << std::endl;
1131 return EXIT_INVALIDARGUMENTS;
1132 }
1133 outputFilename = args.at(i: iArg);
1134 } else if (arg == QLatin1String("--defaultplatform")
1135 || arg == QLatin1String("-defaultplatform")) {
1136 continue;
1137 } else if (arg == QLatin1String("--qapp")
1138 || arg == QLatin1String("-qapp")) {
1139 continue;
1140 } else if (arg == QLatin1String("--strict")
1141 || arg == QLatin1String("-strict")) {
1142 strict = true;
1143 continue;
1144 } else {
1145 std::cerr << "Invalid argument: " << qPrintable(arg) << std::endl;
1146 return EXIT_INVALIDARGUMENTS;
1147 }
1148 }
1149
1150 if (action == Uri) {
1151 if (positionalArgs.size() != 3 && positionalArgs.size() != 4) {
1152 std::cerr << "Incorrect number of positional arguments" << std::endl;
1153 return EXIT_INVALIDARGUMENTS;
1154 }
1155 pluginImportUri = positionalArgs.at(i: 1);
1156 pluginImportVersion = positionalArgs[2];
1157 if (positionalArgs.size() >= 4)
1158 pluginImportPath = positionalArgs.at(i: 3);
1159 } else if (action == Path) {
1160 if (positionalArgs.size() != 2 && positionalArgs.size() != 3) {
1161 std::cerr << "Incorrect number of positional arguments" << std::endl;
1162 return EXIT_INVALIDARGUMENTS;
1163 }
1164 pluginImportPath = QDir::fromNativeSeparators(pathName: positionalArgs.at(i: 1));
1165 if (positionalArgs.size() == 3)
1166 pluginImportVersion = positionalArgs.at(i: 2);
1167 } else if (action == Builtins) {
1168 if (positionalArgs.size() != 1) {
1169 std::cerr << "Incorrect number of positional arguments" << std::endl;
1170 return EXIT_INVALIDARGUMENTS;
1171 }
1172 }
1173 }
1174
1175 QQmlEngine engine;
1176 if (!pluginImportPath.isEmpty()) {
1177 QDir cur = QDir::current();
1178 cur.cd(dirName: pluginImportPath);
1179 pluginImportPath = cur.canonicalPath();
1180 QDir::setCurrent(pluginImportPath);
1181 engine.addImportPath(dir: pluginImportPath);
1182 }
1183
1184 // Merge file.
1185 QStringList mergeDependencies;
1186 QString mergeComponents;
1187 if (!mergeFile.isEmpty()) {
1188 const QStringList merge = readQmlTypes(filename: mergeFile);
1189 if (!merge.isEmpty()) {
1190 QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)");
1191 QRegularExpressionMatchIterator i = re.globalMatch(subject: merge[1]);
1192 while (i.hasNext()) {
1193 QRegularExpressionMatch m = i.next();
1194 mergeDependencies << m.captured(nth: 1);
1195 }
1196 mergeComponents = merge [2];
1197 }
1198 }
1199
1200 // Dependencies.
1201
1202 bool calculateDependencies = !pluginImportUri.isEmpty() && !pluginImportVersion.isEmpty();
1203 QStringList dependencies;
1204 if (!dependenciesFile.isEmpty())
1205 calculateDependencies = !readDependenciesFile(dependenciesFile, dependencies: &dependencies,
1206 urisToSkip: QStringList(pluginImportUri)) && calculateDependencies;
1207 if (calculateDependencies)
1208 getDependencies(engine, pluginImportUri, pluginImportVersion, dependencies: &dependencies,
1209 forceQtQuickDependency);
1210
1211 compactDependencies(dependencies: &dependencies);
1212
1213
1214 QString qtQmlImportString = QString::fromLatin1(str: "import QtQml %1.%2")
1215 .arg(a: qtQmlMajorVersion)
1216 .arg(a: qtQmlMinorVersion);
1217
1218 // load the QtQml builtins and the dependencies
1219 {
1220 QByteArray code(qtQmlImportString.toUtf8());
1221 for (const QString &moduleToImport : qAsConst(t&: dependencies)) {
1222 code.append(s: "\nimport ");
1223 code.append(a: moduleToImport.toUtf8());
1224 }
1225 code.append(s: "\nQtObject {}");
1226 QQmlComponent c(&engine);
1227 c.setData(code, baseUrl: QUrl::fromLocalFile(localfile: pluginImportPath + "/loaddependencies.qml"));
1228 c.create();
1229 const auto errors = c.errors();
1230 if (!errors.isEmpty()) {
1231 for (const QQmlError &error : errors)
1232 std::cerr << qPrintable( error.toString() ) << std::endl;
1233 return EXIT_IMPORTERROR;
1234 }
1235 }
1236
1237 // find all QMetaObjects reachable from the builtin module
1238 QSet<const QMetaObject *> uncreatableMetas;
1239 QSet<const QMetaObject *> singletonMetas;
1240
1241 // this will hold the meta objects we want to dump information of
1242 QSet<const QMetaObject *> metas;
1243
1244 // composite types we want to dump information of
1245 QMap<QString, QList<QQmlType>> compositeTypes;
1246
1247 int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion;
1248 QmlVersionInfo info;
1249 if (action == Builtins) {
1250 QMap<QString, QList<QQmlType>> defaultCompositeTypes;
1251 QSet<const QMetaObject *> builtins = collectReachableMetaObjects(engine: &engine, noncreatables&: uncreatableMetas, singletons&: singletonMetas, compositeTypes&: defaultCompositeTypes, info: {.pluginImportUri: QLatin1String("Qt"), .majorVersion: majorVersion, .minorVersion: minorVersion, .strict: strict});
1252 Q_ASSERT(builtins.size() == 1);
1253 metas.insert(value: *builtins.begin());
1254 } else {
1255 auto versionSplitted = pluginImportVersion.split(sep: ".");
1256 bool ok = versionSplitted.size() == 2;
1257 if (!ok)
1258 qCritical(msg: "Invalid version number");
1259 else {
1260 majorVersion = versionSplitted.at(i: 0).toInt(ok: &ok);
1261 if (!ok)
1262 qCritical(msg: "Invalid major version");
1263 minorVersion = versionSplitted.at(i: 1).toInt(ok: &ok);
1264 if (!ok)
1265 qCritical(msg: "Invalid minor version");
1266 }
1267 QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes();
1268 // find a valid QtQuick import
1269 QByteArray importCode;
1270 QQmlType qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject);
1271 if (!qtObjectType.isValid()) {
1272 std::cerr << "Could not find QtObject type" << std::endl;
1273 importCode = qtQmlImportString.toUtf8();
1274 } else {
1275 QString module = qtObjectType.qmlTypeName();
1276 module = module.mid(position: 0, n: module.lastIndexOf(c: QLatin1Char('/')));
1277 importCode = QString("import %1 %2.%3").arg(args&: module,
1278 args: QString::number(qtObjectType.majorVersion()),
1279 args: QString::number(qtObjectType.minorVersion())).toUtf8();
1280 }
1281 // avoid importing dependencies?
1282 for (const QString &moduleToImport : qAsConst(t&: dependencies)) {
1283 importCode.append(s: "\nimport ");
1284 importCode.append(a: moduleToImport.toUtf8());
1285 }
1286
1287 // find all QMetaObjects reachable when the specified module is imported
1288 if (action != Path) {
1289 importCode += QString("\nimport %0 %1\n").arg(args&: pluginImportUri, args&: pluginImportVersion).toLatin1();
1290 } else {
1291 // pluginImportVersion can be empty
1292 importCode += QString("\nimport \".\" %2\n").arg(a: pluginImportVersion).toLatin1();
1293 }
1294
1295 // create a component with these imports to make sure the imports are valid
1296 // and to populate the declarative meta type system
1297 {
1298 QByteArray code = importCode;
1299 code += "\nQtObject {}";
1300 QQmlComponent c(&engine);
1301
1302 c.setData(code, baseUrl: QUrl::fromLocalFile(localfile: pluginImportPath + "/typelist.qml"));
1303 c.create();
1304 const auto errors = c.errors();
1305 if (!errors.isEmpty()) {
1306 for (const QQmlError &error : errors)
1307 std::cerr << qPrintable( error.toString() ) << std::endl;
1308 return EXIT_IMPORTERROR;
1309 }
1310 }
1311 info = {.pluginImportUri: pluginImportUri, .majorVersion: majorVersion, .minorVersion: minorVersion, .strict: strict};
1312 QSet<const QMetaObject *> candidates = collectReachableMetaObjects(engine: &engine, noncreatables&: uncreatableMetas, singletons&: singletonMetas, compositeTypes, info, skip: defaultTypes);
1313
1314 for (auto it = compositeTypes.begin(), end = compositeTypes.end(); it != end; ++it) {
1315 std::sort(first: it->begin(), last: it->end());
1316 it->erase(afirst: std::unique(first: it->begin(), last: it->end()), alast: it->end());
1317 }
1318
1319 for (const QMetaObject *mo : qAsConst(t&: candidates)) {
1320 if (mo->className() != QLatin1String("Qt"))
1321 metas.insert(value: mo);
1322 }
1323 }
1324
1325 // setup static rewrites of type names
1326 cppToId.insert(akey: "QString", avalue: "string");
1327 cppToId.insert(akey: "QQmlEasingValueType::Type", avalue: "Type");
1328
1329 // start dumping data
1330 QByteArray bytes;
1331 QmlStreamWriter qml(&bytes);
1332
1333 qml.writeStartDocument();
1334 qml.writeLibraryImport(uri: QLatin1String("QtQuick.tooling"), majorVersion: 1, minorVersion: 2);
1335 qml.write(data: QString("\n"
1336 "// This file describes the plugin-supplied types contained in the library.\n"
1337 "// It is used for QML tooling purposes only.\n"
1338 "//\n"
1339 "// This file was auto-generated by:\n"
1340 "// '%1 %2'\n"
1341 "\n").arg(args: QFileInfo(args.at(i: 0)).baseName(), args: args.mid(pos: 1).join(sep: QLatin1Char(' '))));
1342 qml.writeStartObject(component: "Module");
1343
1344 // Insert merge dependencies.
1345 if (!mergeDependencies.isEmpty()) {
1346 dependencies << mergeDependencies;
1347 }
1348 compactDependencies(dependencies: &dependencies);
1349
1350 QStringList quotedDependencies;
1351 for (const QString &dep : qAsConst(t&: dependencies))
1352 quotedDependencies << enquote(string: dep);
1353 qml.writeArrayBinding(name: "dependencies", elements: quotedDependencies);
1354
1355 // put the metaobjects into a map so they are always dumped in the same order
1356 QMap<QString, const QMetaObject *> nameToMeta;
1357 for (const QMetaObject *meta : qAsConst(t&: metas))
1358 nameToMeta.insert(akey: convertToId(mo: meta), avalue: meta);
1359
1360 Dumper dumper(&qml);
1361 if (relocatable)
1362 dumper.setRelocatableModuleUri(pluginImportUri);
1363 for (const QMetaObject *meta : qAsConst(t&: nameToMeta)) {
1364 dumper.dump(engine: QQmlEnginePrivate::get(e: &engine), meta, isUncreatable: uncreatableMetas.contains(value: meta), isSingleton: singletonMetas.contains(value: meta));
1365 }
1366
1367 QMap<QString, QList<QQmlType>>::const_iterator iter = compositeTypes.constBegin();
1368 for (; iter != compositeTypes.constEnd(); ++iter)
1369 dumper.dumpComposite(engine: &engine, compositeType: iter.value(), versionInfo: info);
1370
1371 // define QEasingCurve as an extension of QQmlEasingValueType, this way
1372 // properties using the QEasingCurve type get useful type information.
1373 if (pluginImportUri.isEmpty())
1374 dumper.writeEasingCurve();
1375
1376 // Insert merge file.
1377 qml.write(data: mergeComponents);
1378
1379 qml.writeEndObject();
1380 qml.writeEndDocument();
1381
1382 if (!outputFilename.isEmpty()) {
1383 QFile file(outputFilename);
1384 if (file.open(flags: QIODevice::WriteOnly)) {
1385 QTextStream stream(&file);
1386 stream << bytes.constData();
1387 }
1388 } else {
1389 std::cout << bytes.constData() << std::flush;
1390 }
1391
1392 // workaround to avoid crashes on exit
1393 QTimer timer;
1394 timer.setSingleShot(true);
1395 timer.setInterval(0);
1396 QObject::connect(sender: &timer, SIGNAL(timeout()), receiver: app.data(), SLOT(quit()));
1397 timer.start();
1398
1399 return app->exec();
1400}
1401

source code of qtdeclarative/tools/qmlplugindump/main.cpp