Move code generator to tools subdirectory for higher consistency with OSS standards

This commit is contained in:
sangelovic
2019-04-26 00:03:46 +02:00
parent 1b02c604d8
commit 1b1b9ae8ae
13 changed files with 12 additions and 12 deletions

52
tools/CMakeLists.txt Normal file
View File

@@ -0,0 +1,52 @@
#-------------------------------
# PROJECT INFORMATION
#-------------------------------
cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-xml2cpp)
include(GNUInstallDirs)
#-------------------------------
# PERFORMING CHECKS
#-------------------------------
find_package(EXPAT REQUIRED)
#-------------------------------
# SOURCE FILES CONFIGURATION
#-------------------------------
set(SDBUSCPP_XML2CPP_SRCS
xml2cpp-codegen/xml2cpp.cpp
xml2cpp-codegen/xml.h
xml2cpp-codegen/xml.cpp
xml2cpp-codegen/generator_utils.h
xml2cpp-codegen/generator_utils.cpp
xml2cpp-codegen/BaseGenerator.h
xml2cpp-codegen/BaseGenerator.cpp
xml2cpp-codegen/AdaptorGenerator.h
xml2cpp-codegen/AdaptorGenerator.cpp
xml2cpp-codegen/ProxyGenerator.h
xml2cpp-codegen/ProxyGenerator.cpp)
#-------------------------------
# GENERAL COMPILER CONFIGURATION
#-------------------------------
set(CMAKE_CXX_STANDARD 14)
#----------------------------------
# EXECUTABLE BUILD INFORMATION
#----------------------------------
add_executable(${PROJECT_NAME} ${SDBUSCPP_XML2CPP_SRCS})
target_link_libraries (${PROJECT_NAME} ${EXPAT_LIBRARIES})
#----------------------------------
# INSTALLATION
#----------------------------------
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -0,0 +1,371 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file AdaptorGenerator.cpp
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "generator_utils.h"
#include "AdaptorGenerator.h"
// STL
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <iterator>
using std::endl;
using sdbuscpp::xml::Document;
using sdbuscpp::xml::Node;
using sdbuscpp::xml::Nodes;
/**
* Generate adaptor code - server glue
*/
int AdaptorGenerator::transformXmlToFileImpl(const Document& doc, const char* filename) const
{
Node &root = *(doc.root);
Nodes interfaces = root["interface"];
std::ostringstream code;
code << createHeader(filename, StubType::ADAPTOR);
for (const auto& interface : interfaces)
{
code << processInterface(*interface);
}
code << "#endif" << endl;
return writeToFile(filename, code.str());
}
std::string AdaptorGenerator::processInterface(Node& interface) const
{
std::string ifaceName = interface.get("name");
std::cout << "Generating adaptor code for interface " << ifaceName << endl;
unsigned int namespacesCount = 0;
std::string namespacesStr;
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
std::ostringstream body;
body << namespacesStr;
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
+ "_adaptor";
body << "class " << className << endl
<< "{" << endl
<< "public:" << endl
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl
<< tab << className << "(sdbus::IObject& object)" << endl
<< tab << tab << ": object_(object)" << endl;
Nodes methods = interface["method"];
Nodes signals = interface["signal"];
Nodes properties = interface["property"];
auto annotations = getAnnotations(interface);
std::string annotationRegistration;
for (const auto& annotation : annotations)
{
const auto& annotationName = annotation.first;
const auto& annotationValue = annotation.second;
if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
annotationRegistration += ".markAsDeprecated()";
else if (annotationName == "org.freedesktop.systemd1.Privileged" && annotationValue == "true")
annotationRegistration += ".markAsPrivileged()";
else if (annotationName == "org.freedesktop.DBus.Property.EmitsChangedSignal")
annotationRegistration += ".withPropertyUpdateBehavior(" + propertyAnnotationToFlag(annotationValue) + ")";
else
std::cerr << "Node: " << ifaceName << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
if(!annotationRegistration.empty())
{
std::stringstream str;
str << tab << tab << "object_.setInterfaceFlags(interfaceName)" << annotationRegistration << ";" << endl;
annotationRegistration = str.str();
}
std::string methodRegistration, methodDeclaration;
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
std::string signalRegistration, signalMethods;
std::tie(signalRegistration, signalMethods) = processSignals(signals);
std::string propertyRegistration, propertyAccessorDeclaration;
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
body << tab << "{" << endl
<< annotationRegistration
<< methodRegistration
<< signalRegistration
<< propertyRegistration
<< tab << "}" << endl << endl;
if (!signalMethods.empty())
{
body << "public:" << endl << signalMethods;
}
if (!methodDeclaration.empty())
{
body << "private:" << endl << methodDeclaration << endl;
}
if (!propertyAccessorDeclaration.empty())
{
body << "private:" << endl << propertyAccessorDeclaration << endl;
}
body << "private:" << endl
<< tab << "sdbus::IObject& object_;" << endl
<< "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
return body.str();
}
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
{
std::ostringstream registrationSS, declarationSS;
for (const auto& method : methods)
{
auto methodName = method->get("name");
auto annotations = getAnnotations(*method);
bool async{false};
std::string annotationRegistration;
for (const auto& annotation : annotations)
{
const auto& annotationName = annotation.first;
const auto& annotationValue = annotation.second;
if (annotationName == "org.freedesktop.DBus.Deprecated")
{
if (annotationValue == "true")
annotationRegistration += ".markAsDeprecated()";
}
else if (annotationName == "org.freedesktop.DBus.Method.NoReply")
{
if (annotationValue == "true")
annotationRegistration += ".withNoReply()";
}
else if (annotationName == "org.freedesktop.DBus.Method.Async")
{
if (annotationValue == "server" || annotationValue == "clientserver")
async = true;
}
else if (annotationName == "org.freedesktop.systemd1.Privileged")
{
if (annotationValue == "true")
annotationRegistration += ".markAsPrivileged()";
}
else
{
std::cerr << "Node: " << methodName << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
}
Nodes args = (*method)["arg"];
Nodes inArgs = args.select("direction" , "in");
Nodes outArgs = args.select("direction" , "out");
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs, async);
using namespace std::string_literals;
registrationSS << tab << tab << "object_.registerMethod(\""
<< methodName << "\")"
<< ".onInterface(interfaceName)"
<< ".implementedAs("
<< "[this]("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
<< argTypeStr
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "("
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
<< argStr << "); })"
<< annotationRegistration << ";" << endl;
declarationSS << tab
<< "virtual "
<< (async ? "void" : outArgsToType(outArgs))
<< " " << methodName
<< "("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
<< argTypeStr
<< ") = 0;" << endl;
}
return std::make_tuple(registrationSS.str(), declarationSS.str());
}
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
{
std::ostringstream signalRegistrationSS, signalMethodSS;
for (const auto& signal : signals)
{
auto name = signal->get("name");
auto annotations = getAnnotations(*signal);
std::string annotationRegistration;
for (const auto& annotation : annotations)
{
const auto& annotationName = annotation.first;
const auto& annotationValue = annotation.second;
if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
annotationRegistration += ".markAsDeprecated()";
else
std::cerr << "Node: " << name << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
Nodes args = (*signal)["arg"];
std::string argStr, argTypeStr, typeStr;;
std::tie(argStr, argTypeStr, typeStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab
<< "object_.registerSignal(\"" << name << "\")"
".onInterface(interfaceName)";
if (args.size() > 0)
{
signalRegistrationSS << ".withParameters<" << typeStr << ">()";
}
signalRegistrationSS << annotationRegistration;
signalRegistrationSS << ";" << endl;
signalMethodSS << tab << "void " << name << "(" << argTypeStr << ")" << endl
<< tab << "{" << endl
<< tab << tab << "object_.emitSignal(\"" << name << "\")"
".onInterface(interfaceName)";
if (!argStr.empty())
{
signalMethodSS << ".withArguments(" << argStr << ")";
}
signalMethodSS << ";" << endl
<< tab << "}" << endl << endl;
}
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
}
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
{
std::ostringstream registrationSS, declarationSS;
for (const auto& property : properties)
{
auto propertyName = property->get("name");
auto propertyAccess = property->get("access");
auto propertySignature = property->get("type");
auto propertyType = signature_to_type(propertySignature);
auto propertyArg = std::string("value");
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
auto annotations = getAnnotations(*property);
std::string annotationRegistration;
for (const auto& annotation : annotations)
{
const auto& annotationName = annotation.first;
const auto& annotationValue = annotation.second;
if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
annotationRegistration += ".markAsDeprecated()";
else if (annotationName == "org.freedesktop.DBus.Property.EmitsChangedSignal")
annotationRegistration += ".withUpdateBehavior(" + propertyAnnotationToFlag(annotationValue) + ")";
else if (annotationName == "org.freedesktop.systemd1.Privileged" && annotationValue == "true")
annotationRegistration += ".markAsPrivileged()";
else
std::cerr << "Node: " << propertyName << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
registrationSS << tab << tab << "object_.registerProperty(\""
<< propertyName << "\")"
<< ".onInterface(interfaceName)";
if (propertyAccess == "read" || propertyAccess == "readwrite")
{
registrationSS << ".withGetter([this](){ return this->" << propertyName << "(); })";
}
if (propertyAccess == "readwrite" || propertyAccess == "write")
{
registrationSS
<< ".withSetter([this](" << propertyTypeArg << ")"
"{ this->" << propertyName << "(" << propertyArg << "); })";
}
registrationSS << annotationRegistration;
registrationSS << ";" << endl;
if (propertyAccess == "read" || propertyAccess == "readwrite")
declarationSS << tab << "virtual " << propertyType << " " << propertyName << "() = 0;" << endl;
if (propertyAccess == "readwrite" || propertyAccess == "write")
declarationSS << tab << "virtual void " << propertyName << "(" << propertyTypeArg << ") = 0;" << endl;
}
return std::make_tuple(registrationSS.str(), declarationSS.str());
}
std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const
{
std::map<std::string, std::string> result;
Nodes annotations = (node)["annotation"];
for (const auto& annotation : annotations)
{
result[annotation->get("name")] = annotation->get("value");
}
return result;
}
std::string AdaptorGenerator::propertyAnnotationToFlag(const std::string& annotationValue) const
{
return annotationValue == "true" ? "sdbus::Flags::EMITS_CHANGE_SIGNAL"
: annotationValue == "invalidates" ? "sdbus::Flags::EMITS_INVALIDATION_SIGNAL"
: annotationValue == "const" ? "sdbus::Flags::CONST_PROPERTY_VALUE"
: annotationValue == "false" ? "sdbus::Flags::EMITS_NO_SIGNAL"
: "EMITS_CHANGE_SIGNAL"; // Default
}

View File

@@ -0,0 +1,94 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file AdaptorGenerator.h
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H
#define __SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H
// Own headers
#include "xml.h"
#include "BaseGenerator.h"
// STL
#include <tuple>
#include <set>
class AdaptorGenerator : public BaseGenerator
{
protected:
/**
* Transform xml to adaptor code
* @param doc
* @param filename
* @return 0 if ok
*/
int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const override;
private:
/**
* Generate source code for interface
* @param interface
* @return source code
*/
std::string processInterface(sdbuscpp::xml::Node& interface) const;
/**
* Generate source code for methods
* @param methods
* @return tuple: registration of methods, declaration of abstract methods
*/
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
/**
* Generate source code for signals
* @param signals
* @return tuple: registration of signals, definition of signal methods
*/
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
/**
* Generate source code for properties
* @param properties
* @return tuple: registration of properties, declaration of property accessor virtual methods
*/
std::tuple<std::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
/**
* Get annotations listed for a given node
* @param node
* @return map of annotation names to their values
*/
std::map<std::string, std::string> getAnnotations(sdbuscpp::xml::Node& node) const;
/**
* Get flag for property update behavior annotation value
* @param annotationValue
* @return flag
*/
std::string propertyAnnotationToFlag(const std::string& annotationValue) const;
};
#endif //__SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H

View File

@@ -0,0 +1,180 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file BaseGenerator.cpp
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "generator_utils.h"
#include "BaseGenerator.h"
// STL
#include <algorithm>
#include <fstream>
#include <iterator>
#include <iostream>
#include <map>
#include <sstream>
using std::endl;
using sdbuscpp::xml::Document;
using sdbuscpp::xml::Node;
using sdbuscpp::xml::Nodes;
int BaseGenerator::transformXmlToFile(const Document& doc, const char* filename) const
{
return transformXmlToFileImpl(doc, filename);
}
int BaseGenerator::writeToFile(const char* filename, const std::string& data) const
{
std::ofstream file(filename);
if (file.bad())
{
std::cerr << "Unable to write file " << filename << endl;
return 1;
}
file << data;
file.close();
return 0;
}
std::string BaseGenerator::createHeader(const char* filename, const StubType& stubType) const
{
std::ostringstream head;
head << getHeaderComment();
std::string specialization = stubType == StubType::ADAPTOR ? "adaptor" : "proxy";
std::string cond_comp{"__sdbuscpp__" + underscorize(filename)
+ "__" + specialization + "__H__"};
head << "#ifndef " << cond_comp << endl
<< "#define " << cond_comp << endl << endl;
head << "#include <sdbus-c++/sdbus-c++.h>" << endl
<< "#include <string>" << endl
<< "#include <tuple>" << endl
<< endl;
return head.str();
}
std::tuple<unsigned, std::string> BaseGenerator::generateNamespaces(const std::string& ifaceName) const
{
std::stringstream ss{ifaceName};
std::ostringstream body;
unsigned count{0};
// prints the namespaces X and Y defined with <interface name="X.Y.Z">
while (ss.str().find('.', ss.tellg()) != std::string::npos)
{
std::string nspace;
getline(ss, nspace, '.');
body << "namespace " << nspace << " {" << endl;
++count;
}
body << endl;
return std::make_tuple(count, body.str());
}
std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args, bool async) const
{
std::ostringstream argSS, argTypeSS, typeSS;
for (size_t i = 0; i < args.size(); ++i)
{
auto arg = args.at(i);
if (i > 0)
{
argSS << ", ";
argTypeSS << ", ";
typeSS << ", ";
}
auto argName = arg->get("name");
if (argName.empty())
{
argName = "arg" + std::to_string(i);
}
auto type = signature_to_type(arg->get("type"));
if (!async)
{
argSS << argName;
argTypeSS << "const " << type << "& " << argName;
}
else
{
argSS << "std::move(" << argName << ")";
argTypeSS << type << " " << argName;
}
typeSS << type;
}
return std::make_tuple(argSS.str(), argTypeSS.str(), typeSS.str());
}
/**
*
*/
std::string BaseGenerator::outArgsToType(const Nodes& args, bool bareList) const
{
std::ostringstream retTypeSS;
if (args.size() == 0)
{
if (bareList)
return "";
retTypeSS << "void";
}
else if (args.size() == 1)
{
const auto& arg = *args.begin();
retTypeSS << signature_to_type(arg->get("type"));
}
else if (args.size() >= 2)
{
if (!bareList)
retTypeSS << "std::tuple<";
bool firstArg = true;
for (const auto& arg : args)
{
if (firstArg) firstArg = false; else retTypeSS << ", ";
retTypeSS << signature_to_type(arg->get("type"));
}
if (!bareList)
retTypeSS << ">";
}
return retTypeSS.str();
}

View File

@@ -0,0 +1,103 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file BaseGenerator.h
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SDBUSCPP_TOOLS_BASE_GENERATOR_H
#define __SDBUSCPP_TOOLS_BASE_GENERATOR_H
// Own headers
#include "xml.h"
// STL
#include <string>
#include <tuple>
class BaseGenerator
{
public:
int transformXmlToFile(const sdbuscpp::xml::Document& doc, const char* filename) const;
protected:
enum class StubType
{
ADAPTOR,
PROXY
};
constexpr static const char *tab = " ";
virtual ~BaseGenerator() {}
/**
* Implementation of public function that is provided by inherited class
* @param doc
* @param filename
* @return
*/
virtual int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const = 0;
/**
* Write data to file
* @param filename Written file
* @param data Data to write
* @return 0 if ok
*/
int writeToFile(const char* filename, const std::string& data) const;
/**
* Crete header of file - include guard, includes
* @param filename
* @param stubType
* @return
*/
std::string createHeader(const char* filename, const StubType& stubType) const;
/**
* Namespaces according to the interface name
* @param ifaceName
* @return tuple: count of namespaces, string with code
*/
std::tuple<unsigned, std::string> generateNamespaces(const std::string& ifaceName) const;
/**
* Transform arguments into source code
* @param args
* @return tuple: argument names, argument types and names, argument types
*/
std::tuple<std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args, bool async = false) const;
/**
* Output arguments to return type
* @param args
* @return return type
*/
std::string outArgsToType(const sdbuscpp::xml::Nodes& args, bool bareList = false) const;
};
#endif //__SDBUSCPP_TOOLS_BASE_GENERATOR_H

View File

@@ -0,0 +1,267 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file ProxyGenerator.cpp
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "generator_utils.h"
#include "ProxyGenerator.h"
// STL
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <iterator>
using std::endl;
using sdbuscpp::xml::Document;
using sdbuscpp::xml::Node;
using sdbuscpp::xml::Nodes;
/**
* Generate proxy code - client glue
*/
int ProxyGenerator::transformXmlToFileImpl(const Document &doc, const char *filename) const
{
Node &root = *(doc.root);
Nodes interfaces = root["interface"];
std::ostringstream code;
code << createHeader(filename, StubType::PROXY);
for (const auto& interface : interfaces)
{
code << processInterface(*interface);
}
code << "#endif" << endl;
return writeToFile(filename, code.str());
}
std::string ProxyGenerator::processInterface(Node& interface) const
{
std::string ifaceName = interface.get("name");
std::cout << "Generating proxy code for interface " << ifaceName << endl;
unsigned int namespacesCount = 0;
std::string namespacesStr;
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
std::ostringstream body;
body << namespacesStr;
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
+ "_proxy";
body << "class " << className << endl
<< "{" << endl
<< "public:" << endl
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl
<< tab << className << "(sdbus::IProxy& proxy)" << endl
<< tab << tab << ": proxy_(proxy)" << endl;
Nodes methods = interface["method"];
Nodes signals = interface["signal"];
Nodes properties = interface["property"];
std::string registration, declaration;
std::tie(registration, declaration) = processSignals(signals);
body << tab << "{" << endl
<< registration
<< tab << "}" << endl << endl;
if (!declaration.empty())
body << declaration << endl;
std::string methodDefinitions, asyncDeclarations;
std::tie(methodDefinitions, asyncDeclarations) = processMethods(methods);
if (!asyncDeclarations.empty())
{
body << asyncDeclarations << endl;
}
if (!methodDefinitions.empty())
{
body << "public:" << endl << methodDefinitions;
}
std::string propertyDefinitions = processProperties(properties);
if (!propertyDefinitions.empty())
{
body << "public:" << endl << propertyDefinitions;
}
body << "private:" << endl
<< tab << "sdbus::IProxy& proxy_;" << endl
<< "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
return body.str();
}
std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes& methods) const
{
std::ostringstream definitionSS, asyncDeclarationSS;
for (const auto& method : methods)
{
auto name = method->get("name");
Nodes args = (*method)["arg"];
Nodes inArgs = args.select("direction" , "in");
Nodes outArgs = args.select("direction" , "out");
bool dontExpectReply{false};
bool async{false};
Nodes annotations = (*method)["annotation"];
for (const auto& annotation : annotations)
{
if (annotation->get("name") == "org.freedesktop.DBus.Method.NoReply" && annotation->get("value") == "true")
dontExpectReply = true;
else if (annotation->get("name") == "org.freedesktop.DBus.Method.Async"
&& (annotation->get("value") == "client" || annotation->get("value") == "clientserver"))
async = true;
}
if (dontExpectReply && outArgs.size() > 0)
{
std::cerr << "Function: " << name << ": ";
std::cerr << "Option 'org.freedesktop.DBus.Method.NoReply' not allowed for methods with 'out' variables! Option ignored..." << std::endl;
dontExpectReply = false;
}
auto retType = outArgsToType(outArgs);
std::string inArgStr, inArgTypeStr;
std::tie(inArgStr, inArgTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
std::string outArgStr, outArgTypeStr;
std::tie(outArgStr, outArgTypeStr, std::ignore) = argsToNamesAndTypes(outArgs);
definitionSS << tab << (async ? "void" : retType) << " " << name << "(" << inArgTypeStr << ")" << endl
<< tab << "{" << endl;
if (outArgs.size() > 0 && !async)
{
definitionSS << tab << tab << retType << " result;" << endl;
}
definitionSS << tab << tab << "proxy_.callMethod" << (async ? "Async" : "") << "(\"" << name << "\")"
".onInterface(interfaceName)";
if (inArgs.size() > 0)
{
definitionSS << ".withArguments(" << inArgStr << ")";
}
if (async && !dontExpectReply)
{
auto nameBigFirst = name;
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
definitionSS << ".uponReplyInvoke([this](const sdbus::Error* error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "error); })";
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "Reply("
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "const sdbus::Error* error) = 0;" << endl;
}
else if (outArgs.size() > 0)
{
definitionSS << ".storeResultsTo(result);" << endl
<< tab << tab << "return result";
}
else if (dontExpectReply)
{
definitionSS << ".dontExpectReply()";
}
definitionSS << ";" << endl << tab << "}" << endl << endl;
}
return std::make_tuple(definitionSS.str(), asyncDeclarationSS.str());
}
std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes& signals) const
{
std::ostringstream registrationSS, declarationSS;
for (const auto& signal : signals)
{
auto name = signal->get("name");
Nodes args = (*signal)["arg"];
auto nameBigFirst = name;
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(args);
registrationSS << tab << tab << "proxy_"
".uponSignal(\"" << name << "\")"
".onInterface(interfaceName)"
".call([this](" << argTypeStr << ")"
"{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl;
declarationSS << tab << "virtual void on" << nameBigFirst << "(" << argTypeStr << ") = 0;" << endl;
}
return std::make_tuple(registrationSS.str(), declarationSS.str());
}
std::string ProxyGenerator::processProperties(const Nodes& properties) const
{
std::ostringstream propertySS;
for (const auto& property : properties)
{
auto propertyName = property->get("name");
auto propertyAccess = property->get("access");
auto propertySignature = property->get("type");
auto propertyType = signature_to_type(propertySignature);
auto propertyArg = std::string("value");
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
if (propertyAccess == "read" || propertyAccess == "readwrite")
{
propertySS << tab << propertyType << " " << propertyName << "()" << endl
<< tab << "{" << endl;
propertySS << tab << tab << "return proxy_.getProperty(\"" << propertyName << "\")"
".onInterface(interfaceName)";
propertySS << ";" << endl << tab << "}" << endl << endl;
}
if (propertyAccess == "readwrite" || propertyAccess == "write")
{
propertySS << tab << "void " << propertyName << "(" << propertyTypeArg << ")" << endl
<< tab << "{" << endl;
propertySS << tab << tab << "proxy_.setProperty(\"" << propertyName << "\")"
".onInterface(interfaceName)"
".toValue(" << propertyArg << ")";
propertySS << ";" << endl << tab << "}" << endl << endl;
}
}
return propertySS.str();
}

