[gnome-builder] flatpak: Discover flatpak manifests and add them as runtimes



commit 457fc3d8f08a4a8e1a60c2f6bf9842fc38ca6694
Author: Matthew Leeds <mleeds redhat com>
Date:   Mon Nov 14 23:26:00 2016 -0800

    flatpak: Discover flatpak manifests and add them as runtimes
    
    When a repo includes a flatpak manifest, GNOME Builder should know
    how to use it with flatpak-builder to build the project's dependencies
    so the build will be more likely to work. This commit teaches
    GbpFlatpakRuntimeProvider how to identify manifests (by file name and
    doing a sanity check on the contents) and add them to the list of
    runtimes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=773764

 plugins/flatpak/configure.ac                   |    3 +-
 plugins/flatpak/gbp-flatpak-runtime-provider.c |  243 ++++++++++++++++++++++++
 plugins/flatpak/gbp-flatpak-runtime.c          |   66 +++++++-
 3 files changed, 310 insertions(+), 2 deletions(-)
---
diff --git a/plugins/flatpak/configure.ac b/plugins/flatpak/configure.ac
index 8b2ccf6..d820b50 100644
--- a/plugins/flatpak/configure.ac
+++ b/plugins/flatpak/configure.ac
@@ -9,7 +9,8 @@ AC_ARG_ENABLE([flatpak-plugin],
 
 AS_IF([test "$enable_flatpak_plugin" != no],[
        PKG_CHECK_MODULES(FLATPAK,
-                         [flatpak >= flatpak_required_version],
+                         [flatpak >= flatpak_required_version
+                          json-glib-1.0 >= json_glib_required_version],
                          [have_flatpak=yes],
                          [have_flatpak=no])
 
diff --git a/plugins/flatpak/gbp-flatpak-runtime-provider.c b/plugins/flatpak/gbp-flatpak-runtime-provider.c
index 82652f1..1cb9193 100644
--- a/plugins/flatpak/gbp-flatpak-runtime-provider.c
+++ b/plugins/flatpak/gbp-flatpak-runtime-provider.c
@@ -20,6 +20,7 @@
 
 #include <string.h>
 #include <flatpak.h>
+#include <json-glib/json-glib.h>
 
 #include "util/ide-posix.h"
 
@@ -36,6 +37,16 @@ struct _GbpFlatpakRuntimeProvider
   GPtrArray           *runtimes;
 };
 
+typedef struct
+{
+  gchar *platform;
+  gchar *branch;
+  gchar *sdk;
+  gchar *app_id;
+  gchar *primary_module;
+  GFile *file;
+} FlatpakManifest;
+
 static void runtime_provider_iface_init (IdeRuntimeProviderInterface *);
 
 G_DEFINE_TYPE_EXTENDED (GbpFlatpakRuntimeProvider, gbp_flatpak_runtime_provider, G_TYPE_OBJECT, 0,
@@ -71,6 +82,20 @@ contains_id (GPtrArray   *ar,
   return FALSE;
 }
 
+static void
+flatpak_manifest_free (void *data)
+{
+  FlatpakManifest *manifest = data;
+
+  g_free (manifest->branch);
+  g_free (manifest->sdk);
+  g_free (manifest->platform);
+  g_free (manifest->app_id);
+  g_free (manifest->primary_module);
+  g_clear_object (&manifest->file);
+  g_slice_free (FlatpakManifest, manifest);
+}
+
 static gboolean
 gbp_flatpak_runtime_provider_load_refs (GbpFlatpakRuntimeProvider  *self,
                                         FlatpakInstallation        *installation,
@@ -180,6 +205,215 @@ gbp_flatpak_runtime_provider_load_refs (GbpFlatpakRuntimeProvider  *self,
   return TRUE;
 }
 
+static gchar *
+guess_primary_module (JsonObject *manifest_object,
+                      GFile      *directory)
+{
+  JsonArray *modules = NULL;
+  gchar *dir_name;
+  guint num_modules;
+
+  g_assert (manifest_object != NULL);
+  g_assert (G_IS_FILE (directory));
+
+  dir_name = g_file_get_basename (directory);
+  g_assert (!ide_str_empty0 (dir_name));
+  modules = json_object_get_array_member (manifest_object, "modules");
+  g_assert (modules != NULL);
+
+  num_modules = json_array_get_length (modules);
+  for (guint i = 0; i < num_modules; i++)
+    {
+      JsonNode *module;
+      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 (num_modules == 1 || g_strcmp0 (module_name, dir_name) == 0)
+            return g_strdup (module_name);
+        }
+    }
+
+  g_warning ("Unable to determine the primary module in the flatpak manifest");
+  return NULL;
+}
+
+static GPtrArray *
+gbp_flatpak_runtime_provider_find_flatpak_manifests (GbpFlatpakRuntimeProvider *self,
+                                                     GCancellable              *cancellable,
+                                                     GFile                     *directory,
+                                                     GError                   **error)
+{
+  g_autoptr(GFileEnumerator) enumerator = NULL;
+  GFileInfo *file_info = NULL;
+  GPtrArray *ar;
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_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);
+
+  enumerator = g_file_enumerate_children (directory,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                          G_FILE_QUERY_INFO_NONE,
+                                          cancellable,
+                                          error);
+  if (!enumerator)
+    return NULL;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+    {
+      GFileType file_type;
+      g_autofree gchar *name = NULL;
+      g_autofree gchar *path = NULL;
+      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;
+      JsonObject *root_object = NULL;
+      g_autoptr(GError) local_error = NULL;
+      g_autoptr(GFile) file = NULL;
+      FlatpakManifest *manifest;
+
+      file_type = g_file_info_get_file_type (file_info);
+      name = g_strdup (g_file_info_get_name (file_info));
+      g_clear_object (&file_info);
+
+      if (name == NULL || file_type == G_FILE_TYPE_DIRECTORY)
+        continue;
+
+      file = g_file_get_child (directory, name);
+
+      /* This regex is based on https://wiki.gnome.org/HowDoI/ChooseApplicationID */
+      filename_regex = g_regex_new ("^[[:alnum:]-_]+\\.[[:alnum:]-_]+(\\.[[:alnum:]-_]+)*\\.json$",
+                                    0, 0, NULL);
+
+      g_regex_match (filename_regex, name, 0, &match_info);
+      if (!g_match_info_matches (match_info))
+        continue;
+
+      /* Check if the contents look like a flatpak manifest */
+      path = g_file_get_path (file);
+      parser = json_parser_new ();
+      json_parser_load_from_file (parser, path, &local_error);
+      if (local_error != NULL)
+        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);
+      manifest->platform = json_node_dup_string (runtime_node);
+      if (!JSON_NODE_HOLDS_VALUE (runtime_version_node) || ide_str_empty0 (json_node_get_string 
(runtime_version_node)))
+        manifest->branch = g_strdup ("master");
+      else
+        manifest->branch = json_node_dup_string (runtime_version_node);
+      manifest->sdk = json_node_dup_string (sdk_node);
+      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);
+      manifest->primary_module = guess_primary_module (root_object, directory);
+
+      g_ptr_array_add (ar, manifest);
+    }
+
+  return ar;
+}
+
+static gboolean
+gbp_flatpak_runtime_provider_load_manifests (GbpFlatpakRuntimeProvider  *self,
+                                             GPtrArray                  *runtimes,
+                                             GCancellable               *cancellable,
+                                             GError                    **error)
+{
+  g_autoptr(GPtrArray) ar = NULL;
+  g_autoptr(GFileInfo) file_info = NULL;
+  IdeContext *context;
+  GFile *project_file;
+  g_autoptr(GFile) project_dir = NULL;
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME_PROVIDER (self));
+
+  context = ide_object_get_context (IDE_OBJECT (self->manager));
+  project_file = ide_context_get_project_file (context);
+
+  g_assert (G_IS_FILE (project_file));
+
+  file_info = g_file_query_info (project_file,
+                                 G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                 G_FILE_QUERY_INFO_NONE,
+                                 cancellable,
+                                 error);
+
+  if (file_info == NULL)
+    return FALSE;
+
+  if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
+    project_dir = g_object_ref (project_file);
+  else
+    project_dir = g_file_get_parent (project_file);
+
+  ar = gbp_flatpak_runtime_provider_find_flatpak_manifests (self, cancellable, project_dir, error);
+
+  if (ar == NULL)
+    return FALSE;
+
+  IDE_TRACE_MSG ("Found %u flatpak manifests", ar->len);
+
+  for (guint i = 0; i < ar->len; i++)
+    {
+      FlatpakManifest *manifest = g_ptr_array_index (ar, i);
+      gchar *id;
+
+      id = g_file_get_basename (manifest->file);
+      if (contains_id (runtimes, id))
+        continue;
+
+      g_ptr_array_add (runtimes,
+                       g_object_new (GBP_TYPE_FLATPAK_RUNTIME,
+                                     "branch", manifest->branch,
+                                     "sdk", manifest->sdk,
+                                     "platform", manifest->platform,
+                                     "manifest", manifest->file,
+                                     "primary-module", manifest->primary_module,
+                                     "app-id", manifest->app_id,
+                                     "context", context,
+                                     "id", id,
+                                     "display-name", id,
+                                     NULL));
+    }
+
+  return TRUE;
+}
+
 static void
 gbp_flatpak_runtime_provider_load_worker (GTask        *task,
                                           gpointer      source_object,
@@ -200,6 +434,7 @@ gbp_flatpak_runtime_provider_load_worker (GTask        *task,
 
   ret = g_ptr_array_new_with_free_func (g_object_unref);
 
+  /* Load system flatpak runtimes */
   if (NULL == (self->system_installation = flatpak_installation_new_system (cancellable, &error)) ||
       !gbp_flatpak_runtime_provider_load_refs (self, self->system_installation, ret, cancellable, &error))
     {
@@ -207,6 +442,7 @@ gbp_flatpak_runtime_provider_load_worker (GTask        *task,
       g_clear_error (&error);
     }
 
+  /* Load user flatpak runtimes */
   path = g_build_filename (g_get_home_dir (), ".local", "share", "flatpak", NULL);
   file = g_file_new_for_path (path);
 
@@ -217,6 +453,13 @@ gbp_flatpak_runtime_provider_load_worker (GTask        *task,
       g_clear_error (&error);
     }
 
+  /* Load flatpak manifests in the repo */
+  if (!gbp_flatpak_runtime_provider_load_manifests (self, ret, cancellable, &error))
+    {
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+
   g_task_return_pointer (task, g_steal_pointer (&ret), (GDestroyNotify)g_ptr_array_unref);
 
   IDE_EXIT;
diff --git a/plugins/flatpak/gbp-flatpak-runtime.c b/plugins/flatpak/gbp-flatpak-runtime.c
index 39a9393..75dc51e 100644
--- a/plugins/flatpak/gbp-flatpak-runtime.c
+++ b/plugins/flatpak/gbp-flatpak-runtime.c
@@ -28,6 +28,9 @@ struct _GbpFlatpakRuntime
   gchar *sdk;
   gchar *platform;
   gchar *branch;
+  gchar *primary_module;
+  gchar *app_id;
+  GFile *manifest;
 };
 
 G_DEFINE_TYPE (GbpFlatpakRuntime, gbp_flatpak_runtime, IDE_TYPE_RUNTIME)
@@ -37,6 +40,9 @@ enum {
   PROP_BRANCH,
   PROP_PLATFORM,
   PROP_SDK,
+  PROP_PRIMARY_MODULE,
+  PROP_APP_ID,
+  PROP_MANIFEST,
   LAST_PROP
 };
 
@@ -219,9 +225,13 @@ static void
 gbp_flatpak_runtime_prepare_configuration (IdeRuntime       *runtime,
                                            IdeConfiguration *configuration)
 {
-  g_assert (IDE_IS_RUNTIME (runtime));
+  GbpFlatpakRuntime* self = (GbpFlatpakRuntime *)runtime;
+
+  g_assert (GBP_IS_FLATPAK_RUNTIME (self));
   g_assert (IDE_IS_CONFIGURATION (configuration));
 
+  if (!ide_str_empty0 (self->app_id))
+    ide_configuration_set_app_id (configuration, self->app_id);
   ide_configuration_set_prefix (configuration, "/app");
 }
 
@@ -247,6 +257,18 @@ gbp_flatpak_runtime_get_property (GObject    *object,
       g_value_set_string (value, self->sdk);
       break;
 
+    case PROP_PRIMARY_MODULE:
+      g_value_set_string (value, self->primary_module);
+      break;
+
+    case PROP_APP_ID:
+      g_value_set_string (value, self->app_id);
+      break;
+
+    case PROP_MANIFEST:
+      g_value_set_object (value, self->manifest);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     }
@@ -274,6 +296,18 @@ gbp_flatpak_runtime_set_property (GObject      *object,
       self->sdk = g_value_dup_string (value);
       break;
 
+    case PROP_PRIMARY_MODULE:
+      self->primary_module = g_value_dup_string (value);
+      break;
+
+    case PROP_APP_ID:
+      self->app_id = g_value_dup_string (value);
+      break;
+
+    case PROP_MANIFEST:
+      self->manifest = g_value_dup_object (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     }
@@ -287,6 +321,9 @@ gbp_flatpak_runtime_finalize (GObject *object)
   g_clear_pointer (&self->sdk, g_free);
   g_clear_pointer (&self->platform, g_free);
   g_clear_pointer (&self->branch, g_free);
+  g_clear_pointer (&self->primary_module, g_free);
+  g_clear_pointer (&self->app_id, g_free);
+  g_clear_object (&self->manifest);
 
   G_OBJECT_CLASS (gbp_flatpak_runtime_parent_class)->finalize (object);
 }
@@ -334,6 +371,33 @@ gbp_flatpak_runtime_class_init (GbpFlatpakRuntimeClass *klass)
                           G_PARAM_CONSTRUCT |
                           G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_PRIMARY_MODULE] =
+    g_param_spec_string ("primary-module",
+                         "Primary module",
+                         "Primary module",
+                         NULL,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT |
+                          G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_APP_ID] =
+    g_param_spec_string ("app-id",
+                         "App ID",
+                         "App ID",
+                         NULL,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT |
+                          G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_MANIFEST] =
+    g_param_spec_object ("manifest",
+                         "Manifest",
+                         "Manifest file for use with flatpak-builder",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT |
+                          G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, LAST_PROP, properties);
 }
 


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