Changeset View
Changeset View
Standalone View
Standalone View
plugins/flatpak/flatpakplugin.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | Copyright 2017 Aleix Pol Gonzalez <aleixpol@kde.org> | ||||
3 | | ||||
4 | This library is free software; you can redistribute it and/or | ||||
5 | modify it under the terms of the GNU Library General Public | ||||
6 | License version 2 as published by the Free Software Foundation. | ||||
7 | | ||||
8 | This library is distributed in the hope that it will be useful, | ||||
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
11 | Library General Public License for more details. | ||||
12 | | ||||
13 | You should have received a copy of the GNU Library General Public License | ||||
14 | along with this library; see the file COPYING.LIB. If not, write to | ||||
15 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
16 | Boston, MA 02110-1301, USA. | ||||
17 | */ | ||||
18 | | ||||
19 | #include "flatpakplugin.h" | ||||
20 | #include "flatpakruntime.h" | ||||
21 | #include <interfaces/icore.h> | ||||
22 | #include <interfaces/iruntimecontroller.h> | ||||
23 | #include <interfaces/iuicontroller.h> | ||||
24 | #include <interfaces/iprojectcontroller.h> | ||||
25 | #include <interfaces/iruncontroller.h> | ||||
26 | #include <interfaces/context.h> | ||||
27 | #include <interfaces/contextmenuextension.h> | ||||
28 | #include <project/projectmodel.h> | ||||
29 | #include <util/executecompositejob.h> | ||||
30 | #include <QDebug> | ||||
31 | #include <QStandardPaths> | ||||
32 | #include <QAction> | ||||
33 | #include <QProcess> | ||||
34 | #include <QRegularExpression> | ||||
35 | #include <QInputDialog> | ||||
36 | #include <QTemporaryDir> | ||||
37 | #include <QTemporaryFile> | ||||
38 | #include <QMenu> | ||||
39 | #include <QFileDialog> | ||||
40 | #include <KPluginFactory> | ||||
41 | #include <KActionCollection> | ||||
42 | #include <KLocalizedString> | ||||
43 | #include <KParts/MainWindow> | ||||
44 | #include <KJob> | ||||
45 | #include <KSharedConfig> | ||||
46 | #include <KConfigGroup> | ||||
47 | | ||||
48 | K_PLUGIN_FACTORY_WITH_JSON(KDevFlatpakFactory, "kdevflatpak.json", registerPlugin<FlatpakPlugin>();) | ||||
49 | | ||||
50 | using namespace KDevelop; | ||||
51 | | ||||
52 | FlatpakPlugin::FlatpakPlugin(QObject *parent, const QVariantList & /*args*/) | ||||
53 | : KDevelop::IPlugin( QStringLiteral("kdevflatpak"), parent ) | ||||
54 | { | ||||
55 | auto ac = actionCollection(); | ||||
56 | | ||||
57 | auto action = new QAction(QIcon::fromTheme(QStringLiteral("run-build-clean")), i18n("Rebuild environment"), this); | ||||
58 | action->setWhatsThis(i18n("Recompiles all dependencies for a fresh environment.")); | ||||
59 | ac->setDefaultShortcut(action, Qt::CTRL | Qt::META | Qt::Key_X); | ||||
60 | connect(action, &QAction::triggered, this, &FlatpakPlugin::rebuildCurrent); | ||||
61 | ac->addAction(QStringLiteral("runtime_flatpak_rebuild"), action); | ||||
62 | | ||||
63 | auto exportAction = new QAction(QIcon::fromTheme(QStringLiteral("document-export")), i18n("Export flatpak bundle..."), this); | ||||
64 | exportAction->setWhatsThis(i18n("Exports the current build into a 'bundle.flatpak' file.")); | ||||
65 | ac->setDefaultShortcut(exportAction, Qt::CTRL | Qt::META | Qt::Key_E); | ||||
66 | connect(exportAction, &QAction::triggered, this, &FlatpakPlugin::exportCurrent); | ||||
67 | ac->addAction(QStringLiteral("runtime_flatpak_export"), exportAction); | ||||
68 | | ||||
69 | auto remoteAction = new QAction(QIcon::fromTheme(QStringLiteral("folder-remote-symbolic")), i18n("Send to device..."), this); | ||||
70 | ac->setDefaultShortcut(remoteAction, Qt::CTRL | Qt::META | Qt::Key_D); | ||||
71 | connect(remoteAction, &QAction::triggered, this, &FlatpakPlugin::executeOnRemoteDevice); | ||||
72 | ac->addAction(QStringLiteral("runtime_flatpak_remote"), remoteAction); | ||||
73 | | ||||
74 | runtimeChanged(ICore::self()->runtimeController()->currentRuntime()); | ||||
75 | | ||||
76 | setXMLFile( QStringLiteral("kdevflatpakplugin.rc") ); | ||||
77 | connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &FlatpakPlugin::runtimeChanged); | ||||
78 | } | ||||
79 | | ||||
80 | FlatpakPlugin::~FlatpakPlugin() = default; | ||||
81 | | ||||
82 | void FlatpakPlugin::runtimeChanged(KDevelop::IRuntime* newRuntime) | ||||
83 | { | ||||
84 | const bool isFlatpak = qobject_cast<FlatpakRuntime*>(newRuntime); | ||||
85 | | ||||
86 | for(auto action: actionCollection()->actions()) { | ||||
87 | action->setEnabled(isFlatpak); | ||||
88 | } | ||||
89 | } | ||||
90 | | ||||
91 | void FlatpakPlugin::rebuildCurrent() | ||||
92 | { | ||||
93 | const auto runtime = qobject_cast<FlatpakRuntime*>(ICore::self()->runtimeController()->currentRuntime()); | ||||
94 | Q_ASSERT(runtime); | ||||
95 | ICore::self()->runController()->registerJob(runtime->rebuild()); | ||||
96 | } | ||||
97 | | ||||
98 | void FlatpakPlugin::exportCurrent() | ||||
99 | { | ||||
100 | const auto runtime = qobject_cast<FlatpakRuntime*>(ICore::self()->runtimeController()->currentRuntime()); | ||||
101 | Q_ASSERT(runtime); | ||||
102 | | ||||
103 | const QString path = QFileDialog::getSaveFileName(ICore::self()->uiController()->activeMainWindow(), i18n("Export %1 to...", runtime->name()), {}, i18n("Flatpak Bundle (*.flatpak)")); | ||||
104 | if (!path.isEmpty()) { | ||||
105 | ICore::self()->runController()->registerJob(new ExecuteCompositeJob(runtime, runtime->exportBundle(path))); | ||||
106 | } | ||||
107 | } | ||||
108 | | ||||
109 | void FlatpakPlugin::createRuntime(const KDevelop::Path &file, const QString &arch) | ||||
110 | { | ||||
111 | QTemporaryDir* dir = new QTemporaryDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/kdevelop-flatpak-")); | ||||
112 | const KDevelop::Path path(dir->path()); | ||||
113 | | ||||
114 | auto process = FlatpakRuntime::createBuildDirectory(path, file, arch); | ||||
115 | connect(process, &KJob::finished, this, [this, path, file, arch, dir] (KJob* job) { | ||||
116 | if (job->error() != 0) { | ||||
117 | delete dir; | ||||
118 | return; | ||||
119 | } | ||||
120 | | ||||
121 | auto rt = new FlatpakRuntime(path, file, arch); | ||||
122 | connect(rt, &QObject::destroyed, rt, [dir]() { delete dir; }); | ||||
123 | ICore::self()->runtimeController()->addRuntimes(rt); | ||||
124 | }); | ||||
125 | process->start(); | ||||
126 | } | ||||
127 | | ||||
128 | static QStringList availableArches(const KDevelop::Path& url) | ||||
129 | { | ||||
130 | QProcess supportedArchesProcess; | ||||
131 | QStringList ret; | ||||
132 | | ||||
133 | QObject::connect(&supportedArchesProcess, QOverload<int>::of(&QProcess::finished), &supportedArchesProcess, [&supportedArchesProcess, &ret]() { | ||||
134 | supportedArchesProcess.deleteLater(); | ||||
135 | | ||||
136 | QTextStream stream(&supportedArchesProcess); | ||||
137 | while (!stream.atEnd()) { | ||||
138 | const QString line = stream.readLine(); | ||||
139 | ret << line.section(QLatin1Char('/'), 2, 2); | ||||
140 | } | ||||
141 | }); | ||||
142 | | ||||
143 | const auto doc = FlatpakRuntime::config(url); | ||||
144 | const QString sdkName = doc[QLatin1String("sdk")].toString(); | ||||
145 | const QString runtimeVersion = doc[QLatin1String("runtime-version")].toString(); | ||||
146 | supportedArchesProcess.start("flatpak", {"info", "-r", sdkName + "//" + runtimeVersion }); | ||||
147 | supportedArchesProcess.waitForFinished(); | ||||
148 | return ret; | ||||
149 | } | ||||
150 | | ||||
151 | KDevelop::ContextMenuExtension FlatpakPlugin::contextMenuExtension(KDevelop::Context* context) | ||||
152 | { | ||||
153 | QList<QUrl> urls; | ||||
154 | | ||||
155 | if ( context->type() == KDevelop::Context::FileContext ) { | ||||
156 | KDevelop::FileContext* filectx = dynamic_cast<KDevelop::FileContext*>( context ); | ||||
157 | urls = filectx->urls(); | ||||
158 | } else if ( context->type() == KDevelop::Context::ProjectItemContext ) { | ||||
159 | KDevelop::ProjectItemContext* projctx = dynamic_cast<KDevelop::ProjectItemContext*>( context ); | ||||
160 | foreach( KDevelop::ProjectBaseItem* item, projctx->items() ) { | ||||
161 | if ( item->file() ) { | ||||
162 | urls << item->file()->path().toUrl(); | ||||
163 | } | ||||
164 | } | ||||
165 | } | ||||
166 | | ||||
167 | const QRegularExpression nameRx(".*\\..*\\.*.json$"); | ||||
168 | for(auto it = urls.begin(); it != urls.end(); ) { | ||||
169 | if (it->isLocalFile() && it->path().contains(nameRx)) { | ||||
170 | ++it; | ||||
171 | } else { | ||||
172 | it = urls.erase(it); | ||||
173 | } | ||||
174 | } | ||||
175 | | ||||
176 | if ( !urls.isEmpty() ) { | ||||
177 | KDevelop::ContextMenuExtension ext; | ||||
178 | foreach(const QUrl &url, urls) { | ||||
179 | const KDevelop::Path file(url); | ||||
180 | | ||||
181 | foreach(const QString &arch, availableArches(file)) { | ||||
182 | auto action = new QAction(i18n("Build flatpak %1 for %2", file.lastPathSegment(), arch), this); | ||||
183 | connect(action, &QAction::triggered, this, [this, file, arch]() { | ||||
184 | createRuntime(file, arch); | ||||
185 | }); | ||||
186 | ext.addAction(KDevelop::ContextMenuExtension::RunGroup, action); | ||||
187 | } | ||||
188 | } | ||||
189 | | ||||
190 | return ext; | ||||
191 | } | ||||
192 | | ||||
193 | return KDevelop::IPlugin::contextMenuExtension( context ); | ||||
194 | } | ||||
195 | | ||||
196 | void FlatpakPlugin::executeOnRemoteDevice() | ||||
197 | { | ||||
198 | const auto runtime = qobject_cast<FlatpakRuntime*>(ICore::self()->runtimeController()->currentRuntime()); | ||||
199 | Q_ASSERT(runtime); | ||||
200 | | ||||
201 | KConfigGroup group(KSharedConfig::openConfig(), "Flatpak"); | ||||
202 | const QString lastDeviceAddress = group.readEntry("DeviceAddress"); | ||||
203 | const QString host = QInputDialog::getText( | ||||
204 | ICore::self()->uiController()->activeMainWindow(), i18n("Choose tag name..."), | ||||
205 | i18n("Device hostname"), | ||||
206 | QLineEdit::Normal, lastDeviceAddress | ||||
207 | ); | ||||
208 | if (host.isEmpty()) | ||||
209 | return; | ||||
210 | group.writeEntry("DeviceAddress", host); | ||||
211 | | ||||
212 | QTemporaryFile* file = new QTemporaryFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') + runtime->name() + "XXXXXX.flatpak"); | ||||
213 | file->open(); | ||||
214 | file->close(); | ||||
215 | auto job = runtime->executeOnDevice(host, file->fileName()); | ||||
216 | file->setParent(file); | ||||
217 | | ||||
218 | ICore::self()->runController()->registerJob(job); | ||||
219 | } | ||||
220 | | ||||
221 | #include "flatpakplugin.moc" |