mirror of https://github.com/nymea/nymea.git
337 lines
16 KiB
C++
337 lines
16 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* *
|
|
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
|
|
* *
|
|
* This file is part of nymea. *
|
|
* *
|
|
* This library 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. *
|
|
* *
|
|
* This library 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 this library; If not, see *
|
|
* <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "plugininfocompiler.h"
|
|
|
|
#include <QJsonObject>
|
|
#include <QFile>
|
|
#include <QJsonParseError>
|
|
#include <QDataStream>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
|
|
PluginInfoCompiler::PluginInfoCompiler()
|
|
{
|
|
|
|
}
|
|
|
|
int PluginInfoCompiler::compile(const QString &inputFile, const QString &outputFile, const QString outputFileExtern, const QString &translationsPath)
|
|
{
|
|
// First, process the input json...
|
|
QFile jsonFile(inputFile);
|
|
if (!jsonFile.open(QFile::ReadOnly)) {
|
|
qWarning() << "Error opening input JSON file for reading. Aborting.";
|
|
return 1;
|
|
}
|
|
QJsonParseError error;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonFile.readAll(), &error);
|
|
jsonFile.close();
|
|
|
|
if (error.error != QJsonParseError::NoError) {
|
|
qWarning() << "Error parsing input JSON. Aborting.";
|
|
qWarning() << "Parser details:" << error.errorString();
|
|
return 1;
|
|
}
|
|
QJsonObject jsonObject = QJsonObject::fromVariantMap(jsonDoc.toVariant().toMap());
|
|
|
|
PluginMetadata metadata(jsonObject);
|
|
if (!metadata.isValid()) {
|
|
qWarning() << "Plugin JSON failed validation. Aborting.";
|
|
return 2;
|
|
}
|
|
|
|
// OK. Json parsed fine. Let's open files for writing
|
|
|
|
if (!outputFile.isEmpty()) {
|
|
if (outputFile == "-") {
|
|
if (!m_outputFile.open(stdout, QFile::WriteOnly | QFile::Text)) {
|
|
qWarning() << "Error opening stdout for writing. Aborting.";
|
|
return 1;
|
|
}
|
|
} else {
|
|
m_outputFile.setFileName(outputFile);
|
|
if (!m_outputFile.open(QFile::WriteOnly | QFile::Text)) {
|
|
qWarning() << "Error opening output file for writing. Aborting.";
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!outputFileExtern.isEmpty()) {
|
|
if (outputFileExtern == "-") {
|
|
if (!m_outputFileExtern.open(stdout, QFile::WriteOnly | QFile::Text)) {
|
|
qWarning() << "Error opening stdout for writing. Aborting.";
|
|
return 1;
|
|
}
|
|
} else {
|
|
m_outputFileExtern.setFileName(outputFileExtern);
|
|
if (!m_outputFileExtern.open(QFile::WriteOnly | QFile::Text)) {
|
|
qWarning() << "Error opening output file for writing. Aborting.";
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!translationsPath.isEmpty()) {
|
|
QDir dir;
|
|
if (!dir.exists(translationsPath)) {
|
|
if(!dir.mkpath(translationsPath)) {
|
|
qWarning() << "Error creating translation file directory" << translationsPath;
|
|
return 1;
|
|
}
|
|
qDebug() << "Created translations dir";
|
|
}
|
|
|
|
QFile f(translationsPath + '/' + metadata.pluginId().toString().remove(QRegExp("[{}]")) + "-en_US.ts");
|
|
QByteArray translationsStub = "<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE TS><TS version=\"2.1\"></TS>";
|
|
if (!f.exists()) {
|
|
if (!f.open(QFile::WriteOnly | QFile::Text)) {
|
|
qWarning() << "Error creating translation file";
|
|
return 1;
|
|
}
|
|
if (f.write(translationsStub) == -1) {
|
|
qWarning() << "Error writing translation file";
|
|
return 1;
|
|
}
|
|
f.close();
|
|
qDebug() << "Created translations stub";
|
|
}
|
|
}
|
|
|
|
|
|
// Files are open. Ready to write content.
|
|
|
|
QString header;
|
|
header.append("/* This file is generated by the nymea build system. Any changes to this file will *\n");
|
|
header.append(" * be lost. If you want to change this file, edit the plugin's json file. */\n");
|
|
write(header);
|
|
writeExtern(header);
|
|
|
|
write("#ifndef PLUGININFO_H");
|
|
write("#define PLUGININFO_H");
|
|
write();
|
|
writeExtern("#ifndef EXTERNPLUGININFO_H");
|
|
writeExtern("#define EXTERNPLUGININFO_H");
|
|
writeExtern();
|
|
|
|
write("#include \"typeutils.h\"");
|
|
write();
|
|
writeExtern("#include \"typeutils.h\"");
|
|
writeExtern();
|
|
|
|
write("#include <QLoggingCategory>");
|
|
writeExtern("#include <QLoggingCategory>");
|
|
write("#include <QObject>");
|
|
write();
|
|
writeExtern();
|
|
|
|
// Include our API version in plugininfo.h so we can know against which library this plugin was built.
|
|
write(QString("extern \"C\" const QString libnymea_api_version() { return QString(\"%1\");}").arg(LIBNYMEA_API_VERSION));
|
|
write();
|
|
|
|
// Declare a logging category for this plugin
|
|
QString debugCategoryName = metadata.pluginName()[0].toUpper() + metadata.pluginName().right(metadata.pluginName().length() - 1);
|
|
QString debugCategoryDeclaration = QString("Q_DECLARE_LOGGING_CATEGORY(dc%1)").arg(debugCategoryName);
|
|
write(debugCategoryDeclaration);
|
|
write(QString("Q_LOGGING_CATEGORY(dc%1, \"%1\")").arg(debugCategoryName));
|
|
write();
|
|
writeExtern(debugCategoryDeclaration);
|
|
writeExtern();
|
|
|
|
// Write down all the IDs
|
|
writePlugin(metadata);
|
|
write();
|
|
writeExtern();
|
|
|
|
// And the translations
|
|
write(QString("const QString translations[] {"));
|
|
for (auto i = m_translationStrings.begin(); i != m_translationStrings.end();) {
|
|
write(QString(" //: %1").arg(i.value()));
|
|
QString line = QString(" QT_TRANSLATE_NOOP(\"%1\", \"%2\")").arg(metadata.pluginName(), i.key());
|
|
i++;
|
|
if (i != m_translationStrings.end()) {
|
|
line.append(",\n");
|
|
}
|
|
write(line);
|
|
}
|
|
write("};");
|
|
write();
|
|
|
|
write("#endif // PLUGININFO_H");
|
|
writeExtern("#endif // EXTERNPLUGININFO_H");
|
|
|
|
m_outputFile.close();
|
|
m_outputFileExtern.close();
|
|
return 0;
|
|
}
|
|
|
|
void PluginInfoCompiler::writePlugin(const PluginMetadata &metadata)
|
|
{
|
|
write(QString("PluginId pluginId = PluginId(\"%1\");").arg(metadata.pluginId().toString()));
|
|
m_translationStrings.insert(metadata.pluginDisplayName(), QString("The name of the plugin %1 (%2)").arg(metadata.pluginName()).arg(metadata.pluginId().toString()));
|
|
writeExtern(QString("extern %1 %2;").arg("PluginId").arg("pluginId"));
|
|
|
|
writeParams(metadata.pluginSettings(), metadata.pluginName()[0].toLower() + metadata.pluginName().right(metadata.pluginName().length() - 1), "", "plugin");
|
|
|
|
foreach (const Vendor &vendor, metadata.vendors()) {
|
|
writeVendor(vendor);
|
|
}
|
|
|
|
foreach (const DeviceClass &deviceClass, metadata.deviceClasses()) {
|
|
writeDeviceClass(deviceClass);
|
|
}
|
|
|
|
}
|
|
|
|
void PluginInfoCompiler::writeParams(const ParamTypes ¶mTypes, const QString &deviceClassName, const QString &typeClass, const QString &typeName)
|
|
{
|
|
foreach (const ParamType ¶mType, paramTypes) {
|
|
QString variableName = QString("%1ParamTypeId").arg(deviceClassName + typeName[0].toUpper() + typeName.right(typeName.length()-1) + typeClass + paramType.name()[0].toUpper() + paramType.name().right(paramType.name().length() -1 ));
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for ParamTypeId " << paramType.id() << ". Skipping entry.";
|
|
continue;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
|
|
write(QString("ParamTypeId %1 = ParamTypeId(\"%2\");").arg(variableName).arg(paramType.id().toString()));
|
|
m_translationStrings.insert(paramType.displayName(), QString("The name of the ParamType (DeviceClass: %1, %2Type: %3, ID: %4)").arg(deviceClassName).arg(typeClass).arg(typeName).arg(paramType.id().toString()));
|
|
writeExtern(QString("extern ParamTypeId %1;").arg(variableName));
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeVendor(const Vendor &vendor)
|
|
{
|
|
QString variableName = QString("%1VendorId").arg(vendor.name());
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for Vendor " << vendor.id() << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
|
|
write(QString("VendorId %1 = VendorId(\"%2\");").arg(variableName).arg(vendor.id().toString()));
|
|
m_translationStrings.insert(vendor.displayName(), QString("The name of the vendor (%1)").arg(vendor.id().toString()));
|
|
writeExtern(QString("extern VendorId %1;").arg(variableName));
|
|
}
|
|
|
|
void PluginInfoCompiler::writeDeviceClass(const DeviceClass &deviceClass)
|
|
{
|
|
QString variableName = QString("%1DeviceClassId").arg(deviceClass.name());
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for DeviceClass " << deviceClass.id() << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
|
|
write(QString("DeviceClassId %1 = DeviceClassId(\"%2\");").arg(variableName).arg(deviceClass.id().toString()));
|
|
m_translationStrings.insert(deviceClass.displayName(), QString("The name of the DeviceClass (%1)").arg(deviceClass.id().toString()));
|
|
writeExtern(QString("extern DeviceClassId %1;").arg(variableName));
|
|
|
|
writeParams(deviceClass.paramTypes(), deviceClass.name(), "", "device");
|
|
writeParams(deviceClass.settingsTypes(), deviceClass.name(), "", "settings");
|
|
writeParams(deviceClass.discoveryParamTypes(), deviceClass.name(), "", "discovery");
|
|
|
|
writeStateTypes(deviceClass.stateTypes(), deviceClass.name());
|
|
writeEventTypes(deviceClass.eventTypes(), deviceClass.name());
|
|
writeActionTypes(deviceClass.actionTypes(), deviceClass.name());
|
|
writeBrowserItemActionTypes(deviceClass.browserItemActionTypes(), deviceClass.name());
|
|
}
|
|
|
|
void PluginInfoCompiler::writeStateTypes(const StateTypes &stateTypes, const QString &deviceClassName)
|
|
{
|
|
foreach (const StateType &stateType, stateTypes) {
|
|
QString variableName = QString("%1%2StateTypeId").arg(deviceClassName, stateType.name()[0].toUpper() + stateType.name().right(stateType.name().length() - 1));
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for StateType " << stateType.name() << " in DeviceClass " << deviceClassName << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
write(QString("StateTypeId %1 = StateTypeId(\"%2\");").arg(variableName).arg(stateType.id().toString()));
|
|
m_translationStrings.insert(stateType.displayName(), QString("The name of the StateType (%1) of DeviceClass %2").arg(stateType.id().toString()).arg(deviceClassName));
|
|
writeExtern(QString("extern StateTypeId %1;").arg(variableName));
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeEventTypes(const EventTypes &eventTypes, const QString &deviceClassName)
|
|
{
|
|
foreach (const EventType &eventType, eventTypes) {
|
|
QString variableName = QString("%1%2EventTypeId").arg(deviceClassName, eventType.name()[0].toUpper() + eventType.name().right(eventType.name().length() - 1));
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for EventType " << eventType.name() << " in DeviceClass " << deviceClassName << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
write(QString("EventTypeId %1 = EventTypeId(\"%2\");").arg(variableName).arg(eventType.id().toString()));
|
|
m_translationStrings.insert(eventType.displayName(), QString("The name of the EventType (%1) of DeviceClass %2").arg(eventType.id().toString()).arg(deviceClassName));
|
|
writeExtern(QString("extern EventTypeId %1;").arg(variableName));
|
|
|
|
writeParams(eventType.paramTypes(), deviceClassName, "Event", eventType.name());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeActionTypes(const ActionTypes &actionTypes, const QString &deviceClassName)
|
|
{
|
|
foreach (const ActionType &actionType, actionTypes) {
|
|
QString variableName = QString("%1%2ActionTypeId").arg(deviceClassName, actionType.name()[0].toUpper() + actionType.name().right(actionType.name().length() - 1));
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for ActionType " << actionType.name() << " in DeviceClass " << deviceClassName << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
write(QString("ActionTypeId %1 = ActionTypeId(\"%2\");").arg(variableName).arg(actionType.id().toString()));
|
|
m_translationStrings.insert(actionType.displayName(), QString("The name of the ActionType (%1) of DeviceClass %2").arg(actionType.id().toString()).arg(deviceClassName));
|
|
writeExtern(QString("extern ActionTypeId %1;").arg(variableName));
|
|
|
|
writeParams(actionType.paramTypes(), deviceClassName, "Action", actionType.name());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &deviceClassName)
|
|
{
|
|
foreach (const ActionType &actionType, actionTypes) {
|
|
QString variableName = QString("%1%2BrowserItemActionTypeId").arg(deviceClassName, actionType.name()[0].toUpper() + actionType.name().right(actionType.name().length() - 1));
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for Browser Item ActionType " << actionType.name() << " in DeviceClass " << deviceClassName << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
write(QString("ActionTypeId %1 = ActionTypeId(\"%2\");").arg(variableName).arg(actionType.id().toString()));
|
|
m_translationStrings.insert(actionType.displayName(), QString("The name of the Browser Item ActionType (%1) of DeviceClass %2").arg(actionType.id().toString()).arg(deviceClassName));
|
|
writeExtern(QString("extern ActionTypeId %1;").arg(variableName));
|
|
|
|
writeParams(actionType.paramTypes(), deviceClassName, "BrowserItemAction", actionType.name());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::write(const QString &line)
|
|
{
|
|
if (m_outputFile.isOpen()) {
|
|
m_outputFile.write(QString("%1\n").arg(line).toUtf8());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeExtern(const QString &line)
|
|
{
|
|
if (m_outputFileExtern.isOpen()) {
|
|
m_outputFileExtern.write(QString("%1\n").arg(line).toUtf8());
|
|
}
|
|
}
|