McuSupport: Evaluate environment variables in paths

Packages are created as defined in JSON. Some paths
contain variables. This creates dependencies between
packages. After all packages are created and collected
environment variables are evaluated in one pass. McuTarget
packages get updated.

This is needed because we shouldn't show the user paths
with variables in them. Also file picker wouldn't work.

There will be cmake variables support in separate commit.

Change-Id: Id210ea394f3f5bb5a14d87f3cf6a0a9a99e690bf
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Piotr Mućko
2022-08-09 10:56:33 +02:00
parent 662281e837
commit cb24da01b3
9 changed files with 121 additions and 35 deletions

View File

@@ -57,6 +57,7 @@ public:
virtual Utils::FilePath basePath() const = 0;
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 QString settingsKey() const = 0;

View File

@@ -124,7 +124,7 @@ FilePath McuPackage::basePath() const
FilePath McuPackage::path() const
{
return (basePath() / m_relativePathModifier.path()).absoluteFilePath().cleanPath();
return (basePath() / m_relativePathModifier.path()).cleanPath();
}
FilePath McuPackage::defaultPath() const
@@ -137,6 +137,13 @@ FilePath McuPackage::detectionPath() const
return m_detectionPath;
}
void McuPackage::setPath(const FilePath &newPath)
{
m_path = newPath;
m_defaultPath = newPath;
updateStatus();
}
void McuPackage::updatePath()
{
m_path = m_fileChooser->rawFilePath();

View File

@@ -90,6 +90,8 @@ public:
QWidget *widget() override;
const McuPackageVersionDetector *getVersionDetector() const override;
void setPath(const Utils::FilePath &);
private:
void updatePath();
void updateStatusUi();
@@ -100,7 +102,7 @@ private:
Utils::InfoLabel *m_infoLabel = nullptr;
const QString m_label;
const Utils::FilePath m_defaultPath;
Utils::FilePath m_defaultPath;
const Utils::FilePath m_detectionPath;
const QString m_settingsKey;
QScopedPointer<const McuPackageVersionDetector> m_versionDetector;

View File

@@ -78,6 +78,34 @@ McuPackageVersionDetector *createVersionDetection(const VersionDetection &versio
versionDetection.isFile);
}
static void evaluateVariables(McuTarget &target)
{
const static QRegularExpression variableRegex{R"(\${*\w+}*)",
QRegularExpression::CaseInsensitiveOption};
for (const auto &package : target.packages()) {
const QRegularExpressionMatch match{variableRegex.match(package->path().toString())};
if (!match.hasMatch())
continue;
const QString variable{match.captured(0).remove(0, 1)};
McuPackagePtr packageDefiningVariable{
Utils::findOrDefault(target.packages(), [variable](const McuPackagePtr &pkg) {
return pkg->cmakeVariableName() == variable;
// return pkg->cmakeVariableName() == variable || pkg->environmentVariableName() == variable;
})};
if (packageDefiningVariable == nullptr) // nothing provides the variable
continue;
const auto evaluatedPath{Utils::FilePath::fromUserInput(
package->path().toString().replace(match.capturedStart(),
match.capturedLength(),
packageDefiningVariable->path().toString()))};
package->setPath(evaluatedPath);
}
}
McuTargetFactory::McuTargetFactory(const SettingsHandler::Ptr &settingsHandler)
: settingsHandler{settingsHandler}
{}
@@ -102,13 +130,17 @@ QPair<Targets, Packages> McuTargetFactory::createTargets(const McuTargetDescript
targetPackages.insert({toolchainPtr});
targetPackages.unite({toolchainFile});
packages.unite(targetPackages);
mcuTargets.append(McuTargetPtr{new McuTarget{QVersionNumber::fromString(desc.qulVersion),
McuTargetPtr target{new McuTarget{QVersionNumber::fromString(desc.qulVersion),
platform,
deduceOperatingSystem(desc),
targetPackages,
toolchainPtr,
toolchainFile,
colorDepth}});
colorDepth}};
evaluateVariables(*target);
mcuTargets.append(target);
}
return {mcuTargets, packages};
}

View File

@@ -76,7 +76,7 @@ QPair<Targets, Packages> McuTargetFactory::createTargets(const McuTargetDescript
boardSdkPkgs.insert(desc.boardSdk.envVar, boardSdkPkg);
}
McuPackagePtr boardSdkPkg{boardSdkPkgs.value(desc.boardSdk.envVar)};
boardSdkDefaultPath = boardSdkPkg->defaultPath();
boardSdkDefaultPath = boardSdkPkg->path();
required3rdPartyPkgs.insert(boardSdkPkg);
}

