[gnome-builder] flatpak: move manifest parsing code to GbpFlatpakConfiguration



commit fcd42473f3dcf34c7a8d44065322578c86b002fa
Author: Matthew Leeds <mleeds redhat com>
Date:   Sat Feb 25 17:34:58 2017 -0800

    flatpak: move manifest parsing code to GbpFlatpakConfiguration
    
    It makes more sense for the code that parses flatpak manifests to be in
    the Config rather than the ConfigProvider class, especially since it
    will make it easier to reload them when the manifest is modified.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=779330

 .../flatpak/gbp-flatpak-configuration-provider.c   |  429 +++-----------------
 plugins/flatpak/gbp-flatpak-configuration.c        |  319 +++++++++++++++
 plugins/flatpak/gbp-flatpak-configuration.h        |   46 ++-
 3 files changed, 396 insertions(+), 398 deletions(-)
---
diff --git a/plugins/flatpak/gbp-flatpak-configuration-provider.c 
b/plugins/flatpak/gbp-flatpak-configuration-provider.c
index 6283d93..e52c488 100644
--- a/plugins/flatpak/gbp-flatpak-configuration-provider.c
+++ b/plugins/flatpak/gbp-flatpak-configuration-provider.c
@@ -43,22 +43,6 @@ struct _GbpFlatpakConfigurationProvider
   guint                    change_count;
 };
 
-typedef struct
-{
-  gchar          *app_id;
-  gchar          *branch;
-  gchar          *command;
-  gchar          *config_opts;
-  gchar         **finish_args;
-  gchar          *platform;
-  gchar          *prefix;
-  gchar          *primary_module;
-  gchar          *runtime_id;
-  gchar          *sdk;
-  GFile          *file;
-  IdeEnvironment *environment;
-} FlatpakManifest;
-
 static void configuration_provider_iface_init (IdeConfigurationProviderInterface *);
 
 G_DEFINE_TYPE_EXTENDED (GbpFlatpakConfigurationProvider, gbp_flatpak_configuration_provider, G_TYPE_OBJECT, 
0,
@@ -624,95 +608,23 @@ contains_id (GPtrArray   *ar,
   return FALSE;
 }
 
-static void
-flatpak_manifest_free (void *data)
-{
-  FlatpakManifest *manifest = data;
-
-  g_free (manifest->app_id);
-  g_free (manifest->branch);
-  g_free (manifest->command);
-  g_free (manifest->config_opts);
-  g_free (manifest->platform);
-  g_free (manifest->prefix);
-  g_free (manifest->primary_module);
-  g_free (manifest->runtime_id);
-  g_free (manifest->sdk);
-  g_strfreev (manifest->finish_args);
-  g_clear_object (&manifest->environment);
-  g_clear_object (&manifest->file);
-  g_slice_free (FlatpakManifest, manifest);
-}
-
-JsonNode *
-guess_primary_module (JsonNode *modules_node,
-                      GFile    *directory)
-{
-  JsonArray *modules;
-  JsonNode *module;
-  JsonNode *parent;
-  g_autofree gchar *dir_name;
-
-  g_assert (G_IS_FILE (directory));
-
-  dir_name = g_file_get_basename (directory);
-  g_assert (!ide_str_empty0 (dir_name));
-  g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (modules_node), NULL);
-
-  /* TODO: Support module strings that refer to other files? */
-  modules = json_node_get_array (modules_node);
-  if (json_array_get_length (modules) == 1)
-    {
-      module = json_array_get_element (modules, 0);
-      if (JSON_NODE_HOLDS_OBJECT (module))
-        return module;
-    }
-  else
-    {
-      for (guint i = 0; i < json_array_get_length (modules); i++)
-        {
-          module = json_array_get_element (modules, i);
-          if (JSON_NODE_HOLDS_OBJECT (module))
-            {
-              const gchar *module_name;
-              module_name = json_object_get_string_member (json_node_get_object (module), "name");
-              if (g_strcmp0 (module_name, dir_name) == 0)
-                return module;
-              if (json_object_has_member (json_node_get_object (module), "modules"))
-                {
-                  JsonNode *nested_modules_node;
-                  JsonNode *nested_primary_module;
-                  nested_modules_node = json_object_get_member (json_node_get_object (module), "modules");
-                  nested_primary_module = guess_primary_module (nested_modules_node, directory);
-                  if (nested_primary_module != NULL)
-                    return nested_primary_module;
-                }
-            }
-        }
-        /* If none match, assume the last module in the list is the primary one */
-        parent = json_node_get_parent (modules_node);
-        if (JSON_NODE_HOLDS_OBJECT (parent) &&
-            json_node_get_parent (parent) == NULL &&
-            json_array_get_length (modules) > 0)
-          {
-            JsonNode *last_node;
-            last_node = json_array_get_element (modules, json_array_get_length (modules) - 1);
-            if (JSON_NODE_HOLDS_OBJECT (last_node))
-              return last_node;
-          }
-    }
-
-  return NULL;
-}
-
 static gboolean
