diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index 1558d2cbc9..d7ce8a8ab7 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,434 +1,491 @@ /* This file is part of KDevelop Copyright 2017 Aleix Pol Gonzalez Copyright 2018 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 "mesonmanager.h" #include "debug.h" #include "mesonbuilder.h" #include "mesonconfig.h" #include "mesonintrospectjob.h" #include "mesontargets.h" #include "settings/mesonconfigpage.h" #include "settings/mesonnewbuilddir.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include using namespace KDevelop; using namespace std; static const QString GENERATOR_NINJA = QStringLiteral("ninja"); K_PLUGIN_FACTORY_WITH_JSON(MesonSupportFactory, "kdevmesonmanager.json", registerPlugin();) +// ******************************** +// * Error job for failed imports * +// ******************************** + +namespace mmanager_internal { + +class ErrorJob : public KJob +{ + Q_OBJECT +public: + ErrorJob(QObject* parent, const QString& error) + : KJob(parent) + , m_error(error) + { + } + + void start() override + { + QMessageBox::critical(nullptr, i18n("Importing project failed"), m_error); + + setError(KJob::UserDefinedError + 1); // Indicate that there was an error + setErrorText(m_error); + emitResult(); + } + +private: + QString m_error; +}; + +} + +using namespace mmanager_internal; + // *********************************** // * Meson specific executable class * // *********************************** class MesonProjectExecutableTargetItem final : public ProjectExecutableTargetItem { public: MesonProjectExecutableTargetItem(IProject* project, const QString& name, ProjectBaseItem* parent, Path buildPath, Path installPath = Path()) : ProjectExecutableTargetItem(project, name, parent) , m_buildPath(buildPath) , m_installPath(installPath) { } QUrl builtUrl() const override { return m_buildPath.toUrl(); } QUrl installedUrl() const override { return m_installPath.toUrl(); } private: Path m_buildPath; Path m_installPath; }; // *************** // * Constructor * // *************** MesonManager::MesonManager(QObject* parent, const QVariantList& args) : AbstractFileManagerPlugin(QStringLiteral("KDevMesonManager"), parent, args) , m_builder(new MesonBuilder(this)) { if (m_builder->hasError()) { setErrorDescription(i18n("Meson builder error: %1", m_builder->errorDescription())); } } MesonManager::~MesonManager() { delete m_builder; } // ********************************* // * AbstractFileManagerPlugin API * // ********************************* IProjectFileManager::Features MesonManager::features() const { return IProjectFileManager::Files | IProjectFileManager::Folders | IProjectFileManager::Targets; } ProjectFolderItem* MesonManager::createFolderItem(IProject* project, const Path& path, ProjectBaseItem* parent) { // TODO: Maybe use meson targets instead if (QFile::exists(path.toLocalFile() + QStringLiteral("/meson.build"))) return new ProjectBuildFolderItem(project, path, parent); else return AbstractFileManagerPlugin::createFolderItem(project, path, parent); } bool MesonManager::reload(KDevelop::ProjectFolderItem* item) { // "Inspired" by CMakeManager::reload IProject* project = item->project(); if (!project->isReady()) { return false; } qCDebug(KDEV_Meson) << "reloading meson project" << project->name() << "; Path:" << item->path(); KJob* job = createImportJob(item); project->setReloadJob(job); ICore::self()->runController()->registerJob(job); if (item == project->projectItem()) { connect(job, &KJob::finished, this, [project](KJob* job) -> void { if (job->error()) { return; } KDevelop::ICore::self()->projectController()->projectConfigurationChanged(project); KDevelop::ICore::self()->projectController()->reparseProject(project); }); } return true; } // *************************** // * IBuildSystemManager API * // *************************** void MesonManager::populateTargets(ProjectFolderItem* item, QVector targets) { // Remove all old targets for (ProjectTargetItem* i : item->targetList()) { delete i; } // Add the new targets auto dirPath = item->path(); for (MesonTarget* i : targets) { if (!dirPath.isDirectParentOf(i->definedIn())) { continue; } if (i->type().contains(QStringLiteral("executable"))) { auto outFiles = i->filename(); Path outFile; if (outFiles.size() > 0) { outFile = outFiles[0]; } new MesonProjectExecutableTargetItem(item->project(), i->name(), item, outFile); } else if (i->type().contains(QStringLiteral("library"))) { new ProjectLibraryTargetItem(item->project(), i->name(), item); } else { new ProjectTargetItem(item->project(), i->name(), item); } } // Recurse for (ProjectFolderItem* i : item->folderList()) { QVector filteredTargets; copy_if(begin(targets), end(targets), back_inserter(filteredTargets), [i](MesonTarget* t) -> bool { return i->path().isParentOf(t->definedIn()); }); populateTargets(i, filteredTargets); } } QList MesonManager::targets(ProjectFolderItem* item) const { Q_ASSERT(item); QList res = item->targetList(); for (ProjectFolderItem* i : item->folderList()) { res << targets(i); } return res; } void MesonManager::onMesonInfoChanged(QString path, QString projectName) { qCDebug(KDEV_Meson) << "File" << path << "changed --> reparsing project"; IProject* foundProject = ICore::self()->projectController()->findProjectByName(projectName); if (!foundProject) { return; } KJob* job = createImportJob(foundProject->projectItem()); foundProject->setReloadJob(job); ICore::self()->runController()->registerJob(job); connect(job, &KJob::finished, this, [foundProject](KJob* job) -> void { if (job->error()) { return; } KDevelop::ICore::self()->projectController()->projectConfigurationChanged(foundProject); KDevelop::ICore::self()->projectController()->reparseProject(foundProject); }); } KJob* MesonManager::createImportJob(ProjectFolderItem* item) { IProject* project = item->project(); Q_ASSERT(project); + qCDebug(KDEV_Meson) << "Importing project" << project->name(); + auto buildDir = Meson::currentBuildDir(project); + + KJob* configureJob = nullptr; + if (!buildDir.isValid()) { + configureJob = newBuildDirectory(project, &buildDir); + if (!configureJob) { + QString error = i18n("Importing %1 failed because no build directory could be created.", project->name()); + qCDebug(KDEV_Meson) << error; + return new ErrorJob(this, error); + } + } + auto introJob = new MesonIntrospectJob(project, buildDir, { MesonIntrospectJob::TARGETS, MesonIntrospectJob::TESTS }, MesonIntrospectJob::BUILD_DIR, this); KDirWatchPtr watcher = m_projectWatchers[project]; if (!watcher) { // Create a new watcher watcher = m_projectWatchers[project] = make_shared(nullptr); QString projectName = project->name(); connect(watcher.get(), &KDirWatch::dirty, this, [=](QString p) { onMesonInfoChanged(p, projectName); }); connect(watcher.get(), &KDirWatch::created, this, [=](QString p) { onMesonInfoChanged(p, projectName); }); } Path watchFile = buildDir.buildDir; watchFile.addPath(QStringLiteral("meson-info")); watchFile.addPath(QStringLiteral("meson-info.json")); if (!watcher->contains(watchFile.path())) { qCDebug(KDEV_Meson) << "Start watching file" << watchFile; watcher->addFile(watchFile.path()); } connect(introJob, &KJob::result, this, [this, introJob, item, project]() { auto targets = introJob->targets(); auto tests = introJob->tests(); if (!targets || !tests) { return; } // Remove old test suites before deleting them if (m_projectTestSuites[project]) { for (auto i : m_projectTestSuites[project]->testSuites()) { ICore::self()->testController()->removeTestSuite(i.get()); } } m_projectTargets[project] = targets; m_projectTestSuites[project] = tests; auto tgtList = targets->targets(); QVector tgtCopy; tgtCopy.reserve(tgtList.size()); transform(begin(tgtList), end(tgtList), back_inserter(tgtCopy), [](auto const& a) { return a.get(); }); populateTargets(item, tgtCopy); // Add test suites for (auto& i : tests->testSuites()) { ICore::self()->testController()->addTestSuite(i.get()); } }); QList jobs; // Configure the project if necessary - if (m_builder->evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend) != MesonBuilder::MESON_CONFIGURED) { - jobs << builder()->configure(project); + if (!configureJob + && m_builder->evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend) + != MesonBuilder::MESON_CONFIGURED) { + configureJob = builder()->configure(project); + } + + if (configureJob) { + jobs << configureJob; } jobs << AbstractFileManagerPlugin::createImportJob(item); // generate the file system listing jobs << introJob; Q_ASSERT(!jobs.contains(nullptr)); auto composite = new ExecuteCompositeJob(this, jobs); composite->setAbortOnError(false); return composite; } Path MesonManager::buildDirectory(ProjectBaseItem* item) const { Q_ASSERT(item); Meson::BuildDir buildDir = Meson::currentBuildDir(item->project()); return buildDir.buildDir; } IProjectBuilder* MesonManager::builder() const { return m_builder; } MesonSourcePtr MesonManager::sourceFromItem(KDevelop::ProjectBaseItem* item) const { Q_ASSERT(item); auto it = m_projectTargets.find(item->project()); if (it == end(m_projectTargets)) { qCDebug(KDEV_Meson) << item->path().toLocalFile() << "not found"; return {}; } auto targets = *it; return targets->fileSource(item->path()); } KDevelop::Path::List MesonManager::includeDirectories(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->includeDirs(); } KDevelop::Path::List MesonManager::frameworkDirectories(KDevelop::ProjectBaseItem*) const { return {}; } QHash MesonManager::defines(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->defines(); } QString MesonManager::extraArguments(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->extraArgs().join(QChar::fromLatin1(' ')); } bool MesonManager::hasBuildInfo(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return false; } return true; } KDevelop::Path MesonManager::compiler(KDevelop::ProjectTargetItem* item) const { const auto source = sourceFromItem(item); return source && !source->compiler().isEmpty() ? KDevelop::Path(source->compiler().constFirst()) : KDevelop::Path(); } // ******************** // * Custom functions * // ******************** -KJob* MesonManager::newBuildDirectory(IProject* project) +KJob* MesonManager::newBuildDirectory(IProject* project, Meson::BuildDir* outBuildDir) { Q_ASSERT(project); MesonNewBuildDir newBD(project); if (!newBD.exec() || !newBD.isConfigValid()) { qCWarning(KDEV_Meson) << "Failed to create new build directory for project " << project->name(); return nullptr; } Meson::BuildDir buildDir = newBD.currentConfig(); Meson::MesonConfig mesonCfg = Meson::getMesonConfig(project); buildDir.canonicalizePaths(); mesonCfg.currentIndex = mesonCfg.addBuildDir(buildDir); Meson::writeMesonConfig(project, mesonCfg); + if (outBuildDir) { + *outBuildDir = buildDir; + } + return m_builder->configure(project, buildDir, newBD.mesonArgs()); } QStringList MesonManager::supportedMesonBackends() const { // Maybe add support for other generators return { GENERATOR_NINJA }; } QString MesonManager::defaultMesonBackend() const { return GENERATOR_NINJA; } Path MesonManager::findMeson() const { QString mesonPath; const static QStringList mesonExecutables = { QStringLiteral("meson"), QStringLiteral("meson.py") }; const static QStringList mesonPaths = { QStringLiteral("%1/.local/bin").arg(QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0]) }; for (auto const& i : mesonExecutables) { mesonPath = QStandardPaths::findExecutable(i); if (!mesonPath.isEmpty()) { break; } mesonPath = QStandardPaths::findExecutable(i, mesonPaths); if (!mesonPath.isEmpty()) { break; } } return Path(mesonPath); } // *********** // * IPlugin * // *********** ConfigPage* MesonManager::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) { if (number == 0) { return new MesonConfigPage(this, options.project, parent); } return nullptr; } int MesonManager::perProjectConfigPages() const { return 1; } #include "mesonmanager.moc" diff --git a/plugins/meson/mesonmanager.h b/plugins/meson/mesonmanager.h index 1f8d6584a6..e3f81e1fb1 100644 --- a/plugins/meson/mesonmanager.h +++ b/plugins/meson/mesonmanager.h @@ -1,129 +1,129 @@ /* This file is part of KDevelop Copyright 2017 Aleix Pol Gonzalez Copyright 2018 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 "mesontests.h" #include #include #include class MesonBuilder; class MesonTarget; class MesonTargets; class MesonTargetSources; class KDirWatch; using MesonSourcePtr = std::shared_ptr; using MesonTargetsPtr = std::shared_ptr; using KDirWatchPtr = std::shared_ptr; class MesonManager : public KDevelop::AbstractFileManagerPlugin, public KDevelop::IBuildSystemManager { Q_OBJECT Q_INTERFACES(KDevelop::IBuildSystemManager) public: explicit MesonManager(QObject* parent = nullptr, const QVariantList& args = QVariantList()); ~MesonManager() override; // ******************** // * Custom functions * // ******************** /** * Create a new build directory and write it into the config. * @returns The configuration job on success or nullptr on error. */ - KJob* newBuildDirectory(KDevelop::IProject* project); + KJob* newBuildDirectory(KDevelop::IProject* project, Meson::BuildDir *outBuildDir = nullptr); /// Returns a list of all supported Meson backends (for now only ninja) QStringList supportedMesonBackends() const; QString defaultMesonBackend() const; KDevelop::Path findMeson() const; // ********************************* // * AbstractFileManagerPlugin API * // ********************************* KDevelop::IProjectFileManager::Features features() const override; KDevelop::ProjectFolderItem* createFolderItem(KDevelop::IProject* project, const KDevelop::Path& path, KDevelop::ProjectBaseItem* parent = nullptr) override; bool reload(KDevelop::ProjectFolderItem* item) override; // *********** // * IPlugin * // *********** KDevelop::ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent) override; int perProjectConfigPages() const override; // *************************** // * IBuildSystemManager API * // *************************** KJob* createImportJob(KDevelop::ProjectFolderItem* item) override; KDevelop::IProjectBuilder* builder() const override; KDevelop::Path::List includeDirectories(KDevelop::ProjectBaseItem* item) const override; KDevelop::Path::List frameworkDirectories(KDevelop::ProjectBaseItem* item) const override; QHash defines(KDevelop::ProjectBaseItem* item) const override; QString extraArguments(KDevelop::ProjectBaseItem* item) const override; bool hasBuildInfo(KDevelop::ProjectBaseItem* item) const override; KDevelop::Path buildDirectory(KDevelop::ProjectBaseItem*) const override; QList targets(KDevelop::ProjectFolderItem*) const override; // Not sure when/if these will be implemented. This would require modifying meson files. Regardless: TODO KDevelop::ProjectTargetItem* createTarget(const QString& /*target*/, KDevelop::ProjectFolderItem* /*parent*/) override { return nullptr; } bool removeTarget(KDevelop::ProjectTargetItem* /*target*/) override { return false; } bool addFilesToTarget(const QList& /*files*/, KDevelop::ProjectTargetItem* /*target*/) override { return false; } bool removeFilesFromTargets(const QList& /*files*/) override { return false; } KDevelop::Path compiler(KDevelop::ProjectTargetItem* p) const override; private: void onMesonInfoChanged(QString path, QString projectName); private: MesonBuilder* m_builder; QHash m_projectTargets; QHash m_projectTestSuites; QHash m_projectWatchers; MesonSourcePtr sourceFromItem(KDevelop::ProjectBaseItem* item) const; void populateTargets(KDevelop::ProjectFolderItem* item, QVector targets); }; diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp index dc0330866c..eae43427b5 100644 --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -1,356 +1,357 @@ /* This file is part of KDevelop Copyright 2018 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 "mesonconfigpage.h" #include "mesonbuilder.h" #include "mesonintrospectjob.h" #include "mesonjob.h" #include "mesonmanager.h" #include "mesonnewbuilddir.h" #include "ui_mesonconfigpage.h" #include #include #include #include #include #include using namespace KDevelop; MesonConfigPage::MesonConfigPage(IPlugin* plugin, IProject* project, QWidget* parent) : ConfigPage(plugin, nullptr, parent) , m_project(project) { Q_ASSERT(project); // Catch errors early MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); // This dialog only works with the MesonManager m_ui = new Ui::MesonConfigPage; m_ui->setupUi(this); m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends()); m_config = Meson::getMesonConfig(m_project); if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; reset(); return; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } QStringList buildPathList; for (auto& i : m_config.buildDirs) { buildPathList << i.buildDir.toLocalFile(); } m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->clear(); m_ui->i_buildDirs->addItems(buildPathList); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); reset(); } void MesonConfigPage::writeConfig() { qCDebug(KDEV_Meson) << "Writing config to file"; if (m_config.currentIndex >= 0) { m_config.buildDirs[m_config.currentIndex] = m_current; } if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } Meson::writeMesonConfig(m_project, m_config); } void MesonConfigPage::apply() { qCDebug(KDEV_Meson) << "Applying meson config for build dir " << m_current.buildDir; readUI(); writeConfig(); 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, mesonArgs, 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); updateUI(); }); setDisabled(true); m_configChanged = false; job->start(); } } void MesonConfigPage::defaults() { qCDebug(KDEV_Meson) << "Restoring build dir " << m_current.buildDir << " to it's default values"; MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); Q_ASSERT(mgr); m_current.mesonArgs.clear(); m_current.mesonBackend = mgr->defaultMesonBackend(); m_current.mesonExecutable = mgr->findMeson(); m_ui->options->resetAll(); updateUI(); } void MesonConfigPage::reset() { if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; m_ui->i_buildDirs->clear(); setWidgetsDisabled(true); + m_ui->b_addDir->setDisabled(false); // Allow adding a new build dir when there are none return; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); } setWidgetsDisabled(false); 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::checkStatus() { // 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 { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; switch (color) { case 0: role = KColorScheme::PositiveText; break; case 1: role = KColorScheme::NeutralText; break; case 2: default: role = KColorScheme::NegativeText; break; } QPalette pal = m_ui->l_status->palette(); pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); m_ui->l_status->setPalette(pal); m_ui->l_status->setText(i18n("Status: %1", msg)); }; switch (status) { case MesonBuilder::DOES_NOT_EXIST: setStatus(i18n("The current build directory does not exist"), 1); break; case MesonBuilder::CLEAN: setStatus(i18n("The current build directory is empty"), 1); break; case MesonBuilder::MESON_CONFIGURED: setStatus(i18n("Build directory configured"), 0); break; case MesonBuilder::MESON_FAILED_CONFIGURATION: setStatus(i18n("This meson build directory is not fully configured"), 1); break; case MesonBuilder::INVALID_BUILD_DIR: setStatus(i18n("The current build directory is invalid"), 2); break; case MesonBuilder::DIR_NOT_EMPTY: setStatus(i18n("This directory does not seem to be a meson build directory"), 2); break; case MesonBuilder::EMPTY_STRING: setStatus(i18n("Invalid build directory configuration (empty build directory string)"), 2); break; case MesonBuilder::___UNDEFINED___: setStatus(i18n("Something went very 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(); auto aConf = m_ui->advanced->getConfig(); m_current.mesonArgs = aConf.args; m_current.mesonBackend = aConf.backend; m_current.mesonExecutable = aConf.meson; } void MesonConfigPage::setWidgetsDisabled(bool disabled) { m_ui->advanced->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() { qCDebug(KDEV_Meson) << "Adding build directory"; MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); MesonBuilder* bld = dynamic_cast(mgr->builder()); Q_ASSERT(mgr); Q_ASSERT(bld); MesonNewBuildDir newBD(m_project); if (!newBD.exec() || !newBD.isConfigValid()) { qCDebug(KDEV_Meson) << "Failed to create a new build directory"; return; } m_current = newBD.currentConfig(); m_current.canonicalizePaths(); m_config.currentIndex = m_config.addBuildDir(m_current); m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->addItem(m_current.buildDir.toLocalFile()); m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); setWidgetsDisabled(true); writeConfig(); KJob* job = bld->configure(m_project, m_current, newBD.mesonArgs()); connect(job, &KJob::result, this, [this]() { reset(); }); job->start(); } void MesonConfigPage::removeBuildDir() { qCDebug(KDEV_Meson) << "Removing current build directory"; m_ui->i_buildDirs->blockSignals(true); m_ui->i_buildDirs->removeItem(m_config.currentIndex); m_config.removeBuildDir(m_config.currentIndex); if (m_config.buildDirs.isEmpty()) { m_config.currentIndex = -1; } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { m_config.currentIndex = 0; } m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); m_ui->i_buildDirs->blockSignals(false); reset(); writeConfig(); } void MesonConfigPage::changeBuildDirIndex(int index) { if (index == m_config.currentIndex || m_config.buildDirs.isEmpty()) { return; } if (index < 0 || index >= m_config.buildDirs.size()) { qCWarning(KDEV_Meson) << "Invalid build dir index " << index; return; } qCDebug(KDEV_Meson) << "Changing build directory to index " << index; m_config.currentIndex = index; reset(); writeConfig(); } void MesonConfigPage::emitChanged() { m_configChanged = true; checkStatus(); emit changed(); } QString MesonConfigPage::name() const { return i18n("Meson"); } QString MesonConfigPage::fullName() const { return i18n("Meson project configuration"); } QIcon MesonConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("meson")); }