[gnome-builder] flatpak: implement basic bootstrapping for new runtime provider



commit 3c173ec3087d11c826f25f526ad6061b9b1d9c48
Author: Christian Hergert <chergert redhat com>
Date:   Tue May 4 09:24:30 2021 -0700

    flatpak: implement basic bootstrapping for new runtime provider
    
    We still need to do a bit more here so that we show the dialog again.
    However, that will get done via callbacks from the IpcFlatpakTransfer
    object in an upcoming commit.

 .../flatpak/daemon/ipc-flatpak-service-impl.c      | 133 +++++++--
 .../daemon/org.gnome.Builder.Flatpak.Service.xml   |  11 +-
 src/plugins/flatpak/daemon/test-flatpak.c          |   7 +
 src/plugins/flatpak/daemon/test-install.c          |  11 +-
 src/plugins/flatpak/gbp-flatpak-manifest.c         |  73 +++--
 src/plugins/flatpak/gbp-flatpak-manifest.h         |   1 +
 src/plugins/flatpak/gbp-flatpak-runtime-provider.c | 331 +++++++++++++++++++++
 7 files changed, 519 insertions(+), 48 deletions(-)
---
diff --git a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c 
b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
index 46f776ebd..55a2fc167 100644
--- a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
+++ b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
@@ -593,14 +593,19 @@ ipc_flatpak_service_impl_install_changed_cb (IpcFlatpakServiceImpl *self,
     }
 }
 
+typedef struct
+{
+  char *ref;
+  char *remote;
+} InstallRef;
+
 typedef struct
 {
   FlatpakInstallation   *installation;
   GDBusMethodInvocation *invocation;
   IpcFlatpakTransfer    *transfer;
-  char                  *remote;
-  char                  *ref;
   char                  *parent_window;
+  GArray                *refs;
 } InstallState;
 
 static void
@@ -609,8 +614,7 @@ install_state_free (InstallState *state)
   g_clear_object (&state->installation);
   g_clear_object (&state->invocation);
   g_clear_object (&state->transfer);
-  g_clear_pointer (&state->remote, g_free);
-  g_clear_pointer (&state->ref, g_free);
+  g_clear_pointer (&state->refs, g_array_unref);
   g_clear_pointer (&state->parent_window, g_free);
   g_slice_free (InstallState, state);
 }
@@ -664,6 +668,22 @@ connect_signals (FlatpakTransaction  *transaction,
   return TRUE;
 }
 
