diff --git a/src/plugins/mcusupport/mcuabstractpackage.h b/src/plugins/mcusupport/mcuabstractpackage.h index c6131919e54..98d69144aa8 100644 --- a/src/plugins/mcusupport/mcuabstractpackage.h +++ b/src/plugins/mcusupport/mcuabstractpackage.h @@ -7,7 +7,10 @@ #include -namespace Utils { class FilePath; } +namespace Utils { +class FilePath; +using FilePaths = QList; +} // namespace Utils namespace McuSupport::Internal { @@ -38,7 +41,8 @@ public: virtual Utils::FilePath path() const = 0; virtual void setPath(const Utils::FilePath &) = 0; virtual Utils::FilePath defaultPath() const = 0; - virtual Utils::FilePath detectionPath() const = 0; + virtual Utils::FilePaths detectionPaths() const = 0; + virtual QString detectionPathsToString() const { return {}; }; virtual Utils::Key settingsKey() const = 0; virtual void updateStatus() = 0; diff --git a/src/plugins/mcusupport/mcuhelpers.cpp b/src/plugins/mcusupport/mcuhelpers.cpp index 8df68eff826..17c687b4074 100644 --- a/src/plugins/mcusupport/mcuhelpers.cpp +++ b/src/plugins/mcusupport/mcuhelpers.cpp @@ -26,4 +26,23 @@ QString removeRtosSuffix(const QString &environmentVariable) return result.replace(freeRtosSuffix, QString{}); } +// Get all the paths matching a path with regular expression or only the given path +// if it exists +std::optional firstMatchingPath(const Utils::FilePath &path) +{ + if (path.exists()) + return path; + + if (!path.contains("*")) + return std::nullopt; + + // fallback to wildcards + Utils::FilePath parentDir = path.parentDir(); + auto entries = parentDir.dirEntries( + Utils::FileFilter({path.fileName()}, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); + if (entries.empty()) + return std::nullopt; + return entries.first(); +} + } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcuhelpers.h b/src/plugins/mcusupport/mcuhelpers.h index 3b5eb2736f9..78779e3befb 100644 --- a/src/plugins/mcusupport/mcuhelpers.h +++ b/src/plugins/mcusupport/mcuhelpers.h @@ -5,6 +5,8 @@ #include "mcutarget.h" +#include + #include namespace McuSupport::Internal { @@ -13,6 +15,7 @@ struct McuTargetDescription; McuTarget::OS deduceOperatingSystem(const McuTargetDescription &); QString removeRtosSuffix(const QString &environmentVariable); +std::optional firstMatchingPath(const Utils::FilePath &path); template class asKeyValueRange diff --git a/src/plugins/mcusupport/mcukitmanager.cpp b/src/plugins/mcusupport/mcukitmanager.cpp index f1f890271a0..9424cde2e10 100644 --- a/src/plugins/mcusupport/mcukitmanager.cpp +++ b/src/plugins/mcusupport/mcukitmanager.cpp @@ -178,8 +178,10 @@ public: auto processPackage = [&dependencies](const McuPackagePtr &package) { const auto cmakeVariableName = package->cmakeVariableName(); - if (!cmakeVariableName.isEmpty()) - dependencies.append({cmakeVariableName, package->detectionPath().toUserOutput()}); + if (!cmakeVariableName.isEmpty() && !package->detectionPaths().empty()) + // Relying only on the first detection paths as a dependency as dependencies is not a multi-map + dependencies.append( + {cmakeVariableName, package->detectionPaths().constFirst().toUserOutput()}); }; for (const auto &package : mcuTarget->packages()) processPackage(package); @@ -502,10 +504,9 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler) if (!qtForMCUsPackage->isValidStatus()) { switch (qtForMCUsPackage->status()) { case McuAbstractPackage::Status::ValidPathInvalidPackage: { - const QString message - = Tr::tr("Path %1 exists, but does not contain %2.") - .arg(qtForMCUsPackage->path().toUserOutput(), - qtForMCUsPackage->detectionPath().toUserOutput()); + const QString message = Tr::tr("Path %1 exists, but does not contain %2.") + .arg(qtForMCUsPackage->path().toUserOutput(), + qtForMCUsPackage->detectionPathsToString()); autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message}); printMessage(message, true); break; @@ -520,8 +521,9 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler) break; } case McuAbstractPackage::Status::EmptyPath: { - const QString message = Tr::tr("Missing %1. Add the path in Edit > Preferences > Devices > MCU.") - .arg(qtForMCUsPackage->detectionPath().toUserOutput()); + const QString message + = Tr::tr("Missing %1. Add the path in Edit > Preferences > Devices > MCU.") + .arg(qtForMCUsPackage->detectionPathsToString()); autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message}); printMessage(message, true); return; diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index a5439e9da35..c6235a3d4e6 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -2,9 +2,10 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "mcupackage.h" +#include "mcuhelpers.h" +#include "mcusupporttr.h" #include "mcusupportversiondetection.h" #include "settingshandler.h" -#include "mcusupporttr.h" #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include @@ -32,7 +34,7 @@ namespace McuSupport::Internal { McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, const QString &label, const FilePath &defaultPath, - const FilePath &detectionPath, + const Utils::FilePaths &detectionPaths, const Key &settingsKey, const QString &cmakeVarName, const QString &envVarName, @@ -44,7 +46,7 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, : settingsHandler(settingsHandler) , m_label(label) , m_defaultPath(settingsHandler->getPath(settingsKey, QSettings::SystemScope, defaultPath)) - , m_detectionPath(detectionPath) + , m_detectionPaths(detectionPaths) , m_settingsKey(settingsKey) , m_versionDetector(versionDetector) , m_versions(versions) @@ -110,9 +112,16 @@ FilePath McuPackage::defaultPath() const return m_defaultPath.cleanPath(); } -FilePath McuPackage::detectionPath() const +QList McuPackage::detectionPaths() const { - return m_detectionPath; + return m_detectionPaths; +} + +QString McuPackage::detectionPathsToString() const +{ + return joinStrings(transform(m_detectionPaths, + [](const FilePath &filePath) { return filePath.toUserOutput(); }), + '|'); } void McuPackage::setPath(const FilePath &newPath) @@ -128,8 +137,20 @@ void McuPackage::setPath(const FilePath &newPath) void McuPackage::updateStatus() { bool validPath = !m_path.isEmpty() && m_path.exists(); - const FilePath detectionPath = basePath() / m_detectionPath.path(); - const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists(); + bool validPackage = false; + if (m_detectionPaths.empty()) { + validPackage = true; + } else { + for (const FilePath &detectionPath : m_detectionPaths) { + std::optional alternativeDetectionPath = firstMatchingPath( + basePath() / detectionPath.path()); + if (!alternativeDetectionPath) + continue; + validPackage = true; + m_usedDetectionPath = detectionPath; + break; + } + } m_detectedVersion = validPath && validPackage && m_versionDetector ? m_versionDetector->parseVersion(basePath()) : QString(); @@ -185,19 +206,20 @@ QString McuPackage::statusText() const { const QString displayPackagePath = m_path.toUserOutput(); const QString displayVersions = m_versions.join(Tr::tr(" or ")); - const QString outDetectionPath = m_detectionPath.toUserOutput(); + const QStringList detectionPathsAsString = Utils::transform(m_detectionPaths, &FilePath::toUserOutput); + const QString outDetectionPath = joinStrings(detectionPathsAsString, '|'); const QString displayRequiredPath = m_versions.empty() ? outDetectionPath : QString("%1 %2").arg(outDetectionPath, displayVersions); const QString displayDetectedPath = m_versions.empty() - ? outDetectionPath - : QString("%1 %2").arg(outDetectionPath, + ? m_usedDetectionPath.toString() + : QString("%1 %2").arg(m_usedDetectionPath.toString(), m_detectedVersion); QString response; switch (m_status) { case Status::ValidPackage: - response = m_detectionPath.isEmpty() + response = m_detectionPaths.isEmpty() ? (m_detectedVersion.isEmpty() ? Tr::tr("Path %1 exists.").arg(displayPackagePath) : Tr::tr("Path %1 exists. Version %2 was found.") @@ -222,7 +244,7 @@ QString McuPackage::statusText() const response = Tr::tr("Path %1 does not exist.").arg(displayPackagePath); break; case Status::EmptyPath: - response = m_detectionPath.isEmpty() + response = m_detectionPaths.isEmpty() ? Tr::tr("Path is empty.") : Tr::tr("Path is empty, %1 not found.").arg(displayRequiredPath); break; @@ -333,7 +355,7 @@ const QMap McuPackage::packageLabelTranslations { McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler, const QString &label, const FilePath &defaultPath, - const FilePath &detectionPath, + const QList &detectionPaths, const Key &settingsKey, McuToolChainPackage::ToolChainType type, const QStringList &versions, @@ -343,7 +365,7 @@ McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHan : McuPackage(settingsHandler, label, defaultPath, - detectionPath, + detectionPaths, settingsKey, cmakeVarName, envVarName, diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index eab3b34c91f..88171bc93bc 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -29,17 +29,18 @@ class McuPackage : public McuAbstractPackage public: McuPackage(const SettingsHandler::Ptr &settingsHandler, - const QString &label, - const Utils::FilePath &defaultPath, - const Utils::FilePath &detectionPath, - const Utils::Key &settingsKey, - const QString &cmakeVarName, - const QString &envVarName, - const QStringList &versions = {}, - const QString &downloadUrl = {}, - const McuPackageVersionDetector *versionDetector = nullptr, - const bool addToPath = false, - const Utils::PathChooser::Kind &valueType = Utils::PathChooser::Kind::ExistingDirectory); + const QString &label, + const Utils::FilePath &defaultPath, + const Utils::FilePaths &detectionPaths, + const Utils::Key &settingsKey, + const QString &cmakeVarName, + const QString &envVarName, + const QStringList &versions = {}, + const QString &downloadUrl = {}, + const McuPackageVersionDetector *versionDetector = nullptr, + const bool addToPath = false, + const Utils::PathChooser::Kind &valueType + = Utils::PathChooser::Kind::ExistingDirectory); ~McuPackage() override = default; @@ -54,7 +55,8 @@ public: Utils::FilePath basePath() const override; Utils::FilePath path() const override; Utils::FilePath defaultPath() const override; - Utils::FilePath detectionPath() const override; + Utils::FilePaths detectionPaths() const override; + QString detectionPathsToString() const override; Utils::Key settingsKey() const final; void updateStatus() override; @@ -81,7 +83,8 @@ private: const QString m_label; Utils::FilePath m_defaultPath; - const Utils::FilePath m_detectionPath; + const Utils::FilePaths m_detectionPaths; + Utils::FilePath m_usedDetectionPath; const Utils::Key m_settingsKey; QScopedPointer m_versionDetector; @@ -106,7 +109,7 @@ public: McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler, const QString &label, const Utils::FilePath &defaultPath, - const Utils::FilePath &detectionPath, + const Utils::FilePaths &detectionPaths, const Utils::Key &settingsKey, ToolChainType toolchainType, const QStringList &versions, diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index d2326949c02..29d6b97ebfe 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -50,8 +50,8 @@ McuPackagePtr createQtForMCUsPackage(const SettingsHandler::Ptr &settingsHandler new McuPackage(settingsHandler, {}, FileUtils::homePath(), // defaultPath - FilePath(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) - .withExecutableSuffix(), // detectionPath + {FilePath(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) + .withExecutableSuffix()}, // detectionPaths Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, // settingsKey Legacy::Constants::QUL_CMAKE_VAR, Legacy::Constants::QUL_ENV_VAR)}; @@ -144,7 +144,7 @@ McuPackagePtr createFreeRTOSSourcesPackage(const SettingsHandler::Ptr &settingsH new McuPackage(settingsHandler, QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix), defaultPath, - "tasks.c", // detection path + {"tasks.c"}, // detection path Constants::SETTINGS_KEY_FREERTOS_PREFIX + keyFromString(envVarPrefix), "FREERTOS_DIR", // cmake var envVar, // env var @@ -190,14 +190,14 @@ McuToolChainPackagePtr createMsvcToolChainPackage(const SettingsHandler::Ptr &se const FilePath detectionPath = FilePath("cl").withExecutableSuffix(); const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir() : FilePath(); - const auto *versionDetector = new McuPackageExecutableVersionDetector(detectionPath, + const auto *versionDetector = new McuPackageExecutableVersionDetector({detectionPath}, {"/?"}, R"(\b(\d+\.\d+)\.\d+\b)"); return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler, Tr::tr("MSVC Binary directory"), defaultPath, - detectionPath, + {detectionPath}, "MsvcToolchain", McuToolChainPackage::ToolChainType::MSVC, versions, @@ -212,18 +212,18 @@ McuToolChainPackagePtr createGccToolChainPackage(const SettingsHandler::Ptr &set Toolchain *toolChain = McuToolChainPackage::gccToolChain( ProjectExplorer::Constants::CXX_LANGUAGE_ID); - const FilePath detectionPath = FilePath("bin/g++").withExecutableSuffix(); + const FilePath detectionPath = FilePath("bin/g++*").withExecutableSuffix(); const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir().parentDir() : FilePath(); - const auto *versionDetector = new McuPackageExecutableVersionDetector(detectionPath, + const auto *versionDetector = new McuPackageExecutableVersionDetector({detectionPath}, {"--version"}, R"(\b(\d+\.\d+\.\d+)\b)"); return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler, Tr::tr("GCC Toolchain"), defaultPath, - detectionPath, + {detectionPath}, "GnuToolchain", McuToolChainPackage::ToolChainType::GCC, versions, @@ -252,7 +252,7 @@ McuToolChainPackagePtr createArmGccToolchainPackage(const SettingsHandler::Ptr & } const FilePath detectionPath = FilePath("bin/arm-none-eabi-g++").withExecutableSuffix(); - const auto *versionDetector = new McuPackageExecutableVersionDetector(detectionPath, + const auto *versionDetector = new McuPackageExecutableVersionDetector({detectionPath}, {"--version"}, R"(\b(\d+\.\d+\.\d+)\b)"); @@ -260,7 +260,7 @@ McuToolChainPackagePtr createArmGccToolchainPackage(const SettingsHandler::Ptr & new McuToolChainPackage(settingsHandler, Tr::tr("GNU Arm Embedded Toolchain"), defaultPath, - detectionPath, + {detectionPath}, "GNUArmEmbeddedToolchain", // settingsKey McuToolChainPackage::ToolChainType::ArmGcc, // toolchainType versions, @@ -277,7 +277,7 @@ McuToolChainPackagePtr createGhsToolchainPackage(const SettingsHandler::Ptr &set const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar)); const auto *versionDetector - = new McuPackageExecutableVersionDetector(FilePath("gversion").withExecutableSuffix(), + = new McuPackageExecutableVersionDetector({FilePath("gversion").withExecutableSuffix()}, {"-help"}, R"(\bv(\d+\.\d+\.\d+)\b)"); @@ -285,9 +285,9 @@ McuToolChainPackagePtr createGhsToolchainPackage(const SettingsHandler::Ptr &set new McuToolChainPackage(settingsHandler, "Green Hills Compiler", defaultPath, - FilePath("ccv850").withExecutableSuffix(), // detectionPath - "GHSToolchain", // settingsKey - McuToolChainPackage::ToolChainType::GHS, // toolchainType + {FilePath("ccv850").withExecutableSuffix()}, // detectionPath + "GHSToolchain", // settingsKey + McuToolChainPackage::ToolChainType::GHS, // toolchainType versions, Constants::TOOLCHAIN_DIR_CMAKE_VARIABLE, // cmake var envVar, // env var @@ -302,7 +302,7 @@ McuToolChainPackagePtr createGhsArmToolchainPackage(const SettingsHandler::Ptr & const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar)); const auto *versionDetector - = new McuPackageExecutableVersionDetector(FilePath("gversion").withExecutableSuffix(), + = new McuPackageExecutableVersionDetector({FilePath("gversion").withExecutableSuffix()}, {"-help"}, R"(\bv(\d+\.\d+\.\d+)\b)"); @@ -310,7 +310,7 @@ McuToolChainPackagePtr createGhsArmToolchainPackage(const SettingsHandler::Ptr & new McuToolChainPackage(settingsHandler, "Green Hills Compiler for ARM", defaultPath, - FilePath("cxarm").withExecutableSuffix(), // detectionPath + {FilePath("cxarm").withExecutableSuffix()}, // detectionPath "GHSArmToolchain", // settingsKey McuToolChainPackage::ToolChainType::GHSArm, // toolchainType versions, @@ -340,7 +340,7 @@ McuToolChainPackagePtr createIarToolChainPackage(const SettingsHandler::Ptr &set const FilePath detectionPath = FilePath("bin/iccarm").withExecutableSuffix(); const auto *versionDetector - = new McuPackageExecutableVersionDetector(detectionPath, + = new McuPackageExecutableVersionDetector({detectionPath}, {"--version"}, R"(\bV(\d+\.\d+\.\d+)\.\d+\b)"); @@ -348,7 +348,7 @@ McuToolChainPackagePtr createIarToolChainPackage(const SettingsHandler::Ptr &set new McuToolChainPackage(settingsHandler, "IAR ARM Compiler", defaultPath, - detectionPath, + {detectionPath}, "IARToolchain", // settings key McuToolChainPackage::ToolChainType::IAR, // toolchainType versions, @@ -376,7 +376,7 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti new McuPackage(settingsHandler, Tr::tr("STM32CubeProgrammer"), defaultPath, - detectionPath, + {detectionPath}, "Stm32CubeProgrammer", {}, // cmake var {}, // env var @@ -413,10 +413,10 @@ McuPackagePtr createMcuXpressoIdePackage(const SettingsHandler::Ptr &settingsHan return McuPackagePtr{new McuPackage(settingsHandler, "MCUXpresso IDE", defaultPath, - FilePath("ide/binaries/crt_emu_cm_redlink") - .withExecutableSuffix(), // detection path - "MCUXpressoIDE", // settings key - "MCUXPRESSO_IDE_PATH", // cmake var + {FilePath("ide/binaries/crt_emu_cm_redlink") + .withExecutableSuffix()}, // detection path + "MCUXpressoIDE", // settings key + "MCUXPRESSO_IDE_PATH", // cmake var envVar, {}, // versions "https://www.nxp.com/mcuxpresso/ide")}; // download url @@ -444,7 +444,7 @@ McuPackagePtr createCypressProgrammerPackage(const SettingsHandler::Ptr &setting new McuPackage(settingsHandler, "Cypress Auto Flash Utility", defaultPath, - FilePath::fromUserInput("/bin/openocd").withExecutableSuffix(), + {FilePath::fromUserInput("/bin/openocd").withExecutableSuffix()}, "CypressAutoFlashUtil", // settings key "INFINEON_AUTO_FLASH_UTILITY_DIR", // cmake var envVar)}; // env var @@ -471,7 +471,7 @@ McuPackagePtr createRenesasProgrammerPackage(const SettingsHandler::Ptr &setting return McuPackagePtr{new McuPackage(settingsHandler, "Renesas Flash Programmer", defaultPath, - FilePath("rfp-cli").withExecutableSuffix(), + {FilePath("rfp-cli").withExecutableSuffix()}, "RenesasFlashProgrammer", // settings key "RENESAS_FLASH_PROGRAMMER_PATH", // cmake var envVar)}; // env var @@ -609,14 +609,14 @@ static FilePaths targetDescriptionFiles(const FilePath &dir) return kitsPath(dir).dirEntries(Utils::FileFilter({"*.json"}, QDir::Files)); } -static QString getOsSpecificValue(const QJsonValue &entry) +static QVariant getOsSpecificValue(const QJsonValue &entry) { if (entry.isObject()) { //The json entry has os-specific values - return entry[HostOsInfo::isWindowsHost() ? QString("windows") : QString("linux")].toString(); + return entry[HostOsInfo::isWindowsHost() ? QString("windows") : QString("linux")].toVariant(); } //The entry does not have os-specific values - return entry.toString(); + return entry.toVariant(); } static VersionDetection parseVersionDetection(const QJsonObject &packageEntry) @@ -624,7 +624,7 @@ static VersionDetection parseVersionDetection(const QJsonObject &packageEntry) const QJsonObject versioning = packageEntry.value("versionDetection").toObject(); return { versioning["regex"].toString(), - getOsSpecificValue(versioning["filePattern"]), + getOsSpecificValue(versioning["filePattern"]).toString(), versioning["executableArgs"].toString(), versioning["xmlElement"].toString(), versioning["xmlAttribute"].toString(), @@ -675,8 +675,13 @@ static PackageDescription parsePackage(const QJsonObject &cmakeEntry) }); //Parse the default value depending on the operating system - QString defaultPathString = getOsSpecificValue(cmakeEntry["defaultValue"]); - QString detectionPathString = getOsSpecificValue(cmakeEntry["detectionPath"]); + QString defaultPathString = getOsSpecificValue(cmakeEntry["defaultValue"]).toString(); + QString detectionPathString = getOsSpecificValue(cmakeEntry["detectionPath"]).toString(); + QList detectionPaths; + if (!detectionPathString.isEmpty()) + detectionPaths.push_back(FilePath::fromUserInput(detectionPathString)); + detectionPaths + += Utils::transform(getOsSpecificValue(cmakeEntry["additionalDetectionPaths"]).toStringList(), &FilePath::fromUserInput); QString label = cmakeEntry["label"].toString(); @@ -689,7 +694,7 @@ static PackageDescription parsePackage(const QJsonObject &cmakeEntry) cmakeEntry["description"].toString(), keyFromString(cmakeEntry["setting"].toString()), FilePath::fromUserInput(defaultPathString), - FilePath::fromUserInput(detectionPathString), + detectionPaths, versions, parseVersionDetection(cmakeEntry), cmakeEntry["addToSystemPath"].toBool(), diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp index f92a443c4fc..f24fb0bbe56 100644 --- a/src/plugins/mcusupport/mcusupportversiondetection.cpp +++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "mcusupportversiondetection.h" +#include "mcuhelpers.h" #include @@ -24,21 +25,29 @@ QString matchRegExp(const QString &text, const QString ®Exp) } McuPackageExecutableVersionDetector::McuPackageExecutableVersionDetector( - const FilePath &detectionPath, const QStringList &detectionArgs, const QString &detectionRegExp) + const QList &detectionPaths, + const QStringList &detectionArgs, + const QString &detectionRegExp) : McuPackageVersionDetector() - , m_detectionPath(detectionPath) + , m_detectionPaths(detectionPaths) , m_detectionArgs(detectionArgs) , m_detectionRegExp(detectionRegExp) {} QString McuPackageExecutableVersionDetector::parseVersion(const FilePath &packagePath) const { - if (m_detectionPath.isEmpty() || m_detectionRegExp.isEmpty()) + if (m_detectionPaths.isEmpty() || m_detectionRegExp.isEmpty()) return {}; - const FilePath binaryPath = packagePath / m_detectionPath.path(); - if (!binaryPath.exists()) - return {}; + FilePath binaryPath; + + for (const FilePath &detectionPath : m_detectionPaths) { + std::optional path = firstMatchingPath(packagePath / detectionPath.path()); + if (!path) + continue; + binaryPath = *path; + break; + } const int timeout = 3000; // usually runs below 1s, but we want to be on the safe side Process process; diff --git a/src/plugins/mcusupport/mcusupportversiondetection.h b/src/plugins/mcusupport/mcusupportversiondetection.h index 2f30b2f9786..6382e3854fc 100644 --- a/src/plugins/mcusupport/mcusupportversiondetection.h +++ b/src/plugins/mcusupport/mcusupportversiondetection.h @@ -20,13 +20,13 @@ public: class McuPackageExecutableVersionDetector : public McuPackageVersionDetector { public: - McuPackageExecutableVersionDetector(const Utils::FilePath &detectionPath, + McuPackageExecutableVersionDetector(const Utils::FilePaths &detectionPaths, const QStringList &detectionArgs, const QString &detectionRegExp); QString parseVersion(const Utils::FilePath &packagePath) const final; private: - const Utils::FilePath m_detectionPath; + const Utils::FilePaths m_detectionPaths; const QStringList m_detectionArgs; const QString m_detectionRegExp; }; diff --git a/src/plugins/mcusupport/mcutargetdescription.h b/src/plugins/mcusupport/mcutargetdescription.h index e495b261922..3b6d783320a 100644 --- a/src/plugins/mcusupport/mcutargetdescription.h +++ b/src/plugins/mcusupport/mcutargetdescription.h @@ -30,7 +30,7 @@ struct PackageDescription QString description; Utils::Key setting; Utils::FilePath defaultPath; - Utils::FilePath detectionPath; + Utils::FilePaths detectionPaths; QStringList versions; VersionDetection versionDetection; bool shouldAddToSystemPath; diff --git a/src/plugins/mcusupport/mcutargetfactory.cpp b/src/plugins/mcusupport/mcutargetfactory.cpp index 24c8119972d..98f9854e3ac 100644 --- a/src/plugins/mcusupport/mcutargetfactory.cpp +++ b/src/plugins/mcusupport/mcutargetfactory.cpp @@ -33,8 +33,8 @@ McuPackageVersionDetector *createVersionDetection(const VersionDetection &versio versionDetection.xmlAttribute, versionDetection.regex}; else if (!versionDetection.executableArgs.isEmpty()) - return new McuPackageExecutableVersionDetector{Utils::FilePath::fromUserInput( - versionDetection.filePattern), + return new McuPackageExecutableVersionDetector{{Utils::FilePath::fromUserInput( + versionDetection.filePattern)}, QStringList{versionDetection.executableArgs}, versionDetection.regex}; else if (!versionDetection.filePattern.isEmpty() && !versionDetection.regex.isEmpty()) @@ -130,7 +130,7 @@ McuPackagePtr McuTargetFactory::createPackage(const PackageDescription &pkgDesc) return McuPackagePtr{new McuPackage{settingsHandler, pkgDesc.label, pkgDesc.defaultPath, - pkgDesc.detectionPath, + pkgDesc.detectionPaths, pkgDesc.setting, pkgDesc.cmakeVar, pkgDesc.envVar, @@ -166,7 +166,7 @@ McuToolChainPackage *McuTargetFactory::createToolchain( return new McuToolChainPackage{settingsHandler, compilerDescription.label, compilerDescription.defaultPath, - compilerDescription.detectionPath, + compilerDescription.detectionPaths, {}, toolchainType, toolchain.versions, @@ -206,7 +206,7 @@ McuToolChainPackage *McuTargetFactory::createToolchain( return new McuToolChainPackage{settingsHandler, compilerDescription.label, compilerDescription.defaultPath, - compilerDescription.detectionPath, + compilerDescription.detectionPaths, compilerDescription.setting, toolchainType, toolchain.versions, diff --git a/src/plugins/mcusupport/test/gcc_desktop_json.h b/src/plugins/mcusupport/test/gcc_desktop_json.h index 39c38bc07b1..e26276891a0 100644 --- a/src/plugins/mcusupport/test/gcc_desktop_json.h +++ b/src/plugins/mcusupport/test/gcc_desktop_json.h @@ -27,10 +27,10 @@ constexpr auto gcc_desktop_json = R"( "defaultValue": "/usr", "versionDetection": { "executableArgs": "--version", - "filePattern": "bin/g++", + "filePattern": "bin/g++*", "regex": "\\b(\\d+\\.\\d+\\.\\d+)\\b" }, - "detectionPath": "bin/g++" + "detectionPath": "bin/g++*" } } } diff --git a/src/plugins/mcusupport/test/packagemock.h b/src/plugins/mcusupport/test/packagemock.h index 06d3be62305..ab2e425ef54 100644 --- a/src/plugins/mcusupport/test/packagemock.h +++ b/src/plugins/mcusupport/test/packagemock.h @@ -20,7 +20,7 @@ public: MOCK_METHOD(void, setPath, (const Utils::FilePath &) ); MOCK_METHOD(QString, label, (), (const)); MOCK_METHOD(Utils::FilePath, defaultPath, (), (const)); - MOCK_METHOD(Utils::FilePath, detectionPath, (), (const)); + MOCK_METHOD(Utils::FilePaths, detectionPaths, (), (const)); MOCK_METHOD(QString, statusText, (), (const)); MOCK_METHOD(void, updateStatus, ()); MOCK_METHOD(Utils::Key, settingsKey, (), (const)); diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 5fb68340870..01feb09a005 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -190,7 +190,7 @@ const PackageDescription {}, Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, qtForMcuSdkPath, - Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH, + {Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH}, {}, VersionDetection{}, false, @@ -323,7 +323,7 @@ void verifyBoardSdk(const McuPackagePtr &boardSdk, QCOMPARE(boardSdk->cmakeVariableName(), cmakeVariable); QCOMPARE(boardSdk->environmentVariableName(), environmentVariable); QCOMPARE(boardSdk->settingsKey(), keyFromString(environmentVariable)); - QCOMPARE(boardSdk->detectionPath().toString(), empty); + QVERIFY(boardSdk->detectionPaths().empty()); QCOMPARE(boardSdk->versions(), versions); } @@ -339,7 +339,7 @@ void verifyFreeRtosPackage(const McuPackagePtr &freeRtos, QCOMPARE(freeRtos->cmakeVariableName(), freeRtosCMakeVar); QCOMPARE(freeRtos->settingsKey(), expectedSettingsKey); QCOMPARE(freeRtos->path().cleanPath().toString(), freeRtosPath); - QCOMPARE(freeRtos->detectionPath().cleanPath().toString(), freeRtosDetectionPath); + QCOMPARE(freeRtos->detectionPaths().first().cleanPath().toString(), freeRtosDetectionPath); QVERIFY(freeRtos->path().toUserOutput().startsWith(boardSdkDir.cleanPath().toUserOutput())); } @@ -359,7 +359,10 @@ void verifyPackage(const McuPackagePtr &package, QCOMPARE(package->cmakeVariableName(), cmakeVar); QCOMPARE(package->environmentVariableName(), envVar); QCOMPARE(package->label(), label); - QCOMPARE(package->detectionPath().toString(), detectionPath); + if (!detectionPath.isEmpty()) { + QVERIFY(!package->detectionPaths().empty()); + QCOMPARE(package->detectionPaths().first().toString(), detectionPath); + } QCOMPARE(package->settingsKey(), setting); QCOMPARE(package->versions(), versions); } @@ -464,7 +467,7 @@ void McuSupportTest::initTestCase() EXPECT_CALL(*freeRtosPackage, path()) .WillRepeatedly(Return(FilePath::fromUserInput(freeRtosPath))); EXPECT_CALL(*freeRtosPackage, isAddToSystemPath()).WillRepeatedly(Return(true)); - EXPECT_CALL(*freeRtosPackage, detectionPath()).WillRepeatedly(Return(FilePath{})); + EXPECT_CALL(*freeRtosPackage, detectionPaths()).WillRepeatedly(Return(QList{})); ON_CALL(*sdkPackage, label()).WillByDefault(Return(QString{QUL_LABEL})); ON_CALL(*sdkPackage, settingsKey()) @@ -474,7 +477,7 @@ void McuSupportTest::initTestCase() ON_CALL(*sdkPackage, isValidStatus()).WillByDefault(Return(true)); ON_CALL(*sdkPackage, path()).WillByDefault(Return(FilePath::fromUserInput(qtForMcuSdkPath))); ON_CALL(*sdkPackage, isAddToSystemPath()).WillByDefault(Return(true)); - ON_CALL(*sdkPackage, detectionPath()).WillByDefault(Return(FilePath{})); + ON_CALL(*sdkPackage, detectionPaths()).WillByDefault(Return(QList{})); EXPECT_CALL(*armGccToolchainFilePackage, environmentVariableName()) .WillRepeatedly(Return(QString{QString{}})); @@ -484,7 +487,8 @@ void McuSupportTest::initTestCase() EXPECT_CALL(*armGccToolchainFilePackage, path()) .WillRepeatedly(Return(FilePath::fromUserInput(armGccToolchainFilePath))); EXPECT_CALL(*armGccToolchainFilePackage, isAddToSystemPath()).WillRepeatedly(Return(false)); - EXPECT_CALL(*armGccToolchainFilePackage, detectionPath()).WillRepeatedly(Return(FilePath{})); + EXPECT_CALL(*armGccToolchainFilePackage, detectionPaths()) + .WillRepeatedly(Return(QList{})); ON_CALL(*settingsMockPtr, getPath) .WillByDefault([](const Key &, QSettings::Scope, const FilePath &m_defaultPath) { @@ -743,7 +747,7 @@ void McuSupportTest::test_createTargets() freeRtosSetting, freeRtosLabel, freeRtosPath, - freeRtosDetectionPath, + {freeRtosDetectionPath}, {}, VersionDetection{}, true, @@ -796,7 +800,7 @@ void McuSupportTest::test_createPackages() freeRtosLabel, freeRtosSetting, freeRtosPath, - freeRtosDetectionPath, + {freeRtosDetectionPath}, {}, VersionDetection{}, true, @@ -1259,9 +1263,9 @@ void McuSupportTest::test_legacy_createQtMCUsPackage() QVERIFY(qtForMCUsSDK); QCOMPARE(qtForMCUsSDK->settingsKey(), Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK); - QCOMPARE(qtForMCUsSDK->detectionPath(), - FilePath::fromUserInput(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) - .withExecutableSuffix()); + QCOMPARE(qtForMCUsSDK->detectionPaths(), + {FilePath::fromUserInput(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) + .withExecutableSuffix()}); QCOMPARE(qtForMCUsSDK->path().toString(), qtForMcuSdkPath); } @@ -1713,7 +1717,9 @@ void McuSupportTest::test_differentValueForEachOperationSystem() { const auto packageDescription = parseDescriptionJson(armgcc_mimxrt1050_evk_freertos_json); auto default_path_entry = packageDescription.platform.entries[0].defaultPath.toString(); - auto validation_path_entry = packageDescription.platform.entries[0].detectionPath.toString(); + QCOMPARE(packageDescription.platform.entries[0].detectionPaths.size(), 1); + auto validation_path_entry + = packageDescription.platform.entries[0].detectionPaths.first().toString(); //TODO: Revisit whether this test is required and not currently covered by the third party packages if (HostOsInfo::isWindowsHost()) {