[gnome-builder/wip/tintou/sysroot: 14/20] sysroot: add a new plugin allowing to specify the sysroot



commit c39addc7a47582f9a13359516bb762e00aaf8f36
Author: Corentin Noël <corentin noel collabora co uk>
Date:   Thu Feb 15 11:39:08 2018 +0000

    sysroot: add a new plugin allowing to specify the sysroot
    
    A developer might want to specify the sysroot (the path to another system root where all the libraries 
will be used).
    This is notably used when compiling for a foreign architecture or system.
    
    This is ensured to work with the default GNOME project generated by GNOME Builder.
    
    It is currently possible to specify the sysroot path and give extra arguments to Pkg-Config as
    the differents multiarch implementations are difficult to handle.

 meson_options.txt                                  |   1 +
 src/plugins/meson.build                            |   2 +
 src/plugins/sysroot/ide-host-subprocess-launcher.c |  96 +++++++++
 src/plugins/sysroot/ide-host-subprocess-launcher.h |  32 +++
 src/plugins/sysroot/ide-sysroot-manager.c          | 232 +++++++++++++++++++++
 src/plugins/sysroot/ide-sysroot-manager.h          |  56 +++++
 .../sysroot/ide-sysroot-preferences-addin.c        | 201 ++++++++++++++++++
 .../sysroot/ide-sysroot-preferences-addin.h        |  30 +++
 src/plugins/sysroot/ide-sysroot-preferences-row.c  | 230 ++++++++++++++++++++
 src/plugins/sysroot/ide-sysroot-preferences-row.h  |  32 +++
 src/plugins/sysroot/ide-sysroot-preferences-row.ui | 123 +++++++++++
 src/plugins/sysroot/ide-sysroot-runtime-provider.c | 171 +++++++++++++++
 src/plugins/sysroot/ide-sysroot-runtime-provider.h |  30 +++
 src/plugins/sysroot/ide-sysroot-runtime.c          | 187 +++++++++++++++++
 src/plugins/sysroot/ide-sysroot-runtime.h          |  33 +++
 src/plugins/sysroot/meson.build                    |  28 +++
 src/plugins/sysroot/sysroot-plugin.c               |  30 +++
 src/plugins/sysroot/sysroot.gresource.xml          |   9 +
 src/plugins/sysroot/sysroot.plugin                 |   8 +
 19 files changed, 1531 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index aa0e00f5c..03e386b06 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -71,6 +71,7 @@ option('with_support', type: 'boolean')
 option('with_symbol_tree', type: 'boolean')
 option('with_sysmon', type: 'boolean')
 option('with_sysprof', type: 'boolean')
+option('with_sysroot', type: 'boolean')
 option('with_todo', type: 'boolean')
 option('with_vala_pack', type: 'boolean')
 option('with_valgrind', type: 'boolean')
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index d97d7e373..b37ea3fac 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -65,6 +65,7 @@ subdir('support')
 subdir('symbol-tree')
 subdir('sysmon')
 subdir('sysprof')
+subdir('sysroot')
 subdir('terminal')
 subdir('todo')
 subdir('vala-pack')
