[gnome-builder] buildsystem: add IdeBuildTargetProvider interface



commit d61623d55bdb3461ef8b4ebb229bea175db03d3b
Author: Christian Hergert <chergert redhat com>
Date:   Sun Nov 19 19:29:36 2017 -0800

    buildsystem: add IdeBuildTargetProvider interface
    
    This moves away from using the build system API for getting
    build targets to this new interface. Build systems will need
    to implement this if they want to support running.
    
    This will also allow for things like Flatpak to provide a
    default target matching the "command" from the manifest.

 src/libide/buildsystem/ide-build-target-provider.c |  117 +++++++++++++++
 src/libide/buildsystem/ide-build-target-provider.h |   53 +++++++
 src/libide/buildsystem/ide-build-target.c          |   30 ++++
 src/libide/buildsystem/ide-build-target.h          |   19 +--
 src/libide/buildsystem/meson.build                 |    2 +
 src/libide/ide.h                                   |    1 +
 src/libide/runner/ide-run-manager.c                |  154 +++++++++++++-------
 7 files changed, 314 insertions(+), 62 deletions(-)
---
diff --git a/src/libide/buildsystem/ide-build-target-provider.c 
b/src/libide/buildsystem/ide-build-target-provider.c
new file mode 100644
index 0000000..558e5f8
--- /dev/null
+++ b/src/libide/buildsystem/ide-build-target-provider.c
@@ -0,0 +1,117 @@
+/* ide-build-target-provider.c
+ *
+ * Copyright (C) 2017 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-target-provider"
+
+#include "ide-context.h"
+#include "ide-debug.h"
+
+#include "buildsystem/ide-build-target-provider.h"
+
+G_DEFINE_INTERFACE (IdeBuildTargetProvider, ide_build_target_provider, G_TYPE_OBJECT)
+
+static void
+ide_build_target_provider_real_get_targets_async (IdeBuildTargetProvider *provider,
+                                                  GCancellable           *cancellable,
+                                                  GAsyncReadyCallback     callback,
+                                                  gpointer                user_data)
+{
+  g_task_report_new_error (provider, callback, user_data,
+                           ide_build_target_provider_real_get_targets_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "Loading targets is not supported by %s",
+                           G_OBJECT_TYPE_NAME (provider));
+}
+
+static GPtrArray *
+ide_build_target_provider_real_get_targets_finish (IdeBuildTargetProvider  *provider,
+                                                   GAsyncResult            *result,
+                                                   GError                 **error)
+{
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_build_target_provider_default_init (IdeBuildTargetProviderInterface *iface)
+{
+  iface->get_targets_async = ide_build_target_provider_real_get_targets_async;
+  iface->get_targets_finish = ide_build_target_provider_real_get_targets_finish;
+
+  g_object_interface_install_property (iface,
+                                       g_param_spec_object ("context", NULL, NULL,
+                                                            IDE_TYPE_CONTEXT,
+                                                            (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS)));
+}
+
+/**
+ * ide_build_target_provider_get_targets_async:
+ * @self: an #IdeBuildTargetProvider
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: (scope async): a callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously requests that the provider fetch all of the known build
+ * targets that are part of the project. Generally this should be limited to
+ * executables that Builder might be interested in potentially running.
+ *
+ * @callback should call ide_build_target_provider_get_targets_finish() to
+ * complete the asynchronous operation.
+ *
+ * See also: ide_build_target_provider_get_targets_finish()
+ *
+ * Since: 3.28
+ */
+void
+ide_build_target_provider_get_targets_async (IdeBuildTargetProvider *self,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data)
+{
+  g_return_if_fail (IDE_IS_BUILD_TARGET_PROVIDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_BUILD_TARGET_PROVIDER_GET_IFACE (self)->get_targets_async (self,
+                                                                 cancellable,
+                                                                 callback,
+                                                                 user_data);
+}
+
+/**
+ * ide_build_target_provider_get_targets_finish:
+ * @self: an #IdeBuildTargetProvider
+ * @result: a #GAsyncResult provided to the callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes a request to get the targets for the project.
+ *
+ * See also: ide_build_target_provider_get_targets_async()
+ *
+ * Returns: (transfer container) (element-type Ide.BuildTarget): The array of
+ *   build targets or %NULL upon failure and @error is set.
+ */
+GPtrArray *
+ide_build_target_provider_get_targets_finish (IdeBuildTargetProvider  *self,
+                                              GAsyncResult            *result,
+                                              GError                 **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_TARGET_PROVIDER (self), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  return IDE_BUILD_TARGET_PROVIDER_GET_IFACE (self)->get_targets_finish (self, result, error);
+}
diff --git a/src/libide/buildsystem/ide-build-target-provider.h 
b/src/libide/buildsystem/ide-build-target-provider.h
new file mode 100644
index 0000000..1a485dc
--- /dev/null
+++ b/src/libide/buildsystem/ide-build-target-provider.h
@@ -0,0 +1,53 @@
+/* ide-build-target-provider.h
+ *
+ * Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "ide-object.h"
+#include "ide-version-macros.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_TARGET_PROVIDER (ide_build_target_provider_get_type())
+
+G_DECLARE_INTERFACE (IdeBuildTargetProvider, ide_build_target_provider, IDE, BUILD_TARGET_PROVIDER, 
IdeObject)
+
+struct _IdeBuildTargetProviderInterface
+{
+  GTypeInterface parent_iface;
+
+  void       (*get_targets_async)  (IdeBuildTargetProvider  *self,
+                                    GCancellable            *cancellable,
+                                    GAsyncReadyCallback      callback,
+                                    gpointer                 user_data);
+  GPtrArray *(*get_targets_finish) (IdeBuildTargetProvider  *self,
+                                    GAsyncResult            *result,
+                                    GError                 **error);
+};
+
+IDE_AVAILABLE_IN_3_28
+void       ide_build_target_provider_get_targets_async  (IdeBuildTargetProvider  *self,
+                                                         GCancellable            *cancellable,
+                                                         GAsyncReadyCallback      callback,
+                                                         gpointer                 user_data);
+IDE_AVAILABLE_IN_3_28
+GPtrArray *ide_build_target_provider_get_targets_finish (IdeBuildTargetProvider  *self,
+                                                         GAsyncResult            *result,
+                                                         GError                 **error);
+
+G_END_DECLS
diff --git a/src/libide/buildsystem/ide-build-target.c b/src/libide/buildsystem/ide-build-target.c
index 144c3ce..3e0ad2d 100644
--- a/src/libide/buildsystem/ide-build-target.c
+++ b/src/libide/buildsystem/ide-build-target.c
@@ -58,3 +58,33 @@ ide_build_target_get_name (IdeBuildTarget *self)
 
   return NULL;
 }
+
+/**
+ * ide_build_target_get_priority:
+ * @self: an #IdeBuildTarget
+ *
+ * Gets the priority of the build target. This is used to sort build targets by
+ * their importance. The lowest value (negative values are allowed) will be run
+ * as the default run target by Builder.
+ *
+ * Returns: the priority of the build target
+ *
+ * Since: 3.28
+ */
+gint
+ide_build_target_get_priority (IdeBuildTarget *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_TARGET (self), 0);
+
+  if (IDE_BUILD_TARGET_GET_IFACE (self)->get_priority)
+    return IDE_BUILD_TARGET_GET_IFACE (self)->get_priority (self);
+  return 0;
+}
+
+gint
+ide_build_target_compare (const IdeBuildTarget *left,
+                          const IdeBuildTarget *right)
+{
+  return ide_build_target_get_priority ((IdeBuildTarget *)left) -
+         ide_build_target_get_priority ((IdeBuildTarget *)right);
+}
diff --git a/src/libide/buildsystem/ide-build-target.h b/src/libide/buildsystem/ide-build-target.h
index 460295f..ae46c31 100644
--- a/src/libide/buildsystem/ide-build-target.h
+++ b/src/libide/buildsystem/ide-build-target.h
@@ -36,20 +36,17 @@ struct _IdeBuildTargetInterface
 
   GFile  *(*get_install_directory) (IdeBuildTarget *self);
   gchar  *(*get_name)              (IdeBuildTarget *self);
