Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdbuscpp2xml.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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 <qbuffer.h>
5#include <qbytearray.h>
6#include <qdebug.h>
7#include <qfile.h>
8#include <qlist.h>
9#include <qstring.h>
10#include <qvarlengtharray.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <qdbusconnection.h> // for the Export* flags
17#include <private/qdbusconnection_p.h> // for the qDBusCheckAsyncTag
18#include <private/qdbusmetatype_p.h> // for QDBusMetaTypeId::init()
19
20using namespace Qt::StringLiterals;
21
22// copied from dbus-protocol.h:
23static const char docTypeHeader[] =
24 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
25 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
26
27#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
28#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface"
29#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection"
30
31#include <qdbusmetatype.h>
32#include <private/qdbusmetatype_p.h>
33#include <private/qdbusutil_p.h>
34
35#include "moc.h"
36#include "generator.h"
37#include "preprocessor.h"
38
39#define PROGRAMNAME "qdbuscpp2xml"
40#define PROGRAMVERSION "0.2"
41#define PROGRAMCOPYRIGHT QT_COPYRIGHT
42
44static int flags;
45
46static const char help[] =
47 "Usage: " PROGRAMNAME " [options...] [files...]\n"
48 "Parses the C++ source or header file containing a QObject-derived class and\n"
49 "produces the D-Bus Introspection XML."
50 "\n"
51 "Options:\n"
52 " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n"
53 " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n"
54 " -a Output all scriptable contents (equivalent to -psm)\n"
55 " -A Output all contents (equivalent to -PSM)\n"
56 " -t <type>=<dbustype> Output <type> (ex: MyStruct) as <dbustype> (ex: {ss})\n"
57 " -o <filename> Write the output to file <filename>\n"
58 " -h Show this information\n"
59 " -V Show the program version and quit.\n"
60 "\n";
61
62int qDBusParametersForMethod(const FunctionDef &mm, QList<QMetaType> &metaTypes, QString &errorMsg)
63{
64 QList<QByteArray> parameterTypes;
65 parameterTypes.reserve(mm.arguments.size());
66
67 for (const ArgumentDef &arg : mm.arguments)
68 parameterTypes.append(arg.normalizedType);
69
70 return qDBusParametersForMethod(parameterTypes, metaTypes, errorMsg);
71}
72
73
74static inline QString typeNameToXml(const char *typeName)
75{
77 return plain.toHtmlEscaped();
78}
79
80static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
81
82 QString xml = QString::asprintf(" <%s name=\"%s\">\n",
83 isSignal ? "signal" : "method", mm.name.constData());
84
85 // check the return type first
86 int typeId = QMetaType::fromName(mm.normalizedType).id();
87 if (typeId != QMetaType::Void) {
88 if (typeId) {
90 if (typeName) {
91 xml += QString::fromLatin1(" <arg type=\"%1\" direction=\"out\"/>\n")
93
94 // do we need to describe this argument?
96 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
98 } else {
99 return QString();
100 }
101 } else if (!mm.normalizedType.isEmpty()) {
102 qWarning() << "Unregistered return type:" << mm.normalizedType.constData();
103 return QString();
104 }
105 }
106 QList<ArgumentDef> names = mm.arguments;
107 QList<QMetaType> types;
108 QString errorMsg;
109 int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
110 if (inputCount == -1) {
111 qWarning() << qPrintable(errorMsg);
112 return QString(); // invalid form
113 }
114 if (isSignal && inputCount + 1 != types.size())
115 return QString(); // signal with output arguments?
116 if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message())
117 return QString(); // signal with QDBusMessage argument?
118
119 bool isScriptable = mm.isScriptable;
120 for (qsizetype j = 1; j < types.size(); ++j) {
121 // input parameter for a slot or output for a signal
122 if (types.at(j) == QDBusMetaTypeId::message()) {
123 isScriptable = true;
124 continue;
125 }
126
128 if (!names.at(j - 1).name.isEmpty())
129 name = QString::fromLatin1("name=\"%1\" ").arg(QString::fromLatin1(names.at(j - 1).name));
130
131 bool isOutput = isSignal || j > inputCount;
132
133 const char *signature = QDBusMetaType::typeToSignature(QMetaType(types.at(j)));
134 xml += QString::fromLatin1(" <arg %1type=\"%2\" direction=\"%3\"/>\n")
135 .arg(name,
136 QLatin1StringView(signature),
137 isOutput ? "out"_L1 : "in"_L1);
138
139 // do we need to describe this argument?
140 if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
141 const char *typeName = QMetaType(types.at(j)).name();
142 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
143 .arg(isOutput ? "Out"_L1 : "In"_L1)
144 .arg(isOutput && !isSignal ? j - inputCount : j - 1)
146 }
147 }
148
149 int wantedMask;
150 if (isScriptable)
151 wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
153 else
154 wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
156 if ((flags & wantedMask) != wantedMask)
157 return QString();
158
160 // add the no-reply annotation
161 xml += " <annotation name=\"" ANNOTATION_NO_WAIT "\" value=\"true\"/>\n"_L1;
162
163 QString retval = xml;
164 retval += QString::fromLatin1(" </%1>\n").arg(isSignal ? "signal"_L1 : "method"_L1);
165
166 return retval;
167}
168
169
171{
172 QString retval;
173
174 // start with properties:
177 static const char *accessvalues[] = {nullptr, "read", "write", "readwrite"};
178 for (const PropertyDef &mp : mo->propertyList) {
179 if (!((!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportScriptableProperties)) ||
180 (!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportNonScriptableProperties))))
181 continue;
182
183 int access = 0;
184 if (!mp.read.isEmpty())
185 access |= 1;
186 if (!mp.write.isEmpty())
187 access |= 2;
188 if (!mp.member.isEmpty())
189 access |= 3;
190
191 int typeId = QMetaType::fromName(mp.type).id();
192 if (!typeId) {
193 fprintf(stderr, PROGRAMNAME ": unregistered type: '%s', ignoring\n",
194 mp.type.constData());
195 continue;
196 }
197 const char *signature = QDBusMetaType::typeToSignature(QMetaType(typeId));
198 if (!signature)
199 continue;
200
201 retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
202 .arg(QLatin1StringView(mp.name),
203 QLatin1StringView(signature),
204 QLatin1StringView(accessvalues[access]));
205
206 if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
207 retval += QString::fromLatin1(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
208 .arg(typeNameToXml(mp.type.constData()));
209 } else {
210 retval += "/>\n"_L1;
211 }
212 }
213 }
214
215 // now add methods:
216
218 for (const FunctionDef &mm : mo->signalList) {
219 if (mm.wasCloned)
220 continue;
221 if (!mm.isScriptable && !(flags & QDBusConnection::ExportNonScriptableSignals))
222 continue;
223
224 retval += addFunction(mm, true);
225 }
226 }
227
229 for (const FunctionDef &slot : mo->slotList) {
230 if (!slot.isScriptable && !(flags & QDBusConnection::ExportNonScriptableSlots))
231 continue;
232 if (slot.access == FunctionDef::Public)
233 retval += addFunction(slot);
234 }
235 for (const FunctionDef &method : mo->methodList) {
237 continue;
238 if (method.access == FunctionDef::Public)
239 retval += addFunction(method);
240 }
241 }
242 return retval;
243}
244
246{
248
249 for (const ClassInfoDef &cid : mo->classInfoList) {
250 if (cid.name == QCLASSINFO_DBUS_INTERFACE)
251 return QString::fromUtf8(cid.value);
252 }
253 interface = QLatin1StringView(mo->classname);
254 interface.replace("::"_L1, "."_L1);
255
256 if (interface.startsWith("QDBus"_L1)) {
257 interface.prepend("org.qtproject.QtDBus."_L1);
258 } else if (interface.startsWith(u'Q') &&
259 interface.size() >= 2 && interface.at(1).isUpper()) {
260 // assume it's Qt
261 interface.prepend("local.org.qtproject.Qt."_L1);
262 } else {
263 interface.prepend("local."_L1);
264 }
265
266 return interface;
267}
268
269
271{
272 for (const ClassInfoDef &cid : cdef->classInfoList) {
273 if (cid.name == QCLASSINFO_DBUS_INTROSPECTION)
274 return QString::fromUtf8(cid.value);
275 }
276
277 // generate the interface name from the meta object
278 QString interface = qDBusInterfaceFromClassDef(cdef);
279
281
282 if (xml.isEmpty())
283 return QString(); // don't add an empty interface
284 return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n")
285 .arg(interface, xml);
286}
287
288static void showHelp()
289{
290 printf("%s", help);
291 exit(0);
292}
293
294static void showVersion()
295{
296 printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
297 printf("D-Bus QObject-to-XML converter\n");
298 exit(0);
299}
300
301class CustomType {
302public:
303 CustomType(const QByteArray &typeName)
305 {
306 metaTypeImpl.name = typeName.constData();
307 }
308 QMetaType metaType() const { return QMetaType(&metaTypeImpl); }
309
310private:
311 // not copiable and not movable because of QBasicAtomicInt
312 QtPrivate::QMetaTypeInterface metaTypeImpl =
313 { /*.revision=*/ 0,
314 /*.alignment=*/ 0,
315 /*.size=*/ 0,
316 /*.flags=*/ 0,
317 /*.typeId=*/ 0,
318 /*.metaObjectFn=*/ 0,
319 /*.name=*/ nullptr, // set by the constructor
320 /*.defaultCtr=*/ nullptr,
321 /*.copyCtr=*/ nullptr,
322 /*.moveCtr=*/ nullptr,
323 /*.dtor=*/ nullptr,
324 /*.equals=*/ nullptr,
325 /*.lessThan=*/ nullptr,
326 /*.debugStream=*/ nullptr,
327 /*.dataStreamOut=*/ nullptr,
328 /*.dataStreamIn=*/ nullptr,
329 /*.legacyRegisterOp=*/ nullptr
330 };
331 QByteArray typeName;
332};
333// Unlike std::vector, std::deque works with non-copiable non-movable types
334static std::deque<CustomType> s_customTypes;
335
337{
338 flags = 0;
339 for (qsizetype i = 0; i < arguments.size(); ++i) {
340 const QString arg = arguments.at(i);
341
342 if (arg == "--help"_L1)
343 showHelp();
344
345 if (!arg.startsWith(u'-'))
346 continue;
347
348 char c = arg.size() == 2 ? arg.at(1).toLatin1() : char(0);
349 switch (c) {
350 case 'P':
353 case 'p':
355 break;
356
357 case 'S':
360 case 's':
362 break;
363
364 case 'M':
367 case 'm':
369 break;
370
371 case 'A':
374 case 'a':
376 break;
377
378 case 't':
379 if (arguments.size() < i + 2) {
380 printf("-t expects a type=dbustype argument\n");
381 exit(1);
382 } else {
383 const QByteArray arg = arguments.takeAt(i + 1).toUtf8();
384 // lastIndexOf because the C++ type could contain '=' while the DBus type can't
385 const qsizetype separator = arg.lastIndexOf('=');
386 if (separator == -1) {
387 printf("-t expects a type=dbustype argument, but no '=' was found\n");
388 exit(1);
389 }
390 const QByteArray type = arg.left(separator);
391 const QByteArray dbustype = arg.mid(separator+1);
392
393 s_customTypes.emplace_back(type);
394 QMetaType metaType = s_customTypes.back().metaType();
395 QDBusMetaType::registerCustomType(metaType, dbustype);
396 }
397 break;
398
399 case 'o':
400 if (arguments.size() < i + 2 || arguments.at(i + 1).startsWith(u'-')) {
401 printf("-o expects a filename\n");
402 exit(1);
403 }
405 break;
406
407 case 'h':
408 case '?':
409 showHelp();
410 break;
411
412 case 'V':
413 showVersion();
414 break;
415
416 default:
417 printf("unknown option: \"%s\"\n", qPrintable(arg));
418 exit(1);
419 }
420 }
421
422 if (flags == 0)
425}
426
427int main(int argc, char **argv)
428{
430 args.reserve(argc - 1);
431 for (int n = 1; n < argc; ++n)
434
436
437 QList<ClassDef> classes;
438
439 if (args.isEmpty())
440 args << u"-"_s;
441 for (const auto &arg: std::as_const(args)) {
442 if (arg.startsWith(u'-') && arg.size() > 1)
443 continue;
444
445 QFile f;
446 bool fileIsOpen;
448 if (arg == u'-') {
449 fileName = "stdin"_L1;
450 fileIsOpen = f.open(stdin, QIODevice::ReadOnly | QIODevice::Text);
451 } else {
452 fileName = arg;
453 f.setFileName(arg);
454 fileIsOpen = f.open(QIODevice::ReadOnly | QIODevice::Text);
455 }
456 if (!fileIsOpen) {
457 fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
458 qPrintable(fileName), qPrintable(f.errorString()));
459 return 1;
460 }
461
462 Preprocessor pp;
463 Moc moc;
464 pp.macros["Q_MOC_RUN"];
465 pp.macros["__cplusplus"];
466
467 const QByteArray filename = arg.toLocal8Bit();
468
469 moc.filename = filename;
470 moc.currentFilenames.push(filename);
471
472 moc.symbols = pp.preprocessed(moc.filename, &f);
473 moc.parse();
474
475 if (moc.classList.isEmpty())
476 return 0;
477 classes = moc.classList;
478
479 f.close();
480 }
481
483 if (outputFile.isEmpty()) {
484 if (!output.open(stdout, QIODevice::WriteOnly)) {
485 fprintf(stderr, PROGRAMNAME ": could not open standard output: %s\n",
486 qPrintable(output.errorString()));
487 return 1;
488 }
489 } else {
490 output.setFileName(outputFile);
491 if (!output.open(QIODevice::WriteOnly)) {
492 fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s\n",
493 qPrintable(outputFile), qPrintable(output.errorString()));
494 return 1;
495 }
496 }
497
498 output.write(docTypeHeader);
499 output.write("<node>\n");
500 for (const ClassDef &cdef : std::as_const(classes)) {
502 output.write(std::move(xml).toLocal8Bit());
503 }
504 output.write("</node>\n");
505
506 return 0;
507}
508
QMetaType metaType() const
CustomType(const QByteArray &typeName)
Definition moc.h:208
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QMetaType signatureToMetaType(const char *signature)
static const char * typeToSignature(QMetaType type)
static void registerCustomType(QMetaType type, const QByteArray &signature)
\inmodule QtCore
Definition qfile.h:93
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T takeAt(qsizetype i)
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmetatype.h:341
static QMetaType fromName(QByteArrayView name)
Returns a QMetaType matching typeName.
constexpr const char * name() const
Definition qmetatype.h:2680
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
int main()
[0]
auto mo
[7]
QList< QVariant > arguments
Q_DBUS_EXPORT void init()
QMetaType message()
#define Q_FALLTHROUGH()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
Q_DBUS_EXPORT bool qDBusCheckAsyncTag(const char *tag)
Definition qdbusmisc.cpp:25
static QString outputFile
int qDBusParametersForMethod(const FunctionDef &mm, QList< QMetaType > &metaTypes, QString &errorMsg)
#define PROGRAMNAME
#define QCLASSINFO_DBUS_INTERFACE
static const char help[]
static QString addFunction(const FunctionDef &mm, bool isSignal=false)
static std::deque< CustomType > s_customTypes
QString qDBusGenerateClassDefXml(const ClassDef *cdef)
#define QCLASSINFO_DBUS_INTROSPECTION
static void showVersion()
static const char docTypeHeader[]
static void parseCmdLine(QStringList &arguments)
#define ANNOTATION_NO_WAIT
QString qDBusInterfaceFromClassDef(const ClassDef *mo)
static QString typeNameToXml(const char *typeName)
#define PROGRAMVERSION
static int flags
static void showHelp()
static QString generateInterfaceXml(const ClassDef *mo)
static QString moc(const QString &name)
static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
#define qWarning
Definition qlogging.h:166
const char * typeName
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLenum type
GLenum access
GLbitfield flags
GLuint name
GLfloat n
const GLubyte * c
GLuint GLuint * names
SSL_CTX int void * arg
#define qPrintable(string)
Definition qstring.h:1531
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
ptrdiff_t qsizetype
Definition qtypes.h:165
QT_BEGIN_NAMESPACE typedef uchar * output
QXmlStreamReader xml
[0]
QJSValueList args
QList< ClassInfoDef > classInfoList
Definition moc.h:150
bool isScriptable
Definition moc.h:89
QByteArray normalizedType
Definition moc.h:70
QByteArray name
Definition moc.h:72
QList< ArgumentDef > arguments
Definition moc.h:69
@ Public
Definition moc.h:75
QByteArray tag
Definition moc.h:71