5#include <qcommandlineparser.h>
6#include <qcoreapplication.h>
10#include <qloggingcategory.h>
12#include <qstringlist.h>
13#include <qtextstream.h>
16#include <qdbusmetatype.h>
17#include <private/qdbusintrospection_p.h>
22#define PROGRAMNAME "qdbusxml2cpp"
23#define PROGRAMVERSION "0.8"
24#define PROGRAMCOPYRIGHT QT_COPYRIGHT
26#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
28using namespace Qt::StringLiterals;
30class QDBusXmlToCpp
final
33 int run(
const QCoreApplication &app);
36 class DiagnosticsReporter
final :
public QDBusIntrospection::DiagnosticsReporter
39 void setFileName(
const QString &fileName) { m_fileName = fileName; }
40 bool hadErrors()
const {
return m_hadErrors; }
42 void warning(
const QDBusIntrospection::SourceLocation &location,
const char *msg,
44 void error(
const QDBusIntrospection::SourceLocation &location,
const char *msg,
46 void note(
const QDBusIntrospection::SourceLocation &location,
const char *msg, ...)
47 Q_ATTRIBUTE_FORMAT_PRINTF(3, 4);
51 bool m_hadErrors =
false;
53 void report(
const QDBusIntrospection::SourceLocation &location,
const char *msg, va_list ap,
54 const char *severity);
57 enum ClassType { Proxy, Adaptor };
59 void writeAdaptor(
const QString &filename,
const QDBusIntrospection::Interfaces &interfaces);
60 void writeProxy(
const QString &filename,
const QDBusIntrospection::Interfaces &interfaces);
62 QDBusIntrospection::Interfaces readInput();
63 void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces);
64 QTextStream &writeHeader(QTextStream &ts,
bool changesWillBeLost);
65 QString classNameForInterface(
const QString &interface, ClassType classType);
66 QByteArray qtTypeName(
const QDBusIntrospection::SourceLocation &location,
67 const QString &signature,
68 const QDBusIntrospection::Annotations &annotations,
69 qsizetype paramId = -1,
const char *direction =
"Out");
71 writeArgList(QTextStream &ts,
const QStringList &argNames,
72 const QDBusIntrospection::Annotations &annotations,
73 const QDBusIntrospection::Arguments &inputArgs,
74 const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments());
75 void writeSignalArgList(QTextStream &ts,
const QStringList &argNames,
76 const QDBusIntrospection::Annotations &annotations,
77 const QDBusIntrospection::Arguments &outputArgs);
78 QString propertyGetter(
const QDBusIntrospection::Property &property);
79 QString propertySetter(
const QDBusIntrospection::Property &property);
81 QString globalClassName;
82 QString parentClassName;
83 QString customNamespace;
85 bool skipNamespaces =
false;
86 bool includeMocs =
false;
89 QStringList globalIncludes;
90 QStringList wantedInterfaces;
92 DiagnosticsReporter reporter;
96 "#include <QtCore/QByteArray>\n"
97 "#include <QtCore/QList>\n"
98 "#include <QtCore/QMap>\n"
99 "#include <QtCore/QString>\n"
100 "#include <QtCore/QStringList>\n"
101 "#include <QtCore/QVariant>\n";
104 "#include <QtCore/qcontainerfwd.h>\n";
106void QDBusXmlToCpp::DiagnosticsReporter::warning(
const QDBusIntrospection::SourceLocation &location,
107 const char *msg, ...)
111 report(location, msg, ap,
"warning");
115void QDBusXmlToCpp::DiagnosticsReporter::error(
const QDBusIntrospection::SourceLocation &location,
116 const char *msg, ...)
120 report(location, msg, ap,
"error");
125void QDBusXmlToCpp::DiagnosticsReporter::note(
const QDBusIntrospection::SourceLocation &location,
126 const char *msg, ...)
130 report(location, msg, ap,
"note");
135void QDBusXmlToCpp::DiagnosticsReporter::report(
const QDBusIntrospection::SourceLocation &location,
136 const char *msg, va_list ap,
const char *severity)
138 fprintf(stderr,
"%s:%lld:%lld: %s: ",
qPrintable(m_fileName),
139 (
long long int)location.lineNumber, (
long long int)location.columnNumber + 1, severity);
140 vfprintf(stderr, msg, ap);
143QDBusIntrospection::Interfaces QDBusXmlToCpp::readInput()
145 QFile input(inputFile);
146 if (inputFile.isEmpty() || inputFile ==
"-"_L1) {
147 reporter.setFileName(
"<standard input>"_L1);
148 if (!input.open(stdin, QIODevice::ReadOnly)) {
149 fprintf(stderr,
PROGRAMNAME ": could not open standard input: %s\n",
154 reporter.setFileName(inputFile);
155 if (!input.open(QIODevice::ReadOnly)) {
156 fprintf(stderr,
PROGRAMNAME ": could not open input file '%s': %s\n",
163 auto interfaces = QDBusIntrospection::parseInterfaces(QString::fromUtf8(data), &reporter);
164 if (reporter.hadErrors())
170void QDBusXmlToCpp::cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
172 if (!wantedInterfaces.isEmpty()) {
173 QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();
174 while (it != interfaces.end())
175 if (!wantedInterfaces.contains(it.key()))
176 it = interfaces.erase(it);
184 const QLatin1StringView candidates[] = {
190 for (
auto candidate : candidates)
191 if (suffix == candidate)
198static QString
header(
const QString &name)
200 QStringList parts = name.split(u':');
201 QString retval = parts.front();
203 if (retval.isEmpty() || retval ==
"-"_L1)
207 if (!isSupportedSuffix(header.suffix()))
208 retval.append(
".h"_L1);
214static QString
cpp(
const QString &name)
216 QStringList parts = name.split(u':');
217 QString retval = parts.back();
219 if (retval.isEmpty() || retval ==
"-"_L1)
223 if (!isSupportedSuffix(source.suffix()))
224 retval.append(
".cpp"_L1);
230static QString
moc(
const QString &name)
233 const QStringList fileNames = name.split(u':');
235 if (fileNames.size() == 1) {
237 if (isSupportedSuffix(fi.suffix())) {
239 retval += fi.completeBaseName();
244 retval += fi.fileName();
248 QString headerName = fileNames.front();
249 QString sourceName = fileNames.back();
251 if (sourceName.isEmpty() || sourceName ==
"-"_L1) {
253 }
else if (headerName.isEmpty() || headerName ==
"-"_L1) {
258 retval += source.completeBaseName();
261 fprintf(stderr,
"warning: no header name is provided, assuming it to be \"%s\"\n",
262 qPrintable(source.completeBaseName() +
".h"_L1));
268 retval += header.completeBaseName();
276QTextStream &QDBusXmlToCpp::writeHeader(QTextStream &ts,
bool changesWillBeLost)
280 " * Source file was " << QFileInfo(inputFile).fileName() <<
"\n"
284 " * This is an auto-generated file.\n";
286 if (changesWillBeLost)
287 ts <<
" * Do not edit! All changes made to it will be lost.\n";
289 ts <<
" * This file may have been hand-edited. Look for HAND-EDIT comments\n"
290 " * before re-generating it.\n";
297QString QDBusXmlToCpp::classNameForInterface(
const QString &interface,
298 QDBusXmlToCpp::ClassType classType)
300 if (!globalClassName.isEmpty())
301 return globalClassName;
303 const auto parts = QStringView{interface}.split(u'.');
306 if (classType == Proxy) {
307 for (
const auto &part : parts) {
308 retval += part[0].toUpper();
309 retval += part.mid(1);
312 retval += parts.last()[0].toUpper() + parts.last().mid(1);
315 if (classType == Proxy)
316 retval +=
"Interface"_L1;
318 retval +=
"Adaptor"_L1;
323QByteArray QDBusXmlToCpp::qtTypeName(
const QDBusIntrospection::SourceLocation &location,
324 const QString &signature,
325 const QDBusIntrospection::Annotations &annotations,
326 qsizetype paramId,
const char *direction)
328 int type = QDBusMetaType::signatureToMetaType(signature.toLatin1()).id();
329 if (type == QMetaType::UnknownType) {
330 QString annotationName = u"org.qtproject.QtDBus.QtTypeName"_s;
332 annotationName +=
".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
333 auto annotation = annotations.value(annotationName);
334 QString qttype = annotation.value;
335 if (!qttype.isEmpty())
336 return std::move(qttype).toLatin1();
338 QString oldAnnotationName = u"com.trolltech.QtDBus.QtTypeName"_s;
340 oldAnnotationName +=
".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
341 annotation = annotations.value(oldAnnotationName);
342 qttype = annotation.value;
344 if (qttype.isEmpty()) {
345 reporter.error(location,
"unknown type `%s'\n",
qPrintable(signature));
346 reporter.note(location,
"you should add <annotation name=\"%s\" value=\"<type>\"/>\n",
352 reporter.warning(annotation.location,
"deprecated annotation '%s' found\n",
354 reporter.note(annotation.location,
"suggest updating to '%s'\n",
356 return std::move(qttype).toLatin1();
359 return QMetaType(type).name();
364 return QLatin1StringView(arg) +
" &"_L1;
369 if (!arg.endsWith(
'>'))
370 return QLatin1StringView(arg);
372 return QLatin1StringView(arg) +
" "_L1;
377 if (!arg.startsWith(
'Q'))
378 return QLatin1StringView(arg) +
" "_L1;
380 return "const %1 &"_L1.arg(QLatin1StringView(arg));
384 const QDBusIntrospection::Arguments &outputArgs =
388 const qsizetype numInputArgs = inputArgs.size();
389 const qsizetype numOutputArgs = outputArgs.size();
390 retval.reserve(numInputArgs + numOutputArgs);
391 for (qsizetype i = 0; i < numInputArgs; ++i) {
392 const QDBusIntrospection::Argument &arg = inputArgs.at(i);
393 QString name = arg.name;
395 name = u"in%1"_s.arg(i);
397 name.replace(u'-', u'_');
398 while (retval.contains(name))
402 for (qsizetype i = 0; i < numOutputArgs; ++i) {
403 const QDBusIntrospection::Argument &arg = outputArgs.at(i);
404 QString name = arg.name;
406 name = u"out%1"_s.arg(i);
408 name.replace(u'-', u'_');
409 while (retval.contains(name))
416void QDBusXmlToCpp::writeArgList(QTextStream &ts,
const QStringList &argNames,
417 const QDBusIntrospection::Annotations &annotations,
418 const QDBusIntrospection::Arguments &inputArgs,
419 const QDBusIntrospection::Arguments &outputArgs)
423 qsizetype argPos = 0;
424 for (qsizetype i = 0; i < inputArgs.size(); ++i) {
425 const QDBusIntrospection::Argument &arg = inputArgs.at(i);
426 QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i,
"In"));
430 ts << type << argNames.at(argPos++);
438 for (qsizetype i = 1; i < outputArgs.size(); ++i) {
439 const QDBusIntrospection::Argument &arg = outputArgs.at(i);
443 ts << nonConstRefArg(qtTypeName(arg.location, arg.type, annotations, i,
"Out"))
444 << argNames.at(argPos++);
449void QDBusXmlToCpp::writeSignalArgList(QTextStream &ts,
const QStringList &argNames,
450 const QDBusIntrospection::Annotations &annotations,
451 const QDBusIntrospection::Arguments &outputArgs)
454 qsizetype argPos = 0;
455 for (qsizetype i = 0; i < outputArgs.size(); ++i) {
456 const QDBusIntrospection::Argument &arg = outputArgs.at(i);
457 QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i,
"Out"));
461 ts << type << argNames.at(argPos++);
466QString QDBusXmlToCpp::propertyGetter(
const QDBusIntrospection::Property &property)
468 auto annotation = property.annotations.value(
"org.qtproject.QtDBus.PropertyGetter"_L1);
469 if (!annotation.value.isEmpty())
470 return annotation.value;
472 annotation = property.annotations.value(
"com.trolltech.QtDBus.propertyGetter"_L1);
473 if (!annotation.value.isEmpty()) {
474 reporter.warning(annotation.location,
475 "deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found\n");
476 reporter.note(annotation.location,
477 "suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n");
478 return annotation.value;
481 QString getter = property.name;
482 getter[0] = getter[0].toLower();
486QString QDBusXmlToCpp::propertySetter(
const QDBusIntrospection::Property &property)
488 auto annotation = property.annotations.value(
"org.qtproject.QtDBus.PropertySetter"_L1);
489 if (!annotation.value.isEmpty())
490 return annotation.value;
492 annotation = property.annotations.value(
"com.trolltech.QtDBus.propertySetter"_L1);
493 if (!annotation.value.isEmpty()) {
494 reporter.warning(annotation.location,
495 "deprecated annotation 'com.trolltech.QtDBus.propertySetter' found\n");
496 reporter.note(annotation.location,
497 "suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n");
498 return annotation.value;
501 QString setter =
"set"_L1 + property.name;
502 setter[3] = setter[3].toUpper();
506static QString
methodName(
const QDBusIntrospection::Method &method)
508 QString name = method.annotations.value(u"org.qtproject.QtDBus.MethodName"_s).value;
519 for (i = 0; i < data.size(); ++i) {
521 for ( ; i < data.size() && data[i] != u'\n' && data[i] != u'\r'; ++i)
522 if (data[i] == u'\"')
526 if (i+1 < data.size() && data[i] == u'\r' && data[i+1] == u'\n')
528 retval +=
"\\n\"\n"_L1;
533static bool openFile(
const QString &fileName, QFile &file)
535 if (fileName.isEmpty())
539 if (fileName ==
"-"_L1) {
540 isOk = file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
542 file.setFileName(fileName);
543 isOk = file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
547 fprintf(stderr,
"%s: Unable to open '%s': %s\n",
552void QDBusXmlToCpp::writeProxy(
const QString &filename,
553 const QDBusIntrospection::Interfaces &interfaces)
556 QString headerName = header(filename);
560 QString cppName = cpp(filename);
565 writeHeader(hs,
true);
566 if (cppName != headerName)
567 writeHeader(cs,
false);
570 QString includeGuard;
571 if (!headerName.isEmpty() && headerName !=
"-"_L1) {
572 includeGuard = headerName.toUpper().replace(u'.', u'_');
573 qsizetype pos = includeGuard.lastIndexOf(u'/');
575 includeGuard = includeGuard.mid(pos + 1);
577 includeGuard = u"QDBUSXML2CPP_PROXY"_s;
580 hs <<
"#ifndef " << includeGuard <<
"\n"
581 "#define " << includeGuard <<
"\n\n";
584 hs <<
"#include <QtCore/QObject>\n"
586#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
587 hs <<
"#include <QtDBus/QtDBus>\n";
589 hs <<
"#include <QtDBus/QDBusAbstractInterface>\n"
590 "#include <QtDBus/QDBusPendingReply>\n";
593 for (
const QString &include : std::as_const(includes)) {
594 hs <<
"#include \"" << include <<
"\"\n";
595 if (headerName.isEmpty())
596 cs <<
"#include \"" << include <<
"\"\n";
599 for (
const QString &include : std::as_const(globalIncludes)) {
600 hs <<
"#include <" << include <<
">\n";
601 if (headerName.isEmpty())
602 cs <<
"#include <" << include <<
">\n";
607 if (cppName != headerName) {
608 if (!headerName.isEmpty() && headerName !=
"-"_L1)
609 cs <<
"#include \"" << headerName <<
"\"\n\n";
612 if (!customNamespace.isEmpty()) {
613 hs <<
"namespace " << customNamespace <<
" { \n"
615 cs <<
"namespace " << customNamespace <<
" { \n"
619 for (
const QDBusIntrospection::Interface *interface : interfaces) {
620 QString className = classNameForInterface(interface->name, Proxy);
624 " * Proxy class for interface " << interface->name <<
"\n"
627 " * Implementation of interface class " << className <<
"\n"
631 hs <<
"class " << className <<
": public QDBusAbstractInterface\n"
637 " static inline const char *staticInterfaceName()\n"
638 " { return \"" << interface->name <<
"\"; }\n\n";
642 " " << className <<
"(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);\n\n"
643 " ~" << className <<
"();\n\n";
644 cs << className <<
"::" << className <<
"(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)\n"
645 " : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)\n"
648 << className <<
"::~" << className <<
"()\n"
653 for (
const QDBusIntrospection::Property &property : interface->properties) {
654 QByteArray type = qtTypeName(property.location, property.type, property.annotations);
655 QString getter = propertyGetter(property);
656 QString setter = propertySetter(property);
658 hs <<
" Q_PROPERTY(" << type <<
" " << property.name;
661 if (property.access != QDBusIntrospection::Property::Write)
663 hs <<
" READ " << getter;
666 if (property.access != QDBusIntrospection::Property::Read)
668 hs <<
" WRITE " << setter;
673 if (property.access != QDBusIntrospection::Property::Write) {
674 hs <<
" inline " << type <<
" " << getter <<
"() const\n"
675 " { return qvariant_cast< " << type <<
" >(property(\""
676 << property.name <<
"\")); }\n";
680 if (property.access != QDBusIntrospection::Property::Read) {
681 hs <<
" inline void " << setter <<
"(" << constRefArg(type) <<
"value)\n"
682 " { setProperty(\"" << property.name
683 <<
"\", QVariant::fromValue(value)); }\n";
690 hs <<
"public Q_SLOTS: // METHODS\n";
691 for (
const QDBusIntrospection::Method &method : interface->methods) {
692 bool isDeprecated = method.annotations.value(
"org.freedesktop.DBus.Deprecated"_L1).value
694 bool isNoReply = method.annotations.value(
ANNOTATION_NO_WAIT ""_L1).value ==
"true"_L1;
695 if (isNoReply && !method.outputArgs.isEmpty()) {
696 reporter.warning(method.location,
697 "method %s in interface %s is marked 'no-reply' but has output "
704 hs <<
" Q_DECL_DEPRECATED ";
709 hs <<
"Q_NOREPLY inline void ";
711 hs <<
"inline QDBusPendingReply<";
712 for (qsizetype i = 0; i < method.outputArgs.size(); ++i)
713 hs << (i > 0 ?
", " :
"")
714 << templateArg(qtTypeName(method.outputArgs.at(i).location,
715 method.outputArgs.at(i).type, method.annotations,
720 hs << methodName(method) <<
"(";
722 QStringList argNames = makeArgNames(method.inputArgs);
723 writeArgList(hs, argNames, method.annotations, method.inputArgs);
727 " QList<QVariant> argumentList;\n";
729 if (!method.inputArgs.isEmpty()) {
730 hs <<
" argumentList";
731 for (qsizetype argPos = 0; argPos < method.inputArgs.size(); ++argPos)
732 hs <<
" << QVariant::fromValue(" << argNames.at(argPos) <<
')';
737 hs <<
" callWithArgumentList(QDBus::NoBlock, "
738 "QStringLiteral(\"" << method.name <<
"\"), argumentList);\n";
740 hs <<
" return asyncCallWithArgumentList(QStringLiteral(\""
741 << method.name <<
"\"), argumentList);\n";
746 if (method.outputArgs.size() > 1) {
748 hs << (isDeprecated ?
" Q_DECL_DEPRECATED " :
" ") <<
"inline QDBusReply<"
749 << templateArg(qtTypeName(method.outputArgs.first().location,
750 method.outputArgs.first().type, method.annotations, 0,
753 hs << method.name <<
"(";
755 QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
756 writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
760 " QList<QVariant> argumentList;\n";
762 qsizetype argPos = 0;
763 if (!method.inputArgs.isEmpty()) {
764 hs <<
" argumentList";
765 for (argPos = 0; argPos < method.inputArgs.size(); ++argPos)
766 hs <<
" << QVariant::fromValue(" << argNames.at(argPos) <<
')';
770 hs <<
" QDBusMessage reply = callWithArgumentList(QDBus::Block, "
771 "QStringLiteral(\"" << method.name <<
"\"), argumentList);\n";
774 hs <<
" if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().size() == "
775 << method.outputArgs.size() <<
") {\n";
778 for (qsizetype i = 1; i < method.outputArgs.size(); ++i)
779 hs <<
" " << argNames.at(argPos++) <<
" = qdbus_cast<"
780 << templateArg(qtTypeName(method.outputArgs.at(i).location,
781 method.outputArgs.at(i).type, method.annotations,
783 <<
">(reply.arguments().at(" << i <<
"));\n";
792 hs <<
"Q_SIGNALS: // SIGNALS\n";
793 for (
const QDBusIntrospection::Signal &signal : interface->signals_) {
795 if (signal.annotations.value(
"org.freedesktop.DBus.Deprecated"_L1).value ==
"true"_L1)
796 hs <<
"Q_DECL_DEPRECATED ";
798 hs <<
"void " << signal.name <<
"(";
800 QStringList argNames = makeArgNames(signal.outputArgs);
801 writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs);
810 if (!customNamespace.isEmpty()) {
811 hs <<
"} // end of namespace " << customNamespace <<
"\n"
813 cs <<
"} // end of namespace " << customNamespace <<
"\n"
817 if (!skipNamespaces) {
819 QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin();
824 if (it != interfaces.constEnd()) {
825 current = it->constData()->name.split(u'.');
826 name = current.takeLast();
830 while (i < current.size() && i < last.size() && current.at(i) == last.at(i))
835 for (qsizetype j = i; j < last.size(); ++j)
836 hs << QString((last.size() - j - 1 + i) * 2, u' ') <<
"}\n";
839 for (qsizetype j = i; j < current.size(); ++j)
840 hs << QString(j * 2, u' ') <<
"namespace " << current.at(j) <<
" {\n";
843 if (!name.isEmpty()) {
844 hs << QString(current.size() * 2, u' ')
845 <<
"using " << name <<
" = " << (customNamespace.isEmpty() ?
"" :
"::")
846 << customNamespace <<
"::" << classNameForInterface(it->constData()->name, Proxy)
850 if (it == interfaces.constEnd())
862 QString mocName = moc(filename);
863 if (includeMocs && !mocName.isEmpty())
865 "#include \"" << mocName <<
"\"\n";
871 const bool headerOpen = openFile(headerName, file);
873 file.write(headerData);
875 if (headerName == cppName) {
880 if (openFile(cppName, cppFile))
881 cppFile.write(cppData);
885void QDBusXmlToCpp::writeAdaptor(
const QString &filename,
886 const QDBusIntrospection::Interfaces &interfaces)
889 QString headerName = header(filename);
893 QString cppName = cpp(filename);
898 writeHeader(hs,
false);
899 if (cppName != headerName)
900 writeHeader(cs,
true);
903 QString includeGuard;
904 if (!headerName.isEmpty() && headerName !=
"-"_L1) {
905 includeGuard = headerName.toUpper().replace(u'.', u'_');
906 qsizetype pos = includeGuard.lastIndexOf(u'/');
908 includeGuard = includeGuard.mid(pos + 1);
910 includeGuard = u"QDBUSXML2CPP_ADAPTOR"_s;
913 hs <<
"#ifndef " << includeGuard <<
"\n"
914 "#define " << includeGuard <<
"\n\n";
917 hs <<
"#include <QtCore/QObject>\n";
918 if (cppName == headerName)
919 hs <<
"#include <QtCore/QMetaObject>\n"
920 "#include <QtCore/QVariant>\n";
921#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
922 hs <<
"#include <QtDBus/QtDBus>\n";
924 hs <<
"#include <QtDBus/QDBusAbstractAdaptor>\n"
925 "#include <QtDBus/QDBusObjectPath>\n";
928 for (
const QString &include : std::as_const(includes)) {
929 hs <<
"#include \"" << include <<
"\"\n";
930 if (headerName.isEmpty())
931 cs <<
"#include \"" << include <<
"\"\n";
934 for (
const QString &include : std::as_const(globalIncludes)) {
935 hs <<
"#include <" << include <<
">\n";
936 if (headerName.isEmpty())
937 cs <<
"#include <" << include <<
">\n";
940 if (cppName != headerName) {
941 if (!headerName.isEmpty() && headerName !=
"-"_L1)
942 cs <<
"#include \"" << headerName <<
"\"\n";
944 cs <<
"#include <QtCore/QMetaObject>\n"
947 hs << forwardDeclarations;
954 QString parent = parentClassName;
955 if (parentClassName.isEmpty())
956 parent = u"QObject"_s;
958 if (!customNamespace.isEmpty()) {
959 hs <<
"namespace " << customNamespace <<
" { \n"
961 cs <<
"namespace " << customNamespace <<
" { \n"
965 for (
const QDBusIntrospection::Interface *interface : interfaces) {
966 QString className = classNameForInterface(interface->name, Adaptor);
970 " * Adaptor class for interface " << interface->name <<
"\n"
973 " * Implementation of adaptor class " << className <<
"\n"
977 hs <<
"class " << className <<
": public QDBusAbstractAdaptor\n"
980 " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name <<
"\")\n"
981 " Q_CLASSINFO(\"D-Bus Introspection\", \"\"\n"
982 << stringify(interface->introspection)
985 " " << className <<
"(" << parent <<
" *parent);\n"
986 " ~" << className <<
"() override;\n\n";
988 if (!parentClassName.isEmpty())
989 hs <<
" inline " << parent <<
" *parent() const\n"
990 " { return static_cast<" << parent <<
" *>(QObject::parent()); }\n\n";
993 cs << className <<
"::" << className <<
"(" << parent <<
" *parent)\n"
994 " : QDBusAbstractAdaptor(parent)\n"
997 " setAutoRelaySignals(true);\n"
999 << className <<
"::~" << className <<
"()\n"
1004 hs <<
"public: // PROPERTIES\n";
1005 for (
const QDBusIntrospection::Property &property : interface->properties) {
1006 QByteArray type = qtTypeName(property.location, property.type, property.annotations);
1007 QString constRefType = constRefArg(type);
1008 QString getter = propertyGetter(property);
1009 QString setter = propertySetter(property);
1011 hs <<
" Q_PROPERTY(" << type <<
" " << property.name;
1012 if (property.access != QDBusIntrospection::Property::Write)
1013 hs <<
" READ " << getter;
1014 if (property.access != QDBusIntrospection::Property::Read)
1015 hs <<
" WRITE " << setter;
1019 if (property.access != QDBusIntrospection::Property::Write) {
1020 hs <<
" " << type <<
" " << getter <<
"() const;\n";
1022 << className <<
"::" << getter <<
"() const\n"
1024 " // get the value of property " << property.name <<
"\n"
1025 " return qvariant_cast< " << type <<
" >(parent()->property(\"" << property.name <<
"\"));\n"
1030 if (property.access != QDBusIntrospection::Property::Read) {
1031 hs <<
" void " << setter <<
"(" << constRefType <<
"value);\n";
1032 cs <<
"void " << className <<
"::" << setter <<
"(" << constRefType <<
"value)\n"
1034 " // set the value of property " << property.name <<
"\n"
1035 " parent()->setProperty(\"" << property.name <<
"\", QVariant::fromValue(value";
1036 if (constRefType.contains(
"QDBusVariant"_L1))
1045 hs <<
"public Q_SLOTS: // METHODS\n";
1046 for (
const QDBusIntrospection::Method &method : interface->methods) {
1047 bool isNoReply = method.annotations.value(
ANNOTATION_NO_WAIT ""_L1).value ==
"true"_L1;
1048 if (isNoReply && !method.outputArgs.isEmpty()) {
1049 reporter.warning(method.location,
1050 "method %s in interface %s is marked 'no-reply' but has output "
1057 QByteArray returnType;
1059 hs <<
"Q_NOREPLY void ";
1061 }
else if (method.outputArgs.isEmpty()) {
1066 qtTypeName(method.outputArgs.first().location,
1067 method.outputArgs.first().type, method.annotations, 0,
"Out");
1068 hs << returnType <<
" ";
1069 cs << returnType <<
" ";
1072 QString name = methodName(method);
1074 cs << className <<
"::" << name <<
"(";
1076 QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
1077 writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
1078 writeArgList(cs, argNames, method.annotations, method.inputArgs, method.outputArgs);
1083 " // handle method call " << interface->name <<
"." << methodName(method) <<
"\n";
1086 bool usingInvokeMethod =
false;
1087 if (parentClassName.isEmpty() && method.inputArgs.size() <= 10
1088 && method.outputArgs.size() <= 1)
1089 usingInvokeMethod =
true;
1091 if (usingInvokeMethod) {
1093 if (!returnType.isEmpty())
1094 cs <<
" " << returnType <<
" " << argNames.at(method.inputArgs.size())
1097 static const char invoke[] =
" QMetaObject::invokeMethod(parent(), \"";
1098 cs << invoke << name <<
"\"";
1100 if (!method.outputArgs.isEmpty())
1101 cs <<
", Q_RETURN_ARG("
1102 << qtTypeName(method.outputArgs.at(0).location, method.outputArgs.at(0).type,
1103 method.annotations, 0,
"Out")
1104 <<
", " << argNames.at(method.inputArgs.size()) <<
")";
1106 for (qsizetype i = 0; i < method.inputArgs.size(); ++i)
1108 << qtTypeName(method.inputArgs.at(i).location, method.inputArgs.at(i).type,
1109 method.annotations, i,
"In")
1110 <<
", " << argNames.at(i) <<
")";
1114 if (!returnType.isEmpty())
1115 cs <<
" return " << argNames.at(method.inputArgs.size()) <<
";\n";
1117 if (parentClassName.isEmpty())
1122 if (!method.outputArgs.isEmpty())
1125 if (parentClassName.isEmpty())
1126 cs <<
"static_cast<YourObjectType *>(parent())->";
1131 qsizetype argPos = 0;
1133 for (qsizetype i = 0; i < method.inputArgs.size(); ++i) {
1134 cs << (first ?
"" :
", ") << argNames.at(argPos++);
1138 for (qsizetype i = 1; i < method.outputArgs.size(); ++i) {
1139 cs << (first ?
"" :
", ") << argNames.at(argPos++);
1148 hs <<
"Q_SIGNALS: // SIGNALS\n";
1149 for (
const QDBusIntrospection::Signal &signal : interface->signals_) {
1150 hs <<
" void " << signal.name <<
"(";
1152 QStringList argNames = makeArgNames(signal.outputArgs);
1153 writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs);
1162 if (!customNamespace.isEmpty()) {
1163 hs <<
"} // end of namespace " << customNamespace <<
"\n"
1165 cs <<
"} // end of namespace " << customNamespace <<
"\n"
1172 QString mocName = moc(filename);
1173 if (includeMocs && !mocName.isEmpty())
1175 "#include \"" << mocName <<
"\"\n";
1181 const bool headerOpen = openFile(headerName, file);
1183 file.write(headerData);
1185 if (headerName == cppName) {
1187 file.write(cppData);
1190 if (openFile(cppName, cppFile))
1191 cppFile.write(cppData);
1195int QDBusXmlToCpp::
run(
const QCoreApplication &app)
1198 parser.setApplicationDescription(
1199 "Produces the C++ code to implement the interfaces defined in the input file.\n\n"
1200 "If the file name given to the options -a and -p does not end in .cpp or .h, the\n"
1201 "program will automatically append the suffixes and produce both files.\n"
1202 "You can also use a colon (:) to separate the header name from the source file\n"
1203 "name, as in '-a filename_p.h:filename.cpp'.\n\n"
1204 "If you pass a dash (-) as the argument to either -p or -a, the output is written\n"
1205 "to the standard output."_L1);
1207 parser.addHelpOption();
1208 parser.addVersionOption();
1209 parser.addPositionalArgument(u"xml-or-xml-file"_s, u"XML file to use."_s);
1210 parser.addPositionalArgument(u"interfaces"_s, u"List of interfaces to use."_s,
1211 u"[interfaces ...]"_s);
1213 QCommandLineOption adapterCodeOption(QStringList{u"a"_s, u"adaptor"_s},
1214 u"Write the adaptor code to <filename>"_s, u"filename"_s);
1215 parser.addOption(adapterCodeOption);
1217 QCommandLineOption classNameOption(QStringList{u"c"_s, u"classname"_s},
1218 u"Use <classname> as the class name for the generated classes. "
1219 u"This option can only be used when processing a single interface."_s,
1221 parser.addOption(classNameOption);
1223 QCommandLineOption namespaceOption(QStringList{u"namespace"_s},
1224 u"Put all generated classes into the namespace <namespace>. "_s, u"namespace"_s);
1225 parser.addOption(namespaceOption);
1227 QCommandLineOption addIncludeOption(QStringList{u"i"_s, u"include"_s},
1228 u"Add #include \"filename\" to the output"_s, u"filename"_s);
1229 parser.addOption(addIncludeOption);
1231 QCommandLineOption addGlobalIncludeOption(QStringList{u"I"_s, u"global-include"_s},
1232 u"Add #include <filename> to the output"_s, u"filename"_s);
1233 parser.addOption(addGlobalIncludeOption);
1235 QCommandLineOption adapterParentOption(u"l"_s,
1236 u"When generating an adaptor, use <classname> as the parent class"_s, u"classname"_s);
1237 parser.addOption(adapterParentOption);
1239 QCommandLineOption mocIncludeOption(QStringList{u"m"_s, u"moc"_s},
1240 u"Generate #include \"filename.moc\" statements in the .cpp files"_s);
1241 parser.addOption(mocIncludeOption);
1243 QCommandLineOption noNamespaceOption(QStringList{u"N"_s, u"no-namespaces"_s},
1244 u"Do not export the generated class into the D-Bus specific namespace"_s);
1245 parser.addOption(noNamespaceOption);
1247 QCommandLineOption proxyCodeOption(QStringList{u"p"_s, u"proxy"_s},
1248 u"Write the proxy code to <filename>"_s, u"filename"_s);
1249 parser.addOption(proxyCodeOption);
1251 QCommandLineOption verboseOption(QStringList{u"V"_s, u"verbose"_s},
1253 parser.addOption(verboseOption);
1255 parser.process(app);
1257 QString adaptorFile = parser.value(adapterCodeOption);
1258 globalClassName = parser.value(classNameOption);
1259 includes = parser.values(addIncludeOption);
1260 globalIncludes = parser.values(addGlobalIncludeOption);
1261 parentClassName = parser.value(adapterParentOption);
1262 customNamespace = parser.value(namespaceOption);
1263 includeMocs = parser.isSet(mocIncludeOption);
1264 skipNamespaces = parser.isSet(noNamespaceOption);
1265 QString proxyFile = parser.value(proxyCodeOption);
1266 bool verbose = parser.isSet(verboseOption);
1268 wantedInterfaces = parser.positionalArguments();
1269 if (!wantedInterfaces.isEmpty()) {
1270 inputFile = wantedInterfaces.takeFirst();
1273 if (!inputInfo.exists() || !inputInfo.isFile() || !inputInfo.isReadable()) {
1274 qCritical(
"Error: Input %s is not a file or cannot be accessed\n",
qPrintable(inputFile));
1280 QLoggingCategory::setFilterRules(u"dbus.parser.debug=true"_s);
1282 QDBusIntrospection::Interfaces interfaces = readInput();
1283 cleanInterfaces(interfaces);
1285 if (!globalClassName.isEmpty() && interfaces.count() != 1) {
1286 qCritical(
"Option -c/--classname can only be used with a single interface.\n");
1290 QStringList args = app.arguments();
1292 commandLine =
PROGRAMNAME " "_L1 + args.join(u' ');
1294 if (!proxyFile.isEmpty() || adaptorFile.isEmpty())
1295 writeProxy(proxyFile, interfaces);
1297 if (!adaptorFile.isEmpty())
1298 writeAdaptor(adaptorFile, interfaces);
1305 QCoreApplication app(argc, argv);
1306 QCoreApplication::setApplicationName(QStringLiteral(
PROGRAMNAME));
1307 QCoreApplication::setApplicationVersion(QStringLiteral(
PROGRAMVERSION));
1309 return QDBusXmlToCpp()
.run(app
);
int main(int argc, char *argv[])
[2]
The QCommandLineParser class provides a means for handling the command line options.
int run(const QCoreApplication &app)
QT_FORWARD_DECLARE_CLASS(QTextStream)
const QString & asString(const QString &s)
#define ANNOTATION_NO_WAIT
static const char forwardDeclarations[]
static bool isSupportedSuffix(QStringView suffix)
static QString stringify(const QString &data)
static QString moc(const QString &name)
static const char includeList[]
static QString cpp(const QString &name)
static QString templateArg(const QByteArray &arg)
static QString methodName(const QDBusIntrospection::Method &method)
static QString header(const QString &name)
static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs, const QDBusIntrospection::Arguments &outputArgs=QDBusIntrospection::Arguments())
static QString constRefArg(const QByteArray &arg)
static bool openFile(const QString &fileName, QFile &file)
static QString nonConstRefArg(const QByteArray &arg)
#define qPrintable(string)