diff options
-rw-r--r-- | Help/release/dev/cmake-gui-environment.rst | 4 | ||||
-rw-r--r-- | Source/QtDialog/CMakeLists.txt | 4 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.cxx | 17 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.h | 1 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetupDialog.ui | 76 | ||||
-rw-r--r-- | Source/QtDialog/EnvironmentDialog.cxx | 194 | ||||
-rw-r--r-- | Source/QtDialog/EnvironmentDialog.h | 59 | ||||
-rw-r--r-- | Source/QtDialog/EnvironmentDialog.ui | 130 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.cxx | 87 | ||||
-rw-r--r-- | Source/QtDialog/QCMake.h | 8 | ||||
-rw-r--r-- | Tests/CMakeGUI/CMakeGUITest.cmake | 6 | ||||
-rw-r--r-- | Tests/CMakeGUI/CMakeGUITest.cxx | 29 | ||||
-rw-r--r-- | Tests/CMakeGUI/CMakeGUITest.h | 1 | ||||
-rw-r--r-- | Tests/CMakeGUI/CMakeLists.txt | 7 | ||||
-rw-r--r-- | Tests/CMakeGUI/EnvironmentDialogTest.cxx | 142 | ||||
-rw-r--r-- | Tests/CMakeGUI/EnvironmentDialogTest.h | 15 | ||||
-rw-r--r-- | Tests/CMakeGUI/environment/CMakeLists.txt.in | 18 |
17 files changed, 761 insertions, 37 deletions
diff --git a/Help/release/dev/cmake-gui-environment.rst b/Help/release/dev/cmake-gui-environment.rst new file mode 100644 index 0000000..1c8485e --- /dev/null +++ b/Help/release/dev/cmake-gui-environment.rst @@ -0,0 +1,4 @@ +cmake-gui-environment +--------------------- + +* The :manual:`CMake GUI <cmake-gui(1)>` now has an environment variable editor. diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 0ec012c..5fd0e89 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -84,6 +84,8 @@ set(SRCS CMakeSetupDialog.cxx CMakeSetupDialog.h Compilers.h + EnvironmentDialog.cxx + EnvironmentDialog.h FirstConfigure.cxx FirstConfigure.h QCMake.cxx @@ -102,6 +104,7 @@ qt5_wrap_ui(UI_SRCS Compilers.ui CrossCompiler.ui AddCacheEntry.ui + EnvironmentDialog.ui RegexExplorer.ui WarningMessagesDialog.ui ) @@ -109,6 +112,7 @@ qt5_wrap_cpp(MOC_SRCS AddCacheEntry.h Compilers.h CMakeSetupDialog.h + EnvironmentDialog.h FirstConfigure.h QCMake.h QCMakeCacheView.h diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index f6bde8f..a904b94 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -16,6 +16,7 @@ #include <QMenuBar> #include <QMessageBox> #include <QMimeData> +#include <QProcessEnvironment> #include <QProgressBar> #include <QSettings> #include <QShortcut> @@ -35,6 +36,7 @@ #include "cmVersion.h" #include "AddCacheEntry.h" +#include "EnvironmentDialog.h" #include "FirstConfigure.h" #include "RegexExplorer.h" #include "WarningMessagesDialog.h" @@ -292,6 +294,9 @@ void CMakeSetupDialog::initialize() QObject::connect(this->AddEntry, &QToolButton::clicked, this, &CMakeSetupDialog::addCacheEntry); + QObject::connect(this->Environment, &QToolButton::clicked, this, + &CMakeSetupDialog::editEnvironment); + QObject::connect(this->WarnUninitializedAction, &QAction::triggered, this->CMakeThread->cmakeInstance(), &QCMake::setWarnUninitializedMode); @@ -742,6 +747,7 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->ConfigureAction->setEnabled(enabled); this->AddEntry->setEnabled(enabled); this->RemoveEntry->setEnabled(false); // let selection re-enable it + this->Environment->setEnabled(enabled); } bool CMakeSetupDialog::setupFirstConfigure() @@ -1075,6 +1081,17 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) } } +void CMakeSetupDialog::editEnvironment() +{ + EnvironmentDialog dialog(this->CMakeThread->cmakeInstance()->environment(), + this); + if (dialog.exec() == QDialog::Accepted) { + QMetaObject::invokeMethod( + this->CMakeThread->cmakeInstance(), "setEnvironment", + Q_ARG(QProcessEnvironment, dialog.environment())); + } +} + void CMakeSetupDialog::addCacheEntry() { QDialog dialog(this); diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index eba0b1e..d752ef2 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -65,6 +65,7 @@ protected slots: void setCacheModified(); void removeSelectedCacheEntries(); void selectionChanged(); + void editEnvironment(); void addCacheEntry(); void startSearch(); void setDebugOutput(bool); diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index dc22a29..5feee91 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -11,7 +11,16 @@ </rect> </property> <layout class="QGridLayout"> - <property name="margin"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> <number>9</number> </property> <property name="spacing"> @@ -19,7 +28,16 @@ </property> <item row="0" column="0"> <layout class="QGridLayout"> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <property name="spacing"> @@ -90,7 +108,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -98,7 +125,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -191,6 +227,13 @@ </property> </widget> </item> + <item> + <widget class="QPushButton" name="Environment"> + <property name="text"> + <string>E&nvironment...</string> + </property> + </widget> + </item> </layout> </item> <item> @@ -224,7 +267,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -241,13 +293,13 @@ </property> </widget> </item> - <item> - <widget class="QPushButton" name="OpenProjectButton"> - <property name="text"> - <string>Open &Project</string> - </property> - </widget> - </item> + <item> + <widget class="QPushButton" name="OpenProjectButton"> + <property name="text"> + <string>Open &Project</string> + </property> + </widget> + </item> <item> <widget class="QLabel" name="Generator"> <property name="text"> diff --git a/Source/QtDialog/EnvironmentDialog.cxx b/Source/QtDialog/EnvironmentDialog.cxx new file mode 100644 index 0000000..846456c --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.cxx @@ -0,0 +1,194 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "EnvironmentDialog.h" + +#include <QDialogButtonBox> +#include <QGridLayout> +#include <QItemSelectionModel> +#include <QLabel> +#include <QLineEdit> +#include <QMessageBox> +#include <QStandardItem> + +EnvironmentItemModel::EnvironmentItemModel( + const QProcessEnvironment& environment, QObject* parent) + : QStandardItemModel(parent) +{ + this->clear(); + for (auto const& key : environment.keys()) { + auto value = environment.value(key); + this->appendVariable(key, value); + } +} + +QProcessEnvironment EnvironmentItemModel::environment() const +{ + QProcessEnvironment env; + for (int i = 0; i < this->rowCount(); ++i) { + auto name = this->data(this->index(i, 0), Qt::DisplayRole).toString(); + auto value = this->data(this->index(i, 1), Qt::DisplayRole).toString(); + env.insert(name, value); + } + return env; +} + +void EnvironmentItemModel::clear() +{ + this->QStandardItemModel::clear(); + + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +QModelIndex EnvironmentItemModel::buddy(const QModelIndex& index) const +{ + if (index.column() == 0) { + return this->index(index.row(), index.column() + 1, index.parent()); + } + return index; +} + +void EnvironmentItemModel::appendVariable(const QString& key, + const QString& value) +{ + this->insertVariable(this->rowCount(), key, value); +} + +void EnvironmentItemModel::insertVariable(int row, const QString& key, + const QString& value) +{ + for (int i = 0; i < this->rowCount(); ++i) { + if (this->data(this->index(i, 0), Qt::DisplayRole) == key) { + this->setData(this->index(i, 1), value, Qt::DisplayRole); + return; + } + } + + auto* keyItem = new QStandardItem(key); + auto* valueItem = new QStandardItem(value); + this->insertRow(row, { keyItem, valueItem }); +} + +EnvironmentSearchFilter::EnvironmentSearchFilter(QObject* parent) + : QSortFilterProxyModel(parent) +{ +} + +bool EnvironmentSearchFilter::filterAcceptsRow(int row, + const QModelIndex& parent) const +{ + auto* model = this->sourceModel(); + auto key = + model->data(model->index(row, 0, parent), Qt::DisplayRole).toString(); + return key.contains(this->filterRegExp()); +} + +EnvironmentDialog::EnvironmentDialog(const QProcessEnvironment& environment, + QWidget* parent) + : QDialog(parent) +{ + this->setupUi(this); + + this->RemoveEntry->setEnabled(false); + + this->m_model = new EnvironmentItemModel(environment, this); + this->m_filter = new EnvironmentSearchFilter(this); + this->m_filter->setSourceModel(this->m_model); + this->Environment->setModel(this->m_filter); + + this->Environment->setUniformRowHeights(true); + this->Environment->setRootIsDecorated(false); + this->Environment->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->Environment->setSelectionBehavior(QAbstractItemView::SelectRows); + + QObject::connect(this->AddEntry, &QToolButton::clicked, this, + &EnvironmentDialog::addEntry); + QObject::connect(this->RemoveEntry, &QToolButton::clicked, this, + &EnvironmentDialog::removeSelectedEntries); + QObject::connect(this->Search, &QLineEdit::textChanged, this->m_filter, + &EnvironmentSearchFilter::setFilterFixedString); + QObject::connect(this->Environment->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &EnvironmentDialog::selectionChanged); +} + +QProcessEnvironment EnvironmentDialog::environment() const +{ + return this->m_model->environment(); +} + +void EnvironmentDialog::addEntry() +{ + // Build the dialog manually because it's simple enough + QDialog dialog(this); + dialog.setWindowTitle("Add Environment Variable"); + + auto* layout = new QGridLayout; + dialog.setLayout(layout); + + auto* nameLabel = new QLabel; + nameLabel->setText("Name:"); + layout->addWidget(nameLabel, 0, 0); + + auto* nameEdit = new QLineEdit; + nameEdit->setObjectName("name"); + layout->addWidget(nameEdit, 0, 1); + + auto* valueLabel = new QLabel; + valueLabel->setText("Value:"); + layout->addWidget(valueLabel, 1, 0); + + auto* valueEdit = new QLineEdit; + valueEdit->setObjectName("value"); + layout->addWidget(valueEdit, 1, 1); + + auto* buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + QObject::connect( + buttons, &QDialogButtonBox::accepted, &dialog, + [this, &dialog, nameEdit]() { + auto text = nameEdit->text(); + if (text.isEmpty()) { + QMessageBox::critical(&dialog, "Error", "Name must be non-empty."); + return; + } + + auto* model = this->Environment->model(); + for (int i = 0; i < model->rowCount(); ++i) { + if (model->data(model->index(i, 0), Qt::DisplayRole) == text) { + QMessageBox::critical( + &dialog, "Error", + tr("Environment variable \"%1\" already exists.").arg(text)); + return; + } + } + + dialog.accept(); + }); + QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, + &QDialog::reject); + layout->addWidget(buttons, 2, 0, 1, 2); + + if (dialog.exec() == QDialog::Accepted) { + this->m_model->insertVariable(0, nameEdit->text(), valueEdit->text()); + } +} + +void EnvironmentDialog::removeSelectedEntries() +{ + QModelIndexList idxs = this->Environment->selectionModel()->selectedRows(); + QList<QPersistentModelIndex> pidxs; + foreach (QModelIndex const& i, idxs) { + pidxs.append(i); + } + foreach (QPersistentModelIndex const& pi, pidxs) { + this->Environment->model()->removeRow(pi.row(), pi.parent()); + } +} + +void EnvironmentDialog::selectionChanged() +{ + auto selected = this->Environment->selectionModel()->selectedRows(); + this->RemoveEntry->setEnabled(!selected.isEmpty()); +} diff --git a/Source/QtDialog/EnvironmentDialog.h b/Source/QtDialog/EnvironmentDialog.h new file mode 100644 index 0000000..6aae798 --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.h @@ -0,0 +1,59 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QDialog> +#include <QObject> +#include <QProcessEnvironment> +#include <QSortFilterProxyModel> +#include <QStandardItemModel> + +#include "ui_EnvironmentDialog.h" + +class EnvironmentItemModel : public QStandardItemModel +{ + Q_OBJECT +public: + EnvironmentItemModel(const QProcessEnvironment& environment, + QObject* parent = nullptr); + + QProcessEnvironment environment() const; + void clear(); + + QModelIndex buddy(const QModelIndex& index) const override; + +public slots: + void appendVariable(const QString& key, const QString& value); + void insertVariable(int row, const QString& key, const QString& value); +}; + +class EnvironmentSearchFilter : public QSortFilterProxyModel +{ + Q_OBJECT +public: + EnvironmentSearchFilter(QObject* parent = nullptr); + +protected: + bool filterAcceptsRow(int row, const QModelIndex& parent) const override; +}; + +class EnvironmentDialog + : public QDialog + , public Ui::EnvironmentDialog +{ + Q_OBJECT +public: + EnvironmentDialog(const QProcessEnvironment& environment, + QWidget* parent = nullptr); + + QProcessEnvironment environment() const; + +protected slots: + void addEntry(); + void removeSelectedEntries(); + void selectionChanged(); + +private: + EnvironmentItemModel* m_model; + EnvironmentSearchFilter* m_filter; +}; diff --git a/Source/QtDialog/EnvironmentDialog.ui b/Source/QtDialog/EnvironmentDialog.ui new file mode 100644 index 0000000..dea7624 --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.ui @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>EnvironmentDialog</class> + <widget class="QDialog" name="EnvironmentDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Environment Editor</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>S&earch:</string> + </property> + <property name="buddy"> + <cstring>Search</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="Search"/> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>12</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="AddEntry"> + <property name="text"> + <string>&Add Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Plus16.png</normaloff>:/Icons/Plus16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="RemoveEntry"> + <property name="text"> + <string>&Remove Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Delete16.png</normaloff>:/Icons/Delete16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="Environment"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="CMakeSetup.qrc"/> + </resources> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>EnvironmentDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>EnvironmentDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 6090256..974c545 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -19,10 +19,12 @@ QCMake::QCMake(QObject* p) : QObject(p) + , Environment(QProcessEnvironment::systemEnvironment()) { this->WarnUninitializedMode = false; qRegisterMetaType<QCMakeProperty>(); qRegisterMetaType<QCMakePropertyList>(); + qRegisterMetaType<QProcessEnvironment>(); cmSystemTools::DisableRunCommandOutput(); cmSystemTools::SetRunCommandHideConsole(true); @@ -151,34 +153,46 @@ void QCMake::setToolset(const QString& toolset) } } +void QCMake::setEnvironment(const QProcessEnvironment& environment) +{ + this->Environment = environment; +} + void QCMake::configure() { + int err; + { + cmSystemTools::SaveRestoreEnvironment restoreEnv; + this->setUpEnvironment(); + #ifdef Q_OS_WIN - UINT lastErrorMode = SetErrorMode(0); + UINT lastErrorMode = SetErrorMode(0); #endif - this->CMakeInstance->SetHomeDirectory( - this->SourceDirectory.toLocal8Bit().data()); - this->CMakeInstance->SetHomeOutputDirectory( - this->BinaryDirectory.toLocal8Bit().data()); - this->CMakeInstance->SetGlobalGenerator( - this->CMakeInstance->CreateGlobalGenerator( - this->Generator.toLocal8Bit().data())); - this->CMakeInstance->SetGeneratorPlatform( - this->Platform.toLocal8Bit().data()); - this->CMakeInstance->SetGeneratorToolset(this->Toolset.toLocal8Bit().data()); - this->CMakeInstance->LoadCache(); - this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); - this->CMakeInstance->PreLoadCMakeFiles(); - - InterruptFlag = 0; - cmSystemTools::ResetErrorOccuredFlag(); - - int err = this->CMakeInstance->Configure(); + this->CMakeInstance->SetHomeDirectory( + this->SourceDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetHomeOutputDirectory( + this->BinaryDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetGlobalGenerator( + this->CMakeInstance->CreateGlobalGenerator( + this->Generator.toLocal8Bit().data())); + this->CMakeInstance->SetGeneratorPlatform( + this->Platform.toLocal8Bit().data()); + this->CMakeInstance->SetGeneratorToolset( + this->Toolset.toLocal8Bit().data()); + this->CMakeInstance->LoadCache(); + this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); + this->CMakeInstance->PreLoadCMakeFiles(); + + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); + + err = this->CMakeInstance->Configure(); #ifdef Q_OS_WIN - SetErrorMode(lastErrorMode); + SetErrorMode(lastErrorMode); #endif + } emit this->propertiesChanged(this->properties()); emit this->configureDone(err); @@ -186,18 +200,24 @@ void QCMake::configure() void QCMake::generate() { + int err; + { + cmSystemTools::SaveRestoreEnvironment restoreEnv; + this->setUpEnvironment(); + #ifdef Q_OS_WIN - UINT lastErrorMode = SetErrorMode(0); + UINT lastErrorMode = SetErrorMode(0); #endif - InterruptFlag = 0; - cmSystemTools::ResetErrorOccuredFlag(); + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); - int err = this->CMakeInstance->Generate(); + err = this->CMakeInstance->Generate(); #ifdef Q_OS_WIN - SetErrorMode(lastErrorMode); + SetErrorMode(lastErrorMode); #endif + } emit this->generateDone(err); checkOpenPossible(); @@ -373,6 +393,18 @@ void QCMake::stderrCallback(std::string const& msg) QCoreApplication::processEvents(); } +void QCMake::setUpEnvironment() const +{ + auto env = QProcessEnvironment::systemEnvironment(); + for (auto const& key : env.keys()) { + cmSystemTools::UnsetEnv(key.toLocal8Bit().data()); + } + + for (auto const& var : this->Environment.toStringList()) { + cmSystemTools::PutEnv(var.toLocal8Bit().data()); + } +} + QString QCMake::binaryDirectory() const { return this->BinaryDirectory; @@ -388,6 +420,11 @@ QString QCMake::generator() const return this->Generator; } +QProcessEnvironment QCMake::environment() const +{ + return this->Environment; +} + std::vector<cmake::GeneratorInfo> const& QCMake::availableGenerators() const { return AvailableGenerators; diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index e87660b..f569951 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -18,6 +18,7 @@ #include <QList> #include <QMetaType> #include <QObject> +#include <QProcessEnvironment> #include <QString> #include <QStringList> #include <QVariant> @@ -55,6 +56,7 @@ using QCMakePropertyList = QList<QCMakeProperty>; // allow QVariant to be a property or list of properties Q_DECLARE_METATYPE(QCMakeProperty) Q_DECLARE_METATYPE(QCMakePropertyList) +Q_DECLARE_METATYPE(QProcessEnvironment) /// Qt API for CMake library. /// Wrapper like class allows for easier integration with @@ -78,6 +80,8 @@ public slots: void setPlatform(const QString& platform); /// set the desired generator to use void setToolset(const QString& toolset); + /// set the configure and generate environment + void setEnvironment(const QProcessEnvironment& environment); /// do the configure step void configure(); /// generate the files @@ -125,6 +129,8 @@ public: QString sourceDirectory() const; /// get the current generator QString generator() const; + /// get the configure and generate environment + QProcessEnvironment environment() const; /// get the available generators std::vector<cmake::GeneratorInfo> const& availableGenerators() const; /// get whether to do debug output @@ -170,6 +176,7 @@ protected: void messageCallback(std::string const& msg, const char* title); void stdoutCallback(std::string const& msg); void stderrCallback(std::string const& msg); + void setUpEnvironment() const; bool WarnUninitializedMode; QString SourceDirectory; @@ -180,4 +187,5 @@ protected: std::vector<cmake::GeneratorInfo> AvailableGenerators; QString CMakeExecutable; QAtomicInt InterruptFlag; + QProcessEnvironment Environment; }; diff --git a/Tests/CMakeGUI/CMakeGUITest.cmake b/Tests/CMakeGUI/CMakeGUITest.cmake index 632ab09..b60ec35 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cmake +++ b/Tests/CMakeGUI/CMakeGUITest.cmake @@ -112,3 +112,9 @@ run_cmake_gui_test(sourceBinaryArgs:noExistConfigExists run_cmake_gui_test(simpleConfigure:success) run_cmake_gui_test(simpleConfigure:fail) + +unset(ENV{ADDED_VARIABLE}) +set(ENV{KEPT_VARIABLE} "Kept variable") +set(ENV{CHANGED_VARIABLE} "This variable will be changed") +set(ENV{REMOVED_VARIABLE} "Removed variable") +run_cmake_gui_test(environment) diff --git a/Tests/CMakeGUI/CMakeGUITest.cxx b/Tests/CMakeGUI/CMakeGUITest.cxx index 4087d6b..80ea08d 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cxx +++ b/Tests/CMakeGUI/CMakeGUITest.cxx @@ -143,6 +143,35 @@ void CMakeGUITest::simpleConfigure_data() << -1; } +void CMakeGUITest::environment() +{ + auto* cmake = this->m_window->findChild<QCMakeThread*>()->cmakeInstance(); + + this->m_window->SourceDirectory->setText(CMakeGUITest_BINARY_DIR + "/environment/src"); + this->m_window->BinaryDirectory->setCurrentText(CMakeGUITest_BINARY_DIR + "/environment/build"); + + // We are already testing EnvironmentDialog, so just trust that it's + // connected correctly and modify the environment directly. + auto env = cmake->environment(); + env.insert("ADDED_VARIABLE", "Added variable"); + env.insert("CHANGED_VARIABLE", "Changed variable"); + env.remove("REMOVED_VARIABLE"); + cmake->setEnvironment(env); + + // Wait a bit for everything to update + loopSleep(); + + this->tryConfigure(); + + auto penv = QProcessEnvironment::systemEnvironment(); + QVERIFY(!penv.contains("ADDED_VARIABLE")); + QCOMPARE(penv.value("KEPT_VARIABLE"), "Kept variable"); + QCOMPARE(penv.value("CHANGED_VARIABLE"), "This variable will be changed"); + QCOMPARE(penv.value("REMOVED_VARIABLE"), "Removed variable"); +} + void SetupDefaultQSettings() { QSettings::setDefaultFormat(QSettings::IniFormat); diff --git a/Tests/CMakeGUI/CMakeGUITest.h b/Tests/CMakeGUI/CMakeGUITest.h index f39dcc1..891cf62 100644 --- a/Tests/CMakeGUI/CMakeGUITest.h +++ b/Tests/CMakeGUI/CMakeGUITest.h @@ -22,4 +22,5 @@ private slots: void sourceBinaryArgs_data(); void simpleConfigure(); void simpleConfigure_data(); + void environment(); }; diff --git a/Tests/CMakeGUI/CMakeLists.txt b/Tests/CMakeGUI/CMakeLists.txt index ea97fa0..c6bc88a 100644 --- a/Tests/CMakeGUI/CMakeLists.txt +++ b/Tests/CMakeGUI/CMakeLists.txt @@ -58,6 +58,13 @@ add_cmake_gui_lib_test(CatchShow MOC_SOURCES CatchShowTest.h ) +add_cmake_gui_lib_test(EnvironmentDialog + SOURCES + EnvironmentDialogTest.cxx + EnvironmentDialogTest.h + MOC_SOURCES + EnvironmentDialogTest.h + ) add_cmake_gui_lib_test(QCMakeCacheModel SOURCES QCMakeCacheModelTest.cxx diff --git a/Tests/CMakeGUI/EnvironmentDialogTest.cxx b/Tests/CMakeGUI/EnvironmentDialogTest.cxx new file mode 100644 index 0000000..9ec4996 --- /dev/null +++ b/Tests/CMakeGUI/EnvironmentDialogTest.cxx @@ -0,0 +1,142 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "EnvironmentDialogTest.h" + +#include <QDialogButtonBox> +#include <QMessageBox> +#include <QObject> +#include <QPushButton> +#include <QString> +#include <QtTest> + +#include "CatchShow.h" +#include "EnvironmentDialog.h" + +EnvironmentDialogTest::EnvironmentDialogTest(QObject* parent) + : QObject(parent) +{ +} + +void EnvironmentDialogTest::environmentDialog() +{ + CatchShow catcher; + catcher.setCallback<QMessageBox>([](QMessageBox* box) { box->accept(); }); + + QProcessEnvironment env; + env.insert("DELETED_VARIABLE_1", "Deleted variable 1"); + env.insert("DELETED_VARIABLE_2", "Deleted variable 2"); + env.insert("KEPT_VARIABLE", "Kept variable"); + env.insert("CHANGED_VARIABLE", "This will be changed"); + + EnvironmentDialog dialog(env); + + { + QStringList expected{ + "CHANGED_VARIABLE=This will be changed", + "DELETED_VARIABLE_1=Deleted variable 1", + "DELETED_VARIABLE_2=Deleted variable 2", + "KEPT_VARIABLE=Kept variable", + }; + QCOMPARE(dialog.environment().toStringList(), expected); + QCOMPARE(catcher.count(), 0); + } + + { + CatchShow catcher2; + bool done = false; + catcher2.setCallback<QDialog>([&catcher, &done](QDialog* box) { + if (done) { + return; + } + done = true; + + auto name = box->findChild<QLineEdit*>("name"); + auto value = box->findChild<QLineEdit*>("value"); + auto acceptReject = box->findChild<QDialogButtonBox*>(); + + name->setText(""); + value->setText(""); + acceptReject->button(QDialogButtonBox::Ok)->click(); + QCOMPARE(catcher.count(), 1); + + name->setText("KEPT_VARIABLE"); + value->setText(""); + acceptReject->button(QDialogButtonBox::Ok)->click(); + QCOMPARE(catcher.count(), 2); + + name->setText("ADDED_VARIABLE"); + value->setText("Added variable"); + acceptReject->button(QDialogButtonBox::Ok)->click(); + QCOMPARE(catcher.count(), 2); + }); + dialog.AddEntry->click(); + + QStringList expected{ + "ADDED_VARIABLE=Added variable", + "CHANGED_VARIABLE=This will be changed", + "DELETED_VARIABLE_1=Deleted variable 1", + "DELETED_VARIABLE_2=Deleted variable 2", + "KEPT_VARIABLE=Kept variable", + }; + QCOMPARE(dialog.environment().toStringList(), expected); + QCOMPARE(catcher.count(), 2); + QVERIFY(done); + } + + { + CatchShow catcher2; + bool done = false; + catcher2.setCallback<QDialog>([&done](QDialog* box) { + if (done) { + return; + } + done = true; + + auto name = box->findChild<QLineEdit*>("name"); + auto value = box->findChild<QLineEdit*>("value"); + auto acceptReject = box->findChild<QDialogButtonBox*>(); + + name->setText("DISCARDED_VARIABLE"); + value->setText("Discarded variable"); + acceptReject->button(QDialogButtonBox::Cancel)->click(); + }); + dialog.AddEntry->click(); + + QStringList expected{ + "ADDED_VARIABLE=Added variable", + "CHANGED_VARIABLE=This will be changed", + "DELETED_VARIABLE_1=Deleted variable 1", + "DELETED_VARIABLE_2=Deleted variable 2", + "KEPT_VARIABLE=Kept variable", + }; + QCOMPARE(dialog.environment().toStringList(), expected); + QCOMPARE(catcher.count(), 2); + QVERIFY(done); + } + + { + auto* model = dialog.Environment->model(); + auto* selectionModel = dialog.Environment->selectionModel(); + for (int i = 0; i < model->rowCount(); ++i) { + auto index1 = model->index(i, 0); + auto index2 = model->buddy(index1); + auto name = model->data(index1, Qt::DisplayRole).toString(); + if (name == "DELETED_VARIABLE_1" || name == "DELETED_VARIABLE_2") { + selectionModel->select(index1, QItemSelectionModel::Select); + selectionModel->select(index2, QItemSelectionModel::Select); + } else if (name == "CHANGED_VARIABLE") { + model->setData(index2, "Changed variable", Qt::DisplayRole); + } + } + dialog.RemoveEntry->click(); + + QStringList expected{ + "ADDED_VARIABLE=Added variable", + "CHANGED_VARIABLE=Changed variable", + "KEPT_VARIABLE=Kept variable", + }; + QCOMPARE(dialog.environment().toStringList(), expected); + } +} + +QTEST_MAIN(EnvironmentDialogTest) diff --git a/Tests/CMakeGUI/EnvironmentDialogTest.h b/Tests/CMakeGUI/EnvironmentDialogTest.h new file mode 100644 index 0000000..bcba2c5 --- /dev/null +++ b/Tests/CMakeGUI/EnvironmentDialogTest.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QObject> + +class EnvironmentDialogTest : public QObject +{ + Q_OBJECT +public: + EnvironmentDialogTest(QObject* parent = nullptr); + +private slots: + void environmentDialog(); +}; diff --git a/Tests/CMakeGUI/environment/CMakeLists.txt.in b/Tests/CMakeGUI/environment/CMakeLists.txt.in new file mode 100644 index 0000000..1eeeb85 --- /dev/null +++ b/Tests/CMakeGUI/environment/CMakeLists.txt.in @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.18) +project(environment NONE) + +if(NOT "$ENV{KEPT_VARIABLE}" STREQUAL "Kept variable") + message(SEND_ERROR "KEPT_VARIABLE is \"$ENV{KEPT_VARIABLE}\", should be \"Kept variable\"") +endif() + +if(NOT "$ENV{ADDED_VARIABLE}" STREQUAL "Added variable") + message(SEND_ERROR "ADDED_VARIABLE is \"$ENV{ADDED_VARIABLE}\", should be \"Added variable\"") +endif() + +if(NOT "$ENV{CHANGED_VARIABLE}" STREQUAL "Changed variable") + message(SEND_ERROR "CHANGED_VARIABLE is \"$ENV{CHANGED_VARIABLE}\", should be \"Changed variable\"") +endif() + +if(DEFINED ENV{REMOVED_VARIABLE}) + message(SEND_ERROR "REMOVED_VARIABLE should not be defined") +endif() |