View File

@@ -53,6 +53,7 @@ constexpr auto armgcc_stm32f769i_freertos_json = R"({
"id": "Qul_DIR",
"label": "Qt for MCUs SDK",
"type": "path",
"setting": "QtForMCUsSdk",
"cmakeVar": "Qul_ROOT",
"optional": false
}
@@ -83,7 +84,7 @@ constexpr auto armgcc_stm32f769i_freertos_json = R"({
"label": "CMake Toolchain File",
"cmakeVar": "CMAKE_TOOLCHAIN_FILE",
"type": "file",
"defaultValue": "/opt/qtformcu/2.2//lib/cmake/Qul/toolchain/armgcc.cmake",
"defaultValue": "$Qul_ROOT//lib/cmake/Qul/toolchain/armgcc.cmake",
"visible": false,
"optional": false
}

View File

@@ -37,6 +37,7 @@ class PackageMock : public McuAbstractPackage
public:
MOCK_METHOD(Utils::FilePath, basePath, (), (const));
MOCK_METHOD(Utils::FilePath, path, (), (const));
MOCK_METHOD(void, setPath, (const Utils::FilePath &) );
MOCK_METHOD(QString, label, (), (const));
MOCK_METHOD(Utils::FilePath, defaultPath, (), (const));
MOCK_METHOD(Utils::FilePath, detectionPath, (), (const));

View File

