[gnome-builder] flatpak: add flatpak installation management to application addin



commit 9fd9e222b767bc414542df01809558f2fffe6c8d
Author: Christian Hergert <chergert redhat com>
Date:   Wed Feb 15 21:02:45 2017 -0800

    flatpak: add flatpak installation management to application addin
    
    This allows us to simplify a bunch of things by moving the management of
    the flatpak installation instances to the application addin. Other moving
    parts will need to be adapted to wrap this.

 plugins/flatpak/gbp-flatpak-application-addin.c |  880 ++++++++++++++++++++++-
 plugins/flatpak/gbp-flatpak-application-addin.h |   32 +
 2 files changed, 894 insertions(+), 18 deletions(-)
---
diff --git a/plugins/flatpak/gbp-flatpak-application-addin.c b/plugins/flatpak/gbp-flatpak-application-addin.c
index 83994bb..2963c3b 100644
--- a/plugins/flatpak/gbp-flatpak-application-addin.c
+++ b/plugins/flatpak/gbp-flatpak-application-addin.c
@@ -16,61 +16,905 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "gbp-flatpak-application-addin"
+
+#include <flatpak.h>
+
 #include "gbp-flatpak-application-addin.h"
 #include "gbp-flatpak-runtime.h"
 
+typedef struct
+{
+  FlatpakInstallation        *installation;
+  GFileMonitor               *monitor;
+  GbpFlatpakApplicationAddin *self;
+} InstallInfo;
+
+typedef struct
+{
+  gchar               *id;
+  gchar               *arch;
+  gchar               *branch;
+  GPtrArray           *installations;
+  IdeProgress         *progress;
+  FlatpakInstalledRef *ref;
+  guint                did_added : 1;
+} InstallRequest;
+
+typedef struct
+{
+  gchar     *id;
+  gchar     *arch;
+  gchar     *branch;
+  gchar     *sdk_id;
+  gchar     *sdk_arch;
+  gchar     *sdk_branch;
+  GPtrArray *installations;
+} LocateSdk;
+
 struct _GbpFlatpakApplicationAddin
 {
-  GObject parent_instance;
+  GObject    parent_instance;
+
+  /*
+   * @installations is never modified after creation. Whenever we reload
+   * the runtimes we reload completely, so that threaded operations that
+   * are accessing this structure (albeit with a full reference to the
+   * ptrarray) will not be affected.
+   */
+  GPtrArray *installations;
 };
 
-static void application_addin_iface_init (IdeApplicationAddinInterface *iface);
+typedef struct
+{
+  const gchar *name;
+  const gchar *url;
+} BuiltinFlatpakRepo;
 
-G_DEFINE_TYPE_EXTENDED (GbpFlatpakApplicationAddin,
-                        gbp_flatpak_application_addin,
-                        G_TYPE_OBJECT,
-                        0,
-                        G_IMPLEMENT_INTERFACE (IDE_TYPE_APPLICATION_ADDIN, application_addin_iface_init))
+enum {
+  RUNTIME_ADDED,
+  N_SIGNALS
+};
+
+static GbpFlatpakApplicationAddin *instance;
+static guint signals [N_SIGNALS];
+static BuiltinFlatpakRepo builtin_flatpak_repos[] = {
+  { "gnome",         "https://sdk.gnome.org/gnome.flatpakrepo"; },
+  { "gnome-nightly", "https://sdk.gnome.org/gnome-nightly.flatpakrepo"; },
+};
+
+static void gbp_flatpak_application_addin_reload (GbpFlatpakApplicationAddin *self);
+
+static gboolean
+is_ignored (FlatpakRef *ref)
+{
+  const gchar *name = flatpak_ref_get_name (ref);
+
+  return g_str_has_suffix (name, ".Locale") ||
+         g_str_has_suffix (name, ".Debug") ||
+         g_str_has_suffix (name, ".Var");
+}
 
 static void
