[gnome-builder/wip/gtk4-port] plugins/meson: make GbpMesonIntrospection a pipeline stage



commit a71adf1ebb54f14a7f112ee0f38f8b7e2dc81362
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 24 16:51:32 2022 -0700

    plugins/meson: make GbpMesonIntrospection a pipeline stage
    
    The goal here is to attach it to the configure phase after configure runs
    so that we can reload our introspection data.

 src/plugins/meson/gbp-meson-introspection.c | 328 +++++++++++++++++++---------
 src/plugins/meson/gbp-meson-introspection.h |  15 +-
 2 files changed, 233 insertions(+), 110 deletions(-)
---
diff --git a/src/plugins/meson/gbp-meson-introspection.c b/src/plugins/meson/gbp-meson-introspection.c
index 140430700..4ae6be4bb 100644
--- a/src/plugins/meson/gbp-meson-introspection.c
+++ b/src/plugins/meson/gbp-meson-introspection.c
@@ -22,16 +22,23 @@
 
 #include "config.h"
 
+#include <glib/gi18n.h>
 #include <json-glib/json-glib.h>
 
 #include <libide-core.h>
+#include <libide-foundry.h>
 #include <libide-threading.h>
 
+#include "gbp-meson-build-system.h"
 #include "gbp-meson-introspection.h"
 
 struct _GbpMesonIntrospection
 {
-  GObject parent_instance;
+  IdePipelineStage parent_instance;
+
+  char *etag;
+
+  GListStore *run_commands;
 
   char *descriptive_name;
   char *subproject_dir;
@@ -40,32 +47,7 @@ struct _GbpMesonIntrospection
   guint loaded : 1;
 };
 