@@ -79,7 +79,10 @@ const char armGccDirectorySetting[]{"GNUArmEmbeddedToolchain"};
const char armGccEnvVar[]{"ARMGCC_DIR"};
const char armGccLabel[]{"GNU Arm Embedded Toolchain"};
const char armGccSuffix[]{"bin/arm-none-eabi-g++"};
const char armGccToolchainFilePath[]{"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/armgcc.cmake"};
const char armGccToolchainFilePath[]{"/opt/toolchain/armgcc.cmake"};
const char armGccToolchainFileDefaultPath[]{
"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/armgcc.cmake"};
const char armGccToolchainFilePathWithVariable[]{"$Qul_ROOT/lib/cmake/Qul/toolchain/armgcc.cmake"};
const char armGccVersion[]{"9.3.1"};
const char armGccNewVersion[]{"10.3.1"};
const char msvcVersion[]{"14.29"};
@@ -92,7 +95,9 @@ const char freeRtosEnvVar[]{"EVK_MIMXRT1170_FREERTOS_PATH"};
const char freeRtosLabel[]{"FreeRTOS directory"};
const char freeRtosPath[]{"/opt/freertos/default"};
const char freeRtosSetting[]{"Freertos"};
const char greenhillToolchainFilePath[]{"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/ghs.cmake"};
const char greenhillToolchainFilePath[]{"/opt/toolchain/ghs.cmake"};
const char greenhillToolchainFileDefaultPath[]{
"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/ghs.cmake"};
const char greenhillCompilerDir[]{"/abs/ghs"};
const char greenhillSetting[]{"GHSToolchain"};
const QStringList greenhillVersions{{"2018.1.5"}};
@@ -100,7 +105,8 @@ const char iarDir[]{"/opt/iar/compiler"};
const char iarEnvVar[]{"IAR_ARM_COMPILER_DIR"};
const char iarLabel[]{"IAR ARM Compiler"};
const char iarSetting[]{"IARToolchain"};
const char iarToolchainFilePath[]{"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/iar.cmake"};
const char iarToolchainFilePath[]{"/opt/toolchain/iar.cmake"};
const char iarToolchainFileDefaultPath[]{"/opt/qtformcu/2.2/lib/cmake/Qul/toolchain/iar.cmake"};
const char iarVersionDetectionRegex[]{R"(\bV(\d+\.\d+\.\d+)\.\d+\b)"};
const QStringList iarVersions{{"8.50.9"}};
const char iar[]{"iar"};
@@ -247,6 +253,7 @@ void verifyTargetToolchains(const Targets &targets,
QCOMPARE(toolchainFile->cmakeVariableName(), Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE);
QCOMPARE(toolchainFile->settingsKey(), empty);
QCOMPARE(toolchainFile->path().toString(), toolchainFilePath);
QCOMPARE(toolchainFile->defaultPath().toString(), toolchainFilePath);
const auto toolchainCompiler{target->toolChainPackage()};
QVERIFY(toolchainCompiler);
@@ -393,26 +400,26 @@ void McuSupportTest::test_parseToolchainFromJSON_data()
QTest::addColumn<QString>("json");
QTest::addColumn<QString>("environmentVariable");
QTest::addColumn<QString>("label");
QTest::addColumn<QString>("toolchainFile");
QTest::addColumn<QString>("toolchainFileDefaultPath");
QTest::addColumn<QString>("id");
//TODO(me): Add ghs nxp 1064 nxp 1070.
QTest::newRow("armgcc_nxp_1050_json")
<< armgcc_nxp_1050_json << armGccEnvVar << armGccLabel << armGccToolchainFilePath << armGcc;
QTest::newRow("armgcc_nxp_1050_json") << armgcc_nxp_1050_json << armGccEnvVar << armGccLabel
<< armGccToolchainFileDefaultPath << armGcc;
QTest::newRow("armgcc_stm32f769i_freertos_json")
<< armgcc_stm32f769i_freertos_json << armGccEnvVar << armGccLabel << armGccToolchainFilePath
<< armGcc;
<< armgcc_stm32f769i_freertos_json << armGccEnvVar << armGccLabel
<< armGccToolchainFilePathWithVariable << armGcc;
QTest::newRow("armgcc_stm32h750b_metal_json")
<< armgcc_stm32h750b_metal_json << armGccEnvVar << armGccLabel << armGccToolchainFilePath
<< armGcc;
<< armgcc_stm32h750b_metal_json << armGccEnvVar << armGccLabel
<< armGccToolchainFileDefaultPath << armGcc;
QTest::newRow("armgcc_nxp_mimxrt1170_evk_freertos_json")
<< armgcc_nxp_mimxrt1170_evk_freertos_json << armGccEnvVar << armGccLabel
<< armGccToolchainFilePath << armGcc;
<< armGccToolchainFileDefaultPath << armGcc;
QTest::newRow("iar_stm32f469i_metal_json")
<< iar_stm32f469i_metal_json << iarEnvVar << iarLabel << iarToolchainFilePath << iar;
<< iar_stm32f469i_metal_json << iarEnvVar << iarLabel << iarToolchainFileDefaultPath << iar;
}
void McuSupportTest::test_parseToolchainFromJSON()
@@ -420,8 +427,9 @@ void McuSupportTest::test_parseToolchainFromJSON()
QFETCH(QString, json);
QFETCH(QString, environmentVariable);
QFETCH(QString, label);
QFETCH(QString, toolchainFile);
QFETCH(QString, toolchainFileDefaultPath);
QFETCH(QString, id);
McuTargetDescription description{parseDescriptionJson(json.toLocal8Bit())};
QCOMPARE(description.toolchain.id, id);
@@ -433,7 +441,7 @@ void McuSupportTest::test_parseToolchainFromJSON()
QCOMPARE(toolchainFilePackage.label, cmakeToolchainLabel);
QCOMPARE(toolchainFilePackage.envVar, QString{});
QCOMPARE(toolchainFilePackage.cmakeVar, Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE);
QCOMPARE(toolchainFilePackage.defaultPath.cleanPath().toString(), toolchainFile);
QCOMPARE(toolchainFilePackage.defaultPath.cleanPath().toString(), toolchainFileDefaultPath);
}
void McuSupportTest::test_legacy_createIarToolchain()
@@ -836,22 +844,22 @@ void McuSupportTest::test_legacy_createTargetWithToolchainPackages_data()
QTest::addColumn<QString>("compilerSetting");
QTest::addColumn<QStringList>("versions");
QTest::newRow("nxp1050") << armgcc_nxp_1050_json << armGccToolchainFilePath << armGccDir
QTest::newRow("nxp1050") << armgcc_nxp_1050_json << armGccToolchainFileDefaultPath << armGccDir
<< armGccDirectorySetting
<< QStringList{armGccVersion, armGccNewVersion};
QTest::newRow("stm32h750b") << armgcc_stm32h750b_metal_json << armGccToolchainFilePath
QTest::newRow("stm32h750b") << armgcc_stm32h750b_metal_json << armGccToolchainFileDefaultPath
<< armGccDir << armGccDirectorySetting
<< QStringList{armGccVersion};
QTest::newRow("stm32f769i") << armgcc_stm32f769i_freertos_json << armGccToolchainFilePath
QTest::newRow("stm32f769i") << armgcc_stm32f769i_freertos_json << armGccToolchainFileDefaultPath
<< armGccDir << armGccDirectorySetting
<< QStringList{armGccVersion, armGccNewVersion};
QTest::newRow("stm32f469i") << iar_stm32f469i_metal_json << iarToolchainFilePath << iarDir
<< iarSetting << iarVersions;
QTest::newRow("stm32f469i") << iar_stm32f469i_metal_json << iarToolchainFileDefaultPath
<< iarDir << iarSetting << iarVersions;
QTest::newRow("iar_nxp_1064_json")
<< iar_nxp_1064_json << iarToolchainFilePath << iarDir << iarSetting << iarVersions;
<< iar_nxp_1064_json << iarToolchainFileDefaultPath << iarDir << iarSetting << iarVersions;
QTest::newRow("ghs_rh850_d1m1a_baremetal_json")
<< ghs_rh850_d1m1a_baremetal_json << greenhillToolchainFilePath << greenhillCompilerDir
<< greenhillSetting << greenhillVersions;
<< ghs_rh850_d1m1a_baremetal_json << greenhillToolchainFileDefaultPath
<< greenhillCompilerDir << greenhillSetting << greenhillVersions;
}
void McuSupportTest::test_legacy_createTargetWithToolchainPackages()
@@ -893,8 +901,9 @@ void McuSupportTest::test_createTargetWithToolchainPackages()
EXPECT_CALL(*settingsMockPtr, getPath(compilerSetting, _, _))
.WillRepeatedly(Return(FilePath::fromUserInput(compilerPath)));
EXPECT_CALL(*settingsMockPtr, getPath(compilerSetting, _, _))
.WillRepeatedly(Return(FilePath::fromUserInput(compilerPath)));
EXPECT_CALL(*settingsMockPtr,
getPath(QString{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _))
.WillRepeatedly(Return(FilePath::fromUserInput(qtForMcuSdkPath)));
const McuTargetDescription description = parseDescriptionJson(json.toLocal8Bit());
const auto [targets, packages]{
@@ -1134,4 +1143,35 @@ void McuSupportTest::test_passDirectoryVersionDetectorToRenesasBoardSdkPackage()
QCOMPARE(typeid(*versionDetector).name(), typeid(McuPackageDirectoryVersionDetector).name());
}
void McuSupportTest::test_resolveEnvironmentVariablesInDefaultPaths()
{
QVERIFY(qputenv(qulEnvVar, qtForMcuSdkPath));
QCOMPARE(qEnvironmentVariable(qulEnvVar), qtForMcuSdkPath);
toochainFileDescription.defaultPath = "$Qul_ROOT/lib/cmake/Qul/toolchain/iar.cmake";
targetDescription.toolchain.file = toochainFileDescription;
auto [targets, packages] = targetFactory.createTargets(targetDescription, qtForMcuSdkPath);
auto qtForMCUPkg = findOrDefault(packages, [](const McuPackagePtr &pkg) {
return (pkg->cmakeVariableName() == Legacy::Constants::QUL_ENV_VAR);
});
QVERIFY(qtForMCUPkg);
QCOMPARE(qtForMCUPkg->path().toString(), qtForMcuSdkPath);
auto toolchainFilePkg = findOrDefault(packages, [](const McuPackagePtr &pkg) {
return (pkg->cmakeVariableName() == Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE);
});
QVERIFY(toolchainFilePkg);
QVERIFY(targets.size() == 1);
QString expectedPkgPath = QString{qtForMcuSdkPath} + "/lib/cmake/Qul/toolchain/iar.cmake";
QCOMPARE(toolchainFilePkg->path().toString(), expectedPkgPath);
QVERIFY(toolchainFilePkg->path().toString().startsWith(qtForMcuSdkPath));
QCOMPARE(toolchainFilePkg->defaultPath().toString(), expectedPkgPath);
QVERIFY(qunsetenv(qulEnvVar));
}
} // namespace McuSupport::Internal::Test

View File

@@ -106,6 +106,8 @@ private slots:
void test_createBoardSdk_data();
void test_createBoardSdk();
void test_resolveEnvironmentVariablesInDefaultPaths();
private:
QVersionNumber currentQulVersion{2, 0};
PackageMock *freeRtosPackage{new PackageMock};