summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-10-01 14:43:37 +0200
committerUlf Hermann <ulf.hermann@qt.io>2019-10-14 10:42:29 +0200
commitee62b2824fe91e95a3f8218b93ba55a2ef6660d0 (patch)
treeff403362ee4b73c9d90c640d29bf22a0aba94c23 /tools
parent8cb08ca42157bb43b523103a6d1be94534be0597 (diff)
downloadqtdeclarative-ee62b2824fe91e95a3f8218b93ba55a2ef6660d0.tar.gz
Add support for semi-automatic QML type registrations
We can use the new moc JSON output to collect all meta-objects at build time and, for those that include QML element registration meta-data, generate code that automatically registers these types with QML. This eliminates the need to call qmlRegisterType manually. For now this generates free-standing functions (per module) that need to be called manually. This is intended as an intermediate step. Task-number: QTBUG-68796 Change-Id: Ib414eef9757344feee488ebc7388f957b975347f Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmlplugindump/qmlplugindump.pro10
-rw-r--r--tools/qmltyperegistrar/qmltyperegistrar.cpp404
-rw-r--r--tools/qmltyperegistrar/qmltyperegistrar.pro26
-rw-r--r--tools/qmltyperegistrar/qmltypes.prf66
-rw-r--r--tools/qmltyperegistrar/qmltypesclassdescription.cpp161
-rw-r--r--tools/qmltyperegistrar/qmltypesclassdescription.h58
-rw-r--r--tools/qmltyperegistrar/qmltypescreator.cpp357
-rw-r--r--tools/qmltyperegistrar/qmltypescreator.h68
-rw-r--r--tools/shared/qmlstreamwriter.cpp (renamed from tools/qmlplugindump/qmlstreamwriter.cpp)0
-rw-r--r--tools/shared/qmlstreamwriter.h (renamed from tools/qmlplugindump/qmlstreamwriter.h)0
-rw-r--r--tools/tools.pro2
11 files changed, 1148 insertions, 4 deletions
diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro
index 62b08e9334..e374ae45f4 100644
--- a/tools/qmlplugindump/qmlplugindump.pro
+++ b/tools/qmlplugindump/qmlplugindump.pro
@@ -5,14 +5,16 @@ CONFIG += no_import_scan
QTPLUGIN.platforms = qminimal
+INCLUDEPATH += ../shared
+
SOURCES += \
main.cpp \
- qmlstreamwriter.cpp \
- qmltypereader.cpp
+ qmltypereader.cpp \
+ ../shared/qmlstreamwriter.cpp
HEADERS += \
- qmlstreamwriter.h \
- qmltypereader.h
+ qmltypereader.h \
+ ../shared/qmlstreamwriter.h
macx {
# Prevent qmlplugindump from popping up in the dock when launched.
diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp
new file mode 100644
index 0000000000..2cf309ff0d
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmltypescreator.h"
+
+#include <QCoreApplication>
+#include <QCommandLineParser>
+#include <QtDebug>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <QJsonObject>
+#include <QFile>
+#include <QScopedPointer>
+#include <QSaveFile>
+#include <QQueue>
+
+#include <cstdlib>
+
+struct ScopedPointerFileCloser
+{
+ static inline void cleanup(FILE *handle) { if (handle) fclose(handle); }
+};
+
+static bool acceptClassForQmlTypeRegistration(const QJsonObject &classDef)
+{
+ const QJsonArray classInfos = classDef[QLatin1String("classInfos")].toArray();
+ for (const QJsonValue &info: classInfos) {
+ if (info[QLatin1String("name")].toString().startsWith(QLatin1String("QML.")))
+ return true;
+ }
+ return false;
+}
+
+static QVector<QJsonObject> foreignRelatedTypes(const QVector<QJsonObject> &types,
+ const QVector<QJsonObject> &foreignTypes)
+{
+ const QLatin1String classInfosKey("classInfos");
+ const QLatin1String nameKey("name");
+ const QLatin1String qualifiedClassNameKey("qualifiedClassName");
+ const QLatin1String qmlNamePrefix("QML.");
+ const QLatin1String qmlForeignName("QML.Foreign");
+ const QLatin1String qmlAttachedName("QML.Attached");
+ const QLatin1String valueKey("value");
+ const QLatin1String superClassesKey("superClasses");
+ const QLatin1String accessKey("access");
+ const QLatin1String publicAccess("public");
+
+ QSet<QString> processedRelatedNames;
+ QQueue<QJsonObject> typeQueue;
+ typeQueue.append(types.toList());
+ QVector<QJsonObject> relatedTypes;
+
+ // First mark all classes registered from this module as already processed.
+ for (const QJsonObject &type : types) {
+ processedRelatedNames.insert(type.value(qualifiedClassNameKey).toString());
+ const auto classInfos = type.value(classInfosKey).toArray();
+ for (const QJsonValue &classInfo : classInfos) {
+ const QJsonObject obj = classInfo.toObject();
+ if (obj.value(nameKey).toString() == qmlForeignName) {
+ processedRelatedNames.insert(obj.value(valueKey).toString());
+ break;
+ }
+ }
+ }
+
+ // Then mark all classes registered from other modules as already processed.
+ // We don't want to generate them again for this module.
+ for (const QJsonObject &foreignType : foreignTypes) {
+ const auto classInfos = foreignType.value(classInfosKey).toArray();
+ bool seenQmlPrefix = false;
+ for (const QJsonValue &classInfo : classInfos) {
+ const QJsonObject obj = classInfo.toObject();
+ const QString name = obj.value(nameKey).toString();
+ if (!seenQmlPrefix && name.startsWith(qmlNamePrefix)) {
+ processedRelatedNames.insert(foreignType.value(qualifiedClassNameKey).toString());
+ seenQmlPrefix = true;
+ }
+ if (name == qmlForeignName) {
+ processedRelatedNames.insert(obj.value(valueKey).toString());
+ break;
+ }
+ }
+ }
+
+ auto addType = [&](const QString &typeName) {
+ if (processedRelatedNames.contains(typeName))
+ return;
+ processedRelatedNames.insert(typeName);
+ if (const QJsonObject *other = QmlTypesClassDescription::findType(foreignTypes, typeName)) {
+ relatedTypes.append(*other);
+ typeQueue.enqueue(*other);
+ }
+ };
+
+ // Then recursively iterate the super types and attached types, marking the
+ // ones we are interested in as related.
+ while (!typeQueue.isEmpty()) {
+ const QJsonObject classDef = typeQueue.dequeue();
+
+ const auto classInfos = classDef.value(classInfosKey).toArray();
+ for (const QJsonValue &classInfo : classInfos) {
+ const QJsonObject obj = classInfo.toObject();
+ if (obj.value(nameKey).toString() == qmlAttachedName) {
+ addType(obj.value(valueKey).toString());
+ } else if (obj.value(nameKey).toString() == qmlForeignName) {
+ const QString foreignClassName = obj.value(valueKey).toString();
+ if (const QJsonObject *other = QmlTypesClassDescription::findType(
+ foreignTypes, foreignClassName)) {
+ const auto otherSupers = other->value(superClassesKey).toArray();
+ if (!otherSupers.isEmpty()) {
+ const QJsonObject otherSuperObject = otherSupers.first().toObject();
+ if (otherSuperObject.value(accessKey).toString() == publicAccess)
+ addType(otherSuperObject.value(nameKey).toString());
+ }
+
+ const auto otherClassInfos = other->value(classInfosKey).toArray();
+ for (const QJsonValue &otherClassInfo : otherClassInfos) {
+ const QJsonObject obj = otherClassInfo.toObject();
+ if (obj.value(nameKey).toString() == qmlAttachedName) {
+ addType(obj.value(valueKey).toString());
+ break;
+ }
+ // No, you cannot chain QML_FOREIGN declarations. Sorry.
+ }
+ break;
+ }
+ }
+ }
+
+ const auto supers = classDef.value(superClassesKey).toArray();
+ if (!supers.isEmpty()) {
+ const QJsonObject superObject = supers.first().toObject();
+ if (superObject.value(accessKey).toString() == publicAccess)
+ addType(superObject.value(nameKey).toString());
+ }
+ }
+
+ return relatedTypes;
+}
+
+int main(int argc, char **argv)
+{
+ // Produce reliably the same output for the same input by disabling QHash's random seeding.
+ qSetGlobalQHashSeed(0);
+
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("qmltyperegistrar"));
+ QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
+
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption outputOption(QStringLiteral("o"));
+ outputOption.setDescription(QStringLiteral("Write output to specified file."));
+ outputOption.setValueName(QStringLiteral("file"));
+ outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
+ parser.addOption(outputOption);
+
+ QCommandLineOption privateIncludesOption(
+ QStringLiteral("private-includes"),
+ QStringLiteral("Include headers ending in \"_p.h\" using \"#include <private/foo_p.h>\""
+ "rather than \"#include <foo_p.h>\"."));
+ parser.addOption(privateIncludesOption);
+
+ QCommandLineOption importNameOption(QStringLiteral("import-name"));
+ importNameOption.setDescription(QStringLiteral("Name of the module to use with QML type registrations."));
+ importNameOption.setValueName(QStringLiteral("QML module name"));
+ parser.addOption(importNameOption);
+
+ QCommandLineOption majorVersionOption(QStringLiteral("major-version"));
+ majorVersionOption.setDescription(QStringLiteral("Major version to use for type registrations."));
+ majorVersionOption.setValueName(QStringLiteral("major version"));
+ parser.addOption(majorVersionOption);
+
+ QCommandLineOption pluginTypesOption(QStringLiteral("generate-plugintypes"));
+ pluginTypesOption.setDescription(QStringLiteral("Generate plugins.qmltypes into specified directory."));
+ pluginTypesOption.setValueName(QStringLiteral("qmltypes target Directory"));
+ parser.addOption(pluginTypesOption);
+
+ QCommandLineOption foreignTypesOption(QStringLiteral("foreign-types"));
+ foreignTypesOption.setDescription(QStringLiteral("Consider foreign types when generating plugins.qmltypes."));
+ foreignTypesOption.setValueName(QStringLiteral("Comma separated list of other modules to consult for types."));
+ parser.addOption(foreignTypesOption);
+
+ QCommandLineOption dependenciesOption(QStringLiteral("dependencies"));
+ dependenciesOption.setDescription(QStringLiteral("Dependencies to be stated in plugins.qmltypes"));
+ dependenciesOption.setValueName(QStringLiteral("name of JSON file with dependencies"));
+ parser.addOption(dependenciesOption);
+
+ parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
+ QStringLiteral("MOC generated json output"));
+
+ parser.process(app);
+
+ FILE *output = stdout;
+ QScopedPointer<FILE, ScopedPointerFileCloser> outputFile;
+
+ if (parser.isSet(outputOption)) {
+ QString outputName = parser.value(outputOption);
+#if defined(_MSC_VER)
+ if (_wfopen_s(&output, reinterpret_cast<const wchar_t *>(outputName.utf16()), L"w") != 0) {
+#else
+ output = fopen(QFile::encodeName(outputName).constData(), "w"); // create output file
+ if (!output) {
+#endif
+ fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(outputName));
+ return EXIT_FAILURE;
+ }
+ outputFile.reset(output);
+ }
+
+ fprintf(output,
+ "/****************************************************************************\n"
+ "** Generated QML type registration code\n**\n");
+ fprintf(output,
+ "** WARNING! All changes made in this file will be lost!\n"
+ "*****************************************************************************/\n\n");
+ fprintf(output,
+ "#include <QtQml/qqmlengine.h>\n");
+
+ QStringList includes;
+ QVector<QJsonObject> types;
+ QVector<QJsonObject> foreignTypes;
+
+ const QString module = parser.value(importNameOption);
+ const QStringList files = parser.positionalArguments();
+ for (const QString &source: files) {
+ QJsonDocument metaObjects;
+ {
+ QFile f(source);
+ if (!f.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Error opening %s for reading\n", qPrintable(source));
+ return EXIT_FAILURE;
+ }
+ QJsonParseError error = {0, QJsonParseError::NoError};
+ metaObjects = QJsonDocument::fromJson(f.readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ fprintf(stderr, "Error parsing %s\n", qPrintable(source));
+ return EXIT_FAILURE;
+ }
+ }
+
+ auto processMetaObject = [&](const QJsonObject &metaObject) {
+ const QJsonArray classes = metaObject[QLatin1String("classes")].toArray();
+ for (const auto &cls : classes) {
+ QJsonObject classDef = cls.toObject();
+ if (acceptClassForQmlTypeRegistration(classDef)) {
+ const QString include = metaObject[QLatin1String("inputFile")].toString();
+ const bool declaredInHeader = include.endsWith(QLatin1String(".h"));
+ if (declaredInHeader) {
+ includes.append(include);
+ classDef.insert(QLatin1String("registerable"), true);
+ } else {
+ fprintf(stderr, "Cannot generate QML type registration for class %s "
+ "because it is not declared in a header.",
+ qPrintable(classDef.value(QLatin1String("qualifiedClassName"))
+ .toString()));
+ }
+ types.append(classDef);
+ } else {
+ foreignTypes.append(classDef);
+ }
+ }
+ };
+
+ if (metaObjects.isArray()) {
+ const QJsonArray metaObjectsArray = metaObjects.array();
+ for (const auto &metaObject : metaObjectsArray) {
+ if (!metaObject.isObject()) {
+ fprintf(stderr, "Error parsing %s: JSON is not an object\n",
+ qPrintable(source));
+ return EXIT_FAILURE;
+ }
+
+ processMetaObject(metaObject.toObject());
+ }
+ } else if (metaObjects.isObject()) {
+ processMetaObject(metaObjects.object());
+ } else {
+ fprintf(stderr, "Error parsing %s: JSON is not an object or an array\n",
+ qPrintable(source));
+ return EXIT_FAILURE;
+ }
+ }
+
+ const QLatin1String qualifiedClassNameKey("qualifiedClassName");
+ auto sortTypes = [&](QVector<QJsonObject> &types) {
+ std::sort(types.begin(), types.end(), [&](const QJsonObject &a, const QJsonObject &b) {
+ return a.value(qualifiedClassNameKey).toString() <
+ b.value(qualifiedClassNameKey).toString();
+ });
+ };
+
+ sortTypes(types);
+
+ fprintf(output, "\n#include <QtQml/qqmlmoduleregistration.h>");
+ const bool privateIncludes = parser.isSet(privateIncludesOption);
+ for (const QString &include : qAsConst(includes)) {
+ if (privateIncludes && include.endsWith(QLatin1String("_p.h")))
+ fprintf(output, "\n#include <private/%s>", qPrintable(include));
+ else
+ fprintf(output, "\n#include <%s>", qPrintable(include));
+ }
+
+ fprintf(output, "\n\n");
+
+ QString moduleAsSymbol = module;
+ moduleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
+
+ const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
+
+ fprintf(output, "void %s()\n{\n", qPrintable(functionName));
+ const auto majorVersion = parser.value(majorVersionOption);
+
+ for (const QJsonObject &classDef : qAsConst(types)) {
+ if (!classDef.value(QLatin1String("registerable")).toBool())
+ continue;
+
+ const QString className = classDef[QLatin1String("qualifiedClassName")].toString();
+ fprintf(output, "\n qmlRegisterTypesAndRevisions<%s>(\"%s\", %s);", qPrintable(className),
+ qPrintable(module), qPrintable(majorVersion));
+ }
+
+ fprintf(output, "\n qmlRegisterModule(\"%s\", %s, QT_VERSION_MINOR);",
+ qPrintable(module), qPrintable(majorVersion));
+ fprintf(output, "\n}\n");
+ fprintf(output, "static const QQmlModuleRegistration registration(\"%s\", %s, %s);\n",
+ qPrintable(module), qPrintable(majorVersion), qPrintable(functionName));
+
+ if (!parser.isSet(pluginTypesOption))
+ return EXIT_SUCCESS;
+
+ if (parser.isSet(foreignTypesOption)) {
+ const QStringList foreignTypesFiles = parser.value(foreignTypesOption)
+ .split(QLatin1Char(','));
+ for (const QString &types : foreignTypesFiles) {
+ QFile typesFile(types);
+ if (!typesFile.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Cannot open foreign types file %s\n", qPrintable(types));
+ continue;
+ }
+
+ QJsonParseError error = {0, QJsonParseError::NoError};
+ QJsonDocument foreignMetaObjects = QJsonDocument::fromJson(typesFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ fprintf(stderr, "Error parsing %s\n", qPrintable(types));
+ continue;
+ }
+
+ const QJsonArray foreignObjectsArray = foreignMetaObjects.array();
+ for (const auto &metaObject : foreignObjectsArray) {
+ if (!metaObject.isObject()) {
+ fprintf(stderr, "Error parsing %s: JSON is not an object\n",
+ qPrintable(types));
+ continue;
+ }
+
+ const QJsonArray classes = metaObject[QLatin1String("classes")].toArray();
+ for (const auto &cls : classes)
+ foreignTypes.append(cls.toObject());
+ }
+ }
+ }
+
+ sortTypes(foreignTypes);
+ types += foreignRelatedTypes(types, foreignTypes);
+ sortTypes(types);
+
+ QmlTypesCreator creator;
+ creator.setOwnTypes(std::move(types));
+ creator.setForeignTypes(std::move(foreignTypes));
+ creator.setModule(module);
+ creator.setMajorVersion(parser.value(majorVersionOption).toInt());
+
+ creator.generate(parser.value(pluginTypesOption), parser.value(dependenciesOption));
+ return EXIT_SUCCESS;
+}
diff --git a/tools/qmltyperegistrar/qmltyperegistrar.pro b/tools/qmltyperegistrar/qmltyperegistrar.pro
new file mode 100644
index 0000000000..802526d964
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltyperegistrar.pro
@@ -0,0 +1,26 @@
+option(host_build)
+
+QT = core-private
+DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
+
+QMAKE_TARGET_DESCRIPTION = QML Types Registrar
+
+INCLUDEPATH += ../shared
+
+SOURCES += \
+ qmltyperegistrar.cpp \
+ ../shared/qmlstreamwriter.cpp \
+ qmltypesclassdescription.cpp \
+ qmltypescreator.cpp
+
+HEADERS += \
+ ../shared/qmlstreamwriter.h \
+ qmltypesclassdescription.h \
+ qmltypescreator.h
+
+build_integration.files = qmltypes.prf
+build_integration.path = $$[QT_HOST_DATA]/mkspecs/features
+prefix_build: INSTALLS += build_integration
+else: COPIES += build_integration
+
+load(qt_tool)
diff --git a/tools/qmltyperegistrar/qmltypes.prf b/tools/qmltyperegistrar/qmltypes.prf
new file mode 100644
index 0000000000..495cc1b0cd
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypes.prf
@@ -0,0 +1,66 @@
+CONFIG += metatypes
+
+qtPrepareTool(QML_TYPEREGISTRAR, qmltyperegistrar)
+
+# from moc.prf
+isEmpty(QML_IMPORT_MAJOR_VERSION):!isEmpty(IMPORT_VERSION): \
+ QML_IMPORT_MAJOR_VERSION = $$section(IMPORT_VERSION, ., 0, 0)
+isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) {
+ QML_IMPORT_NAME = $$replace(TARGETPATH, "/", ".")
+ QML_IMPORT_NAME = $$replace(QML_IMPORT_NAME, .$${QML_IMPORT_MAJOR_VERSION}$, '')
+}
+
+isEmpty(QMLTYPES_FILENAME) {
+ plugin: QMLTYPES_FILENAME = $$OUT_PWD/plugins.qmltypes
+ else: QMLTYPES_FILENAME = $$OUT_PWD/$${TEMPLATE}.qmltypes
+}
+
+qt_module_deps = $$replace(QT, -private$, '')
+qt_module_deps += $$replace(QT_PRIVATE, -private$, '')
+qt_module_deps = $$replace(qt_module_deps, _private$, '')
+all_qt_module_deps = $$resolve_depends(qt_module_deps, "QT.", ".depends" ".run_depends")
+foreign_types =
+for(dep, all_qt_module_deps): \
+ foreign_types += $$[QT_INSTALL_LIBS]/metatypes/$$lower($$eval(QT.$${dep}.module))_metatypes.json
+
+QML_TYPEREGISTRAR_FLAGS = \
+ --generate-plugintypes=$$QMLTYPES_FILENAME \
+ --import-name=$$QML_IMPORT_NAME \
+ --major-version=$$QML_IMPORT_MAJOR_VERSION \
+ --foreign-types=$$join(foreign_types, ',')
+
+DEPENDENCIESFILE = $$_PRO_FILE_PWD_/dependencies.json
+exists($$DEPENDENCIESFILE): QML_TYPEREGISTRAR_FLAGS += --dependencies=$$DEPENDENCIESFILE
+
+!isEmpty(MODULE_PRIVATE_INCLUDES): QML_TYPEREGISTRAR_FLAGS += --private-includes
+
+METATYPES_JSON = $$lower($$basename(TARGET))_metatypes.json
+
+TYPEREGISTRATIONS = $$lower($$basename(TARGET))_qmltyperegistrations$${first(QMAKE_EXT_CPP)}
+
+qmltyperegistrar_compiler.CONFIG += combine
+qmltyperegistrar_compiler.commands = \
+ $$QML_TYPEREGISTRAR $$QML_TYPEREGISTRAR_FLAGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
+qmltyperegistrar_compiler.input = METATYPES_JSON
+qmltyperegistrar_compiler.output = $$TYPEREGISTRATIONS
+qmltyperegistrar_compiler.variable_out = SOURCES
+qmltyperegistrar_compiler.name = Automatic QML type registration
+qmltyperegistrar_compiler.dependency_type = TYPE_C
+
+qmltyperegistrar_qmltypes.input = METATYPES_JSON
+qmltyperegistrar_qmltypes.depends = $$TYPEREGISTRATIONS
+qmltyperegistrar_qmltypes.output = $$QMLTYPES_FILENAME
+qmltyperegistrar_qmltypes.CONFIG = no_link
+qmltyperegistrar_qmltypes.commands = $$escape_expand(\\n) # force creation of rule
+
+install_qmltypes {
+ isEmpty(QMLTYPES_INSTALL_DIR): \
+ QMLTYPES_INSTALL_DIR = $$[QT_INSTALL_QML]/$$TARGETPATH
+ do_install.files = $$QMLTYPES_FILENAME
+ do_install.path = $$QMLTYPES_INSTALL_DIR
+ do_install.CONFIG += no_link
+ prefix_build: INSTALLS += do_install
+ else: COPIES += do_install
+}
+
+QMAKE_EXTRA_COMPILERS += qmltyperegistrar_compiler qmltyperegistrar_qmltypes
diff --git a/tools/qmltyperegistrar/qmltypesclassdescription.cpp b/tools/qmltyperegistrar/qmltypesclassdescription.cpp
new file mode 100644
index 0000000000..8189bcd52e
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypesclassdescription.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmltypesclassdescription.h"
+
+#include <QtCore/qjsonarray.h>
+
+static void collectExtraVersions(const QJsonObject *component, const QString &key,
+ QList<int> &extraVersions)
+{
+ const QJsonArray &items = component->value(key).toArray();
+ for (const QJsonValue &item : items) {
+ const QJsonObject obj = item.toObject();
+ const auto revision = obj.find(QLatin1String("revision"));
+ if (revision != obj.end()) {
+ const int extraVersion = revision.value().toInt();
+ if (!extraVersions.contains(extraVersion))
+ extraVersions.append(extraVersion);
+ }
+ }
+}
+
+const QJsonObject *QmlTypesClassDescription::findType(const QVector<QJsonObject> &types,
+ const QString &name)
+{
+ static const QLatin1String qualifiedClassNameKey("qualifiedClassName");
+ auto it = std::lower_bound(types.begin(), types.end(), name,
+ [&](const QJsonObject &type, const QString &typeName) {
+ return type.value(qualifiedClassNameKey).toString() < typeName;
+ });
+
+ return (it != types.end() && it->value(qualifiedClassNameKey) == name) ? &(*it) : nullptr;
+}
+
+void QmlTypesClassDescription::collect(const QJsonObject *classDef,
+ const QVector<QJsonObject> &types,
+ const QVector<QJsonObject> &foreign,
+ bool topLevel)
+{
+ const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
+ for (const QJsonValue &classInfo : classInfos) {
+ const QJsonObject obj = classInfo.toObject();
+ const QString name = obj[QLatin1String("name")].toString();
+ const QString value = obj[QLatin1String("value")].toString();
+
+ if (name == QLatin1String("DefaultProperty")) {
+ if (defaultProp.isEmpty())
+ defaultProp = value;
+ } else if (name == QLatin1String("QML.AddedInMinorVersion")) {
+ if (topLevel) {
+ addedInRevision = value.toInt();
+ revisions.append(value.toInt());
+ } else if (!elementName.isEmpty()) {
+ revisions.append(value.toInt());
+ }
+ }
+
+ if (!topLevel)
+ continue;
+
+ // These only apply to the original class
+ if (name == QLatin1String("QML.Element")) {
+ if (value == QLatin1String("auto"))
+ elementName = classDef->value(QLatin1String("className")).toString();
+ else if (value != QLatin1String("anonymous"))
+ elementName = value;
+ } else if (name == QLatin1String("QML.RemovedInMinorVersion")) {
+ removedInRevision = value.toInt();
+ } else if (name == QLatin1String("QML.Creatable")) {
+ isCreatable = (value != QLatin1String("false"));
+ } else if (name == QLatin1String("QML.Attached")) {
+ attachedType = value;
+ if (const QJsonObject *other = findType(types, attachedType))
+ collect(other, types, foreign, false);
+ else if (const QJsonObject *other = findType(foreign, attachedType))
+ collect(other, types, foreign, false);
+ } else if (name == QLatin1String("QML.Singleton")) {
+ if (value == QLatin1String("true"))
+ isSingleton = true;
+ } else if (name == QLatin1String("QML.Foreign")) {
+ if (const QJsonObject *other = findType(foreign, value)) {
+ classDef = other;
+ if (defaultProp.isEmpty()) {
+ // Foreign type can have a default property
+ const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
+ for (const QJsonValue &classInfo : classInfos) {
+ QJsonObject obj = classInfo.toObject();
+ if (obj[QLatin1String("name")].toString() == QLatin1String("DefaultProperty")) {
+ defaultProp = obj[QLatin1String("value")].toString();
+ break;
+ }
+ }
+ }
+ }
+ } else if (name == QLatin1String("QML.Root")) {
+ isRootClass = true;
+ isBuiltin = true;
+ } else if (name == QLatin1String("QML.Builtin")) {
+ isBuiltin = true;
+ }
+ }
+
+ if (!elementName.isEmpty()) {
+ collectExtraVersions(classDef, QString::fromLatin1("properties"), revisions);
+ collectExtraVersions(classDef, QString::fromLatin1("slots"), revisions);
+ collectExtraVersions(classDef, QString::fromLatin1("methods"), revisions);
+ collectExtraVersions(classDef, QString::fromLatin1("signals"), revisions);
+ }
+
+ const auto supers = classDef->value(QLatin1String("superClasses")).toArray();
+ if (!supers.isEmpty()) {
+ const QJsonObject superObject = supers.first().toObject();
+ if (superObject[QLatin1String("access")].toString() == QLatin1String("public")) {
+ const QString superName = superObject[QLatin1String("name")].toString();
+ if (topLevel && superClass.isEmpty())
+ superClass = superName;
+
+ if (const QJsonObject *other = findType(types, superName))
+ collect(other, types, foreign, false);
+ else if (const QJsonObject *other = findType(foreign, superName))
+ collect(other, types, foreign, false);
+ }
+ }
+
+ if (addedInRevision == -1) {
+ revisions.append(0);
+ addedInRevision = 0;
+ }
+
+ std::sort(revisions.begin(), revisions.end(),
+ [](int a, int b) { return QByteArray::number(a) < QByteArray::number(b); });
+ const auto end = std::unique(revisions.begin(), revisions.end());
+ revisions.erase(end, revisions.end());
+
+ resolvedClass = classDef;
+}
diff --git a/tools/qmltyperegistrar/qmltypesclassdescription.h b/tools/qmltyperegistrar/qmltypesclassdescription.h
new file mode 100644
index 0000000000..8f3a6ea124
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypesclassdescription.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLTYPESCLASSDESCRIPTION_H
+#define QMLTYPESCLASSDESCRIPTION_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qset.h>
+
+struct QmlTypesClassDescription
+{
+ const QJsonObject *resolvedClass = nullptr;
+ QString elementName;
+ QString defaultProp;
+ QString superClass;
+ QString attachedType;
+ QList<int> revisions;
+ int addedInRevision = -1;
+ int removedInRevision = -1;
+ bool isCreatable = true;
+ bool isSingleton = false;
+ bool isRootClass = false;
+ bool isBuiltin = false;
+
+ void collect(const QJsonObject *classDef, const QVector<QJsonObject> &types,
+ const QVector<QJsonObject> &foreign, bool topLevel);
+
+ static const QJsonObject *findType(const QVector<QJsonObject> &types, const QString &name);
+};
+
+#endif // QMLTYPESCLASSDESCRIPTION_H
diff --git a/tools/qmltyperegistrar/qmltypescreator.cpp b/tools/qmltyperegistrar/qmltypescreator.cpp
new file mode 100644
index 0000000000..7bac6a87d8
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypescreator.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmltypescreator.h"
+#include "qmlstreamwriter.h"
+#include "qmltypesclassdescription.h"
+
+#include <QtCore/qset.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qsavefile.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qjsondocument.h>
+
+static QString enquote(const QString &string)
+{
+ QString s = string;
+ return QString::fromLatin1("\"%1\"").arg(s.replace(QLatin1Char('\\'), QLatin1String("\\\\"))
+ .replace(QLatin1Char('"'),QLatin1String("\\\"")));
+}
+
+void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &collector)
+{
+ m_qml.writeScriptBinding(
+ QLatin1String("name"),
+ enquote(collector.resolvedClass->value(
+ QLatin1String("qualifiedClassName")).toString()));
+
+ if (!collector.defaultProp.isEmpty())
+ m_qml.writeScriptBinding(QLatin1String("defaultProperty"), enquote(collector.defaultProp));
+
+ if (!collector.superClass.isEmpty())
+ m_qml.writeScriptBinding(QLatin1String("prototype"), enquote(collector.superClass));
+
+ if (collector.elementName.isEmpty())
+ return;
+
+ QStringList exports;
+ QStringList metaObjects;
+
+ for (auto it = collector.revisions.begin(), end = collector.revisions.end(); it != end; ++it) {
+ const int revision = *it;
+ if (revision < collector.addedInRevision)
+ continue;
+ if (collector.removedInRevision > collector.addedInRevision
+ && revision >= collector.removedInRevision) {
+ break;
+ }
+
+ if (collector.isBuiltin) {
+ exports.append(enquote(QString::fromLatin1("QML/%1 1.0").arg(collector.elementName)));
+ metaObjects.append(QLatin1String("0"));
+ }
+
+ exports.append(enquote(QString::fromLatin1("%1/%2 %3.%4")
+ .arg(m_module).arg(collector.elementName)
+ .arg(m_majorVersion).arg(revision)));
+ metaObjects.append(QString::number(revision));
+ }
+
+ m_qml.writeArrayBinding(QLatin1String("exports"), exports);
+
+ if (!collector.isCreatable || collector.isSingleton)
+ m_qml.writeScriptBinding(QLatin1String("isCreatable"), QLatin1String("false"));
+
+ if (collector.isSingleton)
+ m_qml.writeScriptBinding(QLatin1String("isSingleton"), QLatin1String("true"));
+
+ m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects);
+
+ if (!collector.attachedType.isEmpty())
+ m_qml.writeScriptBinding(QLatin1String("attachedType"), enquote(collector.attachedType));
+}
+
+void QmlTypesCreator::writeType(const QJsonObject &property, const QString &key, bool isReadonly,
+ bool parsePointer)
+{
+ auto it = property.find(key);
+ if (it == property.end())
+ return;
+
+ QString type = (*it).toString();
+ if (type.isEmpty() || type == QLatin1String("void"))
+ return;
+
+ const QLatin1String typeKey("type");
+
+ bool isList = false;
+ bool isPointer = false;
+
+ if (type == QLatin1String("QString")) {
+ type = QLatin1String("string");
+ } else if (type == QLatin1String("qreal")) {
+ type = QLatin1String("double");
+ } else if (type == QLatin1String("qint32")) {
+ type = QLatin1String("int");
+ } else if (type == QLatin1String("quint32")) {
+ type = QLatin1String("uint");
+ } else if (type == QLatin1String("qint64")) {
+ type = QLatin1String("qlonglong");
+ } else if (type == QLatin1String("quint64")) {
+ type = QLatin1String("qulonglong");
+ } else {
+
+ const QLatin1String listProperty("QQmlListProperty<");
+ if (type.startsWith(listProperty)) {
+ isList = true;
+ const int listPropertySize = listProperty.size();
+ type = type.mid(listPropertySize, type.size() - listPropertySize - 1);
+ }
+
+ if (parsePointer && type.endsWith(QLatin1Char('*'))) {
+ isPointer = true;
+ type = type.left(type.size() - 1);
+ }
+ }
+
+ m_qml.writeScriptBinding(typeKey, enquote(type));
+ const QLatin1String trueString("true");
+ if (isList)
+ m_qml.writeScriptBinding(QLatin1String("isList"), trueString);
+ if (isReadonly)
+ m_qml.writeScriptBinding(QLatin1String("isReadonly"), trueString);
+ if (isPointer)
+ m_qml.writeScriptBinding(QLatin1String("isPointer"), trueString);
+}
+
+void QmlTypesCreator::writeProperties(const QJsonArray &properties, QSet<QString> &notifySignals)
+{
+ for (const QJsonValue &property : properties) {
+ const QJsonObject obj = property.toObject();
+ const QString name = obj[QLatin1String("name")].toString();
+ m_qml.writeStartObject(QLatin1String("Property"));
+ m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
+ const auto it = obj.find(QLatin1String("revision"));
+ if (it != obj.end())
+ m_qml.writeScriptBinding(QLatin1String("revision"), QString::number(it.value().toInt()));
+ writeType(obj, QLatin1String("type"), !obj.contains(QLatin1String("write")), true);
+ m_qml.writeEndObject();
+
+ const QString notify = obj[QLatin1String("notify")].toString();
+ if (notify == name + QLatin1String("Changed"))
+ notifySignals.insert(notify);
+ }
+}
+
+void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &type,
+ const QSet<QString> &notifySignals)
+{
+ for (const QJsonValue &method : methods) {
+ const QJsonObject obj = method.toObject();
+ if (obj[QLatin1String("access")].toString() != QLatin1String("public"))
+ continue;
+ const QString name = obj[QLatin1String("name")].toString();
+ const QJsonArray arguments = method[QLatin1String("arguments")].toArray();
+ const auto revision = obj.find(QLatin1String("revision"));
+ if (notifySignals.contains(name) && arguments.isEmpty() && revision == obj.end())
+ continue;
+ m_qml.writeStartObject(type);
+ m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
+ if (revision != obj.end())
+ m_qml.writeScriptBinding(QLatin1String("revision"), QString::number(revision.value().toInt()));
+ writeType(obj, QLatin1String("returnType"), false, false);
+ for (const QJsonValue &argument : arguments) {
+ const QJsonObject obj = argument.toObject();
+ m_qml.writeStartObject(QLatin1String("Parameter"));
+ const QString name = obj[QLatin1String("name")].toString();
+ if (!name.isEmpty())
+ m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
+ writeType(obj, QLatin1String("type"), false, true);
+ m_qml.writeEndObject();
+ }
+ m_qml.writeEndObject();
+ }
+}
+
+void QmlTypesCreator::writeEnums(const QJsonArray &enums)
+{
+ for (const auto &item : enums) {
+ const QJsonObject obj = item.toObject();
+ const QJsonArray values = obj.value(QLatin1String("values")).toArray();
+ QStringList valueList;
+
+ for (const QJsonValue &value : values)
+ valueList.append(enquote(value.toString()));
+
+ m_qml.writeStartObject(QLatin1String("Enum"));
+ m_qml.writeScriptBinding(QLatin1String("name"),
+ enquote(obj.value(QLatin1String("name")).toString()));
+ m_qml.writeArrayBinding(QLatin1String("values"), valueList);
+ m_qml.writeEndObject();
+ }
+}
+
+void QmlTypesCreator::writeComponents()
+{
+ const QLatin1String nameKey("name");
+ const QLatin1String signalsKey("signals");
+ const QLatin1String enumsKey("enums");
+ const QLatin1String propertiesKey("properties");
+ const QLatin1String slotsKey("slots");
+ const QLatin1String methodsKey("methods");
+ const QLatin1String accessKey("access");
+ const QLatin1String typeKey("type");
+ const QLatin1String argumentsKey("arguments");
+
+ const QLatin1String destroyedName("destroyed");
+ const QLatin1String deleteLaterName("deleteLater");
+ const QLatin1String toStringName("toString");
+ const QLatin1String destroyName("destroy");
+ const QLatin1String delayName("delay");
+
+ const QLatin1String signalElement("Signal");
+ const QLatin1String componentElement("Component");
+ const QLatin1String methodElement("Method");
+
+ const QLatin1String publicAccess("public");
+ const QLatin1String intType("int");
+
+ for (const QJsonObject &component : m_ownTypes) {
+ m_qml.writeStartObject(componentElement);
+
+ QmlTypesClassDescription collector;
+ collector.collect(&component, m_ownTypes, m_foreignTypes, true);
+
+ writeClassProperties(collector);
+
+ const QJsonObject *classDef = collector.resolvedClass;
+ writeEnums(classDef->value(enumsKey).toArray());
+
+ QSet<QString> notifySignals;
+ writeProperties(classDef->value(propertiesKey).toArray(), notifySignals);
+
+ if (collector.isRootClass) {
+
+ // Hide destroyed() signals
+ QJsonArray componentSignals = classDef->value(signalsKey).toArray();
+ for (auto it = componentSignals.begin(); it != componentSignals.end();) {
+ if (it->toObject().value(nameKey).toString() == destroyedName)
+ it = componentSignals.erase(it);
+ else
+ ++it;
+ }
+ writeMethods(componentSignals, signalElement, notifySignals);
+
+ // Hide deleteLater() methods
+ QJsonArray componentMethods = classDef->value(methodsKey).toArray()
+ + classDef->value(slotsKey).toArray();
+ for (auto it = componentMethods.begin(); it != componentMethods.end();) {
+ if (it->toObject().value(nameKey).toString() == deleteLaterName)
+ it = componentMethods.erase(it);
+ else
+ ++it;
+ }
+
+ // Add toString()
+ QJsonObject toStringMethod;
+ toStringMethod.insert(nameKey, toStringName);
+ toStringMethod.insert(accessKey, publicAccess);
+ componentMethods.append(toStringMethod);
+
+ // Add destroy()
+ QJsonObject destroyMethod;
+ destroyMethod.insert(nameKey, destroyName);
+ destroyMethod.insert(accessKey, publicAccess);
+ componentMethods.append(destroyMethod);
+
+ // Add destroy(int)
+ QJsonObject destroyMethodWithArgument;
+ destroyMethodWithArgument.insert(nameKey, destroyName);
+ destroyMethodWithArgument.insert(accessKey, publicAccess);
+ QJsonObject delayArgument;
+ delayArgument.insert(nameKey, delayName);
+ delayArgument.insert(typeKey, intType);
+ QJsonArray destroyArguments;
+ destroyArguments.append(delayArgument);
+ destroyMethodWithArgument.insert(argumentsKey, destroyArguments);
+ componentMethods.append(destroyMethodWithArgument);
+
+ writeMethods(componentMethods, methodElement);
+ } else {
+ writeMethods(classDef->value(signalsKey).toArray(), signalElement, notifySignals);
+ writeMethods(classDef->value(slotsKey).toArray(), methodElement);
+ writeMethods(classDef->value(methodsKey).toArray(), methodElement);
+ }
+ m_qml.writeEndObject();
+ }
+}
+
+void QmlTypesCreator::generate(const QString &outFileName, const QString &dependenciesFileName)
+{
+ m_qml.writeStartDocument();
+ m_qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 2);
+ m_qml.write(QString::fromLatin1(
+ "\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 qmltyperegistrar.\n\n"));
+ m_qml.writeStartObject(QLatin1String("Module"));
+
+ QStringList dependencies;
+ if (!dependenciesFileName.isEmpty()) {
+ QFile file(dependenciesFileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Failed to open %s\n", qPrintable(dependenciesFileName));
+ } else {
+ QJsonParseError error { -1, QJsonParseError::NoError };
+ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ fprintf(stderr, "Failed to parse %s\n", qPrintable(dependenciesFileName));
+ } else {
+ const QJsonArray array = doc.array();
+ for (const QJsonValue &value : array)
+ dependencies.append(enquote(value.toString()));
+ }
+ }
+ } else {
+ // Default dependency is QtQuick 2.0
+ dependencies.append(enquote(QLatin1String("QtQuick 2.0")));
+ }
+
+ m_qml.writeArrayBinding(QLatin1String("dependencies"), dependencies);
+
+ writeComponents();
+
+ m_qml.writeEndObject();
+
+ QSaveFile file(outFileName);
+ file.open(QIODevice::WriteOnly);
+ file.write(m_output);
+ file.commit();
+}
+
diff --git a/tools/qmltyperegistrar/qmltypescreator.h b/tools/qmltyperegistrar/qmltypescreator.h
new file mode 100644
index 0000000000..9207a64b7e
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypescreator.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLTYPESCREATOR_H
+#define QMLTYPESCREATOR_H
+
+#include "qmlstreamwriter.h"
+#include "qmltypesclassdescription.h"
+
+#include <QtCore/qstring.h>
+#include <QtCore/qset.h>
+
+class QmlTypesCreator
+{
+public:
+ QmlTypesCreator() : m_qml(&m_output) {}
+
+ void generate(const QString &outFileName, const QString &dependenciesFileName);
+
+ void setOwnTypes(QVector<QJsonObject> ownTypes) { m_ownTypes = std::move(ownTypes); }
+ void setForeignTypes(QVector<QJsonObject> foreignTypes) { m_foreignTypes = std::move(foreignTypes); }
+ void setModule(QString module) { m_module = std::move(module); }
+ void setMajorVersion(int majorVersion) { m_majorVersion = majorVersion; }
+
+private:
+ void writeClassProperties(const QmlTypesClassDescription &collector);
+ void writeType(const QJsonObject &property, const QString &key, bool isReadonly,
+ bool parsePointer);
+ void writeProperties(const QJsonArray &properties, QSet<QString> &notifySignals);
+ void writeMethods(const QJsonArray &methods, const QString &type,
+ const QSet<QString> &notifySignals = QSet<QString>());
+ void writeEnums(const QJsonArray &enums);
+ void writeComponents();
+
+ QByteArray m_output;
+ QmlStreamWriter m_qml;
+ QVector<QJsonObject> m_ownTypes;
+ QVector<QJsonObject> m_foreignTypes;
+ QString m_module;
+ int m_majorVersion = 0;
+};
+
+#endif // QMLTYPESCREATOR_H
diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/shared/qmlstreamwriter.cpp
index b0fbc4e443..b0fbc4e443 100644
--- a/tools/qmlplugindump/qmlstreamwriter.cpp
+++ b/tools/shared/qmlstreamwriter.cpp
diff --git a/tools/qmlplugindump/qmlstreamwriter.h b/tools/shared/qmlstreamwriter.h
index cb642159ea..cb642159ea 100644
--- a/tools/qmlplugindump/qmlstreamwriter.h
+++ b/tools/shared/qmlstreamwriter.h
diff --git a/tools/tools.pro b/tools/tools.pro
index 25ed760903..69b79e8816 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -10,6 +10,8 @@ qtConfig(qml-devtools) {
qtConfig(commandlineparser):qtConfig(xmlstreamwriter): SUBDIRS += qmlcachegen
}
+qtConfig(commandlineparser): SUBDIRS += qmltyperegistrar
+
qtConfig(thread):!android|android_app:!wasm:!rtems {
SUBDIRS += \
qml