summaryrefslogtreecommitdiffstats
path: root/src/linguist/lupdate/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/linguist/lupdate/main.cpp')
-rw-r--r--src/linguist/lupdate/main.cpp314
1 files changed, 195 insertions, 119 deletions
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index 055a1b7ed..820f750de 100644
--- a/src/linguist/lupdate/main.cpp
+++ b/src/linguist/lupdate/main.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Linguist of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "lupdate.h"
#if QT_CONFIG(clangcpp)
@@ -43,15 +18,19 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QLibraryInfo>
+#include <QtCore/QRegularExpression>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTranslator>
#include <iostream>
+using namespace Qt::StringLiterals;
+
bool useClangToParseCpp = false;
-QString commandLineCompileCommands; // for the path to the json file passed as a command line argument.
+QString commandLineCompilationDatabaseDir; // for the path to the json file passed as a command line argument.
// Has priority over what is in the .pro file and passed to the project.
+QStringList rootDirs;
// Can't have an array of QStaticStringData<N> for different N, so
// use QString, which requires constructor calls. Doesn't matter
@@ -108,7 +87,7 @@ void TrFunctionAliasManager::ensureTrFunctionHashUpdated() const
if (!m_nameToTrFunctionMap.empty())
return;
- QHash<QString, TrFunction> nameToTrFunctionMap;
+ NameToTrFunctionMap nameToTrFunctionMap;
for (int i = 0; i < NumTrFunctions; ++i)
for (const QString &alias : m_trFunctionAliases[i])
nameToTrFunctionMap[alias] = TrFunction(i);
@@ -116,6 +95,12 @@ void TrFunctionAliasManager::ensureTrFunctionHashUpdated() const
m_nameToTrFunctionMap.swap(nameToTrFunctionMap);
}
+const TrFunctionAliasManager::NameToTrFunctionMap &TrFunctionAliasManager::nameToTrFunctionMap() const
+{
+ ensureTrFunctionHashUpdated();
+ return m_nameToTrFunctionMap;
+}
+
static QStringList availableFunctions()
{
QStringList result;
@@ -137,6 +122,19 @@ QStringList TrFunctionAliasManager::availableFunctionsWithAliases() const
return result;
}
+QStringList TrFunctionAliasManager::listAliases() const
+{
+ QStringList result;
+ result.reserve(NumTrFunctions);
+ for (int i = 0; i < NumTrFunctions; ++i) {
+ for (int ii = 1; ii < m_trFunctionAliases[i].size() ; ii++) {
+ // ii = 0 is the default name. Not listed here
+ result.push_back(m_trFunctionAliases[i][ii]);
+ }
+ }
+ return result;
+}
+
TrFunctionAliasManager trFunctionAliasManager;
QString ParserTool::transcode(const QString &str)
@@ -147,11 +145,11 @@ QString ParserTool::transcode(const QString &str)
const QByteArray in = str.toUtf8();
QByteArray out;
- out.reserve(in.length());
- for (int i = 0; i < in.length();) {
+ out.reserve(in.size());
+ for (int i = 0; i < in.size();) {
uchar c = in[i++];
if (c == '\\') {
- if (i >= in.length())
+ if (i >= in.size())
break;
c = in[i++];
@@ -161,7 +159,7 @@ QString ParserTool::transcode(const QString &str)
if (c == 'x' || c == 'u' || c == 'U') {
const bool unicode = (c != 'x');
QByteArray hex;
- while (i < in.length() && isxdigit((c = in[i]))) {
+ while (i < in.size() && isxdigit((c = in[i]))) {
hex += c;
i++;
}
@@ -173,7 +171,7 @@ QString ParserTool::transcode(const QString &str)
QByteArray oct;
int n = 0;
oct += c;
- while (n < 2 && i < in.length() && (c = in[i]) >= '0' && c < '8') {
+ while (n < 2 && i < in.size() && (c = in[i]) >= '0' && c < '8') {
i++;
n++;
oct += c;
@@ -187,7 +185,7 @@ QString ParserTool::transcode(const QString &str)
out += c;
}
}
- return QString::fromUtf8(out.constData(), out.length());
+ return QString::fromUtf8(out.constData(), out.size());
}
static QString m_defaultExtensions;
@@ -215,7 +213,7 @@ static void recursiveFileInfoList(const QDir &dir,
static void printUsage()
{
- printOut(LU::tr(
+ printOut(QStringLiteral(
"Usage:\n"
" lupdate [options] [project-file]...\n"
" lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
@@ -248,11 +246,16 @@ static void printUsage()
" May be specified multiple times.\n"
" -locations {absolute|relative|none}\n"
" Specify/override how source code references are saved in TS files.\n"
+ " absolute: Source file path is relative to target file. Absolute line\n"
+ " number is stored.\n"
+ " relative: Source file path is relative to target file. Line number is\n"
+ " relative to other entries in the same source file.\n"
+ " none: no information about source location is stored.\n"
" Guessed from existing TS files if not specified.\n"
" Default is absolute for new files.\n"
" -no-ui-lines\n"
" Do not record line numbers in references to UI files.\n"
- " -disable-heuristic {sametext|similartext|number}\n"
+ " -disable-heuristic {sametext|similartext}\n"
" Disable the named merge heuristic. Can be specified multiple times.\n"
" -project <filename>\n"
" Name of a file containing the project's description in JSON format.\n"
@@ -280,13 +283,20 @@ static void printUsage()
" Specify the output file(s). This will override the TRANSLATIONS.\n"
" -version\n"
" Display the version of lupdate and exit.\n"
- " -clang-parser \n"
+ " -clang-parser [compilation-database-dir]\n"
" Use clang to parse cpp files. Otherwise a custom parser is used.\n"
- " Need a compile_commands.json for the files that needs to be parsed.\n"
- " The path to this file can be given in the .pro file\n"
- " under LUPDATE_COMPILE_COMMANDS_PATH.\n"
- " If no path is given search for compile_commands.json will be attempted\n"
- " through all parent paths of the first input file.\n"
+ " This option needs a clang compilation database (compile_commands.json)\n"
+ " for the files that needs to be parsed.\n"
+ " The path to the directory containing this file can be specified on the \n"
+ " command line, directly after the -clang-parser option, or in the .pro file\n"
+ " by setting the variable LUPDATE_COMPILE_COMMANDS_PATH.\n"
+ " A directory specified on the command line takes precedence.\n"
+ " If no path is given, the compilation database will be searched\n"
+ " in all parent paths of the first input file.\n"
+ " -project-roots <directory>...\n"
+ " Specify one or more project root directories.\n"
+ " Only files below a project root are considered for translation when using\n"
+ " the -clang-parser option.\n"
" @lst-file\n"
" Read additional file names (one per line) or includepaths (one per\n"
" line, and prefixed with -I) from lst-file.\n"
@@ -300,7 +310,7 @@ static bool handleTrFunctionAliases(const QString &arg)
for (const QString &pair : arg.split(QLatin1Char(','), Qt::SkipEmptyParts)) {
const int equalSign = pair.indexOf(QLatin1Char('='));
if (equalSign < 0) {
- printErr(LU::tr("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n").arg(pair));
+ printErr(QStringLiteral("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n").arg(pair));
return false;
}
const bool plusEqual = equalSign > 0 && pair[equalSign-1] == QLatin1Char('+');
@@ -309,13 +319,13 @@ static bool handleTrFunctionAliases(const QString &arg)
const QString alias = pair.mid(equalSign+1).trimmed();
const int trFunction = trFunctionByDefaultName(trFunctionName);
if (trFunction < 0) {
- printErr(LU::tr("Unknown tr-function '%1' in -tr-function-alias option.\n"
+ printErr(QStringLiteral("Unknown tr-function '%1' in -tr-function-alias option.\n"
"Available tr-functions are: %2")
.arg(trFunctionName, availableFunctions().join(QLatin1Char(','))));
return false;
}
if (alias.isEmpty()) {
- printErr(LU::tr("Empty alias for tr-function '%1' in -tr-function-alias option.\n")
+ printErr(QStringLiteral("Empty alias for tr-function '%1' in -tr-function-alias option.\n")
.arg(trFunctionName));
return false;
}
@@ -333,7 +343,7 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
for (int i = 0; i < fetchedTor.messageCount(); i++) {
const TranslatorMessage &msg = fetchedTor.constMessage(i);
if (!msg.id().isEmpty() && msg.sourceText().isEmpty())
- printErr(LU::tr("lupdate warning: Message with id '%1' has no source.\n")
+ printErr(QStringLiteral("lupdate warning: Message with id '%1' has no source.\n")
.arg(msg.id()));
}
@@ -366,22 +376,22 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
tor.resolveDuplicates();
cd.clearErrors();
if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode())
- printErr(LU::tr("lupdate warning: Specified target language '%1' disagrees with"
+ printErr(QStringLiteral("lupdate warning: Specified target language '%1' disagrees with"
" existing file's language '%2'. Ignoring.\n")
.arg(targetLanguage, tor.languageCode()));
if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode())
- printErr(LU::tr("lupdate warning: Specified source language '%1' disagrees with"
+ printErr(QStringLiteral("lupdate warning: Specified source language '%1' disagrees with"
" existing file's language '%2'. Ignoring.\n")
.arg(sourceLanguage, tor.sourceLanguageCode()));
// If there is translation in the file, the language should be recognized
// (when the language is not recognized, plural translations are lost)
if (tor.translationsExist()) {
QLocale::Language l;
- QLocale::Country c;
- tor.languageAndCountry(tor.languageCode(), &l, &c);
+ QLocale::Territory c;
+ tor.languageAndTerritory(tor.languageCode(), &l, &c);
QStringList forms;
if (!getNumerusInfo(l, c, 0, &forms, 0)) {
- printErr(LU::tr("File %1 won't be updated: it contains translation but the"
+ printErr(QStringLiteral("File %1 won't be updated: it contains translation but the"
" target language is not recognized\n").arg(fileName));
continue;
}
@@ -402,7 +412,7 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
else if (options & AbsoluteLocations)
tor.setLocationsType(Translator::AbsoluteLocations);
if (options & Verbose)
- printOut(LU::tr("Updating '%1'...\n").arg(fn));
+ printOut(QStringLiteral("Updating '%1'...\n").arg(fn));
UpdateOptions theseOptions = options;
if (tor.locationsType() == Translator::NoLocations) // Could be set from file
@@ -415,7 +425,7 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
}
if (options & PluralOnly) {
if (options & Verbose)
- printOut(LU::tr("Stripping non plural forms in '%1'...\n").arg(fn));
+ printOut(QStringLiteral("Stripping non plural forms in '%1'...\n").arg(fn));
out.stripNonPluralForms();
}
if (options & NoObsolete)
@@ -454,6 +464,22 @@ static bool readFileContent(const QString &filePath, QString *content, QString *
return true;
}
+static void removeExcludedSources(Projects &projects)
+{
+ for (Project &project : projects) {
+ for (const QString &ex : project.excluded) {
+ QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
+ for (auto it = project.sources.begin(); it != project.sources.end(); ) {
+ if (rx.match(*it).hasMatch())
+ it = project.sources.erase(it);
+ else
+ ++it;
+ }
+ }
+ removeExcludedSources(project.subProjects);
+ }
+}
+
static QStringList getResources(const QString &resourceFile)
{
if (!QFile::exists(resourceFile))
@@ -461,20 +487,45 @@ static QStringList getResources(const QString &resourceFile)
QString content;
QString errStr;
if (!readFileContent(resourceFile, &content, &errStr)) {
- printErr(LU::tr("lupdate error: Can not read %1: %2\n").arg(resourceFile, errStr));
+ printErr(QStringLiteral("lupdate error: Can not read %1: %2\n").arg(resourceFile, errStr));
return QStringList();
}
ReadQrcResult rqr = readQrcFile(resourceFile, content);
if (rqr.hasError()) {
- printErr(LU::tr("lupdate error: %1:%2: %3\n")
+ printErr(QStringLiteral("lupdate error: %1:%2: %3\n")
.arg(resourceFile, QString::number(rqr.line), rqr.errorString));
}
return rqr.files;
}
+// Remove .qrc files from the project and return them as absolute paths.
+static QStringList extractQrcFiles(Project &project)
+{
+ auto it = project.sources.begin();
+ QStringList qrcFiles;
+ while (it != project.sources.end()) {
+ QFileInfo fi(*it);
+ QString fn = QDir::cleanPath(fi.absoluteFilePath());
+ if (fn.endsWith(QLatin1String(".qrc"), Qt::CaseInsensitive)) {
+ qrcFiles += fn;
+ it = project.sources.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return qrcFiles;
+}
+
+// Replace all .qrc files in the project with their content.
+static void expandQrcFiles(Project &project)
+{
+ for (const QString &qrcFile : extractQrcFiles(project))
+ project.sources << getResources(qrcFile);
+}
+
static bool processTs(Translator &fetchedTor, const QString &file, ConversionData &cd)
{
- for (const Translator::FileFormat &fmt : qAsConst(Translator::registeredFileFormats())) {
+ for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
Translator tor;
if (tor.load(file, cd, fmt.extension)) {
@@ -492,7 +543,7 @@ static bool processTs(Translator &fetchedTor, const QString &file, ConversionDat
}
static void processSources(Translator &fetchedTor,
- const QStringList &sourceFiles, ConversionData &cd)
+ const QStringList &sourceFiles, ConversionData &cd, bool *fail)
{
#ifdef QT_NO_QML
bool requireQmlSupport = false;
@@ -516,18 +567,23 @@ static void processSources(Translator &fetchedTor,
|| sourceFile.endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
requireQmlSupport = true;
#endif // QT_NO_QML
+ else if (sourceFile.endsWith(u".py", Qt::CaseInsensitive))
+ loadPython(fetchedTor, sourceFile, cd);
else if (!processTs(fetchedTor, sourceFile, cd))
sourceFilesCpp << sourceFile;
}
#ifdef QT_NO_QML
if (requireQmlSupport)
- printErr(LU::tr("lupdate warning: Some files have been ignored due to missing qml/javascript support\n"));
+ printErr(QStringLiteral("lupdate warning: Some files have been ignored due to missing qml/javascript support\n"));
#endif
if (useClangToParseCpp) {
#if QT_CONFIG(clangcpp)
- ClangCppParser::loadCPP(fetchedTor, sourceFilesCpp, cd);
+ ClangCppParser::loadCPP(fetchedTor, sourceFilesCpp, cd, fail);
+#else
+ *fail = true;
+ printErr(QStringLiteral("lupdate error: lupdate was built without clang support."));
#endif
}
else
@@ -546,7 +602,7 @@ static QSet<QString> projectRoots(const QString &projectFile, const QStringList
sourceDirs.insert(sf.left(sf.lastIndexOf(QLatin1Char('/')) + 1));
QStringList rootList = sourceDirs.values();
rootList.sort();
- for (int prev = 0, curr = 1; curr < rootList.length(); )
+ for (int prev = 0, curr = 1; curr < rootList.size(); )
if (rootList.at(curr).startsWith(rootList.at(prev)))
rootList.removeAt(curr);
else
@@ -584,7 +640,7 @@ private:
|| codecForSource == QLatin1String("utf8")) {
options &= ~SourceIsUtf16;
} else {
- printErr(LU::tr("lupdate warning: Codec for source '%1' is invalid."
+ printErr(QStringLiteral("lupdate warning: Codec for source '%1' is invalid."
" Falling back to UTF-8.\n").arg(codecForSource));
options &= ~SourceIsUtf16;
}
@@ -595,24 +651,28 @@ private:
ConversionData cd;
cd.m_noUiLines = options & NoUiLines;
cd.m_projectRoots = projectRoots(projectFile, sources);
+ QStringList projectRootDirs;
+ for (auto dir : cd.m_projectRoots)
+ projectRootDirs.append(dir);
+ cd.m_rootDirs = projectRootDirs;
cd.m_includePath = prj.includePaths;
cd.m_excludes = prj.excluded;
cd.m_sourceIsUtf16 = options & SourceIsUtf16;
- if (commandLineCompileCommands.isEmpty())
- cd.m_compileCommandsPath = prj.compileCommands;
+ if (commandLineCompilationDatabaseDir.isEmpty())
+ cd.m_compilationDatabaseDir = prj.compileCommands;
else
- cd.m_compileCommandsPath = commandLineCompileCommands;
+ cd.m_compilationDatabaseDir = commandLineCompilationDatabaseDir;
QStringList tsFiles;
- if (hasTranslations(prj)) {
+ if (prj.translations) {
tsFiles = *prj.translations;
if (parentTor) {
if (topLevel) {
- printErr(LU::tr("lupdate warning: TS files from command line "
+ printErr(QStringLiteral("lupdate warning: TS files from command line "
"will override TRANSLATIONS in %1.\n").arg(projectFile));
goto noTrans;
} else if (nestComplain) {
- printErr(LU::tr("lupdate warning: TS files from command line "
+ printErr(QStringLiteral("lupdate warning: TS files from command line "
"prevent recursing into %1.\n").arg(projectFile));
return;
}
@@ -625,7 +685,7 @@ private:
}
Translator tor;
processProjects(false, options, prj.subProjects, false, &tor, fail);
- processSources(tor, sources, cd);
+ processSources(tor, sources, cd, fail);
updateTsFiles(tor, tsFiles, QStringList(), m_sourceLanguage, m_targetLanguage,
options, fail);
return;
@@ -634,15 +694,15 @@ private:
noTrans:
if (!parentTor) {
if (topLevel) {
- printErr(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
+ printErr(QStringLiteral("lupdate warning: no TS files specified. Only diagnostics "
"will be produced for '%1'.\n").arg(projectFile));
}
Translator tor;
processProjects(false, options, prj.subProjects, nestComplain, &tor, fail);
- processSources(tor, sources, cd);
+ processSources(tor, sources, cd, fail);
} else {
processProjects(false, options, prj.subProjects, nestComplain, parentTor, fail);
- processSources(*parentTor, sources, cd);
+ processSources(*parentTor, sources, cd, fail);
}
}
@@ -685,8 +745,7 @@ int main(int argc, char **argv)
UpdateOptions options =
Verbose | // verbose is on by default starting with Qt 4.2
- HeuristicSameText | HeuristicSimilarText | HeuristicNumber;
- int proDebug = 0;
+ HeuristicSameText | HeuristicSimilarText;
int numFiles = 0;
bool metTsFlag = false;
bool metXTsFlag = false;
@@ -716,16 +775,15 @@ int main(int argc, char **argv)
options &= ~Verbose;
continue;
} else if (arg == QLatin1String("-pro-debug")) {
- proDebug++;
continue;
} else if (arg == QLatin1String("-project")) {
++i;
if (i == argc) {
- printErr(LU::tr("The option -project requires a parameter.\n"));
+ printErr(u"The option -project requires a parameter.\n"_s);
return 1;
}
if (!projectDescriptionFile.isEmpty()) {
- printErr(LU::tr("The option -project must appear only once.\n"));
+ printErr(u"The option -project must appear only once.\n"_s);
return 1;
}
projectDescriptionFile = args[i];
@@ -734,7 +792,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-target-language")) {
++i;
if (i == argc) {
- printErr(LU::tr("The option -target-language requires a parameter.\n"));
+ printErr(u"The option -target-language requires a parameter.\n"_s);
return 1;
}
targetLanguage = args[i];
@@ -742,7 +800,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-source-language")) {
++i;
if (i == argc) {
- printErr(LU::tr("The option -source-language requires a parameter.\n"));
+ printErr(u"The option -source-language requires a parameter.\n"_s);
return 1;
}
sourceLanguage = args[i];
@@ -750,7 +808,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-disable-heuristic")) {
++i;
if (i == argc) {
- printErr(LU::tr("The option -disable-heuristic requires a parameter.\n"));
+ printErr(u"The option -disable-heuristic requires a parameter.\n"_s);
return 1;
}
arg = args[i];
@@ -758,17 +816,15 @@ int main(int argc, char **argv)
options &= ~HeuristicSameText;
} else if (arg == QLatin1String("similartext")) {
options &= ~HeuristicSimilarText;
- } else if (arg == QLatin1String("number")) {
- options &= ~HeuristicNumber;
} else {
- printErr(LU::tr("Invalid heuristic name passed to -disable-heuristic.\n"));
+ printErr(u"Invalid heuristic name passed to -disable-heuristic.\n"_s);
return 1;
}
continue;
} else if (arg == QLatin1String("-locations")) {
++i;
if (i == argc) {
- printErr(LU::tr("The option -locations requires a parameter.\n"));
+ printErr(u"The option -locations requires a parameter.\n"_s);
return 1;
}
if (args[i] == QLatin1String("none")) {
@@ -778,7 +834,7 @@ int main(int argc, char **argv)
} else if (args[i] == QLatin1String("absolute")) {
options |= AbsoluteLocations;
} else {
- printErr(LU::tr("Invalid parameter passed to -locations.\n"));
+ printErr(u"Invalid parameter passed to -locations.\n"_s);
return 1;
}
continue;
@@ -799,7 +855,7 @@ int main(int argc, char **argv)
options |= NoSort;
continue;
} else if (arg == QLatin1String("-version")) {
- printOut(LU::tr("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
+ printOut(QStringLiteral("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
return 0;
} else if (arg == QLatin1String("-ts")) {
metTsFlag = true;
@@ -812,7 +868,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-extensions")) {
++i;
if (i == argc) {
- printErr(LU::tr("The -extensions option should be followed by an extension list.\n"));
+ printErr(u"The -extensions option should be followed by an extension list.\n"_s);
return 1;
}
extensions = args[i];
@@ -820,7 +876,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-tr-function-alias")) {
++i;
if (i == argc) {
- printErr(LU::tr("The -tr-function-alias option should be followed by a list of function=alias mappings.\n"));
+ printErr(u"The -tr-function-alias option should be followed by a list of function=alias mappings.\n"_s);
return 1;
}
if (!handleTrFunctionAliases(args[i]))
@@ -829,7 +885,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-pro")) {
++i;
if (i == argc) {
- printErr(LU::tr("The -pro option should be followed by a filename of .pro file.\n"));
+ printErr(u"The -pro option should be followed by a filename of .pro file.\n"_s);
return 1;
}
QString file = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
@@ -839,16 +895,16 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-pro-out")) {
++i;
if (i == argc) {
- printErr(LU::tr("The -pro-out option should be followed by a directory name.\n"));
+ printErr(u"The -pro-out option should be followed by a directory name.\n"_s);
return 1;
}
outDir = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
continue;
} else if (arg.startsWith(QLatin1String("-I"))) {
- if (arg.length() == 2) {
+ if (arg.size() == 2) {
++i;
if (i == argc) {
- printErr(LU::tr("The -I option should be followed by a path.\n"));
+ printErr(u"The -I option should be followed by a path.\n"_s);
return 1;
}
includePath += args[i];
@@ -863,13 +919,21 @@ int main(int argc, char **argv)
// the option after -clang-parser is optional
if ((i + 1) != argc && !args[i + 1].startsWith(QLatin1String("-"))) {
i++;
- commandLineCompileCommands = args[i];
+ commandLineCompilationDatabaseDir = args[i];
}
continue;
}
+ else if (arg == QLatin1String("-project-roots")) {
+ while ((i + 1) != argc && !args[i + 1].startsWith(QLatin1String("-"))) {
+ i++;
+ rootDirs << args[i];
+ }
+ rootDirs.removeDuplicates();
+ continue;
+ }
#endif
else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
- printErr(LU::tr("Unrecognized option '%1'.\n").arg(arg));
+ printErr(QStringLiteral("Unrecognized option '%1'.\n").arg(arg));
return 1;
}
@@ -877,7 +941,7 @@ int main(int argc, char **argv)
if (arg.startsWith(QLatin1String("@"))) {
QFile lstFile(arg.mid(1));
if (!lstFile.open(QIODevice::ReadOnly)) {
- printErr(LU::tr("lupdate error: List file '%1' is not readable.\n")
+ printErr(QStringLiteral("lupdate error: List file '%1' is not readable.\n")
.arg(lstFile.fileName()));
return 1;
}
@@ -885,8 +949,8 @@ int main(int argc, char **argv)
QString lineContent = QString::fromLocal8Bit(lstFile.readLine().trimmed());
if (lineContent.startsWith(QLatin1String("-I"))) {
- if (lineContent.length() == 2) {
- printErr(LU::tr("The -I option should be followed by a path.\n"));
+ if (lineContent.size() == 2) {
+ printErr(u"The -I option should be followed by a path.\n"_s);
return 1;
}
includePath += lineContent.mid(2);
@@ -898,15 +962,15 @@ int main(int argc, char **argv)
files << arg;
}
if (metTsFlag) {
- for (const QString &file : qAsConst(files)) {
+ for (const QString &file : std::as_const(files)) {
bool found = false;
- for (const Translator::FileFormat &fmt : qAsConst(Translator::registeredFileFormats())) {
+ for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
QFileInfo fi(file);
if (!fi.exists() || fi.isWritable()) {
tsFileNames.append(QFileInfo(file).absoluteFilePath());
} else {
- printErr(LU::tr("lupdate warning: For some reason, '%1' is not writable.\n")
+ printErr(QStringLiteral("lupdate warning: For some reason, '%1' is not writable.\n")
.arg(file));
}
found = true;
@@ -914,7 +978,7 @@ int main(int argc, char **argv)
}
}
if (!found) {
- printErr(LU::tr("lupdate error: File '%1' has no recognized extension.\n")
+ printErr(QStringLiteral("lupdate error: File '%1' has no recognized extension.\n")
.arg(file));
return 1;
}
@@ -923,10 +987,10 @@ int main(int argc, char **argv)
} else if (metXTsFlag) {
alienFiles += files;
} else {
- for (const QString &file : qAsConst(files)) {
+ for (const QString &file : std::as_const(files)) {
QFileInfo fi(file);
if (!fi.exists()) {
- printErr(LU::tr("lupdate error: File '%1' does not exist.\n").arg(file));
+ printErr(QStringLiteral("lupdate error: File '%1' does not exist.\n").arg(file));
return 1;
}
if (isProOrPriFile(file)) {
@@ -934,7 +998,7 @@ int main(int argc, char **argv)
proFiles << cleanFile;
} else if (fi.isDir()) {
if (options & Verbose)
- printOut(LU::tr("Scanning directory '%1'...\n").arg(file));
+ printOut(QStringLiteral("Scanning directory '%1'...\n").arg(file));
QDir dir = QDir(fi.filePath());
projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
if (extensionsNameFilters.isEmpty()) {
@@ -950,8 +1014,8 @@ int main(int argc, char **argv)
filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
QFileInfoList fileinfolist;
recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
- int scanRootLen = dir.absolutePath().length();
- for (const QFileInfo &fi : qAsConst(fileinfolist)) {
+ int scanRootLen = dir.absolutePath().size();
+ for (const QFileInfo &fi : std::as_const(fileinfolist)) {
QString fn = QDir::cleanPath(fi.absoluteFilePath());
if (fn.endsWith(QLatin1String(".qrc"), Qt::CaseInsensitive)) {
resourceFiles << fn;
@@ -992,13 +1056,21 @@ int main(int argc, char **argv)
return 1;
}
- if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
- printErr(LU::tr("lupdate warning: -target-language usually only"
- " makes sense with exactly one TS file.\n"));
+ if (!targetLanguage.isEmpty() && tsFileNames.size() != 1)
+ printErr(u"lupdate warning: -target-language usually only"
+ " makes sense with exactly one TS file.\n"_s);
+
+ if (proFiles.isEmpty() && resourceFiles.isEmpty() && sourceFiles.size() == 1
+ && QFileInfo(sourceFiles.first()).fileName() == u"CMakeLists.txt"_s) {
+ printErr(u"lupdate error: Passing a CMakeLists.txt as project file is not supported.\n"_s
+ u"Please use the 'qt_add_lupdate' CMake command and build the "_s
+ u"'update_translations' target.\n"_s);
+ return 1;
+ }
QString errorString;
if (!proFiles.isEmpty()) {
- runQtTool(QStringLiteral("lupdate-pro"), app.arguments().mid(1));
+ runInternalQtTool(u"lupdate-pro"_s, app.arguments().mid(1));
return 0;
}
@@ -1006,22 +1078,25 @@ int main(int argc, char **argv)
if (!projectDescriptionFile.isEmpty()) {
projectDescription = readProjectDescription(projectDescriptionFile, &errorString);
if (!errorString.isEmpty()) {
- printErr(LU::tr("lupdate error: %1\n").arg(errorString));
+ printErr(QStringLiteral("lupdate error: %1\n").arg(errorString));
return 1;
}
if (projectDescription.empty()) {
- printErr(LU::tr("lupdate error:"
+ printErr(QStringLiteral("lupdate error:"
" Could not find project descriptions in %1.\n")
.arg(projectDescriptionFile));
return 1;
}
+ removeExcludedSources(projectDescription);
+ for (Project &project : projectDescription)
+ expandQrcFiles(project);
}
bool fail = false;
if (projectDescription.empty()) {
if (tsFileNames.isEmpty())
- printErr(LU::tr("lupdate warning:"
- " no TS files specified. Only diagnostics will be produced.\n"));
+ printErr(u"lupdate warning:"
+ " no TS files specified. Only diagnostics will be produced.\n"_s);
Translator fetchedTor;
ConversionData cd;
@@ -1030,15 +1105,16 @@ int main(int argc, char **argv)
cd.m_projectRoots = projectRoots;
cd.m_includePath = includePath;
cd.m_allCSources = allCSources;
- cd.m_compileCommandsPath = commandLineCompileCommands;
- for (const QString &resource : qAsConst(resourceFiles))
+ cd.m_compilationDatabaseDir = commandLineCompilationDatabaseDir;
+ cd.m_rootDirs = rootDirs;
+ for (const QString &resource : std::as_const(resourceFiles))
sourceFiles << getResources(resource);
- processSources(fetchedTor, sourceFiles, cd);
+ processSources(fetchedTor, sourceFiles, cd, &fail);
updateTsFiles(fetchedTor, tsFileNames, alienFiles,
sourceLanguage, targetLanguage, options, &fail);
} else {
if (!sourceFiles.isEmpty() || !resourceFiles.isEmpty() || !includePath.isEmpty()) {
- printErr(LU::tr("lupdate error:"
+ printErr(QStringLiteral("lupdate error:"
" Both project and source files / include paths specified.\n"));
return 1;
}