diff options
37 files changed, 1104 insertions, 61 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f4ebb02..5c9865e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,9 +12,9 @@ include: - local: .gitlab/upload.yml stages: + - prep - build - test - - package - test-ext - upload @@ -34,6 +34,35 @@ stages: # - dependency/needs jobs for required jobs ################################################################################ +# Prep jobs + +prep:source: + extends: + - .linux_prep_source + - .cmake_prep_source_linux + - .linux_builder_tags + - .cmake_release_artifacts + - .run_only_for_package + +prep:doc: + extends: + - .fedora31_sphinx_package + - .cmake_prep_doc_linux + - .linux_builder_tags_qt + - .cmake_doc_artifacts + - .run_only_for_package + +upload:source: + extends: + - .rsync_upload + - .run_only_for_package + dependencies: + - prep:source + needs: + - prep:source + variables: + RSYNC_DESTINATION: dev + # Lint builds build:debian10-iwyu: @@ -57,14 +86,6 @@ build:fedora31-sphinx: - .linux_builder_tags_qt - .run_automatically -build:fedora31-sphinx-package: - extends: - - .fedora31_sphinx_package - - .cmake_build_linux - - .linux_builder_tags_qt - - .cmake_doc_artifacts - - .run_only_for_package - # Linux builds build:centos6-release: @@ -180,7 +201,7 @@ test:macos-xcode: needs: - test:macos-ninja -package:macos: +build:macos-package: extends: - .macos_package - .cmake_build_macos_package @@ -188,18 +209,18 @@ package:macos: - .macos_builder_tags_package - .run_only_for_package dependencies: - - build:fedora31-sphinx-package + - prep:doc needs: - - build:fedora31-sphinx-package + - prep:doc upload:macos: extends: - .rsync_upload - .run_only_for_package dependencies: - - package:macos + - build:macos-package needs: - - package:macos + - build:macos-package variables: RSYNC_DESTINATION: dev diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index f1c0c7e..1c24003 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -74,6 +74,9 @@ # Any packages made. - build/cmake-*-Linux-x86_64.* - build/cmake-*-Darwin-x86_64.* + # Any source packages made. + - build/cmake-*.tar.gz + - build/cmake-*.zip .cmake_test_artifacts: artifacts: diff --git a/.gitlab/ci/cmake_version.cmake b/.gitlab/ci/cmake_version.cmake new file mode 100644 index 0000000..ef9f7f2 --- /dev/null +++ b/.gitlab/ci/cmake_version.cmake @@ -0,0 +1,3 @@ +get_filename_component(CMake_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +include("${CMake_SOURCE_DIR}/Source/CMakeVersion.cmake") +message(STATUS ${CMake_VERSION}) diff --git a/.gitlab/ci/cmake_version.sh b/.gitlab/ci/cmake_version.sh new file mode 100755 index 0000000..03b1614 --- /dev/null +++ b/.gitlab/ci/cmake_version.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cmake -P "${BASH_SOURCE%/*}/cmake_version.cmake" | cut -d ' ' -f 2 diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 75f8156..74b2de7 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -11,6 +11,12 @@ GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" LAUNCHER: "scl enable devtoolset-6 rh-python36 --" +.linux_prep_source: + image: "fedora:32" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + ### Debian .debian10: @@ -143,6 +149,29 @@ - cmake --version - ninja --version +.cmake_prep_source_linux: + stage: prep + + script: + - *before_script_linux + - dnf install --setopt=install_weak_deps=False -y git-core + - v="$(.gitlab/ci/cmake_version.sh)" + - mkdir -p build/ + - git archive --format=tgz "--prefix=cmake-$v/" -o "build/cmake-$v.tar.gz" HEAD + - git -c core.autocrlf=true -c core.eol=crlf archive --format=zip --prefix="cmake-$v/" -o "build/cmake-$v.zip" HEAD + + interruptible: true + +.cmake_prep_doc_linux: + stage: prep + + script: + - *before_script_linux + - "$LAUNCHER ctest -VV -S .gitlab/ci/ctest_configure.cmake" + - "$LAUNCHER ctest -VV -S .gitlab/ci/ctest_build.cmake" + + interruptible: true + .cmake_build_linux: stage: build diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 47e81d1..a500d36 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -111,7 +111,7 @@ interruptible: true .cmake_build_macos_package: - stage: package + stage: build script: - *before_script_macos 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/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake index b3da82d..d530191 100644 --- a/Modules/CMakeOBJCInformation.cmake +++ b/Modules/CMakeOBJCInformation.cmake @@ -170,7 +170,7 @@ endif() # compile an Objective-C file into an object file if(NOT CMAKE_OBJC_COMPILE_OBJECT) set(CMAKE_OBJC_COMPILE_OBJECT - "<CMAKE_OBJC_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -x objective-c -o <OBJECT> -c <SOURCE>") + "<CMAKE_OBJC_COMPILER> <DEFINES> <INCLUDES> -x objective-c <FLAGS> -o <OBJECT> -c <SOURCE>") endif() if(NOT CMAKE_OBJC_LINK_EXECUTABLE) diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake index 4be9762..7a3b9d7 100644 --- a/Modules/CMakeOBJCXXInformation.cmake +++ b/Modules/CMakeOBJCXXInformation.cmake @@ -263,7 +263,7 @@ endif() # compile an Objective-C++ file into an object file if(NOT CMAKE_OBJCXX_COMPILE_OBJECT) set(CMAKE_OBJCXX_COMPILE_OBJECT - "<CMAKE_OBJCXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") + "<CMAKE_OBJCXX_COMPILER> <DEFINES> <INCLUDES> -x objective-c++ <FLAGS> -o <OBJECT> -c <SOURCE>") endif() if(NOT CMAKE_OBJCXX_LINK_EXECUTABLE) diff --git a/Modules/FindCUDA/select_compute_arch.cmake b/Modules/FindCUDA/select_compute_arch.cmake index c11725d..9351288 100644 --- a/Modules/FindCUDA/select_compute_arch.cmake +++ b/Modules/FindCUDA/select_compute_arch.cmake @@ -7,7 +7,7 @@ # ARCH_AND_PTX : NAME | NUM.NUM | NUM.NUM(NUM.NUM) | NUM.NUM+PTX # NAME: Fermi Kepler Maxwell Kepler+Tegra Kepler+Tesla Maxwell+Tegra Pascal Volta Turing Ampere # NUM: Any number. Only those pairs are currently accepted by NVCC though: -# 2.0 2.1 3.0 3.2 3.5 3.7 5.0 5.2 5.3 6.0 6.2 7.0 7.2 7.5 8.0 +# 2.0 2.1 3.0 3.2 3.5 3.7 5.0 5.2 5.3 6.0 6.2 7.0 7.2 7.5 8.0 8.6 # Returns LIST of flags to be added to CUDA_NVCC_FLAGS in ${out_variable} # Additionally, sets ${out_variable}_readable to the resulting numeric list # Example: @@ -82,16 +82,26 @@ if(CUDA_VERSION VERSION_GREATER_EQUAL "10.0") list(APPEND CUDA_ALL_GPU_ARCHITECTURES "7.5") if(CUDA_VERSION VERSION_LESS "11.0") - set(CUDA_LIMIT_GPU_ARCHITECTURE "8.0") list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "7.5+PTX") + set(CUDA_LIMIT_GPU_ARCHITECTURE "8.0") endif() endif() if(CUDA_VERSION VERSION_GREATER_EQUAL "11.0") list(APPEND CUDA_KNOWN_GPU_ARCHITECTURES "Ampere") - list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "8.0" "8.0+PTX") + list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "8.0") list(APPEND CUDA_ALL_GPU_ARCHITECTURES "8.0") + if(CUDA_VERSION VERSION_LESS "11.1") + list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "8.0+PTX") + set(CUDA_LIMIT_GPU_ARCHITECTURE "8.6") + endif() +endif() + +if(CUDA_VERSION VERSION_GREATER_EQUAL "11.1") + list(APPEND CUDA_COMMON_GPU_ARCHITECTURES "8.6" "8.6+PTX") + list(APPEND CUDA_ALL_GPU_ARCHITECTURES "8.6") + if(CUDA_VERSION VERSION_LESS "12.0") set(CUDA_LIMIT_GPU_ARCHITECTURE "9.0") endif() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 818b1bd..3bb5e80 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 18) -set(CMake_VERSION_PATCH 20200929) +set(CMake_VERSION_PATCH 20200930) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) 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/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx index 4f74eba..fb4e380 100644 --- a/Source/kwsys/RegularExpression.cxx +++ b/Source/kwsys/RegularExpression.cxx @@ -359,7 +359,7 @@ bool RegularExpression::compile(const char* exp) this->regmatch.clear(); // Small enough for pointer-storage convention? - if (comp.regsize >= 32767L) { // Probably could be 65535L. + if (comp.regsize >= 65535L) { // RAISE Error, SYM(RegularExpression), SYM(Expr_Too_Big), printf("RegularExpression::compile(): Expression too big.\n"); return false; diff --git a/Tests/CMakeGUI/CMakeGUITest.cmake b/Tests/CMakeGUI/CMakeGUITest.cmake index 3d8c27e..b60ec35 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cmake +++ b/Tests/CMakeGUI/CMakeGUITest.cmake @@ -109,3 +109,12 @@ run_cmake_gui_test(sourceBinaryArgs:noExistConfigExists ARGS "${CMakeGUITest_BINARY_DIR}/sourceBinaryArgs-noExistConfigExists/noexist" ) + +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 a7a5d17..80ea08d 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cxx +++ b/Tests/CMakeGUI/CMakeGUITest.cxx @@ -2,8 +2,10 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "CMakeGUITest.h" +#include "QCMake.h" #include <QApplication> #include <QEventLoop> +#include <QMessageBox> #include <QSettings> #include <QString> #include <QStringList> @@ -13,6 +15,9 @@ #include "CMakeSetupDialog.h" +#include "CatchShow.h" +#include "FirstConfigure.h" + namespace { void loopSleep(int msecs = 100) { @@ -28,6 +33,44 @@ CMakeGUITest::CMakeGUITest(CMakeSetupDialog* window, QObject* parent) { } +void CMakeGUITest::tryConfigure(int expectedResult, int timeout) +{ + auto* cmake = this->m_window->findChild<QCMakeThread*>()->cmakeInstance(); + + bool done = false; + CatchShow catchConfigure; + catchConfigure.setCallback<FirstConfigure>([&done](FirstConfigure* dialog) { + if (done) { + return; + } + done = true; + + dialog->findChild<StartCompilerSetup*>()->setCurrentGenerator( + CMAKE_GENERATOR); + dialog->accept(); + }); + + CatchShow catchMessages; + catchMessages.setCallback<QMessageBox>([](QMessageBox* box) { + if (box->text().contains("Build directory does not exist")) { + box->accept(); + } + + if (box->text().contains("Error in configuration process")) { + box->accept(); + } + }); + + QSignalSpy configureDoneSpy(cmake, &QCMake::configureDone); + QVERIFY(configureDoneSpy.isValid()); + QMetaObject::invokeMethod( + this->m_window, [this]() { this->m_window->ConfigureButton->click(); }, + Qt::QueuedConnection); + QVERIFY(configureDoneSpy.wait(timeout)); + + QCOMPARE(configureDoneSpy, { { expectedResult } }); +} + void CMakeGUITest::sourceBinaryArgs() { QFETCH(QString, sourceDir); @@ -68,6 +111,67 @@ void CMakeGUITest::sourceBinaryArgs_data() << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-noExistConfigExists/build"; } +void CMakeGUITest::simpleConfigure() +{ + QFETCH(QString, sourceDir); + QFETCH(QString, binaryDir); + QFETCH(int, expectedResult); + + this->m_window->SourceDirectory->setText(sourceDir); + this->m_window->BinaryDirectory->setCurrentText(binaryDir); + + // Wait a bit for everything to update + loopSleep(); + + this->tryConfigure(expectedResult, 1000); +} + +void CMakeGUITest::simpleConfigure_data() +{ + QTest::addColumn<QString>("sourceDir"); + QTest::addColumn<QString>("binaryDir"); + QTest::addColumn<int>("expectedResult"); + + QTest::newRow("success") << CMakeGUITest_BINARY_DIR + "/simpleConfigure-success/src" + << CMakeGUITest_BINARY_DIR + "/simpleConfigure-success/build" + << 0; + QTest::newRow("fail") << CMakeGUITest_BINARY_DIR "/simpleConfigure-fail/src" + << CMakeGUITest_BINARY_DIR + "/simpleConfigure-fail/build" + << -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 55a885b..891cf62 100644 --- a/Tests/CMakeGUI/CMakeGUITest.h +++ b/Tests/CMakeGUI/CMakeGUITest.h @@ -15,7 +15,12 @@ public: private: CMakeSetupDialog* m_window = nullptr; + void tryConfigure(int expectedResult = 0, int timeout = 60000); + private slots: void sourceBinaryArgs(); void sourceBinaryArgs_data(); + void simpleConfigure(); + void simpleConfigure_data(); + void environment(); }; diff --git a/Tests/CMakeGUI/CMakeLists.txt b/Tests/CMakeGUI/CMakeLists.txt index 2a2ee1a..c6bc88a 100644 --- a/Tests/CMakeGUI/CMakeLists.txt +++ b/Tests/CMakeGUI/CMakeLists.txt @@ -10,13 +10,24 @@ include_directories( set(MOC_SRCS) qt5_wrap_cpp(MOC_SRCS + CatchShow.h + ) +add_library(CMakeGUITestLib STATIC ${MOC_SRCS} + CatchShow.cxx + CatchShow.h + ) +target_link_libraries(CMakeGUITestLib Qt5::Core Qt5::Gui Qt5::Widgets) + +set(MOC_SRCS) +qt5_wrap_cpp(MOC_SRCS CMakeGUITest.h ) add_executable(CMakeGUITest CMakeGUITest.cxx ${MOC_SRCS}) -target_link_libraries(CMakeGUITest CMakeGUIMainLib Qt5::Core Qt5::Test Qt5::Widgets) +target_link_libraries(CMakeGUITest CMakeGUIMainLib CMakeGUITestLib Qt5::Core Qt5::Test Qt5::Widgets) target_compile_definitions(CMakeGUITest PRIVATE "CMakeGUITest_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" "CMakeGUITest_BINARY_DIR=\"${CMAKE_CURRENT_BINARY_DIR}\"" + "CMAKE_GENERATOR=\"${CMAKE_GENERATOR}\"" ) add_test(NAME CMakeGUI COMMAND ${CMAKE_CMAKE_COMMAND} @@ -35,11 +46,25 @@ function(add_cmake_gui_lib_test name) ${_t_MOC_SOURCES} ) add_executable(${name} ${_t_SOURCES} ${MOC_SRCS}) - target_link_libraries(${name} CMakeGUILib Qt5::Core Qt5::Test Qt5::Widgets) + target_link_libraries(${name} CMakeGUILib CMakeGUITestLib Qt5::Core Qt5::Test Qt5::Widgets) add_test(NAME "CMakeGUILib.${name}" COMMAND ${name}) endfunction() +add_cmake_gui_lib_test(CatchShow + SOURCES + CatchShowTest.cxx + CatchShowTest.h + 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/CatchShow.cxx b/Tests/CMakeGUI/CatchShow.cxx new file mode 100644 index 0000000..aee2d9d --- /dev/null +++ b/Tests/CMakeGUI/CatchShow.cxx @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "CatchShow.h" + +#include <QCoreApplication> + +CatchShow::CatchShow(QObject* parent) + : QObject(parent) +{ + QCoreApplication::instance()->installEventFilter(this); +} + +bool CatchShow::eventFilter(QObject* obj, QEvent* event) +{ + if (this->m_callback && event->type() == QEvent::Show) { + this->m_callback(obj); + } + + return this->QObject::eventFilter(obj, event); +} + +int CatchShow::count() const +{ + return this->m_count; +} diff --git a/Tests/CMakeGUI/CatchShow.h b/Tests/CMakeGUI/CatchShow.h new file mode 100644 index 0000000..0254c15 --- /dev/null +++ b/Tests/CMakeGUI/CatchShow.h @@ -0,0 +1,41 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <functional> +#include <memory> + +#include <QObject> +#include <QWidget> + +class CatchShow : public QObject +{ + Q_OBJECT +public: + CatchShow(QObject* parent = nullptr); + + template <typename T, typename F> + void setCallback(F&& func); + bool eventFilter(QObject* obj, QEvent* event) override; + int count() const; + +private: + std::function<void(QObject* obj)> m_callback; + int m_count = 0; +}; + +template <typename T, typename F> +void CatchShow::setCallback(F&& func) +{ + this->m_callback = [this, func](QObject* obj) { + auto* d = qobject_cast<T*>(obj); + if (d) { + QMetaObject::invokeMethod(obj, + [this, func, d]() { + ++this->m_count; + func(d); + }, + Qt::QueuedConnection); + } + }; +} diff --git a/Tests/CMakeGUI/CatchShowTest.cxx b/Tests/CMakeGUI/CatchShowTest.cxx new file mode 100644 index 0000000..acea8ea --- /dev/null +++ b/Tests/CMakeGUI/CatchShowTest.cxx @@ -0,0 +1,49 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "CatchShowTest.h" + +#include <QMessageBox> +#include <QtTest> + +#include "CatchShow.h" + +CatchShowTest::CatchShowTest(QObject* parent) + : QObject(parent) +{ +} + +void CatchShowTest::catchShow() +{ + bool have = false; + CatchShow catcher; + catcher.setCallback<QMessageBox>([&have](QMessageBox* box) { + have = true; + box->accept(); + }); + + QCOMPARE(catcher.count(), 0); + QCOMPARE(have, false); + + { + QDialog dialog; + dialog.show(); + QCOMPARE(catcher.count(), 0); + QCOMPARE(have, false); + } + + { + have = false; + QMessageBox::critical(nullptr, "Error", "This is an error"); + QCOMPARE(catcher.count(), 1); + QCOMPARE(have, true); + } + + { + have = false; + QMessageBox::information(nullptr, "Info", "This is information"); + QCOMPARE(catcher.count(), 2); + QCOMPARE(have, true); + } +} + +QTEST_MAIN(CatchShowTest) diff --git a/Tests/CMakeGUI/CatchShowTest.h b/Tests/CMakeGUI/CatchShowTest.h new file mode 100644 index 0000000..6da2163 --- /dev/null +++ b/Tests/CMakeGUI/CatchShowTest.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 CatchShowTest : public QObject +{ + Q_OBJECT +public: + CatchShowTest(QObject* parent = nullptr); + +private slots: + void catchShow(); +}; 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() diff --git a/Tests/CMakeGUI/simpleConfigure-fail/CMakeLists.txt.in b/Tests/CMakeGUI/simpleConfigure-fail/CMakeLists.txt.in new file mode 100644 index 0000000..dc55064 --- /dev/null +++ b/Tests/CMakeGUI/simpleConfigure-fail/CMakeLists.txt.in @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.18) +project(simpleConfigure-fail NONE) + +message(STATUS "This is a failed configure") +message(FATAL_ERROR "Error") diff --git a/Tests/CMakeGUI/simpleConfigure-success/CMakeLists.txt.in b/Tests/CMakeGUI/simpleConfigure-success/CMakeLists.txt.in new file mode 100644 index 0000000..fc42c00 --- /dev/null +++ b/Tests/CMakeGUI/simpleConfigure-success/CMakeLists.txt.in @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.18) +project(simpleConfigure-success NONE) + +message(STATUS "This is a successful configure") diff --git a/Tests/ObjCXX/CMakeLists.txt b/Tests/ObjCXX/CMakeLists.txt index a2a907a..cf03771 100644 --- a/Tests/ObjCXX/CMakeLists.txt +++ b/Tests/ObjCXX/CMakeLists.txt @@ -2,3 +2,4 @@ ADD_TEST_MACRO(ObjCXX.ObjC++ ObjC++) ADD_TEST_MACRO(ObjCXX.simple-build-test simple-build-test) ADD_TEST_MACRO(ObjCXX.cxx-file-extension-test cxx-file-extension-test) ADD_TEST_MACRO(ObjCXX.objcxx-file-extension-test objcxx-file-extension-test) +ADD_TEST_MACRO(ObjCXX.cxx-as-objcxx cxx-as-objcxx) diff --git a/Tests/ObjCXX/cxx-as-objcxx/CMakeLists.txt b/Tests/ObjCXX/cxx-as-objcxx/CMakeLists.txt new file mode 100644 index 0000000..23f6891 --- /dev/null +++ b/Tests/ObjCXX/cxx-as-objcxx/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.18) +project(cxx-as-objcxx LANGUAGES OBJCXX) + +add_executable(cxx-as-objcxx main.cpp) +set_source_files_properties(main.cpp PROPERTIES LANGUAGE OBJCXX) diff --git a/Tests/ObjCXX/cxx-as-objcxx/main.cpp b/Tests/ObjCXX/cxx-as-objcxx/main.cpp new file mode 100644 index 0000000..701c567 --- /dev/null +++ b/Tests/ObjCXX/cxx-as-objcxx/main.cpp @@ -0,0 +1,6 @@ +#import <Foundation/Foundation.h> + +int main(int argc, char* argv[]) +{ + return 0; +} |