From ee4c998ff3d58ba693c873e97c4bf5468cb364f8 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 26 Sep 2022 18:59:04 +0200 Subject: [PATCH] CMakePM: Add supprot for CMakePresets version 3 This includes Condition and toolchainFile https://cmake.org/cmake/help/v3.21/manual/cmake-presets.7.html Task-number: QTCREATORBUG-24555 Change-Id: I1026390af67b2be1aa0c3b02b654fc19442d3c89 Reviewed-by: Alessandro Portale --- .../cmakebuildconfiguration.cpp | 55 ++++-- .../cmakeprojectimporter.cpp | 19 ++- .../cmakeprojectmanager/presetsmacros.cpp | 127 ++++++++++++++ .../cmakeprojectmanager/presetsmacros.h | 20 +++ .../cmakeprojectmanager/presetsparser.cpp | 158 ++++++++++++++++++ .../cmakeprojectmanager/presetsparser.h | 44 +++++ tests/manual/cmakepresets/CMakePresets.json | 34 +++- 7 files changed, 430 insertions(+), 27 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 119a5c1d3f8..784ebe419b2 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -1168,7 +1168,8 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArguments, const CMakeProject *project, const Kit *k, - const Utils::Environment &env) + const Utils::Environment &env, + const Utils::FilePath &buildDirectory) { const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k); @@ -1230,6 +1231,11 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume initialArguments.append("--debug-output"); } + CMakePresets::Macros::updateToolchainFile(configurePreset, + env, + project->projectDirectory(), + buildDirectory); + // Merge the presets cache variables CMakeConfig cache; if (configurePreset.cacheVariables) @@ -1547,15 +1553,16 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) CMakeProject *cmakeProject = static_cast(target->project()); setUserConfigureEnvironmentChanges(getEnvironmentItemsFromCMakeConfigurePreset(cmakeProject, k)); + updateAndEmitConfigureEnvironmentChanged(); QStringList initialCMakeArguments = cmd.splitArguments(); addCMakeConfigurePresetToInitialArguments(initialCMakeArguments, cmakeProject, k, - configureEnvironment()); + configureEnvironment(), + info.buildDirectory); m_buildSystem->setInitialCMakeArguments(initialCMakeArguments); m_buildSystem->setCMakeBuildType(buildType); - updateAndEmitConfigureEnvironmentChanged(); setBuildPresetToBuildSteps(target); }); @@ -1825,24 +1832,31 @@ void CMakeBuildConfiguration::setInitialBuildAndCleanSteps(const ProjectExplorer const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem( target->kit()); + int buildSteps = 1; if (!presetItem.isNull()) { const QString presetName = presetItem.expandedValue(target->kit()); const CMakeProject *project = static_cast(target->project()); const auto buildPresets = project->presetsData().buildPresets; - const int count = std::count_if(buildPresets.begin(), - buildPresets.end(), - [presetName](const PresetsDetails::BuildPreset &preset) { - return preset.configurePreset == presetName - && !preset.hidden.value(); - }); + const int count + = std::count_if(buildPresets.begin(), + buildPresets.end(), + [presetName, project](const PresetsDetails::BuildPreset &preset) { + bool enabled = true; + if (preset.condition) + enabled = CMakePresets::Macros::evaluatePresetCondition( + preset, project->projectDirectory()); - for (int i = 0; i < count; ++i) - appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); - - } else { - appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); + return preset.configurePreset == presetName + && !preset.hidden.value() && enabled; + }); + if (count != 0) + buildSteps = count; } + + for (int i = 0; i < buildSteps; ++i) + appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); + appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID); } @@ -1858,10 +1872,15 @@ void CMakeBuildConfiguration::setBuildPresetToBuildSteps(const ProjectExplorer:: const CMakeProject *project = static_cast(target->project()); const auto allBuildPresets = project->presetsData().buildPresets; - const auto buildPresets - = Utils::filtered(allBuildPresets, [presetName](const PresetsDetails::BuildPreset &preset) { - return preset.configurePreset == presetName && !preset.hidden.value(); - }); + const auto buildPresets = Utils::filtered( + allBuildPresets, [presetName, project](const PresetsDetails::BuildPreset &preset) { + bool enabled = true; + if (preset.condition) + enabled = CMakePresets::Macros::evaluatePresetCondition(preset, + project->projectDirectory()); + + return preset.configurePreset == presetName && !preset.hidden.value() && enabled; + }); const QList buildStepList = Utils::filtered(buildSteps()->steps(), [](const BuildStep *bs) { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 2c207226f2f..3e6b0ece8e4 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -123,6 +123,11 @@ FilePaths CMakeProjectImporter::importCandidates() if (configPreset.hidden.value()) continue; + if (configPreset.condition) { + if (!CMakePresets::Macros::evaluatePresetCondition(configPreset, projectFilePath())) + continue; + } + const FilePath configPresetDir = m_presetsTempDir.filePath(configPreset.name); configPresetDir.createDir(); candidates << configPresetDir; @@ -403,13 +408,15 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, auto data = std::make_unique(); const QString presetName = importPath.fileName(); - PresetsDetails::ConfigurePreset configurePreset = Utils::findOrDefault(m_presetsData.configurePresets, [presetName](const PresetsDetails::ConfigurePreset &preset) { return preset.name == presetName; }); + Environment env = Environment::systemEnvironment(); + CMakePresets::Macros::expand(configurePreset, env, projectDirectory()); + if (configurePreset.displayName) data->cmakePresetDisplayname = configurePreset.displayName.value(); else @@ -435,14 +442,16 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, QString binaryDir = importPath.toString(); if (configurePreset.binaryDir) { binaryDir = configurePreset.binaryDir.value(); - CMakePresets::Macros::expand(configurePreset, - Environment::systemEnvironment(), - projectDirectory(), - binaryDir); + CMakePresets::Macros::expand(configurePreset, env, projectDirectory(), binaryDir); } data->buildDirectory = Utils::FilePath::fromString(binaryDir); + CMakePresets::Macros::updateToolchainFile(configurePreset, + env, + projectDirectory(), + data->buildDirectory); + const CMakeConfig cache = configurePreset.cacheVariables ? configurePreset.cacheVariables.value() : CMakeConfig(); diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp index 29ecdf946bf..37ef043c12e 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp +++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp @@ -12,6 +12,25 @@ namespace CMakeProjectManager::Internal::CMakePresets::Macros { +QString getHostSystemName() +{ + Utils::OsType osType = Utils::HostOsInfo::hostOs(); + + switch (osType) { + case Utils::OsTypeWindows: + return "Windows"; + case Utils::OsTypeLinux: + return "Linux"; + case Utils::OsTypeMac: + return "Darwin"; + case Utils::OsTypeOtherUnix: + return "Unix"; + case Utils::OsTypeOther: + return "Other"; + } + return "Other"; +} + void expandAllButEnv(const PresetsDetails::ConfigurePreset &preset, const Utils::FilePath &sourceDirectory, QString &value) @@ -25,6 +44,8 @@ void expandAllButEnv(const PresetsDetails::ConfigurePreset &preset, value.replace("${presetName}", preset.name); if (preset.generator) value.replace("${generator}", preset.generator.value()); + + value.replace("${hostSystemName}", getHostSystemName()); } void expandAllButEnv(const PresetsDetails::BuildPreset &preset, @@ -141,6 +162,106 @@ void expand(const PresetType &preset, value.replace(match.captured(1), env.value(match.captured(2))); } +void updateToolchainFile( + CMakeProjectManager::Internal::PresetsDetails::ConfigurePreset &configurePreset, + const Utils::Environment &env, + const Utils::FilePath &sourceDirectory, + const Utils::FilePath &buildDirectory) +{ + if (!configurePreset.toolchainFile) + return; + + QString toolchainFileName = configurePreset.toolchainFile.value(); + CMakePresets::Macros::expand(configurePreset, env, sourceDirectory, toolchainFileName); + + // Resolve the relative path first to source and afterwards to build directory + Utils::FilePath toolchainFile = Utils::FilePath::fromString(toolchainFileName); + if (toolchainFile.isRelativePath()) { + for (const auto &path : {sourceDirectory, buildDirectory}) { + Utils::FilePath probePath = toolchainFile.resolvePath(path); + if (probePath.exists() && probePath != path) { + toolchainFile = probePath; + break; + } + } + } + + if (!toolchainFile.exists()) + return; + + // toolchainFile takes precedence to CMAKE_TOOLCHAIN_FILE + CMakeConfig cache = configurePreset.cacheVariables ? configurePreset.cacheVariables.value() + : CMakeConfig(); + + auto it = std::find_if(cache.begin(), cache.end(), [](const CMakeConfigItem &item) { + return item.key == "CMAKE_TOOLCHAIN_FILE"; + }); + if (it != cache.end()) + it->value = toolchainFile.toString().toUtf8(); + else + cache << CMakeConfigItem("CMAKE_TOOLCHAIN_FILE", + CMakeConfigItem::FILEPATH, + toolchainFile.toString().toUtf8()); + + configurePreset.cacheVariables = cache; +} + +template +void expandConditionValues(const PresetType &preset, + const Utils::Environment &env, + const Utils::FilePath &sourceDirectory, + PresetsDetails::Condition &condition) +{ + if (condition.isEquals() || condition.isNotEquals()) { + if (condition.lhs) + expand(preset, env, sourceDirectory, condition.lhs.value()); + if (condition.rhs) + expand(preset, env, sourceDirectory, condition.rhs.value()); + } + + if (condition.isInList() || condition.isNotInList()) { + if (condition.string) + expand(preset, env, sourceDirectory, condition.string.value()); + if (condition.list) + for (QString &listValue : condition.list.value()) + expand(preset, env, sourceDirectory, listValue); + } + + if (condition.isMatches() || condition.isNotMatches()) { + if (condition.string) + expand(preset, env, sourceDirectory, condition.string.value()); + if (condition.regex) + expand(preset, env, sourceDirectory, condition.regex.value()); + } + + if (condition.isAnyOf() || condition.isAllOf()) { + if (condition.conditions) + for (PresetsDetails::Condition::ConditionPtr &c : condition.conditions.value()) + expandConditionValues(preset, env, sourceDirectory, *c); + } + + if (condition.isNot()) { + if (condition.condition) + expandConditionValues(preset, env, sourceDirectory, *condition.condition.value()); + } +} + +template +bool evaluatePresetCondition(const PresetType &preset, const Utils::FilePath &sourceDirectory) +{ + if (!preset.condition) + return true; + + Utils::Environment env = Utils::Environment::systemEnvironment(); + expand(preset, env, sourceDirectory); + + PresetsDetails::Condition condition = preset.condition.value(); + expandConditionValues(preset, env, sourceDirectory, condition); + + return condition.evaluate(); +} + + // Expand for PresetsDetails::ConfigurePreset template void expand(const PresetsDetails::ConfigurePreset &preset, Utils::Environment &env, @@ -155,6 +276,9 @@ template void expand(const PresetsDetails::Conf const Utils::FilePath &sourceDirectory, QString &value); +template bool evaluatePresetCondition( + const PresetsDetails::ConfigurePreset &preset, const Utils::FilePath &sourceDirectory); + // Expand for PresetsDetails::BuildPreset template void expand(const PresetsDetails::BuildPreset &preset, Utils::Environment &env, @@ -169,4 +293,7 @@ template void expand(const PresetsDetails::BuildPre const Utils::FilePath &sourceDirectory, QString &value); +template bool evaluatePresetCondition( + const PresetsDetails::BuildPreset &preset, const Utils::FilePath &sourceDirectory); + } // namespace CMakeProjectManager::Internal::CMakePresets::Macros diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.h b/src/plugins/cmakeprojectmanager/presetsmacros.h index 9da2734da2a..0acf4d37cb6 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.h +++ b/src/plugins/cmakeprojectmanager/presetsmacros.h @@ -12,6 +12,10 @@ class FilePath; namespace CMakeProjectManager::Internal { +namespace PresetsDetails { +class ConfigurePreset; +} + namespace CMakePresets::Macros { /** * Expands the CMakePresets Macros using Utils::Environment as target and source for parent environment values. @@ -40,6 +44,22 @@ void expand(const PresetType &preset, const Utils::FilePath &sourceDirectory, QString &value); +/** + * Updates the cacheVariables parameter of the configurePreset with the expandned toolchainFile parameter. + * Including macro expansion and relative paths resolving. + */ +void updateToolchainFile(PresetsDetails::ConfigurePreset &configurePreset, + const Utils::Environment &env, + const Utils::FilePath &sourceDirectory, + const Utils::FilePath &buildDirectory); + +/** + * Expands the condition values and then evaluates the condition object of the preset and returns + * the boolean result. + */ +template +bool evaluatePresetCondition(const PresetType &preset, const Utils::FilePath &sourceDirectory); + } // namespace CMakePresets::Macros } // namespace CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/presetsparser.cpp b/src/plugins/cmakeprojectmanager/presetsparser.cpp index 970b05921a5..441709109db 100644 --- a/src/plugins/cmakeprojectmanager/presetsparser.cpp +++ b/src/plugins/cmakeprojectmanager/presetsparser.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "presetsparser.h" +#include "utils/algorithm.h" #include "cmakeprojectmanagertr.h" @@ -34,6 +35,102 @@ bool parseCMakeMinimumRequired(const QJsonValue &jsonValue, QVersionNumber &vers return true; } +std::optional parseCondition(const QJsonValue &jsonValue) +{ + std::optional condition; + + if (jsonValue.isUndefined()) + return condition; + + condition = PresetsDetails::Condition(); + + if (jsonValue.isNull()) { + condition->type = "null"; + return condition; + } + + if (jsonValue.isBool()) { + condition->type = "const"; + condition->constValue = jsonValue.toBool(); + return condition; + } + + if (!jsonValue.isObject()) + return condition; + + QJsonObject object = jsonValue.toObject(); + QString type = object.value("type").toString(); + if (type.isEmpty()) + return condition; + + if (type == "const") { + condition->type = type; + condition->constValue = object.value("const").toBool(); + return condition; + } + + for (const auto &equals : {QString("equals"), QString("notEquals")}) { + if (type == equals) { + condition->type = equals; + condition->lhs = object.value("lhs").toString(); + condition->rhs = object.value("rhs").toString(); + } + } + if (!condition->type.isEmpty()) + return condition; + + for (const auto &inList : {QString("inList"), QString("notInList")}) { + if (type == inList) { + condition->type = inList; + condition->string = object.value("string").toString(); + if (object.value("list").isArray()) { + condition->list = QStringList(); + const QJsonArray listArray = object.value("list").toArray(); + for (const QJsonValue &listValue : listArray) + condition->list.value() << listValue.toString(); + } + } + } + if (!condition->type.isEmpty()) + return condition; + + for (const auto &matches : {QString("matches"), QString("notMatches")}) { + if (type == matches) { + condition->type = matches; + condition->string = object.value("string").toString(); + condition->regex = object.value("regex").toString(); + } + } + if (!condition->type.isEmpty()) + return condition; + + for (const auto &anyOf : {QString("anyOf"), QString("allOf")}) { + if (type == anyOf) { + condition->type = anyOf; + if (object.value("conditions").isArray()) { + condition->conditions = std::vector(); + const QJsonArray conditionsArray = object.value("conditions").toArray(); + for (const QJsonValue &conditionsValue : conditionsArray) { + condition->conditions.value().emplace_back( + std::make_shared( + parseCondition(conditionsValue).value())); + } + } + } + } + if (!condition->type.isEmpty()) + return condition; + + if (type == "not") { + condition->type = type; + condition->condition = std::make_shared( + parseCondition(object.value("condition")).value()); + return condition; + } + + return condition; +} + bool parseConfigurePresets(const QJsonValue &jsonValue, QList &configurePresets) { @@ -68,6 +165,10 @@ bool parseConfigurePresets(const QJsonValue &jsonValue, preset.inherits.value() << inheritsValue; } } + + if (object.contains("condition")) + preset.condition = parseCondition(object.value("condition")); + if (object.contains("displayName")) preset.displayName = object.value("displayName").toString(); if (object.contains("description")) @@ -76,6 +177,8 @@ bool parseConfigurePresets(const QJsonValue &jsonValue, preset.generator = object.value("generator").toString(); if (object.contains("binaryDir")) preset.binaryDir = object.value("binaryDir").toString(); + if (object.contains("toolchainFile")) + preset.toolchainFile = object.value("toolchainFile").toString(); if (object.contains("cmakeExecutable")) preset.cmakeExecutable = object.value("cmakeExecutable").toString(); @@ -218,6 +321,10 @@ bool parseBuildPresets(const QJsonValue &jsonValue, preset.inherits.value() << inheritsValue; } } + + if (object.contains("condition")) + preset.condition = parseCondition(object.value("condition")); + if (object.contains("displayName")) preset.displayName = object.value("displayName").toString(); if (object.contains("description")) @@ -334,6 +441,9 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other) { + if (!condition && other.condition && !other.condition.value().isNull()) + condition = other.condition; + if (!vendor && other.vendor) vendor = other.vendor; @@ -346,6 +456,9 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other) if (!toolset && other.toolset) toolset = other.toolset; + if (!toolchainFile && other.toolchainFile) + toolchainFile = other.toolchainFile; + if (!binaryDir && other.binaryDir) binaryDir = other.binaryDir; @@ -370,6 +483,9 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other) void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other) { + if (!condition && other.condition && !other.condition.value().isNull()) + condition = other.condition; + if (!vendor && other.vendor) vendor = other.vendor; @@ -401,4 +517,46 @@ void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other) nativeToolOptions = other.nativeToolOptions; } +bool PresetsDetails::Condition::evaluate() const +{ + if (isNull()) + return true; + + if (isConst() && constValue) + return constValue.value(); + + if (isEquals() && lhs && rhs) + return lhs.value() == rhs.value(); + + if (isNotEquals() && lhs && rhs) + return lhs.value() != rhs.value(); + + if (isInList() && string && list) + return list.value().contains(string.value()); + + if (isNotInList() && string && list) + return !list.value().contains(string.value()); + + if (isMatches() && string && regex) { + QRegularExpression qRegex(regex.value()); + return qRegex.match(string.value()).hasMatch(); + } + + if (isNotMatches() && string && regex) { + QRegularExpression qRegex(regex.value()); + return !qRegex.match(string.value()).hasMatch(); + } + + if (isAnyOf() && conditions) + return Utils::anyOf(conditions.value(), [](const ConditionPtr &c) { return c->evaluate(); }); + + if (isAllOf() && conditions) + return Utils::allOf(conditions.value(), [](const ConditionPtr &c) { return c->evaluate(); }); + + if (isNot() && condition) + return !condition.value()->evaluate(); + + return false; +} + } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/presetsparser.h b/src/plugins/cmakeprojectmanager/presetsparser.h index 0dd65be7caa..5577620d5d8 100644 --- a/src/plugins/cmakeprojectmanager/presetsparser.h +++ b/src/plugins/cmakeprojectmanager/presetsparser.h @@ -43,6 +43,47 @@ public: std::optional find = false; }; +class Condition { +public: + QString type; + + bool isNull() const { return type == "null"; } + bool isConst() const { return type == "const"; } + bool isEquals() const { return type == "equals"; } + bool isNotEquals() const { return type == "notEquals"; } + bool isInList() const { return type == "inList"; } + bool isNotInList() const { return type == "notInList"; } + bool isMatches() const { return type == "matches"; } + bool isNotMatches() const { return type == "notMatches"; } + bool isAnyOf() const { return type == "anyOf"; } + bool isAllOf() const { return type == "allOf"; } + bool isNot() const { return type == "not"; } + + bool evaluate() const; + + // const + std::optional constValue; + + // equals, notEquals + std::optional lhs; + std::optional rhs; + + // inList, notInList + std::optional string; + std::optional list; + + // matches, notMatches + std::optional regex; + + using ConditionPtr = std::shared_ptr; + + // anyOf, allOf + std::optional> conditions; + + // not + std::optional condition; +}; + class ConfigurePreset { public: void inheritFrom(const ConfigurePreset &other); @@ -50,12 +91,14 @@ public: QString name; std::optional hidden = false; std::optional inherits; + std::optional condition; std::optional> vendor; std::optional displayName; std::optional description; std::optional generator; std::optional architecture; std::optional toolset; + std::optional toolchainFile; std::optional binaryDir; std::optional cmakeExecutable; std::optional cacheVariables; @@ -72,6 +115,7 @@ public: QString name; std::optional hidden = false; std::optional inherits; + std::optional condition; std::optional> vendor; std::optional displayName; std::optional description; diff --git a/tests/manual/cmakepresets/CMakePresets.json b/tests/manual/cmakepresets/CMakePresets.json index 9af5b24cd40..64e5a6bc965 100644 --- a/tests/manual/cmakepresets/CMakePresets.json +++ b/tests/manual/cmakepresets/CMakePresets.json @@ -1,8 +1,8 @@ { - "version": 2, + "version": 3, "cmakeMinimumRequired": { "major": 3, - "minor": 20, + "minor": 21, "patch": 0 }, "configurePresets": [ @@ -15,6 +15,11 @@ "CMAKE_BUILD_TYPE": "Release", "CMAKE_PREFIX_PATH": "c:/Qt/6.3.2/mingw_64" }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, "environment": { "PATH": "c:/Qt/Tools/mingw1120_64/bin;$penv{PATH}" }, @@ -36,8 +41,29 @@ "architecture" : { "value": "x64" }, - "cacheVariables": { - "CMAKE_PREFIX_PATH": "c:/Qt/6.3.2/msvc2019_64" + "toolchainFile" : "c:/Qt/6.3.2/msvc2019_64/lib/cmake/Qt6/qt.toolchain.cmake", + "condition" : { + "type": "not", + "condition": { + "type": "notEquals", + "lhs": "${hostSystemName}", + "rhs": "$env{HOST_SYSTEM_NAME}" + } + }, + "environment" : { + "HOST_SYSTEM_NAME": "Windows" + } + }, + { + "name": "linux-gcc", + "displayName": "Linux GCC", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-${presetName}", + "toolchainFile" : "$penv{HOME}/Qt/6.3.2/gcc_64/lib/cmake/Qt6/qt.toolchain.cmake", + "condition" : { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" } } ],