Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmltyperegistrar.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <QFile>
5#include <QCborArray>
6#include <QCborValue>
7
12
14using namespace Qt::Literals;
15using namespace Constants;
16using namespace Constants::MetatypesDotJson;
18using namespace QAnyStringViewUtils;
19
21{
25};
26
33{
34 if (x.removedIn.isValid())
35 return y.addedIn.isValid() ? x.removedIn <= y.addedIn : true;
36 else
37 return false;
38}
39
46{
47 return !(x < y) && !(y < x);
48}
49
52{
53 allArguments.reserve(arguments.size());
54 for (const QString &argument : arguments) {
55 // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
56 if (argument.startsWith(QLatin1Char('@'))) {
57 QString optionsFile = argument;
58 optionsFile.remove(0, 1);
59 if (optionsFile.isEmpty()) {
60 fprintf(stderr, "The @ option requires an input file");
61 return false;
62 }
63 QFile f(optionsFile);
65 fprintf(stderr, "Cannot open options file specified with @");
66 return false;
67 }
68 while (!f.atEnd()) {
69 QString line = QString::fromLocal8Bit(f.readLine().trimmed());
70 if (!line.isEmpty())
71 allArguments << line;
72 }
73 } else {
74 allArguments << argument;
75 }
76 }
77 return true;
78}
79
80int QmlTypeRegistrar::runExtract(const QString &baseName, const MetaTypesJsonProcessor &processor)
81{
82 if (processor.types().isEmpty()) {
83 fprintf(stderr, "Error: No types to register found in library\n");
84 return EXIT_FAILURE;
85 }
86 QFile headerFile(baseName + u".h");
87 bool ok = headerFile.open(QFile::WriteOnly);
88 if (!ok) {
89 fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(headerFile.fileName()));
90 return EXIT_FAILURE;
91 }
92 auto prefix = QString::fromLatin1(
93 "#ifndef %1_H\n"
94 "#define %1_H\n"
95 "#include <QtQml/qqml.h>\n"
96 "#include <QtQml/qqmlmoduleregistration.h>\n").arg(baseName.toUpper());
97 const QList<QString> includes = processor.includes();
98 for (const QString &include: includes)
99 prefix += u"\n#include <%1>"_s.arg(include);
100 headerFile.write((prefix + processor.extractRegisteredTypes()).toUtf8() + "\n#endif");
101
102 QFile sourceFile(baseName + u".cpp");
103 ok = sourceFile.open(QFile::WriteOnly);
104 if (!ok) {
105 fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(sourceFile.fileName()));
106 return EXIT_FAILURE;
107 }
108 // the string split is necessaury because cmake's automoc scanner would otherwise pick up the include
109 QString code = u"#include \"%1.h\"\n#include "_s.arg(baseName);
110 code += uR"("moc_%1.cpp")"_s.arg(baseName);
111 sourceFile.write(code.toUtf8());
112 return EXIT_SUCCESS;
113}
114
115QCborValue QmlTypeRegistrar::findType(QAnyStringView name) const
116{
117 for (const QCborMap &type : m_types) {
119 continue;
120 return type;
121 }
122 return QCborValue();
123};
124
125QCborValue QmlTypeRegistrar::findTypeForeign(QAnyStringView name) const
126{
127 for (const QCborMap &type : m_foreignTypes) {
129 continue;
130 return type;
131 }
132 return QCborValue();
133};
134
136{
137 using namespace Qt::StringLiterals;
138
139 QString s = r.claimerName;
140 if (r.addedIn.isValid()) {
141 s += u" (added in %1.%2)"_s.arg(r.addedIn.majorVersion()).arg(r.addedIn.minorVersion());
142 }
143 if (r.removedIn.isValid()) {
144 s += u" (removed in %1.%2)"_s.arg(r.removedIn.majorVersion())
145 .arg(r.removedIn.minorVersion());
146 }
147 return s;
148};
149
151{
152 output << uR"(/****************************************************************************
153** Generated QML type registration code
154**
155** WARNING! All changes made in this file will be lost!
156*****************************************************************************/
157
158)"_s;
159
160 output << u"#include <QtQml/qqml.h>\n"_s;
161 output << u"#include <QtQml/qqmlmoduleregistration.h>\n"_s;
162
163 for (const QString &include : m_includes)
164 output << u"\n#include <%1>"_s.arg(include);
165
166 output << u"\n\n"_s;
167
168 // Keep this in sync with _qt_internal_get_escaped_uri in CMake
169 QString moduleAsSymbol = m_module;
170 static const QRegularExpression nonAlnumRegexp(QLatin1String("[^A-Za-z0-9]"));
171 moduleAsSymbol.replace(nonAlnumRegexp, QStringLiteral("_"));
172
173 QString underscoredModuleAsSymbol = m_module;
174 underscoredModuleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
175
176 if (underscoredModuleAsSymbol != moduleAsSymbol
177 || underscoredModuleAsSymbol.isEmpty()
178 || underscoredModuleAsSymbol.front().isDigit()) {
179 qWarning() << m_module << "is an invalid QML module URI. You cannot import this.";
180 }
181
182 const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
183 output << uR"(
184#if !defined(QT_STATIC)
185#define Q_QMLTYPE_EXPORT Q_DECL_EXPORT
186#else
187#define Q_QMLTYPE_EXPORT
188#endif
189)"_s;
190
191 if (!m_targetNamespace.isEmpty())
192 output << u"namespace "_s << m_targetNamespace << u" {\n"_s;
193
194 output << u"Q_QMLTYPE_EXPORT void "_s << functionName << u"()\n{"_s;
195 const quint8 majorVersion = m_moduleVersion.majorVersion();
196 const quint8 minorVersion = m_moduleVersion.minorVersion();
197
198 for (const auto &version : m_pastMajorVersions) {
199 output << uR"(
200 qmlRegisterModule("%1", %2, 0);
201 qmlRegisterModule("%1", %2, 254);)"_s.arg(m_module)
202 .arg(version);
203 }
204
205 if (minorVersion != 0) {
206 output << uR"(
207 qmlRegisterModule("%1", %2, 0);)"_s.arg(m_module)
208 .arg(majorVersion);
209 }
210
211 QVector<QAnyStringView> typesRegisteredAnonymously;
213
214 for (const QCborMap &classDef : m_types) {
215 const QString className = classDef[S_QUALIFIED_CLASS_NAME].toString();
216
217 QString targetName = className;
218 QAnyStringView extendedName;
219 bool seenQmlElement = false;
220 QString qmlElementName;
221 QTypeRevision addedIn;
222 QTypeRevision removedIn;
223
224 const QCborArray classInfos = classDef.value(S_CLASS_INFOS).toArray();
225 for (const QCborValueConstRef info : classInfos) {
226 const QCborMap v = info.toMap();
228 if (name == S_ELEMENT) {
229 seenQmlElement = true;
230 qmlElementName = v[S_VALUE].toString();
231 } else if (name == S_FOREIGN)
232 targetName = v[S_VALUE].toString();
233 else if (name == S_EXTENDED)
234 extendedName = toStringView(v, S_VALUE);
235 else if (name == S_ADDED_IN_VERSION) {
236 int version = toInt(toStringView(v, S_VALUE));
237 addedIn = QTypeRevision::fromEncodedVersion(version);
238 } else if (name == S_REMOVED_IN_VERSION) {
239 int version = toInt(toStringView(v, S_VALUE));
240 removedIn = QTypeRevision::fromEncodedVersion(version);
241 }
242 }
243
244 if (seenQmlElement && qmlElementName != S_ANONYMOUS) {
245 if (qmlElementName == S_AUTO)
246 qmlElementName = className;
247 qmlElementInfos[qmlElementName].append({ className, addedIn, removedIn });
248 }
249
250 // We want all related metatypes to be registered by name, so that we can look them up
251 // without including the C++ headers. That's the reason for the QMetaType(foo).id() calls.
252
253 if (classDef.value(S_NAMESPACE).toBool()) {
254 // We need to figure out if the _target_ is a namespace. If not, it already has a
255 // QMetaType and we don't need to generate one.
256
257 QString targetTypeName = targetName;
258 const auto targetIsNamespace = [&]() {
259 if (className == targetName)
260 return true;
261
262 const QList<QAnyStringView> namespaces
264
266 m_types, m_foreignTypes, targetName, namespaces);
267
268 if (!target)
269 return false;
270
271 if (target->value(S_NAMESPACE).toBool())
272 return true;
273
274 if (target->value(S_OBJECT).toBool())
275 targetTypeName += QStringLiteral(" *");
276
277 return false;
278 };
279
280 if (targetIsNamespace()) {
281 output << uR"(
282 {
283 Q_CONSTINIT static auto metaType = QQmlPrivate::metaTypeForNamespace(
284 [](const QtPrivate::QMetaTypeInterface *) {return &%1::staticMetaObject;},
285 "%2");
286 QMetaType(&metaType).id();
287 })"_s.arg(targetName, targetTypeName);
288 } else {
289 output << u"\n QMetaType::fromType<%1>().id();"_s.arg(targetTypeName);
290 }
291
292 auto metaObjectPointer = [](QAnyStringView name) -> QString {
294 const QLatin1StringView staticMetaObject = "::staticMetaObject"_L1;
295 result.reserve(1 + name.length() + staticMetaObject.length());
296 result.append('&'_L1);
297 name.visit([&](auto view) { result.append(view); });
298 result.append(staticMetaObject);
299 return result;
300 };
301
302 if (seenQmlElement) {
303 output << uR"(
304 qmlRegisterNamespaceAndRevisions(%1, "%2", %3, nullptr, %4, %5);)"_s
305 .arg(metaObjectPointer(targetName), m_module)
306 .arg(majorVersion)
307 .arg(metaObjectPointer(className),
308 extendedName.isEmpty() ? QStringLiteral("nullptr")
309 : metaObjectPointer(extendedName));
310 }
311 } else {
312 if (seenQmlElement) {
313 auto checkRevisions = [&](const QCborArray &array, QLatin1StringView type) {
314 for (auto it = array.constBegin(); it != array.constEnd(); ++it) {
315 auto object = it->toMap();
316 if (!object.contains(S_REVISION))
317 continue;
318
320 object[S_REVISION].toInteger());
321 if (m_moduleVersion < revision) {
322 qWarning().noquote()
323 << "Warning:" << className << "is trying to register" << type
324 << object[S_NAME].toString()
325 << "with future version" << revision
326 << "when module version is only" << m_moduleVersion;
327 }
328 }
329 };
330
331 const QCborArray methods = classDef[S_METHODS].toArray();
332 const QCborArray properties = classDef[S_PROPERTIES].toArray();
333
334 if (m_moduleVersion.isValid()) {
335 checkRevisions(properties, S_PROPERTY);
336 checkRevisions(methods, S_METHOD);
337 }
338
339 output << uR"(
340 qmlRegisterTypesAndRevisions<%1>("%2", %3);)"_s.arg(className, m_module).arg(majorVersion);
341
342 const QCborValue superClasses = classDef[S_SUPER_CLASSES];
343
344 if (superClasses.isArray()) {
345 for (const QCborValueRef entry : superClasses.toArray()) {
346 const QCborMap object = entry.toMap();
347 if (object[S_ACCESS] != S_PUBLIC)
348 continue;
349
350 QAnyStringView superClassName = toStringView(object, S_NAME);
351
352 QVector<QAnyStringView> classesToCheck;
353
354 auto checkForRevisions = [&](QAnyStringView typeName) -> void {
355 auto type = findType(typeName);
356
357 if (!type.isMap()) {
358 type = findTypeForeign(typeName);
359 if (!type.isMap())
360 return;
361
362 const auto typeAsMap = type.toMap();
363 for (QLatin1StringView section : { S_PROPERTIES, S_SIGNALS, S_METHODS }) {
364 bool foundRevisionEntry = false;
365 for (const QCborValueRef entry : typeAsMap[section].toArray()) {
366 if (entry.toMap().contains(S_REVISION)) {
367 foundRevisionEntry = true;
368 break;
369 }
370 }
371 if (foundRevisionEntry) {
372 if (typesRegisteredAnonymously.contains(typeName))
373 break;
374
375 typesRegisteredAnonymously.append(typeName);
376
377 if (m_followForeignVersioning) {
378 output << uR"(
379 qmlRegisterAnonymousTypesAndRevisions<%1>("%2", %3);)"_s.arg(typeName.toString(), m_module)
380 .arg(majorVersion);
381 break;
382 }
383
384 for (const auto &version : m_pastMajorVersions
385 + decltype(m_pastMajorVersions){
386 majorVersion }) {
387 output << uR"(
388 qmlRegisterAnonymousType<%1, 254>("%2", %3);)"_s.arg(typeName.toString(), m_module)
389 .arg(version);
390 }
391 break;
392 }
393 }
394 }
395
396 const QCborValue superClasses = type.toMap()[S_SUPER_CLASSES];
397
398 if (superClasses.isArray()) {
399 for (const QCborValueRef entry : superClasses.toArray()) {
400 const QCborMap object = entry.toMap();
401 if (object[S_ACCESS] != S_PUBLIC)
402 continue;
403 classesToCheck << toStringView(object, S_NAME);
404 }
405 }
406 };
407
408 checkForRevisions(superClassName);
409
410 while (!classesToCheck.isEmpty())
411 checkForRevisions(classesToCheck.takeFirst());
412 }
413 }
414 } else {
415 output << uR"(
416 QMetaType::fromType<%1%2>().id();)"_s.arg(
417 className, classDef.value(S_OBJECT).toBool() ? u" *" : u"");
418 }
419 }
420 }
421
422 for (const auto [qmlName, exportsForSameQmlName] : qmlElementInfos.asKeyValueRange()) {
423 // needs a least two cpp classes exporting the same qml element to potentially have a
424 // conflict
425 if (exportsForSameQmlName.size() < 2)
426 continue;
427
428 // sort exports by versions to find conflicting exports
429 std::sort(exportsForSameQmlName.begin(), exportsForSameQmlName.end());
430 auto conflictingExportStartIt = exportsForSameQmlName.cbegin();
431 while (1) {
432 // conflicting versions evaluate to true under operator==
433 conflictingExportStartIt =
434 std::adjacent_find(conflictingExportStartIt, exportsForSameQmlName.cend());
435 if (conflictingExportStartIt == exportsForSameQmlName.cend())
436 break;
437
438 auto conflictingExportEndIt = std::find_if_not(
439 conflictingExportStartIt, exportsForSameQmlName.cend(),
440 [=](const auto &x) -> bool { return x == *conflictingExportStartIt; });
441 QString registeringCppClasses = conflictingExportStartIt->claimerName;
442 std::for_each(std::next(conflictingExportStartIt), conflictingExportEndIt,
443 [&](const auto &q) {
444 registeringCppClasses += u", %1"_s.arg(conflictingVersionToString(q));
445 });
446 qWarning().noquote() << "Warning:" << qmlName
447 << "was registered multiple times by following Cpp classes: "
448 << registeringCppClasses;
449 conflictingExportStartIt = conflictingExportEndIt;
450 }
451 }
452 output << uR"(
453 qmlRegisterModule("%1", %2, %3);
454}
455
456static const QQmlModuleRegistration registration("%1", %4);
457)"_s.arg(m_module)
458 .arg(majorVersion)
459 .arg(minorVersion)
460 .arg(functionName);
461
462 if (!m_targetNamespace.isEmpty())
463 output << u"} // namespace %1\n"_s.arg(m_targetNamespace);
464}
465
466bool QmlTypeRegistrar::generatePluginTypes(const QString &pluginTypesFile)
467{
469 creator.setOwnTypes(m_types);
470 creator.setForeignTypes(m_foreignTypes);
471 creator.setReferencedTypes(m_referencedTypes);
472 creator.setModule(m_module.toUtf8());
473 creator.setVersion(QTypeRevision::fromVersion(m_moduleVersion.majorVersion(), 0));
474
475 return creator.generate(pluginTypesFile);
476}
477
479 const QString &targetNamespace)
480{
481 m_module = module;
482 m_targetNamespace = targetNamespace;
483}
485 const QList<quint8> &pastMajorVersions,
486 bool followForeignVersioning)
487{
488 m_moduleVersion = moduleVersion;
489 m_pastMajorVersions = pastMajorVersions;
490 m_followForeignVersioning = followForeignVersioning;
491}
493{
494 m_includes = includes;
495}
497 const QVector<QCborMap> &foreignTypes)
498{
499 m_types = types;
500 m_foreignTypes = foreignTypes;
501}
503{
504 m_referencedTypes = referencedTypes;
505}
506
static JNINativeMethod methods[]
QList< QString > includes() const
QVector< QCborMap > types() const
static QList< QAnyStringView > namespaces(const QCborMap &classDef)
\inmodule QtCore
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
\inmodule QtCore\reentrant
Definition qcborarray.h:20
\inmodule QtCore\reentrant
Definition qcbormap.h:21
\inmodule QtCore\reentrant
Definition qcborvalue.h:50
QCborArray toArray() const
QCborMap toMap() const
bool isArray() const
Returns true if this QCborValue is of the array type.
Definition qcborvalue.h:161
constexpr bool isDigit() const noexcept
Returns true if the character is a decimal digit (Number_DecimalDigit); otherwise returns false.
Definition qchar.h:473
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
QString fileName() const override
Returns the name set by setFileName() or to the QFile constructors.
Definition qfile.cpp:277
\inmodule QtCore
Definition qhash.h:818
auto asKeyValueRange() &
Definition qhash.h:1218
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
constexpr qsizetype length() const noexcept
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
value_type takeFirst()
Definition qlist.h:549
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore \reentrant
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QChar front() const
Definition qstring.h:214
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
QByteArray toUtf8() const &
Definition qstring.h:563
QString toUpper() const &
Definition qstring.h:372
\inmodule QtCore
\inmodule QtCore
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
Produces a QTypeRevision from the given majorVersion and minorVersion, both of which need to be a val...
static constexpr QTypeRevision fromEncodedVersion(Integer value)
Produces a QTypeRevision from the given value.
constexpr quint8 minorVersion() const
Returns the minor version encoded in the revision.
constexpr bool isValid() const
Returns true if the major version or the minor version is known, otherwise false.
constexpr quint8 majorVersion() const
Returns the major version encoded in the revision.
void setModuleNameAndNamespace(const QString &module, const QString &targetNamespace)
void setTypes(const QVector< QCborMap > &types, const QVector< QCborMap > &foreignTypes)
static bool argumentsFromCommandLineAndFile(QStringList &allArguments, const QStringList &arguments)
void setIncludes(const QList< QString > &includes)
void write(QTextStream &os)
void setModuleVersions(QTypeRevision moduleVersion, const QList< quint8 > &pastMajorVersions, bool followForeignVersioning)
bool generatePluginTypes(const QString &pluginTypesFile)
void setReferencedTypes(const QList< QAnyStringView > &referencedTypes)
static int runExtract(const QString &baseName, const MetaTypesJsonProcessor &processor)
QSet< QString >::iterator it
QList< QVariant > arguments
static constexpr QLatin1StringView S_FOREIGN
static constexpr QLatin1StringView S_EXTENDED
static constexpr QLatin1StringView S_REMOVED_IN_VERSION
static constexpr QLatin1StringView S_ELEMENT
static constexpr QLatin1StringView S_ADDED_IN_VERSION
static constexpr QLatin1StringView S_VALUE
static constexpr QLatin1StringView S_ANONYMOUS
static constexpr QLatin1StringView S_SIGNALS
static constexpr QLatin1StringView S_QUALIFIED_CLASS_NAME
static constexpr QLatin1StringView S_NAME
static constexpr QLatin1StringView S_SUPER_CLASSES
static constexpr QLatin1StringView S_ACCESS
static constexpr QLatin1StringView S_CLASS_INFOS
static constexpr QLatin1StringView S_REVISION
static constexpr QLatin1StringView S_PUBLIC
static constexpr QLatin1StringView S_PROPERTIES
static constexpr QLatin1StringView S_PROPERTY
static constexpr QLatin1StringView S_METHODS
static constexpr QLatin1StringView S_AUTO
static constexpr QLatin1StringView S_METHOD
static constexpr QLatin1StringView S_NAMESPACE
static constexpr QLatin1StringView S_OBJECT
QAnyStringView toStringView(const QCborValue &value)
Combined button and popup list for selecting options.
static const QCssKnownValue properties[NumProperties - 1]
#define qWarning
Definition qlogging.h:162
const char * typeName
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLboolean r
[2]
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLenum type
GLenum target
GLuint name
GLint y
GLuint entry
GLenum array
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
bool operator<(const ExclusiveVersionRange &x, const ExclusiveVersionRange &y)
True if x was removed before y was introduced.
QString conflictingVersionToString(const ExclusiveVersionRange &r)
bool operator==(const ExclusiveVersionRange &x, const ExclusiveVersionRange &y)
True when x and y share a common version.
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
unsigned char quint8
Definition qtypes.h:41
static int toInt(const QChar &qc, int R)
QT_BEGIN_NAMESPACE typedef uchar * output
const char className[16]
[1]
Definition qwizard.cpp:100
QFileInfo info(fileName)
[8]
QItemEditorCreatorBase * creator
QQuickView * view
[0]
QDBusArgument argument
\inmodule QtCore \reentrant
Definition qchar.h:17
bool contains(const AT &t) const noexcept
Definition qlist.h:44
static const QCborMap * findType(const QVector< QCborMap > &types, const QVector< QCborMap > &foreign, const QAnyStringView &name, const QList< QAnyStringView > &namespaces)