McuSupport: Accept wildcards in detectionPath and executableVersionDetector

In case the g++ symbolic link does not point to any g++ executable it
should be possible to pick other installed g++ versions

Fixes: QTCREATORBUG-29891
Change-Id: I3070e38617a85489e1e6bfb3b1a6368af684829f
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Yasser Grimes
2023-12-08 15:29:52 +02:00
parent 435761fd0a
commit f7950acd94
14 changed files with 173 additions and 100 deletions

View File

@@ -7,7 +7,10 @@
#include <QObject> #include <QObject>
namespace Utils { class FilePath; } namespace Utils {
class FilePath;
using FilePaths = QList<class FilePath>;
} // namespace Utils
namespace McuSupport::Internal { namespace McuSupport::Internal {
@@ -38,7 +41,8 @@ public:
virtual Utils::FilePath path() const = 0; virtual Utils::FilePath path() const = 0;
virtual void setPath(const Utils::FilePath &) = 0; virtual void setPath(const Utils::FilePath &) = 0;
virtual Utils::FilePath defaultPath() const = 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 Utils::Key settingsKey() const = 0;
virtual void updateStatus() = 0; virtual void updateStatus() = 0;

View File

@@ -26,4 +26,23 @@ QString removeRtosSuffix(const QString &environmentVariable)
return result.replace(freeRtosSuffix, QString{}); 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<Utils::FilePath> 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 } // namespace McuSupport::Internal

View File

@@ -5,6 +5,8 @@
#include "mcutarget.h" #include "mcutarget.h"
#include <utils/filepath.h>
#include <QRegularExpression> #include <QRegularExpression>
namespace McuSupport::Internal { namespace McuSupport::Internal {
@@ -13,6 +15,7 @@ struct McuTargetDescription;
McuTarget::OS deduceOperatingSystem(const McuTargetDescription &); McuTarget::OS deduceOperatingSystem(const McuTargetDescription &);
QString removeRtosSuffix(const QString &environmentVariable); QString removeRtosSuffix(const QString &environmentVariable);
std::optional<Utils::FilePath> firstMatchingPath(const Utils::FilePath &path);
template<typename T> template<typename T>
class asKeyValueRange class asKeyValueRange

View File

@@ -178,8 +178,10 @@ public:
auto processPackage = [&dependencies](const McuPackagePtr &package) { auto processPackage = [&dependencies](const McuPackagePtr &package) {
const auto cmakeVariableName = package->cmakeVariableName(); const auto cmakeVariableName = package->cmakeVariableName();
if (!cmakeVariableName.isEmpty()) if (!cmakeVariableName.isEmpty() && !package->detectionPaths().empty())
dependencies.append({cmakeVariableName, package->detectionPath().toUserOutput()}); // 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()) for (const auto &package : mcuTarget->packages())
processPackage(package); processPackage(package);
@@ -502,10 +504,9 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
if (!qtForMCUsPackage->isValidStatus()) { if (!qtForMCUsPackage->isValidStatus()) {
switch (qtForMCUsPackage->status()) { switch (qtForMCUsPackage->status()) {
case McuAbstractPackage::Status::ValidPathInvalidPackage: { case McuAbstractPackage::Status::ValidPathInvalidPackage: {
const QString message const QString message = Tr::tr("Path %1 exists, but does not contain %2.")
= Tr::tr("Path %1 exists, but does not contain %2.")
.arg(qtForMCUsPackage->path().toUserOutput(), .arg(qtForMCUsPackage->path().toUserOutput(),
qtForMCUsPackage->detectionPath().toUserOutput()); qtForMCUsPackage->detectionPathsToString());
autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message}); autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
printMessage(message, true); printMessage(message, true);
break; break;
@@ -520,8 +521,9 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
break; break;
} }
case McuAbstractPackage::Status::EmptyPath: { case McuAbstractPackage::Status::EmptyPath: {
const QString message = Tr::tr("Missing %1. Add the path in Edit > Preferences > Devices > MCU.") const QString message
.arg(qtForMCUsPackage->detectionPath().toUserOutput()); = Tr::tr("Missing %1. Add the path in Edit > Preferences > Devices > MCU.")
.arg(qtForMCUsPackage->detectionPathsToString());
autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message}); autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
printMessage(message, true); printMessage(message, true);
return; return;

View File

@@ -2,9 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mcupackage.h" #include "mcupackage.h"
#include "mcuhelpers.h"
#include "mcusupporttr.h"
#include "mcusupportversiondetection.h" #include "mcusupportversiondetection.h"
#include "settingshandler.h" #include "settingshandler.h"
#include "mcusupporttr.h"
#include <baremetal/baremetalconstants.h> #include <baremetal/baremetalconstants.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -18,6 +19,7 @@
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/infolabel.h> #include <utils/infolabel.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <QDesktopServices> #include <QDesktopServices>
@@ -32,7 +34,7 @@ namespace McuSupport::Internal {
McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler,
const QString &label, const QString &label,
const FilePath &defaultPath, const FilePath &defaultPath,
const FilePath &detectionPath, const Utils::FilePaths &detectionPaths,
const Key &settingsKey, const Key &settingsKey,
const QString &cmakeVarName, const QString &cmakeVarName,
const QString &envVarName, const QString &envVarName,
@@ -44,7 +46,7 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler,
: settingsHandler(settingsHandler) : settingsHandler(settingsHandler)
, m_label(label) , m_label(label)
, m_defaultPath(settingsHandler->getPath(settingsKey, QSettings::SystemScope, defaultPath)) , m_defaultPath(settingsHandler->getPath(settingsKey, QSettings::SystemScope, defaultPath))
, m_detectionPath(detectionPath) , m_detectionPaths(detectionPaths)
, m_settingsKey(settingsKey) , m_settingsKey(settingsKey)
, m_versionDetector(versionDetector) , m_versionDetector(versionDetector)
, m_versions(versions) , m_versions(versions)
@@ -110,9 +112,16 @@ FilePath McuPackage::defaultPath() const
return m_defaultPath.cleanPath(); return m_defaultPath.cleanPath();
} }
FilePath McuPackage::detectionPath() const QList<FilePath> 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) void McuPackage::setPath(const FilePath &newPath)
@@ -128,8 +137,20 @@ void McuPackage::setPath(const FilePath &newPath)
void McuPackage::updateStatus() void McuPackage::updateStatus()
{ {
bool validPath = !m_path.isEmpty() && m_path.exists(); bool validPath = !m_path.isEmpty() && m_path.exists();
const FilePath detectionPath = basePath() / m_detectionPath.path(); bool validPackage = false;
const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists(); if (m_detectionPaths.empty()) {
validPackage = true;
} else {
for (const FilePath &detectionPath : m_detectionPaths) {
std::optional<FilePath> alternativeDetectionPath = firstMatchingPath(
basePath() / detectionPath.path());
if (!alternativeDetectionPath)
continue;
validPackage = true;
m_usedDetectionPath = detectionPath;
break;
}
}
m_detectedVersion = validPath && validPackage && m_versionDetector m_detectedVersion = validPath && validPackage && m_versionDetector
? m_versionDetector->parseVersion(basePath()) ? m_versionDetector->parseVersion(basePath())
: QString(); : QString();
@@ -185,19 +206,20 @@ QString McuPackage::statusText() const
{ {
const QString displayPackagePath = m_path.toUserOutput(); const QString displayPackagePath = m_path.toUserOutput();
const QString displayVersions = m_versions.join(Tr::tr(" or ")); 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 const QString displayRequiredPath = m_versions.empty() ? outDetectionPath
: QString("%1 %2").arg(outDetectionPath, : QString("%1 %2").arg(outDetectionPath,
displayVersions); displayVersions);
const QString displayDetectedPath = m_versions.empty() const QString displayDetectedPath = m_versions.empty()
? outDetectionPath ? m_usedDetectionPath.toString()
: QString("%1 %2").arg(outDetectionPath, : QString("%1 %2").arg(m_usedDetectionPath.toString(),
m_detectedVersion); m_detectedVersion);
QString response; QString response;
switch (m_status) { switch (m_status) {
case Status::ValidPackage: case Status::ValidPackage:
response = m_detectionPath.isEmpty() response = m_detectionPaths.isEmpty()
? (m_detectedVersion.isEmpty() ? (m_detectedVersion.isEmpty()
? Tr::tr("Path %1 exists.").arg(displayPackagePath) ? Tr::tr("Path %1 exists.").arg(displayPackagePath)
: Tr::tr("Path %1 exists. Version %2 was found.") : 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); response = Tr::tr("Path %1 does not exist.").arg(displayPackagePath);
break; break;
case Status::EmptyPath: case Status::EmptyPath:
response = m_detectionPath.isEmpty() response = m_detectionPaths.isEmpty()
? Tr::tr("Path is empty.") ? Tr::tr("Path is empty.")
: Tr::tr("Path is empty, %1 not found.").arg(displayRequiredPath); : Tr::tr("Path is empty, %1 not found.").arg(displayRequiredPath);
break; break;
@@ -333,7 +355,7 @@ const QMap<QString, QString> McuPackage::packageLabelTranslations {
McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler, McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler,
const QString &label, const QString &label,
const FilePath &defaultPath, const FilePath &defaultPath,
const FilePath &detectionPath, const QList<FilePath> &detectionPaths,
const Key &settingsKey, const Key &settingsKey,
McuToolChainPackage::ToolChainType type, McuToolChainPackage::ToolChainType type,
const QStringList &versions, const QStringList &versions,
@@ -343,7 +365,7 @@ McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHan
: McuPackage(settingsHandler, : McuPackage(settingsHandler,
label, label,
defaultPath, defaultPath,
detectionPath, detectionPaths,
settingsKey, settingsKey,
cmakeVarName, cmakeVarName,
envVarName, envVarName,

View File

@@ -31,7 +31,7 @@ public:
McuPackage(const SettingsHandler::Ptr &settingsHandler, McuPackage(const SettingsHandler::Ptr &settingsHandler,
const QString &label, const QString &label,
const Utils::FilePath &defaultPath, const Utils::FilePath &defaultPath,
const Utils::FilePath &detectionPath, const Utils::FilePaths &detectionPaths,
const Utils::Key &settingsKey, const Utils::Key &settingsKey,
const QString &cmakeVarName, const QString &cmakeVarName,
const QString &envVarName, const QString &envVarName,
@@ -39,7 +39,8 @@ public:
const QString &downloadUrl = {}, const QString &downloadUrl = {},
const McuPackageVersionDetector *versionDetector = nullptr, const McuPackageVersionDetector *versionDetector = nullptr,
const bool addToPath = false, const bool addToPath = false,
const Utils::PathChooser::Kind &valueType = Utils::PathChooser::Kind::ExistingDirectory); const Utils::PathChooser::Kind &valueType
= Utils::PathChooser::Kind::ExistingDirectory);
~McuPackage() override = default; ~McuPackage() override = default;
@@ -54,7 +55,8 @@ public:
Utils::FilePath basePath() const override; Utils::FilePath basePath() const override;
Utils::FilePath path() const override; Utils::FilePath path() const override;
Utils::FilePath defaultPath() 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; Utils::Key settingsKey() const final;
void updateStatus() override; void updateStatus() override;
@@ -81,7 +83,8 @@ private:
const QString m_label; const QString m_label;
Utils::FilePath m_defaultPath; Utils::FilePath m_defaultPath;
const Utils::FilePath m_detectionPath; const Utils::FilePaths m_detectionPaths;
Utils::FilePath m_usedDetectionPath;
const Utils::Key m_settingsKey; const Utils::Key m_settingsKey;
QScopedPointer<const McuPackageVersionDetector> m_versionDetector; QScopedPointer<const McuPackageVersionDetector> m_versionDetector;
@@ -106,7 +109,7 @@ public:
McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler, McuToolChainPackage(const SettingsHandler::Ptr &settingsHandler,
const QString &label, const QString &label,
const Utils::FilePath &defaultPath, const Utils::FilePath &defaultPath,
const Utils::FilePath &detectionPath, const Utils::FilePaths &detectionPaths,
const Utils::Key &settingsKey, const Utils::Key &settingsKey,
ToolChainType toolchainType, ToolChainType toolchainType,
const QStringList &versions, const QStringList &versions,

View File

@@ -50,8 +50,8 @@ McuPackagePtr createQtForMCUsPackage(const SettingsHandler::Ptr &settingsHandler
new McuPackage(settingsHandler, new McuPackage(settingsHandler,
{}, {},
FileUtils::homePath(), // defaultPath FileUtils::homePath(), // defaultPath
FilePath(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) {FilePath(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH)
.withExecutableSuffix(), // detectionPath .withExecutableSuffix()}, // detectionPaths
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, // settingsKey Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, // settingsKey
Legacy::Constants::QUL_CMAKE_VAR, Legacy::Constants::QUL_CMAKE_VAR,
Legacy::Constants::QUL_ENV_VAR)}; Legacy::Constants::QUL_ENV_VAR)};
@@ -144,7 +144,7 @@ McuPackagePtr createFreeRTOSSourcesPackage(const SettingsHandler::Ptr &settingsH
new McuPackage(settingsHandler, new McuPackage(settingsHandler,
QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix), QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix),
defaultPath, defaultPath,
"tasks.c", // detection path {"tasks.c"}, // detection path
Constants::SETTINGS_KEY_FREERTOS_PREFIX + keyFromString(envVarPrefix), Constants::SETTINGS_KEY_FREERTOS_PREFIX + keyFromString(envVarPrefix),
"FREERTOS_DIR", // cmake var "FREERTOS_DIR", // cmake var
envVar, // env var envVar, // env var
@@ -190,14 +190,14 @@ McuToolChainPackagePtr createMsvcToolChainPackage(const SettingsHandler::Ptr &se
const FilePath detectionPath = FilePath("cl").withExecutableSuffix(); const FilePath detectionPath = FilePath("cl").withExecutableSuffix();
const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir() : FilePath(); 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)"); R"(\b(\d+\.\d+)\.\d+\b)");
return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler, return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler,
Tr::tr("MSVC Binary directory"), Tr::tr("MSVC Binary directory"),
defaultPath, defaultPath,
detectionPath, {detectionPath},
"MsvcToolchain", "MsvcToolchain",
McuToolChainPackage::ToolChainType::MSVC, McuToolChainPackage::ToolChainType::MSVC,
versions, versions,
@@ -212,18 +212,18 @@ McuToolChainPackagePtr createGccToolChainPackage(const SettingsHandler::Ptr &set
Toolchain *toolChain = McuToolChainPackage::gccToolChain( Toolchain *toolChain = McuToolChainPackage::gccToolChain(
ProjectExplorer::Constants::CXX_LANGUAGE_ID); 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() const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir().parentDir()
: FilePath(); : FilePath();
const auto *versionDetector = new McuPackageExecutableVersionDetector(detectionPath, const auto *versionDetector = new McuPackageExecutableVersionDetector({detectionPath},
{"--version"}, {"--version"},
R"(\b(\d+\.\d+\.\d+)\b)"); R"(\b(\d+\.\d+\.\d+)\b)");
return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler, return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler,
Tr::tr("GCC Toolchain"), Tr::tr("GCC Toolchain"),
defaultPath, defaultPath,
detectionPath, {detectionPath},
"GnuToolchain", "GnuToolchain",
McuToolChainPackage::ToolChainType::GCC, McuToolChainPackage::ToolChainType::GCC,
versions, versions,
@@ -252,7 +252,7 @@ McuToolChainPackagePtr createArmGccToolchainPackage(const SettingsHandler::Ptr &
} }
const FilePath detectionPath = FilePath("bin/arm-none-eabi-g++").withExecutableSuffix(); const FilePath detectionPath = FilePath("bin/arm-none-eabi-g++").withExecutableSuffix();
const auto *versionDetector = new McuPackageExecutableVersionDetector(detectionPath, const auto *versionDetector = new McuPackageExecutableVersionDetector({detectionPath},
{"--version"}, {"--version"},
R"(\b(\d+\.\d+\.\d+)\b)"); R"(\b(\d+\.\d+\.\d+)\b)");
@@ -260,7 +260,7 @@ McuToolChainPackagePtr createArmGccToolchainPackage(const SettingsHandler::Ptr &
new McuToolChainPackage(settingsHandler, new McuToolChainPackage(settingsHandler,
Tr::tr("GNU Arm Embedded Toolchain"), Tr::tr("GNU Arm Embedded Toolchain"),
defaultPath, defaultPath,
detectionPath, {detectionPath},
"GNUArmEmbeddedToolchain", // settingsKey "GNUArmEmbeddedToolchain", // settingsKey
McuToolChainPackage::ToolChainType::ArmGcc, // toolchainType McuToolChainPackage::ToolChainType::ArmGcc, // toolchainType
versions, versions,
@@ -277,7 +277,7 @@ McuToolChainPackagePtr createGhsToolchainPackage(const SettingsHandler::Ptr &set
const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar)); const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
const auto *versionDetector const auto *versionDetector
= new McuPackageExecutableVersionDetector(FilePath("gversion").withExecutableSuffix(), = new McuPackageExecutableVersionDetector({FilePath("gversion").withExecutableSuffix()},
{"-help"}, {"-help"},
R"(\bv(\d+\.\d+\.\d+)\b)"); R"(\bv(\d+\.\d+\.\d+)\b)");
@@ -285,7 +285,7 @@ McuToolChainPackagePtr createGhsToolchainPackage(const SettingsHandler::Ptr &set
new McuToolChainPackage(settingsHandler, new McuToolChainPackage(settingsHandler,
"Green Hills Compiler", "Green Hills Compiler",
defaultPath, defaultPath,
FilePath("ccv850").withExecutableSuffix(), // detectionPath {FilePath("ccv850").withExecutableSuffix()}, // detectionPath
"GHSToolchain", // settingsKey "GHSToolchain", // settingsKey
McuToolChainPackage::ToolChainType::GHS, // toolchainType McuToolChainPackage::ToolChainType::GHS, // toolchainType
versions, versions,
@@ -302,7 +302,7 @@ McuToolChainPackagePtr createGhsArmToolchainPackage(const SettingsHandler::Ptr &
const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar)); const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
const auto *versionDetector const auto *versionDetector
= new McuPackageExecutableVersionDetector(FilePath("gversion").withExecutableSuffix(), = new McuPackageExecutableVersionDetector({FilePath("gversion").withExecutableSuffix()},
{"-help"}, {"-help"},
R"(\bv(\d+\.\d+\.\d+)\b)"); R"(\bv(\d+\.\d+\.\d+)\b)");
@@ -310,7 +310,7 @@ McuToolChainPackagePtr createGhsArmToolchainPackage(const SettingsHandler::Ptr &
new McuToolChainPackage(settingsHandler, new McuToolChainPackage(settingsHandler,
"Green Hills Compiler for ARM", "Green Hills Compiler for ARM",
defaultPath, defaultPath,
FilePath("cxarm").withExecutableSuffix(), // detectionPath {FilePath("cxarm").withExecutableSuffix()}, // detectionPath
"GHSArmToolchain", // settingsKey "GHSArmToolchain", // settingsKey
McuToolChainPackage::ToolChainType::GHSArm, // toolchainType McuToolChainPackage::ToolChainType::GHSArm, // toolchainType
versions, versions,
@@ -340,7 +340,7 @@ McuToolChainPackagePtr createIarToolChainPackage(const SettingsHandler::Ptr &set
const FilePath detectionPath = FilePath("bin/iccarm").withExecutableSuffix(); const FilePath detectionPath = FilePath("bin/iccarm").withExecutableSuffix();
const auto *versionDetector const auto *versionDetector
= new McuPackageExecutableVersionDetector(detectionPath, = new McuPackageExecutableVersionDetector({detectionPath},
{"--version"}, {"--version"},
R"(\bV(\d+\.\d+\.\d+)\.\d+\b)"); R"(\bV(\d+\.\d+\.\d+)\.\d+\b)");
@@ -348,7 +348,7 @@ McuToolChainPackagePtr createIarToolChainPackage(const SettingsHandler::Ptr &set
new McuToolChainPackage(settingsHandler, new McuToolChainPackage(settingsHandler,
"IAR ARM Compiler", "IAR ARM Compiler",
defaultPath, defaultPath,
detectionPath, {detectionPath},
"IARToolchain", // settings key "IARToolchain", // settings key
McuToolChainPackage::ToolChainType::IAR, // toolchainType McuToolChainPackage::ToolChainType::IAR, // toolchainType
versions, versions,
@@ -376,7 +376,7 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti
new McuPackage(settingsHandler, new McuPackage(settingsHandler,
Tr::tr("STM32CubeProgrammer"), Tr::tr("STM32CubeProgrammer"),
defaultPath, defaultPath,
detectionPath, {detectionPath},
"Stm32CubeProgrammer", "Stm32CubeProgrammer",
{}, // cmake var {}, // cmake var
{}, // env var {}, // env var
@@ -413,8 +413,8 @@ McuPackagePtr createMcuXpressoIdePackage(const SettingsHandler::Ptr &settingsHan
return McuPackagePtr{new McuPackage(settingsHandler, return McuPackagePtr{new McuPackage(settingsHandler,
"MCUXpresso IDE", "MCUXpresso IDE",
defaultPath, defaultPath,
FilePath("ide/binaries/crt_emu_cm_redlink") {FilePath("ide/binaries/crt_emu_cm_redlink")
.withExecutableSuffix(), // detection path .withExecutableSuffix()}, // detection path
"MCUXpressoIDE", // settings key "MCUXpressoIDE", // settings key
"MCUXPRESSO_IDE_PATH", // cmake var "MCUXPRESSO_IDE_PATH", // cmake var
envVar, envVar,
@@ -444,7 +444,7 @@ McuPackagePtr createCypressProgrammerPackage(const SettingsHandler::Ptr &setting
new McuPackage(settingsHandler, new McuPackage(settingsHandler,
"Cypress Auto Flash Utility", "Cypress Auto Flash Utility",
defaultPath, defaultPath,
FilePath::fromUserInput("/bin/openocd").withExecutableSuffix(), {FilePath::fromUserInput("/bin/openocd").withExecutableSuffix()},
"CypressAutoFlashUtil", // settings key "CypressAutoFlashUtil", // settings key
"INFINEON_AUTO_FLASH_UTILITY_DIR", // cmake var "INFINEON_AUTO_FLASH_UTILITY_DIR", // cmake var
envVar)}; // env var envVar)}; // env var
@@ -471,7 +471,7 @@ McuPackagePtr createRenesasProgrammerPackage(const SettingsHandler::Ptr &setting
return McuPackagePtr{new McuPackage(settingsHandler, return McuPackagePtr{new McuPackage(settingsHandler,
"Renesas Flash Programmer", "Renesas Flash Programmer",
defaultPath, defaultPath,
FilePath("rfp-cli").withExecutableSuffix(), {FilePath("rfp-cli").withExecutableSuffix()},
"RenesasFlashProgrammer", // settings key "RenesasFlashProgrammer", // settings key
"RENESAS_FLASH_PROGRAMMER_PATH", // cmake var "RENESAS_FLASH_PROGRAMMER_PATH", // cmake var
envVar)}; // env var envVar)}; // env var
@@ -609,14 +609,14 @@ static FilePaths targetDescriptionFiles(const FilePath &dir)
return kitsPath(dir).dirEntries(Utils::FileFilter({"*.json"}, QDir::Files)); return kitsPath(dir).dirEntries(Utils::FileFilter({"*.json"}, QDir::Files));
} }
static QString getOsSpecificValue(const QJsonValue &entry) static QVariant getOsSpecificValue(const QJsonValue &entry)
{ {
if (entry.isObject()) { if (entry.isObject()) {
//The json entry has os-specific values //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 //The entry does not have os-specific values
return entry.toString(); return entry.toVariant();
} }
static VersionDetection parseVersionDetection(const QJsonObject &packageEntry) static VersionDetection parseVersionDetection(const QJsonObject &packageEntry)
@@ -624,7 +624,7 @@ static VersionDetection parseVersionDetection(const QJsonObject &packageEntry)
const QJsonObject versioning = packageEntry.value("versionDetection").toObject(); const QJsonObject versioning = packageEntry.value("versionDetection").toObject();
return { return {
versioning["regex"].toString(), versioning["regex"].toString(),
getOsSpecificValue(versioning["filePattern"]), getOsSpecificValue(versioning["filePattern"]).toString(),
versioning["executableArgs"].toString(), versioning["executableArgs"].toString(),
versioning["xmlElement"].toString(), versioning["xmlElement"].toString(),
versioning["xmlAttribute"].toString(), versioning["xmlAttribute"].toString(),
@@ -675,8 +675,13 @@ static PackageDescription parsePackage(const QJsonObject &cmakeEntry)
}); });
//Parse the default value depending on the operating system //Parse the default value depending on the operating system
QString defaultPathString = getOsSpecificValue(cmakeEntry["defaultValue"]); QString defaultPathString = getOsSpecificValue(cmakeEntry["defaultValue"]).toString();
QString detectionPathString = getOsSpecificValue(cmakeEntry["detectionPath"]); QString detectionPathString = getOsSpecificValue(cmakeEntry["detectionPath"]).toString();
QList<FilePath> detectionPaths;
if (!detectionPathString.isEmpty())
detectionPaths.push_back(FilePath::fromUserInput(detectionPathString));
detectionPaths
+= Utils::transform(getOsSpecificValue(cmakeEntry["additionalDetectionPaths"]).toStringList(), &FilePath::fromUserInput);
QString label = cmakeEntry["label"].toString(); QString label = cmakeEntry["label"].toString();
@@ -689,7 +694,7 @@ static PackageDescription parsePackage(const QJsonObject &cmakeEntry)
cmakeEntry["description"].toString(), cmakeEntry["description"].toString(),
keyFromString(cmakeEntry["setting"].toString()), keyFromString(cmakeEntry["setting"].toString()),
FilePath::fromUserInput(defaultPathString), FilePath::fromUserInput(defaultPathString),
FilePath::fromUserInput(detectionPathString), detectionPaths,
versions, versions,
parseVersionDetection(cmakeEntry), parseVersionDetection(cmakeEntry),
cmakeEntry["addToSystemPath"].toBool(), cmakeEntry["addToSystemPath"].toBool(),

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mcusupportversiondetection.h" #include "mcusupportversiondetection.h"
#include "mcuhelpers.h"
#include <utils/process.h> #include <utils/process.h>
@@ -24,21 +25,29 @@ QString matchRegExp(const QString &text, const QString &regExp)
} }
McuPackageExecutableVersionDetector::McuPackageExecutableVersionDetector( McuPackageExecutableVersionDetector::McuPackageExecutableVersionDetector(
const FilePath &detectionPath, const QStringList &detectionArgs, const QString &detectionRegExp) const QList<FilePath> &detectionPaths,
const QStringList &detectionArgs,
const QString &detectionRegExp)
: McuPackageVersionDetector() : McuPackageVersionDetector()
, m_detectionPath(detectionPath) , m_detectionPaths(detectionPaths)
, m_detectionArgs(detectionArgs) , m_detectionArgs(detectionArgs)
, m_detectionRegExp(detectionRegExp) , m_detectionRegExp(detectionRegExp)
{} {}
QString McuPackageExecutableVersionDetector::parseVersion(const FilePath &packagePath) const QString McuPackageExecutableVersionDetector::parseVersion(const FilePath &packagePath) const
{ {
if (m_detectionPath.isEmpty() || m_detectionRegExp.isEmpty()) if (m_detectionPaths.isEmpty() || m_detectionRegExp.isEmpty())
return {}; return {};
const FilePath binaryPath = packagePath / m_detectionPath.path(); FilePath binaryPath;
if (!binaryPath.exists())
return {}; for (const FilePath &detectionPath : m_detectionPaths) {
std::optional<FilePath> 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 const int timeout = 3000; // usually runs below 1s, but we want to be on the safe side
Process process; Process process;

View File

@@ -20,13 +20,13 @@ public:
class McuPackageExecutableVersionDetector : public McuPackageVersionDetector class McuPackageExecutableVersionDetector : public McuPackageVersionDetector
{ {
public: public:
McuPackageExecutableVersionDetector(const Utils::FilePath &detectionPath, McuPackageExecutableVersionDetector(const Utils::FilePaths &detectionPaths,
const QStringList &detectionArgs, const QStringList &detectionArgs,
const QString &detectionRegExp); const QString &detectionRegExp);
QString parseVersion(const Utils::FilePath &packagePath) const final; QString parseVersion(const Utils::FilePath &packagePath) const final;
private: private:
const Utils::FilePath m_detectionPath; const Utils::FilePaths m_detectionPaths;
const QStringList m_detectionArgs; const QStringList m_detectionArgs;
const QString m_detectionRegExp; const QString m_detectionRegExp;
}; };

View File

@@ -30,7 +30,7 @@ struct PackageDescription
QString description; QString description;
Utils::Key setting; Utils::Key setting;
Utils::FilePath defaultPath; Utils::FilePath defaultPath;
Utils::FilePath detectionPath; Utils::FilePaths detectionPaths;
QStringList versions; QStringList versions;
VersionDetection versionDetection; VersionDetection versionDetection;
bool shouldAddToSystemPath; bool shouldAddToSystemPath;

View File

@@ -33,8 +33,8 @@ McuPackageVersionDetector *createVersionDetection(const VersionDetection &versio
versionDetection.xmlAttribute, versionDetection.xmlAttribute,
versionDetection.regex}; versionDetection.regex};
else if (!versionDetection.executableArgs.isEmpty()) else if (!versionDetection.executableArgs.isEmpty())
return new McuPackageExecutableVersionDetector{Utils::FilePath::fromUserInput( return new McuPackageExecutableVersionDetector{{Utils::FilePath::fromUserInput(
versionDetection.filePattern), versionDetection.filePattern)},
QStringList{versionDetection.executableArgs}, QStringList{versionDetection.executableArgs},
versionDetection.regex}; versionDetection.regex};
else if (!versionDetection.filePattern.isEmpty() && !versionDetection.regex.isEmpty()) else if (!versionDetection.filePattern.isEmpty() && !versionDetection.regex.isEmpty())
@@ -130,7 +130,7 @@ McuPackagePtr McuTargetFactory::createPackage(const PackageDescription &pkgDesc)
return McuPackagePtr{new McuPackage{settingsHandler, return McuPackagePtr{new McuPackage{settingsHandler,
pkgDesc.label, pkgDesc.label,
pkgDesc.defaultPath, pkgDesc.defaultPath,
pkgDesc.detectionPath, pkgDesc.detectionPaths,
pkgDesc.setting, pkgDesc.setting,
pkgDesc.cmakeVar, pkgDesc.cmakeVar,
pkgDesc.envVar, pkgDesc.envVar,
@@ -166,7 +166,7 @@ McuToolChainPackage *McuTargetFactory::createToolchain(
return new McuToolChainPackage{settingsHandler, return new McuToolChainPackage{settingsHandler,
compilerDescription.label, compilerDescription.label,
compilerDescription.defaultPath, compilerDescription.defaultPath,
compilerDescription.detectionPath, compilerDescription.detectionPaths,
{}, {},
toolchainType, toolchainType,
toolchain.versions, toolchain.versions,
@@ -206,7 +206,7 @@ McuToolChainPackage *McuTargetFactory::createToolchain(
return new McuToolChainPackage{settingsHandler, return new McuToolChainPackage{settingsHandler,
compilerDescription.label, compilerDescription.label,
compilerDescription.defaultPath, compilerDescription.defaultPath,
compilerDescription.detectionPath, compilerDescription.detectionPaths,
compilerDescription.setting, compilerDescription.setting,
toolchainType, toolchainType,
toolchain.versions, toolchain.versions,

View File

@@ -27,10 +27,10 @@ constexpr auto gcc_desktop_json = R"(
"defaultValue": "/usr", "defaultValue": "/usr",
"versionDetection": { "versionDetection": {
"executableArgs": "--version", "executableArgs": "--version",
"filePattern": "bin/g++", "filePattern": "bin/g++*",
"regex": "\\b(\\d+\\.\\d+\\.\\d+)\\b" "regex": "\\b(\\d+\\.\\d+\\.\\d+)\\b"
}, },
"detectionPath": "bin/g++" "detectionPath": "bin/g++*"
} }
} }
} }

View File

@@ -20,7 +20,7 @@ public:
MOCK_METHOD(void, setPath, (const Utils::FilePath &) ); MOCK_METHOD(void, setPath, (const Utils::FilePath &) );
MOCK_METHOD(QString, label, (), (const)); MOCK_METHOD(QString, label, (), (const));
MOCK_METHOD(Utils::FilePath, defaultPath, (), (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(QString, statusText, (), (const));
MOCK_METHOD(void, updateStatus, ()); MOCK_METHOD(void, updateStatus, ());
MOCK_METHOD(Utils::Key, settingsKey, (), (const)); MOCK_METHOD(Utils::Key, settingsKey, (), (const));

View File

@@ -190,7 +190,7 @@ const PackageDescription
{}, {},
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
qtForMcuSdkPath, qtForMcuSdkPath,
Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH, {Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH},
{}, {},
VersionDetection{}, VersionDetection{},
false, false,
@@ -323,7 +323,7 @@ void verifyBoardSdk(const McuPackagePtr &boardSdk,
QCOMPARE(boardSdk->cmakeVariableName(), cmakeVariable); QCOMPARE(boardSdk->cmakeVariableName(), cmakeVariable);
QCOMPARE(boardSdk->environmentVariableName(), environmentVariable); QCOMPARE(boardSdk->environmentVariableName(), environmentVariable);
QCOMPARE(boardSdk->settingsKey(), keyFromString(environmentVariable)); QCOMPARE(boardSdk->settingsKey(), keyFromString(environmentVariable));
QCOMPARE(boardSdk->detectionPath().toString(), empty); QVERIFY(boardSdk->detectionPaths().empty());
QCOMPARE(boardSdk->versions(), versions); QCOMPARE(boardSdk->versions(), versions);
} }
@@ -339,7 +339,7 @@ void verifyFreeRtosPackage(const McuPackagePtr &freeRtos,
QCOMPARE(freeRtos->cmakeVariableName(), freeRtosCMakeVar); QCOMPARE(freeRtos->cmakeVariableName(), freeRtosCMakeVar);
QCOMPARE(freeRtos->settingsKey(), expectedSettingsKey); QCOMPARE(freeRtos->settingsKey(), expectedSettingsKey);
QCOMPARE(freeRtos->path().cleanPath().toString(), freeRtosPath); 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())); QVERIFY(freeRtos->path().toUserOutput().startsWith(boardSdkDir.cleanPath().toUserOutput()));
} }
@@ -359,7 +359,10 @@ void verifyPackage(const McuPackagePtr &package,
QCOMPARE(package->cmakeVariableName(), cmakeVar); QCOMPARE(package->cmakeVariableName(), cmakeVar);
QCOMPARE(package->environmentVariableName(), envVar); QCOMPARE(package->environmentVariableName(), envVar);
QCOMPARE(package->label(), label); 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->settingsKey(), setting);
QCOMPARE(package->versions(), versions); QCOMPARE(package->versions(), versions);
} }
@@ -464,7 +467,7 @@ void McuSupportTest::initTestCase()
EXPECT_CALL(*freeRtosPackage, path()) EXPECT_CALL(*freeRtosPackage, path())
.WillRepeatedly(Return(FilePath::fromUserInput(freeRtosPath))); .WillRepeatedly(Return(FilePath::fromUserInput(freeRtosPath)));
EXPECT_CALL(*freeRtosPackage, isAddToSystemPath()).WillRepeatedly(Return(true)); EXPECT_CALL(*freeRtosPackage, isAddToSystemPath()).WillRepeatedly(Return(true));
EXPECT_CALL(*freeRtosPackage, detectionPath()).WillRepeatedly(Return(FilePath{})); EXPECT_CALL(*freeRtosPackage, detectionPaths()).WillRepeatedly(Return(QList<FilePath>{}));
ON_CALL(*sdkPackage, label()).WillByDefault(Return(QString{QUL_LABEL})); ON_CALL(*sdkPackage, label()).WillByDefault(Return(QString{QUL_LABEL}));
ON_CALL(*sdkPackage, settingsKey()) ON_CALL(*sdkPackage, settingsKey())
@@ -474,7 +477,7 @@ void McuSupportTest::initTestCase()
ON_CALL(*sdkPackage, isValidStatus()).WillByDefault(Return(true)); ON_CALL(*sdkPackage, isValidStatus()).WillByDefault(Return(true));
ON_CALL(*sdkPackage, path()).WillByDefault(Return(FilePath::fromUserInput(qtForMcuSdkPath))); ON_CALL(*sdkPackage, path()).WillByDefault(Return(FilePath::fromUserInput(qtForMcuSdkPath)));
ON_CALL(*sdkPackage, isAddToSystemPath()).WillByDefault(Return(true)); ON_CALL(*sdkPackage, isAddToSystemPath()).WillByDefault(Return(true));
ON_CALL(*sdkPackage, detectionPath()).WillByDefault(Return(FilePath{})); ON_CALL(*sdkPackage, detectionPaths()).WillByDefault(Return(QList<FilePath>{}));
EXPECT_CALL(*armGccToolchainFilePackage, environmentVariableName()) EXPECT_CALL(*armGccToolchainFilePackage, environmentVariableName())
.WillRepeatedly(Return(QString{QString{}})); .WillRepeatedly(Return(QString{QString{}}));
@@ -484,7 +487,8 @@ void McuSupportTest::initTestCase()
EXPECT_CALL(*armGccToolchainFilePackage, path()) EXPECT_CALL(*armGccToolchainFilePackage, path())
.WillRepeatedly(Return(FilePath::fromUserInput(armGccToolchainFilePath))); .WillRepeatedly(Return(FilePath::fromUserInput(armGccToolchainFilePath)));
EXPECT_CALL(*armGccToolchainFilePackage, isAddToSystemPath()).WillRepeatedly(Return(false)); EXPECT_CALL(*armGccToolchainFilePackage, isAddToSystemPath()).WillRepeatedly(Return(false));
EXPECT_CALL(*armGccToolchainFilePackage, detectionPath()).WillRepeatedly(Return(FilePath{})); EXPECT_CALL(*armGccToolchainFilePackage, detectionPaths())
.WillRepeatedly(Return(QList<FilePath>{}));
ON_CALL(*settingsMockPtr, getPath) ON_CALL(*settingsMockPtr, getPath)
.WillByDefault([](const Key &, QSettings::Scope, const FilePath &m_defaultPath) { .WillByDefault([](const Key &, QSettings::Scope, const FilePath &m_defaultPath) {
@@ -743,7 +747,7 @@ void McuSupportTest::test_createTargets()
freeRtosSetting, freeRtosSetting,
freeRtosLabel, freeRtosLabel,
freeRtosPath, freeRtosPath,
freeRtosDetectionPath, {freeRtosDetectionPath},
{}, {},
VersionDetection{}, VersionDetection{},
true, true,
@@ -796,7 +800,7 @@ void McuSupportTest::test_createPackages()
freeRtosLabel, freeRtosLabel,
freeRtosSetting, freeRtosSetting,
freeRtosPath, freeRtosPath,
freeRtosDetectionPath, {freeRtosDetectionPath},
{}, {},
VersionDetection{}, VersionDetection{},
true, true,
@@ -1259,9 +1263,9 @@ void McuSupportTest::test_legacy_createQtMCUsPackage()
QVERIFY(qtForMCUsSDK); QVERIFY(qtForMCUsSDK);
QCOMPARE(qtForMCUsSDK->settingsKey(), Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK); QCOMPARE(qtForMCUsSDK->settingsKey(), Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK);
QCOMPARE(qtForMCUsSDK->detectionPath(), QCOMPARE(qtForMCUsSDK->detectionPaths(),
FilePath::fromUserInput(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH) {FilePath::fromUserInput(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH)
.withExecutableSuffix()); .withExecutableSuffix()});
QCOMPARE(qtForMCUsSDK->path().toString(), qtForMcuSdkPath); QCOMPARE(qtForMCUsSDK->path().toString(), qtForMcuSdkPath);
} }
@@ -1713,7 +1717,9 @@ void McuSupportTest::test_differentValueForEachOperationSystem()
{ {
const auto packageDescription = parseDescriptionJson(armgcc_mimxrt1050_evk_freertos_json); const auto packageDescription = parseDescriptionJson(armgcc_mimxrt1050_evk_freertos_json);
auto default_path_entry = packageDescription.platform.entries[0].defaultPath.toString(); 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 //TODO: Revisit whether this test is required and not currently covered by the third party packages
if (HostOsInfo::isWindowsHost()) { if (HostOsInfo::isWindowsHost()) {