View File

@@ -0,0 +1,81 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file ProxyGenerator.h
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SDBUSCPP_TOOLS_PROXY_GENERATOR_H
#define __SDBUSCPP_TOOLS_PROXY_GENERATOR_H
// Own headers
#include "xml.h"
#include "BaseGenerator.h"
// STL
#include <tuple>
class ProxyGenerator : public BaseGenerator
{
protected:
/**
* Transform xml to proxy code
* @param doc
* @param filename
* @return 0 if ok
*/
int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const override;
private:
/**
* Generate source code for interface
* @param interface
* @return source code
*/
std::string processInterface(sdbuscpp::xml::Node& interface) const;
/**
* Generate method calls
* @param methods
* @return tuple: definition of methods, declaration of virtual async reply handlers
*/
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
/**
* Generate code for handling signals
* @param signals
* @return tuple: registration, declaration of virtual methods
*/
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
/**
* Generate calls for properties
* @param properties
* @return source code
*/
std::string processProperties(const sdbuscpp::xml::Nodes& properties) const;
};
#endif //__SDBUSCPP_TOOLS_PROXY_GENERATOR_H

View File

@@ -0,0 +1,143 @@
/**
* Inspired by: http://dbus-cplusplus.sourceforge.net/
*/
#include <iostream>
#include <cstdlib>
#include <map>
#include "generator_utils.h"
std::string underscorize(const std::string& str)
{
std::string res = str;
for (unsigned int i = 0; i < res.length(); ++i)
{
if (!isalpha(res[i]) && !isdigit(res[i]))
{
res[i] = '_';
}
}
return res;
}
std::string stub_name(const std::string& name)
{
return "_" + underscorize(name) + "_stub";
}
const char *atomic_type_to_string(char t)
{
static std::map<char, const char*> atos
{
{ 'y', "uint8_t" },
{ 'b', "bool" },
{ 'n', "int16_t" },
{ 'q', "uint16_t" },
{ 'i', "int32_t" },
{ 'u', "uint32_t" },
{ 'x', "int64_t" },
{ 't', "uint64_t" },
{ 'd', "double" },
{ 's', "std::string" },
{ 'o', "sdbus::ObjectPath" },
{ 'g', "sdbus::Signature" },
{ 'v', "sdbus::Variant" },
{ '\0', "" }
};
if (atos.count(t))
{
return atos[t];
}
return nullptr;
}
static void _parse_signature(const std::string &signature, std::string &type, unsigned int &i, bool only_once = false)
{
for (; i < signature.length(); ++i)
{
switch (signature[i])
{
case 'a':
{
switch (signature[++i])
{
case '{':
{
type += "std::map<";
++i;
_parse_signature(signature, type, i);
type += ">";
break;
}
case '(':
{
type += "std::vector<sdbus::Struct<";
++i;
_parse_signature(signature, type, i);
type += ">>";
break;
}
default:
{
type += "std::vector<";
_parse_signature(signature, type, i, true);
type += ">";
break;
}
}
break;
}
case '(':
{
type += "sdbus::Struct<";
++i;
_parse_signature(signature, type, i);
type += ">";
break;
}
case ')':
case '}':
{
return;
}
default:
{
const char *atom = atomic_type_to_string(signature[i]);
if (!atom)
{
std::cerr << "Invalid signature: " << signature << std::endl;
exit(-1);
}
type += atom;
break;
}
}
if (only_once)
return;
if (i + 1 < signature.length() && signature[i + 1] != ')' && signature[i + 1] != '}')
{
type += ", ";
}
}
}
std::string signature_to_type(const std::string& signature)
{
std::string type;
unsigned int i = 0;
_parse_signature(signature, type, i);
return type;
}

