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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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