diff --git a/plugins/meson/CMakeLists.txt b/plugins/meson/CMakeLists.txt --- a/plugins/meson/CMakeLists.txt +++ b/plugins/meson/CMakeLists.txt @@ -3,19 +3,31 @@ set(mesonbuilder_SRCS mesonbuilder.cpp mesonconfig.cpp + mesonintrospectjob.cpp mesonimportjob.cpp mesonjob.cpp mesonjobprune.cpp mesonmanager.cpp + mesonoptions.cpp settings/mesonadvancedsettings.cpp settings/mesonconfigpage.cpp + settings/mesonlisteditor.cpp settings/mesonnewbuilddir.cpp + settings/mesonoptionbaseview.cpp + settings/mesonoptionsview.cpp ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -ki18n_wrap_ui(mesonbuilder_SRCS settings/mesonadvancedsettings.ui settings/mesonconfigpage.ui settings/mesonnewbuilddir.ui) +ki18n_wrap_ui(mesonbuilder_SRCS + settings/mesonadvancedsettings.ui + settings/mesonconfigpage.ui + settings/mesonlisteditor.ui + settings/mesonnewbuilddir.ui + settings/mesonoptionbaseview.ui + settings/mesonoptionsview.ui +) ecm_qt_declare_logging_category(mesonbuilder_SRCS HEADER debug.h IDENTIFIER KDEV_Meson @@ -35,3 +47,8 @@ KDev::Util KDev::OutputView ) + +set_target_properties(kdevmesonmanager PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES +) diff --git a/plugins/meson/mesonbuilder.h b/plugins/meson/mesonbuilder.h --- a/plugins/meson/mesonbuilder.h +++ b/plugins/meson/mesonbuilder.h @@ -52,7 +52,7 @@ KJob* prune(KDevelop::IProject* project) override; KJob* configure(KDevelop::IProject* project) override; - KJob* configure(KDevelop::IProject* project, Meson::BuildDir const& buildDir, + KJob* configure(KDevelop::IProject* project, Meson::BuildDir const& buildDir, QStringList args, DirectoryStatus status = ___UNDEFINED___); /// Evaluate a directory for the use with meson diff --git a/plugins/meson/mesonbuilder.cpp b/plugins/meson/mesonbuilder.cpp --- a/plugins/meson/mesonbuilder.cpp +++ b/plugins/meson/mesonbuilder.cpp @@ -140,7 +140,8 @@ return MESON_CONFIGURED; } -KJob* MesonBuilder::configure(IProject* project, const Meson::BuildDir& buildDir, DirectoryStatus status) +KJob* MesonBuilder::configure(IProject* project, const Meson::BuildDir& buildDir, QStringList args, + DirectoryStatus status) { Q_ASSERT(project); @@ -158,11 +159,11 @@ case DOES_NOT_EXIST: case CLEAN: case MESON_FAILED_CONFIGURATION: - job = new MesonJob(buildDir, project, MesonJob::CONFIGURE, {}, this); + job = new MesonJob(buildDir, project, MesonJob::CONFIGURE, args, this); connect(job, &KJob::result, this, [this, project]() { emit configured(project); }); return job; case MESON_CONFIGURED: - job = new MesonJob(buildDir, project, MesonJob::RE_CONFIGURE, {}, this); + job = new MesonJob(buildDir, project, MesonJob::RE_CONFIGURE, args, this); connect(job, &KJob::result, this, [this, project]() { emit configured(project); }); return job; case DIR_NOT_EMPTY: @@ -191,7 +192,7 @@ { Q_ASSERT(project); auto buildDir = Meson::currentBuildDir(project); - return configure(project, buildDir); + return configure(project, buildDir, {}); } KJob* MesonBuilder::configureIfRequired(KDevelop::IProject* project, KJob* realJob) @@ -205,7 +206,7 @@ } QList jobs = { - configure(project, buildDir, status), // First configure the build directory + configure(project, buildDir, {}, status), // First configure the build directory realJob // If this succeeds execute the real job }; diff --git a/plugins/meson/mesonconfig.h b/plugins/meson/mesonconfig.h --- a/plugins/meson/mesonconfig.h +++ b/plugins/meson/mesonconfig.h @@ -33,9 +33,7 @@ struct BuildDir { KDevelop::Path buildDir; - KDevelop::Path installPrefix; KDevelop::Path mesonExecutable; - QString buildType; QString mesonBackend; QString mesonArgs; diff --git a/plugins/meson/mesonconfig.cpp b/plugins/meson/mesonconfig.cpp --- a/plugins/meson/mesonconfig.cpp +++ b/plugins/meson/mesonconfig.cpp @@ -36,10 +36,8 @@ static const QString BUILD_DIR_SEC = QStringLiteral("BuildDir %1"); static const QString BUILD_DIR_PATH = QStringLiteral("Build Directory Path"); -static const QString INSTALL_PREFIX = QStringLiteral("Installation prefix"); static const QString MESON_EXE = QStringLiteral("Meson executable"); static const QString EXTRA_ARGS = QStringLiteral("Additional meson arguments"); -static const QString BUILD_TYPE = QStringLiteral("Build type"); static const QString BACKEND = QStringLiteral("Meson Generator Backend"); int MesonConfig::addBuildDir(BuildDir dir) @@ -99,9 +97,7 @@ KConfigGroup current = root.group(section); BuildDir currBD; currBD.buildDir = Path(current.readEntry(BUILD_DIR_PATH, QString())); - currBD.installPrefix = Path(current.readEntry(INSTALL_PREFIX, QString())); currBD.mesonExecutable = Path(current.readEntry(MESON_EXE, QString())); - currBD.buildType = current.readEntry(BUILD_TYPE, QStringLiteral("debug")); currBD.mesonBackend = current.readEntry(BACKEND, QString()); currBD.mesonArgs = current.readEntry(EXTRA_ARGS, QString()); @@ -149,9 +145,7 @@ KConfigGroup current = root.group(BUILD_DIR_SEC.arg(counter++)); current.writeEntry(BUILD_DIR_PATH, i.buildDir.path()); - current.writeEntry(INSTALL_PREFIX, i.installPrefix.path()); current.writeEntry(MESON_EXE, i.mesonExecutable.path()); - current.writeEntry(BUILD_TYPE, i.buildType); current.writeEntry(BACKEND, i.mesonBackend); current.writeEntry(EXTRA_ARGS, i.mesonArgs); } @@ -184,12 +178,12 @@ bool Meson::BuildDir::isValid() const { - return !(buildDir.isEmpty() || mesonExecutable.isEmpty() || buildType.isEmpty()); + return !(buildDir.isEmpty() || mesonExecutable.isEmpty()); } void Meson::BuildDir::canonicalizePaths() { - for (auto* i : { &buildDir, &installPrefix, &mesonExecutable }) { + for (auto* i : { &buildDir, &mesonExecutable }) { *i = Path(QFileInfo(i->toLocalFile()).canonicalFilePath()); } } diff --git a/plugins/meson/mesonintrospectjob.h b/plugins/meson/mesonintrospectjob.h new file mode 100644 --- /dev/null +++ b/plugins/meson/mesonintrospectjob.h @@ -0,0 +1,68 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "mesonconfig.h" +#include "mesonoptions.h" +#include +#include +#include +#include + +class QJsonObject; + +class MesonIntrospectJob : public KJob +{ + Q_OBJECT + +public: + enum Type { BENCHMARKS, BUILDOPTIONS, BUILDSYSTEM_FILES, DEPENDENCIES, INSTALLED, PROJECTINFO, TARGETS, TESTS }; + enum Mode { BUILD_DIR, MESON_FILE }; + +public: + explicit MesonIntrospectJob(KDevelop::IProject* project, QVector types, Mode mode, QObject* parent); + explicit MesonIntrospectJob(KDevelop::Path projectPath, KDevelop::Path meson, QVector types, QObject* parent); + explicit MesonIntrospectJob(KDevelop::IProject* project, Meson::BuildDir buildDir, QVector types, Mode mode, + QObject* parent); + + void start() override; + bool doKill() override; + + QString getTypeString(Type type) const; + + MESON_OPT_PTR buildOptions(); + +private: + QString importJSONFile(Meson::BuildDir const& buildDir, Type type, QJsonObject* out); + QString importMesonAPI(Meson::BuildDir const& buildDir, Type type, QJsonObject* out); + QString import(Meson::BuildDir buildDir); + void finished(); + + QFutureWatcher m_futureWatcher; + + // The commands to execute + QVector m_types = {}; + Mode m_mode = BUILD_DIR; + Meson::BuildDir m_buildDir; + KDevelop::Path m_projectPath; + + // The results + MESON_OPT_PTR m_res_options = nullptr; +}; diff --git a/plugins/meson/mesonintrospectjob.cpp b/plugins/meson/mesonintrospectjob.cpp new file mode 100644 --- /dev/null +++ b/plugins/meson/mesonintrospectjob.cpp @@ -0,0 +1,251 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mesonintrospectjob.h" +#include "mesonconfig.h" +#include "mesonmanager.h" +#include "mesonoptions.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Meson; +using namespace KDevelop; + +MesonIntrospectJob::MesonIntrospectJob(KDevelop::IProject* project, QVector types, + MesonIntrospectJob::Mode mode, QObject* parent) + : KJob(parent) + , m_types(types) + , m_mode(mode) +{ + Q_ASSERT(project); + + if (mode == MESON_FILE) { + // Since we are parsing the meson file in this mode, no build directory + // is required and we have to fake a build directory + m_buildDir.buildDir = project->path(); + auto* bsm = project->buildSystemManager(); + MesonManager* manager = dynamic_cast(bsm); + if (manager) { + m_buildDir.mesonExecutable = manager->findMeson(); + } + } else { + m_buildDir = Meson::currentBuildDir(project); + } + + m_projectPath = project->path(); + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); +} + +MesonIntrospectJob::MesonIntrospectJob(KDevelop::Path projectPath, KDevelop::Path meson, + QVector types, QObject* parent) + : KJob(parent) + , m_types(types) + , m_mode(MESON_FILE) + , m_projectPath(projectPath) +{ + // Since we are parsing the meson file in this mode, no build directory + // is required and we have to fake a build directory + m_buildDir.buildDir = m_projectPath; + m_buildDir.mesonExecutable = meson; + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); +} + +MesonIntrospectJob::MesonIntrospectJob(KDevelop::IProject* project, Meson::BuildDir buildDir, + QVector types, MesonIntrospectJob::Mode mode, + QObject* parent) + : KJob(parent) + , m_types(types) + , m_mode(mode) + , m_buildDir(buildDir) +{ + Q_ASSERT(project); + + m_projectPath = project->path(); + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); +} + +QString MesonIntrospectJob::getTypeString(MesonIntrospectJob::Type type) const +{ + switch (type) { + case BENCHMARKS: + return QStringLiteral("benchmarks"); + case BUILDOPTIONS: + return QStringLiteral("buildoptions"); + case BUILDSYSTEM_FILES: + return QStringLiteral("buildsystem_files"); + case DEPENDENCIES: + return QStringLiteral("dependencies"); + case INSTALLED: + return QStringLiteral("installed"); + case PROJECTINFO: + return QStringLiteral("projectinfo"); + case TARGETS: + return QStringLiteral("targets"); + case TESTS: + return QStringLiteral("tests"); + } + + return QStringLiteral("error"); +} + +QString MesonIntrospectJob::importJSONFile(const BuildDir& buildDir, MesonIntrospectJob::Type type, QJsonObject* out) +{ + QString typeStr = getTypeString(type); + QString fileName = QStringLiteral("intro-") + typeStr + QStringLiteral(".json"); + QString infoDir = buildDir.buildDir.toLocalFile() + QStringLiteral("/") + QStringLiteral("meson-info"); + QFile introFile(infoDir + QStringLiteral("/") + fileName); + + if (!introFile.exists()) { + return i18n("Introspection file '%1' does not exist", QFileInfo(introFile).canonicalFilePath()); + } + + if (!introFile.open(QFile::ReadOnly | QFile::Text)) { + return i18n("Failed to open introspection file '%1'", QFileInfo(introFile).canonicalFilePath()); + } + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(introFile.readAll(), &error); + if (error.error) { + return i18n("In %1:%2: %3", QFileInfo(introFile).canonicalFilePath(), error.offset, error.errorString()); + } + + if (doc.isArray()) { + (*out)[typeStr] = doc.array(); + } else if (doc.isObject()) { + (*out)[typeStr] = doc.object(); + } else { + return i18n("The introspection file '%1' contains neither an array nor an object", + QFileInfo(introFile).canonicalFilePath()); + } + + return QStringLiteral(""); +} + +QString MesonIntrospectJob::importMesonAPI(const BuildDir& buildDir, MesonIntrospectJob::Type type, QJsonObject* out) +{ + QString typeStr = getTypeString(type); + QString option = QStringLiteral("--") + typeStr; + option.replace(QChar::fromLatin1('_'), QChar::fromLatin1('-')); + + KProcess proc(this); + proc.setWorkingDirectory(m_projectPath.toLocalFile()); + proc.setOutputChannelMode(KProcess::SeparateChannels); + proc.setProgram(buildDir.mesonExecutable.toLocalFile()); + proc << QStringLiteral("introspect") << option << QStringLiteral("meson.build"); + + int ret = proc.execute(); + if (ret != 0) { + return i18n("%1 returned %2", proc.program().join(QChar::fromLatin1(' ')), ret); + } + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(proc.readAll(), &error); + if (error.error) { + return i18n("JSON parser error: %1", error.errorString()); + } + + if (doc.isArray()) { + (*out)[typeStr] = doc.array(); + } else if (doc.isObject()) { + (*out)[typeStr] = doc.object(); + } else { + return i18n("The introspection output of '%1' contains neither an array nor an object", + proc.program().join(QChar::fromLatin1(' '))); + } + + return QStringLiteral(""); +} + +QString MesonIntrospectJob::import(BuildDir buildDir) +{ + QJsonObject rawData; + + // First load the complete JSON data + for (auto i : m_types) { + QString err; + switch (m_mode) { + case BUILD_DIR: + err = importJSONFile(buildDir, i, &rawData); + break; + case MESON_FILE: + err = importMesonAPI(buildDir, i, &rawData); + break; + } + + if (!err.isEmpty()) { + qCWarning(KDEV_Meson) << "MINTRO: " << err; + setError(true); + setErrorText(err); + return err; + } + } + + auto buildOptionsJSON = rawData[QStringLiteral("buildoptions")]; + if (buildOptionsJSON.isArray()) { + m_res_options = std::make_shared(buildOptionsJSON.toArray()); + if(m_res_options) { + qCDebug(KDEV_Meson) << "MINTRO: Imported " << m_res_options->options().size() << " buildoptions"; + } else { + qCWarning(KDEV_Meson) << "MINTRO: Failed to parse buildoptions"; + } + } + + return QStringLiteral(""); +} + +void MesonIntrospectJob::start() +{ + qCDebug(KDEV_Meson) << "MINTRO: Starting meson introspection job"; + if (!m_buildDir.isValid()) { + qCWarning(KDEV_Meson) << "The current build directory is invalid"; + setError(true); + setErrorText(i18n("The current build directory is invalid")); + emitResult(); + return; + } + + auto future = QtConcurrent::run(this, &MesonIntrospectJob::import, m_buildDir); + m_futureWatcher.setFuture(future); +} + +void MesonIntrospectJob::finished() +{ + qCDebug(KDEV_Meson) << "MINTRO: Meson introspection job finished"; + emitResult(); +} + +bool MesonIntrospectJob::doKill() +{ + if (m_futureWatcher.isRunning()) { + m_futureWatcher.cancel(); + } + return true; +} + +MESON_OPT_PTR MesonIntrospectJob::buildOptions() +{ + return m_res_options; +} diff --git a/plugins/meson/mesonjob.cpp b/plugins/meson/mesonjob.cpp --- a/plugins/meson/mesonjob.cpp +++ b/plugins/meson/mesonjob.cpp @@ -44,30 +44,24 @@ switch (m_commandType) { case CONFIGURE: - if (!buildDir.installPrefix.isEmpty()) { - *this << QStringLiteral("--prefix") << buildDir.installPrefix.toLocalFile(); - } - *this << QStringLiteral("--backend") << buildDir.mesonBackend; - *this << QStringLiteral("--buildtype") << buildDir.buildType; - - for (auto const& i : buildDir.mesonArgs) { - *this << i; - } - break; case RE_CONFIGURE: *this << QStringLiteral("--reconfigure"); break; case SET_CONFIG: *this << QStringLiteral("configure"); - if (!buildDir.installPrefix.isEmpty()) { - *this << QStringLiteral("-Dprefix=") + buildDir.installPrefix.toLocalFile(); - } - *this << QStringLiteral("-Dbuildtype=") + buildDir.buildType; + break; } *this << m_arguments; + + for (auto i : buildDir.mesonArgs.split(QChar::fromLatin1(' '))) { + if (!i.isEmpty()) { + *this << i; + } + } + *this << buildDir.buildDir.toLocalFile(); } diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -128,9 +128,10 @@ Meson::BuildDir buildDir = newBD.currentConfig(); Meson::MesonConfig mesonCfg = Meson::getMesonConfig(project); buildDir.canonicalizePaths(); - mesonCfg.addBuildDir(buildDir); + mesonCfg.currentIndex = mesonCfg.addBuildDir(buildDir); Meson::writeMesonConfig(project, mesonCfg); + m_builder->configure(project, buildDir, newBD.mesonArgs())->start(); return buildDir; } diff --git a/plugins/meson/mesonoptions.h b/plugins/meson/mesonoptions.h new file mode 100644 --- /dev/null +++ b/plugins/meson/mesonoptions.h @@ -0,0 +1,178 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include +#include +#include + +class QJsonArray; +class QJsonObject; +class QWidget; +class MesonOptionBase; +class MesonOptions; + +using OPT_PTR = std::shared_ptr; +using MESON_OPT_PTR = std::shared_ptr; + +/*! + * Base class for a single meson option. + */ +class MesonOptionBase +{ +public: + enum Section { CORE, BACKEND, BASE, COMPILER, DIRECTORY, USER, TEST }; + enum Type { ARRAY, BOOLEAN, COMBO, INTEGER, STRING }; + +public: + explicit MesonOptionBase(QString name, QString description, Section section); + virtual ~MesonOptionBase(); + + virtual Type type() const = 0; + virtual QString value() const = 0; + virtual QString initialValue() const = 0; + virtual void reset() = 0; + + QString name() const; + QString description() const; + Section section() const; + + QString mesonArg() const; + bool isUpdated() const; + + static OPT_PTR fromJSON(QJsonObject const& obj); + +private: + QString m_name; + QString m_description; + Section m_section; +}; + +class MesonOptionArray : public MesonOptionBase +{ +public: + MesonOptionArray(QString name, QString description, Section section, QStringList value); + + MesonOptionBase::Type type() const override; + QString value() const override; + QString initialValue() const override; + void reset() override; + + QStringList rawValue() const; + void setValue(QStringList val); + +private: + QStringList m_value; + QStringList m_initialValue; +}; + +class MesonOptionBool : public MesonOptionBase +{ +public: + MesonOptionBool(QString name, QString description, Section section, bool value); + + MesonOptionBase::Type type() const override; + QString value() const override; + QString initialValue() const override; + void reset() override; + + bool rawValue() const; + void setValue(bool val); + +private: + bool m_value; + bool m_initialValue; +}; + +class MesonOptionCombo : public MesonOptionBase +{ +public: + MesonOptionCombo(QString name, QString description, Section section, QString value, QStringList choices); + + MesonOptionBase::Type type() const override; + QString value() const override; + QString initialValue() const override; + void reset() override; + + QString rawValue() const; + void setValue(QString val); + QStringList choices() const; + +private: + QString m_value; + QString m_initialValue; + QStringList m_choices; +}; + +class MesonOptionInteger : public MesonOptionBase +{ +public: + MesonOptionInteger(QString name, QString description, Section section, int value); + + MesonOptionBase::Type type() const override; + QString value() const override; + QString initialValue() const override; + void reset() override; + + int rawValue() const; + void setValue(int val); + +private: + int m_value; + int m_initialValue; +}; + +class MesonOptionString : public MesonOptionBase +{ +public: + MesonOptionString(QString name, QString description, Section section, QString value); + + MesonOptionBase::Type type() const override; + QString value() const override; + QString initialValue() const override; + void reset() override; + + QString rawValue() const; + void setValue(QString val); + +private: + QString m_value; + QString m_initialValue; +}; + +/*! + * Container class for all meson project options. + */ +class MesonOptions +{ +public: + explicit MesonOptions(QJsonArray const& arr); + + int numChanged() const; + QStringList getMesonArgs() const; + void print() const; + + QVector options(); + +private: + QVector m_options; + + void fromJSON(QJsonArray const& arr); +}; diff --git a/plugins/meson/mesonoptions.cpp b/plugins/meson/mesonoptions.cpp new file mode 100644 --- /dev/null +++ b/plugins/meson/mesonoptions.cpp @@ -0,0 +1,437 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mesonoptions.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static const QHash STRING2SECTION = { + { QStringLiteral("core"), MesonOptionBase::CORE }, + { QStringLiteral("backend"), MesonOptionBase::BACKEND }, + { QStringLiteral("base"), MesonOptionBase::BASE }, + { QStringLiteral("compiler"), MesonOptionBase::COMPILER }, + { QStringLiteral("directory"), MesonOptionBase::DIRECTORY }, + { QStringLiteral("user"), MesonOptionBase::USER }, + { QStringLiteral("test"), MesonOptionBase::TEST }, +}; + +static const QHash STRING2TYPE = { + { QStringLiteral("array"), MesonOptionBase::ARRAY }, { QStringLiteral("boolean"), MesonOptionBase::BOOLEAN }, + { QStringLiteral("combo"), MesonOptionBase::COMBO }, { QStringLiteral("integer"), MesonOptionBase::INTEGER }, + { QStringLiteral("string"), MesonOptionBase::STRING }, +}; + +MesonOptions::MesonOptions(QJsonArray const& arr) +{ + fromJSON(arr); +} + +// Option constructors + +MesonOptionBase::MesonOptionBase(QString name, QString description, MesonOptionBase::Section section) + : m_name(name) + , m_description(description) + , m_section(section) +{ +} + +MesonOptionArray::MesonOptionArray(QString name, QString description, MesonOptionBase::Section section, + QStringList value) + : MesonOptionBase(name, description, section) + , m_value(value) + , m_initialValue(value) +{ +} + +MesonOptionBool::MesonOptionBool(QString name, QString description, MesonOptionBase::Section section, bool value) + : MesonOptionBase(name, description, section) + , m_value(value) + , m_initialValue(value) +{ +} + +MesonOptionCombo::MesonOptionCombo(QString name, QString description, MesonOptionBase::Section section, QString value, + QStringList choices) + : MesonOptionBase(name, description, section) + , m_value(value) + , m_initialValue(value) + , m_choices(choices) +{ +} + +MesonOptionInteger::MesonOptionInteger(QString name, QString description, MesonOptionBase::Section section, int value) + : MesonOptionBase(name, description, section) + , m_value(value) + , m_initialValue(value) +{ +} + +MesonOptionString::MesonOptionString(QString name, QString description, MesonOptionBase::Section section, QString value) + : MesonOptionBase(name, description, section) + , m_value(value) + , m_initialValue(value) +{ +} + +QStringList MesonOptionCombo::choices() const +{ + return m_choices; +} + +// Type functions + +MesonOptionBase::Type MesonOptionArray::type() const +{ + return ARRAY; +} + +MesonOptionBase::Type MesonOptionBool::type() const +{ + return BOOLEAN; +} + +MesonOptionBase::Type MesonOptionCombo::type() const +{ + return COMBO; +} + +MesonOptionBase::Type MesonOptionInteger::type() const +{ + return INTEGER; +} + +MesonOptionBase::Type MesonOptionString::type() const +{ + return STRING; +} + +// Value functions + +QString MesonOptionArray::value() const +{ + QStringList tmp; + tmp.reserve(m_value.size()); + transform(begin(m_value), end(m_value), back_inserter(tmp), + [](QString const& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); }); + return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]"); +} + +QString MesonOptionBool::value() const +{ + return m_value ? QStringLiteral("true") : QStringLiteral("false"); +} + +QString MesonOptionCombo::value() const +{ + return m_value; +} + +QString MesonOptionInteger::value() const +{ + return QString::number(m_value); +} + +QString MesonOptionString::value() const +{ + return m_value; +} + +// Initial value functions + +QString MesonOptionArray::initialValue() const +{ + QStringList tmp; + tmp.reserve(m_initialValue.size()); + transform(begin(m_initialValue), end(m_initialValue), back_inserter(tmp), + [](QString const& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); }); + return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]"); +} + +QString MesonOptionBool::initialValue() const +{ + return m_initialValue ? QStringLiteral("true") : QStringLiteral("false"); +} + +QString MesonOptionCombo::initialValue() const +{ + return m_initialValue; +} + +QString MesonOptionInteger::initialValue() const +{ + return QString::number(m_initialValue); +} + +QString MesonOptionString::initialValue() const +{ + return m_initialValue; +} + +// Reset functions + +void MesonOptionArray::reset() +{ + m_value = m_initialValue; +} + +void MesonOptionBool::reset() +{ + m_value = m_initialValue; +} + +void MesonOptionCombo::reset() +{ + m_value = m_initialValue; +} + +void MesonOptionInteger::reset() +{ + m_value = m_initialValue; +} + +void MesonOptionString::reset() +{ + m_value = m_initialValue; +} + +// Raw value functions + +QStringList MesonOptionArray::rawValue() const +{ + return m_value; +} + +bool MesonOptionBool::rawValue() const +{ + return m_value; +} + +QString MesonOptionCombo::rawValue() const +{ + return m_value; +} + +int MesonOptionInteger::rawValue() const +{ + return m_value; +} + +QString MesonOptionString::rawValue() const +{ + return m_value; +} + +// Set value functions + +void MesonOptionArray::setValue(QStringList val) +{ + m_value = val; +} + +void MesonOptionBool::setValue(bool val) +{ + m_value = val; +} + +void MesonOptionCombo::setValue(QString val) +{ + m_value = val; +} + +void MesonOptionInteger::setValue(int val) +{ + m_value = val; +} + +void MesonOptionString::setValue(QString val) +{ + m_value = val; +} + +// Base option functions + +MesonOptionBase::~MesonOptionBase() {} + +QString MesonOptionBase::name() const +{ + return m_name; +} + +QString MesonOptionBase::description() const +{ + return m_description; +} + +MesonOptionBase::Section MesonOptionBase::section() const +{ + return m_section; +} + +QString MesonOptionBase::mesonArg() const +{ + return QStringLiteral("-D") + m_name + QStringLiteral("=") + value(); +} + +bool MesonOptionBase::isUpdated() const +{ + return value() != initialValue(); +} + +OPT_PTR MesonOptionBase::fromJSON(const QJsonObject& obj) +{ + auto nameJV = obj[QStringLiteral("name")]; + auto descriptionJV = obj[QStringLiteral("description")]; + auto sectionJV = obj[QStringLiteral("section")]; + auto typeJV = obj[QStringLiteral("type")]; + auto valueJV = obj[QStringLiteral("value")]; + + // Check all values + for (auto const& i : { nameJV, descriptionJV, sectionJV, typeJV }) { + if (!i.isString()) { + qCWarning(KDEV_Meson) << "OPT: Base type validation failed" << typeJV.toString(); + return nullptr; + } + } + + auto sectionIter = STRING2SECTION.find(sectionJV.toString()); + auto typeIter = STRING2TYPE.find(typeJV.toString()); + + if (sectionIter == end(STRING2SECTION) || typeIter == end(STRING2TYPE)) { + qCWarning(KDEV_Meson) << "OPT: Unknown type or section " << typeJV.toString() << " / " << sectionJV.toString(); + return nullptr; + } + + Section section = *sectionIter; + Type type = *typeIter; + QString name = nameJV.toString(); + QString description = descriptionJV.toString(); + + switch (type) { + case ARRAY: { + if (!valueJV.isArray()) { + return nullptr; + } + + QJsonArray raw = valueJV.toArray(); + QStringList values; + values.reserve(raw.size()); + transform(begin(raw), end(raw), back_inserter(values), [](QJsonValue const& v) { return v.toString(); }); + return make_shared(name, description, section, values); + } + + case BOOLEAN: + if (!valueJV.isBool()) { + return nullptr; + } + return make_shared(name, description, section, valueJV.toBool()); + + case COMBO: { + auto choicesJV = obj[QStringLiteral("choices")]; + if (!valueJV.isString() || !choicesJV.isArray()) { + return nullptr; + } + + QJsonArray raw = choicesJV.toArray(); + QStringList choices; + choices.reserve(raw.size()); + transform(begin(raw), end(raw), back_inserter(choices), [](QJsonValue const& v) { return v.toString(); }); + return make_shared(name, description, section, valueJV.toString(), choices); + } + + case INTEGER: + if (!valueJV.isDouble()) { + return nullptr; + } + return make_shared(name, description, section, valueJV.toInt()); + + case STRING: + if (!valueJV.isString()) { + return nullptr; + } + return make_shared(name, description, section, valueJV.toString()); + } + + qCWarning(KDEV_Meson) << "OPT: Unknown type " << typeJV.toString(); + return nullptr; +} + +int MesonOptions::numChanged() const +{ + int sum = 0; + for(auto i : m_options) { + if (i && i->isUpdated()) { + ++sum; + } + } + return sum; +} + + +QStringList MesonOptions::getMesonArgs() const +{ + QStringList result; + result.reserve(m_options.size()); + + for(auto i : m_options) { + if (i && i->isUpdated()) { + result << i->mesonArg(); + } + } + return result; +} + +void MesonOptions::fromJSON(const QJsonArray& arr) +{ + m_options.clear(); + m_options.reserve(arr.size()); + + for (QJsonValue const& i : arr) { + if (!i.isObject()) { + continue; + } + + auto ptr = MesonOptionBase::fromJSON(i.toObject()); + if (ptr) { + m_options += ptr; + } else { + qCWarning(KDEV_Meson) << "OPT: Failed to parse " << i.toObject(); + } + } +} + +void MesonOptions::print() const +{ + for (auto const& i : m_options) { + qCDebug(KDEV_Meson) << i->name() << " = " << i->value() << " [" << i->type() << "] -- " << i->section(); + } +} + +QVector MesonOptions::options() +{ + return m_options; +} diff --git a/plugins/meson/settings/mesonadvancedsettings.h b/plugins/meson/settings/mesonadvancedsettings.h --- a/plugins/meson/settings/mesonadvancedsettings.h +++ b/plugins/meson/settings/mesonadvancedsettings.h @@ -46,6 +46,8 @@ void setSupportedBackends(QStringList const& backends); + bool hasMesonChanged(); + Q_SIGNALS: void configChanged(); @@ -55,4 +57,5 @@ private: Ui::MesonAdvancedSettings* m_ui = nullptr; QStringList m_backendList; + KDevelop::Path m_mesonOldPath; }; diff --git a/plugins/meson/settings/mesonadvancedsettings.cpp b/plugins/meson/settings/mesonadvancedsettings.cpp --- a/plugins/meson/settings/mesonadvancedsettings.cpp +++ b/plugins/meson/settings/mesonadvancedsettings.cpp @@ -64,3 +64,14 @@ { emit configChanged(); } + +/// Check if meson has changed since the last call +bool MesonAdvancedSettings::hasMesonChanged() +{ + if(m_mesonOldPath != Path(m_ui->i_mesonExe->url())) { + m_mesonOldPath = Path(m_ui->i_mesonExe->url()); // Reset + return true; + } + + return false; +} diff --git a/plugins/meson/settings/mesonadvancedsettings.ui b/plugins/meson/settings/mesonadvancedsettings.ui --- a/plugins/meson/settings/mesonadvancedsettings.ui +++ b/plugins/meson/settings/mesonadvancedsettings.ui @@ -76,6 +76,9 @@ + + Changing this will reset the build options + Extra meson configuration arguments @@ -114,16 +117,16 @@ QLineEdit
klineedit.h
- - KComboBox - QComboBox -
kcombobox.h
-
KUrlRequester QWidget
kurlrequester.h
+ + KComboBox + QComboBox +
kcombobox.h
+
diff --git a/plugins/meson/settings/mesonconfigpage.h b/plugins/meson/settings/mesonconfigpage.h --- a/plugins/meson/settings/mesonconfigpage.h +++ b/plugins/meson/settings/mesonconfigpage.h @@ -21,6 +21,7 @@ #include #include "mesonconfig.h" +#include "mesonoptions.h" namespace KDevelop { @@ -53,6 +54,7 @@ void emitChanged(); private: + void checkStatus(); void updateUI(); void readUI(); void writeConfig(); @@ -64,4 +66,6 @@ Meson::MesonConfig m_config; Meson::BuildDir m_current; bool m_configChanged = false; + + MESON_OPT_PTR m_options = nullptr; }; diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -19,6 +19,7 @@ #include "mesonconfigpage.h" #include "mesonbuilder.h" +#include "mesonintrospectjob.h" #include "mesonjob.h" #include "mesonmanager.h" #include "mesonnewbuilddir.h" @@ -91,14 +92,26 @@ if (m_config.currentIndex >= 0 && m_configChanged) { QList joblist; + auto options = m_ui->options->options(); + if (!options) { + qCWarning(KDEV_Meson) << "Options is nullptr. Can not update meson config"; + return; + } + + QStringList mesonArgs = options->getMesonArgs(); + if (mesonArgs.empty()) { + qCDebug(KDEV_Meson) << "Config has not changed --> nothing has to be updated"; + return; + } + // Check if a configuration is required auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); if (status != MesonBuilder::MESON_CONFIGURED) { - joblist << new MesonJob(m_current, m_project, MesonJob::CONFIGURE, {}, nullptr); + joblist << new MesonJob(m_current, m_project, MesonJob::CONFIGURE, mesonArgs, nullptr); } - joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, {}, nullptr); - joblist << new MesonJob(m_current, m_project, MesonJob::RE_CONFIGURE, {}, nullptr); + joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, mesonArgs, nullptr); + joblist << m_ui->options->repopulateFromBuildDir(m_project, m_current); KJob* job = new ExecuteCompositeJob(nullptr, joblist); connect(job, &KJob::result, this, [this]() { setDisabled(false); @@ -119,6 +132,7 @@ m_current.mesonArgs.clear(); m_current.mesonBackend = mgr->defaultMesonBackend(); m_current.mesonExecutable = mgr->findMeson(); + m_ui->options->resetAll(); updateUI(); } @@ -141,28 +155,12 @@ qCDebug(KDEV_Meson) << "Resetting changes for build dir " << m_current.buildDir; m_current = m_config.buildDirs[m_config.currentIndex]; + m_ui->options->repopulateFromBuildDir(m_project, m_current)->start(); updateUI(); } -void MesonConfigPage::updateUI() +void MesonConfigPage::checkStatus() { - m_ui->i_buildType->setCurrentIndex(1); - - QStringList buildTypes = { QStringLiteral("plain"), QStringLiteral("debug"), QStringLiteral("debugoptimized"), - QStringLiteral("release"), QStringLiteral("minsize"), QStringLiteral("custom") }; - - m_ui->i_buildType->clear(); - m_ui->i_buildType->addItems(buildTypes); - m_ui->i_buildType->setCurrentIndex(std::max(0, buildTypes.indexOf(m_current.buildType))); - - m_ui->i_installPrefix->setUrl(m_current.installPrefix.toUrl()); - - auto aConf = m_ui->advanced->getConfig(); - aConf.args = m_current.mesonArgs; - aConf.backend = m_current.mesonBackend; - aConf.meson = m_current.mesonExecutable; - m_ui->advanced->setConfig(aConf); - // Get the config build dir status auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); auto setStatus = [this](QString const& msg, int color) -> void { @@ -213,13 +211,43 @@ setStatus(i18n("Something went verry wrong. This is a bug"), 2); break; } + + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + int numChanged = 0; + auto options = m_ui->options->options(); + + if (options) { + numChanged = options->numChanged(); + } + + if (numChanged == 0) { + role = KColorScheme::NormalText; + m_ui->l_changed->setText(i18n("No changes")); + } else { + role = KColorScheme::NeutralText; + m_ui->l_changed->setText(i18n("%1 options changed", numChanged)); + } + + QPalette pal = m_ui->l_changed->palette(); + pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + m_ui->l_changed->setPalette(pal); +} + +void MesonConfigPage::updateUI() +{ + auto aConf = m_ui->advanced->getConfig(); + aConf.args = m_current.mesonArgs; + aConf.backend = m_current.mesonBackend; + aConf.meson = m_current.mesonExecutable; + m_ui->advanced->setConfig(aConf); + + checkStatus(); } void MesonConfigPage::readUI() { qCDebug(KDEV_Meson) << "Reading current build configuration from the UI " << m_current.buildDir.toLocalFile(); - m_current.installPrefix = Path(m_ui->i_installPrefix->url()); - m_current.buildType = m_ui->i_buildType->currentText(); auto aConf = m_ui->advanced->getConfig(); m_current.mesonArgs = aConf.args; @@ -230,9 +258,10 @@ void MesonConfigPage::setWidgetsDisabled(bool disabled) { m_ui->advanced->setDisabled(disabled); - m_ui->c_01_basic->setDisabled(disabled); - m_ui->c_02_buildConfig->setDisabled(disabled); + m_ui->i_buildDirs->setDisabled(disabled); + m_ui->b_addDir->setDisabled(disabled); m_ui->b_rmDir->setDisabled(disabled); + m_ui->options->setDisabled(disabled); } void MesonConfigPage::addBuildDir() @@ -258,7 +287,7 @@ setWidgetsDisabled(true); writeConfig(); - KJob* job = bld->configure(m_project); + KJob* job = bld->configure(m_project, m_current, newBD.mesonArgs()); connect(job, &KJob::result, this, [this]() { reset(); }); job->start(); } @@ -301,6 +330,7 @@ void MesonConfigPage::emitChanged() { m_configChanged = true; + checkStatus(); emit changed(); } diff --git a/plugins/meson/settings/mesonconfigpage.ui b/plugins/meson/settings/mesonconfigpage.ui --- a/plugins/meson/settings/mesonconfigpage.ui +++ b/plugins/meson/settings/mesonconfigpage.ui @@ -6,13 +6,13 @@ 0 0 - 587 - 499 + 670 + 500 - + @@ -48,131 +48,48 @@ - - - Basic configuration - - - - - - Build type: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly - - - Full path to the installation prefix - - - - - - - Installation prefix: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - true - - - Build configuration - - - - - - true - - - - - 0 - 0 - 555 - 237 - - - - - - - false - - - TODO -- Implement in seperate UI file - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - + - - - Status message... - - + + + + + Status message... + + + + + + + Num changed + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + +
- KComboBox - QComboBox -
kcombobox.h
-
- - KUrlRequester + MesonAdvancedSettings QWidget -
kurlrequester.h
+
settings/mesonadvancedsettings.h
+ 1 + + configChanged() +
- MesonAdvancedSettings + MesonOptionsView QWidget -
settings/mesonadvancedsettings.h
+
settings/mesonoptionsview.h
1 configChanged() @@ -245,22 +162,6 @@ - - i_installPrefix - textChanged(QString) - MesonConfigPage - emitChanged() - - - 351 - 133 - - - 293 - 249 - - - advanced configChanged() @@ -278,17 +179,17 @@ - i_buildType - currentIndexChanged(int) + options + configChanged() MesonConfigPage emitChanged() - 351 - 95 + 334 + 188 - 293 + 334 249 diff --git a/plugins/meson/settings/mesonadvancedsettings.h b/plugins/meson/settings/mesonlisteditor.h copy from plugins/meson/settings/mesonadvancedsettings.h copy to plugins/meson/settings/mesonlisteditor.h --- a/plugins/meson/settings/mesonadvancedsettings.h +++ b/plugins/meson/settings/mesonlisteditor.h @@ -1,5 +1,5 @@ /* This file is part of KDevelop - Copyright 2018 Daniel Mensinger + Copyright 2019 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -19,40 +19,32 @@ #pragma once -#include "util/path.h" -#include +#include -namespace Ui -{ -class MesonAdvancedSettings; +namespace Ui { + class MesonListEditor; } -class MesonAdvancedSettings : public QWidget -{ +class MesonListEditor : public QDialog { Q_OBJECT -public: - struct Data { - QString backend; - QString args; - KDevelop::Path meson; - }; public: - explicit MesonAdvancedSettings(QWidget* parent = nullptr); - ~MesonAdvancedSettings() override; - - Data getConfig() const; - void setConfig(Data const& conf); + explicit MesonListEditor(QStringList content, QWidget *parent); + virtual ~MesonListEditor(); - void setSupportedBackends(QStringList const& backends); - -Q_SIGNALS: - void configChanged(); + QStringList content() const; public Q_SLOTS: - void updated(); + void add(); + void remove(); + void first(); + void up(); + void down(); + void last(); + void currentItemChanged(); private: - Ui::MesonAdvancedSettings* m_ui = nullptr; - QStringList m_backendList; + Ui::MesonListEditor *m_ui = nullptr; + + void moveItem(int src, int dst); }; diff --git a/plugins/meson/settings/mesonlisteditor.cpp b/plugins/meson/settings/mesonlisteditor.cpp new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonlisteditor.cpp @@ -0,0 +1,145 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mesonlisteditor.h" +#include "ui_mesonlisteditor.h" + +QListWidgetItem* genItem(QString const& label) +{ + QListWidgetItem* item = new QListWidgetItem(label); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled); + return item; +} + +MesonListEditor::MesonListEditor(QStringList content, QWidget* parent) + : QDialog(parent) +{ + m_ui = new Ui::MesonListEditor; + m_ui->setupUi(this); + + for (auto const& i : content) { + m_ui->array->addItem(genItem(i)); + } + + currentItemChanged(); +} + +MesonListEditor::~MesonListEditor() +{ + if (m_ui) { + delete m_ui; + } +} + +QStringList MesonListEditor::content() const +{ + QStringList content; + content.reserve(m_ui->array->count()); + for (int i = 0; i < m_ui->array->count(); ++i) { + content << m_ui->array->item(i)->text(); + } + return content; +} + +void MesonListEditor::add() +{ + auto* item = genItem(i18n("")); + m_ui->array->addItem(item); + m_ui->array->setCurrentItem(item); + m_ui->array->editItem(item); +} + +void MesonListEditor::moveItem(int src, int dst) +{ + auto* item = m_ui->array->takeItem(src); + if (!item) { + return; + } + + m_ui->array->insertItem(dst, item); + m_ui->array->setCurrentItem(item); +} + +void MesonListEditor::remove() +{ + qDeleteAll(m_ui->array->selectedItems()); +} + +void MesonListEditor::first() +{ + int row = m_ui->array->currentRow(); + moveItem(row, 0); +} + +void MesonListEditor::up() +{ + int row = m_ui->array->currentRow(); + moveItem(row, row - 1); +} + +void MesonListEditor::down() +{ + int row = m_ui->array->currentRow(); + moveItem(row, row + 1); +} + +void MesonListEditor::last() +{ + int row = m_ui->array->currentRow(); + moveItem(row, m_ui->array->count() - 1); +} + +void MesonListEditor::currentItemChanged() +{ + auto* current = m_ui->array->currentItem(); + if (!current || m_ui->array->count() == 0) { + m_ui->b_first->setDisabled(true); + m_ui->b_up->setDisabled(true); + m_ui->b_down->setDisabled(true); + m_ui->b_last->setDisabled(true); + m_ui->b_del->setDisabled(true); + return; + } + + int row = m_ui->array->row(current); + + m_ui->b_del->setDisabled(false); + + if (m_ui->array->count() < 2) { + m_ui->b_first->setDisabled(true); + m_ui->b_up->setDisabled(true); + m_ui->b_down->setDisabled(true); + m_ui->b_last->setDisabled(true); + } else if (row == 0) { + m_ui->b_first->setDisabled(true); + m_ui->b_up->setDisabled(true); + m_ui->b_down->setDisabled(false); + m_ui->b_last->setDisabled(false); + } else if (row >= m_ui->array->count() - 1) { + m_ui->b_first->setDisabled(false); + m_ui->b_up->setDisabled(false); + m_ui->b_down->setDisabled(true); + m_ui->b_last->setDisabled(true); + } else { + m_ui->b_first->setDisabled(false); + m_ui->b_up->setDisabled(false); + m_ui->b_down->setDisabled(false); + m_ui->b_last->setDisabled(false); + } +} diff --git a/plugins/meson/settings/mesonlisteditor.ui b/plugins/meson/settings/mesonlisteditor.ui new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonlisteditor.ui @@ -0,0 +1,294 @@ + + + MesonListEditor + + + + 0 + 0 + 400 + 450 + + + + Dialog + + + + + + QFrame::Sunken + + + true + + + true + + + true + + + + + + + + + New + + + + + + + + + + Delete + + + + + + + + + + Qt::Horizontal + + + + + + + First + + + + + + + + + + Up + + + + + + + + + + Down + + + + + + + + + + Last + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + + + buttonBox + accepted() + MesonListEditor + accept() + + + 347 + 404 + + + 199 + 224 + + + + + buttonBox + rejected() + MesonListEditor + reject() + + + 347 + 404 + + + 199 + 224 + + + + + b_new + clicked() + MesonListEditor + add() + + + 347 + 24 + + + 199 + 224 + + + + + b_del + clicked() + MesonListEditor + remove() + + + 347 + 65 + + + 199 + 224 + + + + + b_up + clicked() + MesonListEditor + up() + + + 347 + 115 + + + 199 + 224 + + + + + b_down + clicked() + MesonListEditor + down() + + + 347 + 156 + + + 199 + 224 + + + + + array + currentItemChanged(QListWidgetItem*,QListWidgetItem*) + MesonListEditor + currentItemChanged() + + + 150 + 224 + + + 199 + 224 + + + + + b_first + clicked() + MesonListEditor + first() + + + 347 + 115 + + + 199 + 224 + + + + + b_last + clicked() + MesonListEditor + last() + + + 347 + 238 + + + 199 + 224 + + + + + + add() + remove() + up() + down() + currentItemChanged() + first() + last() + + diff --git a/plugins/meson/settings/mesonnewbuilddir.h b/plugins/meson/settings/mesonnewbuilddir.h --- a/plugins/meson/settings/mesonnewbuilddir.h +++ b/plugins/meson/settings/mesonnewbuilddir.h @@ -45,6 +45,7 @@ bool isConfigValid() const; Meson::BuildDir currentConfig() const; + QStringList mesonArgs() const; private Q_SLOTS: void resetFields(); diff --git a/plugins/meson/settings/mesonnewbuilddir.cpp b/plugins/meson/settings/mesonnewbuilddir.cpp --- a/plugins/meson/settings/mesonnewbuilddir.cpp +++ b/plugins/meson/settings/mesonnewbuilddir.cpp @@ -96,18 +96,6 @@ m_ui->i_buildDir->setUrl(buildDirPath.toUrl()); - // Init build type - // TODO use introspection once https://github.com/mesonbuild/meson/pull/4564 is merged - QStringList buildTypes = { QStringLiteral("plain"), QStringLiteral("debug"), QStringLiteral("debugoptimized"), - QStringLiteral("release"), QStringLiteral("minsize"), QStringLiteral("custom") }; - - m_ui->i_buildType->clear(); - m_ui->i_buildType->addItems(buildTypes); - m_ui->i_buildType->setCurrentIndex(std::max(0, buildTypes.indexOf(QStringLiteral("debug")))); - - // Install prefix - m_ui->i_installPrefix->clear(); - // Extra args aConf.args.clear(); @@ -152,7 +140,7 @@ Path buildDir = Path(m_ui->i_buildDir->url()); QFileInfo mesonExe(advanced.meson.toLocalFile()); - if (!mesonExe.exists() || !mesonExe.isExecutable() + if (!mesonExe.exists() || !mesonExe.isExecutable() || !mesonExe.isFile() || !mesonExe.permission(QFileDevice::ReadUser | QFileDevice::ExeUser)) { setStatus(i18n("Specified meson executable does not exist"), false); return; @@ -183,6 +171,11 @@ setStatus(i18n("You have reached unreachable code. This is a bug"), false); break; } + + bool mesonHasChanged = m_ui->advanced->hasMesonChanged(); // Outside if to prevent lazy evaluation + if (!m_ui->options->options() || mesonHasChanged) { + m_ui->options->repopulateFromMesonFile(m_project->path(), advanced.meson)->start(); + } } Meson::BuildDir MesonNewBuildDir::currentConfig() const @@ -196,15 +189,23 @@ auto advanced = m_ui->advanced->getConfig(); buildDir.buildDir = Path(m_ui->i_buildDir->url()); - buildDir.buildType = m_ui->i_buildType->currentText(); - buildDir.installPrefix = Path(m_ui->i_installPrefix->url()); buildDir.mesonArgs = advanced.args; buildDir.mesonBackend = advanced.backend; buildDir.mesonExecutable = advanced.meson; return buildDir; } +QStringList MesonNewBuildDir::mesonArgs() const +{ + auto options = m_ui->options->options(); + if(!options) { + return {}; + } + + return options->getMesonArgs(); +} + bool MesonNewBuildDir::isConfigValid() const { return m_configIsValid; diff --git a/plugins/meson/settings/mesonnewbuilddir.ui b/plugins/meson/settings/mesonnewbuilddir.ui --- a/plugins/meson/settings/mesonnewbuilddir.ui +++ b/plugins/meson/settings/mesonnewbuilddir.ui @@ -6,141 +6,49 @@ 0 0 - 650 - 625 + 670 + 700 - - - Basic configuration - - - - - - Build directory: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Installation prefix: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly - - - QFileDialog::AcceptSave - - - Full Path to the new build directory - - - - - - - Build type: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly - - - Full path to the installation prefix - - - - - + + + + + Build directory: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + QFileDialog::AcceptSave + + + Full Path to the new build directory + + + + - - - true - - - Build configuration - - - - - - true - - - - - 0 - 0 - 618 - 353 - - - - - - - false - - - TODO -- Implement in seperate UI file - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - + - + @@ -166,11 +74,6 @@ - - KComboBox - QComboBox -
kcombobox.h
-
KUrlRequester QWidget @@ -185,6 +88,15 @@ configChanged()
+ + MesonOptionsView + QWidget +
settings/mesonoptionsview.h
+ 1 + + configChanged() + +
@@ -221,34 +133,34 @@ - i_buildDir - textChanged(QString) + advanced + configChanged() MesonNewBuildDir updated() - 383 - 55 + 324 + 571 324 312 - advanced + options configChanged() MesonNewBuildDir updated() - 324 - 571 + 334 + 263 - 324 - 312 + 334 + 349 diff --git a/plugins/meson/settings/mesonoptionbaseview.h b/plugins/meson/settings/mesonoptionbaseview.h new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonoptionbaseview.h @@ -0,0 +1,150 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "mesonoptions.h" +#include + +namespace Ui +{ +class MesonOptionBaseView; +} + +class QCheckBox; +class QComboBox; +class QLineEdit; +class QPushButton; +class QSpinBox; + +class MesonOptionBaseView : public QWidget +{ + Q_OBJECT + +public: + explicit MesonOptionBaseView(OPT_PTR option, QWidget* parent); + virtual ~MesonOptionBaseView(); + + int nameWidth(); + void setMinNameWidth(int width); + + virtual MesonOptionBase* option() = 0; + virtual void updateInput() = 0; + + void setInputWidget(QWidget* input); + +protected: + void setChanged(bool changed); + +public Q_SLOTS: + void reset(); + +Q_SIGNALS: + void configChanged(); + +private: + Ui::MesonOptionBaseView* m_ui = nullptr; +}; + +class MesonOptionArrayView : public MesonOptionBaseView +{ + Q_OBJECT + +public: + MesonOptionArrayView(OPT_PTR option, QWidget* parent); + + MesonOptionBase* option() override; + void updateInput() override; + +private: + std::shared_ptr m_option; + QPushButton* m_input = nullptr; +}; + +class MesonOptionBoolView : public MesonOptionBaseView +{ + Q_OBJECT + +public: + MesonOptionBoolView(OPT_PTR option, QWidget* parent); + + MesonOptionBase* option() override; + void updateInput() override; + +public Q_SLOTS: + void updated(); + +private: + std::shared_ptr m_option; + QCheckBox* m_input = nullptr; +}; + +class MesonOptionComboView : public MesonOptionBaseView +{ + Q_OBJECT + +public: + MesonOptionComboView(OPT_PTR option, QWidget* parent); + + MesonOptionBase* option() override; + void updateInput() override; + +public Q_SLOTS: + void updated(); + +private: + std::shared_ptr m_option; + QComboBox* m_input = nullptr; +}; + +class MesonOptionIntegerView : public MesonOptionBaseView +{ + Q_OBJECT + +public: + MesonOptionIntegerView(OPT_PTR option, QWidget* parent); + + MesonOptionBase* option() override; + void updateInput() override; + +public Q_SLOTS: + void updated(); + +private: + std::shared_ptr m_option; + QSpinBox* m_input = nullptr; +}; + +class MesonOptionStringView : public MesonOptionBaseView +{ + Q_OBJECT + +public: + MesonOptionStringView(OPT_PTR option, QWidget* parent); + + MesonOptionBase* option() override; + void updateInput() override; + +public Q_SLOTS: + void updated(); + +private: + std::shared_ptr m_option; + QLineEdit* m_input = nullptr; +}; diff --git a/plugins/meson/settings/mesonoptionbaseview.cpp b/plugins/meson/settings/mesonoptionbaseview.cpp new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonoptionbaseview.cpp @@ -0,0 +1,283 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mesonoptionbaseview.h" +#include "mesonlisteditor.h" +#include "ui_mesonoptionbaseview.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Helper class for RAII signal blocking +class SignalBlocker +{ +public: + SignalBlocker(QWidget* widget) + : m_widget(widget) + { + if (m_widget) { + m_widget->blockSignals(true); + } + } + + ~SignalBlocker() + { + if (m_widget) { + m_widget->blockSignals(false); + } + } + +private: + QWidget* m_widget = nullptr; +}; + +MesonOptionBaseView::MesonOptionBaseView(OPT_PTR option, QWidget* parent) + : QWidget(parent) +{ + Q_ASSERT(option); + + m_ui = new Ui::MesonOptionBaseView; + m_ui->setupUi(this); + + m_ui->l_name->setText(option->name() + QStringLiteral(":")); + m_ui->l_name->setToolTip(option->description()); + setToolTip(option->description()); +} + +MesonOptionBaseView::~MesonOptionBaseView() +{ + if (m_ui) { + delete m_ui; + } +} + +// Base class functions + +int MesonOptionBaseView::nameWidth() +{ + return m_ui->l_name->fontMetrics().boundingRect(m_ui->l_name->text()).width() + 25; +} + +void MesonOptionBaseView::setMinNameWidth(int width) +{ + m_ui->l_name->setMinimumWidth(width); +} + +void MesonOptionBaseView::setInputWidget(QWidget* input) +{ + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(input->sizePolicy().hasHeightForWidth()); + input->setSizePolicy(sizePolicy); + input->setToolTip(option()->description()); + m_ui->layout->insertWidget(1, input); + updateInput(); + setChanged(false); +} + +void MesonOptionBaseView::reset() +{ + option()->reset(); + updateInput(); + setChanged(false); +} + +void MesonOptionBaseView::setChanged(bool changed) +{ + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + + if (changed) { + m_ui->l_name->setStyleSheet(QStringLiteral("font-weight: bold")); + m_ui->b_reset->setDisabled(false); + role = KColorScheme::NeutralText; + } else { + m_ui->l_name->setStyleSheet(QStringLiteral("")); + m_ui->b_reset->setDisabled(true); + role = KColorScheme::NormalText; + } + + QPalette pal = m_ui->l_name->palette(); + pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + m_ui->l_name->setPalette(pal); + emit configChanged(); +} + +// Derived class constructors + +MesonOptionArrayView::MesonOptionArrayView(OPT_PTR option, QWidget* parent) + : MesonOptionBaseView(option, parent) + , m_option(dynamic_pointer_cast(option)) +{ + Q_ASSERT(m_option); + + m_input = new QPushButton(this); + connect(m_input, &QPushButton::clicked, this, [this]() { + MesonListEditor editor(m_option->rawValue(), this); + if (editor.exec() == QDialog::Accepted) { + m_option->setValue(editor.content()); + m_input->setText(m_option->value()); + setChanged(m_option->isUpdated()); + } + }); + setInputWidget(m_input); +} + +MesonOptionBoolView::MesonOptionBoolView(OPT_PTR option, QWidget* parent) + : MesonOptionBaseView(option, parent) + , m_option(dynamic_pointer_cast(option)) +{ + Q_ASSERT(m_option); + + m_input = new QCheckBox(this); + connect(m_input, &QCheckBox::stateChanged, this, &MesonOptionBoolView::updated); + setInputWidget(m_input); +} + +MesonOptionComboView::MesonOptionComboView(OPT_PTR option, QWidget* parent) + : MesonOptionBaseView(option, parent) + , m_option(dynamic_pointer_cast(option)) +{ + Q_ASSERT(m_option); + + m_input = new QComboBox(this); + m_input->clear(); + m_input->addItems(m_option->choices()); + m_input->setEditable(false); + connect(m_input, qOverload(&QComboBox::currentIndexChanged), this, &MesonOptionComboView::updated); + setInputWidget(m_input); +} + +MesonOptionIntegerView::MesonOptionIntegerView(OPT_PTR option, QWidget* parent) + : MesonOptionBaseView(option, parent) + , m_option(dynamic_pointer_cast(option)) +{ + Q_ASSERT(m_option); + + m_input = new QSpinBox(this); + m_input->setMinimum(INT32_MIN); + m_input->setMaximum(INT32_MAX); + connect(m_input, qOverload(&QSpinBox::valueChanged), this, &MesonOptionIntegerView::updated); + setInputWidget(m_input); +} + +MesonOptionStringView::MesonOptionStringView(OPT_PTR option, QWidget* parent) + : MesonOptionBaseView(option, parent) + , m_option(dynamic_pointer_cast(option)) +{ + Q_ASSERT(m_option); + + m_input = new QLineEdit(this); + connect(m_input, &QLineEdit::textChanged, this, &MesonOptionStringView::updated); + setInputWidget(m_input); +} + +// Option getters + +MesonOptionBase* MesonOptionArrayView::option() +{ + return m_option.get(); +} + +MesonOptionBase* MesonOptionBoolView::option() +{ + return m_option.get(); +} + +MesonOptionBase* MesonOptionComboView::option() +{ + return m_option.get(); +} + +MesonOptionBase* MesonOptionIntegerView::option() +{ + return m_option.get(); +} + +MesonOptionBase* MesonOptionStringView::option() +{ + return m_option.get(); +} + +// Updaters for the input widget + +void MesonOptionArrayView::updateInput() +{ + SignalBlocker block(m_input); + m_input->setText(m_option->value()); +} + +void MesonOptionBoolView::updateInput() +{ + SignalBlocker block(m_input); + m_input->setCheckState(m_option->rawValue() ? Qt::Checked : Qt::Unchecked); +} + +void MesonOptionComboView::updateInput() +{ + SignalBlocker block(m_input); + m_input->setCurrentText(m_option->rawValue()); +} + +void MesonOptionIntegerView::updateInput() +{ + SignalBlocker block(m_input); + m_input->setValue(m_option->rawValue()); +} + +void MesonOptionStringView::updateInput() +{ + SignalBlocker block(m_input); + m_input->setText(m_option->rawValue()); +} + +// Slots to update the option value + +void MesonOptionBoolView::updated() +{ + m_option->setValue(m_input->isChecked()); + setChanged(m_option->isUpdated()); +} + +void MesonOptionComboView::updated() +{ + m_option->setValue(m_input->currentText()); + setChanged(m_option->isUpdated()); +} + +void MesonOptionIntegerView::updated() +{ + m_option->setValue(m_input->value()); + setChanged(m_option->isUpdated()); +} + +void MesonOptionStringView::updated() +{ + m_option->setValue(m_input->text()); + setChanged(m_option->isUpdated()); +} diff --git a/plugins/meson/settings/mesonoptionbaseview.ui b/plugins/meson/settings/mesonoptionbaseview.ui new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonoptionbaseview.ui @@ -0,0 +1,86 @@ + + + MesonOptionBaseView + + + + 0 + 0 + 500 + 32 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Name: + + + + + + + Reset to default value + + + + + + + .. + + + + + + + + + b_reset + clicked() + MesonOptionBaseView + reset() + + + 475 + 23 + + + 249 + 37 + + + + + + reset() + + diff --git a/plugins/meson/settings/mesonadvancedsettings.h b/plugins/meson/settings/mesonoptionsview.h copy from plugins/meson/settings/mesonadvancedsettings.h copy to plugins/meson/settings/mesonoptionsview.h --- a/plugins/meson/settings/mesonadvancedsettings.h +++ b/plugins/meson/settings/mesonoptionsview.h @@ -1,5 +1,5 @@ /* This file is part of KDevelop - Copyright 2018 Daniel Mensinger + Copyright 2019 Daniel Mensinger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -19,40 +19,58 @@ #pragma once -#include "util/path.h" +#include "mesonoptions.h" +#include #include +#include +#include namespace Ui { -class MesonAdvancedSettings; +class MesonOptionsView; } -class MesonAdvancedSettings : public QWidget +namespace KDevelop +{ +class IProject; +} + +namespace Meson +{ +struct BuildDir; +} + +class MesonOptionBaseView; +class MesonIntrospectJob; +class KJob; + +using OPT_VIEW_PTR = std::shared_ptr; + +class MesonOptionsView : public QWidget { Q_OBJECT -public: - struct Data { - QString backend; - QString args; - KDevelop::Path meson; - }; public: - explicit MesonAdvancedSettings(QWidget* parent = nullptr); - ~MesonAdvancedSettings() override; + explicit MesonOptionsView(QWidget* parent = nullptr); + virtual ~MesonOptionsView(); - Data getConfig() const; - void setConfig(Data const& conf); + void clear(); + void resetAll(); + KJob* repopulateFromBuildDir(KDevelop::IProject* project, Meson::BuildDir const& buildDir); + KJob* repopulateFromMesonFile(KDevelop::Path projectDir, KDevelop::Path mesonExe); - void setSupportedBackends(QStringList const& backends); + MESON_OPT_PTR options(); + +public Q_SLOTS: + void emitChanged(); Q_SIGNALS: void configChanged(); -public Q_SLOTS: - void updated(); - private: - Ui::MesonAdvancedSettings* m_ui = nullptr; - QStringList m_backendList; + Ui::MesonOptionsView* m_ui = nullptr; + QVector m_optViews; + MESON_OPT_PTR m_options; + + KJob* repopulate(MesonIntrospectJob* introJob); }; diff --git a/plugins/meson/settings/mesonoptionsview.cpp b/plugins/meson/settings/mesonoptionsview.cpp new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonoptionsview.cpp @@ -0,0 +1,165 @@ +/* This file is part of KDevelop + Copyright 2019 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mesonoptionsview.h" +#include "mesonconfig.h" +#include "mesonintrospectjob.h" +#include "mesonoptionbaseview.h" +#include "ui_mesonoptionsview.h" +#include +#include +#include + +using namespace std; + +MesonOptionsView::MesonOptionsView(QWidget* parent) + : QWidget(parent) +{ + m_ui = new Ui::MesonOptionsView; + m_ui->setupUi(this); + setDisabled(true); +} + +MesonOptionsView::~MesonOptionsView() +{ + m_optViews.clear(); + if (m_ui) { + delete m_ui; + } +} + +void MesonOptionsView::clear() +{ + setDisabled(true); + m_optViews.clear(); +} + +void MesonOptionsView::resetAll() +{ + for (auto& i : m_optViews) { + i->reset(); + } +} + +KJob* MesonOptionsView::repopulateFromBuildDir(KDevelop::IProject* project, Meson::BuildDir const& buildDir) +{ + return repopulate(new MesonIntrospectJob(project, buildDir, { MesonIntrospectJob::BUILDOPTIONS }, + MesonIntrospectJob::BUILD_DIR, this)); +} + +KJob* MesonOptionsView::repopulateFromMesonFile(KDevelop::Path projectDir, KDevelop::Path mesonExe) +{ + return repopulate(new MesonIntrospectJob(projectDir, mesonExe, { MesonIntrospectJob::BUILDOPTIONS }, this)); +} + +KJob* MesonOptionsView::repopulate(MesonIntrospectJob* introJob) +{ + setDisabled(true); + + connect(introJob, &KJob::result, this, [this, introJob]() { + m_optViews.clear(); + m_options = introJob->buildOptions(); + if (!m_options) { + qCWarning(KDEV_Meson) << "Failed to get introspection data"; + return; + } + + for (auto i : m_options->options()) { + OPT_VIEW_PTR opt = nullptr; + switch (i->type()) { + case MesonOptionBase::ARRAY: + opt = make_shared(i, m_ui->tabWidget); + break; + case MesonOptionBase::BOOLEAN: + opt = make_shared(i, m_ui->tabWidget); + break; + case MesonOptionBase::COMBO: + opt = make_shared(i, m_ui->tabWidget); + break; + case MesonOptionBase::INTEGER: + opt = make_shared(i, m_ui->tabWidget); + break; + case MesonOptionBase::STRING: + opt = make_shared(i, m_ui->tabWidget); + break; + } + + if (!opt) { + qCWarning(KDEV_Meson) << "Unhandled option type " << i->type(); + continue; + } + + QVBoxLayout* layout = nullptr; + + switch (i->section()) { + case MesonOptionBase::CORE: + layout = m_ui->l_core; + break; + case MesonOptionBase::BACKEND: + layout = m_ui->l_backend; + break; + case MesonOptionBase::BASE: + layout = m_ui->l_base; + break; + case MesonOptionBase::COMPILER: + layout = m_ui->l_compiler; + break; + case MesonOptionBase::DIRECTORY: + layout = m_ui->l_directory; + break; + case MesonOptionBase::USER: + layout = m_ui->l_user; + break; + case MesonOptionBase::TEST: + layout = m_ui->l_test; + break; + } + + if (!layout) { + qCWarning(KDEV_Meson) << "Unknown section " << i->section(); + } + + connect(opt.get(), &MesonOptionBaseView::configChanged, this, &MesonOptionsView::emitChanged); + + // Insert at count() - 1 to keep the stretch at the bottom + layout->insertWidget(m_ui->l_test->count() - 1, opt.get()); + m_optViews << opt; + } + + auto maxEl = max_element(begin(m_optViews), end(m_optViews), + [](auto a, auto b) { return a->nameWidth() < b->nameWidth(); }); + int maxWidth = (**maxEl).nameWidth(); + for_each(begin(m_optViews), end(m_optViews), [maxWidth](auto a) { a->setMinNameWidth(maxWidth); }); + + setDisabled(false); + }); + + return introJob; +} + +void MesonOptionsView::emitChanged() +{ + emit configChanged(); +} + +MESON_OPT_PTR MesonOptionsView::options() +{ + return m_options; +} + diff --git a/plugins/meson/settings/mesonoptionsview.ui b/plugins/meson/settings/mesonoptionsview.ui new file mode 100644 --- /dev/null +++ b/plugins/meson/settings/mesonoptionsview.ui @@ -0,0 +1,457 @@ + + + MesonOptionsView + + + + 0 + 0 + 660 + 600 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Meson Options + + + + 6 + + + + + 0 + + + + Core + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 636 + 522 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Backend + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Base + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Compiler + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Directories + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Project + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Testing + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 100 + 30 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + configChanged() + +