@@ -141,6 +142,7 @@ status += [
   'Symbol Tree ........... : @0@'.format(get_option('with_symbol_tree')),
   'System Monitor ........ : @0@'.format(get_option('with_sysmon')),
   'Sysprof Profiler ...... : @0@'.format(get_option('with_sysprof')),
+  'Sysroot ......          : @0@'.format(get_option('with_sysroot')),
   'Todo .................. : @0@'.format(get_option('with_todo')),
   'Vala Language Pack .... : @0@'.format(get_option('with_vala_pack')),
   'Valgrind .............. : @0@'.format(get_option('with_valgrind')),
diff --git a/src/plugins/sysroot/ide-host-subprocess-launcher.c 
b/src/plugins/sysroot/ide-host-subprocess-launcher.c
new file mode 100644
index 000000000..d9d9f371b
--- /dev/null
+++ b/src/plugins/sysroot/ide-host-subprocess-launcher.c
@@ -0,0 +1,96 @@
+/* ide-host-subprocess-launcher.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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-host-subprocess-launcher"
+
+#include <glib/gi18n.h>
+
+#include "ide-host-subprocess-launcher.h"
+
+struct _IdeHostSubprocessLauncher
+{
+  IdeSubprocessLauncher parent_instance;
+};
+
+G_DEFINE_TYPE (IdeHostSubprocessLauncher,
+               ide_host_subprocess_launcher,
+               IDE_TYPE_SUBPROCESS_LAUNCHER)
+
+IdeHostSubprocessLauncher *
+ide_host_subprocess_launcher_new (GSubprocessFlags flags)
+{
+  return g_object_new (IDE_TYPE_HOST_SUBPROCESS_LAUNCHER,
+                       "flags", flags,
+                       NULL);
+}
+
+static IdeSubprocess *
+ide_hostsubprocess_launcher_spawn (IdeSubprocessLauncher  *self,
+                                   GCancellable           *cancellable,
+                                   GError                **error)
+{
+  gchar *argv = NULL;
+  const gchar * const *args = NULL;
+  const gchar * const *environ = NULL;
+  GString *cmd = NULL;
+
+  g_assert (IDE_IS_SUBPROCESS_LAUNCHER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  // don't prepend `sh -c` twice
+  args = ide_subprocess_launcher_get_argv (self);
+  if (g_strv_length ((gchar **)args) >= 2)
+    {
+      if (g_strcmp0 (args[0], "sh") == 0 && g_strcmp0 (args[1], "-c") == 0)
+        {
+          return IDE_SUBPROCESS_LAUNCHER_CLASS (ide_host_subprocess_launcher_parent_class)->spawn (self, 
cancellable, error);
+        }
+    }
+
+  argv = ide_subprocess_launcher_pop_argv (self);
+  cmd = g_string_new (argv);
+  g_free (argv);
+
+  while ((argv = ide_subprocess_launcher_pop_argv (self)) != NULL)
+    {
+      g_string_prepend (cmd, " ");
+      g_string_prepend (cmd, argv);
+      g_free (argv);
+    }
+
+  ide_subprocess_launcher_push_argv (self, "sh");
+  ide_subprocess_launcher_push_argv (self, "-c");
+  ide_subprocess_launcher_push_argv (self, g_string_free (cmd, FALSE));
+
+  return IDE_SUBPROCESS_LAUNCHER_CLASS (ide_host_subprocess_launcher_parent_class)->spawn (self, 
cancellable, error);
+}
+
+static void
+ide_host_subprocess_launcher_class_init (IdeHostSubprocessLauncherClass *klass)
+{
+  IdeSubprocessLauncherClass *subprocess_launcher_class = IDE_SUBPROCESS_LAUNCHER_CLASS (klass);
+
+  subprocess_launcher_class->spawn = ide_hostsubprocess_launcher_spawn;
+}
+
+static void
+ide_host_subprocess_launcher_init (IdeHostSubprocessLauncher *self)
+{
+  
+}
diff --git a/src/plugins/sysroot/ide-host-subprocess-launcher.h 
b/src/plugins/sysroot/ide-host-subprocess-launcher.h
new file mode 100644
index 000000000..e5f0f8f7f
--- /dev/null
+++ b/src/plugins/sysroot/ide-host-subprocess-launcher.h
@@ -0,0 +1,32 @@
+/* ide-host-subprocess-launcher.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_HOST_SUBPROCESS_LAUNCHER (ide_host_subprocess_launcher_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeHostSubprocessLauncher, ide_host_subprocess_launcher, IDE, 
HOST_SUBPROCESS_LAUNCHER, IdeSubprocessLauncher)
+
+IdeHostSubprocessLauncher *ide_host_subprocess_launcher_new (GSubprocessFlags flags);
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/ide-sysroot-manager.c b/src/plugins/sysroot/ide-sysroot-manager.c
new file mode 100644
index 000000000..a0fbc6b72
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-manager.c
@@ -0,0 +1,232 @@
+/* ide-sysroot-manager.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#include "ide-sysroot-manager.h"
+
+struct _IdeSysrootManager
+{
+  GObject parent_instance;
+  GKeyFile *key_file;
+};
+
+G_DEFINE_TYPE (IdeSysrootManager, ide_sysroot_manager, G_TYPE_OBJECT)
+
+enum {
+  TARGET_MODIFIED,
+  TARGET_NAME_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL];
+
+static IdeSysrootManager *instance;
+
+gchar *
+sysroot_manager_get_path (void)
+{
+  gchar *directory_path = NULL;
+  gchar *conf_file = NULL;
+
+  directory_path = g_build_filename (g_get_user_config_dir (),
+                                     ide_get_program_name (),
+                                     "sysroot",
+                                     NULL);
+
+  g_mkdir_with_parents (directory_path, 0750);
+  conf_file = g_build_filename (directory_path, "general.conf", NULL);
+  g_free (directory_path);
+  return conf_file;
+}
+
+static void
+sysroot_manager_save (IdeSysrootManager *self)
+{
+  gchar *conf_file = NULL;
+  GError *error = NULL;
+  conf_file = sysroot_manager_get_path ();
+
+  if (!g_key_file_save_to_file (self->key_file, conf_file, &error))
+    {
+      g_critical ("Error loading the sysroot configuration: %s", error->message);
+      g_error_free (error);
+    }
+
+  g_free (conf_file);
+}
+
+IdeSysrootManager *
+ide_sysroot_manager_get_default (void)
+{
+  if (instance == NULL)
+    {
+      instance = g_object_new (IDE_TYPE_SYSROOT_MANAGER, NULL);
+    }
+
+  return instance;
+}
+
+gchar *
+ide_sysroot_manager_create_target (IdeSysrootManager *self)
+{
+  for (guint i = 0; i < UINT_MAX; i++)
+    {
+      gchar * result;
+      GString *sysroot_name = g_string_new (NULL);
+      g_string_printf (sysroot_name, "Sysroot %u", i);
+      result = g_string_free (sysroot_name, FALSE);
+      if (!g_key_file_has_group (self->key_file, result))
+        {
+          g_key_file_set_string (self->key_file, result, "Name", result);
+          g_key_file_set_string (self->key_file, result, "Path", "/");
+          sysroot_manager_save (self);
+          g_signal_emit (self, signals[TARGET_MODIFIED], 0, result, IDE_SYSROOT_MANAGER_TARGET_CREATED);
+          return result;
+        }
+    }
+
+  return NULL;
+}
+
+void
+ide_sysroot_manager_remove_target (IdeSysrootManager *self, const char *target)
+{
+  GError *error = NULL;
+  g_key_file_remove_group (self->key_file, target, &error);
+  if (error)
+    {
+      g_critical ("Error removing target \"%s\": %s", target, error->message);
+      g_error_free (error);
+    }
+
+  g_signal_emit (self, signals[TARGET_MODIFIED], 0, target, IDE_SYSROOT_MANAGER_TARGET_REMOVED);
+  sysroot_manager_save (self);
+}
+
+void
+ide_sysroot_manager_set_target_name (IdeSysrootManager *self, const char *target, const char *name)
+{
+  g_key_file_set_string (self->key_file, target, "Name", name);
+  g_signal_emit (self, signals[TARGET_MODIFIED], 0, target, IDE_SYSROOT_MANAGER_TARGET_CHANGED);
+  g_signal_emit (self, signals[TARGET_NAME_CHANGED], 0, target, name);
+  sysroot_manager_save (self);
+}
+
+gchar *
+ide_sysroot_manager_get_target_name (IdeSysrootManager *self, const char *target)
+{
+  return g_key_file_get_string (self->key_file, target, "Name", NULL);
+}
+
+void
+ide_sysroot_manager_set_target_path (IdeSysrootManager *self, const char *target, const char *path)
+{
+  g_key_file_set_string (self->key_file, target, "Path", path);
+  g_signal_emit (self, signals[TARGET_MODIFIED], 0, target, IDE_SYSROOT_MANAGER_TARGET_CHANGED);
+  sysroot_manager_save (self);
+}
+
+gchar *
+ide_sysroot_manager_get_target_path (IdeSysrootManager *self, const char *target)
+{
+  return g_key_file_get_string (self->key_file, target, "Path", NULL);
+}
+
+void
+ide_sysroot_manager_set_target_pkg_config_path (IdeSysrootManager *self, const char *target, const char 
*path)
+{
+  g_key_file_set_string (self->key_file, target, "PkgConfigPath", path);
+  g_signal_emit (self, signals[TARGET_MODIFIED], 0, target, IDE_SYSROOT_MANAGER_TARGET_CHANGED);
+  sysroot_manager_save (self);
+}
+
+gchar *
+ide_sysroot_manager_get_target_pkg_config_path (IdeSysrootManager *self, const char *target)
+{
+  return g_key_file_get_string (self->key_file, target, "PkgConfigPath", NULL);
+}
+
+GArray *
+ide_sysroot_manager_list (IdeSysrootManager *self)
+{
+  GArray *list = NULL;
+  gchar **groups = NULL;
+  gsize groups_length = 0;
+
+  list = g_array_new (FALSE, FALSE, sizeof (char*));
+  groups = g_key_file_get_groups (self->key_file, &groups_length);
+  g_array_append_vals (list, groups, groups_length);
+
+  return list;
+}
+
+void
+ide_sysroot_manager_finalize (GObject *object)
+{
+  IdeSysrootManager *self = IDE_SYSROOT_MANAGER(object);
+
+  g_clear_pointer (&self->key_file, g_key_file_free);
+
+  G_OBJECT_CLASS (ide_sysroot_manager_parent_class)->finalize (object);
+}
+
+void
+ide_sysroot_manager_class_init (IdeSysrootManagerClass *klass)
+{
+  G_OBJECT_CLASS (klass)->finalize = ide_sysroot_manager_finalize;
+
+  signals [TARGET_MODIFIED] =
+    g_signal_new_class_handler ("target-changed",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_FIRST,
+                                NULL, NULL, NULL, NULL,
+                                G_TYPE_NONE,
+                                2,
+                                G_TYPE_STRING,
+                                G_TYPE_INT);
+
+  signals [TARGET_NAME_CHANGED] =
+    g_signal_new_class_handler ("target-name-changed",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_FIRST,
+                                NULL, NULL, NULL, NULL,
+                                G_TYPE_NONE,
+                                2,
+                                G_TYPE_STRING,
+                                G_TYPE_STRING);
+}
+
+static void
+ide_sysroot_manager_init (IdeSysrootManager *self)
+{
+  gchar *conf_file = NULL;
+  GError *error = NULL;
+
+  conf_file = sysroot_manager_get_path ();
+  self->key_file = g_key_file_new ();
+  g_key_file_load_from_file (self->key_file, conf_file, G_KEY_FILE_KEEP_COMMENTS, &error);
+  if (error)
+    {
+      if (error->code != G_FILE_ERROR_NOENT)
+        {
+          g_critical ("Error loading the sysroot configuration: %s", error->message);
+        }
+
+      g_error_free (error);
+    }
+}
diff --git a/src/plugins/sysroot/ide-sysroot-manager.h b/src/plugins/sysroot/ide-sysroot-manager.h
new file mode 100644
index 000000000..c1db3b011
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-manager.h
@@ -0,0 +1,56 @@
+/* ide-sysroot-manager.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYSROOT_MANAGER (ide_sysroot_manager_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSysrootManager, ide_sysroot_manager, IDE, SYSROOT_MANAGER, GObject)
+
+typedef enum {
+  IDE_SYSROOT_MANAGER_TARGET_CHANGED,
+  IDE_SYSROOT_MANAGER_TARGET_CREATED,
+  IDE_SYSROOT_MANAGER_TARGET_REMOVED
+} IdeSysrootManagerTargetModificationType;
+
+IdeSysrootManager *ide_sysroot_manager_get_default (void);
+
+gchar *ide_sysroot_manager_create_target (IdeSysrootManager *self);
+
+void ide_sysroot_manager_remove_target (IdeSysrootManager *self, const char *target);
+
+void ide_sysroot_manager_set_target_name (IdeSysrootManager *self, const char *target, const char *path);
+
+gchar *ide_sysroot_manager_get_target_name (IdeSysrootManager *self, const char *target);
+
+void ide_sysroot_manager_set_target_path (IdeSysrootManager *self, const char *target, const char *path);
+
+gchar *ide_sysroot_manager_get_target_path (IdeSysrootManager *self, const char *target);
+
+void ide_sysroot_manager_set_target_pkg_config_path (IdeSysrootManager *self, const char *target, const char 
*path);
+
+gchar *ide_sysroot_manager_get_target_pkg_config_path (IdeSysrootManager *self, const char *target);
+
+GArray *ide_sysroot_manager_list (IdeSysrootManager *self);
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/ide-sysroot-preferences-addin.c 
b/src/plugins/sysroot/ide-sysroot-preferences-addin.c
new file mode 100644
index 000000000..44e03efc5
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-preferences-addin.c
@@ -0,0 +1,201 @@
+/* ide-sysroot-preferences.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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, eitIher 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-sysroot-preferences-addin"
+
+#include <glib/gi18n.h>
+
+#include "ide-sysroot-preferences-addin.h"
+#include "ide-sysroot-preferences-row.h"
+#include "ide-sysroot-manager.h"
+
+struct _IdeSysrootPreferencesAddin
+{
+  GObject         parent_instance;
+
+  GArray         *ids;
+  DzlPreferences *preferences;
+};
+
+static void sysroot_preferences_add_new (IdeSysrootPreferencesAddin *self,
+                                         GtkWidget *emitter)
+{
+  GtkWidget *pref_row = NULL;
+  guint id = 0;
+  gchar *new_target;
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES_BIN (emitter));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  new_target = ide_sysroot_manager_create_target (sysroot_manager);
+  pref_row = g_object_new (IDE_TYPE_SYSROOT_PREFERENCES_ROW,
+                           "visible", TRUE,
+                           "sysroot-id", new_target,
+                           NULL);
+
+  id = dzl_preferences_add_custom (self->preferences, "sdk", "sysroot", pref_row, "", 1);
+  g_array_append_val (self->ids, id);
+
+  ide_sysroot_preferences_row_show_popup (IDE_SYSROOT_PREFERENCES_ROW (pref_row));
+}
+
+GtkWidget *
+sysroot_preferences_get_add_widget (IdeSysrootPreferencesAddin *self)
+{
+  GtkWidget *bin = NULL;
+  GtkWidget *grid = NULL;
+  GtkWidget *label = NULL;
+  GtkWidget *subtitle = NULL;
+  GtkWidget *image = NULL;
+
+  bin = g_object_new (DZL_TYPE_PREFERENCES_BIN,
+                      "visible", TRUE,
+                      NULL);
+
+  grid = g_object_new (GTK_TYPE_GRID,
+                       "visible", TRUE,
+                       NULL);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "visible", TRUE,
+                        "label", _("Add sysroot"),
+                        "xalign", 0.0f,
+                        "hexpand", TRUE,
+                        NULL);
+
+  subtitle = g_object_new (GTK_TYPE_LABEL,
+                           "visible", TRUE,
+                           "label", g_markup_printf_escaped ("<small>%s</small>", _("Define a new sysroot 
target to build against a different target")),
+                           "use-markup", TRUE,
+                           "xalign", 0.0f,
+                           "hexpand", TRUE,
+                           NULL);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (subtitle), GTK_STYLE_CLASS_DIM_LABEL);
+
+  image = g_object_new (GTK_TYPE_IMAGE,
+                        "visible", TRUE,
+                        "icon-name", "list-add-symbolic",
+                        "valign", GTK_ALIGN_CENTER,
+                        NULL);
+
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), subtitle, 0, 1, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), image, 1, 0, 1, 2);
+
+  gtk_container_add (GTK_CONTAINER (bin), grid);
+
+  g_signal_connect_swapped (bin, "preference-activated", G_CALLBACK(sysroot_preferences_add_new), self);
+
+  return bin;
+}
+
+static void
+ide_sysroot_preferences_addin_load (IdePreferencesAddin *addin,
+                                    DzlPreferences      *preferences)
+{
+  IdeSysrootPreferencesAddin *self = IDE_SYSROOT_PREFERENCES_ADDIN (addin);
+  GtkWidget *widget = NULL;
+  IdeSysrootManager *sysroot_manager = NULL;
+  GArray *sysroots = NULL;
+  guint id = 0;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES (preferences));
+
+  self->ids = g_array_new (FALSE, FALSE, sizeof (guint));
+  self->preferences = preferences;
+
+  dzl_preferences_add_list_group (preferences, "sdk", "sysroot", _("Sysroots"), GTK_SELECTION_NONE, 0);
+
+  widget = sysroot_preferences_get_add_widget (self);
+  id = dzl_preferences_add_custom (preferences, "sdk", "sysroot", widget, "", 0);
+
+  g_array_append_val (self->ids, id);
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  sysroots = ide_sysroot_manager_list (sysroot_manager);
+  for (guint i = 0; i < sysroots->len; i++)
+    {
+      gchar *sysroot_id = g_array_index (sysroots, gchar*, i);
+      GtkWidget *pref_row = g_object_new (IDE_TYPE_SYSROOT_PREFERENCES_ROW,
+                                          "visible", TRUE,
+                                          "sysroot-id", sysroot_id,
+                                          NULL);
+
+      id = dzl_preferences_add_custom (self->preferences, "sdk", "sysroot", pref_row, NULL, 1);
+      g_array_append_val (self->ids, id);
+    }
+
+  g_array_free (sysroots, TRUE);
+
+  IDE_EXIT;
+}
+
+static void
+ide_sysroot_preferences_addin_unload (IdePreferencesAddin *addin,
+                                      DzlPreferences      *preferences)
+{
+  IdeSysrootPreferencesAddin *self = IDE_SYSROOT_PREFERENCES_ADDIN (addin);
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES (preferences));
+
+  /* Clear preferences so reload code doesn't try to
+   * make forward progress updating items.
+   */
+  self->preferences = NULL;
+
+  for (guint i = 0; i < self->ids->len; i++)
+    {
+      guint id = g_array_index (self->ids, guint, i);
+
+      dzl_preferences_remove_id (preferences, id);
+    }
+
+  g_clear_pointer (&self->ids, g_array_unref);
+
+  IDE_EXIT;
+}
+
+static void
+preferences_addin_iface_init (IdePreferencesAddinInterface *iface)
+{
+  iface->load = ide_sysroot_preferences_addin_load;
+  iface->unload = ide_sysroot_preferences_addin_unload;
+}
+
+G_DEFINE_TYPE_EXTENDED (IdeSysrootPreferencesAddin, ide_sysroot_preferences_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN, preferences_addin_iface_init))
+
+static void
+ide_sysroot_preferences_addin_class_init (IdeSysrootPreferencesAddinClass *klass)
+{
+}
+
+static void
+ide_sysroot_preferences_addin_init (IdeSysrootPreferencesAddin *self)
+{
+}
diff --git a/src/plugins/sysroot/ide-sysroot-preferences-addin.h 
b/src/plugins/sysroot/ide-sysroot-preferences-addin.h
new file mode 100644
index 000000000..a1f8e7b46
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-preferences-addin.h
@@ -0,0 +1,30 @@
+/* ide-sysroot-preferences.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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, eitIher 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYSROOT_PREFERENCES_ADDIN (ide_sysroot_preferences_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSysrootPreferencesAddin, ide_sysroot_preferences_addin, IDE, 
SYSROOT_PREFERENCES_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/ide-sysroot-preferences-row.c 
b/src/plugins/sysroot/ide-sysroot-preferences-row.c
new file mode 100644
index 000000000..b5880478c
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-preferences-row.c
@@ -0,0 +1,230 @@
+/* ide-sysroot-preferences-row.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#include "ide-sysroot-preferences-row.h"
+#include "ide-sysroot-manager.h"
+
+struct _IdeSysrootPreferencesRow
+{
+  DzlPreferencesBin parent_instance;
+  gchar *sysroot_id;
+  GtkLabel *display_name;
+  GtkEntry *name_entry;
+  GtkEntry *sysroot_entry;
+  GtkEntry *pkg_config_entry;
+  GtkButton *delete_button;
+  GtkWidget *popover;
+};
+
+G_DEFINE_TYPE (IdeSysrootPreferencesRow, ide_sysroot_preferences_row, DZL_TYPE_PREFERENCES_BIN)
+
+enum {
+  PROP_0,
+  PROP_SYSROOT_ID,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+sysroot_preferences_row_name_changed (IdeSysrootPreferencesRow *self, gpointer user_data)
+{
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_ENTRY (user_data));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  ide_sysroot_manager_set_target_name (sysroot_manager, self->sysroot_id, gtk_entry_get_text (GTK_ENTRY 
(user_data)));
+}
+
+static void
+sysroot_preferences_row_sysroot_changed (IdeSysrootPreferencesRow *self, gpointer user_data)
+{
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_ENTRY (user_data));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  ide_sysroot_manager_set_target_path (sysroot_manager, self->sysroot_id, gtk_entry_get_text (GTK_ENTRY 
(user_data)));
+}
+
+static void
+sysroot_preferences_row_pkg_config_changed (IdeSysrootPreferencesRow *self, gpointer user_data)
+{
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_ENTRY (user_data));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  ide_sysroot_manager_set_target_pkg_config_path (sysroot_manager, self->sysroot_id, gtk_entry_get_text 
(GTK_ENTRY (user_data)));
+}
+
+static void
+sysroot_preferences_row_clicked (IdeSysrootPreferencesRow *self, gpointer user_data)
+{
+  ide_sysroot_preferences_row_show_popup (self);
+}
+
+static void
+sysroot_preferences_delete (IdeSysrootPreferencesRow *self, gpointer user_data)
+{
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  g_assert (IDE_IS_SYSROOT_PREFERENCES_ROW (self));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  ide_sysroot_manager_remove_target (sysroot_manager, self->sysroot_id);
+
+  // The row is wrapped into a GtkListBoxRow that won't be removed when child is destroyed
+  gtk_widget_destroy (gtk_widget_get_parent (GTK_WIDGET (self)));
+}
+
+static void
+ide_sysroot_preferences_row_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  IdeSysrootPreferencesRow *self = IDE_SYSROOT_PREFERENCES_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_SYSROOT_ID:
+      g_value_set_string (value, self->sysroot_id);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_sysroot_preferences_row_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  IdeSysrootPreferencesRow *self = IDE_SYSROOT_PREFERENCES_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_SYSROOT_ID:
+      self->sysroot_id = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_sysroot_preferences_row_finalize (GObject *object)
+{
+  IdeSysrootPreferencesRow *self = IDE_SYSROOT_PREFERENCES_ROW (object);
+
+  g_clear_pointer (&self->sysroot_id, g_free);
+
+  G_OBJECT_CLASS (ide_sysroot_preferences_row_parent_class)->finalize (object);
+}
+
+void
+ide_sysroot_preferences_row_show_popup (IdeSysrootPreferencesRow *self)
+{
+  gtk_popover_popup (GTK_POPOVER (self->popover));
+  gtk_popover_set_modal (GTK_POPOVER (self->popover), TRUE);
+}
+
+static GObject *
+ide_sysroot_preferences_row_constructor (GType type,
+                                         guint n_construct_properties,
+                                         GObjectConstructParam * construct_properties)
+{
+  IdeSysrootManager *sysroot_manager = NULL;
+  gchar *value;
+  GObject * obj = G_OBJECT_CLASS (ide_sysroot_preferences_row_parent_class)->constructor (type, 
n_construct_properties, construct_properties);
+  IdeSysrootPreferencesRow *self = IDE_SYSROOT_PREFERENCES_ROW (obj);
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  gtk_entry_set_text (self->name_entry, ide_sysroot_manager_get_target_name (sysroot_manager, 
self->sysroot_id));
+  gtk_entry_set_text (self->sysroot_entry, ide_sysroot_manager_get_target_path (sysroot_manager, 
self->sysroot_id));
+  value = ide_sysroot_manager_get_target_pkg_config_path (sysroot_manager, self->sysroot_id);
+  if (value != NULL)
+    gtk_entry_set_text (self->pkg_config_entry, value);
+
+  g_signal_connect_object (self->name_entry,
+                           "changed",
+                           G_CALLBACK (sysroot_preferences_row_name_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->sysroot_entry,
+                           "changed",
+                           G_CALLBACK (sysroot_preferences_row_sysroot_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->pkg_config_entry,
+                           "changed",
+                           G_CALLBACK (sysroot_preferences_row_pkg_config_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+  return obj;
+}
+
+static void
+ide_sysroot_preferences_row_class_init (IdeSysrootPreferencesRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = ide_sysroot_preferences_row_finalize;
+  object_class->get_property = ide_sysroot_preferences_row_get_property;
+  object_class->set_property = ide_sysroot_preferences_row_set_property;
+  object_class->constructor = ide_sysroot_preferences_row_constructor;
+
+  properties [PROP_SYSROOT_ID] =
+    g_param_spec_string ("sysroot-id",
+                         "Sysroot ID",
+                         "Internal id of the sysroot",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/sysroot-plugin/ide-sysroot-preferences-row.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, display_name);
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, popover);
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, name_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, sysroot_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, pkg_config_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeSysrootPreferencesRow, delete_button);
+}
+
+static void
+ide_sysroot_preferences_row_init (IdeSysrootPreferencesRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect (self, "preference-activated", G_CALLBACK(sysroot_preferences_row_clicked), NULL);
+  g_signal_connect_swapped (self->delete_button, "clicked", G_CALLBACK(sysroot_preferences_delete), self);
+  g_object_bind_property (self->name_entry, "text", self->display_name, "label", 0);
+}
diff --git a/src/plugins/sysroot/ide-sysroot-preferences-row.h 
b/src/plugins/sysroot/ide-sysroot-preferences-row.h
new file mode 100644
index 000000000..4fd314c95
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-preferences-row.h
@@ -0,0 +1,32 @@
+/* ide-sysroot-preferences-row.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYSROOT_PREFERENCES_ROW (ide_sysroot_preferences_row_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSysrootPreferencesRow, ide_sysroot_preferences_row, IDE, SYSROOT_PREFERENCES_ROW, 
DzlPreferencesBin)
+
+void ide_sysroot_preferences_row_show_popup (IdeSysrootPreferencesRow *self);
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/ide-sysroot-preferences-row.ui 
b/src/plugins/sysroot/ide-sysroot-preferences-row.ui
new file mode 100644
index 000000000..bddd70179
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-preferences-row.ui
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.18 -->
+  <template class="IdeSysrootPreferencesRow" parent="DzlPreferencesBin">
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="orientation">horizontal</property>
+        <property name="column-spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel" id="display_name">
+            <property name="hexpand">true</property>
+            <property name="visible">true</property>
+            <property name="xalign">0.0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="image">
+            <property name="icon-name">edit-symbolic</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkPopover" id="popover">
+    <property name="relative-to">image</property>
+    <property name="position">bottom</property>
+    <property name="can-focus">true</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="orientation">horizontal</property>
+        <property name="column-spacing">12</property>
+        <property name="row-spacing">6</property>
+        <property name="margin">6</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">true</property>
+            <property name="xalign">1.0</property>
+            <property name="label" translatable="yes">Name</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="name_entry">
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">true</property>
+            <property name="xalign">1.0</property>
+            <property name="label" translatable="yes">Sysroot path</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="sysroot_entry">
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">true</property>
+            <property name="xalign">1.0</property>
+            <property name="label" translatable="yes">Additional Pkg-config path</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="pkg_config_entry">
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="delete_button">
+            <style>
+              <class name="destructive-action"/>
+            </style>
+            <property name="label" translatable="yes">Delete</property>
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+            <property name="halign">end</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/plugins/sysroot/ide-sysroot-runtime-provider.c 
b/src/plugins/sysroot/ide-sysroot-runtime-provider.c
new file mode 100644
index 000000000..70345f52f
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-runtime-provider.c
@@ -0,0 +1,171 @@
+/* ide-sysroot-runtime-provider.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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-sysroot-runtime-provider"
+
+#include <glib/gi18n.h>
+
+#include "ide-sysroot-runtime.h"
+#include "ide-sysroot-runtime-provider.h"
+#include "ide-sysroot-manager.h"
+
+struct _IdeSysrootRuntimeProvider
+{
+  IdeObject  parent_instance;
+
+  GPtrArray *runtimes;
+  IdeRuntimeManager *runtime_manager;
+};
+
+static void runtime_provider_iface_init (IdeRuntimeProviderInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeSysrootRuntimeProvider,
+                        ide_sysroot_runtime_provider,
+                        IDE_TYPE_OBJECT,
+                        0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_RUNTIME_PROVIDER,
+                                               runtime_provider_iface_init))
+
+static void
+sysroot_runtime_provider_remove_target (IdeSysrootRuntimeProvider *self, gchar *target)
+{
+  if (self->runtimes != NULL)
+    {
+      for (guint i= 0; i < self->runtimes->len; i++)
+        {
+          IdeRuntime *runtime = g_ptr_array_index (self->runtimes, i);
+          const gchar *sysroot_id = ide_sysroot_runtime_get_sysroot_id (IDE_SYSROOT_RUNTIME (runtime));
+          if (g_strcmp0 (target, sysroot_id) == 0)
+            {
+              ide_runtime_manager_remove (self->runtime_manager, runtime);
+              return;
+            }
+        }
+    }
+}
+
+static void
+sysroot_runtime_provider_add_target (IdeSysrootRuntimeProvider *self, gchar *target)
+{
+  GObject *runtime = NULL;
+  IdeContext *context = NULL;
+
+  context = ide_object_get_context (IDE_OBJECT (self->runtime_manager));
+  runtime = ide_sysroot_runtime_new (context, target);
+
+  ide_runtime_manager_add (self->runtime_manager, IDE_RUNTIME (runtime));
+  g_ptr_array_add (self->runtimes, g_steal_pointer (&runtime));
+}
+
+static void
+sysroot_runtime_provider_target_changed (IdeSysrootRuntimeProvider *self,
+                                         gchar *target,
+                                         IdeSysrootManagerTargetModificationType mod_type,
+                                         gpointer user_data)
+{
+  if (mod_type == IDE_SYSROOT_MANAGER_TARGET_CREATED)
+    {
+      sysroot_runtime_provider_add_target (self, target);
+    }
+  else if (mod_type == IDE_SYSROOT_MANAGER_TARGET_REMOVED)
+    {
+      sysroot_runtime_provider_remove_target (self, target);
+    }
+}
+
+static void
+ide_sysroot_runtime_provider_class_init (IdeSysrootRuntimeProviderClass *klass)
+{
+  
+}
+
+static void
+ide_sysroot_runtime_provider_init (IdeSysrootRuntimeProvider *self)
+{
+  
+}
+
+static void
+ide_sysroot_runtime_provider_load (IdeRuntimeProvider *provider,
+                                   IdeRuntimeManager  *manager)
+{
+  IdeSysrootRuntimeProvider *self = IDE_SYSROOT_RUNTIME_PROVIDER (provider);
+  IdeSysrootManager *sysroot_manager = NULL;
+  GArray *sysroots = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SYSROOT_RUNTIME_PROVIDER (self));
+  g_assert (IDE_IS_RUNTIME_MANAGER (manager));
+
+  self->runtime_manager = manager;
+  self->runtimes = g_ptr_array_new_with_free_func (g_object_unref);
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  sysroots = ide_sysroot_manager_list (sysroot_manager);
+  for (guint i = 0; i < sysroots->len; i++)
+    {
+      gchar *sysroot_id = g_array_index (sysroots, gchar*, i);
+      sysroot_runtime_provider_add_target (self, sysroot_id);
+    }
+
+  g_signal_connect_swapped (sysroot_manager, "target-changed", G_CALLBACK 
(sysroot_runtime_provider_target_changed), self);
+
+  g_array_free (sysroots, TRUE);
+
+
+  IDE_EXIT;
+}
+
+static void
+ide_sysroot_runtime_provider_unload (IdeRuntimeProvider *provider,
+                                   IdeRuntimeManager  *manager)
+{
+  IdeSysrootRuntimeProvider *self = IDE_SYSROOT_RUNTIME_PROVIDER (provider);
+  IdeSysrootManager *sysroot_manager = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SYSROOT_RUNTIME_PROVIDER (self));
+  g_assert (IDE_IS_RUNTIME_MANAGER (manager));
+
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  g_object_unref (sysroot_manager);
+
+  if (self->runtimes != NULL)
+    {
+      for (guint i= 0; i < self->runtimes->len; i++)
+        {
+          IdeRuntime *runtime = g_ptr_array_index (self->runtimes, i);
+
+          ide_runtime_manager_remove (manager, runtime);
+        }
+    }
+
+  g_clear_pointer (&self->runtimes, g_ptr_array_unref);
+
+  IDE_EXIT;
+}
+
+static void
+runtime_provider_iface_init (IdeRuntimeProviderInterface *iface)
+{
+  iface->load = ide_sysroot_runtime_provider_load;
+  iface->unload = ide_sysroot_runtime_provider_unload;
+}
diff --git a/src/plugins/sysroot/ide-sysroot-runtime-provider.h 
b/src/plugins/sysroot/ide-sysroot-runtime-provider.h
new file mode 100644
index 000000000..04a1ccc04
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-runtime-provider.h
@@ -0,0 +1,30 @@
+/* ide-sysroot-runtime-provider.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYSROOT_RUNTIME_PROVIDER (ide_sysroot_runtime_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSysrootRuntimeProvider,ide_sysroot_runtime_provider, IDE, SYSROOT_RUNTIME_PROVIDER, 
IdeObject)
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/ide-sysroot-runtime.c b/src/plugins/sysroot/ide-sysroot-runtime.c
new file mode 100644
index 000000000..5f0ee7b46
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-runtime.c
@@ -0,0 +1,187 @@
+/* ide-sysroot-runtime.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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, eitIher 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-sysroot-runtime"
+
+#include "config.h"
+
+#include "ide-sysroot-runtime.h"
+#include "ide-sysroot-manager.h"
+#include "ide-host-subprocess-launcher.h"
+
+// This is a list of common libdirs to use
+#define BASIC_LIBDIRS "/usr/lib/pkgconfig:/usr/share/pkgconfig"
+#define RUNTIME_PREFIX "sysroot:"
+
+struct _IdeSysrootRuntime
+{
+  IdeRuntime  parent_instance;
+};
+
+G_DEFINE_TYPE (IdeSysrootRuntime, ide_sysroot_runtime, IDE_TYPE_RUNTIME)
+
+GObject *
+ide_sysroot_runtime_new (IdeContext *context, gchar* sysroot_id)
+{
+  GObject *runtime = NULL;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  runtime = g_object_new (IDE_TYPE_SYSROOT_RUNTIME,
+                          "id", g_strconcat (RUNTIME_PREFIX, sysroot_id, NULL),
+                          "context", context,
+                          "display-name", "",
+                          NULL);
+  return runtime;
+}
+
+const gchar *
+ide_sysroot_runtime_get_sysroot_id (IdeSysrootRuntime *self)
+{
+  const gchar *runtime_id = ide_runtime_get_id (IDE_RUNTIME (self));
+  if (!g_str_has_prefix (runtime_id, RUNTIME_PREFIX))
+    return runtime_id;
+
+  return g_utf8_offset_to_pointer (runtime_id, g_utf8_strlen (RUNTIME_PREFIX, -1));
+}
+
+static IdeSubprocessLauncher *
+ide_sysroot_runtime_create_launcher (IdeRuntime  *runtime,
+                                     GError      **error)
+{
+  IdeSubprocessLauncher *ret;
+  IdeSysrootRuntime *self = IDE_SYSROOT_RUNTIME(runtime);
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_SYSROOT_RUNTIME (self), NULL);
+
+  ret = (IdeSubprocessLauncher *)ide_host_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | 
G_SUBPROCESS_FLAGS_STDERR_PIPE);
+
+  if (ret != NULL)
+    {
+      IdeSysrootManager *sysroot_manager = NULL;
+      const gchar *env_var = NULL;
+      const gchar *sysroot_id = NULL;
+      gchar *sysroot_cflags = NULL;
+      gchar *sysroot_libdirs = NULL;
+      gchar **path_parts = NULL;
+      gchar *sysroot_path = NULL;
+      gchar *pkgconfig_dirs = NULL;
+
+      sysroot_id = ide_sysroot_runtime_get_sysroot_id (self);
+
+      sysroot_manager = ide_sysroot_manager_get_default ();
+
+      ide_subprocess_launcher_set_run_on_host (ret, TRUE);
+      ide_subprocess_launcher_set_clear_env (ret, FALSE);
+
+      sysroot_path = ide_sysroot_manager_get_target_path (sysroot_manager, sysroot_id);
+
+      env_var = ide_subprocess_launcher_getenv (ret, "CFLAGS");
+      sysroot_cflags = g_strconcat ("--sysroot=", sysroot_path, NULL);
+      ide_subprocess_launcher_setenv (ret, "CFLAGS", g_strjoin (" ", sysroot_cflags, env_var, NULL), TRUE);
+      g_free (sysroot_cflags);
+
+      ide_subprocess_launcher_setenv (ret, "PKG_CONFIG_DIR", "", TRUE);
+
+      ide_subprocess_launcher_setenv (ret, "PKG_CONFIG_SYSROOT_DIR", g_strdup (sysroot_path), TRUE);
+
+      // Prepend the sysroot path to the BASIC_LIBDIRS values
+      path_parts = g_strsplit (BASIC_LIBDIRS, ":", 0);
+      for (gint i = g_strv_length (path_parts) - 1; i >= 0; i--)
+        {
+          gchar *path_i = g_build_path (G_DIR_SEPARATOR_S, sysroot_path, path_parts[i], NULL);
+          gchar *libdir_tmp = g_strjoin (":", path_i, sysroot_libdirs, NULL);
+          g_free (sysroot_libdirs);
+          sysroot_libdirs = libdir_tmp;
+          g_free (path_i);
+        }
+
+      pkgconfig_dirs = ide_sysroot_manager_get_target_pkg_config_path (sysroot_manager, sysroot_id);
+      if (pkgconfig_dirs != NULL && g_strcmp0 (pkgconfig_dirs, "") != 0)
+        {
+          gchar *libdir_tmp = g_strjoin (":", pkgconfig_dirs, sysroot_libdirs, NULL);
+          g_free (sysroot_libdirs);
+          sysroot_libdirs = libdir_tmp;
+        }
+
+      g_strfreev (path_parts);
+      ide_subprocess_launcher_setenv (ret, "PKG_CONFIG_LIBDIR", sysroot_libdirs, TRUE);
+      g_free (sysroot_libdirs);
+    }
+  else
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "An unknown error ocurred");
+    }
+
+  IDE_RETURN (ret);
+}
+
+
+static void
+sysroot_runtime_target_name_changed (IdeSysrootRuntime *self,
+                                     gchar *target,
+                                     gchar *new_name,
+                                     gpointer user_data)
+{
+  const gchar* sysroot_id = ide_sysroot_runtime_get_sysroot_id (self);
+  if (g_strcmp0 (target, sysroot_id) == 0)
+    ide_runtime_set_display_name (IDE_RUNTIME (self), new_name);
+}
+
+static GObject *
+ide_sysroot_runtime_constructor (GType type,
+                                 guint n_construct_properties,
+                                 GObjectConstructParam *construct_properties)
+{
+  GObject *object = G_OBJECT_CLASS (ide_sysroot_runtime_parent_class)->constructor (type, 
n_construct_properties, construct_properties);
+  IdeSysrootManager *sysroot_manager = NULL;
+  gchar *display_name = NULL;
+  const gchar* sysroot_id = NULL;
+
+  sysroot_id = ide_sysroot_runtime_get_sysroot_id (IDE_SYSROOT_RUNTIME (object));
+  sysroot_manager = ide_sysroot_manager_get_default ();
+  display_name = ide_sysroot_manager_get_target_name (sysroot_manager, sysroot_id);
+  ide_runtime_set_display_name (IDE_RUNTIME (object), display_name);
+
+  g_signal_connect_swapped (sysroot_manager, "target-name-changed", G_CALLBACK 
(sysroot_runtime_target_name_changed), object);
+  return object;
+}
+
+static void
+ide_sysroot_runtime_class_init (IdeSysrootRuntimeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeRuntimeClass *runtime_class = IDE_RUNTIME_CLASS (klass);
+
+  object_class->constructor = ide_sysroot_runtime_constructor;
+
+  runtime_class->create_launcher = ide_sysroot_runtime_create_launcher;
+}
+
+static void
+ide_sysroot_runtime_init (IdeSysrootRuntime *self)
+{
+  
+}
+
diff --git a/src/plugins/sysroot/ide-sysroot-runtime.h b/src/plugins/sysroot/ide-sysroot-runtime.h
new file mode 100644
index 000000000..0220aba01
--- /dev/null
+++ b/src/plugins/sysroot/ide-sysroot-runtime.h
@@ -0,0 +1,33 @@
+/* ide-sysroot-runtime.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYSROOT_RUNTIME (ide_sysroot_runtime_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSysrootRuntime, ide_sysroot_runtime, IDE, SYSROOT_RUNTIME, IdeRuntime)
+
+GObject *ide_sysroot_runtime_new (IdeContext *context, gchar* sysroot_id);
+const gchar *ide_sysroot_runtime_get_sysroot_id (IdeSysrootRuntime *self);
+
+G_END_DECLS
diff --git a/src/plugins/sysroot/meson.build b/src/plugins/sysroot/meson.build
new file mode 100644
index 000000000..1a15b79e2
--- /dev/null
+++ b/src/plugins/sysroot/meson.build
@@ -0,0 +1,28 @@
+if get_option('with_sysroot')
+
+sysroot_resources = gnome.compile_resources(
+  'sysroot-resources',
+  'sysroot.gresource.xml',
+  c_name: 'ide_sysroot',
+)
+
+sysroot_sources = [
+  'sysroot-plugin.c',
+  'ide-sysroot-runtime.c',
+  'ide-sysroot-runtime.h',
+  'ide-sysroot-runtime-provider.c',
+  'ide-sysroot-runtime-provider.h',
+  'ide-host-subprocess-launcher.c',
+  'ide-host-subprocess-launcher.h',
+  'ide-sysroot-preferences-addin.c',
+  'ide-sysroot-preferences-addin.h',
+  'ide-sysroot-preferences-row.c',
+  'ide-sysroot-preferences-row.h',
+  'ide-sysroot-manager.c',
+  'ide-sysroot-manager.h'
+]
+
+gnome_builder_plugins_sources += files(sysroot_sources)
+gnome_builder_plugins_sources += sysroot_resources[0]
+
+endif
diff --git a/src/plugins/sysroot/sysroot-plugin.c b/src/plugins/sysroot/sysroot-plugin.c
new file mode 100644
index 000000000..424e2dda2
--- /dev/null
+++ b/src/plugins/sysroot/sysroot-plugin.c
@@ -0,0 +1,30 @@
+/* sysroot-plugin.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#include <libpeas/peas.h>
+
+#include "ide-sysroot-runtime-provider.h"
+#include "ide-sysroot-preferences-addin.h"
+
+void
+ide_sysroot_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module, IDE_TYPE_RUNTIME_PROVIDER, 
IDE_TYPE_SYSROOT_RUNTIME_PROVIDER);
+  peas_object_module_register_extension_type (module, IDE_TYPE_PREFERENCES_ADDIN, 
IDE_TYPE_SYSROOT_PREFERENCES_ADDIN);
+}
diff --git a/src/plugins/sysroot/sysroot.gresource.xml b/src/plugins/sysroot/sysroot.gresource.xml
new file mode 100644
index 000000000..b03f6e6a0
--- /dev/null
+++ b/src/plugins/sysroot/sysroot.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins">
+    <file>sysroot.plugin</file>
+  </gresource>
+  <gresource prefix="/org/gnome/builder/plugins/sysroot-plugin">
+    <file>ide-sysroot-preferences-row.ui</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/sysroot/sysroot.plugin b/src/plugins/sysroot/sysroot.plugin
new file mode 100644
index 000000000..3277b70cc
--- /dev/null
+++ b/src/plugins/sysroot/sysroot.plugin
@@ -0,0 +1,8 @@
+[Plugin]
+Module=sysroot-plugin
+Name=Sysroot Support
+Description=Provides sysroot support
+Authors=Corentin Noël <corentin noel collabora com>
+Copyright=Copyright © 2018 Collabora Ltd.
+Builtin=true
+Embedded=ide_sysroot_register_types


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