-gbp_flatpak_application_addin_load (IdeApplicationAddin *addin,
-                                    IdeApplication      *application)
+install_info_installation_changed (GFileMonitor      *monitor,
+                                   GFile             *file,
+                                   GFile             *other_file,
+                                   GFileMonitorEvent  event_type,
+                                   InstallInfo       *info)
+{
+  g_autoptr(GbpFlatpakApplicationAddin) self = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_FILE_MONITOR (monitor));
+  g_assert (G_IS_FILE (file));
+  g_assert (!other_file || G_IS_FILE (other_file));
+  g_assert (info != NULL);
+
+  self = g_object_ref (info->self);
+
+  gbp_flatpak_application_addin_reload (self);
+
+  IDE_EXIT;
+}
+
+static void
+install_info_free (InstallInfo *info)
+{
+  g_assert (info != NULL);
+  g_assert (!info->installation || FLATPAK_IS_INSTALLATION (info->installation));
+  g_assert (!info->monitor || G_IS_FILE_MONITOR (info->monitor));
+
+  if (info->monitor != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (info->monitor,
+                                            G_CALLBACK (install_info_installation_changed),
+                                            info);
+    }
+
+  ide_clear_weak_pointer (&info->self);
+  g_clear_object (&info->monitor);
+  g_clear_object (&info->installation);
+
+  g_slice_free (InstallInfo, info);
+}
+
+static InstallInfo *
+install_info_new (GbpFlatpakApplicationAddin *self,
+                  FlatpakInstallation        *installation)
+{
+  InstallInfo *info;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (FLATPAK_IS_INSTALLATION (installation));
+
+  info = g_slice_new0 (InstallInfo);
+  info->installation = g_object_ref (installation);
+  info->monitor = flatpak_installation_create_monitor (installation, NULL, NULL);
+  ide_set_weak_pointer (&info->self, self);
+
+  if (info->monitor != NULL)
+    {
+      g_signal_connect (info->monitor,
+                        "changed",
+                        G_CALLBACK (install_info_installation_changed),
+                        info);
+    }
+
+  return info;
+}
+
+static void
+install_request_free (InstallRequest *request)
+{
+  g_clear_pointer (&request->id, g_free);
+  g_clear_pointer (&request->arch, g_free);
+  g_clear_pointer (&request->branch, g_free);
+  g_clear_pointer (&request->installations, g_ptr_array_unref);
+  g_clear_object (&request->progress);
+  g_clear_object (&request->ref);
+  g_slice_free (InstallRequest, request);
+}
+
+static void
+locate_sdk_free (LocateSdk *locate)
+{
+  g_clear_pointer (&locate->id, g_free);
+  g_clear_pointer (&locate->arch, g_free);
+  g_clear_pointer (&locate->branch, g_free);
+  g_clear_pointer (&locate->sdk_id, g_free);
+  g_clear_pointer (&locate->sdk_arch, g_free);
+  g_clear_pointer (&locate->sdk_branch, g_free);
+  g_clear_pointer (&locate->installations, g_ptr_array_unref);
+  g_slice_free (LocateSdk, locate);
+}
+
+static gboolean
+gbp_flatpak_application_addin_remove_old_repo (GbpFlatpakApplicationAddin  *self,
+                                               GCancellable                *cancellable,
+                                               GError                     **error)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
   g_autoptr(IdeSubprocess) process = NULL;
-  g_autoptr(GError) error = NULL;
+  gboolean ret = FALSE;
 
-  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (addin));
-  g_assert (IDE_IS_APPLICATION (application));
+  IDE_ENTRY;
 
   launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE | 
G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+
   ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
   ide_subprocess_launcher_push_argv (launcher, "flatpak");
   ide_subprocess_launcher_push_argv (launcher, "remote-delete");
   ide_subprocess_launcher_push_argv (launcher, "--user");
   ide_subprocess_launcher_push_argv (launcher, "--force");
   ide_subprocess_launcher_push_argv (launcher, FLATPAK_REPO_NAME);
-  process = ide_subprocess_launcher_spawn (launcher, NULL, &error);
+
+  process = ide_subprocess_launcher_spawn (launcher, cancellable, error);
+
   if (process == NULL)
