Files
qt-creator/src/plugins/mcusupport/mcusupportsdk.cpp
Eike Ziller c79d4bd606 MCU: Use qtcEnvironmentVariable* instead of qEnvironmentVariable*
And instead of qgetenv.
Takes Qt Creator's setting at "Environment > System > Environment" into
account, which makes it easier on some platforms to set them (e.g.
macOS), can be configured differently in different settings paths, and
potentially can be changed at runtime (depending on usage).

Change-Id: Ib644dd6178bf9e0a1a75a2c6cf49fd5f3d121cf4
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Piotr Mućko <piotr.mucko@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
2022-08-31 06:19:45 +00:00

820 lines
38 KiB
C++

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "mcusupportsdk.h"
#include "mcuhelpers.h"
#include "mcukitmanager.h"
#include "mculegacyconstants.h"
#include "mcupackage.h"
#include "mcusupportconstants.h"
#include "mcusupportoptions.h"
#include "mcusupportplugin.h"
#include "mcusupportversiondetection.h"
#include "mcutarget.h"
#include "mcutargetdescription.h"
#include "mcutargetfactory.h"
#include "mcutargetfactorylegacy.h"
#include <baremetal/baremetalconstants.h>
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchainmanager.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <QDir>
#include <QDirIterator>
#include <QHash>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariant>
#include <memory>
using namespace ProjectExplorer;
using namespace Utils;
namespace McuSupport::Internal {
namespace {
const char CMAKE_ENTRIES[]{"cmakeEntries"};
} // namespace
McuPackagePtr createQtForMCUsPackage(const SettingsHandler::Ptr &settingsHandler)
{
return McuPackagePtr{
new McuPackage(settingsHandler,
McuPackage::tr("Qt for MCUs SDK"),
FileUtils::homePath(), // defaultPath
FilePath(Legacy::Constants::QT_FOR_MCUS_SDK_PACKAGE_VALIDATION_PATH)
.withExecutableSuffix(), // detectionPath
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, // settingsKey
Legacy::Constants::QUL_CMAKE_VAR,
Legacy::Constants::QUL_ENV_VAR)};
}
namespace Legacy {
static FilePath findInProgramFiles(const QString &folder)
{
for (const auto &envVar : QStringList{"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (!qtcEnvironmentVariableIsSet(envVar))
continue;
const FilePath dir = FilePath::fromUserInput(qtcEnvironmentVariable(envVar)) / folder;
if (dir.exists())
return dir;
}
return {};
}
static McuPackageVersionDetector *generatePackageVersionDetector(const QString &envVar)
{
if (envVar.startsWith("EVK"))
return new McuPackageXmlVersionDetector("*_manifest_*.xml", "ksdk", "version", ".*");
if (envVar.startsWith("STM32"))
return new McuPackageXmlVersionDetector("package.xml",
"PackDescription",
"Release",
R"(\b(\d+\.\d+\.\d+)\b)");
if (envVar.startsWith("RGL"))
return new McuPackageDirectoryVersionDetector("rgl_*_obj_*", R"(\d+\.\d+\.\w+)", false);
return nullptr;
}
McuPackagePtr createBoardSdkPackage(const SettingsHandler::Ptr &settingsHandler,
const McuTargetDescription &desc)
{
const auto generateSdkName = [](const QString &envVar) {
qsizetype postfixPos = envVar.indexOf("_SDK_PATH");
if (postfixPos < 0) {
postfixPos = envVar.indexOf("_DIR");
}
const QString sdkName = postfixPos > 0 ? envVar.left(postfixPos) : envVar;
return QString{"MCU SDK (%1)"}.arg(sdkName);
};
const QString sdkName = generateSdkName(desc.boardSdk.envVar);
const FilePath defaultPath = [&] {
const auto envVar = desc.boardSdk.envVar;
if (qtcEnvironmentVariableIsSet(envVar))
return FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
if (!desc.boardSdk.defaultPath.isEmpty()) {
FilePath defaultPath = FilePath::fromUserInput(QDir::rootPath()
+ desc.boardSdk.defaultPath.toString());
if (defaultPath.exists())
return defaultPath;
}
return FilePath();
}();
const auto versionDetector = generatePackageVersionDetector(desc.boardSdk.envVar);
return McuPackagePtr{new McuPackage(settingsHandler,
sdkName,
defaultPath,
{}, // detection path
desc.boardSdk.envVar, // settings key
Constants::BOARD_SDK_CMAKE_VAR, // cmake var
desc.boardSdk.envVar, // env var
desc.boardSdk.versions,
{}, // download URL
versionDetector)};
}
McuPackagePtr createFreeRTOSSourcesPackage(const SettingsHandler::Ptr &settingsHandler,
const QString &envVar,
const FilePath &boardSdkDir)
{
const QString envVarPrefix = removeRtosSuffix(envVar);
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar))
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
else if (!boardSdkDir.isEmpty())
defaultPath = boardSdkDir;
return McuPackagePtr{
new McuPackage(settingsHandler,
QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix),
defaultPath,
{}, // detection path
QString{Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(envVarPrefix),
"FREERTOS_DIR", // cmake var
envVar, // env var
{}, // versions
"https://freertos.org")}; // download url
}
McuPackagePtr createUnsupportedToolChainFilePackage(const SettingsHandler::Ptr &settingsHandler,
const FilePath &qtForMCUSdkPath)
{
const FilePath toolchainFilePath = qtForMCUSdkPath / Constants::QUL_TOOLCHAIN_CMAKE_DIR
/ "unsupported.cmake";
return McuPackagePtr{new McuPackage(settingsHandler,
{},
toolchainFilePath,
{},
{},
Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
{})};
}
McuToolChainPackagePtr createUnsupportedToolChainPackage(const SettingsHandler::Ptr &settingsHandler)
{
return McuToolChainPackagePtr{
new McuToolChainPackage(settingsHandler,
{},
{},
{},
{},
McuToolChainPackage::ToolChainType::Unsupported,
{},
{},
{},
nullptr)};
}
McuToolChainPackagePtr createMsvcToolChainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
ToolChain *toolChain = McuToolChainPackage::msvcToolChain(
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
const FilePath detectionPath = FilePath("cl").withExecutableSuffix();
const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir() : FilePath();
const auto versionDetector
= new McuPackageExecutableVersionDetector(detectionPath,
{"--version"},
"\\b(\\d+\\.\\d+)\\.\\d+\\b");
return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler,
McuPackage::tr("MSVC Binary directory"),
defaultPath,
detectionPath,
"MsvcToolchain",
McuToolChainPackage::ToolChainType::MSVC,
versions,
{},
{},
versionDetector)};
}
McuToolChainPackagePtr createGccToolChainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
ToolChain *toolChain = McuToolChainPackage::gccToolChain(
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
const FilePath detectionPath = FilePath("bin/g++").withExecutableSuffix();
const FilePath defaultPath = toolChain ? toolChain->compilerCommand().parentDir().parentDir()
: FilePath();
const auto versionDetector
= new McuPackageExecutableVersionDetector(detectionPath,
{"--version"},
"\\b(\\d+\\.\\d+\\.\\d+)\\b");
return McuToolChainPackagePtr{new McuToolChainPackage(settingsHandler,
McuPackage::tr("GCC Toolchain"),
defaultPath,
detectionPath,
"GnuToolchain",
McuToolChainPackage::ToolChainType::GCC,
versions,
{},
{},
versionDetector)};
}
McuToolChainPackagePtr createArmGccToolchainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
const char envVar[] = "ARMGCC_DIR";
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar))
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
if (defaultPath.isEmpty() && HostOsInfo::isWindowsHost()) {
const FilePath installDir = findInProgramFiles("GNU Tools ARM Embedded");
if (installDir.exists()) {
// If GNU Tools installation dir has only one sub dir,
// select the sub dir, otherwise the installation dir.
const FilePaths subDirs = installDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
defaultPath = subDirs.first();
}
}
const FilePath detectionPath = FilePath("bin/arm-none-eabi-g++").withExecutableSuffix();
const auto versionDetector = new McuPackageExecutableVersionDetector(detectionPath,
{"--version"},
R"(\b(\d+\.\d+\.\d+)\b)");
return McuToolChainPackagePtr{
new McuToolChainPackage(settingsHandler,
McuPackage::tr("GNU Arm Embedded Toolchain"),
defaultPath,
detectionPath,
"GNUArmEmbeddedToolchain", // settingsKey
McuToolChainPackage::ToolChainType::ArmGcc, // toolchainType
versions,
Constants::TOOLCHAIN_DIR_CMAKE_VARIABLE, // cmake var
envVar, // env var
versionDetector)};
}
McuToolChainPackagePtr createGhsToolchainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
const char envVar[] = "GHS_COMPILER_DIR";
const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
const auto versionDetector
= new McuPackageExecutableVersionDetector(FilePath("as850").withExecutableSuffix(),
{"-V"},
R"(\bv(\d+\.\d+\.\d+)\b)");
return McuToolChainPackagePtr{
new McuToolChainPackage(settingsHandler,
"Green Hills Compiler",
defaultPath,
FilePath("ccv850").withExecutableSuffix(), // detectionPath
"GHSToolchain", // settingsKey
McuToolChainPackage::ToolChainType::GHS, // toolchainType
versions,
Constants::TOOLCHAIN_DIR_CMAKE_VARIABLE, // cmake var
envVar, // env var
versionDetector)};
}
McuToolChainPackagePtr createGhsArmToolchainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
const char envVar[] = "GHS_ARM_COMPILER_DIR";
const FilePath defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
const auto versionDetector
= new McuPackageExecutableVersionDetector(FilePath("asarm").withExecutableSuffix(),
{"-V"},
R"(\bv(\d+\.\d+\.\d+)\b)");
return McuToolChainPackagePtr{
new McuToolChainPackage(settingsHandler,
"Green Hills Compiler for ARM",
defaultPath,
FilePath("cxarm").withExecutableSuffix(), // detectionPath
"GHSArmToolchain", // settingsKey
McuToolChainPackage::ToolChainType::GHSArm, // toolchainType
versions,
Constants::TOOLCHAIN_DIR_CMAKE_VARIABLE, // cmake var
envVar, // env var
versionDetector)};
}
McuToolChainPackagePtr createIarToolChainPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions)
{
const char envVar[] = "IAR_ARM_COMPILER_DIR";
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar))
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
else {
const ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainManager::toolChain(
[](const ProjectExplorer::ToolChain *t) {
return t->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID;
});
if (tc) {
const FilePath compilerExecPath = tc->compilerCommand();
defaultPath = compilerExecPath.parentDir().parentDir();
}
}
const FilePath detectionPath = FilePath("bin/iccarm").withExecutableSuffix();
const auto *versionDetector
= new McuPackageExecutableVersionDetector(detectionPath,
{"--version"},
R"(\bV(\d+\.\d+\.\d+)\.\d+\b)");
return McuToolChainPackagePtr{
new McuToolChainPackage(settingsHandler,
"IAR ARM Compiler",
defaultPath,
detectionPath,
"IARToolchain", // settings key
McuToolChainPackage::ToolChainType::IAR, // toolchainType
versions,
Constants::TOOLCHAIN_DIR_CMAKE_VARIABLE, // cmake var
envVar, // env var
versionDetector)};
}
McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &settingsHandler)
{
FilePath defaultPath;
const QString cubePath = "STMicroelectronics/STM32Cube/STM32CubeProgrammer";
if (HostOsInfo::isWindowsHost()) {
const FilePath programPath = findInProgramFiles(cubePath);
if (!programPath.isEmpty())
defaultPath = programPath;
} else {
const FilePath programPath = FileUtils::homePath() / cubePath;
if (programPath.exists())
defaultPath = programPath;
}
const FilePath detectionPath = FilePath::fromUserInput(
QLatin1String(Utils::HostOsInfo::isWindowsHost() ? "/bin/STM32_Programmer_CLI.exe"
: "/bin/STM32_Programmer.sh"));
return McuPackagePtr{
new McuPackage(settingsHandler,
McuPackage::tr("STM32CubeProgrammer"),
defaultPath,
detectionPath,
"Stm32CubeProgrammer",
{}, // cmake var
{}, // env var
{}, // versions
"https://www.st.com/en/development-tools/stm32cubeprog.html", // download url
nullptr, // version detector
true, // add to path
"/bin" // relative path modifier
)};
}
McuPackagePtr createMcuXpressoIdePackage(const SettingsHandler::Ptr &settingsHandler)
{
const char envVar[] = "MCUXpressoIDE_PATH";
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar)) {
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
} else if (HostOsInfo::isWindowsHost()) {
const FilePath programPath = FilePath::fromString(QDir::rootPath()) / "nxp";
if (programPath.exists()) {
defaultPath = programPath;
// If default dir has exactly one sub dir that could be the IDE path, pre-select that.
const FilePaths subDirs = defaultPath.dirEntries(
{{"MCUXpressoIDE*"}, QDir::Dirs | QDir::NoDotAndDotDot});
if (subDirs.count() == 1)
defaultPath = subDirs.first();
}
} else {
const FilePath programPath = FilePath::fromUserInput("/usr/local/mcuxpressoide/");
if (programPath.exists())
defaultPath = programPath;
}
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
envVar,
{}, // versions
"https://www.nxp.com/mcuxpresso/ide")}; // download url
}
McuPackagePtr createCypressProgrammerPackage(const SettingsHandler::Ptr &settingsHandler)
{
const char envVar[] = "CYPRESS_AUTO_FLASH_UTILITY_DIR";
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar)) {
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
} else if (HostOsInfo::isWindowsHost()) {
const FilePath candidate = findInProgramFiles("Cypress");
if (candidate.exists()) {
// "Cypress Auto Flash Utility 1.0"
const auto subDirs = candidate.dirEntries({{"Cypress Auto Flash Utility*"}, QDir::Dirs},
QDir::Unsorted);
if (!subDirs.empty())
defaultPath = subDirs.first();
}
}
return McuPackagePtr{
new McuPackage(settingsHandler,
"Cypress Auto Flash Utility",
defaultPath,
FilePath::fromUserInput("/bin/openocd").withExecutableSuffix(),
"CypressAutoFlashUtil", // settings key
"INFINEON_AUTO_FLASH_UTILITY_DIR", // cmake var
envVar)}; // env var
}
McuPackagePtr createRenesasProgrammerPackage(const SettingsHandler::Ptr &settingsHandler)
{
const char envVar[] = "RENESAS_FLASH_PROGRAMMER_PATH";
FilePath defaultPath;
if (qtcEnvironmentVariableIsSet(envVar)) {
defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(envVar));
} else if (HostOsInfo::isWindowsHost()) {
const FilePath candidate = findInProgramFiles("Renesas Electronics/Programming Tools");
if (candidate.exists()) {
// "Renesas Flash Programmer V3.09"
const auto subDirs = candidate.dirEntries({{"Renesas Flash Programmer*"}, QDir::Dirs},
QDir::Unsorted);
if (!subDirs.empty())
defaultPath = subDirs.first();
}
}
return McuPackagePtr{new McuPackage(settingsHandler,
"Renesas Flash Programmer",
defaultPath,
FilePath("rfp-cli").withExecutableSuffix(),
"RenesasFlashProgrammer", // settings key
"RENESAS_FLASH_PROGRAMMER_PATH", // cmake var
envVar)}; // env var
}
} // namespace Legacy
static McuAbstractTargetFactory::Ptr createFactory(bool isLegacy,
const SettingsHandler::Ptr &settingsHandler,
const FilePath &qtMcuSdkPath)
{
McuAbstractTargetFactory::Ptr result;
if (isLegacy) {
static const QHash<QString, Legacy::ToolchainCompilerCreator> toolchainCreators = {
{{"armgcc"}, {[settingsHandler](const QStringList &versions) {
return Legacy::createArmGccToolchainPackage(settingsHandler, versions);
}}},
{{"greenhills"},
[settingsHandler](const QStringList &versions) {
return Legacy::createGhsToolchainPackage(settingsHandler, versions);
}},
{{"iar"}, {[settingsHandler](const QStringList &versions) {
return Legacy::createIarToolChainPackage(settingsHandler, versions);
}}},
{{"msvc"}, {[settingsHandler](const QStringList &versions) {
return Legacy::createMsvcToolChainPackage(settingsHandler, versions);
}}},
{{"gcc"}, {[settingsHandler](const QStringList &versions) {
return Legacy::createGccToolChainPackage(settingsHandler, versions);
}}},
{{"arm-greenhills"}, {[settingsHandler](const QStringList &versions) {
return Legacy::createGhsArmToolchainPackage(settingsHandler, versions);
}}},
};
const FilePath toolchainFilePrefix = qtMcuSdkPath
/ Legacy::Constants::QUL_TOOLCHAIN_CMAKE_DIR;
static const QHash<QString, McuPackagePtr> toolchainFiles
= {{{"armgcc"},
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "armgcc.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
{}}}},
{{"iar"},
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "iar.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
{}}}},
{"greenhills",
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "ghs.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
{}}}},
{"arm-greenhills",
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "arm-ghs.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
{}}}}
};
// Note: the vendor name (the key of the hash) is case-sensitive. It has to match the "platformVendor" key in the
// json file.
static const QHash<QString, McuPackagePtr> vendorPkgs = {
{{"ST"}, McuPackagePtr{Legacy::createStm32CubeProgrammerPackage(settingsHandler)}},
{{"NXP"}, McuPackagePtr{Legacy::createMcuXpressoIdePackage(settingsHandler)}},
{{"CYPRESS"}, McuPackagePtr{Legacy::createCypressProgrammerPackage(settingsHandler)}},
{{"RENESAS"}, McuPackagePtr{Legacy::createRenesasProgrammerPackage(settingsHandler)}},
};
result = std::make_unique<Legacy::McuTargetFactory>(toolchainCreators,
toolchainFiles,
vendorPkgs,
settingsHandler);
} else {
result = std::make_unique<McuTargetFactory>(settingsHandler);
}
return result;
}
McuSdkRepository targetsFromDescriptions(const QList<McuTargetDescription> &descriptions,
const SettingsHandler::Ptr &settingsHandler,
const FilePath &qtForMCUSdkPath,
bool isLegacy)
{
Targets mcuTargets;
Packages mcuPackages;
McuAbstractTargetFactory::Ptr targetFactory = createFactory(isLegacy,
settingsHandler,
qtForMCUSdkPath);
for (const McuTargetDescription &desc : descriptions) {
auto [targets, packages] = targetFactory->createTargets(desc, qtForMCUSdkPath);
mcuTargets.append(targets);
mcuPackages.unite(packages);
}
if (isLegacy) {
auto [toolchainPkgs, vendorPkgs]{targetFactory->getAdditionalPackages()};
for (McuToolChainPackagePtr &package : toolchainPkgs) {
mcuPackages.insert(package);
}
for (McuPackagePtr &package : vendorPkgs) {
mcuPackages.insert(package);
}
}
return McuSdkRepository{mcuTargets, mcuPackages};
}
FilePath kitsPath(const FilePath &qtMcuSdkPath)
{
return qtMcuSdkPath / "kits/";
}
static FilePaths targetDescriptionFiles(const FilePath &dir)
{
return kitsPath(dir).dirEntries(Utils::FileFilter({"*.json"}, QDir::Files));
}
VersionDetection parseVersionDetection(const QJsonObject &packageEntry)
{
const QJsonObject versioning = packageEntry.value("versionDetection").toObject();
return {
versioning["regex"].toString(),
versioning["filePattern"].toString(),
versioning["executableArgs"].toString(),
versioning["xmlElement"].toString(),
versioning["xmlAttribute"].toString(),
versioning["isFile"].toBool(true),
};
}
static PackageDescription parsePackage(const QJsonObject &cmakeEntry)
{
const QVariantList versionsVariantList = cmakeEntry["versions"].toArray().toVariantList();
const auto versions = Utils::transform<QStringList>(versionsVariantList,
[&](const QVariant &version) {
return version.toString();
});
return {cmakeEntry["label"].toString(),
cmakeEntry["envVar"].toString(),
cmakeEntry["cmakeVar"].toString(),
cmakeEntry["description"].toString(),
cmakeEntry["setting"].toString(),
FilePath::fromUserInput(cmakeEntry["defaultValue"].toString()),
FilePath::fromUserInput(cmakeEntry["validation"].toString()),
versions,
parseVersionDetection(cmakeEntry),
false};
}
static QList<PackageDescription> parsePackages(const QJsonArray &cmakeEntries)
{
QList<PackageDescription> result;
for (const auto &cmakeEntryRef : cmakeEntries) {
const QJsonObject cmakeEntry{cmakeEntryRef.toObject()};
result.push_back(parsePackage(cmakeEntry));
}
return result;
}
McuTargetDescription parseDescriptionJson(const QByteArray &data)
{
const QJsonDocument document = QJsonDocument::fromJson(data);
const QJsonObject target = document.object();
const QString qulVersion = target.value("qulVersion").toString();
const QJsonObject platform = target.value("platform").toObject();
const QString compatVersion = target.value("compatVersion").toString();
const QJsonObject toolchain = target.value("toolchain").toObject();
const QJsonObject toolchainFile = toolchain.value("file").toObject();
const QJsonObject compiler = toolchain.value("compiler").toObject();
const QJsonObject boardSdk = target.value("boardSdk").toObject();
const QJsonObject freeRTOS = target.value("freeRTOS").toObject();
const QJsonArray platformEntries = platform.value(CMAKE_ENTRIES).toArray();
const QList<PackageDescription> platformPackages{parsePackages(platformEntries)};
const PackageDescription toolchainPackage = parsePackage(compiler);
const PackageDescription toolchainFilePackage = parsePackage(toolchainFile);
const PackageDescription boardSdkPackage{parsePackage(boardSdk)};
const PackageDescription freeRtosPackage{parsePackage(freeRTOS)};
const QVariantList toolchainVersions = toolchain.value("versions").toArray().toVariantList();
const auto toolchainVersionsList = Utils::transform<QStringList>(toolchainVersions,
[&](const QVariant &version) {
return version.toString();
});
const QVariantList boardSdkVersions = boardSdk.value("versions").toArray().toVariantList();
const auto boardSdkVersionsList = Utils::transform<QStringList>(boardSdkVersions,
[&](const QVariant &version) {
return version.toString();
});
const QVariantList colorDepths = platform.value("colorDepths").toArray().toVariantList();
const auto colorDepthsVector = Utils::transform<QVector<int>>(colorDepths,
[&](const QVariant &colorDepth) {
return colorDepth.toInt();
});
const QString platformName = platform.value("platformName").toString();
return {qulVersion,
compatVersion,
{platform.value("id").toString(),
platformName,
platform.value("vendor").toString(),
colorDepthsVector,
platformName == "Desktop" ? McuTargetDescription::TargetType::Desktop
: McuTargetDescription::TargetType::MCU,
platformPackages},
{toolchain.value("id").toString(),
toolchainVersionsList,
toolchainPackage,
toolchainFilePackage},
boardSdkPackage,
{freeRTOS.value("envVar").toString(), freeRtosPackage}};
}
// https://doc.qt.io/qtcreator/creator-developing-mcu.html#supported-qt-for-mcus-sdks
static const QString legacySupportVersionFor(const QString &sdkVersion)
{
static const QHash<QString, QString> oldSdkQtcRequiredVersion
= {{{"1.0"}, {"4.11.x"}}, {{"1.1"}, {"4.12.0 or 4.12.1"}}, {{"1.2"}, {"4.12.2 or 4.12.3"}}};
if (oldSdkQtcRequiredVersion.contains(sdkVersion))
return oldSdkQtcRequiredVersion.value(sdkVersion);
if (QVersionNumber::fromString(sdkVersion).majorVersion() == 1)
return "4.12.4 up to 6.0";
return QString();
}
bool checkDeprecatedSdkError(const FilePath &qulDir, QString &message)
{
const McuPackagePathVersionDetector versionDetector(R"((?<=\bQtMCUs.)(\d+\.\d+))");
const QString sdkDetectedVersion = versionDetector.parseVersion(qulDir);
const QString legacyVersion = legacySupportVersionFor(sdkDetectedVersion);
if (!legacyVersion.isEmpty()) {
message = McuTarget::tr("Qt for MCUs SDK version %1 detected, "
"only supported by Qt Creator version %2. "
"This version of Qt Creator requires Qt for MCUs %3 or greater.")
.arg(sdkDetectedVersion,
legacyVersion,
McuSupportOptions::minimalQulVersion().toString());
return true;
}
return false;
}
McuSdkRepository targetsAndPackages(const FilePath &qtForMCUSdkPath,
const SettingsHandler::Ptr &settingsHandler)
{
QList<McuTargetDescription> descriptions;
bool isLegacy{false};
const FilePaths descriptionFiles = targetDescriptionFiles(qtForMCUSdkPath);
for (const FilePath &filePath : descriptionFiles) {
if (!filePath.isReadableFile())
continue;
const McuTargetDescription desc = parseDescriptionJson(filePath.fileContents());
bool ok = false;
const int compatVersion = desc.compatVersion.toInt(&ok);
if (!desc.compatVersion.isEmpty() && ok && compatVersion > MAX_COMPATIBILITY_VERSION) {
printMessage(McuTarget::tr("Skipped %1. Unsupported version \"%2\".")
.arg(QDir::toNativeSeparators(filePath.fileNameWithPathComponents(1)),
desc.qulVersion),
false);
continue;
}
const auto qulVersion{QVersionNumber::fromString(desc.qulVersion)};
isLegacy = McuSupportOptions::isLegacyVersion(qulVersion);
if (qulVersion < McuSupportOptions::minimalQulVersion()) {
const QString legacyVersion = legacySupportVersionFor(desc.qulVersion);
const QString qtcSupportText
= !legacyVersion.isEmpty()
? McuTarget::tr("Detected version \"%1\", only supported by Qt Creator %2.")
.arg(desc.qulVersion, legacyVersion)
: McuTarget::tr("Unsupported version \"%1\".").arg(desc.qulVersion);
printMessage(McuTarget::tr("Skipped %1. %2 Qt for MCUs version >= %3 required.")
.arg(QDir::toNativeSeparators(filePath.fileNameWithPathComponents(1)),
qtcSupportText,
McuSupportOptions::minimalQulVersion().toString()),
false);
continue;
}
descriptions.append(desc);
}
// No valid description means invalid or old SDK installation.
if (descriptions.empty()) {
if (kitsPath(qtForMCUSdkPath).exists()) {
printMessage(McuTarget::tr("No valid kit descriptions found at %1.")
.arg(kitsPath(qtForMCUSdkPath).toUserOutput()),
true);
return McuSdkRepository{};
} else {
QString deprecationMessage;
if (checkDeprecatedSdkError(qtForMCUSdkPath, deprecationMessage)) {
printMessage(deprecationMessage, true);
return McuSdkRepository{};
}
}
}
McuSdkRepository repo = targetsFromDescriptions(descriptions,
settingsHandler,
qtForMCUSdkPath,
isLegacy);
// Keep targets sorted lexicographically
Utils::sort(repo.mcuTargets, [](const McuTargetPtr &lhs, const McuTargetPtr &rhs) {
return McuKitManager::generateKitNameFromTarget(lhs.get())
< McuKitManager::generateKitNameFromTarget(rhs.get());
});
for (const auto &target : repo.mcuTargets) {
printMessage(McuTarget::tr("Kit for %1 created.")
.arg(McuKitManager::generateKitNameFromTarget(target.get())),
false);
}
return repo;
}
} // namespace McuSupport::Internal