summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/designer/src/lib/CMakeLists.txt24
-rw-r--r--src/linguist/linguist/doc/src/linguist-manual.qdoc4
-rw-r--r--src/linguist/lprodump/main.cpp19
-rw-r--r--src/linguist/lupdate/cpp.cpp64
-rw-r--r--src/linguist/lupdate/cpp.h55
-rw-r--r--src/linguist/lupdate/cpp_clang.cpp2
-rw-r--r--src/linguist/lupdate/filesignificancecheck.cpp22
-rw-r--r--src/linguist/lupdate/filesignificancecheck.h9
-rw-r--r--src/linguist/lupdate/main.cpp17
-rw-r--r--src/linguist/lupdate/python.cpp49
-rw-r--r--src/linguist/shared/numerus.cpp1
-rw-r--r--src/linguist/shared/projectdescriptionreader.cpp24
-rw-r--r--src/linguist/shared/projectdescriptionreader.h4
-rw-r--r--src/linguist/shared/translator.cpp13
-rw-r--r--src/linguist/shared/translator.h5
-rw-r--r--src/qdoc/doc/qdoc-manual-topiccmds.qdoc26
-rw-r--r--src/qtattributionsscanner/jsongenerator.cpp3
-rw-r--r--src/qtattributionsscanner/package.h3
-rw-r--r--src/qtattributionsscanner/qdocgenerator.cpp3
-rw-r--r--src/qtattributionsscanner/scanner.cpp41
20 files changed, 278 insertions, 110 deletions
diff --git a/src/designer/src/lib/CMakeLists.txt b/src/designer/src/lib/CMakeLists.txt
index a11a6bb0e..ae21ddb73 100644
--- a/src/designer/src/lib/CMakeLists.txt
+++ b/src/designer/src/lib/CMakeLists.txt
@@ -411,6 +411,30 @@ qt_internal_extend_target(Designer CONDITION NOT QT_BUILD_SHARED_LIBS
../../../shared/qtpropertybrowser
)
+if(TARGET zstd::libzstd)
+ qt_internal_disable_find_package_global_promotion(zstd::libzstd)
+endif()
+if(TARGET zstd::libzstd_shared)
+ qt_internal_disable_find_package_global_promotion(zstd::libzstd_shared)
+endif()
+if(TARGET zstd::libzstd_static)
+ qt_internal_disable_find_package_global_promotion(zstd::libzstd_static)
+endif()
+if(NOT TARGET WrapZSTD::WrapZSTD)
+ qt_find_package(WrapZSTD 1.3
+ PROVIDED_TARGETS
+ WrapZSTD::WrapZSTD
+ zstd::libzstd
+ zstd::libzstd_static
+ zstd::libzstd_shared
+ )
+endif()
+
+qt_internal_extend_target(Designer CONDITION QT_FEATURE_zstd
+ LIBRARIES
+ WrapZSTD::WrapZSTD
+)
+
if(NOT QT_BUILD_SHARED_LIBS)
# Resources:
set(qtpropertybrowser_resource_files
diff --git a/src/linguist/linguist/doc/src/linguist-manual.qdoc b/src/linguist/linguist/doc/src/linguist-manual.qdoc
index 9309f5cc7..c6db433af 100644
--- a/src/linguist/linguist/doc/src/linguist-manual.qdoc
+++ b/src/linguist/linguist/doc/src/linguist-manual.qdoc
@@ -580,6 +580,10 @@
installed Qt, you can start \QL in the same way as any other application on
the development host.
+ \note You can obtain an installer for \QL only (without the entire Qt SDK)
+ from \l {https://download.qt.io/linguist_releases/}. The installer is
+ available for Windows, and works with Qt 6.
+
To address issues that arise from the subtleties and complexities of
human language, translators and developers may need to:
diff --git a/src/linguist/lprodump/main.cpp b/src/linguist/lprodump/main.cpp
index caa59992b..3d30e4a63 100644
--- a/src/linguist/lprodump/main.cpp
+++ b/src/linguist/lprodump/main.cpp
@@ -149,7 +149,7 @@ static QStringList getSources(const char *var, const char *vvar, const QStringLi
}
static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir,
- const QStringList &excludes, QMakeVfs *vfs)
+ QMakeVfs *vfs)
{
QStringList baseVPaths;
baseVPaths += visitor.absolutePathValues(QLatin1String("VPATH"), projectDir);
@@ -201,18 +201,6 @@ static QStringList getSources(const ProFileEvaluator &visitor, const QString &pr
sourceFiles.removeDuplicates();
sourceFiles.sort();
-
- for (const QString &ex : excludes) {
- // TODO: take advantage of the file list being sorted
- QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
- for (auto it = sourceFiles.begin(); it != sourceFiles.end(); ) {
- if (rx.match(*it).hasMatch())
- it = sourceFiles.erase(it);
- else
- ++it;
- }
- }
-
return sourceFiles;
}
@@ -281,11 +269,10 @@ static QJsonObject processProject(const QString &proFile, const QStringList &tra
if (!subResults.isEmpty())
setValue(result, "subProjects", subResults);
} else {
- const QStringList excludes = getExcludes(visitor, proPath);
- const QStringList sourceFiles = getSources(visitor, proPath, excludes, vfs);
+ const QStringList sourceFiles = getSources(visitor, proPath, vfs);
setValue(result, "includePaths",
visitor.absolutePathValues(QLatin1String("INCLUDEPATH"), proPath));
- setValue(result, "excluded", excludes);
+ setValue(result, "excluded", getExcludes(visitor, proPath));
setValue(result, "sources", sourceFiles);
}
return result;
diff --git a/src/linguist/lupdate/cpp.cpp b/src/linguist/lupdate/cpp.cpp
index 96ef9625b..4c2075d21 100644
--- a/src/linguist/lupdate/cpp.cpp
+++ b/src/linguist/lupdate/cpp.cpp
@@ -5,7 +5,6 @@
#include <translator.h>
#include <QtCore/QBitArray>
-#include <QtCore/QStack>
#include <QtCore/QTextStream>
#include <QtCore/QRegularExpression>
@@ -59,15 +58,6 @@ private:
QBitArray m_ba;
};
-struct CppParserState
-{
- NamespaceList namespaces;
- QStack<qsizetype> namespaceDepths;
- NamespaceList functionContext;
- QString functionContextUnresolved;
- QString pendingContext;
-};
-
class CppParser : private CppParserState {
public:
@@ -1154,6 +1144,23 @@ void CppParser::truncateNamespaces(NamespaceList *namespaces, int length)
Functions for processing include files.
*/
+size_t qHash(const CppParserState &s, size_t seed)
+{
+ seed = qHash(s.namespaces, seed);
+ seed = qHash(s.namespaceDepths, seed);
+ seed = qHash(s.functionContext, seed);
+ seed = qHash(s.functionContextUnresolved, seed);
+ seed = qHash(s.pendingContext, seed);
+ return seed;
+}
+
+size_t qHash(const ResultsCacheKey &key, size_t seed)
+{
+ seed = qHash(key.cleanFile, seed);
+ seed = qHash(key.parserState, seed);
+ return seed;
+}
+
IncludeCycleHash &CppFiles::includeCycles()
{
static IncludeCycleHash cycles;
@@ -1175,9 +1182,9 @@ QSet<QString> &CppFiles::blacklistedFiles()
return blacklisted;
}
-QSet<const ParseResults *> CppFiles::getResults(const QString &cleanFile)
+QSet<const ParseResults *> CppFiles::getResults(const ResultsCacheKey &key)
{
- IncludeCycle * const cycle = includeCycles().value(cleanFile);
+ IncludeCycle * const cycle = includeCycles().value(key);
if (cycle)
return cycle->results;
@@ -1185,16 +1192,16 @@ QSet<const ParseResults *> CppFiles::getResults(const QString &cleanFile)
return QSet<const ParseResults *>();
}
-void CppFiles::setResults(const QString &cleanFile, const ParseResults *results)
+void CppFiles::setResults(const ResultsCacheKey &key, const ParseResults *results)
{
- IncludeCycle *cycle = includeCycles().value(cleanFile);
+ IncludeCycle *cycle = includeCycles().value(key);
if (!cycle) {
cycle = new IncludeCycle;
- includeCycles().insert(cleanFile, cycle);
+ includeCycles().insert(key, cycle);
}
- cycle->fileNames.insert(cleanFile);
+ cycle->fileNames.insert(key.cleanFile);
cycle->results.insert(results);
}
@@ -1218,14 +1225,15 @@ void CppFiles::setBlacklisted(const QString &cleanFile)
blacklistedFiles().insert(cleanFile);
}
-void CppFiles::addIncludeCycle(const QSet<QString> &fileNames)
+void CppFiles::addIncludeCycle(const QSet<QString> &fileNames, const CppParserState &parserState)
{
IncludeCycle * const cycle = new IncludeCycle;
cycle->fileNames = fileNames;
QSet<IncludeCycle *> intersectingCycles;
for (const QString &fileName : fileNames) {
- IncludeCycle *intersectingCycle = includeCycles().value(fileName);
+ const ResultsCacheKey key = { fileName, parserState };
+ IncludeCycle *intersectingCycle = includeCycles().value(key);
if (intersectingCycle && !intersectingCycles.contains(intersectingCycle)) {
intersectingCycles.insert(intersectingCycle);
@@ -1237,7 +1245,7 @@ void CppFiles::addIncludeCycle(const QSet<QString> &fileNames)
qDeleteAll(intersectingCycles);
for (const QString &fileName : std::as_const(cycle->fileNames))
- includeCycles().insert(fileName, cycle);
+ includeCycles().insert({ fileName, parserState }, cycle);
}
static bool isHeader(const QString &name)
@@ -1251,29 +1259,27 @@ void CppParser::processInclude(const QString &file, ConversionData &cd, const QS
{
QString cleanFile = QDir::cleanPath(file);
- for (const QString &ex : std::as_const(cd.m_excludes)) {
- QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(ex));
+ for (const QRegularExpression &rx : std::as_const(cd.m_excludes)) {
if (rx.match(cleanFile).hasMatch())
return;
}
const int index = includeStack.indexOf(cleanFile);
if (index != -1) {
- CppFiles::addIncludeCycle(QSet<QString>(includeStack.cbegin() + index, includeStack.cend()));
+ CppFiles::addIncludeCycle(QSet<QString>(includeStack.cbegin() + index, includeStack.cend()),
+ *this);
return;
}
- // If the #include is in any kind of namespace, has been blacklisted previously,
+ // If the #include has been blacklisted previously,
// or is not a header file (stdc++ extensionless or *.h*), then really include
// it. Otherwise it is safe to process it stand-alone and re-use the parsed
// namespace data for inclusion into other files.
bool isIndirect = false;
- if (namespaces.size() == 1 && functionContext.size() == 1
- && functionContextUnresolved.isEmpty() && pendingContext.isEmpty()
- && !CppFiles::isBlacklisted(cleanFile)
+ if (!CppFiles::isBlacklisted(cleanFile)
&& isHeader(cleanFile)) {
- QSet<const ParseResults *> res = CppFiles::getResults(cleanFile);
+ QSet<const ParseResults *> res = CppFiles::getResults(ResultsCacheKey(cleanFile, *this));
if (!res.isEmpty()) {
results->includes.unite(res);
return;
@@ -2245,7 +2251,7 @@ const ParseResults *CppParser::recordResults(bool isHeader)
results->fileId = nextFileId++;
pr = results;
}
- CppFiles::setResults(yyFileName, pr);
+ CppFiles::setResults(ResultsCacheKey(yyFileName, *this), pr);
return pr;
} else {
delete results;
@@ -2258,7 +2264,7 @@ void loadCPP(Translator &translator, const QStringList &filenames, ConversionDat
QStringConverter::Encoding e = cd.m_sourceIsUtf16 ? QStringConverter::Utf16 : QStringConverter::Utf8;
for (const QString &filename : filenames) {
- if (!CppFiles::getResults(filename).isEmpty() || CppFiles::isBlacklisted(filename))
+ if (!CppFiles::getResults(ResultsCacheKey(filename)).isEmpty() || CppFiles::isBlacklisted(filename))
continue;
QFile file(filename);
diff --git a/src/linguist/lupdate/cpp.h b/src/linguist/lupdate/cpp.h
index ca3cd72f9..8f6497195 100644
--- a/src/linguist/lupdate/cpp.h
+++ b/src/linguist/lupdate/cpp.h
@@ -7,6 +7,7 @@
#include "lupdate.h"
#include <QtCore/QSet>
+#include <QtCore/QStack>
#include <iostream>
@@ -79,19 +80,63 @@ struct IncludeCycle {
QSet<const ParseResults *> results;
};
-typedef QHash<QString, IncludeCycle *> IncludeCycleHash;
+struct CppParserState
+{
+ NamespaceList namespaces;
+ QStack<qsizetype> namespaceDepths;
+ NamespaceList functionContext;
+ QString functionContextUnresolved;
+ QString pendingContext;
+
+ bool operator==(const CppParserState &other) const
+ {
+ return namespaces == other.namespaces
+ && namespaceDepths == other.namespaceDepths
+ && functionContext == other.functionContext
+ && functionContextUnresolved == other.functionContextUnresolved
+ && pendingContext == other.pendingContext;
+ }
+};
+
+size_t qHash(const CppParserState &s, size_t seed);
+
+struct ResultsCacheKey
+{
+ const QString cleanFile;
+ const CppParserState parserState;
+
+ ResultsCacheKey(const QString &filePath)
+ : cleanFile(filePath)
+ {
+ }
+
+ ResultsCacheKey(const QString &filePath, const CppParserState &state)
+ : cleanFile(filePath),
+ parserState(state)
+ {
+ }
+
+ bool operator==(const ResultsCacheKey &other) const
+ {
+ return cleanFile == other.cleanFile
+ && parserState == other.parserState;
+ }
+};
+
+size_t qHash(const ResultsCacheKey &key, size_t seed);
+
+typedef QHash<ResultsCacheKey, IncludeCycle *> IncludeCycleHash;
typedef QHash<QString, const Translator *> TranslatorHash;
class CppFiles {
-
public:
- static QSet<const ParseResults *> getResults(const QString &cleanFile);
- static void setResults(const QString &cleanFile, const ParseResults *results);
+ static QSet<const ParseResults *> getResults(const ResultsCacheKey &key);
+ static void setResults(const ResultsCacheKey &key, const ParseResults *results);
static const Translator *getTranslator(const QString &cleanFile);
static void setTranslator(const QString &cleanFile, const Translator *results);
static bool isBlacklisted(const QString &cleanFile);
static void setBlacklisted(const QString &cleanFile);
- static void addIncludeCycle(const QSet<QString> &fileNames);
+ static void addIncludeCycle(const QSet<QString> &fileNames, const CppParserState &parserState);
private:
static IncludeCycleHash &includeCycles();
diff --git a/src/linguist/lupdate/cpp_clang.cpp b/src/linguist/lupdate/cpp_clang.cpp
index 180bbb4cb..1900e284c 100644
--- a/src/linguist/lupdate/cpp_clang.cpp
+++ b/src/linguist/lupdate/cpp_clang.cpp
@@ -402,7 +402,7 @@ void ClangCppParser::loadCPP(Translator &translator, const QStringList &files, C
{
FileSignificanceCheck::create();
auto cleanup = qScopeGuard(FileSignificanceCheck::destroy);
- FileSignificanceCheck::the()->setExclusionPatterns(cd.m_excludes);
+ FileSignificanceCheck::the()->setExclusionRegExes(cd.m_excludes);
if (cd.m_rootDirs.size() > 0)
FileSignificanceCheck::the()->setRootDirectories(cd.m_rootDirs);
else
diff --git a/src/linguist/lupdate/filesignificancecheck.cpp b/src/linguist/lupdate/filesignificancecheck.cpp
index 9babe6898..65ae303f7 100644
--- a/src/linguist/lupdate/filesignificancecheck.cpp
+++ b/src/linguist/lupdate/filesignificancecheck.cpp
@@ -3,8 +3,6 @@
#include "filesignificancecheck.h"
-#include <mutex>
-
QT_BEGIN_NAMESPACE
FileSignificanceCheck *FileSignificanceCheck::m_instance = nullptr;
@@ -17,12 +15,9 @@ void FileSignificanceCheck::setRootDirectories(const QStringList &paths)
m_rootDirs[i].setPath(paths.at(i));
}
-void FileSignificanceCheck::setExclusionPatterns(const QStringList &patterns)
+void FileSignificanceCheck::setExclusionRegExes(const QVector<QRegularExpression> &expressions)
{
- const size_t patternsSize = static_cast<size_t>(patterns.size());
- m_exclusionRegExes.resize(patternsSize);
- for (size_t i = 0; i < patternsSize; ++i)
- m_exclusionRegExes[i] = QRegularExpression::fromWildcard(patterns.at(i));
+ m_exclusionRegExes = expressions;
}
/*
@@ -37,14 +32,15 @@ void FileSignificanceCheck::setExclusionPatterns(const QStringList &patterns)
bool FileSignificanceCheck::isFileSignificant(const std::string &filePath) const
{
// cache lookup
- std::shared_lock<std::shared_mutex> readLock(m_cacheMutex);
- auto it = m_cache.find(filePath);
- if (it != m_cache.end())
- return it->second;
+ {
+ QReadLocker locker(&m_cacheLock);
+ auto it = m_cache.find(filePath);
+ if (it != m_cache.end())
+ return it->second;
+ }
// cache miss
- readLock.unlock();
- std::unique_lock<std::shared_mutex> writeLock(m_cacheMutex);
+ QWriteLocker locker(&m_cacheLock);
QString file = QString::fromUtf8(filePath);
QString cleanFile = QDir::cleanPath(file);
for (const QRegularExpression &rx : m_exclusionRegExes) {
diff --git a/src/linguist/lupdate/filesignificancecheck.h b/src/linguist/lupdate/filesignificancecheck.h
index 15a947133..6f6590b52 100644
--- a/src/linguist/lupdate/filesignificancecheck.h
+++ b/src/linguist/lupdate/filesignificancecheck.h
@@ -5,10 +5,11 @@
#define FILESIGNIFICANCECHECK_H
#include <QtCore/qdir.h>
+#include <QtCore/qreadwritelock.h>
#include <QtCore/qregularexpression.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qvector.h>
-#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
@@ -37,16 +38,16 @@ public:
}
void setRootDirectories(const QStringList &paths);
- void setExclusionPatterns(const QStringList &patterns);
+ void setExclusionRegExes(const QVector<QRegularExpression> &expressions);
bool isFileSignificant(const std::string &filePath) const;
private:
static FileSignificanceCheck *m_instance;
std::vector<QDir> m_rootDirs;
- std::vector<QRegularExpression> m_exclusionRegExes;
+ QVector<QRegularExpression> m_exclusionRegExes;
mutable std::unordered_map<std::string, bool> m_cache;
- mutable std::shared_mutex m_cacheMutex;
+ mutable QReadWriteLock m_cacheLock;
};
namespace LupdatePrivate {
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index 0ebe43214..d83ed880a 100644
--- a/src/linguist/lupdate/main.cpp
+++ b/src/linguist/lupdate/main.cpp
@@ -18,6 +18,7 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QLibraryInfo>
+#include <QtCore/QRegularExpression>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTranslator>
@@ -463,6 +464,21 @@ static bool readFileContent(const QString &filePath, QString *content, QString *
return true;
}
+static void removeExcludedSources(Projects &projects)
+{
+ for (Project &project : projects) {
+ for (const QRegularExpression &rx : project.excluded) {
+ 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))
@@ -1070,6 +1086,7 @@ int main(int argc, char **argv)
.arg(projectDescriptionFile));
return 1;
}
+ removeExcludedSources(projectDescription);
for (Project &project : projectDescription)
expandQrcFiles(project);
}
diff --git a/src/linguist/lupdate/python.cpp b/src/linguist/lupdate/python.cpp
index 0bc3bf5e8..bcd37f8be 100644
--- a/src/linguist/lupdate/python.cpp
+++ b/src/linguist/lupdate/python.cpp
@@ -6,6 +6,7 @@
#include "lupdate.h"
#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qstack.h>
@@ -53,8 +54,15 @@ static int yyParenDepth;
static int yyLineNo;
static int yyCurLineNo;
-static QByteArray extraComment;
-static QByteArray id;
+struct ExtraComment
+{
+ QByteArray extraComment;
+ int lineNo;
+};
+
+static QList<ExtraComment> extraComments;
+
+static QList<ExtraComment> ids;
QHash<QByteArray, Token> tokens = {
{"None", Tok_None},
@@ -302,10 +310,10 @@ static Token getToken(StringType stringType = StringType::NoString)
case '#':
switch (getChar()) {
case ':':
- extraComment = readLine().trimmed();
+ extraComments.append({readLine().trimmed(), yyCurLineNo});
break;
case '=':
- id = readLine().trimmed();
+ ids.append({readLine().trimmed(), yyCurLineNo});
break;
case EOF:
return Tok_Eof;
@@ -583,16 +591,24 @@ static bool parseTranslate(QByteArray *text, QByteArray *context, QByteArray *co
return false;
}
-static inline void setMessageParameters(TranslatorMessage *message)
+static void setMessageParameters(TranslatorMessage *message,
+ int lineNo)
{
- if (!extraComment.isEmpty()) {
- message->setExtraComment(QString::fromUtf8(extraComment));
- extraComment.clear();
- }
- if (!id.isEmpty()) {
- message->setId(QString::fromUtf8(id));
- id.clear();
+ // PYSIDE-2863: parseTranslate() can read past the message
+ // and capture extraComments intended for the next message.
+ // Use only extraComments for the current message.
+ QByteArray extraComment;
+ while (!extraComments.isEmpty() && extraComments.constFirst().lineNo <= lineNo) {
+ if (!extraComment.isEmpty())
+ extraComment += ' ';
+ extraComment += extraComments.takeFirst().extraComment;
}
+
+ if (!extraComment.isEmpty())
+ message->setExtraComment(QString::fromUtf8(extraComment));
+
+ while (!ids.isEmpty() && ids.constFirst().lineNo <= lineNo)
+ message->setId(QString::fromUtf8(ids.takeFirst().extraComment));
}
static void parse(Translator &tor, ConversionData &cd,
@@ -635,9 +651,10 @@ static void parse(Translator &tor, ConversionData &cd,
yyTok = getToken();
break;
case Tok_tr:
- case Tok_trUtf8:
+ case Tok_trUtf8: {
utf8 = true;
yyTok = getToken();
+ const int lineNo = yyCurLineNo;
if (match(Tok_LeftParen) && matchString(&text)) {
comment.clear();
bool plural = false;
@@ -670,13 +687,15 @@ static void parse(Translator &tor, ConversionData &cd,
QString::fromUtf8(comment),
{}, yyFileName, yyLineNo,
{}, TranslatorMessage::Unfinished, plural);
- setMessageParameters(&message);
+ setMessageParameters(&message, lineNo);
tor.extend(message, cd);
}
}
+ }
break;
case Tok_translate: {
bool plural{};
+ const int lineNo = yyCurLineNo;
if (parseTranslate(&text, &context, &comment, &utf8, &plural)
&& !text.isEmpty()) {
TranslatorMessage message(QString::fromUtf8(context),
@@ -684,7 +703,7 @@ static void parse(Translator &tor, ConversionData &cd,
QString::fromUtf8(comment),
{}, yyFileName, yyLineNo,
{}, TranslatorMessage::Unfinished, plural);
- setMessageParameters(&message);
+ setMessageParameters(&message, lineNo);
tor.extend(message, cd);
}
}
diff --git a/src/linguist/shared/numerus.cpp b/src/linguist/shared/numerus.cpp
index ef52bcdb6..b3bb86faf 100644
--- a/src/linguist/shared/numerus.cpp
+++ b/src/linguist/shared/numerus.cpp
@@ -151,6 +151,7 @@ static const QLocale::Language englishStyleLanguages[] = {
QLocale::Friulian,
QLocale::WesternFrisian,
QLocale::Galician,
+ QLocale::Ganda,
QLocale::Georgian,
QLocale::German,
QLocale::Greek,
diff --git a/src/linguist/shared/projectdescriptionreader.cpp b/src/linguist/shared/projectdescriptionreader.cpp
index eded3a650..8badd6584 100644
--- a/src/linguist/shared/projectdescriptionreader.cpp
+++ b/src/linguist/shared/projectdescriptionreader.cpp
@@ -15,6 +15,7 @@
#include <functional>
using std::placeholders::_1;
+using namespace Qt::Literals::StringLiterals;
class Validator
{
@@ -131,7 +132,7 @@ private:
result.filePath = stringValue(obj, QLatin1String("projectFile"));
result.compileCommands = stringValue(obj, QLatin1String("compileCommands"));
result.codec = stringValue(obj, QLatin1String("codec"));
- result.excluded = stringListValue(obj, QLatin1String("excluded"));
+ result.excluded = wildcardsToRegExes(stringListValue(obj, QLatin1String("excluded")));
result.includePaths = stringListValue(obj, QLatin1String("includePaths"));
result.sources = stringListValue(obj, QLatin1String("sources"));
if (obj.contains(QLatin1String("translations")))
@@ -171,6 +172,27 @@ private:
return QStringLiteral("unknown");
}
+ static QVector<QRegularExpression> wildcardsToRegExes(const QStringList &wildcardPatterns)
+ {
+ QVector<QRegularExpression> result;
+ result.reserve(wildcardPatterns.size());
+ for (const QString &wildcardPattern : wildcardPatterns)
+ result.append(wildcardToRegEx(wildcardPattern));
+ return result;
+ }
+
+ // Return a QRegularExpression object for a TR_EXCLUDE / QT_EXCLUDE_SOURCES_FROM_TRANSLATION
+ // wildcard pattern. The regular expression is only anchored at the beginning to allow matching
+ // subdirectories.
+ static QRegularExpression wildcardToRegEx(const QString &wildcardPattern)
+ {
+ return QRegularExpression(
+ "\\A"_L1
+ + QRegularExpression::wildcardToRegularExpression(
+ wildcardPattern,
+ QRegularExpression::UnanchoredWildcardConversion));
+ }
+
QString stringValue(const QJsonObject &obj, const QString &key)
{
if (!m_errorString.isEmpty())
diff --git a/src/linguist/shared/projectdescriptionreader.h b/src/linguist/shared/projectdescriptionreader.h
index 0222bdb42..b1af565a6 100644
--- a/src/linguist/shared/projectdescriptionreader.h
+++ b/src/linguist/shared/projectdescriptionreader.h
@@ -4,8 +4,10 @@
#ifndef PROJECTDESCRIPTIONREADER_H
#define PROJECTDESCRIPTIONREADER_H
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qvector.h>
#include <optional>
#include <vector>
@@ -19,7 +21,7 @@ struct Project
QString filePath;
QString compileCommands;
QString codec;
- QStringList excluded;
+ QVector<QRegularExpression> excluded;
QStringList includePaths;
QStringList sources;
Projects subProjects;
diff --git a/src/linguist/shared/translator.cpp b/src/linguist/shared/translator.cpp
index b52b8a253..46f4a067f 100644
--- a/src/linguist/shared/translator.cpp
+++ b/src/linguist/shared/translator.cpp
@@ -19,7 +19,6 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QLocale>
-#include <QtCore/QRegularExpression>
#include <QtCore/QTextStream>
#include <private/qtranslator_p.h>
@@ -339,10 +338,14 @@ void Translator::languageAndTerritory(QStringView languageCode, QLocale::Languag
{
QLocale::Language language = QLocale::AnyLanguage;
QLocale::Territory territory = QLocale::AnyTerritory;
- const auto underScore = languageCode.indexOf(u'_'); // "de_DE"
- if (underScore != -1) {
- language = QLocale::codeToLanguage(languageCode.left(underScore));
- territory = QLocale::codeToTerritory(languageCode.mid(underScore + 1));
+ auto separator = languageCode.indexOf(u'_'); // "de_DE"
+ if (separator == -1) {
+ // compatibility with older .ts files
+ separator = languageCode.indexOf(u'-'); // "de-DE"
+ }
+ if (separator != -1) {
+ language = QLocale::codeToLanguage(languageCode.left(separator));
+ territory = QLocale::codeToTerritory(languageCode.mid(separator + 1));
} else {
language = QLocale::codeToLanguage(languageCode);
territory = QLocale(language).territory();
diff --git a/src/linguist/shared/translator.h b/src/linguist/shared/translator.h
index 4bc88c7df..1fe01cc27 100644
--- a/src/linguist/shared/translator.h
+++ b/src/linguist/shared/translator.h
@@ -12,9 +12,10 @@
#include <QList>
#include <QLocale>
#include <QMultiHash>
+#include <QRegularExpression>
#include <QString>
#include <QSet>
-
+#include <QVector>
QT_BEGIN_NAMESPACE
@@ -53,7 +54,7 @@ public:
QString m_sourceFileName;
QString m_targetFileName;
QString m_compilationDatabaseDir;
- QStringList m_excludes;
+ QVector<QRegularExpression> m_excludes;
QDir m_sourceDir;
QDir m_targetDir; // FIXME: TS specific
QSet<QString> m_projectRoots;
diff --git a/src/qdoc/doc/qdoc-manual-topiccmds.qdoc b/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
index 3e747a7fb..e34471b13 100644
--- a/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
+++ b/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
@@ -1247,9 +1247,8 @@
The \\qmltype command is for documenting a QML type. The command
has one argument, which is the name of the QML type.
- If the QML type is instantiated by a C++ class, that class must be
- specified using the \l{instantiates-command} {\\instantiates}
- context command.
+ If the QML type has an equivalent C++ class, you can specify that class
+ with the \l{instantiates-command}{\\instantiates} context command.
\badcode *
/\1!
@@ -1264,9 +1263,9 @@
\1/
\endcode
- Here, the \e{\\qmltype} comment includes \l{instantiates-command}
- {\\instantiates} to specify that a Transform is instantiated by
- the C++ class QGraphicsTransform. A \\qmltype comment should
+ Here, the \e{\\qmltype} comment includes \e{\\instantiates}
+ to specify that a Transform is the QML counterpart to the
+ C++ class QGraphicsTransform. A \\qmltype comment should
always include a \l {since-command} {\\since} command, because all
QML types are new. It should also include a \l{brief-command}
{\\brief} description. If a QML type is a member of a QML type group,
@@ -1377,12 +1376,13 @@
\target instantiates-command
\section1 \\instantiates
- The \\instantiates command is used in the \l{qmltype-command} {QML
- type} comment of an elemental QML type to specify the name of the
- C++ class that instantiates the QML type.
+ The \\instantiates command must be used in conjunction with the
+ \l{qmltype-command}{\\qmltype} topic command. The command takes a
+ C++ class as its argument. If QDoc cannot find the C++ class, it
+ issues a warning.
- If the QML type is not instantiated by a C++ class, this command
- is not used.
+ Use the \\instantiates command to specify what the type is called in C++.
+ Any one QML type can only be the counterpart of one C++ class.
\badcode *
/\1!
@@ -1398,8 +1398,8 @@
\endcode
Here, the \e{\\qmltype} comment includes \l{instantiates-command}
- {\\instantiates} to specify that a Transform is instantiated by
- the C++ class QGraphicsTransform.
+ {\\instantiates} to specify that a Transform is QGraphicsTransform
+ in C++.
\target typealias-command
\section1 \\typealias
diff --git a/src/qtattributionsscanner/jsongenerator.cpp b/src/qtattributionsscanner/jsongenerator.cpp
index 2a194264e..b03d17e63 100644
--- a/src/qtattributionsscanner/jsongenerator.cpp
+++ b/src/qtattributionsscanner/jsongenerator.cpp
@@ -31,6 +31,9 @@ static QJsonObject generate(Package package)
obj.insert(u"Version"_s, package.version);
obj.insert(u"DownloadLocation"_s, package.downloadLocation);
+ obj.insert(u"CPE"_s, QJsonArray::fromStringList(package.cpeList));
+ obj.insert(u"PURL"_s, QJsonArray::fromStringList(package.purlList));
+
obj.insert(u"License"_s, package.license);
obj.insert(u"LicenseId"_s, package.licenseId);
if (package.licenseFiles.isEmpty())
diff --git a/src/qtattributionsscanner/package.h b/src/qtattributionsscanner/package.h
index 082d60f1e..765335e84 100644
--- a/src/qtattributionsscanner/package.h
+++ b/src/qtattributionsscanner/package.h
@@ -33,6 +33,9 @@ struct Package {
QString copyrightFile; // Path to file containing copyright owners. Optional.
QString copyrightFileContents;
+ QStringList cpeList; // List of CPE values. Optional.
+ QStringList purlList; // List of PURL values. Optional.
+
QString packageComment; // Further comments about the package. Optional.
};
diff --git a/src/qtattributionsscanner/qdocgenerator.cpp b/src/qtattributionsscanner/qdocgenerator.cpp
index 0e57c1106..439aa5e7e 100644
--- a/src/qtattributionsscanner/qdocgenerator.cpp
+++ b/src/qtattributionsscanner/qdocgenerator.cpp
@@ -130,9 +130,6 @@ static void generate(QTextStream &out, const Package &package, const QDir &baseD
&& package.licenseId != "NONE"_L1) {
out << "\\l{https://spdx.org/licenses/" << package.licenseId << ".html}"
<< "{" << package.license << "}.\n\n";
- } else if (package.licenseId.startsWith("urn:dje:license:"_L1)) {
- out << "\\l{https://enterprise.dejacode.com/licenses/public/" << package.licenseId.mid(16)
- << "/}{" << package.license << "}.\n\n";
} else {
out << package.license << ".\n\n";
}
diff --git a/src/qtattributionsscanner/scanner.cpp b/src/qtattributionsscanner/scanner.cpp
index f936ec0d2..f4998f194 100644
--- a/src/qtattributionsscanner/scanner.cpp
+++ b/src/qtattributionsscanner/scanner.cpp
@@ -212,6 +212,31 @@ static bool autoDetectLicenseFiles(Package &p)
return success;
}
+// Tries to interpret a json value either as a string or an array of strings, and assigns the
+// result to outList. Returns true on success, false on failure. On failure, it also conditionally
+// prints an error.
+static bool handleStringOrStringArrayJsonKey(QStringList &outList, const QString &key,
+ QJsonValueConstRef jsonValue, const QString &filePath,
+ LogLevel logLevel)
+{
+ if (jsonValue.isArray()) {
+ auto maybeStringList = toStringList(jsonValue);
+ if (maybeStringList)
+ outList = maybeStringList.value();
+ } else if (jsonValue.isString()) {
+ outList.append(jsonValue.toString());
+ } else {
+ if (logLevel != SilentLog) {
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of strings or "
+ "string as value of %2.").arg(
+ QDir::toNativeSeparators(filePath), key))
+ << std::endl;
+ }
+ return false;
+ }
+ return true;
+}
+
// Transforms a JSON object into a Package object
static std::optional<Package> readPackage(const QJsonObject &object, const QString &filePath,
Checks checks, LogLevel logLevel)
@@ -226,7 +251,7 @@ static std::optional<Package> readPackage(const QJsonObject &object, const QStri
if (!iter.value().isString() && key != "QtParts"_L1 && key != "SecurityCritical"_L1
&& key != "Files"_L1 && key != "LicenseFiles"_L1 && key != "Comment"_L1
- && key != "Copyright"_L1) {
+ && key != "Copyright"_L1 && key != "CPE"_L1 && key != "PURL"_L1) {
if (logLevel != SilentLog)
std::cerr << qPrintable(tr("File %1: Expected JSON string as value of %2.").arg(
QDir::toNativeSeparators(filePath), key)) << std::endl;
@@ -299,13 +324,25 @@ static std::optional<Package> readPackage(const QJsonObject &object, const QStri
p.copyright = value;
} else {
if (logLevel != SilentLog) {
- std::cerr << qPrintable(tr("File %1: Expected JSON array of string or"
+ std::cerr << qPrintable(tr("File %1: Expected JSON array of strings or "
"string as value of %2.").arg(
QDir::toNativeSeparators(filePath), key)) << std::endl;
validPackage = false;
continue;
}
}
+ } else if (key == "CPE"_L1) {
+ const QJsonValueConstRef jsonValue = iter.value();
+ if (!handleStringOrStringArrayJsonKey(p.cpeList, key, jsonValue, filePath, logLevel)) {
+ validPackage = false;
+ continue;
+ }
+ } else if (key == "PURL"_L1) {
+ const QJsonValueConstRef jsonValue = iter.value();
+ if (!handleStringOrStringArrayJsonKey(p.purlList, key, jsonValue, filePath, logLevel)) {
+ validPackage = false;
+ continue;
+ }
} else if (key == "CopyrightFile"_L1) {
p.copyrightFile = QDir(directory).absoluteFilePath(value);
} else if (key == "PackageComment"_L1) {