diff --git a/plugins/meson/CMakeLists.txt b/plugins/meson/CMakeLists.txt index 461c8ca19d..7c3af2086e 100644 --- a/plugins/meson/CMakeLists.txt +++ b/plugins/meson/CMakeLists.txt @@ -1,58 +1,64 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevmesonmanager\") set(mesonbuilder_SRCS mesonbuilder.cpp mesonconfig.cpp mesonjob.cpp mesonjobprune.cpp mesonmanager.cpp mintro/mesonintrospectjob.cpp mintro/mesonoptions.cpp mintro/mesonprojectinfo.cpp mintro/mesontargets.cpp mintro/mesontests.cpp + rewriter/mesonactionbase.cpp + rewriter/mesonkwargsinfo.cpp + rewriter/mesonrewriterjob.cpp + settings/mesonadvancedsettings.cpp settings/mesonconfigpage.cpp settings/mesonlisteditor.cpp settings/mesonnewbuilddir.cpp settings/mesonoptionbaseview.cpp settings/mesonoptionsview.cpp + settings/mesonrewriterpage.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 + settings/mesonrewriterpage.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 75fbb4d9bc..10f0c1e583 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,491 +1,495 @@ /* 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 "mintro/mesonintrospectjob.h" #include "mintro/mesontargets.h" #include "settings/mesonconfigpage.h" #include "settings/mesonnewbuilddir.h" +#include "settings/mesonrewriterpage.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::PROJECTINFO }, 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) { + switch (number) { + case 0: return new MesonConfigPage(this, options.project, parent); + case 1: + return new MesonRewriterPage(this, options.project, parent); } return nullptr; } int MesonManager::perProjectConfigPages() const { - return 1; + return 2; } #include "mesonmanager.moc" diff --git a/plugins/meson/rewriter/mesonactionbase.cpp b/plugins/meson/rewriter/mesonactionbase.cpp new file mode 100644 index 0000000000..6df26abe12 --- /dev/null +++ b/plugins/meson/rewriter/mesonactionbase.cpp @@ -0,0 +1,25 @@ +/* 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 "mesonactionbase.h" + +MesonRewriterActionBase::MesonRewriterActionBase() {} +MesonRewriterActionBase::~MesonRewriterActionBase() {} + +void MesonRewriterActionBase::parseResult(QJsonObject) {} diff --git a/plugins/meson/rewriter/mesonactionbase.h b/plugins/meson/rewriter/mesonactionbase.h new file mode 100644 index 0000000000..0f65e36904 --- /dev/null +++ b/plugins/meson/rewriter/mesonactionbase.h @@ -0,0 +1,37 @@ +/* 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 + +class MesonRewriterActionBase; + +using MesonRewriterActionPtr = std::shared_ptr; + +class MesonRewriterActionBase +{ +public: + explicit MesonRewriterActionBase(); + virtual ~MesonRewriterActionBase(); + + virtual QJsonObject command() = 0; + virtual void parseResult(QJsonObject); +}; diff --git a/plugins/meson/rewriter/mesonkwargsinfo.cpp b/plugins/meson/rewriter/mesonkwargsinfo.cpp new file mode 100644 index 0000000000..e31e1bfa89 --- /dev/null +++ b/plugins/meson/rewriter/mesonkwargsinfo.cpp @@ -0,0 +1,135 @@ +/* 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 "mesonkwargsinfo.h" +#include "debug.h" + +MesonKWARGSInfo::MesonKWARGSInfo(MesonKWARGSInfo::Function fn, QString id) + : m_func(fn) + , m_id(id) +{ +} + +QJsonObject MesonKWARGSInfo::command() +{ + QJsonObject res; + + auto func2str = [](Function fn) -> QString { + switch (fn) { + case PROJECT: + return QStringLiteral("project"); + case TARGET: + return QStringLiteral("target"); + case DEPENDENCY: + return QStringLiteral("dependency"); + } + + return QStringLiteral("ERROR"); + }; + + res[QStringLiteral("type")] = QStringLiteral("kwargs"); + res[QStringLiteral("function")] = func2str(m_func); + res[QStringLiteral("id")] = m_id; + res[QStringLiteral("operation")] = QStringLiteral("info"); + + m_infoID = func2str(m_func) + QStringLiteral("#") + m_id; + + return res; +} + +void MesonKWARGSInfo::parseResult(QJsonObject data) +{ + if (!data[QStringLiteral("kwargs")].isObject()) { + qCWarning(KDEV_Meson) << "REWRITR: Failed to parse rewriter result"; + return; + } + + QJsonObject kwargs = data[QStringLiteral("kwargs")].toObject(); + + if (!kwargs[m_infoID].isObject()) { + qCWarning(KDEV_Meson) << "REWRITR: Failed to extract info data from object"; + return; + } + + m_result = kwargs[m_infoID].toObject(); +} + +MesonKWARGSInfo::Function MesonKWARGSInfo::function() const +{ + return m_func; +} + +QString MesonKWARGSInfo::id() const +{ + return m_id; +} + +QJsonObject MesonKWARGSInfo::result() const +{ + return m_result; +} + +QString MesonKWARGSInfo::getString(QString kwarg) const +{ + return m_result[kwarg].toString(); +} + +// Getters for Project + +QString MesonKWARGSProjectInfo::mesonVersion() const +{ + return getString(QStringLiteral("meson_version")); +} + +QString MesonKWARGSProjectInfo::license() const +{ + return getString(QStringLiteral("license")); +} + +QString MesonKWARGSProjectInfo::subprojectDir() const +{ + return getString(QStringLiteral("subproject_dir")); +} + +QString MesonKWARGSProjectInfo::version() const +{ + return getString(QStringLiteral("version")); +} + +// Constructors + +MesonKWARGSProjectInfo::MesonKWARGSProjectInfo() + : MesonKWARGSInfo(PROJECT, QStringLiteral("/")) +{ +} +MesonKWARGSTargetInfo::MesonKWARGSTargetInfo(QString id) + : MesonKWARGSInfo(TARGET, id) +{ +} +MesonKWARGSDependencyInfo::MesonKWARGSDependencyInfo(QString id) + : MesonKWARGSInfo(DEPENDENCY, id) +{ +} + +// Destructors + +MesonKWARGSInfo::~MesonKWARGSInfo() {} +MesonKWARGSProjectInfo::~MesonKWARGSProjectInfo() {} +MesonKWARGSTargetInfo::~MesonKWARGSTargetInfo() {} +MesonKWARGSDependencyInfo::~MesonKWARGSDependencyInfo() {} diff --git a/plugins/meson/rewriter/mesonkwargsinfo.h b/plugins/meson/rewriter/mesonkwargsinfo.h new file mode 100644 index 0000000000..6e4d88dcf3 --- /dev/null +++ b/plugins/meson/rewriter/mesonkwargsinfo.h @@ -0,0 +1,90 @@ +/* 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 "mesonactionbase.h" + +#include + +class MesonKWARGSInfo; +class MesonKWARGSProjectInfo; +class MesonKWARGSTargetInfo; +class MesonKWARGSDependencyInfo; + +using MesonKWARGSInfoPtr = std::shared_ptr; +using MesonKWARGSProjectInfoPtr = std::shared_ptr; +using MesonKWARGSTargetInfoPtr = std::shared_ptr; +using MesonKWARGSDependencyInfoPtr = std::shared_ptr; + +class MesonKWARGSInfo : public MesonRewriterActionBase +{ +public: + enum Function { PROJECT, TARGET, DEPENDENCY }; + +public: + explicit MesonKWARGSInfo(Function fn, QString id); + virtual ~MesonKWARGSInfo(); + + QJsonObject command() override; + void parseResult(QJsonObject data) override; + + Function function() const; + QString id() const; + QJsonObject result() const; + + QString getString(QString kwarg) const; + +private: + Function m_func; + QString m_id; + QJsonObject m_result; + + QString m_infoID; +}; + +class MesonKWARGSProjectInfo : public MesonKWARGSInfo +{ +public: + explicit MesonKWARGSProjectInfo(); + virtual ~MesonKWARGSProjectInfo(); + + QString mesonVersion() const; + QString license() const; + QString subprojectDir() const; + QString version() const; +}; + +class MesonKWARGSTargetInfo : public MesonKWARGSInfo +{ +public: + explicit MesonKWARGSTargetInfo(QString id); + virtual ~MesonKWARGSTargetInfo(); + + // TODO some accessors similar to MesonKWARGSProjectInfo +}; + +class MesonKWARGSDependencyInfo : public MesonKWARGSInfo +{ +public: + explicit MesonKWARGSDependencyInfo(QString id); + virtual ~MesonKWARGSDependencyInfo(); + + // TODO some accessors similar to MesonKWARGSProjectInfo +}; diff --git a/plugins/meson/rewriter/mesonrewriterjob.cpp b/plugins/meson/rewriter/mesonrewriterjob.cpp new file mode 100644 index 0000000000..e6a1824a0f --- /dev/null +++ b/plugins/meson/rewriter/mesonrewriterjob.cpp @@ -0,0 +1,124 @@ +/* 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 "mesonrewriterjob.h" +#include "mesonconfig.h" + +#include "interfaces/iproject.h" +#include "util/path.h" + +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +using namespace KDevelop; + +MesonRewriterJob::MesonRewriterJob(IProject* project, QVector actions, QObject* parent) + : KJob(parent) + , m_project(project) + , m_actions(actions) +{ + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonRewriterJob::finished); +} + +QString MesonRewriterJob::execute() +{ + QJsonArray command; + for (auto& i : m_actions) { + command.append(i->command()); + } + + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + if (!tempFile.open()) { + return i18n("Failed to create a temorary file"); + } + + QJsonDocument doc(command); + tempFile.write(doc.toJson()); + tempFile.flush(); + + Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); + + KProcess proc(this); + proc.setWorkingDirectory(m_project->path().toLocalFile()); + proc.setOutputChannelMode(KProcess::SeparateChannels); + proc.setProgram(buildDir.mesonExecutable.toLocalFile()); + proc << QStringLiteral("rewrite") << QStringLiteral("command") << tempFile.fileName(); + + int ret = proc.execute(); + if (ret != 0) { + return i18n("%1 returned %2", proc.program().join(QChar::fromLatin1(' ')), ret); + } + + auto rawData = proc.readAllStandardError(); + if (rawData.isEmpty()) { + return QString(); + } + + QJsonParseError error; + QJsonDocument result = QJsonDocument::fromJson(rawData, &error); + if (error.error) { + return i18n("JSON parser error: %1", error.errorString()); + } + + if (!result.isObject()) { + return i18n("The rewriter output of '%1' is not an object", proc.program().join(QChar::fromLatin1(' '))); + } + + for (auto& i : m_actions) { + i->parseResult(result.object()); + } + + return QString(); +} + +void MesonRewriterJob::finished() +{ + QString result = m_futureWatcher.result(); + if (!result.isEmpty()) { + qCWarning(KDEV_Meson) << "REWRITER " << result; + setError(true); + setErrorText(result); + emitResult(); + return; + } + + qCDebug(KDEV_Meson) << "REWRITER: Meson rewriter job finished"; + emitResult(); +} + +bool MesonRewriterJob::doKill() +{ + if (m_futureWatcher.isRunning()) { + m_futureWatcher.cancel(); + } + return true; +} + +void MesonRewriterJob::start() +{ + auto future = QtConcurrent::run(this, &MesonRewriterJob::execute); + m_futureWatcher.setFuture(future); +} diff --git a/plugins/meson/rewriter/mesonrewriterjob.h b/plugins/meson/rewriter/mesonrewriterjob.h new file mode 100644 index 0000000000..c16bc7242f --- /dev/null +++ b/plugins/meson/rewriter/mesonrewriterjob.h @@ -0,0 +1,51 @@ +/* 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 "mesonactionbase.h" + +#include +#include +#include + +namespace KDevelop +{ +class IProject; +} + +class MesonRewriterJob : public KJob +{ + Q_OBJECT +public: + explicit MesonRewriterJob(KDevelop::IProject* project, QVector actions, QObject* parent); + + void start() override; + bool doKill() override; + +private: + QString execute(); + void finished(); + +private: + KDevelop::IProject* m_project = nullptr; + QVector m_actions; + + QFutureWatcher m_futureWatcher; +}; diff --git a/plugins/meson/settings/mesonrewriterpage.cpp b/plugins/meson/settings/mesonrewriterpage.cpp new file mode 100644 index 0000000000..afb3798126 --- /dev/null +++ b/plugins/meson/settings/mesonrewriterpage.cpp @@ -0,0 +1,227 @@ +/* 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 "mesonrewriterpage.h" +#include "mesonconfig.h" +#include "mesonmanager.h" +#include "mintro/mesonintrospectjob.h" +#include "mintro/mesonprojectinfo.h" +#include "rewriter/mesonkwargsinfo.h" +#include "rewriter/mesonrewriterjob.h" + +#include "ui_mesonrewriterpage.h" + +#include +#include +#include + +#include +#include +#include + +using namespace KDevelop; + +class JobDeleter +{ +public: + explicit JobDeleter(QList jobs) + : m_jobs(jobs) + { + } + ~JobDeleter() + { + for (KJob* i : m_jobs) { + delete i; + } + } + +private: + QList m_jobs; +}; + +MesonRewriterPage::MesonRewriterPage(IPlugin* plugin, IProject* project, QWidget* parent) + : ConfigPage(plugin, nullptr, parent) + , m_project(project) +{ + Q_ASSERT(m_project); + + m_ui = new Ui::MesonRewriterPage; + m_ui->setupUi(this); + + reset(); +} + +void MesonRewriterPage::setWidgetsDisabled(bool disabled) +{ + m_ui->c_tabs->setDisabled(disabled); +} + +void MesonRewriterPage::checkStatus() +{ + // Get the config build dir status + auto setStatus = [this](QString const& msg, int color) -> void { + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + switch (color) { + case 0: + role = KColorScheme::PositiveText; + setDisabled(false); + break; + case 1: + role = KColorScheme::NeutralText; + setDisabled(true); + break; + case 2: + default: + role = KColorScheme::NegativeText; + setDisabled(true); + 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 (m_state) { + case START: + setStatus(i18n("Initializing GUI"), 1); + break; + case LOADING: + setStatus(i18n("Loading project data..."), 1); + break; + case WRITING: + setStatus(i18n("Writing project data..."), 1); + break; + case READY: + setStatus(i18n("Initializing GUI"), 0); + break; + case ERROR: + setStatus(i18n("Loading meson rewriter data failed"), 2); + break; + } + + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + int numChanged = 0; + // TODO compute num of changes here + + 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 MesonRewriterPage::setStatus(MesonRewriterPage::State s) +{ + m_state = s; + checkStatus(); +} + +void MesonRewriterPage::apply() +{ + qCDebug(KDEV_Meson) << "REWRITER GUI: APPLY"; +} + +void MesonRewriterPage::reset() +{ + qCDebug(KDEV_Meson) << "REWRITER GUI: RESET"; + + Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); + if (!buildDir.isValid()) { + setStatus(ERROR); + return; + } + + auto projectInfo = std::make_shared(); + + QVector actions = { projectInfo }; + + QVector types = { MesonIntrospectJob::PROJECTINFO }; + MesonIntrospectJob::Mode mode = MesonIntrospectJob::MESON_FILE; + + auto introspectJob = new MesonIntrospectJob(m_project, buildDir, types, mode, this); + auto rewriterJob = new MesonRewriterJob(m_project, actions, this); + + QList jobs = { introspectJob, rewriterJob }; + + // Don't automatically delete jobs beause they are used in the lambda below + for (KJob* i : jobs) { + i->setAutoDelete(false); + } + + KJob* job = new ExecuteCompositeJob(this, jobs); + + connect(job, &KJob::result, this, [=]() -> void { + JobDeleter deleter(jobs); // Make sure to free all jobs with RAII + + auto prInfo = introspectJob->projectInfo(); + if (!prInfo) { + setStatus(ERROR); + return; + } + + m_ui->l_project->setText(QStringLiteral("