View File

@@ -0,0 +1,22 @@
/**
* Inspired by: http://dbus-cplusplus.sourceforge.net/
*/
#ifndef __SDBUSCPP_TOOLS_GENERATOR_UTILS_H
#define __SDBUSCPP_TOOLS_GENERATOR_UTILS_H
#include <string>
#include <sstream>
#include <iomanip>
const char *atomic_type_to_string(char t);
std::string stub_name(const std::string& name);
std::string signature_to_type(const std::string& signature);
std::string underscorize(const std::string& str);
constexpr const char* getHeaderComment() noexcept { return "\n/*\n * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!\n */\n\n"; }
#endif //__SDBUSCPP_TOOLS_GENERATOR_UTILS_H

View File

@@ -0,0 +1,309 @@
/**
* Inspired by: http://dbus-cplusplus.sourceforge.net/
*/
#include "xml.h"
#include <expat.h>
using namespace sdbuscpp::xml;
std::istream &operator >> (std::istream& in, Document& doc)
{
std::stringbuf xmlbuf;
in.get(xmlbuf, '\0');
doc.from_xml(xmlbuf.str());
return in;
}
std::ostream &operator << (std::ostream& out, const Document& doc)
{
return out << doc.to_xml();
}
Error::Error(const char *error, int line, int column)
{
std::ostringstream estream;
estream << "line " << line << ", column " << column << ": " << error;
m_error = estream.str();
}
Node::Node(const char *n, const char **a)
: name(n)
{
if (a)
{
for (int i = 0; a[i]; i += 2)
{
m_attrs[a[i]] = a[i + 1];
}
}
}
Nodes Nodes::operator[](const std::string& key) const
{
Nodes result;
for (auto it = begin(), endIt = end(); it != endIt; ++it)
{
Nodes part = (**it)[key];
result.insert(result.end(), part.begin(), part.end());
}
return result;
}
Nodes Nodes::select(const std::string& attr, const std::string& value) const
{
Nodes result;
for (auto it = begin(), itEnd = end(); it != itEnd; ++it)
{
if ((*it)->get(attr) == value)
{
result.insert(result.end(), *it);
}
}
return result;
}
Nodes Node::operator[](const std::string& key)
{
Nodes result;
if (key.length() == 0)
{
return result;
}
for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
{
if (it->name == key)
{
result.push_back(&(*it));
}
}
return result;
}
std::string Node::get(const std::string& attribute) const
{
const auto it = m_attrs.find(attribute);
if (it != m_attrs.end())
{
return it->second;
}
return "";
}
void Node::set(const std::string& attribute, std::string value)
{
if (value.length())
{
m_attrs[attribute] = value;
}
else
{
m_attrs.erase(value);
}
}
std::string Node::to_xml() const
{
std::string xml;
int depth = 0;
_raw_xml(xml, depth);
return xml;
}
void Node::_raw_xml(std::string& xml, int& depth) const
{
xml.append(depth * 2, ' ');
xml.append("<" + name);
for (auto it = m_attrs.begin(), itEnd = m_attrs.end(); it != itEnd; ++it)
{
xml.append(" " + it->first + "=\"" + it->second + "\"");
}
if (cdata.length() == 0 && children.size() == 0)
{
xml.append("/>\n");
}
else
{
xml.append(">");
if (cdata.length())
{
xml.append(cdata);
}
if (children.size())
{
xml.append("\n");
++depth;
for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
{
it->_raw_xml(xml, depth);
}
--depth;
xml.append(depth * 2, ' ');
}
xml.append("</" + name + ">\n");
}
}
Document::Document() :
root(nullptr),
m_depth(0)
{
}
Document::Document(const std::string &xml) :
root(nullptr),
m_depth(0)
{
from_xml(xml);
}
Document::~Document()
{
delete root;
}
struct Document::Expat
{
static void start_doctype_decl_handler(
void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset
);
static void end_doctype_decl_handler(void *data);
static void start_element_handler(void *data, const XML_Char *name, const XML_Char **atts);
static void character_data_handler(void *data, const XML_Char *chars, int len);
static void end_element_handler(void *data, const XML_Char *name);
};
void Document::from_xml(const std::string& xml)
{
m_depth = 0;
if (root)
{
delete root;
}
root = nullptr;
XML_Parser parser = XML_ParserCreate("UTF-8");
XML_SetUserData(parser, this);
XML_SetDoctypeDeclHandler(
parser,
Document::Expat::start_doctype_decl_handler,
Document::Expat::end_doctype_decl_handler
);
XML_SetElementHandler(
parser,
Document::Expat::start_element_handler,
Document::Expat::end_element_handler
);
XML_SetCharacterDataHandler(
parser,
Document::Expat::character_data_handler
);
XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true);
if (status == XML_STATUS_ERROR)
{
const char* error = XML_ErrorString(XML_GetErrorCode(parser));
int line = XML_GetCurrentLineNumber(parser);
int column = XML_GetCurrentColumnNumber(parser);
XML_ParserFree(parser);
throw Error(error, line, column);
}
else
{
XML_ParserFree(parser);
}
}
std::string Document::to_xml() const
{
return root->to_xml();
}
void Document::Expat::start_doctype_decl_handler(
void* /*data*/,
const XML_Char* /*name*/,
const XML_Char* /*sysid*/,
const XML_Char */*pubid*/,
int /*has_internal_subset*/)
{
}
void Document::Expat::end_doctype_decl_handler(void* /*data*/)
{
}
void Document::Expat::start_element_handler(void* data, const XML_Char* name, const XML_Char** atts)
{
Document* doc = static_cast<Document*>(data);
if (!doc->root)
{
doc->root = new Node(name, atts);
}
else
{
Node::Children* cld = &(doc->root->children);
for (int i = 1; i < doc->m_depth; ++i)
{
cld = &(cld->back().children);
}
cld->push_back(Node(name, atts));
}
doc->m_depth++;
}
void Document::Expat::character_data_handler(void* data, const XML_Char* chars, int len)
{
Document* doc = static_cast<Document*>(data);
Node* nod = doc->root;
for (int i = 1; i < doc->m_depth; ++i)
{
nod = &(nod->children.back());
}
int x = 0, y = len - 1;
while (isspace(chars[y]) && y > 0) --y;
while (isspace(chars[x]) && x < y) ++x;
nod->cdata = std::string(chars, x, y + 1);
}
void Document::Expat::end_element_handler(void* data, const XML_Char* /*name*/)
{
Document* doc = static_cast<Document*>(data);
doc->m_depth--;
}

