/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qmlstreamwriter.h" #ifdef QT_SIMULATOR #include #endif #ifdef Q_OS_UNIX #include #endif QString pluginImportPath; bool verbose = false; bool creatable = true; QString currentProperty; QString inObjectInstantiation; void collectReachableMetaObjects(const QMetaObject *meta, QSet *metas, bool extended = false) { if (! meta || metas->contains(meta)) return; // dynamic meta objects can break things badly // but extended types are usually fine const QMetaObjectPrivate *mop = reinterpret_cast(meta->d.data); if (extended || !(mop->flags & DynamicMetaObject)) metas->insert(meta); collectReachableMetaObjects(meta->superClass(), metas); } void collectReachableMetaObjects(QObject *object, QSet *metas) { if (! object) return; const QMetaObject *meta = object->metaObject(); if (verbose) qDebug() << "Processing object" << meta->className(); collectReachableMetaObjects(meta, metas); for (int index = 0; index < meta->propertyCount(); ++index) { QMetaProperty prop = meta->property(index); if (QQmlMetaType::isQObject(prop.userType())) { if (verbose) qDebug() << " Processing property" << prop.name(); currentProperty = QString("%1::%2").arg(meta->className(), prop.name()); // if the property was not initialized during construction, // accessing a member of oo is going to cause a segmentation fault QObject *oo = QQmlMetaType::toQObject(prop.read(object)); if (oo && !metas->contains(oo->metaObject())) collectReachableMetaObjects(oo, metas); currentProperty.clear(); } } } void collectReachableMetaObjects(const QQmlType *ty, QSet *metas) { collectReachableMetaObjects(ty->metaObject(), metas, ty->isExtendedType()); if (ty->attachedPropertiesType()) collectReachableMetaObjects(ty->attachedPropertiesType(), metas); } /* We want to add the MetaObject for 'Qt' to the list, this is a simple way to access it. */ class FriendlyQObject: public QObject { public: static const QMetaObject *qtMeta() { return &staticQtMetaObject; } }; /* When we dump a QMetaObject, we want to list all the types it is exported as. To do this, we need to find the QQmlTypes associated with this QMetaObject. */ static QHash > qmlTypesByCppName; // No different versioning possible for a composite type. static QMap qmlTypesByCompositeName; static QHash cppToId; /* Takes a C++ type name, such as Qt::LayoutDirection or QString and maps it to how it should appear in the description file. These names need to be unique globally, so we don't change the C++ symbol's name much. It is mostly used to for explicit translations such as QString->string and translations for extended QML objects. */ QByteArray convertToId(const QByteArray &cppName) { return cppToId.value(cppName, cppName); } QByteArray convertToId(const QMetaObject *mo) { QByteArray className(mo->className()); if (!className.isEmpty()) return convertToId(className); // likely a metaobject generated for an extended qml object if (mo->superClass()) { className = convertToId(mo->superClass()); className.append("_extended"); return className; } static QHash generatedNames; className = generatedNames.value(mo); if (!className.isEmpty()) return className; qWarning() << "Found a QMetaObject without a className, generating a random name"; className = QByteArray("error-unknown-name-"); className.append(QByteArray::number(generatedNames.size())); generatedNames.insert(mo, className); return className; } QSet collectReachableMetaObjects(QQmlEngine *engine, const QList &skip = QList()) { QSet metas; metas.insert(FriendlyQObject::qtMeta()); QHash > extensions; foreach (const QQmlType *ty, QQmlMetaType::qmlTypes()) { if (!ty->isComposite()) { qmlTypesByCppName[ty->metaObject()->className()].insert(ty); if (ty->isExtendedType()) extensions[ty->typeName()].insert(ty->metaObject()->className()); collectReachableMetaObjects(ty, &metas); } else { qmlTypesByCompositeName[ty->elementName()] = ty; } } // Adjust exports of the base object if there are extensions. // For each export of a base object there can be a single extension object overriding it. // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export // of QGraphicsWidget. foreach (const QByteArray &baseCpp, extensions.keys()) { QSet baseExports = qmlTypesByCppName.value(baseCpp); const QSet extensionCppNames = extensions.value(baseCpp); foreach (const QByteArray &extensionCppName, extensionCppNames) { const QSet extensionExports = qmlTypesByCppName.value(extensionCppName); // remove extension exports from base imports // unfortunately the QQmlType pointers don't match, so can't use QSet::subtract QSet newBaseExports; foreach (const QQmlType *baseExport, baseExports) { bool match = false; foreach (const QQmlType *extensionExport, extensionExports) { if (baseExport->qmlTypeName() == extensionExport->qmlTypeName() && baseExport->majorVersion() == extensionExport->majorVersion() && baseExport->minorVersion() == extensionExport->minorVersion()) { match = true; break; } } if (!match) newBaseExports.insert(baseExport); } baseExports = newBaseExports; } qmlTypesByCppName[baseCpp] = baseExports; } if (creatable) { // find even more QMetaObjects by instantiating QML types and running // over the instances foreach (QQmlType *ty, QQmlMetaType::qmlTypes()) { if (skip.contains(ty)) continue; if (ty->isExtendedType()) continue; if (!ty->isCreatable()) continue; if (ty->typeName() == "QQmlComponent") continue; QString tyName = ty->qmlTypeName(); tyName = tyName.mid(tyName.lastIndexOf(QLatin1Char('/')) + 1); if (tyName.isEmpty()) continue; inObjectInstantiation = tyName; QObject *object = 0; if (ty->isSingleton()) { QQmlType::SingletonInstanceInfo *siinfo = ty->singletonInstanceInfo(); if (!siinfo) { qWarning() << "Internal error, " << tyName << "(" << QString::fromUtf8(ty->typeName()) << ")" << " is singleton, but has no singletonInstanceInfo"; continue; } if (siinfo->qobjectCallback) { if (verbose) qDebug() << "Trying to get singleton for " << tyName << " (" << siinfo->typeName << ")"; siinfo->init(engine); collectReachableMetaObjects(object, &metas); object = siinfo->qobjectApi(engine); } else { inObjectInstantiation.clear(); continue; // we don't handle QJSValue singleton types. } } else { if (verbose) qDebug() << "Trying to create object " << tyName << " (" << QString::fromUtf8(ty->typeName()) << ")"; object = ty->create(); } inObjectInstantiation.clear(); if (object) { if (verbose) qDebug() << "Got " << tyName << " (" << QString::fromUtf8(ty->typeName()) << ")"; collectReachableMetaObjects(object, &metas); } else { qWarning() << "Could not create" << tyName; } } } return metas; } class Dumper { QmlStreamWriter *qml; QString relocatableModuleUri; public: Dumper(QmlStreamWriter *qml) : qml(qml) {} void setRelocatableModuleUri(const QString &uri) { relocatableModuleUri = uri; } const QString getExportString(QString qmlTyName, int majorVersion, int minorVersion) { if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) { qmlTyName.remove(0, relocatableModuleUri.size() + 1); } if (qmlTyName.startsWith("./")) { qmlTyName.remove(0, 2); } if (qmlTyName.startsWith("/")) { qmlTyName.remove(0, 1); } const QString exportString = enquote( QString("%1 %2.%3").arg( qmlTyName, QString::number(majorVersion), QString::number(minorVersion))); return exportString; } void writeMetaContent(const QMetaObject *meta) { QSet implicitSignals; for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) { const QMetaProperty &property = meta->property(index); dump(property); implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name()))); } if (meta == &QObject::staticMetaObject) { // for QObject, hide deleteLater() and onDestroyed for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) { QMetaMethod method = meta->method(index); QByteArray signature = method.methodSignature(); if (signature == QByteArrayLiteral("destroyed(QObject*)") || signature == QByteArrayLiteral("destroyed()") || signature == QByteArrayLiteral("deleteLater()")) continue; dump(method, implicitSignals); } // and add toString(), destroy() and destroy(int) qml->writeStartObject(QLatin1String("Method")); qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("toString"))); qml->writeEndObject(); qml->writeStartObject(QLatin1String("Method")); qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy"))); qml->writeEndObject(); qml->writeStartObject(QLatin1String("Method")); qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy"))); qml->writeStartObject(QLatin1String("Parameter")); qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("delay"))); qml->writeScriptBinding(QLatin1String("type"), enquote(QLatin1String("int"))); qml->writeEndObject(); qml->writeEndObject(); } else { for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) dump(meta->method(index), implicitSignals); } } QString getPrototypeNameForCompositeType(const QMetaObject *metaObject, QSet &defaultReachableNames) { QString prototypeName; if (!defaultReachableNames.contains(metaObject->className())) { const QMetaObject *superMetaObject = metaObject->superClass(); if (!superMetaObject) prototypeName = "QObject"; else prototypeName = getPrototypeNameForCompositeType(superMetaObject, defaultReachableNames); } else { prototypeName = convertToId(metaObject->className()); } return prototypeName; } void dumpComposite(QQmlEngine *engine, const QQmlType *compositeType, QSet &defaultReachableNames) { QQmlComponent e(engine, compositeType->sourceUrl()); QObject *object = e.create(); if (!object) return; qml->writeStartObject("Component"); const QMetaObject *mainMeta = object->metaObject(); // Get C++ base class name for the composite type QString prototypeName = getPrototypeNameForCompositeType(mainMeta, defaultReachableNames); qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName)); QString qmlTyName = compositeType->qmlTypeName(); // name should be unique qml->writeScriptBinding(QLatin1String("name"), enquote(qmlTyName)); const QString exportString = getExportString(qmlTyName, compositeType->majorVersion(), compositeType->minorVersion()); qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString); qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList() << QString::number(compositeType->minorVersion())); for (int index = mainMeta->classInfoCount() - 1 ; index >= 0 ; --index) { QMetaClassInfo classInfo = mainMeta->classInfo(index); if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value()))); break; } } QSet metas; QSet candidatesComposite; collectReachableMetaObjects(mainMeta, &candidatesComposite); // Also eliminate meta objects with the same classname. // This is required because extended objects seem not to share // a single meta object instance. foreach (const QMetaObject *mo, candidatesComposite) { if (!defaultReachableNames.contains(mo->className())) metas.insert(mo); } // put the metaobjects into a map so they are always dumped in the same order QMap nameToMeta; foreach (const QMetaObject *meta, metas) nameToMeta.insert(convertToId(meta), meta); foreach (const QMetaObject *meta, nameToMeta) writeMetaContent(meta); qml->writeEndObject(); } void dump(const QMetaObject *meta) { qml->writeStartObject("Component"); QByteArray id = convertToId(meta); qml->writeScriptBinding(QLatin1String("name"), enquote(id)); for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) { QMetaClassInfo classInfo = meta->classInfo(index); if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value()))); break; } } if (meta->superClass()) qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()))); QSet qmlTypes = qmlTypesByCppName.value(meta->className()); if (!qmlTypes.isEmpty()) { QHash exports; foreach (const QQmlType *qmlTy, qmlTypes) { const QString exportString = getExportString(qmlTy->qmlTypeName(), qmlTy->majorVersion(), qmlTy->minorVersion()); exports.insert(exportString, qmlTy); } // ensure exports are sorted and don't change order when the plugin is dumped again QStringList exportStrings = exports.keys(); std::sort(exportStrings.begin(), exportStrings.end()); qml->writeArrayBinding(QLatin1String("exports"), exportStrings); // write meta object revisions QStringList metaObjectRevisions; foreach (const QString &exportString, exportStrings) { int metaObjectRevision = exports[exportString]->metaObjectRevision(); metaObjectRevisions += QString::number(metaObjectRevision); } qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions); if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) { // Can happen when a type is registered that returns itself as attachedPropertiesType() // because there is no creatable type to attach to. if (attachedType != meta) { qml->writeScriptBinding(QLatin1String("attachedType"), enquote( convertToId(attachedType))); } } } for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index) dump(meta->enumerator(index)); writeMetaContent(meta); qml->writeEndObject(); } void writeEasingCurve() { qml->writeStartObject(QLatin1String("Component")); qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve"))); qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QQmlEasingValueType"))); qml->writeEndObject(); } private: static QString enquote(const QString &string) { return QString("\"%1\"").arg(string); } /* Removes pointer and list annotations from a type name, returning what was removed in isList and isPointer */ static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer) { static QByteArray declListPrefix = "QQmlListProperty<"; if (typeName->endsWith('*')) { *isPointer = true; typeName->truncate(typeName->length() - 1); removePointerAndList(typeName, isList, isPointer); } else if (typeName->startsWith(declListPrefix)) { *isList = true; typeName->truncate(typeName->length() - 1); // get rid of the suffix '>' *typeName = typeName->mid(declListPrefix.size()); removePointerAndList(typeName, isList, isPointer); } *typeName = convertToId(*typeName); } void writeTypeProperties(QByteArray typeName, bool isWritable) { bool isList = false, isPointer = false; removePointerAndList(&typeName, &isList, &isPointer); qml->writeScriptBinding(QLatin1String("type"), enquote(typeName)); if (isList) qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true")); if (!isWritable) qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true")); if (isPointer) qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true")); } void dump(const QMetaProperty &prop) { qml->writeStartObject("Property"); qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name()))); #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4)) if (int revision = prop.revision()) qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision)); #endif writeTypeProperties(prop.typeName(), prop.isWritable()); qml->writeEndObject(); } void dump(const QMetaMethod &meth, const QSet &implicitSignals) { if (meth.methodType() == QMetaMethod::Signal) { if (meth.access() != QMetaMethod::Public) return; // nothing to do. } else if (meth.access() != QMetaMethod::Public) { return; // nothing to do. } QByteArray name = meth.name(); const QString typeName = convertToId(meth.typeName()); if (implicitSignals.contains(name) && !meth.revision() && meth.methodType() == QMetaMethod::Signal && meth.parameterNames().isEmpty() && typeName == QLatin1String("void")) { // don't mention implicit signals return; } if (meth.methodType() == QMetaMethod::Signal) qml->writeStartObject(QLatin1String("Signal")); else qml->writeStartObject(QLatin1String("Method")); qml->writeScriptBinding(QLatin1String("name"), enquote(name)); #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4)) if (int revision = meth.revision()) qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision)); #endif if (typeName != QLatin1String("void")) qml->writeScriptBinding(QLatin1String("type"), enquote(typeName)); for (int i = 0; i < meth.parameterTypes().size(); ++i) { QByteArray argName = meth.parameterNames().at(i); qml->writeStartObject(QLatin1String("Parameter")); if (! argName.isEmpty()) qml->writeScriptBinding(QLatin1String("name"), enquote(argName)); writeTypeProperties(meth.parameterTypes().at(i), true); qml->writeEndObject(); } qml->writeEndObject(); } void dump(const QMetaEnum &e) { qml->writeStartObject(QLatin1String("Enum")); qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name()))); QList > namesValues; for (int index = 0; index < e.keyCount(); ++index) { namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index)))); } qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues); qml->writeEndObject(); } }; enum ExitCode { EXIT_INVALIDARGUMENTS = 1, EXIT_SEGV = 2, EXIT_IMPORTERROR = 3 }; #ifdef Q_OS_UNIX void sigSegvHandler(int) { fprintf(stderr, "Error: SEGV\n"); if (!currentProperty.isEmpty()) fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData()); if (!inObjectInstantiation.isEmpty()) fprintf(stderr, "While instantiating the object '%s'.\n", inObjectInstantiation.toLatin1().constData()); exit(EXIT_SEGV); } #endif void printUsage(const QString &appName) { qWarning() << qPrintable(QString( "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] module.uri version [module/import/path]\n" " %1 [-v] [-noinstantiate] -path path/to/qmldir/directory [version]\n" " %1 [-v] -builtins\n" "Example: %1 Qt.labs.folderlistmodel 2.0 /home/user/dev/qt-install/imports").arg( appName)); } int main(int argc, char *argv[]) { #ifdef Q_OS_UNIX // qmldump may crash, but we don't want any crash handlers to pop up // therefore we intercept the segfault and just exit() ourselves struct sigaction sigAction; sigemptyset(&sigAction.sa_mask); sigAction.sa_handler = &sigSegvHandler; sigAction.sa_flags = 0; sigaction(SIGSEGV, &sigAction, 0); #endif #ifdef QT_SIMULATOR // Running this application would bring up the Qt Simulator (since it links Qt GUI), avoid that! QtSimulatorPrivate::SimulatorConnection::createStubInstance(); #endif // don't require a window manager even though we're a QGuiApplication bool requireWindowManager = false; for (int index = 1; index < argc; ++index) { if (QString::fromLocal8Bit(argv[index]) == "--defaultplatform" || QString::fromLocal8Bit(argv[index]) == "-defaultplatform") { requireWindowManager = true; break; } } if (!requireWindowManager) qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal")); QGuiApplication app(argc, argv); const QStringList args = app.arguments(); const QString appName = QFileInfo(app.applicationFilePath()).baseName(); if (args.size() < 2) { printUsage(appName); return EXIT_INVALIDARGUMENTS; } QString pluginImportUri; QString pluginImportVersion; bool relocatable = true; enum Action { Uri, Path, Builtins }; Action action = Uri; { QStringList positionalArgs; foreach (const QString &arg, args) { if (!arg.startsWith(QLatin1Char('-'))) { positionalArgs.append(arg); continue; } if (arg == QLatin1String("--notrelocatable") || arg == QLatin1String("-notrelocatable") || arg == QLatin1String("--nonrelocatable") || arg == QLatin1String("-nonrelocatable")) { relocatable = false; } else if (arg == QLatin1String("--relocatable") || arg == QLatin1String("-relocatable")) { relocatable = true; } else if (arg == QLatin1String("--noinstantiate") || arg == QLatin1String("-noinstantiate")) { creatable = false; } else if (arg == QLatin1String("--path") || arg == QLatin1String("-path")) { action = Path; } else if (arg == QLatin1String("--builtins") || arg == QLatin1String("-builtins")) { action = Builtins; } else if (arg == QLatin1String("-v")) { verbose = true; } else if (arg == QLatin1String("--defaultplatform") || arg == QLatin1String("-defaultplatform")) { continue; } else { qWarning() << "Invalid argument: " << arg; return EXIT_INVALIDARGUMENTS; } } if (action == Uri) { if (positionalArgs.size() != 3 && positionalArgs.size() != 4) { qWarning() << "Incorrect number of positional arguments"; return EXIT_INVALIDARGUMENTS; } pluginImportUri = positionalArgs[1]; pluginImportVersion = positionalArgs[2]; if (positionalArgs.size() >= 4) pluginImportPath = positionalArgs[3]; } else if (action == Path) { if (positionalArgs.size() != 2 && positionalArgs.size() != 3) { qWarning() << "Incorrect number of positional arguments"; return EXIT_INVALIDARGUMENTS; } pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]); if (positionalArgs.size() == 3) pluginImportVersion = positionalArgs[2]; } else if (action == Builtins) { if (positionalArgs.size() != 1) { qWarning() << "Incorrect number of positional arguments"; return EXIT_INVALIDARGUMENTS; } } } QQmlEngine engine; if (!pluginImportPath.isEmpty()) { QDir cur = QDir::current(); cur.cd(pluginImportPath); pluginImportPath = cur.absolutePath(); QDir::setCurrent(pluginImportPath); engine.addImportPath(pluginImportPath); } // load the QtQuick 2 plugin { QByteArray code("import QtQuick 2.0\nQtObject {}"); QQmlComponent c(&engine); c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/loadqtquick2.qml")); c.create(); if (!c.errors().isEmpty()) { foreach (const QQmlError &error, c.errors()) qWarning() << error.toString(); return EXIT_IMPORTERROR; } } // find all QMetaObjects reachable from the builtin module QSet defaultReachable = collectReachableMetaObjects(&engine); QList defaultTypes = QQmlMetaType::qmlTypes(); // add some otherwise unreachable QMetaObjects defaultReachable.insert(&QQuickMouseEvent::staticMetaObject); // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported QSet defaultReachableNames; // this will hold the meta objects we want to dump information of QSet metas; if (action == Builtins) { metas = defaultReachable; } else { // find a valid QtQuick import QByteArray importCode; QQmlType *qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject); if (!qtObjectType) { qWarning() << "Could not find QtObject type"; importCode = QByteArray("import QtQuick 2.0\n"); } else { QString module = qtObjectType->qmlTypeName(); module = module.mid(0, module.lastIndexOf(QLatin1Char('/'))); importCode = QString("import %1 %2.%3\n").arg(module, QString::number(qtObjectType->majorVersion()), QString::number(qtObjectType->minorVersion())).toUtf8(); } // find all QMetaObjects reachable when the specified module is imported if (action != Path) { importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toLatin1(); } else { // pluginImportVersion can be empty importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toLatin1(); } // create a component with these imports to make sure the imports are valid // and to populate the declarative meta type system { QByteArray code = importCode; code += "QtObject {}"; QQmlComponent c(&engine); c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml")); c.create(); if (!c.errors().isEmpty()) { foreach (const QQmlError &error, c.errors()) qWarning() << error.toString(); return EXIT_IMPORTERROR; } } QSet candidates = collectReachableMetaObjects(&engine, defaultTypes); candidates.subtract(defaultReachable); // Also eliminate meta objects with the same classname. // This is required because extended objects seem not to share // a single meta object instance. foreach (const QMetaObject *mo, defaultReachable) defaultReachableNames.insert(QByteArray(mo->className())); foreach (const QMetaObject *mo, candidates) { if (!defaultReachableNames.contains(mo->className())) metas.insert(mo); } } // setup static rewrites of type names cppToId.insert("QString", "string"); cppToId.insert("QQmlEasingValueType::Type", "Type"); // start dumping data QByteArray bytes; QmlStreamWriter qml(&bytes); qml.writeStartDocument(); qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 1); qml.write(QString("\n" "// This file describes the plugin-supplied types contained in the library.\n" "// It is used for QML tooling purposes only.\n" "//\n" "// This file was auto-generated by:\n" "// '%1 %2'\n" "\n").arg(QFileInfo(args.at(0)).fileName()).arg(QStringList(args.mid(1)).join(QLatin1String(" ")))); qml.writeStartObject("Module"); // put the metaobjects into a map so they are always dumped in the same order QMap nameToMeta; foreach (const QMetaObject *meta, metas) nameToMeta.insert(convertToId(meta), meta); Dumper dumper(&qml); if (relocatable) dumper.setRelocatableModuleUri(pluginImportUri); foreach (const QMetaObject *meta, nameToMeta) { dumper.dump(meta); } foreach (const QQmlType *compositeType, qmlTypesByCompositeName) dumper.dumpComposite(&engine, compositeType, defaultReachableNames); // define QEasingCurve as an extension of QQmlEasingValueType, this way // properties using the QEasingCurve type get useful type information. if (pluginImportUri.isEmpty()) dumper.writeEasingCurve(); qml.writeEndObject(); qml.writeEndDocument(); std::cout << bytes.constData() << std::flush; // workaround to avoid crashes on exit QTimer timer; timer.setSingleShot(true); timer.setInterval(0); QObject::connect(&timer, SIGNAL(timeout()), &app, SLOT(quit())); timer.start(); return app.exec(); }