diff --git a/interfaces/CMakeLists.txt b/interfaces/CMakeLists.txt --- a/interfaces/CMakeLists.txt +++ b/interfaces/CMakeLists.txt @@ -41,6 +41,8 @@ itoolviewactionlistener.cpp ilanguagecheck.cpp ilanguagecheckprovider.cpp + iruntime.cpp + iruntimecontroller.cpp ) configure_file(ipluginversion.h.in ${CMAKE_CURRENT_BINARY_DIR}/ipluginversion.h) @@ -97,6 +99,8 @@ itestcontroller.h itoolviewactionlistener.h iproblem.h + iruntime.h + iruntimecontroller.h ${CMAKE_CURRENT_BINARY_DIR}/ipluginversion.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/interfaces COMPONENT Devel ) diff --git a/interfaces/icore.h b/interfaces/icore.h --- a/interfaces/icore.h +++ b/interfaces/icore.h @@ -50,6 +50,7 @@ class IPartController; class IDashboardController; class ITestController; +class IRuntimeController; /** * ICore is the container class for all the various objects in use by @@ -121,6 +122,9 @@ /** @return the test controller */ virtual KDevelop::ITestController* testController() = 0; + /** @return the runtime controller */ + Q_SCRIPTABLE virtual KDevelop::IRuntimeController* runtimeController() = 0; + /** @return the about data of the framework, different from the main about data which is created by the application */ virtual KAboutData aboutData() const = 0; diff --git a/interfaces/iruntime.h b/interfaces/iruntime.h new file mode 100644 --- /dev/null +++ b/interfaces/iruntime.h @@ -0,0 +1,64 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol + + 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. +*/ + +#ifndef KDEVPLATFORM_IRUNTIME_H +#define KDEVPLATFORM_IRUNTIME_H + +#include "interfacesexport.h" +#include +#include + +class QProcess; +class KProcess; + +namespace KDevelop +{ +class Path; + +/** + * A runtime represents an environment that will be targetted + * + * It allows the IDE to interact with virtual systems that live in different + * namespaces. + * + * It allows to execute processes in them and translate the paths these runtimes + * offer into ones that will be visible to our process to interact with. + */ +class KDEVPLATFORMINTERFACES_EXPORT IRuntime : public QObject +{ + Q_OBJECT +public: + ~IRuntime() override; + + /** @returns a display string that identifies the runtime */ + virtual QString name() const = 0; + + virtual void startProcess(QProcess* process) = 0; + virtual void startProcess(KProcess* process) = 0; + + virtual Path pathInRuntime(const Path& localPath) = 0; + virtual Path pathInHost(const Path& runtimePath) = 0; + + virtual void setEnabled(bool enabled) = 0; +}; + +} + +#endif + diff --git a/interfaces/iruntime.cpp b/interfaces/iruntime.cpp new file mode 100644 --- /dev/null +++ b/interfaces/iruntime.cpp @@ -0,0 +1,24 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol + + 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 "iruntime.h" + +using namespace KDevelop; + +KDevelop::IRuntime::~IRuntime() = default; diff --git a/interfaces/iruntimecontroller.h b/interfaces/iruntimecontroller.h new file mode 100644 --- /dev/null +++ b/interfaces/iruntimecontroller.h @@ -0,0 +1,55 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef KDEVPLATFORM_IRUNTIMECONTROLLER_H +#define KDEVPLATFORM_IRUNTIMECONTROLLER_H + +#include +#include +#include + +namespace KDevelop { +class IRuntime; + +/** + * Exposes runtimes. + * + * @author Aleix Pol + */ +class KDEVPLATFORMINTERFACES_EXPORT IRuntimeController: public QObject +{ + Q_OBJECT +public: + IRuntimeController(); + ~IRuntimeController() override; + + virtual void addRuntimes(const QVector &runtimes) = 0; + + virtual QVector availableRuntimes() const = 0; + + virtual void setCurrentRuntime(IRuntime* doc) = 0; + + virtual IRuntime* currentRuntime() const = 0; + +Q_SIGNALS: + void currentRuntimeChanged(IRuntime* currentRuntime); +}; + +} + +#endif diff --git a/interfaces/iruntimecontroller.cpp b/interfaces/iruntimecontroller.cpp new file mode 100644 --- /dev/null +++ b/interfaces/iruntimecontroller.cpp @@ -0,0 +1,23 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "iruntimecontroller.h" + +KDevelop::IRuntimeController::IRuntimeController() = default; + +KDevelop::IRuntimeController::~IRuntimeController() = default; diff --git a/language/backgroundparser/backgroundparser.cpp b/language/backgroundparser/backgroundparser.cpp --- a/language/backgroundparser/backgroundparser.cpp +++ b/language/backgroundparser/backgroundparser.cpp @@ -588,8 +588,8 @@ void BackgroundParser::addDocument(const IndexedString& url, TopDUContext::Features features, int priority, QObject* notifyWhenReady, ParseJob::SequentialProcessingFlags flags, int delay) { -// qCDebug(LANGUAGE) << "BackgroundParser::addDocument" << url.toUrl(); - Q_ASSERT(isValidURL(url)); + qCDebug(LANGUAGE) << "BackgroundParser::addDocument" << url.toUrl(); +// Q_ASSERT(isValidURL(url)); QMutexLocker lock(&d->m_mutex); { DocumentParseTarget target; @@ -822,7 +822,7 @@ if ( !isValidURL(url) ) { qCWarning(LANGUAGE) << "Tracker requested for invalild URL:" << url.toUrl(); } - Q_ASSERT(isValidURL(url)); +// Q_ASSERT(isValidURL(url)); QMutexLocker l(&d->m_managedMutex); return d->m_managed.value(url, nullptr); diff --git a/outputview/outputexecutejob.h b/outputview/outputexecutejob.h --- a/outputview/outputexecutejob.h +++ b/outputview/outputexecutejob.h @@ -222,6 +222,9 @@ void start() override; + void setExecuteOnHost(bool executeHost); + bool executeOnHost() const; + protected: bool doKill() override; diff --git a/outputview/outputexecutejob.cpp b/outputview/outputexecutejob.cpp --- a/outputview/outputexecutejob.cpp +++ b/outputview/outputexecutejob.cpp @@ -21,6 +21,9 @@ #include "outputmodel.h" #include "outputdelegate.h" #include "debug.h" +#include +#include +#include #include #include #include @@ -65,6 +68,7 @@ QHash m_environmentOverrides; QString m_jobName; bool m_outputStarted; + bool m_executeOnHost = false; }; OutputExecuteJobPrivate::OutputExecuteJobPrivate( OutputExecuteJob* owner ) : @@ -102,7 +106,7 @@ killSuccessful = doKill(); } - Q_ASSERT( d->m_process->state() == QProcess::NotRunning || !killSuccessful ); + Q_ASSERT( d->m_process->state() != QProcess::Running || !killSuccessful ); delete d; } @@ -277,8 +281,12 @@ d->m_process->setProgram( d->effectiveCommandLine() ); // there is no way to input data in the output view so redirect stdin to the null device d->m_process->setStandardInputFile(QProcess::nullDevice()); - qCDebug(OUTPUTVIEW) << "Starting:" << d->m_process->program().join(QStringLiteral(" ")) << "in" << d->m_process->workingDirectory(); - d->m_process->start(); + qCDebug(OUTPUTVIEW) << "Starting:" << d->effectiveCommandLine() << d->m_process->QProcess::program() << "in" << d->m_process->workingDirectory(); + if (d->m_executeOnHost) { + d->m_process->start(); + } else { + KDevelop::ICore::self()->runtimeController()->currentRuntime()->startProcess(d->m_process); + } } else { QString errorMessage = i18n("Failed to specify program to start"); model()->appendLine( i18n( "*** %1 ***", errorMessage) ); @@ -296,6 +304,7 @@ if( d->m_status != JobRunning ) { return true; } + d->m_status = JobCanceled; d->m_process->terminate(); @@ -473,6 +482,17 @@ d->m_environmentOverrides.remove( name ); } + +void OutputExecuteJob::setExecuteOnHost(bool executeHost) +{ + d->m_executeOnHost = executeHost; +} + +bool OutputExecuteJob::executeOnHost() const +{ + return d->m_executeOnHost; +} + template< typename T > void OutputExecuteJobPrivate::mergeEnvironment( QProcessEnvironment& dest, const T& src ) { diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -22,6 +22,8 @@ ecm_optional_add_subdirectory(bazaar) ecm_optional_add_subdirectory(perforce) add_subdirectory(vcschangesview) +add_subdirectory(flatpak) +add_subdirectory(docker) if (Grantlee5_FOUND) add_subdirectory(filetemplates) add_subdirectory(codeutils) diff --git a/plugins/docker/CMakeLists.txt b/plugins/docker/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/docker/CMakeLists.txt @@ -0,0 +1,14 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevdocker\") + +qt5_add_resources(dockerplugin_SRCS kdevdockerplugin.qrc) +ki18n_wrap_ui(dockerplugin_SRCS dockerpreferences.ui) +kconfig_add_kcfg_files(dockerplugin_SRCS dockerpreferencessettings.kcfgc) + +kdevplatform_add_plugin(kdevdocker JSON kdevdocker.json SOURCES dockerplugin.cpp dockerruntime.cpp dockerpreferences.cpp ${dockerplugin_SRCS}) +target_link_libraries(kdevdocker + KF5::CoreAddons + KDev::Interfaces + KDev::Util + KDev::OutputView + KDev::Project +) diff --git a/plugins/docker/Messages.sh b/plugins/docker/Messages.sh new file mode 100644 --- /dev/null +++ b/plugins/docker/Messages.sh @@ -0,0 +1,4 @@ +#!/bin/sh +$EXTRACTRC `find . -name \*.rc` `find . -name \*.ui` >> rc.cpp +$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/kdevdocker.pot +rm -f rc.cpp diff --git a/plugins/docker/dockerplugin.h b/plugins/docker/dockerplugin.h new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerplugin.h @@ -0,0 +1,50 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef DOCKERRUNTIMEPROVIDER_H +#define DOCKERRUNTIMEPROVIDER_H + +#include +#include +#include +#include + +class DockerPreferencesSettings; + +class DockerPlugin : public KDevelop::IPlugin +{ +Q_OBJECT +public: + DockerPlugin(QObject *parent, const QVariantList & args); + ~DockerPlugin() override; + + KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context * context) override; + + void imagesListFinished(int code); + + int configPages() const override; + KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; + +private: + void runtimeChanged(KDevelop::IRuntime* newRuntime); + + QHash m_runtimes; + QScopedPointer m_settings; +}; + +#endif diff --git a/plugins/docker/dockerplugin.cpp b/plugins/docker/dockerplugin.cpp new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerplugin.cpp @@ -0,0 +1,171 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "dockerplugin.h" +#include "dockerruntime.h" +#include "dockerpreferences.h" +#include "dockerpreferencessettings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KDevDockerFactory, "kdevdocker.json", registerPlugin();) + +using namespace KDevelop; + +DockerPlugin::DockerPlugin(QObject *parent, const QVariantList & /*args*/) + : KDevelop::IPlugin( QStringLiteral("kdevdocker"), parent ) + , m_settings(new DockerPreferencesSettings) +{ + runtimeChanged(ICore::self()->runtimeController()->currentRuntime()); + + setXMLFile( QStringLiteral("kdevdockerplugin.rc") ); + connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &DockerPlugin::runtimeChanged); + + QProcess* process = new QProcess(this); + connect(process, QOverload::of(&QProcess::finished), this, &DockerPlugin::imagesListFinished); + process->start(QStringLiteral("docker"), {QStringLiteral("images"), QStringLiteral("--filter"), QStringLiteral("dangling=false"), QStringLiteral("--format"), QStringLiteral("{{.Repository}}:{{.Tag}}\t{{.ID}}")}, QIODevice::ReadOnly); + + DockerRuntime::s_settings = m_settings.data(); +} + +DockerPlugin::~DockerPlugin() +{ + DockerRuntime::s_settings = nullptr; +} + +void DockerPlugin::imagesListFinished(int code) +{ + if (code != 0) + return; + + QProcess* process = qobject_cast(sender()); + Q_ASSERT(process); + QTextStream stream(process); + QVector runtimes; + while(!stream.atEnd()) { + const QString line = stream.readLine(); + const QStringList parts = line.split(QLatin1Char('\t')); + + const QString tag = parts[0] == QLatin1String("") ? parts[1] : parts[0]; + runtimes << new DockerRuntime(tag); + } + + if (!runtimes.isEmpty()) + ICore::self()->runtimeController()->addRuntimes(runtimes); + process->deleteLater(); +} + +void DockerPlugin::runtimeChanged(KDevelop::IRuntime* newRuntime) +{ + const bool isDocker = qobject_cast(newRuntime); + + for(auto action: actionCollection()->actions()) + action->setEnabled(isDocker); +} + +KDevelop::ContextMenuExtension DockerPlugin::contextMenuExtension(KDevelop::Context* context) +{ + QList urls; + + if ( context->type() == KDevelop::Context::FileContext ) { + KDevelop::FileContext* filectx = dynamic_cast( context ); + urls = filectx->urls(); + } else if ( context->type() == KDevelop::Context::ProjectItemContext ) { + KDevelop::ProjectItemContext* projctx = dynamic_cast( context ); + foreach( KDevelop::ProjectBaseItem* item, projctx->items() ) { + if ( item->file() ) { + urls << item->file()->path().toUrl(); + } + } + } + + for(auto it = urls.begin(); it != urls.end(); ) { + if (it->isLocalFile() && it->fileName() == QLatin1String("Dockerfile")) { + ++it; + } else { + it = urls.erase(it); + } + } + + if ( !urls.isEmpty() ) { + KDevelop::ContextMenuExtension ext; + foreach(const QUrl &url, urls) { + const KDevelop::Path file(url); + + auto action = new QAction(i18n("docker build '%1'", file.path()), this); + connect(action, &QAction::triggered, this, [this, file]() { + const auto dir = file.parent(); + const QString name = QInputDialog::getText( + ICore::self()->uiController()->activeMainWindow(), i18n("Choose tag name..."), + i18n("Tag name for '%1'", file.path()), + QLineEdit::Normal, dir.lastPathSegment() + ); + + auto process = new OutputExecuteJob; + process->setExecuteOnHost(true); + *process << QStringList{"docker", "build", "--tag", name, dir.toLocalFile()}; + connect(process, &KJob::finished, this, [this, name] (KJob* job) { + if (job->error() != 0) + return; + + ICore::self()->runtimeController()->addRuntimes({ new DockerRuntime(name) }); + }); + process->start(); + }); + ext.addAction(KDevelop::ContextMenuExtension::RunGroup, action); + } + + return ext; + } + + return KDevelop::IPlugin::contextMenuExtension( context ); +} + +int DockerPlugin::configPages() const +{ + return 1; +} + +KDevelop::ConfigPage* DockerPlugin::configPage(int number, QWidget* parent) +{ + if (number == 0) { + return new DockerPreferences(this, m_settings.data(), parent); + } + return nullptr; +} + +#include "dockerplugin.moc" diff --git a/plugins/docker/dockerpreferences.h b/plugins/docker/dockerpreferences.h new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerpreferences.h @@ -0,0 +1,39 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef DOCKERPREFERENCES_H +#define DOCKERPREFERENCES_H + +#include +#include + +namespace Ui { class DockerPreferences; } + +class DockerPreferences : public KDevelop::ConfigPage +{ + Q_OBJECT + public: + explicit DockerPreferences(KDevelop::IPlugin* plugin, KCoreConfigSkeleton* config, QWidget* parent = nullptr); + ~DockerPreferences() override; + + QString name() const override; + private: + QScopedPointer m_prefsUi; +}; + +#endif diff --git a/plugins/docker/dockerpreferences.cpp b/plugins/docker/dockerpreferences.cpp new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerpreferences.cpp @@ -0,0 +1,34 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "dockerpreferences.h" +#include "ui_dockerpreferences.h" + +DockerPreferences::DockerPreferences(KDevelop::IPlugin* plugin, KCoreConfigSkeleton* config, QWidget* parent) + : KDevelop::ConfigPage(plugin, config, parent) +{ + auto m_prefsUi = new Ui::DockerPreferences; + m_prefsUi->setupUi(this); +} + +DockerPreferences::~DockerPreferences() = default; + +QString DockerPreferences::name() const +{ + return QStringLiteral("Docker"); +} diff --git a/plugins/docker/dockerpreferences.ui b/plugins/docker/dockerpreferences.ui new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerpreferences.ui @@ -0,0 +1,41 @@ + + + DockerPreferences + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + 'docker run' arguments: + + + + + + + + + + Projects volume + + + + + + + + + + + diff --git a/plugins/docker/dockerpreferencessettings.kcfg b/plugins/docker/dockerpreferencessettings.kcfg new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerpreferencessettings.kcfg @@ -0,0 +1,13 @@ + + + + + + + /src + + + diff --git a/plugins/docker/dockerpreferencessettings.kcfgc b/plugins/docker/dockerpreferencessettings.kcfgc new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerpreferencessettings.kcfgc @@ -0,0 +1,3 @@ +ClassName=DockerPreferencesSettings +File=dockerpreferencessettings.kcfg + diff --git a/plugins/docker/dockerruntime.h b/plugins/docker/dockerruntime.h new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerruntime.h @@ -0,0 +1,51 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef DOCKERRUNTIME_H +#define DOCKERRUNTIME_H + +#include +#include +#include + +class KJob; +class DockerPreferencesSettings; + +class DockerRuntime : public KDevelop::IRuntime +{ + Q_OBJECT +public: + DockerRuntime(const QString& tag); + ~DockerRuntime() override; + + QString name() const override { return m_tag; } + + void setEnabled(bool enabled) override; + + void startProcess(KProcess *process) override; + void startProcess(QProcess *process) override; + KDevelop::Path pathInHost(const KDevelop::Path & runtimePath) override; + KDevelop::Path pathInRuntime(const KDevelop::Path & localPath) override; + + static DockerPreferencesSettings* s_settings; + +private: + const QString m_tag; +}; + +#endif diff --git a/plugins/docker/dockerruntime.cpp b/plugins/docker/dockerruntime.cpp new file mode 100644 --- /dev/null +++ b/plugins/docker/dockerruntime.cpp @@ -0,0 +1,119 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "dockerruntime.h" +#include "dockerpreferencessettings.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; + +DockerPreferencesSettings* DockerRuntime::s_settings = nullptr; + +DockerRuntime::DockerRuntime(const QString &tag) + : KDevelop::IRuntime() + , m_tag(tag) +{ +} + +DockerRuntime::~DockerRuntime() +{ +} + +void DockerRuntime::setEnabled(bool /*enable*/) +{ +} + +static QStringList projectVolumes() +{ + QStringList ret; + QString dir = DockerRuntime::s_settings->projectsVolume(); + if (!dir.endsWith(QLatin1Char('/'))) { + dir.append(QLatin1Char('/')); + } + + for(IProject* project: ICore::self()->projectController()->projects()) { + const Path path = project->path(); + if (path.isLocalFile()) { + ret << "--volume" << QStringLiteral("%1:%2").arg(path.toLocalFile(), dir + path.lastPathSegment()); + } + } + return ret; +} + +void DockerRuntime::startProcess(QProcess* process) +{ + const QStringList args = QStringList{QStringLiteral("run"), "--rm"} << KShell::splitArgs(s_settings->extraArguments()) << projectVolumes() << m_tag << process->program() << process->arguments(); + process->setProgram("docker"); + process->setArguments(args); + qDebug() << "run..." << process->program() << args; + process->start(); +} + +void DockerRuntime::startProcess(KProcess* process) +{ + process->setProgram(QStringList{ "docker", "run", "--rm" } << KShell::splitArgs(s_settings->extraArguments()) << projectVolumes() << m_tag << process->program()); + + qDebug() << "yokai!" << process << process->program().join(' '); + process->start(); +} + +KDevelop::Path DockerRuntime::pathInHost(const KDevelop::Path& runtimePath) +{ + Path ret = runtimePath; + const Path projectsDir(DockerRuntime::s_settings->projectsVolume()); + if (projectsDir.isParentOf(runtimePath)) { + const auto relPath = runtimePath.relativePath(projectsDir); + const int index = relPath.indexOf(QLatin1Char('/')); + auto project = ICore::self()->projectController()->findProjectByName(relPath.left(index)); + if (!project) { + qWarning() << "No project for" << relPath; + } else { + const auto repPathProject = relPath.mid(index+1); + ret = Path(project->path(), repPathProject);; + } + } + return ret; +} + +KDevelop::Path DockerRuntime::pathInRuntime(const KDevelop::Path& localPath) +{ + auto project = ICore::self()->projectController()->findProjectForUrl(localPath.toUrl()); + if (project) { + const Path projectsDir(DockerRuntime::s_settings->projectsVolume()); + const QString relpath = project->path().relativePath(localPath); + const KDevelop::Path ret(projectsDir, project->name() + QLatin1Char('/') + relpath); + qDebug() << "docker pathInRuntime..." << ret << project->path() << relpath; + return ret; + } else { + qWarning() << "only project files are available on the docker runtime" << localPath; + } + return localPath; +} + diff --git a/plugins/docker/kdevdocker.json b/plugins/docker/kdevdocker.json new file mode 100644 --- /dev/null +++ b/plugins/docker/kdevdocker.json @@ -0,0 +1,17 @@ +{ + "KPlugin": { + "Authors": [ + { "Name": "Aleix Pol", "Email": "aleixpol@kde.org" } + ], + "Category": "Global", + "Description": "Exposes Docker runtimes", + "Icon": "docker", + "Id": "kdevdocker", + "License": "GPL", + "Name": "Docker Support", + "ServiceTypes": [ "KDevelop/Plugin" ], + "Version": "0.1" + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/plugins/docker/kdevdockerplugin.qrc b/plugins/docker/kdevdockerplugin.qrc new file mode 100644 --- /dev/null +++ b/plugins/docker/kdevdockerplugin.qrc @@ -0,0 +1,6 @@ + + + + kdevdockerplugin.rc + + diff --git a/plugins/docker/kdevdockerplugin.rc b/plugins/docker/kdevdockerplugin.rc new file mode 100644 --- /dev/null +++ b/plugins/docker/kdevdockerplugin.rc @@ -0,0 +1,7 @@ + + + + + + + diff --git a/plugins/docker/tests/CMakeLists.txt b/plugins/docker/tests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/docker/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +ecm_add_test(test_docker.cpp + LINK_LIBRARIES Qt5::Test Qt5::Core KDev::Interfaces KDevPlatformTests) diff --git a/plugins/docker/tests/test_docker.cpp b/plugins/docker/tests/test_docker.cpp new file mode 100644 --- /dev/null +++ b/plugins/docker/tests/test_docker.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + * This file was partly taken from KDevelop's cvs plugin * + * Copyright 2017 Aleix Pol Gonzalez * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace KDevelop; + +static QString s_testedImage = QStringLiteral("ubuntu:17.04"); + +class DockerTest: public QObject +{ + Q_OBJECT +public: + + DockerTest() { + QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.projectmanagers.cmake.debug=true\n")); + + auto ret = QProcess::execute("docker", {"pull", s_testedImage}); + Q_ASSERT(ret == 0); + + AutoTestShell::init({QStringLiteral("kdevdocker")}); + TestCore::initialize(); + + m_initialRuntime = ICore::self()->runtimeController()->currentRuntime(); + + auto plugin = ICore::self()->pluginController()->loadPlugin("kdevdocker"); + Q_ASSERT(plugin); + + QSignalSpy spy(plugin, SIGNAL(imagesListed())); + Q_ASSERT(spy.wait()); + + + auto projectPath = QUrl::fromLocalFile(QFINDTESTDATA("testproject/test.kdev4")); + qDebug() << "wuuu" << projectPath; + TestCore::self()->projectController()->openProject(projectPath); + QSignalSpy spy2(TestCore::self()->projectController(), &IProjectController::projectOpened); + Q_ASSERT(spy2.wait()); + + } + IRuntime* m_initialRuntime; + +private Q_SLOTS: + void initTestCase() { + QVERIFY(ICore::self()->runtimeController()->currentRuntime() == m_initialRuntime); + for(IRuntime* runtime : ICore::self()->runtimeController()->availableRuntimes()) { + if (s_testedImage == runtime->name()) { + ICore::self()->runtimeController()->setCurrentRuntime(runtime); + } + } + QVERIFY(ICore::self()->runtimeController()->currentRuntime() != m_initialRuntime); + } + + void paths() { + auto rt = ICore::self()->runtimeController()->currentRuntime(); + QVERIFY(rt); + + const Path root("/"); + const Path hostDir = rt->pathInHost(root); + QCOMPARE(root, rt->pathInRuntime(hostDir)); + } + + void projectPath() { + auto rt = ICore::self()->runtimeController()->currentRuntime(); + QVERIFY(rt); + auto project = ICore::self()->projectController()->projects().first(); + QVERIFY(project); + + const Path file = project->projectFile(); + QCOMPARE(file, rt->pathInRuntime(rt->pathInHost(file))); + QCOMPARE(project->path(), rt->pathInHost(rt->pathInRuntime(project->path()))); + } + + void envs() { + auto rt = ICore::self()->runtimeController()->currentRuntime(); + QVERIFY(rt); + + QCOMPARE(rt->getenv("PATH"), QByteArray("/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")); + } + + void cleanupTestCase() { + ICore::self()->runtimeController()->setCurrentRuntime(m_initialRuntime); + } + +}; + +QTEST_MAIN( DockerTest ) + +#include "test_docker.moc" diff --git a/plugins/docker/tests/testproject/test.kdev4 b/plugins/docker/tests/testproject/test.kdev4 new file mode 100644 --- /dev/null +++ b/plugins/docker/tests/testproject/test.kdev4 @@ -0,0 +1,3 @@ +[Project] +Name=test +Manager=KDevGenericManager diff --git a/plugins/docker/tests/testproject/testfile.sh b/plugins/docker/tests/testproject/testfile.sh new file mode 100644 --- /dev/null +++ b/plugins/docker/tests/testproject/testfile.sh @@ -0,0 +1 @@ +# what did you expect, you moron? diff --git a/plugins/executescript/scriptappjob.cpp b/plugins/executescript/scriptappjob.cpp --- a/plugins/executescript/scriptappjob.cpp +++ b/plugins/executescript/scriptappjob.cpp @@ -29,6 +29,8 @@ #include #include +#include +#include #include #include #include @@ -38,6 +40,7 @@ #include #include #include +#include #include "iexecutescriptplugin.h" #include "debug.h" @@ -86,7 +89,7 @@ setErrorText( i18n( "There is no active document to launch." ) ); return; } - script = document->url(); + script = ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(KDevelop::Path(document->url())).toUrl(); } if( !err.isEmpty() ) @@ -145,7 +148,7 @@ { wc = QUrl::fromLocalFile( QFileInfo( script.toLocalFile() ).absolutePath() ); } - proc->setWorkingDirectory( wc.toLocalFile() ); + proc->setWorkingDirectory( ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(KDevelop::Path(wc)).toLocalFile() ); proc->setProperty( "executable", interpreter.first() ); QStringList program; @@ -178,7 +181,7 @@ { startOutput(); appendLine( i18n("Starting: %1", proc->program().join(QLatin1Char( ' ' ) ) ) ); - proc->start(); + ICore::self()->runtimeController()->currentRuntime()->startProcess(proc); } else { qWarning() << "No process, something went wrong when creating the job"; diff --git a/plugins/flatpak/CMakeLists.txt b/plugins/flatpak/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/flatpak/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevflatpak\") + +qt5_add_resources(flatpakplugin_SRCS kdevflatpakplugin.qrc) +kdevplatform_add_plugin(kdevflatpak JSON kdevflatpak.json SOURCES flatpakplugin.cpp flatpakruntime.cpp ${flatpakplugin_SRCS}) +target_link_libraries(kdevflatpak + KF5::CoreAddons + KDev::Interfaces + KDev::Util + KDev::OutputView + KDev::Project +) diff --git a/plugins/flatpak/Messages.sh b/plugins/flatpak/Messages.sh new file mode 100644 --- /dev/null +++ b/plugins/flatpak/Messages.sh @@ -0,0 +1,4 @@ +#!/bin/sh +$EXTRACTRC `find . -name \*.rc` `find . -name \*.ui` >> rc.cpp +$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/kdevflatpak.pot +rm -f rc.cpp diff --git a/plugins/flatpak/flatpakplugin.h b/plugins/flatpak/flatpakplugin.h new file mode 100644 --- /dev/null +++ b/plugins/flatpak/flatpakplugin.h @@ -0,0 +1,44 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef FLATPAKRUNTIMEPROVIDER_H +#define FLATPAKRUNTIMEPROVIDER_H + +#include +#include +#include +#include + +class FlatpakPlugin : public KDevelop::IPlugin +{ +Q_OBJECT +public: + FlatpakPlugin(QObject *parent, const QVariantList & args); + ~FlatpakPlugin() override; + + KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context * context) override; + +private: + void runtimeChanged(KDevelop::IRuntime* newRuntime); + void rebuildCurrent(); + void exportCurrent(); + + QHash m_runtimes; +}; + +#endif diff --git a/plugins/flatpak/flatpakplugin.cpp b/plugins/flatpak/flatpakplugin.cpp new file mode 100644 --- /dev/null +++ b/plugins/flatpak/flatpakplugin.cpp @@ -0,0 +1,153 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "flatpakplugin.h" +#include "flatpakruntime.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KDevFlatpakFactory, "kdevflatpak.json", registerPlugin();) + +using namespace KDevelop; + +FlatpakPlugin::FlatpakPlugin(QObject *parent, const QVariantList & /*args*/) + : KDevelop::IPlugin( QStringLiteral("kdevflatpak"), parent ) +{ + auto ac = actionCollection(); + + auto action = new QAction(QIcon::fromTheme(QStringLiteral("run-build-clean")), i18n("Rebuild environment"), this); + action->setWhatsThis(i18n("Recompiles all dependencies for a fresh environment.")); + action->setShortcut(Qt::CTRL | Qt::META | Qt::Key_X); + connect(action, &QAction::triggered, this, &FlatpakPlugin::rebuildCurrent); + ac->addAction(QStringLiteral("runtime_flatpak_rebuild"), action); + + auto exportAction = new QAction(QIcon::fromTheme(QStringLiteral("document-export")), i18n("Export flatpak environment..."), this); + exportAction->setWhatsThis(i18n("Exports the current build into a 'bundle.flatpak' file.")); + exportAction->setShortcut(Qt::CTRL | Qt::META | Qt::Key_E); + connect(exportAction, &QAction::triggered, this, &FlatpakPlugin::exportCurrent); + ac->addAction(QStringLiteral("runtime_flatpak_export"), exportAction); + + runtimeChanged(ICore::self()->runtimeController()->currentRuntime()); + + setXMLFile( QStringLiteral("kdevflatpakplugin.rc") ); + connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &FlatpakPlugin::runtimeChanged); +} + +FlatpakPlugin::~FlatpakPlugin() = default; + +void FlatpakPlugin::runtimeChanged(KDevelop::IRuntime* newRuntime) +{ + const bool isFlatpak = qobject_cast(newRuntime); + + for(auto action: actionCollection()->actions()) + action->setEnabled(isFlatpak); +} + +void FlatpakPlugin::rebuildCurrent() +{ + const auto runtime = qobject_cast(ICore::self()->runtimeController()->currentRuntime()); + Q_ASSERT(runtime); + runtime->rebuild(); +} + +void FlatpakPlugin::exportCurrent() +{ + const auto runtime = qobject_cast(ICore::self()->runtimeController()->currentRuntime()); + Q_ASSERT(runtime); + + const QString name = runtime->name(); + const QString path = QFileDialog::getSaveFileName(ICore::self()->uiController()->activeMainWindow(), i18n("Export %1 to..."), {}, i18n("Flatpak Bundle (*.flatpak)")); + if (!path.isEmpty()) { + runtime->exportBundle(path); + } +} + +KDevelop::ContextMenuExtension FlatpakPlugin::contextMenuExtension(KDevelop::Context* context) +{ + QList urls; + + if ( context->type() == KDevelop::Context::FileContext ) { + KDevelop::FileContext* filectx = dynamic_cast( context ); + urls = filectx->urls(); + } else if ( context->type() == KDevelop::Context::ProjectItemContext ) { + KDevelop::ProjectItemContext* projctx = dynamic_cast( context ); + foreach( KDevelop::ProjectBaseItem* item, projctx->items() ) { + if ( item->file() ) { + urls << item->file()->path().toUrl(); + } + } + } + + const QRegularExpression nameRx(".*\\..*\\.*.json$"); + for(auto it = urls.begin(); it != urls.end(); ) { + if (it->isLocalFile() && it->path().contains(nameRx)) { + ++it; + } else { + it = urls.erase(it); + } + } + + if ( !urls.isEmpty() ) { + KDevelop::ContextMenuExtension ext; + foreach(const QUrl &url, urls) { + const KDevelop::Path file(url); + + auto action = new QAction(i18n("Create flatpak environment for %1", file.lastPathSegment()), this); + connect(action, &QAction::triggered, this, [this, file]() { + QTemporaryDir dir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/kdevelop-flatpak-")); + dir.setAutoRemove(false); + + const KDevelop::Path path(dir.path()); + + auto process = FlatpakRuntime::createBuildDirectory(path, file); + connect(process, &KJob::finished, this, [this, path, file] (KJob* job) { + if (job->error() != 0) + return; + + auto runtime = new FlatpakRuntime(path, file); + ICore::self()->runtimeController()->addRuntimes({runtime}); + }); + process->start(); + }); + ext.addAction(KDevelop::ContextMenuExtension::RunGroup, action); + } + + return ext; + } + + return KDevelop::IPlugin::contextMenuExtension( context ); +} + + +#include "flatpakplugin.moc" diff --git a/plugins/flatpak/flatpakruntime.h b/plugins/flatpak/flatpakruntime.h new file mode 100644 --- /dev/null +++ b/plugins/flatpak/flatpakruntime.h @@ -0,0 +1,54 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef FLATPAKRUNTIME_H +#define FLATPAKRUNTIME_H + +#include +#include +#include + +class KJob; + +class FlatpakRuntime : public KDevelop::IRuntime +{ + Q_OBJECT +public: + FlatpakRuntime(const KDevelop::Path &buildDirectory, const KDevelop::Path &file); + ~FlatpakRuntime() override; + + QString name() const override { return m_file.lastPathSegment(); } + + void setEnabled(bool enabled) override; + + void startProcess(KProcess *process) override; + void startProcess(QProcess *process) override; + KDevelop::Path pathInHost(const KDevelop::Path & runtimePath) override { return runtimePath; } + KDevelop::Path pathInRuntime(const KDevelop::Path & localPath) override { return localPath; } + + static KJob* createBuildDirectory(const KDevelop::Path &path, const KDevelop::Path &file); + + void rebuild(); + void exportBundle(const QString &path); + +private: + const KDevelop::Path m_file; + const KDevelop::Path m_buildDirectory; +}; + +#endif diff --git a/plugins/flatpak/flatpakruntime.cpp b/plugins/flatpak/flatpakruntime.cpp new file mode 100644 --- /dev/null +++ b/plugins/flatpak/flatpakruntime.cpp @@ -0,0 +1,86 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "flatpakruntime.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; + +KJob* FlatpakRuntime::createBuildDirectory(const KDevelop::Path &buildDirectory, const KDevelop::Path &file) +{ + OutputExecuteJob* process = new OutputExecuteJob; + process->setExecuteOnHost(true); + process->setJobName(i18n("Creating Flatpak %1", file.lastPathSegment())); + *process << QStringList{ "flatpak-builder", "--build-only", buildDirectory.toLocalFile(), file.toLocalFile() }; + return process; +} + +FlatpakRuntime::FlatpakRuntime(const KDevelop::Path &buildDirectory, const KDevelop::Path &file) + : KDevelop::IRuntime() + , m_file(file) + , m_buildDirectory(buildDirectory) +{ +} + +FlatpakRuntime::~FlatpakRuntime() +{ + QDir(m_buildDirectory.toLocalFile()).removeRecursively(); +} + +void FlatpakRuntime::setEnabled(bool enable) +{ +} + +void FlatpakRuntime::startProcess(QProcess* process) +{ + const QStringList args = QStringList{"build", "--socket=x11", m_buildDirectory.toLocalFile(), process->program()} << process->arguments(); + process->setProgram("flatpak"); + process->setArguments(args); + process->start(); +} + +void FlatpakRuntime::startProcess(KProcess* process) +{ + process->setProgram(QStringList{ "flatpak", "--socket=x11", "build", m_buildDirectory.toLocalFile() } << process->program()); + + qDebug() << "yokai!" << process << process->program().join(' '); + process->start(); +} + +void FlatpakRuntime::rebuild() +{ + QDir(m_buildDirectory.toLocalFile()).removeRecursively(); + createBuildDirectory(m_buildDirectory, m_file)->start(); +} + +void FlatpakRuntime::exportBundle(const QString &path) +{ + const QString develName = name().remove(QLatin1String(".json")); + + OutputExecuteJob* process = new OutputExecuteJob; + process->setExecuteOnHost(true); + process->setJobName(i18n("Exporting %1", path)); + *process << QStringList{ "flatpak", "build-bundle", m_buildDirectory.toLocalFile(), path, develName }; +} diff --git a/plugins/flatpak/kdevflatpak.json b/plugins/flatpak/kdevflatpak.json new file mode 100644 --- /dev/null +++ b/plugins/flatpak/kdevflatpak.json @@ -0,0 +1,17 @@ +{ + "KPlugin": { + "Authors": [ + { "Name": "Aleix Pol", "Email": "aleixpol@kde.org" } + ], + "Category": "Global", + "Description": "Exposes Flatpak runtimes", + "Icon": "flatpak", + "Id": "kdevflatpak", + "License": "GPL", + "Name": "Flatpak Support", + "ServiceTypes": [ "KDevelop/Plugin" ], + "Version": "0.1" + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/plugins/flatpak/kdevflatpakplugin.qrc b/plugins/flatpak/kdevflatpakplugin.qrc new file mode 100644 --- /dev/null +++ b/plugins/flatpak/kdevflatpakplugin.qrc @@ -0,0 +1,6 @@ + + + + kdevflatpakplugin.rc + + diff --git a/plugins/flatpak/kdevflatpakplugin.rc b/plugins/flatpak/kdevflatpakplugin.rc new file mode 100644 --- /dev/null +++ b/plugins/flatpak/kdevflatpakplugin.rc @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/plugins/flatpak/replicate.sh b/plugins/flatpak/replicate.sh new file mode 100644 --- /dev/null +++ b/plugins/flatpak/replicate.sh @@ -0,0 +1,3 @@ +export $(cat /proc/$(pidof $1)/environ | tr \\0 \\n) +shift +$@ diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -51,6 +51,8 @@ configdialog.cpp editorconfigpage.cpp environmentconfigurebutton.cpp + runtimecontroller.cpp + runtimesmodel.cpp checkerstatus.cpp problem.cpp diff --git a/shell/core.h b/shell/core.h --- a/shell/core.h +++ b/shell/core.h @@ -43,6 +43,7 @@ class DocumentationController; class DebugController; class WorkingSetController; +class RuntimeController; class TestController; class KDEVPLATFORMSHELL_EXPORT Core: public ICore @@ -85,6 +86,7 @@ IDocumentationController* documentationController() override; IDebugController* debugController() override; ITestController* testController() override; + IRuntimeController* runtimeController() override; ISession *activeSession() override; ISessionLock::Ptr activeSessionLock() override; @@ -104,6 +106,7 @@ WorkingSetController* workingSetControllerInternal(); SourceFormatterController* sourceFormatterControllerInternal(); TestController* testControllerInternal(); + RuntimeController* runtimeControllerInternal(); /// @internal SessionController *sessionController(); diff --git a/shell/core.cpp b/shell/core.cpp --- a/shell/core.cpp +++ b/shell/core.cpp @@ -46,6 +46,7 @@ #include "kdevplatform_version.h" #include "workingsetcontroller.h" #include "testcontroller.h" +#include "runtimecontroller.h" #include "debug.h" #include @@ -210,6 +211,11 @@ documentationController = new DocumentationController(m_core); } + if( !runtimeController ) + { + runtimeController = new RuntimeController(m_core); + } + if( !debugController ) { debugController = new DebugController(m_core); @@ -275,6 +281,7 @@ } debugController.data()->initialize(); testController.data()->initialize(); + runtimeController.data()->initialize(); installSignalHandler(); @@ -298,6 +305,7 @@ delete debugController.data(); delete workingSetController.data(); delete testController.data(); + delete runtimeController.data(); selectionController.clear(); projectController.clear(); @@ -313,6 +321,7 @@ debugController.clear(); workingSetController.clear(); testController.clear(); + runtimeController.clear(); } bool Core::initialize(QObject* splash, Setup mode, const QString& session ) @@ -552,6 +561,16 @@ return d->documentationController.data(); } +IRuntimeController* Core::runtimeController() +{ + return d->runtimeController.data(); +} + +RuntimeController* Core::runtimeControllerInternal() +{ + return d->runtimeController.data(); +} + IDebugController* Core::debugController() { return d->debugController.data(); diff --git a/shell/core_p.h b/shell/core_p.h --- a/shell/core_p.h +++ b/shell/core_p.h @@ -45,6 +45,7 @@ class DebugController; class WorkingSetController; class TestController; +class RuntimeController; class KDEVPLATFORMSHELL_EXPORT CorePrivate { public: @@ -66,6 +67,7 @@ QPointer debugController; QPointer workingSetController; QPointer testController; + QPointer runtimeController; KAboutData m_aboutData; Core *m_core; diff --git a/shell/runtimecontroller.h b/shell/runtimecontroller.h new file mode 100644 --- /dev/null +++ b/shell/runtimecontroller.h @@ -0,0 +1,67 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef KDEVPLATFORM_RUNTIMECONTROLLER_H +#define KDEVPLATFORM_RUNTIMECONTROLLER_H + +#include +#include +#include + +class RuntimeViewFactory; +class QAction; +class QMenu; + +namespace KDevelop +{ + +class Core; +class Context; +class ContextMenuExtension; +class RuntimesModel; + +class RuntimeController : public IRuntimeController +{ + Q_OBJECT +public: + explicit RuntimeController(Core* core); + ~RuntimeController() override; + + void initialize(); + + void addRuntimes(const QVector &runtimes) override; +// void removeRuntimes(const QVector &runtimes) override; + QVector availableRuntimes() const override; + + KDevelop::IRuntime * currentRuntime() const override; + void setCurrentRuntime(KDevelop::IRuntime * runtime) override; + + RuntimesModel* model() const; + +private: + void setRuntimeAt(int pos); + + QScopedPointer const m_runtimesMenu; + RuntimesModel* m_model = nullptr; + QVector m_runtimes; + IRuntime* m_currentRuntime = nullptr; +}; + +} + +#endif diff --git a/shell/runtimecontroller.cpp b/shell/runtimecontroller.cpp new file mode 100644 --- /dev/null +++ b/shell/runtimecontroller.cpp @@ -0,0 +1,146 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "runtimecontroller.h" +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "uicontroller.h" +#include "mainwindow.h" + +#include "runtimesmodel.h" + +using namespace KDevelop; + +class IdentityRuntime : public IRuntime +{ + QString name() const override { return i18n("Host System"); } + + void startProcess(KProcess *process) override { + connect(process, &QProcess::errorOccurred, this, [process](QProcess::ProcessError error) { + qWarning() << "error!!!" << error << process->program(); + }); + process->start(); + } + void startProcess(QProcess *process) override { + connect(process, &QProcess::errorOccurred, this, [process](QProcess::ProcessError error) { + qWarning() << "error!!!" << error << process->program(); + }); + process->start(); + } + KDevelop::Path pathInHost(const KDevelop::Path & runtimePath) override { return runtimePath; } + KDevelop::Path pathInRuntime(const KDevelop::Path & localPath) override { return localPath; } + void setEnabled(bool /*enabled*/) override {} +}; + +KDevelop::RuntimeController::RuntimeController(KDevelop::Core* core) + : m_runtimesMenu(new QMenu()) +{ + m_model = new RuntimesModel(this); + addRuntimes({new IdentityRuntime}); + setCurrentRuntime(m_runtimes.constFirst()); + + // TODO not multi-window friendly, FIXME + KActionCollection* ac = core->uiControllerInternal()->defaultMainWindow()->actionCollection(); + + auto action = new QAction(this); + action->setStatusTip(i18n("Allows to select a runtime")); + action->setMenu(m_runtimesMenu.data()); + action->setIcon(QIcon::fromTheme(QStringLiteral("file-library-symbolic"))); + auto updateActionText = [action](IRuntime* currentRuntime){ + action->setText(i18n("Runtime: %1", currentRuntime->name())); + }; + connect(this, &RuntimeController::currentRuntimeChanged, action, updateActionText); + updateActionText(m_currentRuntime); + + ac->addAction(QStringLiteral("switch_runtimes"), action); +} + +KDevelop::RuntimeController::~RuntimeController() +{ + m_currentRuntime = nullptr; + qDeleteAll(m_runtimes); +} + +void KDevelop::RuntimeController::initialize() +{ +} + +KDevelop::IRuntime * KDevelop::RuntimeController::currentRuntime() const +{ + Q_ASSERT(m_currentRuntime); + return m_currentRuntime; +} + +QVector KDevelop::RuntimeController::availableRuntimes() const +{ + return m_runtimes; +} + +void KDevelop::RuntimeController::setCurrentRuntime(KDevelop::IRuntime* runtime) +{ + if (m_currentRuntime == runtime) + return; + + Q_ASSERT(m_runtimes.contains(runtime)); + + if (m_currentRuntime) { + m_currentRuntime->setEnabled(false); + } + qDebug() << "setting runtime..." << runtime->name() << "was" << m_currentRuntime; + m_currentRuntime = runtime; + Q_EMIT currentRuntimeChanged(runtime); + + m_currentRuntime->setEnabled(false); +} + +void KDevelop::RuntimeController::addRuntimes(const QVector& runtimes) +{ + m_model->beginInsertRows({}, m_runtimes.size(), m_runtimes.size()+runtimes.size()); + m_runtimes << runtimes; + m_model->endInsertRows(); + + for(auto runtime : runtimes) { + QAction* runtimeAction = new QAction(runtime->name(), m_runtimesMenu.data()); + runtimeAction->setCheckable(true); + connect(runtimeAction, &QAction::triggered, runtime, [this, runtime]() { + setCurrentRuntime(runtime); + }); + connect(this, &RuntimeController::currentRuntimeChanged, runtimeAction, [runtimeAction, runtime](IRuntime* currentRuntime) { + runtimeAction->setChecked(runtime == currentRuntime); + }); + + m_runtimesMenu->addAction(runtimeAction); + } +} + +KDevelop::RuntimesModel * KDevelop::RuntimeController::model() const +{ + return m_model; +} + +void KDevelop::RuntimeController::setRuntimeAt(int pos) +{ + setCurrentRuntime(m_runtimes.at(pos)); +} + diff --git a/shell/runtimesmodel.h b/shell/runtimesmodel.h new file mode 100644 --- /dev/null +++ b/shell/runtimesmodel.h @@ -0,0 +1,42 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "runtimecontroller.h" + +namespace KDevelop +{ + +class RuntimeController; + +class RuntimesModel : public QAbstractListModel +{ + Q_OBJECT +public: + friend class KDevelop::RuntimeController; + + RuntimesModel(RuntimeController* controller); + + QVariant data(const QModelIndex & index, int role) const override; + int rowCount(const QModelIndex & parent) const override; + KDevelop::IRuntime* runtimeAt(int row) const; + +private: + KDevelop::RuntimeController* const m_controller; +}; + +} diff --git a/shell/runtimesmodel.cpp b/shell/runtimesmodel.cpp new file mode 100644 --- /dev/null +++ b/shell/runtimesmodel.cpp @@ -0,0 +1,50 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "runtimesmodel.h" + +KDevelop::RuntimesModel::RuntimesModel(KDevelop::RuntimeController* controller) + : QAbstractListModel(controller) + , m_controller(controller) +{ +} + +KDevelop::IRuntime * KDevelop::RuntimesModel::runtimeAt(int row) const +{ + return m_controller->availableRuntimes().at(row); +} + +int KDevelop::RuntimesModel::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : m_controller->availableRuntimes().count(); +} + +QVariant KDevelop::RuntimesModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || index.parent().isValid() || index.row() >= m_controller->availableRuntimes().count()) + return {}; + + const auto runtime = runtimeAt(index.row()); + switch(role) { + case Qt::DisplayRole: + return runtime->name(); + case Qt::UserRole: + return QVariant::fromValue(runtime); + } + return {}; +}