CMakePM: Add support for build CMake presets

This patchset will add support for version 2 of the CMakePresets
feature that has been implemented in CMake 3.20

https://cmake.org/cmake/help/v3.20/manual/cmake-presets.7.html

Task-number: QTCREATORBUG-24555
Change-Id: I08934243cc04487d38c4b59c2ad4a4a8d0484492
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Cristian Adam
2022-09-05 20:09:12 +02:00
parent 860d003a34
commit e02f4a0518
11 changed files with 661 additions and 81 deletions

View File

@@ -1301,8 +1301,8 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
}
}
static Utils::EnvironmentItems getEnvironmentItemsFromCMakePreset(const CMakeProject *project,
const Kit *k)
static Utils::EnvironmentItems getEnvironmentItemsFromCMakeConfigurePreset(
const CMakeProject *project, const Kit *k)
{
Utils::EnvironmentItems envItems;
@@ -1324,6 +1324,27 @@ static Utils::EnvironmentItems getEnvironmentItemsFromCMakePreset(const CMakePro
return envItems;
}
static Utils::EnvironmentItems getEnvironmentItemsFromCMakeBuildPreset(
const CMakeProject *project, const Kit *k, const QString &buildPresetName)
{
Utils::EnvironmentItems envItems;
const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
if (presetItem.isNull())
return envItems;
PresetsDetails::BuildPreset buildPreset
= Utils::findOrDefault(project->presetsData().buildPresets,
[buildPresetName](const PresetsDetails::BuildPreset &preset) {
return preset.name == buildPresetName;
});
CMakePresets::Macros::expand(buildPreset, envItems, project->projectDirectory());
return envItems;
}
// -----------------------------------------------------------------------------
// CMakeBuildConfigurationPrivate:
// -----------------------------------------------------------------------------
@@ -1423,8 +1444,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
addAspect<BuildTypeAspect>();
addAspect<QtSupport::QmlDebuggingAspect>(this);
appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID);
appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID);
setInitialBuildAndCleanSteps(target);
setInitializer([this, target](const BuildInfo &info) {
const Kit *k = target->kit();
@@ -1527,7 +1547,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}");
CMakeProject *cmakeProject = static_cast<CMakeProject *>(target->project());
setUserConfigureEnvironmentChanges(getEnvironmentItemsFromCMakePreset(cmakeProject, k));
setUserConfigureEnvironmentChanges(getEnvironmentItemsFromCMakeConfigurePreset(cmakeProject, k));
QStringList initialCMakeArguments = cmd.splitArguments();
addCMakeConfigurePresetToInitialArguments(initialCMakeArguments,
@@ -1537,6 +1557,8 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
m_buildSystem->setInitialCMakeArguments(initialCMakeArguments);
m_buildSystem->setCMakeBuildType(buildType);
updateAndEmitConfigureEnvironmentChanged();
setBuildPresetToBuildSteps(target);
});
}
@@ -1799,6 +1821,104 @@ CMakeConfig CMakeBuildConfiguration::signingFlags() const
return {};
}
void CMakeBuildConfiguration::setInitialBuildAndCleanSteps(const ProjectExplorer::Target *target)
{
const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(
target->kit());
if (!presetItem.isNull()) {
const QString presetName = presetItem.expandedValue(target->kit());
const CMakeProject *project = static_cast<const CMakeProject *>(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();
});
for (int i = 0; i < count; ++i)
appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID);
} else {
appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID);
}
appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID);
}
void CMakeBuildConfiguration::setBuildPresetToBuildSteps(const ProjectExplorer::Target *target)
{
const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(
target->kit());
if (presetItem.isNull())
return;
const QString presetName = presetItem.expandedValue(target->kit());
const CMakeProject *project = static_cast<const CMakeProject *>(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 QList<BuildStep *> buildStepList
= Utils::filtered(buildSteps()->steps(), [](const BuildStep *bs) {
return bs->id() == Constants::CMAKE_BUILD_STEP_ID;
});
if (buildPresets.size() != buildStepList.size())
return;
for (qsizetype i = 0; i < buildStepList.size(); ++i) {
CMakeBuildStep *cbs = qobject_cast<CMakeBuildStep *>(buildStepList[i]);
cbs->setBuildPreset(buildPresets[i].name);
cbs->setUserEnvironmentChanges(
getEnvironmentItemsFromCMakeBuildPreset(project, target->kit(), buildPresets[i].name));
if (buildPresets[i].targets) {
QString targets = buildPresets[i].targets.value().join(" ");
CMakePresets::Macros::expand(buildPresets[i],
cbs->environment(),
project->projectDirectory(),
targets);
cbs->setBuildTargets(targets.split(" "));
}
QStringList cmakeArguments;
if (buildPresets[i].jobs)
cmakeArguments.append(QString("-j %1").arg(buildPresets[i].jobs.value()));
if (buildPresets[i].verbose && buildPresets[i].verbose.value())
cmakeArguments.append("--verbose");
if (buildPresets[i].cleanFirst && buildPresets[i].cleanFirst.value())
cmakeArguments.append("--clean-first");
if (!cmakeArguments.isEmpty())
cbs->setCMakeArguments(cmakeArguments);
if (buildPresets[i].nativeToolOptions) {
QString nativeToolOptions = buildPresets[i].nativeToolOptions.value().join(" ");
CMakePresets::Macros::expand(buildPresets[i],
cbs->environment(),
project->projectDirectory(),
nativeToolOptions);
cbs->setToolArguments(nativeToolOptions.split(" "));
}
if (buildPresets[i].configuration)
cbs->setConfiguration(buildPresets[i].configuration.value());
// Leave only the first build step enabled
if (i > 0)
cbs->setEnabled(false);
}
}
/*!
\class CMakeBuildConfigurationFactory
*/

View File

@@ -69,6 +69,9 @@ private:
virtual CMakeConfig signingFlags() const;
void setInitialBuildAndCleanSteps(const ProjectExplorer::Target *target);
void setBuildPresetToBuildSteps(const ProjectExplorer::Target *target);
Internal::CMakeBuildSystem *m_buildSystem = nullptr;
friend class Internal::CMakeBuildSettingsWidget;

View File

@@ -7,11 +7,14 @@
#include "cmakebuildsystem.h"
#include "cmakekitinformation.h"
#include "cmakeparser.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include "cmaketool.h"
#include <coreplugin/find/itemviewfind.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/environmentwidget.h>
#include <projectexplorer/gnumakeparser.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/processparameters.h>
@@ -29,6 +32,7 @@
#include <QListWidget>
#include <QRegularExpression>
#include <QTreeView>
#include <QCheckBox>
using namespace Core;
using namespace ProjectExplorer;
@@ -42,6 +46,9 @@ const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments"
const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArguments";
const char IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY[] =
"CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates";
const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment";
const char USER_ENVIRONMENT_CHANGES_KEY[] = "CMakeProjectManager.MakeStep.UserEnvironmentChanges";
const char BUILD_PRESET_KEY[] = "CMakeProjectManager.MakeStep.BuildPreset";
// CmakeProgressParser
@@ -186,11 +193,12 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
setCommandLineProvider([this] { return cmakeCommand(); });
setEnvironmentModifier([](Environment &env) {
setEnvironmentModifier([this](Environment &env) {
const QString ninjaProgressString = "[%f/%t "; // ninja: [33/100
env.setupEnglishOutput();
if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString))
env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] ");
env.modify(m_userEnvironmentChanges);
});
connect(target(), &Target::parsingFinished, this, [this](bool success) {
@@ -207,12 +215,26 @@ QVariantMap CMakeBuildStep::toMap() const
{
QVariantMap map(AbstractProcessStep::toMap());
map.insert(BUILD_TARGETS_KEY, m_buildTargets);
map.insert(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY), m_clearSystemEnvironment);
map.insert(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY), EnvironmentItem::toStringList(m_userEnvironmentChanges));
map.insert(QLatin1String(BUILD_PRESET_KEY), m_buildPreset);
return map;
}
bool CMakeBuildStep::fromMap(const QVariantMap &map)
{
setBuildTargets(map.value(BUILD_TARGETS_KEY).toStringList());
m_clearSystemEnvironment = map.value(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY))
.toBool();
m_userEnvironmentChanges = EnvironmentItem::fromStringList(
map.value(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY)).toStringList());
updateAndEmitEnvironmentChanged();
m_buildPreset = map.value(QLatin1String(BUILD_PRESET_KEY)).toString();
return BuildStep::fromMap(map);
}
@@ -348,6 +370,14 @@ QString CMakeBuildStep::defaultBuildTarget() const
return allTarget();
}
bool CMakeBuildStep::isCleanStep() const
{
const BuildStepList *const bsl = stepList();
QTC_ASSERT(bsl, return false);
const Utils::Id parentId = bsl->id();
return parentId == ProjectExplorer::Constants::BUILDSTEPS_CLEAN;
}
QStringList CMakeBuildStep::buildTargets() const
{
return m_buildTargets;
@@ -401,6 +431,9 @@ CommandLine CMakeBuildStep::cmakeCommand() const
auto bs = qobject_cast<CMakeBuildSystem*>(buildSystem());
if (bs && bs->isMultiConfigReader()) {
cmd.addArg("--config");
if (m_configuration)
cmd.addArg(m_configuration.value());
else
cmd.addArg(bs->cmakeBuildType());
}
@@ -453,13 +486,36 @@ QString CMakeBuildStep::activeRunConfigTarget() const
return rc ? rc->buildKey() : QString();
}
void CMakeBuildStep::setBuildPreset(const QString &preset)
{
m_buildPreset = preset;
}
QWidget *CMakeBuildStep::createConfigWidget()
{
auto updateDetails = [this] {
ProcessParameters param;
setupProcessParameters(&param);
param.setCommandLine(cmakeCommand());
setSummaryText(param.summary(displayName()));
QString summaryText = param.summary(displayName());
if (!m_buildPreset.isEmpty()) {
const CMakeProject *cp = static_cast<const CMakeProject *>(project());
const auto buildPresets = cp->presetsData().buildPresets;
const PresetsDetails::BuildPreset preset
= Utils::findOrDefault(buildPresets, [this](const PresetsDetails::BuildPreset &bp) {
return bp.name == m_buildPreset;
});
const QString presetDisplayName = preset.displayName ? preset.displayName.value()
: preset.name;
if (!presetDisplayName.isEmpty())
summaryText.append(QString("<br><b>Preset</b>: %1").arg(presetDisplayName));
}
setSummaryText(summaryText);
};
setDisplayName(tr("Build", "ConfigWidget display name."));
@@ -473,6 +529,34 @@ QWidget *CMakeBuildStep::createConfigWidget()
auto frame = ItemViewFind::createSearchableWrapper(buildTargetsView,
ItemViewFind::LightColored);
auto createAndAddEnvironmentWidgets = [this](Layouting::Form &builder) {
auto clearBox = new QCheckBox(tr("Clear system environment"));
clearBox->setChecked(useClearEnvironment());
auto envWidget = new EnvironmentWidget(nullptr, EnvironmentWidget::TypeLocal, clearBox);
envWidget->setBaseEnvironment(baseEnvironment());
envWidget->setBaseEnvironmentText(baseEnvironmentText());
envWidget->setUserChanges(userEnvironmentChanges());
connect(envWidget, &EnvironmentWidget::userChangesChanged, this, [this, envWidget] {
setUserEnvironmentChanges(envWidget->userChanges());
});
connect(clearBox, &QAbstractButton::toggled, this, [this, envWidget](bool checked) {
setUseClearEnvironment(checked);
envWidget->setBaseEnvironment(baseEnvironment());
envWidget->setBaseEnvironmentText(baseEnvironmentText());
});
connect(this, &CMakeBuildStep::environmentChanged, this, [this, envWidget] {
envWidget->setBaseEnvironment(baseEnvironment());
envWidget->setBaseEnvironmentText(baseEnvironmentText());
});
builder.addRow(clearBox);
builder.addRow(envWidget);
};
Layouting::Form builder;
builder.addRow(m_cmakeArguments);
builder.addRow(m_toolArguments);
@@ -481,6 +565,10 @@ QWidget *CMakeBuildStep::createConfigWidget()
builder.addRow(m_useiOSAutomaticProvisioningUpdates);
builder.addRow({new QLabel(tr("Targets:")), frame});
if (!isCleanStep() && !m_buildPreset.isEmpty())
createAndAddEnvironmentWidgets(builder);
auto widget = builder.emerge(Layouting::WithoutMargins);
updateDetails();
@@ -552,6 +640,83 @@ void CMakeBuildStep::updateBuildTargetsModel()
emit buildTargetsChanged();
}
void CMakeBuildStep::setConfiguration(const QString &configuration)
{
m_configuration = configuration;
}
void CMakeBuildStep::setToolArguments(const QStringList &nativeToolArguments)
{
m_toolArguments->setValue(nativeToolArguments.join(" "));
}
void CMakeBuildStep::setCMakeArguments(const QStringList &cmakeArguments)
{
m_cmakeArguments->setValue(cmakeArguments.join(" "));
}
Environment CMakeBuildStep::environment() const
{
return m_environment;
}
void CMakeBuildStep::setUserEnvironmentChanges(const Utils::EnvironmentItems &diff)
{
if (m_userEnvironmentChanges == diff)
return;
m_userEnvironmentChanges = diff;
updateAndEmitEnvironmentChanged();
}
EnvironmentItems CMakeBuildStep::userEnvironmentChanges() const
{
return m_userEnvironmentChanges;
}
bool CMakeBuildStep::useClearEnvironment() const
{
return m_clearSystemEnvironment;
}
void CMakeBuildStep::setUseClearEnvironment(bool b)
{
if (useClearEnvironment() == b)
return;
m_clearSystemEnvironment = b;
updateAndEmitEnvironmentChanged();
}
void CMakeBuildStep::updateAndEmitEnvironmentChanged()
{
Environment env = baseEnvironment();
env.modify(userEnvironmentChanges());
if (env == m_environment)
return;
m_environment = env;
emit environmentChanged();
}
Environment CMakeBuildStep::baseEnvironment() const
{
Environment result;
if (!useClearEnvironment()) {
ProjectExplorer::IDevice::ConstPtr devicePtr = BuildDeviceKitAspect::device(kit());
result = devicePtr ? devicePtr->systemEnvironment() : Environment::systemEnvironment();
}
buildConfiguration()->addToEnvironment(result);
kit()->addToBuildEnvironment(result);
result.modify(project()->additionalEnvironment());
return result;
}
QString CMakeBuildStep::baseEnvironmentText() const
{
if (useClearEnvironment())
return tr("Clean Environment");
else
return tr("System Environment");
}
void CMakeBuildStep::processFinished(int exitCode, QProcess::ExitStatus status)
{
AbstractProcessStep::processFinished(exitCode, status);

View File

@@ -54,8 +54,26 @@ public:
QString activeRunConfigTarget() const;
void setBuildPreset(const QString &preset);
Utils::Environment environment() const;
void setUserEnvironmentChanges(const Utils::EnvironmentItems &diff);
Utils::EnvironmentItems userEnvironmentChanges() const;
bool useClearEnvironment() const;
void setUseClearEnvironment(bool b);
void updateAndEmitEnvironmentChanged();
Utils::Environment baseEnvironment() const;
QString baseEnvironmentText() const;
void setCMakeArguments(const QStringList &cmakeArguments);
void setToolArguments(const QStringList &nativeToolArguments);
void setConfiguration(const QString &configuration);
signals:
void buildTargetsChanged();
void environmentChanged();
private:
Utils::CommandLine cmakeCommand() const;
@@ -69,6 +87,7 @@ private:
QWidget *createConfigWidget() override;
QString defaultBuildTarget() const;
bool isCleanStep() const;
void runImpl();
void handleProjectWasParsed(bool success);
@@ -90,6 +109,12 @@ private:
QString m_installTarget = "install";
Utils::TreeModel<Utils::TreeItem, CMakeTargetItem> m_buildTargetModel;
Utils::Environment m_environment;
Utils::EnvironmentItems m_userEnvironmentChanges;
bool m_clearSystemEnvironment = false;
QString m_buildPreset;
std::optional<QString> m_configuration;
};
class CMakeBuildStepFactory : public ProjectExplorer::BuildStepFactory

