Changeset View
Changeset View
Standalone View
Standalone View
src/kconfig_compiler/KConfigCodeGeneratorBase.cpp
- This file was added.
1 | /* This file is part of the KDE libraries | ||||
---|---|---|---|---|---|
2 | Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) | ||||
3 | Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> | ||||
4 | Copyright (c) 2003 Waldo Bastian <bastian@kde.org> | ||||
5 | Copyright (c) 2003 Zack Rusin <zack@kde.org> | ||||
6 | Copyright (c) 2006 Michaël Larouche <michael.larouche@kdemail.net> | ||||
7 | Copyright (c) 2008 Allen Winter <winter@kde.org> | ||||
8 | Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) | ||||
9 | | ||||
10 | This library is free software; you can redistribute it and/or | ||||
11 | modify it under the terms of the GNU Library General Public | ||||
12 | License as published by the Free Software Foundation; either | ||||
13 | version 2 of the License, or (at your option) any later version. | ||||
14 | | ||||
15 | This library is distributed in the hope that it will be useful, | ||||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
18 | Library General Public License for more details. | ||||
19 | | ||||
20 | You should have received a copy of the GNU Library General Public License | ||||
21 | along with this library; see the file COPYING.LIB. If not, write to | ||||
22 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
23 | Boston, MA 02110-1301, USA. | ||||
24 | */ | ||||
25 | | ||||
26 | #include "KConfigCodeGeneratorBase.h" | ||||
27 | #include "KConfigXTParameters.h" | ||||
28 | #include "KConfigCommonStructs.h" | ||||
29 | | ||||
30 | #include <QTextStream> | ||||
31 | #include <QLatin1Char> | ||||
32 | #include <QFileInfo> | ||||
33 | | ||||
34 | #include <ostream> | ||||
35 | #include <QDebug> | ||||
36 | | ||||
37 | using std::endl; | ||||
38 | | ||||
39 | namespace | ||||
40 | { | ||||
41 | QTextStream cout(stdout); | ||||
42 | QTextStream cerr(stderr); | ||||
43 | } | ||||
44 | | ||||
45 | KConfigCodeGeneratorBase::KConfigCodeGeneratorBase( | ||||
46 | const QString& inputFile, | ||||
47 | const QString& baseDir, | ||||
48 | const QString& fileName, | ||||
49 | const KConfigXTParameters ¶meters, | ||||
50 | ParseResult &parseResult) | ||||
51 | : inputFile(inputFile), baseDir(baseDir), fileName(fileName), cfg(parameters), parseResult(parseResult) | ||||
52 | { | ||||
53 | file.setFileName(fileName); | ||||
54 | if (!file.open(QIODevice::WriteOnly)) { | ||||
55 | cerr << "Can not open '" << fileName << "for writing." << endl; | ||||
56 | exit(1); | ||||
57 | } | ||||
58 | stream.setDevice(&file); | ||||
59 | stream.setCodec("utf-8"); | ||||
60 | | ||||
61 | if (cfg.staticAccessors) { | ||||
62 | This = QStringLiteral("self()->"); | ||||
63 | } else { | ||||
64 | Const = QStringLiteral(" const"); | ||||
65 | } | ||||
66 | } | ||||
67 | | ||||
68 | KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() | ||||
69 | { | ||||
70 | save(); | ||||
71 | } | ||||
72 | | ||||
73 | void KConfigCodeGeneratorBase::save() | ||||
74 | { | ||||
75 | file.close(); | ||||
76 | } | ||||
77 | | ||||
78 | void KConfigCodeGeneratorBase::indent() | ||||
79 | { | ||||
80 | if (indentLevel >= 4) { | ||||
81 | indentLevel += 2; | ||||
82 | } else { | ||||
83 | indentLevel += 4; | ||||
84 | } | ||||
85 | } | ||||
86 | | ||||
87 | void KConfigCodeGeneratorBase::unindent() | ||||
88 | { | ||||
89 | if (indentLevel > 4) { | ||||
90 | indentLevel -= 2; | ||||
91 | } else { | ||||
92 | indentLevel -= 4; | ||||
93 | } | ||||
94 | } | ||||
95 | | ||||
96 | QString KConfigCodeGeneratorBase::whitespace() | ||||
97 | { | ||||
98 | QString spaces; | ||||
99 | for (int i = 0; i < indentLevel; i++) { | ||||
100 | spaces.append(QLatin1Char(' ')); | ||||
101 | } | ||||
102 | return spaces; | ||||
103 | } | ||||
104 | | ||||
105 | void KConfigCodeGeneratorBase::startScope() | ||||
106 | { | ||||
107 | stream << whitespace() << QLatin1Char('{'); | ||||
108 | stream << endl; | ||||
109 | indent(); | ||||
110 | } | ||||
111 | | ||||
112 | void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) | ||||
113 | { | ||||
114 | unindent(); | ||||
115 | stream << whitespace() << QLatin1Char('}'); | ||||
116 | if (finalizer == ScopeFinalizer::Semicolon) { | ||||
117 | stream << ';'; | ||||
118 | } | ||||
119 | stream << endl; | ||||
120 | } | ||||
121 | | ||||
122 | void KConfigCodeGeneratorBase::start() | ||||
123 | { | ||||
124 | const QString fileName = QFileInfo(inputFile).fileName(); | ||||
125 | stream << "// This file is generated by kconfig_compiler_kf5 from " << fileName << ".kcfg" << "." << endl; | ||||
126 | stream << "// All changes you do to this file will be lost." << endl; | ||||
127 | } | ||||
128 | | ||||
129 | void KConfigCodeGeneratorBase::addHeaders(const QStringList& headerList) | ||||
130 | { | ||||
131 | for (auto include : qAsConst(headerList)) { | ||||
132 | if (include.startsWith(QLatin1Char('"'))) { | ||||
133 | stream << "#include " << include << endl; | ||||
134 | } else { | ||||
135 | stream << "#include <" << include << ">" << endl; | ||||
136 | } | ||||
137 | } | ||||
138 | } | ||||
139 | | ||||
140 | // adds as many 'namespace foo {' lines to p_out as | ||||
141 | // there are namespaces in p_ns | ||||
142 | void KConfigCodeGeneratorBase::beginNamespaces() | ||||
143 | { | ||||
144 | if (!cfg.nameSpace.isEmpty()) { | ||||
145 | for (const QString &ns : cfg.nameSpace.split(QStringLiteral("::"))) { | ||||
146 | stream << "namespace " << ns << " {" << endl; | ||||
147 | } | ||||
148 | stream << endl; | ||||
149 | } | ||||
150 | } | ||||
151 | | ||||
152 | // adds as many '}' lines to p_out as | ||||
153 | // there are namespaces in p_ns | ||||
154 | void KConfigCodeGeneratorBase::endNamespaces() | ||||
155 | { | ||||
156 | if (!cfg.nameSpace.isEmpty()) { | ||||
157 | stream << endl; | ||||
158 | const int namespaceCount = cfg.nameSpace.count(QStringLiteral("::")) + 1; | ||||
159 | for (int i = 0; i < namespaceCount; ++i) { | ||||
160 | stream << "}" << endl; | ||||
161 | } | ||||
162 | } | ||||
163 | } | ||||
164 | | ||||
165 | // returns the member accesor implementation | ||||
166 | // which should go in the h file if inline | ||||
167 | // or the cpp file if not inline | ||||
168 | QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const | ||||
169 | { | ||||
170 | QString result; | ||||
171 | QTextStream out(&result, QIODevice::WriteOnly); | ||||
172 | QString n = e->name; | ||||
173 | QString t = e->type; | ||||
174 | bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum"); | ||||
175 | | ||||
176 | out << "return "; | ||||
177 | if (useEnumType) { | ||||
178 | out << "static_cast<" << enumType(e, globalEnums) << ">("; | ||||
179 | } | ||||
180 | out << This << varPath(n, cfg); | ||||
181 | if (!e->param.isEmpty()) { | ||||
182 | out << "[i]"; | ||||
183 | } | ||||
184 | if (useEnumType) { | ||||
185 | out << ")"; | ||||
186 | } | ||||
187 | out << ";" << endl; | ||||
188 | | ||||
189 | return result; | ||||
190 | } | ||||
191 | | ||||
192 | void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) | ||||
193 | { | ||||
194 | const QString n = e->name; | ||||
195 | const QString t = e->type; | ||||
196 | const bool hasBody = !e->signalList.empty() || cfg.generateProperties; | ||||
197 | | ||||
198 | stream << whitespace() << "if ("; | ||||
199 | if (hasBody) { | ||||
200 | stream << "v != " << varExpression << " && "; | ||||
201 | } | ||||
202 | stream << "!" << This << "isImmutable( QStringLiteral( \""; | ||||
203 | if (!e->param.isEmpty()) { | ||||
204 | QString paramName = e->paramName; | ||||
205 | | ||||
206 | stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( "; | ||||
207 | if (e->paramType == QLatin1String("Enum")) { | ||||
208 | stream << "QLatin1String( "; | ||||
209 | | ||||
210 | if (cfg.globalEnums) { | ||||
211 | stream << enumName(e->param) << "ToString[i]"; | ||||
212 | } else { | ||||
213 | stream << enumName(e->param) << "::enumToString[i]"; | ||||
214 | } | ||||
215 | | ||||
216 | stream << " )"; | ||||
217 | } else { | ||||
218 | stream << "i"; | ||||
219 | } | ||||
220 | stream << " )"; | ||||
221 | } else { | ||||
222 | stream << n << "\" )"; | ||||
223 | } | ||||
224 | stream << " ))"; | ||||
225 | } | ||||
226 | | ||||
227 | void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) | ||||
228 | { | ||||
229 | QString n = e->name; | ||||
230 | QString t = e->type; | ||||
231 | | ||||
232 | // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. | ||||
233 | if (!e->min.isEmpty()) { | ||||
234 | if (e->min != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) | ||||
235 | stream << whitespace() << "if (v < " << e->min << ")" << endl; | ||||
236 | stream << whitespace() << "{" << endl; | ||||
237 | stream << whitespace(); addDebugMethod(stream, cfg, n); | ||||
238 | stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl; | ||||
239 | stream << whitespace() << " v = " << e->min << ";" << endl; | ||||
240 | stream << whitespace() << "}" << endl; | ||||
241 | } | ||||
242 | } | ||||
243 | | ||||
244 | if (!e->max.isEmpty()) { | ||||
245 | stream << endl; | ||||
246 | stream << whitespace() << "if (v > " << e->max << ")" << endl; | ||||
247 | stream << whitespace() << "{" << endl; | ||||
248 | stream << whitespace(); addDebugMethod(stream, cfg, n); | ||||
249 | stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl; | ||||
250 | stream << whitespace() << " v = " << e->max << ";" << endl; | ||||
251 | stream << whitespace() << "}" << endl << endl; | ||||
252 | } | ||||
253 | | ||||
254 | const QString varExpression = This + varPath(n, cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]")); | ||||
255 | | ||||
256 | // TODO: Remove this `hasBody` logic, always use an '{' for the if. | ||||
257 | const bool hasBody = !e->signalList.empty() || cfg.generateProperties; | ||||
258 | | ||||
259 | // This call creates an `if (someTest ...) that's just to long to throw over the code. | ||||
260 | createIfSetLogic(e, varExpression); | ||||
261 | stream << (hasBody ? " {" : "") << endl; | ||||
262 | stream << whitespace() << " " << varExpression << " = v;" << endl; | ||||
263 | | ||||
264 | const auto listSignal = e->signalList; | ||||
265 | for (const Signal &signal : qAsConst(listSignal)) { | ||||
266 | if (signal.modify) { | ||||
267 | stream << whitespace() << " Q_EMIT " << This << signal.name << "();" << endl; | ||||
268 | } else { | ||||
269 | stream << whitespace() << " " << This << varPath(QStringLiteral("settingsChanged"), cfg) | ||||
270 | << " |= " << signalEnumName(signal.name) << ";" << endl; | ||||
271 | } | ||||
272 | } | ||||
273 | if (hasBody) { | ||||
274 | stream << whitespace() << "}" << endl; | ||||
275 | } | ||||
276 | } |