[gnome-builder] flatpak: re-introduce save support
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] flatpak: re-introduce save support
- Date: Wed, 31 Jan 2018 06:32:59 +0000 (UTC)
commit 3d02ed08ae5c0220b347bfb0f4d7bf6abae848bd
Author: Christian Hergert <chergert redhat com>
Date: Tue Jan 30 22:07:44 2018 -0800
flatpak: re-introduce save support
This uses a much simpler design to do write back of flatpak
manifests, at the cost of loosing our (however broken)
non-destructive write-back.
I'd rather have it working and be destructive than non-working.
Also, I much prefer the simpler implementation using JsonNode
compared to regexes.
.../flatpak/gbp-flatpak-configuration-provider.c | 76 ++++++--
src/plugins/flatpak/gbp-flatpak-manifest.c | 199 ++++++++++++++++++++-
2 files changed, 263 insertions(+), 12 deletions(-)
---
diff --git a/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
b/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
index b94d6ff72..e09f2600a 100644
--- a/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
+++ b/src/plugins/flatpak/gbp-flatpak-configuration-provider.c
@@ -33,23 +33,56 @@ struct _GbpFlatpakConfigurationProvider
GPtrArray *configs;
};
+static void gbp_flatpak_configuration_provider_save_tick (GTask *task);
+
static void
-gbp_flatpak_configuration_provider_save_worker (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+gbp_flatpak_configuration_provider_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GbpFlatpakConfigurationProvider *self = source_object;
+ GbpFlatpakManifest *manifest = (GbpFlatpakManifest *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
- IDE_ENTRY;
+ g_assert (GBP_IS_FLATPAK_MANIFEST (manifest));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!gbp_flatpak_manifest_save_finish (manifest, result, &error))
+ g_warning ("Failed to save manifest: %s", error->message);
+
+ gbp_flatpak_configuration_provider_save_tick (task);
+}
+
+static void
+gbp_flatpak_configuration_provider_save_tick (GTask *task)
+{
+ g_autoptr(GbpFlatpakManifest) manifest = NULL;
+ GbpFlatpakConfigurationProvider *self;
+ GPtrArray *manifests;
g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
g_assert (GBP_IS_FLATPAK_CONFIGURATION_PROVIDER (self));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- g_task_return_boolean (task, TRUE);
+ manifests = g_task_get_task_data (task);
+ g_assert (manifests != NULL);
- IDE_EXIT;
+ if (manifests->len == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ manifest = g_object_ref (g_ptr_array_index (manifests, manifests->len - 1));
+ g_assert (GBP_IS_FLATPAK_MANIFEST (manifest));
+ g_ptr_array_remove_index (manifests, manifests->len - 1);
+
+ gbp_flatpak_manifest_save_async (manifest,
+ g_task_get_cancellable (task),
+ gbp_flatpak_configuration_provider_save_cb,
+ g_object_ref (task));
}
static void
@@ -60,6 +93,7 @@ gbp_flatpak_configuration_provider_save_async (IdeConfigurationProvider *provide
{
GbpFlatpakConfigurationProvider *self = (GbpFlatpakConfigurationProvider *)provider;
g_autoptr(GTask) task = NULL;
+ g_autoptr(GPtrArray) ar = NULL;
IDE_ENTRY;
@@ -69,7 +103,29 @@ gbp_flatpak_configuration_provider_save_async (IdeConfigurationProvider *provide
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gbp_flatpak_configuration_provider_save_async);
g_task_set_priority (task, G_PRIORITY_LOW);
- g_task_run_in_thread (task, gbp_flatpak_configuration_provider_save_worker);
+
+ if (self->configs == NULL || self->configs->len == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ g_assert (self->configs != NULL);
+ g_assert (self->configs->len > 0);
+
+ ar = g_ptr_array_new_with_free_func (g_object_unref);
+
+ for (guint i = 0; i < self->configs->len; i++)
+ {
+ GbpFlatpakManifest *manifest = g_ptr_array_index (self->configs, i);
+
+ if (ide_configuration_get_dirty (IDE_CONFIGURATION (manifest)))
+ g_ptr_array_add (ar, g_object_ref (manifest));
+ }
+
+ g_task_set_task_data (task, g_steal_pointer (&ar), (GDestroyNotify)g_ptr_array_unref);
+
+ gbp_flatpak_configuration_provider_save_tick (task);
IDE_EXIT;
}
diff --git a/src/plugins/flatpak/gbp-flatpak-manifest.c b/src/plugins/flatpak/gbp-flatpak-manifest.c
index cf91d34b8..564a67776 100644
--- a/src/plugins/flatpak/gbp-flatpak-manifest.c
+++ b/src/plugins/flatpak/gbp-flatpak-manifest.c
@@ -427,8 +427,8 @@ gbp_flatpak_manifest_initable_init (GInitable *initable,
(const gchar * const *)build_commands);
if (discover_strv_field (primary, "post-install", &post_install))
- ide_configuration_set_build_commands (IDE_CONFIGURATION (self),
- (const gchar * const *)post_install);
+ ide_configuration_set_post_install_commands (IDE_CONFIGURATION (self),
+ (const gchar * const *)post_install);
if (json_object_has_member (primary, "builddir") &&
json_object_get_boolean_member (primary, "builddir"))
@@ -662,3 +662,198 @@ gbp_flatpak_manifest_get_path (GbpFlatpakManifest *self)
return g_file_get_path (self->file);
}
+
+static void
+apply_changes_to_tree (GbpFlatpakManifest *self)
+{
+ IdeEnvironment *env;
+ const gchar *app_id;
+ const gchar *runtime_id;
+ JsonObject *obj;
+ JsonObject *build_options;
+ JsonObject *env_obj;
+ guint n_items;
+
+ g_assert (GBP_IS_FLATPAK_MANIFEST (self));
+ g_assert (self->root != NULL);
+ g_assert (JSON_NODE_HOLDS_OBJECT (self->root));
+
+ obj = json_node_get_object (self->root);
+
+ if ((runtime_id = ide_configuration_get_runtime_id (IDE_CONFIGURATION (self))))
+ {
+ g_autofree gchar *id = NULL;
+ g_autofree gchar *arch = NULL;
+ g_autofree gchar *branch = NULL;
+
+ if (g_str_has_prefix (runtime_id, "flatpak:"))
+ runtime_id += strlen ("flatpak:");
+
+ if (gbp_flatpak_split_id (runtime_id, &id, &arch, &branch))
+ {
+ json_object_set_string_member (obj, "runtime", id);
+ json_object_set_string_member (obj, "runtime-version", branch);
+ }
+ }
+
+ if ((app_id = ide_configuration_get_app_id (IDE_CONFIGURATION (self))))
+ json_object_set_string_member (obj, "app-id", app_id);
+
+ if (!json_object_has_member (obj, "build-options"))
+ json_object_set_object_member (obj, "build-options", json_object_new ());
+ build_options = json_object_get_object_member (obj, "build-options");
+
+ env_obj = json_object_new ();
+ json_object_set_object_member (build_options, "env", env_obj);
+
+ env = ide_configuration_get_environment (IDE_CONFIGURATION (self));
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (env));
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(IdeEnvironmentVariable) var = g_list_model_get_item (G_LIST_MODEL (env), i);
+ const gchar *key;
+ const gchar *value;
+
+ g_assert (IDE_IS_ENVIRONMENT_VARIABLE (var));
+
+ key = ide_environment_variable_get_key (var);
+ value = ide_environment_variable_get_value (var);
+
+ if (dzl_str_equal0 (key, "CFLAGS"))
+ json_object_set_string_member (build_options, "cflags", value);
+ else if (dzl_str_equal0 (key, "CXXFLAGS"))
+ json_object_set_string_member (build_options, "cxxflags", value);
+ else
+ json_object_set_string_member (env_obj, key, value);
+ }
+
+ if (ide_configuration_get_locality (IDE_CONFIGURATION (self)) == IDE_BUILD_LOCALITY_OUT_OF_TREE)
+ json_object_set_boolean_member (self->primary, "builddir", TRUE);
+ else if (json_object_has_member (self->primary, "builddir"))
+ json_object_remove_member (self->primary, "builddir");
+}
+
+static void
+gbp_flatpak_manifest_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+ GbpFlatpakManifest *self;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!g_file_replace_contents_finish (file, result, NULL, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
+
+ self = g_task_get_source_object (task);
+ ide_configuration_set_dirty (IDE_CONFIGURATION (self), FALSE);
+ g_task_return_boolean (task, TRUE);
+
+ IDE_EXIT;
+}
+
+void
+gbp_flatpak_manifest_save_async (GbpFlatpakManifest *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ g_autoptr(JsonGenerator) generator = NULL;
+ g_autofree gchar *data = NULL;
+ gsize len;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (GBP_IS_FLATPAK_MANIFEST (self));
+ g_return_if_fail (G_IS_FILE (self->file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gbp_flatpak_manifest_save_async);
+ g_task_set_priority (task, G_PRIORITY_LOW);
+
+ if (self->root == NULL || self->primary == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Failed to save, missing JSON node");
+ return;
+ }
+
+ /*
+ * First apply our changes to the saved JsonNode while we are in the
+ * main loop to avoid proxying structures to another thread (and the
+ * mutability issues that would arrise from that).
+ */
+ apply_changes_to_tree (self);
+
+ /*
+ * Now that we have an updated JsonNode tree, convert that to a
+ * pretty-printed JSON document stream. We are destructive here (in that
+ * we lose extended-JSON comments. But that is outside the scope of our
+ * support and needs to be dealt with at a lower layer.
+ */
+ generator = json_generator_new ();
+ json_generator_set_pretty (generator, TRUE);
+ json_generator_set_indent (generator, 4);
+ json_generator_set_indent_char (generator, ' ');
+ json_generator_set_root (generator, self->root);
+ data = json_generator_to_data (generator, &len);
+
+ /*
+ * Since we're writing this as a series of bytes, and not a utf8 string
+ * (even though it is), we can steal the final \0 byte to add a trailing
+ * newline for the file.
+ */
+ data[len] = '\n';
+ bytes = g_bytes_new_take (g_steal_pointer (&data), len + 1);
+
+ /*
+ * Now that we have a buffer containing the UTF-8 encoded formatted
+ * JSON, we can asynchronously write that content to disk without hvaing
+ * to access any of our Json nodes (which are main-thread only).
+ */
+
+ g_file_replace_contents_bytes_async (self->file,
+ bytes,
+ NULL,
+ TRUE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ cancellable,
+ gbp_flatpak_manifest_save_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+gboolean
+gbp_flatpak_manifest_save_finish (GbpFlatpakManifest *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (GBP_IS_FLATPAK_MANIFEST (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ ret = g_task_propagate_boolean (G_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]