aboutsummaryrefslogtreecommitdiff
path: root/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kconfig_compiler/KConfigCodeGeneratorBase.cpp')
-rw-r--r--src/kconfig_compiler/KConfigCodeGeneratorBase.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
new file mode 100644
index 00000000..574aeff4
--- /dev/null
+++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
@@ -0,0 +1,277 @@
+/* m_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)
+
+ m_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.
+
+ m_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 "KConfigParameters.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 KConfigParameters &parameters,
+ ParseResult &parseResult)
+ : m_inputFile(inputFile), m_baseDir(baseDir), m_fileName(fileName), m_cfg(parameters), parseResult(parseResult)
+{
+ m_file.setFileName(m_fileName);
+ if (!m_file.open(QIODevice::WriteOnly)) {
+ cerr << "Can not open '" << m_fileName << "for writing." << endl;
+ exit(1);
+ }
+ m_stream.setDevice(&m_file);
+ m_stream.setCodec("utf-8");
+
+ if (m_cfg.staticAccessors) {
+ m_this = QStringLiteral("self()->");
+ } else {
+ m_const = QStringLiteral(" const");
+ }
+}
+
+KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase()
+{
+ save();
+}
+
+void KConfigCodeGeneratorBase::save()
+{
+ m_file.close();
+}
+
+//TODO: Remove this weird logic and adapt the testcases
+void KConfigCodeGeneratorBase::indent()
+{
+ if (m_indentLevel >= 4) {
+ m_indentLevel += 2;
+ } else {
+ m_indentLevel += 4;
+ }
+}
+
+void KConfigCodeGeneratorBase::unindent()
+{
+ if (m_indentLevel > 4) {
+ m_indentLevel -= 2;
+ } else {
+ m_indentLevel -= 4;
+ }
+}
+
+QString KConfigCodeGeneratorBase::whitespace() const
+{
+ QString spaces;
+ for (int i = 0; i < m_indentLevel; i++) {
+ spaces.append(QLatin1Char(' '));
+ }
+ return spaces;
+}
+
+void KConfigCodeGeneratorBase::startScope()
+{
+ m_stream << whitespace() << QLatin1Char('{');
+ m_stream << endl;
+ indent();
+}
+
+void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer)
+{
+ unindent();
+ m_stream << whitespace() << QLatin1Char('}');
+ if (finalizer == ScopeFinalizer::Semicolon) {
+ m_stream << ';';
+ }
+ m_stream << endl;
+}
+
+void KConfigCodeGeneratorBase::start()
+{
+ const QString m_fileName = QFileInfo(m_inputFile).fileName();
+ m_stream << "// This file is generated by kconfig_compiler_kf5 from " << m_fileName << ".kcfg" << "." << endl;
+ m_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('"'))) {
+ m_stream << "#include " << include << endl;
+ } else {
+ m_stream << "#include <" << include << ">" << endl;
+ }
+ }
+}
+
+// adds as many 'namespace foo {' lines to p_out as
+// there are namespaces in p_ns
+void KConfigCodeGeneratorBase::beginNamespaces()
+{
+ if (!m_cfg.nameSpace.isEmpty()) {
+ for (const QString &ns : m_cfg.nameSpace.split(QStringLiteral("::"))) {
+ m_stream << "namespace " << ns << " {" << endl;
+ }
+ m_stream << endl;
+ }
+}
+
+// adds as many '}' lines to p_out as
+// there are namespaces in p_ns
+void KConfigCodeGeneratorBase::endNamespaces()
+{
+ if (!m_cfg.nameSpace.isEmpty()) {
+ m_stream << endl;
+ const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1;
+ for (int i = 0; i < namespaceCount; ++i) {
+ m_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 = m_cfg.useEnumTypes && t == QLatin1String("Enum");
+
+ out << "return ";
+ if (useEnumType) {
+ out << "static_cast<" << enumType(e, globalEnums) << ">(";
+ }
+ out << m_this << varPath(n, m_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() || m_cfg.generateProperties;
+
+ m_stream << whitespace() << "if (";
+ if (hasBody) {
+ m_stream << "v != " << varExpression << " && ";
+ }
+ m_stream << "!" << m_this << "isImmutable( QStringLiteral( \"";
+ if (!e->param.isEmpty()) {
+ QString paramName = e->paramName;
+
+ m_stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( ";
+ if (e->paramType == QLatin1String("Enum")) {
+ m_stream << "QLatin1String( ";
+
+ if (m_cfg.globalEnums) {
+ m_stream << enumName(e->param) << "ToString[i]";
+ } else {
+ m_stream << enumName(e->param) << "::enumToString[i]";
+ }
+
+ m_stream << " )";
+ } else {
+ m_stream << "i";
+ }
+ m_stream << " )";
+ } else {
+ m_stream << n << "\" )";
+ }
+ m_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)
+ m_stream << whitespace() << "if (v < " << e->min << ")" << endl;
+ m_stream << whitespace() << "{" << endl;
+ m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n);
+ m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl;
+ m_stream << whitespace() << " v = " << e->min << ";" << endl;
+ m_stream << whitespace() << "}" << endl;
+ }
+ }
+
+ if (!e->max.isEmpty()) {
+ m_stream << endl;
+ m_stream << whitespace() << "if (v > " << e->max << ")" << endl;
+ m_stream << whitespace() << "{" << endl;
+ m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n);
+ m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl;
+ m_stream << whitespace() << " v = " << e->max << ";" << endl;
+ m_stream << whitespace() << "}" << endl << endl;
+ }
+
+ const QString varExpression = m_this + varPath(n, m_cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]"));
+
+ // TODO: Remove this `hasBody` logic, always use an '{' for the if.
+ const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
+
+ // m_this call creates an `if (someTest ...) that's just to long to throw over the code.
+ createIfSetLogic(e, varExpression);
+ m_stream << (hasBody ? " {" : "") << endl;
+ m_stream << whitespace() << " " << varExpression << " = v;" << endl;
+
+ const auto listSignal = e->signalList;
+ for (const Signal &signal : qAsConst(listSignal)) {
+ if (signal.modify) {
+ m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();" << endl;
+ } else {
+ m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg)
+ << " |= " << signalEnumName(signal.name) << ";" << endl;
+ }
+ }
+ if (hasBody) {
+ m_stream << whitespace() << "}" << endl;
+ }
+}