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)
69 qWarning("Pro-JSON-generator error: Cannot read %s: %s", qPrintable(resourceFile),
70 qPrintable(errStr));
71 return QStringList();
72 }
73 const ReadQrcResult rqr = readQrcFile(resourceFile, content);
74 if (rqr.hasError()) {
75 qWarning("Pro-JSON-generator error: %s:%lld: %s", qPrintable(resourceFile), rqr.line,
76 qPrintable(rqr.errorString));
77 }
78 return rqr.files;
79}
80
81static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
82 const QString &projectDir, const ProFileEvaluator &visitor)
83{
84 QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir);
85 vPaths += baseVPaths;
86 vPaths.removeDuplicates();
87 return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
88}
89
90static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir,
91 QMakeVfs *vfs)
92{
93 QStringList baseVPaths;
94 baseVPaths += visitor.absolutePathValues("VPATH"_L1, projectDir);
95 baseVPaths << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
96 baseVPaths.removeDuplicates();
97
98 QStringList sourceFiles;
99
100 // app/lib template
101 sourceFiles += getSources("SOURCES", "VPATH_SOURCES", baseVPaths, projectDir, visitor);
102 sourceFiles += getSources("HEADERS", "VPATH_HEADERS", baseVPaths, projectDir, visitor);
103
104 sourceFiles += getSources("FORMS", "VPATH_FORMS", baseVPaths, projectDir, visitor);
105
106 const QStringList resourceFiles =
107 getSources("RESOURCES", "VPATH_RESOURCES", baseVPaths, projectDir, visitor);
108 for (const QString &resource : resourceFiles)
109 sourceFiles += getResources(resource, vfs);
110
111 QStringList installs = visitor.values("INSTALLS"_L1) + visitor.values("DEPLOYMENT"_L1);
112 installs.removeDuplicates();
113 QDir baseDir(projectDir);
114 for (const QString &inst : std::as_const(installs)) {
115 for (const QString &file : visitor.values(inst + ".files"_L1)) {
116 QFileInfo info(file);
117 if (!info.isAbsolute())
118 info.setFile(baseDir.absoluteFilePath(file));
119 QStringList nameFilter;
120 QString searchPath;
121 if (info.isDir()) {
122 nameFilter << "*"_L1;
123 searchPath = info.filePath();
124 } else {
125 nameFilter << info.fileName();
126 searchPath = info.path();
127 }
128
129 QDirIterator iterator(searchPath, nameFilter,
130 QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks,
131 QDirIterator::Subdirectories);
132 while (iterator.hasNext()) {
133 iterator.next();
134 QFileInfo cfi = iterator.fileInfo();
135 if (isSupportedExtension(cfi.suffix()))
136 sourceFiles << cfi.filePath();
137 }
138 }
139 }
140
141 sourceFiles.removeDuplicates();
142 sourceFiles.sort();
143 return sourceFiles;
144}
145
146static QStringList getExcludes(const ProFileEvaluator &visitor, const QString &projectDirPath)
147{
148 const QStringList trExcludes = visitor.values("TR_EXCLUDE"_L1);
149 QStringList excludes;
150 excludes.reserve(trExcludes.size());
151 const QDir projectDir(projectDirPath);
152 for (const QString &ex : trExcludes)
153 excludes << QDir::cleanPath(projectDir.absoluteFilePath(ex));
154 return excludes;
155}
156
157static void excludeProjects(const ProFileEvaluator &visitor, QStringList *subProjects)
158{
159 for (const QString &ex : visitor.values("TR_EXCLUDE"_L1)) {
160 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
161 for (auto it = subProjects->begin(); it != subProjects->end(); ) {
162 if (rx.match(*it).hasMatch())
163 it = subProjects->erase(it);
164 else
165 ++it;
166 }
167 }
168}
169
170static QJsonValue toJsonValue(const QJsonValue &v)
171{
172 return v;
173}
174
175static QJsonValue toJsonValue(const QString &s)
176{
177 return QJsonValue(s);
178}
179
180static QJsonValue toJsonValue(const QStringList &lst)
181{
182 return QJsonArray::fromStringList(lst);
183}
184
185template <class T>
186static void setValue(QJsonObject &obj, const char *key, T value)
187{
188 obj[QLatin1String(key)] = toJsonValue(value);
189}
190
191static QJsonArray processProjects(bool topLevel, const QStringList &proFiles,
192 const QStringList &translationsVariables,
193 const QHash<QString, QString> &outDirMap, ProFileGlobals *option,
194 QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler,
195 bool *fail);
196
197static QJsonObject processProject(const QString &proFile, const QStringList &translationsVariables,
198 ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser,
199 EvalHandler *evalHandler, ProFileEvaluator &visitor)
200{
201 QJsonObject result;
202 QStringList tmp = visitor.values("CODECFORSRC"_L1);
203 if (!tmp.isEmpty())
204 result[QStringLiteral("codec")] = tmp.last();
205 QString proPath = QFileInfo(proFile).path();
207 QStringList subProjects = visitor.values("SUBDIRS"_L1);
208 excludeProjects(visitor, &subProjects);
209 QStringList subProFiles;
210 QDir proDir(proPath);
211 for (const QString &subdir : std::as_const(subProjects)) {
212 QString realdir = visitor.value(subdir + ".subdir"_L1);
213 if (realdir.isEmpty())
214 realdir = visitor.value(subdir + ".file"_L1);
215 if (realdir.isEmpty())
216 realdir = subdir;
217 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(realdir));
218 QFileInfo subInfo(subPro);
219 if (subInfo.isDir()) {
220 subProFiles << (subPro + u'/' + subInfo.fileName() + ".pro"_L1);
221 } else {
222 subProFiles << subPro;
223 }
224 }
225 QJsonArray subResults = processProjects(false, subProFiles, translationsVariables,
226 QHash<QString, QString>(), option, vfs, parser,
227 evalHandler, nullptr);
228 if (!subResults.isEmpty())
229 setValue(result, "subProjects", subResults);
230 } else {
231 const QStringList sourceFiles = getSources(visitor, proPath, vfs);
232 setValue(result, "includePaths", visitor.absolutePathValues("INCLUDEPATH"_L1, proPath));
233 setValue(result, "excluded", getExcludes(visitor, proPath));
234 setValue(result, "sources", sourceFiles);
235 }
236 return result;
237}
238
239static QJsonArray processProjects(bool topLevel, const QStringList &proFiles,
240 const QStringList &translationsVariables,
241 const QHash<QString, QString> &outDirMap, ProFileGlobals *option,
242 QMakeVfs *vfs, QMakeParser *parser, EvalHandler *evalHandler,
243 bool *fail)
244{
245 QJsonArray result;
246 for (const QString &proFile : proFiles) {
247 if (!outDirMap.isEmpty())
248 option->setDirectories(QFileInfo(proFile).path(), outDirMap[proFile]);
249
250 ProFile *pro;
251 if (!(pro = parser->parsedProFile(proFile,
252 topLevel ? QMakeParser::ParseReportMissing
253 : QMakeParser::ParseDefault))) {
254 if (topLevel && fail)
255 *fail = true;
256 continue;
257 }
258 ProFileEvaluator visitor(option, parser, vfs, evalHandler);
259 visitor.setCumulative(true);
260 visitor.setOutputDir(option->shadowedPath(pro->directoryName()));
261 if (!visitor.accept(pro)) {
262 if (topLevel && fail)
263 *fail = true;
264 pro->deref();
265 continue;
266 }
267
268 QJsonObject prj = processProject(proFile, translationsVariables, option, vfs, parser,
269 evalHandler, visitor);
270 setValue(prj, "projectFile", proFile);
271 QStringList tsFiles;
272 for (const QString &varName : translationsVariables) {
273 if (!visitor.contains(varName))
274 continue;
275 QDir proDir(QFileInfo(proFile).path());
276 const QStringList translations = visitor.values(varName);
277 for (const QString &tsFile : translations)
278 tsFiles << proDir.filePath(tsFile);
279 }
280 if (!tsFiles.isEmpty())
281 setValue(prj, "translations", tsFiles);
282 if (visitor.contains("LUPDATE_COMPILE_COMMANDS_PATH"_L1)) {
283 const QStringList thepathjson = visitor.values("LUPDATE_COMPILE_COMMANDS_PATH"_L1);
284 setValue(prj, "compileCommands", thepathjson.value(0));
285 }
286 result.append(prj);
287 pro->deref();
288 }
289 return result;
290}
291
292static std::optional<QJsonArray>
293generateProjectDescription(const QStringList &proFiles, const QStringList &translationsVariables,
294 const QHash<QString, QString> &outDirMap, int proDebug, bool verbose)
295{
296 bool fail = false;
297 ProFileGlobals option;
298 option.qmake_abslocation = QString::fromLocal8Bit(qgetenv("QMAKE"));
299 if (option.qmake_abslocation.isEmpty()) {
300 option.qmake_abslocation = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmake"_L1;
301 }
302 option.debugLevel = proDebug;
303 option.initProperties();
304 option.setCommandLineArguments(QDir::currentPath(), { "CONFIG+=lupdate_run"_L1 });
305
306 QMakeVfs vfs;
307 EvalHandler evalHandler;
308 evalHandler.verbose = verbose;
309 QMakeParser parser(0, &vfs, &evalHandler);
310
311 QJsonArray json = processProjects(true, proFiles, translationsVariables, outDirMap, &option,
312 &vfs, &parser, &evalHandler, &fail);
313 std::optional<QJsonArray> result;
314 if (!fail)
315 result.emplace(std::move(json));
316
317 return result;
318}
319
320QT_BEGIN_NAMESPACE
321
322Projects generateProjects(const QStringList &proFiles, const QStringList &translationsVariables,
323 const QHash<QString, QString> &outDirMap, int proDebug, bool verbose,
324 QString *errorString, QJsonArray *resultJson)
325{
326 errorString->clear();
327
328 std::optional<QJsonArray> jsonResults = generateProjectDescription(proFiles, translationsVariables, outDirMap,
329 proDebug, verbose);
330 if (!jsonResults) {
331 *errorString = "Failed to generate project description from .pro files"_L1;
332 return {};
333 }
334
335 if (resultJson)
336 *resultJson = *jsonResults;
337
338 Projects projects = projectDescriptionFromJson(*jsonResults, errorString);
339 if (!errorString->isEmpty())
340 return {};
341
342 return projects;
343}
344
345QT_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)
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