-
-  /*< private >*/
-  gpointer _reserved1;
-  gpointer _reserved2;
-  gpointer _reserved3;
-  gpointer _reserved4;
-  gpointer _reserved5;
-  gpointer _reserved6;
-  gpointer _reserved7;
+  gint    (*get_priority)          (IdeBuildTarget *self);
 };
 
 IDE_AVAILABLE_IN_ALL
-GFile  *ide_build_target_get_install_directory (IdeBuildTarget *self);
+GFile    *ide_build_target_get_install_directory (IdeBuildTarget       *self);
 IDE_AVAILABLE_IN_ALL
-gchar  *ide_build_target_get_name              (IdeBuildTarget *self);
+gchar    *ide_build_target_get_name              (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_28
+gint      ide_build_target_get_priority          (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_28
+gboolean  ide_build_target_compare               (const IdeBuildTarget *left,
+                                                  const IdeBuildTarget *right);
 
 G_END_DECLS
diff --git a/src/libide/buildsystem/meson.build b/src/libide/buildsystem/meson.build
index 3fc55d4..de193b4 100644
--- a/src/libide/buildsystem/meson.build
+++ b/src/libide/buildsystem/meson.build
@@ -10,6 +10,7 @@ buildsystem_headers = [
   'ide-build-system-discovery.h',
   'ide-build-system.h',
   'ide-build-target.h',
+  'ide-build-target-provider.h',
   'ide-build-utils.h',
   'ide-compile-commands.h',
   'ide-configuration-manager.h',
@@ -30,6 +31,7 @@ buildsystem_sources = [
   'ide-build-system-discovery.c',
   'ide-build-system.c',
   'ide-build-target.c',
+  'ide-build-target-provider.c',
   'ide-build-utils.c',
   'ide-compile-commands.c',
   'ide-configuration-manager.c',
diff --git a/src/libide/ide.h b/src/libide/ide.h
index 091c760..320ce4a 100644
--- a/src/libide/ide.h
+++ b/src/libide/ide.h
@@ -58,6 +58,7 @@ G_BEGIN_DECLS
 #include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-system-discovery.h"
 #include "buildsystem/ide-build-target.h"
+#include "buildsystem/ide-build-target-provider.h"
 #include "buildsystem/ide-compile-commands.h"
 #include "buildsystem/ide-configuration-manager.h"
 #include "buildsystem/ide-configuration.h"
diff --git a/src/libide/runner/ide-run-manager.c b/src/libide/runner/ide-run-manager.c
index 7f4e0a5..9f04b63 100644
--- a/src/libide/runner/ide-run-manager.c
+++ b/src/libide/runner/ide-run-manager.c
@@ -19,6 +19,8 @@
 #define G_LOG_DOMAIN "ide-run-manager"
 
 #include <glib/gi18n.h>
+#include <libpeas/peas.h>
+#include <libpeas/peas-autocleanups.h>
 
 #include "ide-context.h"
 #include "ide-debug.h"
@@ -26,6 +28,7 @@
 #include "buildsystem/ide-build-manager.h"
 #include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-target.h"
+#include "buildsystem/ide-build-target-provider.h"
 #include "buildsystem/ide-configuration.h"
 #include "buildsystem/ide-configuration-manager.h"
 #include "buildsystem/ide-environment.h"
@@ -47,6 +50,13 @@ struct _IdeRunManager
   guint                    busy : 1;
 };
 
+typedef struct
+{
+  GList     *providers;
+  GPtrArray *results;
+  guint      active;
+} DiscoverState;
+
 static void initable_iface_init                      (GInitableIface *iface);
 static void ide_run_manager_actions_run              (IdeRunManager  *self,
                                                       GVariant       *param);
@@ -84,6 +94,18 @@ static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
 
 static void
+discover_state_free (gpointer data)
+{
+  DiscoverState *state = data;
+
+  g_assert (state->active == 0);
+
+  g_list_free_full (state->providers, g_object_unref);
+  g_clear_pointer (&state->results, g_ptr_array_unref);
+  g_slice_free (DiscoverState, state);
+}
+
+static void
 ide_run_manager_real_run (IdeRunManager *self,
                           IdeRunner     *runner)
 {
@@ -814,74 +836,78 @@ ide_run_manager_set_build_target (IdeRunManager  *self,
     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUILD_TARGET]);
 }
 
-static IdeBuildTarget *
-find_best_target (GPtrArray *targets)
+static void
+collect_extensions (PeasExtensionSet *set,
+                    PeasPluginInfo   *plugin_info,
+                    PeasExtension    *exten,
+                    gpointer          user_data)
 {
-  IdeBuildTarget *ret = NULL;
-  guint i;
-
-  g_assert (targets != NULL);
+  DiscoverState *state = user_data;
 
-  /* TODO:
-   *
-   * This is just a barebones way to try to discover a target that matters. We
-   * could probably defer this off to the build system. Either way, it's shit
-   * and should be thought through by someone.
-   */
-
-  for (i = 0; i < targets->len; i++)
-    {
-      IdeBuildTarget *target = g_ptr_array_index (targets, i);
-      g_autoptr(GFile) installdir = NULL;
-
-      installdir = ide_build_target_get_install_directory (target);
+  g_assert (state != NULL);
+  g_assert (IDE_IS_BUILD_TARGET_PROVIDER (exten));
 
-      if (installdir == NULL)
-        continue;
-
-      if (ret == NULL)
-        ret = target;
-    }
-
-  return ret;
+  state->providers = g_list_append (state->providers, g_object_ref (exten));
+  state->active++;
 }
 
 static void
-ide_run_manager_discover_default_target_cb (GObject      *object,
-                                            GAsyncResult *result,
-                                            gpointer      user_data)
+ide_run_manager_provider_get_targets_cb (GObject      *object,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
 {
-  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+  IdeBuildTargetProvider *provider = (IdeBuildTargetProvider *)object;
   g_autoptr(GTask) task = user_data;
-  g_autoptr(GPtrArray) targets = NULL;
+  g_autoptr(GPtrArray) ret = NULL;
   g_autoptr(GError) error = NULL;
-  IdeBuildTarget *best_match;
+  DiscoverState *state;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert (IDE_IS_BUILD_TARGET_PROVIDER (provider));
   g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  state = g_task_get_task_data (task);
 
-  targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
+  g_assert (state != NULL);
+  g_assert (state->active > 0);
+  g_assert (g_list_find (state->providers, provider) != NULL);
 
-  if (targets == NULL)
+  ret = ide_build_target_provider_get_targets_finish (provider, result, &error);
+
+  if (ret != NULL)
     {
-      g_task_return_error (task, g_steal_pointer (&error));
-      IDE_EXIT;
+      for (guint i = 0; i < ret->len; i++)
+        {
+          IdeBuildTarget *target = g_ptr_array_index (ret, i);
+
+          g_ptr_array_add (state->results, g_object_ref (target));
+        }
     }
 
-  best_match = find_best_target (targets);
+  state->active--;
+
+  if (state->active > 0)
+    return;
 
-  if (best_match == NULL)
+  if (state->results->len == 0)
     {
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_FAILED,
-                               "Failed to locate build target");
+      if (error != NULL)
+        g_task_return_error (task, g_steal_pointer (&error));
+      else
+        g_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_FOUND,
+                                 "Failed to locate a build target");
       IDE_EXIT;
     }
 
-  g_task_return_pointer (task, g_object_ref (best_match), g_object_unref);
+  g_ptr_array_sort (state->results, (GCompareFunc)ide_build_target_compare);
+
+  g_task_return_pointer (task,
+                         g_object_ref (g_ptr_array_index (state->results, 0)),
+                         g_object_unref);
 
   IDE_EXIT;
 }
@@ -892,8 +918,9 @@ ide_run_manager_discover_default_target_async (IdeRunManager       *self,
                                                GAsyncReadyCallback  callback,
                                                gpointer             user_data)
 {
+  g_autoptr(PeasExtensionSet) set = NULL;
   g_autoptr(GTask) task = NULL;
-  IdeBuildSystem *build_system;
+  DiscoverState *state;
   IdeContext *context;
 
   IDE_ENTRY;
@@ -903,14 +930,39 @@ ide_run_manager_discover_default_target_async (IdeRunManager       *self,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  build_system = ide_context_get_build_system (context);
 
-  ide_build_system_get_build_targets_async (build_system,
-                                            cancellable,
-                                            ide_run_manager_discover_default_target_cb,
-                                            g_object_ref (task));
+  set = peas_extension_set_new (peas_engine_get_default (),
+                                IDE_TYPE_BUILD_TARGET_PROVIDER,
+                                "context", context,
+                                NULL);
+
+  state = g_slice_new (DiscoverState);
+  state->results = g_ptr_array_new_with_free_func (g_object_unref);
+  state->providers = NULL;
+  state->active = 0;
+
+  peas_extension_set_foreach (set, collect_extensions, state);
+
+  g_task_set_task_data (task, state, discover_state_free);
+
+  for (const GList *iter = state->providers; iter != NULL; iter = iter->next)
+    {
+      IdeBuildTargetProvider *provider = iter->data;
+
+      ide_build_target_provider_get_targets_async (provider,
+                                                   cancellable,
+                                                   ide_run_manager_provider_get_targets_cb,
+                                                   g_object_ref (task));
+    }
+
+  if (state->active == 0)
+    g_task_return_new_error (task,
+                             G_IO_ERROR,
+                             G_IO_ERROR_NOT_FOUND,
+                             "Failed to locate a build target");
 
   IDE_EXIT;
 }


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