diff options
Diffstat (limited to 'src/kconfig_compiler/KConfigHeaderGenerator.cpp')
-rw-r--r-- | src/kconfig_compiler/KConfigHeaderGenerator.cpp | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.cpp b/src/kconfig_compiler/KConfigHeaderGenerator.cpp new file mode 100644 index 00000000..5fc16109 --- /dev/null +++ b/src/kconfig_compiler/KConfigHeaderGenerator.cpp @@ -0,0 +1,614 @@ +/* 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 KConfigParameters &cfg, + ParseResult &result) + : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result) +{ +} + +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 : parseResult.entries) { + const QString n = entry->name; + const QString t = entry->type; + + const QString returnType = (cfg().useEnumTypes && t == QLatin1String("Enum")) + ? enumType(entry, cfg().globalEnums) + : cppType(t); + + createSetters(entry); + createProperties(entry, returnType); + createGetters(entry, returnType); + createDefaultValueMember(entry); + createItemAcessors(entry, returnType); + } + + createSignals(); + stream() << " protected:" << endl; + createSingleton(); + + // TODO: Move those to functions too. + if (parseResult.hasNonModifySignals) { + stream() << whitespace() << "bool usrSave() override;" << endl; + } + + // Member variables + if (!cfg().memberVariables.isEmpty() + && cfg().memberVariables != QLatin1String("private") + && cfg().memberVariables != QLatin1String("dpointer")) { + stream() << " " << cfg().memberVariables << ":" << endl; + } + + // Class Parameters + for (const auto ¶meter : 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 : 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 : 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 : 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 : 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 : 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 : 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 : 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; + } +} |