[gnome-builder/wip/mwleeds/ide-config-provider: 1/2] flatpak: writeback manifest changes to disk



commit 69ba2d187ce24fdd38bbb693a26d5eefc65ae6f1
Author: Matthew Leeds <mleeds redhat com>
Date:   Tue Jan 24 17:28:57 2017 -0600

    flatpak: writeback manifest changes to disk

 .../flatpak/gbp-flatpak-configuration-provider.c   |  504 +++++++++++++++++++-
 .../flatpak/gbp-flatpak-configuration-provider.h   |    8 +
 2 files changed, 511 insertions(+), 1 deletions(-)
---
diff --git a/plugins/flatpak/gbp-flatpak-configuration-provider.c 
b/plugins/flatpak/gbp-flatpak-configuration-provider.c
index 9eed320..3b3ae96 100644
--- a/plugins/flatpak/gbp-flatpak-configuration-provider.c
+++ b/plugins/flatpak/gbp-flatpak-configuration-provider.c
@@ -30,12 +30,17 @@
 #include "gbp-flatpak-configuration-provider.h"
 #include "gbp-flatpak-configuration.h"
 
+#define WRITEBACK_TIMEOUT_SECS 2
+
 struct _GbpFlatpakConfigurationProvider
 {
   GObject                  parent_instance;
   IdeConfigurationManager *manager;
   GCancellable            *cancellable;
   GPtrArray               *configurations;
+
+  gulong                   writeback_handler;
+  guint                    change_count;
 };
 
 typedef struct
