[gnome-builder] config: monitor manifests for changes



commit 23f9747b1a20b01c144b66fa4ede985cf4f8191f
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 31 01:51:06 2018 -0800

    config: monitor manifests for changes
    
    If a flatpak manifest is changed outside of Builder, this
    ensures that we update it. It tries to avoid reacting to changes
    caused by saving the manifest back to disk.

 .../flatpak/gbp-flatpak-configuration-provider.c   | 174 ++++++++++++++++++++-
 src/plugins/flatpak/gbp-flatpak-manifest.c         |  90 ++++++++++-
 2 files changed, 256 insertions(+), 8 deletions(-)
---
diff --git a/src/plugins/flatpak/gbp-flatpak-configuration-provider.c 
b/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
index e09f2600a..af93bedeb 100644
--- a/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
+++ b/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
@@ -33,7 +33,9 @@ struct _GbpFlatpakConfigurationProvider
   GPtrArray *configs;
 };
 
-static void gbp_flatpak_configuration_provider_save_tick (GTask *task);
+static void manifest_save_tick    (GTask                           *task);
+static void manifest_needs_reload (GbpFlatpakConfigurationProvider *self,
+                                   GbpFlatpakManifest              *manifest);
 
 static void
 gbp_flatpak_configuration_provider_save_cb (GObject      *object,
@@ -51,11 +53,11 @@ gbp_flatpak_configuration_provider_save_cb (GObject      *object,
   if (!gbp_flatpak_manifest_save_finish (manifest, result, &error))
     g_warning ("Failed to save manifest: %s", error->message);
 
-  gbp_flatpak_configuration_provider_save_tick (task);
+  manifest_save_tick (task);
 }
 
 static void
-gbp_flatpak_configuration_provider_save_tick (GTask *task)
+manifest_save_tick (GTask *task)
 {
   g_autoptr(GbpFlatpakManifest) manifest = NULL;
   GbpFlatpakConfigurationProvider *self;
@@ -125,7 +127,7 @@ gbp_flatpak_configuration_provider_save_async (IdeConfigurationProvider *provide
 
   g_task_set_task_data (task, g_steal_pointer (&ar), (GDestroyNotify)g_ptr_array_unref);
 
-  gbp_flatpak_configuration_provider_save_tick (task);
+  manifest_save_tick (task);
 
   IDE_EXIT;
 }
@@ -141,6 +143,155 @@ gbp_flatpak_configuration_provider_save_finish (IdeConfigurationProvider  *provi
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static void
+load_manifest_worker (GTask        *task,
+                      gpointer      source_object,
+                      gpointer      task_data,
+                      GCancellable *cancellable)
+{
+  GbpFlatpakConfigurationProvider *self = source_object;
+  g_autoptr(GbpFlatpakManifest) manifest = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *name = NULL;
+  IdeContext *context;
+  GFile *file = task_data;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  name = g_file_get_basename (file);
+  manifest = gbp_flatpak_manifest_new (context, file, name);
+
+  if (!g_initable_init (G_INITABLE (manifest), cancellable, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  g_signal_connect_object (manifest,
+                           "needs-reload",
+                           G_CALLBACK (manifest_needs_reload),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_task_return_pointer (task, g_steal_pointer (&manifest), g_object_unref);
+}
+
+static void
+load_manifest_async (GbpFlatpakConfigurationProvider *self,
+                     GFile                           *file,
+                     GCancellable                    *cancellable,
+                     GAsyncReadyCallback              callback,
+                     gpointer                         user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, load_manifest_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+  g_task_run_in_thread (task, load_manifest_worker);
+}
+
+static GbpFlatpakManifest *
+load_manifest_finish (GbpFlatpakConfigurationProvider  *self,
+                      GAsyncResult                     *result,
+                      GError                          **error)
+{
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (G_IS_TASK (result));
+  g_assert (g_task_is_valid (G_TASK (result), self));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+reload_manifest_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  GbpFlatpakConfigurationProvider *self = (GbpFlatpakConfigurationProvider *)object;
+  g_autoptr(GbpFlatpakManifest) old_manifest = user_data;
+  g_autoptr(GbpFlatpakManifest) new_manifest = NULL;
+  g_autoptr(GError) error = NULL;
+  IdeConfigurationManager *manager;
+  IdeConfiguration *current;
+  IdeContext *context;
+  gboolean is_active;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_FLATPAK_MANIFEST (old_manifest));
+
+  new_manifest = load_manifest_finish (self, result, &error);
+
+  if (new_manifest == NULL)
+    {
+      g_warning ("Failed to reload manifest: %s", error->message);
+
+      /* Watch for future changes */
+      g_signal_connect_object (old_manifest,
+                               "needs-reload",
+                               G_CALLBACK (manifest_needs_reload),
+                               self,
+                               G_CONNECT_SWAPPED);
+      return;
+    }
+
+  g_ptr_array_remove (self->configs, old_manifest);
+  g_ptr_array_add (self->configs, g_object_ref (new_manifest));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  manager = ide_context_get_configuration_manager (context);
+  current = ide_configuration_manager_get_current (manager);
+
+  is_active = current == IDE_CONFIGURATION (old_manifest);
+
+  ide_configuration_provider_emit_added (IDE_CONFIGURATION_PROVIDER (self),
+                                         IDE_CONFIGURATION (new_manifest));
+
+  if (is_active)
+    ide_configuration_manager_set_current (manager,
+                                           IDE_CONFIGURATION (new_manifest));
+
+  ide_configuration_provider_emit_removed (IDE_CONFIGURATION_PROVIDER (self),
+                                           IDE_CONFIGURATION (old_manifest));
+}
+
+static void
+manifest_needs_reload (GbpFlatpakConfigurationProvider *self,
+                       GbpFlatpakManifest              *manifest)
+{
+  g_autoptr(GTask) task = NULL;
+  GFile *file;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (GBP_IS_FLATPAK_MANIFEST (manifest));
+
+  g_signal_handlers_disconnect_by_func (manifest,
+                                        G_CALLBACK (manifest_needs_reload),
+                                        self);
+
+  file = gbp_flatpak_manifest_get_file (manifest);
+
+  load_manifest_async (self,
+                       file,
+                       NULL,
+                       reload_manifest_cb,
+                       g_object_ref (manifest));
+
+  IDE_EXIT;
+}
+
 static void
 gbp_flatpak_configuration_provider_load_worker (GTask        *task,
                                                 gpointer      source_object,
@@ -179,6 +330,12 @@ gbp_flatpak_configuration_provider_load_worker (GTask        *task,
           continue;
         }
 
+      g_signal_connect_object (manifest,
+                               "needs-reload",
+                               G_CALLBACK (manifest_needs_reload),
+                               self,
+                               G_CONNECT_SWAPPED);
+
       g_ptr_array_add (manifests, g_steal_pointer (&manifest));
     }
 
@@ -342,13 +499,16 @@ gbp_flatpak_configuration_provider_unload (IdeConfigurationProvider *provider)
           g_autoptr(IdeConfiguration) config = NULL;
 
           config = g_object_ref (g_ptr_array_index (self->configs, i - 1));
-          g_ptr_array_remove_index (self->configs, i);
+          g_signal_handlers_disconnect_by_func (config,
+                                                G_CALLBACK (manifest_needs_reload),
+                                                self);
+          g_ptr_array_remove_index (self->configs, i - 1);
 
           ide_configuration_provider_emit_removed (provider, config);
         }
-    }
 
-  g_clear_pointer (&self->configs, g_ptr_array_unref);
+      g_clear_pointer (&self->configs, g_ptr_array_unref);
+    }
 
   IDE_EXIT;
 }
diff --git a/src/plugins/flatpak/gbp-flatpak-manifest.c b/src/plugins/flatpak/gbp-flatpak-manifest.c
index 3512a2e18..4e702d11e 100644
--- a/src/plugins/flatpak/gbp-flatpak-manifest.c
+++ b/src/plugins/flatpak/gbp-flatpak-manifest.c
@@ -30,6 +30,7 @@ struct _GbpFlatpakManifest
   IdeConfiguration  parent_instance;
 
   GFile            *file;
+  GFileMonitor     *file_monitor;
 
   JsonNode         *root;
 
@@ -65,7 +66,13 @@ enum {
   N_PROPS
 };
 
+enum {
+  NEEDS_RELOAD,
+  N_SIGNALS
+};
+
 static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
 static GRegex *app_id_regex;
 
 static gboolean
@@ -462,12 +469,81 @@ gbp_flatpak_manifest_supports_runtime (IdeConfiguration *config,
   return GBP_IS_FLATPAK_RUNTIME (runtime);
 }
 
+static void
+gbp_flatpak_manifest_file_changed (GbpFlatpakManifest *self,
+                                   GFile              *file,
+                                   GFile              *other_file,
+                                   GFileMonitorEvent   event,
+                                   GFileMonitor       *file_monitor)
+{
+  g_assert (GBP_IS_FLATPAK_MANIFEST (self));
+  g_assert (G_IS_FILE_MONITOR (file_monitor));
+
+  if (event == G_FILE_MONITOR_EVENT_CHANGED || event == G_FILE_MONITOR_EVENT_CREATED)
+    g_signal_emit (self, signals [NEEDS_RELOAD], 0);
+}
+
+static void
+gbp_flatpak_manifest_block_monitor (GbpFlatpakManifest *self)
+{
+  g_assert (GBP_IS_FLATPAK_MANIFEST (self));
+
+  g_signal_handlers_block_matched (self->file_monitor,
+                                   G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
+                                   g_signal_lookup ("changed", G_TYPE_FILE_MONITOR),
+                                   0,
+                                   NULL,
+                                   gbp_flatpak_manifest_file_changed,
+                                   self);
+}
+
+static void
+gbp_flatpak_manifest_unblock_monitor (GbpFlatpakManifest *self)
+{
+  g_assert (GBP_IS_FLATPAK_MANIFEST (self));
+
+  g_signal_handlers_unblock_matched (self->file_monitor,
+                                     G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
+                                     g_signal_lookup ("changed", G_TYPE_FILE_MONITOR),
+                                     0,
+                                     NULL,
+                                     gbp_flatpak_manifest_file_changed,
+                                     self);
+}
+
+static void
+gbp_flatpak_manifest_set_file (GbpFlatpakManifest *self,
+                               GFile              *file)
+{
+  g_assert (GBP_IS_FLATPAK_MANIFEST (self));
+  g_assert (self->file == NULL);
+  g_assert (self->file_monitor == NULL);
+  g_assert (!file || G_IS_FILE (file));
+
+  if (file == NULL)
+    {
+      g_critical ("GbpFlatpakManifest:file is required upon construction");
+      return;
+    }
+
+  g_set_object (&self->file, file);
+
+  self->file_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
+
+  g_signal_connect_object (self->file_monitor,
+                           "changed",
+                           G_CALLBACK (gbp_flatpak_manifest_file_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
 static void
 gbp_flatpak_manifest_finalize (GObject *object)
 {
   GbpFlatpakManifest *self = (GbpFlatpakManifest *)object;
 
   g_clear_object (&self->file);
+  g_clear_object (&self->file_monitor);
 
   g_clear_pointer (&self->root, json_node_unref);
 
@@ -517,7 +593,7 @@ gbp_flatpak_manifest_set_property (GObject      *object,
   switch (prop_id)
     {
     case PROP_FILE:
-      self->file = g_value_dup_object (value);
+      gbp_flatpak_manifest_set_file (self, g_value_get_object (value));
       break;
 
     default:
@@ -546,6 +622,14 @@ gbp_flatpak_manifest_class_init (GbpFlatpakManifestClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
+  signals [NEEDS_RELOAD] =
+    g_signal_new ("needs-reload",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
   /* This regex is based on https://wiki.gnome.org/HowDoI/ChooseApplicationID */
   app_id_regex = g_regex_new ("^[[:alnum:]-_]+\\.[[:alnum:]-_]+(\\.[[:alnum:]-_]+)*$",
                               G_REGEX_OPTIMIZE, 0, NULL);
@@ -784,6 +868,8 @@ gbp_flatpak_manifest_save_cb (GObject      *object,
   ide_configuration_set_dirty (IDE_CONFIGURATION (self), FALSE);
   g_task_return_boolean (task, TRUE);
 
+  gbp_flatpak_manifest_unblock_monitor (self);
+
   IDE_EXIT;
 }
 
@@ -847,6 +933,8 @@ gbp_flatpak_manifest_save_async (GbpFlatpakManifest  *self,
   data[len] = '\n';
   bytes = g_bytes_new_take (g_steal_pointer (&data), len + 1);
 
+  gbp_flatpak_manifest_block_monitor (self);
+
   /*
    * Now that we have a buffer containing the UTF-8 encoded formatted
    * JSON, we can asynchronously write that content to disk without hvaing


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