4#include <profileevaluator.h>
5#include <profileutils.h>
6#include <qmakeparser.h>
10#include <QtCore/QCoreApplication>
11#include <QtCore/QDebug>
13#include <QtCore/QDirIterator>
14#include <QtCore/QFile>
15#include <QtCore/QFileInfo>
16#include <QtCore/QLibraryInfo>
17#include <QtCore/QRegularExpression>
18#include <QtCore/QString>
19#include <QtCore/QStringList>
21#include <QtCore/QJsonArray>
22#include <QtCore/QJsonDocument>
23#include <QtCore/QJsonObject>
51 return QJsonArray::fromStringList(lst);
55void setValue(QJsonObject &obj,
const char *key, T value)
57 obj[QLatin1String(key)] = toJsonValue(value);
63 lprodump [options] project-file...
64lprodump is part of Qt's Linguist tool chain. It extracts information
65from qmake projects to a .json file. This file can be passed to
66lupdate/lrelease using the -project option.
67
68Options:
69 -help Display this information and exit.
70 -silent
71 Do not explain what is being done.
72 -pro <filename>
73 Name of a .pro file. Useful for files with .pro file syntax but
74 different file suffix. Projects are recursed into and merged.
76 Virtual output directory for processing subsequent .pro files.
78 Trace processing .pro files. Specify twice for more verbosity.
79 -out <filename>
80 Name of the output file.
81 -translations-variables <variable_1>[,<variable_2>,...]
82 Comma-separated list of QMake variables containing .ts files.
84 Display the version of lprodump and exit.
85)"_s);
88static void print(
const QString &fileName,
int lineNo,
const QString &msg)
91 printErr(QString::fromLatin1(
"WARNING: %1:%2: %3\n").arg(fileName, QString::number(lineNo), msg));
93 printErr(QString::fromLatin1(
"WARNING: %1: %2\n").arg(fileName, msg));
95 printErr(QString::fromLatin1(
"WARNING: %1\n").arg(msg));
100 void message(
int type,
const QString &msg,
const QString &fileName,
int lineNo)
override
103 print(fileName, lineNo, msg);
106 void fileMessage(
int type,
const QString &msg)
override
110 printErr(
"WARNING: "_L1 + msg + u'\n');
115 void doneWithEval(
ProFile *)
override {}
120static EvalHandler evalHandler;
122static QStringList getResources(
const QString &resourceFile,
QMakeVfs *vfs)
126 return QStringList();
131 printErr(
QStringLiteral(
"lprodump error: Cannot read %1: %2\n").arg(resourceFile, errStr));
132 return QStringList();
134 const ReadQrcResult rqr = readQrcFile(resourceFile, content);
137 .arg(resourceFile, QString::number(rqr.line), rqr.errorString));
142static QStringList getSources(
const char *var,
const char *vvar,
const QStringList &baseVPaths,
145 QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir);
146 vPaths += baseVPaths;
147 vPaths.removeDuplicates();
148 return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
151static QStringList getSources(
const ProFileEvaluator &visitor,
const QString &projectDir,
154 QStringList baseVPaths;
155 baseVPaths += visitor.absolutePathValues(
"VPATH"_L1, projectDir);
156 baseVPaths << projectDir;
157 baseVPaths.removeDuplicates();
159 QStringList sourceFiles;
162 sourceFiles += getSources(
"SOURCES",
"VPATH_SOURCES", baseVPaths, projectDir, visitor);
163 sourceFiles += getSources(
"HEADERS",
"VPATH_HEADERS", baseVPaths, projectDir, visitor);
165 sourceFiles += getSources(
"FORMS",
"VPATH_FORMS", baseVPaths, projectDir, visitor);
167 const QStringList resourceFiles = getSources(
"RESOURCES",
"VPATH_RESOURCES", baseVPaths, projectDir, visitor);
168 for (
const QString &resource : resourceFiles)
169 sourceFiles += getResources(resource, vfs);
171 QStringList installs = visitor.values(
"INSTALLS"_L1) + visitor.values(
"DEPLOYMENT"_L1);
172 installs.removeDuplicates();
173 QDir baseDir(projectDir);
174 for (
const QString &inst : std::as_const(installs)) {
175 for (
const QString &file : visitor.values(inst +
".files"_L1)) {
176 QFileInfo info(file);
177 if (!info.isAbsolute())
178 info.setFile(baseDir.absoluteFilePath(file));
179 QStringList nameFilter;
182 nameFilter <<
"*"_L1;
183 searchPath = info.filePath();
185 nameFilter << info.fileName();
186 searchPath = info.path();
189 QDirIterator iterator(searchPath, nameFilter,
190 QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks,
191 QDirIterator::Subdirectories);
192 while (iterator.hasNext()) {
194 QFileInfo cfi = iterator.fileInfo();
195 if (isSupportedExtension(cfi.suffix()))
196 sourceFiles << cfi.filePath();
201 sourceFiles.removeDuplicates();
206QStringList getExcludes(
const ProFileEvaluator &visitor,
const QString &projectDirPath)
208 const QStringList trExcludes = visitor.values(
"TR_EXCLUDE"_L1);
209 QStringList excludes;
210 excludes.reserve(trExcludes.size());
211 const QDir projectDir(projectDirPath);
212 for (
const QString &ex : trExcludes)
213 excludes << QDir::cleanPath(projectDir.absoluteFilePath(ex));
217static void excludeProjects(
const ProFileEvaluator &visitor, QStringList *subProjects)
219 for (
const QString &ex : visitor.values(
"TR_EXCLUDE"_L1)) {
220 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
221 for (
auto it = subProjects->begin(); it != subProjects->end(); ) {
222 if (rx.match(*it).hasMatch())
223 it = subProjects->erase(it);
230static QJsonArray processProjects(
bool topLevel,
const QStringList &proFiles,
231 const QStringList &translationsVariables,
232 const QHash<QString, QString> &outDirMap,
236static QJsonObject processProject(
const QString &proFile,
const QStringList &translationsVariables,
241 QStringList tmp = visitor.values(
"CODECFORSRC"_L1);
244 QString proPath = QFileInfo(proFile).path();
246 QStringList subProjects = visitor.values(
"SUBDIRS"_L1);
247 excludeProjects(visitor, &subProjects);
248 QStringList subProFiles;
249 QDir proDir(proPath);
250 for (
const QString &subdir : std::as_const(subProjects)) {
251 QString realdir = visitor.value(subdir +
".subdir"_L1);
252 if (realdir.isEmpty())
253 realdir = visitor.value(subdir +
".file"_L1);
254 if (realdir.isEmpty())
256 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(realdir));
257 QFileInfo subInfo(subPro);
258 if (subInfo.isDir()) {
259 subProFiles << (subPro + u'/' + subInfo.fileName() +
".pro"_L1);
261 subProFiles << subPro;
264 QJsonArray subResults = processProjects(
false, subProFiles, translationsVariables,
265 QHash<QString, QString>(), option, vfs, parser,
267 if (!subResults.isEmpty())
268 setValue(result,
"subProjects", subResults);
270 const QStringList sourceFiles = getSources(visitor, proPath, vfs);
271 setValue(result,
"includePaths", visitor.absolutePathValues(
"INCLUDEPATH"_L1, proPath));
272 setValue(result,
"excluded", getExcludes(visitor, proPath));
273 setValue(result,
"sources", sourceFiles);
278static QJsonArray processProjects(
bool topLevel,
const QStringList &proFiles,
279 const QStringList &translationsVariables,
280 const QHash<QString, QString> &outDirMap,
284 for (
const QString &proFile : proFiles) {
285 if (!outDirMap.isEmpty())
286 option->setDirectories(QFileInfo(proFile).path(), outDirMap[proFile]);
289 if (!(pro = parser->parsedProFile(proFile, topLevel ? QMakeParser::ParseReportMissing
290 : QMakeParser::ParseDefault))) {
295 ProFileEvaluator visitor(option, parser, vfs, &evalHandler);
296 visitor.setCumulative(
true);
297 visitor.setOutputDir(option->shadowedPath(pro->directoryName()));
298 if (!visitor.accept(pro)) {
305 QJsonObject prj = processProject(proFile, translationsVariables, option, vfs, parser,
307 setValue(prj,
"projectFile", proFile);
309 for (
const QString &varName : translationsVariables) {
310 if (!visitor.contains(varName))
312 QDir proDir(QFileInfo(proFile).path());
313 const QStringList translations = visitor.values(varName);
314 for (
const QString &tsFile : translations)
315 tsFiles << proDir.filePath(tsFile);
317 if (!tsFiles.isEmpty())
318 setValue(prj,
"translations", tsFiles);
319 if (visitor.contains(
"LUPDATE_COMPILE_COMMANDS_PATH"_L1)) {
320 const QStringList thepathjson = visitor.values(
"LUPDATE_COMPILE_COMMANDS_PATH"_L1);
321 setValue(prj,
"compileCommands", thepathjson.value(0));
329int main(
int argc,
char **argv)
331 QCoreApplication app(argc, argv);
332 QStringList args = app.arguments();
333 QStringList proFiles;
334 QStringList translationsVariables = { u"TRANSLATIONS"_s };
335 QString outDir = QDir::currentPath();
336 QHash<QString, QString> outDirMap;
337 QString outputFilePath;
340 for (
int i = 1; i < args.size(); ++i) {
341 QString arg = args.at(i);
342 if (arg ==
"-help"_L1 || arg ==
"--help"_L1 || arg ==
"-h"_L1) {
345 }
else if (arg ==
"-out"_L1) {
348 printErr(u"The option -out requires a parameter.\n"_s);
351 outputFilePath = args[i];
352 }
else if (arg ==
"-silent"_L1) {
353 evalHandler.verbose =
false;
354 }
else if (arg ==
"-pro-debug"_L1) {
356 }
else if (arg ==
"-version"_L1) {
357 printOut(
QStringLiteral(
"lprodump version %1\n").arg(QLatin1String(QT_VERSION_STR)));
359 }
else if (arg ==
"-pro"_L1) {
362 printErr(
QStringLiteral(
"The -pro option should be followed by a filename of .pro file.\n"));
365 QString file = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
367 outDirMap[file] = outDir;
368 }
else if (arg ==
"-pro-out"_L1) {
371 printErr(
QStringLiteral(
"The -pro-out option should be followed by a directory name.\n"));
374 outDir = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
375 }
else if (arg == u"-translations-variables"_s) {
378 printErr(u"The -translations-variables option must be followed by a "_s
379 u"comma-separated list of variable names.\n"_s);
382 translationsVariables = args.at(i).split(u',');
383 }
else if (arg.startsWith(
"-"_L1) && arg !=
"-"_L1) {
389 printErr(
QStringLiteral(
"lprodump error: File '%1' does not exist.\n").arg(arg));
392 if (!isProOrPriFile(arg)) {
393 printErr(
QStringLiteral(
"lprodump error: '%1' is neither a .pro nor a .pri file.\n")
397 QString cleanFile = QDir::cleanPath(fi.absoluteFilePath());
398 proFiles << cleanFile;
399 outDirMap[cleanFile] = outDir;
403 if (proFiles.isEmpty()) {
410 option.qmake_abslocation = QString::fromLocal8Bit(qgetenv(
"QMAKE"));
411 if (option.qmake_abslocation.isEmpty()) {
412 option.qmake_abslocation = QLibraryInfo::path(QLibraryInfo::BinariesPath) +
"/qmake"_L1;
414 option.debugLevel = proDebug;
415 option.initProperties();
416 option.setCommandLineArguments(QDir::currentPath(), {
"CONFIG+=lupdate_run"_L1 });
420 QJsonArray results = processProjects(
true, proFiles, translationsVariables, outDirMap, &option,
421 &vfs, &parser, &fail);
425 const QByteArray output = QJsonDocument(results).toJson(QJsonDocument::Compact);
426 if (outputFilePath.isEmpty()) {
427 puts(output.constData());
429 QFile f(outputFilePath);
430 if (!f.open(QIODevice::WriteOnly)) {
431 printErr(
QStringLiteral(
"lprodump error: Cannot open %1 for writing.\n").arg(outputFilePath));
ProFileEvaluator::TemplateType templateType() const
ReadResult readFile(int id, QString *contents, QString *errStr)
const QString & asString(const QString &s)
#define qPrintable(string)
#define QStringLiteral(str)
static void printOut(const QString &out)
void setValue(QJsonObject &obj, const char *key, T value)
static QJsonValue toJsonValue(const QString &s)
static QJsonValue toJsonValue(const QJsonValue &v)
static void printErr(const QString &out)
int main(int argc, char *argv[])
[ctor_close]