[gnome-builder/wip/chergert/sysprof] sysprof: stub out sysprof perspective



commit ff4a98e91cca65471c8f7f3ac50903cc052dbae0
Author: Christian Hergert <chergert redhat com>
Date:   Fri Aug 19 16:30:16 2016 -0700

    sysprof: stub out sysprof perspective

 configure.ac                                  |    2 +
 libide/runner/OVERVIEW.md                     |   11 +
 libide/runner/ide-run-manager.c               |  376 +++++++++++++++++++++--
 libide/runner/ide-run-manager.h               |   47 +++-
 plugins/Makefile.am                           |    1 +
 plugins/autotools/ide-makecache.c             |    1 +
 plugins/run-tools/gbp-run-workbench-addin.c   |  100 +------
 plugins/sysprof/Makefile.am                   |   37 +++
 plugins/sysprof/configure.ac                  |   21 ++
 plugins/sysprof/gbp-sysprof-perspective.c     |  184 +++++++++++
 plugins/sysprof/gbp-sysprof-perspective.h     |   36 +++
 plugins/sysprof/gbp-sysprof-perspective.ui    |   15 +
 plugins/sysprof/gbp-sysprof-plugin.c          |   30 ++
 plugins/sysprof/gbp-sysprof-workbench-addin.c |  401 +++++++++++++++++++++++++
 plugins/sysprof/gbp-sysprof-workbench-addin.h |   33 ++
 plugins/sysprof/gtk/menus.ui                  |   11 +
 plugins/sysprof/sysprof.gresource.xml         |    7 +
 plugins/sysprof/sysprof.plugin                |    7 +
 18 files changed, 1184 insertions(+), 136 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c896021..8f09661 100644
--- a/configure.ac
+++ b/configure.ac
@@ -308,6 +308,7 @@ m4_include([plugins/run-tools/configure.ac])
 m4_include([plugins/support/configure.ac])
 m4_include([plugins/symbol-tree/configure.ac])
 m4_include([plugins/sysmon/configure.ac])
+m4_include([plugins/sysprof/configure.ac])
 m4_include([plugins/todo/configure.ac])
 m4_include([plugins/terminal/configure.ac])
 m4_include([plugins/vala-pack/configure.ac])
@@ -595,6 +596,7 @@ echo "  Python Language Pack ................. : ${enable_python_pack_plugin}"
 echo "  Run Tools ............................ : ${enable_run_tools_plugin}"
 echo "  Support .............................. : ${enable_support_plugin}"
 echo "  System Monitor ....................... : ${enable_sysmon_plugin}"
+echo "  Sysprof System Profiler .............. : ${enable_sysprof_plugin}"
 echo "  Symbol Tree .......................... : ${enable_symbol_tree_plugin}"
 echo "  Todo ................................. : ${enable_todo_plugin}"
 echo "  Terminal ............................. : ${enable_terminal_plugin}"
diff --git a/libide/runner/OVERVIEW.md b/libide/runner/OVERVIEW.md
index cf5801a..94c4e33 100644
--- a/libide/runner/OVERVIEW.md
+++ b/libide/runner/OVERVIEW.md
@@ -87,3 +87,14 @@ So it might need to do something like:
 
   runner.prepend_argv('sysprof-spawner')
 
+
+## RunHandlers
+
+Because we want one runner at a time, and different plugins might implement
+different runners (debugger, profiler, basic run support), we register
+a run handler.
+
+When ide_run_manager_run_async() is called, the run handler will be called
+and it can adjust things as necessary.
+
+
diff --git a/libide/runner/ide-run-manager.c b/libide/runner/ide-run-manager.c
index b86d93a..fbbab46 100644
--- a/libide/runner/ide-run-manager.c
+++ b/libide/runner/ide-run-manager.c
@@ -24,6 +24,7 @@
 #include "ide-debug.h"
 
 #include "buildsystem/ide-build-manager.h"
+#include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-target.h"
 #include "buildsystem/ide-configuration.h"
 #include "buildsystem/ide-configuration-manager.h"
@@ -31,14 +32,29 @@
 #include "runner/ide-runner.h"
 #include "runtimes/ide-runtime.h"
 
+typedef struct
+{
+  gchar          *id;
+  gchar          *title;
+  gchar          *icon_name;
+  gchar          *accel;
+  IdeRunHandler   handler;
+  gpointer        handler_data;
+  GDestroyNotify  handler_data_destroy;
+} IdeRunHandlerInfo;
+
 struct _IdeRunManager
 {
-  IdeObject           parent_instance;
+  IdeObject                parent_instance;
+
+  GCancellable            *cancellable;
+  GSimpleActionGroup      *actions;
+  IdeBuildTarget          *build_target;
 
-  GCancellable       *cancellable;
-  GSimpleActionGroup *actions;
+  const IdeRunHandlerInfo *handler;
+  GList                   *handlers;
 
-  guint               busy : 1;
+  guint                    busy : 1;
 };
 
 G_DEFINE_TYPE (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT)
@@ -52,12 +68,32 @@ enum {
 static GParamSpec *properties [N_PROPS];
 
 static void
+ide_run_handler_info_free (gpointer data)
+{
+  IdeRunHandlerInfo *info = data;
+
+  g_free (info->id);
+  g_free (info->title);
+  g_free (info->icon_name);
+  g_free (info->accel);
+
+  if (info->handler_data_destroy)
+    info->handler_data_destroy (info->handler_data);
+
+  g_slice_free (IdeRunHandlerInfo, info);
+}
+
+static void
 ide_run_manager_finalize (GObject *object)
 {
   IdeRunManager *self = (IdeRunManager *)object;
 
   g_clear_object (&self->cancellable);
   g_clear_object (&self->actions);
+  g_clear_object (&self->build_target);
+
+  g_list_free_full (self->handlers, ide_run_handler_info_free);
+  self->handlers = NULL;
 
   G_OBJECT_CLASS (ide_run_manager_parent_class)->finalize (object);
 }
@@ -217,6 +253,9 @@ ide_run_manager_install_cb (GObject      *object,
   g_assert (IDE_IS_RUNNER (runner));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  if (self->handler != NULL)
+    self->handler->handler (self, runner, self->handler->handler_data);
+
   ide_runner_run_async (runner,
                         cancellable,
                         ide_run_manager_run_cb,
@@ -244,6 +283,66 @@ ide_run_manager_task_completed (IdeRunManager *self,
   IDE_EXIT;
 }
 
+static void
+ide_run_manager_do_install_before_run (IdeRunManager *self,
+                                       GTask         *task)
+{
+  IdeBuildManager *build_manager;
+  IdeContext *context;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (G_IS_TASK (task));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_manager = ide_context_get_build_manager (context);
+
+  /*
+   * First we need to make sure the target is up to date and installed
+   * so that all the dependent resources are available.
+   */
+
+  self->busy = TRUE;
+
+  g_signal_connect_object (task,
+                           "notify::completed",
+                           G_CALLBACK (ide_run_manager_task_completed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  ide_build_manager_install_async (build_manager,
+                                   g_task_get_cancellable (task),
+                                   ide_run_manager_install_cb,
+                                   g_object_ref (task));
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+}
+
+static void
+ide_run_manager_run_discover_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  IdeRunManager *self = (IdeRunManager *)object;
+  g_autoptr(IdeBuildTarget) build_target = NULL;
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  build_target = ide_run_manager_discover_default_target_finish (self, result, &error);
+
+  if (build_target == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_set_task_data (task, g_steal_pointer (&build_target), g_object_unref);
+
+  ide_run_manager_do_install_before_run (self, task);
+}
+
 void
 ide_run_manager_run_async (IdeRunManager       *self,
                            IdeBuildTarget      *build_target,
@@ -253,14 +352,12 @@ ide_run_manager_run_async (IdeRunManager       *self,
 {
   g_autoptr(GTask) task = NULL;
   g_autoptr(GCancellable) local_cancellable = NULL;
-  IdeBuildManager *build_manager;
-  IdeContext *context;
   GError *error = NULL;
 
   IDE_ENTRY;
 
   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
-  g_return_if_fail (IDE_IS_BUILD_TARGET (build_target));
+  g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   if (cancellable == NULL)
@@ -268,40 +365,33 @@ ide_run_manager_run_async (IdeRunManager       *self,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_run_manager_run_async);
-  g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
+
+  g_set_object (&self->cancellable, cancellable);
 
   if (ide_run_manager_check_busy (self, &error))
     {
       g_task_return_error (task, error);
-      IDE_GOTO (failure);
+      IDE_EXIT;
     }
 
-  /*
-   * First we need to make sure the target is up to date and installed
-   * so that all the dependent resources are available.
-   */
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  build_manager = ide_context_get_build_manager (context);
-
-  self->busy = TRUE;
-
-  g_set_object (&self->cancellable, cancellable);
-
-  g_signal_connect_object (task,
-                           "notify::completed",
-                           G_CALLBACK (ide_run_manager_task_completed),
-                           self,
-                           G_CONNECT_SWAPPED);
+  if (build_target == NULL)
+    {
+      build_target = ide_run_manager_get_build_target (self);
+
+      if (build_target == NULL)
+        {
+          ide_run_manager_discover_default_target_async (self,
+                                                         cancellable,
+                                                         ide_run_manager_run_discover_cb,
+                                                         g_object_ref (task));
+          IDE_EXIT;
+        }
+    }
 
-  ide_build_manager_install_async (build_manager,
-                                   cancellable,
-                                   ide_run_manager_install_cb,
-                                   g_steal_pointer (&task));
+  g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
 
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+  ide_run_manager_do_install_before_run (self, task);
 
-failure:
   IDE_EXIT;
 }
 
@@ -325,7 +415,7 @@ ide_run_manager_run_finish (IdeRunManager  *self,
 static gboolean
 do_cancel_in_timeout (gpointer user_data)
 {
-  GCancellable *cancellable = user_data;
+  g_autoptr(GCancellable) cancellable = user_data;
 
   IDE_ENTRY;
 
@@ -349,3 +439,223 @@ ide_run_manager_cancel (IdeRunManager *self)
 
   IDE_EXIT;
 }
+
+void
+ide_run_manager_set_handler (IdeRunManager *self,
+                             const gchar   *id)
+{
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+
+  self->handler = NULL;
+
+  for (GList *iter = self->handlers; iter; iter = iter->next)
+    {
+      const IdeRunHandlerInfo *info = iter->data;
+
+      if (g_strcmp0 (info->id, id) == 0)
+        {
+          self->handler = info;
+          IDE_TRACE_MSG ("run handler set to %s", info->title);
+          break;
+        }
+    }
+}
+
+void
+ide_run_manager_add_handler (IdeRunManager  *self,
+                             const gchar    *id,
+                             const gchar    *title,
+                             const gchar    *icon_name,
+                             const gchar    *accel,
+                             IdeRunHandler   run_handler,
+                             gpointer        user_data,
+                             GDestroyNotify  user_data_destroy)
+{
+  IdeRunHandlerInfo *info;
+
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (id != NULL);
+  g_return_if_fail (title != NULL);
+
+  info = g_slice_new (IdeRunHandlerInfo);
+  info->id = g_strdup (id);
+  info->title = g_strdup (title);
+  info->icon_name = g_strdup (icon_name);
+  info->accel = g_strdup (accel);
+  info->handler = run_handler;
+  info->handler_data = user_data;
+  info->handler_data_destroy = user_data_destroy;
+
+  self->handlers = g_list_append (self->handlers, info);
+
+  if (self->handler == NULL)
+    self->handler = info;
+}
+
+void
+ide_run_manager_remove_handler (IdeRunManager *self,
+                                const gchar   *id)
+{
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (id != NULL);
+
+  for (GList *iter = self->handlers; iter; iter = iter->next)
+    {
+      IdeRunHandlerInfo *info = iter->data;
+
+      if (g_strcmp0 (info->id, id) == 0)
+        {
+          self->handlers = g_list_remove_link (self->handlers, iter);
+
+          if (self->handler == info && self->handlers != NULL)
+            self->handler = self->handlers->data;
+          else
+            self->handler = NULL;
+
+          ide_run_handler_info_free (info);
+
+          break;
+        }
+    }
+}
+
+/**
+ * ide_run_manager_get_build_target:
+ *
+ * Gets the build target that will be executed by the run manager if a
+ * specific build target has not been specified to ide_run_manager_run_async().
+ *
+ * Returns: (transfer none): An #IdeBuildTarget or %NULL if no build target
+ *   has been set.
+ */
+IdeBuildTarget *
+ide_run_manager_get_build_target (IdeRunManager *self)
+{
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+  return self->build_target;
+}
+
+static IdeBuildTarget *
+find_best_target (GPtrArray *targets)
+{
+  IdeBuildTarget *ret = NULL;
+  guint i;
+
+  g_assert (targets != NULL);
+
+  /* TODO:
+   *
+   * This is just a barebones way to try to discover a target that matters. We
+   * could probably defer this off to the build system. Either way, it's shit
+   * and should be thought through by someone.
+   */
+
+  for (i = 0; i < targets->len; i++)
+    {
+      IdeBuildTarget *target = g_ptr_array_index (targets, i);
+      g_autoptr(GFile) installdir = NULL;
+
+      installdir = ide_build_target_get_install_directory (target);
+
+      if (installdir == NULL)
+        continue;
+
+      if (ret == NULL)
+        ret = target;
+    }
+
+  return ret;
+}
+
+static void
+ide_run_manager_discover_default_target_cb (GObject      *object,
+                                            GAsyncResult *result,
+                                            gpointer      user_data)
+{
+  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GPtrArray) targets = NULL;
+  IdeBuildTarget *best_match;
+  GError *error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
+
+  if (targets == NULL)
+    {
+      g_task_return_error (task, error);
+      IDE_EXIT;
+    }
+
+  best_match = find_best_target (targets);
+
+  if (best_match == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to locate build target");
+      IDE_EXIT;
+    }
+
+  g_task_return_pointer (task, g_object_ref (best_match), g_object_unref);
+
+  IDE_EXIT;
+}
+
+void
+ide_run_manager_discover_default_target_async (IdeRunManager       *self,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  IdeBuildSystem *build_system;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_system = ide_context_get_build_system (context);
+
+  ide_build_system_get_build_targets_async (build_system,
+                                            cancellable,
+                                            ide_run_manager_discover_default_target_cb,
+                                            g_object_ref (task));
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_run_manager_discover_default_target_finish:
+ *
+ * Returns: (transfer none): An #IdeBuildTarget if successful; otherwise %NULL
+ *   and @error is set.
+ */
+IdeBuildTarget *
+ide_run_manager_discover_default_target_finish (IdeRunManager  *self,
+                                                GAsyncResult   *result,
+                                                GError        **error)
+{
+  IdeBuildTarget *ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  ret = g_task_propagate_pointer (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
diff --git a/libide/runner/ide-run-manager.h b/libide/runner/ide-run-manager.h
index 49a3e3f..358463a 100644
--- a/libide/runner/ide-run-manager.h
+++ b/libide/runner/ide-run-manager.h
@@ -21,22 +21,49 @@
 
 #include "ide-object.h"
 
+#include "buildsystem/ide-build-target.h"
+#include "runner/ide-runner.h"
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_RUN_MANAGER (ide_run_manager_get_type())
 
 G_DECLARE_FINAL_TYPE (IdeRunManager, ide_run_manager, IDE, RUN_MANAGER, IdeObject)
 
-void     ide_run_manager_cancel     (IdeRunManager        *self);
-gboolean ide_run_manager_get_busy   (IdeRunManager        *self);
-void     ide_run_manager_run_async  (IdeRunManager        *self,
-                                     IdeBuildTarget       *build_target,
-                                     GCancellable         *cancellable,
-                                     GAsyncReadyCallback   callback,
-                                     gpointer              user_data);
-gboolean ide_run_manager_run_finish (IdeRunManager        *self,
-                                     GAsyncResult         *result,
-                                     GError              **error);
+typedef void (*IdeRunHandler) (IdeRunManager *self,
+                               IdeRunner     *runner,
+                               gpointer       user_data);
+
+IdeBuildTarget *ide_run_manager_get_build_target               (IdeRunManager        *self);
+void            ide_run_manager_cancel                         (IdeRunManager        *self);
+gboolean        ide_run_manager_get_busy                       (IdeRunManager        *self);
+void            ide_run_manager_set_handler                    (IdeRunManager        *self,
+                                                                const gchar          *id);
+void            ide_run_manager_add_handler                    (IdeRunManager        *self,
+                                                                const gchar          *id,
+                                                                const gchar          *title,
+                                                                const gchar          *icon_name,
+                                                                const gchar          *accel,
+                                                                IdeRunHandler         run_handler,
+                                                                gpointer              user_data,
+                                                                GDestroyNotify        user_data_destroy);
+void            ide_run_manager_remove_handler                 (IdeRunManager        *self,
+                                                                const gchar          *id);
+void            ide_run_manager_run_async                      (IdeRunManager        *self,
+                                                                IdeBuildTarget       *build_target,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+gboolean        ide_run_manager_run_finish                     (IdeRunManager        *self,
+                                                                GAsyncResult         *result,
+                                                                GError              **error);
+void            ide_run_manager_discover_default_target_async  (IdeRunManager        *self,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+IdeBuildTarget *ide_run_manager_discover_default_target_finish (IdeRunManager        *self,
+                                                                GAsyncResult        *result,
+                                                                GError              **error);
 
 G_END_DECLS
 
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0827d68..14b3b4b 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -31,6 +31,7 @@ SUBDIRS = \
        support \
        symbol-tree \
        sysmon \
+       sysprof \
        terminal \
        todo \
        vala-pack \
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index f06c9a9..765ab08 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -1916,6 +1916,7 @@ ide_makecache_get_build_targets_worker (GTask        *task,
   ide_subprocess_launcher_push_argv (launcher, "print-bindir");
   ide_subprocess_launcher_push_argv (launcher, "print-libexecdir");
   ide_subprocess_launcher_push_argv (launcher, "print-bin_PROGRAMS");
+  ide_subprocess_launcher_push_argv (launcher, "print-bin_SCRIPTS");
   ide_subprocess_launcher_push_argv (launcher, "print-noinst_PROGRAMS");
   ide_subprocess_launcher_push_argv (launcher, "print-libexec_PROGRAMS");
 
diff --git a/plugins/run-tools/gbp-run-workbench-addin.c b/plugins/run-tools/gbp-run-workbench-addin.c
index 6cf8fd6..a5dcff0 100644
--- a/plugins/run-tools/gbp-run-workbench-addin.c
+++ b/plugins/run-tools/gbp-run-workbench-addin.c
@@ -61,93 +61,6 @@ failure:
   IDE_EXIT;
 }
 
-static IdeBuildTarget *
-find_best_target (GPtrArray *targets)
-{
-  IdeBuildTarget *ret = NULL;
-  guint i;
-
-  g_assert (targets != NULL);
-
-  for (i = 0; i < targets->len; i++)
-    {
-      IdeBuildTarget *target = g_ptr_array_index (targets, i);
-      g_autoptr(GFile) installdir = NULL;
-
-      installdir = ide_build_target_get_install_directory (target);
-
-      if (installdir == NULL)
-        continue;
-
-      if (ret == NULL)
-        ret = target;
-
-      /* TODO: Compare likelyhood of primary binary */
-    }
-
-  return ret;
-}
-
-static void
-gbp_run_workbench_addin_get_build_targets_cb (GObject      *object,
-                                              GAsyncResult *result,
-                                              gpointer      user_data)
-{
-  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
-  GbpRunWorkbenchAddin *self;
-  g_autoptr(GPtrArray) targets = NULL;
-  g_autoptr(GTask) task = user_data;
-  IdeBuildTarget *best_match;
-  IdeRunManager *run_manager;
-  GCancellable *cancellable;
-  IdeContext *context;
-  GError *error = NULL;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
-  g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (G_IS_TASK (task));
-
-  self = g_task_get_source_object (task);
-  cancellable = g_task_get_cancellable (task);
-
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-  g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_WORKBENCH (self->workbench));
-
-  targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
-
-  if (targets == NULL)
-    {
-      g_task_return_error (task, error);
-      IDE_EXIT;
-    }
-
-  best_match = find_best_target (targets);
-
-  if (best_match == NULL)
-    {
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_FAILED,
-                               "%s",
-                               _("Failed to locate build target"));
-      IDE_EXIT;
-    }
-
-  context = ide_workbench_get_context (self->workbench);
-  run_manager = ide_context_get_run_manager (context);
-
-  ide_run_manager_run_async (run_manager,
-                             best_match,
-                             cancellable,
-                             gbp_run_workbench_addin_run_cb,
-                             g_steal_pointer (&task));
-
-  IDE_EXIT;
-}
-
 static void
 gbp_run_workbench_addin_run (GSimpleAction *action,
                              GVariant      *param,
@@ -155,7 +68,7 @@ gbp_run_workbench_addin_run (GSimpleAction *action,
 {
   GbpRunWorkbenchAddin *self = user_data;
   g_autoptr(GTask) task = NULL;
-  IdeBuildSystem *build_system;
+  IdeRunManager *run_manager;
   IdeContext *context;
 
   IDE_ENTRY;
@@ -164,15 +77,16 @@ gbp_run_workbench_addin_run (GSimpleAction *action,
   g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
 
   context = ide_workbench_get_context (self->workbench);
-  build_system = ide_context_get_build_system (context);
+  run_manager = ide_context_get_run_manager (context);
 
   task = g_task_new (self, NULL, NULL, NULL);
   g_task_set_source_tag (task, gbp_run_workbench_addin_run);
 
-  ide_build_system_get_build_targets_async (build_system,
-                                            NULL,
-                                            gbp_run_workbench_addin_get_build_targets_cb,
-                                            g_steal_pointer (&task));
+  ide_run_manager_run_async (run_manager,
+                             NULL,
+                             NULL,
+                             gbp_run_workbench_addin_run_cb,
+                             g_steal_pointer (&task));
 
   IDE_EXIT;
 }
diff --git a/plugins/sysprof/Makefile.am b/plugins/sysprof/Makefile.am
new file mode 100644
index 0000000..27bc552
--- /dev/null
+++ b/plugins/sysprof/Makefile.am
@@ -0,0 +1,37 @@
+if ENABLE_SYSPROF_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libsysprof-plugin.la
+dist_plugin_DATA = sysprof.plugin
+
+libsysprof_plugin_la_SOURCES = \
+       gbp-sysprof-plugin.c \
+       gbp-sysprof-perspective.c \
+       gbp-sysprof-perspective.h \
+       gbp-sysprof-workbench-addin.c \
+       gbp-sysprof-workbench-addin.h \
+       $(NULL)
+
+nodist_libsysprof_plugin_la_SOURCES = \
+       gbp-sysprof-resources.c \
+       gbp-sysprof-resources.h
+
+libsysprof_plugin_la_CFLAGS = $(PLUGIN_CFLAGS) $(SYSPROF_CFLAGS)
+libsysprof_plugin_la_LIBADD = $(PLUGIN_LIBS) $(SYSPROF_LIBS)
+
+glib_resources_c = gbp-sysprof-resources.c
+glib_resources_h = gbp-sysprof-resources.h
+glib_resources_xml = sysprof.gresource.xml
+glib_resources_namespace = gbp_sysprof
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/sysprof/configure.ac b/plugins/sysprof/configure.ac
new file mode 100644
index 0000000..657d385
--- /dev/null
+++ b/plugins/sysprof/configure.ac
@@ -0,0 +1,21 @@
+m4_define(sysprof_required_version, [3.20.1])
+
+PKG_CHECK_MODULES(SYSPROF, [sysprof-2 >= sysprof_required_version
+                           sysprof-ui-2 >= sysprof_required_version])
+
+# --enable-sysprof-plugin=yes/no
+AC_ARG_ENABLE([sysprof-plugin],
+              [AS_HELP_STRING([--enable-sysprof-plugin=@<:@yes/no@:>@],
+                              [Build with support for the Sysprof system profiler.])],
+              [enable_sysprof_plugin=$enableval],
+              [enable_sysprof_plugin=yes])
+
+AS_IF([test x$enable_sysprof_plugin = xyes && test x$have_sysprof_ui = xno],[
+       AC_MSG_ERROR([Failed to locate sysprof-ui dependencies. Try installing Sysprof with GTK support.])
+])
+
+# for if ENABLE_SYSPROF_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_SYSPROF_PLUGIN, test x$enable_sysprof_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/sysprof/Makefile])
diff --git a/plugins/sysprof/gbp-sysprof-perspective.c b/plugins/sysprof/gbp-sysprof-perspective.c
new file mode 100644
index 0000000..3928954
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.c
@@ -0,0 +1,184 @@
+/* gbp-sysprof-perspective.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 "gbp-sysprof-perspective"
+
+#include <glib/gi18n.h>
+#include <sysprof.h>
+#include <sysprof-ui.h>
+
+#include "gbp-sysprof-perspective.h"
+
+struct _GbpSysprofPerspective
+{
+  GtkBin           parent_instance;
+
+  SpCallgraphView *callgraph_view;
+};
+
+enum {
+  PROP_0,
+  N_PROPS
+};
+
+static void perspective_iface_init (IdePerspectiveInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpSysprofPerspective, gbp_sysprof_perspective, GTK_TYPE_BIN, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_sysprof_perspective_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gbp_sysprof_perspective_parent_class)->finalize (object);
+}
+
+static void
+gbp_sysprof_perspective_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  GbpSysprofPerspective *self = GBP_SYSPROF_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_sysprof_perspective_set_property (GObject      *object,
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  GbpSysprofPerspective *self = GBP_SYSPROF_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_sysprof_perspective_class_init (GbpSysprofPerspectiveClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_sysprof_perspective_finalize;
+  object_class->get_property = gbp_sysprof_perspective_get_property;
+  object_class->set_property = gbp_sysprof_perspective_set_property;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/sysprof-plugin/gbp-sysprof-perspective.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpSysprofPerspective, callgraph_view);
+
+  g_type_ensure (SP_TYPE_CALLGRAPH_VIEW);
+}
+
+static void
+gbp_sysprof_perspective_init (GbpSysprofPerspective *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static gchar *
+gbp_sysprof_perspective_get_icon_name (IdePerspective *perspective)
+{
+  return g_strdup ("utilities-system-monitor-symbolic");
+}
+
+static gchar *
+gbp_sysprof_perspective_get_title (IdePerspective *perspective)
+{
+  return g_strdup (_("Profiler"));
+}
+
+static gchar *
+gbp_sysprof_perspective_get_id (IdePerspective *perspective)
+{
+  return g_strdup ("profiler");
+}
+
+static gint
+gbp_sysprof_perspective_get_priority (IdePerspective *perspective)
+{
+  return 70000;
+}
+
+static gchar *
+gbp_sysprof_perspective_get_accelerator (IdePerspective *perspective)
+{
+  return g_strdup ("<Alt>2");
+}
+
+static void
+perspective_iface_init (IdePerspectiveInterface *iface)
+{
+  iface->get_icon_name = gbp_sysprof_perspective_get_icon_name;
+  iface->get_title = gbp_sysprof_perspective_get_title;
+  iface->get_id = gbp_sysprof_perspective_get_id;
+  iface->get_priority = gbp_sysprof_perspective_get_priority;
+  iface->get_accelerator = gbp_sysprof_perspective_get_accelerator;
+}
+
+static void
+generate_cb (GObject      *object,
+             GAsyncResult *result,
+             gpointer      user_data)
+{
+  SpCallgraphProfile *profile = (SpCallgraphProfile *)object;
+  g_autoptr(GbpSysprofPerspective) self = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (SP_IS_CALLGRAPH_PROFILE (profile));
+  g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self));
+
+  if (!sp_profile_generate_finish (SP_PROFILE (profile), result, &error))
+    {
+      g_warning ("Failed to generate profile: %s", error->message);
+      return;
+    }
+
+  sp_callgraph_view_set_profile (self->callgraph_view, profile);
+}
+
+void
+gbp_sysprof_perspective_set_reader (GbpSysprofPerspective *self,
+                                    SpCaptureReader       *reader)
+{
+  g_autoptr(SpProfile) profile = NULL;
+
+  g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self));
+
+  if (reader == NULL)
+    {
+      sp_callgraph_view_set_profile (self->callgraph_view, NULL);
+      return;
+    }
+
+  profile = sp_callgraph_profile_new ();
+
+  sp_profile_set_reader (profile, reader);
+
+  sp_profile_generate (profile, NULL, generate_cb, g_object_ref (self));
+}
diff --git a/plugins/sysprof/gbp-sysprof-perspective.h b/plugins/sysprof/gbp-sysprof-perspective.h
new file mode 100644
index 0000000..54355c4
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.h
@@ -0,0 +1,36 @@
+/* gbp-sysprof-perspective.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 GBP_SYSPROF_PERSPECTIVE_H
+#define GBP_SYSPROF_PERSPECTIVE_H
+
+#include <ide.h>
+#include <sysprof.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYSPROF_PERSPECTIVE (gbp_sysprof_perspective_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSysprofPerspective, gbp_sysprof_perspective, GBP, SYSPROF_PERSPECTIVE, GtkBin)
+
+void gbp_sysprof_perspective_set_reader (GbpSysprofPerspective *self,
+                                         SpCaptureReader       *reader);
+
+G_END_DECLS
+
+#endif /* GBP_SYSPROF_PERSPECTIVE_H */
diff --git a/plugins/sysprof/gbp-sysprof-perspective.ui b/plugins/sysprof/gbp-sysprof-perspective.ui
new file mode 100644
index 0000000..253f51b
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.ui
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GbpSysprofPerspective" parent="GtkBin">
+    <child>
+      <object class="GtkStack">
+        <property name="visible">true</property>
+        <child>
+          <object class="SpCallgraphView" id="callgraph_view">
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/plugins/sysprof/gbp-sysprof-plugin.c b/plugins/sysprof/gbp-sysprof-plugin.c
new file mode 100644
index 0000000..987ff07
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-sysprof-plugin.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/>.
+ */
+
+#include <libpeas/peas.h>
+#include <ide.h>
+
+#include "gbp-sysprof-workbench-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              GBP_TYPE_SYSPROF_WORKBENCH_ADDIN);
+}
diff --git a/plugins/sysprof/gbp-sysprof-workbench-addin.c b/plugins/sysprof/gbp-sysprof-workbench-addin.c
new file mode 100644
index 0000000..a98518d
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-workbench-addin.c
@@ -0,0 +1,401 @@
+/* gbp-sysprof-workbench-addin.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/>.
+ */
+
+#include <glib/gi18n.h>
+#include <sysprof.h>
+
+#include "gbp-sysprof-perspective.h"
+#include "gbp-sysprof-workbench-addin.h"
+
+struct _GbpSysprofWorkbenchAddin
+{
+  GObject                parent_instance;
+
+  GSimpleActionGroup    *actions;
+  SpProfiler            *profiler;
+
+  GbpSysprofPerspective *perspective;
+  IdeWorkbench          *workbench;
+};
+
+static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpSysprofWorkbenchAddin, gbp_sysprof_workbench_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, workbench_addin_iface_init))
+
+static void
+set_action_state (GbpSysprofWorkbenchAddin *self,
+                  const gchar              *action_name,
+                  GVariant                 *state,
+                  gboolean                  enabled)
+{
+  GAction *action;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (action_name != NULL);
+
+  action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), action_name);
+  g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
+}
+
+static void
+profiler_stopped (GbpSysprofWorkbenchAddin *self,
+                  SpProfiler               *profiler)
+{
+  IDE_ENTRY;
+
+  g_return_if_fail (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_return_if_fail (SP_IS_PROFILER (profiler));
+
+  if (self->profiler == profiler)
+    {
+      set_action_state (self, "start-profiler", g_variant_new_boolean (FALSE), TRUE);
+      IDE_EXIT;
+    }
+
+  IDE_EXIT;
+}
+
+static void
+profiler_run_handler (IdeRunManager *run_manager,
+                      IdeRunner     *runner,
+                      gpointer       user_data)
+{
+  GbpSysprofWorkbenchAddin *self = user_data;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_RUNNER (runner));
+  g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+  g_printerr ("This is where we would connect to the pid spawn and start profiling.\n");
+}
+
+
+static void
+gbp_sysprof_workbench_addin_start (GbpSysprofWorkbenchAddin *self)
+{
+  IdeContext *context;
+  IdeRunManager *run_manager;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_return_if_fail (IDE_IS_PERSPECTIVE (self->perspective));
+  g_return_if_fail (IDE_IS_WORKBENCH (self->workbench));
+
+  if (self->profiler != NULL)
+    {
+      if (sp_profiler_get_is_running (self->profiler))
+        {
+          g_warning ("Profiler is already running, cannot start profiler.");
+          IDE_EXIT;
+        }
+    }
+
+  context = ide_workbench_get_context (self->workbench);
+
+  run_manager = ide_context_get_run_manager (context);
+  ide_run_manager_set_handler (run_manager, "profiler");
+
+  ide_workbench_set_visible_perspective (self->workbench, IDE_PERSPECTIVE (self->perspective));
+
+  set_action_state (self, "start-profiler", g_variant_new_boolean (TRUE), FALSE);
+
+  g_clear_object (&self->profiler);
+  self->profiler = sp_local_profiler_new ();
+
+  g_signal_connect_object (self->profiler,
+                           "stopped",
+                           G_CALLBACK (profiler_stopped),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  ide_run_manager_run_async (run_manager, NULL, NULL, NULL, NULL);
+
+  sp_profiler_start (self->profiler);
+
+  IDE_EXIT;
+}
+
+static void
+start_profiler_action (GSimpleAction *action,
+                       GVariant      *variant,
+                       gpointer       user_data)
+{
+  GbpSysprofWorkbenchAddin *self = user_data;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+
+  gbp_sysprof_workbench_addin_start (self);
+}
+
+static void
+gbp_sysprof_workbench_addin_open_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)object;
+  g_autoptr(SpCaptureReader) reader = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_TASK (result));
+
+  reader = g_task_propagate_pointer (G_TASK (result), &error);
+
+  g_assert (reader || error != NULL);
+
+  if (reader == NULL)
+    {
+      g_message ("%s", error->message);
+      return;
+    }
+
+  gbp_sysprof_perspective_set_reader (self->perspective, reader);
+}
+
+static void
+gbp_sysprof_workbench_addin_open_worker (GTask        *task,
+                                         gpointer      source_object,
+                                         gpointer      task_data,
+                                         GCancellable *cancellable)
+{
+  GbpSysprofWorkbenchAddin *self = source_object;
+  g_autofree gchar *path = NULL;
+  SpCaptureReader *reader;
+  GFile *file = task_data;
+  GError *error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  path = g_file_get_path (file);
+
+  if (NULL == (reader = sp_capture_reader_new (path, &error)))
+    {
+      g_assert (error != NULL);
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_return_pointer (task, reader, (GDestroyNotify)sp_capture_reader_unref);
+}
+
+static void
+gbp_sysprof_workbench_addin_open (GbpSysprofWorkbenchAddin *self,
+                                  GFile                    *file)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (G_IS_FILE (file));
+
+  if (!g_file_is_native (file))
+    {
+      g_warning ("Can only open local sysprof capture files.");
+      return;
+    }
+
+  task = g_task_new (self, NULL, gbp_sysprof_workbench_addin_open_cb, NULL);
+  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+  g_task_run_in_thread (task, gbp_sysprof_workbench_addin_open_worker);
+}
+
+static void
+open_profile_action (GSimpleAction *action,
+                     GVariant      *variant,
+                     gpointer       user_data)
+{
+  GbpSysprofWorkbenchAddin *self = user_data;
+  GtkFileChooserNative *native;
+  GtkFileFilter *filter;
+  gint ret;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (self->workbench));
+  g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self->perspective));
+
+  ide_workbench_set_visible_perspective (self->workbench, IDE_PERSPECTIVE (self->perspective));
+
+  native = gtk_file_chooser_native_new (_("Open Profile"),
+                                        GTK_WINDOW (self->workbench),
+                                        GTK_FILE_CHOOSER_ACTION_OPEN,
+                                        _("Open"),
+                                        _("Cancel"));
+
+  /* Add our filter for sysprof capture files.  */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Sysprof Capture (*.syscap)"));
+  gtk_file_filter_add_pattern (filter, "*.syscap");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (native), filter);
+
+  /* And all files now */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (native), filter);
+
+  /* Unlike gtk_dialog_run(), this will handle processing
+   * various I/O events and so should be safe to use.
+   */
+  ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native));
+
+  if (ret == GTK_RESPONSE_ACCEPT)
+    {
+      g_autoptr(GFile) file = NULL;
+
+      file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+      if (G_IS_FILE (file))
+        gbp_sysprof_workbench_addin_open (self, file);
+    }
+
+  gtk_native_dialog_hide (GTK_NATIVE_DIALOG (native));
+  gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+}
+
+static void
+gbp_sysprof_workbench_addin_finalize (GObject *object)
+{
+  GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)object;
+
+  g_clear_object (&self->actions);
+
+  G_OBJECT_CLASS (gbp_sysprof_workbench_addin_parent_class)->finalize (object);
+}
+
+static void
+gbp_sysprof_workbench_addin_class_init (GbpSysprofWorkbenchAddinClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_sysprof_workbench_addin_finalize;
+}
+
+static void
+gbp_sysprof_workbench_addin_init (GbpSysprofWorkbenchAddin *self)
+{
+  static const GActionEntry entries[] = {
+    { "start-profiler", start_profiler_action, NULL, "false" },
+    { "open-profile", open_profile_action },
+  };
+
+  self->actions = g_simple_action_group_new ();
+
+  g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
+                                   entries,
+                                   G_N_ELEMENTS (entries),
+                                   self);
+}
+
+static void
+gbp_sysprof_workbench_addin_load (IdeWorkbenchAddin *addin,
+                                  IdeWorkbench      *workbench)
+{
+  GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)addin;
+  IdeWorkbenchHeaderBar *headerbar;
+  IdeRunManager *run_manager;
+  IdeContext *context;
+  GtkWidget *button;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = workbench;
+
+  context = ide_workbench_get_context (workbench);
+
+  /*
+   * Register our custom run handler to activate the profiler.
+   */
+  run_manager = ide_context_get_run_manager (context);
+  ide_run_manager_add_handler (run_manager,
+                               "profiler",
+                               _("Profile"),
+                               "utilities-system-monitor-symbolic",
+                               "<Ctrl>F8",
+                               profiler_run_handler,
+                               self,
+                               NULL);
+
+  /*
+   * Add the perspcetive to the workbench.
+   */
+  self->perspective = g_object_new (GBP_TYPE_SYSPROF_PERSPECTIVE,
+                                    "visible", TRUE,
+                                    NULL);
+  ide_workbench_add_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+
+
+  /*
+   * Add our actions to the workbench so they can be activated via the
+   * headerbar or the perspective.
+   */
+  gtk_widget_insert_action_group (GTK_WIDGET (workbench),
+                                  "profiler",
+                                  G_ACTION_GROUP (self->actions));
+
+
+  /*
+   * Add our Profile button next to "Run".
+   */
+  headerbar = ide_workbench_get_headerbar (workbench);
+  button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+                         "action-name", "profiler.start-profiler",
+                         "focus-on-click", FALSE,
+                         "child", g_object_new (GTK_TYPE_IMAGE,
+                                                "icon-name", "media-record-symbolic",
+                                                "visible", TRUE,
+                                                NULL),
+                         "tooltip-text", _("Profile project"),
+                         "visible", TRUE,
+                         NULL);
+  ide_widget_add_style_class (button, "image-button");
+  ide_workbench_header_bar_insert_right (headerbar, button, GTK_PACK_START, 10);
+}
+
+static void
+gbp_sysprof_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                    IdeWorkbench      *workbench)
+{
+  GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)addin;
+  IdeRunManager *run_manager;
+  IdeContext *context;
+
+  g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  context = ide_workbench_get_context (workbench);
+
+  run_manager = ide_context_get_run_manager (context);
+  ide_run_manager_remove_handler (run_manager, "profiler");
+
+  ide_workbench_remove_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+
+  self->perspective = NULL;
+  self->workbench = NULL;
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = gbp_sysprof_workbench_addin_load;
+  iface->unload = gbp_sysprof_workbench_addin_unload;
+}
diff --git a/plugins/sysprof/gbp-sysprof-workbench-addin.h b/plugins/sysprof/gbp-sysprof-workbench-addin.h
new file mode 100644
index 0000000..3df3d93
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-workbench-addin.h
@@ -0,0 +1,33 @@
+/* gbp-sysprof-workbench-addin.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 GBP_SYSPROF_WORKBENCH_ADDIN_H
+#define GBP_SYSPROF_WORKBENCH_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYSPROF_WORKBENCH_ADDIN (gbp_sysprof_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSysprofWorkbenchAddin, gbp_sysprof_workbench_addin, GBP, SYSPROF_WORKBENCH_ADDIN, 
GObject)
+
+G_END_DECLS
+
+#endif /* GBP_SYSPROF_WORKBENCH_ADDIN_H */
+
diff --git a/plugins/sysprof/gtk/menus.ui b/plugins/sysprof/gtk/menus.ui
new file mode 100644
index 0000000..6004cef
--- /dev/null
+++ b/plugins/sysprof/gtk/menus.ui
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<interface>
+  <menu id="gear-menu">
+    <section id="gear-menu-open-section">
+      <item>
+        <attribute name="label" translatable="yes">Open Profile…</attribute>
+        <attribute name="action">profiler.open-profile</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/plugins/sysprof/sysprof.gresource.xml b/plugins/sysprof/sysprof.gresource.xml
new file mode 100644
index 0000000..2695fd8
--- /dev/null
+++ b/plugins/sysprof/sysprof.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins/sysprof-plugin">
+    <file>gtk/menus.ui</file>
+    <file>gbp-sysprof-perspective.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/sysprof/sysprof.plugin b/plugins/sysprof/sysprof.plugin
new file mode 100644
index 0000000..784959d
--- /dev/null
+++ b/plugins/sysprof/sysprof.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=sysprof-plugin
+Name=Sysprof
+Description=Integration with the Sysprof system profiler
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2016 Christian Hergert
+Builtin=true


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