Commit 727b747e authored by Matthew Leeds's avatar Matthew Leeds
Browse files

config-manager: Use IdeConfigurationProvider interface

This changes IdeConfigurationManager to load configurations via the
IdeConfigurationProvider interface (which can be implemented by any
plugin) rather than checking for a .buildconfig file. The
IdeBuildconfigConfigurationProvider will provides backwards
compatibility for that functionality.

https://bugzilla.gnome.org/show_bug.cgi?id=777959
parent 0978331a
......@@ -25,12 +25,13 @@ other high-level operations.
## ide-configuration-manager.c
Manages all configurations for the project, and reads/writes the .buildconfig
file.
Manages all configurations for the project, which can be provided by plugins
that implement the IdeConfigurationProvider interface.
## ide-configuration.c
An individual configuration that was loaded/persisted to the .buildconfig file.
An individual configuration that has information about how the project should
be built, such as what options to pass to configure and which runtime to use.
## ide-configuration-provider.*
......
......@@ -19,6 +19,7 @@
#define G_LOG_DOMAIN "ide-configuration-manager"
#include <glib/gi18n.h>
#include <libpeas/peas.h>
#include "ide-context.h"
#include "ide-debug.h"
......@@ -27,11 +28,7 @@
#include "buildsystem/ide-configuration-manager.h"
#include "buildsystem/ide-configuration.h"
#include "buildsystem/ide-environment.h"
#include "vcs/ide-vcs.h"
#define DOT_BUILD_CONFIG ".buildconfig"
#define WRITEBACK_TIMEOUT_SECS 2
#include "buildsystem/ide-configuration-provider.h"
struct _IdeConfigurationManager
{
......@@ -39,10 +36,7 @@ struct _IdeConfigurationManager
GPtrArray *configurations;
IdeConfiguration *current;
GKeyFile *key_file;
gulong writeback_handler;
guint change_count;
PeasExtensionSet *extensions;
};
static void async_initable_iface_init (GAsyncInitableIface *iface);
......@@ -68,161 +62,58 @@ static GParamSpec *properties [LAST_PROP];
static guint signals [N_SIGNALS];
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)
ide_configuration_manager_add_default (IdeConfigurationManager *self)
{
g_autoptr(IdeConfiguration) configuration = NULL;
g_autofree gchar *env_group = NULL;
g_autoptr(IdeConfiguration) config = 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");
load_string (configuration, key_file, group, "app-id", "app-id");
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);
config = ide_configuration_new (context, "default", "local", "host");
ide_configuration_set_display_name (config, _("Default"));
ide_configuration_manager_add (self, config);
return TRUE;
if (self->configurations->len == 1)
ide_configuration_manager_set_current (self, config);
}
static gboolean
ide_configuration_manager_restore (IdeConfigurationManager *self,
GFile *file,
GCancellable *cancellable,
GError **error)
static void
ide_configuration_manager_save_provider_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
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;
IdeConfigurationProvider *provider = (IdeConfigurationProvider *)object;
g_autoptr(GTask) task = user_data;
GError *error = NULL;
if (!ide_configuration_manager_load (self, self->key_file, groups [i], error))
IDE_RETURN (FALSE);
}
g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
g_assert (G_IS_TASK (task));
IDE_RETURN (TRUE);
if (!ide_configuration_provider_save_finish (provider, result, &error))
g_task_return_error (task, error);
}
static void
ide_configuration_manager_save_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
ide_configuration_manager_save_provider (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
GError *error = NULL;
GFile *file = (GFile *)object;
IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;
g_assert (G_IS_FILE (file));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
g_assert (G_IS_TASK (task));
if (!g_file_replace_contents_finish (file, result, NULL, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
if (!g_task_had_error (task))
ide_configuration_provider_save_async (provider,
g_task_get_cancellable (task),
ide_configuration_manager_save_provider_cb,
g_object_ref (task));
}
void
......@@ -231,18 +122,7 @@ ide_configuration_manager_save_async (IdeConfigurationManager *self,
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;
......@@ -251,136 +131,11 @@ ide_configuration_manager_save_async (IdeConfigurationManager *self,
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);
PERSIST_STRING_KEY ("app-id", get_app_id);
#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));
peas_extension_set_foreach (self->extensions,
ide_configuration_manager_save_provider,
g_object_ref (task));
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));
g_task_return_boolean (task, TRUE);
IDE_EXIT;
}
......@@ -396,55 +151,6 @@ ide_configuration_manager_save_finish (IdeConfigurationManager *self,
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));
IDE_ENTRY;
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);
IDE_EXIT;
}
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"));
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
......@@ -489,9 +195,7 @@ 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);
if (self->current != NULL)
{
......@@ -629,6 +333,38 @@ list_model_iface_init (GListModelInterface *iface)
iface->get_item = ide_configuration_manager_get_item;
}
static void
ide_configuration_manager_extension_added (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
IdeConfigurationManager *self = user_data;
IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
ide_configuration_provider_load (provider, self);
}
static void
ide_configuration_manager_extension_removed (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
IdeConfigurationManager *self = user_data;
IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
ide_configuration_provider_unload (provider, self);
}
static void
ide_configuration_manager_init_worker (GTask *task,
gpointer source_object,
......@@ -639,26 +375,33 @@ ide_configuration_manager_init_worker (GTask *task,
g_autoptr(GFile) settings_file = NULL;
g_autoptr(GError) error = NULL;
IdeContext *context;
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);
g_assert (IDE_IS_CONTEXT (context));
if (!g_file_query_exists (settings_file, cancellable) ||
!ide_configuration_manager_restore (self, settings_file, cancellable, &error))
{
if (error != NULL)
g_warning ("Failed to restore configuration: %s", error->message);
self->extensions = peas_extension_set_new (peas_engine_get_default (),
IDE_TYPE_CONFIGURATION_PROVIDER,
NULL);
ide_configuration_manager_add_default (self);
}
g_signal_connect (self->extensions,
"extension-added",
G_CALLBACK (ide_configuration_manager_extension_added),
self);
g_signal_connect (self->extensions,
"extension-removed",
G_CALLBACK (ide_configuration_manager_extension_removed),
self);
peas_extension_set_foreach (self->extensions,
ide_configuration_manager_extension_added,
self);
ide_configuration_manager_add_default (self);
g_task_return_boolean (task, TRUE);
}
......@@ -779,15 +522,15 @@ ide_configuration_manager_add (IdeConfigurationManager *self,
g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
g_return_if_fail (IDE_IS_CONFIGURATION (configuration));
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);
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
......@@ -805,6 +548,9 @@ ide_configuration_manager_remove (IdeConfigurationManager *self,
if (item == configuration)
{
g_signal_handlers_disconnect_by_func (configuration,
G_CALLBACK (ide_configuration_manager_changed),
self);
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)
......
......@@ -47,7 +47,6 @@ gboolean ide_configuration_manager_save_finish (IdeConfigurationM
GAsyncResult *result,
GError **error);
G_END_DECLS
#endif /* IDE_CONFIGURATION_MANAGER_H */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment