1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #include <QVarLengthArray>
56 #include "qdbusconnection.h" // for the Export* flags
57 #include "qdbusconnection_p.h" // for the qDBusCheckAsyncTag
59 // copied from dbus-protocol.h:
60 static const char docTypeHeader[] =
61 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
62 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
64 #define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
65 #define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface"
66 #define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection"
68 #include "qdbusmetatype_p.h"
69 #include "qdbusmetatype.h"
70 #include "qdbusutil_p.h"
73 #include "generator.h"
74 #include "preprocessor.h"
76 #define PROGRAMNAME "qdbuscpp2xml"
77 #define PROGRAMVERSION "0.2"
78 #define PROGRAMCOPYRIGHT "Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)."
80 static QString outputFile;
83 static const char help[] =
84 "Usage: " PROGRAMNAME " [options...] [files...]\n"
85 "Parses the C++ source or header file containing a QObject-derived class and\n"
86 "produces the D-Bus Introspection XML."
89 " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n"
90 " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n"
91 " -a Output all scriptable contents (equivalent to -psm)\n"
92 " -A Output all contents (equivalent to -PSM)\n"
93 " -o <filename> Write the output to file <filename>\n"
94 " -h Show this information\n"
95 " -V Show the program version and quit.\n"
99 int qDBusParametersForMethod(const FunctionDef &mm, QVector<int>& metaTypes)
101 QList<QByteArray> parameterTypes;
103 foreach (const ArgumentDef &arg, mm.arguments)
104 parameterTypes.append(arg.normalizedType);
106 return qDBusParametersForMethod(parameterTypes, metaTypes);
110 static inline QString typeNameToXml(const char *typeName)
112 QString plain = QLatin1String(typeName);
113 return plain.toHtmlEscaped();
116 static QString addFunction(const FunctionDef &mm, bool isSignal = false) {
118 QString xml = QString::fromLatin1(" <%1 name=\"%2\">\n")
119 .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
120 .arg(QLatin1String(mm.name));
122 // check the return type first
123 int typeId = QMetaType::type(mm.normalizedType.constData());
124 if (typeId != QMetaType::Void) {
126 const char *typeName = QDBusMetaType::typeToSignature(typeId);
128 xml += QString::fromLatin1(" <arg type=\"%1\" direction=\"out\"/>\n")
129 .arg(typeNameToXml(typeName));
131 // do we need to describe this argument?
132 if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
133 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
134 .arg(typeNameToXml(mm.normalizedType.constData()));
138 } else if (!mm.normalizedType.isEmpty()) {
139 return QString(); // wasn't a valid type
142 QList<ArgumentDef> names = mm.arguments;
144 int inputCount = qDBusParametersForMethod(mm, types);
145 if (inputCount == -1)
146 return QString(); // invalid form
147 if (isSignal && inputCount + 1 != types.count())
148 return QString(); // signal with output arguments?
149 if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
150 return QString(); // signal with QDBusMessage argument?
152 bool isScriptable = mm.isScriptable;
153 for (int j = 1; j < types.count(); ++j) {
154 // input parameter for a slot or output for a signal
155 if (types.at(j) == QDBusMetaTypeId::message) {
161 if (!names.at(j - 1).name.isEmpty())
162 name = QString::fromLatin1("name=\"%1\" ").arg(QString::fromLatin1(names.at(j - 1).name));
164 bool isOutput = isSignal || j > inputCount;
166 const char *signature = QDBusMetaType::typeToSignature(types.at(j));
167 xml += QString::fromLatin1(" <arg %1type=\"%2\" direction=\"%3\"/>\n")
169 .arg(QLatin1String(signature))
170 .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
172 // do we need to describe this argument?
173 if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
174 const char *typeName = QMetaType::typeName(types.at(j));
175 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
176 .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
177 .arg(isOutput && !isSignal ? j - inputCount : j - 1)
178 .arg(typeNameToXml(typeName));
184 wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
185 : QDBusConnection::ExportScriptableSlots;
187 wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
188 : QDBusConnection::ExportNonScriptableSlots;
189 if ((flags & wantedMask) != wantedMask)
192 if (qDBusCheckAsyncTag(mm.tag.constData()))
193 // add the no-reply annotation
194 xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\""
195 " value=\"true\"/>\n");
197 QString retval = xml;
198 retval += QString::fromLatin1(" </%1>\n")
199 .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
205 static QString generateInterfaceXml(const ClassDef *mo)
209 // start with properties:
210 if (flags & (QDBusConnection::ExportScriptableProperties |
211 QDBusConnection::ExportNonScriptableProperties)) {
212 static const char *accessvalues[] = {0, "read", "write", "readwrite"};
213 foreach (const PropertyDef &mp, mo->propertyList) {
214 if (!((!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportScriptableProperties)) ||
215 (!mp.scriptable.isEmpty() && (flags & QDBusConnection::ExportNonScriptableProperties))))
219 if (!mp.read.isEmpty())
221 if (!mp.write.isEmpty())
224 int typeId = QMetaType::type(mp.type.constData());
227 const char *signature = QDBusMetaType::typeToSignature(typeId);
231 retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
232 .arg(QLatin1String(mp.name))
233 .arg(QLatin1String(signature))
234 .arg(QLatin1String(accessvalues[access]));
236 if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
237 retval += QString::fromLatin1(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
238 .arg(typeNameToXml(mp.type.constData()));
240 retval += QLatin1String("/>\n");
247 if (flags & (QDBusConnection::ExportScriptableSignals | QDBusConnection::ExportNonScriptableSignals)) {
248 foreach (const FunctionDef &mm, mo->signalList) {
252 retval += addFunction(mm, true);
256 if (flags & (QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportNonScriptableSlots)) {
257 foreach (const FunctionDef &slot, mo->slotList) {
258 if (slot.access == FunctionDef::Public)
259 retval += addFunction(slot);
261 foreach (const FunctionDef &method, mo->methodList) {
262 if (method.access == FunctionDef::Public)
263 retval += addFunction(method);
269 QString qDBusInterfaceFromClassDef(const ClassDef *mo)
273 foreach (const ClassInfoDef &cid, mo->classInfoList) {
274 if (cid.name == QCLASSINFO_DBUS_INTERFACE)
275 return QString::fromUtf8(cid.value);
277 interface = QLatin1String(mo->classname);
278 interface.replace(QLatin1String("::"), QLatin1String("."));
280 if (interface.startsWith(QLatin1String("QDBus"))) {
281 interface.prepend(QLatin1String("org.qtproject.QtDBus."));
282 } else if (interface.startsWith(QLatin1Char('Q')) &&
283 interface.length() >= 2 && interface.at(1).isUpper()) {
285 interface.prepend(QLatin1String("local.org.qtproject.Qt."));
287 interface.prepend(QLatin1String("local."));
294 QString qDBusGenerateClassDefXml(const ClassDef *cdef)
296 foreach (const ClassInfoDef &cid, cdef->classInfoList) {
297 if (cid.name == QCLASSINFO_DBUS_INTROSPECTION)
298 return QString::fromUtf8(cid.value);
301 // generate the interface name from the meta object
302 QString interface = qDBusInterfaceFromClassDef(cdef);
304 QString xml = generateInterfaceXml(cdef);
307 return QString(); // don't add an empty interface
308 return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n")
309 .arg(interface, xml);
312 static void showHelp()
318 static void showVersion()
320 printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
321 printf("D-Bus QObject-to-XML converter\n");
325 static void parseCmdLine(QStringList &arguments)
328 for (int i = 0; i < arguments.count(); ++i) {
329 const QString arg = arguments.at(i);
331 if (arg == QLatin1String("--help"))
334 if (!arg.startsWith(QLatin1Char('-')))
337 char c = arg.count() == 2 ? arg.at(1).toLatin1() : char(0);
340 flags |= QDBusConnection::ExportNonScriptableProperties;
343 flags |= QDBusConnection::ExportScriptableProperties;
347 flags |= QDBusConnection::ExportNonScriptableSignals;
350 flags |= QDBusConnection::ExportScriptableSignals;
354 flags |= QDBusConnection::ExportNonScriptableSlots;
357 flags |= QDBusConnection::ExportScriptableSlots;
361 flags |= QDBusConnection::ExportNonScriptableContents;
364 flags |= QDBusConnection::ExportScriptableContents;
368 if (arguments.count() < i + 2 || arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
369 printf("-o expects a filename\n");
372 outputFile = arguments.takeAt(i + 1);
385 printf("unknown option: \"%s\"\n", qPrintable(arg));
391 flags = QDBusConnection::ExportScriptableContents
392 | QDBusConnection::ExportNonScriptableContents;
395 int main(int argc, char **argv)
398 for (int n = 1; n < argc; ++n)
399 args.append(QString::fromLocal8Bit(argv[n]));
402 QList<ClassDef> classes;
404 for (int i = 0; i < args.count(); ++i) {
405 const QString arg = args.at(i);
407 if (arg.startsWith(QLatin1Char('-')))
411 if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
412 fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
413 qPrintable(arg), qPrintable(f.errorString()));
419 pp.macros["Q_MOC_RUN"];
420 pp.macros["__cplusplus"];
422 const QByteArray filename = QFile::decodeName(argv[i]).toLatin1();
424 moc.filename = filename;
425 moc.currentFilenames.push(filename);
427 moc.symbols = pp.preprocessed(moc.filename, &f);
430 if (moc.classList.isEmpty())
432 classes = moc.classList;
438 if (outputFile.isEmpty()) {
439 output.open(stdout, QIODevice::WriteOnly);
441 output.setFileName(outputFile);
442 if (!output.open(QIODevice::WriteOnly)) {
443 fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
444 qPrintable(outputFile), qPrintable(output.errorString()));
449 output.write(docTypeHeader);
450 output.write("<node>\n");
451 foreach (const ClassDef &cdef, classes) {
452 QString xml = qDBusGenerateClassDefXml(&cdef);
453 output.write(xml.toLocal8Bit());
455 output.write("</node>\n");