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.cpp189
1 files changed, 119 insertions, 70 deletions
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index 2d594ba4d..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 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
@@ -166,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++];
@@ -180,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++;
}
@@ -192,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;
@@ -206,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;
@@ -267,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"
@@ -309,6 +293,10 @@ static void printUsage()
" 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"
@@ -399,8 +387,8 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
// (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(QStringLiteral("File %1 won't be updated: it contains translation but the"
@@ -476,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))
@@ -494,9 +498,34 @@ static QStringList getResources(const QString &resourceFile)
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)) {
@@ -573,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
@@ -622,6 +651,10 @@ 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;
@@ -712,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;
@@ -743,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(u"The option -project requires a parameter.\n"_qs);
+ printErr(u"The option -project requires a parameter.\n"_s);
return 1;
}
if (!projectDescriptionFile.isEmpty()) {
- printErr(u"The option -project must appear only once.\n"_qs);
+ printErr(u"The option -project must appear only once.\n"_s);
return 1;
}
projectDescriptionFile = args[i];
@@ -761,7 +792,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-target-language")) {
++i;
if (i == argc) {
- printErr(u"The option -target-language requires a parameter.\n"_qs);
+ printErr(u"The option -target-language requires a parameter.\n"_s);
return 1;
}
targetLanguage = args[i];
@@ -769,7 +800,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-source-language")) {
++i;
if (i == argc) {
- printErr(u"The option -source-language requires a parameter.\n"_qs);
+ printErr(u"The option -source-language requires a parameter.\n"_s);
return 1;
}
sourceLanguage = args[i];
@@ -777,7 +808,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-disable-heuristic")) {
++i;
if (i == argc) {
- printErr(u"The option -disable-heuristic requires a parameter.\n"_qs);
+ printErr(u"The option -disable-heuristic requires a parameter.\n"_s);
return 1;
}
arg = args[i];
@@ -785,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(u"Invalid heuristic name passed to -disable-heuristic.\n"_qs);
+ printErr(u"Invalid heuristic name passed to -disable-heuristic.\n"_s);
return 1;
}
continue;
} else if (arg == QLatin1String("-locations")) {
++i;
if (i == argc) {
- printErr(u"The option -locations requires a parameter.\n"_qs);
+ printErr(u"The option -locations requires a parameter.\n"_s);
return 1;
}
if (args[i] == QLatin1String("none")) {
@@ -805,7 +834,7 @@ int main(int argc, char **argv)
} else if (args[i] == QLatin1String("absolute")) {
options |= AbsoluteLocations;
} else {
- printErr(u"Invalid parameter passed to -locations.\n"_qs);
+ printErr(u"Invalid parameter passed to -locations.\n"_s);
return 1;
}
continue;
@@ -839,7 +868,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-extensions")) {
++i;
if (i == argc) {
- printErr(u"The -extensions option should be followed by an extension list.\n"_qs);
+ printErr(u"The -extensions option should be followed by an extension list.\n"_s);
return 1;
}
extensions = args[i];
@@ -847,7 +876,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-tr-function-alias")) {
++i;
if (i == argc) {
- printErr(u"The -tr-function-alias option should be followed by a list of function=alias mappings.\n"_qs);
+ 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]))
@@ -856,7 +885,7 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-pro")) {
++i;
if (i == argc) {
- printErr(u"The -pro option should be followed by a filename of .pro file.\n"_qs);
+ 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());
@@ -866,16 +895,16 @@ int main(int argc, char **argv)
} else if (arg == QLatin1String("-pro-out")) {
++i;
if (i == argc) {
- printErr(u"The -pro-out option should be followed by a directory name.\n"_qs);
+ 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(u"The -I option should be followed by a path.\n"_qs);
+ printErr(u"The -I option should be followed by a path.\n"_s);
return 1;
}
includePath += args[i];
@@ -894,6 +923,14 @@ int main(int argc, char **argv)
}
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(QStringLiteral("Unrecognized option '%1'.\n").arg(arg));
@@ -912,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(u"The -I option should be followed by a path.\n"_qs);
+ if (lineContent.size() == 2) {
+ printErr(u"The -I option should be followed by a path.\n"_s);
return 1;
}
includePath += lineContent.mid(2);
@@ -925,9 +962,9 @@ 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()) {
@@ -950,7 +987,7 @@ 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(QStringLiteral("lupdate error: File '%1' does not exist.\n").arg(file));
@@ -977,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;
@@ -1019,13 +1056,21 @@ int main(int argc, char **argv)
return 1;
}
- if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
+ if (!targetLanguage.isEmpty() && tsFileNames.size() != 1)
printErr(u"lupdate warning: -target-language usually only"
- " makes sense with exactly one TS file.\n"_qs);
+ " 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()) {
- runInternalQtTool(u"lupdate-pro"_qs, app.arguments().mid(1));
+ runInternalQtTool(u"lupdate-pro"_s, app.arguments().mid(1));
return 0;
}
@@ -1042,13 +1087,16 @@ int main(int argc, char **argv)
.arg(projectDescriptionFile));
return 1;
}
+ removeExcludedSources(projectDescription);
+ for (Project &project : projectDescription)
+ expandQrcFiles(project);
}
bool fail = false;
if (projectDescription.empty()) {
if (tsFileNames.isEmpty())
printErr(u"lupdate warning:"
- " no TS files specified. Only diagnostics will be produced.\n"_qs);
+ " no TS files specified. Only diagnostics will be produced.\n"_s);
Translator fetchedTor;
ConversionData cd;
@@ -1058,7 +1106,8 @@ int main(int argc, char **argv)
cd.m_includePath = includePath;
cd.m_allCSources = allCSources;
cd.m_compilationDatabaseDir = commandLineCompilationDatabaseDir;
- for (const QString &resource : qAsConst(resourceFiles))
+ cd.m_rootDirs = rootDirs;
+ for (const QString &resource : std::as_const(resourceFiles))
sourceFiles << getResources(resource);
processSources(fetchedTor, sourceFiles, cd, &fail);
updateTsFiles(fetchedTor, tsFileNames, alienFiles,