Qt 6.x
The Qt SDK
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
19using namespace Qt::StringLiterals;
20
21// copied from dbus-protocol.h:
22static const char docTypeHeader[] =
23 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
24 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
25
26#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
27#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface"
28#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection"
29
30#include <qdbusmetatype.h>
31#include <private/qdbusmetatype_p.h>
32#include <private/qdbusutil_p.h>
33
34#include "moc.h"
35#include "generator.h"
36#include "preprocessor.h"
37
38#define PROGRAMNAME "qdbuscpp2xml"
39#define PROGRAMVERSION "0.2"
40#define PROGRAMCOPYRIGHT QT_COPYRIGHT
41
43static int flags;
44
45static const char help[] =
46 "Usage: " PROGRAMNAME " [options...] [files...]\n"
47 "Parses the C++ source or header file containing a QObject-derived class and\n"
48 "produces the D-Bus Introspection XML."
49 "\n"
50 "Options:\n"
51 " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n"
52 " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n"
53 " -a Output all scriptable contents (equivalent to -psm)\n"
54 " -A Output all contents (equivalent to -PSM)\n"
55 " -t <type>=<dbustype> Output <type> (ex: MyStruct) as <dbustype> (ex: {ss})\n"
56 " -o <filename> Write the output to file <filename>\n"
57 " -h Show this information\n"
58 " -V Show the program version and quit.\n"
59 "\n";
60
61int qDBusParametersForMethod(const FunctionDef &mm, QList<QMetaType> &metaTypes, QString &errorMsg)
62{
63 QList<QByteArray> parameterTypes;
64 parameterTypes.reserve(mm.arguments.size());
65
66 for (const ArgumentDef &arg : mm.arguments)
67 parameterTypes.append(arg.normalizedType);
68
69 return qDBusParametersForMethod(parameterTypes, metaTypes, errorMsg);
70}
71
72
73static inline QString typeNameToXml(const char *typeName)
74{
76 return plain.toHtmlEscaped();
77}
78
79static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
80
81 QString xml = QString::asprintf(" <%s name=\"%s\">\n",
82 isSignal ? "signal" : "method", mm.name.constData());
83
84 // check the return type first
85 int typeId = QMetaType::fromName(mm.normalizedType).id();
86 if (typeId != QMetaType::Void) {
87 if (typeId) {
89 if (typeName) {
90 xml += QString::fromLatin1(" <arg type=\"%1\" direction=\"out\"/>\n")
92
93 // do we need to describe this argument?
95 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
97 } else {
98 return QString();
99 }
100 } else if (!mm.normalizedType.isEmpty()) {
101 qWarning() << "Unregistered return type:" << mm.normalizedType.constData();
102 return QString();
103 }
104 }
107 QString errorMsg;
108 int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
109 if (inputCount == -1) {
110 qWarning() << qPrintable(errorMsg);
111 return QString(); // invalid form
112 }
113 if (isSignal && inputCount + 1 != types.size())
114 return QString(); // signal with output arguments?
115 if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message())
116 return QString(); // signal with QDBusMessage argument?
117
118 bool isScriptable = mm.isScriptable;
119 for (qsizetype j = 1; j < types.size(); ++j) {
120 // input parameter for a slot or output for a signal
121 if (types.at(j) == QDBusMetaTypeId::message()) {
122 isScriptable = true;
123 continue;
124 }
125
127 if (!names.at(j - 1).name.isEmpty())
128 name = QString::fromLatin1("name=\"%1\" ").arg(QString::fromLatin1(names.at(j - 1).name));
129
130 bool isOutput = isSignal || j > inputCount;
131
132 const char *signature = QDBusMetaType::typeToSignature(QMetaType(types.at(j)));
133 xml += QString::fromLatin1(" <arg %1type=\"%2\" direction=\"%3\"/>\n")
134 .arg(name,
135 QLatin1StringView(signature),
136 isOutput ? "out"_L1 : "in"_L1);
137
138 // do we need to describe this argument?
140 const char *typeName = QMetaType(types.at(j)).name();
141 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
142 .arg(isOutput ? "Out"_L1 : "In"_L1)
143 .arg(isOutput && !isSignal ? j - inputCount : j - 1)
145 }
146 }
147
148 int wantedMask;
149 if (isScriptable)
150 wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
152 else
153 wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
155 if ((flags & wantedMask) != wantedMask)
156 return QString();
157
159 // add the no-reply annotation
160 xml += " <annotation name=\"" ANNOTATION_NO_WAIT "\" value=\"true\"/>\n"_L1;
161
162 QString retval = xml;
163 retval += QString::fromLatin1(" </%1>\n").arg(isSignal ? "signal"_L1 : "method"_L1);
164
165 return retval;
166}
167
168
170{
171 QString retval;
172
173 // start with properties:
176 static const char *accessvalues[] = {nullptr, "read", "write", "readwrite"};
177 for (const PropertyDef &mp : mo->propertyList) {
178 if (!((!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportScriptableProperties)) ||
179 (!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportNonScriptableProperties))))
180 continue;
181
182 int access = 0;
183 if (!mp.read.isEmpty())
184 access |= 1;
185 if (!mp.write.isEmpty())
186 access |= 2;
187
188 int typeId = QMetaType::fromName(mp.type).id();
189 if (!typeId) {
190 fprintf(stderr, PROGRAMNAME ": unregistered type: '%s', ignoring\n",
191 mp.type.constData());
192 continue;
193 }
194 const char *signature = QDBusMetaType::typeToSignature(QMetaType(typeId));
195 if (!signature)
196 continue;
197
198 retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
199 .arg(QLatin1StringView(mp.name),
200 QLatin1StringView(signature),
201 QLatin1StringView(accessvalues[access]));
202
203 if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
204 retval += QString::fromLatin1(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
205 .arg(typeNameToXml(mp.type.constData()));
206 } else {
207 retval += "/>\n"_L1;
208 }
209 }
210 }
211
212 // now add methods:
213
215 for (const FunctionDef &mm : mo->signalList) {
216 if (mm.wasCloned)
217 continue;
218 if (!mm.isScriptable && !(flags & QDBusConnection::ExportNonScriptableSignals))
219 continue;
220
221 retval += addFunction(mm, true);
222 }
223 }
224
226 for (const FunctionDef &slot : mo->slotList) {
227 if (!slot.isScriptable && !(flags & QDBusConnection::ExportNonScriptableSlots))
228 continue;
229 if (slot.access == FunctionDef::Public)
230 retval += addFunction(slot);
231 }
232 for (const FunctionDef &method : mo->methodList) {
234 continue;
235 if (method.access == FunctionDef::Public)
236 retval += addFunction(method);
237 }
238 }
239 return retval;
240}
241
243{
245
246 for (const ClassInfoDef &cid : mo->classInfoList) {
247 if (cid.name == QCLASSINFO_DBUS_INTERFACE)
248 return QString::fromUtf8(cid.value);
249 }
250 interface = QLatin1StringView(mo->classname);
251 interface.replace("::"_L1, "."_L1);
252
253 if (interface.startsWith("QDBus"_L1)) {
254 interface.prepend("org.qtproject.QtDBus."_L1);
255 } else if (interface.startsWith(u'Q') &&
256 interface.size() >= 2 && interface.at(1).isUpper()) {
257 // assume it's Qt
258 interface.prepend("local.org.qtproject.Qt."_L1);
259 } else {
260 interface.prepend("local."_L1);
261 }
262
263 return interface;
264}
265
266
268{
269 for (const ClassInfoDef &cid : cdef->classInfoList) {
270 if (cid.name == QCLASSINFO_DBUS_INTROSPECTION)
271 return QString::fromUtf8(cid.value);
272 }
273
274 // generate the interface name from the meta object
275 QString interface = qDBusInterfaceFromClassDef(cdef);
276
278
279 if (xml.isEmpty())
280 return QString(); // don't add an empty interface
281 return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n")
282 .arg(interface, xml);
283}
284
285static void showHelp()
286{
287 printf("%s", help);
288 exit(0);
289}
290
291static void showVersion()
292{
293 printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
294 printf("D-Bus QObject-to-XML converter\n");
295 exit(0);
296}
297
298class CustomType {
299public:
300 CustomType(const QByteArray &typeName)
302 {
303 metaTypeImpl.name = typeName.constData();
304 }
305 QMetaType metaType() const { return QMetaType(&metaTypeImpl); }
306
307private:
308 // not copiable and not movable because of QBasicAtomicInt
309 QtPrivate::QMetaTypeInterface metaTypeImpl =
310 { /*.revision=*/ 0,
311 /*.alignment=*/ 0,
312 /*.size=*/ 0,
313 /*.flags=*/ 0,
314 /*.typeId=*/ 0,
315 /*.metaObjectFn=*/ 0,
316 /*.name=*/ nullptr, // set by the constructor
317 /*.defaultCtr=*/ nullptr,
318 /*.copyCtr=*/ nullptr,
319 /*.moveCtr=*/ nullptr,
320 /*.dtor=*/ nullptr,
321 /*.equals=*/ nullptr,
322 /*.lessThan=*/ nullptr,
323 /*.debugStream=*/ nullptr,
324 /*.dataStreamOut=*/ nullptr,
325 /*.dataStreamIn=*/ nullptr,
326 /*.legacyRegisterOp=*/ nullptr
327 };
328 QByteArray typeName;
329};
330// Unlike std::vector, std::deque works with non-copiable non-movable types
331static std::deque<CustomType> s_customTypes;
332
334{
335 flags = 0;
336 for (qsizetype i = 0; i < arguments.size(); ++i) {
337 const QString arg = arguments.at(i);
338
339 if (arg == "--help"_L1)
340 showHelp();
341
342 if (!arg.startsWith(u'-'))
343 continue;
344
345 char c = arg.size() == 2 ? arg.at(1).toLatin1() : char(0);
346 switch (c) {
347 case 'P':
350 case 'p':
352 break;
353
354 case 'S':
357 case 's':
359 break;
360
361 case 'M':
364 case 'm':
366 break;
367
368 case 'A':
371 case 'a':
373 break;
374
375 case 't':
376 if (arguments.size() < i + 2) {
377 printf("-t expects a type=dbustype argument\n");
378 exit(1);
379 } else {
380 const QByteArray arg = arguments.takeAt(i + 1).toUtf8();
381 // lastIndexOf because the C++ type could contain '=' while the DBus type can't
382 const qsizetype separator = arg.lastIndexOf('=');
383 if (separator == -1) {
384 printf("-t expects a type=dbustype argument, but no '=' was found\n");
385 exit(1);
386 }
387 const QByteArray type = arg.left(separator);
388 const QByteArray dbustype = arg.mid(separator+1);
389
390 s_customTypes.emplace_back(type);
391 QMetaType metaType = s_customTypes.back().metaType();
392 QDBusMetaType::registerCustomType(metaType, dbustype);
393 }
394 break;
395
396 case 'o':
397 if (arguments.size() < i + 2 || arguments.at(i + 1).startsWith(u'-')) {
398 printf("-o expects a filename\n");
399 exit(1);
400 }
402 break;
403
404 case 'h':
405 case '?':
406 showHelp();
407 break;
408
409 case 'V':
410 showVersion();
411 break;
412
413 default:
414 printf("unknown option: \"%s\"\n", qPrintable(arg));
415 exit(1);
416 }
417 }
418
419 if (flags == 0)
422}
423
424int main(int argc, char **argv)
425{
427 args.reserve(argc - 1);
428 for (int n = 1; n < argc; ++n)
431
432 QList<ClassDef> classes;
433
434 for (const auto &arg: std::as_const(args)) {
435 if (arg.startsWith(u'-'))
436 continue;
437
438 QFile f(arg);
440 fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
441 qPrintable(arg), qPrintable(f.errorString()));
442 return 1;
443 }
444
445 Preprocessor pp;
446 Moc moc;
447 pp.macros["Q_MOC_RUN"];
448 pp.macros["__cplusplus"];
449
450 const QByteArray filename = arg.toLocal8Bit();
451
452 moc.filename = filename;
453 moc.currentFilenames.push(filename);
454
455 moc.symbols = pp.preprocessed(moc.filename, &f);
456 moc.parse();
457
458 if (moc.classList.isEmpty())
459 return 0;
460 classes = moc.classList;
461
462 f.close();
463 }
464
466 if (outputFile.isEmpty()) {
467 output.open(stdout, QIODevice::WriteOnly);
468 } else {
469 output.setFileName(outputFile);
470 if (!output.open(QIODevice::WriteOnly)) {
471 fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
472 qPrintable(outputFile), qPrintable(output.errorString()));
473 return 1;
474 }
475 }
476
477 output.write(docTypeHeader);
478 output.write("<node>\n");
479 for (const ClassDef &cdef : std::as_const(classes)) {
481 output.write(std::move(xml).toLocal8Bit());
482 }
483 output.write("</node>\n");
484
485 return 0;
486}
487
QMetaType metaType() const
CustomType(const QByteArray &typeName)
Definition moc.h:202
Symbols preprocessed(const QByteArray &filename, QFile *device)
\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:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
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
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
T takeAt(qsizetype i)
Definition qlist.h:592
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmetatype.h:320
static QMetaType fromName(QByteArrayView name)
Returns a QMetaType matching typeName.
bool isValid() const
int id(int=0) const
Definition qmetatype.h:454
constexpr const char * name() const
Definition qmetatype.h:2650
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
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
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
QString toHtmlEscaped() const
Definition qstring.cpp:9926
int main()
[0]
auto mo
[7]
QList< QVariant > arguments
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:162
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:1391
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
ptrdiff_t qsizetype
Definition qtypes.h:70
QT_BEGIN_NAMESPACE typedef uchar * output
QXmlStreamReader xml
[0]
QJSValueList args
QList< ClassInfoDef > classInfoList
Definition moc.h:151
bool isScriptable
Definition moc.h:90
QByteArray normalizedType
Definition moc.h:71
QByteArray name
Definition moc.h:73
QList< ArgumentDef > arguments
Definition moc.h:70
@ Public
Definition moc.h:76
QByteArray tag
Definition moc.h:72