diff --git a/autotests/kconfig_compiler/kconfigcompiler_test.cpp b/autotests/kconfig_compiler/kconfigcompiler_test.cpp --- a/autotests/kconfig_compiler/kconfigcompiler_test.cpp +++ b/autotests/kconfig_compiler/kconfigcompiler_test.cpp @@ -125,9 +125,7 @@ if (content != contentRef) { appendFileDiff(fileRef.fileName(), file.fileName()); } - // use split('\n') to avoid - // the whole output shown inline - QCOMPARE(content.split('\n'), contentRef.split('\n')); + QVERIFY(content == contentRef); } @@ -166,17 +164,23 @@ return; } - QStringList args; - args << QStringLiteral("-u"); - args << QFileInfo(oldFile).absoluteFilePath(); - args << QFileInfo(newFile).absoluteFilePath(); + QStringList args({ + QStringLiteral("-u"), + QFileInfo(oldFile).absoluteFilePath(), + QFileInfo(newFile).absoluteFilePath() }); QProcess process; process.start(m_diffExe, args, QIODevice::ReadOnly); process.waitForStarted(); process.waitForFinished(); + if (process.exitCode() == 1) { - QByteArray out = process.readAllStandardOutput(); - qDebug() << '\n' << out; + const QString diffFileName = oldFile + QStringLiteral(".diff"); + QFile diffFile(diffFileName); + QVERIFY2(diffFile.open(QIODevice::WriteOnly), qPrintable(QLatin1String("Could not save diff file for ") + oldFile)); + diffFile.write(process.readAllStandardOutput()); + + // force a failure to print the flename where we stored the diff. + QVERIFY2(false, qPrintable(QLatin1String("This test failed, look at the following file for details: ") + diffFileName)); } } diff --git a/autotests/kconfig_compiler/test1.h.ref b/autotests/kconfig_compiler/test1.h.ref --- a/autotests/kconfig_compiler/test1.h.ref +++ b/autotests/kconfig_compiler/test1.h.ref @@ -8,6 +8,7 @@ #include #include + class Test1 : public KConfigSkeleton { public: diff --git a/autotests/kconfig_compiler/test10.h.ref b/autotests/kconfig_compiler/test10.h.ref --- a/autotests/kconfig_compiler/test10.h.ref +++ b/autotests/kconfig_compiler/test10.h.ref @@ -14,7 +14,6 @@ static Test10 *self(); ~Test10(); - /** Get foo bar */ @@ -24,7 +23,6 @@ return self()->mFooBar; } - /** Get bar foo */ diff --git a/autotests/kconfig_compiler/test11.h.ref b/autotests/kconfig_compiler/test11.h.ref --- a/autotests/kconfig_compiler/test11.h.ref +++ b/autotests/kconfig_compiler/test11.h.ref @@ -11,6 +11,7 @@ #include #include "test11_types.h" + class Test11 : public MyPrefs { public: diff --git a/autotests/kconfig_compiler/test11a.h.ref b/autotests/kconfig_compiler/test11a.h.ref --- a/autotests/kconfig_compiler/test11a.h.ref +++ b/autotests/kconfig_compiler/test11a.h.ref @@ -11,6 +11,7 @@ #include #include "test11_types.h" + class Test11a : public MyPrefs { public: diff --git a/autotests/kconfig_compiler/test12.h.ref b/autotests/kconfig_compiler/test12.h.ref --- a/autotests/kconfig_compiler/test12.h.ref +++ b/autotests/kconfig_compiler/test12.h.ref @@ -15,7 +15,6 @@ Test12( ); ~Test12(); - /** Get RnRSource */ diff --git a/autotests/kconfig_compiler/test13.h.ref b/autotests/kconfig_compiler/test13.h.ref --- a/autotests/kconfig_compiler/test13.h.ref +++ b/autotests/kconfig_compiler/test13.h.ref @@ -16,7 +16,6 @@ Test13( ); ~Test13(); - Q_PROPERTY(QUrl picturesDir READ picturesDir CONSTANT) /** Get picturesDir @@ -46,7 +45,6 @@ return mBrightness; } - Q_PROPERTY(bool startsWithUppercase READ startsWithUppercase CONSTANT) /** Get StartsWithUppercase diff --git a/autotests/kconfig_compiler/test9.h.ref b/autotests/kconfig_compiler/test9.h.ref --- a/autotests/kconfig_compiler/test9.h.ref +++ b/autotests/kconfig_compiler/test9.h.ref @@ -8,6 +8,7 @@ #include #include + class Test9 : public KConfigSkeleton { public: diff --git a/autotests/kconfig_compiler/test_dpointer.cpp.ref b/autotests/kconfig_compiler/test_dpointer.cpp.ref --- a/autotests/kconfig_compiler/test_dpointer.cpp.ref +++ b/autotests/kconfig_compiler/test_dpointer.cpp.ref @@ -157,8 +157,8 @@ void TestDPointer::setAutoSave( bool v ) { - if (!self()->isImmutable( QStringLiteral( "AutoSave" ) )) - self()->d->autoSave = v; + if (!self()->isImmutable( QStringLiteral( "AutoSave" ) )) + self()->d->autoSave = v; } bool TestDPointer::autoSave() @@ -174,8 +174,8 @@ void TestDPointer::setAutoSaveInterval( int v ) { - if (!self()->isImmutable( QStringLiteral( "AutoSaveInterval" ) )) - self()->d->autoSaveInterval = v; + if (!self()->isImmutable( QStringLiteral( "AutoSaveInterval" ) )) + self()->d->autoSaveInterval = v; } int TestDPointer::autoSaveInterval() @@ -191,8 +191,8 @@ void TestDPointer::setConfirm( bool v ) { - if (!self()->isImmutable( QStringLiteral( "Confirm" ) )) - self()->d->confirm = v; + if (!self()->isImmutable( QStringLiteral( "Confirm" ) )) + self()->d->confirm = v; } bool TestDPointer::confirm() @@ -208,8 +208,8 @@ void TestDPointer::setArchiveFile( const QString & v ) { - if (!self()->isImmutable( QStringLiteral( "ArchiveFile" ) )) - self()->d->archiveFile = v; + if (!self()->isImmutable( QStringLiteral( "ArchiveFile" ) )) + self()->d->archiveFile = v; } QString TestDPointer::archiveFile() @@ -225,8 +225,8 @@ void TestDPointer::setDestination( int v ) { - if (!self()->isImmutable( QStringLiteral( "Destination" ) )) - self()->d->destination = v; + if (!self()->isImmutable( QStringLiteral( "Destination" ) )) + self()->d->destination = v; } int TestDPointer::destination() @@ -242,8 +242,8 @@ void TestDPointer::setHourSize( int v ) { - if (!self()->isImmutable( QStringLiteral( "HourSize" ) )) - self()->d->hourSize = v; + if (!self()->isImmutable( QStringLiteral( "HourSize" ) )) + self()->d->hourSize = v; } int TestDPointer::hourSize() @@ -259,8 +259,8 @@ void TestDPointer::setSelectionStartsEditor( bool v ) { - if (!self()->isImmutable( QStringLiteral( "SelectionStartsEditor" ) )) - self()->d->selectionStartsEditor = v; + if (!self()->isImmutable( QStringLiteral( "SelectionStartsEditor" ) )) + self()->d->selectionStartsEditor = v; } bool TestDPointer::selectionStartsEditor() @@ -276,8 +276,8 @@ void TestDPointer::setSelectedPlugins( const QStringList & v ) { - if (!self()->isImmutable( QStringLiteral( "SelectedPlugins" ) )) - self()->d->selectedPlugins = v; + if (!self()->isImmutable( QStringLiteral( "SelectedPlugins" ) )) + self()->d->selectedPlugins = v; } QStringList TestDPointer::selectedPlugins() @@ -293,8 +293,8 @@ void TestDPointer::setHighlightColor( const QColor & v ) { - if (!self()->isImmutable( QStringLiteral( "HighlightColor" ) )) - self()->d->highlightColor = v; + if (!self()->isImmutable( QStringLiteral( "HighlightColor" ) )) + self()->d->highlightColor = v; } QColor TestDPointer::highlightColor() @@ -310,8 +310,8 @@ void TestDPointer::setAgendaBgColor( const QColor & v ) { - if (!self()->isImmutable( QStringLiteral( "AgendaBgColor" ) )) - self()->d->agendaBgColor = v; + if (!self()->isImmutable( QStringLiteral( "AgendaBgColor" ) )) + self()->d->agendaBgColor = v; } QColor TestDPointer::agendaBgColor() @@ -327,8 +327,8 @@ void TestDPointer::setTimeBarFont( const QFont & v ) { - if (!self()->isImmutable( QStringLiteral( "TimeBarFont" ) )) - self()->d->timeBarFont = v; + if (!self()->isImmutable( QStringLiteral( "TimeBarFont" ) )) + self()->d->timeBarFont = v; } QFont TestDPointer::timeBarFont() diff --git a/autotests/kconfig_compiler/test_translation_kde.h.ref b/autotests/kconfig_compiler/test_translation_kde.h.ref --- a/autotests/kconfig_compiler/test_translation_kde.h.ref +++ b/autotests/kconfig_compiler/test_translation_kde.h.ref @@ -17,7 +17,6 @@ TestTranslationKde( ); ~TestTranslationKde(); - /** Get Enable automatic saving of calendar */ diff --git a/autotests/kconfig_compiler/test_translation_kde_domain.h.ref b/autotests/kconfig_compiler/test_translation_kde_domain.h.ref --- a/autotests/kconfig_compiler/test_translation_kde_domain.h.ref +++ b/autotests/kconfig_compiler/test_translation_kde_domain.h.ref @@ -17,7 +17,6 @@ TestTranslationKdeDomain( ); ~TestTranslationKdeDomain(); - /** Get Enable automatic saving of calendar */ diff --git a/autotests/kconfig_compiler/test_translation_qt.h.ref b/autotests/kconfig_compiler/test_translation_qt.h.ref --- a/autotests/kconfig_compiler/test_translation_qt.h.ref +++ b/autotests/kconfig_compiler/test_translation_qt.h.ref @@ -17,7 +17,6 @@ TestTranslationQt( ); ~TestTranslationQt(); - /** Get Enable automatic saving of calendar */ diff --git a/autotests/kconfigtest.h b/autotests/kconfigtest.h --- a/autotests/kconfigtest.h +++ b/autotests/kconfigtest.h @@ -16,6 +16,7 @@ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #ifndef KCONFIGTEST_H #define KCONFIGTEST_H diff --git a/src/kconfig_compiler/CMakeLists.txt b/src/kconfig_compiler/CMakeLists.txt --- a/src/kconfig_compiler/CMakeLists.txt +++ b/src/kconfig_compiler/CMakeLists.txt @@ -1,6 +1,13 @@ -set(kconfig_compiler_SRCS kconfig_compiler.cpp) +set(kconfig_compiler_SRCS + KConfigXTParameters.cpp + KConfigCodeGeneratorBase.cpp + KConfigHeaderGenerator.cpp + KConfigSourceGenerator.cpp + KCFGXmlParser.cpp + kconfig_compiler.cpp +) add_executable(kconfig_compiler ${kconfig_compiler_SRCS}) diff --git a/src/kconfig_compiler/KCFGXmlParser.h b/src/kconfig_compiler/KCFGXmlParser.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KCFGXmlParser.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCFGXMLPARSER_H +#define KCFGXMLPARSER_H + +#include +#include +#include + +#include "KConfigCommonStructs.h" +#include "KConfigXTParameters.h" + +/* This parses the contents of a Xml file into a ParseResult Structure, + * It also fails hard: + * If start() succeeds, you can use the result, + * if start() fails, the program aborts with an error message so there's + * no possibility of generating incorrect code information. + */ +class KCFGXmlParser { +public: + KCFGXmlParser(const KConfigXTParameters &cfg, const QString& inputFileName); + + // Start the parser and reads the contents of the inputFileName into the ParseResult Structure + void start(); + + // Get the result of the parse + ParseResult getParseResult() const; + +private: + // creates a `somethingChanged` signal for every property + void createChangedSignal(CfgEntry &readEntry); + + void validateNameAndKey(CfgEntry &readEntry, const QDomElement &element); + + // TODO: Use std::optional and CfgEntry (without heap allocation) for this function + // *or* fail hard if the parse fails. + CfgEntry *parseEntry(const QString &group, const QDomElement &element); + + // Steps + void readIncludeTag(const QDomElement &element); + void readGroupTag(const QDomElement &element); + void readKcfgfileTag(const QDomElement &element); + void readSignalTag(const QDomElement &element); + + // Those are the Entries in the Xml, that represent a parameter within the tag. + void readParameterFromEntry(CfgEntry &entry, const QDomElement &element); + bool hasDefaultCode(CfgEntry &entry, const QDomElement &element); + void readChoicesFromEntry(CfgEntry &entry, const QDomElement &element); + void readGroupElements(CfgEntry &entry, const QDomElement &element); + void readParamDefaultValues(CfgEntry &entry, const QDomElement &element); + +private: + ParseResult mParseResult; + KConfigXTParameters cfg; + QString mInputFileName; + QStringList mAllNames; + QRegularExpression mValidNameRegexp; +}; + +#endif diff --git a/src/kconfig_compiler/KCFGXmlParser.cpp b/src/kconfig_compiler/KCFGXmlParser.cpp new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KCFGXmlParser.cpp @@ -0,0 +1,559 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KCFGXmlParser.h" +#include "KConfigXTParameters.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +//TODO: Move preprocessDefault to Header / CPP implementation. +// it makes no sense for a parser to process those values and generate code. + +static void preProcessDefault(QString &defaultValue, const QString &name, + const QString &type, + const CfgEntry::Choices &choices, + QString &code, const KConfigXTParameters &cfg) +{ + if (type == QLatin1String("String") && !defaultValue.isEmpty()) { + defaultValue = literalString(defaultValue); + + } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { + defaultValue = literalString(defaultValue); + } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { + // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. + defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); + } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { + QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); + if (!code.isEmpty()) { + cpp << endl; + } + + if (type == QLatin1String("UrlList")) { + cpp << " QList default" << name << ";" << endl; + } else { + cpp << " QStringList default" << name << ";" << endl; + } + const QStringList defaults = defaultValue.split(QLatin1Char(',')); + QStringList::ConstIterator it; + for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { + cpp << " default" << name << ".append( "; + if (type == QLatin1String("UrlList")) { + cpp << "QUrl::fromUserInput("; + } + cpp << "QString::fromUtf8( \"" << *it << "\" ) "; + if (type == QLatin1String("UrlList")) { + cpp << ") "; + } + cpp << ");" << endl; + } + defaultValue = QLatin1String("default") + name; + + } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { + const QRegularExpression colorRe(QRegularExpression::anchoredPattern( + QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); + + if (colorRe.match(defaultValue).hasMatch()) { + defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); + } else { + defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); + } + + } else if (type == QLatin1String("Enum")) { + QList::ConstIterator it; + for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { + if ((*it).name == defaultValue) { + if (cfg.globalEnums && choices.name().isEmpty()) { + defaultValue.prepend(choices.prefix); + } else { + defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); + } + break; + } + } + + } else if (type == QLatin1String("IntList")) { + QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); + if (!code.isEmpty()) { + cpp << endl; + } + + cpp << " QList default" << name << ";" << endl; + if (!defaultValue.isEmpty()) { + const QStringList defaults = defaultValue.split(QLatin1Char(',')); + QStringList::ConstIterator it; + for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { + cpp << " default" << name << ".append( " << *it << " );" + << endl; + } + } + defaultValue = QLatin1String("default") + name; + } +} + +static QString dumpNode(const QDomNode &node) +{ + QString msg; + QTextStream s(&msg, QIODevice::WriteOnly); + node.save(s, 0); + + msg = msg.simplified(); + if (msg.length() > 40) { + return msg.left(37) + QLatin1String("..."); + } + return msg; +} + +void KCFGXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e) +{ + readEntry.param = e.attribute(QStringLiteral("name")); + readEntry.paramType = e.attribute(QStringLiteral("type")); + + if (readEntry.param.isEmpty()) { + cerr << "Parameter must have a name: " << dumpNode(e) << endl; + exit (1); + } + + if (readEntry.paramType.isEmpty()) { + cerr << "Parameter must have a type: " << dumpNode(e) << endl; + exit(1); + } + + if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) { + bool ok; + readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); + if (!ok) { + cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << dumpNode(e) << endl; + exit(1); + } + } else if (readEntry.paramType == QLatin1String("Enum")) { + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { + if (e2.tagName() == QLatin1String("values")) { + for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { + if (e3.tagName() == QLatin1String("value")) { + readEntry.paramValues.append(e3.text()); + } + } + break; + } + } + if (readEntry.paramValues.isEmpty()) { + cerr << "No values specified for parameter '" << readEntry.param << "'." << endl; + exit(1); + } + readEntry.paramMax = readEntry.paramValues.count() - 1; + } else { + cerr << "Parameter '" << readEntry.param << "' has type " << readEntry.paramType + << " but must be of type int, uint or Enum." << endl; + exit(1); + } +} + +bool KCFGXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element) +{ + for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { + if (e.attribute(QStringLiteral("param")).isEmpty()) { + if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { + return true; + } + } + } + return false; +} + + +void KCFGXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e) +{ + QList chlist; + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { + if (e2.tagName() != QLatin1String("choice")) { + continue; + } + CfgEntry::Choice choice; + choice.name = e2.attribute(QStringLiteral("name")); + if (choice.name.isEmpty()) { + cerr << "Tag requires attribute 'name'." << endl; + } + for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { + if (e3.tagName() == QLatin1String("label")) { + choice.label = e3.text(); + choice.context = e3.attribute(QStringLiteral("context")); + } + if (e3.tagName() == QLatin1String("tooltip")) { + choice.toolTip = e3.text(); + choice.context = e3.attribute(QStringLiteral("context")); + } + if (e3.tagName() == QLatin1String("whatsthis")) { + choice.whatsThis = e3.text(); + choice.context = e3.attribute(QStringLiteral("context")); + } + } + chlist.append(choice); + } + + QString name = e.attribute(QStringLiteral("name")); + QString prefix = e.attribute(QStringLiteral("prefix")); + + readEntry.choices = CfgEntry::Choices(chlist, name, prefix); +} + +void KCFGXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) +{ + for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { + QString tag = e.tagName(); + if (tag == QLatin1String("label")) { + readEntry.label = e.text(); + readEntry.labelContext = e.attribute(QStringLiteral("context")); + } else if (tag == QLatin1String("tooltip")) { + readEntry.toolTip = e.text(); + readEntry.toolTipContext = e.attribute(QStringLiteral("context")); + } else if (tag == QLatin1String("whatsthis")) { + readEntry.whatsThis = e.text(); + readEntry.whatsThisContext = e.attribute(QStringLiteral("context")); + } else if (tag == QLatin1String("min")) { + readEntry.min = e.text(); + } else if (tag == QLatin1String("max")) { + readEntry.max = e.text(); + } else if (tag == QLatin1String("code")) { + readEntry.code = e.text(); + } else if (tag == QLatin1String("parameter")) { + readParameterFromEntry(readEntry, e); + } else if (tag == QLatin1String("default")) { + if (e.attribute(QStringLiteral("param")).isEmpty()) { + readEntry.defaultValue = e.text(); + } + } else if (tag == QLatin1String("choices")) { + readChoicesFromEntry(readEntry, e); + } else if (tag == QLatin1String("emit")) { + Signal signal; + signal.name = e.attribute(QStringLiteral("signal")); + readEntry.signalList.append(signal); + } + } +} + +void KCFGXmlParser::createChangedSignal(CfgEntry &readEntry) +{ + if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) { + Signal s; + s.name = changeSignalName(readEntry.name); + s.modify = true; + readEntry.signalList.append(s); + } +} + +void KCFGXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element) +{ + bool nameIsEmpty = readEntry.name.isEmpty(); + if (nameIsEmpty && readEntry.key.isEmpty()) { + cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; + exit (1); + } + + if (readEntry.key.isEmpty()) { + readEntry.key = readEntry.name; + } + + if (nameIsEmpty) { + readEntry.name = readEntry.key; + readEntry.name.remove(QLatin1Char(' ')); + } else if (readEntry.name.contains(QLatin1Char(' '))) { + cout << "Entry '" << readEntry.name << "' contains spaces! elements can not contain spaces!" << endl; + readEntry.name.remove(QLatin1Char(' ')); + } + + if (readEntry.name.contains(QStringLiteral("$("))) { + if (readEntry.param.isEmpty()) { + cerr << "Name may not be parameterized: " << readEntry.name << endl; + exit (1); + } + } else { + if (!readEntry.param.isEmpty()) { + cerr << "Name must contain '$(" << readEntry.param << ")': " << readEntry.name << endl; + exit (1); + } + } +} + +void KCFGXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element) +{ + if (readEntry.param.isEmpty()) { + return; + } + // Adjust name + readEntry.paramName = readEntry.name; + + readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')')); + // Lookup defaults for indexed entries + for (int i = 0; i <= readEntry.paramMax; i++) { + readEntry.paramDefaultValues.append(QString()); + } + + for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { + QString tag = e.tagName(); + if (tag != QLatin1String("default")) { + continue; + } + QString index = e.attribute(QStringLiteral("param")); + if (index.isEmpty()) { + continue; + } + + bool ok; + int i = index.toInt(&ok); + if (!ok) { + i = readEntry.paramValues.indexOf(index); + if (i == -1) { + cerr << "Index '" << index << "' for default value is unknown." << endl; + exit (1); + } + } + + if ((i < 0) || (i > readEntry.paramMax)) { + cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << endl; + exit (1); + } + + QString tmpDefaultValue = e.text(); + + if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { + preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); + } + + readEntry.paramDefaultValues[i] = tmpDefaultValue; + } +} + +CfgEntry *KCFGXmlParser::parseEntry(const QString &group, const QDomElement &element) +{ + CfgEntry readEntry; + readEntry.type = element.attribute(QStringLiteral("type")); + readEntry.name = element.attribute(QStringLiteral("name")); + readEntry.key = element.attribute(QStringLiteral("key")); + readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");; + readEntry.group = group; + + const bool nameIsEmpty = readEntry.name.isEmpty(); + + readGroupElements(readEntry, element); + + createChangedSignal(readEntry); + validateNameAndKey(readEntry, element); + + if (readEntry.label.isEmpty()) { + readEntry.label = readEntry.key; + } + + if (readEntry.type.isEmpty()) { + readEntry.type = QStringLiteral("String"); // XXX : implicit type might be bad + } + + readParamDefaultValues(readEntry, element); + + if (!mValidNameRegexp.match(readEntry.name).hasMatch()) { + if (nameIsEmpty) + cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " + "it is not a valid name. You need to specify a valid name for this entry." << endl; + else { + cerr << "The name '" << readEntry.name << "' is not a valid name for an entry." << endl; + } + exit (1); + } + + if (mAllNames.contains(readEntry.name)) { + if (nameIsEmpty) + cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " + "it does not result in a unique name. You need to specify a unique name for this entry." << endl; + else { + cerr << "The name '" << readEntry.name << "' is not unique." << endl; + } + exit (1); + } + + mAllNames.append(readEntry.name); + + if (!hasDefaultCode(readEntry, element)) { + // TODO: Move all the options to CfgEntry. + preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); + } + + // TODO: Try to Just return the CfgEntry we populated instead of + // creating another one to fill the code. + CfgEntry *result = new CfgEntry(); + result->group = readEntry.group; + result->type = readEntry.type; + result->key = readEntry.key; + result->name = readEntry.name; + result->labelContext = readEntry.labelContext; + result->label = readEntry.label; + result->toolTipContext = readEntry.toolTipContext; + result->toolTip = readEntry.toolTip; + result->whatsThisContext = readEntry.whatsThisContext; + result->whatsThis = readEntry.whatsThis; + result->code = readEntry.code; + result->defaultValue = readEntry.defaultValue; + result->choices = readEntry.choices; + result->signalList = readEntry.signalList; + result->hidden = readEntry.hidden; + + if (!readEntry.param.isEmpty()) { + result->param = readEntry.param; + result->paramName = readEntry.paramName; + result->paramType = readEntry.paramType; + result->paramValues = readEntry.paramValues; + result->paramDefaultValues = readEntry.paramDefaultValues; + result->paramMax = readEntry.paramMax; + } + result->min = readEntry.min; + result->max = readEntry.max; + + return result; +} + +// TODO: Change the name of the config variable. +KCFGXmlParser::KCFGXmlParser(const KConfigXTParameters &cfg, const QString& inputFileName) + : cfg(cfg), mInputFileName(inputFileName) +{ + mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); +} + +void KCFGXmlParser::start() +{ + QFile input(mInputFileName); + QDomDocument doc; + QString errorMsg; + int errorRow; + int errorCol; + if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { + cerr << "Unable to load document." << endl; + cerr << "Parse error in " << mInputFileName << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; + exit (1); + } + + QDomElement cfgElement = doc.documentElement(); + if (cfgElement.isNull()) { + cerr << "No document in kcfg file" << endl; + exit (1); + } + + for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) { + QString tag = element.tagName(); + + if (tag == QLatin1String("include")) { + readIncludeTag(element); + } else if (tag == QLatin1String("kcfgfile")) { + readKcfgfileTag(element); + } else if (tag == QLatin1String("group")) { + readGroupTag(element); + } else if (tag == QLatin1String("signal")) { + readSignalTag(element); + } + } +} + +ParseResult KCFGXmlParser::getParseResult() const +{ + return mParseResult; +} + +void KCFGXmlParser::readIncludeTag(const QDomElement &e) +{ + QString includeFile = e.text(); + if (!includeFile.isEmpty()) { + mParseResult.includes.append(includeFile); + } +} + +void KCFGXmlParser::readGroupTag(const QDomElement &e) +{ + QString group = e.attribute(QStringLiteral("name")); + if (group.isEmpty()) { + cerr << "Group without name" << endl; + exit (1); + } + + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { + if (e2.tagName() != QLatin1String("entry")) { + continue; + } + CfgEntry *entry = parseEntry(group, e2); + if (entry) { + mParseResult.entries.append(entry); + } else { + cerr << "Can not parse entry." << endl; + exit (1); + } + } +} + +void KCFGXmlParser::readKcfgfileTag(const QDomElement &e) +{ + mParseResult.cfgFileName = e.attribute(QStringLiteral("name")); + mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { + if (e2.tagName() == QLatin1String("parameter")) { + Param p; + p.name = e2.attribute(QStringLiteral("name")); + p.type = e2.attribute(QStringLiteral("type")); + if (p.type.isEmpty()) { + p.type = QStringLiteral("String"); + } + mParseResult.parameters.append(p); + } + } +} + +void KCFGXmlParser::readSignalTag(const QDomElement &e) +{ + QString signalName = e.attribute(QStringLiteral("name")); + if (signalName.isEmpty()) { + cerr << "Signal without name." << endl; + exit (1); + } + Signal theSignal; + theSignal.name = signalName; + + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { + if (e2.tagName() == QLatin1String("argument")) { + Param argument; + argument.type = e2.attribute(QStringLiteral("type")); + if (argument.type.isEmpty()) { + cerr << "Signal argument without type." << endl; + exit (1); + } + argument.name= e2.text(); + theSignal.arguments.append(argument); + } else if (e2.tagName() == QLatin1String("label")) { + theSignal.label = e2.text(); + } + } + + mParseResult.signalList.append(theSignal); +} diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.h b/src/kconfig_compiler/KConfigCodeGeneratorBase.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.h @@ -0,0 +1,112 @@ +/* + This file is part of KDE. + + Copyright (c) 2019 Tomaz Canabrava + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGCODEGENERATORBASE_H +#define KCONFIGCODEGENERATORBASE_H + +#include +#include +#include +#include + +#include "KConfigXTParameters.h" + +class CfgEntry; +struct ParseResult; + +/* This class manages the base of writing a C - Based code */ +class KConfigCodeGeneratorBase { +public: + enum ScopeFinalizer {None, Semicolon}; + + KConfigCodeGeneratorBase( + const QString& inputFileName, // The kcfg file + const QString& baseDir, // where we should store the generated file + const QString& fileName, // the name of the generated file + const KConfigXTParameters ¶meters, // parameters passed to the generator + ParseResult &parseResult // The pre processed configuration entries + ); + virtual ~KConfigCodeGeneratorBase(); + + // iterates over the header list adding an #include directive. + void addHeaders(const QStringList& header); + + // Create all the namespace indentation levels based on the parsed result and parameters */ + void beginNamespaces(); + + // Closes all the namespaces adding lines with single '}' + void endNamespaces(); + + // Add the correct amount of whitespace in the code. + QString whitespace(); + + // start a block scope `{` and increase indentation level. + void endScope(ScopeFinalizer finalizer = None); + + // end a block scope `}` and decrease indentation level. + void startScope(); + + // start writing to the output file + virtual void start(); + + // save the result on the disk + void save(); + + // Code Implementations + // Implements the `Get` methods for the CfgEntry + // TODO: write to the stream directly without returning a QString. + QString memberAccessorBody(const CfgEntry *e, bool globalEnums) const; + + // Implements the `Set` methods for the CfgEntry + void memberMutatorBody(const CfgEntry *e); + + // This is the code that creates the logic for the Setter / Mutator. + // It *just* creates the if test, no body. The reason is that just + // the if test was more than 20 lines of code and hard to understand + // what was happening in a bigger function. + void createIfSetLogic(const CfgEntry *e, const QString &varExpression); + +protected: + /* advance the number of spaces for the indentation level */ + void indent(); + + /* reduce the number of spaces for the indentation level */ + void unindent(); + + QString inputFile; // the base file name, input file is based on this. + + QString baseDir; // Where we are going to save the file + QString fileName; // The file name + + const KConfigXTParameters &cfg; // The parameters passed via the kcfgc file + ParseResult &parseResult; // the result of the parsed kcfg file + QTextStream stream; // the stream that operates in the file to write data. + QFile file; // The file handler. + + // Special access to `this->` and `const` thru the code. + QString This; + QString Const; + +private: + int indentLevel = 0; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp @@ -0,0 +1,270 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigXTParameters.h" +#include "KConfigCommonStructs.h" + +#include +#include +#include + +#include +#include + +using std::endl; + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigCodeGeneratorBase::KConfigCodeGeneratorBase( + const QString& inputFile, + const QString& baseDir, + const QString& fileName, + const KConfigXTParameters ¶meters, + ParseResult &parseResult) + : inputFile(inputFile), baseDir(baseDir), fileName(fileName), cfg(parameters), parseResult(parseResult) +{ + file.setFileName(fileName); + if (!file.open(QIODevice::WriteOnly)) { + cerr << "Can not open '" << fileName << "for writing." << endl; + exit(1); + } + stream.setDevice(&file); + stream.setCodec("utf-8"); + + if (cfg.staticAccessors) { + This = QStringLiteral("self()->"); + } else { + Const = QStringLiteral(" const"); + } +} + +KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() +{ + save(); +} + +void KConfigCodeGeneratorBase::save() +{ + file.close(); +} + +void KConfigCodeGeneratorBase::indent() +{ + if (indentLevel >= 4) { + indentLevel += 2; + } else { + indentLevel += 4; + } +} + +void KConfigCodeGeneratorBase::unindent() +{ + if (indentLevel > 4) { + indentLevel -= 2; + } else { + indentLevel -= 4; + } +} + +QString KConfigCodeGeneratorBase::whitespace() +{ + QString spaces; + for (int i = 0; i < indentLevel; i++) { + spaces.append(QLatin1Char(' ')); + } + return spaces; +} + +void KConfigCodeGeneratorBase::startScope() +{ + stream << whitespace() << QLatin1Char('{'); + stream << endl; + indent(); +} + +void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) +{ + unindent(); + stream << whitespace() << QLatin1Char('}'); + if (finalizer == ScopeFinalizer::Semicolon) { + stream << ';'; + } + stream << endl; +} + +void KConfigCodeGeneratorBase::start() +{ + const QString fileName = QFileInfo(inputFile).fileName(); + stream << "// This file is generated by kconfig_compiler_kf5 from " << fileName << ".kcfg" << "." << endl; + stream << "// All changes you do to this file will be lost." << endl; +} + +void KConfigCodeGeneratorBase::addHeaders(const QStringList& headerList) +{ + for (auto include : qAsConst(headerList)) { + if (include.startsWith(QLatin1Char('"'))) { + stream << "#include " << include << endl; + } else { + stream << "#include <" << include << ">" << endl; + } + } +} + +// adds as many 'namespace foo {' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::beginNamespaces() +{ + if (!cfg.nameSpace.isEmpty()) { + for (const QString &ns : cfg.nameSpace.split(QStringLiteral("::"))) { + stream << "namespace " << ns << " {" << endl; + } + stream << endl; + } +} + +// adds as many '}' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::endNamespaces() +{ + if (!cfg.nameSpace.isEmpty()) { + stream << endl; + const int namespaceCount = cfg.nameSpace.count(QStringLiteral("::")) + 1; + for (int i = 0; i < namespaceCount; ++i) { + stream << "}" << endl; + } + } +} + +// returns the member accesor implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + QString n = e->name; + QString t = e->type; + bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum"); + + out << "return "; + if (useEnumType) { + out << "static_cast<" << enumType(e, globalEnums) << ">("; + } + out << This << varPath(n, cfg); + if (!e->param.isEmpty()) { + out << "[i]"; + } + if (useEnumType) { + out << ")"; + } + out << ";" << endl; + + return result; +} + +void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) +{ + const QString n = e->name; + const QString t = e->type; + const bool hasBody = !e->signalList.empty() || cfg.generateProperties; + + stream << whitespace() << "if ("; + if (hasBody) { + stream << "v != " << varExpression << " && "; + } + stream << "!" << This << "isImmutable( QStringLiteral( \""; + if (!e->param.isEmpty()) { + QString paramName = e->paramName; + + stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( "; + if (e->paramType == QLatin1String("Enum")) { + stream << "QLatin1String( "; + + if (cfg.globalEnums) { + stream << enumName(e->param) << "ToString[i]"; + } else { + stream << enumName(e->param) << "::enumToString[i]"; + } + + stream << " )"; + } else { + stream << "i"; + } + stream << " )"; + } else { + stream << n << "\" )"; + } + stream << " ))"; +} + +void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) +{ + QString n = e->name; + QString t = e->type; + + // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. + if (!e->min.isEmpty()) { + if (e->min != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) + stream << whitespace() << "if (v < " << e->min << ")" << endl; + stream << whitespace() << "{" << endl; + stream << whitespace(); addDebugMethod(stream, cfg, n); + stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl; + stream << whitespace() << " v = " << e->min << ";" << endl; + stream << whitespace() << "}" << endl; + } + } + + if (!e->max.isEmpty()) { + stream << endl; + stream << whitespace() << "if (v > " << e->max << ")" << endl; + stream << whitespace() << "{" << endl; + stream << whitespace(); addDebugMethod(stream, cfg, n); + stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl; + stream << whitespace() << " v = " << e->max << ";" << endl; + stream << whitespace() << "}" << endl << endl; + } + + const QString varExpression = This + varPath(n, cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]")); + + // TODO: Remove this `hasBody` logic, always use an '{' for the if. + const bool hasBody = !e->signalList.empty() || cfg.generateProperties; + + // This call creates an `if (someTest ...) that's just to long to throw over the code. + createIfSetLogic(e, varExpression); + stream << (hasBody ? " {" : "") << endl; + stream << whitespace() << " " << varExpression << " = v;" << endl; + + const auto listSignal = e->signalList; + for (const Signal &signal : qAsConst(listSignal)) { + if (signal.modify) { + stream << whitespace() << " Q_EMIT " << This << signal.name << "();" << endl; + } else { + stream << whitespace() << " " << This << varPath(QStringLiteral("settingsChanged"), cfg) + << " |= " << signalEnumName(signal.name) << ";" << endl; + } + } + if (hasBody) { + stream << whitespace() << "}" << endl; + } +} diff --git a/src/kconfig_compiler/KConfigCommonStructs.h b/src/kconfig_compiler/KConfigCommonStructs.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigCommonStructs.h @@ -0,0 +1,189 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGCOMMONSTRUCTS_H +#define KCONFIGCOMMONSTRUCTS_H + +#include +#include +#include + +#include "KConfigXTParameters.h" + +struct Param +{ + QString name; + QString type; +}; + +struct Signal +{ + QString name; + QString label; + QList arguments; + bool modify = false; +}; + +class CfgEntry +{ +public: + struct Choice { + QString name; + QString context; + QString label; + QString toolTip; + QString whatsThis; + }; + class Choices + { + public: + Choices() {} + Choices(const QList &d, const QString &n, const QString &p) + : prefix(p), choices(d), mName(n) + { + int i = n.indexOf(QLatin1String("::")); + if (i >= 0) { + mExternalQual = n.left(i + 2); + } + } + QString prefix; + QList choices; + const QString &name() const + { + return mName; + } + const QString &externalQualifier() const + { + return mExternalQual; + } + bool external() const + { + return !mExternalQual.isEmpty(); + } + private: + QString mName; + QString mExternalQual; + }; + +public: + QString group; + QString type; + QString key; + QString name; + QString labelContext; + QString label; + QString toolTipContext; + QString toolTip; + QString whatsThisContext; + QString whatsThis; + QString code; + QString defaultValue; + QString param; + QString paramName; + QString paramType; + Choices choices; + QList signalList; + QStringList paramValues; + QStringList paramDefaultValues; + int paramMax; + bool hidden; + QString min; + QString max; +}; + +struct ParseResult { + QString cfgFileName; + bool cfgFileNameArg = false; + QList parameters; + QList signalList; + QStringList includes; + QList entries; + bool hasNonModifySignals = false; +}; + +// TODO: Move those methods +QString enumName(const QString &n); +QString enumName(const QString &n, const CfgEntry::Choices &c); +QString param(const QString &t); +QString cppType(const QString &t); +QString itemType(const QString &type); +QString changeSignalName(const QString &n); + +QString enumType(const CfgEntry *e, bool globalEnums); + +QString getDefaultFunction(const QString &n, const QString &className = QString()); +QString setFunction(const QString &n, const QString &className = QString()); +QString getFunction(const QString &n, const QString &className = QString()); + +QString itemAccessorBody(const CfgEntry *e, const KConfigXTParameters &cfg); +QString signalEnumName(const QString &signalName); + +bool isUnsigned(const QString &type); + +// returns the name of an member variable +// use itemPath to know the full path +// like using d-> in case of dpointer +QString varName(const QString &n, const KConfigXTParameters &cfg); + +QString varPath(const QString &n, const KConfigXTParameters &cfg); + +// returns the name of an item variable +// use itemPath to know the full path +// like using d-> in case of dpointer +QString itemVar(const CfgEntry *e, const KConfigXTParameters &cfg); + +QString itemPath(const CfgEntry *e, const KConfigXTParameters &cfg); + +QString filenameOnly(const QString &path); + +QString itemDeclaration(const CfgEntry *e, const KConfigXTParameters &cfg); + +QString translatedString( + const KConfigXTParameters &cfg, + const QString &string, + const QString &context = QString(), + const QString ¶m = QString(), + const QString ¶mValue = QString()); + +// TODO: Sanitize those functions. +QString newItem( + const CfgEntry* entry, + const QString &key, + const QString& defaultValue, + const KConfigXTParameters &cfg, + const QString ¶m = QString()); + +QString userTextsFunctions( + const CfgEntry *e, + const KConfigXTParameters &cfg, + QString itemVarStr = QString(), + const QString &i = QString()); + +QString paramString(const QString &s, const CfgEntry *e, int i); +QString paramString(const QString &group, const QList ¶meters); + +QString defaultValue(const QString &t); +QString memberGetDefaultBody(const CfgEntry *e); +QString literalString(const QString &s); +QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c); + +void addQuotes(QString &s); +void addDebugMethod(QTextStream &out, const KConfigXTParameters &cfg, const QString &n); + +#endif diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.h b/src/kconfig_compiler/KConfigHeaderGenerator.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigHeaderGenerator.h @@ -0,0 +1,72 @@ +/* + This file is part of KDE. + + Copyright (c) 2019 Tomaz Canabrava + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGHEADERGENERATOR_H +#define KCONFIGHEADERGENERATOR_H + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigCommonStructs.h" + +#include +#include + +class KConfigXTParameters; +class CfgEntry; +class QTextStream; +struct ParseResult; + +class KConfigHeaderGenerator : public KConfigCodeGeneratorBase { +public: + KConfigHeaderGenerator( + const QString& inputFile, + const QString& baseDir, + const KConfigXTParameters ¶meters, + ParseResult &parseResult); + + void start() override; + +private: + void startHeaderGuards(); + void endHeaderGuards(); + + void implementEnums(); + void implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices); + void implementValueEnums(const CfgEntry *entry, const QStringList &values); + + void doClassDefinition(); + void createHeaders(); + void createDPointer(); + void createNonDPointerHelpers(); + + void createConstructor(); + void createDestructor(); + void createForwardDeclarations(); + void createSingleton(); + void createSignals(); + + void createSetters(const CfgEntry *entry); + void createItemAcessors(const CfgEntry *entry, const QString& returnType); + void createGetters(const CfgEntry *entry, const QString& returnType); + void createProperties(const CfgEntry *entry, const QString& returnType); + void createDefaultValueMember(const CfgEntry *entry); +}; + +#endif diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.cpp b/src/kconfig_compiler/KConfigHeaderGenerator.cpp new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigHeaderGenerator.cpp @@ -0,0 +1,606 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KConfigHeaderGenerator.h" +#include "KConfigCommonStructs.h" + +#include +#include + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigHeaderGenerator::KConfigHeaderGenerator( + const QString& inputFile, + const QString& baseDir, + const KConfigXTParameters &cfg, + ParseResult &parseResult) + : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, parseResult) +{ +} + +void KConfigHeaderGenerator::start() +{ + KConfigCodeGeneratorBase::start(); + startHeaderGuards(); + createHeaders(); + + beginNamespaces(); + + createForwardDeclarations(); + + doClassDefinition(); + + endNamespaces(); + endHeaderGuards(); +} + +void KConfigHeaderGenerator::doClassDefinition() +{ + stream << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits << endl; + startScope(); + + // Add Q_OBJECT macro if the config need signals. + if (!parseResult.signalList.isEmpty() || cfg.generateProperties) { + stream << " Q_OBJECT" << endl; + } + stream << " public:" << endl; + implementEnums(); + createConstructor(); + createDestructor(); + + for (auto *entry : qAsConst(parseResult.entries)) { + const QString n = entry->name; + const QString t = entry->type; + + const QString returnType = (cfg.useEnumTypes && t == QLatin1String("Enum")) + ? enumType(entry, cfg.globalEnums) + : cppType(t); + + createSetters(entry); + createProperties(entry, returnType); + createGetters(entry, returnType); + createDefaultValueMember(entry); + createItemAcessors(entry, returnType); + } + + createSignals(); + stream << " protected:" << endl; + createSingleton(); + + // TODO: Move those to functions too. + if (parseResult.hasNonModifySignals) { + stream << whitespace() << "bool usrSave() override;" << endl; + } + + // Member variables + if (!cfg.memberVariables.isEmpty() && cfg.memberVariables != QLatin1String("private") && cfg.memberVariables != QLatin1String("dpointer")) { + stream << " " << cfg.memberVariables << ":" << endl; + } + + // Class Parameters + for (const auto ¶meter : qAsConst(parseResult.parameters)) { + stream << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";" << endl; + } + + createNonDPointerHelpers(); + createDPointer(); + + if (cfg.customAddons) { + stream << whitespace() << "// Include custom additions" << endl; + stream << whitespace() << "#include \"" << cfg.baseName << "_addons." << cfg.headerExtension << '"' << endl; + } + + endScope(ScopeFinalizer::Semicolon); +} + +void KConfigHeaderGenerator::createHeaders() +{ + addHeaders(cfg.headerIncludes); + if (cfg.headerIncludes.size()) { + stream << endl; + } + + if (!cfg.singleton && parseResult.parameters.isEmpty()) { + addHeaders({QStringLiteral("qglobal.h")}); + } + + if (cfg.inherits == QLatin1String("KCoreConfigSkeleton")) { + addHeaders({QStringLiteral("kcoreconfigskeleton.h")}); + } else { + addHeaders({QStringLiteral("kconfigskeleton.h")}); + } + + addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")}); + stream << endl; + + addHeaders(parseResult.includes); + if (parseResult.includes.size()) { + stream << endl; + } +} + +void KConfigHeaderGenerator::startHeaderGuards() +{ + const bool hasNamespace = !cfg.nameSpace.isEmpty(); + const QString namespaceName = QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper(); + const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QStringLiteral(""); + const QString defineName = namespaceStr + cfg.className.toUpper() + QStringLiteral("_H"); + + stream << "#ifndef " << defineName << endl; + stream << "#define " << defineName << endl; + stream << endl; +} + +void KConfigHeaderGenerator::endHeaderGuards() +{ + stream << endl; + stream << "#endif"; + stream << endl; + // HACK: Original files ended with two last newlines, add them. + stream << endl; +} + +void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices) +{ + const QList chlist = choices.choices; + + if (chlist.isEmpty()) { + return; + } + + QStringList values; + for (const auto choice : qAsConst(chlist)) { + values.append(choices.prefix + choice.name); + } + + if (choices.name().isEmpty()) { + if (cfg.globalEnums) { + stream << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; + } else { + // Create an automatically named enum + stream << whitespace() << "class " << enumName(entry->name, entry->choices) << endl; + stream << whitespace() << "{" << endl; + stream << whitespace() << " public:" << endl; + stream << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; + stream << whitespace() << "};" << endl; + } + } else if (!choices.external()) { + // Create a named enum + stream << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; + } +} + +void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values) +{ + if (values.isEmpty()) { + return; + } + + if (cfg.globalEnums) { + // ### FIXME!! + // make the following string table an index-based string search! + // ### + stream << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; + stream << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];" << endl; + } else { + stream << whitespace() << "class " << enumName(entry->param) << endl; + stream << whitespace() << "{" << endl; + stream << whitespace() << " public:" << endl; + stream << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; + stream << whitespace() << " static const char* const enumToString[];" << endl; + stream << whitespace() << "};" << endl; + } +} + +void KConfigHeaderGenerator::implementEnums() +{ + if (!parseResult.entries.size()) { + return; + } + + for (const auto entry : qAsConst(parseResult.entries)) { + const CfgEntry::Choices &choices = entry->choices; + const QStringList values = entry->paramValues; + + implementChoiceEnums(entry, choices); + implementValueEnums(entry, values); + } + stream << endl; +} + +void KConfigHeaderGenerator::createSignals() +{ + // Signal definition. + const bool hasSignals = !parseResult.signalList.isEmpty(); + + unsigned val = 1 << parseResult.signalList.size(); + if (!val) { + cerr << "Too many signals to create unique bit masks" << endl; + exit(1); + } + + if (!hasSignals) { + return; + } + + stream << "\n enum {" << endl; + val = 1; + + // HACK: Use C-Style for add a comma in all but the last element, + // just to make the source generated code equal to the old one. + // When we are sure, revert this to a range-based-for and just add + // a last comma, as it's valid c++. + for (int i = 0, end = parseResult.signalList.size(); i < end; i++) { + auto signal = parseResult.signalList.at(i); + stream << whitespace() << " " << signalEnumName(signal.name) << " = 0x" << hex << val; + if (i != end-1) { + stream << "," << endl; + } + + val <<= 1; + } + stream << endl; + stream << whitespace() << "};" << dec << endl << endl; + + stream << " Q_SIGNALS:"; + for (const Signal &signal : qAsConst(parseResult.signalList)) { + stream << endl; + if (!signal.label.isEmpty()) { + stream << whitespace() << "/**" << endl; + stream << whitespace() << " " << signal.label << endl; + stream << whitespace() << "*/" << endl; + } + stream << whitespace() << "void " << signal.name << "("; + QList::ConstIterator it, itEnd = signal.arguments.constEnd(); + for (it = signal.arguments.constBegin(); it != itEnd;) { + Param argument = *it; + QString type = param(argument.type); + if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { + for (auto *entry : qAsConst(parseResult.entries)) { + if (entry->name == argument.name) { + type = enumType(entry, cfg.globalEnums); + break; + } + } + } + stream << type << " " << argument.name; + if (++it != itEnd) { + stream << ", "; + } + } + stream << ");" << endl; + } + stream << endl; + + stream << " private:" << endl; + stream << whitespace() << "void itemChanged(quint64 flags);" << endl; + stream << endl; +} + +void KConfigHeaderGenerator::createDPointer() +{ + if (!cfg.dpointer) { + return; + } + + // use a private class for both member variables and items + stream << " private:" << endl; + for (const auto &entry : qAsConst(parseResult.entries)) { + if (cfg.allDefaultGetters || cfg.defaultGetters.contains(entry->name)) { + stream << whitespace() << ""; + if (cfg.staticAccessors) { + stream << "static "; + } + stream << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const << ";" << endl; + } + } + stream << whitespace() << "" << cfg.className << "Private *d;" << endl; +} + +void KConfigHeaderGenerator::createConstructor() +{ + if (cfg.singleton) { + stream << whitespace() << "static " << cfg.className << " *self();" << endl; + if (parseResult.cfgFileNameArg) { + stream << whitespace() << "static void instance(const QString& cfgfilename);" << endl; + stream << whitespace() << "static void instance(KSharedConfig::Ptr config);" << endl; + } + return; + } + + stream << whitespace() << "" << cfg.className << "("; + if (parseResult.cfgFileNameArg) { + if (cfg.forceStringFilename) + stream << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", "); + else + stream << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); + } + + bool first = true; + for (const auto parameter : qAsConst(parseResult.parameters)) { + if (first) { + first = false; + } else { + stream << ","; + } + + stream << " " << param(parameter.type) << " " << parameter.name; + } + + if (cfg.parentInConstructor) { + if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { + stream << ","; + } + stream << " QObject *parent = nullptr"; + } + stream << " );" << endl; +} + +void KConfigHeaderGenerator::createDestructor() +{ + stream << whitespace() << "~" << cfg.className << "();" << endl << endl; +} + +void KConfigHeaderGenerator::createForwardDeclarations() +{ + // Private class declaration + if (cfg.dpointer) { + stream << "class " << cfg.className << "Private;" << endl << endl; + } +} + +void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString& returnType) +{ + if (!cfg.generateProperties) { + return; + } + stream << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name); + stream << " READ " << getFunction(entry->name); + + if (cfg.allMutators || cfg.mutators.contains(entry->name)) { + const QString signal = changeSignalName(entry->name); + stream << " WRITE " << setFunction(entry->name); + stream << " NOTIFY " << signal; + + //If we have the modified signal, we'll also need + //the changed signal as well + Signal s; + s.name = signal; + s.modify = true; + parseResult.signalList.append(s); + } else { + stream << " CONSTANT"; + } + stream << ")" << endl; +} + +void KConfigHeaderGenerator::createSetters(const CfgEntry *entry) +{ + // Manipulator + if (!cfg.allMutators && !cfg.mutators.contains(entry->name)) { + return; + } + + stream << whitespace() << "/**" << endl; + stream << whitespace() << " Set " << entry->label << endl; + stream << whitespace() << "*/" << endl; + + if (cfg.staticAccessors) { + stream << whitespace() << "static" << endl; + } + + stream << whitespace() << "void " << setFunction(entry->name) << "( "; + if (!entry->param.isEmpty()) { + stream <paramType) << " i, "; + } + + stream << (cfg.useEnumTypes && entry->type == QLatin1String("Enum") + ? enumType(entry, cfg.globalEnums) + : param(entry->type)); + + stream << " v )"; + + // function body inline only if not using dpointer + // for BC mode + if (!cfg.dpointer) { + stream << endl; + startScope(); + memberMutatorBody(entry); + endScope(); + stream << endl; + } else { + stream << ";" << endl << endl; + } +} + +void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString& returnType) +{ + // Accessor + stream << whitespace() << "/**" << endl; + stream << whitespace() << " Get " << entry->label << endl; + stream << whitespace() << "*/" << endl; + if (cfg.staticAccessors) { + stream << whitespace() << "static" << endl; + } + stream << whitespace() << ""; + stream << returnType; + stream << " " << getFunction(entry->name) << "("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const; + + // function body inline only if not using dpointer + // for BC mode + if (!cfg.dpointer) { + stream << endl; + startScope(); + stream << whitespace() << memberAccessorBody(entry, cfg.globalEnums); + endScope(); + stream << endl; + } else { + stream << ";" << endl << endl; + } +} + +void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString& returnType) +{ + // Item accessor + if (!cfg.itemAccessors) { + return; + } + stream << whitespace() << "/**" << endl; + stream << whitespace() << " Get Item object corresponding to " << entry->name << "()" + << endl; + stream << whitespace() << "*/" << endl; + stream << whitespace() << "Item" << itemType(entry->type) << " *" + << getFunction(entry->name) << "Item("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")"; + if (!cfg.dpointer) { + stream << endl; + startScope(); + stream << whitespace() << itemAccessorBody(entry, cfg); + endScope(); + } else { + stream << ";" << endl; + } + + stream <name)) && !entry->defaultValue.isEmpty())) { + return; + } + stream << whitespace() << "/**" << endl; + stream << whitespace() << " Get " << entry->label << " default value" << endl; + stream << whitespace() << "*/" << endl; + if (cfg.staticAccessors) { + stream << whitespace() << "static" << endl; + } + stream << whitespace() << ""; + if (cfg.useEnumTypes && entry->type == QLatin1String("Enum")) { + stream << enumType(entry, cfg.globalEnums); + } else { + stream << cppType(entry->type); + } + stream << " " << getDefaultFunction(entry->name) << "("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const << endl; + stream << whitespace() << "{" << endl; + stream << whitespace() << " return "; + if (cfg.useEnumTypes && entry->type == QLatin1String("Enum")) { + stream << "static_cast<" << enumType(entry, cfg.globalEnums) << ">("; + } + stream << getDefaultFunction(entry->name) << "_helper("; + if (!entry->param.isEmpty()) { + stream << " i "; + } + stream << ")"; + if (cfg.useEnumTypes && entry->type == QLatin1String("Enum")) { + stream << ")"; + } + stream << ";" << endl; + stream << whitespace() << "}" << endl; + stream << endl; +} + +void KConfigHeaderGenerator::createSingleton() +{ + // Private constructor for singleton + if (!cfg.singleton) { + return; + } + + stream << whitespace() << "" << cfg.className << "("; + if (parseResult.cfgFileNameArg) { + stream << "KSharedConfig::Ptr config"; + } + if (cfg.parentInConstructor) { + if (parseResult.cfgFileNameArg) { + stream << ", "; + } + stream << "QObject *parent = nullptr"; + } + stream << ");" << endl; + stream << whitespace() << "friend class " << cfg.className << "Helper;" << endl << endl; +} + +void KConfigHeaderGenerator::createNonDPointerHelpers() +{ + if (cfg.memberVariables == QLatin1String("dpointer")) { + return; + } + + QString group; + for (auto *entry : qAsConst(parseResult.entries)) { + if (entry->group != group) { + group = entry->group; + stream << endl; + stream << whitespace() << "// " << group << endl; + } + stream << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg); + if (!entry->param.isEmpty()) { + stream << QStringLiteral("[%1]").arg(entry->paramMax + 1); + } + stream << ";" << endl; + + if (cfg.allDefaultGetters || cfg.defaultGetters.contains(entry->name)) { + stream << whitespace() << ""; + if (cfg.staticAccessors) { + stream << "static "; + } + stream << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const << ";" << endl; + } + } + + stream << endl << " private:" << endl; + if (cfg.itemAccessors) { + for (auto *entry : qAsConst(parseResult.entries)) { + stream << whitespace() << "Item" << itemType(entry->type) << " *" << itemVar(entry, cfg); + if (!entry->param.isEmpty()) { + stream << QStringLiteral("[%1]").arg(entry->paramMax + 1); + } + stream << ";" << endl; + } + } + + if (parseResult.hasNonModifySignals) { + stream << whitespace() << "uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; + } +} diff --git a/src/kconfig_compiler/KConfigSourceGenerator.h b/src/kconfig_compiler/KConfigSourceGenerator.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigSourceGenerator.h @@ -0,0 +1,80 @@ +/* + This file is part of KDE. + + Copyright (c) 2019 Tomaz Canabrava + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGSOURCEGENERATOR_H +#define KCONFIGSOURCEGENERATOR_H + +#include "KConfigCodeGeneratorBase.h" +#include "KConfigCommonStructs.h" + +#include +#include + +class KConfigXTParameters; +class CfgEntry; +class QTextStream; +struct ParseResult; + +class KConfigSourceGenerator : public KConfigCodeGeneratorBase { +public: + KConfigSourceGenerator( + const QString& inputFile, + const QString& baseDir, + const KConfigXTParameters ¶meters, + ParseResult &parseResult); + + void start() override; + +private: + // Those are fairly self contained functions. + void createHeaders(); + void createPrivateDPointerImplementation(); + void createSingletonImplementation(); + void createPreamble(); + void createDestructor(); + void createConstructorParameterList(); + void createParentConstructorCall(); + void createInitializerList(); + void createDefaultValueGetterSetter(); + void createNonModifyingSignalsHelper(); + void createSignalFlagsHandler(); + void includeMoc(); + + // Constructor related methods + // the `do` methods have related helper functions that are only related + // to it. So we can break the function into many smaller ones and create + // logic inside of the `do` function. + void doConstructor(); + void createEnums(const CfgEntry *entry); + void createNormalEntry(const CfgEntry *entry, const QString& key); + void createIndexedEntry(const CfgEntry *entry, const QString& key); + void handleCurrentGroupChange(const CfgEntry *entry); + + void doGetterSetterDPointerMode(); + void createGetterDPointerMode(const CfgEntry *entry); + void createSetterDPointerMode(const CfgEntry *entry); + void createItemGetterDPointerMode(const CfgEntry *entry); + +private: + QString mCurrentGroup; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp @@ -0,0 +1,651 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KConfigSourceGenerator.h" +#include "KConfigCommonStructs.h" + + +KConfigSourceGenerator::KConfigSourceGenerator( + const QString& inputFile, + const QString& baseDir, + const KConfigXTParameters &cfg, + ParseResult &parseResult) + : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.sourceExtension, cfg, parseResult) +{ +} + +void KConfigSourceGenerator::start() +{ + KConfigCodeGeneratorBase::start(); + stream << endl; + createHeaders(); + + if (!cfg.nameSpace.isEmpty()) { + stream << "using namespace " << cfg.nameSpace << ";"; + stream << endl << endl; + } + + createPrivateDPointerImplementation(); + createSingletonImplementation(); + createPreamble(); + doConstructor(); + doGetterSetterDPointerMode(); + createDefaultValueGetterSetter(); + createDestructor(); + createNonModifyingSignalsHelper(); + createSignalFlagsHandler(); + includeMoc(); +} + +void KConfigSourceGenerator::createHeaders() +{ + QString headerName = cfg.baseName + QLatin1Char('.') + cfg.headerExtension; + + // TODO: Make addQuotes return a string instead of replacing it inplace. + addQuotes(headerName); + + addHeaders({ headerName }); + stream << endl; + + addHeaders(cfg.sourceIncludes); + if (cfg.setUserTexts && cfg.translationSystem == KConfigXTParameters::KdeTranslation) { + addHeaders({QStringLiteral("klocalizedstring.h")}); + stream << endl; + } + + // Header required by singleton implementation + if (cfg.singleton) { + addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")}); + + // HACK: Add single line to fix test. + if (cfg.singleton && parseResult.cfgFileNameArg) { + stream << endl; + } + } + + if (cfg.singleton && parseResult.cfgFileNameArg) { + addHeaders({QStringLiteral("QDebug")}); + } + + if (cfg.singleton) { + stream << endl; + } +} + +void KConfigSourceGenerator::createPrivateDPointerImplementation() +{ + // private class implementation + if (!cfg.dpointer) { + return; + } + + QString group; + beginNamespaces(); + stream << "class " << cfg.className << "Private" << endl; + stream << "{" << endl; + stream << " public:" << endl; + + // Create Members + for (auto *entry : qAsConst(parseResult.entries)) { + if (entry->group != group) { + group = entry->group; + stream << endl; + stream << " // " << group << endl; + } + stream << " " << cppType(entry->type) << " " << varName(entry->name, cfg); + if (!entry->param.isEmpty()) { + stream << QStringLiteral("[%1]").arg(entry->paramMax + 1); + } + stream << ";" << endl; + } + stream << endl << " // items" << endl; + + // Create Items. + for (auto *entry : qAsConst(parseResult.entries)) { + const QString declType = entry->signalList.isEmpty() + ? QString(cfg.inherits + QStringLiteral("::Item") + itemType(entry->type)) + : QStringLiteral("KConfigCompilerSignallingItem"); + + stream << " " << declType << " *" << itemVar( entry, cfg ); + if (!entry->param.isEmpty()) { + stream << QStringLiteral("[%1]").arg(entry->paramMax + 1); + } + stream << ";" << endl; + } + + if (parseResult.hasNonModifySignals) { + stream << " uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; + } + + stream << "};" << endl << endl; + endNamespaces(); +} + +void KConfigSourceGenerator::createSingletonImplementation() +{ + // Singleton implementation + if (!cfg.singleton) { + return; + } + + beginNamespaces(); + stream << "class " << cfg.className << "Helper" << endl; + stream << '{' << endl; + stream << " public:" << endl; + stream << " " << cfg.className << "Helper() : q(nullptr) {}" << endl; + stream << " ~" << cfg.className << "Helper() { delete q; }" << endl; + stream << " " << cfg.className << "Helper(const " << cfg.className << "Helper&) = delete;" << endl; + stream << " " << cfg.className << "Helper& operator=(const " << cfg.className << "Helper&) = delete;" << endl; + stream << " " << cfg.className << " *q;" << endl; + stream << "};" << endl; + endNamespaces(); + + stream << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << cfg.className << ")" << endl; + + stream << cfg.className << " *" << cfg.className << "::self()" << endl; + stream << "{" << endl; + if (parseResult.cfgFileNameArg) { + stream << " if (!s_global" << cfg.className << "()->q)" << endl; + stream << " qFatal(\"you need to call " << cfg.className << "::instance before using\");" << endl; + } else { + stream << " if (!s_global" << cfg.className << "()->q) {" << endl; + stream << " new " << cfg.className << ';' << endl; + stream << " s_global" << cfg.className << "()->q->read();" << endl; + stream << " }" << endl << endl; + } + stream << " return s_global" << cfg.className << "()->q;" << endl; + stream << "}" << endl << endl; + + if (parseResult.cfgFileNameArg) { + auto instance = [this] (const QString &type, const QString &arg, bool isString) { + stream << "void " << cfg.className << "::instance(" << type << " " << arg << ")" << endl; + stream << "{" << endl; + stream << " if (s_global" << cfg.className << "()->q) {" << endl; + stream << " qDebug() << \"" << cfg.className << "::instance called after the first use - ignoring\";" << endl; + stream << " return;" << endl; + stream << " }" << endl; + stream << " new " << cfg.className << "("; + if (isString) { + stream << "KSharedConfig::openConfig(" << arg << ")"; + } else { + stream << "std::move(" << arg << ")"; + } + stream << ");" << endl; + stream << " s_global" << cfg.className << "()->q->read();" << endl; + stream << "}" << endl << endl; + }; + instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); + instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); + } +} + +void KConfigSourceGenerator::createPreamble() +{ + QString cppPreamble; + for (const auto entry : qAsConst(parseResult.entries)) { + if (entry->paramValues.isEmpty()) { + continue; + } + + cppPreamble += QStringLiteral("const char* const ") + cfg.className + QStringLiteral("::") + enumName(entry->param); + cppPreamble += cfg.globalEnums + ? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n") + : QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n"); + } + + if (!cppPreamble.isEmpty()) { + stream << cppPreamble << endl; + } +} + +void KConfigSourceGenerator::createConstructorParameterList() +{ + if (parseResult.cfgFileNameArg) { + if (!cfg.forceStringFilename) { + stream << " KSharedConfig::Ptr config"; + } else { + stream << " const QString& config"; + } + stream << (parseResult.parameters.isEmpty() ? "" : ","); + } + + for (QList::ConstIterator it = parseResult.parameters.constBegin(); + it != parseResult.parameters.constEnd(); ++it) { + if (it != parseResult.parameters.constBegin()) { + stream << ","; + } + stream << " " << param((*it).type) << " " << (*it).name; + } + + if (cfg.parentInConstructor) { + if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { + stream << ","; + } + stream << " QObject *parent"; + } + +} + +void KConfigSourceGenerator::createParentConstructorCall() +{ + stream << cfg.inherits << "("; + if (!parseResult.cfgFileName.isEmpty()) { + stream << " QStringLiteral( \"" << parseResult.cfgFileName << "\" "; + } + if (parseResult.cfgFileNameArg) { + if (! cfg.forceStringFilename) { + stream << " std::move( config ) "; + } else { + stream << " config "; + } + } + if (!parseResult.cfgFileName.isEmpty()) { + stream << ") "; + } + stream << ")" << endl; +} + +void KConfigSourceGenerator::createInitializerList() +{ + for (const auto ¶meter : qAsConst(parseResult.parameters)) { + stream << " , mParam" << parameter.name << "(" << parameter.name << ")" << endl; + } + + if (parseResult.hasNonModifySignals && !cfg.dpointer) { + stream << " , " << varName(QStringLiteral("settingsChanged"), cfg) << "(0)" << endl; + } +} + +void KConfigSourceGenerator::createEnums(const CfgEntry *entry) +{ + if (entry->type != QLatin1String("Enum")) { + return; + } + stream << " QList<" << cfg.inherits << "::ItemEnum::Choice> values" << entry->name << ";" << endl; + + for (const auto &choice : qAsConst(entry->choices.choices)) { + stream << " {" << endl; + stream << " " << cfg.inherits << "::ItemEnum::Choice choice;" << endl; + stream << " choice.name = QStringLiteral(\"" << choice.name << "\");" << endl; + if (cfg.setUserTexts) { + if (!choice.label.isEmpty()) { + stream << " choice.label = " + << translatedString(cfg, choice.label, choice.context) + << ";" << endl; + } + if (!choice.toolTip.isEmpty()) { + stream << " choice.toolTip = " + << translatedString(cfg, choice.toolTip, choice.context) + << ";" << endl; + } + if (!choice.whatsThis.isEmpty()) { + stream << " choice.whatsThis = " + << translatedString(cfg, choice.whatsThis, choice.context) + << ";" << endl; + } + } + stream << " values" << entry->name << ".append( choice );" << endl; + stream << " }" << endl; + } +} + +void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString& key) +{ + stream << " " << itemPath(entry, cfg) << " = " + << newItem(entry, key, entry->defaultValue, cfg) << endl; + + if (!entry->min.isEmpty()) { + stream << " " << itemPath(entry, cfg) << "->setMinValue(" << entry->min << ");" << endl; + } + if (!entry->max.isEmpty()) { + stream << " " << itemPath(entry, cfg) << "->setMaxValue(" << entry->max << ");" << endl; + } + + if (cfg.setUserTexts) { + stream << userTextsFunctions(entry, cfg); + } + + if (cfg.allNotifiers || cfg.notifiers.contains(entry->name)) { + stream << " " << itemPath(entry, cfg) << "->setWriteFlags(KConfigBase::Notify);" << endl; + } + + stream << " addItem( " << itemPath(entry, cfg); + QString quotedName = entry->name; + addQuotes(quotedName); + if (quotedName != key) { + stream << ", QStringLiteral( \"" << entry->name << "\" )"; + } + stream << " );" << endl; +} + +void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString& key) +{ + for (int i = 0; i <= entry->paramMax; i++) { + QString itemVarStr(itemPath(entry, cfg) + QStringLiteral("[%1]").arg(i)); + + QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i] + : !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i) + : defaultValue(entry->type); + + stream << " " << itemVarStr << " = " + << newItem(entry, paramString(key, entry, i), defaultStr, cfg, QStringLiteral("[%1]").arg(i)) << endl; + + if (cfg.setUserTexts) { + stream << userTextsFunctions(entry, cfg, itemVarStr, entry->paramName); + } + + // Make mutators for enum parameters work by adding them with $(..) replaced by the + // param name. The check for isImmutable in the set* functions doesn't have the param + // name available, just the corresponding enum value (int), so we need to store the + // param names in a separate static list!. + const bool isEnum = entry->paramType == QLatin1String("Enum"); + const QString arg = isEnum ? entry->paramValues[i] : QString::number(i); + + QString paramName = entry->paramName; + + stream << " addItem( " << itemVarStr << ", QStringLiteral( \""; + stream << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg( arg ); + stream << "\" ) );" << endl; + } +} + +void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry) +{ + if (entry->group == mCurrentGroup) { + return; + } + + // HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases. + static bool first = true; + if (!entry->group.isEmpty()) { + if (!first) { + stream << endl; + } + first = false; + } + + mCurrentGroup = entry->group; + stream << " setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );"; + stream << endl << endl; +} + +void KConfigSourceGenerator::doConstructor() +{ + // Constructor + stream << cfg.className << "::" << cfg.className << "("; + createConstructorParameterList(); + stream << " )" << endl; + stream << " : "; + createParentConstructorCall(); + createInitializerList(); + + stream << "{" << endl; + + if (cfg.parentInConstructor) { + stream << " setParent(parent);" << endl; + } + + if (cfg.dpointer) { + stream << " d = new " << cfg.className << "Private;" << endl; + if (parseResult.hasNonModifySignals) { + stream << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; + } + } + + // Needed in case the singleton class is used as baseclass for + // another singleton. + if (cfg.singleton) { + stream << " Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl; + stream << " s_global" << cfg.className << "()->q = this;" << endl; + } + + if (!parseResult.signalList.isEmpty()) { + // this cast to base-class pointer-to-member is valid C++ + // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ + stream << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" + << " static_cast(&" + << cfg.className << "::itemChanged);" << endl; + + stream << endl; + } + + for (auto *entry : qAsConst(parseResult.entries)) { + handleCurrentGroupChange(entry); + + const QString key = paramString(entry->key, parseResult.parameters); + if (!entry->code.isEmpty()) { + stream << entry->code << endl; + } + createEnums(entry); + + if (!cfg.dpointer) { + stream << itemDeclaration(entry, cfg); + } + + if (entry->param.isEmpty()) { + createNormalEntry(entry, key); + } else { + createIndexedEntry(entry, key); + } + } + + stream << "}" << endl << endl; +} + +void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry) +{ + // Accessor + if (cfg.useEnumTypes && entry->type == QLatin1String("Enum")) { + stream << enumType(entry, cfg.globalEnums); + } else { + stream << cppType(entry->type); + } + + stream << " " << getFunction(entry->name, cfg.className) << "("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const << endl; + + // function body inline only if not using dpointer + // for BC mode + startScope(); + // HACK: Fix memberAccessorBody + stream << " " << memberAccessorBody(entry, cfg.globalEnums); + endScope(); + stream << endl; +} + +void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry) +{ + // Manipulator + if (!(cfg.allMutators || cfg.mutators.contains(entry->name))) { + return; + } + + stream << "void " << setFunction(entry->name, cfg.className) << "( "; + if (!entry->param.isEmpty()) { + stream << cppType(entry->paramType) << " i, "; + } + + if (cfg.useEnumTypes && entry->type == QLatin1String("Enum")) { + stream << enumType(entry, cfg.globalEnums); + } else { + stream << param(entry->type); + } + stream << " v )" << endl; + + // function body inline only if not using dpointer + // for BC mode + startScope(); + memberMutatorBody(entry); + endScope(); + stream << endl; +} + +void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry) +{ + // Item accessor + if (!cfg.itemAccessors) { + return; + } + stream << endl; + stream << cfg.inherits << "::Item" << itemType(entry->type) << " *" + << getFunction(entry->name, cfg.className) << "Item("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << endl; + startScope(); + stream << " " << itemAccessorBody(entry, cfg); + endScope(); +} + +void KConfigSourceGenerator::doGetterSetterDPointerMode() +{ + if (!cfg.dpointer) { + return; + } + + // setters and getters go in Cpp if in dpointer mode + for (auto *entry : qAsConst(parseResult.entries)) { + createSetterDPointerMode(entry); + createGetterDPointerMode(entry); + createItemGetterDPointerMode(entry); + stream << endl; + } +} + +void KConfigSourceGenerator::createDefaultValueGetterSetter() +{ + // default value getters always go in Cpp + for (auto *entry : qAsConst(parseResult.entries)) { + QString n = entry->name; + QString t = entry->type; + + // Default value Accessor, as "helper" function + if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) { + stream << cppType(t) << " " << getDefaultFunction(n, cfg.className) << "_helper("; + if (!entry->param.isEmpty()) { + stream << " " << cppType(entry->paramType) << " i "; + } + stream << ")" << Const << endl; + startScope(); + stream << memberGetDefaultBody(entry) << endl; + endScope(); + stream << endl; + } + } +} + +void KConfigSourceGenerator::createDestructor() +{ + stream << cfg.className << "::~" << cfg.className << "()" << endl; + startScope(); + if (cfg.dpointer) { + stream << " delete d;" << endl; + } + if (cfg.singleton) { + stream << " s_global" << cfg.className << "()->q = nullptr;" << endl; + } + endScope(); + stream << endl; +} + +void KConfigSourceGenerator::createNonModifyingSignalsHelper() +{ + if (!parseResult.hasNonModifySignals) { + return; + } + stream << "bool " << cfg.className << "::" << "usrSave()" << endl; + startScope(); + stream << " const bool res = " << cfg.inherits << "::usrSave();" << endl; + stream << " if (!res) return false;" << endl << endl; + for (const Signal &signal : qAsConst(parseResult.signalList)) { + if (signal.modify) { + continue; + } + + stream << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg) << " & " << signalEnumName(signal.name) << " )" << endl; + stream << " Q_EMIT " << signal.name << "("; + QList::ConstIterator it, itEnd = signal.arguments.constEnd(); + for (it = signal.arguments.constBegin(); it != itEnd;) { + Param argument = *it; + bool cast = false; + if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { + for (int i = 0, end = parseResult.entries.count(); i < end; ++i) { + if (parseResult.entries.at(i)->name == argument.name) { + stream << "static_cast<" << enumType(parseResult.entries.at(i), cfg.globalEnums) << ">("; + cast = true; + break; + } + } + } + stream << varPath(argument.name, cfg); + if (cast) { + stream << ")"; + } + if (++it != itEnd) { + stream << ", "; + } + } + + stream << ");" << endl; + } + + stream << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; + stream << " return true;" << endl; + endScope(); +} + +void KConfigSourceGenerator::createSignalFlagsHandler() +{ + if (parseResult.signalList.isEmpty()) { + return; + } + + stream << endl; + stream << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl; + if (parseResult.hasNonModifySignals) + stream << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " |= flags;" << endl; + + if (!parseResult.signalList.isEmpty()) + stream << endl; + + for (const Signal &signal : qAsConst(parseResult.signalList)) { + if (signal.modify) { + stream << " if ( flags & " << signalEnumName(signal.name) << " ) {" << endl; + stream << " Q_EMIT " << signal.name << "();" << endl; + stream << " }" << endl; + } + } + + stream << "}" << endl; +} + +void KConfigSourceGenerator::includeMoc() { + const QString mocFileName = cfg.baseName + QStringLiteral(".moc"); + + if (parseResult.signalList.count() || cfg.generateProperties) { + // Add includemoc if they are signals defined. + stream << endl; + stream << "#include \"" << mocFileName << "\"" << endl; + stream << endl; + } +} diff --git a/src/kconfig_compiler/KConfigXTParameters.h b/src/kconfig_compiler/KConfigXTParameters.h new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigXTParameters.h @@ -0,0 +1,76 @@ +/* + This file is part of KDE. + + Copyright (c) 2019 Tomaz Canabrava + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCOFIGXTPARAMETERS_H +#define KCOFIGXTPARAMETERS_H + +#include +#include +#include + +/** + Configuration Compiler Configuration +*/ +class KConfigXTParameters +{ +public: + KConfigXTParameters(const QString &codegenFilename); + +public: + enum TranslationSystem { + QtTranslation, + KdeTranslation + }; + + // These are read from the .kcfgc configuration file + QString nameSpace; // The namespace for the class to be generated + QString className; // The class name to be generated + QString inherits; // The class the generated class inherits (if empty, from KConfigSkeleton) + QString visibility; + bool parentInConstructor; // The class has the optional parent parameter in its constructor + bool forceStringFilename; + bool singleton; // The class will be a singleton + bool staticAccessors; // provide or not static accessors + bool customAddons; + QString memberVariables; + QStringList headerIncludes; + QStringList sourceIncludes; + QStringList mutators; + QStringList defaultGetters; + QStringList notifiers; + QString qCategoryLoggingName; + QString headerExtension; + QString sourceExtension; + bool allMutators; + bool setUserTexts; + bool allDefaultGetters; + bool dpointer; + bool globalEnums; + bool useEnumTypes; + bool itemAccessors; + bool allNotifiers; + TranslationSystem translationSystem; + QString translationDomain; + bool generateProperties; + QString baseName; +}; + +#endif diff --git a/src/kconfig_compiler/KConfigXTParameters.cpp b/src/kconfig_compiler/KConfigXTParameters.cpp new file mode 100644 --- /dev/null +++ b/src/kconfig_compiler/KConfigXTParameters.cpp @@ -0,0 +1,94 @@ +/* This file is part of the KDE libraries + Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KConfigXTParameters.h" + +// TODO: Remove this. +#undef QT_NO_CAST_FROM_ASCII + +#include +#include + +namespace +{ +QTextStream cout(stdout); +QTextStream cerr(stderr); +} + +KConfigXTParameters::KConfigXTParameters(const QString &codegenFilename) +{ + if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) { + cerr << "Codegen options file must have extension .kcfgc" << endl; + exit(1); + } + + baseName = QFileInfo(codegenFilename).fileName(); + baseName = baseName.left(baseName.length() - 6); + + // Configure the compiler with some settings + QSettings codegenConfig(codegenFilename, QSettings::IniFormat); + + nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString(); + className = codegenConfig.value(QStringLiteral("ClassName")).toString(); + if (className.isEmpty()) { + cerr << "Class name missing" << endl; + exit(1); + } + inherits = codegenConfig.value(QStringLiteral("Inherits")).toString(); + if (inherits.isEmpty()) { + inherits = QStringLiteral("KConfigSkeleton"); + } + visibility = codegenConfig.value(QStringLiteral("Visibility")).toString(); + if (!visibility.isEmpty()) { + visibility += QLatin1Char(' '); + } + parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool(); + forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool(); + singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool(); + staticAccessors = singleton; + customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool(); + memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString(); + dpointer = (memberVariables == QLatin1String("dpointer")); + headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList(); + sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList(); + mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList(); + allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true"))); + itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool(); + setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool(); + defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList(); + allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true")); + notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList(); + allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true"))); + globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool(); + useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool(); + const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower(); + generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool(); + if (trString == QLatin1String("kde")) { + translationSystem = KdeTranslation; + translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString(); + } else { + if (!trString.isEmpty() && trString != QLatin1String("qt")) { + cerr << "Unknown translation system, falling back to Qt tr()" << endl; + } + translationSystem = QtTranslation; + } + qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString(); + headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString(); + sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString(); +} diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp --- a/src/kconfig_compiler/kconfig_compiler.cpp +++ b/src/kconfig_compiler/kconfig_compiler.cpp @@ -34,460 +34,27 @@ #include #include #include - -#include -#include +#include +#include #include #include #include #include "../../kconfig_version.h" +#include "KConfigXTParameters.h" +#include "KConfigCommonStructs.h" +#include "KConfigHeaderGenerator.h" +#include "KConfigSourceGenerator.h" +#include "KCFGXmlParser.h" namespace { QTextStream cout(stdout); QTextStream cerr(stderr); } -QStringList allNames; -QRegularExpression *validNameRegexp; -QString This; -QString Const; - -/** - Configuration Compiler Configuration -*/ -class CfgConfig -{ -public: - CfgConfig(const QString &codegenFilename) - { - // Configure the compiler with some settings - QSettings codegenConfig(codegenFilename, QSettings::IniFormat); - - nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString(); - className = codegenConfig.value(QStringLiteral("ClassName")).toString(); - if (className.isEmpty()) { - cerr << "Class name missing" << endl; - exit(1); - } - inherits = codegenConfig.value(QStringLiteral("Inherits")).toString(); - if (inherits.isEmpty()) { - inherits = QStringLiteral("KConfigSkeleton"); - } - visibility = codegenConfig.value(QStringLiteral("Visibility")).toString(); - if (!visibility.isEmpty()) { - visibility += ' '; - } - parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool(); - forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool(); - singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool(); - staticAccessors = singleton; - customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool(); - memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString(); - dpointer = (memberVariables == QLatin1String("dpointer")); - headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList(); - sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList(); - mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList(); - allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true"))); - itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool(); - setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool(); - defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList(); - allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true")); - notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList(); - allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true"))); - globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool(); - useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool(); - const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower(); - generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool(); - if (trString == QLatin1String("kde")) { - translationSystem = KdeTranslation; - translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString(); - } else { - if (!trString.isEmpty() && trString != QLatin1String("qt")) { - cerr << "Unknown translation system, falling back to Qt tr()" << endl; - } - translationSystem = QtTranslation; - } - qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString(); - headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString(); - sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString(); - } - -public: - enum TranslationSystem { - QtTranslation, - KdeTranslation - }; - - // These are read from the .kcfgc configuration file - QString nameSpace; // The namespace for the class to be generated - QString className; // The class name to be generated - QString inherits; // The class the generated class inherits (if empty, from KConfigSkeleton) - QString visibility; - bool parentInConstructor; // The class has the optional parent parameter in its constructor - bool forceStringFilename; - bool singleton; // The class will be a singleton - bool staticAccessors; // provide or not static accessors - bool customAddons; - QString memberVariables; - QStringList headerIncludes; - QStringList sourceIncludes; - QStringList mutators; - QStringList defaultGetters; - QStringList notifiers; - QString qCategoryLoggingName; - QString headerExtension; - QString sourceExtension; - bool allMutators; - bool setUserTexts; - bool allDefaultGetters; - bool dpointer; - bool globalEnums; - bool useEnumTypes; - bool itemAccessors; - bool allNotifiers; - TranslationSystem translationSystem; - QString translationDomain; - bool generateProperties; -}; - -struct SignalArguments { - QString type; - QString variableName; -}; - -class Signal -{ -public: - Signal() : modify(false) {} - - QString name; - QString label; - QList arguments; - bool modify; -}; - -class CfgEntry -{ -public: - struct Choice { - QString name; - QString context; - QString label; - QString toolTip; - QString whatsThis; - }; - class Choices - { - public: - Choices() {} - Choices(const QList &d, const QString &n, const QString &p) - : prefix(p), choices(d), mName(n) - { - int i = n.indexOf(QLatin1String("::")); - if (i >= 0) { - mExternalQual = n.left(i + 2); - } - } - QString prefix; - QList choices; - const QString &name() const - { - return mName; - } - const QString &externalQualifier() const - { - return mExternalQual; - } - bool external() const - { - return !mExternalQual.isEmpty(); - } - private: - QString mName; - QString mExternalQual; - }; - - CfgEntry(const QString &group, const QString &type, const QString &key, - const QString &name, const QString &labelContext, const QString &label, - const QString &toolTipContext, const QString &toolTip, const QString &whatsThisContext, const QString &whatsThis, const QString &code, - const QString &defaultValue, const Choices &choices, const QList &signalList, - bool hidden) - : mGroup(group), mType(type), mKey(key), mName(name), - mLabelContext(labelContext), mLabel(label), mToolTipContext(toolTipContext), mToolTip(toolTip), - mWhatsThisContext(whatsThisContext), mWhatsThis(whatsThis), - mCode(code), mDefaultValue(defaultValue), mChoices(choices), - mSignalList(signalList), mParamMax(0), mHidden(hidden) - { - } - - void setGroup(const QString &group) - { - mGroup = group; - } - QString group() const - { - return mGroup; - } - - void setType(const QString &type) - { - mType = type; - } - QString type() const - { - return mType; - } - - void setKey(const QString &key) - { - mKey = key; - } - QString key() const - { - return mKey; - } - - void setName(const QString &name) - { - mName = name; - } - QString name() const - { - return mName; - } - - void setLabelContext(const QString &labelContext) - { - mLabelContext = labelContext; - } - QString labelContext() const - { - return mLabelContext; - } - - void setLabel(const QString &label) - { - mLabel = label; - } - QString label() const - { - return mLabel; - } - - void setToolTipContext(const QString &toolTipContext) - { - mToolTipContext = toolTipContext; - } - QString toolTipContext() const - { - return mToolTipContext; - } - - void setToolTip(const QString &toolTip) - { - mToolTip = toolTip; - } - QString toolTip() const - { - return mToolTip; - } - - void setWhatsThisContext(const QString &whatsThisContext) - { - mWhatsThisContext = whatsThisContext; - } - QString whatsThisContext() const - { - return mWhatsThisContext; - } - - void setWhatsThis(const QString &whatsThis) - { - mWhatsThis = whatsThis; - } - QString whatsThis() const - { - return mWhatsThis; - } - - void setDefaultValue(const QString &d) - { - mDefaultValue = d; - } - QString defaultValue() const - { - return mDefaultValue; - } - - void setCode(const QString &d) - { - mCode = d; - } - QString code() const - { - return mCode; - } - - void setMinValue(const QString &d) - { - mMin = d; - } - QString minValue() const - { - return mMin; - } - - void setMaxValue(const QString &d) - { - mMax = d; - } - QString maxValue() const - { - return mMax; - } - - void setParam(const QString &d) - { - mParam = d; - } - QString param() const - { - return mParam; - } - - void setParamName(const QString &d) - { - mParamName = d; - } - QString paramName() const - { - return mParamName; - } - - void setParamType(const QString &d) - { - mParamType = d; - } - QString paramType() const - { - return mParamType; - } - - void setChoices(const QList &d, const QString &n, const QString &p) - { - mChoices = Choices(d, n, p); - } - Choices choices() const - { - return mChoices; - } - - void setParamValues(const QStringList &d) - { - mParamValues = d; - } - QStringList paramValues() const - { - return mParamValues; - } - - void setParamDefaultValues(const QStringList &d) - { - mParamDefaultValues = d; - } - QString paramDefaultValue(int i) const - { - return mParamDefaultValues[i]; - } - - void setParamMax(int d) - { - mParamMax = d; - } - int paramMax() const - { - return mParamMax; - } - - void setSignalList(const QList &value) - { - mSignalList = value; - } - QList signalList() const - { - return mSignalList; - } - - bool hidden() const - { - return mHidden; - } - - void dump() const - { - cerr << "" << endl; - cerr << " group: " << mGroup << endl; - cerr << " type: " << mType << endl; - cerr << " key: " << mKey << endl; - cerr << " name: " << mName << endl; - cerr << " label context: " << mLabelContext << endl; - cerr << " label: " << mLabel << endl; -// whatsthis - cerr << " code: " << mCode << endl; -// cerr << " values: " << mValues.join(":") << endl; - - if (!param().isEmpty()) { - cerr << " param name: " << mParamName << endl; - cerr << " param type: " << mParamType << endl; - cerr << " paramvalues: " << mParamValues.join(QChar::fromLatin1(':')) << endl; - } - cerr << " default: " << mDefaultValue << endl; - cerr << " hidden: " << mHidden << endl; - cerr << " min: " << mMin << endl; - cerr << " max: " << mMax << endl; - cerr << "" << endl; - } - -private: - QString mGroup; - QString mType; - QString mKey; - QString mName; - QString mLabelContext; - QString mLabel; - QString mToolTipContext; - QString mToolTip; - QString mWhatsThisContext; - QString mWhatsThis; - QString mCode; - QString mDefaultValue; - QString mParam; - QString mParamName; - QString mParamType; - Choices mChoices; - QList mSignalList; - QStringList mParamValues; - QStringList mParamDefaultValues; - int mParamMax; - bool mHidden; - QString mMin; - QString mMax; -}; - -class Param -{ -public: - QString name; - QString type; -}; - -// returns the name of an member variable -// use itemPath to know the full path -// like using d-> in case of dpointer -static QString varName(const QString &n, const CfgConfig &cfg) +QString varName(const QString &n, const KConfigXTParameters &cfg) { QString result; if (!cfg.dpointer) { @@ -500,7 +67,7 @@ return result; } -static QString varPath(const QString &n, const CfgConfig &cfg) +QString varPath(const QString &n, const KConfigXTParameters &cfg) { QString result; if (cfg.dpointer) { @@ -511,14 +78,14 @@ return result; } -static QString enumName(const QString &n) +QString enumName(const QString &n) { QString result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); return result; } -static QString enumName(const QString &n, const CfgEntry::Choices &c) +QString enumName(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { @@ -528,20 +95,20 @@ return result; } -static QString enumType(const CfgEntry *e, bool globalEnums) +QString enumType(const CfgEntry *e, bool globalEnums) { - QString result = e->choices().name(); + QString result = e->choices.name(); if (result.isEmpty()) { - result = QLatin1String("Enum") + e->name(); + result = QLatin1String("Enum") + e->name; if (!globalEnums) { result += QLatin1String("::type"); } result[4] = result[4].toUpper(); } return result; } -static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) +QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { @@ -555,7 +122,7 @@ return result; } -static QString setFunction(const QString &n, const QString &className = QString()) +QString setFunction(const QString &n, const QString &className) { QString result = QLatin1String("set") + n; result[3] = result[3].toUpper(); @@ -566,12 +133,12 @@ return result; } -static QString changeSignalName(const QString &n) +QString changeSignalName(const QString &n) { return n+QStringLiteral("Changed"); } -static QString getDefaultFunction(const QString &n, const QString &className = QString()) +QString getDefaultFunction(const QString &n, const QString &className) { QString result = QLatin1String("default") + n + QLatin1String("Value"); result[7] = result[7].toUpper(); @@ -582,7 +149,7 @@ return result; } -static QString getFunction(const QString &n, const QString &className = QString()) +QString getFunction(const QString &n, const QString &className) { QString result = n; result[0] = result[0].toLower(); @@ -593,7 +160,7 @@ return result; } -static void addQuotes(QString &s) +void addQuotes(QString &s) { if (!s.startsWith(QLatin1Char('"'))) { s.prepend(QLatin1Char('"')); @@ -613,7 +180,7 @@ return QLatin1Char('\"') + r + QLatin1Char('\"'); } -static QString literalString(const QString &s) +QString literalString(const QString &s) { bool isAscii = true; for (int i = s.length(); i--;) @@ -628,367 +195,18 @@ } } -static QString dumpNode(const QDomNode &node) -{ - QString msg; - QTextStream s(&msg, QIODevice::WriteOnly); - node.save(s, 0); - - msg = msg.simplified(); - if (msg.length() > 40) { - return msg.left(37) + QLatin1String("..."); - } - return msg; -} -static QString signalEnumName(const QString &signalName) +QString signalEnumName(const QString &signalName) { QString result; result = QLatin1String("signal") + signalName; result[6] = result[6].toUpper(); return result; } -static void preProcessDefault(QString &defaultValue, const QString &name, - const QString &type, - const CfgEntry::Choices &choices, - QString &code, const CfgConfig &cfg) -{ - if (type == QLatin1String("String") && !defaultValue.isEmpty()) { - defaultValue = literalString(defaultValue); - - } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { - defaultValue = literalString(defaultValue); - } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { - // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. - defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); - } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { - QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); - if (!code.isEmpty()) { - cpp << endl; - } - - if (type == QLatin1String("UrlList")) { - cpp << " QList default" << name << ";" << endl; - } else { - cpp << " QStringList default" << name << ";" << endl; - } - const QStringList defaults = defaultValue.split(QLatin1Char(',')); - QStringList::ConstIterator it; - for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { - cpp << " default" << name << ".append( "; - if (type == QLatin1String("UrlList")) { - cpp << "QUrl::fromUserInput("; - } - cpp << "QString::fromUtf8( \"" << *it << "\" ) "; - if (type == QLatin1String("UrlList")) { - cpp << ") "; - } - cpp << ");" << endl; - } - defaultValue = QLatin1String("default") + name; - - } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { - const QRegularExpression colorRe(QRegularExpression::anchoredPattern( - QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); - - if (colorRe.match(defaultValue).hasMatch()) { - defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); - } else { - defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); - } - - } else if (type == QLatin1String("Enum")) { - QList::ConstIterator it; - for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { - if ((*it).name == defaultValue) { - if (cfg.globalEnums && choices.name().isEmpty()) { - defaultValue.prepend(choices.prefix); - } else { - defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); - } - break; - } - } - - } else if (type == QLatin1String("IntList")) { - QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); - if (!code.isEmpty()) { - cpp << endl; - } - - cpp << " QList default" << name << ";" << endl; - if (!defaultValue.isEmpty()) { - const QStringList defaults = defaultValue.split(QLatin1Char(',')); - QStringList::ConstIterator it; - for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { - cpp << " default" << name << ".append( " << *it << " );" - << endl; - } - } - defaultValue = QLatin1String("default") + name; - } -} - -CfgEntry *parseEntry(const QString &group, const QDomElement &element, const CfgConfig &cfg) -{ - bool defaultCode = false; - QString type = element.attribute(QStringLiteral("type")); - QString name = element.attribute(QStringLiteral("name")); - QString key = element.attribute(QStringLiteral("key")); - QString hidden = element.attribute(QStringLiteral("hidden")); - QString labelContext; - QString label; - QString toolTipContext; - QString toolTip; - QString whatsThisContext; - QString whatsThis; - QString defaultValue; - QString code; - QString param; - QString paramName; - QString paramType; - CfgEntry::Choices choices; - QList signalList; - QStringList paramValues; - QStringList paramDefaultValues; - QString minValue; - QString maxValue; - int paramMax = 0; - - for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { - QString tag = e.tagName(); - if (tag == QLatin1String("label")) { - label = e.text(); - labelContext = e.attribute(QStringLiteral("context")); - } else if (tag == QLatin1String("tooltip")) { - toolTip = e.text(); - toolTipContext = e.attribute(QStringLiteral("context")); - } else if (tag == QLatin1String("whatsthis")) { - whatsThis = e.text(); - whatsThisContext = e.attribute(QStringLiteral("context")); - } else if (tag == QLatin1String("min")) { - minValue = e.text(); - } else if (tag == QLatin1String("max")) { - maxValue = e.text(); - } else if (tag == QLatin1String("code")) { - code = e.text(); - } else if (tag == QLatin1String("parameter")) { - param = e.attribute(QStringLiteral("name")); - paramType = e.attribute(QStringLiteral("type")); - if (param.isEmpty()) { - cerr << "Parameter must have a name: " << dumpNode(e) << endl; - return nullptr; - } - if (paramType.isEmpty()) { - cerr << "Parameter must have a type: " << dumpNode(e) << endl; - return nullptr; - } - if ((paramType == QLatin1String("Int")) || (paramType == QLatin1String("UInt"))) { - bool ok; - paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); - if (!ok) { - cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " - << dumpNode(e) << endl; - return nullptr; - } - } else if (paramType == QLatin1String("Enum")) { - for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { - if (e2.tagName() == QLatin1String("values")) { - for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { - if (e3.tagName() == QLatin1String("value")) { - paramValues.append(e3.text()); - } - } - break; - } - } - if (paramValues.isEmpty()) { - cerr << "No values specified for parameter '" << param - << "'." << endl; - return nullptr; - } - paramMax = paramValues.count() - 1; - } else { - cerr << "Parameter '" << param << "' has type " << paramType - << " but must be of type int, uint or Enum." << endl; - return nullptr; - } - } else if (tag == QLatin1String("default")) { - if (e.attribute(QStringLiteral("param")).isEmpty()) { - defaultValue = e.text(); - if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { - defaultCode = true; - } - } - } else if (tag == QLatin1String("choices")) { - QString name = e.attribute(QStringLiteral("name")); - QString prefix = e.attribute(QStringLiteral("prefix")); - QList chlist; - for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { - if (e2.tagName() == QLatin1String("choice")) { - CfgEntry::Choice choice; - choice.name = e2.attribute(QStringLiteral("name")); - if (choice.name.isEmpty()) { - cerr << "Tag requires attribute 'name'." << endl; - } - for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { - if (e3.tagName() == QLatin1String("label")) { - choice.label = e3.text(); - choice.context = e3.attribute(QStringLiteral("context")); - } - if (e3.tagName() == QLatin1String("tooltip")) { - choice.toolTip = e3.text(); - choice.context = e3.attribute(QStringLiteral("context")); - } - if (e3.tagName() == QLatin1String("whatsthis")) { - choice.whatsThis = e3.text(); - choice.context = e3.attribute(QStringLiteral("context")); - } - } - chlist.append(choice); - } - } - choices = CfgEntry::Choices(chlist, name, prefix); - } else if (tag == QLatin1String("emit")) { - Signal signal; - signal.name = e.attribute(QStringLiteral("signal")); - signalList.append(signal); - } - } - - if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(name))) { - Signal s; - s.name = changeSignalName(name); - s.modify = true; - signalList.append(s); - } - - bool nameIsEmpty = name.isEmpty(); - if (nameIsEmpty && key.isEmpty()) { - cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; - return nullptr; - } - - if (key.isEmpty()) { - key = name; - } - - if (nameIsEmpty) { - name = key; - name.remove(' '); - } else if (name.contains(' ')) { - cout << "Entry '" << name << "' contains spaces! elements can not contain spaces!" << endl; - name.remove(' '); - } - - if (name.contains(QStringLiteral("$("))) { - if (param.isEmpty()) { - cerr << "Name may not be parameterized: " << name << endl; - return nullptr; - } - } else { - if (!param.isEmpty()) { - cerr << "Name must contain '$(" << param << ")': " << name << endl; - return nullptr; - } - } - - if (label.isEmpty()) { - label = key; - } - - if (type.isEmpty()) { - type = QStringLiteral("String"); // XXX : implicit type might be bad - } - - if (!param.isEmpty()) { - // Adjust name - paramName = name; - name.remove("$(" + param + ')'); - // Lookup defaults for indexed entries - for (int i = 0; i <= paramMax; i++) { - paramDefaultValues.append(QString()); - } - - for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { - QString tag = e.tagName(); - if (tag == QLatin1String("default")) { - QString index = e.attribute(QStringLiteral("param")); - if (index.isEmpty()) { - continue; - } - - bool ok; - int i = index.toInt(&ok); - if (!ok) { - i = paramValues.indexOf(index); - if (i == -1) { - cerr << "Index '" << index << "' for default value is unknown." << endl; - return nullptr; - } - } - - if ((i < 0) || (i > paramMax)) { - cerr << "Index '" << i << "' for default value is out of range [0, " << paramMax << "]." << endl; - return nullptr; - } - - QString tmpDefaultValue = e.text(); - - if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { - preProcessDefault(tmpDefaultValue, name, type, choices, code, cfg); - } - - paramDefaultValues[i] = tmpDefaultValue; - } - } - } - - if (!validNameRegexp->match(name).hasMatch()) { - if (nameIsEmpty) - cerr << "The key '" << key << "' can not be used as name for the entry because " - "it is not a valid name. You need to specify a valid name for this entry." << endl; - else { - cerr << "The name '" << name << "' is not a valid name for an entry." << endl; - } - return nullptr; - } - - if (allNames.contains(name)) { - if (nameIsEmpty) - cerr << "The key '" << key << "' can not be used as name for the entry because " - "it does not result in a unique name. You need to specify a unique name for this entry." << endl; - else { - cerr << "The name '" << name << "' is not unique." << endl; - } - return nullptr; - } - allNames.append(name); - - if (!defaultCode) { - preProcessDefault(defaultValue, name, type, choices, code, cfg); - } - - CfgEntry *result = new CfgEntry(group, type, key, name, labelContext, label, toolTipContext, toolTip, whatsThisContext, whatsThis, - code, defaultValue, choices, signalList, - hidden == QLatin1String("true")); - if (!param.isEmpty()) { - result->setParam(param); - result->setParamName(paramName); - result->setParamType(paramType); - result->setParamValues(paramValues); - result->setParamDefaultValues(paramDefaultValues); - result->setParamMax(paramMax); - } - result->setMinValue(minValue); - result->setMaxValue(maxValue); - return result; -} - -static bool isUnsigned(const QString &type) +bool isUnsigned(const QString &type) { if (type == QLatin1String("UInt")) { return true; @@ -1168,47 +386,47 @@ return t; } -static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg) +QString itemDeclaration(const CfgEntry *e, const KConfigXTParameters &cfg) { if (cfg.itemAccessors) { return QString(); } QString type; - if (!e->signalList().isEmpty()) { + if (!e->signalList.isEmpty()) { type = QStringLiteral("KConfigCompilerSignallingItem"); } else { - type = cfg.inherits + "::Item" + itemType(e->type()); + type = cfg.inherits + "::Item" + itemType(e->type); } - QString fCap = e->name(); + QString fCap = e->name; fCap[0] = fCap[0].toUpper(); return " " + type + " *item" + fCap + - ( (!e->param().isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax()+1)) : QString()) + ";\n"; + ( (!e->param.isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax+1)) : QString()) + ";\n"; } // returns the name of an item variable // use itemPath to know the full path // like using d-> in case of dpointer -static QString itemVar(const CfgEntry *e, const CfgConfig &cfg) +QString itemVar(const CfgEntry *e, const KConfigXTParameters &cfg) { QString result; if (cfg.itemAccessors) { if (!cfg.dpointer) { - result = 'm' + e->name() + "Item"; + result = 'm' + e->name + "Item"; result[1] = result[1].toUpper(); } else { - result = e->name() + "Item"; + result = e->name + "Item"; result[0] = result[0].toLower(); } } else { - result = "item" + e->name(); + result = "item" + e->name; result[4] = result[4].toUpper(); } return result; } -static QString itemPath(const CfgEntry *e, const CfgConfig &cfg) +QString itemPath(const CfgEntry *e, const KConfigXTParameters &cfg) { QString result; if (cfg.dpointer) { @@ -1220,18 +438,18 @@ } QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue, - const CfgConfig &cfg, const QString ¶m = QString()) { + const KConfigXTParameters &cfg, const QString ¶m) { - QList sigs = entry->signalList(); + QList sigs = entry->signalList; QString t; if (!sigs.isEmpty()) { t += QLatin1String("new KConfigCompilerSignallingItem("); } - t += "new "+ cfg.inherits + "::Item" + itemType(entry->type()) + "( currentGroup(), " - + key + ", " + varPath( entry->name(), cfg ) + param; + t += "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), " + + key + ", " + varPath( entry->name, cfg ) + param; - if (entry->type() == QLatin1String("Enum")) { - t += ", values" + entry->name(); + if (entry->type == QLatin1String("Enum")) { + t += ", values" + entry->name; } if (!defaultValue.isEmpty()) { t += QLatin1String(", ") + defaultValue; @@ -1255,11 +473,11 @@ QString paramString(const QString &s, const CfgEntry *e, int i) { QString result = s; - QString needle = "$(" + e->param() + ')'; + QString needle = "$(" + e->param + ')'; if (result.contains(needle)) { QString tmp; - if (e->paramType() == QLatin1String("Enum")) { - tmp = e->paramValues().at(i); + if (e->paramType == QLatin1String("Enum")) { + tmp = e->paramValues.at(i); } else { tmp = QString::number(i); } @@ -1289,21 +507,21 @@ return "QStringLiteral( \"" + paramString + "\" )" + arguments; } -QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString ¶m = QString(), const QString ¶mValue = QString()) +QString translatedString(const KConfigXTParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue) { QString result; switch (cfg.translationSystem) { - case CfgConfig::QtTranslation: + case KConfigXTParameters::QtTranslation: if (!context.isEmpty()) { result += "/*: " + context + " */ QCoreApplication::translate(\""; } else { result += QLatin1String("QCoreApplication::translate(\""); } result += cfg.className + "\", "; break; - case CfgConfig::KdeTranslation: + case KConfigXTParameters::KdeTranslation: if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) { result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", "; } else if (!cfg.translationDomain.isEmpty()) { @@ -1330,180 +548,83 @@ } /* int i is the value of the parameter */ -QString userTextsFunctions(CfgEntry *e, const CfgConfig &cfg, QString itemVarStr = QString(), const QString &i = QString()) +QString userTextsFunctions(const CfgEntry *e, const KConfigXTParameters &cfg, QString itemVarStr, const QString &i) { QString txt; if (itemVarStr.isNull()) { itemVarStr = itemPath(e, cfg); } - if (!e->label().isEmpty()) { + if (!e->label.isEmpty()) { txt += " " + itemVarStr + "->setLabel( "; - txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i); + txt += translatedString(cfg, e->label, e->labelContext, e->param, i); txt += QLatin1String(" );\n"); } - if (!e->toolTip().isEmpty()) { + if (!e->toolTip.isEmpty()) { txt += " " + itemVarStr + "->setToolTip( "; - txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i); + txt += translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i); txt += QLatin1String(" );\n"); } - if (!e->whatsThis().isEmpty()) { + if (!e->whatsThis.isEmpty()) { txt += " " + itemVarStr + "->setWhatsThis( "; - txt += translatedString(cfg, e->whatsThis(), e->whatsThisContext(), e->param(), i); + txt += translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i); txt += QLatin1String(" );\n"); } return txt; } -// returns the member accesor implementation -// which should go in the h file if inline -// or the cpp file if not inline -QString memberAccessorBody(CfgEntry *e, bool globalEnums, const CfgConfig &cfg) -{ - QString result; - QTextStream out(&result, QIODevice::WriteOnly); - QString n = e->name(); - QString t = e->type(); - bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum"); - - out << "return "; - if (useEnumType) { - out << "static_cast<" << enumType(e, globalEnums) << ">("; - } - out << This << varPath(n, cfg); - if (!e->param().isEmpty()) { - out << "[i]"; - } - if (useEnumType) { - out << ")"; - } - out << ";" << endl; - - return result; -} // returns the member mutator implementation // which should go in the h file if inline // or the cpp file if not inline - -void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &n) +//TODO: Fix add Debug Method, it should also take the debug string. +void addDebugMethod(QTextStream &out, const KConfigXTParameters &cfg, const QString &n) { if (cfg.qCategoryLoggingName.isEmpty()) { out << " qDebug() << \"" << setFunction(n); } else { out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n); } } -QString memberMutatorBody(CfgEntry *e, const CfgConfig &cfg) -{ - QString result; - QTextStream out(&result, QIODevice::WriteOnly); - QString n = e->name(); - QString t = e->type(); - - if (!e->minValue().isEmpty()) { - if (e->minValue() != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) - out << "if (v < " << e->minValue() << ")" << endl; - out << "{" << endl; - addDebugMethod(out, cfg, n); - out << ": value \" << v << \" is less than the minimum value of "; - out << e->minValue() << "\";" << endl; - out << " v = " << e->minValue() << ";" << endl; - out << "}" << endl; - } - } - - if (!e->maxValue().isEmpty()) { - out << endl << "if (v > " << e->maxValue() << ")" << endl; - out << "{" << endl; - addDebugMethod(out, cfg, n); - out << ": value \" << v << \" is greater than the maximum value of "; - out << e->maxValue() << "\";" << endl; - out << " v = " << e->maxValue() << ";" << endl; - out << "}" << endl << endl; - } - - const QString varExpression = This + varPath(n, cfg) + (e->param().isEmpty() ? QString() : QStringLiteral("[i]")); - - const bool hasBody = !e->signalList().empty() || cfg.generateProperties; - out << "if ("; - if (hasBody) { - out << "v != " << varExpression << " && "; - } - out << "!" << This << "isImmutable( QStringLiteral( \""; - if (!e->param().isEmpty()) { - out << e->paramName().replace("$(" + e->param() + ")", QLatin1String("%1")) << "\" ).arg( "; - if (e->paramType() == QLatin1String("Enum")) { - out << "QLatin1String( "; - - if (cfg.globalEnums) { - out << enumName(e->param()) << "ToString[i]"; - } else { - out << enumName(e->param()) << "::enumToString[i]"; - } - - out << " )"; - } else { - out << "i"; - } - out << " )"; - } else { - out << n << "\" )"; - } - out << " ))" << (hasBody ? " {" : "") << endl; - out << " " << varExpression << " = v;" << endl; - - const auto listSignal = e->signalList(); - for (const Signal &signal : listSignal) { - if (signal.modify) { - out << " Q_EMIT " << This << signal.name << "();" << endl; - } else { - out << " " << This << varPath(QStringLiteral("settingsChanged"), cfg) << " |= " << signalEnumName(signal.name) << ";" << endl; - } - } - if (hasBody) { - out << "}" << endl; - } - - return result; -} // returns the member get default implementation // which should go in the h file if inline // or the cpp file if not inline -QString memberGetDefaultBody(CfgEntry *e) +QString memberGetDefaultBody(const CfgEntry *e) { - QString result = e->code(); + QString result = e->code; QTextStream out(&result, QIODevice::WriteOnly); out << endl; - if (!e->param().isEmpty()) { + if (!e->param.isEmpty()) { out << " switch (i) {" << endl; - for (int i = 0; i <= e->paramMax(); ++i) { - if (!e->paramDefaultValue(i).isEmpty()) { - out << " case " << i << ": return " << e->paramDefaultValue(i) << ';' << endl; + for (int i = 0; i <= e->paramMax; ++i) { + if (!e->paramDefaultValues[i].isEmpty()) { + out << " case " << i << ": return " << e->paramDefaultValues[i] << ';' << endl; } } + QString defaultValue = e->defaultValue; + out << " default:" << endl; - out << " return " << e->defaultValue().replace("$(" + e->param() + ')', QLatin1String("i")) << ';' << endl; + out << " return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ';' << endl; out << " }" << endl; } else { - out << " return " << e->defaultValue() << ';'; + out << " return " << e->defaultValue << ';'; } return result; } // returns the item accesor implementation // which should go in the h file if inline // or the cpp file if not inline -QString itemAccessorBody(CfgEntry *e, const CfgConfig &cfg) +QString itemAccessorBody(const CfgEntry *e, const KConfigXTParameters &cfg) { QString result; QTextStream out(&result, QIODevice::WriteOnly); out << "return " << itemPath(e, cfg); - if (!e->param().isEmpty()) { + if (!e->param.isEmpty()) { out << "[i]"; } out << ";" << endl; @@ -1529,62 +650,61 @@ return result; } -// adds as many 'namespace foo {' lines to p_out as -// there are namespaces in p_ns -void beginNamespaces(const QString &p_ns, QTextStream &p_out) +bool hasErrors(KCFGXmlParser &parser, const ParseResult& parseResult, const KConfigXTParameters &cfg) { - if (!p_ns.isEmpty()) { - const QStringList nameSpaces = p_ns.split(QStringLiteral("::")); - for (const QString &ns : nameSpaces) { - p_out << "namespace " << ns << " {" << endl; - } - p_out << endl; + if (cfg.className.isEmpty()) { + cerr << "Class name missing" << endl; + return true; } -} -// adds as many '}' lines to p_out as -// there are namespaces in p_ns -void endNamespaces(const QString &p_ns, QTextStream &p_out) -{ - if (!p_ns.isEmpty()) { - const int namespaceCount = p_ns.count(QStringLiteral("::")) + 1; - for (int i = 0; i < namespaceCount; ++i) { - p_out << "}" << endl; - } - p_out << endl; + if (cfg.singleton && !parseResult.parameters.isEmpty()) { + cerr << "Singleton class can not have parameters" << endl; + return true; } + + if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) { + cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; + return true; + } + + if (parseResult.entries.isEmpty()) { + cerr << "No entries." << endl; + return true; + } + + return false; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kconfig_compiler")); app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING)); - validNameRegexp = new QRegularExpression(QRegularExpression::anchoredPattern( - QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); - QString inputFilename, codegenFilename; + QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, + QCoreApplication::translate("main", "Directory to generate files in [.]"), + QCoreApplication::translate("main", "directory"), QStringLiteral(".")); + + QCommandLineOption licenseOption ( + QStringList { QStringLiteral("l"), QStringLiteral("license") }, + QCoreApplication::translate("main", "Display software license.")); + QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file")); parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file")); - QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, - QCoreApplication::translate("main", "Directory to generate files in [.]"), - QCoreApplication::translate("main", "directory"), QStringLiteral(".")); parser.addOption(targetDirectoryOption); - - QCommandLineOption licenseOption (QStringList { QStringLiteral("l"), QStringLiteral("license") }, QCoreApplication::translate("main", "Display software license.")); parser.addOption (licenseOption); parser.addVersionOption(); parser.addHelpOption(); parser.process(app); if (parser.isSet(licenseOption)) { - cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl; + cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl; cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << endl; cout << "This program comes with ABSOLUTELY NO WARRANTY." << endl; cout << "You may redistribute copies of this program" << endl; @@ -1595,16 +715,18 @@ const QStringList args = parser.positionalArguments(); if (args.count() < 2) { - cerr << "Too few arguments." << endl; - return 1; + cerr << "Too few arguments." << endl; + return 1; } + if (args.count() > 2) { - cerr << "Too many arguments." << endl; - return 1; + cerr << "Too many arguments." << endl; + return 1; } inputFilename = args.at(0); codegenFilename = args.at(1); + // TODO: Transform baseDir into a helper. QString baseDir = parser.value(targetDirectoryOption); #ifdef Q_OS_WIN @@ -1614,1124 +736,34 @@ #endif baseDir.append("/"); - if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) { - cerr << "Codegen options file must have extension .kcfgc" << endl; - return 1; - } - QString baseName = QFileInfo(codegenFilename).fileName(); - baseName = baseName.left(baseName.length() - 6); + KConfigXTParameters cfg(codegenFilename); - CfgConfig cfg = CfgConfig(codegenFilename); + KCFGXmlParser xmlParser(cfg, inputFilename); + + // The Xml Parser aborts in the case of an error, so if we get + // to parseResult, we have a working Xml file. + xmlParser.start(); - QFile input(inputFilename); + ParseResult parseResult = xmlParser.getParseResult(); - QDomDocument doc; - QString errorMsg; - int errorRow; - int errorCol; - if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { - cerr << "Unable to load document." << endl; - cerr << "Parse error in " << inputFilename << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; + if (hasErrors(xmlParser, parseResult, cfg)) { return 1; } - QDomElement cfgElement = doc.documentElement(); - - if (cfgElement.isNull()) { - cerr << "No document in kcfg file" << endl; - return 1; + // TODO: Move this to somewhere saner. + for (const auto &signal : qAsConst(parseResult.signalList)) { + parseResult.hasNonModifySignals |= !signal.modify; } - QString cfgFileName; - bool cfgFileNameArg = false; - QList parameters; - QList signalList; - QStringList includes; - - QList entries; - - for (QDomElement e = cfgElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { - QString tag = e.tagName(); - - if (tag == QLatin1String("include")) { - QString includeFile = e.text(); - if (!includeFile.isEmpty()) { - includes.append(includeFile); - } - - } else if (tag == QLatin1String("kcfgfile")) { - cfgFileName = e.attribute(QStringLiteral("name")); - cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); - for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { - if (e2.tagName() == QLatin1String("parameter")) { - Param p; - p.name = e2.attribute(QStringLiteral("name")); - p.type = e2.attribute(QStringLiteral("type")); - if (p.type.isEmpty()) { - p.type = QStringLiteral("String"); - } - parameters.append(p); - } - } - - } else if (tag == QLatin1String("group")) { - QString group = e.attribute(QStringLiteral("name")); - if (group.isEmpty()) { - cerr << "Group without name" << endl; - return 1; - } - for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { - if (e2.tagName() != QLatin1String("entry")) { - continue; - } - CfgEntry *entry = parseEntry(group, e2, cfg); - if (entry) { - entries.append(entry); - } else { - cerr << "Can not parse entry." << endl; - return 1; - } - } - } else if (tag == QLatin1String("signal")) { - QString signalName = e.attribute(QStringLiteral("name")); - if (signalName.isEmpty()) { - cerr << "Signal without name." << endl; - return 1; - } - Signal theSignal; - theSignal.name = signalName; - - for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { - if (e2.tagName() == QLatin1String("argument")) { - SignalArguments argument; - argument.type = e2.attribute(QStringLiteral("type")); - if (argument.type.isEmpty()) { - cerr << "Signal argument without type." << endl; - return 1; - } - argument.variableName = e2.text(); - theSignal.arguments.append(argument); - } else if (e2.tagName() == QLatin1String("label")) { - theSignal.label = e2.text(); - } - } - signalList.append(theSignal); - } - } - - if (cfg.className.isEmpty()) { - cerr << "Class name missing" << endl; - return 1; - } - - if (cfg.singleton && !parameters.isEmpty()) { - cerr << "Singleton class can not have parameters" << endl; - return 1; - } - - if (!cfgFileName.isEmpty() && cfgFileNameArg) { - cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; - return 1; - } - - if (entries.isEmpty()) { - cerr << "No entries." << endl; - } - -#if 0 - CfgEntry *cfg; - for (cfg = entries.first(); cfg; cfg = entries.next()) { - cfg->dump(); - } -#endif - - QString headerFileName = baseName + '.' + cfg.headerExtension; - QString implementationFileName = baseName + '.' + cfg.sourceExtension; - QString mocFileName = baseName + ".moc"; - QString cppPreamble; // code to be inserted at the beginnin of the cpp file, e.g. initialization of static values - - QFile header(baseDir + headerFileName); - if (!header.open(QIODevice::WriteOnly)) { - cerr << "Can not open '" << baseDir << headerFileName << "for writing." << endl; - return 1; - } - - QTextStream h(&header); - - h.setCodec("utf-8"); - - h << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl; - h << "// All changes you do to this file will be lost." << endl; - - h << "#ifndef " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String("")) - << cfg.className.toUpper() << "_H" << endl; - h << "#define " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String("")) - << cfg.className.toUpper() << "_H" << endl << endl; - - // Includes - QStringList::ConstIterator it; - for (it = cfg.headerIncludes.constBegin(); it != cfg.headerIncludes.constEnd(); ++it) { - if ((*it).startsWith('"')) { - h << "#include " << *it << endl; - } else { - h << "#include <" << *it << ">" << endl; - } - } - - if (!cfg.headerIncludes.isEmpty()) { - h << endl; - } - - if (!cfg.singleton && parameters.isEmpty()) { - h << "#include " << endl; - } - - if (cfg.inherits == QLatin1String("KCoreConfigSkeleton")) { - h << "#include " << endl; - } else { - h << "#include " << endl; - } - - h << "#include " << endl; - h << "#include " << endl << endl; - - // Includes - for (it = includes.constBegin(); it != includes.constEnd(); ++it) { - if ((*it).startsWith('"')) { - h << "#include " << *it << endl; - } else { - h << "#include <" << *it << ">" << endl; - } - } - - beginNamespaces(cfg.nameSpace, h); - - // Private class declaration - if (cfg.dpointer) { - h << "class " << cfg.className << "Private;" << endl << endl; - } - - // Class declaration header - h << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits << endl; - - h << "{" << endl; - // Add Q_OBJECT macro if the config need signals. - if (!signalList.isEmpty() || cfg.generateProperties) { - h << " Q_OBJECT" << endl; - } - h << " public:" << endl; - - // enums - QList::ConstIterator itEntry; - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - const CfgEntry::Choices &choices = (*itEntry)->choices(); - const QList chlist = choices.choices; - if (!chlist.isEmpty()) { - QStringList values; - QList::ConstIterator itChoice; - for (itChoice = chlist.constBegin(); itChoice != chlist.constEnd(); ++itChoice) { - values.append(choices.prefix + (*itChoice).name); - } - if (choices.name().isEmpty()) { - if (cfg.globalEnums) { - h << " enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; - } else { - // Create an automatically named enum - h << " class " << enumName((*itEntry)->name(), (*itEntry)->choices()) << endl; - h << " {" << endl; - h << " public:" << endl; - h << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; - h << " };" << endl; - } - } else if (!choices.external()) { - // Create a named enum - h << " enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; - } - } - const QStringList values = (*itEntry)->paramValues(); - if (!values.isEmpty()) { - if (cfg.globalEnums) { - // ### FIXME!! - // make the following string table an index-based string search! - // ### - h << " enum " << enumName((*itEntry)->param()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; - h << " static const char* const " << enumName((*itEntry)->param()) << "ToString[];" << endl; - cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) + - "ToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n"; - } else { - h << " class " << enumName((*itEntry)->param()) << endl; - h << " {" << endl; - h << " public:" << endl; - h << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; - h << " static const char* const enumToString[];" << endl; - h << " };" << endl; - cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) + - "::enumToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n"; - } - } - } - h << endl; - - - // Constructor or singleton accessor - if (!cfg.singleton) { - h << " " << cfg.className << "("; - if (cfgFileNameArg) { - if (cfg.forceStringFilename) - h << " const QString &cfgfilename" - << (parameters.isEmpty() ? " = QString()" : ", "); - else - h << " KSharedConfig::Ptr config" - << (parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); - } - for (QList::ConstIterator it = parameters.constBegin(); - it != parameters.constEnd(); ++it) { - if (it != parameters.constBegin()) { - h << ","; - } - h << " " << param((*it).type) << " " << (*it).name; - } - if (cfg.parentInConstructor) { - if (cfgFileNameArg || !parameters.isEmpty()) { - h << ","; - } - h << " QObject *parent = nullptr"; - } - h << " );" << endl; - } else { - h << " static " << cfg.className << " *self();" << endl; - if (cfgFileNameArg) { - h << " static void instance(const QString& cfgfilename);" << endl; - h << " static void instance(KSharedConfig::Ptr config);" << endl; - } - } - - // Destructor - h << " ~" << cfg.className << "();" << endl << endl; - - // global variables - if (cfg.staticAccessors) { - This = QStringLiteral("self()->"); - } else { - Const = QStringLiteral(" const"); - } - - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - QString n = (*itEntry)->name(); - QString t = (*itEntry)->type(); - - // Manipulator - if (cfg.allMutators || cfg.mutators.contains(n)) { - h << " /**" << endl; - h << " Set " << (*itEntry)->label() << endl; - h << " */" << endl; - if (cfg.staticAccessors) { - h << " static" << endl; - } - h << " void " << setFunction(n) << "( "; - if (!(*itEntry)->param().isEmpty()) { - h << cppType((*itEntry)->paramType()) << " i, "; - } - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - h << enumType(*itEntry, cfg.globalEnums); - } else { - h << param(t); - } - h << " v )"; - // function body inline only if not using dpointer - // for BC mode - if (!cfg.dpointer) { - h << endl << " {" << endl; - h << indent(memberMutatorBody(*itEntry, cfg), 6); - h << " }" << endl; - } else { - h << ";" << endl; - } - } - h << endl; - - QString returnType; - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - returnType = enumType(*itEntry, cfg.globalEnums); - } else { - returnType = cppType(t); - } - - if (cfg.generateProperties) { - h << " Q_PROPERTY(" << returnType << ' ' << getFunction(n); - h << " READ " << getFunction(n); - if (cfg.allMutators || cfg.mutators.contains(n)) { - const QString signal = changeSignalName(n); - h << " WRITE " << setFunction(n); - h << " NOTIFY " << signal; - - //If we have the modified signal, we'll also need - //the changed signal as well - Signal s; - s.name = signal; - s.modify = true; - signalList.append(s); - } else { - h << " CONSTANT"; - } - h << ")" << endl; - } - // Accessor - h << " /**" << endl; - h << " Get " << (*itEntry)->label() << endl; - h << " */" << endl; - if (cfg.staticAccessors) { - h << " static" << endl; - } - h << " "; - h << returnType; - h << " " << getFunction(n) << "("; - if (!(*itEntry)->param().isEmpty()) { - h << " " << cppType((*itEntry)->paramType()) << " i "; - } - h << ")" << Const; - // function body inline only if not using dpointer - // for BC mode - if (!cfg.dpointer) { - h << endl << " {" << endl; - h << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 6); - h << " }" << endl; - } else { - h << ";" << endl; - } - - // Default value Accessor - if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) { - h << endl; - h << " /**" << endl; - h << " Get " << (*itEntry)->label() << " default value" << endl; - h << " */" << endl; - if (cfg.staticAccessors) { - h << " static" << endl; - } - h << " "; - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - h << enumType(*itEntry, cfg.globalEnums); - } else { - h << cppType(t); - } - h << " " << getDefaultFunction(n) << "("; - if (!(*itEntry)->param().isEmpty()) { - h << " " << cppType((*itEntry)->paramType()) << " i "; - } - h << ")" << Const << endl; - h << " {" << endl; - h << " return "; - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - h << "static_cast<" << enumType(*itEntry, cfg.globalEnums) << ">("; - } - h << getDefaultFunction(n) << "_helper("; - if (!(*itEntry)->param().isEmpty()) { - h << " i "; - } - h << ")"; - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - h << ")"; - } - h << ";" << endl; - h << " }" << endl; - } - - // Item accessor - if (cfg.itemAccessors) { - h << endl; - h << " /**" << endl; - h << " Get Item object corresponding to " << n << "()" - << endl; - h << " */" << endl; - h << " Item" << itemType((*itEntry)->type()) << " *" - << getFunction(n) << "Item("; - if (!(*itEntry)->param().isEmpty()) { - h << " " << cppType((*itEntry)->paramType()) << " i "; - } - h << ")"; - if (!cfg.dpointer) { - h << endl << " {" << endl; - h << indent(itemAccessorBody((*itEntry), cfg), 6); - h << " }" << endl; - } else { - h << ";" << endl; - } - } - - h << endl; - } - - // Signal definition. - const bool hasSignals = !signalList.isEmpty(); - bool hasNonModifySignals = false; - if (hasSignals) { - h << "\n enum {" << endl; - unsigned val = 1; - QList::ConstIterator it, itEnd = signalList.constEnd(); - for (it = signalList.constBegin(); it != itEnd; val <<= 1) { - hasNonModifySignals |= !it->modify; - if (!val) { - cerr << "Too many signals to create unique bit masks" << endl; - exit(1); - } - Signal signal = *it; - h << " " << signalEnumName(signal.name) << " = 0x" << - #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - hex - #else - Qt::hex - #endif - << val; - if (++it != itEnd) { - h << ","; - } - h << endl; - } - h << " };" << - #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - dec - #else - Qt::dec - #endif - << endl << endl; - - h << " Q_SIGNALS:"; - for (const Signal &signal : qAsConst(signalList)) { - h << endl; - if (!signal.label.isEmpty()) { - h << " /**" << endl; - h << " " << signal.label << endl; - h << " */" << endl; - } - h << " void " << signal.name << "("; - QList::ConstIterator it, itEnd = signal.arguments.constEnd(); - for (it = signal.arguments.constBegin(); it != itEnd;) { - SignalArguments argument = *it; - QString type = param(argument.type); - if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { - for (int i = 0, end = entries.count(); i < end; ++i) { - if (entries[i]->name() == argument.variableName) { - type = enumType(entries[i], cfg.globalEnums); - break; - } - } - } - h << type << " " << argument.variableName; - if (++it != itEnd) { - h << ", "; - } - } - h << ");" << endl; - } - h << endl; - - h << " private:" << endl; - h << " void itemChanged(quint64 flags);" << endl; - h << endl; - } - - h << " protected:" << endl; - - // Private constructor for singleton - if (cfg.singleton) { - h << " " << cfg.className << "("; - if (cfgFileNameArg) { - h << "KSharedConfig::Ptr config"; - } - if (cfg.parentInConstructor) { - if (cfgFileNameArg) { - h << ", "; - } - h << "QObject *parent = nullptr"; - } - h << ");" << endl; - h << " friend class " << cfg.className << "Helper;" << endl << endl; - } - - if (hasNonModifySignals) { - h << " bool usrSave() override;" << endl; - } - - // Member variables - if (!cfg.memberVariables.isEmpty() && cfg.memberVariables != QLatin1String("private") && cfg.memberVariables != QLatin1String("dpointer")) { - h << " " << cfg.memberVariables << ":" << endl; - } - - // Class Parameters - for (QList::ConstIterator it = parameters.constBegin(); - it != parameters.constEnd(); ++it) { - h << " " << cppType((*it).type) << " mParam" << (*it).name << ";" << endl; - } - - if (cfg.memberVariables != QLatin1String("dpointer")) { - QString group; - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - if ((*itEntry)->group() != group) { - group = (*itEntry)->group(); - h << endl; - h << " // " << group << endl; - } - h << " " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg); - if (!(*itEntry)->param().isEmpty()) { - h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); - } - h << ";" << endl; - - if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) { - h << " "; - if (cfg.staticAccessors) { - h << "static "; - } - h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; - if (!(*itEntry)->param().isEmpty()) { - h << " " << cppType((*itEntry)->paramType()) << " i "; - } - h << ")" << Const << ";" << endl; - } - } - - h << endl << " private:" << endl; - if (cfg.itemAccessors) { - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - h << " Item" << itemType((*itEntry)->type()) << " *" << itemVar(*itEntry, cfg); - if (!(*itEntry)->param().isEmpty()) { - h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); - } - h << ";" << endl; - } - } - if (hasNonModifySignals) { - h << " uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; - } - - } else { - // use a private class for both member variables and items - h << " private:" << endl; - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) { - h << " "; - if (cfg.staticAccessors) { - h << "static "; - } - h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; - if (!(*itEntry)->param().isEmpty()) { - h << " " << cppType((*itEntry)->paramType()) << " i "; - } - h << ")" << Const << ";" << endl; - } - } - h << " " + cfg.className + "Private *d;" << endl; - } - - if (cfg.customAddons) { - h << " // Include custom additions" << endl; - h << " #include \"" << baseName << "_addons." << cfg.headerExtension << '"' << endl; - } - - h << "};" << endl << endl; - - endNamespaces(cfg.nameSpace, h); - - h << "#endif" << endl << endl; - - header.close(); - - QFile implementation(baseDir + implementationFileName); - if (!implementation.open(QIODevice::WriteOnly)) { - cerr << "Can not open '" << implementationFileName << "for writing." - << endl; - return 1; - } - - QTextStream cpp(&implementation); - - cpp.setCodec("utf-8"); - - cpp << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl; - cpp << "// All changes you do to this file will be lost." << endl << endl; - - cpp << "#include \"" << headerFileName << "\"" << endl << endl; - - for (it = cfg.sourceIncludes.constBegin(); it != cfg.sourceIncludes.constEnd(); ++it) { - if ((*it).startsWith('"')) { - cpp << "#include " << *it << endl; - } else { - cpp << "#include <" << *it << ">" << endl; - } - } - - if (!cfg.sourceIncludes.isEmpty()) { - cpp << endl; - } - - if (cfg.setUserTexts && cfg.translationSystem == CfgConfig::KdeTranslation) { - cpp << "#include " << endl << endl; - } - - // Header required by singleton implementation - if (cfg.singleton) { - cpp << "#include " << endl << "#include " << endl << endl; - } - if (cfg.singleton && cfgFileNameArg) { - cpp << "#include " << endl << endl; - } - - if (!cfg.nameSpace.isEmpty()) { - cpp << "using namespace " << cfg.nameSpace << ";" << endl << endl; - } - - QString group; - - // private class implementation - if (cfg.dpointer) { - beginNamespaces(cfg.nameSpace, cpp); - cpp << "class " << cfg.className << "Private" << endl; - cpp << "{" << endl; - cpp << " public:" << endl; - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - if ((*itEntry)->group() != group) { - group = (*itEntry)->group(); - cpp << endl; - cpp << " // " << group << endl; - } - cpp << " " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg); - if (!(*itEntry)->param().isEmpty()) { - cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); - } - cpp << ";" << endl; - } - cpp << endl << " // items" << endl; - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - const QString declType = (*itEntry)->signalList().isEmpty() - ? QString(cfg.inherits + "::Item" + itemType((*itEntry)->type())) - : QStringLiteral("KConfigCompilerSignallingItem"); - cpp << " " << declType << " *" << itemVar( *itEntry, cfg ); - if (!(*itEntry)->param().isEmpty()) { - cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1); - } - cpp << ";" << endl; - } - if (hasNonModifySignals) { - cpp << " uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl; - } - - cpp << "};" << endl << endl; - endNamespaces(cfg.nameSpace, cpp); - } - - // Singleton implementation - if (cfg.singleton) { - beginNamespaces(cfg.nameSpace, cpp); - cpp << "class " << cfg.className << "Helper" << endl; - cpp << '{' << endl; - cpp << " public:" << endl; - cpp << " " << cfg.className << "Helper() : q(nullptr) {}" << endl; - cpp << " ~" << cfg.className << "Helper() { delete q; }" << endl; - cpp << " " << cfg.className << "Helper(const " << cfg.className << "Helper&) = delete;" << endl; - cpp << " " << cfg.className << "Helper& operator=(const " << cfg.className << "Helper&) = delete;" << endl; - cpp << " " << cfg.className << " *q;" << endl; - cpp << "};" << endl; - endNamespaces(cfg.nameSpace, cpp); - cpp << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << cfg.className << ")" << endl; - - cpp << cfg.className << " *" << cfg.className << "::self()" << endl; - cpp << "{" << endl; - if (cfgFileNameArg) { - cpp << " if (!s_global" << cfg.className << "()->q)" << endl; - cpp << " qFatal(\"you need to call " << cfg.className << "::instance before using\");" << endl; - } else { - cpp << " if (!s_global" << cfg.className << "()->q) {" << endl; - cpp << " new " << cfg.className << ';' << endl; - cpp << " s_global" << cfg.className << "()->q->read();" << endl; - cpp << " }" << endl << endl; - } - cpp << " return s_global" << cfg.className << "()->q;" << endl; - cpp << "}" << endl << endl; - - if (cfgFileNameArg) { - auto instance = [&cfg, &cpp] (const QString &type, const QString &arg, bool isString) { - cpp << "void " << cfg.className << "::instance(" << type << " " << arg << ")" << endl; - cpp << "{" << endl; - cpp << " if (s_global" << cfg.className << "()->q) {" << endl; - cpp << " qDebug() << \"" << cfg.className << "::instance called after the first use - ignoring\";" << endl; - cpp << " return;" << endl; - cpp << " }" << endl; - cpp << " new " << cfg.className << "("; - if (isString) { - cpp << "KSharedConfig::openConfig(" << arg << ")"; - } else { - cpp << "std::move(" << arg << ")"; - } - cpp << ");" << endl; - cpp << " s_global" << cfg.className << "()->q->read();" << endl; - cpp << "}" << endl << endl; - }; - instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); - instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); - } - } - - if (!cppPreamble.isEmpty()) { - cpp << cppPreamble << endl; - } - - // Constructor - cpp << cfg.className << "::" << cfg.className << "("; - if (cfgFileNameArg) { - if (! cfg.forceStringFilename) { - cpp << " KSharedConfig::Ptr config"; - } else { - cpp << " const QString& config"; - } - cpp << (parameters.isEmpty() ? "" : ","); - } - - for (QList::ConstIterator it = parameters.constBegin(); - it != parameters.constEnd(); ++it) { - if (it != parameters.constBegin()) { - cpp << ","; - } - cpp << " " << param((*it).type) << " " << (*it).name; - } - - if (cfg.parentInConstructor) { - if (cfgFileNameArg || !parameters.isEmpty()) { - cpp << ","; - } - cpp << " QObject *parent"; - } - cpp << " )" << endl; - - cpp << " : " << cfg.inherits << "("; - if (!cfgFileName.isEmpty()) { - cpp << " QStringLiteral( \"" << cfgFileName << "\" "; - } - if (cfgFileNameArg) { - if (! cfg.forceStringFilename) { - cpp << " std::move( config ) "; - } else { - cpp << " config "; - } - } - if (!cfgFileName.isEmpty()) { - cpp << ") "; - } - cpp << ")" << endl; - - // Store parameters - for (QList::ConstIterator it = parameters.constBegin(); - it != parameters.constEnd(); ++it) { - cpp << " , mParam" << (*it).name << "(" << (*it).name << ")" << endl; - } - - if (hasNonModifySignals && !cfg.dpointer) { - cpp << " , " << varName(QStringLiteral("settingsChanged"), cfg) << "(0)" << endl; - } - - cpp << "{" << endl; - - if (cfg.parentInConstructor) { - cpp << " setParent(parent);" << endl; - } - - if (cfg.dpointer) { - cpp << " d = new " + cfg.className + "Private;" << endl; - if (hasNonModifySignals) { - cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; - } - } - // Needed in case the singleton class is used as baseclass for - // another singleton. - if (cfg.singleton) { - cpp << " Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl; - cpp << " s_global" << cfg.className << "()->q = this;" << endl; - } - - group.clear(); - - if (hasSignals) { - // this cast to base-class pointer-to-member is valid C++ - // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ - cpp << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" - << " static_cast(&" - << cfg.className << "::itemChanged);" << endl << endl; - } - - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - if ((*itEntry)->group() != group) { - if (!group.isEmpty()) { - cpp << endl; - } - group = (*itEntry)->group(); - cpp << " setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl; - } - - QString key = paramString((*itEntry)->key(), parameters); - if (!(*itEntry)->code().isEmpty()) { - cpp << (*itEntry)->code() << endl; - } - if ((*itEntry)->type() == QLatin1String("Enum")) { - cpp << " QList<" + cfg.inherits + "::ItemEnum::Choice> values" - << (*itEntry)->name() << ";" << endl; - const QList choices = (*itEntry)->choices().choices; - QList::ConstIterator it; - for (it = choices.constBegin(); it != choices.constEnd(); ++it) { - cpp << " {" << endl; - cpp << " " + cfg.inherits + "::ItemEnum::Choice choice;" << endl; - cpp << " choice.name = QStringLiteral(\"" << (*it).name << "\");" << endl; - if (cfg.setUserTexts) { - if (!(*it).label.isEmpty()) { - cpp << " choice.label = " - << translatedString(cfg, (*it).label, (*it).context) - << ";" << endl; - } - if (!(*it).toolTip.isEmpty()) { - cpp << " choice.toolTip = " - << translatedString(cfg, (*it).toolTip, (*it).context) - << ";" << endl; - } - if (!(*it).whatsThis.isEmpty()) { - cpp << " choice.whatsThis = " - << translatedString(cfg, (*it).whatsThis, (*it).context) - << ";" << endl; - } - } - cpp << " values" << (*itEntry)->name() << ".append( choice );" << endl; - cpp << " }" << endl; - } - } - - if (!cfg.dpointer) { - cpp << itemDeclaration(*itEntry, cfg); - } - - if ((*itEntry)->param().isEmpty()) { - // Normal case - cpp << " " << itemPath(*itEntry, cfg) << " = " - << newItem((*itEntry), key, (*itEntry)->defaultValue(), cfg) << endl; - - if (!(*itEntry)->minValue().isEmpty()) { - cpp << " " << itemPath(*itEntry, cfg) << "->setMinValue(" << (*itEntry)->minValue() << ");" << endl; - } - if (!(*itEntry)->maxValue().isEmpty()) { - cpp << " " << itemPath(*itEntry, cfg) << "->setMaxValue(" << (*itEntry)->maxValue() << ");" << endl; - } - - if (cfg.setUserTexts) { - cpp << userTextsFunctions((*itEntry), cfg); - } - - if (cfg.allNotifiers || cfg.notifiers.contains((*itEntry)->name())) { - cpp << " " << itemPath(*itEntry, cfg) << "->setWriteFlags(KConfigBase::Notify);" << endl; - } - - cpp << " addItem( " << itemPath(*itEntry, cfg); - QString quotedName = (*itEntry)->name(); - addQuotes(quotedName); - if (quotedName != key) { - cpp << ", QStringLiteral( \"" << (*itEntry)->name() << "\" )"; - } - cpp << " );" << endl; - } else { - // Indexed - for (int i = 0; i <= (*itEntry)->paramMax(); i++) { - QString defaultStr; - QString itemVarStr(itemPath(*itEntry, cfg) + QStringLiteral("[%1]").arg(i)); - - if (!(*itEntry)->paramDefaultValue(i).isEmpty()) { - defaultStr = (*itEntry)->paramDefaultValue(i); - } else if (!(*itEntry)->defaultValue().isEmpty()) { - defaultStr = paramString((*itEntry)->defaultValue(), (*itEntry), i); - } else { - defaultStr = defaultValue((*itEntry)->type()); - } - - cpp << " " << itemVarStr << " = " - << newItem((*itEntry), paramString(key, *itEntry, i), defaultStr, cfg, QStringLiteral("[%1]").arg(i)) << endl; - - if (cfg.setUserTexts) { - cpp << userTextsFunctions(*itEntry, cfg, itemVarStr, (*itEntry)->paramName()); - } - - // Make mutators for enum parameters work by adding them with $(..) replaced by the - // param name. The check for isImmutable in the set* functions doesn't have the param - // name available, just the corresponding enum value (int), so we need to store the - // param names in a separate static list!. - cpp << " addItem( " << itemVarStr << ", QStringLiteral( \""; - if ((*itEntry)->paramType() == QLatin1String("Enum")) { - cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg((*itEntry)->paramValues()[i]); - } else { - cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg(i); - } - cpp << "\" ) );" << endl; - } - } - } - - cpp << "}" << endl << endl; - - if (cfg.dpointer) { - // setters and getters go in Cpp if in dpointer mode - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - QString n = (*itEntry)->name(); - QString t = (*itEntry)->type(); - - // Manipulator - if (cfg.allMutators || cfg.mutators.contains(n)) { - cpp << "void " << setFunction(n, cfg.className) << "( "; - if (!(*itEntry)->param().isEmpty()) { - cpp << cppType((*itEntry)->paramType()) << " i, "; - } - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - cpp << enumType(*itEntry, cfg.globalEnums); - } else { - cpp << param(t); - } - cpp << " v )" << endl; - // function body inline only if not using dpointer - // for BC mode - cpp << "{" << endl; - cpp << indent(memberMutatorBody(*itEntry, cfg), 6); - cpp << "}" << endl << endl; - } - - // Accessor - if (cfg.useEnumTypes && t == QLatin1String("Enum")) { - cpp << enumType(*itEntry, cfg.globalEnums); - } else { - cpp << cppType(t); - } - cpp << " " << getFunction(n, cfg.className) << "("; - if (!(*itEntry)->param().isEmpty()) { - cpp << " " << cppType((*itEntry)->paramType()) << " i "; - } - cpp << ")" << Const << endl; - // function body inline only if not using dpointer - // for BC mode - cpp << "{" << endl; - cpp << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 2); - cpp << "}" << endl << endl; - - // Default value Accessor -- written by the loop below - - // Item accessor - if (cfg.itemAccessors) { - cpp << endl; - cpp << cfg.inherits + "::Item" << itemType((*itEntry)->type()) << " *" - << getFunction(n, cfg.className) << "Item("; - if (!(*itEntry)->param().isEmpty()) { - cpp << " " << cppType((*itEntry)->paramType()) << " i "; - } - cpp << ")" << endl; - cpp << "{" << endl; - cpp << indent(itemAccessorBody(*itEntry, cfg), 2); - cpp << "}" << endl; - } - - cpp << endl; - } - } - - // default value getters always go in Cpp - for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) { - QString n = (*itEntry)->name(); - QString t = (*itEntry)->type(); - - // Default value Accessor, as "helper" function - if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) { - cpp << cppType(t) << " " << getDefaultFunction(n, cfg.className) << "_helper("; - if (!(*itEntry)->param().isEmpty()) { - cpp << " " << cppType((*itEntry)->paramType()) << " i "; - } - cpp << ")" << Const << endl; - cpp << "{" << endl; - cpp << memberGetDefaultBody(*itEntry) << endl; - cpp << "}" << endl << endl; - } - } - - // Destructor - cpp << cfg.className << "::~" << cfg.className << "()" << endl; - cpp << "{" << endl; - if (cfg.dpointer) { - cpp << " delete d;" << endl; - } - if (cfg.singleton) { - cpp << " s_global" << cfg.className << "()->q = nullptr;" << endl; - } - cpp << "}" << endl << endl; - - if (hasNonModifySignals) { - cpp << "bool " << cfg.className << "::" << "usrSave()" << endl; - cpp << "{" << endl; - cpp << " const bool res = " << cfg.inherits << "::usrSave();" << endl; - cpp << " if (!res) return false;" << endl << endl; - for (const Signal &signal : qAsConst(signalList)) { - if (signal.modify) { - continue; - } - - cpp << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg) << " & " << signalEnumName(signal.name) << " )" << endl; - cpp << " Q_EMIT " << signal.name << "("; - QList::ConstIterator it, itEnd = signal.arguments.constEnd(); - for (it = signal.arguments.constBegin(); it != itEnd;) { - SignalArguments argument = *it; - bool cast = false; - if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) { - for (int i = 0, end = entries.count(); i < end; ++i) { - if (entries[i]->name() == argument.variableName) { - cpp << "static_cast<" << enumType(entries[i], cfg.globalEnums) << ">("; - cast = true; - break; - } - } - } - cpp << varPath(argument.variableName, cfg); - if (cast) { - cpp << ")"; - } - if (++it != itEnd) { - cpp << ", "; - } - } - cpp << ");" << endl; - } - - cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl; - cpp << " return true;" << endl; - cpp << "}" << endl; - } - - if (hasSignals) { - cpp << endl; - cpp << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl; - if (hasNonModifySignals) - cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " |= flags;" << endl; - - if (!signalList.isEmpty()) - cpp << endl; - - for (const Signal &signal : qAsConst(signalList)) { - if (signal.modify) { - cpp << " if ( flags & " << signalEnumName(signal.name) << " ) {" << endl; - cpp << " Q_EMIT " << signal.name << "();" << endl; - cpp << " }" << endl; - } - } - - cpp << "}" << endl; - } - - if (hasSignals || cfg.generateProperties) { - // Add includemoc if they are signals defined. - cpp << endl; - cpp << "#include \"" << mocFileName << "\"" << endl; - cpp << endl; - } + // remove '.kcfg' from the name. + const QString baseName = inputFilename.mid(0, inputFilename.size()-5); + KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult); + headerGenerator.start(); + headerGenerator.save(); - // clear entries list - qDeleteAll(entries); + KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult); + sourceGenerator.start(); + sourceGenerator.save(); - implementation.close(); + qDeleteAll(parseResult.entries); }