From 8da8d51939c56b8f8e4887e8f7237fba1b139127 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 11 Oct 2019 17:47:21 +0200 Subject: qmlplugindump: Drop defaultReachableNames They are unused. Change-Id: Id49720bead07e353e448d92a53c2c6aabe265d3a Reviewed-by: Simon Hausmann --- tools/qmlplugindump/main.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 5e999c557a..25447becff 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -441,8 +441,9 @@ public: } } - QString getPrototypeNameForCompositeType(const QMetaObject *metaObject, QSet &defaultReachableNames, - QList *objectsToMerge, const QmlVersionInfo &versionInfo) + QString getPrototypeNameForCompositeType( + const QMetaObject *metaObject, QList *objectsToMerge, + const QmlVersionInfo &versionInfo) { auto ty = QQmlMetaType::qmlType(metaObject); QString prototypeName; @@ -458,20 +459,20 @@ public: prototypeName = "QObject"; else prototypeName = getPrototypeNameForCompositeType( - superMetaObject, defaultReachableNames, objectsToMerge, versionInfo); + superMetaObject, objectsToMerge, versionInfo); } else { prototypeName = convertToId(metaObject->className()); } return prototypeName; } - void dumpComposite(QQmlEngine *engine, const QList &compositeType, QSet &defaultReachableNames, const QmlVersionInfo &versionInfo) + void dumpComposite(QQmlEngine *engine, const QList &compositeType, const QmlVersionInfo &versionInfo) { for (const QQmlType &type : compositeType) - dumpCompositeItem(engine, type, defaultReachableNames, versionInfo); + dumpCompositeItem(engine, type, versionInfo); } - void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, QSet &defaultReachableNames, const QmlVersionInfo &versionInfo) + void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, const QmlVersionInfo &versionInfo) { QQmlComponent e(engine, compositeType.sourceUrl()); if (!e.isReady()) { @@ -492,8 +493,8 @@ public: QList objectsToMerge; KnownAttributes knownAttributes; // Get C++ base class name for the composite type - QString prototypeName = getPrototypeNameForCompositeType(mainMeta, defaultReachableNames, - &objectsToMerge, versionInfo); + QString prototypeName = getPrototypeNameForCompositeType(mainMeta, &objectsToMerge, + versionInfo); qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName)); QString qmlTyName = compositeType.qmlTypeName(); @@ -1239,9 +1240,6 @@ int main(int argc, char *argv[]) QSet uncreatableMetas; QSet singletonMetas; - // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported - QSet defaultReachableNames; - // this will hold the meta objects we want to dump information of QSet metas; @@ -1370,7 +1368,7 @@ int main(int argc, char *argv[]) QMap>::const_iterator iter = compositeTypes.constBegin(); for (; iter != compositeTypes.constEnd(); ++iter) - dumper.dumpComposite(&engine, iter.value(), defaultReachableNames, info); + dumper.dumpComposite(&engine, iter.value(), info); // define QEasingCurve as an extension of QQmlEasingValueType, this way // properties using the QEasingCurve type get useful type information. -- cgit v1.2.3 From ee62b2824fe91e95a3f8218b93ba55a2ef6660d0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 1 Oct 2019 14:43:37 +0200 Subject: 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 Reviewed-by: Simon Hausmann --- tools/qmlplugindump/qmlplugindump.pro | 10 +- tools/qmlplugindump/qmlstreamwriter.cpp | 190 ---------- tools/qmlplugindump/qmlstreamwriter.h | 67 ---- tools/qmltyperegistrar/qmltyperegistrar.cpp | 404 +++++++++++++++++++++ tools/qmltyperegistrar/qmltyperegistrar.pro | 26 ++ tools/qmltyperegistrar/qmltypes.prf | 66 ++++ .../qmltyperegistrar/qmltypesclassdescription.cpp | 161 ++++++++ tools/qmltyperegistrar/qmltypesclassdescription.h | 58 +++ tools/qmltyperegistrar/qmltypescreator.cpp | 357 ++++++++++++++++++ tools/qmltyperegistrar/qmltypescreator.h | 68 ++++ tools/shared/qmlstreamwriter.cpp | 190 ++++++++++ tools/shared/qmlstreamwriter.h | 67 ++++ tools/tools.pro | 2 + 13 files changed, 1405 insertions(+), 261 deletions(-) delete mode 100644 tools/qmlplugindump/qmlstreamwriter.cpp delete mode 100644 tools/qmlplugindump/qmlstreamwriter.h create mode 100644 tools/qmltyperegistrar/qmltyperegistrar.cpp create mode 100644 tools/qmltyperegistrar/qmltyperegistrar.pro create mode 100644 tools/qmltyperegistrar/qmltypes.prf create mode 100644 tools/qmltyperegistrar/qmltypesclassdescription.cpp create mode 100644 tools/qmltyperegistrar/qmltypesclassdescription.h create mode 100644 tools/qmltyperegistrar/qmltypescreator.cpp create mode 100644 tools/qmltyperegistrar/qmltypescreator.h create mode 100644 tools/shared/qmlstreamwriter.cpp create mode 100644 tools/shared/qmlstreamwriter.h (limited to 'tools') 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/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp deleted file mode 100644 index b0fbc4e443..0000000000 --- a/tools/qmlplugindump/qmlstreamwriter.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications 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 "qmlstreamwriter.h" - -#include -#include - -QmlStreamWriter::QmlStreamWriter(QByteArray *array) - : m_indentDepth(0) - , m_pendingLineLength(0) - , m_maybeOneline(false) - , m_stream(new QBuffer(array)) -{ - m_stream->open(QIODevice::WriteOnly); -} - -void QmlStreamWriter::writeStartDocument() -{ -} - -void QmlStreamWriter::writeEndDocument() -{ -} - -void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as) -{ - m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8()); - if (!as.isEmpty()) - m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8()); - m_stream->write("\n"); -} - -void QmlStreamWriter::writeStartObject(const QString &component) -{ - flushPotentialLinesWithNewlines(); - writeIndent(); - m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8()); - ++m_indentDepth; - m_maybeOneline = true; -} - -void QmlStreamWriter::writeEndObject() -{ - if (m_maybeOneline && !m_pendingLines.isEmpty()) { - --m_indentDepth; - for (int i = 0; i < m_pendingLines.size(); ++i) { - m_stream->write(" "); - m_stream->write(m_pendingLines.at(i).trimmed()); - if (i != m_pendingLines.size() - 1) - m_stream->write(";"); - } - m_stream->write(" }\n"); - m_pendingLines.clear(); - m_pendingLineLength = 0; - m_maybeOneline = false; - } else { - flushPotentialLinesWithNewlines(); - --m_indentDepth; - writeIndent(); - m_stream->write("}\n"); - } -} - -void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs) -{ - writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8()); -} - -void QmlStreamWriter::writeBooleanBinding(const QString &name, bool value) -{ - writeScriptBinding(name, value ? QLatin1String("true") : QLatin1String("false")); -} - -void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &elements) -{ - flushPotentialLinesWithNewlines(); - writeIndent(); - - // try to use a single line - QString singleLine; - singleLine += QString::fromLatin1("%1: [").arg(name); - for (int i = 0; i < elements.size(); ++i) { - singleLine += elements.at(i); - if (i != elements.size() - 1) - singleLine += QLatin1String(", "); - } - singleLine += QLatin1String("]\n"); - if (singleLine.size() + m_indentDepth * 4 < 80) { - m_stream->write(singleLine.toUtf8()); - return; - } - - // write multi-line - m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8()); - ++m_indentDepth; - for (int i = 0; i < elements.size(); ++i) { - writeIndent(); - m_stream->write(elements.at(i).toUtf8()); - if (i != elements.size() - 1) { - m_stream->write(",\n"); - } else { - m_stream->write("\n"); - } - } - --m_indentDepth; - writeIndent(); - m_stream->write("]\n"); -} - -void QmlStreamWriter::write(const QString &data) -{ - flushPotentialLinesWithNewlines(); - m_stream->write(data.toUtf8()); -} - -void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const QList > &keyValue) -{ - flushPotentialLinesWithNewlines(); - writeIndent(); - m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8()); - ++m_indentDepth; - for (int i = 0; i < keyValue.size(); ++i) { - const QString key = keyValue.at(i).first; - const QString value = keyValue.at(i).second; - writeIndent(); - m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8()); - if (i != keyValue.size() - 1) { - m_stream->write(",\n"); - } else { - m_stream->write("\n"); - } - } - --m_indentDepth; - writeIndent(); - m_stream->write("}\n"); -} - -void QmlStreamWriter::writeIndent() -{ - m_stream->write(QByteArray(m_indentDepth * 4, ' ')); -} - -void QmlStreamWriter::writePotentialLine(const QByteArray &line) -{ - m_pendingLines.append(line); - m_pendingLineLength += line.size(); - if (m_pendingLineLength >= 80) { - flushPotentialLinesWithNewlines(); - } -} - -void QmlStreamWriter::flushPotentialLinesWithNewlines() -{ - if (m_maybeOneline) - m_stream->write("\n"); - for (const QByteArray &line : qAsConst(m_pendingLines)) { - writeIndent(); - m_stream->write(line); - m_stream->write("\n"); - } - m_pendingLines.clear(); - m_pendingLineLength = 0; - m_maybeOneline = false; -} diff --git a/tools/qmlplugindump/qmlstreamwriter.h b/tools/qmlplugindump/qmlstreamwriter.h deleted file mode 100644 index cb642159ea..0000000000 --- a/tools/qmlplugindump/qmlstreamwriter.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications 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 QMLSTREAMWRITER_H -#define QMLSTREAMWRITER_H - -#include -#include -#include -#include -#include - -class QmlStreamWriter -{ -public: - QmlStreamWriter(QByteArray *array); - - void writeStartDocument(); - void writeEndDocument(); - void writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as = QString()); - //void writeFilesystemImport(const QString &file, const QString &as = QString()); - void writeStartObject(const QString &component); - void writeEndObject(); - void writeScriptBinding(const QString &name, const QString &rhs); - void writeScriptObjectLiteralBinding(const QString &name, const QList > &keyValue); - void writeArrayBinding(const QString &name, const QStringList &elements); - void write(const QString &data); - void writeBooleanBinding(const QString &name, bool value); - -private: - void writeIndent(); - void writePotentialLine(const QByteArray &line); - void flushPotentialLinesWithNewlines(); - - int m_indentDepth; - QList m_pendingLines; - int m_pendingLineLength; - bool m_maybeOneline; - QScopedPointer m_stream; -}; - -#endif // QMLSTREAMWRITER_H 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 foreignRelatedTypes(const QVector &types, + const QVector &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 processedRelatedNames; + QQueue typeQueue; + typeQueue.append(types.toList()); + QVector 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 \"" + "rather than \"#include \".")); + 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 outputFile; + + if (parser.isSet(outputOption)) { + QString outputName = parser.value(outputOption); +#if defined(_MSC_VER) + if (_wfopen_s(&output, reinterpret_cast(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 \n"); + + QStringList includes; + QVector types; + QVector 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 &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 "); + const bool privateIncludes = parser.isSet(privateIncludesOption); + for (const QString &include : qAsConst(includes)) { + if (privateIncludes && include.endsWith(QLatin1String("_p.h"))) + fprintf(output, "\n#include ", 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 + +static void collectExtraVersions(const QJsonObject *component, const QString &key, + QList &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 &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 &types, + const QVector &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 +#include +#include +#include + +struct QmlTypesClassDescription +{ + const QJsonObject *resolvedClass = nullptr; + QString elementName; + QString defaultProp; + QString superClass; + QString attachedType; + QList 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 &types, + const QVector &foreign, bool topLevel); + + static const QJsonObject *findType(const QVector &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 +#include +#include +#include +#include + +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 ¬ifySignals) +{ + 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 ¬ifySignals) +{ + 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 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 +#include + +class QmlTypesCreator +{ +public: + QmlTypesCreator() : m_qml(&m_output) {} + + void generate(const QString &outFileName, const QString &dependenciesFileName); + + void setOwnTypes(QVector ownTypes) { m_ownTypes = std::move(ownTypes); } + void setForeignTypes(QVector 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 ¬ifySignals); + void writeMethods(const QJsonArray &methods, const QString &type, + const QSet ¬ifySignals = QSet()); + void writeEnums(const QJsonArray &enums); + void writeComponents(); + + QByteArray m_output; + QmlStreamWriter m_qml; + QVector m_ownTypes; + QVector m_foreignTypes; + QString m_module; + int m_majorVersion = 0; +}; + +#endif // QMLTYPESCREATOR_H diff --git a/tools/shared/qmlstreamwriter.cpp b/tools/shared/qmlstreamwriter.cpp new file mode 100644 index 0000000000..b0fbc4e443 --- /dev/null +++ b/tools/shared/qmlstreamwriter.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qmlstreamwriter.h" + +#include +#include + +QmlStreamWriter::QmlStreamWriter(QByteArray *array) + : m_indentDepth(0) + , m_pendingLineLength(0) + , m_maybeOneline(false) + , m_stream(new QBuffer(array)) +{ + m_stream->open(QIODevice::WriteOnly); +} + +void QmlStreamWriter::writeStartDocument() +{ +} + +void QmlStreamWriter::writeEndDocument() +{ +} + +void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as) +{ + m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8()); + if (!as.isEmpty()) + m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8()); + m_stream->write("\n"); +} + +void QmlStreamWriter::writeStartObject(const QString &component) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8()); + ++m_indentDepth; + m_maybeOneline = true; +} + +void QmlStreamWriter::writeEndObject() +{ + if (m_maybeOneline && !m_pendingLines.isEmpty()) { + --m_indentDepth; + for (int i = 0; i < m_pendingLines.size(); ++i) { + m_stream->write(" "); + m_stream->write(m_pendingLines.at(i).trimmed()); + if (i != m_pendingLines.size() - 1) + m_stream->write(";"); + } + m_stream->write(" }\n"); + m_pendingLines.clear(); + m_pendingLineLength = 0; + m_maybeOneline = false; + } else { + flushPotentialLinesWithNewlines(); + --m_indentDepth; + writeIndent(); + m_stream->write("}\n"); + } +} + +void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs) +{ + writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8()); +} + +void QmlStreamWriter::writeBooleanBinding(const QString &name, bool value) +{ + writeScriptBinding(name, value ? QLatin1String("true") : QLatin1String("false")); +} + +void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &elements) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + + // try to use a single line + QString singleLine; + singleLine += QString::fromLatin1("%1: [").arg(name); + for (int i = 0; i < elements.size(); ++i) { + singleLine += elements.at(i); + if (i != elements.size() - 1) + singleLine += QLatin1String(", "); + } + singleLine += QLatin1String("]\n"); + if (singleLine.size() + m_indentDepth * 4 < 80) { + m_stream->write(singleLine.toUtf8()); + return; + } + + // write multi-line + m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8()); + ++m_indentDepth; + for (int i = 0; i < elements.size(); ++i) { + writeIndent(); + m_stream->write(elements.at(i).toUtf8()); + if (i != elements.size() - 1) { + m_stream->write(",\n"); + } else { + m_stream->write("\n"); + } + } + --m_indentDepth; + writeIndent(); + m_stream->write("]\n"); +} + +void QmlStreamWriter::write(const QString &data) +{ + flushPotentialLinesWithNewlines(); + m_stream->write(data.toUtf8()); +} + +void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const QList > &keyValue) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8()); + ++m_indentDepth; + for (int i = 0; i < keyValue.size(); ++i) { + const QString key = keyValue.at(i).first; + const QString value = keyValue.at(i).second; + writeIndent(); + m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8()); + if (i != keyValue.size() - 1) { + m_stream->write(",\n"); + } else { + m_stream->write("\n"); + } + } + --m_indentDepth; + writeIndent(); + m_stream->write("}\n"); +} + +void QmlStreamWriter::writeIndent() +{ + m_stream->write(QByteArray(m_indentDepth * 4, ' ')); +} + +void QmlStreamWriter::writePotentialLine(const QByteArray &line) +{ + m_pendingLines.append(line); + m_pendingLineLength += line.size(); + if (m_pendingLineLength >= 80) { + flushPotentialLinesWithNewlines(); + } +} + +void QmlStreamWriter::flushPotentialLinesWithNewlines() +{ + if (m_maybeOneline) + m_stream->write("\n"); + for (const QByteArray &line : qAsConst(m_pendingLines)) { + writeIndent(); + m_stream->write(line); + m_stream->write("\n"); + } + m_pendingLines.clear(); + m_pendingLineLength = 0; + m_maybeOneline = false; +} diff --git a/tools/shared/qmlstreamwriter.h b/tools/shared/qmlstreamwriter.h new file mode 100644 index 0000000000..cb642159ea --- /dev/null +++ b/tools/shared/qmlstreamwriter.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QMLSTREAMWRITER_H +#define QMLSTREAMWRITER_H + +#include +#include +#include +#include +#include + +class QmlStreamWriter +{ +public: + QmlStreamWriter(QByteArray *array); + + void writeStartDocument(); + void writeEndDocument(); + void writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as = QString()); + //void writeFilesystemImport(const QString &file, const QString &as = QString()); + void writeStartObject(const QString &component); + void writeEndObject(); + void writeScriptBinding(const QString &name, const QString &rhs); + void writeScriptObjectLiteralBinding(const QString &name, const QList > &keyValue); + void writeArrayBinding(const QString &name, const QStringList &elements); + void write(const QString &data); + void writeBooleanBinding(const QString &name, bool value); + +private: + void writeIndent(); + void writePotentialLine(const QByteArray &line); + void flushPotentialLinesWithNewlines(); + + int m_indentDepth; + QList m_pendingLines; + int m_pendingLineLength; + bool m_maybeOneline; + QScopedPointer m_stream; +}; + +#endif // 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 -- cgit v1.2.3 From e9ae2014976cfc406f74f791ab1313ee369296fc Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 11 Oct 2019 18:03:52 +0200 Subject: qmlplugindump: Fix prototype and name for composite types Before we would always merge everything up to QObject. That would duplicate entries between types that inherit from the same base class. Also, when the composite type doesn't specify a module (because we just created a component from a plain QML file), use the module URI from our version info. As we're dumping the component we can assume it belongs to the module we're dumping. Change-Id: Icf9a58cfe1165f557ebbf7309251e98a0782dc33 Reviewed-by: Fabian Kosmale Reviewed-by: Simon Hausmann --- tools/qmlplugindump/main.cpp | 46 +++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 25447becff..1556718471 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -380,23 +380,21 @@ public: relocatableModuleUri = uri; } - const QString getExportString(QString qmlTyName, int majorVersion, int minorVersion) + QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo) { - if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) { - qmlTyName.remove(0, relocatableModuleUri.size() + 1); - } - if (qmlTyName.startsWith("./")) { - qmlTyName.remove(0, 2); - } - if (qmlTyName.startsWith(QLatin1Char('/'))) { - qmlTyName.remove(0, 1); - } - const QString exportString = enquote( - QString("%1 %2.%3").arg( - qmlTyName, - QString::number(majorVersion), - QString::number(minorVersion))); - return exportString; + const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri + : type.module(); + const int majorVersion = type.majorVersion() >= 0 ? type.majorVersion() + : versionInfo.majorVersion; + const int minorVersion = type.minorVersion() >= 0 ? type.minorVersion() + : versionInfo.minorVersion; + + const QString versionedElement = type.elementName() + + QString::fromLatin1(" %1.%2").arg(majorVersion).arg(minorVersion); + + return enquote((module == relocatableModuleUri) + ? versionedElement + : module + QLatin1Char('/') + versionedElement); } void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr) @@ -455,11 +453,15 @@ public: && !objectsToMerge->contains(metaObject)) objectsToMerge->append(metaObject); const QMetaObject *superMetaObject = metaObject->superClass(); - if (!superMetaObject) + if (!superMetaObject) { prototypeName = "QObject"; - else + } else { + QQmlType superType = QQmlMetaType::qmlType(superMetaObject); + if (superType.isValid() && !superType.isComposite()) + return convertToId(superMetaObject->className()); prototypeName = getPrototypeNameForCompositeType( superMetaObject, objectsToMerge, versionInfo); + } } else { prototypeName = convertToId(metaObject->className()); } @@ -498,8 +500,12 @@ public: qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName)); QString qmlTyName = compositeType.qmlTypeName(); - const QString exportString = getExportString(qmlTyName, compositeType.majorVersion(), compositeType.minorVersion()); + const QString exportString = getExportString(compositeType, versionInfo); + + // TODO: why don't we simply output the compositeType.elementName() here? + // That would make more sense, but it would change the format quite a bit. qml->writeScriptBinding(QLatin1String("name"), exportString); + qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString); qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList() << QString::number(compositeType.minorVersion())); qml->writeBooleanBinding(QLatin1String("isComposite"), true); @@ -566,7 +572,7 @@ public: if (attachedType != meta) attachedTypeId = convertToId(attachedType); } - const QString exportString = getExportString(type.qmlTypeName(), type.majorVersion(), type.minorVersion()); + const QString exportString = getExportString(type, { QString(), -1, -1, false }); int metaObjectRevision = type.metaObjectRevision(); if (extendedObject) { // emulate custom metaobjectrevision out of import -- cgit v1.2.3 From 3d0bec491e6d823df510ba12dba38be820116e86 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 15 Oct 2019 10:39:40 +0200 Subject: qmllint: Parse .mjs files as JavaScript rather than QML Change-Id: I80783289452300d4609a27f5b470ecdeeeca5e71 Reviewed-by: Fabian Kosmale Reviewed-by: Ulf Hermann --- tools/qmllint/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 56f72dd020..232b31934e 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -62,11 +62,13 @@ static bool lint_file(const QString &filename, const bool silent, const bool war QQmlJS::Lexer lexer(&engine); QFileInfo info(filename); - bool isJavaScript = info.suffix().toLower() == QLatin1String("js"); + const QString lowerSuffix = info.suffix().toLower(); + const bool isJavaScript = (lowerSuffix == QLatin1String("js") || lowerSuffix == QLatin1String("mjs")); + const bool isESModule = lowerSuffix == QLatin1String("mjs"); lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript); QQmlJS::Parser parser(&engine); - bool success = isJavaScript ? parser.parseProgram() : parser.parse(); + bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram()) : parser.parse(); if (!success && !silent) { const auto diagnosticMessages = parser.diagnosticMessages(); -- cgit v1.2.3 From fbc463e84d5cc6012953140c93f6b18f78e66bf8 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 22 Oct 2019 16:53:26 +0200 Subject: Add support for scanning qrc files in qmlimportscanner This reuses the ResourceFileMapper and extends it slightly to return full paths on request. Subsequently, this is moved into a shared directory inside tools. Fixes: QTBUG-55259 Change-Id: Ice5fc68d03b767a4185742e182556ab4290bd28d Reviewed-by: Simon Hausmann Reviewed-by: Fabian Kosmale --- tools/qmlcachegen/qmlcachegen.pro | 8 +- tools/qmlcachegen/resourcefilemapper.cpp | 167 --------------------------- tools/qmlcachegen/resourcefilemapper.h | 50 -------- tools/qmlimportscanner/main.cpp | 11 +- tools/qmlimportscanner/qmlimportscanner.pro | 1 + tools/shared/resourcefilemapper.cpp | 170 ++++++++++++++++++++++++++++ tools/shared/resourcefilemapper.h | 54 +++++++++ tools/shared/shared.pri | 3 + 8 files changed, 242 insertions(+), 222 deletions(-) delete mode 100644 tools/qmlcachegen/resourcefilemapper.cpp delete mode 100644 tools/qmlcachegen/resourcefilemapper.h create mode 100644 tools/shared/resourcefilemapper.cpp create mode 100644 tools/shared/resourcefilemapper.h create mode 100644 tools/shared/shared.pri (limited to 'tools') diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro index bee0b9a37e..ec65cdb5e6 100644 --- a/tools/qmlcachegen/qmlcachegen.pro +++ b/tools/qmlcachegen/qmlcachegen.pro @@ -5,8 +5,10 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII SOURCES = qmlcachegen.cpp \ resourcefilter.cpp \ - generateloader.cpp \ - resourcefilemapper.cpp + generateloader.cpp + +include(../shared/shared.pri) + TARGET = qmlcachegen build_integration.files = qmlcache.prf qtquickcompiler.prf @@ -38,5 +40,3 @@ QMAKE_TARGET_DESCRIPTION = QML Cache Generator load(qt_tool) -HEADERS += \ - resourcefilemapper.h diff --git a/tools/qmlcachegen/resourcefilemapper.cpp b/tools/qmlcachegen/resourcefilemapper.cpp deleted file mode 100644 index 244874717f..0000000000 --- a/tools/qmlcachegen/resourcefilemapper.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "resourcefilemapper.h" - -#include -#include -#include - -ResourceFileMapper::ResourceFileMapper(const QStringList &resourceFiles) -{ - for (const QString &fileName: resourceFiles) { - QFile f(fileName); - if (!f.open(QIODevice::ReadOnly)) - continue; - populateFromQrcFile(f); - } -} - -bool ResourceFileMapper::isEmpty() const -{ - return qrcPathToFileSystemPath.isEmpty(); -} - -QStringList ResourceFileMapper::resourcePaths(const QString &fileName) -{ - const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName)); - QStringList resourcePaths; - for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) { - if (QFileInfo(it.value()) == QFileInfo(absPath)) - resourcePaths.append(it.key()); - } - return resourcePaths; -} - -QStringList ResourceFileMapper::qmlCompilerFiles() const -{ - QStringList files; - for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd(); - it != end; ++it) { - const QString &qrcPath = it.key(); - const QString suffix = QFileInfo(qrcPath).suffix(); - if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs")) - continue; - files << qrcPath; - } - return files; -} - -void ResourceFileMapper::populateFromQrcFile(QFile &file) -{ - enum State { - InitialState, - InRCC, - InResource, - InFile - }; - State state = InitialState; - - QDir qrcDir = QFileInfo(file).absoluteDir(); - - QString prefix; - QString currentFileName; - QXmlStreamAttributes currentFileAttributes; - - QXmlStreamReader reader(&file); - while (!reader.atEnd()) { - switch (reader.readNext()) { - case QXmlStreamReader::StartElement: - if (reader.name() == QStringLiteral("RCC")) { - if (state != InitialState) - return; - state = InRCC; - continue; - } else if (reader.name() == QStringLiteral("qresource")) { - if (state != InRCC) - return; - state = InResource; - QXmlStreamAttributes attributes = reader.attributes(); - if (attributes.hasAttribute(QStringLiteral("prefix"))) - prefix = attributes.value(QStringLiteral("prefix")).toString(); - if (!prefix.startsWith(QLatin1Char('/'))) - prefix.prepend(QLatin1Char('/')); - if (!prefix.endsWith(QLatin1Char('/'))) - prefix.append(QLatin1Char('/')); - continue; - } else if (reader.name() == QStringLiteral("file")) { - if (state != InResource) - return; - state = InFile; - currentFileAttributes = reader.attributes(); - continue; - } - return; - - case QXmlStreamReader::EndElement: - if (reader.name() == QStringLiteral("file")) { - if (state != InFile) - return; - state = InResource; - continue; - } else if (reader.name() == QStringLiteral("qresource")) { - if (state != InResource) - return; - state = InRCC; - continue; - } else if (reader.name() == QStringLiteral("RCC")) { - if (state != InRCC) - return; - state = InitialState; - continue; - } - return; - - case QXmlStreamReader::Characters: { - if (reader.isWhitespace()) - break; - if (state != InFile) - return; - currentFileName = reader.text().toString(); - if (currentFileName.isEmpty()) - continue; - - const QString fsPath = QDir::cleanPath(qrcDir.absoluteFilePath(currentFileName)); - - if (currentFileAttributes.hasAttribute(QStringLiteral("alias"))) - currentFileName = currentFileAttributes.value(QStringLiteral("alias")).toString(); - - currentFileName = QDir::cleanPath(currentFileName); - while (currentFileName.startsWith(QLatin1String("../"))) - currentFileName.remove(0, 3); - - const QString qrcPath = prefix + currentFileName; - if (QFile::exists(fsPath)) - qrcPathToFileSystemPath.insert(qrcPath, fsPath); - continue; - } - - default: break; - } - } -} diff --git a/tools/qmlcachegen/resourcefilemapper.h b/tools/qmlcachegen/resourcefilemapper.h deleted file mode 100644 index 2e0ab45171..0000000000 --- a/tools/qmlcachegen/resourcefilemapper.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 RESOURCEFILEMAPPER_H -#define RESOURCEFILEMAPPER_H - -#include -#include -#include - -struct ResourceFileMapper -{ - ResourceFileMapper(const QStringList &resourceFiles); - - bool isEmpty() const; - - QStringList resourcePaths(const QString &fileName); - QStringList qmlCompilerFiles() const; - -private: - void populateFromQrcFile(QFile &file); - - QHash qrcPathToFileSystemPath; -}; - -#endif // RESOURCEFILEMAPPER_H diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 6d48f6203d..c37910bdaf 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -49,6 +49,8 @@ #include #include +#include + #include #include @@ -80,7 +82,8 @@ void printUsage(const QString &appNameIn) #endif std::wcerr << "Usage: " << appName << " -rootPath path/to/app/qml/directory -importPath path/to/qt/qml/directory\n" - " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n\n" + " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n" + " " << appName << " -qrcFiles file1.qrc file2.qrc -importPath path/to/qt/qml/directory\n\n" "Example: " << appName << " -rootPath . -importPath " << QDir::toNativeSeparators(qmlPath).toStdWString() << '\n'; @@ -542,6 +545,7 @@ int main(int argc, char *argv[]) QStringList qmlRootPaths; QStringList scanFiles; QStringList qmlImportPaths; + QStringList qrcFiles; bool generateCmakeContent = false; int i = 1; @@ -569,6 +573,8 @@ int main(int argc, char *argv[]) argReceiver = &qmlImportPaths; } else if (arg == QLatin1String("-cmake-output")) { generateCmakeContent = true; + } else if (arg == QLatin1String("-qrcFiles")) { + argReceiver = &qrcFiles; } else { std::cerr << qPrintable(appName) << ": Invalid argument: \"" << qPrintable(arg) << "\"\n"; @@ -590,6 +596,9 @@ int main(int argc, char *argv[]) } } + if (!qrcFiles.isEmpty()) + scanFiles << ResourceFileMapper(qrcFiles).qmlCompilerFiles(ResourceFileMapper::FileOutput::AbsoluteFilePath); + g_qmlImportPaths = qmlImportPaths; // Find the imports! diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro index a29b582274..d69f1a3b0b 100644 --- a/tools/qmlimportscanner/qmlimportscanner.pro +++ b/tools/qmlimportscanner/qmlimportscanner.pro @@ -4,6 +4,7 @@ QT = core qmldevtools-private DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII SOURCES += main.cpp +include(../shared/shared.pri) load(cmake_functions) diff --git a/tools/shared/resourcefilemapper.cpp b/tools/shared/resourcefilemapper.cpp new file mode 100644 index 0000000000..b9cf463575 --- /dev/null +++ b/tools/shared/resourcefilemapper.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "resourcefilemapper.h" + +#include +#include +#include + +ResourceFileMapper::ResourceFileMapper(const QStringList &resourceFiles) +{ + for (const QString &fileName: resourceFiles) { + QFile f(fileName); + if (!f.open(QIODevice::ReadOnly)) + continue; + populateFromQrcFile(f); + } +} + +bool ResourceFileMapper::isEmpty() const +{ + return qrcPathToFileSystemPath.isEmpty(); +} + +QStringList ResourceFileMapper::resourcePaths(const QString &fileName) +{ + const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName)); + QStringList resourcePaths; + for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) { + if (QFileInfo(it.value()) == QFileInfo(absPath)) + resourcePaths.append(it.key()); + } + return resourcePaths; +} + +QStringList ResourceFileMapper::qmlCompilerFiles(FileOutput fo) const +{ + QStringList files; + for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd(); + it != end; ++it) { + const QString &qrcPath = it.key(); + const QString suffix = QFileInfo(qrcPath).suffix(); + if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs")) + continue; + if (fo == FileOutput::AbsoluteFilePath) + files << it.value(); + else + files << qrcPath; + } + return files; +} + +void ResourceFileMapper::populateFromQrcFile(QFile &file) +{ + enum State { + InitialState, + InRCC, + InResource, + InFile + }; + State state = InitialState; + + QDir qrcDir = QFileInfo(file).absoluteDir(); + + QString prefix; + QString currentFileName; + QXmlStreamAttributes currentFileAttributes; + + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QStringLiteral("RCC")) { + if (state != InitialState) + return; + state = InRCC; + continue; + } else if (reader.name() == QStringLiteral("qresource")) { + if (state != InRCC) + return; + state = InResource; + QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(QStringLiteral("prefix"))) + prefix = attributes.value(QStringLiteral("prefix")).toString(); + if (!prefix.startsWith(QLatin1Char('/'))) + prefix.prepend(QLatin1Char('/')); + if (!prefix.endsWith(QLatin1Char('/'))) + prefix.append(QLatin1Char('/')); + continue; + } else if (reader.name() == QStringLiteral("file")) { + if (state != InResource) + return; + state = InFile; + currentFileAttributes = reader.attributes(); + continue; + } + return; + + case QXmlStreamReader::EndElement: + if (reader.name() == QStringLiteral("file")) { + if (state != InFile) + return; + state = InResource; + continue; + } else if (reader.name() == QStringLiteral("qresource")) { + if (state != InResource) + return; + state = InRCC; + continue; + } else if (reader.name() == QStringLiteral("RCC")) { + if (state != InRCC) + return; + state = InitialState; + continue; + } + return; + + case QXmlStreamReader::Characters: { + if (reader.isWhitespace()) + break; + if (state != InFile) + return; + currentFileName = reader.text().toString(); + if (currentFileName.isEmpty()) + continue; + + const QString fsPath = QDir::cleanPath(qrcDir.absoluteFilePath(currentFileName)); + + if (currentFileAttributes.hasAttribute(QStringLiteral("alias"))) + currentFileName = currentFileAttributes.value(QStringLiteral("alias")).toString(); + + currentFileName = QDir::cleanPath(currentFileName); + while (currentFileName.startsWith(QLatin1String("../"))) + currentFileName.remove(0, 3); + + const QString qrcPath = prefix + currentFileName; + if (QFile::exists(fsPath)) + qrcPathToFileSystemPath.insert(qrcPath, fsPath); + continue; + } + + default: break; + } + } +} diff --git a/tools/shared/resourcefilemapper.h b/tools/shared/resourcefilemapper.h new file mode 100644 index 0000000000..ed3e486149 --- /dev/null +++ b/tools/shared/resourcefilemapper.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 RESOURCEFILEMAPPER_H +#define RESOURCEFILEMAPPER_H + +#include +#include +#include + +struct ResourceFileMapper +{ + enum class FileOutput { + RelativeFilePath, + AbsoluteFilePath + }; + ResourceFileMapper(const QStringList &resourceFiles); + + bool isEmpty() const; + + QStringList resourcePaths(const QString &fileName); + QStringList qmlCompilerFiles(FileOutput fo = FileOutput::RelativeFilePath) const; + +private: + void populateFromQrcFile(QFile &file); + + QHash qrcPathToFileSystemPath; +}; + +#endif // RESOURCEFILEMAPPER_H diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri new file mode 100644 index 0000000000..c094b51d5f --- /dev/null +++ b/tools/shared/shared.pri @@ -0,0 +1,3 @@ +INCLUDEPATH += $$PWD +SOURCES += $$PWD/resourcefilemapper.cpp +HEADERS += $$PWD/resourcefilemapper.h -- cgit v1.2.3 From 3467d829d406565373eab6df2d2906a77ae3ce67 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 23 Oct 2019 15:13:23 +0200 Subject: qmllint: fix typo Change-Id: I02a6f4dd1804251e0420f2fbc334ee9c38d3dc49 Reviewed-by: Ulf Hermann --- tools/qmllint/findunqualified.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 27939608d7..49d64adb6e 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -466,7 +466,7 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Catch *) bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::WithStatement *withStatement) { m_colorOut.write(QString::asprintf("Warning: "), Warning); - m_colorOut.write(QString::asprintf("%d:%d: with statements are strongly discouraged in QML and might cause false positives when analying unqalified identifiers\n", withStatement->firstSourceLocation().startLine, withStatement->firstSourceLocation().startColumn), Normal); + m_colorOut.write(QString::asprintf("%d:%d: with statements are strongly discouraged in QML and might cause false positives when analysing unqalified identifiers\n", withStatement->firstSourceLocation().startLine, withStatement->firstSourceLocation().startColumn), Normal); enterEnvironment(ScopeType::JSLexicalScope, "with"); return true; } -- cgit v1.2.3 From 6d02fe20d94169839b661c18b46f3841dfbf0d9b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 22 Oct 2019 10:51:52 +0200 Subject: qmltyperegistrar: Throw an error if import name or version is empty We cannot generate anything sensible in those cases. Change-Id: I6005b970cedd999212920759a3dd45827b79b77c Reviewed-by: Fabian Kosmale Reviewed-by: Simon Hausmann --- tools/qmltyperegistrar/qmltypes.prf | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltypes.prf b/tools/qmltyperegistrar/qmltypes.prf index 495cc1b0cd..535abd3662 100644 --- a/tools/qmltyperegistrar/qmltypes.prf +++ b/tools/qmltyperegistrar/qmltypes.prf @@ -10,6 +10,14 @@ isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) { QML_IMPORT_NAME = $$replace(QML_IMPORT_NAME, .$${QML_IMPORT_MAJOR_VERSION}$, '') } +isEmpty(QML_IMPORT_NAME) { + error("Need TARGET_PATH or QML_IMPORT_NAME in order to generate qml types."); +} + +isEmpty(QML_IMPORT_MAJOR_VERSION) { + error("Need IMPORT_VERSION or QML_IMPORT_MAJOR_VERSION in order to generate qml types."); +} + isEmpty(QMLTYPES_FILENAME) { plugin: QMLTYPES_FILENAME = $$OUT_PWD/plugins.qmltypes else: QMLTYPES_FILENAME = $$OUT_PWD/$${TEMPLATE}.qmltypes -- cgit v1.2.3 From 220e32c0925b060a5eed72b555bf47b02012e8ea Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 22 Oct 2019 15:46:42 +0200 Subject: qmltyperegistrar: Allow specifying the minor version Unconditionally using QT_VERSION_MINOR is not a great idea for user projects. Yet, we have to specify some minor version, at least for now. Change-Id: Id9d0d0417a2ac6b8775cef755357274d8fbd637d Reviewed-by: Simon Hausmann Reviewed-by: Fabian Kosmale --- tools/qmltyperegistrar/qmltyperegistrar.cpp | 10 ++++++++-- tools/qmltyperegistrar/qmltypes.prf | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp index 2cf309ff0d..98bd0f8c6f 100644 --- a/tools/qmltyperegistrar/qmltyperegistrar.cpp +++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp @@ -199,6 +199,11 @@ int main(int argc, char **argv) majorVersionOption.setValueName(QStringLiteral("major version")); parser.addOption(majorVersionOption); + QCommandLineOption minorVersionOption(QStringLiteral("minor-version")); + minorVersionOption.setDescription(QStringLiteral("Minor version to use for module registration.")); + minorVersionOption.setValueName(QStringLiteral("minor version")); + parser.addOption(minorVersionOption); + QCommandLineOption pluginTypesOption(QStringLiteral("generate-plugintypes")); pluginTypesOption.setDescription(QStringLiteral("Generate plugins.qmltypes into specified directory.")); pluginTypesOption.setValueName(QStringLiteral("qmltypes target Directory")); @@ -348,8 +353,9 @@ int main(int argc, char **argv) qPrintable(module), qPrintable(majorVersion)); } - fprintf(output, "\n qmlRegisterModule(\"%s\", %s, QT_VERSION_MINOR);", - qPrintable(module), qPrintable(majorVersion)); + fprintf(output, "\n qmlRegisterModule(\"%s\", %s, %s);", + qPrintable(module), qPrintable(majorVersion), + qPrintable(parser.value(minorVersionOption))); fprintf(output, "\n}\n"); fprintf(output, "static const QQmlModuleRegistration registration(\"%s\", %s, %s);\n", qPrintable(module), qPrintable(majorVersion), qPrintable(functionName)); diff --git a/tools/qmltyperegistrar/qmltypes.prf b/tools/qmltyperegistrar/qmltypes.prf index 535abd3662..8d7fd3a498 100644 --- a/tools/qmltyperegistrar/qmltypes.prf +++ b/tools/qmltyperegistrar/qmltypes.prf @@ -5,6 +5,10 @@ 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_MINOR_VERSION):!isEmpty(IMPORT_VERSION): \ + QML_IMPORT_MINOR_VERSION = $$section(IMPORT_VERSION, ., 1, 1) + isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) { QML_IMPORT_NAME = $$replace(TARGETPATH, "/", ".") QML_IMPORT_NAME = $$replace(QML_IMPORT_NAME, .$${QML_IMPORT_MAJOR_VERSION}$, '') @@ -18,6 +22,10 @@ isEmpty(QML_IMPORT_MAJOR_VERSION) { error("Need IMPORT_VERSION or QML_IMPORT_MAJOR_VERSION in order to generate qml types."); } +isEmpty(QML_IMPORT_MINOR_VERSION) { + QML_IMPORT_MINOR_VERSION = 0 +} + isEmpty(QMLTYPES_FILENAME) { plugin: QMLTYPES_FILENAME = $$OUT_PWD/plugins.qmltypes else: QMLTYPES_FILENAME = $$OUT_PWD/$${TEMPLATE}.qmltypes @@ -35,6 +43,7 @@ QML_TYPEREGISTRAR_FLAGS = \ --generate-plugintypes=$$QMLTYPES_FILENAME \ --import-name=$$QML_IMPORT_NAME \ --major-version=$$QML_IMPORT_MAJOR_VERSION \ + --minor-version=$$QML_IMPORT_MINOR_VERSION \ --foreign-types=$$join(foreign_types, ',') DEPENDENCIESFILE = $$_PRO_FILE_PWD_/dependencies.json -- cgit v1.2.3 From fbe32d83b3e16bc51866daf5635cd36e1f70753b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 22 Oct 2019 13:55:44 +0200 Subject: qmltyperegistrar: Register only classes with QML.Element classInfo We may have QML.Attached in some classes we don't want to register. QML.Attached needs to be on the original class, even if the class is registered as a foreign type in some other context. Change-Id: I3d46fa351a2e595e879f0d2d8cc441d59c9dd754 Reviewed-by: Simon Hausmann Reviewed-by: Fabian Kosmale --- tools/qmltyperegistrar/qmltyperegistrar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp index 98bd0f8c6f..e64c23d2c9 100644 --- a/tools/qmltyperegistrar/qmltyperegistrar.cpp +++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp @@ -51,7 +51,7 @@ 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."))) + if (info[QLatin1String("name")].toString() == QLatin1String("QML.Element")) return true; } return false; -- cgit v1.2.3 From 7ecc96305f5ec0ed093e2e1b182cb85b71609bcd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 Oct 2019 09:33:04 +0200 Subject: Deprecate qmlmin [ChangeLog] The qmlmin tool is deprecated and not needed anymore. The built-in caching of qml files addresses the needs and unfortunately the tool does not work with more advanced concepts such as required properties or the ES 7 yield statement. It will be removed in Qt 6. Change-Id: I2830f91d1dc2ad5e3c66270dad8e25793587ae7f Reviewed-by: Ulf Hermann --- tools/qmlmin/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp index 26833d2a08..3c9b3c7251 100644 --- a/tools/qmlmin/main.cpp +++ b/tools/qmlmin/main.cpp @@ -638,6 +638,8 @@ int runQmlmin(int argc, char *argv[]) return 0; } + std::cerr << "qmlmin: This tool is deprecated and will be removed in Qt 6. It is not needed anymore due to QtQml's built-in caching." << std::endl; + QFile file(fileName); if (! file.open(QFile::ReadOnly)) { std::cerr << "qmlmin: '" << qPrintable(fileName) << "' no such file or directory" << std::endl; -- cgit v1.2.3 From a428a2199f4ce75d68585cabe3471356933fa404 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 22 Oct 2019 11:22:01 +0200 Subject: qmltyperegistrar: Improve legibility of generated code Change-Id: I0084b03651c93ea26cd93edf1badcce4721ef993 Reviewed-by: Fabian Kosmale --- tools/qmltyperegistrar/qmltyperegistrar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp index e64c23d2c9..8813907697 100644 --- a/tools/qmltyperegistrar/qmltyperegistrar.cpp +++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp @@ -341,7 +341,7 @@ int main(int argc, char **argv) const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol; - fprintf(output, "void %s()\n{\n", qPrintable(functionName)); + fprintf(output, "void %s()\n{", qPrintable(functionName)); const auto majorVersion = parser.value(majorVersionOption); for (const QJsonObject &classDef : qAsConst(types)) { @@ -357,7 +357,7 @@ int main(int argc, char **argv) qPrintable(module), qPrintable(majorVersion), qPrintable(parser.value(minorVersionOption))); fprintf(output, "\n}\n"); - fprintf(output, "static const QQmlModuleRegistration registration(\"%s\", %s, %s);\n", + fprintf(output, "\nstatic const QQmlModuleRegistration registration(\"%s\", %s, %s);\n", qPrintable(module), qPrintable(majorVersion), qPrintable(functionName)); if (!parser.isSet(pluginTypesOption)) -- cgit v1.2.3 From 8f4765534f83c27301ea758f5189f8f9b6684b6b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 7 Nov 2019 14:45:16 +0100 Subject: qmltyperegistrar: Allow QML_IMPORT_VERSION as alias for IMPORT_VERSION As we use QML_IMPORT_MAJOR_VERSION, QML_IMPORT_MINOR_VERSION, and QML_IMPORT_NAME, it's strange that we required the raw import version to come without the "QML_". Change-Id: Ida7d677851edf8f4d0179ea2a14772620d65bfa1 Reviewed-by: Fabian Kosmale --- tools/qmltyperegistrar/qmltypes.prf | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltypes.prf b/tools/qmltyperegistrar/qmltypes.prf index 8d7fd3a498..d8e826c671 100644 --- a/tools/qmltyperegistrar/qmltypes.prf +++ b/tools/qmltyperegistrar/qmltypes.prf @@ -2,12 +2,15 @@ CONFIG += metatypes qtPrepareTool(QML_TYPEREGISTRAR, qmltyperegistrar) +isEmpty(QML_IMPORT_VERSION): \ + QML_IMPORT_VERSION = $$IMPORT_VERSION + # from moc.prf -isEmpty(QML_IMPORT_MAJOR_VERSION):!isEmpty(IMPORT_VERSION): \ - QML_IMPORT_MAJOR_VERSION = $$section(IMPORT_VERSION, ., 0, 0) +isEmpty(QML_IMPORT_MAJOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \ + QML_IMPORT_MAJOR_VERSION = $$section(QML_IMPORT_VERSION, ., 0, 0) -isEmpty(QML_IMPORT_MINOR_VERSION):!isEmpty(IMPORT_VERSION): \ - QML_IMPORT_MINOR_VERSION = $$section(IMPORT_VERSION, ., 1, 1) +isEmpty(QML_IMPORT_MINOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \ + QML_IMPORT_MINOR_VERSION = $$section(QML_IMPORT_VERSION, ., 1, 1) isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) { QML_IMPORT_NAME = $$replace(TARGETPATH, "/", ".") @@ -19,7 +22,7 @@ isEmpty(QML_IMPORT_NAME) { } isEmpty(QML_IMPORT_MAJOR_VERSION) { - error("Need IMPORT_VERSION or QML_IMPORT_MAJOR_VERSION in order to generate qml types."); + error("Need IMPORT_VERSION, QML_IMPORT_VERSION, or QML_IMPORT_MAJOR_VERSION in order to generate qml types."); } isEmpty(QML_IMPORT_MINOR_VERSION) { -- cgit v1.2.3 From 9d89702ab26958dbd075de11041af0b230b1230b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 7 Nov 2019 16:47:26 +0100 Subject: Move qmltyperegistrar into src We want it to be available early in the build process. qmltyperegistrar will be required to build qml, quick, and various imports. Change-Id: I15b827cd4b322a2bcea2780eeedb8dbe132aa59a Reviewed-by: Fabian Kosmale --- tools/qmltyperegistrar/qmltyperegistrar.cpp | 410 --------------------- tools/qmltyperegistrar/qmltyperegistrar.pro | 26 -- tools/qmltyperegistrar/qmltypes.prf | 86 ----- .../qmltyperegistrar/qmltypesclassdescription.cpp | 161 -------- tools/qmltyperegistrar/qmltypesclassdescription.h | 58 --- tools/qmltyperegistrar/qmltypescreator.cpp | 357 ------------------ tools/qmltyperegistrar/qmltypescreator.h | 68 ---- tools/shared/shared.pri | 10 +- tools/tools.pro | 2 - 9 files changed, 8 insertions(+), 1170 deletions(-) delete mode 100644 tools/qmltyperegistrar/qmltyperegistrar.cpp delete mode 100644 tools/qmltyperegistrar/qmltyperegistrar.pro delete mode 100644 tools/qmltyperegistrar/qmltypes.prf delete mode 100644 tools/qmltyperegistrar/qmltypesclassdescription.cpp delete mode 100644 tools/qmltyperegistrar/qmltypesclassdescription.h delete mode 100644 tools/qmltyperegistrar/qmltypescreator.cpp delete mode 100644 tools/qmltyperegistrar/qmltypescreator.h (limited to 'tools') diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp deleted file mode 100644 index 8813907697..0000000000 --- a/tools/qmltyperegistrar/qmltyperegistrar.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -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() == QLatin1String("QML.Element")) - return true; - } - return false; -} - -static QVector foreignRelatedTypes(const QVector &types, - const QVector &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 processedRelatedNames; - QQueue typeQueue; - typeQueue.append(types.toList()); - QVector 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 \"" - "rather than \"#include \".")); - 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 minorVersionOption(QStringLiteral("minor-version")); - minorVersionOption.setDescription(QStringLiteral("Minor version to use for module registration.")); - minorVersionOption.setValueName(QStringLiteral("minor version")); - parser.addOption(minorVersionOption); - - 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 outputFile; - - if (parser.isSet(outputOption)) { - QString outputName = parser.value(outputOption); -#if defined(_MSC_VER) - if (_wfopen_s(&output, reinterpret_cast(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 \n"); - - QStringList includes; - QVector types; - QVector 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 &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 "); - const bool privateIncludes = parser.isSet(privateIncludesOption); - for (const QString &include : qAsConst(includes)) { - if (privateIncludes && include.endsWith(QLatin1String("_p.h"))) - fprintf(output, "\n#include ", 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{", 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, %s);", - qPrintable(module), qPrintable(majorVersion), - qPrintable(parser.value(minorVersionOption))); - fprintf(output, "\n}\n"); - fprintf(output, "\nstatic 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 deleted file mode 100644 index 802526d964..0000000000 --- a/tools/qmltyperegistrar/qmltyperegistrar.pro +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index d8e826c671..0000000000 --- a/tools/qmltyperegistrar/qmltypes.prf +++ /dev/null @@ -1,86 +0,0 @@ -CONFIG += metatypes - -qtPrepareTool(QML_TYPEREGISTRAR, qmltyperegistrar) - -isEmpty(QML_IMPORT_VERSION): \ - QML_IMPORT_VERSION = $$IMPORT_VERSION - -# from moc.prf -isEmpty(QML_IMPORT_MAJOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \ - QML_IMPORT_MAJOR_VERSION = $$section(QML_IMPORT_VERSION, ., 0, 0) - -isEmpty(QML_IMPORT_MINOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \ - QML_IMPORT_MINOR_VERSION = $$section(QML_IMPORT_VERSION, ., 1, 1) - -isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) { - QML_IMPORT_NAME = $$replace(TARGETPATH, "/", ".") - QML_IMPORT_NAME = $$replace(QML_IMPORT_NAME, .$${QML_IMPORT_MAJOR_VERSION}$, '') -} - -isEmpty(QML_IMPORT_NAME) { - error("Need TARGET_PATH or QML_IMPORT_NAME in order to generate qml types."); -} - -isEmpty(QML_IMPORT_MAJOR_VERSION) { - error("Need IMPORT_VERSION, QML_IMPORT_VERSION, or QML_IMPORT_MAJOR_VERSION in order to generate qml types."); -} - -isEmpty(QML_IMPORT_MINOR_VERSION) { - QML_IMPORT_MINOR_VERSION = 0 -} - -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 \ - --minor-version=$$QML_IMPORT_MINOR_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 deleted file mode 100644 index 8189bcd52e..0000000000 --- a/tools/qmltyperegistrar/qmltypesclassdescription.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** 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 - -static void collectExtraVersions(const QJsonObject *component, const QString &key, - QList &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 &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 &types, - const QVector &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 deleted file mode 100644 index 8f3a6ea124..0000000000 --- a/tools/qmltyperegistrar/qmltypesclassdescription.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include -#include - -struct QmlTypesClassDescription -{ - const QJsonObject *resolvedClass = nullptr; - QString elementName; - QString defaultProp; - QString superClass; - QString attachedType; - QList 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 &types, - const QVector &foreign, bool topLevel); - - static const QJsonObject *findType(const QVector &types, const QString &name); -}; - -#endif // QMLTYPESCLASSDESCRIPTION_H diff --git a/tools/qmltyperegistrar/qmltypescreator.cpp b/tools/qmltyperegistrar/qmltypescreator.cpp deleted file mode 100644 index 7bac6a87d8..0000000000 --- a/tools/qmltyperegistrar/qmltypescreator.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include -#include -#include - -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 ¬ifySignals) -{ - 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 ¬ifySignals) -{ - 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 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 deleted file mode 100644 index 9207a64b7e..0000000000 --- a/tools/qmltyperegistrar/qmltypescreator.h +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** 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 -#include - -class QmlTypesCreator -{ -public: - QmlTypesCreator() : m_qml(&m_output) {} - - void generate(const QString &outFileName, const QString &dependenciesFileName); - - void setOwnTypes(QVector ownTypes) { m_ownTypes = std::move(ownTypes); } - void setForeignTypes(QVector 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 ¬ifySignals); - void writeMethods(const QJsonArray &methods, const QString &type, - const QSet ¬ifySignals = QSet()); - void writeEnums(const QJsonArray &enums); - void writeComponents(); - - QByteArray m_output; - QmlStreamWriter m_qml; - QVector m_ownTypes; - QVector m_foreignTypes; - QString m_module; - int m_majorVersion = 0; -}; - -#endif // QMLTYPESCREATOR_H diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri index c094b51d5f..1438c3b3da 100644 --- a/tools/shared/shared.pri +++ b/tools/shared/shared.pri @@ -1,3 +1,9 @@ INCLUDEPATH += $$PWD -SOURCES += $$PWD/resourcefilemapper.cpp -HEADERS += $$PWD/resourcefilemapper.h + +SOURCES += \ + $$PWD/resourcefilemapper.cpp \ + $$PWD/qmlstreamwriter.cpp + +HEADERS += \ + $$PWD/resourcefilemapper.h \ + $$PWD/qmlstreamwriter.h diff --git a/tools/tools.pro b/tools/tools.pro index 69b79e8816..25ed760903 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -10,8 +10,6 @@ qtConfig(qml-devtools) { qtConfig(commandlineparser):qtConfig(xmlstreamwriter): SUBDIRS += qmlcachegen } -qtConfig(commandlineparser): SUBDIRS += qmltyperegistrar - qtConfig(thread):!android|android_app:!wasm:!rtems { SUBDIRS += \ qml -- cgit v1.2.3