diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index 2c4e62c0c1..d0ccc53fea 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,351 +1,388 @@ /* 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 "debug.h" #include "settings/mesonconfigpage.h" #include "settings/mesonnewbuilddir.h" #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();) // *********************************** // * 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, true); + }); + } + + 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; } KJob* MesonManager::createImportJob(ProjectFolderItem* item) { IProject* project = item->project(); auto buildDir = Meson::currentBuildDir(project); auto introJob = new MesonIntrospectJob(project, buildDir, { MesonIntrospectJob::TARGETS, MesonIntrospectJob::TESTS }, MesonIntrospectJob::BUILD_DIR, this); 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()); } }); const QList jobs = { builder()->configure(project), // Make sure the project is configured AbstractFileManagerPlugin::createImportJob(item), // generate the file system listing introJob // Load targets from the build directory introspection files }; 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) { 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); 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 16053a6b24..047cf2326a 100644 --- a/plugins/meson/mesonmanager.h +++ b/plugins/meson/mesonmanager.h @@ -1,120 +1,122 @@ /* 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; using MesonSourcePtr = std::shared_ptr; using MesonTargetsPtr = 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); /// 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; + KDevelop::Path compiler(KDevelop::ProjectTargetItem* p) const override; private: MesonBuilder* m_builder; QHash m_projectTargets; QHash m_projectTestSuites; MesonSourcePtr sourceFromItem(KDevelop::ProjectBaseItem* item) const; void populateTargets(KDevelop::ProjectFolderItem* item, QVector targets); };