@@ -58,6 +63,495 @@ G_DEFINE_TYPE_EXTENDED (GbpFlatpakConfigurationProvider, gbp_flatpak_configurati
 static void gbp_flatpak_configuration_provider_load (IdeConfigurationProvider *provider, 
IdeConfigurationManager *manager);
 static void gbp_flatpak_configuration_provider_unload (IdeConfigurationProvider *provider, 
IdeConfigurationManager *manager);
 
+static void
+gbp_flatpak_configuration_provider_save_worker (GTask        *task,
+                                                gpointer      source_object,
+                                                gpointer      task_data,
+                                                GCancellable *cancellable)
+{
+  GbpFlatpakConfigurationProvider *self = source_object;
+  GError *error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  self->change_count = 0;
+
+  if (self->configurations == NULL)
+    IDE_EXIT;
+
+  for (guint i = 0; i < self->configurations->len; i++)
+    {
+      g_autoptr(GFileInputStream) file_stream = NULL;
+      g_autoptr(GDataInputStream) data_stream = NULL;
+      g_autoptr(GRegex) runtime_regex = NULL;
+      g_autoptr(GRegex) build_options_regex = NULL;
+      g_autoptr(GRegex) config_opts_regex = NULL;
+      g_autoptr(GRegex) primary_module_regex = NULL;
+      g_autoptr(GPtrArray) new_lines = NULL;
+      g_autoptr(GBytes) bytes = NULL;
+      g_auto(GStrv) new_config_opts = NULL;
+      g_auto(GStrv) new_runtime_parts = NULL;
+      g_auto(GStrv) new_environ = NULL;
+      g_autofree gchar *primary_module_regex_str = NULL;
+      g_autofree gchar *primary_module_right_curly_brace = NULL;
+      g_autofree gchar *right_curly_brace_line = NULL;
+      g_autofree gchar *primary_module_indent = NULL;
+      g_autofree gchar *build_options_indent = NULL;
+      g_autofree gchar *config_opt_indent = NULL;
+      g_autofree gchar *array_prefix = NULL;
+      g_autofree gchar *new_config_opts_string = NULL;
+      const gchar *primary_module;
+      const gchar *new_runtime_id;
+      const gchar *config_prefix;
+      const gchar *new_prefix;
+      gchar *json_string;
+      gchar *new_runtime_name;
+      GFile *manifest;
+      gboolean in_config_opts_array;
+      gboolean in_primary_module;
+      gboolean in_build_options;
+      gboolean config_opts_replaced;
+      gboolean build_options_replaced;
+      guint opts_per_line;
+      guint nested_curly_braces;
+
+      GbpFlatpakConfiguration *configuration = (GbpFlatpakConfiguration *)g_ptr_array_index 
(self->configurations, i);
+
+      manifest = gbp_flatpak_configuration_get_manifest (configuration);
+      if (manifest == NULL)
+        continue;
+
+      primary_module = gbp_flatpak_configuration_get_primary_module (configuration);
+      if (primary_module == NULL)
+        {
+          g_warning ("Flatpak manifest configuration has no primary module set");
+          continue;
+        }
+
+      file_stream = g_file_read (manifest, NULL, &error);
+      if (file_stream == NULL)
+        {
+          g_task_return_error (task, error);
+          IDE_EXIT;
+        }
+
+      data_stream = g_data_input_stream_new (G_INPUT_STREAM (file_stream));
+
+      runtime_regex = g_regex_new ("^\\s*\"runtime\"\\s*:\\s*\"(?<id>.+)\",$", 0, 0, NULL);
+      build_options_regex = g_regex_new ("^\\s*\"build-options\"\\s*:\\s*{$", 0, 0, NULL);
+      config_opts_regex = g_regex_new ("^(\\s*\"config-opts\"\\s*:\\s*\\[\\s*).+$", 0, 0, NULL);
+      primary_module_regex_str = g_strdup_printf ("^(\\s*)\"name\"\\s*:\\s*\"%s\",$", primary_module);
+      primary_module_regex = g_regex_new (primary_module_regex_str, 0, 0, NULL);
+
+      new_runtime_id = ide_configuration_get_runtime_id (IDE_CONFIGURATION (configuration));
+      if (g_str_has_prefix (new_runtime_id, "flatpak:"))
+        {
+          new_runtime_parts = g_strsplit (new_runtime_id + 8, "/", 3);
+          if (new_runtime_parts[0] != NULL)
+            new_runtime_name = new_runtime_parts[0];
+        }
+
+      new_config_opts_string = g_strdup (ide_configuration_get_config_opts (IDE_CONFIGURATION 
(configuration)));
+      if (!ide_str_empty0 (new_config_opts_string))
+        {
+          new_config_opts = g_strsplit (g_strstrip (new_config_opts_string), " ", 0);
+        }
+
+      new_environ = ide_configuration_get_environ (IDE_CONFIGURATION (configuration));
+
+      config_prefix = ide_configuration_get_prefix (IDE_CONFIGURATION (configuration));
+      new_prefix = (g_strcmp0 (config_prefix, "/app") != 0) ? config_prefix : "";
+
+      new_lines = g_ptr_array_new_with_free_func (g_free);
+      in_config_opts_array = FALSE;
+      in_primary_module = FALSE;
+      in_build_options = FALSE;
+      config_opts_replaced = FALSE;
+      build_options_replaced = FALSE;
+      nested_curly_braces = 0;
+      for (;;)
+        {
+          gchar *line;
+
+          line = g_data_input_stream_read_line_utf8 (data_stream, NULL, NULL, &error);
+          if (error != NULL)
+            {
+              g_task_return_error (task, error);
+              IDE_EXIT;
+            }
+          if (line == NULL)
+            break;
+
+          if (!in_primary_module)
+            {
+              g_autoptr(GMatchInfo) match_info = NULL;
+              g_regex_match (primary_module_regex, line, 0, &match_info);
+              if (g_match_info_matches (match_info))
+                {
+                  gchar *previous_line;
+                  g_auto(GStrv) previous_line_parts = NULL;
+
+                  in_primary_module = TRUE;
+                  primary_module_indent = g_match_info_fetch (match_info, 1);
+
+                  /* Replace '}' with '{' in the last line to get the right indentation */
+                  previous_line = (gchar *)g_ptr_array_index (new_lines, new_lines->len - 1);
+                  previous_line_parts = g_strsplit (previous_line, "{", 0);
+                  primary_module_right_curly_brace = g_strjoinv ("}", previous_line_parts);
+                }
+            }
+
+          /* Replace the runtime with the user-chosen one */
+          if (!ide_str_empty0 (new_runtime_name))
+            {
+              g_autoptr(GMatchInfo) match_info = NULL;
+              g_regex_match (runtime_regex, line, 0, &match_info);
+              if (g_match_info_matches (match_info))
+                {
+                  gchar *old_runtime_ptr;
+                  gchar *new_line;
+                  g_autofree gchar *id = NULL;
+                  id = g_match_info_fetch_named (match_info, "id");
+                  old_runtime_ptr = g_strstr_len (line, -1, id);
+                  *old_runtime_ptr = '\0';
+                  new_line = g_strdup_printf ("%s%s\",", line, new_runtime_name);
+                  g_free (line);
+                  line = new_line;
+                }
+            }
+
+          /* Update the environment variables */
+          //TODO add support for single-line build-options?
+          //TODO account for module-specific build-options?
+          if (!in_build_options && !build_options_replaced)
+            {
+              g_autoptr(GMatchInfo) match_info = NULL;
+              g_regex_match (build_options_regex, line, 0, &match_info);
+              if (g_match_info_matches (match_info))
+                {
+                  in_build_options = TRUE;
+                }
+            }
+          else if (in_build_options)
+            {
+              if (g_strstr_len (line, -1, "{") != NULL)
+                nested_curly_braces++;
+              if (g_strstr_len (line, -1, "}") == NULL)
+                {
+                  if (build_options_indent == NULL)
+                    {
+                      g_autoptr(GRegex) build_options_internal_regex = NULL;
+                      g_autoptr(GMatchInfo) match_info = NULL;
+                      build_options_internal_regex = g_regex_new ("^(\\s*)\".+\"\\s*:.*$", 0, 0, NULL);
+                      g_regex_match (build_options_internal_regex, line, 0, &match_info);
+                      if (g_match_info_matches (match_info))
+                        {
+                          build_options_indent = g_match_info_fetch (match_info, 1);
+                        }
+                    }
+                  g_free (line);
+                  continue;
+                }
+              else
+                {
+                  if (nested_curly_braces > 0)
+                    {
+                      nested_curly_braces--;
+                      g_free (line);
+                      continue;
+                    }
+                  else
+                    {
+                      /* We're at the closing curly brace for build-options */
+                      guint num_env;
+                      num_env = g_strv_length (new_environ);
+                      if (num_env > 0)
+                        {
+                          g_autofree gchar *cflags_line = NULL;
+                          g_autofree gchar *cxxflags_line = NULL;
+                          g_autoptr(GPtrArray) env_lines = NULL;
+                          if (build_options_indent == NULL)
+                            build_options_indent = g_strdup ("        ");
+                          for (guint j = 0; new_environ[j]; j++)
+                            {
+                              g_auto(GStrv) line_parts = NULL;
+                              line_parts = g_strsplit (new_environ[j], "=", 2);
+                              if (g_strcmp0 (line_parts[0], "CFLAGS") == 0)
+                                cflags_line = g_strdup_printf ("%s\"cflags\": \"%s\"",
+                                                               build_options_indent,
+                                                               line_parts[1]);
+                              else if (g_strcmp0 (line_parts[0], "CXXFLAGS") == 0)
+                                cxxflags_line = g_strdup_printf ("%s\"cxxflags\": \"%s\"",
+                                                                 build_options_indent,
+                                                                 line_parts[1]);
+                              else
+                                {
+                                  if (env_lines == NULL)
+                                    {
+                                      env_lines = g_ptr_array_new_with_free_func (g_free);
+                                      g_ptr_array_add (env_lines, g_strdup_printf ("%s\"env\": {", 
build_options_indent));
+                                    }
+                                  g_ptr_array_add (env_lines, g_strdup_printf ("%s    \"%s\": \"%s\"",
+                                                                               build_options_indent,
+                                                                               line_parts[0],
+                                                                               line_parts[1]));
+                                }
+                            }
+                          if (cflags_line != NULL)
+                            {
+                              gchar *line_ending;
+                              line_ending = (!ide_str_empty0 (new_prefix) || cxxflags_line != NULL || 
env_lines != NULL) ? "," : "";
+                              g_ptr_array_add (new_lines, g_strdup_printf ("%s%s", cflags_line, 
line_ending));
+                            }
+                          if (cxxflags_line != NULL)
+                            {
+                              gchar *line_ending;
+                              line_ending = (!ide_str_empty0 (new_prefix) || env_lines != NULL) ? "," : "";
+                              g_ptr_array_add (new_lines, g_strdup_printf ("%s%s", cxxflags_line, 
line_ending));
+                            }
+                          if (!ide_str_empty0 (new_prefix))
+                            {
+                              gchar *line_ending;
+                              line_ending = (env_lines != NULL) ? "," : "";
+                              g_ptr_array_add (new_lines, g_strdup_printf ("%s\"prefix\": \"%s\"%s",
+                                                                           build_options_indent,
+                                                                           new_prefix,
+                                                                           line_ending));
+                            }
+                          if (env_lines != NULL)
+                            {
+                              g_ptr_array_add (env_lines, g_strdup_printf ("%s}", build_options_indent));
+                              for (guint j = 0; j < env_lines->len; j++)
+                                {
+                                  gchar *env_line;
+                                  gchar *line_ending;
+                                  line_ending = (j > 0 && j < env_lines->len - 2) ? "," : "";
+                                  env_line = (gchar *)g_ptr_array_index (env_lines, j);
+                                  g_ptr_array_add (new_lines, g_strdup_printf ("%s%s", env_line, 
line_ending));
+                                }
+                            }
+                        }
+                       in_build_options = FALSE;
+                       build_options_replaced = TRUE;
+                    }
+                }
+            }
+
+          if (in_primary_module)
+            {
+              g_autoptr(GMatchInfo) match_info = NULL;
+
+              /* Check if we're at the end of the module and haven't seen a config-opts property */
+              if (g_str_has_prefix (line, primary_module_right_curly_brace))
+                {
+                  in_primary_module = FALSE;
+                  if (!config_opts_replaced && new_config_opts != NULL)
+                    {
+                      gchar *previous_line;
+
+                      previous_line = (gchar *)g_ptr_array_remove_index (new_lines, new_lines->len - 1);
+                      g_ptr_array_add (new_lines, g_strdup_printf ("%s,", previous_line));
+                      right_curly_brace_line = line;
+                      line = g_strdup_printf ("%s\"config-opts\": []", primary_module_indent);
+                    }
+                }
+
+              /* Update the list of configure options, or omit it entirely */
+              g_regex_match (config_opts_regex, line, 0, &match_info);
+              if (g_match_info_matches (match_info) || in_config_opts_array)
+                {
+                  gchar *right_bracket;
+
+                  right_bracket = g_strstr_len (line, -1, "]");
+                  if (g_match_info_matches (match_info))
+                    {
+                      array_prefix = g_match_info_fetch (match_info, 1);
+                      if (right_bracket != NULL)
+                        {
+                          /*
+                           * Ensure that all options will be on one line,
+                           * even if there are more than before
+                           */
+                          if (new_config_opts == NULL)
+                            opts_per_line = 1;
+                          else
+                            opts_per_line = g_strv_length (new_config_opts);
+                        }
+                      else
+                        {
+                          in_config_opts_array = TRUE;
+                          if (new_config_opts == NULL)
+                            opts_per_line = 1;
+                          else
+                            opts_per_line = (g_strv_length (g_strsplit (line, "\"", 0)) - 3) / 2;
+                          g_free (line);
+                          continue;
+                        }
+                    }
+                  if (right_bracket == NULL)
+                    {
+                      in_config_opts_array = TRUE;
+                      config_opt_indent = g_strsplit (line, "\"", 0)[0];
+                      g_free (line);
+                      continue;
+                    }
+
+                  /* At this point it's either a single line or we're on the last line */
+                  in_config_opts_array = FALSE;
+                  config_opts_replaced = TRUE;
+                  if (new_config_opts == NULL)
+                    {
+                      g_free (line);
+                      line = g_strdup_printf ("%s],", array_prefix);
+                    }
+                  else
+                    {
+                      gchar *array_suffix;
+                      array_suffix = *(right_bracket - 1) == ' ' ? " ]" : "]";
+                      if (config_opt_indent == NULL)
+                        {
+                          g_auto(GStrv) line_parts = NULL;
+                          line_parts = g_strsplit (line, "\"", 0);
+                          config_opt_indent = g_strdup (line_parts[0]);
+                        }
+                      for (guint j = 0; g_strv_length (new_config_opts) > j; j += opts_per_line)
+                        {
+                          g_autoptr(GPtrArray) config_opts_subset = NULL;
+                          g_autofree gchar *opts_this_line = NULL;
+                          gchar *prefix;
+                          gchar *suffix;
+                          gchar *new_line;
+
+                          prefix = (j == 0) ? array_prefix : config_opt_indent;
+                          suffix = (g_strv_length (new_config_opts) <= j + opts_per_line) ? array_suffix : 
"";
+                          config_opts_subset = g_ptr_array_new ();
+                          for (guint k = 0; k < opts_per_line && new_config_opts[j+k]; ++k)
+                            g_ptr_array_add (config_opts_subset, new_config_opts[j+k]);
+                          g_ptr_array_add (config_opts_subset, NULL);
+                          opts_this_line = g_strjoinv ("\", \"", (gchar **)config_opts_subset->pdata);
+
+                          new_line = g_strdup_printf ("%s\"%s\"%s,", prefix, opts_this_line, suffix);
+                          g_ptr_array_add (new_lines, new_line);
+                        }
+                      g_free (line);
+                      continue;
+                    }
+                }
+            }
+
+          g_ptr_array_add (new_lines, line);
+          if (right_curly_brace_line != NULL)
+            {
+              g_ptr_array_add (new_lines, right_curly_brace_line);
+              right_curly_brace_line = NULL;
+            }
+        }
+
+      /* Ensure there's a newline at the end of the file */
+      g_ptr_array_add (new_lines, g_strdup (""));
+      g_ptr_array_add (new_lines, NULL);
+      json_string = g_strjoinv ("\n", (gchar **)new_lines->pdata);
+      bytes = g_bytes_new_take (json_string, strlen (json_string));
+
+      if (!g_file_replace_contents (manifest,
+                                    g_bytes_get_data (bytes, NULL),
+                                    g_bytes_get_size (bytes),
+                                    NULL,
+                                    FALSE,
+                                    G_FILE_CREATE_NONE,
+                                    NULL,
+                                    cancellable,
+                                    &error))
+        {
+          g_task_return_error (task, error);
+          IDE_EXIT;
+        }
+    }
+
+  IDE_EXIT;
+}
+
+void
+gbp_flatpak_configuration_provider_save_async (GbpFlatpakConfigurationProvider *self,
+                                               GCancellable                    *cancellable,
+                                               GAsyncReadyCallback              callback,
+                                               gpointer                         user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_FLATPAK_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);
+  else
+    g_task_run_in_thread (task, gbp_flatpak_configuration_provider_save_worker);
+
+  IDE_EXIT;
+}
+
+gboolean
+gbp_flatpak_configuration_provider_save_finish (GbpFlatpakConfigurationProvider  *self,
+                                                GAsyncResult                         *result,
+                                                GError                              **error)
+{
+  g_return_val_if_fail (GBP_IS_FLATPAK_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
+gbp_flatpak_configuration_provider_do_writeback (gpointer data)
+{
+  GbpFlatpakConfigurationProvider *self = data;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+
+  self->writeback_handler = 0;
+
+  gbp_flatpak_configuration_provider_save_async (self, NULL, NULL, NULL);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+gbp_flatpak_configuration_provider_queue_writeback (GbpFlatpakConfigurationProvider *self)
+{
+  g_assert (GBP_IS_FLATPAK_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,
+                                                   gbp_flatpak_configuration_provider_do_writeback,
+                                                   self);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_flatpak_configuration_provider_changed (GbpFlatpakConfigurationProvider *self,
+                                            IdeConfiguration *configuration)
+{
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  self->change_count++;
+
+  gbp_flatpak_configuration_provider_queue_writeback (self);
+}
+
 static gboolean
 contains_id (GPtrArray   *ar,
              const gchar *id)
@@ -450,6 +944,12 @@ gbp_flatpak_configuration_provider_load_manifests (GbpFlatpakConfigurationProvid
       if (manifest->config_opts != NULL)
         ide_configuration_set_config_opts (IDE_CONFIGURATION (configuration), manifest->config_opts);
 
+      g_signal_connect_object (configuration,
+                               "changed",
+                               G_CALLBACK (gbp_flatpak_configuration_provider_changed),
+                               self,
+                               G_CONNECT_SWAPPED);
+
       g_ptr_array_add (configurations, configuration);
     }
 
@@ -555,9 +1055,11 @@ gbp_flatpak_configuration_provider_unload (IdeConfigurationProvider *provider,
   g_assert (GBP_IS_FLATPAK_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++)
+      for (guint i = 0; i < self->configurations->len; i++)
         {
           IdeConfiguration *configuration = g_ptr_array_index (self->configurations, i);
 
diff --git a/plugins/flatpak/gbp-flatpak-configuration-provider.h 
b/plugins/flatpak/gbp-flatpak-configuration-provider.h
index 7a262b7..dfc7905 100644
--- a/plugins/flatpak/gbp-flatpak-configuration-provider.h
+++ b/plugins/flatpak/gbp-flatpak-configuration-provider.h
@@ -27,6 +27,14 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpFlatpakConfigurationProvider, gbp_flatpak_configuration_provider, GBP, 
FLATPAK_CONFIGURATION_PROVIDER, GObject)
 
+void gbp_flatpak_configuration_provider_save_async      (GbpFlatpakConfigurationProvider *self,
+                                                         GCancellable                    *cancellable,
+                                                         GAsyncReadyCallback              callback,
+                                                         gpointer                         user_data);
+gboolean gbp_flatpak_configuration_provider_save_finish (GbpFlatpakConfigurationProvider  *self,
+                                                         GAsyncResult                     *result,
+                                                         GError                          **error);
+
 G_END_DECLS
 
 #endif /* GBP_FLATPAK_CONFIGURATION_PROVIDER_H */


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