mirror of https://github.com/nymea/nymea.git
360 lines
16 KiB
C++
360 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 "version.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;
|
|
QByteArray data = jsonFile.readAll();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
|
jsonFile.close();
|
|
|
|
if (error.error != QJsonParseError::NoError) {
|
|
int errorOffset = error.offset;
|
|
int newLineIndex = data.indexOf("\n");
|
|
int lineIndex = 1;
|
|
while (newLineIndex > 0 && errorOffset > newLineIndex) {
|
|
data.remove(0, newLineIndex + 2);
|
|
errorOffset -= (newLineIndex + 2);
|
|
newLineIndex = data.indexOf("\n");
|
|
lineIndex++;
|
|
}
|
|
if (newLineIndex >= 0) {
|
|
data = data.left(newLineIndex);
|
|
}
|
|
QString spacer;
|
|
for (int i = 0; i < errorOffset; i++) {
|
|
spacer += ' ';
|
|
}
|
|
QDebug dbg = qWarning().nospace().noquote();
|
|
dbg << inputFile << ":" << lineIndex << ":" << errorOffset + 2 << ": error: JSON parsing failed: " << error.errorString() << ": " << data.trimmed() << endl;
|
|
dbg << data << endl;
|
|
dbg << spacer << "^";
|
|
return 1;
|
|
}
|
|
QJsonObject jsonObject = QJsonObject::fromVariantMap(jsonDoc.toVariant().toMap());
|
|
|
|
PluginMetadata metadata(jsonObject);
|
|
if (!metadata.isValid()) {
|
|
foreach (const QString &error, metadata.validationErrors()) {
|
|
QDebug dbg = qWarning().noquote().nospace();
|
|
dbg << inputFile << ": error: Plugin JSON failed validation: " << error;
|
|
}
|
|
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());
|
|
}
|
|
}
|