1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <qbytearray.h> |
30 | #include <qcommandlineparser.h> |
31 | #include <qcoreapplication.h> |
32 | #include <qdebug.h> |
33 | #include <qfile.h> |
34 | #include <qfileinfo.h> |
35 | #include <qloggingcategory.h> |
36 | #include <qstring.h> |
37 | #include <qstringlist.h> |
38 | #include <qtextstream.h> |
39 | #include <qset.h> |
40 | |
41 | #include <qdbusmetatype.h> |
42 | #include <private/qdbusintrospection_p.h> |
43 | |
44 | #include <stdio.h> |
45 | #include <stdlib.h> |
46 | |
47 | #define PROGRAMNAME "qdbusxml2cpp" |
48 | #define PROGRAMVERSION "0.8" |
49 | #define PROGRAMCOPYRIGHT "Copyright (C) 2022 The Qt Company Ltd." |
50 | |
51 | #define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply" |
52 | |
53 | static QString globalClassName; |
54 | static QString parentClassName; |
55 | static QString proxyFile; |
56 | static QString adaptorFile; |
57 | static QString inputFile; |
58 | static bool skipNamespaces; |
59 | static bool verbose; |
60 | static bool includeMocs; |
61 | static QString commandLine; |
62 | static QStringList includes; |
63 | static QStringList wantedInterfaces; |
64 | |
65 | static const char includeList[] = |
66 | "#include <QtCore/QByteArray>\n" |
67 | "#include <QtCore/QList>\n" |
68 | "#include <QtCore/QMap>\n" |
69 | "#include <QtCore/QString>\n" |
70 | "#include <QtCore/QStringList>\n" |
71 | "#include <QtCore/QVariant>\n" ; |
72 | |
73 | static const char forwardDeclarations[] = |
74 | "QT_BEGIN_NAMESPACE\n" |
75 | "class QByteArray;\n" |
76 | "template<class T> class QList;\n" |
77 | "template<class Key, class Value> class QMap;\n" |
78 | "class QString;\n" |
79 | "class QStringList;\n" |
80 | "class QVariant;\n" |
81 | "QT_END_NAMESPACE\n" ; |
82 | |
83 | static QDBusIntrospection::Interfaces readInput() |
84 | { |
85 | QFile input(inputFile); |
86 | if (inputFile.isEmpty() || inputFile == QLatin1String("-" )) { |
87 | input.open(stdin, ioFlags: QIODevice::ReadOnly); |
88 | } else { |
89 | input.open(flags: QIODevice::ReadOnly); |
90 | } |
91 | |
92 | QByteArray data = input.readAll(); |
93 | |
94 | // check if the input is already XML |
95 | data = data.trimmed(); |
96 | if (data.startsWith(c: "<!DOCTYPE " ) || data.startsWith(c: "<?xml" ) || |
97 | data.startsWith(c: "<node" ) || data.startsWith(c: "<interface" )) |
98 | // already XML |
99 | return QDBusIntrospection::parseInterfaces(xml: QString::fromUtf8(str: data)); |
100 | |
101 | fprintf(stderr, format: "%s: Cannot process input: '%s'. Stop.\n" , |
102 | PROGRAMNAME, qPrintable(inputFile)); |
103 | exit(status: 1); |
104 | } |
105 | |
106 | static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces) |
107 | { |
108 | if (!wantedInterfaces.isEmpty()) { |
109 | QDBusIntrospection::Interfaces::Iterator it = interfaces.begin(); |
110 | while (it != interfaces.end()) |
111 | if (!wantedInterfaces.contains(str: it.key())) |
112 | it = interfaces.erase(it); |
113 | else |
114 | ++it; |
115 | } |
116 | } |
117 | |
118 | // produce a header name from the file name |
119 | static QString (const QString &name) |
120 | { |
121 | QStringList parts = name.split(sep: QLatin1Char(':')); |
122 | QString retval = parts.first(); |
123 | |
124 | if (retval.isEmpty() || retval == QLatin1String("-" )) |
125 | return retval; |
126 | |
127 | if (!retval.endsWith(s: QLatin1String(".h" )) && !retval.endsWith(s: QLatin1String(".cpp" )) && |
128 | !retval.endsWith(s: QLatin1String(".cc" ))) |
129 | retval.append(s: QLatin1String(".h" )); |
130 | |
131 | return retval; |
132 | } |
133 | |
134 | // produce a cpp name from the file name |
135 | static QString cpp(const QString &name) |
136 | { |
137 | QStringList parts = name.split(sep: QLatin1Char(':')); |
138 | QString retval = parts.last(); |
139 | |
140 | if (retval.isEmpty() || retval == QLatin1String("-" )) |
141 | return retval; |
142 | |
143 | if (!retval.endsWith(s: QLatin1String(".h" )) && !retval.endsWith(s: QLatin1String(".cpp" )) && |
144 | !retval.endsWith(s: QLatin1String(".cc" ))) |
145 | retval.append(s: QLatin1String(".cpp" )); |
146 | |
147 | return retval; |
148 | } |
149 | |
150 | // produce a moc name from the file name |
151 | static QString moc(const QString &name) |
152 | { |
153 | QString retval = header(name); |
154 | if (retval.isEmpty()) |
155 | return retval; |
156 | |
157 | retval.truncate(pos: retval.length() - 1); // drop the h in .h |
158 | retval += QLatin1String("moc" ); |
159 | return retval; |
160 | } |
161 | |
162 | static QTextStream &(QTextStream &ts, bool changesWillBeLost) |
163 | { |
164 | ts << "/*" << Qt::endl |
165 | << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << Qt::endl |
166 | << " * Command line was: " << commandLine << Qt::endl |
167 | << " *" << Qt::endl |
168 | << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << Qt::endl |
169 | << " *" << Qt::endl |
170 | << " * This is an auto-generated file." << Qt::endl; |
171 | |
172 | if (changesWillBeLost) |
173 | ts << " * Do not edit! All changes made to it will be lost." << Qt::endl; |
174 | else |
175 | ts << " * This file may have been hand-edited. Look for HAND-EDIT comments" << Qt::endl |
176 | << " * before re-generating it." << Qt::endl; |
177 | |
178 | ts << " */" << Qt::endl |
179 | << Qt::endl; |
180 | |
181 | return ts; |
182 | } |
183 | |
184 | enum ClassType { Proxy, Adaptor }; |
185 | static QString classNameForInterface(const QString &interface, ClassType classType) |
186 | { |
187 | if (!globalClassName.isEmpty()) |
188 | return globalClassName; |
189 | |
190 | const auto parts = interface.splitRef(sep: QLatin1Char('.')); |
191 | |
192 | QString retval; |
193 | if (classType == Proxy) { |
194 | for (const auto &part : parts) |
195 | retval += part[0].toUpper() + part.mid(pos: 1); |
196 | } else { |
197 | retval += parts.last()[0].toUpper() + parts.last().mid(pos: 1); |
198 | } |
199 | |
200 | if (classType == Proxy) |
201 | retval += QLatin1String("Interface" ); |
202 | else |
203 | retval += QLatin1String("Adaptor" ); |
204 | |
205 | return retval; |
206 | } |
207 | |
208 | // ### Qt6 Remove the two isSignal ifs |
209 | // They are only here because before signal arguments where previously searched as "In" so to maintain compatibility |
210 | // we first search for "Out" and if not found we search for "In" |
211 | static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection::Annotations &annotations, int paramId = -1, const char *direction = "Out" , bool isSignal = false) |
212 | { |
213 | int type = QDBusMetaType::signatureToType(signature: signature.toLatin1()); |
214 | if (type == QMetaType::UnknownType) { |
215 | QString annotationName = QString::fromLatin1(str: "org.qtproject.QtDBus.QtTypeName" ); |
216 | if (paramId >= 0) |
217 | annotationName += QString::fromLatin1(str: ".%1%2" ).arg(a: QLatin1String(direction)).arg(a: paramId); |
218 | QString qttype = annotations.value(akey: annotationName); |
219 | if (!qttype.isEmpty()) |
220 | return std::move(qttype).toLatin1(); |
221 | |
222 | QString oldAnnotationName = QString::fromLatin1(str: "com.trolltech.QtDBus.QtTypeName" ); |
223 | if (paramId >= 0) |
224 | oldAnnotationName += QString::fromLatin1(str: ".%1%2" ).arg(a: QLatin1String(direction)).arg(a: paramId); |
225 | qttype = annotations.value(akey: oldAnnotationName); |
226 | |
227 | if (qttype.isEmpty()) { |
228 | if (!isSignal || qstrcmp(str1: direction, str2: "Out" ) == 0) { |
229 | fprintf(stderr, format: "%s: Got unknown type `%s' processing '%s'\n" , |
230 | PROGRAMNAME, qPrintable(signature), qPrintable(inputFile)); |
231 | fprintf(stderr, format: "You should add <annotation name=\"%s\" value=\"<type>\"/> to the XML description\n" , |
232 | qPrintable(annotationName)); |
233 | } |
234 | |
235 | if (isSignal) |
236 | return qtTypeName(signature, annotations, paramId, direction: "In" , isSignal); |
237 | |
238 | exit(status: 1); |
239 | } |
240 | |
241 | fprintf(stderr, format: "%s: Warning: deprecated annotation '%s' found while processing '%s'; " |
242 | "suggest updating to '%s'\n" , |
243 | PROGRAMNAME, qPrintable(oldAnnotationName), qPrintable(inputFile), |
244 | qPrintable(annotationName)); |
245 | return std::move(qttype).toLatin1(); |
246 | } |
247 | |
248 | return QVariant::typeToName(typeId: QVariant::Type(type)); |
249 | } |
250 | |
251 | static QString nonConstRefArg(const QByteArray &arg) |
252 | { |
253 | return QLatin1String(arg + " &" ); |
254 | } |
255 | |
256 | static QString templateArg(const QByteArray &arg) |
257 | { |
258 | if (!arg.endsWith(c: '>')) |
259 | return QLatin1String(arg); |
260 | |
261 | return QLatin1String(arg + ' '); |
262 | } |
263 | |
264 | static QString constRefArg(const QByteArray &arg) |
265 | { |
266 | if (!arg.startsWith(c: 'Q')) |
267 | return QLatin1String(arg + ' '); |
268 | else |
269 | return QString( QLatin1String("const %1 &" ) ).arg( a: QLatin1String(arg) ); |
270 | } |
271 | |
272 | static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs, |
273 | const QDBusIntrospection::Arguments &outputArgs = |
274 | QDBusIntrospection::Arguments()) |
275 | { |
276 | QStringList retval; |
277 | const int numInputArgs = inputArgs.count(); |
278 | const int numOutputArgs = outputArgs.count(); |
279 | retval.reserve(alloc: numInputArgs + numOutputArgs); |
280 | for (int i = 0; i < numInputArgs; ++i) { |
281 | const QDBusIntrospection::Argument &arg = inputArgs.at(i); |
282 | QString name = arg.name; |
283 | if (name.isEmpty()) |
284 | name = QString( QLatin1String("in%1" ) ).arg(a: i); |
285 | else |
286 | name.replace(before: QLatin1Char('-'), after: QLatin1Char('_')); |
287 | while (retval.contains(str: name)) |
288 | name += QLatin1String("_" ); |
289 | retval << name; |
290 | } |
291 | for (int i = 0; i < numOutputArgs; ++i) { |
292 | const QDBusIntrospection::Argument &arg = outputArgs.at(i); |
293 | QString name = arg.name; |
294 | if (name.isEmpty()) |
295 | name = QString( QLatin1String("out%1" ) ).arg(a: i); |
296 | else |
297 | name.replace(before: QLatin1Char('-'), after: QLatin1Char('_')); |
298 | while (retval.contains(str: name)) |
299 | name += QLatin1String("_" ); |
300 | retval << name; |
301 | } |
302 | return retval; |
303 | } |
304 | |
305 | static void writeArgList(QTextStream &ts, const QStringList &argNames, |
306 | const QDBusIntrospection::Annotations &annotations, |
307 | const QDBusIntrospection::Arguments &inputArgs, |
308 | const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments()) |
309 | { |
310 | // input args: |
311 | bool first = true; |
312 | int argPos = 0; |
313 | for (int i = 0; i < inputArgs.count(); ++i) { |
314 | const QDBusIntrospection::Argument &arg = inputArgs.at(i); |
315 | QString type = constRefArg(arg: qtTypeName(signature: arg.type, annotations, paramId: i, direction: "In" )); |
316 | |
317 | if (!first) |
318 | ts << ", " ; |
319 | ts << type << argNames.at(i: argPos++); |
320 | first = false; |
321 | } |
322 | |
323 | argPos++; |
324 | |
325 | // output args |
326 | // yes, starting from 1 |
327 | for (int i = 1; i < outputArgs.count(); ++i) { |
328 | const QDBusIntrospection::Argument &arg = outputArgs.at(i); |
329 | |
330 | if (!first) |
331 | ts << ", " ; |
332 | ts << nonConstRefArg(arg: qtTypeName(signature: arg.type, annotations, paramId: i, direction: "Out" )) |
333 | << argNames.at(i: argPos++); |
334 | first = false; |
335 | } |
336 | } |
337 | |
338 | static void writeSignalArgList(QTextStream &ts, const QStringList &argNames, |
339 | const QDBusIntrospection::Annotations &annotations, |
340 | const QDBusIntrospection::Arguments &outputArgs) |
341 | { |
342 | bool first = true; |
343 | int argPos = 0; |
344 | for (int i = 0; i < outputArgs.count(); ++i) { |
345 | const QDBusIntrospection::Argument &arg = outputArgs.at(i); |
346 | QString type = constRefArg(arg: qtTypeName(signature: arg.type, annotations, paramId: i, direction: "Out" , isSignal: true /* isSignal */)); |
347 | |
348 | if (!first) |
349 | ts << ", " ; |
350 | ts << type << argNames.at(i: argPos++); |
351 | first = false; |
352 | } |
353 | } |
354 | |
355 | static QString propertyGetter(const QDBusIntrospection::Property &property) |
356 | { |
357 | QString getter = property.annotations.value(akey: QLatin1String("org.qtproject.QtDBus.PropertyGetter" )); |
358 | if (!getter.isEmpty()) |
359 | return getter; |
360 | |
361 | getter = property.annotations.value(akey: QLatin1String("com.trolltech.QtDBus.propertyGetter" )); |
362 | if (!getter.isEmpty()) { |
363 | fprintf(stderr, format: "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found" |
364 | " while processing '%s';" |
365 | " suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n" , |
366 | PROGRAMNAME, qPrintable(inputFile)); |
367 | return getter; |
368 | } |
369 | |
370 | getter = property.name; |
371 | getter[0] = getter[0].toLower(); |
372 | return getter; |
373 | } |
374 | |
375 | static QString propertySetter(const QDBusIntrospection::Property &property) |
376 | { |
377 | QString setter = property.annotations.value(akey: QLatin1String("org.qtproject.QtDBus.PropertySetter" )); |
378 | if (!setter.isEmpty()) |
379 | return setter; |
380 | |
381 | setter = property.annotations.value(akey: QLatin1String("com.trolltech.QtDBus.propertySetter" )); |
382 | if (!setter.isEmpty()) { |
383 | fprintf(stderr, format: "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertySetter' found" |
384 | " while processing '%s';" |
385 | " suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n" , |
386 | PROGRAMNAME, qPrintable(inputFile)); |
387 | return setter; |
388 | } |
389 | |
390 | setter = QLatin1String("set" ) + property.name; |
391 | setter[3] = setter[3].toUpper(); |
392 | return setter; |
393 | } |
394 | |
395 | static QString methodName(const QDBusIntrospection::Method &method) |
396 | { |
397 | QString name = method.annotations.value(QStringLiteral("org.qtproject.QtDBus.MethodName" )); |
398 | if (!name.isEmpty()) |
399 | return name; |
400 | |
401 | return method.name; |
402 | } |
403 | |
404 | static QString stringify(const QString &data) |
405 | { |
406 | QString retval; |
407 | int i; |
408 | for (i = 0; i < data.length(); ++i) { |
409 | retval += QLatin1Char('\"'); |
410 | for ( ; i < data.length() && data[i] != QLatin1Char('\n') && data[i] != QLatin1Char('\r'); ++i) |
411 | if (data[i] == QLatin1Char('\"')) |
412 | retval += QLatin1String("\\\"" ); |
413 | else |
414 | retval += data[i]; |
415 | if (i+1 < data.length() && data[i] == QLatin1Char('\r') && data[i+1] == QLatin1Char('\n')) |
416 | i++; |
417 | retval += QLatin1String("\\n\"\n" ); |
418 | } |
419 | return retval; |
420 | } |
421 | |
422 | static bool openFile(const QString &fileName, QFile &file) |
423 | { |
424 | if (fileName.isEmpty()) |
425 | return false; |
426 | |
427 | bool isOk = false; |
428 | if (fileName == QLatin1String("-" )) { |
429 | isOk = file.open(stdout, ioFlags: QIODevice::WriteOnly | QIODevice::Text); |
430 | } else { |
431 | file.setFileName(fileName); |
432 | isOk = file.open(flags: QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); |
433 | } |
434 | |
435 | if (!isOk) |
436 | fprintf(stderr, format: "%s: Unable to open '%s': %s\n" , |
437 | PROGRAMNAME, qPrintable(fileName), qPrintable(file.errorString())); |
438 | return isOk; |
439 | } |
440 | |
441 | static void writeProxy(const QString &filename, const QDBusIntrospection::Interfaces &interfaces) |
442 | { |
443 | // open the file |
444 | QString = header(name: filename); |
445 | QByteArray ; |
446 | QTextStream hs(&headerData); |
447 | |
448 | QString cppName = cpp(name: filename); |
449 | QByteArray cppData; |
450 | QTextStream cs(&cppData); |
451 | |
452 | // write the header: |
453 | writeHeader(ts&: hs, changesWillBeLost: true); |
454 | if (cppName != headerName) |
455 | writeHeader(ts&: cs, changesWillBeLost: false); |
456 | |
457 | // include guards: |
458 | QString includeGuard; |
459 | if (!headerName.isEmpty() && headerName != QLatin1String("-" )) { |
460 | includeGuard = headerName.toUpper().replace(before: QLatin1Char('.'), after: QLatin1Char('_')); |
461 | int pos = includeGuard.lastIndexOf(c: QLatin1Char('/')); |
462 | if (pos != -1) |
463 | includeGuard = includeGuard.mid(position: pos + 1); |
464 | } else { |
465 | includeGuard = QLatin1String("QDBUSXML2CPP_PROXY" ); |
466 | } |
467 | includeGuard = QString(QLatin1String("%1" )) |
468 | .arg(a: includeGuard); |
469 | hs << "#ifndef " << includeGuard << Qt::endl |
470 | << "#define " << includeGuard << Qt::endl |
471 | << Qt::endl; |
472 | |
473 | // include our stuff: |
474 | hs << "#include <QtCore/QObject>" << Qt::endl |
475 | << includeList |
476 | << "#include <QtDBus/QtDBus>" << Qt::endl; |
477 | |
478 | for (const QString &include : qAsConst(t&: includes)) { |
479 | hs << "#include \"" << include << "\"" << Qt::endl; |
480 | if (headerName.isEmpty()) |
481 | cs << "#include \"" << include << "\"" << Qt::endl; |
482 | } |
483 | |
484 | hs << Qt::endl; |
485 | |
486 | if (cppName != headerName) { |
487 | if (!headerName.isEmpty() && headerName != QLatin1String("-" )) |
488 | cs << "#include \"" << headerName << "\"" << Qt::endl << Qt::endl; |
489 | } |
490 | |
491 | for (const QDBusIntrospection::Interface *interface : interfaces) { |
492 | QString className = classNameForInterface(interface: interface->name, classType: Proxy); |
493 | |
494 | // comment: |
495 | hs << "/*" << Qt::endl |
496 | << " * Proxy class for interface " << interface->name << Qt::endl |
497 | << " */" << Qt::endl; |
498 | cs << "/*" << Qt::endl |
499 | << " * Implementation of interface class " << className << Qt::endl |
500 | << " */" << Qt::endl |
501 | << Qt::endl; |
502 | |
503 | // class header: |
504 | hs << "class " << className << ": public QDBusAbstractInterface" << Qt::endl |
505 | << "{" << Qt::endl |
506 | << " Q_OBJECT" << Qt::endl; |
507 | |
508 | // the interface name |
509 | hs << "public:" << Qt::endl |
510 | << " static inline const char *staticInterfaceName()" << Qt::endl |
511 | << " { return \"" << interface->name << "\"; }" << Qt::endl |
512 | << Qt::endl; |
513 | |
514 | // constructors/destructors: |
515 | hs << "public:" << Qt::endl |
516 | << " " << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);" << Qt::endl |
517 | << Qt::endl |
518 | << " ~" << className << "();" << Qt::endl |
519 | << Qt::endl; |
520 | cs << className << "::" << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)" << Qt::endl |
521 | << " : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)" << Qt::endl |
522 | << "{" << Qt::endl |
523 | << "}" << Qt::endl |
524 | << Qt::endl |
525 | << className << "::~" << className << "()" << Qt::endl |
526 | << "{" << Qt::endl |
527 | << "}" << Qt::endl |
528 | << Qt::endl; |
529 | |
530 | // properties: |
531 | for (const QDBusIntrospection::Property &property : interface->properties) { |
532 | QByteArray type = qtTypeName(signature: property.type, annotations: property.annotations); |
533 | QString getter = propertyGetter(property); |
534 | QString setter = propertySetter(property); |
535 | |
536 | hs << " Q_PROPERTY(" << type << " " << property.name; |
537 | |
538 | // getter: |
539 | if (property.access != QDBusIntrospection::Property::Write) |
540 | // it's readble |
541 | hs << " READ " << getter; |
542 | |
543 | // setter |
544 | if (property.access != QDBusIntrospection::Property::Read) |
545 | // it's writeable |
546 | hs << " WRITE " << setter; |
547 | |
548 | hs << ")" << Qt::endl; |
549 | |
550 | // getter: |
551 | if (property.access != QDBusIntrospection::Property::Write) { |
552 | hs << " inline " << type << " " << getter << "() const" << Qt::endl |
553 | << " { return qvariant_cast< " << type << " >(property(\"" |
554 | << property.name << "\")); }" << Qt::endl; |
555 | } |
556 | |
557 | // setter: |
558 | if (property.access != QDBusIntrospection::Property::Read) { |
559 | hs << " inline void " << setter << "(" << constRefArg(arg: type) << "value)" << Qt::endl |
560 | << " { setProperty(\"" << property.name |
561 | << "\", QVariant::fromValue(value)); }" << Qt::endl; |
562 | } |
563 | |
564 | hs << Qt::endl; |
565 | } |
566 | |
567 | // methods: |
568 | hs << "public Q_SLOTS: // METHODS" << Qt::endl; |
569 | for (const QDBusIntrospection::Method &method : interface->methods) { |
570 | bool isDeprecated = method.annotations.value(akey: QLatin1String("org.freedesktop.DBus.Deprecated" )) == QLatin1String("true" ); |
571 | bool isNoReply = |
572 | method.annotations.value(akey: QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true" ); |
573 | if (isNoReply && !method.outputArgs.isEmpty()) { |
574 | fprintf(stderr, format: "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n" , |
575 | PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name), |
576 | qPrintable(interface->name)); |
577 | continue; |
578 | } |
579 | |
580 | hs << " inline " |
581 | << (isDeprecated ? "Q_DECL_DEPRECATED " : "" ); |
582 | |
583 | if (isNoReply) { |
584 | hs << "Q_NOREPLY void " ; |
585 | } else { |
586 | hs << "QDBusPendingReply<" ; |
587 | for (int i = 0; i < method.outputArgs.count(); ++i) |
588 | hs << (i > 0 ? ", " : "" ) |
589 | << templateArg(arg: qtTypeName(signature: method.outputArgs.at(i).type, annotations: method.annotations, paramId: i, direction: "Out" )); |
590 | hs << "> " ; |
591 | } |
592 | |
593 | hs << methodName(method) << "(" ; |
594 | |
595 | QStringList argNames = makeArgNames(inputArgs: method.inputArgs); |
596 | writeArgList(ts&: hs, argNames, annotations: method.annotations, inputArgs: method.inputArgs); |
597 | |
598 | hs << ")" << Qt::endl |
599 | << " {" << Qt::endl |
600 | << " QList<QVariant> argumentList;" << Qt::endl; |
601 | |
602 | if (!method.inputArgs.isEmpty()) { |
603 | hs << " argumentList" ; |
604 | for (int argPos = 0; argPos < method.inputArgs.count(); ++argPos) |
605 | hs << " << QVariant::fromValue(" << argNames.at(i: argPos) << ')'; |
606 | hs << ";" << Qt::endl; |
607 | } |
608 | |
609 | if (isNoReply) |
610 | hs << " callWithArgumentList(QDBus::NoBlock, " |
611 | << "QStringLiteral(\"" << method.name << "\"), argumentList);" << Qt::endl; |
612 | else |
613 | hs << " return asyncCallWithArgumentList(QStringLiteral(\"" |
614 | << method.name << "\"), argumentList);" << Qt::endl; |
615 | |
616 | // close the function: |
617 | hs << " }" << Qt::endl; |
618 | |
619 | if (method.outputArgs.count() > 1) { |
620 | // generate the old-form QDBusReply methods with multiple incoming parameters |
621 | hs << " inline " |
622 | << (isDeprecated ? "Q_DECL_DEPRECATED " : "" ) |
623 | << "QDBusReply<" |
624 | << templateArg(arg: qtTypeName(signature: method.outputArgs.first().type, annotations: method.annotations, paramId: 0, direction: "Out" )) << "> " ; |
625 | hs << method.name << "(" ; |
626 | |
627 | QStringList argNames = makeArgNames(inputArgs: method.inputArgs, outputArgs: method.outputArgs); |
628 | writeArgList(ts&: hs, argNames, annotations: method.annotations, inputArgs: method.inputArgs, outputArgs: method.outputArgs); |
629 | |
630 | hs << ")" << Qt::endl |
631 | << " {" << Qt::endl |
632 | << " QList<QVariant> argumentList;" << Qt::endl; |
633 | |
634 | int argPos = 0; |
635 | if (!method.inputArgs.isEmpty()) { |
636 | hs << " argumentList" ; |
637 | for (argPos = 0; argPos < method.inputArgs.count(); ++argPos) |
638 | hs << " << QVariant::fromValue(" << argNames.at(i: argPos) << ')'; |
639 | hs << ";" << Qt::endl; |
640 | } |
641 | |
642 | hs << " QDBusMessage reply = callWithArgumentList(QDBus::Block, " |
643 | << "QStringLiteral(\"" << method.name << "\"), argumentList);" << Qt::endl; |
644 | |
645 | argPos++; |
646 | hs << " if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == " |
647 | << method.outputArgs.count() << ") {" << Qt::endl; |
648 | |
649 | // yes, starting from 1 |
650 | for (int i = 1; i < method.outputArgs.count(); ++i) |
651 | hs << " " << argNames.at(i: argPos++) << " = qdbus_cast<" |
652 | << templateArg(arg: qtTypeName(signature: method.outputArgs.at(i).type, annotations: method.annotations, paramId: i, direction: "Out" )) |
653 | << ">(reply.arguments().at(" << i << "));" << Qt::endl; |
654 | hs << " }" << Qt::endl |
655 | << " return reply;" << Qt::endl |
656 | << " }" << Qt::endl; |
657 | } |
658 | |
659 | hs << Qt::endl; |
660 | } |
661 | |
662 | hs << "Q_SIGNALS: // SIGNALS" << Qt::endl; |
663 | for (const QDBusIntrospection::Signal &signal : interface->signals_) { |
664 | hs << " " ; |
665 | if (signal.annotations.value(akey: QLatin1String("org.freedesktop.DBus.Deprecated" )) == |
666 | QLatin1String("true" )) |
667 | hs << "Q_DECL_DEPRECATED " ; |
668 | |
669 | hs << "void " << signal.name << "(" ; |
670 | |
671 | QStringList argNames = makeArgNames(inputArgs: signal.outputArgs); |
672 | writeSignalArgList(ts&: hs, argNames, annotations: signal.annotations, outputArgs: signal.outputArgs); |
673 | |
674 | hs << ");" << Qt::endl; // finished for header |
675 | } |
676 | |
677 | // close the class: |
678 | hs << "};" << Qt::endl |
679 | << Qt::endl; |
680 | } |
681 | |
682 | if (!skipNamespaces) { |
683 | QStringList last; |
684 | QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin(); |
685 | do |
686 | { |
687 | QStringList current; |
688 | QString name; |
689 | if (it != interfaces.constEnd()) { |
690 | current = it->constData()->name.split(sep: QLatin1Char('.')); |
691 | name = current.takeLast(); |
692 | } |
693 | |
694 | int i = 0; |
695 | while (i < current.count() && i < last.count() && current.at(i) == last.at(i)) |
696 | ++i; |
697 | |
698 | // i parts matched |
699 | // close last.arguments().count() - i namespaces: |
700 | for (int j = i; j < last.count(); ++j) |
701 | hs << QString((last.count() - j - 1 + i) * 2, QLatin1Char(' ')) << "}" << Qt::endl; |
702 | |
703 | // open current.arguments().count() - i namespaces |
704 | for (int j = i; j < current.count(); ++j) |
705 | hs << QString(j * 2, QLatin1Char(' ')) << "namespace " << current.at(i: j) << " {" << Qt::endl; |
706 | |
707 | // add this class: |
708 | if (!name.isEmpty()) { |
709 | hs << QString(current.count() * 2, QLatin1Char(' ')) |
710 | << "typedef ::" << classNameForInterface(interface: it->constData()->name, classType: Proxy) |
711 | << " " << name << ";" << Qt::endl; |
712 | } |
713 | |
714 | if (it == interfaces.constEnd()) |
715 | break; |
716 | ++it; |
717 | last = current; |
718 | } while (true); |
719 | } |
720 | |
721 | // close the include guard |
722 | hs << "#endif" << Qt::endl; |
723 | |
724 | QString mocName = moc(name: filename); |
725 | if (includeMocs && !mocName.isEmpty()) |
726 | cs << Qt::endl |
727 | << "#include \"" << mocName << "\"" << Qt::endl; |
728 | |
729 | cs.flush(); |
730 | hs.flush(); |
731 | |
732 | QFile file; |
733 | const bool = openFile(fileName: headerName, file); |
734 | if (headerOpen) |
735 | file.write(data: headerData); |
736 | |
737 | if (headerName == cppName) { |
738 | if (headerOpen) |
739 | file.write(data: cppData); |
740 | } else { |
741 | QFile cppFile; |
742 | if (openFile(fileName: cppName, file&: cppFile)) |
743 | cppFile.write(data: cppData); |
744 | } |
745 | } |
746 | |
747 | static void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces) |
748 | { |
749 | // open the file |
750 | QString = header(name: filename); |
751 | QByteArray ; |
752 | QTextStream hs(&headerData); |
753 | |
754 | QString cppName = cpp(name: filename); |
755 | QByteArray cppData; |
756 | QTextStream cs(&cppData); |
757 | |
758 | // write the headers |
759 | writeHeader(ts&: hs, changesWillBeLost: false); |
760 | if (cppName != headerName) |
761 | writeHeader(ts&: cs, changesWillBeLost: true); |
762 | |
763 | // include guards: |
764 | QString includeGuard; |
765 | if (!headerName.isEmpty() && headerName != QLatin1String("-" )) { |
766 | includeGuard = headerName.toUpper().replace(before: QLatin1Char('.'), after: QLatin1Char('_')); |
767 | int pos = includeGuard.lastIndexOf(c: QLatin1Char('/')); |
768 | if (pos != -1) |
769 | includeGuard = includeGuard.mid(position: pos + 1); |
770 | } else { |
771 | includeGuard = QLatin1String("QDBUSXML2CPP_ADAPTOR" ); |
772 | } |
773 | includeGuard = QString(QLatin1String("%1" )) |
774 | .arg(a: includeGuard); |
775 | hs << "#ifndef " << includeGuard << Qt::endl |
776 | << "#define " << includeGuard << Qt::endl |
777 | << Qt::endl; |
778 | |
779 | // include our stuff: |
780 | hs << "#include <QtCore/QObject>" << Qt::endl; |
781 | if (cppName == headerName) |
782 | hs << "#include <QtCore/QMetaObject>" << Qt::endl |
783 | << "#include <QtCore/QVariant>" << Qt::endl; |
784 | hs << "#include <QtDBus/QtDBus>" << Qt::endl; |
785 | |
786 | for (const QString &include : qAsConst(t&: includes)) { |
787 | hs << "#include \"" << include << "\"" << Qt::endl; |
788 | if (headerName.isEmpty()) |
789 | cs << "#include \"" << include << "\"" << Qt::endl; |
790 | } |
791 | |
792 | if (cppName != headerName) { |
793 | if (!headerName.isEmpty() && headerName != QLatin1String("-" )) |
794 | cs << "#include \"" << headerName << "\"" << Qt::endl; |
795 | |
796 | cs << "#include <QtCore/QMetaObject>" << Qt::endl |
797 | << includeList |
798 | << Qt::endl; |
799 | hs << forwardDeclarations; |
800 | } else { |
801 | hs << includeList; |
802 | } |
803 | |
804 | hs << Qt::endl; |
805 | |
806 | QString parent = parentClassName; |
807 | if (parentClassName.isEmpty()) |
808 | parent = QLatin1String("QObject" ); |
809 | |
810 | for (const QDBusIntrospection::Interface *interface : interfaces) { |
811 | QString className = classNameForInterface(interface: interface->name, classType: Adaptor); |
812 | |
813 | // comment: |
814 | hs << "/*" << Qt::endl |
815 | << " * Adaptor class for interface " << interface->name << Qt::endl |
816 | << " */" << Qt::endl; |
817 | cs << "/*" << Qt::endl |
818 | << " * Implementation of adaptor class " << className << Qt::endl |
819 | << " */" << Qt::endl |
820 | << Qt::endl; |
821 | |
822 | // class header: |
823 | hs << "class " << className << ": public QDBusAbstractAdaptor" << Qt::endl |
824 | << "{" << Qt::endl |
825 | << " Q_OBJECT" << Qt::endl |
826 | << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << Qt::endl |
827 | << " Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << Qt::endl |
828 | << stringify(data: interface->introspection) |
829 | << " \"\")" << Qt::endl |
830 | << "public:" << Qt::endl |
831 | << " " << className << "(" << parent << " *parent);" << Qt::endl |
832 | << " virtual ~" << className << "();" << Qt::endl |
833 | << Qt::endl; |
834 | |
835 | if (!parentClassName.isEmpty()) |
836 | hs << " inline " << parent << " *parent() const" << Qt::endl |
837 | << " { return static_cast<" << parent << " *>(QObject::parent()); }" << Qt::endl |
838 | << Qt::endl; |
839 | |
840 | // constructor/destructor |
841 | cs << className << "::" << className << "(" << parent << " *parent)" << Qt::endl |
842 | << " : QDBusAbstractAdaptor(parent)" << Qt::endl |
843 | << "{" << Qt::endl |
844 | << " // constructor" << Qt::endl |
845 | << " setAutoRelaySignals(true);" << Qt::endl |
846 | << "}" << Qt::endl |
847 | << Qt::endl |
848 | << className << "::~" << className << "()" << Qt::endl |
849 | << "{" << Qt::endl |
850 | << " // destructor" << Qt::endl |
851 | << "}" << Qt::endl |
852 | << Qt::endl; |
853 | |
854 | hs << "public: // PROPERTIES" << Qt::endl; |
855 | for (const QDBusIntrospection::Property &property : interface->properties) { |
856 | QByteArray type = qtTypeName(signature: property.type, annotations: property.annotations); |
857 | QString constRefType = constRefArg(arg: type); |
858 | QString getter = propertyGetter(property); |
859 | QString setter = propertySetter(property); |
860 | |
861 | hs << " Q_PROPERTY(" << type << " " << property.name; |
862 | if (property.access != QDBusIntrospection::Property::Write) |
863 | hs << " READ " << getter; |
864 | if (property.access != QDBusIntrospection::Property::Read) |
865 | hs << " WRITE " << setter; |
866 | hs << ")" << Qt::endl; |
867 | |
868 | // getter: |
869 | if (property.access != QDBusIntrospection::Property::Write) { |
870 | hs << " " << type << " " << getter << "() const;" << Qt::endl; |
871 | cs << type << " " |
872 | << className << "::" << getter << "() const" << Qt::endl |
873 | << "{" << Qt::endl |
874 | << " // get the value of property " << property.name << Qt::endl |
875 | << " return qvariant_cast< " << type <<" >(parent()->property(\"" << property.name << "\"));" << Qt::endl |
876 | << "}" << Qt::endl |
877 | << Qt::endl; |
878 | } |
879 | |
880 | // setter |
881 | if (property.access != QDBusIntrospection::Property::Read) { |
882 | hs << " void " << setter << "(" << constRefType << "value);" << Qt::endl; |
883 | cs << "void " << className << "::" << setter << "(" << constRefType << "value)" << Qt::endl |
884 | << "{" << Qt::endl |
885 | << " // set the value of property " << property.name << Qt::endl |
886 | << " parent()->setProperty(\"" << property.name << "\", QVariant::fromValue(value" ; |
887 | if (constRefType.contains(s: QLatin1String("QDBusVariant" ))) |
888 | cs << ".variant()" ; |
889 | cs << "));" << Qt::endl |
890 | << "}" << Qt::endl |
891 | << Qt::endl; |
892 | } |
893 | |
894 | hs << Qt::endl; |
895 | } |
896 | |
897 | hs << "public Q_SLOTS: // METHODS" << Qt::endl; |
898 | for (const QDBusIntrospection::Method &method : interface->methods) { |
899 | bool isNoReply = |
900 | method.annotations.value(akey: QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true" ); |
901 | if (isNoReply && !method.outputArgs.isEmpty()) { |
902 | fprintf(stderr, format: "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n" , |
903 | PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name), qPrintable(interface->name)); |
904 | continue; |
905 | } |
906 | |
907 | hs << " " ; |
908 | if (method.annotations.value(akey: QLatin1String("org.freedesktop.DBus.Deprecated" )) == |
909 | QLatin1String("true" )) |
910 | hs << "Q_DECL_DEPRECATED " ; |
911 | |
912 | QByteArray returnType; |
913 | if (isNoReply) { |
914 | hs << "Q_NOREPLY void " ; |
915 | cs << "void " ; |
916 | } else if (method.outputArgs.isEmpty()) { |
917 | hs << "void " ; |
918 | cs << "void " ; |
919 | } else { |
920 | returnType = qtTypeName(signature: method.outputArgs.first().type, annotations: method.annotations, paramId: 0, direction: "Out" ); |
921 | hs << returnType << " " ; |
922 | cs << returnType << " " ; |
923 | } |
924 | |
925 | QString name = methodName(method); |
926 | hs << name << "(" ; |
927 | cs << className << "::" << name << "(" ; |
928 | |
929 | QStringList argNames = makeArgNames(inputArgs: method.inputArgs, outputArgs: method.outputArgs); |
930 | writeArgList(ts&: hs, argNames, annotations: method.annotations, inputArgs: method.inputArgs, outputArgs: method.outputArgs); |
931 | writeArgList(ts&: cs, argNames, annotations: method.annotations, inputArgs: method.inputArgs, outputArgs: method.outputArgs); |
932 | |
933 | hs << ");" << Qt::endl; // finished for header |
934 | cs << ")" << Qt::endl |
935 | << "{" << Qt::endl |
936 | << " // handle method call " << interface->name << "." << methodName(method) << Qt::endl; |
937 | |
938 | // make the call |
939 | bool usingInvokeMethod = false; |
940 | if (parentClassName.isEmpty() && method.inputArgs.count() <= 10 |
941 | && method.outputArgs.count() <= 1) |
942 | usingInvokeMethod = true; |
943 | |
944 | if (usingInvokeMethod) { |
945 | // we are using QMetaObject::invokeMethod |
946 | if (!returnType.isEmpty()) |
947 | cs << " " << returnType << " " << argNames.at(i: method.inputArgs.count()) |
948 | << ";" << Qt::endl; |
949 | |
950 | static const char invoke[] = " QMetaObject::invokeMethod(parent(), \"" ; |
951 | cs << invoke << name << "\"" ; |
952 | |
953 | if (!method.outputArgs.isEmpty()) |
954 | cs << ", Q_RETURN_ARG(" |
955 | << qtTypeName(signature: method.outputArgs.at(i: 0).type, annotations: method.annotations, |
956 | paramId: 0, direction: "Out" ) |
957 | << ", " |
958 | << argNames.at(i: method.inputArgs.count()) |
959 | << ")" ; |
960 | |
961 | for (int i = 0; i < method.inputArgs.count(); ++i) |
962 | cs << ", Q_ARG(" |
963 | << qtTypeName(signature: method.inputArgs.at(i).type, annotations: method.annotations, |
964 | paramId: i, direction: "In" ) |
965 | << ", " |
966 | << argNames.at(i) |
967 | << ")" ; |
968 | |
969 | cs << ");" << Qt::endl; |
970 | |
971 | if (!returnType.isEmpty()) |
972 | cs << " return " << argNames.at(i: method.inputArgs.count()) << ";" << Qt::endl; |
973 | } else { |
974 | if (parentClassName.isEmpty()) |
975 | cs << " //" ; |
976 | else |
977 | cs << " " ; |
978 | |
979 | if (!method.outputArgs.isEmpty()) |
980 | cs << "return " ; |
981 | |
982 | if (parentClassName.isEmpty()) |
983 | cs << "static_cast<YourObjectType *>(parent())->" ; |
984 | else |
985 | cs << "parent()->" ; |
986 | cs << name << "(" ; |
987 | |
988 | int argPos = 0; |
989 | bool first = true; |
990 | for (int i = 0; i < method.inputArgs.count(); ++i) { |
991 | cs << (first ? "" : ", " ) << argNames.at(i: argPos++); |
992 | first = false; |
993 | } |
994 | ++argPos; // skip retval, if any |
995 | for (int i = 1; i < method.outputArgs.count(); ++i) { |
996 | cs << (first ? "" : ", " ) << argNames.at(i: argPos++); |
997 | first = false; |
998 | } |
999 | |
1000 | cs << ");" << Qt::endl; |
1001 | } |
1002 | cs << "}" << Qt::endl |
1003 | << Qt::endl; |
1004 | } |
1005 | |
1006 | hs << "Q_SIGNALS: // SIGNALS" << Qt::endl; |
1007 | for (const QDBusIntrospection::Signal &signal : interface->signals_) { |
1008 | hs << " " ; |
1009 | if (signal.annotations.value(akey: QLatin1String("org.freedesktop.DBus.Deprecated" )) == |
1010 | QLatin1String("true" )) |
1011 | hs << "Q_DECL_DEPRECATED " ; |
1012 | |
1013 | hs << "void " << signal.name << "(" ; |
1014 | |
1015 | QStringList argNames = makeArgNames(inputArgs: signal.outputArgs); |
1016 | writeSignalArgList(ts&: hs, argNames, annotations: signal.annotations, outputArgs: signal.outputArgs); |
1017 | |
1018 | hs << ");" << Qt::endl; // finished for header |
1019 | } |
1020 | |
1021 | // close the class: |
1022 | hs << "};" << Qt::endl |
1023 | << Qt::endl; |
1024 | } |
1025 | |
1026 | // close the include guard |
1027 | hs << "#endif" << Qt::endl; |
1028 | |
1029 | QString mocName = moc(name: filename); |
1030 | if (includeMocs && !mocName.isEmpty()) |
1031 | cs << Qt::endl |
1032 | << "#include \"" << mocName << "\"" << Qt::endl; |
1033 | |
1034 | cs.flush(); |
1035 | hs.flush(); |
1036 | |
1037 | QFile file; |
1038 | const bool = openFile(fileName: headerName, file); |
1039 | if (headerOpen) |
1040 | file.write(data: headerData); |
1041 | |
1042 | if (headerName == cppName) { |
1043 | if (headerOpen) |
1044 | file.write(data: cppData); |
1045 | } else { |
1046 | QFile cppFile; |
1047 | if (openFile(fileName: cppName, file&: cppFile)) |
1048 | cppFile.write(data: cppData); |
1049 | } |
1050 | } |
1051 | |
1052 | int main(int argc, char **argv) |
1053 | { |
1054 | QCoreApplication app(argc, argv); |
1055 | QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME)); |
1056 | QCoreApplication::setApplicationVersion(QStringLiteral(PROGRAMVERSION)); |
1057 | |
1058 | QCommandLineParser parser; |
1059 | parser.setApplicationDescription(QLatin1String( |
1060 | "Produces the C++ code to implement the interfaces defined in the input file.\n\n" |
1061 | "If the file name given to the options -a and -p does not end in .cpp or .h, the\n" |
1062 | "program will automatically append the suffixes and produce both files.\n" |
1063 | "You can also use a colon (:) to separate the header name from the source file\n" |
1064 | "name, as in '-a filename_p.h:filename.cpp'.\n\n" |
1065 | "If you pass a dash (-) as the argument to either -p or -a, the output is written\n" |
1066 | "to the standard output." )); |
1067 | |
1068 | parser.addHelpOption(); |
1069 | parser.addVersionOption(); |
1070 | parser.addPositionalArgument(QStringLiteral("xml-or-xml-file" ), QStringLiteral("XML file to use." )); |
1071 | parser.addPositionalArgument(QStringLiteral("interfaces" ), QStringLiteral("List of interfaces to use." ), |
1072 | QStringLiteral("[interfaces ...]" )); |
1073 | |
1074 | QCommandLineOption adapterCodeOption(QStringList() << QStringLiteral("a" ) << QStringLiteral("adaptor" ), |
1075 | QStringLiteral("Write the adaptor code to <filename>" ), QStringLiteral("filename" )); |
1076 | parser.addOption(commandLineOption: adapterCodeOption); |
1077 | |
1078 | QCommandLineOption classNameOption(QStringList() << QStringLiteral("c" ) << QStringLiteral("classname" ), |
1079 | QStringLiteral("Use <classname> as the class name for the generated classes" ), QStringLiteral("classname" )); |
1080 | parser.addOption(commandLineOption: classNameOption); |
1081 | |
1082 | QCommandLineOption addIncludeOption(QStringList() << QStringLiteral("i" ) << QStringLiteral("include" ), |
1083 | QStringLiteral("Add #include to the output" ), QStringLiteral("filename" )); |
1084 | parser.addOption(commandLineOption: addIncludeOption); |
1085 | |
1086 | QCommandLineOption adapterParentOption(QStringLiteral("l" ), |
1087 | QStringLiteral("When generating an adaptor, use <classname> as the parent class" ), QStringLiteral("classname" )); |
1088 | parser.addOption(commandLineOption: adapterParentOption); |
1089 | |
1090 | QCommandLineOption mocIncludeOption(QStringList() << QStringLiteral("m" ) << QStringLiteral("moc" ), |
1091 | QStringLiteral("Generate #include \"filename.moc\" statements in the .cpp files" )); |
1092 | parser.addOption(commandLineOption: mocIncludeOption); |
1093 | |
1094 | QCommandLineOption noNamespaceOption(QStringList() << QStringLiteral("N" ) << QStringLiteral("no-namespaces" ), |
1095 | QStringLiteral("Don't use namespaces" )); |
1096 | parser.addOption(commandLineOption: noNamespaceOption); |
1097 | |
1098 | QCommandLineOption proxyCodeOption(QStringList() << QStringLiteral("p" ) << QStringLiteral("proxy" ), |
1099 | QStringLiteral("Write the proxy code to <filename>" ), QStringLiteral("filename" )); |
1100 | parser.addOption(commandLineOption: proxyCodeOption); |
1101 | |
1102 | QCommandLineOption verboseOption(QStringList() << QStringLiteral("V" ) << QStringLiteral("verbose" ), |
1103 | QStringLiteral("Be verbose." )); |
1104 | parser.addOption(commandLineOption: verboseOption); |
1105 | |
1106 | parser.process(app); |
1107 | |
1108 | adaptorFile = parser.value(option: adapterCodeOption); |
1109 | globalClassName = parser.value(option: classNameOption); |
1110 | includes = parser.values(option: addIncludeOption); |
1111 | parentClassName = parser.value(option: adapterParentOption); |
1112 | includeMocs = parser.isSet(option: mocIncludeOption); |
1113 | skipNamespaces = parser.isSet(option: noNamespaceOption); |
1114 | proxyFile = parser.value(option: proxyCodeOption); |
1115 | verbose = parser.isSet(option: verboseOption); |
1116 | |
1117 | wantedInterfaces = parser.positionalArguments(); |
1118 | if (!wantedInterfaces.isEmpty()) { |
1119 | inputFile = wantedInterfaces.takeFirst(); |
1120 | |
1121 | QFileInfo inputInfo(inputFile); |
1122 | if (!inputInfo.exists() || !inputInfo.isFile() || !inputInfo.isReadable()) { |
1123 | qCritical(msg: "Error: Input %s is not a file or cannot be accessed\n" , qPrintable(inputFile)); |
1124 | return 1; |
1125 | } |
1126 | } |
1127 | |
1128 | if (verbose) |
1129 | QLoggingCategory::setFilterRules(QStringLiteral("dbus.parser.debug=true" )); |
1130 | |
1131 | QDBusIntrospection::Interfaces interfaces = readInput(); |
1132 | cleanInterfaces(interfaces); |
1133 | |
1134 | QStringList args = app.arguments(); |
1135 | args.removeFirst(); |
1136 | commandLine = QLatin1String(PROGRAMNAME " " ); |
1137 | commandLine += args.join(sep: QLatin1Char(' ')); |
1138 | |
1139 | if (!proxyFile.isEmpty() || adaptorFile.isEmpty()) |
1140 | writeProxy(filename: proxyFile, interfaces); |
1141 | |
1142 | if (!adaptorFile.isEmpty()) |
1143 | writeAdaptor(filename: adaptorFile, interfaces); |
1144 | |
1145 | return 0; |
1146 | } |
1147 | |
1148 | |