[gnome-builder/wip/gtk4-port: 1571/1774] libide/foundry: couple deploy strategy to pipelines




commit 8eded4156aaee98768cf7039e08929debb0f6b37
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 17 14:41:09 2022 -0700

    libide/foundry: couple deploy strategy to pipelines
    
    This also adds a new local deploy strategy so that we can always go
    through a deploy strategy for running. That can simplify divergence in
    how things run, even if it's just pass-through.
    
    Once this is in place, we can start looking at replacing the whole
    IdeRunner infrastructure with something simpler.

 src/libide/foundry/ide-build-manager.c            |   1 +
 src/libide/foundry/ide-build-system.c             |   1 +
 src/libide/foundry/ide-deploy-strategy.c          |  21 +-
 src/libide/foundry/ide-deploy-strategy.h          |  56 ++--
 src/libide/foundry/ide-device-manager.c           | 310 +++-------------------
 src/libide/foundry/ide-local-deploy-strategy.c    | 217 +++++++++++++++
 src/libide/foundry/ide-local-deploy-strategy.h    |  33 +++
 src/libide/foundry/ide-pipeline.c                 | 136 ++++++++--
 src/libide/foundry/ide-pipeline.h                 |  12 +-
 src/libide/foundry/meson.build                    |   2 +
 src/plugins/deviced/gbp-deviced-deploy-strategy.c |  14 +-
 11 files changed, 464 insertions(+), 339 deletions(-)
---
diff --git a/src/libide/foundry/ide-build-manager.c b/src/libide/foundry/ide-build-manager.c
index e52893ef3..1c6f3405a 100644
--- a/src/libide/foundry/ide-build-manager.c
+++ b/src/libide/foundry/ide-build-manager.c
@@ -48,6 +48,7 @@
 #include "ide-runtime.h"
 #include "ide-toolchain-manager.h"
 #include "ide-toolchain-private.h"
+#include "ide-triplet.h"
 
 /**
  * SECTION:ide-build-manager
diff --git a/src/libide/foundry/ide-build-system.c b/src/libide/foundry/ide-build-system.c
index 9c3d5b4ac..76cb20297 100644
--- a/src/libide/foundry/ide-build-system.c
+++ b/src/libide/foundry/ide-build-system.c
@@ -37,6 +37,7 @@
 #include "ide-config.h"
 #include "ide-device.h"
 #include "ide-foundry-compat.h"
+#include "ide-runtime.h"
 #include "ide-toolchain.h"
 
 G_DEFINE_INTERFACE (IdeBuildSystem, ide_build_system, IDE_TYPE_OBJECT)
diff --git a/src/libide/foundry/ide-deploy-strategy.c b/src/libide/foundry/ide-deploy-strategy.c
index 4fde25c92..1e0bbd45d 100644
--- a/src/libide/foundry/ide-deploy-strategy.c
+++ b/src/libide/foundry/ide-deploy-strategy.c
@@ -29,7 +29,7 @@ G_DEFINE_ABSTRACT_TYPE (IdeDeployStrategy, ide_deploy_strategy, IDE_TYPE_OBJECT)
 
 static void
 ide_deploy_strategy_real_load_async (IdeDeployStrategy   *self,
-                                     IdePipeline    *pipeline,
+                                     IdePipeline         *pipeline,
                                      GCancellable        *cancellable,
                                      GAsyncReadyCallback  callback,
                                      gpointer             user_data)
@@ -45,18 +45,21 @@ ide_deploy_strategy_real_load_async (IdeDeployStrategy   *self,
 static gboolean
 ide_deploy_strategy_real_load_finish (IdeDeployStrategy  *self,
                                       GAsyncResult       *result,
+                                      int                *priority,
                                       GError            **error)
 {
   g_assert (IDE_IS_DEPLOY_STRATEGY (self));
   g_assert (G_IS_TASK (result));
   g_assert (g_task_is_valid (G_TASK (result), self));
 
+  *priority = G_MAXINT;
+
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
 static void
 ide_deploy_strategy_real_deploy_async (IdeDeployStrategy     *self,
-                                       IdePipeline      *pipeline,
+                                       IdePipeline           *pipeline,
                                        GFileProgressCallback  progress,
                                        gpointer               progress_data,
                                        GDestroyNotify         progress_data_destroy,
@@ -114,7 +117,7 @@ ide_deploy_strategy_real_create_runner_finish (IdeDeployStrategy  *self,
   g_assert (IDE_IS_TASK (result));
   g_assert (ide_task_is_valid (G_TASK (result), self));
 
-  return g_task_propagate_pointer (G_TASK (result), error);
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
 }
 
 static void
@@ -154,7 +157,7 @@ ide_deploy_strategy_init (IdeDeployStrategy *self)
  */
 void
 ide_deploy_strategy_load_async (IdeDeployStrategy   *self,
-                                IdePipeline    *pipeline,
+                                IdePipeline         *pipeline,
                                 GCancellable        *cancellable,
                                 GAsyncReadyCallback  callback,
                                 gpointer             user_data)