-check_dir_for_manifests (GFile         *directory,
-                         GPtrArray     *manifests,
-                         GCancellable  *cancellable,
-                         GError       **error)
+gbp_flatpak_configuration_provider_find_manifests (GbpFlatpakConfigurationProvider  *self,
+                                                   GFile                            *directory,
+                                                   GPtrArray                        *configs,
+                                                   GCancellable                     *cancellable,
+                                                   GError                          **error)
 {
   g_autoptr(GFileEnumerator) enumerator = NULL;
   GFileInfo *file_info = NULL;
+  IdeContext *context;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
+  g_assert (G_IS_FILE (directory));
+  g_assert (configs != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (self->manager));
 
   enumerator = g_file_enumerate_children (directory,
                                           G_FILE_ATTRIBUTE_STANDARD_NAME,
@@ -725,43 +637,32 @@ check_dir_for_manifests (GFile         *directory,
   while ((file_info = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
     {
       GFileType file_type;
-      g_autofree gchar *name = NULL;
+      g_autofree gchar *filename = NULL;
       g_autofree gchar *path = NULL;
-      const gchar *platform;
-      const gchar *branch;
-      const gchar *arch;
+      g_autofree gchar *hash = NULL;
+      g_autofree gchar *id = NULL;
+      g_autofree gchar *manifest_data = NULL;
+      gsize manifest_data_len = 0;
       g_autoptr(GRegex) filename_regex = NULL;
       g_autoptr(GMatchInfo) match_info = NULL;
-      g_autoptr(JsonParser) parser = NULL;
-      JsonNode *root_node = NULL;
-      JsonNode *app_id_node = NULL;
-      JsonNode *id_node = NULL;
-      JsonNode *runtime_node = NULL;
-      JsonNode *runtime_version_node = NULL;
-      JsonNode *sdk_node = NULL;
-      JsonNode *modules_node = NULL;
-      JsonNode *primary_module_node = NULL;
-      JsonNode *command_node = NULL;
-      JsonNode *finish_args_node = NULL;
-      JsonObject *root_object = NULL;
-      g_autoptr(GError) local_error = NULL;
       g_autoptr(GFile) file = NULL;
-      FlatpakManifest *manifest;
+      g_autoptr(GbpFlatpakConfiguration) possible_config = NULL;
 
       file_type = g_file_info_get_file_type (file_info);
-      name = g_strdup (g_file_info_get_name (file_info));
+      filename = g_strdup (g_file_info_get_name (file_info));
       g_clear_object (&file_info);
 
-      if (name == NULL)
+      if (filename == NULL)
         continue;
 
-      file = g_file_get_child (directory, name);
+      file = g_file_get_child (directory, filename);
 
+      /* Recurse unless it's a directory that should be ignored */
       if (file_type == G_FILE_TYPE_DIRECTORY)
         {
-          if (g_strcmp0 (name, ".git") == 0 || g_strcmp0 (name, ".flatpak-builder") == 0)
+          if (g_strcmp0 (filename, ".git") == 0 || g_strcmp0 (filename, ".flatpak-builder") == 0)
             continue;
-          else if (!check_dir_for_manifests (file, manifests, cancellable, error))
+          else if (!gbp_flatpak_configuration_provider_find_manifests (self, file, configs, cancellable, 
error))
             return FALSE;
           continue;
         }
@@ -770,199 +671,44 @@ check_dir_for_manifests (GFile         *directory,
       filename_regex = g_regex_new ("^[[:alnum:]-_]+\\.[[:alnum:]-_]+(\\.[[:alnum:]-_]+)*\\.json$",
                                     0, 0, NULL);
 
-      g_regex_match (filename_regex, name, 0, &match_info);
+      /* Check if the filename resembles APP_ID.json */
+      g_regex_match (filename_regex, filename, 0, &match_info);
       if (!g_match_info_matches (match_info))
         continue;
 
-      /* Check if the contents look like a flatpak manifest */
+      /* Check if the file has already been loaded as a config */
       path = g_file_get_path (file);
-      parser = json_parser_new ();
-      json_parser_load_from_file (parser, path, &local_error);
-      if (local_error != NULL)
-        {
-          g_warning ("Error parsing potential flatpak manifest %s: %s", path, local_error->message);
-          continue;
-        }
-
-      root_node = json_parser_get_root (parser);
-      if (!JSON_NODE_HOLDS_OBJECT (root_node))
-        continue;
-
-      root_object = json_node_get_object (root_node);
-      app_id_node = json_object_get_member (root_object, "app-id");
-      id_node = json_object_get_member (root_object, "id");
-      runtime_node = json_object_get_member (root_object, "runtime");
-      runtime_version_node = json_object_get_member (root_object, "runtime-version");
-      sdk_node = json_object_get_member (root_object, "sdk");
-      modules_node = json_object_get_member (root_object, "modules");
-
-      if ((!JSON_NODE_HOLDS_VALUE (app_id_node) && !JSON_NODE_HOLDS_VALUE (id_node)) ||
-           !JSON_NODE_HOLDS_VALUE (runtime_node) ||
-           !JSON_NODE_HOLDS_VALUE (sdk_node) ||
-           !JSON_NODE_HOLDS_ARRAY (modules_node))
-        continue;
-
-      IDE_TRACE_MSG ("Discovered flatpak manifest at %s", path);
-
-      manifest = g_slice_new0 (FlatpakManifest);
-
-      manifest->file = g_steal_pointer (&file);
-
-      /**
-       * TODO: Currently we just support the build-options object that's global to the
-       * manifest, but modules can have their own build-options as well that override
-       * global ones, so we should consider supporting that. The main difficulty would
-       * be keeping track of each so they can be written back to the file properly when
-       * the user makes changes in the Builder interface.
-       */
-      if (json_object_has_member (root_object, "build-options") &&
-          JSON_NODE_HOLDS_OBJECT (json_object_get_member (root_object, "build-options")))
+      if (g_file_get_contents (path, &manifest_data, &manifest_data_len, NULL))
         {
-          JsonObject *build_options = NULL;
-          IdeEnvironment *environment;
-
-          build_options = json_object_get_object_member (root_object, "build-options");
-
-          if (json_object_has_member (build_options, "prefix"))
-            {
-              const gchar *prefix;
-              prefix = json_object_get_string_member (build_options, "prefix");
-              if (prefix != NULL)
-                manifest->prefix = g_strdup (prefix);
-            }
+          g_autoptr(GChecksum) checksum = NULL;
 
-          environment = ide_environment_new ();
-          if (json_object_has_member (build_options, "cflags"))
-            {
-              const gchar *cflags;
-              cflags = json_object_get_string_member (build_options, "cflags");
-              if (cflags != NULL)
-                ide_environment_setenv (environment, "CFLAGS", cflags);
-            }
-          if (json_object_has_member (build_options, "cxxflags"))
-            {
-              const gchar *cxxflags;
-              cxxflags = json_object_get_string_member (build_options, "cxxflags");
-              if (cxxflags != NULL)
-                ide_environment_setenv (environment, "CXXFLAGS", cxxflags);
-            }
-          if (json_object_has_member (build_options, "env"))
-            {
-              JsonObject *env_vars;
-              env_vars = json_object_get_object_member (build_options, "env");
-              if (env_vars != NULL)
-                {
-                  g_autoptr(GList) env_list = NULL;
-                  GList *l;
-                  env_list = json_object_get_members (env_vars);
-                  for (l = env_list; l != NULL; l = l->next)
-                    {
-                      const gchar *env_name = (gchar *)l->data;
-                      const gchar *env_value = json_object_get_string_member (env_vars, env_name);
-                      if (!ide_str_empty0 (env_name) && !ide_str_empty0 (env_value))
-                        ide_environment_setenv (environment, env_name, env_value);
-                    }
-                }
-            }
-          manifest->environment = environment;
+          checksum = g_checksum_new (G_CHECKSUM_SHA1);
+          g_checksum_update (checksum, (const guint8 *)manifest_data, manifest_data_len);
+          hash = g_strdup (g_checksum_get_string (checksum));
         }
 
-      platform = json_node_get_string (runtime_node);
-      manifest->platform = g_strdup (platform);
-
-      if (!JSON_NODE_HOLDS_VALUE (runtime_version_node) || ide_str_empty0 (json_node_get_string 
(runtime_version_node)))
-        branch = "master";
-      else
-        branch = json_node_get_string (runtime_version_node);
-      manifest->branch = g_strdup (branch);
-
-      arch = flatpak_get_default_arch ();
-      manifest->runtime_id = g_strdup_printf ("flatpak:%s/%s/%s", platform, arch, branch);
-
-      manifest->sdk = json_node_dup_string (sdk_node);
+      id = (hash != NULL) ? g_strdup_printf ("%s@%s", filename, hash) : g_strdup (filename);
 
-      command_node = json_object_get_member (root_object, "command");
-      if (JSON_NODE_HOLDS_VALUE (command_node))
-        manifest->command = json_node_dup_string (command_node);
-
-      finish_args_node = json_object_get_member (root_object, "finish-args");
-      if (JSON_NODE_HOLDS_ARRAY (finish_args_node))
-        {
-          JsonArray *finish_args_array;
-          GPtrArray *finish_args;
-          finish_args = g_ptr_array_new ();
-          finish_args_array = json_node_get_array (finish_args_node);
-          for (guint i = 0; i < json_array_get_length (finish_args_array); i++)
-            {
-              const gchar *arg = json_array_get_string_element (finish_args_array, i);
-              if (!ide_str_empty0 (arg))
-                g_ptr_array_add (finish_args, g_strdup (arg));
-            }
-          g_ptr_array_add (finish_args, NULL);
-          manifest->finish_args = (gchar **)g_ptr_array_free (finish_args, FALSE);
-        }
+      if (contains_id (configs, id))
+        continue;
 
-      if (JSON_NODE_HOLDS_VALUE (app_id_node))
-        manifest->app_id = json_node_dup_string (app_id_node);
-      else
-        manifest->app_id = json_node_dup_string (id_node);
+      /* Finally, try to parse the file as a manifest */
+      possible_config = gbp_flatpak_configuration_new (context, id, filename);
+      if (!gbp_flatpak_configuration_load_from_file (possible_config, file))
+        continue;
 
-      primary_module_node = guess_primary_module (modules_node, directory);
-      if (primary_module_node != NULL && JSON_NODE_HOLDS_OBJECT (primary_module_node))
-        {
-          JsonObject *primary_module_object = json_node_get_object (primary_module_node);
-          manifest->primary_module = g_strdup (json_object_get_string_member (primary_module_object, 
"name"));
-          if (json_object_has_member (primary_module_object, "config-opts"))
-            {
-              JsonArray *config_opts_array;
-              config_opts_array = json_object_get_array_member (primary_module_object, "config-opts");
-              if (config_opts_array != NULL)
-                {
-                  GPtrArray *config_opts_strv;
-                  config_opts_strv = g_ptr_array_new_with_free_func (g_free);
-                  for (guint i = 0; i < json_array_get_length (config_opts_array); i++)
-                    {
-                      const gchar *next_option;
-                      next_option = json_array_get_string_element (config_opts_array, i);
-                      g_ptr_array_add (config_opts_strv, g_strdup (next_option));
-                    }
-                  g_ptr_array_add (config_opts_strv, NULL);
-                  manifest->config_opts = g_strjoinv (" ", (gchar **)config_opts_strv->pdata);
-                  g_ptr_array_free (config_opts_strv, TRUE);
-                }
-            }
-        }
+      g_signal_connect_object (possible_config,
+                               "changed",
+                               G_CALLBACK (gbp_flatpak_configuration_provider_changed),
+                               self,
+                               G_CONNECT_SWAPPED);
 
-      g_ptr_array_add (manifests, manifest);
+      g_ptr_array_add (configs, g_steal_pointer (&possible_config));
     }
 
   return TRUE;
 }
 
-static GPtrArray *
-gbp_flatpak_configuration_provider_find_flatpak_manifests (GbpFlatpakConfigurationProvider *self,
-                                                           GCancellable                    *cancellable,
-                                                           GFile                           *directory,
-                                                           GError                         **error)
-{
-  GPtrArray *ar;
-
-  g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-  g_assert (G_IS_FILE (directory));
-
-  ar = g_ptr_array_new ();
-  g_ptr_array_set_free_func (ar, flatpak_manifest_free);
-
-  if (!check_dir_for_manifests (directory, ar, cancellable, error))
-    {
-      g_ptr_array_free (ar, TRUE);
-      return NULL;
-    }
-
-  return ar;
-}
-
 static gboolean
 gbp_flatpak_configuration_provider_load_manifests (GbpFlatpakConfigurationProvider  *self,
                                                    GPtrArray                        *configurations,
@@ -996,85 +742,14 @@ gbp_flatpak_configuration_provider_load_manifests (GbpFlatpakConfigurationProvid
   else
     project_dir = g_file_get_parent (project_file);
 
-  ar = gbp_flatpak_configuration_provider_find_flatpak_manifests (self, cancellable, project_dir, error);
-
-  if (ar == NULL)
+  if (!gbp_flatpak_configuration_provider_find_manifests (self,
+                                                          project_dir,
+                                                          configurations,
+                                                          cancellable,
+                                                          error))
     return FALSE;
 
-  IDE_TRACE_MSG ("Found %u flatpak manifests", ar->len);
-
-  for (guint i = 0; i < ar->len; i++)
-    {
-      GbpFlatpakConfiguration *configuration;
-      FlatpakManifest *manifest = g_ptr_array_index (ar, i);
-      g_autofree gchar *filename = NULL;
-      g_autofree gchar *hash = NULL;
-      g_autofree gchar *id = NULL;
-      g_autofree gchar *manifest_data = NULL;
-      g_autofree gchar *path = NULL;
-      gsize manifest_data_len = 0;
-
-      path = g_file_get_path (manifest->file);
-
-      if (g_file_get_contents (path, &manifest_data, &manifest_data_len, NULL))
-        {
-          g_autoptr(GChecksum) checksum = NULL;
-
-          checksum = g_checksum_new (G_CHECKSUM_SHA1);
-          g_checksum_update (checksum, (const guint8 *)manifest_data, manifest_data_len);
-          hash = g_strdup (g_checksum_get_string (checksum));
-        }
-
-      filename = g_file_get_basename (manifest->file);
-
-      if (hash != NULL)
-        id = g_strdup_printf ("%s@%s", filename, hash);
-      else
-        id = g_strdup (filename);
-
-      if (contains_id (configurations, id))
-        continue;
-
-      /**
-       * TODO: There are a few more fields in the manifests that Builder needs, but they
-       * are read when needed by the runtime. If we set up a file monitor to reload the
-       * configuration when it changes on disk, it might make more sense for those fields
-       * to be read and processed here so we're only parsing the manifest in one place.
-       *
-       * CJH: We probably want gbp_flatpak_configuration_load_from_file() or similar.
-       */
-      configuration = g_object_new (GBP_TYPE_FLATPAK_CONFIGURATION,
-                                    "app-id", manifest->app_id,
-                                    "branch", manifest->branch,
-                                    "context", context,
-                                    "display-name", filename,
-                                    "device-id", "local",
-                                    "id", id,
-                                    "manifest", manifest->file,
-                                    "platform", manifest->platform,
-                                    "prefix", (manifest->prefix != NULL ? manifest->prefix : "/app"),
-                                    "runtime-id", manifest->runtime_id,
-                                    "sdk", manifest->sdk,
-                                    NULL);
-      if (manifest->primary_module != NULL)
-        gbp_flatpak_configuration_set_primary_module (configuration, manifest->primary_module);
-      if (manifest->command != NULL)
-        gbp_flatpak_configuration_set_command (configuration, manifest->command);
-      if (manifest->finish_args != NULL)
-        gbp_flatpak_configuration_set_finish_args (configuration, (const gchar * const 
*)manifest->finish_args);
-      if (manifest->environment != NULL)
-        ide_configuration_set_environment (IDE_CONFIGURATION (configuration), manifest->environment);
-      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);
-    }
+  IDE_TRACE_MSG ("Found %u flatpak manifests", configurations->len);
 
   return TRUE;
 }
diff --git a/plugins/flatpak/gbp-flatpak-configuration.c b/plugins/flatpak/gbp-flatpak-configuration.c
index d3dea00..a240f08 100644
--- a/plugins/flatpak/gbp-flatpak-configuration.c
+++ b/plugins/flatpak/gbp-flatpak-configuration.c
@@ -50,6 +50,315 @@ enum {
 
 static GParamSpec *properties [N_PROPS];
 
+static void _gbp_flatpak_configuration_set_manifest (GbpFlatpakConfiguration *self,
+                                                     GFile *manifest);
+
+GbpFlatpakConfiguration *
+gbp_flatpak_configuration_new (IdeContext  *context,
+                               const gchar *id,
+                               const gchar *display_name)
+{
+  g_assert (IDE_IS_CONTEXT (context));
+  g_assert (!ide_str_empty0 (id));
+
+  return g_object_new (GBP_TYPE_FLATPAK_CONFIGURATION,
+                       "context", context,
+                       "display-name", display_name,
+                       "id", id,
+                       NULL);
+}
+
+gchar *
+get_project_dir_name (IdeContext *context)
+{
+  GFile *project_file;
+  g_autoptr(GFileInfo) file_info = NULL;
+  g_autoptr(GFile) project_dir = NULL;
+  g_autofree gchar *project_file_path = NULL;
+
+  g_assert (IDE_IS_CONTEXT (context));
+
+  project_file = ide_context_get_project_file (context);
+
+  g_return_val_if_fail (G_IS_FILE (project_file), NULL);
+
+  project_file_path = g_file_get_path (project_file);
+
+  if (g_file_test (project_file_path, G_FILE_TEST_IS_DIR))
+    project_dir = g_object_ref (project_file);
+  else
+    project_dir = g_file_get_parent (project_file);
+
+  return g_file_get_basename (project_dir);
+}
+
+JsonNode *
+guess_primary_module (JsonNode    *modules_node,
+                      const gchar *project_dir_name)
+{
+  JsonArray *modules;
+  JsonNode *module;
+  JsonNode *parent;
+
+  g_return_val_if_fail (!ide_str_empty0 (project_dir_name), NULL);
+  g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (modules_node), NULL);
+
+  /* TODO: Support module strings that refer to other files? */
+  modules = json_node_get_array (modules_node);
+  if (json_array_get_length (modules) == 1)
+    {
+      module = json_array_get_element (modules, 0);
+      if (JSON_NODE_HOLDS_OBJECT (module))
+        return module;
+    }
+  else
+    {
+      for (guint i = 0; i < json_array_get_length (modules); i++)
+        {
+          module = json_array_get_element (modules, i);
+          if (JSON_NODE_HOLDS_OBJECT (module))
+            {
+              const gchar *module_name;
+              module_name = json_object_get_string_member (json_node_get_object (module), "name");
+              if (g_strcmp0 (module_name, project_dir_name) == 0)
+                return module;
+              if (json_object_has_member (json_node_get_object (module), "modules"))
+                {
+                  JsonNode *nested_modules_node;
+                  JsonNode *nested_primary_module;
+                  nested_modules_node = json_object_get_member (json_node_get_object (module), "modules");
+                  nested_primary_module = guess_primary_module (nested_modules_node, project_dir_name);
+                  if (nested_primary_module != NULL)
+                    return nested_primary_module;
+                }
+            }
+        }
+        /* If none match, assume the last module in the list is the primary one */
+        parent = json_node_get_parent (modules_node);
+        if (JSON_NODE_HOLDS_OBJECT (parent) &&
+            json_node_get_parent (parent) == NULL &&
+            json_array_get_length (modules) > 0)
+          {
+            JsonNode *last_node;
+            last_node = json_array_get_element (modules, json_array_get_length (modules) - 1);
+            if (JSON_NODE_HOLDS_OBJECT (last_node))
+              return last_node;
+          }
+    }
+
+  return NULL;
+}
+
+/**
+ * gbp_flatpak_configuration_load_from_file:
+ * @self: a #GbpFlatpakConfiguration
+ * @manifest: a #GFile, possibly containing a flatpak manifest
+ *
+ * Returns: %TRUE if the manifest is successfully parsed, %FALSE otherwise
+ */
+gboolean
+gbp_flatpak_configuration_load_from_file (GbpFlatpakConfiguration *self,
+                                          GFile                   *manifest)
+{
+  g_autofree gchar *path = NULL;
+  g_autofree gchar *project_dir_name = NULL;
+  const gchar *prefix = NULL;
+  const gchar *platform = NULL;
+  const gchar *sdk = NULL;
+  const gchar *branch = NULL;
+  const gchar *arch = NULL;
+  const gchar *runtime_id = NULL;
+  g_autoptr(JsonParser) parser = NULL;
+  JsonNode *root_node = NULL;
+  JsonNode *app_id_node = NULL;
+  JsonNode *id_node = NULL;
+  JsonNode *runtime_node = NULL;
+  JsonNode *runtime_version_node = NULL;
+  JsonNode *sdk_node = NULL;
+  JsonNode *modules_node = NULL;
+  JsonNode *primary_module_node = NULL;
+  JsonNode *command_node = NULL;
+  JsonNode *finish_args_node = NULL;
+  JsonObject *root_object = NULL;
+  g_autoptr(GError) local_error = NULL;
+  IdeContext *context;
+
+  g_assert (GBP_IS_FLATPAK_CONFIGURATION (self));
+  g_assert (G_IS_FILE (manifest));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  /* Check if the contents look like a flatpak manifest */
+  path = g_file_get_path (manifest);
+  parser = json_parser_new ();
+  json_parser_load_from_file (parser, path, &local_error);
+  if (local_error != NULL)
+    {
+      g_warning ("Error parsing potential flatpak manifest %s: %s", path, local_error->message);
+      return FALSE;
+    }
+
+  root_node = json_parser_get_root (parser);
+  if (!JSON_NODE_HOLDS_OBJECT (root_node))
+    return FALSE;
+
+  root_object = json_node_get_object (root_node);
+  app_id_node = json_object_get_member (root_object, "app-id");
+  id_node = json_object_get_member (root_object, "id");
+  runtime_node = json_object_get_member (root_object, "runtime");
+  runtime_version_node = json_object_get_member (root_object, "runtime-version");
+  sdk_node = json_object_get_member (root_object, "sdk");
+  modules_node = json_object_get_member (root_object, "modules");
+
+  if ((!JSON_NODE_HOLDS_VALUE (app_id_node) && !JSON_NODE_HOLDS_VALUE (id_node)) ||
+       !JSON_NODE_HOLDS_VALUE (runtime_node) ||
+       !JSON_NODE_HOLDS_VALUE (sdk_node) ||
+       !JSON_NODE_HOLDS_ARRAY (modules_node))
+    return FALSE;
+
+  IDE_TRACE_MSG ("Discovered flatpak manifest at %s", path);
+
+  _gbp_flatpak_configuration_set_manifest (self, manifest);
+
+  /**
+   * TODO: Currently we just support the build-options object that's global to the
+   * manifest, but modules can have their own build-options as well that override
+   * global ones, so we should consider supporting that. The main difficulty would
+   * be keeping track of each so they can be written back to the file properly when
+   * the user makes changes in the Builder interface.
+   */
+  if (json_object_has_member (root_object, "build-options") &&
+      JSON_NODE_HOLDS_OBJECT (json_object_get_member (root_object, "build-options")))
+    {
+      JsonObject *build_options = NULL;
+      IdeEnvironment *environment;
+
+      build_options = json_object_get_object_member (root_object, "build-options");
+
+      if (json_object_has_member (build_options, "prefix"))
+        prefix = json_object_get_string_member (build_options, "prefix");
+
+      environment = ide_environment_new ();
+      if (json_object_has_member (build_options, "cflags"))
+        {
+          const gchar *cflags;
+          cflags = json_object_get_string_member (build_options, "cflags");
+          if (cflags != NULL)
+            ide_environment_setenv (environment, "CFLAGS", cflags);
+        }
+      if (json_object_has_member (build_options, "cxxflags"))
+        {
+          const gchar *cxxflags;
+          cxxflags = json_object_get_string_member (build_options, "cxxflags");
+          if (cxxflags != NULL)
+            ide_environment_setenv (environment, "CXXFLAGS", cxxflags);
+        }
+      if (json_object_has_member (build_options, "env"))
+        {
+          JsonObject *env_vars;
+          env_vars = json_object_get_object_member (build_options, "env");
+          if (env_vars != NULL)
+            {
+              g_autoptr(GList) env_list = NULL;
+              GList *l;
+              env_list = json_object_get_members (env_vars);
+              for (l = env_list; l != NULL; l = l->next)
+                {
+                  const gchar *env_name = (gchar *)l->data;
+                  const gchar *env_value = json_object_get_string_member (env_vars, env_name);
+                  if (!ide_str_empty0 (env_name) && !ide_str_empty0 (env_value))
+                    ide_environment_setenv (environment, env_name, env_value);
+                }
+            }
+        }
+      ide_configuration_set_environment (IDE_CONFIGURATION (self), environment);
+    }
+
+  if (ide_str_empty0 (prefix))
+    prefix = "/app";
+  ide_configuration_set_prefix (IDE_CONFIGURATION (self), prefix);
+
+  platform = json_node_get_string (runtime_node);
+  gbp_flatpak_configuration_set_platform (self, platform);
+
+  if (JSON_NODE_HOLDS_VALUE (runtime_version_node))
+    branch = json_node_get_string (runtime_version_node);
+  if (ide_str_empty0 (branch))
+    branch = "master";
+  gbp_flatpak_configuration_set_branch (self, branch);
+
+  arch = flatpak_get_default_arch ();
+  runtime_id = g_strdup_printf ("flatpak:%s/%s/%s", platform, arch, branch);
+  ide_configuration_set_runtime_id (IDE_CONFIGURATION (self), runtime_id);
+
+  sdk = json_node_get_string (sdk_node);
+  gbp_flatpak_configuration_set_sdk (self, sdk);
+
+  command_node = json_object_get_member (root_object, "command");
+  if (JSON_NODE_HOLDS_VALUE (command_node))
+    gbp_flatpak_configuration_set_command (self, json_node_get_string (command_node));
+
+  finish_args_node = json_object_get_member (root_object, "finish-args");
+  if (JSON_NODE_HOLDS_ARRAY (finish_args_node))
+    {
+      JsonArray *finish_args_array;
+      GPtrArray *finish_args;
+      g_auto(GStrv) finish_args_strv = NULL;
+      finish_args = g_ptr_array_new ();
+      finish_args_array = json_node_get_array (finish_args_node);
+      for (guint i = 0; i < json_array_get_length (finish_args_array); i++)
+        {
+          const gchar *arg = json_array_get_string_element (finish_args_array, i);
+          if (!ide_str_empty0 (arg))
+            g_ptr_array_add (finish_args, g_strdup (arg));
+        }
+      g_ptr_array_add (finish_args, NULL);
+      finish_args_strv = (gchar **)g_ptr_array_free (finish_args, FALSE);
+      gbp_flatpak_configuration_set_finish_args (self, (const gchar * const *)finish_args_strv);
+    }
+
+  if (JSON_NODE_HOLDS_VALUE (app_id_node))
+    ide_configuration_set_app_id (IDE_CONFIGURATION (self), json_node_get_string (app_id_node));
+  else
+    ide_configuration_set_app_id (IDE_CONFIGURATION (self), json_node_get_string (id_node));
+
+  project_dir_name = get_project_dir_name (context);
+  primary_module_node = guess_primary_module (modules_node, (const gchar *)project_dir_name);
+  if (primary_module_node != NULL && JSON_NODE_HOLDS_OBJECT (primary_module_node))
+    {
+      const gchar *primary_module_name;
+      JsonObject *primary_module_object;
+      primary_module_object = json_node_get_object (primary_module_node);
+      primary_module_name = json_object_get_string_member (primary_module_object, "name");
+      gbp_flatpak_configuration_set_primary_module (self, primary_module_name);
+      if (json_object_has_member (primary_module_object, "config-opts"))
+        {
+          JsonArray *config_opts_array;
+          config_opts_array = json_object_get_array_member (primary_module_object, "config-opts");
+          if (config_opts_array != NULL)
+            {
+              g_autoptr(GPtrArray) config_opts_strv = NULL;
+              config_opts_strv = g_ptr_array_new_with_free_func (g_free);
+              for (guint i = 0; i < json_array_get_length (config_opts_array); i++)
+                {
+                  const gchar *next_option;
+                  next_option = json_array_get_string_element (config_opts_array, i);
+                  g_ptr_array_add (config_opts_strv, g_strdup (next_option));
+                }
+              g_ptr_array_add (config_opts_strv, NULL);
+              if (config_opts_strv->len > 1)
+                {
+                  const gchar *config_opts;
+                  config_opts = g_strjoinv (" ", (gchar **)config_opts_strv->pdata);
+                  ide_configuration_set_config_opts (IDE_CONFIGURATION (self), config_opts);
+                }
+            }
+        }
+    }
+
+  return TRUE;
+}
+
 const gchar *
 gbp_flatpak_configuration_get_branch (GbpFlatpakConfiguration *self)
 {
@@ -129,6 +438,16 @@ gbp_flatpak_configuration_get_manifest (GbpFlatpakConfiguration *self)
   return self->manifest;
 }
 
+static void
+_gbp_flatpak_configuration_set_manifest (GbpFlatpakConfiguration *self,
+                                         GFile                   *manifest)
+{
+  g_return_if_fail (GBP_IS_FLATPAK_CONFIGURATION (self));
+
+  g_clear_object (&self->manifest);
+  self->manifest = g_object_ref (manifest);
+}
+
 const gchar *
 gbp_flatpak_configuration_get_platform (GbpFlatpakConfiguration *self)
 {
diff --git a/plugins/flatpak/gbp-flatpak-configuration.h b/plugins/flatpak/gbp-flatpak-configuration.h
index 889982d..02ae845 100644
--- a/plugins/flatpak/gbp-flatpak-configuration.h
+++ b/plugins/flatpak/gbp-flatpak-configuration.h
@@ -27,27 +27,31 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpFlatpakConfiguration, gbp_flatpak_configuration, GBP, FLATPAK_CONFIGURATION, 
IdeConfiguration)
 
-const gchar         *gbp_flatpak_configuration_get_branch         (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_branch         (GbpFlatpakConfiguration *self,
-                                                                   const gchar             *branch);
-const gchar         *gbp_flatpak_configuration_get_command        (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_command        (GbpFlatpakConfiguration *self,
-                                                                   const gchar             *command);
-const gchar * const *gbp_flatpak_configuration_get_finish_args    (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_finish_args    (GbpFlatpakConfiguration *self,
-                                                                   const gchar * const     *finish_args);
-GFile               *gbp_flatpak_configuration_get_manifest       (GbpFlatpakConfiguration *self);
-gchar               *gbp_flatpak_configuration_get_manifest_path  (GbpFlatpakConfiguration *self);
-const gchar         *gbp_flatpak_configuration_get_platform       (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_platform       (GbpFlatpakConfiguration *self,
-                                                                   const gchar             *platform);
-const gchar         *gbp_flatpak_configuration_get_primary_module (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_primary_module (GbpFlatpakConfiguration *self,
-                                                                   const gchar             *primary_module);
-const gchar         *gbp_flatpak_configuration_get_sdk            (GbpFlatpakConfiguration *self);
-void                 gbp_flatpak_configuration_set_sdk            (GbpFlatpakConfiguration *self,
-                                                                   const gchar             *sdk);
-
+GbpFlatpakConfiguration  *gbp_flatpak_configuration_new                (IdeContext  *context,
+                                                                        const gchar *id,
+                                                                        const gchar *display_name);
+gboolean                  gbp_flatpak_configuration_load_from_file     (GbpFlatpakConfiguration *self,
+                                                                        GFile *manifest);
+const gchar              *gbp_flatpak_configuration_get_branch         (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_branch         (GbpFlatpakConfiguration *self,
+                                                                        const gchar             *branch);
+const gchar              *gbp_flatpak_configuration_get_command        (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_command        (GbpFlatpakConfiguration *self,
+                                                                        const gchar             *command);
+const gchar * const      *gbp_flatpak_configuration_get_finish_args    (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_finish_args    (GbpFlatpakConfiguration *self,
+                                                                        const gchar *const      
*finish_args);
+GFile                    *gbp_flatpak_configuration_get_manifest       (GbpFlatpakConfiguration *self);
+gchar                    *gbp_flatpak_configuration_get_manifest_path  (GbpFlatpakConfiguration *self);
+const gchar              *gbp_flatpak_configuration_get_platform       (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_platform       (GbpFlatpakConfiguration *self,
+                                                                        const gchar             *platform);
+const gchar              *gbp_flatpak_configuration_get_primary_module (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_primary_module (GbpFlatpakConfiguration *self,
+                                                                        const gchar             
*primary_module);
+const gchar              *gbp_flatpak_configuration_get_sdk            (GbpFlatpakConfiguration *self);
+void                      gbp_flatpak_configuration_set_sdk            (GbpFlatpakConfiguration *self,
+                                                                        const gchar             *sdk);
 G_END_DECLS
 
 #endif /* GBP_FLATPAK_CONFIGURATION_H */



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