summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2020-10-06 14:49:44 (GMT)
committerKitware Robot <kwrobot@kitware.com>2020-10-06 14:49:59 (GMT)
commit8c4c658ab062f2920448a02fe3a5d42fe3d48876 (patch)
tree97656d990c21802ec181b9269d63500bb461437c /Source
parenta12da6fb5ece6baf3b4540d8204b8b29dde92538 (diff)
parent6f8fdc686c0e0710a9c1981c66359a205cc127dd (diff)
downloadCMake-8c4c658ab062f2920448a02fe3a5d42fe3d48876.zip
CMake-8c4c658ab062f2920448a02fe3a5d42fe3d48876.tar.gz
CMake-8c4c658ab062f2920448a02fe3a5d42fe3d48876.tar.bz2
Merge topic 'cmake-presets'
6f8fdc686c .gitignore: Add CMakeUserPresets.json 1d25760198 Help: Add presets documentation and release notes a4382f72d7 CMake GUI: Add presets functionality 8617479061 CMake: Add presets functionality 06128cf949 Presets: Add cmCMakePresetsFile class 5a36542086 Refactor: Add allowArch parameter to cmake::CreateGlobalGenerator() 3059e6aed7 cmJSONHelpers: Add new Bind() function 8682d1b7b2 CMake GUI Tests: Increase default sleep time Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Michael Hirsch, Ph.D. <michael@scivision.dev> Merge-request: !5169
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/QtDialog/CMakeLists.txt8
-rw-r--r--Source/QtDialog/CMakeSetup.cxx25
-rw-r--r--Source/QtDialog/CMakeSetupDialog.cxx86
-rw-r--r--Source/QtDialog/CMakeSetupDialog.h12
-rw-r--r--Source/QtDialog/CMakeSetupDialog.ui23
-rw-r--r--Source/QtDialog/FirstConfigure.cxx54
-rw-r--r--Source/QtDialog/FirstConfigure.h15
-rw-r--r--Source/QtDialog/QCMake.cxx160
-rw-r--r--Source/QtDialog/QCMake.h23
-rw-r--r--Source/QtDialog/QCMakePreset.cxx50
-rw-r--r--Source/QtDialog/QCMakePreset.h30
-rw-r--r--Source/QtDialog/QCMakePresetComboBox.cxx64
-rw-r--r--Source/QtDialog/QCMakePresetComboBox.h35
-rw-r--r--Source/QtDialog/QCMakePresetItemModel.cxx143
-rw-r--r--Source/QtDialog/QCMakePresetItemModel.h45
-rw-r--r--Source/cmCMakePresetsFile.cxx763
-rw-r--r--Source/cmCMakePresetsFile.h141
-rw-r--r--Source/cmGlobalGeneratorFactory.h4
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudio11Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudio12Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudio14Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudio9Generator.cxx4
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.cxx6
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx3
-rw-r--r--Source/cmJSONHelpers.h11
-rw-r--r--Source/cmake.cxx343
-rw-r--r--Source/cmake.h56
-rw-r--r--Source/cmakemain.cxx7
30 files changed, 2059 insertions, 70 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index d0d9459..1e16e17 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -184,6 +184,8 @@ set(SRCS
cmCLocaleEnvironmentScope.cxx
cmCMakePath.h
cmCMakePath.cxx
+ cmCMakePresetsFile.cxx
+ cmCMakePresetsFile.h
cmCommandArgumentParserHelper.cxx
cmCommonTargetGenerator.cxx
cmCommonTargetGenerator.h
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
index 5fd0e89..394762a 100644
--- a/Source/QtDialog/CMakeLists.txt
+++ b/Source/QtDialog/CMakeLists.txt
@@ -92,6 +92,12 @@ set(SRCS
QCMake.h
QCMakeCacheView.cxx
QCMakeCacheView.h
+ QCMakePreset.cxx
+ QCMakePreset.h
+ QCMakePresetComboBox.cxx
+ QCMakePresetComboBox.h
+ QCMakePresetItemModel.cxx
+ QCMakePresetItemModel.h
QCMakeWidgets.cxx
QCMakeWidgets.h
RegexExplorer.cxx
@@ -116,6 +122,8 @@ qt5_wrap_cpp(MOC_SRCS
FirstConfigure.h
QCMake.h
QCMakeCacheView.h
+ QCMakePresetComboBox.h
+ QCMakePresetItemModel.h
QCMakeWidgets.h
RegexExplorer.h
WarningMessagesDialog.h
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index 37c1f15..a5b2f34 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -32,7 +32,8 @@ static const char* cmDocumentationUsage[][2] = {
" cmake-gui [options]\n"
" cmake-gui [options] <path-to-source>\n"
" cmake-gui [options] <path-to-existing-build>\n"
- " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" },
+ " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n"
+ " cmake-gui [options] -S <path-to-source> --preset=<preset-name>\n" },
{ nullptr, nullptr }
};
@@ -147,6 +148,7 @@ int main(int argc, char** argv)
QStringList args = QApplication::arguments();
std::string binaryDirectory;
std::string sourceDirectory;
+ std::string presetName;
for (int i = 1; i < args.size(); ++i) {
const QString& arg = args[i];
if (arg.startsWith("-S")) {
@@ -185,11 +187,28 @@ int main(int argc, char** argv)
binaryDirectory =
cmSystemTools::CollapseFullPath(path.toLocal8Bit().data());
cmSystemTools::ConvertToUnixSlashes(binaryDirectory);
+ } else if (arg.startsWith("--preset=")) {
+ QString preset = arg.mid(cmStrLen("--preset="));
+ if (preset.isEmpty()) {
+ std::cerr << "No preset specified for --preset" << std::endl;
+ return 1;
+ }
+ presetName = preset.toLocal8Bit().data();
}
}
- if (!sourceDirectory.empty() && !binaryDirectory.empty()) {
+ if (!sourceDirectory.empty() &&
+ (!binaryDirectory.empty() || !presetName.empty())) {
dialog.setSourceDirectory(QString::fromLocal8Bit(sourceDirectory.c_str()));
- dialog.setBinaryDirectory(QString::fromLocal8Bit(binaryDirectory.c_str()));
+ if (!binaryDirectory.empty()) {
+ dialog.setBinaryDirectory(
+ QString::fromLocal8Bit(binaryDirectory.c_str()));
+ if (!presetName.empty()) {
+ dialog.setStartupBinaryDirectory(true);
+ }
+ }
+ if (!presetName.empty()) {
+ dialog.setDeferredPreset(QString::fromLocal8Bit(presetName.c_str()));
+ }
} else {
if (args.count() == 2) {
std::string filePath =
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index df22028..acd32ec 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -21,8 +21,10 @@
#include <QSettings>
#include <QShortcut>
#include <QStatusBar>
+#include <QString>
#include <QToolButton>
#include <QUrl>
+#include <QVector>
#ifdef QT_WINEXTRAS
# include <QWinTaskbarButton>
@@ -263,6 +265,8 @@ void CMakeSetupDialog::initialize()
&CMakeSetupDialog::onBinaryDirectoryChanged);
QObject::connect(this->SourceDirectory, &QLineEdit::textChanged, this,
&CMakeSetupDialog::onSourceDirectoryChanged);
+ QObject::connect(this->Preset, &QCMakePresetComboBox::presetChanged, this,
+ &CMakeSetupDialog::onBuildPresetChanged);
QObject::connect(this->CMakeThread->cmakeInstance(),
&QCMake::sourceDirChanged, this,
@@ -270,6 +274,13 @@ void CMakeSetupDialog::initialize()
QObject::connect(this->CMakeThread->cmakeInstance(),
&QCMake::binaryDirChanged, this,
&CMakeSetupDialog::updateBinaryDirectory);
+ QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetsChanged,
+ this, &CMakeSetupDialog::updatePresets);
+ QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetChanged,
+ this, &CMakeSetupDialog::updatePreset);
+ QObject::connect(this->CMakeThread->cmakeInstance(),
+ &QCMake::presetLoadError, this,
+ &CMakeSetupDialog::showPresetLoadError);
QObject::connect(this->CMakeThread->cmakeInstance(),
&QCMake::progressChanged, this,
@@ -314,9 +325,15 @@ void CMakeSetupDialog::initialize()
QObject::connect(this->WarnUninitializedAction, &QAction::triggered,
this->CMakeThread->cmakeInstance(),
&QCMake::setWarnUninitializedMode);
+ QObject::connect(this->CMakeThread->cmakeInstance(),
+ &QCMake::warnUninitializedModeChanged,
+ this->WarnUninitializedAction, &QAction::setChecked);
- if (!this->SourceDirectory->text().isEmpty() ||
- !this->BinaryDirectory->lineEdit()->text().isEmpty()) {
+ if (!this->SourceDirectory->text().isEmpty() &&
+ !this->DeferredPreset.isNull()) {
+ this->onSourceDirectoryChanged(this->SourceDirectory->text());
+ } else if (!this->SourceDirectory->text().isEmpty() ||
+ !this->BinaryDirectory->lineEdit()->text().isEmpty()) {
this->onSourceDirectoryChanged(this->SourceDirectory->text());
this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text());
} else {
@@ -671,6 +688,41 @@ void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
}
}
+void CMakeSetupDialog::updatePresets(const QVector<QCMakePreset>& presets)
+{
+ if (this->Preset->presets() != presets) {
+ this->Preset->blockSignals(true);
+ this->Preset->setPresets(presets);
+ this->Preset->blockSignals(false);
+ }
+
+ this->Preset->setHidden(presets.isEmpty());
+ this->PresetLabel->setHidden(presets.isEmpty());
+
+ if (!this->DeferredPreset.isNull()) {
+ this->Preset->setPresetName(this->DeferredPreset);
+ this->DeferredPreset = QString{};
+ }
+}
+
+void CMakeSetupDialog::updatePreset(const QString& name)
+{
+ if (this->Preset->presetName() != name) {
+ this->Preset->blockSignals(true);
+ this->Preset->setPresetName(name);
+ this->Preset->blockSignals(false);
+ }
+}
+
+void CMakeSetupDialog::showPresetLoadError(
+ const QString& dir, cmCMakePresetsFile::ReadFileResult result)
+{
+ QMessageBox::warning(
+ this, "Error Reading CMake Presets",
+ QString::fromLocal8Bit("Could not read presets from %1: %2")
+ .arg(dir, cmCMakePresetsFile::ResultToString(result)));
+}
+
void CMakeSetupDialog::doBinaryBrowse()
{
QString dir = QFileDialog::getExistingDirectory(
@@ -686,6 +738,11 @@ void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
this->BinaryDirectory->setEditText(dir);
}
+void CMakeSetupDialog::setStartupBinaryDirectory(bool startup)
+{
+ this->StartupBinaryDirectory = startup;
+}
+
void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir)
{
this->Output->clear();
@@ -711,11 +768,24 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir)
Q_ARG(QString, dir));
}
+void CMakeSetupDialog::onBuildPresetChanged(const QString& name)
+{
+ QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setPreset",
+ Qt::QueuedConnection, Q_ARG(QString, name),
+ Q_ARG(bool, !this->StartupBinaryDirectory));
+ this->StartupBinaryDirectory = false;
+}
+
void CMakeSetupDialog::setSourceDirectory(const QString& dir)
{
this->SourceDirectory->setText(dir);
}
+void CMakeSetupDialog::setDeferredPreset(const QString& preset)
+{
+ this->DeferredPreset = preset;
+}
+
void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent)
{
percent = (percent * ProgressFactor) + ProgressOffset;
@@ -753,6 +823,7 @@ void CMakeSetupDialog::setEnabledState(bool enabled)
this->CacheValues->cacheModel()->setEditEnabled(enabled);
this->SourceDirectory->setEnabled(enabled);
this->BrowseSourceDirectoryButton->setEnabled(enabled);
+ this->Preset->setEnabled(enabled);
this->BinaryDirectory->setEnabled(enabled);
this->BrowseBinaryDirectoryButton->setEnabled(enabled);
this->ReloadCacheAction->setEnabled(enabled);
@@ -777,6 +848,17 @@ bool CMakeSetupDialog::setupFirstConfigure()
// restore from settings
dialog.loadFromSettings();
+ auto presetData = this->Preset->currentData();
+ if (presetData.isValid()) {
+ auto preset = presetData.value<QCMakePreset>();
+ dialog.setCurrentGenerator(preset.generator);
+ if (preset.setGenConfig) {
+ dialog.setPlatform(preset.architecture);
+ dialog.setToolset(preset.toolset);
+ }
+ dialog.setCompilerOption(CompilerOption::DefaultNative);
+ }
+
if (dialog.exec() == QDialog::Accepted) {
dialog.saveToSettings();
this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator());
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h
index d752ef2..f0cc929 100644
--- a/Source/QtDialog/CMakeSetupDialog.h
+++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -5,12 +5,15 @@
#include <memory>
#include "QCMake.h"
+#include "QCMakePreset.h"
#include <QEventLoop>
#include <QMainWindow>
#include <QThread>
+#include <QVector>
#include "ui_CMakeSetupDialog.h"
+class QCMakePresetItemModel;
class QCMakeThread;
class CMakeCacheModel;
class QProgressBar;
@@ -33,6 +36,8 @@ public:
public slots:
void setBinaryDirectory(const QString& dir);
void setSourceDirectory(const QString& dir);
+ void setDeferredPreset(const QString& preset);
+ void setStartupBinaryDirectory(bool startup);
protected slots:
void initialize();
@@ -52,6 +57,10 @@ protected slots:
void doDeleteCache();
void updateSourceDirectory(const QString& dir);
void updateBinaryDirectory(const QString& dir);
+ void updatePresets(const QVector<QCMakePreset>& presets);
+ void updatePreset(const QString& name);
+ void showPresetLoadError(const QString& dir,
+ cmCMakePresetsFile::ReadFileResult result);
void showProgress(const QString& msg, float percent);
void setEnabledState(bool);
bool setupFirstConfigure();
@@ -62,6 +71,7 @@ protected slots:
void saveBuildPaths(const QStringList&);
void onBinaryDirectoryChanged(const QString& dir);
void onSourceDirectoryChanged(const QString& dir);
+ void onBuildPresetChanged(const QString& name);
void setCacheModified();
void removeSelectedCacheEntries();
void selectionChanged();
@@ -113,6 +123,8 @@ protected:
QAction* WarnUninitializedAction;
QAction* InstallForCommandLineAction;
State CurrentState;
+ QString DeferredPreset;
+ bool StartupBinaryDirectory = false;
QTextCharFormat ErrorFormat;
QTextCharFormat MessageFormat;
diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui
index 5feee91..afb25eb 100644
--- a/Source/QtDialog/CMakeSetupDialog.ui
+++ b/Source/QtDialog/CMakeSetupDialog.ui
@@ -44,7 +44,7 @@
<number>6</number>
</property>
<item row="0" column="0">
- <widget class="QLabel" name="label">
+ <widget class="QLabel" name="SourceLabel">
<property name="text">
<string>Where is the source code:</string>
</property>
@@ -61,13 +61,23 @@
</widget>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="label_2">
+ <widget class="QLabel" name="PresetLabel">
<property name="text">
- <string>Where to build the binaries:</string>
+ <string>Preset:</string>
</property>
</widget>
</item>
<item row="1" column="1">
+ <widget class="QCMakePresetComboBox" name="Preset"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="BinaryLabel">
+ <property name="text">
+ <string>Where to build the binaries:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
<widget class="QComboBox" name="BinaryDirectory">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
@@ -83,7 +93,7 @@
</property>
</widget>
</item>
- <item row="1" column="2">
+ <item row="2" column="2">
<widget class="QPushButton" name="BrowseBinaryDirectoryButton">
<property name="text">
<string>Browse &amp;Build...</string>
@@ -367,6 +377,11 @@
<extends>QTreeView</extends>
<header>QCMakeCacheView.h</header>
</customwidget>
+ <customwidget>
+ <class>QCMakePresetComboBox</class>
+ <extends>QComboBox</extends>
+ <header>QCMakePresetComboBox.h</header>
+ </customwidget>
</customwidgets>
<resources>
<include location="CMakeSetup.qrc"/>
diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx
index 918f137..10360bb 100644
--- a/Source/QtDialog/FirstConfigure.cxx
+++ b/Source/QtDialog/FirstConfigure.cxx
@@ -145,6 +145,36 @@ void StartCompilerSetup::setCurrentGenerator(const QString& gen)
}
}
+void StartCompilerSetup::setPlatform(const QString& platform)
+{
+ this->PlatformOptions->setCurrentText(platform);
+}
+
+void StartCompilerSetup::setToolset(const QString& toolset)
+{
+ this->Toolset->setText(toolset);
+}
+
+void StartCompilerSetup::setCompilerOption(CompilerOption option)
+{
+ std::size_t index = 0;
+ switch (option) {
+ case CompilerOption::DefaultNative:
+ index = 0;
+ break;
+ case CompilerOption::SpecifyNative:
+ index = 1;
+ break;
+ case CompilerOption::ToolchainFile:
+ index = 2;
+ break;
+ case CompilerOption::Options:
+ index = 3;
+ break;
+ }
+ this->CompilerSetupOptions[index]->setChecked(true);
+}
+
QString StartCompilerSetup::getGenerator() const
{
return this->GeneratorOptions->currentText();
@@ -482,6 +512,26 @@ void FirstConfigure::setGenerators(
this->mStartCompilerSetupPage->setGenerators(gens);
}
+void FirstConfigure::setCurrentGenerator(const QString& gen)
+{
+ this->mStartCompilerSetupPage->setCurrentGenerator(gen);
+}
+
+void FirstConfigure::setPlatform(const QString& platform)
+{
+ this->mStartCompilerSetupPage->setPlatform(platform);
+}
+
+void FirstConfigure::setToolset(const QString& toolset)
+{
+ this->mStartCompilerSetupPage->setToolset(toolset);
+}
+
+void FirstConfigure::setCompilerOption(CompilerOption option)
+{
+ this->mStartCompilerSetupPage->setCompilerOption(option);
+}
+
QString FirstConfigure::getGenerator() const
{
return this->mStartCompilerSetupPage->getGenerator();
@@ -503,7 +553,7 @@ void FirstConfigure::loadFromSettings()
// restore generator
settings.beginGroup("Settings/StartPath");
QString lastGen = settings.value("LastGenerator").toString();
- this->mStartCompilerSetupPage->setCurrentGenerator(lastGen);
+ this->setCurrentGenerator(lastGen);
settings.endGroup();
// restore compiler setup
@@ -550,7 +600,7 @@ void FirstConfigure::loadFromSettings()
// this prevents them from being taken from environment, while the
// generator is taken from application settings
if (!mDefaultGenerator.isEmpty()) {
- this->mStartCompilerSetupPage->setCurrentGenerator(mDefaultGenerator);
+ this->setCurrentGenerator(mDefaultGenerator);
}
}
diff --git a/Source/QtDialog/FirstConfigure.h b/Source/QtDialog/FirstConfigure.h
index ca5f52e..5844f3a 100644
--- a/Source/QtDialog/FirstConfigure.h
+++ b/Source/QtDialog/FirstConfigure.h
@@ -22,6 +22,14 @@ enum FirstConfigurePages
Done
};
+enum class CompilerOption
+{
+ DefaultNative,
+ SpecifyNative,
+ ToolchainFile,
+ Options,
+};
+
//! the first page that gives basic options for what compilers setup to choose
//! from
class StartCompilerSetup : public QWizardPage
@@ -33,6 +41,9 @@ public:
~StartCompilerSetup();
void setGenerators(std::vector<cmake::GeneratorInfo> const& gens);
void setCurrentGenerator(const QString& gen);
+ void setToolset(const QString& toolset);
+ void setPlatform(const QString& platform);
+ void setCompilerOption(CompilerOption option);
QString getGenerator() const;
QString getToolset() const;
QString getPlatform() const;
@@ -167,6 +178,10 @@ public:
~FirstConfigure();
void setGenerators(std::vector<cmake::GeneratorInfo> const& gens);
+ void setCurrentGenerator(const QString& gen);
+ void setToolset(const QString& toolset);
+ void setPlatform(const QString& platform);
+ void setCompilerOption(CompilerOption option);
QString getGenerator() const;
QString getPlatform() const;
QString getToolset() const;
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index 974c545..9017a63 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -2,10 +2,14 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "QCMake.h"
+#include <algorithm>
+
#include <cm/memory>
#include <QCoreApplication>
#include <QDir>
+#include <QString>
+#include <QVector>
#include "cmExternalMakefileProjectGenerator.h"
#include "cmGlobalGenerator.h"
@@ -19,12 +23,15 @@
QCMake::QCMake(QObject* p)
: QObject(p)
+ , StartEnvironment(QProcessEnvironment::systemEnvironment())
, Environment(QProcessEnvironment::systemEnvironment())
{
this->WarnUninitializedMode = false;
qRegisterMetaType<QCMakeProperty>();
qRegisterMetaType<QCMakePropertyList>();
qRegisterMetaType<QProcessEnvironment>();
+ qRegisterMetaType<QVector<QCMakePreset>>();
+ qRegisterMetaType<cmCMakePresetsFile::ReadFileResult>();
cmSystemTools::DisableRunCommandOutput();
cmSystemTools::SetRunCommandHideConsole(true);
@@ -57,6 +64,17 @@ QCMake::QCMake(QObject* p)
for (cmake::GeneratorInfo const& gen : generators) {
this->AvailableGenerators.push_back(gen);
}
+
+ connect(&this->LoadPresetsTimer, &QTimer::timeout, this, [this]() {
+ this->loadPresets();
+ if (!this->PresetName.isEmpty() &&
+ this->CMakePresetsFile.Presets.find(
+ std::string(this->PresetName.toLocal8Bit())) ==
+ this->CMakePresetsFile.Presets.end()) {
+ this->setPreset(QString{});
+ }
+ });
+ this->LoadPresetsTimer.start(1000);
}
QCMake::~QCMake() = default;
@@ -73,6 +91,8 @@ void QCMake::setSourceDirectory(const QString& _dir)
if (this->SourceDirectory != dir) {
this->SourceDirectory = QDir::fromNativeSeparators(dir);
emit this->sourceDirChanged(this->SourceDirectory);
+ this->loadPresets();
+ this->setPreset(QString{});
}
}
@@ -129,6 +149,56 @@ void QCMake::setBinaryDirectory(const QString& _dir)
}
}
+void QCMake::setPreset(const QString& name, bool setBinary)
+{
+ if (this->PresetName != name) {
+ this->PresetName = name;
+ emit this->presetChanged(this->PresetName);
+
+ if (!name.isNull()) {
+ std::string presetName(name.toLocal8Bit());
+ auto const& preset = this->CMakePresetsFile.Presets[presetName];
+ auto expandedPreset = this->CMakePresetsFile.ExpandMacros(preset);
+ if (expandedPreset) {
+ if (setBinary) {
+ QString binaryDir =
+ QString::fromLocal8Bit(expandedPreset->BinaryDir.data());
+ this->setBinaryDirectory(binaryDir);
+ }
+ if (expandedPreset->WarnDev) {
+ this->CMakeInstance->SetSuppressDevWarnings(
+ !*expandedPreset->WarnDev);
+ }
+ if (expandedPreset->ErrorDev) {
+ this->CMakeInstance->SetDevWarningsAsErrors(
+ *expandedPreset->ErrorDev);
+ }
+ if (expandedPreset->WarnDeprecated) {
+ this->CMakeInstance->SetSuppressDeprecatedWarnings(
+ !*expandedPreset->WarnDeprecated);
+ }
+ if (expandedPreset->ErrorDeprecated) {
+ this->CMakeInstance->SetDeprecatedWarningsAsErrors(
+ *expandedPreset->ErrorDeprecated);
+ }
+ if (expandedPreset->WarnUninitialized) {
+ this->WarnUninitializedMode = *expandedPreset->WarnUninitialized;
+ emit this->warnUninitializedModeChanged(
+ *expandedPreset->WarnUninitialized);
+ }
+ this->Environment = this->StartEnvironment;
+ for (auto const& v : expandedPreset->Environment) {
+ if (v.second) {
+ this->Environment.insert(QString::fromLocal8Bit(v.first.data()),
+ QString::fromLocal8Bit(v.second->data()));
+ }
+ }
+ }
+ }
+ emit this->propertiesChanged(this->properties());
+ }
+}
+
void QCMake::setGenerator(const QString& gen)
{
if (this->Generator != gen) {
@@ -348,6 +418,56 @@ QCMakePropertyList QCMake::properties() const
ret.append(prop);
}
+ if (!this->PresetName.isNull()) {
+ std::string presetName(this->PresetName.toLocal8Bit());
+ auto p = this->CMakePresetsFile.ExpandMacros(
+ this->CMakePresetsFile.Presets.at(presetName));
+ if (p) {
+ for (auto const& v : p->CacheVariables) {
+ if (!v.second) {
+ continue;
+ }
+ QCMakeProperty prop;
+ prop.Key = QString::fromLocal8Bit(v.first.data());
+ prop.Value = QString::fromLocal8Bit(v.second->Value.data());
+ prop.Type = QCMakeProperty::STRING;
+ if (!v.second->Type.empty()) {
+ auto type = cmState::StringToCacheEntryType(v.second->Type);
+ switch (type) {
+ case cmStateEnums::BOOL:
+ prop.Type = QCMakeProperty::BOOL;
+ prop.Value = cmIsOn(v.second->Value);
+ break;
+ case cmStateEnums::PATH:
+ prop.Type = QCMakeProperty::PATH;
+ break;
+ case cmStateEnums::FILEPATH:
+ prop.Type = QCMakeProperty::FILEPATH;
+ break;
+ default:
+ prop.Type = QCMakeProperty::STRING;
+ break;
+ }
+ }
+
+ // QCMakeCacheModel prefers variables earlier in the list rather than
+ // later, so overwrite them if they already exist rather than simply
+ // appending
+ bool found = false;
+ for (auto& orig : ret) {
+ if (orig.Key == prop.Key) {
+ orig = prop;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ret.append(prop);
+ }
+ }
+ }
+ }
+
return ret;
}
@@ -405,6 +525,46 @@ void QCMake::setUpEnvironment() const
}
}
+void QCMake::loadPresets()
+{
+ auto result = this->CMakePresetsFile.ReadProjectPresets(
+ this->SourceDirectory.toLocal8Bit().data(), true);
+ if (result != this->LastLoadPresetsResult &&
+ result != cmCMakePresetsFile::ReadFileResult::READ_OK) {
+ emit this->presetLoadError(this->SourceDirectory, result);
+ }
+ this->LastLoadPresetsResult = result;
+
+ QVector<QCMakePreset> presets;
+ for (auto const& name : this->CMakePresetsFile.PresetOrder) {
+ auto const& p = this->CMakePresetsFile.Presets[name];
+ if (p.Hidden) {
+ continue;
+ }
+
+ QCMakePreset preset;
+ preset.name = std::move(QString::fromLocal8Bit(p.Name.data()));
+ preset.displayName =
+ std::move(QString::fromLocal8Bit(p.DisplayName.data()));
+ preset.description =
+ std::move(QString::fromLocal8Bit(p.Description.data()));
+ preset.generator = std::move(QString::fromLocal8Bit(p.Generator.data()));
+ preset.architecture =
+ std::move(QString::fromLocal8Bit(p.Architecture.data()));
+ preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data()));
+ preset.setGenConfig = !p.GeneratorConfig ||
+ p.GeneratorConfig == cmCMakePresetsFile::CMakeGeneratorConfig::Default;
+ preset.enabled = std::find_if(this->AvailableGenerators.begin(),
+ this->AvailableGenerators.end(),
+ [&p](const cmake::GeneratorInfo& g) {
+ return g.name == p.Generator;
+ }) != this->AvailableGenerators.end() &&
+ this->CMakePresetsFile.ExpandMacros(p);
+ presets.push_back(preset);
+ }
+ emit this->presetsChanged(presets);
+}
+
QString QCMake::binaryDirectory() const
{
return this->BinaryDirectory;
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
index f569951..a6751b0 100644
--- a/Source/QtDialog/QCMake.h
+++ b/Source/QtDialog/QCMake.h
@@ -4,6 +4,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include "cmCMakePresetsFile.h"
#include "cmake.h"
#ifdef _MSC_VER
@@ -14,6 +15,7 @@
#include <memory>
#include <vector>
+#include "QCMakePreset.h"
#include <QAtomicInt>
#include <QList>
#include <QMetaType>
@@ -21,6 +23,7 @@
#include <QProcessEnvironment>
#include <QString>
#include <QStringList>
+#include <QTimer>
#include <QVariant>
/// struct to represent cmake properties in Qt
@@ -57,6 +60,7 @@ using QCMakePropertyList = QList<QCMakeProperty>;
Q_DECLARE_METATYPE(QCMakeProperty)
Q_DECLARE_METATYPE(QCMakePropertyList)
Q_DECLARE_METATYPE(QProcessEnvironment)
+Q_DECLARE_METATYPE(cmCMakePresetsFile::ReadFileResult)
/// Qt API for CMake library.
/// Wrapper like class allows for easier integration with
@@ -74,6 +78,8 @@ public slots:
void setSourceDirectory(const QString& dir);
/// set the binary directory to build in
void setBinaryDirectory(const QString& dir);
+ /// set the preset name to use
+ void setPreset(const QString& name, bool setBinary = true);
/// set the desired generator to use
void setGenerator(const QString& generator);
/// set the desired generator to use
@@ -147,6 +153,15 @@ signals:
void sourceDirChanged(const QString& dir);
/// signal when the binary directory changes
void binaryDirChanged(const QString& dir);
+ /// signal when the preset list changes
+ void presetsChanged(const QVector<QCMakePreset>& presets);
+ /// signal when the selected preset changes
+ void presetChanged(const QString& name);
+ /// signal when there's an error reading the presets files
+ void presetLoadError(const QString& dir,
+ cmCMakePresetsFile::ReadFileResult error);
+ /// signal when uninitialized warning changes
+ void warnUninitializedModeChanged(bool value);
/// signal for progress events
void progressChanged(const QString& msg, float percent);
/// signal when configure is done
@@ -178,6 +193,8 @@ protected:
void stderrCallback(std::string const& msg);
void setUpEnvironment() const;
+ void loadPresets();
+
bool WarnUninitializedMode;
QString SourceDirectory;
QString BinaryDirectory;
@@ -185,7 +202,13 @@ protected:
QString Platform;
QString Toolset;
std::vector<cmake::GeneratorInfo> AvailableGenerators;
+ cmCMakePresetsFile CMakePresetsFile;
+ cmCMakePresetsFile::ReadFileResult LastLoadPresetsResult =
+ cmCMakePresetsFile::ReadFileResult::READ_OK;
+ QString PresetName;
QString CMakeExecutable;
QAtomicInt InterruptFlag;
+ QProcessEnvironment StartEnvironment;
QProcessEnvironment Environment;
+ QTimer LoadPresetsTimer;
};
diff --git a/Source/QtDialog/QCMakePreset.cxx b/Source/QtDialog/QCMakePreset.cxx
new file mode 100644
index 0000000..b10cf07
--- /dev/null
+++ b/Source/QtDialog/QCMakePreset.cxx
@@ -0,0 +1,50 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "QCMakePreset.h"
+
+bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return lhs.name == rhs.name && lhs.displayName == rhs.displayName &&
+ lhs.description == rhs.description && lhs.generator == rhs.generator &&
+ lhs.architecture == rhs.architecture && lhs.toolset == rhs.toolset &&
+ lhs.setGenConfig == rhs.setGenConfig && lhs.enabled == rhs.enabled;
+}
+
+bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return lhs.name < rhs.name ||
+ (lhs.name == rhs.name &&
+ (lhs.displayName < rhs.displayName ||
+ (lhs.displayName == rhs.displayName &&
+ (lhs.description < rhs.description ||
+ (lhs.description == rhs.description &&
+ (lhs.generator < rhs.generator ||
+ (lhs.generator == rhs.generator &&
+ (lhs.architecture < rhs.architecture ||
+ (lhs.architecture == rhs.architecture &&
+ (lhs.toolset < rhs.toolset ||
+ (lhs.toolset == rhs.toolset &&
+ (lhs.setGenConfig < rhs.setGenConfig ||
+ (lhs.setGenConfig == rhs.setGenConfig &&
+ (lhs.enabled < rhs.enabled))))))))))))));
+}
+
+bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return rhs >= lhs;
+}
+
+bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return rhs < lhs;
+}
+
+bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs)
+{
+ return !(lhs < rhs);
+}
diff --git a/Source/QtDialog/QCMakePreset.h b/Source/QtDialog/QCMakePreset.h
new file mode 100644
index 0000000..93d70d8
--- /dev/null
+++ b/Source/QtDialog/QCMakePreset.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <QString>
+#include <QVariant>
+
+#include "cmCMakePresetsFile.h"
+
+class QCMakePreset
+{
+public:
+ QString name;
+ QString displayName;
+ QString description;
+ QString generator;
+ QString architecture;
+ QString toolset;
+ bool setGenConfig;
+ bool enabled;
+};
+
+bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs);
+bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs);
+bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs);
+bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs);
+bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs);
+bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs);
+
+Q_DECLARE_METATYPE(QCMakePreset)
diff --git a/Source/QtDialog/QCMakePresetComboBox.cxx b/Source/QtDialog/QCMakePresetComboBox.cxx
new file mode 100644
index 0000000..efadb73
--- /dev/null
+++ b/Source/QtDialog/QCMakePresetComboBox.cxx
@@ -0,0 +1,64 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "QCMakePresetComboBox.h"
+
+#include "QCMakePresetItemModel.h"
+
+QCMakePresetComboBox::QCMakePresetComboBox(QWidget* parent)
+ : QComboBox(parent)
+{
+ this->m_model = new QCMakePresetItemModel(this);
+ this->setModel(this->m_model);
+
+ QObject::connect(this->m_model, &QCMakePresetItemModel::modelAboutToBeReset,
+ this, [this]() { this->m_resetting = true; });
+ QObject::connect(this->m_model, &QCMakePresetItemModel::modelReset, this,
+ [this]() {
+ this->setPresetName(this->m_lastPreset);
+ this->m_resetting = false;
+ this->emitPresetChanged();
+ });
+ QObject::connect(
+ this,
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, [this](int /*row*/) {
+ if (!this->m_resetting) {
+ this->emitPresetChanged();
+ }
+ });
+}
+
+const QVector<QCMakePreset>& QCMakePresetComboBox::presets() const
+{
+ return this->m_model->presets();
+}
+
+QString QCMakePresetComboBox::presetName() const
+{
+ auto preset = this->currentData();
+ if (preset.canConvert<QCMakePreset>()) {
+ return preset.value<QCMakePreset>().name;
+ }
+ return QString{};
+}
+
+void QCMakePresetComboBox::setPresets(const QVector<QCMakePreset>& presets)
+{
+ this->m_model->setPresets(presets);
+}
+
+void QCMakePresetComboBox::setPresetName(const QString& name)
+{
+ this->setCurrentIndex(this->m_model->presetNameToRow(name));
+ if (this->signalsBlocked()) {
+ this->m_lastPreset = this->presetName();
+ }
+}
+
+void QCMakePresetComboBox::emitPresetChanged()
+{
+ if (this->presetName() != this->m_lastPreset) {
+ emit this->presetChanged(this->presetName());
+ this->m_lastPreset = this->presetName();
+ }
+}
diff --git a/Source/QtDialog/QCMakePresetComboBox.h b/Source/QtDialog/QCMakePresetComboBox.h
new file mode 100644
index 0000000..d1eeffe
--- /dev/null
+++ b/Source/QtDialog/QCMakePresetComboBox.h
@@ -0,0 +1,35 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "QCMakePreset.h"
+#include <QComboBox>
+#include <QObject>
+#include <QString>
+#include <QVector>
+
+class QCMakePresetItemModel;
+
+class QCMakePresetComboBox : public QComboBox
+{
+ Q_OBJECT
+public:
+ QCMakePresetComboBox(QWidget* parent = nullptr);
+
+ const QVector<QCMakePreset>& presets() const;
+ QString presetName() const;
+
+public slots:
+ void setPresets(const QVector<QCMakePreset>& presets);
+ void setPresetName(const QString& name);
+
+signals:
+ void presetChanged(const QString& name);
+
+private:
+ QCMakePresetItemModel* m_model;
+ bool m_resetting = false;
+ QString m_lastPreset;
+
+ void emitPresetChanged();
+};
diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx
new file mode 100644
index 0000000..00a4e18
--- /dev/null
+++ b/Source/QtDialog/QCMakePresetItemModel.cxx
@@ -0,0 +1,143 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "QCMakePresetItemModel.h"
+
+#include <QFont>
+
+QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent)
+ : QAbstractItemModel(parent)
+{
+}
+
+QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const
+{
+ switch (role) {
+ case Qt::AccessibleDescriptionRole:
+ // Separators have to return "separator" for the
+ // AccessibleDescriptionRole. This was determined by looking at
+ // QComboBoxDelegate::isSeparator() (located in qcombobox_p.h.)
+ if (index.internalId() == SEPARATOR_INDEX) {
+ return QString::fromLocal8Bit("separator");
+ }
+ return QString{};
+ case Qt::DisplayRole: {
+ if (index.internalId() == CUSTOM_INDEX) {
+ return QString::fromLocal8Bit("<custom>");
+ }
+ if (index.internalId() == SEPARATOR_INDEX) {
+ return QVariant{};
+ }
+ auto const& preset = this->m_presets[index.internalId()];
+ return preset.displayName.isEmpty() ? preset.name : preset.displayName;
+ }
+ case Qt::ToolTipRole:
+ if (index.internalId() == CUSTOM_INDEX) {
+ return QString::fromLocal8Bit("Specify all settings manually");
+ }
+ if (index.internalId() == SEPARATOR_INDEX) {
+ return QVariant{};
+ }
+ return this->m_presets[index.internalId()].description;
+ case Qt::UserRole:
+ if (index.internalId() == CUSTOM_INDEX) {
+ return QVariant{};
+ }
+ if (index.internalId() == SEPARATOR_INDEX) {
+ return QVariant{};
+ }
+ return QVariant::fromValue(this->m_presets[index.internalId()]);
+ case Qt::FontRole:
+ if (index.internalId() == CUSTOM_INDEX) {
+ QFont font;
+ font.setItalic(true);
+ return font;
+ }
+ return QFont{};
+ default:
+ return QVariant{};
+ }
+}
+
+Qt::ItemFlags QCMakePresetItemModel::flags(const QModelIndex& index) const
+{
+ Qt::ItemFlags flags =
+ Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
+ if (index.internalId() != SEPARATOR_INDEX &&
+ (index.internalId() == CUSTOM_INDEX ||
+ this->m_presets[index.internalId()].enabled)) {
+ flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+ }
+ return flags;
+}
+
+int QCMakePresetItemModel::rowCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ if (this->m_presets.empty()) {
+ return 1;
+ }
+ return this->m_presets.size() + 2;
+}
+
+int QCMakePresetItemModel::columnCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ return 1;
+}
+
+QModelIndex QCMakePresetItemModel::index(int row, int column,
+ const QModelIndex& parent) const
+{
+ if (parent.isValid() || column != 0 || row < 0 ||
+ row >= this->rowCount(QModelIndex{})) {
+ return QModelIndex{};
+ }
+
+ if (this->m_presets.empty() || row == this->m_presets.size() + 1) {
+ return this->createIndex(row, column, CUSTOM_INDEX);
+ }
+
+ if (row == this->m_presets.size()) {
+ return this->createIndex(row, column, SEPARATOR_INDEX);
+ }
+
+ return this->createIndex(row, column, static_cast<quintptr>(row));
+}
+
+QModelIndex QCMakePresetItemModel::parent(const QModelIndex& /*index*/) const
+{
+ return QModelIndex{};
+}
+
+QVector<QCMakePreset> const& QCMakePresetItemModel::presets() const
+{
+ return this->m_presets;
+}
+
+void QCMakePresetItemModel::setPresets(QVector<QCMakePreset> const& presets)
+{
+ this->beginResetModel();
+ this->m_presets = presets;
+ this->endResetModel();
+}
+
+int QCMakePresetItemModel::presetNameToRow(const QString& name) const
+{
+ if (this->m_presets.empty()) {
+ return 0;
+ }
+
+ int index = 0;
+ for (auto const& preset : this->m_presets) {
+ if (preset.name == name) {
+ return index;
+ }
+ index++;
+ }
+
+ return this->m_presets.size() + 1;
+}
diff --git a/Source/QtDialog/QCMakePresetItemModel.h b/Source/QtDialog/QCMakePresetItemModel.h
new file mode 100644
index 0000000..79fba29
--- /dev/null
+++ b/Source/QtDialog/QCMakePresetItemModel.h
@@ -0,0 +1,45 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <cm/optional>
+
+#include "QCMakePreset.h"
+#include <QAbstractItemModel>
+#include <QModelIndex>
+#include <QString>
+#include <QVariant>
+#include <QVector>
+#include <QtGlobal>
+
+class QObject;
+
+class QCMakePresetItemModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ QCMakePresetItemModel(QObject* parent = nullptr);
+
+ QVariant data(const QModelIndex& index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ int rowCount(const QModelIndex& parent = QModelIndex{}) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex{}) const override;
+
+ QModelIndex index(int row, int column,
+ const QModelIndex& parent = QModelIndex{}) const override;
+ QModelIndex parent(const QModelIndex& index) const override;
+
+ QVector<QCMakePreset> const& presets() const;
+
+ int presetNameToRow(const QString& name) const;
+
+public slots:
+ void setPresets(QVector<QCMakePreset> const& presets);
+
+private:
+ QVector<QCMakePreset> m_presets;
+
+ static constexpr quintptr SEPARATOR_INDEX = static_cast<quintptr>(-2);
+ static constexpr quintptr CUSTOM_INDEX = static_cast<quintptr>(-1);
+};
diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx
new file mode 100644
index 0000000..25997fd
--- /dev/null
+++ b/Source/cmCMakePresetsFile.cxx
@@ -0,0 +1,763 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCMakePresetsFile.h"
+
+#include <cstdlib>
+#include <functional>
+#include <utility>
+
+#include <cmext/string_view>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmJSONHelpers.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmVersion.h"
+
+namespace {
+enum class CycleStatus
+{
+ Unvisited,
+ InProgress,
+ Verified,
+};
+
+using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
+using CacheVariable = cmCMakePresetsFile::CacheVariable;
+using UnexpandedPreset = cmCMakePresetsFile::UnexpandedPreset;
+using CMakeGeneratorConfig = cmCMakePresetsFile::CMakeGeneratorConfig;
+
+constexpr int MIN_VERSION = 1;
+constexpr int MAX_VERSION = 1;
+
+struct CMakeVersion
+{
+ unsigned int Major = 0;
+ unsigned int Minor = 0;
+ unsigned int Patch = 0;
+};
+
+struct RootPresets
+{
+ CMakeVersion CMakeMinimumRequired;
+ std::vector<cmCMakePresetsFile::UnexpandedPreset> Presets;
+};
+
+cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
+{
+ return [error](std::nullptr_t& /*out*/,
+ const Json::Value* value) -> ReadFileResult {
+ if (!value) {
+ return ReadFileResult::READ_OK;
+ }
+
+ if (!value->isObject()) {
+ return error;
+ }
+
+ return ReadFileResult::READ_OK;
+ };
+}
+
+auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+
+auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
+ ReadFileResult::NO_VERSION, VersionIntHelper);
+
+auto const RootVersionHelper =
+ cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
+ ReadFileResult::INVALID_ROOT)
+ .Bind("version"_s, VersionHelper, false);
+
+auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
+
+auto const VariableObjectHelper =
+ cmJSONObjectHelper<CacheVariable, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
+ .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
+ .Bind("value"_s, &CacheVariable::Value, VariableStringHelper);
+
+ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
+ const Json::Value* value)
+{
+ if (value->isString()) {
+ out = CacheVariable{
+ /*Type=*/"",
+ /*Value=*/value->asString(),
+ };
+ return ReadFileResult::READ_OK;
+ }
+ if (value->isObject()) {
+ out.emplace();
+ return VariableObjectHelper(*out, value);
+ }
+ if (value->isNull()) {
+ out = cm::nullopt;
+ return ReadFileResult::READ_OK;
+ }
+ return ReadFileResult::INVALID_VARIABLE;
+}
+
+auto const VariablesHelper =
+ cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
+
+auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+
+ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
+ const Json::Value* value)
+{
+ if (!value || value->isNull()) {
+ out = cm::nullopt;
+ return ReadFileResult::READ_OK;
+ }
+ if (value->isString()) {
+ out = value->asString();
+ return ReadFileResult::READ_OK;
+ }
+ return ReadFileResult::INVALID_PRESET;
+}
+
+auto const EnvironmentMapHelper =
+ cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
+ EnvironmentHelper);
+
+auto const PresetVectorStringHelper =
+ cmJSONVectorHelper<std::string, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
+ PresetStringHelper);
+
+ReadFileResult PresetInheritsHelper(std::vector<std::string>& out,
+ const Json::Value* value)
+{
+ out.clear();
+ if (!value) {
+ return ReadFileResult::READ_OK;
+ }
+
+ if (value->isString()) {
+ out.push_back(value->asString());
+ return ReadFileResult::READ_OK;
+ }
+
+ return PresetVectorStringHelper(out, value);
+}
+
+auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+
+auto const PresetOptionalBoolHelper =
+ cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK,
+ PresetBoolHelper);
+
+auto const PresetWarningsHelper =
+ cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ .Bind("dev"_s, &UnexpandedPreset::WarnDev, PresetOptionalBoolHelper, false)
+ .Bind("deprecated"_s, &UnexpandedPreset::WarnDeprecated,
+ PresetOptionalBoolHelper, false)
+ .Bind("uninitialized"_s, &UnexpandedPreset::WarnUninitialized,
+ PresetOptionalBoolHelper, false)
+ .Bind("unusedCli"_s, &UnexpandedPreset::WarnUnusedCli,
+ PresetOptionalBoolHelper, false)
+ .Bind("systemVars"_s, &UnexpandedPreset::WarnSystemVars,
+ PresetOptionalBoolHelper, false);
+
+auto const PresetErrorsHelper =
+ cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ .Bind("dev"_s, &UnexpandedPreset::ErrorDev, PresetOptionalBoolHelper,
+ false)
+ .Bind("deprecated"_s, &UnexpandedPreset::ErrorDeprecated,
+ PresetOptionalBoolHelper, false);
+
+auto const PresetDebugHelper =
+ cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ .Bind("output"_s, &UnexpandedPreset::DebugOutput, PresetOptionalBoolHelper,
+ false)
+ .Bind("tryCompile"_s, &UnexpandedPreset::DebugTryCompile,
+ PresetOptionalBoolHelper, false)
+ .Bind("find"_s, &UnexpandedPreset::DebugFind, PresetOptionalBoolHelper,
+ false);
+
+ReadFileResult CMakeGeneratorConfigHelper(
+ cm::optional<CMakeGeneratorConfig>& out, const Json::Value* value)
+{
+ if (!value) {
+ out = cm::nullopt;
+ return ReadFileResult::READ_OK;
+ }
+
+ if (!value->isString()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+
+ if (value->asString() == "default") {
+ out = CMakeGeneratorConfig::Default;
+ return ReadFileResult::READ_OK;
+ }
+
+ if (value->asString() == "ignore") {
+ out = CMakeGeneratorConfig::Ignore;
+ return ReadFileResult::READ_OK;
+ }
+
+ return ReadFileResult::INVALID_PRESET;
+}
+
+auto const PresetHelper =
+ cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ .Bind("name"_s, &UnexpandedPreset::Name, PresetStringHelper)
+ .Bind("inherits"_s, &UnexpandedPreset::Inherits, PresetInheritsHelper,
+ false)
+ .Bind("hidden"_s, &UnexpandedPreset::Hidden, PresetBoolHelper, false)
+ .Bind<std::nullptr_t>("vendor"_s, nullptr,
+ VendorHelper(ReadFileResult::INVALID_PRESET), false)
+ .Bind("displayName"_s, &UnexpandedPreset::DisplayName, PresetStringHelper,
+ false)
+ .Bind("description"_s, &UnexpandedPreset::Description, PresetStringHelper,
+ false)
+ .Bind("generator"_s, &UnexpandedPreset::Generator, PresetStringHelper,
+ false)
+ .Bind("architecture"_s, &UnexpandedPreset::Architecture,
+ PresetStringHelper, false)
+ .Bind("toolset"_s, &UnexpandedPreset::Toolset, PresetStringHelper, false)
+ .Bind("cmakeGeneratorConfig"_s, &UnexpandedPreset::GeneratorConfig,
+ CMakeGeneratorConfigHelper, false)
+ .Bind("binaryDir"_s, &UnexpandedPreset::BinaryDir, PresetStringHelper,
+ false)
+ .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false)
+ .Bind("cacheVariables"_s, &UnexpandedPreset::CacheVariables,
+ VariablesHelper, false)
+ .Bind("environment"_s, &UnexpandedPreset::Environment,
+ EnvironmentMapHelper, false)
+ .Bind("warnings"_s, PresetWarningsHelper, false)
+ .Bind("errors"_s, PresetErrorsHelper, false)
+ .Bind("debug"_s, PresetDebugHelper, false);
+
+auto const PresetsHelper =
+ cmJSONVectorHelper<UnexpandedPreset, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, PresetHelper);
+
+auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+
+auto const CMakeVersionHelper =
+ cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
+ .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
+ .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
+ .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
+
+auto const RootPresetsHelper =
+ cmJSONObjectHelper<RootPresets, ReadFileResult>(
+ ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
+ .Bind<int>("version"_s, nullptr, VersionHelper)
+ .Bind("configurePresets"_s, &RootPresets::Presets, PresetsHelper, false)
+ .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
+ CMakeVersionHelper, false)
+ .Bind<std::nullptr_t>("vendor"_s, nullptr,
+ VendorHelper(ReadFileResult::INVALID_ROOT), false);
+
+void InheritString(std::string& child, const std::string& parent)
+{
+ if (child.empty()) {
+ child = parent;
+ }
+}
+
+void InheritOptionalBool(cm::optional<bool>& child,
+ const cm::optional<bool>& parent)
+{
+ if (!child) {
+ child = parent;
+ }
+}
+
+/**
+ * Check preset inheritance for cycles (using a DAG check algorithm) while
+ * also bubbling up fields through the inheritance hierarchy, then verify
+ * that each preset has the required fields, either directly or through
+ * inheritance.
+ */
+ReadFileResult VisitPreset(std::map<std::string, UnexpandedPreset>& presets,
+ UnexpandedPreset& preset,
+ std::map<std::string, CycleStatus> cycleStatus)
+{
+ switch (cycleStatus[preset.Name]) {
+ case CycleStatus::InProgress:
+ return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
+ case CycleStatus::Verified:
+ return ReadFileResult::READ_OK;
+ default:
+ break;
+ }
+
+ cycleStatus[preset.Name] = CycleStatus::InProgress;
+
+ for (auto const& i : preset.Inherits) {
+ auto parent = presets.find(i);
+ if (parent == presets.end()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+
+ if (!preset.User && parent->second.User) {
+ return ReadFileResult::USER_PRESET_INHERITANCE;
+ }
+
+ auto result = VisitPreset(presets, parent->second, cycleStatus);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+
+ InheritString(preset.Generator, parent->second.Generator);
+ InheritString(preset.Architecture, parent->second.Architecture);
+ InheritString(preset.Toolset, parent->second.Toolset);
+ if (!preset.GeneratorConfig) {
+ preset.GeneratorConfig = parent->second.GeneratorConfig;
+ }
+ InheritString(preset.BinaryDir, parent->second.BinaryDir);
+ InheritOptionalBool(preset.WarnDev, parent->second.WarnDev);
+ InheritOptionalBool(preset.ErrorDev, parent->second.ErrorDev);
+ InheritOptionalBool(preset.WarnDeprecated, parent->second.WarnDeprecated);
+ InheritOptionalBool(preset.ErrorDeprecated,
+ parent->second.ErrorDeprecated);
+ InheritOptionalBool(preset.WarnUninitialized,
+ parent->second.WarnUninitialized);
+ InheritOptionalBool(preset.WarnUnusedCli, parent->second.WarnUnusedCli);
+ InheritOptionalBool(preset.WarnSystemVars, parent->second.WarnSystemVars);
+ for (auto const& v : parent->second.CacheVariables) {
+ preset.CacheVariables.insert(v);
+ }
+ for (auto const& v : parent->second.Environment) {
+ preset.Environment.insert(v);
+ }
+ }
+
+ if (!preset.Hidden) {
+ if (preset.Generator.empty()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+ if (preset.BinaryDir.empty()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+ if (preset.WarnDev == false && preset.ErrorDev == true) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+ if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+ }
+
+ cycleStatus[preset.Name] = CycleStatus::Verified;
+ return ReadFileResult::READ_OK;
+}
+
+ReadFileResult ComputePresetInheritance(
+ std::map<std::string, UnexpandedPreset>& presets)
+{
+ std::map<std::string, CycleStatus> cycleStatus;
+ for (auto const& it : presets) {
+ cycleStatus[it.first] = CycleStatus::Unvisited;
+ }
+
+ for (auto& it : presets) {
+ auto result = VisitPreset(presets, it.second, cycleStatus);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+ }
+
+ return ReadFileResult::READ_OK;
+}
+
+constexpr const char* ValidPrefixes[] = {
+ "",
+ "env",
+ "penv",
+ "vendor",
+};
+
+bool PrefixesValidMacroNamespace(const std::string& str)
+{
+ for (auto const& prefix : ValidPrefixes) {
+ if (cmHasPrefix(prefix, str)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IsValidMacroNamespace(const std::string& str)
+{
+ for (auto const& prefix : ValidPrefixes) {
+ if (str == prefix) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VisitEnv(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& value, CycleStatus& status);
+bool ExpandMacros(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& out);
+bool ExpandMacro(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& out, const std::string& macroNamespace,
+ const std::string& macroName);
+
+bool VisitEnv(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& value, CycleStatus& status)
+{
+ if (status == CycleStatus::Verified) {
+ return true;
+ }
+ if (status == CycleStatus::InProgress) {
+ return false;
+ }
+
+ status = CycleStatus::InProgress;
+ if (!ExpandMacros(file, preset, envCycles, value)) {
+ return false;
+ }
+ status = CycleStatus::Verified;
+ return true;
+}
+
+bool ExpandMacros(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& out)
+{
+ std::string result;
+ std::string macroNamespace;
+ std::string macroName;
+
+ enum class State
+ {
+ Default,
+ MacroNamespace,
+ MacroName,
+ } state = State::Default;
+
+ for (auto c : out) {
+ switch (state) {
+ case State::Default:
+ if (c == '$') {
+ state = State::MacroNamespace;
+ } else {
+ result += c;
+ }
+ break;
+
+ case State::MacroNamespace:
+ if (c == '{') {
+ if (IsValidMacroNamespace(macroNamespace)) {
+ state = State::MacroName;
+ } else {
+ result += '$';
+ result += macroNamespace;
+ result += '{';
+ macroNamespace.clear();
+ state = State::Default;
+ }
+ } else {
+ macroNamespace += c;
+ if (!PrefixesValidMacroNamespace(macroNamespace)) {
+ result += '$';
+ result += macroNamespace;
+ macroNamespace.clear();
+ state = State::Default;
+ }
+ }
+ break;
+
+ case State::MacroName:
+ if (c == '}') {
+ if (!ExpandMacro(file, preset, envCycles, result, macroNamespace,
+ macroName)) {
+ return false;
+ }
+ macroNamespace.clear();
+ macroName.clear();
+ state = State::Default;
+ } else {
+ macroName += c;
+ }
+ break;
+ }
+ }
+
+ switch (state) {
+ case State::Default:
+ break;
+ case State::MacroNamespace:
+ result += '$';
+ result += macroNamespace;
+ break;
+ case State::MacroName:
+ return false;
+ }
+
+ out = std::move(result);
+ return true;
+}
+
+bool ExpandMacro(const cmCMakePresetsFile& file,
+ cmCMakePresetsFile::ExpandedPreset& preset,
+ std::map<std::string, CycleStatus>& envCycles,
+ std::string& out, const std::string& macroNamespace,
+ const std::string& macroName)
+{
+ if (macroNamespace.empty()) {
+ if (macroName == "sourceDir") {
+ out += file.SourceDir;
+ return true;
+ }
+ if (macroName == "sourceParentDir") {
+ out += cmSystemTools::GetParentDirectory(file.SourceDir);
+ return true;
+ }
+ if (macroName == "presetName") {
+ out += preset.Name;
+ return true;
+ }
+ if (macroName == "generator") {
+ out += preset.Generator;
+ return true;
+ }
+ if (macroName == "dollar") {
+ out += '$';
+ return true;
+ }
+ }
+
+ if (macroNamespace == "env") {
+ auto v = preset.Environment.find(macroName);
+ if (v != preset.Environment.end() && v->second) {
+ if (!VisitEnv(file, preset, envCycles, *v->second,
+ envCycles[macroName])) {
+ return false;
+ }
+ out += *v->second;
+ return true;
+ }
+ }
+
+ if (macroNamespace == "env" || macroNamespace == "penv") {
+ const char* value = std::getenv(macroName.c_str());
+ if (value) {
+ out += value;
+ }
+ return true;
+ }
+
+ // "vendor" falls through to here
+ return false;
+}
+}
+
+std::string cmCMakePresetsFile::GetFilename(const std::string& sourceDir)
+{
+ return cmStrCat(sourceDir, "/CMakePresets.json");
+}
+
+std::string cmCMakePresetsFile::GetUserFilename(const std::string& sourceDir)
+{
+ return cmStrCat(sourceDir, "/CMakeUserPresets.json");
+}
+
+cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets(
+ const std::string& sourceDir, bool allowNoFiles)
+{
+ bool haveOneFile = false;
+ this->SourceDir = sourceDir;
+ this->Presets.clear();
+ this->PresetOrder.clear();
+
+ std::vector<std::string> presetOrder;
+ std::map<std::string, UnexpandedPreset> presetMap;
+
+ std::string filename = GetUserFilename(this->SourceDir);
+ if (cmSystemTools::FileExists(filename)) {
+ auto result = this->ReadJSONFile(filename, presetOrder, presetMap, true);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+ haveOneFile = true;
+ }
+
+ filename = GetFilename(this->SourceDir);
+ if (cmSystemTools::FileExists(filename)) {
+ auto result = this->ReadJSONFile(filename, presetOrder, presetMap, false);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+ haveOneFile = true;
+ }
+
+ if (!haveOneFile) {
+ return allowNoFiles ? ReadFileResult::READ_OK
+ : ReadFileResult::FILE_NOT_FOUND;
+ }
+
+ auto result = ComputePresetInheritance(presetMap);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+
+ this->PresetOrder = std::move(presetOrder);
+ this->Presets = std::move(presetMap);
+ return ReadFileResult::READ_OK;
+}
+
+const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
+{
+ switch (result) {
+ case ReadFileResult::READ_OK:
+ return "OK";
+ case ReadFileResult::FILE_NOT_FOUND:
+ return "File not found";
+ case ReadFileResult::JSON_PARSE_ERROR:
+ return "JSON parse error";
+ case ReadFileResult::INVALID_ROOT:
+ return "Invalid root object";
+ case ReadFileResult::NO_VERSION:
+ return "No \"version\" field";
+ case ReadFileResult::INVALID_VERSION:
+ return "Invalid \"version\" field";
+ case ReadFileResult::UNRECOGNIZED_VERSION:
+ return "Unrecognized \"version\" field";
+ case ReadFileResult::INVALID_CMAKE_VERSION:
+ return "Invalid \"cmakeMinimumRequired\" field";
+ case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
+ return "\"cmakeMinimumRequired\" version too new";
+ case ReadFileResult::INVALID_PRESETS:
+ return "Invalid \"configurePresets\" field";
+ case ReadFileResult::INVALID_PRESET:
+ return "Invalid preset";
+ case ReadFileResult::INVALID_VARIABLE:
+ return "Invalid CMake variable definition";
+ case ReadFileResult::DUPLICATE_PRESETS:
+ return "Duplicate presets";
+ case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
+ return "Cyclic preset inheritance";
+ case ReadFileResult::USER_PRESET_INHERITANCE:
+ return "Project preset inherits from user preset";
+ default:
+ return "Unknown error";
+ }
+}
+
+cm::optional<cmCMakePresetsFile::ExpandedPreset>
+cmCMakePresetsFile::ExpandMacros(const UnexpandedPreset& preset) const
+{
+ ExpandedPreset retval{ preset };
+
+ std::map<std::string, CycleStatus> envCycles;
+ for (auto const& v : retval.Environment) {
+ envCycles[v.first] = CycleStatus::Unvisited;
+ }
+
+ for (auto& v : retval.Environment) {
+ if (v.second &&
+ !VisitEnv(*this, retval, envCycles, *v.second, envCycles[v.first])) {
+ return cm::nullopt;
+ }
+ }
+
+ std::string binaryDir = preset.BinaryDir;
+ if (!::ExpandMacros(*this, retval, envCycles, binaryDir)) {
+ return cm::nullopt;
+ }
+ if (!cmSystemTools::FileIsFullPath(binaryDir)) {
+ binaryDir = cmStrCat(this->SourceDir, '/', binaryDir);
+ }
+ retval.BinaryDir = cmSystemTools::CollapseFullPath(binaryDir);
+ cmSystemTools::ConvertToUnixSlashes(retval.BinaryDir);
+
+ for (auto& variable : retval.CacheVariables) {
+ if (variable.second &&
+ !::ExpandMacros(*this, retval, envCycles, variable.second->Value)) {
+ return cm::nullopt;
+ }
+ }
+
+ return cm::make_optional(retval);
+}
+
+cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
+ const std::string& filename, std::vector<std::string>& presetOrder,
+ std::map<std::string, UnexpandedPreset>& presetMap, bool user)
+{
+ cmsys::ifstream fin(filename.c_str());
+ if (!fin) {
+ return ReadFileResult::FILE_NOT_FOUND;
+ }
+ // If there's a BOM, toss it.
+ cmsys::FStream::ReadBOM(fin);
+
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
+ return ReadFileResult::JSON_PARSE_ERROR;
+ }
+
+ int v = 0;
+ auto result = RootVersionHelper(v, &root);
+ if (result != ReadFileResult::READ_OK) {
+ return result;
+ }
+ if (v < MIN_VERSION || v > MAX_VERSION) {
+ return ReadFileResult::UNRECOGNIZED_VERSION;
+ }
+
+ RootPresets presets;
+ if ((result = RootPresetsHelper(presets, &root)) !=
+ ReadFileResult::READ_OK) {
+ return result;
+ }
+
+ unsigned int currentMajor = cmVersion::GetMajorVersion();
+ unsigned int currentMinor = cmVersion::GetMinorVersion();
+ unsigned int currentPatch = cmVersion::GetPatchVersion();
+ auto const& required = presets.CMakeMinimumRequired;
+ if (required.Major > currentMajor ||
+ (required.Major == currentMajor &&
+ (required.Minor > currentMinor ||
+ (required.Minor == currentMinor &&
+ (required.Patch > currentPatch))))) {
+ return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
+ }
+
+ for (auto& preset : presets.Presets) {
+ preset.User = user;
+ if (preset.Name.empty()) {
+ return ReadFileResult::INVALID_PRESET;
+ }
+ if (!presetMap.insert({ preset.Name, preset }).second) {
+ return ReadFileResult::DUPLICATE_PRESETS;
+ }
+ presetOrder.push_back(preset.Name);
+ }
+
+ return ReadFileResult::READ_OK;
+}
diff --git a/Source/cmCMakePresetsFile.h b/Source/cmCMakePresetsFile.h
new file mode 100644
index 0000000..70ec4c5
--- /dev/null
+++ b/Source/cmCMakePresetsFile.h
@@ -0,0 +1,141 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/optional>
+
+class cmCMakePresetsFile
+{
+public:
+ enum class CMakeGeneratorConfig
+ {
+ Default,
+ Ignore,
+ };
+
+ class CacheVariable
+ {
+ public:
+ std::string Type;
+ std::string Value;
+ };
+
+ class Preset
+ {
+ public:
+#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L)
+ Preset() = default;
+ Preset(const Preset& /*other*/) = default;
+ Preset(Preset&& /*other*/) = default;
+
+ Preset& operator=(const Preset& /*other*/) = default;
+
+ // The move assignment operators for several STL classes did not become
+ // noexcept until C++17, which causes some tools to warn about this move
+ // assignment operator throwing an exception when it shouldn't. Disable the
+ // move assignment operator until C++17 is enabled.
+ Preset& operator=(Preset&& /*other*/) = delete;
+#endif
+
+ std::string Name;
+ std::vector<std::string> Inherits;
+ bool Hidden;
+ bool User;
+ std::string DisplayName;
+ std::string Description;
+ std::string Generator;
+ std::string Architecture;
+ std::string Toolset;
+ cm::optional<CMakeGeneratorConfig> GeneratorConfig;
+ std::string BinaryDir;
+
+ std::map<std::string, cm::optional<CacheVariable>> CacheVariables;
+ std::map<std::string, cm::optional<std::string>> Environment;
+
+ cm::optional<bool> WarnDev;
+ cm::optional<bool> ErrorDev;
+ cm::optional<bool> WarnDeprecated;
+ cm::optional<bool> ErrorDeprecated;
+ cm::optional<bool> WarnUninitialized;
+ cm::optional<bool> WarnUnusedCli;
+ cm::optional<bool> WarnSystemVars;
+
+ cm::optional<bool> DebugOutput;
+ cm::optional<bool> DebugTryCompile;
+ cm::optional<bool> DebugFind;
+ };
+
+ class UnexpandedPreset : public Preset
+ {
+ public:
+ using Preset::Preset;
+
+ UnexpandedPreset() = default;
+ UnexpandedPreset(const Preset& preset)
+ : Preset(preset)
+ {
+ }
+ UnexpandedPreset(Preset&& preset)
+ : Preset(std::move(preset))
+ {
+ }
+ };
+
+ class ExpandedPreset : public Preset
+ {
+ public:
+ using Preset::Preset;
+
+ ExpandedPreset() = default;
+ ExpandedPreset(const Preset& preset)
+ : Preset(preset)
+ {
+ }
+ ExpandedPreset(Preset&& preset)
+ : Preset(std::move(preset))
+ {
+ }
+ };
+
+ std::string SourceDir;
+ std::map<std::string, UnexpandedPreset> Presets;
+ std::vector<std::string> PresetOrder;
+
+ enum class ReadFileResult
+ {
+ READ_OK,
+ FILE_NOT_FOUND,
+ JSON_PARSE_ERROR,
+ INVALID_ROOT,
+ NO_VERSION,
+ INVALID_VERSION,
+ UNRECOGNIZED_VERSION,
+ INVALID_CMAKE_VERSION,
+ UNRECOGNIZED_CMAKE_VERSION,
+ INVALID_PRESETS,
+ INVALID_PRESET,
+ INVALID_VARIABLE,
+ DUPLICATE_PRESETS,
+ CYCLIC_PRESET_INHERITANCE,
+ USER_PRESET_INHERITANCE,
+ };
+
+ static std::string GetFilename(const std::string& sourceDir);
+ static std::string GetUserFilename(const std::string& sourceDir);
+ ReadFileResult ReadProjectPresets(const std::string& sourceDir,
+ bool allowNoFiles = false);
+ static const char* ResultToString(ReadFileResult result);
+
+ cm::optional<ExpandedPreset> ExpandMacros(
+ const UnexpandedPreset& preset) const;
+
+private:
+ ReadFileResult ReadJSONFile(
+ const std::string& filename, std::vector<std::string>& presetOrder,
+ std::map<std::string, UnexpandedPreset>& presetMap, bool user);
+};
diff --git a/Source/cmGlobalGeneratorFactory.h b/Source/cmGlobalGeneratorFactory.h
index 3a7f806..d6ababb 100644
--- a/Source/cmGlobalGeneratorFactory.h
+++ b/Source/cmGlobalGeneratorFactory.h
@@ -25,7 +25,7 @@ public:
/** Create a GlobalGenerator */
virtual std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& n, cmake* cm) const = 0;
+ const std::string& n, bool allowArch, cmake* cm) const = 0;
/** Get the documentation entry for this factory */
virtual void GetDocumentation(cmDocumentationEntry& entry) const = 0;
@@ -53,7 +53,7 @@ class cmGlobalGeneratorSimpleFactory : public cmGlobalGeneratorFactory
public:
/** Create a GlobalGenerator */
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool /*allowArch*/, cmake* cm) const override
{
if (name != T::GetActualName()) {
return std::unique_ptr<cmGlobalGenerator>();
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 3805546..7794df3 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -59,7 +59,7 @@ class cmGlobalVisualStudio10Generator::Factory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
std::string genName;
const char* p = cmVS10GenName(name, genName);
@@ -70,7 +70,7 @@ public:
return std::unique_ptr<cmGlobalGenerator>(
new cmGlobalVisualStudio10Generator(cm, genName, ""));
}
- if (*p++ != ' ') {
+ if (!allowArch || *p++ != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
if (strcmp(p, "Win64") == 0) {
diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx
index a385375..a5ffcf0 100644
--- a/Source/cmGlobalVisualStudio11Generator.cxx
+++ b/Source/cmGlobalVisualStudio11Generator.cxx
@@ -31,7 +31,7 @@ class cmGlobalVisualStudio11Generator::Factory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
std::string genName;
const char* p = cmVS11GenName(name, genName);
@@ -42,7 +42,7 @@ public:
return std::unique_ptr<cmGlobalGenerator>(
new cmGlobalVisualStudio11Generator(cm, genName, ""));
}
- if (*p++ != ' ') {
+ if (!allowArch || *p++ != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
if (strcmp(p, "Win64") == 0) {
diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx
index 5a27994..8bdf356 100644
--- a/Source/cmGlobalVisualStudio12Generator.cxx
+++ b/Source/cmGlobalVisualStudio12Generator.cxx
@@ -29,7 +29,7 @@ class cmGlobalVisualStudio12Generator::Factory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
std::string genName;
const char* p = cmVS12GenName(name, genName);
@@ -40,7 +40,7 @@ public:
return std::unique_ptr<cmGlobalGenerator>(
new cmGlobalVisualStudio12Generator(cm, genName, ""));
}
- if (*p++ != ' ') {
+ if (!allowArch || *p++ != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
if (strcmp(p, "Win64") == 0) {
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index a12b7e1..e17c6d7 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -30,7 +30,7 @@ class cmGlobalVisualStudio14Generator::Factory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
std::string genName;
const char* p = cmVS14GenName(name, genName);
@@ -41,7 +41,7 @@ public:
return std::unique_ptr<cmGlobalGenerator>(
new cmGlobalVisualStudio14Generator(cm, genName, ""));
}
- if (*p++ != ' ') {
+ if (!allowArch || *p++ != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
if (strcmp(p, "Win64") == 0) {
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
index 9f73c15..2339a80 100644
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ b/Source/cmGlobalVisualStudio9Generator.cxx
@@ -16,7 +16,7 @@ class cmGlobalVisualStudio9Generator::Factory : public cmGlobalGeneratorFactory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
if (strncmp(name.c_str(), vs9generatorName,
sizeof(vs9generatorName) - 1) != 0) {
@@ -29,7 +29,7 @@ public:
new cmGlobalVisualStudio9Generator(cm, name, ""));
}
- if (p[0] != ' ') {
+ if (!allowArch || p[0] != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 4b877ce..95357e7 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -140,7 +140,7 @@ class cmGlobalVisualStudioVersionedGenerator::Factory15
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool allowArch, cmake* cm) const override
{
std::string genName;
const char* p = cmVS15GenName(name, genName);
@@ -152,7 +152,7 @@ public:
new cmGlobalVisualStudioVersionedGenerator(
cmGlobalVisualStudioGenerator::VS15, cm, genName, ""));
}
- if (*p++ != ' ') {
+ if (!allowArch || *p++ != ' ') {
return std::unique_ptr<cmGlobalGenerator>();
}
if (strcmp(p, "Win64") == 0) {
@@ -234,7 +234,7 @@ class cmGlobalVisualStudioVersionedGenerator::Factory16
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override
+ const std::string& name, bool /*allowArch*/, cmake* cm) const override
{
std::string genName;
const char* p = cmVS16GenName(name, genName);
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index f1a1ce6..952a179 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -135,7 +135,7 @@ class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
{
public:
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name, cmake* cm) const override;
+ const std::string& name, bool allowArch, cmake* cm) const override;
void GetDocumentation(cmDocumentationEntry& entry) const override
{
@@ -197,6 +197,7 @@ std::unique_ptr<cmGlobalGeneratorFactory> cmGlobalXCodeGenerator::NewFactory()
std::unique_ptr<cmGlobalGenerator>
cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(const std::string& name,
+ bool /*allowArch*/,
cmake* cm) const
{
if (name != GetActualName()) {
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
index 2da2a03..a63347d 100644
--- a/Source/cmJSONHelpers.h
+++ b/Source/cmJSONHelpers.h
@@ -29,6 +29,9 @@ public:
template <typename M, typename F>
cmJSONObjectHelper& Bind(const cm::string_view& name, std::nullptr_t, F func,
bool required = true);
+ template <typename F>
+ cmJSONObjectHelper& Bind(const cm::string_view& name, F func,
+ bool required = true);
E operator()(T& out, const Json::Value* value) const;
@@ -87,6 +90,14 @@ cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
}
template <typename T, typename E>
+template <typename F>
+cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
+ const cm::string_view& name, F func, bool required)
+{
+ return this->BindPrivate(name, MemberFunction(func), required);
+}
+
+template <typename T, typename E>
cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::BindPrivate(
const cm::string_view& name, MemberFunction&& func, bool required)
{
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index d9c6b7a..2a14b03 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -13,6 +13,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cm/string_view>
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
# include <cm/iterator>
@@ -27,6 +28,7 @@
#include "cm_sys_stat.h"
+#include "cmCMakePresetsFile.h"
#include "cmCommands.h"
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
@@ -286,6 +288,97 @@ void cmake::CleanupCommandsAndMacros()
this->CurrentSnapshot.SetDefaultDefinitions();
}
+#ifndef CMAKE_BOOTSTRAP
+void cmake::SetWarningFromPreset(const std::string& name,
+ const cm::optional<bool>& warning,
+ const cm::optional<bool>& error)
+{
+ if (warning) {
+ if (*warning) {
+ this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN);
+ } else {
+ this->DiagLevels[name] = DIAG_IGNORE;
+ }
+ }
+ if (error) {
+ if (*error) {
+ this->DiagLevels[name] = DIAG_ERROR;
+ } else {
+ this->DiagLevels[name] = std::min(this->DiagLevels[name], DIAG_WARN);
+ }
+ }
+}
+
+void cmake::ProcessPresetVariables()
+{
+ for (auto const& var : this->UnprocessedPresetVariables) {
+ if (!var.second) {
+ continue;
+ }
+ cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
+ if (!var.second->Type.empty()) {
+ type = cmState::StringToCacheEntryType(var.second->Type);
+ }
+ this->ProcessCacheArg(var.first, var.second->Value, type);
+ }
+}
+
+void cmake::PrintPresetVariables()
+{
+ bool first = true;
+ for (auto const& var : this->UnprocessedPresetVariables) {
+ if (!var.second) {
+ continue;
+ }
+ cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
+ if (!var.second->Type.empty()) {
+ type = cmState::StringToCacheEntryType(var.second->Type);
+ }
+ if (first) {
+ std::cout << "Preset CMake variables:\n\n";
+ first = false;
+ }
+ std::cout << " " << var.first;
+ if (type != cmStateEnums::UNINITIALIZED) {
+ std::cout << ':' << cmState::CacheEntryTypeToString(type);
+ }
+ std::cout << "=\"" << var.second->Value << "\"\n";
+ }
+ if (!first) {
+ std::cout << '\n';
+ }
+ this->UnprocessedPresetVariables.clear();
+}
+
+void cmake::ProcessPresetEnvironment()
+{
+ for (auto const& var : this->UnprocessedPresetEnvironment) {
+ if (var.second) {
+ cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
+ }
+ }
+}
+
+void cmake::PrintPresetEnvironment()
+{
+ bool first = true;
+ for (auto const& var : this->UnprocessedPresetEnvironment) {
+ if (!var.second) {
+ continue;
+ }
+ if (first) {
+ std::cout << "Preset environment variables:\n\n";
+ first = false;
+ }
+ std::cout << " " << var.first << "=\"" << *var.second << "\"\n";
+ }
+ if (!first) {
+ std::cout << '\n';
+ }
+ this->UnprocessedPresetEnvironment.clear();
+}
+#endif
+
// Parse the args
bool cmake::SetCacheArgs(const std::vector<std::string>& args)
{
@@ -308,28 +401,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
std::string value;
cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
if (cmState::ParseCacheEntry(entry, var, value, type)) {
- // The value is transformed if it is a filepath for example, so
- // we can't compare whether the value is already in the cache until
- // after we call AddCacheEntry.
- bool haveValue = false;
- std::string cachedValue;
- if (this->WarnUnusedCli) {
- if (cmProp v = this->State->GetInitializedCacheValue(var)) {
- haveValue = true;
- cachedValue = *v;
- }
- }
-
- this->AddCacheEntry(var, value.c_str(),
- "No help, variable specified on the command line.",
- type);
-
- if (this->WarnUnusedCli) {
- if (!haveValue ||
- cachedValue != *this->State->GetInitializedCacheValue(var)) {
- this->WatchUnusedCli(var);
- }
- }
+#ifndef CMAKE_BOOTSTRAP
+ this->UnprocessedPresetVariables.erase(var);
+#endif
+ this->ProcessCacheArg(var, value, type);
} else {
cmSystemTools::Error("Parse error in command line argument: " + arg +
"\n" + "Should be: VAR:type=value\n");
@@ -409,6 +484,9 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
// now remove them from the cache
for (std::string const& currentEntry : entriesToDelete) {
+#ifndef CMAKE_BOOTSTRAP
+ this->UnprocessedPresetVariables.erase(currentEntry);
+#endif
this->State->RemoveCacheEntry(currentEntry);
}
} else if (cmHasLiteralPrefix(arg, "-C")) {
@@ -462,6 +540,33 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
return true;
}
+void cmake::ProcessCacheArg(const std::string& var, const std::string& value,
+ cmStateEnums::CacheEntryType type)
+{
+ // The value is transformed if it is a filepath for example, so
+ // we can't compare whether the value is already in the cache until
+ // after we call AddCacheEntry.
+ bool haveValue = false;
+ std::string cachedValue;
+ if (this->WarnUnusedCli) {
+ if (cmProp v = this->State->GetInitializedCacheValue(var)) {
+ haveValue = true;
+ cachedValue = *v;
+ }
+ }
+
+ this->AddCacheEntry(var, value.c_str(),
+ "No help, variable specified on the command line.",
+ type);
+
+ if (this->WarnUnusedCli) {
+ if (!haveValue ||
+ cachedValue != *this->State->GetInitializedCacheValue(var)) {
+ this->WatchUnusedCli(var);
+ }
+ }
+}
+
void cmake::ReadListFile(const std::vector<std::string>& args,
const std::string& path)
{
@@ -625,6 +730,8 @@ void cmake::SetArgs(const std::vector<std::string>& args)
#if !defined(CMAKE_BOOTSTRAP)
std::string profilingFormat;
std::string profilingOutput;
+ std::string presetName;
+ bool listPresets = false;
#endif
for (unsigned int i = 1; i < args.size(); ++i) {
std::string const& arg = args[i];
@@ -830,19 +937,9 @@ void cmake::SetArgs(const std::vector<std::string>& args)
}
value = args[i];
}
- auto gen = this->CreateGlobalGenerator(value);
- if (!gen) {
- std::string kdevError;
- if (value.find("KDevelop3", 0) != std::string::npos) {
- kdevError = "\nThe KDevelop3 generator is not supported anymore.";
- }
-
- cmSystemTools::Error(
- cmStrCat("Could not create named generator ", value, kdevError));
- this->PrintGeneratorList();
+ if (!this->CreateAndSetGlobalGenerator(value, true)) {
return;
}
- this->SetGlobalGenerator(std::move(gen));
#if !defined(CMAKE_BOOTSTRAP)
} else if (cmHasLiteralPrefix(arg, "--profiling-format")) {
profilingFormat = arg.substr(strlen("--profiling-format="));
@@ -856,6 +953,13 @@ void cmake::SetArgs(const std::vector<std::string>& args)
if (profilingOutput.empty()) {
cmSystemTools::Error("No path specified for --profiling-output");
}
+ } else if (cmHasLiteralPrefix(arg, "--preset")) {
+ presetName = arg.substr(strlen("--preset="));
+ if (presetName.empty()) {
+ cmSystemTools::Error("No preset specified for --preset");
+ }
+ } else if (cmHasLiteralPrefix(arg, "--list-presets")) {
+ listPresets = true;
#endif
}
// no option assume it is the path to the source or an existing build
@@ -915,6 +1019,91 @@ void cmake::SetArgs(const std::vector<std::string>& args)
if (!haveBinaryDir) {
this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
}
+
+#if !defined(CMAKE_BOOTSTRAP)
+ if (listPresets || !presetName.empty()) {
+ cmCMakePresetsFile settingsFile;
+ auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
+ if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) {
+ cmSystemTools::Error(
+ cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
+ ": ", cmCMakePresetsFile::ResultToString(result)));
+ return;
+ }
+ if (listPresets) {
+ this->PrintPresetList(settingsFile);
+ return;
+ }
+ auto preset = settingsFile.Presets.find(presetName);
+ if (preset == settingsFile.Presets.end()) {
+ cmSystemTools::Error(cmStrCat("No such preset in ",
+ this->GetHomeDirectory(), ": \"",
+ presetName, '"'));
+ this->PrintPresetList(settingsFile);
+ return;
+ }
+ if (preset->second.Hidden) {
+ cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ",
+ this->GetHomeDirectory(), ": \"",
+ presetName, '"'));
+ this->PrintPresetList(settingsFile);
+ return;
+ }
+ auto expandedPreset = settingsFile.ExpandMacros(preset->second);
+ if (!expandedPreset) {
+ cmSystemTools::Error(cmStrCat("Could not evaluate preset \"",
+ preset->second.Name,
+ "\": Invalid macro expansion"));
+ return;
+ }
+
+ if (!haveBinaryDir) {
+ this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
+ }
+ if (!this->GlobalGenerator) {
+ if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator,
+ false)) {
+ return;
+ }
+ }
+ this->UnprocessedPresetVariables = expandedPreset->CacheVariables;
+ this->UnprocessedPresetEnvironment = expandedPreset->Environment;
+
+ if (!expandedPreset->GeneratorConfig ||
+ expandedPreset->GeneratorConfig ==
+ cmCMakePresetsFile::CMakeGeneratorConfig::Default) {
+ if (!this->GeneratorPlatformSet) {
+ this->SetGeneratorPlatform(expandedPreset->Architecture);
+ }
+ if (!this->GeneratorToolsetSet) {
+ this->SetGeneratorToolset(expandedPreset->Toolset);
+ }
+ }
+
+ this->SetWarningFromPreset("dev", expandedPreset->WarnDev,
+ expandedPreset->ErrorDev);
+ this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated,
+ expandedPreset->ErrorDeprecated);
+ if (expandedPreset->WarnUninitialized == true) {
+ this->SetWarnUninitialized(true);
+ }
+ if (expandedPreset->WarnUnusedCli == false) {
+ this->SetWarnUnusedCli(false);
+ }
+ if (expandedPreset->WarnSystemVars == true) {
+ this->SetCheckSystemVars(true);
+ }
+ if (expandedPreset->DebugOutput == true) {
+ this->SetDebugOutputOn(true);
+ }
+ if (expandedPreset->DebugTryCompile == true) {
+ this->DebugTryCompileOn();
+ }
+ if (expandedPreset->DebugFind == true) {
+ this->SetDebugFindOutputOn(true);
+ }
+ }
+#endif
}
cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr)
@@ -1217,7 +1406,7 @@ createExtraGenerator(
}
std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
- const std::string& gname)
+ const std::string& gname, bool allowArch)
{
std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>, std::string>
extra = createExtraGenerator(this->ExtraGenerators, gname);
@@ -1227,7 +1416,7 @@ std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
std::unique_ptr<cmGlobalGenerator> generator;
for (const auto& g : this->Generators) {
- generator = g->CreateGlobalGenerator(name, this);
+ generator = g->CreateGlobalGenerator(name, allowArch, this);
if (generator) {
break;
}
@@ -1240,6 +1429,79 @@ std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
return generator;
}
+bool cmake::CreateAndSetGlobalGenerator(const std::string& name,
+ bool allowArch)
+{
+ auto gen = this->CreateGlobalGenerator(name, allowArch);
+ if (!gen) {
+ std::string kdevError;
+ std::string vsError;
+ if (name.find("KDevelop3", 0) != std::string::npos) {
+ kdevError = "\nThe KDevelop3 generator is not supported anymore.";
+ }
+ if (!allowArch && cmHasLiteralPrefix(name, "Visual Studio ") &&
+ name.length() >= cmStrLen("Visual Studio xx xxxx ")) {
+ vsError = "\nUsing platforms in Visual Studio generator names is not "
+ "supported in CMakePresets.json.";
+ }
+
+ cmSystemTools::Error(
+ cmStrCat("Could not create named generator ", name, kdevError, vsError));
+ this->PrintGeneratorList();
+ return false;
+ }
+
+ this->SetGlobalGenerator(std::move(gen));
+ return true;
+}
+
+#ifndef CMAKE_BOOTSTRAP
+void cmake::PrintPresetList(const cmCMakePresetsFile& file) const
+{
+ std::vector<GeneratorInfo> generators;
+ this->GetRegisteredGenerators(generators, false);
+
+ std::vector<cmCMakePresetsFile::UnexpandedPreset> presets;
+ for (auto const& p : file.PresetOrder) {
+ auto const& preset = file.Presets.at(p);
+ if (!preset.Hidden &&
+ std::find_if(generators.begin(), generators.end(),
+ [&preset](const GeneratorInfo& info) {
+ return info.name == preset.Generator;
+ }) != generators.end() &&
+ file.ExpandMacros(preset)) {
+ presets.push_back(preset);
+ }
+ }
+
+ if (presets.empty()) {
+ return;
+ }
+
+ std::cout << "Available presets:\n\n";
+
+ auto longestPresetName =
+ std::max_element(presets.begin(), presets.end(),
+ [](const cmCMakePresetsFile::UnexpandedPreset& a,
+ const cmCMakePresetsFile::UnexpandedPreset& b) {
+ return a.Name.length() < b.Name.length();
+ });
+ auto longestLength = longestPresetName->Name.length();
+
+ for (auto const& preset : presets) {
+ std::cout << " \"" << preset.Name << '"';
+ auto const& description = preset.DisplayName;
+ if (!description.empty()) {
+ for (std::size_t i = 0; i < longestLength - preset.Name.length(); ++i) {
+ std::cout << ' ';
+ }
+ std::cout << " - " << description;
+ }
+ std::cout << '\n';
+ }
+}
+#endif
+
void cmake::SetHomeDirectory(const std::string& dir)
{
this->State->SetSourceDirectory(dir);
@@ -1803,6 +2065,9 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
if (cmSystemTools::GetErrorOccuredFlag()) {
return -1;
}
+ if (this->GetWorkingMode() == HELP_MODE) {
+ return 0;
+ }
// Log the trace format version to the desired output
if (this->GetTrace()) {
@@ -1831,11 +2096,19 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
this->AddCMakePaths();
}
+#ifndef CMAKE_BOOTSTRAP
+ this->ProcessPresetVariables();
+ this->ProcessPresetEnvironment();
+#endif
// Add any cache args
if (!this->SetCacheArgs(args)) {
cmSystemTools::Error("Problem processing arguments. Aborting.\n");
return -1;
}
+#ifndef CMAKE_BOOTSTRAP
+ this->PrintPresetVariables();
+ this->PrintPresetEnvironment();
+#endif
// In script mode we terminate after running the script.
if (this->GetWorkingMode() != NORMAL_MODE) {
diff --git a/Source/cmake.h b/Source/cmake.h
index 63635cb..262d673 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -27,7 +27,11 @@
#include "cmStateTypes.h"
#if !defined(CMAKE_BOOTSTRAP)
+# include <cm/optional>
+
# include <cm3p/json/value.h>
+
+# include "cmCMakePresetsFile.h"
#endif
class cmExternalMakefileProjectGeneratorFactory;
@@ -88,13 +92,22 @@ public:
enum WorkingMode
{
NORMAL_MODE, ///< Cmake runs to create project files
- /** \brief Script mode (started by using -P).
- *
- * In script mode there is no generator and no cache. Also,
- * languages are not enabled, so add_executable and things do
- * nothing.
- */
+
+ /** \brief Script mode (started by using -P).
+ *
+ * In script mode there is no generator and no cache. Also,
+ * languages are not enabled, so add_executable and things do
+ * nothing.
+ */
SCRIPT_MODE,
+
+ /** \brief Help mode
+ *
+ * Used to print help for things that can only be determined after finding
+ * the source directory, for example, the list of presets.
+ */
+ HELP_MODE,
+
/** \brief A pkg-config like mode
*
* In this mode cmake just searches for a package and prints the results to
@@ -219,7 +232,15 @@ public:
//! Create a GlobalGenerator
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
- const std::string& name);
+ const std::string& name, bool allowArch = true);
+
+ //! Create a GlobalGenerator and set it as our own
+ bool CreateAndSetGlobalGenerator(const std::string& name, bool allowArch);
+
+#ifndef CMAKE_BOOTSTRAP
+ //! Print list of presets
+ void PrintPresetList(const cmCMakePresetsFile& file) const;
+#endif
//! Return the global generator assigned to this instance of cmake
cmGlobalGenerator* GetGlobalGenerator()
@@ -329,9 +350,22 @@ public:
bool GetIsInTryCompile() const;
void SetIsInTryCompile(bool b);
+#ifndef CMAKE_BOOTSTRAP
+ void SetWarningFromPreset(const std::string& name,
+ const cm::optional<bool>& warning,
+ const cm::optional<bool>& error);
+ void ProcessPresetVariables();
+ void PrintPresetVariables();
+ void ProcessPresetEnvironment();
+ void PrintPresetEnvironment();
+#endif
+
//! Parse command line arguments that might set cache values
bool SetCacheArgs(const std::vector<std::string>&);
+ void ProcessCacheArg(const std::string& var, const std::string& value,
+ cmStateEnums::CacheEntryType type);
+
using ProgressCallbackType = std::function<void(const std::string&, float)>;
/**
* Set the function used by GUIs to receive progress updates
@@ -625,6 +659,12 @@ private:
std::unique_ptr<cmFileTimeCache> FileTimeCache;
std::string GraphVizFile;
InstalledFilesMap InstalledFiles;
+#ifndef CMAKE_BOOTSTRAP
+ std::map<std::string, cm::optional<cmCMakePresetsFile::CacheVariable>>
+ UnprocessedPresetVariables;
+ std::map<std::string, cm::optional<std::string>>
+ UnprocessedPresetEnvironment;
+#endif
#if !defined(CMAKE_BOOTSTRAP)
std::unique_ptr<cmVariableWatch> VariableWatch;
@@ -664,6 +704,8 @@ private:
#define CMAKE_STANDARD_OPTIONS_TABLE \
{ "-S <path-to-source>", "Explicitly specify a source directory." }, \
{ "-B <path-to-build>", "Explicitly specify a build directory." }, \
+ { "--preset=<preset-name>", "Explicitly specify a preset." }, \
+ { "--list-presets", "List available presets." }, \
{ "-C <initial-cache>", "Pre-load a script to populate the cache." }, \
{ "-D <var>[:<type>]=<value>", "Create or update a cmake cache entry." }, \
{ "-U <globbing_expr>", "Remove matching entries from CMake cache." }, \
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 4600fc5..c769227 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -49,7 +49,8 @@ const char* cmDocumentationUsage[][2] = {
{ nullptr,
" cmake [options] <path-to-source>\n"
" cmake [options] <path-to-existing-build>\n"
- " cmake [options] -S <path-to-source> -B <path-to-build>" },
+ " cmake [options] -S <path-to-source> -B <path-to-build>\n"
+ " cmake [options] -S <path-to-source> --preset=<preset-name>" },
{ nullptr,
"Specify a source directory to (re-)generate a build system for "
"it in the current working directory. Specify an existing build "
@@ -253,6 +254,9 @@ int do_cmake(int ac, char const* const* av)
} else if (cmHasLiteralPrefix(av[i], "--find-package")) {
workingMode = cmake::FIND_PACKAGE_MODE;
args.emplace_back(av[i]);
+ } else if (strcmp(av[i], "--list-presets") == 0) {
+ workingMode = cmake::HELP_MODE;
+ args.emplace_back(av[i]);
} else {
args.emplace_back(av[i]);
}
@@ -269,6 +273,7 @@ int do_cmake(int ac, char const* const* av)
cmState::Mode mode = cmState::Unknown;
switch (workingMode) {
case cmake::NORMAL_MODE:
+ case cmake::HELP_MODE:
mode = cmState::Project;
break;
case cmake::SCRIPT_MODE: