Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
projsongenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
6
7#include "qmake-parser/profileevaluator.h"
8#include "qmake-parser/qmakeparser.h"
9#include "qmake-parser/qmakevfs.h"
10#include "qrcreader.h"
11
12#include <QtCore/QDir>
13#include <QtCore/QDirIterator>
14#include <QtCore/QFile>
15#include <QtCore/QFileInfo>
16#include <QtCore/QJsonArray>
17#include <QtCore/QJsonDocument>
18#include <QtCore/QJsonObject>
19#include <QtCore/QLibraryInfo>
20#include <QtCore/QRegularExpression>
21
22using namespace Qt::StringLiterals;
23
24static QStringList getResources(const QString &resourceFile, QMakeVfs *vfs);
25static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
26 const QString &projectDir, const ProFileEvaluator &visitor);
27static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir,
28 QMakeVfs *vfs);
29static QStringList getExcludes(const ProFileEvaluator &visitor, const QString &projectDirPath);
30static void excludeProjects(const ProFileEvaluator &visitor, QStringList *subProjects);
31
33{
34public:
35 void message(int type, const QString &msg, const QString &fileName, int lineNo) override
36 {
37 if (verbose && !(type & CumulativeEvalMessage) && (type & CategoryMask) == ErrorMessage) {
38 if (lineNo > 0)
39 qWarning("WARNING: %s:%d: %s", qPrintable(fileName), lineNo, qPrintable(msg));
40 else if (lineNo)
41 qWarning("WARNING: %s: %s", qPrintable(fileName), qPrintable(msg));
42 else
43 qWarning("WARNING: %s", qPrintable(msg));
44 }
45 }
46
47 void fileMessage(int type, const QString &msg) override
48 {
49 if (verbose && !(type & CumulativeEvalMessage) && (type & CategoryMask) == ErrorMessage) {
50 qWarning("WARNING: %s", qPrintable(msg));
51 }
52 }
53
54 void aboutToEval(ProFile *, ProFile *, EvalFileType) override {}
55 void doneWithEval(ProFile *) override {}
56
57 bool verbose = true;
58};
59
60static QStringList getResources(const QString &resourceFile, QMakeVfs *vfs)
61{
62 Q_ASSERT(vfs);
63 if (!vfs->exists(resourceFile, QMakeVfs::VfsCumulative))
64 return QStringList();
65 QString content;
66 QString errStr;
67 if (vfs->readFile(vfs->idForFileName(resourceFile, QMakeVfs::VfsCumulative), &content, &errStr,
68 true)
70 qWarning("Pro-JSON-generator error: Cannot read %s: %s", qPrintable(resourceFile),
71 qPrintable(errStr));
72 return QStringList();
73 }
74 const ReadQrcResult rqr = readQrcFile(resourceFile, content);
75 if (rqr.hasError()) {
76 qWarning("Pro-JSON-generator error: %s:%lld: %s", qPrintable(resourceFile), rqr.line,
77 qPrintable(rqr.errorString));
78 }
79 return rqr.files;
80}
81
82static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
83 const QString &projectDir, const ProFileEvaluator &visitor)
84{
85 QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir);
86 vPaths += baseVPaths;
87 vPaths.removeDuplicates();
88 return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
89}
90
91static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir,
92 QMakeVfs *vfs)
93{
94 QStringList baseVPaths;
95 baseVPaths += visitor.absolutePathValues("VPATH"_L1, projectDir);
96 baseVPaths << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
97 baseVPaths.removeDuplicates();
98
99 QStringList sourceFiles;
100
101 // app/lib template
102 sourceFiles += getSources("SOURCES", "VPATH_SOURCES", baseVPaths, projectDir, visitor);
103 sourceFiles += getSources("HEADERS", "VPATH_HEADERS", baseVPaths, projectDir, visitor);
104
105 sourceFiles += getSources("FORMS", "VPATH_FORMS", baseVPaths, projectDir, visitor);
106
107 const QStringList resourceFiles =
108 getSources("RESOURCES", "VPATH_RESOURCES", baseVPaths, projectDir, visitor);
109 for (const QString &resource : resourceFiles)
110 sourceFiles += getResources(resource, vfs);
111
112 QStringList installs = visitor.values("INSTALLS"_L1) + visitor.values("DEPLOYMENT"_L1);
113 installs.removeDuplicates();
114 QDir baseDir(projectDir);
115 for (const QString &inst : std::as_const(installs)) {
116 for (const QString &file : visitor.values(inst + ".files"_L1)) {
117 QFileInfo info(file);
118 if (!info.isAbsolute())
119 info.setFile(baseDir.absoluteFilePath(file));
120 QStringList nameFilter;
121 QString searchPath;
122 if (info.isDir()) {
123 nameFilter << "*"_L1;
124 searchPath = info.filePath();
125 } else {
126 nameFilter << info.fileName();
127 searchPath = info.path();
128 }
129
130 QDirIterator iterator(searchPath, nameFilter,
131 QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks,
132 QDirIterator::Subdirectories);
133 while (iterator.hasNext()) {
134 iterator.next();
135 QFileInfo cfi = iterator.fileInfo();
136 if (isSupportedExtension(cfi.suffix()))
137 sourceFiles << cfi.filePath();
138 }
139 }
140 }
141
142 sourceFiles.removeDuplicates();
143 sourceFiles.sort();
144 return sourceFiles;
145}
146
147static QStringList getExcludes(const ProFileEvaluator &visitor, const QString &projectDirPath)
148{
149 const QStringList trExcludes = visitor.values("TR_EXCLUDE"_L1);
150 QStringList excludes;
151 excludes.reserve(trExcludes.size());
152 const QDir projectDir(projectDirPath);
153 for (const QString &ex : trExcludes)
154 excludes << QDir::cleanPath(projectDir.absoluteFilePath(ex));
155 return excludes;
156}
157
158static void excludeProjects(const ProFileEvaluator &visitor, QStringList *subProjects)
159{
160 for (const QString &ex : visitor.values("TR_EXCLUDE"_L1)) {
161 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
162 for (auto it = subProjects->begin(); it != subProjects->end(); ) {
163 if (rx.match(*it).hasMatch())
164 it = subProjects->erase(it);
165 else
166 ++it;
167 }
168 }
169}
170
171static QJsonValue toJsonValue(const QJsonValue &v)
172{
173 return v;
174}
175
176static QJsonValue toJsonValue(const QString &s)
177{
178 return QJsonValue(s);
179}
180
181static QJsonValue toJsonValue(const QStringList &lst)
182{
183 return QJsonArray::fromStringList(lst);
184}
185
186template <class T>
187static void setValue(QJsonObject &obj, const char *key, T value)
188{
189 obj[QLatin1String(key)] = toJsonValue(value);
190}
191
192static QJsonArray processProjects(bool topLevel, const QStringList &proFiles,
193 const QStringList &translationsVariables,
194 const QHash<QString, QString> &outDirMap, ProFileGlobals *option,
195 QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler,
196 bool *fail);
197
198static QJsonObject processProject(const QString &proFile, const QStringList &translationsVariables,
199 ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser,
200 EvalHandler *evalHandler, ProFileEvaluator &visitor)
201{
202 QJsonObject result;
203 QStringList tmp = visitor.values("CODECFORSRC"_L1);
204 if (!tmp.isEmpty())
205 result[QStringLiteral("codec")] = tmp.last();
206 QString proPath = QFileInfo(proFile).path();
208 QStringList subProjects = visitor.values("SUBDIRS"_L1);
209 excludeProjects(visitor, &subProjects);
210 QStringList subProFiles;
211 QDir proDir(proPath);
212 for (const QString &subdir : std::as_const(subProjects)) {
213 QString realdir = visitor.value(subdir + ".subdir"_L1);
214 if (realdir.isEmpty())
215 realdir = visitor.value(subdir + ".file"_L1);
216 if (realdir.isEmpty())
217 realdir = subdir;
218 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(realdir));
219 QFileInfo subInfo(subPro);
220 if (subInfo.isDir()) {
221 subProFiles << (subPro + u'/' + subInfo.fileName() + ".pro"_L1);
222 } else {
223 subProFiles << subPro;
224 }
225 }
226 QJsonArray subResults = processProjects(false, subProFiles, translationsVariables,
227 QHash<QString, QString>(), option, vfs, parser,
228 evalHandler, nullptr);
229 if (!subResults.isEmpty())
230 setValue(result, "subProjects", subResults);
231 } else {
232 const QStringList sourceFiles = getSources(visitor, proPath, vfs);
233 setValue(result, "includePaths", visitor.absolutePathValues("INCLUDEPATH"_L1, proPath));
234 setValue(result, "excluded", getExcludes(visitor, proPath));
235 setValue(result, "sources", sourceFiles);
236 }
237 return result;
238}
239
240static QJsonArray processProjects(bool topLevel, const QStringList &proFiles,
241 const QStringList &translationsVariables,
242 const QHash<QString, QString> &outDirMap, ProFileGlobals *option,
243 QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler,
244 bool *fail)
245{
246 QJsonArray result;
247 for (const QString &proFile : proFiles) {
248 if (!outDirMap.isEmpty())
249 option->setDirectories(QFileInfo(proFile).path(), outDirMap[proFile]);
250
251 ProFile *pro;
252 if (!(pro = parser->parsedProFile(proFile,
253 topLevel ? QMakeParser::ParseReportMissing
254 : QMakeParser::ParseDefault))) {
255 if (topLevel && fail)
256 *fail = true;
257 continue;
258 }
259 ProFileEvaluator visitor(option, parser, vfs, evalHandler);
260 visitor.setCumulative(true);
261 visitor.setOutputDir(option->shadowedPath(pro->directoryName()));
262 if (!visitor.accept(pro)) {
263 if (topLevel && fail)
264 *fail = true;
265 pro->deref();
266 continue;
267 }
268
269 QJsonObject prj = processProject(proFile, translationsVariables, option, vfs, parser,
270 evalHandler, visitor);
271 setValue(prj, "projectFile", proFile);
272 QStringList tsFiles;
273 for (const QString &varName : translationsVariables) {
274 if (!visitor.contains(varName))
275 continue;
276 QDir proDir(QFileInfo(proFile).path());
277 const QStringList translations = visitor.values(varName);
278 for (const QString &tsFile : translations)
279 tsFiles << proDir.filePath(tsFile);
280 }
281 if (!tsFiles.isEmpty())
282 setValue(prj, "translations", tsFiles);
283 if (visitor.contains("LUPDATE_COMPILE_COMMANDS_PATH"_L1)) {
284 const QStringList thepathjson = visitor.values("LUPDATE_COMPILE_COMMANDS_PATH"_L1);
285 setValue(prj, "compileCommands", thepathjson.value(0));
286 }
287 result.append(prj);
288 pro->deref();
289 }
290 return result;
291}
292
293static std::optional<QJsonArray>
294generateProjectDescription(const QStringList &proFiles, const QStringList &translationsVariables,
295 const QHash<QString, QString> &outDirMap, int proDebug, bool verbose)
296{
297 bool fail = false;
298 ProFileGlobals option;
299 option.qmake_abslocation = QString::fromLocal8Bit(qgetenv("QMAKE"));
300 if (option.qmake_abslocation.isEmpty()) {
301 option.qmake_abslocation = QLibraryInfo::paths(QLibraryInfo::BinariesPath).value(0) + "/qmake"_L1;
302 }
303 option.debugLevel = proDebug;
304 option.initProperties();
305 option.setCommandLineArguments(QDir::currentPath(), { "CONFIG+=lupdate_run"_L1 });
306
307 QMakeVfs vfs;
308 EvalHandler evalHandler;
309 evalHandler.verbose = verbose;
310 QMakeParser parser(0, &vfs, &evalHandler);
311
312 QJsonArray json = processProjects(true, proFiles, translationsVariables, outDirMap, &option,
313 &vfs, &parser, &evalHandler, &fail);
314 std::optional<QJsonArray> result;
315 if (!fail)
316 result.emplace(std::move(json));
317
318 return result;
319}
320
321QT_BEGIN_NAMESPACE
322
323Projects generateProjects(const QStringList &proFiles, const QStringList &translationsVariables,
324 const QHash<QString, QString> &outDirMap, int proDebug, bool verbose,
325 QString *errorString, QJsonArray *resultJson)
326{
327 errorString->clear();
328
329 std::optional<QJsonArray> jsonResults = generateProjectDescription(proFiles, translationsVariables, outDirMap,
330 proDebug, verbose);
331 if (!jsonResults) {
332 *errorString = "Failed to generate project description from .pro files"_L1;
333 return {};
334 }
335
336 if (resultJson)
337 *resultJson = *jsonResults;
338
339 Projects projects = projectDescriptionFromJson(*jsonResults, errorString);
340 if (!errorString->isEmpty())
341 return {};
342
343 return projects;
344}
345
346QT_END_NAMESPACE
void message(int type, const QString &msg, const QString &fileName, int lineNo) override
void fileMessage(int type, const QString &msg) override
void doneWithEval(ProFile *) override
void aboutToEval(ProFile *, ProFile *, EvalFileType) override
ProFileEvaluator::TemplateType templateType() const
\inmodule QtCore
Definition qhash.h:843
ReadResult readFile(int id, QString *contents, QString *errStr, bool utf8=false)
Definition qmakevfs.cpp:165
@ VfsCumulative
Definition qmakevfs.h:41
const QString & asString(const QString &s)
Definition qstring.h:1678
static QStringList getExcludes(const ProFileEvaluator &visitor, const QString &projectDirPath)
static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir, QMakeVfs *vfs)
static void excludeProjects(const ProFileEvaluator &visitor, QStringList *subProjects)
static QJsonValue toJsonValue(const QString &s)
static QJsonObject processProject(const QString &proFile, const QStringList &translationsVariables, ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler, ProFileEvaluator &visitor)
static QJsonValue toJsonValue(const QJsonValue &v)
static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths, const QString &projectDir, const ProFileEvaluator &visitor)
static QStringList getResources(const QString &resourceFile, QMakeVfs *vfs)
static void setValue(QJsonObject &obj, const char *key, T value)
static QJsonArray processProjects(bool topLevel, const QStringList &proFiles, const QStringList &translationsVariables, const QHash< QString, QString > &outDirMap, ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler, bool *fail)
static std::optional< QJsonArray > generateProjectDescription(const QStringList &proFiles, const QStringList &translationsVariables, const QHash< QString, QString > &outDirMap, int proDebug, bool verbose)
#define qPrintable(string)
Definition qstring.h:1683
#define QStringLiteral(str)
Definition qstring.h:1825