+    ret = ide_subprocess_wait (process, cancellable, error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+gbp_flatpak_application_addin_reload (GbpFlatpakApplicationAddin *self)
+{
+  g_autofree gchar *user_path = NULL;
+  g_autoptr(GFile) user_file = NULL;
+  g_autoptr(GPtrArray) system_installs = NULL;
+  g_autoptr(GPtrArray) runtimes = NULL;
+  g_autoptr(FlatpakInstallation) user = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+
+  /* Clear any previous installations */
+  g_clear_pointer (&self->installations, g_ptr_array_unref);
+  self->installations = g_ptr_array_new_with_free_func ((GDestroyNotify)install_info_free);
+
+  /*
+   * First we want to load the user installation so that it is at index 0.
+   * This naturally prefers the user installation for various operations
+   * which is precisely what we want.
+   *
+   * We can't use flatpak_installation_new_user() since that will not map to
+   * the users real flatpak user installation. It will instead map to the
+   * reidrected XDG_DATA_DIRS version. Therefore, we synthesize the path to the
+   * location we know it should be at.
+   */
+  user_path = g_build_filename (g_get_home_dir (), ".local", "share", "flatpak", NULL);
+  user_file = g_file_new_for_path (user_path);
+  user = flatpak_installation_new_for_path (user_file, TRUE, NULL, NULL);
+  if (user != NULL)
+    g_ptr_array_add (self->installations, install_info_new (self, user));
+
+  /*
+   * Now load any of the system installations. As of more recent flatpak
+   * versions, we can have multiple system installations. So try to load all of
+   * them.
+   */
+  system_installs = flatpak_get_system_installations (NULL, NULL);
+
+  if (system_installs != NULL)
     {
-      g_warning ("%s", error->message);
-      return;
+      for (guint i = 0; i < system_installs->len; i++)
+        {
+          FlatpakInstallation *installation = g_ptr_array_index (system_installs, i);
+          g_ptr_array_add (self->installations, install_info_new (self, installation));
+        }
     }
-  ide_subprocess_wait (process, NULL, NULL);
+
+  /*
+   * Now notify any listeners of new runtimes. They are responsible for
+   * dealing with deduplicating by id/arch/branch.
+   */
+  runtimes = gbp_flatpak_application_addin_get_runtimes (self);
+
+  if (runtimes != NULL)
+    {
+      for (guint i = 0; i < runtimes->len; i++)
+        {
+          FlatpakRef *ref = g_ptr_array_index (runtimes, i);
+
+          g_signal_emit (self, signals[RUNTIME_ADDED], 0, ref);
+        }
+    }
+
+  IDE_EXIT;
 }
 
 static void
-gbp_flatpak_application_addin_class_init (GbpFlatpakApplicationAddinClass *klass)
+gbp_flatpak_application_addin_load (IdeApplicationAddin *addin,
+                                    IdeApplication      *application)
 {
+  GbpFlatpakApplicationAddin *self = (GbpFlatpakApplicationAddin *)addin;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (IDE_IS_APPLICATION (application));
+
+  instance = self;
+
+  gbp_flatpak_application_addin_remove_old_repo (self, NULL, NULL);
+  gbp_flatpak_application_addin_reload (self);
+
+  IDE_EXIT;
 }
 
 static void
-gbp_flatpak_application_addin_init (GbpFlatpakApplicationAddin *self)
+gbp_flatpak_application_addin_unload (IdeApplicationAddin *addin,
+                                      IdeApplication      *application)
+{
+  GbpFlatpakApplicationAddin *self = (GbpFlatpakApplicationAddin *)addin;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (IDE_IS_APPLICATION (application));
+
+  instance = NULL;
+
+  g_clear_pointer (&self->installations, g_ptr_array_unref);
+  gbp_flatpak_application_addin_remove_old_repo (self, NULL, NULL);
+
+  IDE_EXIT;
+}
+
+/**
+ * gbp_flatpak_application_addin_get_runtimes:
+ *
+ * Gets an array of runtimes available on the system.
+ *
+ * Returns: (transfer container) (element-type Flatpak.InstalledRef): Array of runtimes.
+ */
+GPtrArray *
+gbp_flatpak_application_addin_get_runtimes (GbpFlatpakApplicationAddin *self)
 {
+  GPtrArray *ret;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+
+  ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (guint i = 0; i < self->installations->len; i++)
+    {
+      InstallInfo *info = g_ptr_array_index (self->installations, i);
+      g_autoptr(GPtrArray) ar = NULL;
+
+      ar = flatpak_installation_list_installed_refs_by_kind (info->installation,
+                                                             FLATPAK_REF_KIND_RUNTIME,
+                                                             NULL,
+                                                             NULL);
+
+      if (ar != NULL)
+        {
+          for (guint j = 0; j < ar->len; j++)
+            {
+              FlatpakInstalledRef *ref = g_ptr_array_index (ar, j);
+
+              if (!is_ignored (FLATPAK_REF (ref)))
+                g_ptr_array_add (ret, g_object_ref (ref));
+            }
+        }
+    }
+
+  IDE_RETURN (ret);
+}
+
+GbpFlatpakApplicationAddin *
+gbp_flatpak_application_addin_get_default (void)
+{
+  return instance;
+}
+
+static void
+gbp_flatpak_application_addin_install_completed (GbpFlatpakApplicationAddin *self,
+                                                 GParamSpec                 *pspec,
+                                                 GTask                      *task)
+{
+  InstallRequest *request;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (pspec != NULL);
+  g_assert (G_IS_TASK (task));
+
+  request = g_task_get_task_data (task);
+
+  if (request->ref != NULL && !request->did_added)
+    {
+      request->did_added = TRUE;
+      g_signal_emit (self, signals[RUNTIME_ADDED], 0, request->ref);
+    }
+
+  IDE_EXIT;
+}
+
+static void
+gbp_flatpak_application_addin_install_runtime_worker (GTask        *task,
+                                                      gpointer      source_object,
+                                                      gpointer      task_data,
+                                                      GCancellable *cancellable)
+{
+  GbpFlatpakApplicationAddin *self = source_object;
+  InstallRequest *request = task_data;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (request != NULL);
+  g_assert (request->id != NULL);
+  g_assert (request->arch != NULL);
+  g_assert (request->branch != NULL);
+  g_assert (request->installations != NULL);
+
+  /*
+   * First ensure we have our repositories that we need to locate various
+   * runtimes for GNOME.
+   */
+  for (guint i = 0; i < G_N_ELEMENTS (builtin_flatpak_repos); i++)
+    {
+      g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+      g_autoptr(IdeSubprocess) subprocess = NULL;
+      g_autoptr(GError) error = NULL;
+      const gchar *name = builtin_flatpak_repos[i].name;
+      const gchar *url = builtin_flatpak_repos[i].url;
+
+      launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                              G_SUBPROCESS_FLAGS_STDERR_PIPE);
+      ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+      ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+      ide_subprocess_launcher_push_argv (launcher, "flatpak");
+      ide_subprocess_launcher_push_argv (launcher, "remote-add");
+      ide_subprocess_launcher_push_argv (launcher, "--user");
+      ide_subprocess_launcher_push_argv (launcher, "--if-not-exists");
+      ide_subprocess_launcher_push_argv (launcher, "--from");
+      ide_subprocess_launcher_push_argv (launcher, name);
+      ide_subprocess_launcher_push_argv (launcher, url);
+
+      subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error);
+
+      if (subprocess == NULL || !ide_subprocess_wait_check (subprocess, cancellable, &error))
+        {
+          g_task_return_error (task, g_steal_pointer (&error));
+          IDE_EXIT;
+        }
+    }
+
+  /*
+   * First we want to try to locate the runtime within a previous install.
+   * If so, we will just update from that.
+   */
+  for (guint i = 0; i < request->installations->len; i++)
+    {
+      InstallInfo *info = g_ptr_array_index (request->installations, i);
+      FlatpakInstallation *installation = info->installation;
+      g_autoptr(GPtrArray) refs = NULL;
+
+      g_assert (FLATPAK_IS_INSTALLATION (installation));
+
+      refs = flatpak_installation_list_installed_refs (installation, cancellable, NULL);
+      if (refs == NULL)
+        continue;
+
+      for (guint j = 0; j < refs->len; j++)
+        {
+          FlatpakInstalledRef *ref = g_ptr_array_index (refs, j);
+          const gchar *id = flatpak_ref_get_name (FLATPAK_REF (ref));
+          const gchar *arch = flatpak_ref_get_arch (FLATPAK_REF (ref));
+          const gchar *branch = flatpak_ref_get_branch (FLATPAK_REF (ref));
+
+          g_assert (FLATPAK_IS_INSTALLED_REF (ref));
+
+          if (g_strcmp0 (request->id, id) == 0 &&
+              g_strcmp0 (request->branch, branch) == 0 &&
+              g_strcmp0 (request->arch, arch) == 0)
+            {
+              g_autoptr(GError) error = NULL;
+
+              request->ref = flatpak_installation_update (installation,
+                                                          FLATPAK_UPDATE_FLAGS_NONE,
+                                                          FLATPAK_REF_KIND_RUNTIME,
+                                                          request->id,
+                                                          request->arch,
+                                                          request->branch,
+                                                          ide_progress_flatpak_progress_callback,
+                                                          request->progress,
+                                                          cancellable,
+                                                          &error);
+
+              if (request->ref == NULL)
+                g_task_return_error (task, g_steal_pointer (&error));
+              else
+                g_task_return_boolean (task, TRUE);
+
+              IDE_EXIT;
+            }
+        }
+    }
+
+  /*
+   * We failed to locate a previous install, so instead lets discover the
+   * ref from a remote symmary description.
+   */
+  for (guint i = 0; i < request->installations->len; i++)
+    {
+      InstallInfo *info = g_ptr_array_index (request->installations, i);
+      FlatpakInstallation *installation = info->installation;
+      g_autoptr(GPtrArray) remotes = NULL;
+
+      g_assert (FLATPAK_IS_INSTALLATION (installation));
+
+      remotes = flatpak_installation_list_remotes (installation, cancellable, NULL);
+      if (remotes == NULL)
+        continue;
+
+      for (guint j = 0; j < remotes->len; j++)
+        {
+          FlatpakRemote *remote = g_ptr_array_index (remotes, j);
+          const gchar *name = flatpak_remote_get_name (remote);
+          g_autoptr(GPtrArray) refs = NULL;
+
+          g_assert (FLATPAK_IS_REMOTE (remote));
+
+          refs = flatpak_installation_list_remote_refs_sync (installation, name, cancellable, NULL);
+          if (refs == NULL)
+            continue;
+
+          for (guint k = 0; k < refs->len; k++)
+            {
+              FlatpakRemoteRef *ref = g_ptr_array_index (refs, k);
+              const gchar *id = flatpak_ref_get_name (FLATPAK_REF (ref));
+              const gchar *arch = flatpak_ref_get_arch (FLATPAK_REF (ref));
+              const gchar *branch = flatpak_ref_get_branch (FLATPAK_REF (ref));
+
+              g_assert (FLATPAK_IS_REMOTE_REF (ref));
+
+              if (g_strcmp0 (request->id, id) == 0 &&
+                  g_strcmp0 (request->arch, arch) == 0 &&
+                  g_strcmp0 (request->branch, branch) == 0)
+                {
+                  g_autoptr(GError) error = NULL;
+
+                  request->ref = flatpak_installation_install (installation,
+                                                               name,
+                                                               FLATPAK_REF_KIND_RUNTIME,
+                                                               request->id,
+                                                               request->arch,
+                                                               request->branch,
+                                                               ide_progress_flatpak_progress_callback,
+                                                               request->progress,
+                                                               cancellable,
+                                                               &error);
+
+                  if (request->ref != NULL)
+                    g_task_return_boolean (task, TRUE);
+                  else
+                    g_task_return_error (task, g_steal_pointer (&error));
+
+                  IDE_EXIT;
+                }
+            }
+        }
+    }
+
+  g_task_return_new_error (task,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_FOUND,
+                           "Failed to locate runtime within installed flatpak remotes");
+
+  IDE_EXIT;
+}
+
+void
+gbp_flatpak_application_addin_install_runtime_async (GbpFlatpakApplicationAddin  *self,
+                                                     const gchar                 *runtime_id,
+                                                     const gchar                 *arch,
+                                                     const gchar                 *branch,
+                                                     GCancellable                *cancellable,
+                                                     IdeProgress                **progress,
+                                                     GAsyncReadyCallback          callback,
+                                                     gpointer                     user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  InstallRequest *request;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (runtime_id != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (self->installations != NULL);
+
+  if (arch == NULL)
+    arch = flatpak_get_default_arch ();
+
+  if (branch == NULL)
+    branch = "master";
+
+  request = g_slice_new0 (InstallRequest);
+  request->id = g_strdup (runtime_id);
+  request->arch = g_strdup (arch);
+  request->branch = g_strdup (branch);
+  request->installations = g_ptr_array_ref (self->installations);
+  request->progress = ide_progress_new ();
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gbp_flatpak_application_addin_install_runtime_async);
+  g_task_set_task_data (task, request, (GDestroyNotify)install_request_free);
+
+  g_signal_connect_object (task,
+                           "notify::completed",
+                           G_CALLBACK (gbp_flatpak_application_addin_install_completed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  if (progress != NULL)
+    *progress = g_object_ref (request->progress);
+
+  g_task_run_in_thread (task, gbp_flatpak_application_addin_install_runtime_worker);
+
+  IDE_EXIT;
+}
+
+gboolean
+gbp_flatpak_application_addin_install_runtime_finish (GbpFlatpakApplicationAddin  *self,
+                                                      GAsyncResult                *result,
+                                                      GError                     **error)
+{
+  InstallRequest *request;
+
+  g_return_val_if_fail (GBP_IS_FLATPAK_APPLICATION_ADDIN (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  request = g_task_get_task_data (G_TASK (result));
+
+  /*
+   * We might want to immediately notify about the ref so that the
+   * caller can access the runtime after calling this. Otherwise our
+   * notify:;completed might not have yet run.
+   */
+  if (request->ref != NULL && !request->did_added)
+    {
+      request->did_added = TRUE;
+      g_signal_emit (self, signals[RUNTIME_ADDED], 0, request->ref);
+    }
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gbp_flatpak_application_addin_has_runtime (GbpFlatpakApplicationAddin *self,
+                                           const gchar                *id,
+                                           const gchar                *arch,
+                                           const gchar                *branch)
+{
+  g_autoptr(GPtrArray) ar = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (id != NULL);
+  g_assert (arch != NULL);
+  g_assert (branch != NULL);
+
+  IDE_TRACE_MSG ("Looking for runtime %s/%s/%s", id, arch, branch);
+
+  ar = gbp_flatpak_application_addin_get_runtimes (self);
+
+  if (ar != NULL)
+    {
+      for (guint i = 0; i < ar->len; i++)
+        {
+          FlatpakInstalledRef *ref = g_ptr_array_index (ar, i);
+          const gchar *ref_id = flatpak_ref_get_name (FLATPAK_REF (ref));
+          const gchar *ref_arch = flatpak_ref_get_arch (FLATPAK_REF (ref));
+          const gchar *ref_branch = flatpak_ref_get_branch (FLATPAK_REF (ref));
+
+          if (g_strcmp0 (id, ref_id) == 0 &&
+              g_strcmp0 (arch, ref_arch) == 0 &&
+              g_strcmp0 (branch, ref_branch) == 0)
+            IDE_RETURN (TRUE);
+        }
+    }
+
+  IDE_RETURN (FALSE);
 }
 
 static void
 application_addin_iface_init (IdeApplicationAddinInterface *iface)
 {
   iface->load = gbp_flatpak_application_addin_load;
+  iface->unload = gbp_flatpak_application_addin_unload;
+}
+
+G_DEFINE_TYPE_EXTENDED (GbpFlatpakApplicationAddin,
+                        gbp_flatpak_application_addin,
+                        G_TYPE_OBJECT,
+                        0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_APPLICATION_ADDIN, application_addin_iface_init))
+
+static void
+gbp_flatpak_application_addin_class_init (GbpFlatpakApplicationAddinClass *klass)
+{
+  /**
+   * GbpFlatpakApplicationAddin::runtime-added:
+   * @self: An #GbpFlatpakApplicationAddin
+   * @runtime: A #FlatpakInstalledRef
+   *
+   * This signal is emitted when a new runtime is discovered. No deduplication
+   * is dealth with here, so consumers will need to ensure they have not seen
+   * the runtime before by deduplicating with id/arch/branch.
+   */
+  signals [RUNTIME_ADDED] = g_signal_new ("runtime-added",
+                                          G_TYPE_FROM_CLASS (klass),
+                                          G_SIGNAL_RUN_LAST,
+                                          0,
+                                          NULL, NULL, NULL,
+                                          G_TYPE_NONE, 1, FLATPAK_TYPE_INSTALLED_REF);
+}
+
+static void
+gbp_flatpak_application_addin_init (GbpFlatpakApplicationAddin *self)
+{
+}
+
+static void
+gbp_flatpak_application_addin_locate_sdk_worker (GTask        *task,
+                                                 gpointer      source_object,
+                                                 gpointer      task_data,
+                                                 GCancellable *cancellable)
+{
+  LocateSdk *locate = task_data;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (source_object));
+  g_assert (locate != NULL);
+  g_assert (locate->id != NULL);
+  g_assert (locate->arch != NULL);
+  g_assert (locate->branch != NULL);
+  g_assert (locate->installations != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * Look through all of our remote refs and see if we find a match for
+   * the runtime for which we need to locate the SDK. Afterwards, we need
+   * to get the metedata for that runtime so that we can find the sdk field
+   * which maps to another runtime.
+   *
+   * We might have to make a request to the server for the ref if we do not
+   * have a cached copy of the file.
+   */
+
+  for (guint i = 0; i < locate->installations->len; i++)
+    {
+      InstallInfo *info = g_ptr_array_index (locate->installations, i);
+      FlatpakInstallation *installation = info->installation;
+      g_autoptr(GPtrArray) remotes = NULL;
+
+      g_assert (FLATPAK_IS_INSTALLATION (installation));
+
+      remotes = flatpak_installation_list_remotes (installation, cancellable, NULL);
+      if (remotes == NULL)
+        continue;
+
+      for (guint j = 0; j < remotes->len; j++)
+        {
+          FlatpakRemote *remote = g_ptr_array_index (remotes, j);
+          const gchar *name = flatpak_remote_get_name (remote);
+          g_autoptr(GPtrArray) refs = NULL;
+
+          g_assert (FLATPAK_IS_REMOTE (remote));
+
+          refs = flatpak_installation_list_remote_refs_sync (installation, name, cancellable, NULL);
+          if (refs == NULL)
+            continue;
+
+          for (guint k = 0; k < refs->len; k++)
+            {
+              FlatpakRemoteRef *ref = g_ptr_array_index (refs, k);
+              const gchar *id = flatpak_ref_get_name (FLATPAK_REF (ref));
+              const gchar *arch = flatpak_ref_get_arch (FLATPAK_REF (ref));
+              const gchar *branch = flatpak_ref_get_branch (FLATPAK_REF (ref));
+
+              if (g_strcmp0 (locate->id, id) == 0 &&
+                  g_strcmp0 (locate->arch, arch) == 0 &&
+                  g_strcmp0 (locate->branch, branch) == 0)
+                {
+                  g_autoptr(GError) error = NULL;
+                  g_autoptr(GBytes) bytes = NULL;
+                  g_autoptr(GKeyFile) keyfile = NULL;
+                  g_autofree gchar *idstr = NULL;
+                  const gchar *data;
+                  gsize len;
+
+                  bytes = flatpak_installation_fetch_remote_metadata_sync (installation,
+                                                                           name,
+                                                                           FLATPAK_REF (ref),
+                                                                           cancellable,
+                                                                           &error);
+
+                  if (bytes == NULL)
+                    {
+                      g_task_return_error (task, g_steal_pointer (&error));
+                      IDE_EXIT;
+                    }
+
+                  keyfile = g_key_file_new ();
+                  data = (gchar *)g_bytes_get_data (bytes, &len);
+
+                  if (!g_key_file_load_from_data (keyfile, data, len, 0, &error))
+                    {
+                      g_task_return_error (task, g_steal_pointer (&error));
+                      IDE_EXIT;
+                    }
+
+                  idstr = g_key_file_get_string (keyfile, "Runtime", "sdk", NULL);
+
+                  if (idstr != NULL)
+                    {
+                      g_auto(GStrv) parts = g_strsplit (idstr, "/", 3);
+
+                      if (g_strv_length (parts) != 3)
+                        {
+                          g_task_return_new_error (task,
+                                                   G_IO_ERROR,
+                                                   G_IO_ERROR_INVALID_DATA,
+                                                   "Invalid runtime id %s",
+                                                   idstr);
+                          IDE_EXIT;
+                        }
+
+                      locate->sdk_id = g_strdup (parts[0]);
+                      locate->sdk_arch = g_strdup (parts[1]);
+                      locate->sdk_branch = g_strdup (parts[2]);
+                    }
+
+                  g_task_return_boolean (task, TRUE);
+
+                  IDE_EXIT;
+                }
+            }
+        }
+    }
+
+  IDE_EXIT;
+}
+
+void
+gbp_flatpak_application_addin_locate_sdk_async (GbpFlatpakApplicationAddin  *self,
+                                                const gchar                 *runtime_id,
+                                                const gchar                 *arch,
+                                                const gchar                 *branch,
+                                                GCancellable                *cancellable,
+                                                GAsyncReadyCallback          callback,
+                                                gpointer                     user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  LocateSdk *locate;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (runtime_id != NULL);
+  g_assert (arch != NULL);
+  g_assert (branch != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gbp_flatpak_application_addin_locate_sdk_async);
+
+  locate = g_slice_new0 (LocateSdk);
+  locate->id = g_strdup (runtime_id);
+  locate->arch = g_strdup (arch);
+  locate->branch = g_strdup (branch);
+  locate->installations = g_ptr_array_ref (self->installations);
+
+  g_task_set_task_data (task, locate, (GDestroyNotify)locate_sdk_free);
+  g_task_run_in_thread (task, gbp_flatpak_application_addin_locate_sdk_worker);
+
+  IDE_EXIT;
+}
+
+gboolean
+gbp_flatpak_application_addin_locate_sdk_finish (GbpFlatpakApplicationAddin  *self,
+                                                 GAsyncResult                *result,
+                                                 gchar                      **sdk_id,
+                                                 gchar                      **sdk_arch,
+                                                 gchar                      **sdk_branch,
+                                                 GError                     **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_APPLICATION_ADDIN (self));
+  g_assert (G_IS_TASK (result));
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  if (ret)
+    {
+      LocateSdk *state = g_task_get_task_data (G_TASK (result));
+
+      if (sdk_id)
+        *sdk_id = g_strdup (state->sdk_id);
+
+      if (sdk_arch)
+        *sdk_arch = g_strdup (state->sdk_arch);
+
+      if (sdk_branch)
+        *sdk_branch = g_strdup (state->sdk_branch);
+    }
+
+  IDE_RETURN (ret);
 }
diff --git a/plugins/flatpak/gbp-flatpak-application-addin.h b/plugins/flatpak/gbp-flatpak-application-addin.h
index 31de47d..69f23e3 100644
--- a/plugins/flatpak/gbp-flatpak-application-addin.h
+++ b/plugins/flatpak/gbp-flatpak-application-addin.h
@@ -19,6 +19,7 @@
 #ifndef GBP_FLATPAK_APPLICATION_ADDIN_H
 #define GBP_FLATPAK_APPLICATION_ADDIN_H
 
+#include <flatpak.h>
 #include <ide.h>
 
 G_BEGIN_DECLS
@@ -27,6 +28,37 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpFlatpakApplicationAddin, gbp_flatpak_application_addin, GBP, 
FLATPAK_APPLICATION_ADDIN, GObject)
 
+GbpFlatpakApplicationAddin *gbp_flatpak_application_addin_get_default            (void);
+GPtrArray                  *gbp_flatpak_application_addin_get_runtimes           (GbpFlatpakApplicationAddin 
 *self);
+gboolean                    gbp_flatpak_application_addin_has_runtime            (GbpFlatpakApplicationAddin 
 *self,
+                                                                                  const gchar                
 *id,
+                                                                                  const gchar                
 *arch,
+                                                                                  const gchar                
 *branch);
+void                        gbp_flatpak_application_addin_install_runtime_async  (GbpFlatpakApplicationAddin 
 *self,
+                                                                                  const gchar                
 *runtime_id,
+                                                                                  const gchar                
 *arch,
+                                                                                  const gchar                
 *branch,
+                                                                                  GCancellable               
 *cancellable,
+                                                                                  IdeProgress                
**progress,
+                                                                                  GAsyncReadyCallback        
  callback,
+                                                                                  gpointer                   
  user_data);
+gboolean                    gbp_flatpak_application_addin_install_runtime_finish (GbpFlatpakApplicationAddin 
 *self,
+                                                                                  GAsyncResult               
 *result,
+                                                                                  GError                     
**error);
+void                        gbp_flatpak_application_addin_locate_sdk_async       (GbpFlatpakApplicationAddin 
 *self,
+                                                                                  const gchar                
 *runtime_id,
+                                                                                  const gchar                
 *arch,
+                                                                                  const gchar                
 *branch,
+                                                                                  GCancellable               
 *cancellable,
+                                                                                  GAsyncReadyCallback        
  callback,
+                                                                                  gpointer                   
  user_data);
+gboolean                    gbp_flatpak_application_addin_locate_sdk_finish      (GbpFlatpakApplicationAddin 
 *self,
+                                                                                  GAsyncResult               
 *result,
+                                                                                  gchar                      
**sdk_id,
+                                                                                  gchar                      
**sdk_arch,
+                                                                                  gchar                      
**sdk_branch,
+                                                                                  GError                     
**error);
+
 G_END_DECLS
 
 #endif /* GBP_FLATPAK_APPLICATION_ADDIN_H */



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