[gnome-builder/wip/mwleeds/ide-config-provider: 14/15] buildsystem: Make IdeBuildconfigConfigurationProvider writeback work



commit a30f253891e4fa56b363b7e15b781a44bf7ab046
Author: Matthew Leeds <mleeds redhat com>
Date:   Thu Jan 12 17:02:29 2017 -0600

    buildsystem: Make IdeBuildconfigConfigurationProvider writeback work

 .../ide-buildconfig-configuration-provider.c       |  245 ++++++++++++++++++++
 .../ide-buildconfig-configuration-provider.h       |    8 +
 2 files changed, 253 insertions(+), 0 deletions(-)
---
diff --git a/libide/buildsystem/ide-buildconfig-configuration-provider.c 
b/libide/buildsystem/ide-buildconfig-configuration-provider.c
index 47a4636..12944b7 100644
--- a/libide/buildsystem/ide-buildconfig-configuration-provider.c
+++ b/libide/buildsystem/ide-buildconfig-configuration-provider.c
@@ -35,14 +35,19 @@
 #include "vcs/ide-vcs.h"
 
 #define DOT_BUILD_CONFIG ".buildconfig"
+#define WRITEBACK_TIMEOUT_SECS 2
 
 struct _IdeBuildconfigConfigurationProvider
 {
   GObject                  parent_instance;
+
   IdeConfigurationManager *manager;
   GCancellable            *cancellable;
   GPtrArray               *configurations;
   GKeyFile                *key_file;
+
+  gulong                   writeback_handler;
+  guint                    change_count;
 };
 
 static void configuration_provider_iface_init (IdeConfigurationProviderInterface *);