View File

@@ -88,54 +88,93 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
result.version = cmakePresetsData.version;
result.cmakeMinimimRequired = cmakePresetsData.cmakeMinimimRequired;
QHash<QString, PresetsDetails::ConfigurePreset> configurePresets;
auto combinePresetsInternal = [](auto &presetsHash,
auto &presets,
auto &userPresets,
const QString &presetType) {
// Populate the hash map with the CMakePresets
for (const PresetsDetails::ConfigurePreset &p: cmakePresetsData.configurePresets)
configurePresets.insert(p.name, p);
for (const auto &p : presets)
presetsHash.insert(p.name, p);
auto resolveInherits =
[configurePresets](std::vector<PresetsDetails::ConfigurePreset> &configurePresetsList) {
for (PresetsDetails::ConfigurePreset &cp : configurePresetsList) {
if (!cp.inherits)
auto resolveInherits = [](const auto &presetsHash, auto &presetsList) {
for (auto &p : presetsList) {
if (!p.inherits)
continue;
for (const QString &inheritFromName : cp.inherits.value())
if (configurePresets.contains(inheritFromName))
cp.inheritFrom(configurePresets[inheritFromName]);
for (const QString &inheritFromName : p.inherits.value())
if (presetsHash.contains(inheritFromName))
p.inheritFrom(presetsHash[inheritFromName]);
}
};
// First resolve the CMakePresets
resolveInherits(cmakePresetsData.configurePresets);
resolveInherits(presetsHash, presets);
// Add the CMakeUserPresets to the resolve hash map
for (const PresetsDetails::ConfigurePreset &cp : cmakeUserPresetsData.configurePresets) {
if (configurePresets.contains(cp.name)) {
TaskHub::addTask(BuildSystemTask(
Task::TaskType::Error,
tr("CMakeUserPresets.json cannot re-define the configure preset: %1").arg(cp.name),
for (const auto &p : userPresets) {
if (presetsHash.contains(p.name)) {
TaskHub::addTask(
BuildSystemTask(Task::TaskType::Error,
tr("CMakeUserPresets.json cannot re-define the %1 preset: %2")
.arg(presetType)
.arg(p.name),
"CMakeUserPresets.json"));
TaskHub::requestPopup();
} else {
configurePresets.insert(cp.name, cp);
presetsHash.insert(p.name, p);
}
}
// Then resolve the CMakeUserPresets
resolveInherits(cmakeUserPresetsData.configurePresets);
resolveInherits(presetsHash, userPresets);
// Get both CMakePresets and CMakeUserPresets into the result
result.configurePresets = cmakePresetsData.configurePresets;
auto result = presets;
// std::vector doesn't have append
std::copy(cmakeUserPresetsData.configurePresets.begin(),
cmakeUserPresetsData.configurePresets.end(),
std::back_inserter(result.configurePresets));
std::copy(userPresets.begin(), userPresets.end(), std::back_inserter(result));
return result;
};
QHash<QString, PresetsDetails::ConfigurePreset> configurePresetsHash;
QHash<QString, PresetsDetails::BuildPreset> buildPresetsHash;
result.configurePresets = combinePresetsInternal(configurePresetsHash,
cmakePresetsData.configurePresets,
cmakeUserPresetsData.configurePresets,
"configure");
result.buildPresets = combinePresetsInternal(buildPresetsHash,
cmakePresetsData.buildPresets,
cmakeUserPresetsData.buildPresets,
"build");
return result;
}
void CMakeProject::setupBuildPresets(Internal::PresetsData &presetsData)
{
for (auto &buildPreset : presetsData.buildPresets) {
if (buildPreset.inheritConfigureEnvironment) {
if (!buildPreset.configurePreset) {
TaskHub::addTask(BuildSystemTask(
Task::TaskType::Error,
tr("Build preset %1 is missing a corresponding configure preset.")
.arg(buildPreset.name)));
TaskHub::requestPopup();
}
const QString &configurePresetName = buildPreset.configurePreset.value();
buildPreset.environment
= Utils::findOrDefault(presetsData.configurePresets,
[configurePresetName](
const PresetsDetails::ConfigurePreset &configurePreset) {
return configurePresetName == configurePreset.name;
})
.environment;
}
}
}
void CMakeProject::readPresets()
{
auto parsePreset = [](const Utils::FilePath &presetFile) -> Internal::PresetsData {
@@ -168,6 +207,7 @@ void CMakeProject::readPresets()
Internal::PresetsData cmakeUserPresetsData = parsePreset(cmakeUserPresetsJson);
m_presetsData = combinePresets(cmakePresetsData, cmakeUserPresetsData);
setupBuildPresets(m_presetsData);
}
bool CMakeProject::setupTarget(Target *t)

View File

@@ -38,6 +38,7 @@ private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
Internal::PresetsData combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData);
void setupBuildPresets(Internal::PresetsData &presetsData);
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;

View File

@@ -12,7 +12,7 @@
namespace CMakeProjectManager::Internal::CMakePresets::Macros {
static void expandAllButEnv(const PresetsDetails::ConfigurePreset &configurePreset,
void expandAllButEnv(const PresetsDetails::ConfigurePreset &preset,
const Utils::FilePath &sourceDirectory,
QString &value)
{
@@ -22,23 +22,36 @@ static void expandAllButEnv(const PresetsDetails::ConfigurePreset &configurePres
value.replace("${sourceParentDir}", sourceDirectory.parentDir().toString());
value.replace("${sourceDirName}", sourceDirectory.fileName());
value.replace("${presetName}", configurePreset.name);
if (configurePreset.generator)
value.replace("${generator}", configurePreset.generator.value());
value.replace("${presetName}", preset.name);
if (preset.generator)
value.replace("${generator}", preset.generator.value());
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
void expandAllButEnv(const PresetsDetails::BuildPreset &preset,
const Utils::FilePath &sourceDirectory,
QString &value)
{
value.replace("${dollar}", "$");
value.replace("${sourceDir}", sourceDirectory.toString());
value.replace("${sourceParentDir}", sourceDirectory.parentDir().toString());
value.replace("${sourceDirName}", sourceDirectory.fileName());
value.replace("${presetName}", preset.name);
}
template<class PresetType>
void expand(const PresetType &preset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory)
{
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
const QHash<QString, QString> presetEnv = preset.environment ? preset.environment.value()
: QHash<QString, QString>();
for (auto it = presetEnv.constKeyValueBegin(); it != presetEnv.constKeyValueEnd(); ++it) {
const QString key = it->first;
QString value = it->second;
expandAllButEnv(configurePreset, sourceDirectory, value);
expandAllButEnv(preset, sourceDirectory, value);
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
for (const QRegularExpressionMatch &match : envRegex.globalMatch(value)) {
@@ -69,19 +82,19 @@ void expand(const PresetsDetails::ConfigurePreset &configurePreset,
}
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
template<class PresetType>
void expand(const PresetType &preset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory)
{
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
const QHash<QString, QString> presetEnv = preset.environment ? preset.environment.value()
: QHash<QString, QString>();
for (auto it = presetEnv.constKeyValueBegin(); it != presetEnv.constKeyValueEnd(); ++it) {
const QString key = it->first;
QString value = it->second;
expandAllButEnv(configurePreset, sourceDirectory, value);
expandAllButEnv(preset, sourceDirectory, value);
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
for (const QRegularExpressionMatch &match : envRegex.globalMatch(value)) {
@@ -108,15 +121,15 @@ void expand(const PresetsDetails::ConfigurePreset &configurePreset,
}
}
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
template<class PresetType>
void expand(const PresetType &preset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value)
{
expandAllButEnv(configurePreset, sourceDirectory, value);
expandAllButEnv(preset, sourceDirectory, value);
const QHash<QString, QString> presetEnv = configurePreset.environment
? configurePreset.environment.value()
const QHash<QString, QString> presetEnv = preset.environment ? preset.environment.value()
: QHash<QString, QString>();
QRegularExpression envRegex(R"((\$env\{(\w+)\}))");
@@ -128,4 +141,32 @@ void expand(const PresetsDetails::ConfigurePreset &configurePreset,
value.replace(match.captured(1), env.value(match.captured(2)));
}
// Expand for PresetsDetails::ConfigurePreset
template void expand<PresetsDetails::ConfigurePreset>(const PresetsDetails::ConfigurePreset &preset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory);
template void expand<PresetsDetails::ConfigurePreset>(const PresetsDetails::ConfigurePreset &preset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory);
template void expand<PresetsDetails::ConfigurePreset>(const PresetsDetails::ConfigurePreset &preset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value);
// Expand for PresetsDetails::BuildPreset
template void expand<PresetsDetails::BuildPreset>(const PresetsDetails::BuildPreset &preset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory);
template void expand<PresetsDetails::BuildPreset>(const PresetsDetails::BuildPreset &preset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory);
template void expand<PresetsDetails::BuildPreset>(const PresetsDetails::BuildPreset &preset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value);
} // namespace CMakeProjectManager::Internal::CMakePresets::Macros

View File

@@ -12,16 +12,13 @@ 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.
* $penv{PATH} is taken from Utils::Environment
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
template<class PresetType>
void expand(const PresetType &preset,
Utils::Environment &env,
const Utils::FilePath &sourceDirectory);
@@ -29,14 +26,16 @@ void expand(const PresetsDetails::ConfigurePreset &configurePreset,
* Expands the CMakePresets Macros using Utils::Environment as target
* $penv{PATH} is replaced with Qt Creator macros ${PATH}
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
template<class PresetType>
void expand(const PresetType &preset,
Utils::EnvironmentItems &envItems,
const Utils::FilePath &sourceDirectory);
/**
* Expands the CMakePresets macros inside the @value QString parameter.
*/
void expand(const PresetsDetails::ConfigurePreset &configurePreset,
template<class PresetType>
void expand(const PresetType &preset,
const Utils::Environment &env,
const Utils::FilePath &sourceDirectory,
QString &value);

View File

@@ -13,7 +13,7 @@ namespace Internal {
bool parseVersion(const QJsonValue &jsonValue, int &version)
{
if (jsonValue.isNull())
if (jsonValue.isUndefined())
return false;
const int invalidVersion = -1;
@@ -23,7 +23,7 @@ bool parseVersion(const QJsonValue &jsonValue, int &version)
bool parseCMakeMinimumRequired(const QJsonValue &jsonValue, QVersionNumber &versionNumber)
{
if (jsonValue.isNull() || !jsonValue.isObject())
if (jsonValue.isUndefined() || !jsonValue.isObject())
return false;
QJsonObject object = jsonValue.toObject();
@@ -38,7 +38,7 @@ bool parseConfigurePresets(const QJsonValue &jsonValue,
std::vector<PresetsDetails::ConfigurePreset> &configurePresets)
{
// The whole section is optional
if (jsonValue.isNull())
if (jsonValue.isUndefined())
return true;
if (!jsonValue.isArray())
@@ -56,7 +56,7 @@ bool parseConfigurePresets(const QJsonValue &jsonValue,
preset.hidden = object.value("hidden").toBool();
QJsonValue inherits = object.value("inherits");
if (!inherits.isNull()) {
if (!inherits.isUndefined()) {
preset.inherits = QStringList();
if (inherits.isArray()) {
const QJsonArray inheritsArray = inherits.toArray();
@@ -184,6 +184,97 @@ bool parseConfigurePresets(const QJsonValue &jsonValue,
return true;
}
bool parseBuildPresets(const QJsonValue &jsonValue,
std::vector<PresetsDetails::BuildPreset> &buildPresets)
{
// The whole section is optional
if (jsonValue.isUndefined())
return true;
if (!jsonValue.isArray())
return false;
const QJsonArray buildPresetsArray = jsonValue.toArray();
for (const QJsonValue &presetJson : buildPresetsArray) {
if (!presetJson.isObject())
continue;
QJsonObject object = presetJson.toObject();
PresetsDetails::BuildPreset preset;
preset.name = object.value("name").toString();
preset.hidden = object.value("hidden").toBool();
QJsonValue inherits = object.value("inherits");
if (!inherits.isUndefined()) {
preset.inherits = QStringList();
if (inherits.isArray()) {
const QJsonArray inheritsArray = inherits.toArray();
for (const QJsonValue &inheritsValue : inheritsArray)
preset.inherits.value() << inheritsValue.toString();
} else {
QString inheritsValue = inherits.toString();
if (!inheritsValue.isEmpty())
preset.inherits.value() << inheritsValue;
}
}
if (object.contains("displayName"))
preset.displayName = object.value("displayName").toString();
if (object.contains("description"))
preset.description = object.value("description").toString();
const QJsonObject environmentObj = object.value("environment").toObject();
for (const QString &envKey : environmentObj.keys()) {
if (!preset.environment)
preset.environment = QHash<QString, QString>();
QJsonValue envValue = environmentObj.value(envKey);
preset.environment.value().insert(envKey, envValue.toString());
}
if (object.contains("configurePreset"))
preset.configurePreset = object.value("configurePreset").toString();
if (object.contains("inheritConfigureEnvironment"))
preset.inheritConfigureEnvironment = object.value("inheritConfigureEnvironment").toBool();
if (object.contains("jobs"))
preset.jobs = object.value("jobs").toInt();
QJsonValue targets = object.value("targets");
if (!targets.isUndefined()) {
preset.targets = QStringList();
if (targets.isArray()) {
const QJsonArray targetsArray = targets.toArray();
for (const QJsonValue &targetsValue : targetsArray)
preset.targets.value() << targetsValue.toString();
} else {
QString targetsValue = targets.toString();
if (!targetsValue.isEmpty())
preset.targets.value() << targetsValue;
}
}
if (object.contains("configuration"))
preset.configuration = object.value("configuration").toString();
if (object.contains("verbose"))
preset.verbose = object.value("verbose").toBool();
if (object.contains("cleanFirst"))
preset.cleanFirst = object.value("cleanFirst").toBool();
QJsonValue nativeToolOptions = object.value("nativeToolOptions");
if (!nativeToolOptions.isUndefined()) {
if (nativeToolOptions.isArray()) {
preset.nativeToolOptions = QStringList();
const QJsonArray toolOptionsArray = nativeToolOptions.toArray();
for (const QJsonValue &toolOptionsValue : toolOptionsArray)
preset.nativeToolOptions.value() << toolOptionsValue.toString();
}
}
buildPresets.emplace_back(preset);
}
return true;
}
const PresetsData &PresetsParser::presetsData() const
{
return m_presetsData;
@@ -232,9 +323,19 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
// optional
if (!parseConfigurePresets(root.value("configurePresets"), m_presetsData.configurePresets)) {
errorMessage = QCoreApplication::translate(
"CMakeProjectManager::Internal",
"Invalid \"configurePresets\" section in %1 file").arg(jsonFile.fileName());
errorMessage
= QCoreApplication::translate("CMakeProjectManager::Internal",
"Invalid \"configurePresets\" section in %1 file")
.arg(jsonFile.fileName());
return false;
}
// optional
if (!parseBuildPresets(root.value("buildPresets"), m_presetsData.buildPresets)) {
errorMessage
= QCoreApplication::translate("CMakeProjectManager::Internal",
"Invalid \"buildPresets\" section in %1 file")
.arg(jsonFile.fileName());
return false;
}
@@ -277,5 +378,38 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
debug = other.debug;
}
void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other)
{
if (!vendor && other.vendor)
vendor = other.vendor;
if (!environment && other.environment)
environment = other.environment;
if (!configurePreset && other.configurePreset)
configurePreset = other.configurePreset;
if (!inheritConfigureEnvironment && other.inheritConfigureEnvironment)
inheritConfigureEnvironment = other.inheritConfigureEnvironment;
if (!jobs && other.jobs)
jobs = other.jobs;
if (!targets && other.targets)
targets = other.targets;
if (!configuration && other.configuration)
configuration = other.configuration;
if (!verbose && other.verbose)
verbose = other.verbose;
if (!cleanFirst && other.cleanFirst)
cleanFirst = other.cleanFirst;
if (!nativeToolOptions && other.nativeToolOptions)
nativeToolOptions = other.nativeToolOptions;
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -67,6 +67,27 @@ public:
std::optional<Debug> debug;
};
class BuildPreset {
public:
void inheritFrom(const BuildPreset &other);
QString name;
std::optional<bool> hidden = false;
std::optional<QStringList> inherits;
std::optional<QHash<QString, QString>> vendor;
std::optional<QString> displayName;
std::optional<QString> description;
std::optional<QHash<QString, QString>> environment;
std::optional<QString> configurePreset;
std::optional<bool> inheritConfigureEnvironment = true;
std::optional<int> jobs;
std::optional<QStringList> targets;
std::optional<QString> configuration;
std::optional<bool> verbose;
std::optional<bool> cleanFirst;
std::optional<QStringList> nativeToolOptions;
};
} // namespace PresetsDetails
class PresetsData
@@ -76,6 +97,7 @@ public:
QVersionNumber cmakeMinimimRequired;
QHash<QString, QString> vendor;
std::vector<PresetsDetails::ConfigurePreset> configurePresets;
std::vector<PresetsDetails::BuildPreset> buildPresets;
};
class PresetsParser

View File

@@ -1,8 +1,8 @@
{
"version": 1,
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"minor": 20,
"patch": 0
},
"configurePresets": [
@@ -13,10 +13,10 @@
"binaryDir": "${sourceDir}/build-${presetName}-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.1/mingw_64"
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.2/mingw_64"
},
"environment": {
"PATH": "c:/mingw64/bin;$penv{PATH}"
"PATH": "c:/Qt/Tools/mingw1120_64/bin;$penv{PATH}"
},
"debug" : {
"find" : true
@@ -37,8 +37,38 @@
"value": "x64"
},
"cacheVariables": {
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.1/msvc2019_64"
"CMAKE_PREFIX_PATH": "c:/Qt/6.3.2/msvc2019_64"
}
}
],
"buildPresets": [
{
"name": "mingw",
"displayName": "MinGW default",
"configurePreset": "mingw",
"targets": "${sourceDirName}"
},
{
"name": "mingw-verbose",
"inherits": "mingw",
"displayName": "MinGW verbose",
"verbose": true
},
{
"name": "mingw-make",
"displayName": "MinGW make 4 CPUs",
"configurePreset": "mingw-make",
"jobs": 4
},
{
"name": "visualc-debug",
"configurePreset": "visualc",
"configuration": "Debug"
},
{
"name": "visualc-relwithdebinfo",
"inherits": "visualc-debug",
"configuration": "RelWithDebInfo"
}
]
}