+static gboolean
+add_refs_to_transaction (FlatpakTransaction  *transaction,
+                         GArray              *refs,
+                         GError             **error)
+{
+  for (guint i = 0; i < refs->len; i++)
+    {
+      const InstallRef *ref = &g_array_index (refs, InstallRef, i);
+
+      if (!flatpak_transaction_add_install (transaction, ref->remote, ref->ref, NULL, error))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
 static void
 install_worker (GTask        *task,
                 gpointer      source_object,
@@ -677,7 +697,7 @@ install_worker (GTask        *task,
   g_assert (G_IS_TASK (task));
   g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (source_object));
   g_assert (state != NULL);
-  g_assert (state->remote != NULL);
+  g_assert (state->refs != NULL);
   g_assert (G_IS_DBUS_METHOD_INVOCATION (state->invocation));
   g_assert (IPC_IS_FLATPAK_TRANSFER (state->transfer));
   g_assert (FLATPAK_IS_INSTALLATION (state->installation));
@@ -686,7 +706,7 @@ install_worker (GTask        *task,
   ipc_flatpak_transfer_set_message (state->transfer, "");
 
   if (!(transaction = flatpak_transaction_new_for_installation (state->installation, NULL, &error)) ||
-      !flatpak_transaction_add_install (transaction, state->remote, state->ref, NULL, &error) ||
+      !add_refs_to_transaction (transaction, state->refs, &error) ||
       !connect_signals (transaction, state->transfer, &error) ||
       !flatpak_transaction_run (transaction, cancellable, &error))
     {
@@ -759,53 +779,78 @@ find_remote_for_ref (IpcFlatpakServiceImpl *self,
   return NULL;
 }
 
+static void
+clear_install_ref (gpointer data)
+{
+  InstallRef *r = data;
+
+  g_free (r->ref);
+  g_free (r->remote);
+}
+
 static gboolean
 ipc_flatpak_service_impl_install (IpcFlatpakService     *service,
                                   GDBusMethodInvocation *invocation,
-                                  const char            *full_ref_name,
+                                  const char * const    *full_ref_names,
                                   const char            *transfer_path,
                                   const char            *parent_window)
 {
   IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
   g_autoptr(IpcFlatpakTransfer) transfer = NULL;
-  g_autoptr(FlatpakRef) ref = NULL;
+  g_autoptr(GArray) refs = NULL;
   g_autoptr(GError) error = NULL;
   g_autoptr(GTask) task = NULL;
-  g_autofree char *remote = NULL;
   GDBusConnection *connection;
   InstallState *state;
 
   g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
   g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
-  g_assert (full_ref_name != NULL);
+  g_assert (full_ref_names != NULL);
   g_assert (transfer_path != NULL);
   g_assert (parent_window != NULL);
 
-  if (!(ref = flatpak_ref_parse (full_ref_name, &error)))
-    return complete_wrapped_error (invocation, error);
+  refs = g_array_new (FALSE, FALSE, sizeof (InstallRef));
+  g_array_set_clear_func (refs, clear_install_ref);
 
   connection = g_dbus_method_invocation_get_connection (invocation);
-  remote = find_remote_for_ref (self, ref);
   transfer = ipc_flatpak_transfer_proxy_new_sync (connection,
                                                   G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
                                                   NULL,
                                                   transfer_path,
                                                   NULL, NULL);
 
-  if (remote == NULL)
+  if (full_ref_names[0] == NULL)
     {
       g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
                                              G_DBUS_ERROR,
                                              G_DBUS_ERROR_FAILED,
-                                             "No configured remote contains ref");
+                                             "No refs to install");
       return TRUE;
     }
 
+  for (guint i = 0; full_ref_names[i]; i++)
+    {
+      g_autoptr(FlatpakRef) ref = flatpak_ref_parse (full_ref_names[i], NULL);
+      InstallRef iref = {0};
+
+      if (ref == NULL ||
+          !(iref.remote = find_remote_for_ref (self, ref)))
+        {
+          g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_FAILED,
+                                                 "No configured remote contains ref");
+          return TRUE;
+        }
+
+      iref.ref = g_strdup (full_ref_names[i]);
+      g_array_append_val (refs, iref);
+    }
+
   state = g_slice_new0 (InstallState);
   state->installation = ipc_flatpak_service_impl_ref_user_installation (self);
   state->invocation = g_steal_pointer (&invocation);
-  state->ref = g_strdup (full_ref_name);
-  state->remote = g_steal_pointer (&remote);
+  state->refs = g_array_ref (refs);
   state->parent_window = parent_window[0] ? g_strdup (parent_window) : NULL;
   state->transfer = g_object_ref (transfer);
 
@@ -1105,6 +1150,59 @@ ipc_flatpak_service_impl_resolve_extension (IpcFlatpakService     *service,
   return TRUE;
 }
 
+static gboolean
+ipc_flatpak_service_impl_get_runtime (IpcFlatpakService     *service,
+                                      GDBusMethodInvocation *invocation,
+                                      const char            *runtime_id)
+{
+  IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
+  g_autoptr(FlatpakRef) ref = NULL;
+  g_autofree char *full_name = NULL;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+  g_assert (runtime_id != NULL);
+
+  /* Homogenize names into runtime/name/arch/branch */
+  if (g_str_has_prefix (runtime_id, "runtime/"))
+    runtime_id += strlen ("runtime/");
+  full_name = g_strdup_printf ("runtime/%s", runtime_id);
+
+  if (!(ref = flatpak_ref_parse (full_name, NULL)))
+    {
+      g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_FAILED,
+                                             "Invalid runtime id %s",
+                                             full_name);
+      return TRUE;
+    }
+
+  for (guint i = 0; i < self->runtimes->len; i++)
+    {
+      const Runtime *runtime = g_ptr_array_index (self->runtimes, i);
+
+      if (str_equal0 (flatpak_ref_get_name (ref), runtime->name) &&
+          str_equal0 (flatpak_ref_get_arch (ref), runtime->arch) &&
+          str_equal0 (flatpak_ref_get_branch (ref), runtime->branch))
+        {
+          g_autoptr(GVariant) ret = runtime_to_variant (runtime);
+          ipc_flatpak_service_complete_get_runtime (service,
+                                                    g_steal_pointer (&invocation),
+                                                    ret);
+          return TRUE;
+        }
+    }
+
+  g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                         G_DBUS_ERROR,
+                                         G_DBUS_ERROR_FAILED,
+                                         "No such runtime %s",
+                                         full_name);
+
+  return TRUE;
+}
+
 static void
 service_iface_init (IpcFlatpakServiceIface *iface)
 {
@@ -1112,6 +1210,7 @@ service_iface_init (IpcFlatpakServiceIface *iface)
   iface->handle_list_runtimes = ipc_flatpak_service_impl_list_runtimes;
   iface->handle_runtime_is_known = ipc_flatpak_service_impl_runtime_is_known;
   iface->handle_install = ipc_flatpak_service_impl_install;
+  iface->handle_get_runtime = ipc_flatpak_service_impl_get_runtime;
   iface->handle_resolve_extension = ipc_flatpak_service_impl_resolve_extension;
 }
 
diff --git a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml 
b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
index 49d3ff505..df1b7d7bc 100644
--- a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
+++ b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
@@ -50,6 +50,13 @@
     <method name="ListRuntimes">
       <arg name="runtimes" direction="out" type="a(sssssssb)"/>
     </method>
+    <!--
+      Get information on the runtime.
+    -->
+    <method name="GetRuntime">
+      <arg name="id" direction="in" type="s"/>
+      <arg name="runtime" direction="out" type="(sssssssb)"/>
+    </method>
     <!--
       RuntimeIsKnown:
       @name: a runtime name to lookup in form name/arch/branch
@@ -63,14 +70,14 @@
     </method>
     <!--
       Install:
-      @full_ref_name: the full name of the target to install such as "runtime/org.gnome.Sdk/x86_64/master"
+      @full_ref_names: the full name of the targets to install such as "runtime/org.gnome.Sdk/x86_64/master"
       @transfer: the path of an org.gnome.Builder.Flatpak.Transfer object on the caller side
 
       Begins the installation of a ref from a configured remote. The operation will not
       complete until the installation has completed.
     -->
     <method name="Install">
-      <arg name="full_ref_name" direction="in" type="s"/>
+      <arg name="full_ref_name" direction="in" type="as"/>
       <arg name="transfer" direction="in" type="o"/>
       <arg name="parent_window" direction="in" type="s"/>
     </method>
diff --git a/src/plugins/flatpak/daemon/test-flatpak.c b/src/plugins/flatpak/daemon/test-flatpak.c
index f6a047672..aa911bb34 100644
--- a/src/plugins/flatpak/daemon/test-flatpak.c
+++ b/src/plugins/flatpak/daemon/test-flatpak.c
@@ -66,6 +66,7 @@ add_install_cb (GObject      *object,
   g_autofree gchar *resolved = NULL;
   g_autoptr(GMainLoop) main_loop = user_data;
   g_autoptr(GVariant) runtimes = NULL;
+  g_autoptr(GVariant) info = NULL;
   g_autoptr(GError) error = NULL;
   GVariantIter iter;
   gboolean is_known = TRUE;
@@ -119,6 +120,12 @@ add_install_cb (GObject      *object,
   sizestr = g_format_size (download_size);
   g_message ("  Found, Download Size: <=%s", sizestr);
 
+  g_message ("Getting runtime info for known runtime");
+  ret = ipc_flatpak_service_call_get_runtime_sync (service, "org.gnome.Sdk/x86_64/master", &info, NULL, 
&error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+  g_message ("  Found");
+
   g_message ("Resolving org.freedesktop.Sdk.Extension.rust-stable for org.gnome.Sdk/x86_64/master");
   ret = ipc_flatpak_service_call_resolve_extension_sync (service, "org.gnome.Sdk/x86_64/master", 
"org.freedesktop.Sdk.Extension.rust-stable", &resolved, NULL, &error);
   g_assert_no_error (error);
diff --git a/src/plugins/flatpak/daemon/test-install.c b/src/plugins/flatpak/daemon/test-install.c
index 33fd386dd..687e4c7de 100644
--- a/src/plugins/flatpak/daemon/test-install.c
+++ b/src/plugins/flatpak/daemon/test-install.c
@@ -71,15 +71,20 @@ main (gint argc,
   g_autoptr(GSubprocessLauncher) launcher = NULL;
   g_autoptr(IpcFlatpakService) service = NULL;
   g_autoptr(IpcFlatpakTransfer) transfer = NULL;
+  g_autoptr(GPtrArray) all = g_ptr_array_new ();
   GMainLoop *main_loop;
   gboolean ret;
 
-  if (argc != 2)
+  if (argc < 2)
     {
-      g_printerr ("usage: %s REF\n", argv[0]);
+      g_printerr ("usage: %s REF [REF..]\n", argv[0]);
       return EXIT_FAILURE;
     }
 
+  for (guint i = 1; i < argc; i++)
+    g_ptr_array_add (all, argv[i]);
+  g_ptr_array_add (all, NULL);
+
   launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE);
   subprocess = g_subprocess_launcher_spawn (launcher, &error,
 #if 0
@@ -122,7 +127,7 @@ main (gint argc,
 
   g_message ("Installing %s\n", argv[1]);
   ipc_flatpak_service_call_install (service,
-                                    argv[1],
+                                    (const char * const *)all->pdata,
                                     transfer_path,
                                     "",
                                     NULL,
diff --git a/src/plugins/flatpak/gbp-flatpak-manifest.c b/src/plugins/flatpak/gbp-flatpak-manifest.c
index 28a339b21..af686141a 100644
--- a/src/plugins/flatpak/gbp-flatpak-manifest.c
+++ b/src/plugins/flatpak/gbp-flatpak-manifest.c
@@ -30,7 +30,8 @@
 #include "gbp-flatpak-runtime.h"
 #include "gbp-flatpak-util.h"
 
-#include "ipc-flatpak-service.h"
+#include "daemon/ipc-flatpak-service.h"
+#include "daemon/ipc-flatpak-util.h"
 
 struct _GbpFlatpakManifest
 {
@@ -551,43 +552,51 @@ gbp_flatpak_manifest_set_file (GbpFlatpakManifest *self,
 
 static IdeRuntime *
 find_extension (GbpFlatpakManifest *self,
-                const gchar        *name)
+                const gchar        *runtime_id)
 {
-  g_autoptr(FlatpakInstalledRef) ref = NULL;
   g_autoptr(IpcFlatpakService) service = NULL;
-  g_autofree char *resolved = NULL;
-  GbpFlatpakApplicationAddin *addin;
-  GbpFlatpakRuntime *ret = NULL;
+  g_autoptr(GVariant) info = NULL;
   GbpFlatpakClient *client;
   IdeContext *context;
-  g_autoptr(GBytes) bytes = NULL;
 
   g_assert (GBP_IS_FLATPAK_MANIFEST (self));
-  g_assert (name != NULL);
+  g_assert (runtime_id != NULL);
 
   context = ide_object_get_context (IDE_OBJECT (self));
 
-#if 0
   if ((client = gbp_flatpak_client_from_context (context)) &&
       (service = gbp_flatpak_client_get_service (client, NULL, NULL)) &&
-      ipc_flatpak_service_call_resolve_extension_sync (service, self->sdk, name, &resolved, NULL, NULL))
+      ipc_flatpak_service_call_get_runtime_sync (service, runtime_id, &info, NULL, NULL))
     {
-
-      addin = gbp_flatpak_application_addin_get_default ();
-      ref = gbp_flatpak_application_addin_find_extension (addin, self->sdk, name);
-      if (ref != NULL)
-        ret = gbp_flatpak_runtime_new (flatpak_ref_get_name (FLATPAK_REF (ref)),
-                                       flatpak_ref_get_arch (FLATPAK_REF (ref)),
-                                       flatpak_ref_get_branch (FLATPAK_REF (ref)),
-                                       (bytes = flatpak_installed_ref_load_metadata (ref, NULL, NULL)),
-                                       flatpak_installed_ref_get_deploy_dir (ref),
-                                       TRUE, NULL, NULL);
+      GbpFlatpakRuntime *ret = NULL;
+      const gchar *name;
+      const gchar *arch;
+      const gchar *branch;
+      const gchar *sdk_name;
+      const gchar *sdk_branch;
+      const gchar *deploy_dir;
+      const gchar *metadata;
+      gboolean is_extension;
+
+      if (runtime_variant_parse (info,
+                                 &name, &arch, &branch,
+                                 &sdk_name, &sdk_branch,
+                                 &deploy_dir,
+                                 &metadata,
+                                 &is_extension))
+        ret = gbp_flatpak_runtime_new (name,
+                                       arch,
+                                       branch,
+                                       sdk_name,
+                                       sdk_branch,
+                                       deploy_dir,
+                                       metadata,
+                                       is_extension);
+
+      return IDE_RUNTIME (g_steal_pointer (&ret));
     }
-#endif
-
-  g_printerr ("TODO: fix find_extension()\n");
 
-  return IDE_RUNTIME (g_steal_pointer (&ret));
+  IDE_RETURN (NULL);
 }
 
 static GPtrArray *
@@ -928,7 +937,6 @@ apply_changes_to_tree (GbpFlatpakManifest *self)
         }
 
     }
-
 }
 
 static void
@@ -1098,7 +1106,12 @@ gbp_flatpak_manifest_get_runtimes (GbpFlatpakManifest *self,
   if (self->sdk_extensions != NULL)
     {
       for (guint i = 0; self->sdk_extensions[i]; i++)
-        g_ptr_array_add (ar, g_strdup_printf ("%s/%s/", self->sdk_extensions[i], for_arch));
+        {
+          if (strchr (self->sdk_extensions[i], '/') != NULL)
+            g_ptr_array_add (ar, g_strdup (self->sdk_extensions[i]));
+          else
+            g_ptr_array_add (ar, g_strdup_printf ("%s/%s/", self->sdk_extensions[i], for_arch));
+        }
     }
 
   g_ptr_array_add (ar, NULL);
@@ -1155,3 +1168,11 @@ gbp_flatpak_manifest_resolve_extensions (GbpFlatpakManifest *self,
         }
     }
 }
+
+const char *
+gbp_flatpak_manifest_get_branch (GbpFlatpakManifest *self)
+{
+  g_return_val_if_fail (GBP_IS_FLATPAK_MANIFEST (self), NULL);
+
+  return self->runtime_version;
+}
diff --git a/src/plugins/flatpak/gbp-flatpak-manifest.h b/src/plugins/flatpak/gbp-flatpak-manifest.h
index fbda0e2e2..320bb1816 100644
--- a/src/plugins/flatpak/gbp-flatpak-manifest.h
+++ b/src/plugins/flatpak/gbp-flatpak-manifest.h
@@ -44,6 +44,7 @@ const gchar         *gbp_flatpak_manifest_get_sdk            (GbpFlatpakManifest
 const gchar         *gbp_flatpak_manifest_get_platform       (GbpFlatpakManifest   *self);
 gchar              **gbp_flatpak_manifest_get_runtimes       (GbpFlatpakManifest   *self,
                                                               const gchar          *for_arch);
+const char          *gbp_flatpak_manifest_get_branch         (GbpFlatpakManifest   *self);
 void                 gbp_flatpak_manifest_save_async         (GbpFlatpakManifest   *self,
                                                               GCancellable         *cancellable,
                                                               GAsyncReadyCallback   callback,
diff --git a/src/plugins/flatpak/gbp-flatpak-runtime-provider.c 
b/src/plugins/flatpak/gbp-flatpak-runtime-provider.c
index bbe920687..df6f4c202 100644
--- a/src/plugins/flatpak/gbp-flatpak-runtime-provider.c
+++ b/src/plugins/flatpak/gbp-flatpak-runtime-provider.c
@@ -22,13 +22,17 @@
 
 #include "config.h"
 
+#include <glib/gi18n.h>
 #include <libide-foundry.h>
 
 #include "gbp-flatpak-client.h"
+#include "gbp-flatpak-manifest.h"
 #include "gbp-flatpak-runtime.h"
 #include "gbp-flatpak-runtime-provider.h"
+#include "gbp-flatpak-util.h"
 
 #include "daemon/ipc-flatpak-service.h"
+#include "daemon/ipc-flatpak-transfer.h"
 #include "daemon/ipc-flatpak-util.h"
 
 struct _GbpFlatpakRuntimeProvider
@@ -193,9 +197,336 @@ gbp_flatpak_runtime_provider_unload (IdeRuntimeProvider *provider,
   g_ptr_array_remove_range (self->runtimes, 0, self->runtimes->len);
 }
 
+typedef struct
+{
+  char               *runtime_id;
+  char               *transfer_path;
+  GPtrArray          *to_install;
+  IpcFlatpakTransfer *transfer;
+  IpcFlatpakService  *service;
+  IdeNotification    *notif;
+} Bootstrap;
+
+static void
+bootstrap_free (Bootstrap *b)
+{
+  g_clear_pointer (&b->runtime_id, g_free);
+  g_clear_pointer (&b->transfer_path, g_free);
+  g_clear_object (&b->notif);
+  g_clear_object (&b->service);
+  g_clear_object (&b->transfer);
+  g_clear_pointer (&b->to_install, g_ptr_array_unref);
+  g_slice_free (Bootstrap, b);
+}
+
+static IdeRuntime *
+find_runtime (GbpFlatpakRuntimeProvider *self,
+              const char                *runtime_id)
+{
+  IdeRuntimeManager *manager;
+  IdeContext *context;
+  IdeRuntime *runtime;
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (self));
+  g_assert (runtime_id != NULL);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  manager = ide_runtime_manager_from_context (context);
+  runtime = ide_runtime_manager_get_runtime (manager, runtime_id);
+
+  return runtime ? g_object_ref (runtime) : NULL;
+}
+
+static void
+gbp_flatpak_runtime_provider_bootstrap_install_cb (GObject      *object,
+                                                   GAsyncResult *result,
+                                                   gpointer      user_data)
+{
+  GbpFlatpakRuntimeProvider *self;
+  IpcFlatpakService *service = (IpcFlatpakService *)object;
+  g_autoptr(IdeRuntime) runtime = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  Bootstrap *state;
+
+  IDE_ENTRY;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE (service));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  state = ide_task_get_task_data (task);
+  self = ide_task_get_source_object (task);
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (self));
+  g_assert (state != NULL);
+  g_assert (IDE_IS_NOTIFICATION (state->notif));
+
+  if (!ipc_flatpak_service_call_install_finish (service, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else if (!(runtime = find_runtime (self, state->runtime_id)))
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_CANCELLED,
+                               "Operation was cancelled");
+  else
+    ide_task_return_pointer (task, g_steal_pointer (&runtime), g_object_unref);
+
+  ide_notification_withdraw (state->notif);
+
+  IDE_EXIT;
+}
+
+static gboolean
+gbp_flatpak_runtime_provider_bootstrap_complete (gpointer data)
+{
+  GbpFlatpakRuntimeProvider *self;
+  g_autoptr(IdeRuntime) runtime = NULL;
+  IdeTask *task = data;
+  Bootstrap *state;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_TASK (task));
+
+  self = ide_task_get_source_object (task);
+  state = ide_task_get_task_data (task);
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (self));
+  g_assert (state->to_install != NULL);
+  g_assert (IPC_IS_FLATPAK_SERVICE (state->service));
+  g_assert (state->transfer_path != NULL);
+  g_assert (IDE_IS_NOTIFICATION (state->notif));
+
+  ide_notification_attach (state->notif, IDE_OBJECT (self));
+
+  ipc_flatpak_service_call_install (state->service,
+                                    (const char * const *)state->to_install->pdata,
+                                    state->transfer_path,
+                                    "",
+                                    ide_task_get_cancellable (task),
+                                    gbp_flatpak_runtime_provider_bootstrap_install_cb,
+                                    g_object_ref (task));
+
+  IDE_RETURN (G_SOURCE_REMOVE);
+}
+
+static void
+gbp_flatpak_runtime_provider_bootstrap (IdeTask      *task,
+                                        gpointer      source_object,
+                                        gpointer      task_data,
+                                        GCancellable *cancellable)
+{
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GbpFlatpakClient) client = NULL;
+  g_autoptr(IpcFlatpakService) service = NULL;
+  g_autoptr(IpcFlatpakTransfer) transfer = NULL;
+  g_autoptr(IdeNotification) notif = NULL;
+  Bootstrap *state = task_data;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_TASK (task));
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (source_object));
+  g_assert (state != NULL);
+  g_assert (state->to_install != NULL);
+  g_assert (state->to_install->len > 0);
+
+  if (!(context = ide_object_ref_context (source_object)) ||
+      !(client = gbp_flatpak_client_ensure (context)) ||
+      !(service = gbp_flatpak_client_get_service (client, NULL, NULL)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_CANCELLED,
+                                 "Operation was cancelled");
+      IDE_GOTO (failure);
+    }
+
+  /* Filter out anything we can't find or is already installed */
+  for (guint i = state->to_install->len; i > 0; i--)
+    {
+      const char *id = g_ptr_array_index (state->to_install, i-1);
+      gboolean is_known = FALSE;
+      gint64 size = 0;
+
+      if (!ipc_flatpak_service_call_runtime_is_known_sync (service, id, &is_known, &size, NULL, NULL) ||
+          is_known == TRUE)
+        g_ptr_array_remove_index (state->to_install, i-1);
+    }
+
+  /* Now create a transfer to install the rest */
+  if (state->to_install->len > 0)
+    {
+      g_autofree char *guid = g_dbus_generate_guid ();
+      g_autofree char *transfer_path = g_strdup_printf ("/org/gnome/Builder/Flatpak/Transfer/%s", guid);
+      g_autoptr(GError) error = NULL;
+
+      notif = ide_notification_new ();
+      ide_notification_set_icon_name (notif, "system-software-install");
+      ide_notification_set_title (notif, _("Installing Necessary SDKs"));
+      ide_notification_set_body (notif, _("Builder is installing Software Development Kits necessary for 
building your application."));
+      ide_notification_set_has_progress (notif, TRUE);
+      ide_notification_set_progress_is_imprecise (notif, FALSE);
+
+      transfer = ipc_flatpak_transfer_skeleton_new ();
+      g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (transfer),
+                                        g_dbus_proxy_get_connection (G_DBUS_PROXY (service)),
+                                        transfer_path,
+                                        NULL);
+
+      g_object_bind_property (transfer, "fraction", notif, "progress", G_BINDING_SYNC_CREATE);
+      g_object_bind_property (transfer, "message", notif, "body", G_BINDING_DEFAULT);
+
+      state->service = g_object_ref (service);
+      state->notif = g_object_ref (notif);
+      state->transfer_path = g_strdup (transfer_path);
+
+      g_ptr_array_add (state->to_install, NULL);
+    }
+
+  g_timeout_add_full (G_PRIORITY_DEFAULT,
+                      0,
+                      gbp_flatpak_runtime_provider_bootstrap_complete,
+                      g_object_ref (task),
+                      g_object_unref);
+
+failure:
+
+  if (transfer != NULL)
+    g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (transfer));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_flatpak_runtime_provider_bootstrap_async (IdeRuntimeProvider  *provider,
+                                              IdePipeline         *pipeline,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
+{
+  GbpFlatpakRuntimeProvider *self = (GbpFlatpakRuntimeProvider *)provider;
+  g_autoptr(IdeTask) task = NULL;
+  g_autofree char *full_sdk = NULL;
+  g_autofree char *full_platform = NULL;
+  const char *arch;
+  Bootstrap *state;
+  IdeConfig *config;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (self));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  arch = ide_pipeline_get_arch (pipeline);
+  config = ide_pipeline_get_config (pipeline);
+
+  state = g_slice_new0 (Bootstrap);
+  state->runtime_id = g_strdup (ide_config_get_runtime_id (config));
+  state->to_install = g_ptr_array_new_with_free_func (g_free);
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_flatpak_runtime_provider_bootstrap_async);
+  ide_task_set_task_data (task, state, bootstrap_free);
+
+  /* Collect all of the runtimes that could be needed */
+  if (GBP_IS_FLATPAK_MANIFEST (config))
+    {
+      const char *sdk = gbp_flatpak_manifest_get_sdk (GBP_FLATPAK_MANIFEST (config));
+      const char *platform = gbp_flatpak_manifest_get_platform (GBP_FLATPAK_MANIFEST (config));
+      const char *branch = gbp_flatpak_manifest_get_branch (GBP_FLATPAK_MANIFEST (config));
+      const char * const *extensions = gbp_flatpak_manifest_get_sdk_extensions (GBP_FLATPAK_MANIFEST 
(config));
+
+      if (sdk == NULL)
+        sdk = platform;
+
+      if (branch == NULL)
+        branch = "master";
+
+      full_sdk = g_strdup_printf ("runtime/%s/%s/%s", sdk, arch, branch);
+      full_platform = g_strdup_printf ("runtime/%s/%s/%s", platform, arch, branch);
+
+      g_ptr_array_add (state->to_install, g_strdup (full_sdk));
+      if (g_strcmp0 (full_sdk, full_platform) != 0)
+        g_ptr_array_add (state->to_install, g_strdup (full_platform));
+
+      if (extensions != NULL)
+        {
+          for (guint i = 0; extensions[i]; i++)
+            g_ptr_array_add (state->to_install, g_strdup_printf ("runtime/%s", extensions[i]));
+        }
+    }
+  else
+    {
+      const char *runtime_id = ide_config_get_runtime_id (config);
+
+      if (g_str_has_prefix (runtime_id, "flatpak:"))
+        {
+          g_autofree char *resolved_id = NULL;
+          g_autofree char *resolved_arch = NULL;
+          g_autofree char *resolved_branch = NULL;
+
+          if (gbp_flatpak_split_id (runtime_id + strlen ("flatpak:"),
+                                    &resolved_id,
+                                    &resolved_arch,
+                                    &resolved_branch))
+            {
+              full_sdk = g_strdup_printf ("runtime/%s/%s/%s",
+                                          resolved_id,
+                                          arch,
+                                          resolved_branch ?: "master");
+              g_ptr_array_add (state->to_install, g_strdup (full_sdk));
+            }
+        }
+    }
+
+  if (state->to_install->len == 0)
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "No runtime provided to bootstrap");
+      g_warn_if_reached ();
+      IDE_EXIT;
+    }
+
+  ide_task_run_in_thread (task, gbp_flatpak_runtime_provider_bootstrap);
+
+  IDE_EXIT;
+}
+
+static IdeRuntime *
+gbp_flatpak_runtime_provider_bootstrap_finish (IdeRuntimeProvider  *provider,
+                                               GAsyncResult        *result,
+                                               GError             **error)
+{
+  IdeRuntime *ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME_PROVIDER (provider), NULL);
+  g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+  ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static gboolean
+gbp_flatpak_runtime_provider_provides (IdeRuntimeProvider *provider,
+                                       const char         *runtime_id)
+{
+  return g_str_has_prefix (runtime_id, "flatpak:");
+}
+
 static void
 runtime_provider_iface_init (IdeRuntimeProviderInterface *iface)
 {
   iface->load = gbp_flatpak_runtime_provider_load;
   iface->unload = gbp_flatpak_runtime_provider_unload;
+  iface->bootstrap_async = gbp_flatpak_runtime_provider_bootstrap_async;
+  iface->bootstrap_finish = gbp_flatpak_runtime_provider_bootstrap_finish;
+  iface->provides = gbp_flatpak_runtime_provider_provides;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]