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

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