mirror of https://github.com/nymea/nymea.git
373 lines
17 KiB
C++
373 lines
17 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright 2013 - 2020, nymea GmbH
|
|
* Contact: contact@nymea.io
|
|
*
|
|
* This file is part of nymea.
|
|
* This project including source code and documentation is protected by
|
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
* reproduction, publication, editing and translation, are reserved. The use of
|
|
* this project is subject to the terms of a license agreement to be concluded
|
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
* under https://nymea.io/license
|
|
*
|
|
* GNU General Public License Usage
|
|
* Alternatively, this project may be redistributed and/or modified under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, GNU version 3. This project 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 General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this project. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* For any further details and any questions please contact us under
|
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
* https://nymea.io/license/faq
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#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, bool strictMode)
|
|
{
|
|
// 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, false, strictMode);
|
|
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");
|
|
header.append(" * *\n");
|
|
header.append(" * NOTE: This file can be included only once per plugin. If you need to access *\n");
|
|
header.append(" * definitions from this file in multiple source files, use *\n");
|
|
header.append(" * #include extern-plugininfo.h *\n");
|
|
header.append(" * instead and re-run qmake. */\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 ThingClass &thingClass, metadata.thingClasses()) {
|
|
writeThingClass(thingClass);
|
|
}
|
|
|
|
}
|
|
|
|
void PluginInfoCompiler::writeParams(const ParamTypes ¶mTypes, const QString &thingClassName, const QString &typeClass, const QString &typeName)
|
|
{
|
|
foreach (const ParamType ¶mType, paramTypes) {
|
|
QString variableName = QString("%1ParamTypeId").arg(thingClassName + 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 (ThingClass: %1, %2Type: %3, ID: %4)").arg(thingClassName).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::writeThingClass(const ThingClass &thingClass)
|
|
{
|
|
QString variableName = QString("%1ThingClassId").arg(thingClass.name());
|
|
if (m_variableNames.contains(variableName)) {
|
|
qWarning().nospace() << "Error: Duplicate name " << variableName << " for ThingClass " << thingClass.id() << ". Skipping entry.";
|
|
return;
|
|
}
|
|
m_variableNames.append(variableName);
|
|
|
|
write(QString("ThingClassId %1 = ThingClassId(\"%2\");").arg(variableName).arg(thingClass.id().toString()));
|
|
m_translationStrings.insert(thingClass.displayName(), QString("The name of the ThingClass (%1)").arg(thingClass.id().toString()));
|
|
writeExtern(QString("extern ThingClassId %1;").arg(variableName));
|
|
|
|
writeParams(thingClass.paramTypes(), thingClass.name(), "", "thing");
|
|
writeParams(thingClass.settingsTypes(), thingClass.name(), "", "settings");
|
|
writeParams(thingClass.discoveryParamTypes(), thingClass.name(), "", "discovery");
|
|
|
|
writeStateTypes(thingClass.stateTypes(), thingClass.name());
|
|
writeEventTypes(thingClass.eventTypes(), thingClass.name());
|
|
writeActionTypes(thingClass.actionTypes(), thingClass.name());
|
|
writeBrowserItemActionTypes(thingClass.browserItemActionTypes(), thingClass.name());
|
|
}
|
|
|
|
void PluginInfoCompiler::writeStateTypes(const StateTypes &stateTypes, const QString &thingClassName)
|
|
{
|
|
foreach (const StateType &stateType, stateTypes) {
|
|
QString variableName = QString("%1%2StateTypeId").arg(thingClassName, 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 ThingClass " << thingClassName << ". 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 ThingClass %2").arg(stateType.id().toString()).arg(thingClassName));
|
|
writeExtern(QString("extern StateTypeId %1;").arg(variableName));
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeEventTypes(const EventTypes &eventTypes, const QString &thingClassName)
|
|
{
|
|
foreach (const EventType &eventType, eventTypes) {
|
|
QString variableName = QString("%1%2EventTypeId").arg(thingClassName, 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 ThingClass " << thingClassName << ". 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 ThingClass %2").arg(eventType.id().toString()).arg(thingClassName));
|
|
writeExtern(QString("extern EventTypeId %1;").arg(variableName));
|
|
|
|
writeParams(eventType.paramTypes(), thingClassName, "Event", eventType.name());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeActionTypes(const ActionTypes &actionTypes, const QString &thingClassName)
|
|
{
|
|
foreach (const ActionType &actionType, actionTypes) {
|
|
QString variableName = QString("%1%2ActionTypeId").arg(thingClassName, 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 ThingClass " << thingClassName << ". 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 ThingClass %2").arg(actionType.id().toString()).arg(thingClassName));
|
|
writeExtern(QString("extern ActionTypeId %1;").arg(variableName));
|
|
|
|
writeParams(actionType.paramTypes(), thingClassName, "Action", actionType.name());
|
|
}
|
|
}
|
|
|
|
void PluginInfoCompiler::writeBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &thingClassName)
|
|
{
|
|
foreach (const ActionType &actionType, actionTypes) {
|
|
QString variableName = QString("%1%2BrowserItemActionTypeId").arg(thingClassName, 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 ThingClass " << thingClassName << ". 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 ThingClass %2").arg(actionType.id().toString()).arg(thingClassName));
|
|
writeExtern(QString("extern ActionTypeId %1;").arg(variableName));
|
|
|
|
writeParams(actionType.paramTypes(), thingClassName, "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());
|
|
}
|
|
}
|