113
tools/xml2cpp-codegen/xml.h Normal file
View File

@@ -0,0 +1,113 @@
/**
* Inspired by: http://dbus-cplusplus.sourceforge.net/
*/
#ifndef __SDBUSCPP_TOOLS_XML_H
#define __SDBUSCPP_TOOLS_XML_H
#include <exception>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <iostream>
#include <sstream>
namespace sdbuscpp
{
namespace xml
{
class Error : public std::exception
{
public:
Error(const char *error, int line, int column);
~Error() {}
const char *what() const noexcept { return m_error.c_str(); }
private:
std::string m_error;
};
class Node;
class Nodes : public std::vector<Node *>
{
public:
Nodes operator[](const std::string &key) const;
Nodes select(const std::string &attr, const std::string &value) const;
};
class Node
{
public:
using Attributes = std::map<std::string, std::string>;
using Children = std::vector<Node>;
std::string name;
std::string cdata;
Children children;
Node(std::string n, Attributes a) :
name(n),
m_attrs(a)
{}
Node(const char* n, const char** a = nullptr);
Nodes operator[](const std::string& key);
std::string get(const std::string& attribute) const;
void set(const std::string &attribute, std::string value);
std::string to_xml() const;
Node& add(Node child)
{
children.push_back(child);
return children.back();
}
private:
void _raw_xml(std::string& xml, int& depth) const;
Attributes m_attrs;
};
class Document
{
public:
struct Expat;
Node* root;
Document();
~Document();
Document(const std::string& xml);
void from_xml(const std::string& xml);
std::string to_xml() const;
private:
int m_depth;
};
} // namespace xml
} // namespace sdbuscpp
std::istream &operator >> (std::istream &, sdbuscpp::xml::Document &);
std::ostream &operator << (std::ostream &, sdbuscpp::xml::Document &);
#endif//__SDBUSCPP_TOOLS_XML_H

