From 3e082109980df5b6f10d7a91fee3e819bf2b976b Mon Sep 17 00:00:00 2001 From: Christiaan Janssen Date: Mon, 15 Feb 2021 16:20:38 +0100 Subject: [PATCH] McuSupport: Detect dependency versions when building MCU kits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTCREATORBUG-25261 Change-Id: I79d24aa2e26a3f647bd2251c0407c7d09eea73b3 Reviewed-by: hjk Reviewed-by: Dawid Śliwa Reviewed-by: Alessandro Portale --- src/plugins/mcusupport/CMakeLists.txt | 1 + src/plugins/mcusupport/mcusupport.pro | 6 +- src/plugins/mcusupport/mcusupport.qbs | 2 + src/plugins/mcusupport/mcusupportoptions.cpp | 81 ++++++++--- src/plugins/mcusupport/mcusupportoptions.h | 17 ++- .../mcusupport/mcusupportoptionspage.cpp | 2 +- src/plugins/mcusupport/mcusupportsdk.cpp | 69 ++++++++- .../mcusupport/mcusupportversiondetection.cpp | 135 ++++++++++++++++++ .../mcusupport/mcusupportversiondetection.h | 84 +++++++++++ 9 files changed, 365 insertions(+), 32 deletions(-) create mode 100644 src/plugins/mcusupport/mcusupportversiondetection.cpp create mode 100644 src/plugins/mcusupport/mcusupportversiondetection.h diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index 38630ca9b1d..3729ac7256c 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -11,4 +11,5 @@ add_qtc_plugin(McuSupport mcusupportplugin.cpp mcusupportplugin.h mcusupportsdk.cpp mcusupportsdk.h mcusupportrunconfiguration.cpp mcusupportrunconfiguration.h + mcusupportversiondetection.cpp mcusupportversiondetection.h ) diff --git a/src/plugins/mcusupport/mcusupport.pro b/src/plugins/mcusupport/mcusupport.pro index b6564b27b5c..f2d7a1c501c 100644 --- a/src/plugins/mcusupport/mcusupport.pro +++ b/src/plugins/mcusupport/mcusupport.pro @@ -12,7 +12,8 @@ HEADERS += \ mcusupportoptionspage.h \ mcusupportplugin.h \ mcusupportsdk.h \ - mcusupportrunconfiguration.h + mcusupportrunconfiguration.h \ + mcusupportversiondetection.h SOURCES += \ mcusupportdevice.cpp \ @@ -20,7 +21,8 @@ SOURCES += \ mcusupportoptionspage.cpp \ mcusupportplugin.cpp \ mcusupportsdk.cpp \ - mcusupportrunconfiguration.cpp + mcusupportrunconfiguration.cpp \ + mcusupportversiondetection.cpp RESOURCES += \ mcusupport.qrc diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index f194ea1b2ad..8818514b2b5 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -30,5 +30,7 @@ QtcPlugin { "mcusupportsdk.h", "mcusupportrunconfiguration.cpp", "mcusupportrunconfiguration.h", + "mcusupportversiondetection.cpp", + "mcusupportversiondetection.h", ] } diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp index 56bcf57165a..a40158b2bec 100644 --- a/src/plugins/mcusupport/mcusupportoptions.cpp +++ b/src/plugins/mcusupport/mcusupportoptions.cpp @@ -96,11 +96,13 @@ static bool kitNeedsQtVersion() } McuPackage::McuPackage(const QString &label, const QString &defaultPath, - const QString &detectionPath, const QString &settingsKey) + const QString &detectionPath, const QString &settingsKey, + const McuPackageVersionDetector *versionDetector) : m_label(label) , m_defaultPath(packagePathFromSettings(settingsKey, QSettings::SystemScope, defaultPath)) , m_detectionPath(detectionPath) , m_settingsKey(settingsKey) + , m_versionDetector(versionDetector) { m_path = packagePathFromSettings(settingsKey, QSettings::UserScope, m_defaultPath); m_automaticKitCreation = automaticKitCreationFromSettings(QSettings::UserScope); @@ -182,6 +184,11 @@ McuPackage::Status McuPackage::status() const return m_status; } +bool McuPackage::validStatus() const +{ + return m_status == McuPackage::ValidPackage || m_status == McuPackage::ValidPackageMismatchedVersion; +} + void McuPackage::setDownloadUrl(const QString &url) { m_downloadUrl = url; @@ -227,6 +234,11 @@ void McuPackage::setRelativePathModifier(const QString &path) m_relativePathModifier = path; } +void McuPackage::setVersions(const QVector &versions) +{ + m_versions = versions; +} + bool McuPackage::automaticKitCreationEnabled() const { return m_automaticKitCreation; @@ -249,34 +261,59 @@ void McuPackage::updateStatus() bool validPath = !m_path.isEmpty() && FilePath::fromString(m_path).exists(); const FilePath detectionPath = FilePath::fromString(basePath() + "/" + m_detectionPath); const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists(); + m_detectedVersion = validPath && validPackage && m_versionDetector ? m_versionDetector->parseVersion(basePath()) : QString(); + const bool validVersion = m_detectedVersion.isEmpty() || + m_versions.isEmpty() || m_versions.contains(m_detectedVersion); - m_status = validPath ? (validPackage ? ValidPackage : ValidPathInvalidPackage) : - m_path.isEmpty() ? EmptyPath : InvalidPath; + m_status = validPath ? + ( validPackage ? + (validVersion ? ValidPackage : ValidPackageMismatchedVersion) + : ValidPathInvalidPackage ) + : m_path.isEmpty() ? EmptyPath : InvalidPath; emit statusChanged(); } void McuPackage::updateStatusUi() { - m_infoLabel->setType(m_status == ValidPackage ? InfoLabel::Ok : InfoLabel::NotOk); + m_infoLabel->setType(validStatus() ? InfoLabel::Ok : InfoLabel::NotOk); m_infoLabel->setText(statusText()); } QString McuPackage::statusText() const { const QString displayPackagePath = FilePath::fromString(m_path).toUserOutput(); - const QString displayDetectionPath = FilePath::fromString(m_detectionPath).toUserOutput(); + const QString displayVersions = m_versions.isEmpty() ? "" : + QString(" (%1)").arg(QStringList(m_versions.toList()).join(" / ")); + const QString displayRequiredPath = QString("%1 %2").arg( + FilePath::fromString(m_detectionPath).toUserOutput(), + displayVersions); + const QString displayDetectedPath = QString("%1 %2").arg( + FilePath::fromString(m_detectionPath).toUserOutput(), + m_detectedVersion); + QString response; switch (m_status) { case ValidPackage: response = m_detectionPath.isEmpty() - ? tr("Path %1 exists.").arg(displayPackagePath) + ? ( m_detectedVersion.isEmpty() + ? tr("Path %1 exists.").arg(displayPackagePath) + : tr("Path %1 exists. Version %2 was found.") + .arg(displayPackagePath, m_detectedVersion) ) : tr("Path %1 is valid, %2 was found.") - .arg(displayPackagePath, displayDetectionPath); + .arg(displayPackagePath, displayDetectedPath); break; + case ValidPackageMismatchedVersion: { + const QString versionWarning = m_versions.size() == 1 ? + tr("version %1 is recommended").arg(m_versions.first()) : + tr("versions %1 are recommended").arg(displayVersions); + response = tr("Path %1 is valid, %2 was found, but %3.") + .arg(displayPackagePath, displayDetectedPath, versionWarning); + break; + } case ValidPathInvalidPackage: response = tr("Path %1 exists, but does not contain %2.") - .arg(displayPackagePath, displayDetectionPath); + .arg(displayPackagePath, displayRequiredPath); break; case InvalidPath: response = tr("Path %1 does not exist.").arg(displayPackagePath); @@ -285,7 +322,7 @@ QString McuPackage::statusText() const response = m_detectionPath.isEmpty() ? tr("Path is empty.") : tr("Path is empty, %1 not found.") - .arg(displayDetectionPath); + .arg(displayRequiredPath); break; } return response; @@ -295,8 +332,9 @@ McuToolChainPackage::McuToolChainPackage(const QString &label, const QString &defaultPath, const QString &detectionPath, const QString &settingsKey, - McuToolChainPackage::Type type) - : McuPackage(label, defaultPath, detectionPath, settingsKey) + McuToolChainPackage::Type type, + const McuPackageVersionDetector *versionDetector) + : McuPackage(label, defaultPath, detectionPath, settingsKey, versionDetector) , m_type(type) { } @@ -484,9 +522,9 @@ McuTarget::Platform McuTarget::platform() const bool McuTarget::isValid() const { - return !Utils::anyOf(packages(), [](McuPackage *package) { + return Utils::allOf(packages(), [](McuPackage *package) { package->updateStatus(); - return package->status() != McuPackage::ValidPackage; + return package->validStatus(); }); } @@ -494,12 +532,18 @@ void McuTarget::printPackageProblems() const { for (auto package: packages()) { package->updateStatus(); - if (package->status() != McuPackage::ValidPackage) - printMessage(QString("Error creating kit for target %1, package %2: %3").arg( + if (!package->validStatus()) + printMessage(tr("Error creating kit for target %1, package %2: %3").arg( McuSupportOptions::kitName(this), package->label(), package->statusText()), true); + if (package->status() == McuPackage::ValidPackageMismatchedVersion) + printMessage(tr("Warning creating kit for target %1, package %2: %3").arg( + McuSupportOptions::kitName(this), + package->label(), + package->statusText()), + false); } } @@ -598,7 +642,7 @@ void McuSupportOptions::setQulDir(const FilePath &dir) { deletePackagesAndTargets(); qtForMCUsSdkPackage->updateStatus(); - if (qtForMCUsSdkPackage->status() == McuPackage::Status::ValidPackage) + if (qtForMCUsSdkPackage->validStatus()) Sdk::targetsAndPackages(dir, &packages, &mcuTargets); for (auto package : qAsConst(packages)) connect(package, &McuPackage::changed, this, &McuSupportOptions::changed); @@ -858,7 +902,7 @@ void McuSupportOptions::createAutomaticKits() const auto createKits = [qtForMCUsPackage]() { if (qtForMCUsPackage->automaticKitCreationEnabled()) { qtForMCUsPackage->updateStatus(); - if (qtForMCUsPackage->status() != McuPackage::ValidPackage) { + if (!qtForMCUsPackage->validStatus()) { switch (qtForMCUsPackage->status()) { case McuPackage::ValidPathInvalidPackage: { const QString displayPath = FilePath::fromString(qtForMCUsPackage->detectionPath()) @@ -900,8 +944,7 @@ void McuSupportOptions::createAutomaticKits() if (existingKits(target).isEmpty()) { if (target->isValid()) newKit(target, qtForMCUsPackage); - else - target->printPackageProblems(); + target->printPackageProblems(); } qDeleteAll(packages); diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h index 57772bafb22..33dc340b571 100644 --- a/src/plugins/mcusupport/mcusupportoptions.h +++ b/src/plugins/mcusupport/mcusupportoptions.h @@ -25,6 +25,8 @@ #pragma once +#include "mcusupportversiondetection.h" + #include #include @@ -58,11 +60,13 @@ public: EmptyPath, InvalidPath, ValidPathInvalidPackage, + ValidPackageMismatchedVersion, ValidPackage }; - McuPackage(const QString &label, const QString &defaultPath, const QString &detectionPath, - const QString &settingsKey); + McuPackage(const QString &label, const QString &defaultPath, + const QString &detectionPath, const QString &settingsKey, + const McuPackageVersionDetector *versionDetector = nullptr); virtual ~McuPackage() = default; QString basePath() const; @@ -74,6 +78,7 @@ public: void updateStatus(); Status status() const; + bool validStatus() const; void setDownloadUrl(const QString &url); void setEnvironmentVariableName(const QString &name); void setAddToPath(bool addToPath); @@ -81,6 +86,7 @@ public: void writeGeneralSettings() const; void writeToSettings() const; void setRelativePathModifier(const QString &path); + void setVersions(const QVector &versions); bool automaticKitCreationEnabled() const; void setAutomaticKitCreationEnabled(const bool enabled); @@ -105,9 +111,12 @@ private: const QString m_defaultPath; const QString m_detectionPath; const QString m_settingsKey; + const McuPackageVersionDetector *m_versionDetector; QString m_path; QString m_relativePathModifier; // relative path to m_path to be returned by path() + QString m_detectedVersion; + QVector m_versions; QString m_downloadUrl; QString m_environmentVariableName; bool m_addToPath = false; @@ -133,7 +142,9 @@ public: const QString &defaultPath, const QString &detectionPath, const QString &settingsKey, - Type type); + Type type, + const McuPackageVersionDetector *versionDetector = nullptr + ); Type type() const; bool isDesktopToolchain() const; diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index b4e6e2a6b07..4d577ebdd38 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -185,7 +185,7 @@ void McuSupportOptionsWidget::updateStatus() { m_qtForMCUsSdkGroupBox->setVisible(cMakeAvailable); const bool valid = cMakeAvailable && - m_options.qtForMCUsSdkPackage->status() == McuPackage::ValidPackage; + m_options.qtForMCUsSdkPackage->validStatus(); const bool ready = valid && mcuTarget; m_mcuTargetsGroupBox->setVisible(ready); m_packagesGroupBox->setVisible(ready && !mcuTarget->packages().isEmpty()); diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index 75a2d115336..db7a83dcaa2 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -26,6 +26,7 @@ #include "mcusupportconstants.h" #include "mcusupportoptions.h" #include "mcusupportsdk.h" +#include "mcusupportversiondetection.h" #include #include @@ -107,12 +108,20 @@ static McuToolChainPackage *createArmGccPackage() if (defaultPath.isEmpty()) defaultPath = QDir::homePath(); + const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/arm-none-eabi-g++"); + const auto versionDetector = new McuPackageExecutableVersionDetector( + detectionPath, + { "--version" }, + "\\b(\\d+\\.\\d+\\.\\d+)\\b" + ); + auto result = new McuToolChainPackage( McuPackage::tr("GNU Arm Embedded Toolchain"), defaultPath, - Utils::HostOsInfo::withExecutableSuffix("bin/arm-none-eabi-g++"), + detectionPath, "GNUArmEmbeddedToolchain", - McuToolChainPackage::TypeArmGcc); + McuToolChainPackage::TypeArmGcc, + versionDetector); result->setEnvironmentVariableName(envVar); return result; } @@ -124,12 +133,19 @@ static McuToolChainPackage *createGhsToolchainPackage() const QString defaultPath = qEnvironmentVariableIsSet(envVar) ? qEnvironmentVariable(envVar) : QDir::homePath(); + const auto versionDetector = new McuPackageExecutableVersionDetector( + Utils::HostOsInfo::withExecutableSuffix("as850"), + {"-V"}, + "\\bv(\\d+\\.\\d+\\.\\d+)\\b" + ); + auto result = new McuToolChainPackage( "Green Hills Compiler", defaultPath, Utils::HostOsInfo::withExecutableSuffix("ccv850"), "GHSToolchain", - McuToolChainPackage::TypeGHS); + McuToolChainPackage::TypeGHS, + versionDetector); result->setEnvironmentVariableName(envVar); return result; } @@ -154,12 +170,20 @@ static McuToolChainPackage *createIarToolChainPackage() defaultPath = QDir::homePath(); } + const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/iccarm"); + const auto versionDetector = new McuPackageExecutableVersionDetector( + detectionPath, + {"--version"}, + "\\bV(\\d+\\.\\d+\\.\\d+)\\.\\d+\\b" + ); + auto result = new McuToolChainPackage( "IAR ARM Compiler", defaultPath, - Utils::HostOsInfo::withExecutableSuffix("bin/iccarm"), + detectionPath, "IARToolchain", - McuToolChainPackage::TypeIAR); + McuToolChainPackage::TypeIAR, + versionDetector); result->setEnvironmentVariableName(envVar); return result; } @@ -257,14 +281,30 @@ struct McuTargetDescription QString platformVendor; QVector colorDepths; QString toolchainId; + QVector toolchainVersions; QString boardSdkEnvVar; QString boardSdkName; QString boardSdkDefaultPath; + QVector boardSdkVersions; QString freeRTOSEnvVar; QString freeRTOSBoardSdkSubDir; TargetType type; }; +static McuPackageVersionDetector* generatePackageVersionDetector(QString envVar) +{ + if (envVar.startsWith("EVK")) + return new McuPackageXmlVersionDetector("*_manifest_*.xml", "ksdk", "version", ".*"); + + if (envVar.startsWith("STM32")) + return new McuPackageXmlVersionDetector("package.xml", "PackDescription", "Release", "\\b(\\d+\\.\\d+\\.\\d+)\\b"); + + if (envVar.startsWith("RGL")) + return new McuPackageDirectoryVersionDetector("rgl_*_obj_*", "\\d+\\.\\d+\\.\\w+", false); + + return nullptr; +} + static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc) { const auto generateSdkName = [](const QString& envVar) { @@ -291,11 +331,14 @@ static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc) return QDir::homePath(); }(); + const auto versionDetector = generatePackageVersionDetector(desc.boardSdkEnvVar); + auto result = new McuPackage( sdkName, defaultPath, {}, - desc.boardSdkEnvVar); + desc.boardSdkEnvVar, + versionDetector); result->setEnvironmentVariableName(desc.boardSdkEnvVar); return result; } @@ -429,7 +472,10 @@ protected: QVector mcuTargets; McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchainId); - if (!tcPkg) + if (tcPkg) { + if (QVersionNumber::fromString(desc.qulVersion) >= QVersionNumber({1,8})) + tcPkg->setVersions(desc.toolchainVersions); + } else tcPkg = createUnsupportedToolChainPackage(); for (int colorDepth : desc.colorDepths) { QVector required3rdPartyPkgs; @@ -451,6 +497,7 @@ protected: boardSdkPkgs.insert(desc.boardSdkEnvVar, boardSdkPkg); } auto boardSdkPkg = boardSdkPkgs.value(desc.boardSdkEnvVar); + boardSdkPkg->setVersions(desc.boardSdkVersions); boardSdkDefaultPath = boardSdkPkg->defaultPath(); required3rdPartyPkgs.append(boardSdkPkg); } @@ -537,6 +584,12 @@ static McuTargetDescription parseDescriptionJson(const QByteArray &data) const QVariantList colorDepths = target.value("colorDepths").toArray().toVariantList(); const auto colorDepthsVector = Utils::transform >( colorDepths, [&](const QVariant &colorDepth) { return colorDepth.toInt(); }); + const QVariantList toolchainVersions = toolchain.value("versions").toArray().toVariantList(); + const auto toolchainVersionsVector = Utils::transform >( + toolchainVersions, [&](const QVariant &version) { return version.toString(); }); + const QVariantList boardSdkVersions = boardSdk.value("versions").toArray().toVariantList(); + const auto boardSdkVersionsVector = Utils::transform >( + boardSdkVersions, [&](const QVariant &version) { return version.toString(); }); return { target.value("qulVersion").toString(), @@ -545,9 +598,11 @@ static McuTargetDescription parseDescriptionJson(const QByteArray &data) target.value("platformVendor").toString(), colorDepthsVector, toolchain.value("id").toString(), + toolchainVersionsVector, boardSdk.value("envVar").toString(), boardSdk.value("name").toString(), boardSdk.value("defaultPath").toString(), + boardSdkVersionsVector, freeRTOS.value("envVar").toString(), freeRTOS.value("boardSdkSubDir").toString(), boardSdk.empty() ? McuTargetDescription::TargetType::Desktop : McuTargetDescription::TargetType::MCU diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp new file mode 100644 index 00000000000..e1b10d30ac4 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#include "mcusupportversiondetection.h" + +#include +#include +#include +#include + +namespace McuSupport { +namespace Internal { + +McuPackageVersionDetector::McuPackageVersionDetector() +{ +} + +McuPackageExecutableVersionDetector::McuPackageExecutableVersionDetector( + const QString &detectionPath, + const QStringList &detectionArgs, + const QString &detectionRegExp) + : McuPackageVersionDetector() + , m_detectionPath(detectionPath) + , m_detectionArgs(detectionArgs) + , m_detectionRegExp(detectionRegExp) +{ +} + +QString McuPackageExecutableVersionDetector::parseVersion(const QString &packagePath) const +{ + if (m_detectionPath.isEmpty() || m_detectionRegExp.isEmpty()) + return QString(); + + QString binaryPath = QDir::toNativeSeparators(packagePath + "/" + m_detectionPath); + if (!Utils::FilePath::fromString(binaryPath).exists()) + return QString(); + + const QRegularExpression regExp(m_detectionRegExp); + + const int execTimeout = 3000; // usually runs below 1s, but we want to be on the safe side + QProcess binaryProcess; + binaryProcess.start(binaryPath, m_detectionArgs); + if (!binaryProcess.waitForStarted()) + return QString(); + binaryProcess.waitForFinished(execTimeout); + if (binaryProcess.exitCode() == QProcess::ExitStatus::NormalExit) { + const QString processOutput = QString::fromUtf8( + binaryProcess.readAllStandardOutput().append( + binaryProcess.readAllStandardError())); + const QRegularExpressionMatch match = regExp.match(processOutput); + if (match.hasMatch()) + return match.captured(regExp.captureCount()); + } + + // Fail gracefully: return empty string if execution failed or regexp did not match + return QString(); +} + +McuPackageXmlVersionDetector::McuPackageXmlVersionDetector(const QString &filePattern, + const QString &versionElement, + const QString &versionAttribute, + const QString &versionRegExp) + : m_filePattern(filePattern), + m_versionElement(versionElement), + m_versionAttribute(versionAttribute), + m_versionRegExp(versionRegExp) +{ +} + +QString McuPackageXmlVersionDetector::parseVersion(const QString &packagePath) const +{ + const QRegularExpression regExp(m_versionRegExp); + const auto files = QDir(packagePath, m_filePattern).entryInfoList(); + for (const auto &xmlFile: files) { + QFile sdkXmlFile = QFile(xmlFile.absoluteFilePath()); + sdkXmlFile.open(QFile::OpenModeFlag::ReadOnly); + QXmlStreamReader xmlReader(&sdkXmlFile); + while (xmlReader.readNext()) { + if (xmlReader.name() == m_versionElement) { + const QString versionString = xmlReader.attributes().value(m_versionAttribute).toString(); + const QRegularExpressionMatch match = regExp.match(versionString); + return match.hasMatch() ? match.captured(regExp.captureCount()) : versionString; + } + } + } + + return QString(); +} + +McuPackageDirectoryVersionDetector::McuPackageDirectoryVersionDetector(const QString &filePattern, + const QString &versionRegExp, + const bool isFile) + : m_filePattern(filePattern), + m_versionRegExp(versionRegExp), + m_isFile(isFile) +{ +} + +QString McuPackageDirectoryVersionDetector::parseVersion(const QString &packagePath) const +{ + const auto files = QDir(packagePath, m_filePattern) + .entryInfoList(m_isFile ? QDir::Filter::Files : QDir::Filter::Dirs); + for (const auto &entry: files) { + const QRegularExpression regExp(m_versionRegExp); + const QRegularExpressionMatch match = regExp.match(entry.fileName()); + if (match.hasMatch()) + return match.captured(regExp.captureCount()); + } + return QString(); +} + +} // Internal +} // McuSupport diff --git a/src/plugins/mcusupport/mcusupportversiondetection.h b/src/plugins/mcusupport/mcusupportversiondetection.h new file mode 100644 index 00000000000..ecf60296908 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportversiondetection.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#ifndef MCUSUPPORTVERSIONDETECTION_H +#define MCUSUPPORTVERSIONDETECTION_H + +#include + +namespace McuSupport { +namespace Internal { + +class McuPackageVersionDetector : public QObject { + Q_OBJECT +public: + McuPackageVersionDetector(); + virtual ~McuPackageVersionDetector() = default; + virtual QString parseVersion(const QString &packagePath) const = 0; +}; + +// Get version from the output of an executable +class McuPackageExecutableVersionDetector : public McuPackageVersionDetector { +public: + McuPackageExecutableVersionDetector(const QString &detectionPath, + const QStringList &detectionArgs, + const QString &detectionRegExp); + virtual QString parseVersion(const QString &packagePath) const; +private: + const QString m_detectionPath; + const QStringList m_detectionArgs; + const QString m_detectionRegExp; +}; + +// Get version through parsing an XML file +class McuPackageXmlVersionDetector : public McuPackageVersionDetector { +public: + McuPackageXmlVersionDetector(const QString &filePattern, + const QString &elementName, + const QString &versionAttribute, + const QString &versionRegExp); + QString parseVersion(const QString &packagePath) const; +private: + const QString m_filePattern; + const QString m_versionElement; + const QString m_versionAttribute; + const QString m_versionRegExp; +}; + +// Get version from the filename of a given file/dir in the package directory +class McuPackageDirectoryVersionDetector : public McuPackageVersionDetector { +public: + McuPackageDirectoryVersionDetector(const QString &filePattern, const QString &versionRegExp, const bool isFile); + QString parseVersion(const QString &packagePath) const; +private: + const QString m_filePattern; + const QString m_versionRegExp; + const bool m_isFile; +}; + +} // Internal +} // McuSupport + +#endif // MCUSUPPORTVERSIONDETECTION_H