diff --git a/plugins/meson/CMakeLists.txt b/plugins/meson/CMakeLists.txt index cd73a17530..16b1a7d00b 100644 --- a/plugins/meson/CMakeLists.txt +++ b/plugins/meson/CMakeLists.txt @@ -1,56 +1,57 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevmesonmanager\") set(mesonbuilder_SRCS mesonbuilder.cpp mesonconfig.cpp - mesonintrospectjob.cpp mesonjob.cpp mesonjobprune.cpp mesonmanager.cpp - mesonoptions.cpp - mesontargets.cpp - mesontests.cpp + + mintro/mesonintrospectjob.cpp + mintro/mesonoptions.cpp + mintro/mesontargets.cpp + mintro/mesontests.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/mesonlisteditor.ui settings/mesonnewbuilddir.ui settings/mesonoptionbaseview.ui settings/mesonoptionsview.ui ) ecm_qt_declare_logging_category(mesonbuilder_SRCS HEADER debug.h IDENTIFIER KDEV_Meson CATEGORY_NAME "kdevelop.plugins.meson" ) kdevplatform_add_plugin(kdevmesonmanager JSON kdevmesonmanager.json SOURCES ${mesonbuilder_SRCS}) add_subdirectory(icons) target_link_libraries(kdevmesonmanager Qt5::Concurrent KDev::Interfaces KDev::Language KDev::Project KDev::Util KDev::OutputView ) set_target_properties(kdevmesonmanager PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES ) diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index d7ce8a8ab7..09b6323835 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,491 +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 "mintro/mesonintrospectjob.h" +#include "mintro/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 (!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, 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 e3f81e1fb1..ca68528b1a 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 "mintro/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, 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/mesonintrospectjob.cpp b/plugins/meson/mintro/mesonintrospectjob.cpp similarity index 100% rename from plugins/meson/mesonintrospectjob.cpp rename to plugins/meson/mintro/mesonintrospectjob.cpp diff --git a/plugins/meson/mesonintrospectjob.h b/plugins/meson/mintro/mesonintrospectjob.h similarity index 100% rename from plugins/meson/mesonintrospectjob.h rename to plugins/meson/mintro/mesonintrospectjob.h diff --git a/plugins/meson/mesonoptions.cpp b/plugins/meson/mintro/mesonoptions.cpp similarity index 100% rename from plugins/meson/mesonoptions.cpp rename to plugins/meson/mintro/mesonoptions.cpp diff --git a/plugins/meson/mesonoptions.h b/plugins/meson/mintro/mesonoptions.h similarity index 100% rename from plugins/meson/mesonoptions.h rename to plugins/meson/mintro/mesonoptions.h diff --git a/plugins/meson/mesontargets.cpp b/plugins/meson/mintro/mesontargets.cpp similarity index 100% rename from plugins/meson/mesontargets.cpp rename to plugins/meson/mintro/mesontargets.cpp diff --git a/plugins/meson/mesontargets.h b/plugins/meson/mintro/mesontargets.h similarity index 100% rename from plugins/meson/mesontargets.h rename to plugins/meson/mintro/mesontargets.h diff --git a/plugins/meson/mesontests.cpp b/plugins/meson/mintro/mesontests.cpp similarity index 100% rename from plugins/meson/mesontests.cpp rename to plugins/meson/mintro/mesontests.cpp diff --git a/plugins/meson/mesontests.h b/plugins/meson/mintro/mesontests.h similarity index 100% rename from plugins/meson/mesontests.h rename to plugins/meson/mintro/mesontests.h diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp index eae43427b5..d3c21cf52d 100644 --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -1,357 +1,356 @@ /* 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 "mintro/mesonintrospectjob.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")); } diff --git a/plugins/meson/settings/mesonconfigpage.h b/plugins/meson/settings/mesonconfigpage.h index e1864675ad..d687e3c108 100644 --- a/plugins/meson/settings/mesonconfigpage.h +++ b/plugins/meson/settings/mesonconfigpage.h @@ -1,72 +1,73 @@ /* 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. */ #pragma once #include "mesonconfig.h" -#include "mesonoptions.h" +#include "mintro/mesonoptions.h" #include namespace KDevelop { class IPlugin; class IProject; } -namespace Ui { - class MesonConfigPage; +namespace Ui +{ +class MesonConfigPage; } class MesonConfigPage : public KDevelop::ConfigPage { Q_OBJECT public: - explicit MesonConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject *project, QWidget* parent = nullptr); + explicit MesonConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject* project, QWidget* parent = nullptr); QString name() const override; QString fullName() const override; QIcon icon() const override; public Q_SLOTS: void apply() override; void defaults() override; void reset() override; void addBuildDir(); void removeBuildDir(); void changeBuildDirIndex(int index); void emitChanged(); private: void checkStatus(); void updateUI(); void readUI(); void writeConfig(); void setWidgetsDisabled(bool disabled); private: - KDevelop::IProject *m_project = nullptr; - Ui::MesonConfigPage *m_ui = nullptr; + KDevelop::IProject* m_project = nullptr; + Ui::MesonConfigPage* m_ui = nullptr; Meson::MesonConfig m_config; Meson::BuildDir m_current; bool m_configChanged = false; MesonOptsPtr m_options = nullptr; }; diff --git a/plugins/meson/settings/mesonoptionbaseview.h b/plugins/meson/settings/mesonoptionbaseview.h index f7d6037a07..e4e1c78ddb 100644 --- a/plugins/meson/settings/mesonoptionbaseview.h +++ b/plugins/meson/settings/mesonoptionbaseview.h @@ -1,151 +1,151 @@ /* 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 "mintro/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(MesonOptionPtr 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(MesonOptionPtr 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(MesonOptionPtr 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(MesonOptionPtr 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(MesonOptionPtr 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(MesonOptionPtr 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/mesonoptionsview.cpp b/plugins/meson/settings/mesonoptionsview.cpp index 583bd6dabc..d5980dd71a 100644 --- a/plugins/meson/settings/mesonoptionsview.cpp +++ b/plugins/meson/settings/mesonoptionsview.cpp @@ -1,167 +1,167 @@ /* 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 "mintro/mesonintrospectjob.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::IProject* project, KDevelop::Path mesonExe) { return repopulate(new MesonIntrospectJob(project, 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(layout->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(); } MesonOptsPtr MesonOptionsView::options() { return m_options; } diff --git a/plugins/meson/settings/mesonoptionsview.h b/plugins/meson/settings/mesonoptionsview.h index 407876676a..c8a3b4fbff 100644 --- a/plugins/meson/settings/mesonoptionsview.h +++ b/plugins/meson/settings/mesonoptionsview.h @@ -1,79 +1,79 @@ /* 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 "mintro/mesonoptions.h" #include #include #include #include namespace Ui { class MesonOptionsView; } 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: explicit MesonOptionsView(QWidget* parent = nullptr); virtual ~MesonOptionsView(); void clear(); void resetAll(); KJob* repopulateFromBuildDir(KDevelop::IProject *project, Meson::BuildDir const& buildDir); KJob* repopulateFromMesonFile(KDevelop::IProject *project, KDevelop::Path mesonExe); MesonOptsPtr options(); public Q_SLOTS: void emitChanged(); Q_SIGNALS: void configChanged(); private: Ui::MesonOptionsView* m_ui = nullptr; QVector m_optViews; MesonOptsPtr m_options; KJob* repopulate(MesonIntrospectJob* introJob); };