@@ -55,6 +60,238 @@ static void ide_buildconfig_configuration_provider_load (IdeConfigurationProvide
 static void ide_buildconfig_configuration_provider_unload (IdeConfigurationProvider *provider, 
IdeConfigurationManager *manager);
 
 static void
+ide_buildconfig_configuration_provider_save_cb (GObject      *object,
+                                                GAsyncResult *result,
+                                                gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+  GFile *file = (GFile *)object;
+
+  g_assert (G_IS_FILE (file));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!g_file_replace_contents_finish (file, result, NULL, &error))
+    g_task_return_error (task, error);
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+void
+ide_buildconfig_configuration_provider_save_async (IdeBuildconfigConfigurationProvider *self,
+                                                   GCancellable                        *cancellable,
+                                                   GAsyncReadyCallback                  callback,
+                                                   gpointer                             user_data)
+{
+  g_autoptr(GHashTable) group_names = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_auto(GStrv) groups = NULL;
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(GBytes) bytes = NULL;
+  gchar *data;
+  gsize length;
+  IdeContext *context;
+  IdeVcs *vcs;
+  GFile *workdir;
+  GError *error = NULL;
+  guint i;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  if (self->change_count == 0)
+    {
+      g_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  self->change_count = 0;
+
+  context = ide_object_get_context (IDE_OBJECT (self->manager));
+  vcs = ide_context_get_vcs (context);
+  workdir = ide_vcs_get_working_directory (vcs);
+  file = g_file_get_child (workdir, DOT_BUILD_CONFIG);
+
+  /*
+   * NOTE:
+   *
+   * We keep the GKeyFile around from when we parsed .buildconfig, so that
+   * we can try to preserve comments and such when writing back.
+   *
+   * This means that we need to fill in all our known configuration
+   * sections, and then remove any that were removed since we were
+   * parsed it last.
+   */
+
+  if (self->key_file == NULL)
+    self->key_file = g_key_file_new ();
+
+  group_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  for (i = 0; i < self->configurations->len; i++)
+    {
+      IdeConfiguration *configuration = g_ptr_array_index (self->configurations, i);
+      IdeEnvironment *environment;
+      guint n_items;
+      guint j;
+      gchar *group;
+      gchar *group_environ;
+
+      group = g_strdup (ide_configuration_get_id (configuration));
+      group_environ = g_strdup_printf ("%s.environment", group);
+
+      /*
+       * Track our known group names, so we can remove missing names after
+       * we've updated the GKeyFile.
+       */
+      g_hash_table_insert (group_names, group, NULL);
+      g_hash_table_insert (group_names, group_environ, NULL);
+
+#define PERSIST_STRING_KEY(key, getter) \
+      g_key_file_set_string (self->key_file, group, key, \
+                             ide_configuration_##getter (configuration) ?: "")
+      PERSIST_STRING_KEY ("name", get_display_name);
+      PERSIST_STRING_KEY ("device", get_device_id);
+      PERSIST_STRING_KEY ("runtime", get_runtime_id);
+      PERSIST_STRING_KEY ("config-opts", get_config_opts);
+      PERSIST_STRING_KEY ("prefix", get_prefix);
+      PERSIST_STRING_KEY ("app-id", get_app_id);
+#undef PERSIST_STRING_KEY
+
+      if (configuration == ide_configuration_manager_get_current (self->manager))
+        g_key_file_set_boolean (self->key_file, group, "default", TRUE);
+      else
+        g_key_file_remove_key (self->key_file, group, "default", NULL);
+
+      environment = ide_configuration_get_environment (configuration);
+
+      /*
+       * Remove all environment keys that are no longer specified in the
+       * environment. This allows us to just do a single pass of additions
+       * from the environment below.
+       */
+      if (g_key_file_has_group (self->key_file, group_environ))
+        {
+          g_auto(GStrv) keys = NULL;
+
+          if (NULL != (keys = g_key_file_get_keys (self->key_file, group_environ, NULL, NULL)))
+            {
+              for (j = 0; keys [j]; j++)
+                {
+                  if (!ide_environment_getenv (environment, keys [j]))
+                    g_key_file_remove_key (self->key_file, group_environ, keys [j], NULL);
+                }
+            }
+        }
+
+      n_items = g_list_model_get_n_items (G_LIST_MODEL (environment));
+
+      for (j = 0; j < n_items; j++)
+        {
+          g_autoptr(IdeEnvironmentVariable) var = NULL;
+          const gchar *key;
+          const gchar *value;
+
+          var = g_list_model_get_item (G_LIST_MODEL (environment), j);
+          key = ide_environment_variable_get_key (var);
+          value = ide_environment_variable_get_value (var);
+
+          if (!ide_str_empty0 (key))
+            g_key_file_set_string (self->key_file, group_environ, key, value ?: "");
+        }
+    }
+
+  /*
+   * Now truncate any old groups in the keyfile.
+   */
+  if (NULL != (groups = g_key_file_get_groups (self->key_file, NULL)))
+    {
+      for (i = 0; groups [i]; i++)
+        {
+          if (!g_hash_table_contains (group_names, groups [i]))
+            g_key_file_remove_group (self->key_file, groups [i], NULL);
+        }
+    }
+
+  if (NULL == (data = g_key_file_to_data (self->key_file, &length, &error)))
+    {
+      g_task_return_error (task, error);
+      IDE_EXIT;
+    }
+
+  bytes = g_bytes_new_take (data, length);
+
+  g_file_replace_contents_bytes_async (file,
+                                       bytes,
+                                       NULL,
+                                       FALSE,
+                                       G_FILE_CREATE_NONE,
+                                       cancellable,
+                                       ide_buildconfig_configuration_provider_save_cb,
+                                       g_object_ref (task));
+
+  IDE_EXIT;
+}
+
+gboolean
+ide_buildconfig_configuration_provider_save_finish (IdeBuildconfigConfigurationProvider  *self,
+                                                    GAsyncResult                         *result,
+                                                    GError                              **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ide_buildconfig_configuration_provider_do_writeback (gpointer data)
+{
+  IdeBuildconfigConfigurationProvider *self = data;
+
+  g_assert (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self));
+
+  self->writeback_handler = 0;
+
+  ide_buildconfig_configuration_provider_save_async (self, NULL, NULL, NULL);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+ide_buildconfig_configuration_provider_queue_writeback (IdeBuildconfigConfigurationProvider *self)
+{
+  g_assert (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self));
+
+  IDE_ENTRY;
+
+  if (self->writeback_handler != 0)
+    g_source_remove (self->writeback_handler);
+
+  self->writeback_handler = g_timeout_add_seconds (WRITEBACK_TIMEOUT_SECS,
+                                                   ide_buildconfig_configuration_provider_do_writeback,
+                                                   self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_buildconfig_configuration_provider_changed (IdeBuildconfigConfigurationProvider *self,
+                                                IdeConfiguration *configuration)
+{
+  g_assert (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  self->change_count++;
+
+  ide_buildconfig_configuration_provider_queue_writeback (self);
+}
+
+static void
 load_string (IdeConfiguration *configuration,
              GKeyFile         *key_file,
              const gchar      *group,
@@ -197,6 +434,12 @@ ide_buildconfig_configuration_provider_load_group (IdeBuildconfigConfigurationPr
   if (g_key_file_get_boolean (key_file, group, "default", NULL))
     ide_configuration_manager_set_current (self->manager, configuration);
 
+  g_signal_connect_object (configuration,
+                           "changed",
+                           G_CALLBACK (ide_buildconfig_configuration_provider_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
   return TRUE;
 }
 
@@ -337,6 +580,8 @@ ide_buildconfig_configuration_provider_unload (IdeConfigurationProvider *provide
   g_assert (IDE_IS_BUILDCONFIG_CONFIGURATION_PROVIDER (self));
   g_assert (IDE_IS_CONFIGURATION_MANAGER (manager));
 
+  ide_clear_source (&self->writeback_handler);
+
   if (self->configurations != NULL)
     {
       for (guint i= 0; i < self->configurations->len; i++)
diff --git a/libide/buildsystem/ide-buildconfig-configuration-provider.h 
b/libide/buildsystem/ide-buildconfig-configuration-provider.h
index 2ec8dff..6097399 100644
--- a/libide/buildsystem/ide-buildconfig-configuration-provider.h
+++ b/libide/buildsystem/ide-buildconfig-configuration-provider.h
@@ -29,6 +29,14 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeBuildconfigConfigurationProvider, ide_buildconfig_configuration_provider, IDE, 
BUILDCONFIG_CONFIGURATION_PROVIDER, GObject)
 
+void ide_buildconfig_configuration_provider_save_async      (IdeBuildconfigConfigurationProvider *self,
+                                                             GCancellable                        
*cancellable,
+                                                             GAsyncReadyCallback                  callback,
+                                                             gpointer                             user_data);
+gboolean ide_buildconfig_configuration_provider_save_finish (IdeBuildconfigConfigurationProvider  *self,
+                                                             GAsyncResult                         *result,
+                                                             GError                              **error);
+
 G_END_DECLS
 
 #endif /* IDE_BUILDCONFIG_CONFIGURATION_PROVIDER_H */


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