View File

@@ -0,0 +1,202 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file xml2cpp.cpp
*
* Created on: Feb 1, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "xml.h"
#include "AdaptorGenerator.h"
#include "ProxyGenerator.h"
// STL
#include <cstdlib>
#include <cstring>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
using std::endl;
using namespace sdbuscpp;
void usage(std::ostream& output, const char* programName)
{
output << "Usage: " << programName << " [OPTION]... [FILE]" << endl <<
"Creates C++ stubs for DBus API for adaptor and/or client" << endl <<
endl <<
"Available options:" << endl <<
" --proxy=FILE Generate header file FILE with proxy class (client)" << endl <<
" --adaptor=FILE Generate header file FILE with stub class (server)" << endl <<
" -h, --help " << endl <<
" --verbose Explain what is being done" << endl <<
endl <<
"The stub generator takes an XML file describing DBus interface and creates" << endl <<
"C++ header files to be used by C++ code wanting to cumminicate through that" << endl <<
"interface. Clients of the interface (those making the calls) need header" << endl <<
"created with the --proxy option as this header forwards the calls via DBus" << endl <<
"to provider of the service and the returns the result to the caller. Server" << endl <<
"implementing the service should derive from interface classes in header" << endl <<
"generated for --adaptor option and implement their methods." << endl <<
endl <<
"When FILE is not specified, standard input is read. Exit status is 0 when" << endl <<
"no error was encountered and all requested headers were sucessfully generated." << endl <<
"Otherwise 1 is returned." << endl;
}
int main(int argc, char **argv)
{
const char* programName = argv[0];
argv++;
argc--;
const char* proxy = nullptr;
const char* adaptor = nullptr;
const char* xmlFile = nullptr;
bool verbose = false;
while (argc > 0)
{
if (!strncmp(*argv, "--proxy=", 8))
{
if (proxy != nullptr)
{
std::cerr << "Multiple occurrencies of --proxy is not allowed" << endl;
usage(std::cerr, programName);
return 1;
}
proxy = *argv + 8;
}
else if (!strncmp(*argv, "--adaptor=", 10) || !strncmp(*argv, "--adapter=", 10))
{
if (adaptor != nullptr)
{
std::cerr << "Multiple occurrencies of --adaptor is not allowed" << endl;
usage(std::cerr, programName);
return 1;
}
adaptor = *argv + 10;
}
else if (!strcmp(*argv, "--help") || !strcmp(*argv, "-h"))
{
usage(std::cout, programName);
return 0;
}
else if (!strcmp(*argv, "--verbose"))
{
verbose = true;
}
else if (**argv == '-')
{
std::cerr << "Unknown option " << *argv << endl;
usage(std::cerr, programName);
return 1;
}
else
{
if (xmlFile != nullptr)
{
std::cerr << "More than one input file specified: " << *argv << endl;
usage(std::cerr, programName);
return 1;
}
xmlFile = *argv;
}
argc--;
argv++;
}
if (!proxy && !adaptor)
{
std::cerr << "Either --proxy or --adapter need to be specified" << endl;
usage(std::cerr, programName);
return 1;
}
xml::Document doc;
try
{
if (xmlFile != nullptr)
{
if (verbose)
{
std::cerr << "Reading DBus interface from " << xmlFile << endl;
}
std::ifstream input(xmlFile);
if (input.bad())
{
std::cerr << "Unable to open file " << xmlFile << endl;
return 1;
}
input >> doc;
}
else
{
if (verbose)
{
std::cerr << "Reading DBus interface from standard input" << endl;
}
std::cin >> doc;
}
}
catch (const xml::Error& e)
{
std::cerr << "Parsing error: " << e.what() << endl;
return 1;
}
if (!doc.root)
{
std::cerr << "Empty document" << endl;
return 1;
}
if (proxy)
{
if (verbose)
{
std::cerr << "Generating proxy header " << proxy << endl;
}
ProxyGenerator pg;
pg.transformXmlToFile(doc, proxy);
}
if (adaptor)
{
if (verbose)
{
std::cerr << "Generating adaptor header " << adaptor << endl;
}
AdaptorGenerator ag;
ag.transformXmlToFile(doc, adaptor);
}
return 0;
}