aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaz Canabrava <tcanabrava@kde.org>2020-01-14 18:52:43 +0000
committerTomaz Canabrava <tcanabrava@kde.org>2020-01-16 10:15:36 +0000
commit98c32e29f50465d4d4e16bafdf0491edbfb422b0 (patch)
tree06898a9ff9a5ed2e385b25a6b7fe258bb9eacb9c
parenta601ac563d33681569befd22c9182cddaecf30b1 (diff)
downloadkconfig-98c32e29f50465d4d4e16bafdf0491edbfb422b0.tar.gz
kconfig-98c32e29f50465d4d4e16bafdf0491edbfb422b0.tar.bz2
WIP: Refactor KConfigXT
Summary: The current KConfigXT compiler is in a sad state: It's a massive file with loads of global variables that handle state, the generator is done within the main() function and it seems to have grown organically. There are no classes to separate logic / state / generation, what exists is code that generates code from a xml / ini pair, but it's hard to even discover what a bit of code is doing. The code istyle is C++ / Java from the nineties, which is not bad per see but it also uses quite a few things that are going to be deprecated in Qt 6 so I'm also taking the time make the code more streamlined with newer code style (no iterators, lambdas, auto usage, etc). The code that generates the files simplly pushes strings to a text stream, and it's hard to figure out when something starts or something ends: for instance, the code that generates the Constructor has more than sixty lines of code englobing some nested if - for - if - for constructs. Currently the code is "done" - there's one bug that I still need to find & fix regarding Translations, but the rest seems sane. The current testcode generates incorrect *whitespaces* regarding the old code (there's some parts that I feel that it's important to fix before merging, but overall, the whitespace changes are not bad and easier to handle, old code had a hand-counted amount of spaces before each line, new code has a function whitespace() that adds the current-and-correct amount of whitespaces based on indentation level that you start by startScope() and ends with endScope(). rest of the code still needs to be ported to it. I plan to fix the testcases whitespace by manually adding them, I'v fougth with the code for a while and added a few hacks there but I don't want to make the code hackish again. New code is not perfect by any means, but is a good step in the right direction. This code tries to Separate the compiler code into many different files / classes to be more obvious what's happening, and each class also has many helper methods to minimize copypaste. - CodeGenerator: Has base code for the header and source files that can be shared - HeaderGenerator: Logic for generating the header file - SourceGenerator: Logic for generating the source file - KcfgParser: Logic for parsing the kcfg file and extracting the information from the Xml file - CommonStructs: a header that contains the structs that are currently used everywhere. - KConfigParameters: (was CfgConfig - ConfigConfig, wat) - Has information passed via the kcfgc file - kcfg_compiler - will be renamed to main - start the other classes and generates the files. This code here currently has the begining of this separation, with the CodeGenerator and the HeaderGenerator in a ~good~ state, but unfinished. Test Plan: - Run the test cases, - Compare the diffs generated by the testcases and fix in the code the errors / differences - Run and compare real kde source with the new and old generators to look for errors Reviewers: #frameworks, ervin, bport, dfaure Reviewed By: dfaure Subscribers: bport, ngraham, kde-frameworks-devel Tags: #frameworks Differential Revision: https://phabricator.kde.org/D26202
-rw-r--r--autotests/kconfig_compiler/kconfigcompiler_test.cpp22
-rw-r--r--autotests/kconfig_compiler/test1.h.ref1
-rw-r--r--autotests/kconfig_compiler/test10.h.ref2
-rw-r--r--autotests/kconfig_compiler/test11.h.ref1
-rw-r--r--autotests/kconfig_compiler/test11a.h.ref1
-rw-r--r--autotests/kconfig_compiler/test12.h.ref1
-rw-r--r--autotests/kconfig_compiler/test13.h.ref2
-rw-r--r--autotests/kconfig_compiler/test9.h.ref1
-rw-r--r--autotests/kconfig_compiler/test_dpointer.cpp.ref44
-rw-r--r--autotests/kconfig_compiler/test_translation_kde.h.ref1
-rw-r--r--autotests/kconfig_compiler/test_translation_kde_domain.h.ref1
-rw-r--r--autotests/kconfig_compiler/test_translation_qt.h.ref1
-rw-r--r--autotests/kconfigtest.h1
-rw-r--r--src/kconfig_compiler/CMakeLists.txt9
-rw-r--r--src/kconfig_compiler/KCFGXmlParser.cpp564
-rw-r--r--src/kconfig_compiler/KCFGXmlParser.h82
-rw-r--r--src/kconfig_compiler/KConfigCodeGeneratorBase.cpp276
-rw-r--r--src/kconfig_compiler/KConfigCodeGeneratorBase.h118
-rw-r--r--src/kconfig_compiler/KConfigCommonStructs.h195
-rw-r--r--src/kconfig_compiler/KConfigHeaderGenerator.cpp612
-rw-r--r--src/kconfig_compiler/KConfigHeaderGenerator.h78
-rw-r--r--src/kconfig_compiler/KConfigSourceGenerator.cpp657
-rw-r--r--src/kconfig_compiler/KConfigSourceGenerator.h86
-rw-r--r--src/kconfig_compiler/KConfigXTParameters.cpp100
-rw-r--r--src/kconfig_compiler/KConfigXTParameters.h81
-rw-r--r--src/kconfig_compiler/kconfig_compiler.cpp2208
26 files changed, 3017 insertions, 2128 deletions
diff --git a/autotests/kconfig_compiler/kconfigcompiler_test.cpp b/autotests/kconfig_compiler/kconfigcompiler_test.cpp
index 383745b1..7ab4a923 100644
--- a/autotests/kconfig_compiler/kconfigcompiler_test.cpp
+++ b/autotests/kconfig_compiler/kconfigcompiler_test.cpp
@@ -125,9 +125,7 @@ void KConfigCompiler_Test::testBaselineComparison()
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 @@ void KConfigCompiler_Test::appendFileDiff(const QString &oldFile, const QString
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
index 77c3b65f..52921ac6 100644
--- a/autotests/kconfig_compiler/test1.h.ref
+++ b/autotests/kconfig_compiler/test1.h.ref
@@ -8,6 +8,7 @@
#include <QDebug>
#include <qdir.h>
+
class Test1 : public KConfigSkeleton
{
public:
diff --git a/autotests/kconfig_compiler/test10.h.ref b/autotests/kconfig_compiler/test10.h.ref
index 93839a54..82bbab84 100644
--- a/autotests/kconfig_compiler/test10.h.ref
+++ b/autotests/kconfig_compiler/test10.h.ref
@@ -14,7 +14,6 @@ class Test10 : public KConfigSkeleton
static Test10 *self();
~Test10();
-
/**
Get foo bar
*/
@@ -24,7 +23,6 @@ class Test10 : public KConfigSkeleton
return self()->mFooBar;
}
-
/**
Get bar foo
*/
diff --git a/autotests/kconfig_compiler/test11.h.ref b/autotests/kconfig_compiler/test11.h.ref
index 395810ef..2acbe9e5 100644
--- a/autotests/kconfig_compiler/test11.h.ref
+++ b/autotests/kconfig_compiler/test11.h.ref
@@ -11,6 +11,7 @@
#include <QDebug>
#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
index 23c83713..62b59381 100644
--- a/autotests/kconfig_compiler/test11a.h.ref
+++ b/autotests/kconfig_compiler/test11a.h.ref
@@ -11,6 +11,7 @@
#include <QDebug>
#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
index d96d4609..e3954282 100644
--- a/autotests/kconfig_compiler/test12.h.ref
+++ b/autotests/kconfig_compiler/test12.h.ref
@@ -15,7 +15,6 @@ class Test12 : public KConfigSkeleton
Test12( );
~Test12();
-
/**
Get RnRSource
*/
diff --git a/autotests/kconfig_compiler/test13.h.ref b/autotests/kconfig_compiler/test13.h.ref
index 6c67fc5d..3a6e7f89 100644
--- a/autotests/kconfig_compiler/test13.h.ref
+++ b/autotests/kconfig_compiler/test13.h.ref
@@ -16,7 +16,6 @@ class Test13 : public KConfigSkeleton
Test13( );
~Test13();
-
Q_PROPERTY(QUrl picturesDir READ picturesDir CONSTANT)
/**
Get picturesDir
@@ -46,7 +45,6 @@ class Test13 : public KConfigSkeleton
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
index 9bcf0001..6e40cf48 100644
--- a/autotests/kconfig_compiler/test9.h.ref
+++ b/autotests/kconfig_compiler/test9.h.ref
@@ -8,6 +8,7 @@
#include <QDebug>
#include <qdir.h>
+
class Test9 : public KConfigSkeleton
{
public:
diff --git a/autotests/kconfig_compiler/test_dpointer.cpp.ref b/autotests/kconfig_compiler/test_dpointer.cpp.ref
index c69d38a4..1bfb9f52 100644
--- a/autotests/kconfig_compiler/test_dpointer.cpp.ref
+++ b/autotests/kconfig_compiler/test_dpointer.cpp.ref
@@ -157,8 +157,8 @@ TestDPointer::TestDPointer( )
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 @@ KConfigSkeleton::ItemBool *TestDPointer::autoSaveItem()
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 @@ KConfigSkeleton::ItemInt *TestDPointer::autoSaveIntervalItem()
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 @@ KConfigSkeleton::ItemBool *TestDPointer::confirmItem()
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 @@ KConfigSkeleton::ItemString *TestDPointer::archiveFileItem()
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 @@ KConfigSkeleton::ItemEnum *TestDPointer::destinationItem()
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 @@ KConfigSkeleton::ItemInt *TestDPointer::hourSizeItem()
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 @@ KConfigSkeleton::ItemBool *TestDPointer::selectionStartsEditorItem()
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 @@ KConfigSkeleton::ItemStringList *TestDPointer::selectedPluginsItem()
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 @@ KConfigSkeleton::ItemColor *TestDPointer::highlightColorItem()
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 @@ KConfigSkeleton::ItemColor *TestDPointer::agendaBgColorItem()
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
index f9b582c4..0e50b5a2 100644
--- a/autotests/kconfig_compiler/test_translation_kde.h.ref
+++ b/autotests/kconfig_compiler/test_translation_kde.h.ref
@@ -17,7 +17,6 @@ class TestTranslationKde : public KConfigSkeleton
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
index 2fa42c71..4ed5d9bd 100644
--- a/autotests/kconfig_compiler/test_translation_kde_domain.h.ref
+++ b/autotests/kconfig_compiler/test_translation_kde_domain.h.ref
@@ -17,7 +17,6 @@ class TestTranslationKdeDomain : public KConfigSkeleton
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
index 9831468b..2fe3274a 100644
--- a/autotests/kconfig_compiler/test_translation_qt.h.ref
+++ b/autotests/kconfig_compiler/test_translation_qt.h.ref
@@ -17,7 +17,6 @@ class TestTranslationQt : public KConfigSkeleton
TestTranslationQt( );
~TestTranslationQt();
-
/**
Get Enable automatic saving of calendar
*/
diff --git a/autotests/kconfigtest.h b/autotests/kconfigtest.h
index 26d8e7f5..9963d476 100644
--- 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
index dc0a08db..3543dfa2 100644
--- 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.cpp b/src/kconfig_compiler/KCFGXmlParser.cpp
new file mode 100644
index 00000000..214335c3
--- /dev/null
+++ b/src/kconfig_compiler/KCFGXmlParser.cpp
@@ -0,0 +1,564 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QDomAttr>
+#include <QDomElement>
+#include <QDomNode>
+#include <QFile>
+#include <QList>
+#include <QStringList>
+#include <QTextStream>
+
+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<QUrl> 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<CfgEntry::Choice>::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<int> 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<CfgEntry::Choice> 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 <choice> 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! <name> 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/KCFGXmlParser.h b/src/kconfig_compiler/KCFGXmlParser.h
new file mode 100644
index 00000000..8c85d878
--- /dev/null
+++ b/src/kconfig_compiler/KCFGXmlParser.h
@@ -0,0 +1,82 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QDomDocument>
+#include <QString>
+#include <QRegularExpression>
+
+#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 <group> </group> 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/KConfigCodeGeneratorBase.cpp b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
new file mode 100644
index 00000000..024af19c
--- /dev/null
+++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
@@ -0,0 +1,276 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QTextStream>
+#include <QLatin1Char>
+#include <QFileInfo>
+
+#include <ostream>
+#include <QDebug>
+
+using std::endl;
+
+namespace
+{
+QTextStream cout(stdout);
+QTextStream cerr(stderr);
+}
+
+KConfigCodeGeneratorBase::KConfigCodeGeneratorBase(
+ const QString& inputFile,
+ const QString& baseDir,
+ const QString& fileName,
+ const KConfigXTParameters &parameters,
+ 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/KConfigCodeGeneratorBase.h b/src/kconfig_compiler/KConfigCodeGeneratorBase.h
new file mode 100644
index 00000000..fdf3b7e3
--- /dev/null
+++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.h
@@ -0,0 +1,118 @@
+/*
+ This file is part of KDE.
+
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 KCONFIGCODEGENERATORBASE_H
+#define KCONFIGCODEGENERATORBASE_H
+
+#include <QString>
+#include <QTextStream>
+#include <QFile>
+#include <QVector>
+
+#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 &parameters, // 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/KConfigCommonStructs.h b/src/kconfig_compiler/KConfigCommonStructs.h
new file mode 100644
index 00000000..c78c7b76
--- /dev/null
+++ b/src/kconfig_compiler/KConfigCommonStructs.h
@@ -0,0 +1,195 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QString>
+#include <QVector>
+#include <QList>
+
+#include "KConfigXTParameters.h"
+
+struct Param
+{
+ QString name;
+ QString type;
+};
+
+struct Signal
+{
+ QString name;
+ QString label;
+ QList<Param> 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<Choice> &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<Choice> 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<Signal> signalList;
+ QStringList paramValues;
+ QStringList paramDefaultValues;
+ int paramMax;
+ bool hidden;
+ QString min;
+ QString max;
+};
+
+struct ParseResult {
+ QString cfgFileName;
+ bool cfgFileNameArg = false;
+ QList<Param> parameters;
+ QList<Signal> signalList;
+ QStringList includes;
+ QList<CfgEntry *> 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 &param = QString(),
+ const QString &paramValue = QString());
+
+// TODO: Sanitize those functions.
+QString newItem(
+ const CfgEntry* entry,
+ const QString &key,
+ const QString& defaultValue,
+ const KConfigXTParameters &cfg,
+ const QString &param = 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<Param> &parameters);
+
+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.cpp b/src/kconfig_compiler/KConfigHeaderGenerator.cpp
new file mode 100644
index 00000000..088f64e7
--- /dev/null
+++ b/src/kconfig_compiler/KConfigHeaderGenerator.cpp
@@ -0,0 +1,612 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QTextStream>
+#include <QDebug>
+
+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 &parameter : 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<CfgEntry::Choice> 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<Param>::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 <<cppType(entry->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 <<endl;
+}
+
+void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry)
+{
+ // Default value Accessor
+ if (! ((cfg.allDefaultGetters || cfg.defaultGetters.contains(entry->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/KConfigHeaderGenerator.h b/src/kconfig_compiler/KConfigHeaderGenerator.h
new file mode 100644
index 00000000..30f09ac4
--- /dev/null
+++ b/src/kconfig_compiler/KConfigHeaderGenerator.h
@@ -0,0 +1,78 @@
+/*
+ This file is part of KDE.
+
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 KCONFIGHEADERGENERATOR_H
+#define KCONFIGHEADERGENERATOR_H
+
+#include "KConfigCodeGeneratorBase.h"
+#include "KConfigCommonStructs.h"
+
+#include <QString>
+#include <QList>
+
+class KConfigXTParameters;
+class CfgEntry;
+class QTextStream;
+struct ParseResult;
+
+class KConfigHeaderGenerator : public KConfigCodeGeneratorBase {
+public:
+ KConfigHeaderGenerator(
+ const QString& inputFile,
+ const QString& baseDir,
+ const KConfigXTParameters &parameters,
+ 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/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp
new file mode 100644
index 00000000..1949b420
--- /dev/null
+++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp
@@ -0,0 +1,657 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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<Param>::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 &parameter : 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<KConfigCompilerSignallingItem::NotifyFunction>(&"
+ << 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<Param>::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/KConfigSourceGenerator.h b/src/kconfig_compiler/KConfigSourceGenerator.h
new file mode 100644
index 00000000..77409329
--- /dev/null
+++ b/src/kconfig_compiler/KConfigSourceGenerator.h
@@ -0,0 +1,86 @@
+/*
+ This file is part of KDE.
+
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 KCONFIGSOURCEGENERATOR_H
+#define KCONFIGSOURCEGENERATOR_H
+
+#include "KConfigCodeGeneratorBase.h"
+#include "KConfigCommonStructs.h"
+
+#include <QString>
+#include <QList>
+
+class KConfigXTParameters;
+class CfgEntry;
+class QTextStream;
+struct ParseResult;
+
+class KConfigSourceGenerator : public KConfigCodeGeneratorBase {
+public:
+ KConfigSourceGenerator(
+ const QString& inputFile,
+ const QString& baseDir,
+ const KConfigXTParameters &parameters,
+ 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/KConfigXTParameters.cpp b/src/kconfig_compiler/KConfigXTParameters.cpp
new file mode 100644
index 00000000..1d488e9e
--- /dev/null
+++ b/src/kconfig_compiler/KConfigXTParameters.cpp
@@ -0,0 +1,100 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 <QDebug>
+#include <QFileInfo>
+
+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/KConfigXTParameters.h b/src/kconfig_compiler/KConfigXTParameters.h
new file mode 100644
index 00000000..db2cbcb5
--- /dev/null
+++ b/src/kconfig_compiler/KConfigXTParameters.h
@@ -0,0 +1,81 @@
+/*
+ This file is part of KDE.
+ Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org)
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@kde.org>
+ 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 KCOFIGXTPARAMETERS_H
+#define KCOFIGXTPARAMETERS_H
+
+#include <QSettings>
+#include <QString>
+#include <QStringList>
+
+/**
+ 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/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp
index 848595f2..00699c36 100644
--- a/src/kconfig_compiler/kconfig_compiler.cpp
+++ b/src/kconfig_compiler/kconfig_compiler.cpp
@@ -34,15 +34,19 @@
#include <QDomAttr>
#include <QRegularExpression>
#include <QStringList>
-
-#include <qcommandlineparser.h>
-#include <qcommandlineoption.h>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
#include <ostream>
#include <iostream>
#include <stdlib.h>
#include "../../kconfig_version.h"
+#include "KConfigXTParameters.h"
+#include "KConfigCommonStructs.h"
+#include "KConfigHeaderGenerator.h"
+#include "KConfigSourceGenerator.h"
+#include "KCFGXmlParser.h"
namespace
{
@@ -50,444 +54,7 @@ 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<SignalArguments> 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<Choice> &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<Choice> 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<Signal> &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<Choice> &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<Signal> &value)
- {
- mSignalList = value;
- }
- QList<Signal> signalList() const
- {
- return mSignalList;
- }
-
- bool hidden() const
- {
- return mHidden;
- }
-
- void dump() const
- {
- cerr << "<entry>" << 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 << "</entry>" << 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<Signal> 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 @@ static QString varName(const QString &n, const CfgConfig &cfg)
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 @@ static QString varPath(const QString &n, const CfgConfig &cfg)
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,11 +95,11 @@ static QString enumName(const QString &n, const CfgEntry::Choices &c)
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");
}
@@ -541,7 +108,7 @@ static QString enumType(const CfgEntry *e, bool globalEnums)
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 @@ static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
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 @@ static QString setFunction(const QString &n, const QString &className = QString(
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 @@ static QString getDefaultFunction(const QString &n, const QString &className = Q
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 @@ static QString getFunction(const QString &n, const QString &className = QString(
return result;
}
-static void addQuotes(QString &s)
+void addQuotes(QString &s)
{
if (!s.startsWith(QLatin1Char('"'))) {
s.prepend(QLatin1Char('"'));
@@ -613,7 +180,7 @@ static QString quoteString(const QString &s)
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,20 +195,8 @@ static QString literalString(const QString &s)
}
}
-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;
@@ -650,345 +205,8 @@ static QString signalEnumName(const QString &signalName)
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<QUrl> 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<CfgEntry::Choice>::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<int> 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<Signal> 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<CfgEntry::Choice> 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 <choice> 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! <name> 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 @@ QString itemType(const QString &type)
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 @@ static QString itemPath(const CfgEntry *e, const CfgConfig &cfg)
}
QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue,
- const CfgConfig &cfg, const QString &param = QString()) {
+ const KConfigXTParameters &cfg, const QString &param) {
- QList<Signal> sigs = entry->signalList();
+ QList<Signal> 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 newItem(const CfgEntry* entry, const QString &key, const QString& defaul
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,12 +507,12 @@ QString paramString(const QString &group, const QList<Param> &parameters)
return "QStringLiteral( \"" + paramString + "\" )" + arguments;
}
-QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString &param = QString(), const QString &paramValue = QString())
+QString translatedString(const KConfigXTParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
{
QString result;
switch (cfg.translationSystem) {
- case CfgConfig::QtTranslation:
+ case KConfigXTParameters::QtTranslation:
if (!context.isEmpty()) {
result += "/*: " + context + " */ QCoreApplication::translate(\"";
} else {
@@ -1303,7 +521,7 @@ QString translatedString(const CfgConfig &cfg, const QString &string, const QStr
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,62 +548,36 @@ QString translatedString(const CfgConfig &cfg, const QString &string, const QStr
}
/* 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);
@@ -1394,101 +586,30 @@ void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &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;
@@ -1497,13 +618,13 @@ QString memberGetDefaultBody(CfgEntry *e)
// 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,30 +650,29 @@ QString indent(QString text, int spaces)
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)
@@ -1561,22 +681,22 @@ int main(int argc, char **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();
@@ -1584,7 +704,7 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
#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);
-
- CfgConfig cfg = CfgConfig(codegenFilename);
+ KConfigXTParameters cfg(codegenFilename);
- QFile input(inputFilename);
+ 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();
- 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;
- return 1;
- }
+ ParseResult parseResult = xmlParser.getParseResult();
- QDomElement cfgElement = doc.documentElement();
-
- if (cfgElement.isNull()) {
- cerr << "No document in kcfg file" << endl;
+ if (hasErrors(xmlParser, parseResult, cfg)) {
return 1;
}
- QString cfgFileName;
- bool cfgFileNameArg = false;
- QList<Param> parameters;
- QList<Signal> signalList;
- QStringList includes;
-
- QList<CfgEntry *> 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;
+ // TODO: Move this to somewhere saner.
+ for (const auto &signal : qAsConst(parseResult.signalList)) {
+ parseResult.hasNonModifySignals |= !signal.modify;
}
- 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 <qglobal.h>" << endl;
- }
-
- if (cfg.inherits == QLatin1String("KCoreConfigSkeleton")) {
- h << "#include <kcoreconfigskeleton.h>" << endl;
- } else {
- h << "#include <kconfigskeleton.h>" << endl;
- }
-
- h << "#include <QCoreApplication>" << endl;
- h << "#include <QDebug>" << 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<CfgEntry *>::ConstIterator itEntry;
- for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
- const CfgEntry::Choices &choices = (*itEntry)->choices();
- const QList<CfgEntry::Choice> chlist = choices.choices;
- if (!chlist.isEmpty()) {
- QStringList values;
- QList<CfgEntry::Choice>::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<Param>::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<Signal>::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<SignalArguments>::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<Param>::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 <klocalizedstring.h>" << endl << endl;
- }
-
- // Header required by singleton implementation
- if (cfg.singleton) {
- cpp << "#include <qglobal.h>" << endl << "#include <QFile>" << endl << endl;
- }
- if (cfg.singleton && cfgFileNameArg) {
- cpp << "#include <QDebug>" << 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<Param>::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<Param>::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<KConfigCompilerSignallingItem::NotifyFunction>(&"
- << 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<CfgEntry::Choice> choices = (*itEntry)->choices().choices;
- QList<CfgEntry::Choice>::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<SignalArguments>::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);
}