diff options
Diffstat (limited to 'src/kconfig_compiler/KConfigCodeGeneratorBase.cpp')
-rw-r--r-- | src/kconfig_compiler/KConfigCodeGeneratorBase.cpp | 276 |
1 files changed, 276 insertions, 0 deletions
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 ¶meters, + ParseResult &parseResult) + : inputFile(inputFile), baseDir(baseDir), fileName(fileName), cfg(parameters), parseResult(parseResult) +{ + file.setFileName(fileName); + if (!file.open(QIODevice::WriteOnly)) { + cerr << "Can not open '" << fileName << "for writing." << endl; + exit(1); + } + stream.setDevice(&file); + stream.setCodec("utf-8"); + + if (cfg.staticAccessors) { + This = QStringLiteral("self()->"); + } else { + Const = QStringLiteral(" const"); + } +} + +KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() +{ + save(); +} + +void KConfigCodeGeneratorBase::save() +{ + file.close(); +} + +void KConfigCodeGeneratorBase::indent() +{ + if (indentLevel >= 4) { + indentLevel += 2; + } else { + indentLevel += 4; + } +} + +void KConfigCodeGeneratorBase::unindent() +{ + if (indentLevel > 4) { + indentLevel -= 2; + } else { + indentLevel -= 4; + } +} + +QString KConfigCodeGeneratorBase::whitespace() +{ + QString spaces; + for (int i = 0; i < indentLevel; i++) { + spaces.append(QLatin1Char(' ')); + } + return spaces; +} + +void KConfigCodeGeneratorBase::startScope() +{ + stream << whitespace() << QLatin1Char('{'); + stream << endl; + indent(); +} + +void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) +{ + unindent(); + stream << whitespace() << QLatin1Char('}'); + if (finalizer == ScopeFinalizer::Semicolon) { + stream << ';'; + } + stream << endl; +} + +void KConfigCodeGeneratorBase::start() +{ + const QString fileName = QFileInfo(inputFile).fileName(); + stream << "// This file is generated by kconfig_compiler_kf5 from " << fileName << ".kcfg" << "." << endl; + stream << "// All changes you do to this file will be lost." << endl; +} + +void KConfigCodeGeneratorBase::addHeaders(const QStringList& headerList) +{ + for (auto include : qAsConst(headerList)) { + if (include.startsWith(QLatin1Char('"'))) { + stream << "#include " << include << endl; + } else { + stream << "#include <" << include << ">" << endl; + } + } +} + +// adds as many 'namespace foo {' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::beginNamespaces() +{ + if (!cfg.nameSpace.isEmpty()) { + for (const QString &ns : cfg.nameSpace.split(QStringLiteral("::"))) { + stream << "namespace " << ns << " {" << endl; + } + stream << endl; + } +} + +// adds as many '}' lines to p_out as +// there are namespaces in p_ns +void KConfigCodeGeneratorBase::endNamespaces() +{ + if (!cfg.nameSpace.isEmpty()) { + stream << endl; + const int namespaceCount = cfg.nameSpace.count(QStringLiteral("::")) + 1; + for (int i = 0; i < namespaceCount; ++i) { + stream << "}" << endl; + } + } +} + +// returns the member accesor implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + QString n = e->name; + QString t = e->type; + bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum"); + + out << "return "; + if (useEnumType) { + out << "static_cast<" << enumType(e, globalEnums) << ">("; + } + out << This << varPath(n, cfg); + if (!e->param.isEmpty()) { + out << "[i]"; + } + if (useEnumType) { + out << ")"; + } + out << ";" << endl; + + return result; +} + +void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) +{ + const QString n = e->name; + const QString t = e->type; + const bool hasBody = !e->signalList.empty() || cfg.generateProperties; + + stream << whitespace() << "if ("; + if (hasBody) { + stream << "v != " << varExpression << " && "; + } + stream << "!" << This << "isImmutable( QStringLiteral( \""; + if (!e->param.isEmpty()) { + QString paramName = e->paramName; + + stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( "; + if (e->paramType == QLatin1String("Enum")) { + stream << "QLatin1String( "; + + if (cfg.globalEnums) { + stream << enumName(e->param) << "ToString[i]"; + } else { + stream << enumName(e->param) << "::enumToString[i]"; + } + + stream << " )"; + } else { + stream << "i"; + } + stream << " )"; + } else { + stream << n << "\" )"; + } + stream << " ))"; +} + +void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) +{ + QString n = e->name; + QString t = e->type; + + // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. + if (!e->min.isEmpty()) { + if (e->min != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) + stream << whitespace() << "if (v < " << e->min << ")" << endl; + stream << whitespace() << "{" << endl; + stream << whitespace(); addDebugMethod(stream, cfg, n); + stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl; + stream << whitespace() << " v = " << e->min << ";" << endl; + stream << whitespace() << "}" << endl; + } + } + + if (!e->max.isEmpty()) { + stream << endl; + stream << whitespace() << "if (v > " << e->max << ")" << endl; + stream << whitespace() << "{" << endl; + stream << whitespace(); addDebugMethod(stream, cfg, n); + stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl; + stream << whitespace() << " v = " << e->max << ";" << endl; + stream << whitespace() << "}" << endl << endl; + } + + const QString varExpression = This + varPath(n, cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]")); + + // TODO: Remove this `hasBody` logic, always use an '{' for the if. + const bool hasBody = !e->signalList.empty() || cfg.generateProperties; + + // This call creates an `if (someTest ...) that's just to long to throw over the code. + createIfSetLogic(e, varExpression); + stream << (hasBody ? " {" : "") << endl; + stream << whitespace() << " " << varExpression << " = v;" << endl; + + const auto listSignal = e->signalList; + for (const Signal &signal : qAsConst(listSignal)) { + if (signal.modify) { + stream << whitespace() << " Q_EMIT " << This << signal.name << "();" << endl; + } else { + stream << whitespace() << " " << This << varPath(QStringLiteral("settingsChanged"), cfg) + << " |= " << signalEnumName(signal.name) << ";" << endl; + } + } + if (hasBody) { + stream << whitespace() << "}" << endl; + } +} |