[gnome-builder] configuration: add IdeConfiguration and IdeConfigurationManager
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] configuration: add IdeConfiguration and IdeConfigurationManager
- Date: Mon, 15 Feb 2016 04:58:32 +0000 (UTC)
commit d17f108b95f71849613dcd24d09584ac478ded81
Author: Christian Hergert <chergert redhat com>
Date: Sun Feb 14 20:40:45 2016 -0800
configuration: add IdeConfiguration and IdeConfigurationManager
IdeConfiguration wraps all the important information required to do a
build. This means, runtime, device, environment variables, and possibly
more as needed.
libide/Makefile.am | 4 +
libide/ide-configuration-manager.c | 747 ++++++++++++++++++++++++++++++++
libide/ide-configuration-manager.h | 53 +++
libide/ide-configuration.c | 824 ++++++++++++++++++++++++++++++++++++
libide/ide-configuration.h | 79 ++++
libide/ide-context.c | 105 +++++
libide/ide-context.h | 1 +
7 files changed, 1813 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 7a89836..84dabe9 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -61,6 +61,10 @@ libide_1_0_la_public_sources = \
ide-completion-provider.h \
ide-completion-results.c \
ide-completion-results.h \
+ ide-configuration.c \
+ ide-configuration.h \
+ ide-configuration-manager.c \
+ ide-configuration-manager.h \
ide-context.c \
ide-context.h \
ide-debugger.c \
diff --git a/libide/ide-configuration-manager.c b/libide/ide-configuration-manager.c
new file mode 100644
index 0000000..0ecb2fd
--- /dev/null
+++ b/libide/ide-configuration-manager.c
@@ -0,0 +1,747 @@
+/* ide-configuration-manager.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-configuration-manager"
+
+#include <glib/gi18n.h>
+
+#include "ide-configuration.h"
+#include "ide-configuration-manager.h"
+#include "ide-context.h"
+#include "ide-debug.h"
+#include "ide-environment.h"
+#include "ide-macros.h"
+#include "ide-vcs.h"
+
+#define DOT_BUILD_CONFIG ".buildconfig"
+#define WRITEBACK_TIMEOUT_SECS 2
+
+struct _IdeConfigurationManager
+{
+ GObject parent_instance;
+
+ GPtrArray *configurations;
+ IdeConfiguration *current;
+ GKeyFile *key_file;
+
+ gulong writeback_handler;
+ guint change_count;
+};
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeConfigurationManager, ide_configuration_manager, IDE_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))
+
+enum {
+ PROP_0,
+ PROP_CURRENT,
+ PROP_CURRENT_DISPLAY_NAME,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+load_string (IdeConfiguration *configuration,
+ GKeyFile *key_file,
+ const gchar *group,
+ const gchar *key,
+ const gchar *property)
+{
+ g_assert (IDE_IS_CONFIGURATION (configuration));
+ g_assert (key_file != NULL);
+ g_assert (group != NULL);
+ g_assert (key != NULL);
+
+ if (g_key_file_has_key (key_file, group, key, NULL))
+ {
+ g_auto(GValue) value = G_VALUE_INIT;
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_take_string (&value, g_key_file_get_string (key_file, group, key, NULL));
+ g_object_set_property (G_OBJECT (configuration), property, &value);
+ }
+}
+
+static void
+load_environ (IdeConfiguration *configuration,
+ GKeyFile *key_file,
+ const gchar *group)
+{
+ IdeEnvironment *environment;
+ g_auto(GStrv) keys = NULL;
+
+ g_assert (IDE_IS_CONFIGURATION (configuration));
+ g_assert (key_file != NULL);
+ g_assert (group != NULL);
+
+ environment = ide_configuration_get_environment (configuration);
+ keys = g_key_file_get_keys (key_file, group, NULL, NULL);
+
+ if (keys != NULL)
+ {
+ guint i;
+
+ for (i = 0; keys [i]; i++)
+ {
+ g_autofree gchar *value = NULL;
+
+ value = g_key_file_get_string (key_file, group, keys [i], NULL);
+
+ if (value != NULL)
+ ide_environment_setenv (environment, keys [i], value);
+ }
+ }
+}
+
+static gboolean
+ide_configuration_manager_load (IdeConfigurationManager *self,
+ GKeyFile *key_file,
+ const gchar *group,
+ GError **error)
+{
+ g_autoptr(IdeConfiguration) configuration = NULL;
+ g_autofree gchar *env_group = NULL;
+ IdeContext *context;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_assert (key_file != NULL);
+ g_assert (group != NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+
+ configuration = g_object_new (IDE_TYPE_CONFIGURATION,
+ "id", group,
+ "context", context,
+ NULL);
+
+ load_string (configuration, key_file, group, "config-opts", "config-opts");
+ load_string (configuration, key_file, group, "device", "device-id");
+ load_string (configuration, key_file, group, "name", "display-name");
+ load_string (configuration, key_file, group, "runtime", "runtime-id");
+ load_string (configuration, key_file, group, "prefix", "prefix");
+
+ env_group = g_strdup_printf ("%s.environment", group);
+
+ if (g_key_file_has_group (key_file, env_group))
+ load_environ (configuration, key_file, env_group);
+
+ ide_configuration_set_dirty (configuration, FALSE);
+
+ ide_configuration_manager_add (self, configuration);
+
+ if (g_key_file_get_boolean (key_file, group, "default", NULL))
+ ide_configuration_manager_set_current (self, configuration);
+
+ return TRUE;
+}
+
+static gboolean
+ide_configuration_manager_restore (IdeConfigurationManager *self,
+ GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *contents = NULL;
+ g_auto(GStrv) groups = NULL;
+ gsize length = 0;
+ guint i;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_assert (self->key_file == NULL);
+ g_assert (G_IS_FILE (file));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ self->key_file = g_key_file_new ();
+
+ if (!g_file_load_contents (file, cancellable, &contents, &length, NULL, error))
+ IDE_RETURN (FALSE);
+
+ if (!g_key_file_load_from_data (self->key_file,
+ contents,
+ length,
+ G_KEY_FILE_KEEP_COMMENTS,
+ error))
+ IDE_RETURN (FALSE);
+
+ groups = g_key_file_get_groups (self->key_file, NULL);
+
+ for (i = 0; groups [i]; i++)
+ {
+ if (g_str_has_suffix (groups [i], ".environment"))
+ continue;
+
+ if (!ide_configuration_manager_load (self, self->key_file, groups [i], error))
+ IDE_RETURN (FALSE);
+ }
+
+ IDE_RETURN (TRUE);
+}
+
+static void
+ide_configuration_manager_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+ GFile *file = (GFile *)object;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!g_file_replace_contents_finish (file, result, NULL, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+void
+ide_configuration_manager_save_async (IdeConfigurationManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GHashTable) group_names = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_auto(GStrv) groups = NULL;
+ g_autoptr(GFile) file = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ gchar *data;
+ gsize length;
+ IdeContext *context;
+ IdeVcs *vcs;
+ GFile *workdir;
+ GError *error = NULL;
+ guint i;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (self->change_count == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ self->change_count = 0;
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+ file = g_file_get_child (workdir, DOT_BUILD_CONFIG);
+
+ /*
+ * NOTE:
+ *
+ * We keep the GKeyFile around from when we parsed .buildconfig, so that
+ * we can try to preserve comments and such when writing back.
+ *
+ * This means that we need to fill in all our known configuration
+ * sections, and then remove any that were removed since we were
+ * parsed it last.
+ */
+
+ if (self->key_file == NULL)
+ self->key_file = g_key_file_new ();
+
+ group_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (i = 0; i < self->configurations->len; i++)
+ {
+ IdeConfiguration *configuration = g_ptr_array_index (self->configurations, i);
+ IdeEnvironment *environment;
+ guint n_items;
+ guint j;
+ gchar *group;
+ gchar *group_environ;
+
+ group = g_strdup (ide_configuration_get_id (configuration));
+ group_environ = g_strdup_printf ("%s.environment", group);
+
+ /*
+ * Track our known group names, so we can remove missing names after
+ * we've updated the GKeyFile.
+ */
+ g_hash_table_insert (group_names, group, NULL);
+ g_hash_table_insert (group_names, group_environ, NULL);
+
+#define PERSIST_STRING_KEY(key, getter) \
+ g_key_file_set_string (self->key_file, group, key, \
+ ide_configuration_##getter (configuration))
+ PERSIST_STRING_KEY ("name", get_display_name);
+ PERSIST_STRING_KEY ("device", get_device_id);
+ PERSIST_STRING_KEY ("runtime", get_runtime_id);
+ PERSIST_STRING_KEY ("config-opts", get_config_opts);
+ PERSIST_STRING_KEY ("prefix", get_prefix);
+#undef PERSIST_STRING_KEY
+
+ if (configuration == self->current)
+ g_key_file_set_boolean (self->key_file, group, "default", TRUE);
+ else
+ g_key_file_remove_key (self->key_file, group, "default", NULL);
+
+ environment = ide_configuration_get_environment (configuration);
+
+ /*
+ * Remove all environment keys that are no longer specified in the
+ * environment. This allows us to just do a single pass of additions
+ * from the environment below.
+ */
+ if (g_key_file_has_group (self->key_file, group_environ))
+ {
+ g_auto(GStrv) keys = NULL;
+
+ if (NULL != (keys = g_key_file_get_keys (self->key_file, group_environ, NULL, NULL)))
+ {
+ for (j = 0; keys [j]; j++)
+ {
+ if (!ide_environment_getenv (environment, keys [j]))
+ g_key_file_remove_key (self->key_file, group_environ, keys [j], NULL);
+ }
+ }
+ }
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (environment));
+
+ for (j = 0; j < n_items; j++)
+ {
+ g_autoptr(IdeEnvironmentVariable) var = NULL;
+ const gchar *key;
+ const gchar *value;
+
+ var = g_list_model_get_item (G_LIST_MODEL (environment), j);
+ key = ide_environment_variable_get_key (var);
+ value = ide_environment_variable_get_value (var);
+
+ if (!ide_str_empty0 (key))
+ g_key_file_set_string (self->key_file, group_environ, key, value ?: "");
+ }
+ }
+
+ /*
+ * Now truncate any old groups in the keyfile.
+ */
+ if (NULL != (groups = g_key_file_get_groups (self->key_file, NULL)))
+ {
+ for (i = 0; groups [i]; i++)
+ {
+ if (!g_hash_table_contains (group_names, groups [i]))
+ g_key_file_remove_group (self->key_file, groups [i], NULL);
+ }
+ }
+
+ if (NULL == (data = g_key_file_to_data (self->key_file, &length, &error)))
+ {
+ g_task_return_error (task, error);
+ IDE_EXIT;
+ }
+
+ bytes = g_bytes_new_take (data, length);
+
+ g_file_replace_contents_bytes_async (file,
+ bytes,
+ NULL,
+ FALSE,
+ G_FILE_CREATE_NONE,
+ cancellable,
+ ide_configuration_manager_save_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_configuration_manager_save_finish (IdeConfigurationManager *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ide_configuration_manager_do_writeback (gpointer data)
+{
+ IdeConfigurationManager *self = data;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+
+ self->writeback_handler = 0;
+
+ ide_configuration_manager_save_async (self, NULL, NULL, NULL);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ide_configuration_manager_queue_writeback (IdeConfigurationManager *self)
+{
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+
+ if (self->writeback_handler != 0)
+ g_source_remove (self->writeback_handler);
+
+ self->writeback_handler = g_timeout_add_seconds (WRITEBACK_TIMEOUT_SECS,
+ ide_configuration_manager_do_writeback,
+ self);
+}
+
+static void
+ide_configuration_manager_add_default (IdeConfigurationManager *self)
+{
+ g_autoptr(IdeConfiguration) config = NULL;
+ IdeContext *context;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+
+ config = ide_configuration_new (context, "default", "local", "host");
+ ide_configuration_set_display_name (config, _("Default Configuration"));
+ ide_configuration_manager_add (self, config);
+
+ if (self->configurations->len == 1)
+ ide_configuration_manager_set_current (self, config);
+}
+
+/**
+ * ide_configuration_manager_get_configuration:
+ * @self: An #IdeConfigurationManager
+ * @id: The string identifier of the configuration
+ *
+ * Gets the #IdeConfiguration by id. See ide_configuration_get_id().
+ *
+ * Returns: (transfer none) (nullable): An #IdeConfiguration or %NULL if
+ * the configuration could not be found.
+ */
+IdeConfiguration *
+ide_configuration_manager_get_configuration (IdeConfigurationManager *self,
+ const gchar *id)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ for (guint i = 0; i < self->configurations->len; i++)
+ {
+ IdeConfiguration *configuration = g_ptr_array_index (self->configurations, i);
+
+ if (g_strcmp0 (id, ide_configuration_get_id (configuration)) == 0)
+ return configuration;
+ }
+
+ return NULL;
+}
+
+static void
+ide_configuration_manager_finalize (GObject *object)
+{
+ IdeConfigurationManager *self = (IdeConfigurationManager *)object;
+
+ ide_clear_source (&self->writeback_handler);
+ g_clear_pointer (&self->configurations, g_ptr_array_unref);
+ g_clear_pointer (&self->key_file, g_key_file_free);
+ g_clear_object (&self->current);
+
+ G_OBJECT_CLASS (ide_configuration_manager_parent_class)->finalize (object);
+}
+
+static void
+ide_configuration_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeConfigurationManager *self = IDE_CONFIGURATION_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CURRENT:
+ g_value_set_object (value, ide_configuration_manager_get_current (self));
+ break;
+
+ case PROP_CURRENT_DISPLAY_NAME:
+ {
+ IdeConfiguration *current = ide_configuration_manager_get_current (self);
+ g_value_set_string (value, ide_configuration_get_display_name (current));
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+ide_configuration_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeConfigurationManager *self = IDE_CONFIGURATION_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CURRENT:
+ ide_configuration_manager_set_current (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+ide_configuration_manager_class_init (IdeConfigurationManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_configuration_manager_finalize;
+ object_class->get_property = ide_configuration_manager_get_property;
+ object_class->set_property = ide_configuration_manager_set_property;
+
+ properties [PROP_CURRENT] =
+ g_param_spec_object ("current",
+ "Current",
+ "The current configuration for the context",
+ IDE_TYPE_CONFIGURATION,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CURRENT_DISPLAY_NAME] =
+ g_param_spec_string ("current-display-name",
+ "Current Display Name",
+ "The display name of the current configuration",
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_configuration_manager_init (IdeConfigurationManager *self)
+{
+ self->configurations = g_ptr_array_new_with_free_func (g_object_unref);
+}
+
+static GType
+ide_configuration_manager_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_CONFIGURATION;
+}
+
+static guint
+ide_configuration_manager_get_n_items (GListModel *model)
+{
+ IdeConfigurationManager *self = (IdeConfigurationManager *)model;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+
+ return self->configurations->len;
+}
+
+static gpointer
+ide_configuration_manager_get_item (GListModel *model,
+ guint position)
+{
+ IdeConfigurationManager *self = (IdeConfigurationManager *)model;
+
+ g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
+ g_return_val_if_fail (position < self->configurations->len, NULL);
+
+ return g_object_ref (g_ptr_array_index (self->configurations, position));
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ide_configuration_manager_get_item_type;
+ iface->get_n_items = ide_configuration_manager_get_n_items;
+ iface->get_item = ide_configuration_manager_get_item;
+}
+
+static void
+ide_configuration_manager_init_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeConfigurationManager *self = source_object;
+ g_autoptr(GFile) settings_file = NULL;
+ IdeContext *context;
+ GError *error = NULL;
+ IdeVcs *vcs;
+ GFile *workdir;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+ settings_file = g_file_get_child (workdir, DOT_BUILD_CONFIG);
+
+ if (!g_file_query_exists (settings_file, cancellable) ||
+ !ide_configuration_manager_restore (self, settings_file, cancellable, &error))
+ ide_configuration_manager_add_default (self);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_configuration_manager_init_async (GAsyncInitable *initable,
+ gint priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeConfigurationManager *self = (IdeConfigurationManager *)initable;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (G_IS_ASYNC_INITABLE (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_run_in_thread (task, ide_configuration_manager_init_worker);
+}
+
+static gboolean
+ide_configuration_manager_init_finish (GAsyncInitable *initable,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (initable));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = ide_configuration_manager_init_async;
+ iface->init_finish = ide_configuration_manager_init_finish;
+}
+
+void
+ide_configuration_manager_set_current (IdeConfigurationManager *self,
+ IdeConfiguration *current)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_return_if_fail (!current || IDE_IS_CONFIGURATION (current));
+
+ if (g_set_object (&self->current, current))
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_DISPLAY_NAME]);
+ }
+}
+
+/**
+ * ide_configuration_manager_get_current:
+ * @self: An #IdeConfigurationManager
+ *
+ * Gets the current configuration to use for building.
+ *
+ * Many systems allow you to pass a configuration in instead of relying on the
+ * default configuration. This sets the default configuration that various
+ * background items might use, such as tags builders which need to discover
+ * settings.
+ *
+ * Returns: (transfer none): An #IdeConfiguration
+ */
+IdeConfiguration *
+ide_configuration_manager_get_current (IdeConfigurationManager *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
+
+ if ((self->current == NULL) && (self->configurations->len > 0))
+ return g_ptr_array_index (self->configurations, 0);
+
+ return self->current;
+}
+
+static void
+ide_configuration_manager_changed (IdeConfigurationManager *self,
+ GParamSpec *pspec,
+ IdeConfiguration *configuration)
+{
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_assert (IDE_IS_CONFIGURATION (configuration));
+
+ self->change_count++;
+
+ ide_configuration_manager_queue_writeback (self);
+}
+
+void
+ide_configuration_manager_add (IdeConfigurationManager *self,
+ IdeConfiguration *configuration)
+{
+ guint position;
+
+ g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_return_if_fail (IDE_IS_CONFIGURATION (configuration));
+
+ g_signal_connect_object (configuration,
+ "changed",
+ G_CALLBACK (ide_configuration_manager_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ position = self->configurations->len;
+ g_ptr_array_add (self->configurations, g_object_ref (configuration));
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
+}
+
+void
+ide_configuration_manager_remove (IdeConfigurationManager *self,
+ IdeConfiguration *configuration)
+{
+ guint i;
+
+ g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
+ g_return_if_fail (IDE_IS_CONFIGURATION (configuration));
+
+ for (i = 0; i < self->configurations->len; i++)
+ {
+ IdeConfiguration *item = g_ptr_array_index (self->configurations, i);
+
+ if (item == configuration)
+ {
+ g_ptr_array_remove_index (self->configurations, i);
+ g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
+ if (self->configurations->len == 0)
+ ide_configuration_manager_add_default (self);
+ if (self->current == configuration)
+ ide_configuration_manager_set_current (self, NULL);
+ break;
+ }
+ }
+}
diff --git a/libide/ide-configuration-manager.h b/libide/ide-configuration-manager.h
new file mode 100644
index 0000000..efe8dbc
--- /dev/null
+++ b/libide/ide-configuration-manager.h
@@ -0,0 +1,53 @@
+/* ide-configuration-manager.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CONFIGURATION_MANAGER_H
+#define IDE_CONFIGURATION_MANAGER_H
+
+#include <gio/gio.h>
+
+#include "ide-object.h"
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CONFIGURATION_MANAGER (ide_configuration_manager_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeConfigurationManager, ide_configuration_manager, IDE, CONFIGURATION_MANAGER,
IdeObject)
+
+IdeConfiguration *ide_configuration_manager_get_current (IdeConfigurationManager *self);
+void ide_configuration_manager_set_current (IdeConfigurationManager *self,
+ IdeConfiguration *configuration);
+IdeConfiguration *ide_configuration_manager_get_configuration (IdeConfigurationManager *self,
+ const gchar *id);
+void ide_configuration_manager_add (IdeConfigurationManager *self,
+ IdeConfiguration *configuration);
+void ide_configuration_manager_remove (IdeConfigurationManager *self,
+ IdeConfiguration *configuration);
+void ide_configuration_manager_save_async (IdeConfigurationManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_configuration_manager_save_finish (IdeConfigurationManager *self,
+ GAsyncResult *result,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* IDE_CONFIGURATION_MANAGER_H */
diff --git a/libide/ide-configuration.c b/libide/ide-configuration.c
new file mode 100644
index 0000000..e6d9fcf
--- /dev/null
+++ b/libide/ide-configuration.c
@@ -0,0 +1,824 @@
+/* ide-configuration.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-configuration"
+
+#include <string.h>
+
+#include "ide-configuration.h"
+#include "ide-context.h"
+#include "ide-device.h"
+#include "ide-device-manager.h"
+#include "ide-environment.h"
+#include "ide-runtime.h"
+#include "ide-runtime-manager.h"
+
+struct _IdeConfiguration
+{
+ IdeObject parent_instance;
+
+ gchar *config_opts;
+ gchar *device_id;
+ gchar *display_name;
+ gchar *id;
+ gchar *prefix;
+ gchar *runtime_id;
+
+ IdeEnvironment *environment;
+
+ gint parallelism;
+
+ guint dirty : 1;
+ guint debug : 1;
+};
+
+G_DEFINE_TYPE (IdeConfiguration, ide_configuration, IDE_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_CONFIG_OPTS,
+ PROP_DEBUG,
+ PROP_DEVICE,
+ PROP_DEVICE_ID,
+ PROP_DIRTY,
+ PROP_DISPLAY_NAME,
+ PROP_ENVIRON,
+ PROP_ID,
+ PROP_PARALLELISM,
+ PROP_PREFIX,
+ PROP_RUNTIME,
+ PROP_RUNTIME_ID,
+ N_PROPS
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [LAST_SIGNAL];
+
+static void
+ide_configuration_set_id (IdeConfiguration *self,
+ const gchar *id)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (id != NULL);
+
+ if (g_strcmp0 (id, self->id) != 0)
+ {
+ g_free (self->id);
+ self->id = g_strdup (id);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
+ }
+}
+
+static void
+ide_configuration_device_manager_items_changed (IdeConfiguration *self,
+ guint position,
+ guint added,
+ guint removed,
+ IdeDeviceManager *device_manager)
+{
+ IdeDevice *device;
+
+ g_assert (IDE_IS_CONFIGURATION (self));
+ g_assert (IDE_IS_DEVICE_MANAGER (device_manager));
+
+ device = ide_device_manager_get_device (device_manager, self->device_id);
+
+ if (device != NULL)
+ ide_device_prepare_configuration (device, self);
+}
+
+static void
+ide_configuration_runtime_manager_items_changed (IdeConfiguration *self,
+ guint position,
+ guint added,
+ guint removed,
+ IdeRuntimeManager *runtime_manager)
+{
+ IdeRuntime *runtime;
+
+ g_assert (IDE_IS_CONFIGURATION (self));
+ g_assert (IDE_IS_RUNTIME_MANAGER (runtime_manager));
+
+ runtime = ide_runtime_manager_get_runtime (runtime_manager, self->runtime_id);
+
+ if (runtime != NULL)
+ ide_runtime_prepare_configuration (runtime, self);
+}
+
+static void
+ide_configuration_environment_changed (IdeConfiguration *self,
+ guint position,
+ guint added,
+ guint removed,
+ IdeEnvironment *environment)
+{
+ g_assert (IDE_IS_CONFIGURATION (self));
+ g_assert (IDE_IS_ENVIRONMENT (environment));
+
+ ide_configuration_set_dirty (self, TRUE);
+}
+
+static void
+ide_configuration_constructed (GObject *object)
+{
+ IdeConfiguration *self = (IdeConfiguration *)object;
+ IdeContext *context;
+ IdeDeviceManager *device_manager;
+ IdeRuntimeManager *runtime_manager;
+
+ G_OBJECT_CLASS (ide_configuration_parent_class)->constructed (object);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ device_manager = ide_context_get_device_manager (context);
+ runtime_manager = ide_context_get_runtime_manager (context);
+
+ g_signal_connect_object (device_manager,
+ "items-changed",
+ G_CALLBACK (ide_configuration_device_manager_items_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (runtime_manager,
+ "items-changed",
+ G_CALLBACK (ide_configuration_runtime_manager_items_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_configuration_device_manager_items_changed (self, 0, 0, 0, device_manager);
+ ide_configuration_runtime_manager_items_changed (self, 0, 0, 0, runtime_manager);
+}
+
+static void
+ide_configuration_finalize (GObject *object)
+{
+ IdeConfiguration *self = (IdeConfiguration *)object;
+
+ g_clear_object (&self->environment);
+
+ g_clear_pointer (&self->config_opts, g_free);
+ g_clear_pointer (&self->device_id, g_free);
+ g_clear_pointer (&self->display_name, g_free);
+ g_clear_pointer (&self->id, g_free);
+ g_clear_pointer (&self->prefix, g_free);
+ g_clear_pointer (&self->runtime_id, g_free);
+
+ G_OBJECT_CLASS (ide_configuration_parent_class)->finalize (object);
+}
+
+static void
+ide_configuration_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeConfiguration *self = IDE_CONFIGURATION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONFIG_OPTS:
+ g_value_set_string (value, ide_configuration_get_config_opts (self));
+ break;
+
+ case PROP_DEBUG:
+ g_value_set_boolean (value, ide_configuration_get_debug (self));
+ break;
+
+ case PROP_DEVICE:
+ g_value_set_object (value, ide_configuration_get_device (self));
+ break;
+
+ case PROP_DIRTY:
+ g_value_set_boolean (value, ide_configuration_get_dirty (self));
+ break;
+
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, ide_configuration_get_display_name (self));
+ break;
+
+ case PROP_ENVIRON:
+ g_value_set_boxed (value, ide_configuration_get_environ (self));
+ break;
+
+ case PROP_ID:
+ g_value_set_string (value, ide_configuration_get_id (self));
+ break;
+
+ case PROP_PARALLELISM:
+ g_value_set_int (value, ide_configuration_get_parallelism (self));
+ break;
+
+ case PROP_PREFIX:
+ g_value_set_string (value, ide_configuration_get_prefix (self));
+ break;
+
+ case PROP_RUNTIME:
+ g_value_set_object (value, ide_configuration_get_runtime (self));
+ break;
+
+ case PROP_RUNTIME_ID:
+ g_value_set_object (value, ide_configuration_get_runtime (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_configuration_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeConfiguration *self = IDE_CONFIGURATION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONFIG_OPTS:
+ ide_configuration_set_config_opts (self, g_value_get_string (value));
+ break;
+
+ case PROP_DEBUG:
+ ide_configuration_set_debug (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_DEVICE:
+ ide_configuration_set_device (self, g_value_get_object (value));
+ break;
+
+ case PROP_DEVICE_ID:
+ ide_configuration_set_device_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_DIRTY:
+ ide_configuration_set_dirty (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_DISPLAY_NAME:
+ ide_configuration_set_display_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_ID:
+ ide_configuration_set_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_PREFIX:
+ ide_configuration_set_prefix (self, g_value_get_string (value));
+ break;
+
+ case PROP_PARALLELISM:
+ ide_configuration_set_parallelism (self, g_value_get_int (value));
+ break;
+
+ case PROP_RUNTIME:
+ ide_configuration_set_runtime (self, g_value_get_object (value));
+ break;
+
+ case PROP_RUNTIME_ID:
+ ide_configuration_set_runtime_id (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_configuration_class_init (IdeConfigurationClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = ide_configuration_constructed;
+ object_class->finalize = ide_configuration_finalize;
+ object_class->get_property = ide_configuration_get_property;
+ object_class->set_property = ide_configuration_set_property;
+
+ properties [PROP_CONFIG_OPTS] =
+ g_param_spec_string ("config-opts",
+ "Config Options",
+ "Parameters to bootstrap the project",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DEBUG] =
+ g_param_spec_boolean ("debug",
+ "Debug",
+ "Debug",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DEVICE] =
+ g_param_spec_object ("device",
+ "Device",
+ "Device",
+ IDE_TYPE_DEVICE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DEVICE_ID] =
+ g_param_spec_string ("device-id",
+ "Device Id",
+ "The identifier of the device",
+ "local",
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DIRTY] =
+ g_param_spec_boolean ("dirty",
+ "Dirty",
+ "If the configuration has been changed.",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DISPLAY_NAME] =
+ g_param_spec_string ("display-name",
+ "Display Name",
+ "Display Name",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ENVIRON] =
+ g_param_spec_boxed ("environ",
+ "Environ",
+ "Environ",
+ G_TYPE_STRV,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ID] =
+ g_param_spec_string ("id",
+ "Id",
+ "Id",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PARALLELISM] =
+ g_param_spec_int ("parallelism",
+ "Parallelism",
+ "Parallelism",
+ -1,
+ G_MAXINT,
+ -1,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PREFIX] =
+ g_param_spec_string ("prefix",
+ "Prefix",
+ "Prefix",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RUNTIME] =
+ g_param_spec_object ("runtime",
+ "Runtime",
+ "Runtime",
+ IDE_TYPE_RUNTIME,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RUNTIME_ID] =
+ g_param_spec_string ("runtime-id",
+ "Runtime Id",
+ "The identifier of the runtime",
+ "host",
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+ide_configuration_init (IdeConfiguration *self)
+{
+ self->device_id = g_strdup ("local");
+ self->runtime_id = g_strdup ("host");
+ self->debug = TRUE;
+ self->environment = ide_environment_new ();
+
+ g_signal_connect_object (self->environment,
+ "items-changed",
+ G_CALLBACK (ide_configuration_environment_changed),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+IdeConfiguration *
+ide_configuration_new (IdeContext *context,
+ const gchar *id,
+ const gchar *device_id,
+ const gchar *runtime_id)
+{
+ g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+ g_return_val_if_fail (device_id != NULL, NULL);
+ g_return_val_if_fail (runtime_id != NULL, NULL);
+
+ return g_object_new (IDE_TYPE_CONFIGURATION,
+ "context", context,
+ "device-id", device_id,
+ "id", id,
+ "runtime-id", runtime_id,
+ NULL);
+}
+
+const gchar *
+ide_configuration_get_device_id (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->device_id;
+}
+
+void
+ide_configuration_set_device_id (IdeConfiguration *self,
+ const gchar *device_id)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (device_id != NULL);
+
+ if (g_strcmp0 (device_id, self->device_id) != 0)
+ {
+ IdeContext *context;
+ IdeDeviceManager *device_manager;
+
+ g_free (self->device_id);
+ self->device_id = g_strdup (device_id);
+
+ ide_configuration_set_dirty (self, TRUE);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEVICE_ID]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEVICE]);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ device_manager = ide_context_get_device_manager (context);
+ ide_configuration_device_manager_items_changed (self, 0, 0, 0, device_manager);
+ }
+}
+
+/**
+ * ide_configuration_get_device:
+ * @self: An #IdeConfiguration
+ *
+ * Gets the device for the configuration.
+ *
+ * Returns: (transfer none) (nullable): An #IdeDevice.
+ */
+IdeDevice *
+ide_configuration_get_device (IdeConfiguration *self)
+{
+ IdeDeviceManager *device_manager;
+ IdeContext *context;
+
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ device_manager = ide_context_get_device_manager (context);
+
+ return ide_device_manager_get_device (device_manager, self->device_id);
+}
+
+void
+ide_configuration_set_device (IdeConfiguration *self,
+ IdeDevice *device)
+{
+ const gchar *device_id = "local";
+
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (!device || IDE_IS_DEVICE (device));
+
+ if (device != NULL)
+ device_id = ide_device_get_id (device);
+
+ ide_configuration_set_device_id (self, device_id);
+}
+
+const gchar *
+ide_configuration_get_runtime_id (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->runtime_id;
+}
+
+void
+ide_configuration_set_runtime_id (IdeConfiguration *self,
+ const gchar *runtime_id)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (runtime_id != NULL);
+
+ if (g_strcmp0 (runtime_id, self->runtime_id) != 0)
+ {
+ IdeRuntimeManager *runtime_manager;
+ IdeContext *context;
+
+ g_free (self->runtime_id);
+ self->runtime_id = g_strdup (runtime_id);
+
+ ide_configuration_set_dirty (self, TRUE);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNTIME_ID]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNTIME]);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ runtime_manager = ide_context_get_runtime_manager (context);
+ ide_configuration_runtime_manager_items_changed (self, 0, 0, 0, runtime_manager);
+ }
+}
+
+/**
+ * ide_configuration_get_runtime:
+ * @self: An #IdeConfiguration
+ *
+ * Gets the runtime for the configuration.
+ *
+ * Returns: (transfer none) (nullable): An #IdeRuntime
+ */
+IdeRuntime *
+ide_configuration_get_runtime (IdeConfiguration *self)
+{
+ IdeRuntimeManager *runtime_manager;
+ IdeContext *context;
+
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ runtime_manager = ide_context_get_runtime_manager (context);
+
+ return ide_runtime_manager_get_runtime (runtime_manager, self->runtime_id);
+}
+
+void
+ide_configuration_set_runtime (IdeConfiguration *self,
+ IdeRuntime *runtime)
+{
+ const gchar *runtime_id = "host";
+
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (!runtime || IDE_IS_RUNTIME (runtime));
+
+ if (runtime != NULL)
+ runtime_id = ide_runtime_get_id (runtime);
+
+ ide_configuration_set_runtime_id (self, runtime_id);
+}
+
+/**
+ * ide_configuration_get_environ:
+ * @self: An #IdeConfiguration
+ *
+ * Gets the environment to use when spawning processes.
+ *
+ * Returns: (transfer full): An array of key=value environment variables.
+ */
+gchar **
+ide_configuration_get_environ (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return ide_environment_get_environ (self->environment);
+}
+
+const gchar *
+ide_configuration_getenv (IdeConfiguration *self,
+ const gchar *key)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ return ide_environment_getenv (self->environment, key);
+}
+
+void
+ide_configuration_setenv (IdeConfiguration *self,
+ const gchar *key,
+ const gchar *value)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (key != NULL);
+
+ ide_environment_setenv (self->environment, key, value);
+}
+
+const gchar *
+ide_configuration_get_id (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->id;
+}
+
+const gchar *
+ide_configuration_get_prefix (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->prefix;
+}
+
+void
+ide_configuration_set_prefix (IdeConfiguration *self,
+ const gchar *prefix)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+
+ if (g_strcmp0 (prefix, self->prefix) != 0)
+ {
+ g_free (self->prefix);
+ self->prefix = g_strdup (prefix);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PREFIX]);
+ ide_configuration_set_dirty (self, TRUE);
+ }
+}
+
+gint
+ide_configuration_get_parallelism (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), -1);
+
+ return self->parallelism;
+}
+
+void
+ide_configuration_set_parallelism (IdeConfiguration *self,
+ gint parallelism)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+ g_return_if_fail (parallelism >= -1);
+
+ if (parallelism != self->parallelism)
+ {
+ self->parallelism = parallelism;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PARALLELISM]);
+ }
+}
+
+gboolean
+ide_configuration_get_debug (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), FALSE);
+
+ return self->debug;
+}
+
+void
+ide_configuration_set_debug (IdeConfiguration *self,
+ gboolean debug)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+
+ debug = !!debug;
+
+ if (debug != self->debug)
+ {
+ self->debug = debug;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG]);
+ ide_configuration_set_dirty (self, TRUE);
+ }
+}
+
+const gchar *
+ide_configuration_get_display_name (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->display_name;
+}
+
+void
+ide_configuration_set_display_name (IdeConfiguration *self,
+ const gchar *display_name)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+
+ if (g_strcmp0 (display_name, self->display_name) != 0)
+ {
+ g_free (self->display_name);
+ self->display_name = g_strdup (display_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
+ }
+}
+
+gboolean
+ide_configuration_get_dirty (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), FALSE);
+
+ return self->dirty;
+}
+
+void
+ide_configuration_set_dirty (IdeConfiguration *self,
+ gboolean dirty)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+
+ dirty = !!dirty;
+
+ if (dirty != self->dirty)
+ {
+ self->dirty = dirty;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRTY]);
+ }
+
+ /*
+ * Always emit the changed signal so that the configuration manager
+ * can queue a writeback of the configuration.
+ */
+ g_signal_emit (self, signals [CHANGED], 0);
+}
+
+/**
+ * ide_configuration_get_environment:
+ *
+ * Returns: (transfer none): An #IdeEnvironment.
+ */
+IdeEnvironment *
+ide_configuration_get_environment (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->environment;
+}
+
+const gchar *
+ide_configuration_get_config_opts (IdeConfiguration *self)
+{
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ return self->config_opts;
+}
+
+void
+ide_configuration_set_config_opts (IdeConfiguration *self,
+ const gchar *config_opts)
+{
+ g_return_if_fail (IDE_IS_CONFIGURATION (self));
+
+ if (g_strcmp0 (config_opts, self->config_opts) != 0)
+ {
+ g_free (self->config_opts);
+ self->config_opts = g_strdup (config_opts);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONFIG_OPTS]);
+ ide_configuration_set_dirty (self, TRUE);
+ }
+}
+
+/**
+ * ide_configuration_duplicate:
+ * @self: An #IdeConfiguration
+ *
+ * Copies the configuration into a new configuration.
+ *
+ * Returns: (transfer full): An #IdeConfiguration.
+ */
+IdeConfiguration *
+ide_configuration_duplicate (IdeConfiguration *self)
+{
+ static gint next_counter = 2;
+ IdeConfiguration *copy;
+ IdeContext *context;
+ g_autofree gchar *id = NULL;
+ g_autofree gchar *name = NULL;
+
+ g_return_val_if_fail (IDE_IS_CONFIGURATION (self), NULL);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ id = g_strdup_printf ("%s %d", self->id, next_counter++);
+ name = g_strdup_printf ("%s Copy", self->display_name);
+
+ copy = g_object_new (IDE_TYPE_CONFIGURATION,
+ "config-opts", self->config_opts,
+ "context", context,
+ "device-id", self->device_id,
+ "display-name", name,
+ "id", id,
+ "prefix", self->prefix,
+ "runtime-id", self->runtime_id,
+ NULL);
+
+ copy->environment = ide_environment_copy (self->environment);
+
+ return copy;
+}
diff --git a/libide/ide-configuration.h b/libide/ide-configuration.h
new file mode 100644
index 0000000..e4d0596
--- /dev/null
+++ b/libide/ide-configuration.h
@@ -0,0 +1,79 @@
+/* ide-configuration.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CONFIGURATION_H
+#define IDE_CONFIGURATION_H
+
+#include <gio/gio.h>
+
+#include "ide-object.h"
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CONFIGURATION (ide_configuration_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeConfiguration, ide_configuration, IDE, CONFIGURATION, IdeObject)
+
+IdeConfiguration *ide_configuration_new (IdeContext *context,
+ const gchar *id,
+ const gchar *device_id,
+ const gchar *runtime_id);
+const gchar *ide_configuration_get_id (IdeConfiguration *self);
+const gchar *ide_configuration_get_runtime_id (IdeConfiguration *self);
+void ide_configuration_set_runtime_id (IdeConfiguration *self,
+ const gchar *runtime_id);
+const gchar *ide_configuration_get_device_id (IdeConfiguration *self);
+void ide_configuration_set_device_id (IdeConfiguration *self,
+ const gchar *device_id);
+IdeDevice *ide_configuration_get_device (IdeConfiguration *self);
+void ide_configuration_set_device (IdeConfiguration *self,
+ IdeDevice *device);
+gboolean ide_configuration_get_dirty (IdeConfiguration *self);
+void ide_configuration_set_dirty (IdeConfiguration *self,
+ gboolean dirty);
+const gchar *ide_configuration_get_display_name (IdeConfiguration *self);
+void ide_configuration_set_display_name (IdeConfiguration *self,
+ const gchar *display_name);
+IdeRuntime *ide_configuration_get_runtime (IdeConfiguration *self);
+void ide_configuration_set_runtime (IdeConfiguration *self,
+ IdeRuntime *runtime);
+gchar **ide_configuration_get_environ (IdeConfiguration *self);
+const gchar *ide_configuration_getenv (IdeConfiguration *self,
+ const gchar *key);
+void ide_configuration_setenv (IdeConfiguration *self,
+ const gchar *key,
+ const gchar *value);
+gboolean ide_configuration_get_debug (IdeConfiguration *self);
+void ide_configuration_set_debug (IdeConfiguration *self,
+ gboolean debug);
+const gchar *ide_configuration_get_prefix (IdeConfiguration *self);
+void ide_configuration_set_prefix (IdeConfiguration *self,
+ const gchar *prefix);
+const gchar *ide_configuration_get_config_opts (IdeConfiguration *self);
+void ide_configuration_set_config_opts (IdeConfiguration *self,
+ const gchar *config_opts);
+gint ide_configuration_get_parallelism (IdeConfiguration *self);
+void ide_configuration_set_parallelism (IdeConfiguration *self,
+ gint parallelism);
+IdeEnvironment *ide_configuration_get_environment (IdeConfiguration *self);
+IdeConfiguration *ide_configuration_duplicate (IdeConfiguration *self);
+
+G_END_DECLS
+
+#endif /* IDE_CONFIGURATION_H */
diff --git a/libide/ide-context.c b/libide/ide-context.c
index 5196892..b6c5edd 100644
--- a/libide/ide-context.c
+++ b/libide/ide-context.c
@@ -27,6 +27,7 @@
#include "ide-buffer-manager.h"
#include "ide-buffer.h"
#include "ide-build-system.h"
+#include "ide-configuration-manager.h"
#include "ide-context.h"
#include "ide-debug.h"
#include "ide-device-manager.h"
@@ -57,6 +58,7 @@ struct _IdeContext
IdeBackForwardList *back_forward_list;
IdeBufferManager *buffer_manager;
IdeBuildSystem *build_system;
+ IdeConfigurationManager *configuration_manager;
IdeDeviceManager *device_manager;
IdeDoap *doap;
GtkRecentManager *recent_manager;
@@ -91,6 +93,7 @@ enum {
PROP_BACK_FORWARD_LIST,
PROP_BUFFER_MANAGER,
PROP_BUILD_SYSTEM,
+ PROP_CONFIGURATION_MANAGER,
PROP_DEVICE_MANAGER,
PROP_PROJECT_FILE,
PROP_PROJECT,
@@ -531,6 +534,7 @@ ide_context_finalize (GObject *object)
g_clear_pointer (&self->recent_projects_path, g_free);
g_clear_object (&self->build_system);
+ g_clear_object (&self->configuration_manager);
g_clear_object (&self->device_manager);
g_clear_object (&self->doap);
g_clear_object (&self->project);
@@ -570,6 +574,10 @@ ide_context_get_property (GObject *object,
g_value_set_object (value, ide_context_get_build_system (self));
break;
+ case PROP_CONFIGURATION_MANAGER:
+ g_value_set_object (value, ide_context_get_configuration_manager (self));
+ break;
+
case PROP_DEVICE_MANAGER:
g_value_set_object (value, ide_context_get_device_manager (self));
break;
@@ -665,6 +673,13 @@ ide_context_class_init (IdeContextClass *klass)
IDE_TYPE_BUILD_SYSTEM,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ properties [PROP_CONFIGURATION_MANAGER] =
+ g_param_spec_object ("configuration-manager",
+ "Configuration Manager",
+ "The configuration manager for the context",
+ IDE_TYPE_CONFIGURATION_MANAGER,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
properties [PROP_DEVICE_MANAGER] =
g_param_spec_object ("device-manager",
"Device Manager",
@@ -782,6 +797,10 @@ ide_context_init (IdeContext *self)
"context", self,
NULL);
+ self->configuration_manager = g_object_new (IDE_TYPE_CONFIGURATION_MANAGER,
+ "context", self,
+ NULL);
+
self->project = g_object_new (IDE_TYPE_PROJECT,
"context", self,
NULL);
@@ -1338,6 +1357,44 @@ ide_context_init_search_engine (gpointer source_object,
}
static void
+ide_context_init_configuration_manager_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAsyncInitable *initable = (GAsyncInitable *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (G_IS_ASYNC_INITABLE (initable));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!g_async_initable_init_finish (initable, result, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_configuration_manager (gpointer source_object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeContext *self = source_object;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (IDE_IS_CONTEXT (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_async_initable_init_async (G_ASYNC_INITABLE (self->configuration_manager),
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ ide_context_init_configuration_manager_cb,
+ g_object_ref (task));
+}
+
+static void
ide_context_init_loaded (gpointer source_object,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -1381,6 +1438,7 @@ ide_context_init_async (GAsyncInitable *initable,
ide_context_init_unsaved_files,
ide_context_init_add_recent,
ide_context_init_search_engine,
+ ide_context_init_configuration_manager,
ide_context_init_loaded,
NULL);
}
@@ -1501,6 +1559,50 @@ ide_context_unload_buffer_manager (gpointer source_object,
}
static void
+ide_context_unload__configuration_manager_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeConfigurationManager *manager = (IdeConfigurationManager *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (manager));
+ g_assert (G_IS_TASK (task));
+
+ /* unfortunate if this happens, but not much we can do */
+ if (!ide_configuration_manager_save_finish (manager, result, &error))
+ g_warning ("%s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_unload_configuration_manager (gpointer source_object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeContext *self = source_object;
+ g_autoptr(GTask) task = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_CONTEXT (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (IDE_IS_CONFIGURATION_MANAGER (self->configuration_manager));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ide_configuration_manager_save_async (self->configuration_manager,
+ cancellable,
+ ide_context_unload__configuration_manager_save_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+static void
ide_context_unload__back_forward_list_save_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
@@ -1618,10 +1720,13 @@ ide_context_do_unload_locked (IdeContext *self)
task = self->delayed_unload_task;
self->delayed_unload_task = NULL;
+ g_clear_object (&self->device_manager);
+
ide_async_helper_run (self,
g_task_get_cancellable (task),
ide_context_unload_cb,
g_object_ref (task),
+ ide_context_unload_configuration_manager,
ide_context_unload_back_forward_list,
ide_context_unload_buffer_manager,
ide_context_unload_unsaved_files,
diff --git a/libide/ide-context.h b/libide/ide-context.h
index ef249ee..0742e48 100644
--- a/libide/ide-context.h
+++ b/libide/ide-context.h
@@ -74,6 +74,7 @@ void ide_context_hold (IdeContext
void ide_context_hold_for_object (IdeContext *self,
gpointer instance);
void ide_context_release (IdeContext *self);
+IdeConfigurationManager *ide_context_get_configuration_manager (IdeContext *self);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]