Commit 20223cd2 authored by Christian Hergert's avatar Christian Hergert
Browse files

xdg-app: add xdg-app implementation of IdeRuntime

We still have plenty to do, but this gets things started for an
IdeRuntime implementation.

Some things that still need to be plumbed include how we do build-finish
as well as translating paths for include files (to provide during clang
operations).
parent d70f5e4b
......@@ -215,7 +215,6 @@ enable_python_scripting=no
AS_IF([test "x$have_pygobject" = "xyes"],[
AM_PATH_PYTHON([3.2.3])
AC_PATH_TOOL(PYTHON3_CONFIG, "python3-config")
AS_IF([test -z "${PYTHON3_CONFIG}"],[
AC_MSG_RESULT([Failed to locate python3-config.])
],[
......@@ -261,6 +260,7 @@ m4_include([plugins/sysmon/configure.ac])
m4_include([plugins/todo/configure.ac])
m4_include([plugins/terminal/configure.ac])
m4_include([plugins/vala-pack/configure.ac])
m4_include([plugins/xdg-app/configure.ac])
m4_include([plugins/xml-pack/configure.ac])
......@@ -504,6 +504,7 @@ echo " Symbol Tree .......................... : ${enable_symbol_tree_plugin}"
echo " Todo ................................. : ${enable_todo_plugin}"
echo " Terminal ............................. : ${enable_terminal_plugin}"
echo " Vala Language Pack ................... : ${enable_vala_pack_plugin}"
echo " Xdg-App .............................. : ${enable_xdg_app_plugin}"
echo " XML Language Pack .................... : ${enable_xml_pack_plugin}"
echo ""
echo " Templates"
......
......@@ -27,6 +27,7 @@ SUBDIRS = \
terminal \
todo \
vala-pack \
xdg-app \
xml-pack \
$(NULL)
......
if ENABLE_XDG_APP_PLUGIN
DISTCLEANFILES =
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST = $(plugin_DATA)
plugindir = $(libdir)/gnome-builder/plugins
plugin_LTLIBRARIES = libxdg-app-plugin.la
dist_plugin_DATA = xdg-app.plugin
libxdg_app_plugin_la_SOURCES = \
gbp-xdg-runtime-provider.c \
gbp-xdg-runtime-provider.h \
gbp-xdg-runtime.c \
gbp-xdg-runtime.h \
gbp-xdg-plugin.c \
$(NULL)
libxdg_app_plugin_la_CFLAGS = \
$(LIBIDE_CFLAGS) \
$(XDG_APP_CFLAGS) \
$(OPTIMIZE_CFLAGS) \
-I$(top_srcdir)/libide \
$(NULL)
libxdg_app_plugin_la_LIBADD = $(XDG_APP_LIBS)
libxdg_app_plugin_la_LDFLAGS = \
$(OPTIMIZE_LDFLAGS) \
-avoid-version \
-module \
-export-regex peas_register_types \
$(NULL)
include $(top_srcdir)/plugins/Makefile.plugin
endif
-include $(top_srcdir)/git.mk
m4_define([xdg_app_required_version], [0.4.8])
# --enable-xdg-app-plugin=yes/no/auto
AC_ARG_ENABLE([xdg-app-plugin],
[AS_HELP_STRING([--enable-xdg-app-plugin=@<:@yes/no/auto@:>@],
[Build with support for xdg-app.])],
[enable_xdg_app_plugin=$enableval],
[enable_xdg_app_plugin=auto])
AS_IF([test "$enable_xdg_app_plugin" != no],[
PKG_CHECK_MODULES(XDG_APP,
[xdg-app >= xdg_app_required_version],
[have_xdg_app=yes],
[have_xdg_app=no])
AS_IF([test "$enable_xdg_app_plugin" = "yes" && "$have_xdg_app" = "no"],[
AC_MSG_ERROR([--enable-xdg-app-plugin requires xdg-app >= xdg_app_required_version])
])
enable_xdg_app_plugin=yes
])
AM_CONDITIONAL(ENABLE_XDG_APP_PLUGIN, test x$enable_xdg_app_plugin = xyes)
AC_CONFIG_FILES([plugins/xdg-app/Makefile])
/* gbp-xdg-plugin.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <libpeas/peas.h>
#include <ide.h>
#include "gbp-xdg-runtime-provider.h"
void
peas_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module,
IDE_TYPE_RUNTIME_PROVIDER,
GBP_TYPE_XDG_RUNTIME_PROVIDER);
}
/* gbp-xdg-runtime-provider.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <xdg-app.h>
#include "util/ide-posix.h"
#include "gbp-xdg-runtime.h"
#include "gbp-xdg-runtime-provider.h"
struct _GbpXdgRuntimeProvider
{
GObject parent_instance;
IdeRuntimeManager *manager;
XdgAppInstallation *installation;
GCancellable *cancellable;
GPtrArray *runtimes;
};
static void runtime_provider_iface_init (IdeRuntimeProviderInterface *);
G_DEFINE_TYPE_EXTENDED (GbpXdgRuntimeProvider, gbp_xdg_runtime_provider, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (IDE_TYPE_RUNTIME_PROVIDER,
runtime_provider_iface_init))
static inline void
sanitize_name (gchar *name)
{
gchar *tmp = strchr (name, '/');
if (tmp != NULL)
*tmp = '\0';
}
static void
gbp_xdg_runtime_provider_load_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GbpXdgRuntimeProvider *self = source_object;
g_autofree gchar *host_type = NULL;
IdeContext *context;
GPtrArray *ret;
GPtrArray *ar;
GError *error = NULL;
guint i;
g_assert (G_IS_TASK (task));
g_assert (GBP_IS_XDG_RUNTIME_PROVIDER (self));
g_assert (IDE_IS_RUNTIME_MANAGER (self->manager));
context = ide_object_get_context (IDE_OBJECT (self->manager));
host_type = ide_get_system_arch ();
self->installation = xdg_app_installation_new_user (cancellable, &error);
if (self->installation == NULL)
{
g_task_return_error (task, error);
return;
}
ar = xdg_app_installation_list_installed_refs_by_kind (self->installation,
XDG_APP_REF_KIND_RUNTIME,
cancellable,
&error);
if (ar == NULL)
{
g_task_return_error (task, error);
return;
}
ret = g_ptr_array_new_with_free_func (g_object_unref);
for (i = 0; i < ar->len; i++)
{
XdgAppInstalledRef *ref = g_ptr_array_index (ar, i);
g_autofree gchar *str = NULL;
g_autofree gchar *id = NULL;
g_autofree gchar *name = NULL;
const gchar *arch;
const gchar *branch;
g_autofree gchar *metadata = NULL;
g_autofree gchar *sdk = NULL;
g_autoptr(GKeyFile) key_file = NULL;
g_assert (XDG_APP_IS_INSTALLED_REF (ref));
name = g_strdup (xdg_app_ref_get_name (XDG_APP_REF (ref)));
sanitize_name (name);
arch = xdg_app_ref_get_arch (XDG_APP_REF (ref));
branch = xdg_app_ref_get_branch (XDG_APP_REF (ref));
id = g_strdup_printf ("xdg-app:%s/%s/%s", name, branch, arch);
if (g_strcmp0 (host_type, arch) == 0)
str = g_strdup_printf ("%s <b>%s</b>", name, branch);
else
str = g_strdup_printf ("%s <b>%s</b> <sup>%s</sup>", name, branch, arch);
metadata = xdg_app_installed_ref_load_metadata (XDG_APP_INSTALLED_REF (ref),
cancellable, &error);
if (metadata == NULL)
{
g_warning ("%s", error->message);
g_clear_error (&error);
continue;
}
key_file = g_key_file_new ();
if (!g_key_file_load_from_data (key_file, metadata, -1, G_KEY_FILE_NONE, &error))
{
/*
* If this is not really a runtime, but something like a locale, then
* the metadata file will not exist.
*/
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&error);
continue;
}
g_warning ("%s", error->message);
g_clear_error (&error);
continue;
}
if (!(sdk = g_key_file_get_string (key_file, "Runtime", "sdk", NULL)))
sdk = g_strdup (name);
sanitize_name (sdk);
g_ptr_array_add (ret,
g_object_new (GBP_TYPE_XDG_RUNTIME,
"branch", branch,
"sdk", sdk,
"platform", name,
"context", context,
"id", id,
"display-name", str,
NULL));
}
g_ptr_array_unref (ar);
g_task_return_pointer (task, ret, (GDestroyNotify)g_ptr_array_unref);
}
static void
gbp_xdg_runtime_provider_load_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GbpXdgRuntimeProvider *self = (GbpXdgRuntimeProvider *)object;
GPtrArray *ret;
GError *error = NULL;
guint i;
g_assert (GBP_IS_XDG_RUNTIME_PROVIDER (self));
g_assert (G_IS_TASK (result));
if (!(ret = g_task_propagate_pointer (G_TASK (result), &error)))
{
g_warning ("%s", error->message);
g_clear_error (&error);
return;
}
for (i = 0; i < ret->len; i++)
{
IdeRuntime *runtime = g_ptr_array_index (ret, i);
ide_runtime_manager_add (self->manager, runtime);
}
self->runtimes = ret;
}
static void
gbp_xdg_runtime_provider_load (IdeRuntimeProvider *provider,
IdeRuntimeManager *manager)
{
GbpXdgRuntimeProvider *self = (GbpXdgRuntimeProvider *)provider;
g_autoptr(GTask) task = NULL;
g_assert (GBP_IS_XDG_RUNTIME_PROVIDER (self));
g_assert (IDE_IS_RUNTIME_MANAGER (manager));
ide_set_weak_pointer (&self->manager, manager);
self->cancellable = g_cancellable_new ();
task = g_task_new (self, self->cancellable, gbp_xdg_runtime_provider_load_cb, NULL);
g_task_run_in_thread (task, gbp_xdg_runtime_provider_load_worker);
}
static void
gbp_xdg_runtime_provider_unload (IdeRuntimeProvider *provider,
IdeRuntimeManager *manager)
{
GbpXdgRuntimeProvider *self = (GbpXdgRuntimeProvider *)provider;
g_assert (GBP_IS_XDG_RUNTIME_PROVIDER (self));
g_assert (IDE_IS_RUNTIME_MANAGER (manager));
if (self->cancellable != NULL)
g_cancellable_cancel (self->cancellable);
ide_clear_weak_pointer (&self->manager);
g_clear_object (&self->cancellable);
}
static void
gbp_xdg_runtime_provider_class_init (GbpXdgRuntimeProviderClass *klass)
{
}
static void
gbp_xdg_runtime_provider_init (GbpXdgRuntimeProvider *self)
{
}
static void
runtime_provider_iface_init (IdeRuntimeProviderInterface *iface)
{
iface->load = gbp_xdg_runtime_provider_load;
iface->unload = gbp_xdg_runtime_provider_unload;
}
/* gbp-xdg-runtime-provider.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef GBP_XDG_RUNTIME_PROVIDER_H
#define GBP_XDG_RUNTIME_PROVIDER_H
#include <ide.h>
G_BEGIN_DECLS
#define GBP_TYPE_XDG_RUNTIME_PROVIDER (gbp_xdg_runtime_provider_get_type())
G_DECLARE_FINAL_TYPE (GbpXdgRuntimeProvider, gbp_xdg_runtime_provider, GBP, XDG_RUNTIME_PROVIDER, GObject)
G_END_DECLS
#endif /* GBP_XDG_RUNTIME_PROVIDER_H */
/* gb-xdg-runtime.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "gbp-xdg-runtime.h"
struct _GbpXdgRuntime
{
IdeRuntime parent_instance;
gchar *sdk;
gchar *platform;
gchar *branch;
};
G_DEFINE_TYPE (GbpXdgRuntime, gbp_xdg_runtime, IDE_TYPE_RUNTIME)
enum {
PROP_0,
PROP_BRANCH,
PROP_PLATFORM,
PROP_SDK,
LAST_PROP
};
static GParamSpec *properties [LAST_PROP];
static gchar *
get_build_directory (GbpXdgRuntime *self)
{
IdeContext *context;
IdeProject *project;
g_assert (GBP_IS_XDG_RUNTIME (self));
context = ide_object_get_context (IDE_OBJECT (self));
project = ide_context_get_project (context);
return g_build_filename (g_get_user_cache_dir (),
"gnome-builder",
"builds",
ide_project_get_name (project),
"xdg-app",
ide_runtime_get_id (IDE_RUNTIME (self)),
NULL);
}
static gboolean
gbp_xdg_runtime_contains_program_in_path (IdeRuntime *runtime,
const gchar *program,
GCancellable *cancellable)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subprocess = NULL;
g_assert (IDE_IS_RUNTIME (runtime));
g_assert (program != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
launcher = ide_runtime_create_launcher (runtime, 0);
ide_subprocess_launcher_push_argv (launcher, "which");
ide_subprocess_launcher_push_argv (launcher, program);
subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, NULL);
return (subprocess != NULL) && g_subprocess_wait_check (subprocess, cancellable, NULL);
}
static void
gbp_xdg_runtime_prebuild_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GbpXdgRuntime *self = source_object;
g_autofree gchar *build_path = NULL;
g_autoptr(GFile) build_dir = NULL;
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subprocess = NULL;
g_autoptr(GFile) parent = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
g_assert (GBP_IS_XDG_RUNTIME (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
build_path = get_build_directory (self);
build_dir = g_file_new_for_path (build_path);
if (g_file_query_exists (build_dir, cancellable))
{
g_task_return_boolean (task, TRUE);
return;
}
parent = g_file_get_parent (build_dir);
if (!g_file_query_exists (parent, cancellable))
{
if (!g_file_make_directory_with_parents (parent, cancellable, &error))
{
g_task_return_error (task, error);
return;
}
}
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
subprocess = g_subprocess_launcher_spawn (launcher, &error,
"xdg-app",
"build-init",
build_path,
/* XXX: Fake name, probably okay, but
* can be proper once we get IdeConfiguration
* in place.
*/
"org.gnome.Builder.XdgApp.Build",
self->sdk,
self->platform,
self->branch,
NULL);
g_task_return_boolean (task, TRUE);
}
static void
gbp_xdg_runtime_prebuild_async (IdeRuntime *runtime,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GbpXdgRuntime *self = (GbpXdgRuntime *)runtime;
g_autoptr(GTask) task = NULL;
g_assert (GBP_IS_XDG_RUNTIME (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_run_in_thread (task, gbp_xdg_runtime_prebuild_worker);
}
static gboolean
gbp_xdg_runtime_prebuild_finish (IdeRuntime *runtime,
GAsyncResult *result,
GError **error)
{
GbpXdgRuntime *self = (GbpXdgRuntime *)runtime;
g_assert (GBP_IS_XDG_RUNTIME (self));
g_assert (G_IS_TASK (result));
return g_task_propagate_boolean (G_TASK (result), error);
}
static IdeSubprocessLauncher *
gbp_xdg_runtime_create_launcher (IdeRuntime *runtime,
GError **error)
{
IdeSubprocessLauncher *ret;
GbpXdgRuntime *self = (GbpXdgRuntime *)runtime;
g_return_val_if_fail (GBP_IS_XDG_RUNTIME (self), NULL);
ret = IDE_RUNTIME_CLASS (gbp_xdg_runtime_parent_class)->create_launcher (runtime, error);
if (ret != NULL)
{
g_autofree gchar *build_path = get_build_directory (self);
ide_subprocess_launcher_push_argv (ret, "xdg-app");
ide_subprocess_launcher_push_argv (ret, "build");
ide_subprocess_launcher_push_argv (ret, build_path);
}
return ret;
}
static void
gbp_xdg_runtime_prepare_configuration (IdeRuntime *runtime,
IdeConfiguration *configuration)
{
g_assert (IDE_IS_RUNTIME (runtime));
g_assert (IDE_IS_CONFIGURATION (configuration));
ide_configuration_set_prefix (configuration, "/app");
}
static void
gbp_xdg_runtime_get_property (GObject *object,
guint prop_id,