diff options
Diffstat (limited to 'src/linguist/lupdate/main.cpp')
-rw-r--r-- | src/linguist/lupdate/main.cpp | 314 |
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; } |