") + prInfo->name() + + QStringLiteral("

")); + m_ui->l_version->setText(projectInfo->version()); + m_ui->l_license->setText(projectInfo->license()); + m_ui->l_mversion->setText(projectInfo->mesonVersion()); + + setStatus(READY); + return; + }); + + setStatus(LOADING); + job->start(); +} + +void MesonRewriterPage::defaults() +{ + reset(); +} + +void MesonRewriterPage::emitChanged() +{ + m_configChanged = true; + checkStatus(); + emit changed(); +} + +QString MesonRewriterPage::name() const +{ + return i18n("Project"); +} + +QString MesonRewriterPage::fullName() const +{ + return i18n("Meson project settings"); +} + +QIcon MesonRewriterPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("meson")); +} diff --git a/plugins/meson/settings/mesonrewriterpage.h b/plugins/meson/settings/mesonrewriterpage.h new file mode 100644 index 0000000000..3b6869bc20 --- /dev/null +++ b/plugins/meson/settings/mesonrewriterpage.h @@ -0,0 +1,66 @@ +/* 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 + +namespace KDevelop +{ +class IPlugin; +class IProject; +} + +namespace Ui +{ +class MesonRewriterPage; +} + +class MesonRewriterPage : public KDevelop::ConfigPage +{ + Q_OBJECT +public: + enum State {START, LOADING, WRITING, READY, ERROR}; + +public: + explicit MesonRewriterPage(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 emitChanged(); + +private: + void setWidgetsDisabled(bool disabled); + void checkStatus(); + void setStatus(State s); + +private: + KDevelop::IProject* m_project = nullptr; + Ui::MesonRewriterPage* m_ui = nullptr; + bool m_configChanged = false; + State m_state = START; +}; diff --git a/plugins/meson/settings/mesonrewriterpage.ui b/plugins/meson/settings/mesonrewriterpage.ui new file mode 100644 index 0000000000..618c29dd09 --- /dev/null +++ b/plugins/meson/settings/mesonrewriterpage.ui @@ -0,0 +1,162 @@ + + + MesonRewriterPage + + + + 0 + 0 + 824 + 543 + + + + Meson project settings + + + + + + 0 + + + + Project + + + Project settings + + + + + + <html><head/><body><h3>projectName</h3></body></html> + + + + + + + License: + + + + + + + Project version: + + + + + + + version + + + Qt::RichText + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Name: + + + + + + + Meson version: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + + + + + Status message... + + + + + + + Num changed + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + +