diff options
| author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-10-10 12:01:59 +0300 |
|---|---|---|
| committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-10-10 12:01:59 +0300 |
| commit | cf5106463bd3aeb78a6a51839673b0e4f008fd82 (patch) | |
| tree | b675d72375ae88453e1118c4e7be627f94fccd7c | |
| parent | 786bdcbf8cdc7ba3d4e5a8c15e1ead03c5926967 (diff) | |
| parent | 6444be988641a3a96a49fb39f28af2c532397e65 (diff) | |
Merge tag 'v6.5.7-lts' into tqtc/lts-6.5-opensourcev6.5.7-lts-lgpl6.5
Qt 6.5.7-lts release
Conflicts solved:
dependencies.yaml
Change-Id: I17f2889bbcdd447aacb628177f1df4cad4b2cd16
38 files changed, 478 insertions, 130 deletions
diff --git a/.cmake.conf b/.cmake.conf index 1aa29cee6..98c2813ee 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -1,3 +1,3 @@ -set(QT_REPO_MODULE_VERSION "6.5.6") +set(QT_REPO_MODULE_VERSION "6.5.7") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1") diff --git a/CMakeLists.txt b/CMakeLists.txt index 0af185486..8e6cd54d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,9 +18,9 @@ if(WIN32) list(APPEND optional_components AxContainer) endif() -find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core Network) +find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core) find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS - DBus Xml Widgets Quick QuickWidgets Qml + DBus Network Xml Widgets Quick QuickWidgets Qml Sql PrintSupport OpenGL OpenGLWidgets ${optional_components}) qt_internal_project_setup() diff --git a/cmake/FindWrapLibClang.cmake b/cmake/FindWrapLibClang.cmake index 3a7ed71b4..bfbb41ee6 100644 --- a/cmake/FindWrapLibClang.cmake +++ b/cmake/FindWrapLibClang.cmake @@ -16,6 +16,18 @@ endif() find_package(Clang CONFIG) +# LLVM versions >= 16 come with Findzstd.cmake that creates a target for libzstd. +# Disable its global promotion to prevent interference with FindWrapZSTD.cmake. +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(__qt_wrap_clang_backup_prefix) set(CMAKE_PREFIX_PATH "${__qt_wrap_clang_backup_prefix}") unset(__qt_wrap_clang_backup_prefix) diff --git a/configure.cmake b/configure.cmake index 76ccac16a..1ee2f467c 100644 --- a/configure.cmake +++ b/configure.cmake @@ -42,7 +42,7 @@ endif() qt_feature("assistant" PRIVATE LABEL "Qt Assistant" PURPOSE "Qt Assistant is a tool for viewing on-line documentation in Qt help file format." - CONDITION TARGET Qt::Widgets AND QT_FEATURE_png AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND (sqlite_plugin_available OR QT_BUILD_SHARED_LIBS) + CONDITION TARGET Qt::Widgets AND TARGET Qt::Network AND QT_FEATURE_png AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND (sqlite_plugin_available OR QT_BUILD_SHARED_LIBS) ) qt_feature("clang" PRIVATE LABEL "QDoc" @@ -55,7 +55,7 @@ qt_feature("clangcpp" PRIVATE qt_feature("designer" PRIVATE LABEL "Qt Designer" PURPOSE "Qt Designer is the Qt tool for designing and building graphical user interfaces (GUIs) with Qt Widgets. You can compose and customize your windows or dialogs in a what-you-see-is-what-you-get (WYSIWYG) manner, and test them using different styles and resolutions." - CONDITION TARGET Qt::Widgets AND QT_FEATURE_png AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton + CONDITION TARGET Qt::Widgets AND TARGET Qt::Network AND QT_FEATURE_png AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton ) qt_feature("distancefieldgenerator" PRIVATE LABEL "Qt Distance Field Generator" @@ -69,6 +69,7 @@ qt_feature("kmap2qmap" PRIVATE qt_feature("linguist" PRIVATE LABEL "Qt Linguist" PURPOSE "Qt Linguist can be used by translator to translate text in Qt applications." + CONDITION TARGET Qt::PrintSupport ) qt_feature("pixeltool" PRIVATE LABEL "pixeltool" @@ -129,3 +130,15 @@ qt_configure_add_report_entry( You will need to set the FEATURE_clangcpp CMake variable to ON to re-evaluate this check." CONDITION NOT QT_FEATURE_clangcpp ) + +qt_configure_add_report_entry( + TYPE WARNING + MESSAGE "Qt Assistant will not be compiled because it requires Qt Network." + CONDITION NOT QT_FEATURE_assistant AND NOT TARGET Qt::Network +) + +qt_configure_add_report_entry( + TYPE WARNING + MESSAGE "Qt Designer will not be compiled because it requires Qt Network." + CONDITION NOT QT_FEATURE_designer AND NOT TARGET Qt::Network +) diff --git a/dependencies.yaml b/dependencies.yaml index 9076b897d..b3301eef8 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,10 +1,10 @@ dependencies: ../tqtc-qtactiveqt: - ref: 504b47eb00e64d70b192b98a36d692d64aaa1fe8 + ref: fe2f23a7bf6444321981a090005430451ab94eaa required: false ../tqtc-qtbase: - ref: 5d8e9a8415562ba004b38508d91e1fa0254c17d3 + ref: fc0e66eefe3a08428ca4a6e92c66f37ac126d3c4 required: true ../tqtc-qtdeclarative: - ref: ff0a47c8f267e905113b82c53af2742027f0eca6 + ref: 844f9b9b376838bcb44324984876f8bf99d85d38 required: false 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) { diff --git a/tests/auto/linguist/lupdate/testdata/good/language/main.cpp b/tests/auto/linguist/lupdate/testdata/good/language/main.cpp new file mode 100644 index 000000000..3ac23e2ab --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/language/main.cpp @@ -0,0 +1,6 @@ +#include <QCoreApplication> + +int main() +{ + QCoreApplication::translate("main", "Hello %i World", "", 3); +} diff --git a/tests/auto/linguist/lupdate/testdata/good/language/project.pro b/tests/auto/linguist/lupdate/testdata/good/language/project.pro new file mode 100644 index 000000000..57a78e74d --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/language/project.pro @@ -0,0 +1,2 @@ +SOURCES = main.cpp +TRANSLATIONS = project.ts diff --git a/tests/auto/linguist/lupdate/testdata/good/language/project.ts.before b/tests/auto/linguist/lupdate/testdata/good/language/project.ts.before new file mode 100644 index 000000000..2574db313 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/language/project.ts.before @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="en-US"> +<context> + <name>main</name> + <message numerus="yes"> + <location filename="main.cpp" line="5"/> + <source>Hello %i World</source> + <translation type="unfinished"> + <numerusform></numerusform> + <numerusform></numerusform> + </translation> + </message> +</context> +</TS> diff --git a/tests/auto/linguist/lupdate/testdata/good/language/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/language/project.ts.result new file mode 100644 index 000000000..2574db313 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/language/project.ts.result @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="en-US"> +<context> + <name>main</name> + <message numerus="yes"> + <location filename="main.cpp" line="5"/> + <source>Hello %i World</source> + <translation type="unfinished"> + <numerusform></numerusform> + <numerusform></numerusform> + </translation> + </message> +</context> +</TS> diff --git a/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py b/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py index 5c0c77851..05c7e711e 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py +++ b/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py @@ -77,6 +77,17 @@ class Window(QMainWindow): msg = self.tr(r"A raw strin\g""continued\\") msg = self.tr(r"A raw string with escaped quote\"bla") + def test_pyside2863(self): + """PYSIDE-2863, Check whether id and extra comments are correctly associated.""" + #= id_1 + msg = self.tr("msg1") + #= id_2 + msg = self.tr("msg2") + #: Extra comment 3 + msg = self.tr("msg3") + #: Extra comment 4 + msg = self.tr("msg4") + if __name__ == '__main__': app = QApplication(sys.argv) diff --git a/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result index df1788672..b71acfd8d 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result +++ b/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result @@ -101,9 +101,31 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="main.py" line="81"/> + <location filename="main.py" line="80"/> <source>A raw string with escaped quote"bla</source> <translation type="unfinished"></translation> </message> + <message id="id_1"> + <location filename="main.py" line="85"/> + <source>msg1</source> + <translation type="unfinished"></translation> + </message> + <message id="id_2"> + <location filename="main.py" line="87"/> + <source>msg2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="main.py" line="89"/> + <source>msg3</source> + <extracomment>Extra comment 3</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="main.py" line="92"/> + <source>msg4</source> + <extracomment>Extra comment 4</extracomment> + <translation type="unfinished"></translation> + </message> </context> </TS> diff --git a/tests/auto/linguist/lupdate/tst_lupdate.cpp b/tests/auto/linguist/lupdate/tst_lupdate.cpp index 6e04417aa..b0c850ad0 100644 --- a/tests/auto/linguist/lupdate/tst_lupdate.cpp +++ b/tests/auto/linguist/lupdate/tst_lupdate.cpp @@ -9,6 +9,7 @@ #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFile> +#include <QtCore/QElapsedTimer> #include <QtCore/private/qconfig_p.h> #include <QtCore/QSet> @@ -19,6 +20,10 @@ using namespace Qt::Literals::StringLiterals; +// The slowest test (clang-proparsing) has been observed to take 22s in COIN/Linux. +// Windows does not run the clang tests. +static constexpr int TIMEOUT = 120000; + class tst_lupdate : public QObject { Q_OBJECT @@ -26,6 +31,7 @@ public: tst_lupdate(); private slots: + void cleanupTestCase(); void good_data(); void good(); #if CHECK_SIMTEXTH @@ -36,6 +42,8 @@ private slots: private: QString m_cmdLupdate; QString m_basePath; + QElapsedTimer m_timer; + qint64 m_maxElapsed = -1; void doCompare(QStringList actual, const QString &expectedFn, bool err); void doCompare(const QString &actualFn, const QString &expectedFn, bool err); @@ -44,11 +52,18 @@ private: tst_lupdate::tst_lupdate() { + m_timer.start(); QString binPath = QLibraryInfo::path(QLibraryInfo::BinariesPath); m_cmdLupdate = binPath + QLatin1String("/lupdate"); m_basePath = QFINDTESTDATA("testdata/"); } +void tst_lupdate::cleanupTestCase() +{ + if (m_maxElapsed > 0) + qInfo().noquote().nospace() << "max elapsed: " << m_maxElapsed << "ms"; +} + static bool prepareMatch(const QString &expect, QString *tmpl, int *require, int *accept) { if (expect.startsWith(QLatin1Char('\\'))) { @@ -245,6 +260,35 @@ void tst_lupdate::good_data() #endif } +static QByteArray msgStartFailed(const QProcess &process) +{ + const QString result = u'"' + process.program() + u' ' + process.arguments().join(u' ') + + "\": "_L1 + process.errorString(); + return result.toLocal8Bit(); +} + +static QByteArray msgTimeout(const QProcess &process) +{ + const QString result = u'"' + process.program() + u' ' + process.arguments().join(u' ') + + "\" timed out: "_L1 + process.errorString(); + return result.toLocal8Bit(); +} + +static QByteArray msgCrashed(const QProcess &process, const QByteArray &output) +{ + const QString result = u'"' + process.program() + u' ' + process.arguments().join(u' ') + + "\" crashed\n"_L1; + return result.toLocal8Bit() + output; +} + +static QByteArray msgExitCode(const QProcess &process, const QByteArray &output) +{ + const QString result = u'"' + process.program() + u' ' + process.arguments().join(u' ') + + "\" exited with code "_L1 + QString::number(process.exitCode()) + + u'\n'; + return result.toLocal8Bit() + output; +} + void tst_lupdate::good() { QFETCH(QString, directory); @@ -309,22 +353,30 @@ void tst_lupdate::good() QProcess proc; proc.setWorkingDirectory(workDir); proc.setProcessChannelMode(QProcess::MergedChannels); - const QString command = m_cmdLupdate + ' ' + lupdateArguments.join(' '); + const auto startTime = m_timer.elapsed(); proc.start(m_cmdLupdate, lupdateArguments, QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(proc.waitForStarted(), qPrintable(command + QLatin1String(" :") + proc.errorString())); - QVERIFY2(proc.waitForFinished(30000), qPrintable(command)); - const QString output = QString::fromLocal8Bit(proc.readAll()); - QVERIFY2(proc.exitStatus() == QProcess::NormalExit, - qPrintable(QLatin1Char('"') + command + "\" crashed\n" + output)); - QVERIFY2(!proc.exitCode(), - qPrintable(QLatin1Char('"') + command + "\" exited with code " + - QString::number(proc.exitCode()) + '\n' + output)); + QVERIFY2(proc.waitForStarted(), msgStartFailed(proc).constData()); + if (!proc.waitForFinished(TIMEOUT)) { + const auto message = msgTimeout(proc); + proc.kill(); + proc.waitForFinished(50); + QFAIL(message.constData()); + } + const auto elapsed = m_timer.elapsed() - startTime; + if (elapsed > m_maxElapsed) + m_maxElapsed = elapsed; + + const QByteArray output = proc.readAll(); + QVERIFY2(proc.exitStatus() == QProcess::NormalExit, msgCrashed(proc, output).constData()); + QVERIFY2(proc.exitCode() == 0, msgExitCode(proc, output).constData()); + + qInfo().noquote().nospace() << elapsed << "ms"; // If the file expectedoutput.txt exists, compare the // console output with the content of that file QFile outfile(dir + "/expectedoutput.txt"); if (outfile.exists()) { - QStringList errslist = output.split(QLatin1Char('\n')); + QStringList errslist = QString::fromLocal8Bit(output).split(u'\n'); doCompare(errslist, outfile.fileName(), true); if (QTest::currentTestFailed()) return; diff --git a/tests/auto/qtattributionsscanner/testdata/good/complete/qt_attribution_test.json b/tests/auto/qtattributionsscanner/testdata/good/complete/qt_attribution_test.json index 22eadd8cd..bdd68cdfd 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/complete/qt_attribution_test.json +++ b/tests/auto/qtattributionsscanner/testdata/good/complete/qt_attribution_test.json @@ -17,6 +17,14 @@ Line Usage", "Homepage": "www.qt.io", "Version": "1.0", + "CPE": [ + "cpe:2.3:a:qt:qt:test-version:*:*:*:*:*:*:*", + "cpe:2.3:a:qt:qtbase:test-version:*:*:*:*:*:*:*" + ], + "PURL": [ + "pkg:github/qt/qt@test-version", + "pkg:github/qt/qtbase@test-version" + ], "DownloadLocation": "www.qt.io/1.0", "SecurityCritical": true, "LicenseId": "xxx", diff --git a/tests/auto/qtattributionsscanner/testdata/good/expected.json b/tests/auto/qtattributionsscanner/testdata/good/expected.json index adde0d86b..6aae0ddae 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/expected.json +++ b/tests/auto/qtattributionsscanner/testdata/good/expected.json @@ -1,5 +1,6 @@ [ { + "CPE": [], "Copyright": "", "CopyrightFile": "", "Description": "Hello world!\nLine 2", @@ -11,6 +12,7 @@ "LicenseFile": "", "LicenseId": "", "Name": "Test", + "PURL": [], "PackageComment": "", "Path": "%{PWD}/chromium", "QDocModule": "qtwebengine", @@ -22,6 +24,10 @@ "Version": "" }, { + "CPE": [ + "cpe:2.3:a:qt:qt:test-version:*:*:*:*:*:*:*", + "cpe:2.3:a:qt:qtbase:test-version:*:*:*:*:*:*:*" + ], "Copyright": "Copyright", "CopyrightFile": "", "Description": "Multi\nLine\nDescription", @@ -33,6 +39,10 @@ "LicenseFile": "%{PWD}/complete/../../../../../../LICENSES/BSD-3-Clause.txt", "LicenseId": "xxx", "Name": "Complete", + "PURL": [ + "pkg:github/qt/qt@test-version", + "pkg:github/qt/qtbase@test-version" + ], "PackageComment": "just a test package", "Path": "%{PWD}/complete", "QDocModule": "qtest", @@ -44,6 +54,7 @@ "Version": "1.0" }, { + "CPE": [], "Copyright": "Copyright", "CopyrightFile": "", "Description": "", @@ -55,6 +66,7 @@ "LicenseFile": "%{LICENSES_DIR}/BSD-3-Clause.txt", "LicenseId": "BSD-3-Clause", "Name": "LicensesDir", + "PURL": [], "PackageComment": "", "Path": "%{PWD}/licenses-dir", "QDocModule": "qtest", @@ -66,6 +78,7 @@ "Version": "" }, { + "CPE": ["cpe:2.3:a:qt:qtbase:test-version:*:*:*:*:*:*:*"], "Copyright": "Copyright", "CopyrightFile": "", "Description": "", @@ -77,6 +90,7 @@ "LicenseFile": "", "LicenseId": "", "Name": "Minimal", + "PURL": ["pkg:github/qt/qtbase@test-version"], "PackageComment": "", "Path": "%{PWD}/minimal", "QDocModule": "qtest", @@ -88,6 +102,7 @@ "Version": "" }, { + "CPE": [], "Copyright": "", "CopyrightFile": "%{PWD}/variants/COPYRIGHT.txt", "Description": "", @@ -102,6 +117,7 @@ ], "LicenseId": "", "Name": "Variants Test", + "PURL": [], "PackageComment": "", "Path": "%{PWD}/variants", "QDocModule": "qtest", diff --git a/tests/auto/qtattributionsscanner/testdata/good/licenses-dir/expected.json b/tests/auto/qtattributionsscanner/testdata/good/licenses-dir/expected.json index ab5c31309..9625f38d5 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/licenses-dir/expected.json +++ b/tests/auto/qtattributionsscanner/testdata/good/licenses-dir/expected.json @@ -1,5 +1,6 @@ [ { + "CPE": [], "Copyright": "Copyright", "CopyrightFile": "", "Description": "", @@ -11,6 +12,7 @@ "LicenseFile": "%{LICENSES_DIR}/BSD-3-Clause.txt", "LicenseId": "BSD-3-Clause", "Name": "LicensesDir", + "PURL": [], "PackageComment": "", "Path": "%{PWD}", "QDocModule": "qtest", diff --git a/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json b/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json index 546830d3f..c6086231c 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json +++ b/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json @@ -1,5 +1,6 @@ [ { + "CPE": ["cpe:2.3:a:qt:qtbase:test-version:*:*:*:*:*:*:*"], "Copyright": "Copyright", "CopyrightFile": "", "Description": "", @@ -11,6 +12,7 @@ "LicenseFile": "", "LicenseId": "", "Name": "Minimal", + "PURL": ["pkg:github/qt/qtbase@test-version"], "PackageComment": "", "Path": "%{PWD}", "QDocModule": "qtest", diff --git a/tests/auto/qtattributionsscanner/testdata/good/minimal/qt_attribution_test.json b/tests/auto/qtattributionsscanner/testdata/good/minimal/qt_attribution_test.json index 2dc53f852..eb2649db5 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/minimal/qt_attribution_test.json +++ b/tests/auto/qtattributionsscanner/testdata/good/minimal/qt_attribution_test.json @@ -5,5 +5,7 @@ "QDocModule": "qtest", "QtUsage": "Usage", "License": "License", - "Copyright": "Copyright" + "Copyright": "Copyright", + "CPE": "cpe:2.3:a:qt:qtbase:test-version:*:*:*:*:*:*:*", + "PURL": "pkg:github/qt/qtbase@test-version" } diff --git a/tests/auto/qtattributionsscanner/testdata/good/variants/expected.json b/tests/auto/qtattributionsscanner/testdata/good/variants/expected.json index f69f55726..e69b3e96e 100644 --- a/tests/auto/qtattributionsscanner/testdata/good/variants/expected.json +++ b/tests/auto/qtattributionsscanner/testdata/good/variants/expected.json @@ -1,5 +1,6 @@ [ { + "CPE": [], "Copyright": "", "CopyrightFile": "%{PWD}/COPYRIGHT.txt", "Description": "", @@ -14,6 +15,7 @@ ], "LicenseId": "", "Name": "Variants Test", + "PURL": [], "PackageComment": "", "Path": "%{PWD}", "QDocModule": "qtest", |
