1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309 | // Copyright (C) 2021 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0#include <qbytearray.h>#include <qcommandlineparser.h>#include <qcoreapplication.h>#include <qdebug.h>#include <qfile.h>#include <qfileinfo.h>#include <qloggingcategory.h>#include <qstring.h>#include <qstringlist.h>#include <qtextstream.h>#include <qset.h>#include <qdbusmetatype.h>#include <private/qdbusintrospection_p.h>#include <stdio.h>#include <stdlib.h>#define PROGRAMNAME"qdbusxml2cpp"#define PROGRAMVERSION"0.8"#define PROGRAMCOPYRIGHT QT_COPYRIGHT#define ANNOTATION_NO_WAIT"org.freedesktop.DBus.Method.NoReply"using namespaceQt::StringLiterals;class QDBusXmlToCpp final {public:intrun(const QCoreApplication &app);private:class DiagnosticsReporter final :public QDBusIntrospection::DiagnosticsReporter {public:voidsetFileName(const QString &fileName) { m_fileName = fileName; }boolhadErrors()const{return m_hadErrors; }voidwarning(constQDBusIntrospection::SourceLocation &location,const char*msg,...) override;voiderror(constQDBusIntrospection::SourceLocation &location,const char*msg,...) override;voidnote(constQDBusIntrospection::SourceLocation &location,const char*msg, ...)Q_ATTRIBUTE_FORMAT_PRINTF(3,4);private: QString m_fileName;bool m_hadErrors =false;voidreport(constQDBusIntrospection::SourceLocation &location,const char*msg,va_list ap,const char*severity);};enum ClassType { Proxy, Adaptor };voidwriteAdaptor(const QString &filename,constQDBusIntrospection::Interfaces &interfaces);voidwriteProxy(const QString &filename,constQDBusIntrospection::Interfaces &interfaces);QDBusIntrospection::Interfaces readInput();voidcleanInterfaces(QDBusIntrospection::Interfaces &interfaces); QTextStream &writeHeader(QTextStream &ts,bool changesWillBeLost); QString classNameForInterface(const QString &interface, ClassType classType); QByteArray qtTypeName(constQDBusIntrospection::SourceLocation &location,const QString &signature,constQDBusIntrospection::Annotations &annotations, qsizetype paramId = -1,const char*direction ="Out");voidwriteArgList(QTextStream &ts,const QStringList &argNames,constQDBusIntrospection::Annotations &annotations,constQDBusIntrospection::Arguments &inputArgs,constQDBusIntrospection::Arguments &outputArgs =QDBusIntrospection::Arguments());voidwriteSignalArgList(QTextStream &ts,const QStringList &argNames,constQDBusIntrospection::Annotations &annotations,constQDBusIntrospection::Arguments &outputArgs); QString propertyGetter(constQDBusIntrospection::Property &property); QString propertySetter(constQDBusIntrospection::Property &property); QString globalClassName; QString parentClassName; QString customNamespace; QString inputFile;bool skipNamespaces =false;bool includeMocs =false; QString commandLine; QStringList includes; QStringList globalIncludes; QStringList wantedInterfaces; DiagnosticsReporter reporter;};static const char includeList[] ="#include <QtCore/QByteArray>\n""#include <QtCore/QList>\n""#include <QtCore/QMap>\n""#include <QtCore/QString>\n""#include <QtCore/QStringList>\n""#include <QtCore/QVariant>\n";static const char forwardDeclarations[] ="#include <QtCore/qcontainerfwd.h>\n";voidQDBusXmlToCpp::DiagnosticsReporter::warning(constQDBusIntrospection::SourceLocation &location,const char*msg, ...){va_list ap;va_start(ap, msg);report(location, msg, ap,"warning");va_end(ap);}voidQDBusXmlToCpp::DiagnosticsReporter::error(constQDBusIntrospection::SourceLocation &location,const char*msg, ...){va_list ap;va_start(ap, msg);report(location, msg, ap,"error");va_end(ap); m_hadErrors =true;}voidQDBusXmlToCpp::DiagnosticsReporter::note(constQDBusIntrospection::SourceLocation &location,const char*msg, ...){va_list ap;va_start(ap, msg);report(location, msg, ap,"note");va_end(ap); m_hadErrors =true;}voidQDBusXmlToCpp::DiagnosticsReporter::report(constQDBusIntrospection::SourceLocation &location,const char*msg,va_list ap,const char*severity){fprintf(stderr,"%s:%lld:%lld: %s: ",qPrintable(m_fileName),(long long int)location.lineNumber, (long long int)location.columnNumber +1, severity);vfprintf(stderr, msg, ap);}QDBusIntrospection::Interfaces QDBusXmlToCpp::readInput(){ QFile input(inputFile);if(inputFile.isEmpty() || inputFile =="-"_L1) { reporter.setFileName("<standard input>"_L1);if(!input.open(stdin,QIODevice::ReadOnly)) {fprintf(stderr, PROGRAMNAME ": could not open standard input: %s\n",qPrintable(input.errorString()));exit(1);}}else{ reporter.setFileName(inputFile);if(!input.open(QIODevice::ReadOnly)) {fprintf(stderr, PROGRAMNAME ": could not open input file '%s': %s\n",qPrintable(inputFile),qPrintable(input.errorString()));exit(1);}} QByteArray data = input.readAll();auto interfaces =QDBusIntrospection::parseInterfaces(QString::fromUtf8(data), &reporter);if(reporter.hadErrors())exit(1);return interfaces;}voidQDBusXmlToCpp::cleanInterfaces(QDBusIntrospection::Interfaces &interfaces){if(!wantedInterfaces.isEmpty()) {QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();while(it != interfaces.end())if(!wantedInterfaces.contains(it.key())) it = interfaces.erase(it);else++it;}}static boolisSupportedSuffix(QStringView suffix){const QLatin1StringView candidates[] = {"h"_L1,"cpp"_L1,"cc"_L1 };for(auto candidate : candidates)if(suffix == candidate)return true;return false;}// produce a header name from the file namestatic QString header(const QString &name){ QStringList parts = name.split(u':'); QString retval = parts.front();if(retval.isEmpty() || retval =="-"_L1)return retval; QFileInfo header{retval};if(!isSupportedSuffix(header.suffix())) retval.append(".h"_L1);return retval;}// produce a cpp name from the file namestatic QString cpp(const QString &name){ QStringList parts = name.split(u':'); QString retval = parts.back();if(retval.isEmpty() || retval =="-"_L1)return retval; QFileInfo source{retval};if(!isSupportedSuffix(source.suffix())) retval.append(".cpp"_L1);return retval;}// produce a moc name from the file namestatic QString moc(const QString &name){ QString retval;const QStringList fileNames = name.split(u':');if(fileNames.size() ==1) { QFileInfo fi{fileNames.front()};if(isSupportedSuffix(fi.suffix())) {// Generates a file that contains the header and the implementation: include "filename.moc" retval += fi.completeBaseName(); retval +=".moc"_L1;}else{// Separate source and header files are generated: include "moc_filename.cpp" retval +="moc_"_L1; retval += fi.fileName(); retval +=".cpp"_L1;}}else{ QString headerName = fileNames.front(); QString sourceName = fileNames.back();if(sourceName.isEmpty() || sourceName =="-"_L1) {// If only a header is generated, don't include anything}else if(headerName.isEmpty() || headerName =="-"_L1) {// If only source file is generated: include "moc_sourcename.cpp" QFileInfo source{sourceName}; retval +="moc_"_L1; retval += source.completeBaseName(); retval +=".cpp"_L1;fprintf(stderr,"warning: no header name is provided, assuming it to be\"%s\"\n",qPrintable(source.completeBaseName() +".h"_L1));}else{// Both source and header generated: include "moc_headername.cpp" QFileInfo header{headerName}; retval +="moc_"_L1; retval += header.completeBaseName(); retval +=".cpp"_L1;}}return retval;} QTextStream &QDBusXmlToCpp::writeHeader(QTextStream &ts,bool changesWillBeLost){ ts <<"/*\n"" * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION "\n"" * Source file was "<<QFileInfo(inputFile).fileName() <<"\n"" *\n"" * " PROGRAMNAME " is " PROGRAMCOPYRIGHT "\n"" *\n"" * This is an auto-generated file.\n";if(changesWillBeLost) ts <<" * Do not edit! All changes made to it will be lost.\n";else ts <<" * This file may have been hand-edited. Look for HAND-EDIT comments\n"" * before re-generating it.\n"; ts <<" */\n\n";return ts;} QString QDBusXmlToCpp::classNameForInterface(const QString &interface,QDBusXmlToCpp::ClassType classType){if(!globalClassName.isEmpty())return globalClassName;constauto parts = QStringView{interface}.split(u'.'); QString retval;if(classType == Proxy) {for(constauto&part : parts) { retval += part[0].toUpper(); retval += part.mid(1);}}else{ retval += parts.last()[0].toUpper() + parts.last().mid(1);}if(classType == Proxy) retval +="Interface"_L1;else retval +="Adaptor"_L1;return retval;} QByteArray QDBusXmlToCpp::qtTypeName(constQDBusIntrospection::SourceLocation &location,const QString &signature,constQDBusIntrospection::Annotations &annotations, qsizetype paramId,const char*direction){int type =QDBusMetaType::signatureToMetaType(signature.toLatin1()).id();if(type ==QMetaType::UnknownType) { QString annotationName = u"org.qtproject.QtDBus.QtTypeName"_s;if(paramId >=0) annotationName +=".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);auto annotation = annotations.value(annotationName); QString qttype = annotation.value;if(!qttype.isEmpty())returnstd::move(qttype).toLatin1(); QString oldAnnotationName = u"com.trolltech.QtDBus.QtTypeName"_s;if(paramId >=0) oldAnnotationName +=".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId); annotation = annotations.value(oldAnnotationName); qttype = annotation.value;if(qttype.isEmpty()) { reporter.error(location,"unknown type `%s'\n",qPrintable(signature)); reporter.note(location,"you should add <annotation name=\"%s\"value=\"<type>\"/>\n",qPrintable(annotationName));exit(1);} reporter.warning(annotation.location,"deprecated annotation '%s' found\n",qPrintable(oldAnnotationName)); reporter.note(annotation.location,"suggest updating to '%s'\n",qPrintable(annotationName));returnstd::move(qttype).toLatin1();}returnQMetaType(type).name();}static QString nonConstRefArg(const QByteArray &arg){returnQLatin1StringView(arg) +" &"_L1;}static QString templateArg(const QByteArray &arg){if(!arg.endsWith('>'))returnQLatin1StringView(arg);returnQLatin1StringView(arg) +" "_L1;}static QString constRefArg(const QByteArray &arg){if(!arg.startsWith('Q'))returnQLatin1StringView(arg) +" "_L1;elsereturn"const %1 &"_L1.arg(QLatin1StringView(arg));}static QStringList makeArgNames(constQDBusIntrospection::Arguments &inputArgs,constQDBusIntrospection::Arguments &outputArgs =QDBusIntrospection::Arguments()){ QStringList retval;const qsizetype numInputArgs = inputArgs.size();const qsizetype numOutputArgs = outputArgs.size(); retval.reserve(numInputArgs + numOutputArgs);for(qsizetype i =0; i < numInputArgs; ++i) {constQDBusIntrospection::Argument &arg = inputArgs.at(i); QString name = arg.name;if(name.isEmpty()) name = u"in%1"_s.arg(i);else name.replace(u'-', u'_');while(retval.contains(name)) name +="_"_L1; retval << name;}for(qsizetype i =0; i < numOutputArgs; ++i) {constQDBusIntrospection::Argument &arg = outputArgs.at(i); QString name = arg.name;if(name.isEmpty()) name = u"out%1"_s.arg(i);else name.replace(u'-', u'_');while(retval.contains(name)) name +="_"_L1; retval << name;}return retval;}voidQDBusXmlToCpp::writeArgList(QTextStream &ts,const QStringList &argNames,constQDBusIntrospection::Annotations &annotations,constQDBusIntrospection::Arguments &inputArgs,constQDBusIntrospection::Arguments &outputArgs){// input args:bool first =true; qsizetype argPos =0;for(qsizetype i =0; i < inputArgs.size(); ++i) {constQDBusIntrospection::Argument &arg = inputArgs.at(i); QString type =constRefArg(qtTypeName(arg.location, arg.type, annotations, i,"In"));if(!first) ts <<", "; ts << type << argNames.at(argPos++); first =false;} argPos++;// output args// yes, starting from 1for(qsizetype i =1; i < outputArgs.size(); ++i) {constQDBusIntrospection::Argument &arg = outputArgs.at(i);if(!first) ts <<", "; ts <<nonConstRefArg(qtTypeName(arg.location, arg.type, annotations, i,"Out"))<< argNames.at(argPos++); first =false;}}voidQDBusXmlToCpp::writeSignalArgList(QTextStream &ts,const QStringList &argNames,constQDBusIntrospection::Annotations &annotations,constQDBusIntrospection::Arguments &outputArgs){bool first =true; qsizetype argPos =0;for(qsizetype i =0; i < outputArgs.size(); ++i) {constQDBusIntrospection::Argument &arg = outputArgs.at(i); QString type =constRefArg(qtTypeName(arg.location, arg.type, annotations, i,"Out"));if(!first) ts <<", "; ts << type << argNames.at(argPos++); first =false;}} QString QDBusXmlToCpp::propertyGetter(constQDBusIntrospection::Property &property){auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertyGetter"_L1);if(!annotation.value.isEmpty())return annotation.value; annotation = property.annotations.value("com.trolltech.QtDBus.propertyGetter"_L1);if(!annotation.value.isEmpty()) { reporter.warning(annotation.location,"deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found\n"); reporter.note(annotation.location,"suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n");return annotation.value;} QString getter = property.name; getter[0] = getter[0].toLower();return getter;} QString QDBusXmlToCpp::propertySetter(constQDBusIntrospection::Property &property){auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertySetter"_L1);if(!annotation.value.isEmpty())return annotation.value; annotation = property.annotations.value("com.trolltech.QtDBus.propertySetter"_L1);if(!annotation.value.isEmpty()) { reporter.warning(annotation.location,"deprecated annotation 'com.trolltech.QtDBus.propertySetter' found\n"); reporter.note(annotation.location,"suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n");return annotation.value;} QString setter ="set"_L1 + property.name; setter[3] = setter[3].toUpper();return setter;}static QString methodName(constQDBusIntrospection::Method &method){ QString name = method.annotations.value(u"org.qtproject.QtDBus.MethodName"_s).value;if(!name.isEmpty())return name;return method.name;}static QString stringify(const QString &data){ QString retval; qsizetype i;for(i =0; i < data.size(); ++i) { retval += u'\"';for( ; i < data.size() && data[i] != u'\n'&& data[i] != u'\r'; ++i)if(data[i] == u'\"') retval +="\\\""_L1;else retval += data[i];if(i+1< data.size() && data[i] == u'\r'&& data[i+1] == u'\n') i++; retval +="\\n\"\n"_L1;}return retval;}static boolopenFile(const QString &fileName, QFile &file){if(fileName.isEmpty())return false;bool isOk =false;if(fileName =="-"_L1) { isOk = file.open(stdout,QIODevice::WriteOnly |QIODevice::Text);}else{ file.setFileName(fileName); isOk = file.open(QIODevice::WriteOnly |QIODevice::Truncate |QIODevice::Text);}if(!isOk)fprintf(stderr,"%s: Unable to open '%s': %s\n", PROGRAMNAME,qPrintable(fileName),qPrintable(file.errorString()));return isOk;}voidQDBusXmlToCpp::writeProxy(const QString &filename,constQDBusIntrospection::Interfaces &interfaces){// open the file QString headerName =header(filename); QByteArray headerData; QTextStream hs(&headerData); QString cppName =cpp(filename); QByteArray cppData; QTextStream cs(&cppData);// write the header:writeHeader(hs,true);if(cppName != headerName)writeHeader(cs,false);// include guards: QString includeGuard;if(!headerName.isEmpty() && headerName !="-"_L1) { includeGuard = headerName.toUpper().replace(u'.', u'_'); qsizetype pos = includeGuard.lastIndexOf(u'/');if(pos != -1) includeGuard = includeGuard.mid(pos +1);}else{ includeGuard = u"QDBUSXML2CPP_PROXY"_s;} hs <<"#ifndef "<< includeGuard <<"\n""#define "<< includeGuard <<"\n\n";// include our stuff: hs <<"#include <QtCore/QObject>\n"<< includeList;#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) hs <<"#include <QtDBus/QtDBus>\n";#else hs <<"#include <QtDBus/QDBusAbstractInterface>\n""#include <QtDBus/QDBusPendingReply>\n";#endiffor(const QString &include :std::as_const(includes)) { hs <<"#include\""<< include <<"\"\n";if(headerName.isEmpty()) cs <<"#include\""<< include <<"\"\n";}for(const QString &include :std::as_const(globalIncludes)) { hs <<"#include <"<< include <<">\n";if(headerName.isEmpty()) cs <<"#include <"<< include <<">\n";} hs <<"\n";if(cppName != headerName) {if(!headerName.isEmpty() && headerName !="-"_L1) cs <<"#include\""<< headerName <<"\"\n\n";}if(!customNamespace.isEmpty()) { hs <<"namespace "<< customNamespace <<" {\n""\n"; cs <<"namespace "<< customNamespace <<" {\n""\n";}for(constQDBusIntrospection::Interface *interface : interfaces) { QString className =classNameForInterface(interface->name, Proxy);// comment: hs <<"/*\n"" * Proxy class for interface "<< interface->name <<"\n"" */\n"; cs <<"/*\n"" * Implementation of interface class "<< className <<"\n"" */\n\n";// class header: hs <<"class "<< className <<": public QDBusAbstractInterface\n""{\n"" Q_OBJECT\n";// the interface name hs <<"public:\n"" static inline const char *staticInterfaceName()\n"" { return\""<< interface->name <<"\"; }\n\n";// constructors/destructors: hs <<"public:\n"" "<< className <<"(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);\n\n"" ~"<< className <<"();\n\n"; cs << className <<"::"<< className <<"(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)\n"" : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)\n""{\n""}\n\n"<< className <<"::~"<< className <<"()\n""{\n""}\n\n";// properties:for(constQDBusIntrospection::Property &property : interface->properties) { QByteArray type =qtTypeName(property.location, property.type, property.annotations); QString getter =propertyGetter(property); QString setter =propertySetter(property); hs <<" Q_PROPERTY("<< type <<" "<< property.name;// getter:if(property.access !=QDBusIntrospection::Property::Write)// it's readable hs <<" READ "<< getter;// setterif(property.access !=QDBusIntrospection::Property::Read)// it's writeable hs <<" WRITE "<< setter; hs <<")\n";// getter:if(property.access !=QDBusIntrospection::Property::Write) { hs <<" inline "<< type <<" "<< getter <<"() const\n"" { return qvariant_cast< "<< type <<" >(property(\""<< property.name <<"\")); }\n";}// setter:if(property.access !=QDBusIntrospection::Property::Read) { hs <<" inline void "<< setter <<"("<<constRefArg(type) <<"value)\n"" { setProperty(\""<< property.name <<"\", QVariant::fromValue(value)); }\n";} hs <<"\n";}// methods: hs <<"public Q_SLOTS: // METHODS\n";for(constQDBusIntrospection::Method &method : interface->methods) {bool isDeprecated = method.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value =="true"_L1;bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value =="true"_L1;if(isNoReply && !method.outputArgs.isEmpty()) { reporter.warning(method.location,"method %s in interface %s is marked 'no-reply' but has output ""arguments.\n",qPrintable(method.name),qPrintable(interface->name));continue;}if(isDeprecated) hs <<" Q_DECL_DEPRECATED ";else hs <<" ";if(isNoReply) { hs <<"Q_NOREPLY inline void ";}else{ hs <<"inline QDBusPendingReply<";for(qsizetype i =0; i < method.outputArgs.size(); ++i) hs << (i >0?", ":"")<<templateArg(qtTypeName(method.outputArgs.at(i).location, method.outputArgs.at(i).type, method.annotations, i,"Out")); hs <<"> ";} hs <<methodName(method) <<"("; QStringList argNames =makeArgNames(method.inputArgs);writeArgList(hs, argNames, method.annotations, method.inputArgs); hs <<")\n"" {\n"" QList<QVariant> argumentList;\n";if(!method.inputArgs.isEmpty()) { hs <<" argumentList";for(qsizetype argPos =0; argPos < method.inputArgs.size(); ++argPos) hs <<" << QVariant::fromValue("<< argNames.at(argPos) <<')'; hs <<";\n";}if(isNoReply) hs <<" callWithArgumentList(QDBus::NoBlock, ""QStringLiteral(\""<< method.name <<"\"), argumentList);\n";else hs <<" return asyncCallWithArgumentList(QStringLiteral(\""<< method.name <<"\"), argumentList);\n";// close the function: hs <<" }\n";if(method.outputArgs.size() >1) {// generate the old-form QDBusReply methods with multiple incoming parameters hs << (isDeprecated ?" Q_DECL_DEPRECATED ":" ") <<"inline QDBusReply<"<<templateArg(qtTypeName(method.outputArgs.first().location, method.outputArgs.first().type, method.annotations,0,"Out"))<<"> "; hs << method.name <<"("; QStringList argNames =makeArgNames(method.inputArgs, method.outputArgs);writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs); hs <<")\n"" {\n"" QList<QVariant> argumentList;\n"; qsizetype argPos =0;if(!method.inputArgs.isEmpty()) { hs <<" argumentList";for(argPos =0; argPos < method.inputArgs.size(); ++argPos) hs <<" << QVariant::fromValue("<< argNames.at(argPos) <<')'; hs <<";\n";} hs <<" QDBusMessage reply = callWithArgumentList(QDBus::Block, ""QStringLiteral(\""<< method.name <<"\"), argumentList);\n"; argPos++; hs <<" if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().size() == "<< method.outputArgs.size() <<") {\n";// yes, starting from 1for(qsizetype i =1; i < method.outputArgs.size(); ++i) hs <<" "<< argNames.at(argPos++) <<" = qdbus_cast<"<<templateArg(qtTypeName(method.outputArgs.at(i).location, method.outputArgs.at(i).type, method.annotations, i,"Out"))<<">(reply.arguments().at("<< i <<"));\n"; hs <<" }\n"" return reply;\n"" }\n";} hs <<"\n";} hs <<"Q_SIGNALS: // SIGNALS\n";for(constQDBusIntrospection::Signal &signal : interface->signals_) { hs <<" ";if(signal.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value =="true"_L1) hs <<"Q_DECL_DEPRECATED "; hs <<"void "<< signal.name <<"("; QStringList argNames =makeArgNames(signal.outputArgs);writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs); hs <<");\n";// finished for header}// close the class: hs <<"};\n\n";}if(!customNamespace.isEmpty()) { hs <<"} // end of namespace "<< customNamespace <<"\n"<<"\n"; cs <<"} // end of namespace "<< customNamespace <<"\n";}if(!skipNamespaces) { QStringList last;QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin();do{ QStringList current; QString name;if(it != interfaces.constEnd()) { current = it->constData()->name.split(u'.'); name = current.takeLast();} qsizetype i =0;while(i < current.size() && i < last.size() && current.at(i) == last.at(i))++i;// i parts matched// close last.arguments().size() - i namespaces:for(qsizetype j = i; j < last.size(); ++j) hs <<QString((last.size() - j -1+ i) *2, u' ') <<"}\n";// open current.arguments().size() - i namespacesfor(qsizetype j = i; j < current.size(); ++j) hs <<QString(j *2, u' ') <<"namespace "<< current.at(j) <<" {\n";// add this class:if(!name.isEmpty()) { hs <<QString(current.size() *2, u' ')<<"using "<< name <<" = "<< (customNamespace.isEmpty() ?"":"::")<< customNamespace <<"::"<<classNameForInterface(it->constData()->name, Proxy)<<";\n";}if(it == interfaces.constEnd())break;++it; last = current;}while(true); hs <<"\n";}// close the include guard hs <<"#endif\n"; QString mocName =moc(filename);if(includeMocs && !mocName.isEmpty()) cs <<"\n""#include\""<< mocName <<"\"\n"; cs.flush(); hs.flush(); QFile file;const bool headerOpen =openFile(headerName, file);if(headerOpen) file.write(headerData);if(headerName == cppName) {if(headerOpen) file.write(cppData);}else{ QFile cppFile;if(openFile(cppName, cppFile)) cppFile.write(cppData);}}voidQDBusXmlToCpp::writeAdaptor(const QString &filename,constQDBusIntrospection::Interfaces &interfaces){// open the file QString headerName =header(filename); QByteArray headerData; QTextStream hs(&headerData); QString cppName =cpp(filename); QByteArray cppData; QTextStream cs(&cppData);// write the headerswriteHeader(hs,false);if(cppName != headerName)writeHeader(cs,true);// include guards: QString includeGuard;if(!headerName.isEmpty() && headerName !="-"_L1) { includeGuard = headerName.toUpper().replace(u'.', u'_'); qsizetype pos = includeGuard.lastIndexOf(u'/');if(pos != -1) includeGuard = includeGuard.mid(pos +1);}else{ includeGuard = u"QDBUSXML2CPP_ADAPTOR"_s;} hs <<"#ifndef "<< includeGuard <<"\n""#define "<< includeGuard <<"\n\n";// include our stuff: hs <<"#include <QtCore/QObject>\n";if(cppName == headerName) hs <<"#include <QtCore/QMetaObject>\n""#include <QtCore/QVariant>\n";#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) hs <<"#include <QtDBus/QtDBus>\n";#else hs <<"#include <QtDBus/QDBusAbstractAdaptor>\n""#include <QtDBus/QDBusObjectPath>\n";#endiffor(const QString &include :std::as_const(includes)) { hs <<"#include\""<< include <<"\"\n";if(headerName.isEmpty()) cs <<"#include\""<< include <<"\"\n";}for(const QString &include :std::as_const(globalIncludes)) { hs <<"#include <"<< include <<">\n";if(headerName.isEmpty()) cs <<"#include <"<< include <<">\n";}if(cppName != headerName) {if(!headerName.isEmpty() && headerName !="-"_L1) cs <<"#include\""<< headerName <<"\"\n"; cs <<"#include <QtCore/QMetaObject>\n"<< includeList <<"\n"; hs << forwardDeclarations;}else{ hs << includeList;} hs <<"\n"; QString parent = parentClassName;if(parentClassName.isEmpty()) parent = u"QObject"_s;if(!customNamespace.isEmpty()) { hs <<"namespace "<< customNamespace <<" {\n""\n"; cs <<"namespace "<< customNamespace <<" {\n""\n";}for(constQDBusIntrospection::Interface *interface : interfaces) { QString className =classNameForInterface(interface->name, Adaptor);// comment: hs <<"/*\n"" * Adaptor class for interface "<< interface->name <<"\n"" */\n"; cs <<"/*\n"" * Implementation of adaptor class "<< className <<"\n"" */\n\n";// class header: hs <<"class "<< className <<": public QDBusAbstractAdaptor\n""{\n"" Q_OBJECT\n"" Q_CLASSINFO(\"D-Bus Interface\",\""<< interface->name <<"\")\n"" Q_CLASSINFO(\"D-Bus Introspection\",\"\"\n"<<stringify(interface->introspection)<<"\"\")\n""public:\n"" "<< className <<"("<< parent <<" *parent);\n"" ~"<< className <<"() override;\n\n";if(!parentClassName.isEmpty()) hs <<" inline "<< parent <<" *parent() const\n"" { return static_cast<"<< parent <<" *>(QObject::parent()); }\n\n";// constructor/destructor cs << className <<"::"<< className <<"("<< parent <<" *parent)\n"" : QDBusAbstractAdaptor(parent)\n""{\n"" // constructor\n"" setAutoRelaySignals(true);\n""}\n\n"<< className <<"::~"<< className <<"()\n""{\n"" // destructor\n""}\n\n"; hs <<"public: // PROPERTIES\n";for(constQDBusIntrospection::Property &property : interface->properties) { QByteArray type =qtTypeName(property.location, property.type, property.annotations); QString constRefType =constRefArg(type); QString getter =propertyGetter(property); QString setter =propertySetter(property); hs <<" Q_PROPERTY("<< type <<" "<< property.name;if(property.access !=QDBusIntrospection::Property::Write) hs <<" READ "<< getter;if(property.access !=QDBusIntrospection::Property::Read) hs <<" WRITE "<< setter; hs <<")\n";// getter:if(property.access !=QDBusIntrospection::Property::Write) { hs <<" "<< type <<" "<< getter <<"() const;\n"; cs << type <<" "<< className <<"::"<< getter <<"() const\n""{\n"" // get the value of property "<< property.name <<"\n"" return qvariant_cast< "<< type <<" >(parent()->property(\""<< property.name <<"\"));\n""}\n\n";}// setterif(property.access !=QDBusIntrospection::Property::Read) { hs <<" void "<< setter <<"("<< constRefType <<"value);\n"; cs <<"void "<< className <<"::"<< setter <<"("<< constRefType <<"value)\n""{\n"" // set the value of property "<< property.name <<"\n"" parent()->setProperty(\""<< property.name <<"\", QVariant::fromValue(value";if(constRefType.contains("QDBusVariant"_L1)) cs <<".variant()"; cs <<"));\n""}\n\n";} hs <<"\n";} hs <<"public Q_SLOTS: // METHODS\n";for(constQDBusIntrospection::Method &method : interface->methods) {bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value =="true"_L1;if(isNoReply && !method.outputArgs.isEmpty()) { reporter.warning(method.location,"method %s in interface %s is marked 'no-reply' but has output ""arguments.\n",qPrintable(method.name),qPrintable(interface->name));continue;} hs <<" "; QByteArray returnType;if(isNoReply) { hs <<"Q_NOREPLY void "; cs <<"void ";}else if(method.outputArgs.isEmpty()) { hs <<"void "; cs <<"void ";}else{ returnType =qtTypeName(method.outputArgs.first().location, method.outputArgs.first().type, method.annotations,0,"Out"); hs << returnType <<" "; cs << returnType <<" ";} QString name =methodName(method); hs << name <<"("; cs << className <<"::"<< name <<"("; QStringList argNames =makeArgNames(method.inputArgs, method.outputArgs);writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);writeArgList(cs, argNames, method.annotations, method.inputArgs, method.outputArgs); hs <<");\n";// finished for header cs <<")\n""{\n"" // handle method call "<< interface->name <<"."<<methodName(method) <<"\n";// make the callbool usingInvokeMethod =false;if(parentClassName.isEmpty() && method.inputArgs.size() <=10&& method.outputArgs.size() <=1) usingInvokeMethod =true;if(usingInvokeMethod) {// we are using QMetaObject::invokeMethodif(!returnType.isEmpty()) { cs <<" "<< returnType <<" "<< argNames.at(method.inputArgs.size())<<"{};\n";}static const char invoke[] =" QMetaObject::invokeMethod(parent(),\""; cs << invoke << name <<"\"";if(!method.outputArgs.isEmpty()) cs <<", Q_RETURN_ARG("<<qtTypeName(method.outputArgs.at(0).location, method.outputArgs.at(0).type, method.annotations,0,"Out")<<", "<< argNames.at(method.inputArgs.size()) <<")";for(qsizetype i =0; i < method.inputArgs.size(); ++i) cs <<", Q_ARG("<<qtTypeName(method.inputArgs.at(i).location, method.inputArgs.at(i).type, method.annotations, i,"In")<<", "<< argNames.at(i) <<")"; cs <<");\n";if(!returnType.isEmpty()) cs <<" return "<< argNames.at(method.inputArgs.size()) <<";\n";}else{if(parentClassName.isEmpty()) cs <<" //";else cs <<" ";if(!method.outputArgs.isEmpty()) cs <<"return ";if(parentClassName.isEmpty()) cs <<"static_cast<YourObjectType *>(parent())->";else cs <<"parent()->"; cs << name <<"("; qsizetype argPos =0;bool first =true;for(qsizetype i =0; i < method.inputArgs.size(); ++i) { cs << (first ?"":", ") << argNames.at(argPos++); first =false;}++argPos;// skip retval, if anyfor(qsizetype i =1; i < method.outputArgs.size(); ++i) { cs << (first ?"":", ") << argNames.at(argPos++); first =false;} cs <<");\n";} cs <<"}\n\n";} hs <<"Q_SIGNALS: // SIGNALS\n";for(constQDBusIntrospection::Signal &signal : interface->signals_) { hs <<" void "<< signal.name <<"("; QStringList argNames =makeArgNames(signal.outputArgs);writeSignalArgList(hs, argNames, signal.annotations, signal.outputArgs); hs <<");\n";// finished for header}// close the class: hs <<"};\n\n";}if(!customNamespace.isEmpty()) { hs <<"} // end of namespace "<< customNamespace <<"\n"<<"\n"; cs <<"} // end of namespace "<< customNamespace <<"\n";}// close the include guard hs <<"#endif\n"; QString mocName =moc(filename);if(includeMocs && !mocName.isEmpty()) cs <<"\n""#include\""<< mocName <<"\"\n"; cs.flush(); hs.flush(); QFile file;const bool headerOpen =openFile(headerName, file);if(headerOpen) file.write(headerData);if(headerName == cppName) {if(headerOpen) file.write(cppData);}else{ QFile cppFile;if(openFile(cppName, cppFile)) cppFile.write(cppData);}}intQDBusXmlToCpp::run(const QCoreApplication &app){ QCommandLineParser parser; parser.setApplicationDescription("Produces the C++ code to implement the interfaces defined in the input file.\n\n""If the file name given to the options -a and -p does not end in .cpp or .h, the\n""program will automatically append the suffixes and produce both files.\n""You can also use a colon (:) to separate the header name from the source file\n""name, as in '-a filename_p.h:filename.cpp'.\n\n""If you pass a dash (-) as the argument to either -p or -a, the output is written\n""to the standard output."_L1); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument(u"xml-or-xml-file"_s, u"XML file to use."_s); parser.addPositionalArgument(u"interfaces"_s, u"List of interfaces to use."_s, u"[interfaces ...]"_s); QCommandLineOption adapterCodeOption(QStringList{u"a"_s, u"adaptor"_s}, u"Write the adaptor code to <filename>"_s, u"filename"_s); parser.addOption(adapterCodeOption); QCommandLineOption classNameOption(QStringList{u"c"_s, u"classname"_s}, u"Use <classname> as the class name for the generated classes. " u"This option can only be used when processing a single interface."_s, u"classname"_s); parser.addOption(classNameOption); QCommandLineOption namespaceOption(QStringList{u"namespace"_s}, u"Put all generated classes into the namespace <namespace>. "_s, u"namespace"_s); parser.addOption(namespaceOption); QCommandLineOption addIncludeOption(QStringList{u"i"_s, u"include"_s}, u"Add #include\"filename\"to the output"_s, u"filename"_s); parser.addOption(addIncludeOption); QCommandLineOption addGlobalIncludeOption(QStringList{u"I"_s, u"global-include"_s}, u"Add #include <filename> to the output"_s, u"filename"_s); parser.addOption(addGlobalIncludeOption); QCommandLineOption adapterParentOption(u"l"_s, u"When generating an adaptor, use <classname> as the parent class"_s, u"classname"_s); parser.addOption(adapterParentOption); QCommandLineOption mocIncludeOption(QStringList{u"m"_s, u"moc"_s}, u"Generate #include\"filename.moc\"statements in the .cpp files"_s); parser.addOption(mocIncludeOption); QCommandLineOption noNamespaceOption(QStringList{u"N"_s, u"no-namespaces"_s}, u"Do not export the generated class into the D-Bus specific namespace"_s); parser.addOption(noNamespaceOption); QCommandLineOption proxyCodeOption(QStringList{u"p"_s, u"proxy"_s}, u"Write the proxy code to <filename>"_s, u"filename"_s); parser.addOption(proxyCodeOption); QCommandLineOption verboseOption(QStringList{u"V"_s, u"verbose"_s}, u"Be verbose."_s); parser.addOption(verboseOption); parser.process(app); QString adaptorFile = parser.value(adapterCodeOption); globalClassName = parser.value(classNameOption); includes = parser.values(addIncludeOption); globalIncludes = parser.values(addGlobalIncludeOption); parentClassName = parser.value(adapterParentOption); customNamespace = parser.value(namespaceOption); includeMocs = parser.isSet(mocIncludeOption); skipNamespaces = parser.isSet(noNamespaceOption); QString proxyFile = parser.value(proxyCodeOption);bool verbose = parser.isSet(verboseOption); wantedInterfaces = parser.positionalArguments();if(!wantedInterfaces.isEmpty()) { inputFile = wantedInterfaces.takeFirst(); QFileInfo inputInfo(inputFile);if(!inputInfo.exists() || !inputInfo.isFile() || !inputInfo.isReadable()) {qCritical("Error: Input %s is not a file or cannot be accessed\n",qPrintable(inputFile));return1;}}if(verbose)QLoggingCategory::setFilterRules(u"dbus.parser.debug=true"_s);QDBusIntrospection::Interfaces interfaces =readInput();cleanInterfaces(interfaces);if(!globalClassName.isEmpty() && interfaces.count() !=1) {qCritical("Option -c/--classname can only be used with a single interface.\n");return1;} QStringList args = app.arguments(); args.removeFirst(); commandLine = PROGRAMNAME " "_L1 + args.join(u' ');if(!proxyFile.isEmpty() || adaptorFile.isEmpty())writeProxy(proxyFile, interfaces);if(!adaptorFile.isEmpty())writeAdaptor(adaptorFile, interfaces);return0;}intmain(int argc,char**argv){ QCoreApplication app(argc, argv);QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME));QCoreApplication::setApplicationVersion(QStringLiteral(PROGRAMVERSION));returnQDBusXmlToCpp().run(app);}
|