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

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