@@ -184,6 +187,7 @@ ide_deploy_strategy_load_async (IdeDeployStrategy   *self,
 gboolean
 ide_deploy_strategy_load_finish (IdeDeployStrategy  *self,
                                  GAsyncResult       *result,
+                                 int                *priority,
                                  GError            **error)
 {
   gboolean ret;
@@ -192,8 +196,9 @@ ide_deploy_strategy_load_finish (IdeDeployStrategy  *self,
 
   g_assert (IDE_IS_DEPLOY_STRATEGY (self));
   g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (priority != NULL);
 
-  ret = IDE_DEPLOY_STRATEGY_GET_CLASS (self)->load_finish (self, result, error);
+  ret = IDE_DEPLOY_STRATEGY_GET_CLASS (self)->load_finish (self, result, priority, error);
 
   IDE_RETURN (ret);
 }
@@ -218,7 +223,7 @@ ide_deploy_strategy_load_finish (IdeDeployStrategy  *self,
  */
 void
 ide_deploy_strategy_deploy_async (IdeDeployStrategy     *self,
-                                  IdePipeline      *pipeline,
+                                  IdePipeline           *pipeline,
                                   GFileProgressCallback  progress,
                                   gpointer               progress_data,
                                   GDestroyNotify         progress_data_destroy,
@@ -264,8 +269,8 @@ ide_deploy_strategy_deploy_finish (IdeDeployStrategy  *self,
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_DEPLOY_STRATEGY (self));
-  g_assert (G_IS_ASYNC_RESULT (result));
+  g_return_val_if_fail (IDE_IS_DEPLOY_STRATEGY (self), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
 
   ret = IDE_DEPLOY_STRATEGY_GET_CLASS (self)->deploy_finish (self, result, error);
 
diff --git a/src/libide/foundry/ide-deploy-strategy.h b/src/libide/foundry/ide-deploy-strategy.h
index 2bb4d7162..14061c544 100644
--- a/src/libide/foundry/ide-deploy-strategy.h
+++ b/src/libide/foundry/ide-deploy-strategy.h
@@ -38,33 +38,34 @@ struct _IdeDeployStrategyClass
 {
   IdeObjectClass parent;
 
-  void     (*load_async)    (IdeDeployStrategy     *self,
-                             IdePipeline      *pipeline,
-                             GCancellable          *cancellable,
-                             GAsyncReadyCallback    callback,
-                             gpointer               user_data);
-  gboolean (*load_finish)   (IdeDeployStrategy     *self,
-                             GAsyncResult          *result,
-                             GError               **error);
-  void     (*deploy_async)  (IdeDeployStrategy     *self,
-                             IdePipeline      *pipeline,
-                             GFileProgressCallback  progress,
-                             gpointer               progress_data,
-                             GDestroyNotify         progress_data_destroy,
-                             GCancellable          *cancellable,
-                             GAsyncReadyCallback    callback,
-                             gpointer               user_data);
-  gboolean (*deploy_finish) (IdeDeployStrategy     *self,
-                             GAsyncResult          *result,
-                             GError               **error);
-  void       (*create_runner_async)  (IdeDeployStrategy   *self,
-                                      IdePipeline         *pipeline,
-                                      GCancellable        *cancellable,
-                                      GAsyncReadyCallback  callback,
-                                      gpointer             user_data);
-  IdeRunner *(*create_runner_finish) (IdeDeployStrategy   *self,
-                                      GAsyncResult        *result,
-                                      GError             **error);
+  void           (*load_async)           (IdeDeployStrategy      *self,
+                                          IdePipeline            *pipeline,
+                                          GCancellable           *cancellable,
+                                          GAsyncReadyCallback     callback,
+                                          gpointer                user_data);
+  gboolean       (*load_finish)          (IdeDeployStrategy      *self,
+                                          GAsyncResult           *result,
+                                          int                    *priority,
+                                          GError                **error);
+  void           (*deploy_async)         (IdeDeployStrategy      *self,
+                                          IdePipeline            *pipeline,
+                                          GFileProgressCallback   progress,
+                                          gpointer                progress_data,
+                                          GDestroyNotify          progress_data_destroy,
+                                          GCancellable           *cancellable,
+                                          GAsyncReadyCallback     callback,
+                                          gpointer                user_data);
+  gboolean       (*deploy_finish)        (IdeDeployStrategy      *self,
+                                          GAsyncResult           *result,
+                                          GError                **error);
+  void           (*create_runner_async)  (IdeDeployStrategy      *self,
+                                          IdePipeline            *pipeline,
+                                          GCancellable           *cancellable,
+                                          GAsyncReadyCallback     callback,
+                                          gpointer                user_data);
+  IdeRunner     *(*create_runner_finish) (IdeDeployStrategy      *self,
+                                          GAsyncResult           *result,
+                                          GError                **error);
 
   gpointer _reserved[16];
 };
@@ -78,6 +79,7 @@ void     ide_deploy_strategy_load_async    (IdeDeployStrategy      *self,
 IDE_AVAILABLE_IN_ALL
 gboolean ide_deploy_strategy_load_finish   (IdeDeployStrategy      *self,
                                             GAsyncResult           *result,
+                                            int                    *priority,
                                             GError                **error);
 IDE_AVAILABLE_IN_ALL
 void     ide_deploy_strategy_deploy_async  (IdeDeployStrategy      *self,
diff --git a/src/libide/foundry/ide-device-manager.c b/src/libide/foundry/ide-device-manager.c
index 25ebe056d..674430c1e 100644
--- a/src/libide/foundry/ide-device-manager.c
+++ b/src/libide/foundry/ide-device-manager.c
@@ -37,6 +37,8 @@
 #include "ide-device.h"
 #include "ide-foundry-compat.h"
 #include "ide-local-device.h"
+#include "ide-runner.h"
+#include "ide-triplet.h"
 
 struct _IdeDeviceManager
 {
@@ -76,12 +78,6 @@ struct _IdeDeviceManager
   guint loading : 1;
 };
 
-typedef struct
-{
-  IdeObjectArray   *strategies;
-  IdePipeline *pipeline;
-} DeployState;
-
 typedef struct
 {
   gint n_active;
@@ -93,7 +89,6 @@ static void ide_device_manager_action_device (IdeDeviceManager    *self,
                                               GVariant            *param);
 static void ide_device_manager_action_deploy (IdeDeviceManager    *self,
                                               GVariant            *param);
-static void ide_device_manager_deploy_tick   (IdeTask             *task);
 
 IDE_DEFINE_ACTION_GROUP (IdeDeviceManager, ide_device_manager, {
   { "device", ide_device_manager_action_device, "s", "'local'" },
@@ -122,14 +117,6 @@ enum {
 static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
 
-static void
-deploy_state_free (DeployState *state)
-{
-  g_clear_object (&state->pipeline);
-  g_clear_pointer (&state->strategies, ide_object_array_unref);
-  g_slice_free (DeployState, state);
-}
-
 static void
 ide_device_manager_provider_device_added_cb (IdeDeviceManager  *self,
                                              IdeDevice         *device,
@@ -743,31 +730,14 @@ deploy_progress_cb (goffset  current_num_bytes,
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
 }
 
-static void
-collect_strategies (PeasExtensionSet *set,
-                    PeasPluginInfo   *plugin_info,
-                    PeasExtension    *exten,
-                    gpointer          user_data)
-{
-  IdeObjectArray *strategies = user_data;
-  IdeDeployStrategy *strategy = (IdeDeployStrategy *)exten;
-
-  g_assert (PEAS_IS_EXTENSION_SET (set));
-  g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
-  g_assert (strategies != NULL);
-
-  ide_object_array_add (strategies, strategy);
-}
-
 static void
 ide_device_manager_deploy_cb (GObject      *object,
                               GAsyncResult *result,
                               gpointer      user_data)
 {
   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
-  g_autoptr(GError) error = NULL;
   g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
 
   IDE_ENTRY;
 
@@ -783,94 +753,13 @@ ide_device_manager_deploy_cb (GObject      *object,
   IDE_EXIT;
 }
 
-static void
-ide_device_manager_deploy_load_cb (GObject      *object,
-                                   GAsyncResult *result,
-                                   gpointer      user_data)
-{
-  IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
-  g_autoptr(GError) error = NULL;
-  g_autoptr(IdeTask) task = user_data;
-  IdeDeviceManager *self;
-  DeployState *state;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
-  g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_TASK (task));
-
-  if (!ide_deploy_strategy_load_finish (strategy, result, &error))
-    {
-      g_debug ("Deploy strategy failed to load: %s", error->message);
-      ide_object_destroy (IDE_OBJECT (strategy));
-      ide_device_manager_deploy_tick (task);
-      IDE_EXIT;
-    }
-
-  /* Okay, we found a match. Now deploy to the device. */
-
-  self = ide_task_get_source_object (task);
-  state = ide_task_get_task_data (task);
-
-  g_assert (IDE_IS_DEVICE_MANAGER (self));
-  g_assert (state != NULL);
-  g_assert (state->strategies != NULL);
-  g_assert (IDE_IS_PIPELINE (state->pipeline));
-
-  ide_deploy_strategy_deploy_async (strategy,
-                                    state->pipeline,
-                                    deploy_progress_cb,
-                                    g_object_ref (self),
-                                    g_object_unref,
-                                    ide_task_get_cancellable (task),
-                                    ide_device_manager_deploy_cb,
-                                    g_object_ref (task));
-
-  IDE_EXIT;
-}
-
-static void
-ide_device_manager_deploy_tick (IdeTask *task)
-{
-  g_autoptr(IdeDeployStrategy) strategy = NULL;
-  DeployState *state;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_TASK (task));
-
-  state = ide_task_get_task_data (task);
-
-  g_assert (state != NULL);
-  g_assert (state->strategies != NULL);
-  g_assert (IDE_IS_PIPELINE (state->pipeline));
-
-  if (state->strategies->len == 0)
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_NOT_SUPPORTED,
-                                 "Failed to locate deployment strategy for device");
-      IDE_EXIT;
-    }
-
-  strategy = ide_object_array_steal_index (state->strategies, 0);
-
-  ide_deploy_strategy_load_async (strategy,
-                                  state->pipeline,
-                                  ide_task_get_cancellable (task),
-                                  ide_device_manager_deploy_load_cb,
-                                  g_object_ref (task));
-
-  IDE_EXIT;
-}
-
 static void
 ide_device_manager_deploy_completed (IdeDeviceManager *self,
                                      GParamSpec       *pspec,
                                      IdeTask          *task)
 {
+  IDE_ENTRY;
+
   g_assert (IDE_IS_DEVICE_MANAGER (self));
   g_assert (IDE_IS_TASK (task));
 
@@ -881,6 +770,8 @@ ide_device_manager_deploy_completed (IdeDeviceManager *self,
     }
 
   g_signal_emit (self, signals [DEPLOY_FINISHED], 0);
+
+  IDE_EXIT;
 }
 
 /**
@@ -902,10 +793,8 @@ ide_device_manager_deploy_async (IdeDeviceManager    *self,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
 {
-  g_autoptr(PeasExtensionSet) set = NULL;
   g_autoptr(IdeTask) task = NULL;
-  DeployState *state;
-  IdeDevice *device;
+  IdeDeployStrategy *strategy;
 
   IDE_ENTRY;
 
@@ -927,37 +816,20 @@ ide_device_manager_deploy_async (IdeDeviceManager    *self,
                            self,
                            G_CONNECT_SWAPPED);
 
-  if (!(device = ide_pipeline_get_device (pipeline)))
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
-                                 "Missing device in pipeline");
-      IDE_EXIT;
-    }
-
-  if (IDE_IS_LOCAL_DEVICE (device))
-    {
-      ide_task_return_boolean (task, TRUE);
-      IDE_EXIT;
-    }
-
-  state = g_slice_new0 (DeployState);
-  state->pipeline = g_object_ref (pipeline);
-  state->strategies = ide_object_array_new ();
-  ide_task_set_task_data (task, state, deploy_state_free);
-
-  set = peas_extension_set_new (peas_engine_get_default (),
-                                IDE_TYPE_DEPLOY_STRATEGY,
-                                NULL);
-  peas_extension_set_foreach (set, collect_strategies, state->strategies);
-
-  /* Root the addins as children of us so that they get context access */
-  for (guint i = 0; i < state->strategies->len; i++)
-    ide_object_append (IDE_OBJECT (self),
-                       ide_object_array_index (state->strategies, i));
-
-  ide_device_manager_deploy_tick (task);
+  if (!(strategy = ide_pipeline_get_deploy_strategy (pipeline)))
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Missing device in pipeline, cannot deploy");
+  else
+    ide_deploy_strategy_deploy_async (strategy,
+                                      pipeline,
+                                      deploy_progress_cb,
+                                      g_object_ref (self),
+                                      g_object_unref,
+                                      cancellable,
+                                      ide_device_manager_deploy_cb,
+                                      g_steal_pointer (&task));
 
   IDE_EXIT;
 }
@@ -996,9 +868,9 @@ ide_device_manager_create_runner_cb (GObject      *object,
                                      gpointer      user_data)
 {
   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
-  g_autoptr(GError) error = NULL;
+  g_autoptr(IdeRunner) runner = NULL;
   g_autoptr(IdeTask) task = user_data;
-  IdeRunner *runner;
+  g_autoptr(GError) error = NULL;
 
   IDE_ENTRY;
 
@@ -1006,94 +878,10 @@ ide_device_manager_create_runner_cb (GObject      *object,
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
 
-  runner = ide_deploy_strategy_create_runner_finish (strategy, result, &error);
-
-  if (error)
+  if (!(runner = ide_deploy_strategy_create_runner_finish (strategy, result, &error)))
     ide_task_return_error (task, g_steal_pointer (&error));
   else
-    ide_task_return_pointer (task, runner, g_object_unref);
-
-  ide_object_destroy (IDE_OBJECT (strategy));
-
-  IDE_EXIT;
-}
-
-static void
-ide_device_manager_create_runner_load_cb (GObject      *object,
-                                          GAsyncResult *result,
-                                          gpointer      user_data)
-{
-  IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
-  g_autoptr(GError) error = NULL;
-  g_autoptr(IdeTask) task = user_data;
-  IdeDeviceManager *self;
-  DeployState *state;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
-  g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_TASK (task));
-
-  if (!ide_deploy_strategy_load_finish (strategy, result, &error))
-    {
-      g_debug ("Deploy strategy failed to load: %s", error->message);
-      ide_object_destroy (IDE_OBJECT (strategy));
-      ide_device_manager_deploy_tick (task);
-      IDE_EXIT;
-    }
-
-  /* Okay, we found a match. Now run on the device. */
-
-  self = ide_task_get_source_object (task);
-  state = ide_task_get_task_data (task);
-
-  g_assert (IDE_IS_DEVICE_MANAGER (self));
-  g_assert (state != NULL);
-  g_assert (state->strategies != NULL);
-  g_assert (IDE_IS_PIPELINE (state->pipeline));
-
-  ide_deploy_strategy_create_runner_async (strategy,
-                                           state->pipeline,
-                                           ide_task_get_cancellable (task),
-                                           ide_device_manager_create_runner_cb,
-                                           g_object_ref (task));
-
-  IDE_EXIT;
-}
-
-static void
-ide_device_manager_create_runner_tick (IdeTask *task)
-{
-  g_autoptr(IdeDeployStrategy) strategy = NULL;
-  DeployState *state;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_TASK (task));
-
-  state = ide_task_get_task_data (task);
-
-  g_assert (state != NULL);
-  g_assert (state->strategies != NULL);
-  g_assert (IDE_IS_PIPELINE (state->pipeline));
-
-  if (state->strategies->len == 0)
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_NOT_SUPPORTED,
-                                 "Failed to locate deployment strategy for device");
-      IDE_EXIT;
-    }
-
-  strategy = ide_object_array_steal_index (state->strategies, 0);
-
-  ide_deploy_strategy_load_async (strategy,
-                                  state->pipeline,
-                                  ide_task_get_cancellable (task),
-                                  ide_device_manager_create_runner_load_cb,
-                                  g_object_ref (task));
+    ide_task_return_pointer (task, g_steal_pointer (&runner), g_object_unref);
 
   IDE_EXIT;
 }
@@ -1116,10 +904,8 @@ ide_device_manager_create_runner_async (IdeDeviceManager    *self,
                                         GAsyncReadyCallback  callback,
                                         gpointer             user_data)
 {
-  g_autoptr(PeasExtensionSet) set = NULL;
   g_autoptr(IdeTask) task = NULL;
-  DeployState *state;
-  IdeDevice *device;
+  IdeDeployStrategy *strategy;
 
   IDE_ENTRY;
 
@@ -1130,37 +916,17 @@ ide_device_manager_create_runner_async (IdeDeviceManager    *self,
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_source_tag (task, ide_device_manager_create_runner_async);
 
-  if (!(device = ide_pipeline_get_device (pipeline)))
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
-                                 "Missing device in pipeline");
-      IDE_EXIT;
-    }
-
-  if (IDE_IS_LOCAL_DEVICE (device))
-    {
-      (ide_task_return_pointer) (task, NULL, NULL);
-      IDE_EXIT;
-    }
-
-  state = g_slice_new0 (DeployState);
-  state->pipeline = g_object_ref (pipeline);
-  state->strategies = ide_object_array_new ();
-  ide_task_set_task_data (task, state, deploy_state_free);
-
-  set = peas_extension_set_new (peas_engine_get_default (),
-                                IDE_TYPE_DEPLOY_STRATEGY,
-                                NULL);
-  peas_extension_set_foreach (set, collect_strategies, state->strategies);
-
-  /* Root the addins as children of us so that they get context access */
-  for (guint i = 0; i < state->strategies->len; i++)
-    ide_object_append (IDE_OBJECT (self),
-                       ide_object_array_index (state->strategies, i));
-
-  ide_device_manager_create_runner_tick (task);
+  if (!(strategy = ide_pipeline_get_deploy_strategy (pipeline)))
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Missing device in pipeline");
+  else
+    ide_deploy_strategy_create_runner_async (strategy,
+                                             pipeline,
+                                             cancellable,
+                                             ide_device_manager_create_runner_cb,
+                                             g_steal_pointer (&task));
 
   IDE_EXIT;
 }
diff --git a/src/libide/foundry/ide-local-deploy-strategy.c b/src/libide/foundry/ide-local-deploy-strategy.c
new file mode 100644
index 000000000..e947f3daf
--- /dev/null
+++ b/src/libide/foundry/ide-local-deploy-strategy.c
@@ -0,0 +1,217 @@
+/* ide-local-deploy-strategy.c
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-local-deploy-strategy"
+
+#include "config.h"
+
+#include <libide-threading.h>
+
+#include "ide-local-deploy-strategy.h"
+#include "ide-local-device.h"
+#include "ide-pipeline.h"
+#include "ide-runtime.h"
+#include "ide-runner.h"
+
+struct _IdeLocalDeployStrategy
+{
+  GObject parent_instance;
+};
+
+G_DEFINE_FINAL_TYPE (IdeLocalDeployStrategy, ide_local_deploy_strategy, IDE_TYPE_DEPLOY_STRATEGY)
+
+static void
+ide_local_deploy_strategy_load_async (IdeDeployStrategy   *strategy,
+                                      IdePipeline         *pipeline,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  IdeDevice *device;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_LOCAL_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (strategy, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_local_deploy_strategy_load_async);
+
+  device = ide_pipeline_get_device (pipeline);
+
+  if (IDE_IS_LOCAL_DEVICE (device))
+    ide_task_return_boolean (task, TRUE);
+  else
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               "Cannot deploy to this device");
+
+  IDE_EXIT;
+}
+
+static gboolean
+ide_local_deploy_strategy_load_finish (IdeDeployStrategy  *strategy,
+                                       GAsyncResult       *result,
+                                       int                *priority,
+                                       GError            **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_LOCAL_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_TASK (result));
+  g_assert (priority != NULL);
+
+  *priority = 0;
+
+  ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+ide_local_deploy_strategy_deploy_async (IdeDeployStrategy     *strategy,
+                                        IdePipeline           *pipeline,
+                                        GFileProgressCallback  progress,
+                                        gpointer               progress_data,
+                                        GDestroyNotify         progress_data_destroy,
+                                        GCancellable          *cancellable,
+                                        GAsyncReadyCallback    callback,
+                                        gpointer               user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  IdeDevice *device;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_LOCAL_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (strategy, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_local_deploy_strategy_deploy_async);
+
+  device = ide_pipeline_get_device (pipeline);
+
+  if (IDE_IS_LOCAL_DEVICE (device))
+    ide_task_return_boolean (task, TRUE);
+  else
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               "Cannot deploy to this device: %s",
+                               device ? G_OBJECT_TYPE_NAME (device) : "None");
+
+  IDE_EXIT;
+}
+
+static gboolean
+ide_local_deploy_strategy_deploy_finish (IdeDeployStrategy  *strategy,
+                                         GAsyncResult       *result,
+                                         GError            **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_LOCAL_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_TASK (result));
+
+  ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+ide_local_deploy_strategy_create_runner_async (IdeDeployStrategy   *strategy,
+                                               IdePipeline         *pipeline,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  g_autoptr(IdeRunner) runner = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  IdeRuntime *runtime;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_LOCAL_DEPLOY_STRATEGY (strategy));
+  g_return_if_fail (IDE_IS_PIPELINE (pipeline));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (strategy, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_local_deploy_strategy_create_runner_async);
+
+  if (!(runtime = ide_pipeline_get_runtime (pipeline)) ||
+      !(runner = ide_runtime_create_runner (runtime, NULL)))
+    ide_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to create IdeRunner for pipeline");
+  else
+    ide_task_return_pointer (task, g_steal_pointer (&runner), g_object_unref);
+
+  IDE_EXIT;
+}
+
+static IdeRunner *
+ide_local_deploy_strategy_create_runner_finish (IdeDeployStrategy  *strategy,
+                                                GAsyncResult       *result,
+                                                GError            **error)
+{
+  IdeRunner *ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_TASK (result));
+
+  ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+ide_local_deploy_strategy_class_init (IdeLocalDeployStrategyClass *klass)
+{
+  IdeDeployStrategyClass *deploy_strategy_class = IDE_DEPLOY_STRATEGY_CLASS (klass);
+
+  deploy_strategy_class->load_async = ide_local_deploy_strategy_load_async;
+  deploy_strategy_class->load_finish = ide_local_deploy_strategy_load_finish;
+  deploy_strategy_class->deploy_async = ide_local_deploy_strategy_deploy_async;
+  deploy_strategy_class->deploy_finish = ide_local_deploy_strategy_deploy_finish;
+  deploy_strategy_class->create_runner_async = ide_local_deploy_strategy_create_runner_async;
+  deploy_strategy_class->create_runner_finish = ide_local_deploy_strategy_create_runner_finish;
+}
+
+static void
+ide_local_deploy_strategy_init (IdeLocalDeployStrategy *self)
+{
+}
+
+IdeDeployStrategy *
+ide_local_deploy_strategy_new (void)
+{
+  return g_object_new (IDE_TYPE_LOCAL_DEPLOY_STRATEGY, NULL);
+}
diff --git a/src/libide/foundry/ide-local-deploy-strategy.h b/src/libide/foundry/ide-local-deploy-strategy.h
new file mode 100644
index 000000000..da6ed64c7
--- /dev/null
+++ b/src/libide/foundry/ide-local-deploy-strategy.h
@@ -0,0 +1,33 @@
+/* ide-local-deploy-strategy.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-deploy-strategy.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LOCAL_DEPLOY_STRATEGY (ide_local_deploy_strategy_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeLocalDeployStrategy, ide_local_deploy_strategy, IDE, LOCAL_DEPLOY_STRATEGY, 
IdeDeployStrategy)
+
+IdeDeployStrategy *ide_local_deploy_strategy_new (void);
+
+G_END_DECLS
diff --git a/src/libide/foundry/ide-pipeline.c b/src/libide/foundry/ide-pipeline.c
index 51285cc05..45f94d4f5 100644
--- a/src/libide/foundry/ide-pipeline.c
+++ b/src/libide/foundry/ide-pipeline.c
@@ -35,7 +35,8 @@
 #include <libide-threading.h>
 
 #include "ide-build-log-private.h"
-#include "ide-build-log.h"
+#include "ide-config.h"
+#include "ide-deploy-strategy.h"
 #include "ide-pipeline-addin.h"
 #include "ide-pipeline.h"
 #include "ide-build-private.h"
@@ -47,6 +48,8 @@
 #include "ide-device.h"
 #include "ide-foundry-compat.h"
 #include "ide-foundry-enums.h"
+#include "ide-local-deploy-strategy.h"
+#include "ide-local-device.h"
 #include "ide-run-manager-private.h"
 #include "ide-runtime.h"
 #include "ide-toolchain-manager.h"
@@ -97,16 +100,16 @@ G_DEFINE_QUARK (ide_build_error, ide_build_error)
 
 typedef struct
 {
-  guint          id;
+  guint             id;
   IdePipelinePhase  phase;
-  gint           priority;
+  int               priority;
   IdePipelineStage *stage;
 } PipelineEntry;
 
 typedef struct
 {
   IdePipeline *self;
-  GPtrArray        *addins;
+  GPtrArray   *addins;
 } IdleLoadState;
 
 typedef struct
@@ -133,6 +136,15 @@ struct _IdePipeline
    */
   IdeExtensionSetAdapter *addins;
 
+  /*
+   * Deployment stategies help discover how to make a deployment to
+   * a device which might require sending data to another system such
+   * as a phone or tablet.
+   */
+  IdeExtensionSetAdapter *deploy_strategies;
+  IdeDeployStrategy *best_strategy;
+  int best_strategy_priority;
+
   /*
    * This is the configuration for the build. It is a snapshot of
    * the real configuration so that we do not need to synchronize
@@ -1156,6 +1168,60 @@ collect_pipeline_addins (IdeExtensionSetAdapter *set,
   g_ptr_array_add (addins, g_object_ref (exten));
 }
 
+static void
+ide_pipeline_deploy_strategy_load_cb (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
+  g_autoptr(IdePipeline) self = user_data;
+  g_autoptr(GError) error = NULL;
+  int priority = 0;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_PIPELINE (self));
+
+  if (ide_deploy_strategy_load_finish (strategy, result, &priority, &error))
+    {
+      if (self->best_strategy == NULL || priority < self->best_strategy_priority)
+        {
+          g_set_object (&self->best_strategy, strategy);
+          self->best_strategy_priority = priority;
+          IDE_EXIT;
+        }
+    }
+
+  IDE_EXIT;
+}
+
+static void
+ide_pipeline_deploy_strategy_added_cb (IdeExtensionSetAdapter *set,
+                                       PeasPluginInfo         *plugin_info,
+                                       PeasExtension          *exten,
+                                       gpointer                user_data)
+{
+  IdePipeline *self = user_data;
+  IdeDeployStrategy *strategy = (IdeDeployStrategy *)exten;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
+  g_assert (IDE_IS_PIPELINE (self));
+
+  ide_deploy_strategy_load_async (strategy,
+                                  self,
+                                  self->cancellable,
+                                  ide_pipeline_deploy_strategy_load_cb,
+                                  g_object_ref (self));
+
+  IDE_EXIT;
+}
+
 static gboolean
 ide_pipeline_load_cb (IdleLoadState *state)
 {
@@ -1191,6 +1257,15 @@ ide_pipeline_load_cb (IdleLoadState *state)
         return G_SOURCE_CONTINUE;
     }
 
+  /* Now setup deployment strategies */
+  g_signal_connect (state->self->deploy_strategies,
+                    "extension-added",
+                    G_CALLBACK (ide_pipeline_deploy_strategy_added_cb),
+                    state->self);
+  ide_extension_set_adapter_foreach (state->self->deploy_strategies,
+                                     ide_pipeline_deploy_strategy_added_cb,
+                                     state);
+
   state->self->loaded = TRUE;
   state->self->idle_addins_load_source = 0;
 
@@ -1213,7 +1288,6 @@ ide_pipeline_load_cb (IdleLoadState *state)
 static void
 ide_pipeline_load (IdePipeline *self)
 {
-  g_autoptr(GPtrArray) addins = NULL;
   IdleLoadState *state;
   IdeContext *context;
 
@@ -1229,42 +1303,39 @@ ide_pipeline_load (IdePipeline *self)
   register_build_commands_stage (self, context);
   register_post_install_commands_stage (self, context);
 
+  /* Setup pipeline addins */
   self->addins = ide_extension_set_adapter_new (IDE_OBJECT (self),
                                                 peas_engine_get_default (),
                                                 IDE_TYPE_PIPELINE_ADDIN,
                                                 NULL, NULL);
-
   g_signal_connect (self->addins,
                     "extension-added",
                     G_CALLBACK (ide_pipeline_extension_prepare),
                     self);
-
   ide_extension_set_adapter_foreach (self->addins,
                                      ide_pipeline_extension_prepare,
                                      self);
-
   g_signal_connect_after (self->addins,
                           "extension-added",
                           G_CALLBACK (ide_pipeline_extension_added),
                           self);
-
   g_signal_connect (self->addins,
                     "extension-removed",
                     G_CALLBACK (ide_pipeline_extension_removed),
                     self);
 
-  /* Collect our addins so we can incrementally load them in an
-   * idle callback to reduce chances of stalling the main loop.
-   */
-  addins = g_ptr_array_new_with_free_func (g_object_unref);
-  ide_extension_set_adapter_foreach (self->addins,
-                                     collect_pipeline_addins,
-                                     addins);
+  /* Create deployment strategies */
+  self->deploy_strategies = ide_extension_set_adapter_new (IDE_OBJECT (self),
+                                                           peas_engine_get_default (),
+                                                           IDE_TYPE_DEPLOY_STRATEGY,
+                                                           NULL, NULL);
 
   state = g_slice_new0 (IdleLoadState);
   state->self = g_object_ref (self);
-  state->addins = g_steal_pointer (&addins);
-
+  state->addins = g_ptr_array_new_with_free_func (g_object_unref);
+  ide_extension_set_adapter_foreach (self->addins,
+                                     collect_pipeline_addins,
+                                     state->addins);
   self->idle_addins_load_source =
     g_idle_add_full (G_PRIORITY_LOW,
                      (GSourceFunc) ide_pipeline_load_cb,
@@ -1347,7 +1418,10 @@ ide_pipeline_unload (IdePipeline *self)
 
   g_assert (IDE_IS_PIPELINE (self));
 
+  g_clear_object (&self->best_strategy);
+
   ide_clear_and_destroy_object (&self->addins);
+  ide_clear_and_destroy_object (&self->deploy_strategies);
 
   IDE_EXIT;
 }
@@ -1779,6 +1853,9 @@ ide_pipeline_init (IdePipeline *self)
   self->position = -1;
   self->pty_slave = -1;
 
+  self->best_strategy_priority = G_MAXINT;
+  self->best_strategy = ide_local_deploy_strategy_new ();
+
   self->pipeline = g_array_new (FALSE, FALSE, sizeof (PipelineEntry));
   g_array_set_clear_func (self->pipeline, clear_pipeline_entry);
 
@@ -1792,8 +1869,8 @@ ide_pipeline_init (IdePipeline *self)
 
 static void
 ide_pipeline_stage_build_cb (GObject      *object,
-                                     GAsyncResult *result,
-                                     gpointer      user_data)
+                             GAsyncResult *result,
+                             gpointer      user_data)
 {
   IdePipelineStage *stage = (IdePipelineStage *)object;
   IdePipeline *self;
@@ -4223,6 +4300,25 @@ ide_pipeline_contains_program_in_path (IdePipeline  *self,
   return FALSE;
 }
 
+/**
+ * ide_pipeline_get_deploy_strategy:
+ * @self: a #IdePipeline
+ *
+ * Gets the best discovered deployment strategry.
+ *
+ * Returns: (transfer none) (nullable): the best deployment strategy
+ *   if any are supported for the current configuration.
+ */
+IdeDeployStrategy *
+ide_pipeline_get_deploy_strategy (IdePipeline *self)
+{
+  g_return_val_if_fail (IDE_IS_PIPELINE (self), NULL);
+  g_return_val_if_fail (!self->best_strategy ||
+                        IDE_IS_DEPLOY_STRATEGY (self->best_strategy), NULL);
+
+  return self->best_strategy;
+}
+
 /**
  * ide_pipeline_addin_find_by_module_name:
  * @pipeline: an #IdePipeline
diff --git a/src/libide/foundry/ide-pipeline.h b/src/libide/foundry/ide-pipeline.h
index ba590a54a..8812cec51 100644
--- a/src/libide/foundry/ide-pipeline.h
+++ b/src/libide/foundry/ide-pipeline.h
@@ -24,19 +24,15 @@
 # error "Only <libide-foundry.h> can be included directly."
 #endif
 
+#include <vte/vte.h>
+
 #include <libide-core.h>
 #include <libide-code.h>
 #include <libide-threading.h>
-#include <vte/vte.h>
-
-#include "ide-foundry-types.h"
 
 #include "ide-build-log.h"
-#include "ide-config.h"
-#include "ide-pipeline-stage.h"
+#include "ide-foundry-types.h"
 #include "ide-pipeline-phase.h"
-#include "ide-runtime.h"
-#include "ide-triplet.h"
 
 G_BEGIN_DECLS
 
@@ -200,5 +196,7 @@ IDE_AVAILABLE_IN_ALL
 gboolean               ide_pipeline_contains_program_in_path (IdePipeline            *self,
                                                               const gchar            *name,
                                                               GCancellable           *cancellable);
+IDE_AVAILABLE_IN_ALL
+IdeDeployStrategy     *ide_pipeline_get_deploy_strategy      (IdePipeline            *self);
 
 G_END_DECLS
diff --git a/src/libide/foundry/meson.build b/src/libide/foundry/meson.build
index ee4d8569d..6cf96bcbd 100644
--- a/src/libide/foundry/meson.build
+++ b/src/libide/foundry/meson.build
@@ -69,6 +69,7 @@ libide_foundry_private_headers = [
   'ide-config-private.h',
   'ide-device-private.h',
   'ide-foundry-init.h',
+  'ide-local-deploy-strategy.h',
   'ide-run-manager-private.h',
   'ide-runtime-private.h',
   'ide-toolchain-private.h',
@@ -144,6 +145,7 @@ libide_foundry_private_sources = [
   'ide-build-log.c',
   'ide-build-utils.c',
   'ide-foundry-init.c',
+  'ide-local-deploy-strategy.c',
 ]
 
 libide_foundry_sources += libide_foundry_public_sources
diff --git a/src/plugins/deviced/gbp-deviced-deploy-strategy.c 
b/src/plugins/deviced/gbp-deviced-deploy-strategy.c
index f8877cf5a..8e9108a97 100644
--- a/src/plugins/deviced/gbp-deviced-deploy-strategy.c
+++ b/src/plugins/deviced/gbp-deviced-deploy-strategy.c
@@ -99,12 +99,16 @@ gbp_deviced_deploy_strategy_load_async (IdeDeployStrategy   *strategy,
 }
 
 static gboolean
-gbp_deviced_deploy_strategy_load_finish (IdeDeployStrategy *self,
-                                         GAsyncResult *result,
-                                         GError **error)
+gbp_deviced_deploy_strategy_load_finish (IdeDeployStrategy  *self,
+                                         GAsyncResult       *result,
+                                         int                *priority,
+                                         GError            **error)
 {
-  g_return_val_if_fail (GBP_IS_DEVICED_DEPLOY_STRATEGY (self), FALSE);
-  g_return_val_if_fail (ide_task_is_valid (result, self), FALSE);
+  g_assert (GBP_IS_DEVICED_DEPLOY_STRATEGY (self));
+  g_assert (ide_task_is_valid (result, self));
+  g_assert (priority != NULL);
+
+  *priority = -100;
 
   return ide_task_propagate_boolean (IDE_TASK (result), error);
 }


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