-G_DEFINE_FINAL_TYPE (GbpMesonIntrospection, gbp_meson_introspection, G_TYPE_OBJECT)
-
-static void
-gbp_meson_introspection_dispose (GObject *object)
-{
-  GbpMesonIntrospection *self = (GbpMesonIntrospection *)object;
-
-  g_clear_pointer (&self->descriptive_name, g_free);
-  g_clear_pointer (&self->subproject_dir, g_free);
-  g_clear_pointer (&self->version, g_free);
-
-  G_OBJECT_CLASS (gbp_meson_introspection_parent_class)->dispose (object);
-}
-
-static void
-gbp_meson_introspection_class_init (GbpMesonIntrospectionClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->dispose = gbp_meson_introspection_dispose;
-}
-
-static void
-gbp_meson_introspection_init (GbpMesonIntrospection *self)
-{
-}
+G_DEFINE_FINAL_TYPE (GbpMesonIntrospection, gbp_meson_introspection, IDE_TYPE_PIPELINE_STAGE)
 
 static gboolean
 get_string_member (JsonObject  *object,
@@ -203,11 +185,13 @@ static void
 gbp_meson_introspection_load_test (GbpMesonIntrospection *self,
                                    JsonObject            *test)
 {
+  g_autoptr(IdeRunCommand) run_command = NULL;
   g_auto(GStrv) cmd = NULL;
   g_auto(GStrv) env = NULL;
   g_auto(GStrv) suite = NULL;
   g_autofree char *name = NULL;
   g_autofree char *workdir = NULL;
+  g_autofree char *id = NULL;
 
   IDE_ENTRY;
 
@@ -220,6 +204,18 @@ gbp_meson_introspection_load_test (GbpMesonIntrospection *self,
   get_string_member (test, "name", &name);
   get_string_member (test, "workdir", &workdir);
 
+  id = g_strdup_printf ("meson:%s", name);
+
+  run_command = ide_run_command_new ();
+  ide_run_command_set_id (run_command, id);
+  ide_run_command_set_kind (run_command, IDE_RUN_COMMAND_KIND_TEST);
+  ide_run_command_set_display_name (run_command, name);
+  ide_run_command_set_environ (run_command, (const char * const *)env);
+  ide_run_command_set_argv (run_command, (const char * const *)cmd);
+  ide_run_command_set_cwd (run_command, workdir);
+
+  g_list_store_append (self->run_commands, run_command);
+
   IDE_EXIT;
 }
 
@@ -274,131 +270,265 @@ gbp_meson_introspection_load_installed (GbpMesonIntrospection *self,
   IDE_EXIT;
 }
 
-static void
-gbp_meson_introspection_load_file_worker (IdeTask      *task,
-                                          gpointer      source_object,
-                                          gpointer      task_data,
-                                          GCancellable *cancellable)
+static char *
+get_current_etag (IdePipeline *pipeline)
 {
-  g_autoptr(JsonParser) parser = NULL;
-  g_autoptr(GError) error = NULL;
-  GbpMesonIntrospection *self = source_object;
-  const char *filename = task_data;
-  JsonObject *obj;
-  JsonNode *root;
-  JsonNode *member;
+  g_autofree char *build_dot_ninja = NULL;
+  g_autoptr(GFileInfo) info = NULL;
+  g_autoptr(GFile) file = NULL;
 
-  IDE_ENTRY;
+  g_assert (IDE_IS_PIPELINE (pipeline));
+
+  build_dot_ninja = ide_pipeline_build_builddir_path (pipeline, "build.ninja", NULL);
+  file = g_file_new_for_path (build_dot_ninja);
+  info = g_file_query_info (file,
+                            G_FILE_ATTRIBUTE_ETAG_VALUE,
+                            G_FILE_QUERY_INFO_NONE,
+                            NULL, NULL);
+
+  if (info == NULL)
+    return NULL;
+
+  return g_strdup (g_file_info_get_etag (info));
+}
+
+static void
+gbp_meson_introspection_query (IdePipelineStage *stage,
+                               IdePipeline      *pipeline,
+                               GPtrArray        *targets,
+                               GCancellable     *cancellable)
+{
+  GbpMesonIntrospection *self = (GbpMesonIntrospection *)stage;
+  g_autofree char *etag = NULL;
 
-  g_assert (IDE_IS_TASK (task));
   g_assert (GBP_IS_MESON_INTROSPECTION (self));
-  g_assert (filename != NULL);
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  g_debug ("Loading meson introspection from %s", filename);
+  etag = get_current_etag (pipeline);
 
-  parser = json_parser_new ();
+  ide_pipeline_stage_set_completed (stage,
+                                    ide_str_equal0 (etag, self->etag));
+}
 
-  if (!json_parser_load_from_mapped_file (parser, filename, &error))
-    {
-      ide_task_return_error (task, g_steal_pointer (&error));
-      IDE_EXIT;
-    }
+static void
+gbp_meson_introspection_load_json (GbpMesonIntrospection *self,
+                                   JsonObject            *root)
+{
+  JsonNode *member;
 
-  if (!(root = json_parser_get_root (parser)) ||
-      !JSON_NODE_HOLDS_OBJECT (root) ||
-      !(obj = json_node_get_object (root)))
-    {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_INVALID_DATA,
-                                 "Root json node is not an object");
-      IDE_EXIT;
-    }
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_MESON_INTROSPECTION (self));
+  g_assert (root != NULL);
 
-  if (json_object_has_member (obj, "buildoptions") &&
-      (member = json_object_get_member (obj, "buildoptions")) &&
+  if (json_object_has_member (root, "buildoptions") &&
+      (member = json_object_get_member (root, "buildoptions")) &&
       JSON_NODE_HOLDS_ARRAY (member))
     gbp_meson_introspection_load_buildoptions (self, json_node_get_array (member));
 
-  if (json_object_has_member (obj, "projectinfo") &&
-      (member = json_object_get_member (obj, "projectinfo")) &&
+  if (json_object_has_member (root, "projectinfo") &&
+      (member = json_object_get_member (root, "projectinfo")) &&
       JSON_NODE_HOLDS_OBJECT (member))
     gbp_meson_introspection_load_projectinfo (self, json_node_get_object (member));
 
-  if (json_object_has_member (obj, "tests") &&
-      (member = json_object_get_member (obj, "tests")) &&
+  if (json_object_has_member (root, "tests") &&
+      (member = json_object_get_member (root, "tests")) &&
       JSON_NODE_HOLDS_ARRAY (member))
     gbp_meson_introspection_load_tests (self, json_node_get_array (member));
 
-  if (json_object_has_member (obj, "benchmarks") &&
-      (member = json_object_get_member (obj, "benchmarks")) &&
+  if (json_object_has_member (root, "benchmarks") &&
+      (member = json_object_get_member (root, "benchmarks")) &&
       JSON_NODE_HOLDS_ARRAY (member))
     gbp_meson_introspection_load_benchmarks (self, json_node_get_array (member));
 
-  if (json_object_has_member (obj, "installed") &&
-      (member = json_object_get_member (obj, "installed")) &&
+  if (json_object_has_member (root, "installed") &&
+      (member = json_object_get_member (root, "installed")) &&
       JSON_NODE_HOLDS_OBJECT (member))
     gbp_meson_introspection_load_installed (self, json_node_get_object (member));
 
-  ide_task_return_boolean (task, TRUE);
-
   IDE_EXIT;
 }
 
-GbpMesonIntrospection *
-gbp_meson_introspection_new (void)
+static void
+gbp_meson_introspection_load_stream_cb (GObject      *object,
+                                        GAsyncResult *result,
+                                        gpointer      user_data)
 {
-  return g_object_new (GBP_TYPE_MESON_INTROSPECTION, NULL);
+  JsonParser *parser = (JsonParser *)object;
+  GbpMesonIntrospection *self;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  const char *etag;
+  JsonObject *obj;
+  JsonNode *root;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (JSON_IS_PARSER (parser));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!json_parser_load_from_stream_finish (parser, result, &error))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  self = ide_task_get_source_object (task);
+  etag = ide_task_get_task_data (task);
+
+  g_assert (GBP_IS_MESON_INTROSPECTION (self));
+  g_assert (etag != NULL);
+
+  /* Clear all of our previously loaded state */
+  ide_set_string (&self->etag, etag);
+  g_list_store_remove_all (self->run_commands);
+
+  if ((root = json_parser_get_root (parser)) &&
+      JSON_NODE_HOLDS_OBJECT (root) &&
+      (obj = json_node_get_object (root)))
+    gbp_meson_introspection_load_json (self, obj);
+
+  ide_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
 }
 
-void
-gbp_meson_introspection_load_file_async (GbpMesonIntrospection *self,
-                                         const char            *path,
-                                         GCancellable          *cancellable,
-                                         GAsyncReadyCallback    callback,
-                                         gpointer               user_data)
+static void
+gbp_meson_introspection_build_async (IdePipelineStage    *stage,
+                                     IdePipeline         *pipeline,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
 {
+  GbpMesonIntrospection *self = (GbpMesonIntrospection *)stage;
+  g_autoptr(IdeRunContext) run_context = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(JsonParser) parser = NULL;
+  g_autoptr(GIOStream) io_stream = NULL;
   g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree char *meson = NULL;
+  IdeBuildSystem *build_system;
+  IdeContext *context;
 
   IDE_ENTRY;
 
-  g_return_if_fail (GBP_IS_MESON_INTROSPECTION (self));
-  g_return_if_fail (path != NULL);
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (GBP_IS_MESON_INTROSPECTION (self));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_meson_introspection_build_async);
+  ide_task_set_task_data (task, get_current_etag (pipeline), g_free);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_system = ide_build_system_from_context (context);
+  meson = gbp_meson_build_system_locate_meson (GBP_MESON_BUILD_SYSTEM (build_system), pipeline);
+
+  g_assert (IDE_IS_CONTEXT (context));
+  g_assert (GBP_IS_MESON_BUILD_SYSTEM (build_system));
+  g_assert (meson != NULL);
 
-  task = ide_task_new (task, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, gbp_meson_introspection_load_file_async);
+  run_context = ide_run_context_new ();
+  ide_pipeline_prepare_run_context (pipeline, run_context);
+  ide_run_context_append_args (run_context, IDE_STRV_INIT (meson, "introspect", "--all", 
"--force-object-output"));
 
-  if (self->loaded)
+  /* Create a stream to communicate with the subprocess and then spawn it */
+  if (!(io_stream = ide_run_context_create_stdio_stream (run_context, &error)) ||
+      !(subprocess = ide_run_context_spawn (run_context, &error)))
     {
-      ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_INVAL,
-                                 "Already initialized");
+      ide_task_return_error (task, g_steal_pointer (&error));
       IDE_EXIT;
     }
 
-  self->loaded = TRUE;
+  /* Keep stream alive for duration of operation */
+  g_object_set_data_full (G_OBJECT (task),
+                          "IO_STREAM",
+                          g_object_ref (io_stream),
+                          g_object_unref);
+
+  /* Start parsing our input stream */
+  parser = json_parser_new ();
+  json_parser_load_from_stream_async (parser,
+                                      g_io_stream_get_input_stream (io_stream),
+                                      cancellable,
+                                      gbp_meson_introspection_load_stream_cb,
+                                      g_steal_pointer (&task));
 
-  ide_task_set_task_data (task, g_strdup (path), g_free);
-  ide_task_run_in_thread (task, gbp_meson_introspection_load_file_worker);
+  /* Make sure something watches the child */
+  ide_subprocess_wait_async (subprocess, NULL, NULL, NULL);
 
   IDE_EXIT;
 }
 
-gboolean
-gbp_meson_introspection_load_file_finish (GbpMesonIntrospection *self,
-                                          GAsyncResult *result,
-                                          GError **error)
+static gboolean
+gbp_meson_introspection_build_finish (IdePipelineStage  *stage,
+                                      GAsyncResult      *result,
+                                      GError           **error)
 {
   gboolean ret;
 
   IDE_ENTRY;
 
-  g_return_val_if_fail (GBP_IS_MESON_INTROSPECTION (self), FALSE);
-  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+  g_assert (GBP_IS_MESON_INTROSPECTION (stage));
+  g_assert (IDE_IS_TASK (result));
 
   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
 
   IDE_RETURN (ret);
 }
+
+static void
+gbp_meson_introspection_dispose (GObject *object)
+{
+  GbpMesonIntrospection *self = (GbpMesonIntrospection *)object;
+
+  g_clear_object (&self->run_commands);
+
+  g_clear_pointer (&self->descriptive_name, g_free);
+  g_clear_pointer (&self->subproject_dir, g_free);
+  g_clear_pointer (&self->version, g_free);
+  g_clear_pointer (&self->etag, g_free);
+
+  G_OBJECT_CLASS (gbp_meson_introspection_parent_class)->dispose (object);
+}
+
+static void
+gbp_meson_introspection_class_init (GbpMesonIntrospectionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdePipelineStageClass *pipeline_stage_class = IDE_PIPELINE_STAGE_CLASS (klass);
+
+  object_class->dispose = gbp_meson_introspection_dispose;
+
+  pipeline_stage_class->query = gbp_meson_introspection_query;
+  pipeline_stage_class->build_async = gbp_meson_introspection_build_async;
+  pipeline_stage_class->build_finish = gbp_meson_introspection_build_finish;
+}
+
+static void
+gbp_meson_introspection_init (GbpMesonIntrospection *self)
+{
+  self->run_commands = g_list_store_new (IDE_TYPE_RUN_COMMAND);
+
+  ide_pipeline_stage_set_name (IDE_PIPELINE_STAGE (self),
+                               _("Load Meson Introspection"));
+}
+
+GbpMesonIntrospection *
+gbp_meson_introspection_new (void)
+{
+  return g_object_new (GBP_TYPE_MESON_INTROSPECTION, NULL);
+}
+
+GListModel *
+gbp_meson_introspection_list_run_commands (GbpMesonIntrospection *self)
+{
+  g_return_val_if_fail (GBP_IS_MESON_INTROSPECTION (self), NULL);
+
+  return g_object_ref (G_LIST_MODEL (self->run_commands));
+}
diff --git a/src/plugins/meson/gbp-meson-introspection.h b/src/plugins/meson/gbp-meson-introspection.h
index 68e709593..1573c0f63 100644
--- a/src/plugins/meson/gbp-meson-introspection.h
+++ b/src/plugins/meson/gbp-meson-introspection.h
@@ -20,22 +20,15 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#include <libide-foundry.h>
 
 G_BEGIN_DECLS
 
 #define GBP_TYPE_MESON_INTROSPECTION (gbp_meson_introspection_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpMesonIntrospection, gbp_meson_introspection, GBP, MESON_INTROSPECTION, GObject)
+G_DECLARE_FINAL_TYPE (GbpMesonIntrospection, gbp_meson_introspection, GBP, MESON_INTROSPECTION, 
IdePipelineStage)
 
-GbpMesonIntrospection *gbp_meson_introspection_new              (void);
-void                   gbp_meson_introspection_load_file_async  (GbpMesonIntrospection  *self,
-                                                                 const char             *path,
-                                                                 GCancellable           *cancellable,
-                                                                 GAsyncReadyCallback     callback,
-                                                                 gpointer                user_data);
-gboolean               gbp_meson_introspection_load_file_finish (GbpMesonIntrospection  *self,
-                                                                 GAsyncResult           *result,
-                                                                 GError                **error);
+GbpMesonIntrospection *gbp_meson_introspection_new               (void);
+GListModel            *gbp_meson_introspection_list_run_commands (GbpMesonIntrospection *self);
 
 G_END_DECLS


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