[gnome-builder/wip/chergert/runners] wip
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/runners] wip
- Date: Tue, 12 Jul 2016 05:32:32 +0000 (UTC)
commit 819d742228a70e896f20cf81610fba92cb4abb6d
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 22:32:13 2016 -0700
wip
configure.ac | 2 +
libide/Makefile.am | 6 +
libide/buildsystem/ide-build-system.c | 61 +++
libide/buildsystem/ide-build-system.h | 76 ++--
libide/buildsystem/ide-build-target.c | 28 +
libide/buildsystem/ide-build-target.h | 39 ++
libide/ide-types.h | 1 +
libide/ide.h | 3 +
libide/runner/OVERVIEW.md | 89 ++++
libide/runner/ide-runner-addin.c | 141 +++++
libide/runner/ide-runner-addin.h | 78 +++
libide/runner/ide-runner.c | 641 +++++++++++++++++++++++
libide/runner/ide-runner.h | 43 ++-
libide/runtimes/ide-runtime.c | 51 ++
libide/runtimes/ide-runtime.h | 23 +
plugins/Makefile.am | 7 +-
plugins/autotools/Makefile.am | 2 +
plugins/autotools/ide-autotools-build-system.c | 90 ++++
plugins/autotools/ide-autotools-build-target.c | 147 ++++++
plugins/autotools/ide-autotools-build-target.h | 32 ++
plugins/autotools/ide-autotools-runner-addin.h | 32 ++
plugins/autotools/ide-makecache.c | 391 ++++++++++++++-
plugins/autotools/ide-makecache.h | 55 ++-
plugins/build-tools/gbp-build-workbench-addin.c | 40 --
plugins/run-tools/Makefile.am | 27 +
plugins/run-tools/configure.ac | 12 +
plugins/run-tools/gbp-run-plugin.c | 28 +
plugins/run-tools/gbp-run-workbench-addin.c | 260 +++++++++
plugins/run-tools/gbp-run-workbench-addin.h | 32 ++
plugins/run-tools/run-tools.plugin | 11 +
30 files changed, 2339 insertions(+), 109 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1b7f016..a4a02a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -301,6 +301,7 @@ m4_include([plugins/mingw/configure.ac])
m4_include([plugins/project-tree/configure.ac])
m4_include([plugins/python-gi-imports-completion/configure.ac])
m4_include([plugins/python-pack/configure.ac])
+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])
@@ -593,6 +594,7 @@ echo " Project Tree ......................... : ${enable_project_tree_plugin}"
echo " Python GObject Introspection ......... : ${enable_python_gi_imports_completion_plugin}"
echo " Python Jedi Autocompletion ........... : ${enable_jedi_plugin}"
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 " Symbol Tree .......................... : ${enable_symbol_tree_plugin}"
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 91459fb..9811c30 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -33,6 +33,7 @@ libide_1_0_la_public_headers = \
buildsystem/ide-build-result-addin.h \
buildsystem/ide-build-result.h \
buildsystem/ide-build-system.h \
+ buildsystem/ide-build-target.h \
buildsystem/ide-builder.h \
buildsystem/ide-configuration-manager.h \
buildsystem/ide-configuration.h \
@@ -87,6 +88,8 @@ libide_1_0_la_public_headers = \
projects/ide-project-miner.h \
projects/ide-project.h \
projects/ide-recent-projects.h \
+ runner/ide-runner.h \
+ runner/ide-runner-addin.h \
runtimes/ide-runtime-manager.h \
runtimes/ide-runtime-provider.h \
runtimes/ide-runtime.h \
@@ -173,6 +176,7 @@ libide_1_0_la_public_sources = \
buildsystem/ide-build-result-addin.c \
buildsystem/ide-build-result.c \
buildsystem/ide-build-system.c \
+ buildsystem/ide-build-target.c \
buildsystem/ide-builder.c \
buildsystem/ide-configuration-manager.c \
buildsystem/ide-configuration.c \
@@ -232,6 +236,8 @@ libide_1_0_la_public_sources = \
projects/ide-project-miner.c \
projects/ide-project.c \
projects/ide-recent-projects.c \
+ runner/ide-runner.c \
+ runner/ide-runner-addin.c \
runtimes/ide-runtime-manager.c \
runtimes/ide-runtime-provider.c \
runtimes/ide-runtime.c \
diff --git a/libide/buildsystem/ide-build-system.c b/libide/buildsystem/ide-build-system.c
index a2697cb..546bd58 100644
--- a/libide/buildsystem/ide-build-system.c
+++ b/libide/buildsystem/ide-build-system.c
@@ -129,9 +129,42 @@ ide_build_system_real_get_builder (IdeBuildSystem *self,
}
static void
+ide_build_system_real_get_build_targets_async (IdeBuildSystem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (IDE_IS_BUILD_SYSTEM (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_build_system_real_get_build_targets_async);
+ g_task_return_pointer (task, g_ptr_array_new (), (GDestroyNotify)g_ptr_array_unref);
+}
+
+static GPtrArray *
+ide_build_system_real_get_build_targets_finish (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_assert (IDE_IS_BUILD_SYSTEM (self));
+ g_assert (G_IS_TASK (task));
+ g_assert (g_task_is_valid (task, self));
+ g_assert (g_task_get_source_tag (task) == ide_build_system_real_get_build_targets_async);
+
+ return g_task_propagate_pointer (task, error);
+}
+
+static void
ide_build_system_default_init (IdeBuildSystemInterface *iface)
{
iface->get_builder = ide_build_system_real_get_builder;
+ iface->get_build_targets_async = ide_build_system_real_get_build_targets_async;
+ iface->get_build_targets_finish = ide_build_system_real_get_build_targets_finish;
properties [PROP_PROJECT_FILE] =
g_param_spec_object ("project-file",
@@ -242,3 +275,31 @@ ide_build_system_get_builder (IdeBuildSystem *system,
return IDE_BUILD_SYSTEM_GET_IFACE (system)->get_builder (system, configuration, error);
}
+void
+ide_build_system_get_build_targets_async (IdeBuildSystem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_build_system_get_build_targets_finish:
+ *
+ * Returns: (transfer container) (element-type Ide.BuildTarget): An array of build targets
+ * or %NULL upon failure and @error is set.
+ */
+GPtrArray *
+ide_build_system_get_build_targets_finish (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+ return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_finish (self, result, error);
+}
diff --git a/libide/buildsystem/ide-build-system.h b/libide/buildsystem/ide-build-system.h
index 3cb9e98..760a0ba 100644
--- a/libide/buildsystem/ide-build-system.h
+++ b/libide/buildsystem/ide-build-system.h
@@ -33,39 +33,53 @@ struct _IdeBuildSystemInterface
{
GTypeInterface parent_iface;
- gint (*get_priority) (IdeBuildSystem *system);
- IdeBuilder *(*get_builder) (IdeBuildSystem *system,
- IdeConfiguration *configuration,
- GError **error);
- void (*get_build_flags_async) (IdeBuildSystem *self,
- IdeFile *file,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gchar **(*get_build_flags_finish) (IdeBuildSystem *self,
- GAsyncResult *result,
- GError **error);
+ gint (*get_priority) (IdeBuildSystem *system);
+ IdeBuilder *(*get_builder) (IdeBuildSystem *system,
+ IdeConfiguration *configuration,
+ GError **error);
+ void (*get_build_flags_async) (IdeBuildSystem *self,
+ IdeFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gchar **(*get_build_flags_finish) (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*get_build_targets_async) (IdeBuildSystem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GPtrArray *(*get_build_targets_finish) (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error);
};
-gint ide_build_system_get_priority (IdeBuildSystem *self);
-void ide_build_system_get_build_flags_async (IdeBuildSystem *self,
- IdeFile *file,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gchar **ide_build_system_get_build_flags_finish (IdeBuildSystem *self,
- GAsyncResult *result,
- GError **error);
-void ide_build_system_new_async (IdeContext *context,
- GFile *project_file,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IdeBuildSystem *ide_build_system_new_finish (GAsyncResult *result,
- GError **error);
-IdeBuilder *ide_build_system_get_builder (IdeBuildSystem *system,
- IdeConfiguration *configuration,
- GError **error);
+gint ide_build_system_get_priority (IdeBuildSystem *self);
+void ide_build_system_get_build_flags_async (IdeBuildSystem *self,
+ IdeFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar **ide_build_system_get_build_flags_finish (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_build_system_new_async (IdeContext *context,
+ GFile *project_file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeBuildSystem *ide_build_system_new_finish (GAsyncResult *result,
+ GError **error);
+IdeBuilder *ide_build_system_get_builder (IdeBuildSystem *system,
+ IdeConfiguration *configuration,
+ GError **error);
+void ide_build_system_get_build_targets_async (IdeBuildSystem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GPtrArray *ide_build_system_get_build_targets_finish (IdeBuildSystem *self,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/libide/buildsystem/ide-build-target.c b/libide/buildsystem/ide-build-target.c
new file mode 100644
index 0000000..f0cbd4c
--- /dev/null
+++ b/libide/buildsystem/ide-build-target.c
@@ -0,0 +1,28 @@
+/* ide-build-target.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-target"
+
+#include "ide-build-target.h"
+
+G_DEFINE_INTERFACE (IdeBuildTarget, ide_build_target, IDE_TYPE_OBJECT)
+
+static void
+ide_build_target_default_init (IdeBuildTargetInterface *iface)
+{
+}
diff --git a/libide/buildsystem/ide-build-target.h b/libide/buildsystem/ide-build-target.h
new file mode 100644
index 0000000..a8a4fe0
--- /dev/null
+++ b/libide/buildsystem/ide-build-target.h
@@ -0,0 +1,39 @@
+/* ide-build-target.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_TARGET_H
+#define IDE_BUILD_TARGET_H
+
+#include <glib-object.h>
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_TARGET (ide_build_target_get_type())
+
+G_DECLARE_INTERFACE (IdeBuildTarget, ide_build_target, IDE, BUILD_TARGET, IdeObject)
+
+struct _IdeBuildTargetInterface
+{
+ GTypeInterface parent_iface;
+};
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_TARGET_H */
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 7040331..b694b9d 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -40,6 +40,7 @@ typedef struct _IdeBuilder IdeBuilder;
typedef struct _IdeBuildResult IdeBuildResult;
typedef struct _IdeBuildSystem IdeBuildSystem;
+typedef struct _IdeBuildTarget IdeBuildTarget;
typedef struct _IdeConfiguration IdeConfiguration;
typedef struct _IdeConfigurationManager IdeConfigurationManager;
diff --git a/libide/ide.h b/libide/ide.h
index 51ea85f..317f316 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -38,6 +38,7 @@ G_BEGIN_DECLS
#include "buildsystem/ide-build-result-addin.h"
#include "buildsystem/ide-build-result.h"
#include "buildsystem/ide-build-system.h"
+#include "buildsystem/ide-build-target.h"
#include "buildsystem/ide-builder.h"
#include "buildsystem/ide-configuration-manager.h"
#include "buildsystem/ide-configuration.h"
@@ -83,6 +84,8 @@ G_BEGIN_DECLS
#include "projects/ide-project-miner.h"
#include "projects/ide-project.h"
#include "projects/ide-recent-projects.h"
+#include "runner/ide-runner.h"
+#include "runner/ide-runner-addin.h"
#include "runtimes/ide-runtime-manager.h"
#include "runtimes/ide-runtime-provider.h"
#include "runtimes/ide-runtime.h"
diff --git a/libide/runner/OVERVIEW.md b/libide/runner/OVERVIEW.md
new file mode 100644
index 0000000..cf5801a
--- /dev/null
+++ b/libide/runner/OVERVIEW.md
@@ -0,0 +1,89 @@
+# Running Projects
+
+Running a project target in Builder needs to accomidate a few difficult
+to plumb components. For example, Builder supports runtimes which might
+be different than the current host (such as org.gnome.Platform 3.22).
+Additionally, we might need to attach a debugger. The build system might
+also need to perform an installation of the application bits into a
+runtime so that the project can run as it's "installed state".
+
+All of these complexities results in project execution being abstracted
+into an “IdeRunner” object and series of “IdeRunnerAddin” extensions.
+This allows the various plugins involved (build system, runtime, possible
+debugger or profilers) to hook in to various stages and modify things as
+necessary.
+
+# IdeRunner
+
+This is our core runner structure. It manages some basic process execution
+stuff, but most things get tweaked and configured by the addins.
+
+The process is launched by the IdeSubprocessLauncher created by the configured
+runtime. However, addins can tweak things as necessary.
+
+## IdeRunnerAddin
+
+Plugins should implement this interface so that they can modify the runner
+as necessary. This should be used for custom extensoin points that are always
+needed, not to add integration points like debugger, profiler, etc.
+
+## Debugger Integration
+
+Debuggers may need to hook into the runtime pid namespace (and possibly mount
+namespace to bring along tooling). This could also mean starting the process as
+an inferior of something like `gdb`. In the flatpak scenario, this could be
+
+ flatpak reaper → gdb → inferior
+
+Additionally, the debugger will need to be able to IPC with the gdb instance
+inside the runtime.
+
+While we don't have support for this yet, I think the design we can use is that
+the `IdeRunner` can have API to bring in "sidecars" to the runtime which
+contain the debugging tooling. For the local system, this would be a
+passthrough. For something like flatpak, it would need to enter the namespace
+and add a mountpoint for the tooling. (This is probably something that needs to
+be implemented in bubblewrap to some degree).
+
+When the debugger sets up the runner, it will need to be able to modify argv
+similar to our IdeSubprocessLauncher. This will allow it to add gdb to the
+prefix of the command.
+
+Getting a runner will be somewhat consistent with:
+
+ BuildSystem = context.get_build_system()
+ ConfigManager = context.get_configuration_manager()
+
+ config = ConfigManager.get_current()
+ runtime = config.get_runtime()
+ target = BuildSystem.get_default_run_command(config)
+ runner = runtime.create_runner(config, target)
+
+ -> the build system, at this point, may have added a prehook via
+ the runner addin that will install the project before it can
+ be run.
+
+And the debugger might do something like:
+
+ runner.prepend_argv('gdbserver')
+ runner.prepend_argv('--once')
+ runner.prepend_argv('--args')
+
+When runner.run() is called, the implementation might choose to prepend
+additional argv parameters. This allows gdb to simply prepend the command
+line, but still have flatpak spawn the container process (bubblewrap).
+
+## Profiling Integration
+
+A profiler (such as sysprof) will need to know the process identifier of the
+spawned process (and possibly children processes). This is somewhat similar
+to debugging except that it isn't strictly necessary that the process stops
+before reaching `main()`.
+
+To synchronize on startup, sysprof might spawn a helper process that
+communicates with the listener process, and then execs the child command.
+
+So it might need to do something like:
+
+ runner.prepend_argv('sysprof-spawner')
+
diff --git a/libide/runner/ide-runner-addin.c b/libide/runner/ide-runner-addin.c
new file mode 100644
index 0000000..d2a455b
--- /dev/null
+++ b/libide/runner/ide-runner-addin.c
@@ -0,0 +1,141 @@
+/* ide-runner-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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-runner-addin"
+
+#include "ide-runner-addin.h"
+
+G_DEFINE_INTERFACE (IdeRunnerAddin, ide_runner_addin, G_TYPE_OBJECT)
+
+static void
+ide_runner_addin_real_load (IdeRunnerAddin *self,
+ IdeRunner *runner)
+{
+}
+
+static void
+ide_runner_addin_real_unload (IdeRunnerAddin *self,
+ IdeRunner *runner)
+{
+}
+
+static void
+dummy_async (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (IDE_IS_RUNNER_ADDIN (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (callback == NULL)
+ return;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+dummy_finish (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_RUNNER_ADDIN (self));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_runner_addin_default_init (IdeRunnerAddinInterface *iface)
+{
+ iface->load = ide_runner_addin_real_load;
+ iface->unload = ide_runner_addin_real_unload;
+ iface->prehook_async = dummy_async;
+ iface->prehook_finish = dummy_finish;
+ iface->posthook_async = dummy_async;
+ iface->posthook_finish = dummy_finish;
+}
+
+void
+ide_runner_addin_load (IdeRunnerAddin *self,
+ IdeRunner *runner)
+{
+ g_assert (IDE_IS_RUNNER_ADDIN (self));
+ g_assert (IDE_IS_RUNNER (runner));
+
+ IDE_RUNNER_ADDIN_GET_IFACE (self)->load (self, runner);
+}
+
+void
+ide_runner_addin_unload (IdeRunnerAddin *self,
+ IdeRunner *runner)
+{
+ g_assert (IDE_IS_RUNNER_ADDIN (self));
+ g_assert (IDE_IS_RUNNER (runner));
+
+ IDE_RUNNER_ADDIN_GET_IFACE (self)->unload (self, runner);
+}
+
+void
+ide_runner_addin_prehook_async (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_RUNNER_ADDIN (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_RUNNER_ADDIN_GET_IFACE (self)->prehook_async (self, cancellable, callback, user_data);
+}
+
+gboolean
+ide_runner_addin_prehook_finish (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_RUNNER_ADDIN_GET_IFACE (self)->prehook_finish (self, result, error);
+}
+
+void
+ide_runner_addin_posthook_async (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_RUNNER_ADDIN (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_RUNNER_ADDIN_GET_IFACE (self)->posthook_async (self, cancellable, callback, user_data);
+}
+
+gboolean
+ide_runner_addin_posthook_finish (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_RUNNER_ADDIN_GET_IFACE (self)->posthook_finish (self, result, error);
+}
diff --git a/libide/runner/ide-runner-addin.h b/libide/runner/ide-runner-addin.h
new file mode 100644
index 0000000..60e6a17
--- /dev/null
+++ b/libide/runner/ide-runner-addin.h
@@ -0,0 +1,78 @@
+/* ide-runner-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 IDE_RUNNER_ADDIN_H
+#define IDE_RUNNER_ADDIN_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+#include "ide-runner.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUNNER_ADDIN (ide_runner_addin_get_type())
+
+G_DECLARE_INTERFACE (IdeRunnerAddin, ide_runner_addin, IDE, RUNNER_ADDIN, GObject)
+
+struct _IdeRunnerAddinInterface
+{
+ GTypeInterface parent_interface;
+
+ void (*load) (IdeRunnerAddin *self,
+ IdeRunner *runner);
+ void (*unload) (IdeRunnerAddin *self,
+ IdeRunner *runner);
+ void (*prehook_async) (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*prehook_finish) (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*posthook_async) (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*posthook_finish) (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+void ide_runner_addin_load (IdeRunnerAddin *self,
+ IdeRunner *runner);
+void ide_runner_addin_unload (IdeRunnerAddin *self,
+ IdeRunner *runner);
+void ide_runner_addin_prehook_async (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_runner_addin_prehook_finish (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_runner_addin_posthook_async (IdeRunnerAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_runner_addin_posthook_finish (IdeRunnerAddin *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_RUNNER_ADDIN_H */
diff --git a/libide/runner/ide-runner.c b/libide/runner/ide-runner.c
new file mode 100644
index 0000000..054a898
--- /dev/null
+++ b/libide/runner/ide-runner.c
@@ -0,0 +1,641 @@
+/* ide-runner.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-runner"
+
+#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+#include <stdlib.h>
+
+#include "ide-context.h"
+
+#include "runner/ide-runner.h"
+#include "runner/ide-runner-addin.h"
+#include "workers/ide-subprocess-launcher.h"
+
+typedef struct
+{
+ PeasExtensionSet *addins;
+ GQueue argv;
+} IdeRunnerPrivate;
+
+typedef struct
+{
+ GSList *prehook_queue;
+ GSList *posthook_queue;
+} IdeRunnerRunState;
+
+enum {
+ PROP_0,
+ PROP_ARGV,
+ N_PROPS
+};
+
+static void ide_runner_tick_posthook (GTask *task);
+static void ide_runner_tick_prehook (GTask *task);
+static void ide_runner_tick_run (GTask *task);
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeRunner, ide_runner, IDE_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static IdeRunnerAddin *
+pop_runner_addin (GSList **list)
+{
+ IdeRunnerAddin *ret;
+
+ g_assert (list != NULL);
+ g_assert (*list != NULL);
+
+ ret = (*list)->data;
+
+ *list = g_slist_delete_link (*list, *list);
+
+ return ret;
+}
+
+static void
+ide_runner_run_state_free (gpointer data)
+{
+ IdeRunnerRunState *state = data;
+
+ g_slist_foreach (state->prehook_queue, (GFunc)g_object_unref, NULL);
+ g_slist_free (state->prehook_queue);
+
+ g_slist_foreach (state->posthook_queue, (GFunc)g_object_unref, NULL);
+ g_slist_free (state->posthook_queue);
+
+ g_slice_free (IdeRunnerRunState, state);
+}
+
+static void
+ide_runner_run_wait_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSubprocess *subprocess = (GSubprocess *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (G_IS_SUBPROCESS (subprocess));
+
+ if (!g_subprocess_wait_finish (subprocess, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess))
+ {
+ gint exit_code;
+
+ exit_code = g_subprocess_get_exit_status (subprocess);
+
+ if (exit_code == EXIT_SUCCESS)
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+ }
+
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "%s",
+ _("Process quit unexpectedly"));
+}
+
+static void
+ide_runner_real_run_async (IdeRunner *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(GSubprocess) subprocess = NULL;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_RUNNER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_runner_real_run_async);
+
+ launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+
+ for (GList *iter = priv->argv.head; iter != NULL; iter = iter->next)
+ ide_subprocess_launcher_push_argv (launcher, iter->data);
+
+ ide_subprocess_launcher_set_cwd (launcher, g_get_home_dir ());
+
+ subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, &error);
+
+ if (subprocess == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_subprocess_wait_async (subprocess,
+ cancellable,
+ ide_runner_run_wait_cb,
+ g_steal_pointer (&task));
+}
+
+static gboolean
+ide_runner_real_run_finish (IdeRunner *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_RUNNER (self));
+ g_assert (G_IS_TASK (result));
+ g_assert (g_task_is_valid (G_TASK (result), self));
+ g_assert (g_task_get_source_tag (G_TASK (result)) == ide_runner_real_run_async);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_runner_extension_added (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunnerAddin *addin = (IdeRunnerAddin *)exten;
+ IdeRunner *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUNNER_ADDIN (addin));
+ g_assert (IDE_IS_RUNNER (self));
+
+ ide_runner_addin_load (addin, self);
+}
+
+static void
+ide_runner_extension_removed (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunnerAddin *addin = (IdeRunnerAddin *)exten;
+ IdeRunner *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUNNER_ADDIN (addin));
+ g_assert (IDE_IS_RUNNER (self));
+
+ ide_runner_addin_unload (addin, self);
+}
+
+static void
+ide_runner_constructed (GObject *object)
+{
+ IdeRunner *self = (IdeRunner *)object;
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ G_OBJECT_CLASS (ide_runner_parent_class)->constructed (object);
+
+ priv->addins = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_RUNNER_ADDIN,
+ NULL);
+
+ g_signal_connect (priv->addins,
+ "extension-added",
+ G_CALLBACK (ide_runner_extension_added),
+ self);
+
+ g_signal_connect (priv->addins,
+ "extension-removed",
+ G_CALLBACK (ide_runner_extension_removed),
+ self);
+
+ peas_extension_set_foreach (priv->addins,
+ ide_runner_extension_added,
+ self);
+}
+
+static void
+ide_runner_finalize (GObject *object)
+{
+ IdeRunner *self = (IdeRunner *)object;
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ g_queue_foreach (&priv->argv, (GFunc)g_free, NULL);
+ g_queue_clear (&priv->argv);
+
+ G_OBJECT_CLASS (ide_runner_parent_class)->finalize (object);
+}
+
+static void
+ide_runner_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeRunner *self = IDE_RUNNER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ARGV:
+ g_value_take_boxed (value, ide_runner_get_argv (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_runner_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeRunner *self = IDE_RUNNER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ARGV:
+ ide_runner_set_argv (self, g_value_get_boxed (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_runner_class_init (IdeRunnerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = ide_runner_constructed;
+ object_class->finalize = ide_runner_finalize;
+ object_class->get_property = ide_runner_get_property;
+ object_class->set_property = ide_runner_set_property;
+
+ klass->run_async = ide_runner_real_run_async;
+ klass->run_finish = ide_runner_real_run_finish;
+
+ properties [PROP_ARGV] =
+ g_param_spec_boxed ("argv",
+ "Argv",
+ "The argument list for the command",
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_runner_init (IdeRunner *self)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ g_queue_init (&priv->argv);
+}
+
+/**
+ * ide_runner_get_stdin:
+ *
+ * Returns: (nullable) (transfer full): An #GOutputStream or %NULL.
+ */
+GInputStream *
+ide_runner_get_stdin (IdeRunner *self)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER (self), NULL);
+
+ return IDE_RUNNER_GET_CLASS (self)->get_stdin (self);
+}
+
+/**
+ * ide_runner_get_stdout:
+ *
+ * Returns: (nullable) (transfer full): An #GOutputStream or %NULL.
+ */
+GOutputStream *
+ide_runner_get_stdout (IdeRunner *self)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER (self), NULL);
+
+ return IDE_RUNNER_GET_CLASS (self)->get_stdout (self);
+}
+
+/**
+ * ide_runner_get_stderr:
+ *
+ * Returns: (nullable) (transfer full): An #GOutputStream or %NULL.
+ */
+GOutputStream *
+ide_runner_get_stderr (IdeRunner *self)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER (self), NULL);
+
+ return IDE_RUNNER_GET_CLASS (self)->get_stderr (self);
+}
+
+void
+ide_runner_force_quit (IdeRunner *self)
+{
+ g_return_if_fail (IDE_IS_RUNNER (self));
+
+ IDE_RUNNER_GET_CLASS (self)->force_quit (self);
+}
+
+void
+ide_runner_set_argv (IdeRunner *self,
+ const gchar * const *argv)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+ guint i;
+
+ g_return_if_fail (IDE_IS_RUNNER (self));
+
+ g_queue_foreach (&priv->argv, (GFunc)g_free, NULL);
+ g_queue_clear (&priv->argv);
+
+ if (argv != NULL)
+ {
+ for (i = 0; argv [i]; i++)
+ g_queue_push_tail (&priv->argv, g_strdup (argv [i]));
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ARGV]);
+}
+
+/**
+ * ide_runner_get_argv:
+ *
+ * Gets the argument list as a newly allocated string array.
+ *
+ * Returns: (transfer full): A newly allocated string array that should
+ * be freed with g_strfreev().
+ */
+gchar **
+ide_runner_get_argv (IdeRunner *self)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+ GPtrArray *ar;
+ GList *iter;
+
+ g_return_val_if_fail (IDE_IS_RUNNER (self), NULL);
+
+ ar = g_ptr_array_new ();
+
+ for (iter = priv->argv.head; iter != NULL; iter = iter->next)
+ {
+ const gchar *param = iter->data;
+
+ g_ptr_array_add (ar, g_strdup (param));
+ }
+
+ g_ptr_array_add (ar, NULL);
+
+ return (gchar **)g_ptr_array_free (ar, FALSE);
+}
+
+static void
+ide_runner_collect_addins_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ GSList **list = user_data;
+
+ *list = g_slist_prepend (*list, exten);
+}
+
+static void
+ide_runner_collect_addins (IdeRunner *self,
+ GSList **list)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ g_assert (IDE_IS_RUNNER (self));
+ g_assert (list != NULL);
+
+ peas_extension_set_foreach (priv->addins,
+ ide_runner_collect_addins_cb,
+ list);
+}
+
+static void
+ide_runner_posthook_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunnerAddin *addin = (IdeRunnerAddin *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_RUNNER_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!ide_runner_addin_posthook_finish (addin, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ ide_runner_tick_posthook (task);
+}
+
+static void
+ide_runner_tick_posthook (GTask *task)
+{
+ IdeRunnerRunState *state;
+
+ g_assert (G_IS_TASK (task));
+
+ state = g_task_get_task_data (task);
+
+ if (state->posthook_queue != NULL)
+ {
+ g_autoptr(IdeRunnerAddin) addin = NULL;
+
+ addin = pop_runner_addin (&state->posthook_queue);
+ ide_runner_addin_posthook_async (addin,
+ g_task_get_cancellable (task),
+ ide_runner_posthook_cb,
+ g_object_ref (task));
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_runner_run_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunner *self = (IdeRunner *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_RUNNER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!IDE_RUNNER_GET_CLASS (self)->run_finish (self, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ ide_runner_tick_posthook (task);
+}
+
+static void
+ide_runner_tick_run (GTask *task)
+{
+ IdeRunner *self;
+
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+
+ IDE_RUNNER_GET_CLASS (self)->run_async (self,
+ g_task_get_cancellable (task),
+ ide_runner_run_cb,
+ g_object_ref (task));
+}
+
+static void
+ide_runner_prehook_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunnerAddin *addin = (IdeRunnerAddin *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_RUNNER_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!ide_runner_addin_prehook_finish (addin, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ ide_runner_tick_prehook (task);
+}
+
+static void
+ide_runner_tick_prehook (GTask *task)
+{
+ IdeRunnerRunState *state;
+
+ g_assert (G_IS_TASK (task));
+
+ state = g_task_get_task_data (task);
+
+ if (state->prehook_queue != NULL)
+ {
+ g_autoptr(IdeRunnerAddin) addin = NULL;
+
+ addin = pop_runner_addin (&state->prehook_queue);
+ ide_runner_addin_prehook_async (addin,
+ g_task_get_cancellable (task),
+ ide_runner_prehook_cb,
+ g_object_ref (task));
+ return;
+ }
+
+ ide_runner_tick_run (task);
+}
+
+void
+ide_runner_run_async (IdeRunner *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ IdeRunnerRunState *state;
+
+ g_return_if_fail (IDE_IS_RUNNER (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_runner_run_async);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_priority (task, G_PRIORITY_LOW);
+
+ /*
+ * We need to run the prehook functions for each addin first before we
+ * can call our IdeRunnerClass.run vfunc. Since these are async, we
+ * have to bring some state along with us.
+ */
+ state = g_slice_new0 (IdeRunnerRunState);
+ ide_runner_collect_addins (self, &state->prehook_queue);
+ ide_runner_collect_addins (self, &state->posthook_queue);
+ g_task_set_task_data (task, state, ide_runner_run_state_free);
+
+ ide_runner_tick_prehook (task);
+}
+
+gboolean
+ide_runner_run_finish (IdeRunner *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_RUNNER (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+ide_runner_append_argv (IdeRunner *self,
+ const gchar *param)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUNNER (self));
+ g_return_if_fail (param != NULL);
+
+ g_queue_push_tail (&priv->argv, g_strdup (param));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ARGV]);
+}
+
+void
+ide_runner_prepend_argv (IdeRunner *self,
+ const gchar *param)
+{
+ IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUNNER (self));
+ g_return_if_fail (param != NULL);
+
+ g_queue_push_head (&priv->argv, g_strdup (param));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ARGV]);
+}
+
+IdeRunner *
+ide_runner_new (IdeContext *context)
+{
+ g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+ return g_object_new (IDE_TYPE_RUNNER,
+ "context", context,
+ NULL);
+}
diff --git a/libide/runner/ide-runner.h b/libide/runner/ide-runner.h
index 94f2c12..ff7aa12 100644
--- a/libide/runner/ide-runner.h
+++ b/libide/runner/ide-runner.h
@@ -19,7 +19,9 @@
#ifndef IDE_RUNNER_H
#define IDE_RUNNER_H
-#include "ide-types.h"
+#include <gio/gio.h>
+
+#include "ide-object.h"
G_BEGIN_DECLS
@@ -27,7 +29,9 @@ typedef enum
{
IDE_RUNNER_INVALID,
IDE_RUNNER_READY,
+ IDE_RUNNER_PREHOOK,
IDE_RUNNER_RUNNING,
+ IDE_RUNNER_POSTHOOK,
IDE_RUNNER_EXITED,
IDE_RUNNER_FAILED,
} IdeRunnerState;
@@ -38,15 +42,40 @@ G_DECLARE_DERIVABLE_TYPE (IdeRunner, ide_runner, IDE, RUNNER, IdeObject)
struct _IdeRunnerClass
{
- IdeObject parent_instance;
+ IdeObjectClass parent;
- void (*force_quit) (IdeRunner *self);
- void (*run) (IdeRunner *self);
+ void (*force_quit) (IdeRunner *self);
+ GInputStream *(*get_stdin) (IdeRunner *self);
+ GOutputStream *(*get_stdout) (IdeRunner *self);
+ GOutputStream *(*get_stderr) (IdeRunner *self);
+ void (*run_async) (IdeRunner *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*run_finish) (IdeRunner *self,
+ GAsyncResult *result,
+ GError **error);
};
-IdeRunner *ide_runner_new (IdeContext *context);
-void ide_runner_force_quit (IdeRunner *self);
-void ide_runner_run (IdeRunner *self);
+IdeRunner *ide_runner_new (IdeContext *context);
+void ide_runner_force_quit (IdeRunner *self);
+void ide_runner_run_async (IdeRunner *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_runner_run_finish (IdeRunner *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_runner_prepend_argv (IdeRunner *self,
+ const gchar *param);
+void ide_runner_append_argv (IdeRunner *self,
+ const gchar *param);
+gchar **ide_runner_get_argv (IdeRunner *self);
+void ide_runner_set_argv (IdeRunner *self,
+ const gchar * const *argv);
+GInputStream *ide_runner_get_stdin (IdeRunner *self);
+GOutputStream *ide_runner_get_stdout (IdeRunner *self);
+GOutputStream *ide_runner_get_stderr (IdeRunner *self);
G_END_DECLS
diff --git a/libide/runtimes/ide-runtime.c b/libide/runtimes/ide-runtime.c
index a67f9d1..bba4b26 100644
--- a/libide/runtimes/ide-runtime.c
+++ b/libide/runtimes/ide-runtime.c
@@ -169,6 +169,36 @@ ide_runtime_real_prepare_configuration (IdeRuntime *self,
ide_configuration_set_prefix (configuration, install_path);
}
+static IdeRunner *
+ide_runtime_real_create_runner (IdeRuntime *self,
+ IdeBuildTarget *build_target)
+{
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *binpath = NULL;
+ g_autoptr(GFile) installdir = NULL;
+ g_autoptr(GFile) bin = NULL;
+ IdeContext *context;
+ IdeRunner *runner;
+
+ g_assert (IDE_IS_RUNTIME (self));
+ g_assert (IDE_IS_BUILD_TARGET (build_target));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ runner = ide_runner_new (context);
+
+ g_object_get (build_target,
+ "install-directory", &installdir,
+ "name", &name,
+ NULL);
+
+ bin = g_file_get_child (installdir, name);
+ binpath = g_file_get_path (bin);
+
+ ide_runner_append_argv (runner, binpath);
+
+ return runner;
+}
+
static void
ide_runtime_finalize (GObject *object)
{
@@ -240,6 +270,7 @@ ide_runtime_class_init (IdeRuntimeClass *klass)
klass->postbuild_async = ide_runtime_real_postbuild_async;
klass->postbuild_finish = ide_runtime_real_postbuild_finish;
klass->create_launcher = ide_runtime_real_create_launcher;
+ klass->create_runner = ide_runtime_real_create_runner;
klass->contains_program_in_path = ide_runtime_real_contains_program_in_path;
klass->prepare_configuration = ide_runtime_real_prepare_configuration;
@@ -407,3 +438,23 @@ ide_runtime_prepare_configuration (IdeRuntime *self,
IDE_RUNTIME_GET_CLASS (self)->prepare_configuration (self, configuration);
}
+
+/**
+ * ide_runtime_create_runner:
+ *
+ * Creates a new runner that can be used to execute the build target within
+ * the runtime. This should be used to implement such features as "run target"
+ * or "run unit test" inside the target runtime.
+ *
+ * Returns: (transfer full) (nullable): An #IdeRunner if successful, otherwise
+ * %NULL and @error is set.
+ */
+IdeRunner *
+ide_runtime_create_runner (IdeRuntime *self,
+ IdeBuildTarget *build_target)
+{
+ g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
+ g_return_val_if_fail (IDE_IS_BUILD_TARGET (build_target), NULL);
+
+ return IDE_RUNTIME_GET_CLASS (self)->create_runner (self, build_target);
+}
diff --git a/libide/runtimes/ide-runtime.h b/libide/runtimes/ide-runtime.h
index daa0fe0..58ef1b0 100644
--- a/libide/runtimes/ide-runtime.h
+++ b/libide/runtimes/ide-runtime.h
@@ -23,6 +23,8 @@
#include "ide-object.h"
+#include "buildsystem/ide-build-target.h"
+#include "runner/ide-runner.h"
#include "workers/ide-subprocess-launcher.h"
G_BEGIN_DECLS
@@ -56,6 +58,25 @@ struct _IdeRuntimeClass
GError **error);
void (*prepare_configuration) (IdeRuntime *self,
IdeConfiguration *configuration);
+ IdeRunner *(*create_runner) (IdeRuntime *self,
+ IdeBuildTarget *build_target);
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
+ gpointer _reserved9;
+ gpointer _reserved10;
+ gpointer _reserved11;
+ gpointer _reserved12;
+ gpointer _reserved13;
+ gpointer _reserved14;
+ gpointer _reserved15;
+ gpointer _reserved16;
};
void ide_runtime_prebuild_async (IdeRuntime *self,
@@ -77,6 +98,8 @@ gboolean ide_runtime_contains_program_in_path (IdeRuntime
GCancellable *cancellable);
IdeSubprocessLauncher *ide_runtime_create_launcher (IdeRuntime *self,
GError **error);
+IdeRunner *ide_runtime_create_runner (IdeRuntime *self,
+ IdeBuildTarget *build_target);
void ide_runtime_prepare_configuration (IdeRuntime *self,
IdeConfiguration *configuration);
IdeRuntime *ide_runtime_new (IdeContext *context,
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index ca2af88..d1617c8 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -2,11 +2,11 @@ SUBDIRS = \
autotools \
autotools-templates \
build-tools \
+ c-pack \
clang \
command-bar \
- contributing \
- c-pack \
comment-code \
+ contributing \
create-project \
ctags \
devhelp \
@@ -22,10 +22,11 @@ SUBDIRS = \
html-preview \
jedi \
jhbuild \
+ mingw \
project-tree \
python-gi-imports-completion \
- mingw \
python-pack \
+ run-tools \
support \
symbol-tree \
sysmon \
diff --git a/plugins/autotools/Makefile.am b/plugins/autotools/Makefile.am
index 195a390..24481ad 100644
--- a/plugins/autotools/Makefile.am
+++ b/plugins/autotools/Makefile.am
@@ -10,6 +10,8 @@ libautotools_plugin_la_SOURCES = \
ide-autotools-builder.h \
ide-autotools-build-system.c \
ide-autotools-build-system.h \
+ ide-autotools-build-target.c \
+ ide-autotools-build-target.h \
ide-autotools-build-task.c \
ide-autotools-build-task.h \
ide-autotools-project-miner.c \
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 12c9f97..9eb1807 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -546,6 +546,94 @@ ide_autotools_build_system_constructed (GObject *object)
G_CONNECT_SWAPPED);
}
+static void
+ide_autotools_build_system_get_build_targets_cb2 (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeMakecache *makecache = (IdeMakecache *)object;
+ g_autoptr(GTask) task = user_data;
+ GPtrArray *ret;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_MAKECACHE (makecache));
+ g_assert (G_IS_TASK (task));
+
+ ret = ide_makecache_get_build_targets_finish (makecache, result, &error);
+
+ if (ret == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_pointer (task, ret, (GDestroyNotify)g_ptr_array_unref);
+}
+
+static void
+ide_autotools_build_system_get_build_targets_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
+ g_autoptr(IdeMakecache) makecache = NULL;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
+ g_assert (G_IS_TASK (task));
+
+ makecache = ide_autotools_build_system_get_makecache_finish (self, result, &error);
+
+ if (makecache == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ ide_makecache_get_build_targets_async (makecache,
+ g_task_get_cancellable (task),
+ ide_autotools_build_system_get_build_targets_cb2,
+ g_object_ref (task));
+}
+
+static void
+ide_autotools_build_system_get_build_targets_async (IdeBuildSystem *build_system,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_autotools_build_system_get_build_targets_async);
+
+ ide_autotools_build_system_get_makecache_async (self,
+ cancellable,
+ ide_autotools_build_system_get_build_targets_cb,
+ g_object_ref (task));
+}
+
+static GPtrArray *
+ide_autotools_build_system_get_build_targets_finish (IdeBuildSystem *build_system,
+ GAsyncResult *result,
+ GError **error)
+{
+ IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
+ GTask *task = (GTask *)result;
+
+ g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
+ g_assert (G_IS_TASK (task));
+ g_assert (g_task_is_valid (task, self));
+ g_assert (g_task_get_source_tag (task) == ide_autotools_build_system_get_build_targets_async);
+
+ return g_task_propagate_pointer (task, error);
+}
+
static gint
ide_autotools_build_system_get_priority (IdeBuildSystem *system)
{
@@ -614,6 +702,8 @@ build_system_iface_init (IdeBuildSystemInterface *iface)
iface->get_builder = ide_autotools_build_system_get_builder;
iface->get_build_flags_async = ide_autotools_build_system_get_build_flags_async;
iface->get_build_flags_finish = ide_autotools_build_system_get_build_flags_finish;
+ iface->get_build_targets_async = ide_autotools_build_system_get_build_targets_async;
+ iface->get_build_targets_finish = ide_autotools_build_system_get_build_targets_finish;
}
static void
diff --git a/plugins/autotools/ide-autotools-build-target.c b/plugins/autotools/ide-autotools-build-target.c
new file mode 100644
index 0000000..909468a
--- /dev/null
+++ b/plugins/autotools/ide-autotools-build-target.c
@@ -0,0 +1,147 @@
+/* ide-autotools-build-target.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-autotools-build-target"
+
+#include "ide-autotools-build-target.h"
+
+struct _IdeAutotoolsBuildTarget
+{
+ IdeObject parent_instance;
+
+ GFile *build_directory;
+ GFile *install_directory;
+ gchar *name;
+};
+
+enum {
+ PROP_0,
+ PROP_BUILD_DIRECTORY,
+ PROP_INSTALL_DIRECTORY,
+ PROP_NAME,
+ N_PROPS
+};
+
+G_DEFINE_TYPE_EXTENDED (IdeAutotoolsBuildTarget, ide_autotools_build_target, IDE_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_TARGET, NULL))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_autotools_build_target_finalize (GObject *object)
+{
+ IdeAutotoolsBuildTarget *self = (IdeAutotoolsBuildTarget *)object;
+
+ g_clear_object (&self->build_directory);
+ g_clear_object (&self->install_directory);
+ g_clear_pointer (&self->name, g_free);
+
+ G_OBJECT_CLASS (ide_autotools_build_target_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_build_target_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeAutotoolsBuildTarget *self = IDE_AUTOTOOLS_BUILD_TARGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUILD_DIRECTORY:
+ g_value_set_object (value, self->build_directory);
+ break;
+
+ case PROP_INSTALL_DIRECTORY:
+ g_value_set_object (value, self->install_directory);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, self->name);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_autotools_build_target_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeAutotoolsBuildTarget *self = IDE_AUTOTOOLS_BUILD_TARGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUILD_DIRECTORY:
+ self->build_directory = g_value_dup_object (value);
+ break;
+
+ case PROP_INSTALL_DIRECTORY:
+ self->install_directory = g_value_dup_object (value);
+ break;
+
+ case PROP_NAME:
+ self->name = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_autotools_build_target_class_init (IdeAutotoolsBuildTargetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_autotools_build_target_finalize;
+ object_class->get_property = ide_autotools_build_target_get_property;
+ object_class->set_property = ide_autotools_build_target_set_property;
+
+ properties [PROP_BUILD_DIRECTORY] =
+ g_param_spec_object ("build-directory",
+ NULL,
+ NULL,
+ G_TYPE_FILE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_INSTALL_DIRECTORY] =
+ g_param_spec_object ("install-directory",
+ NULL,
+ NULL,
+ G_TYPE_FILE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NAME] =
+ g_param_spec_string ("name",
+ NULL,
+ NULL,
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_autotools_build_target_init (IdeAutotoolsBuildTarget *self)
+{
+}
diff --git a/plugins/autotools/ide-autotools-build-target.h b/plugins/autotools/ide-autotools-build-target.h
new file mode 100644
index 0000000..986e17a
--- /dev/null
+++ b/plugins/autotools/ide-autotools-build-target.h
@@ -0,0 +1,32 @@
+/* ide-autotools-build-target.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_AUTOTOOLS_BUILD_TARGET_H
+#define IDE_AUTOTOOLS_BUILD_TARGET_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_BUILD_TARGET (ide_autotools_build_target_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsBuildTarget, ide_autotools_build_target, IDE, AUTOTOOLS_BUILD_TARGET,
IdeObject)
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_BUILD_TARGET_H */
diff --git a/plugins/autotools/ide-autotools-runner-addin.h b/plugins/autotools/ide-autotools-runner-addin.h
new file mode 100644
index 0000000..0220d33
--- /dev/null
+++ b/plugins/autotools/ide-autotools-runner-addin.h
@@ -0,0 +1,32 @@
+/* ide-autotools-runner-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 IDE_AUTOTOOLS_RUNNER_ADDIN_H
+#define IDE_AUTOTOOLS_RUNNER_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_RUNNER_ADDIN (ide_autotools_runner_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsRunnerAddin, ide_autotools_runner_addin, IDE, AUTOTOOLS_RUNNER_ADDIN,
GObject)
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_RUNNER_ADDIN_H */
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index 95cf869..9a61c62 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -35,16 +35,18 @@
#include <unistd.h>
#include <ide.h>
+#include "ide-autotools-build-target.h"
#include "ide-makecache.h"
#include "ide-makecache-target.h"
-#define FAKE_CC "__LIBIDE_FAKE_CC__"
-#define FAKE_CXX "__LIBIDE_FAKE_CXX__"
-#define FAKE_VALAC "__LIBIDE_FAKE_VALAC__"
+#define FAKE_CC "__LIBIDE_FAKE_CC__"
+#define FAKE_CXX "__LIBIDE_FAKE_CXX__"
+#define FAKE_VALAC "__LIBIDE_FAKE_VALAC__"
+#define PRINT_VARS "include Makefile\nprint-%: ; @echo $* = $($*)\n"
struct _IdeMakecache
{
- IdeObject parent_instance;
+ IdeObject parent_instance;
GFile *makefile;
GFile *parent;
@@ -52,6 +54,7 @@ struct _IdeMakecache
GMappedFile *mapped;
EggTaskCache *file_targets_cache;
EggTaskCache *file_flags_cache;
+ GPtrArray *build_targets;
};
typedef struct
@@ -1391,6 +1394,7 @@ ide_makecache_finalize (GObject *object)
g_clear_object (&self->file_targets_cache);
g_clear_object (&self->file_flags_cache);
g_clear_pointer (&self->llvm_flags, g_free);
+ g_clear_pointer (&self->build_targets, g_ptr_array_unref);
G_OBJECT_CLASS (ide_makecache_parent_class)->finalize (object);
@@ -1707,3 +1711,382 @@ ide_makecache_get_file_flags_finish (IdeMakecache *self,
IDE_RETURN (ret);
}
+
+static gboolean
+_find_make_directories (IdeMakecache *self,
+ GFile *dir,
+ GPtrArray *ret,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GFileEnumerator) enumerator = NULL;
+ g_autoptr(GPtrArray) dirs = NULL;
+ gboolean has_makefile = FALSE;
+ gboolean has_makefile_am = FALSE;
+ GError *local_error = NULL;
+ gpointer infoptr;
+ guint i;
+
+ g_assert (IDE_IS_MAKECACHE (self));
+ g_assert (G_IS_FILE (dir));
+ g_assert (ret != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ enumerator = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ error);
+
+ dirs = g_ptr_array_new_with_free_func (g_object_unref);
+
+ while (NULL != (infoptr = g_file_enumerator_next_file (enumerator, cancellable, &local_error)))
+ {
+ g_autoptr(GFileInfo) info = infoptr;
+ const gchar *name;
+ GFileType type;
+
+ name = g_file_info_get_name (info);
+ type = g_file_info_get_file_type (info);
+
+ if (g_strcmp0 (name, "Makefile") == 0)
+ has_makefile = TRUE;
+ if (g_strcmp0 (name, "Makefile.am") == 0)
+ has_makefile_am = TRUE;
+ else if (type == G_FILE_TYPE_DIRECTORY)
+ g_ptr_array_add (dirs, g_file_get_child (dir, name));
+ }
+
+ if (local_error != NULL)
+ {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ if (has_makefile && has_makefile_am)
+ g_ptr_array_add (ret, g_object_ref (dir));
+
+ if (!g_file_enumerator_close (enumerator, cancellable, error))
+ return FALSE;
+
+ for (i = 0; i < dirs->len; i++)
+ {
+ GFile *item = g_ptr_array_index (dirs, i);
+
+ if (!_find_make_directories (self, item, ret, cancellable, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GPtrArray *
+find_make_directories (IdeMakecache *self,
+ GFile *root,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GPtrArray) ret = NULL;
+
+ g_assert (IDE_IS_MAKECACHE (self));
+ g_assert (G_IS_FILE (root));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /*
+ * TODO: Make this work for builddir != srcdir.
+ */
+
+ ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+ if (!_find_make_directories (self, root, ret, cancellable, error))
+ return NULL;
+
+ if (ret->len == 0)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "No targets were found");
+ return NULL;
+ }
+
+ return g_steal_pointer (&ret);
+}
+
+static GFile *
+find_install_dir (const gchar *key,
+ GHashTable *dirs)
+{
+ g_auto(GStrv) parts = g_strsplit (key, "_", 2);
+ g_autofree gchar *lookup = g_strdup_printf ("%sdir", parts[0]);
+ const gchar *path = g_hash_table_lookup (dirs, lookup);
+
+ if (path != NULL)
+ return g_file_new_for_path (path);
+
+ return NULL;
+}
+
+static void
+ide_makecache_get_build_targets_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeMakecache *self = source_object;
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(GPtrArray) makedirs = NULL;
+ g_autoptr(GPtrArray) targets = NULL;
+ g_autofree gchar *stdout_buf = NULL;
+ IdeConfigurationManager *configmgr;
+ IdeConfiguration *config;
+ const gchar *make_name = "make";
+ IdeContext *context;
+ IdeRuntime *runtime;
+ IdeVcs *vcs;
+ GFile *workdir;
+ GError *error = NULL;
+ gchar *line;
+ gsize line_len;
+ IdeLineReader reader;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_MAKECACHE (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /*
+ * This works by performing a dry run using the fake install path. We then
+ * extract things that are installed into locations that look like they could
+ * be binaries. It's not foolproof, but generally gets the job done.
+ *
+ * We don't pass the tasks #GCancellable into many operations because we
+ * don't want the operation to fail. This is because we cache the results of
+ * this function and want them to be available for future access.
+ */
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ configmgr = ide_context_get_configuration_manager (context);
+ config = ide_configuration_manager_get_current (configmgr);
+ runtime = ide_configuration_get_runtime (config);
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+
+ if (runtime != NULL)
+ launcher = ide_runtime_create_launcher (runtime, NULL);
+
+ if (launcher == NULL)
+ {
+ g_autofree gchar *path = NULL;
+ path = g_file_get_path (workdir);
+
+ launcher = ide_subprocess_launcher_new (0);
+ ide_subprocess_launcher_set_cwd (launcher, path);
+ }
+
+ ide_subprocess_launcher_set_flags (launcher,
+ (G_SUBPROCESS_FLAGS_STDIN_PIPE |
+ G_SUBPROCESS_FLAGS_STDOUT_PIPE));
+
+ /* Default to "make" in runtimes other than the host, since we cannot
+ * rely on our configure-time check for the path there. This isn't totally
+ * correct, since we could be in jhbuild.
+ *
+ * TODO: We might want to rely on the runtime to discover basic utilities
+ * like GNU Make.
+ */
+ if (g_strcmp0 (ide_configuration_get_runtime_id (config), "host") == 0)
+ make_name = GNU_MAKE_NAME;
+
+ ide_subprocess_launcher_push_argv (launcher, make_name);
+ ide_subprocess_launcher_push_argv (launcher, "-f");
+ ide_subprocess_launcher_push_argv (launcher, "-");
+ 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-noinst_PROGRAMS");
+ ide_subprocess_launcher_push_argv (launcher, "print-libexec_PROGRAMS");
+
+ /*
+ * We need to extract the common automake targets from each of the
+ * directories that we know there is a standalone Makefile within.
+ */
+
+ makedirs = find_make_directories (self, workdir, cancellable, &error);
+
+ if (makedirs == NULL)
+ {
+ g_task_return_error (task, error);
+ IDE_GOTO (failure);
+ }
+
+ /*
+ * We need to extract various programs/libraries/targets from each of
+ * our make directories containing a Makefile.am (translated into a Makefile
+ * so that we can know what targets are available. With that knowledge, we
+ * can build our targets list and cache it for later.
+ */
+
+ targets = g_ptr_array_new_with_free_func (g_object_unref);
+
+ for (guint j = 0; j < makedirs->len; j++)
+ {
+ g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(GHashTable) amdirs = NULL;
+ g_autofree gchar *path = NULL;
+ GFile *makedir;
+
+ /*
+ * Make sure we are running within the directory containing the
+ * Makefile.am that we care about.
+ */
+ makedir = g_ptr_array_index (makedirs, j);
+ path = g_file_get_path (makedir);
+ ide_subprocess_launcher_set_cwd (launcher, path);
+
+ /*
+ * Spawn make, waiting for our stdin input which will add our debug
+ * printf target.
+ */
+ if (NULL == (subprocess = ide_subprocess_launcher_spawn_sync (launcher, NULL, &error)))
+ {
+ g_task_return_error (task, error);
+ IDE_GOTO (failure);
+ }
+
+ /*
+ * Write our helper target that will include the Makefile and then print
+ * debug variables we care about.
+ */
+ if (!g_subprocess_communicate_utf8 (subprocess, PRINT_VARS, NULL, &stdout_buf, NULL, &error))
+ {
+ g_task_return_error (task, error);
+ IDE_GOTO (failure);
+ }
+
+ amdirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ /*
+ * Read through the output from make, and parse the installation targets
+ * that we care about.
+ */
+ ide_line_reader_init (&reader, stdout_buf, -1);
+
+ while (NULL != (line = ide_line_reader_next (&reader, &line_len)))
+ {
+ g_auto(GStrv) parts = NULL;
+ g_auto(GStrv) names = NULL;
+ const gchar *key;
+
+ line [line_len] = '\0';
+
+ parts = g_strsplit (line, "=", 2);
+
+ if (!parts[0] || !parts[1])
+ continue;
+
+ g_strstrip (parts [0]);
+ g_strstrip (parts [1]);
+
+ key = parts [0];
+
+ if (g_str_has_suffix (key, "dir"))
+ {
+ g_hash_table_insert (amdirs, g_strdup (key), g_strdup (parts [1]));
+ continue;
+ }
+
+ names = g_strsplit (parts [1], " ", 0);
+
+ for (guint i = 0; names [i]; i++)
+ {
+ g_autoptr(IdeBuildTarget) target = NULL;
+ g_autoptr(GFile) installdir = NULL;
+ const gchar *name = names [i];
+
+ installdir = find_install_dir (key, amdirs);
+
+ target = g_object_new (IDE_TYPE_AUTOTOOLS_BUILD_TARGET,
+ "build-directory", makedir,
+ "context", context,
+ "install-directory", installdir,
+ "name", name,
+ NULL);
+
+ g_ptr_array_add (targets, g_steal_pointer (&target));
+ }
+ }
+ }
+
+ g_task_return_pointer (task, g_steal_pointer (&targets), (GDestroyNotify)g_ptr_array_unref);
+
+failure:
+ IDE_EXIT;
+}
+
+void
+ide_makecache_get_build_targets_async (IdeMakecache *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ GPtrArray *ret;
+ guint i;
+
+ g_return_if_fail (IDE_IS_MAKECACHE (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_makecache_get_build_targets_async);
+ g_task_set_check_cancellable (task, FALSE);
+
+ if (self->build_targets == NULL)
+ {
+ g_task_run_in_thread (task, ide_makecache_get_build_targets_worker);
+ return;
+ }
+
+ ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+ for (i = 0; i < self->build_targets->len; i++)
+ {
+ IdeBuildTarget *target = g_ptr_array_index (self->build_targets, i);
+
+ g_ptr_array_add (ret, g_object_ref (target));
+ }
+
+ g_task_return_pointer (task, ret, (GDestroyNotify)g_ptr_array_unref);
+}
+
+GPtrArray *
+ide_makecache_get_build_targets_finish (IdeMakecache *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GPtrArray *ret;
+
+ g_return_val_if_fail (IDE_IS_MAKECACHE (self), NULL);
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+ ret = g_task_propagate_pointer (G_TASK (result), error);
+
+ /*
+ * Save a copy of all the build targets for future lookups.
+ */
+ if (ret != NULL && self->build_targets == NULL)
+ {
+ self->build_targets = g_ptr_array_new_with_free_func (g_object_unref);
+
+ for (guint i = 0; i < ret->len; i++)
+ {
+ IdeBuildTarget *item = g_ptr_array_index (ret, i);
+
+ g_ptr_array_add (self->build_targets, g_object_ref (item));
+ }
+ }
+
+ return ret;
+}
diff --git a/plugins/autotools/ide-makecache.h b/plugins/autotools/ide-makecache.h
index 42ed3d0..9fba3c2 100644
--- a/plugins/autotools/ide-makecache.h
+++ b/plugins/autotools/ide-makecache.h
@@ -29,30 +29,37 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeMakecache, ide_makecache, IDE, MAKECACHE, IdeObject)
-void ide_makecache_new_for_makefile_async (IdeContext *context,
- GFile *makefile,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IdeMakecache *ide_makecache_new_for_makefile_finish (GAsyncResult *result,
- GError **error);
-GFile *ide_makecache_get_makefile (IdeMakecache *self);
-void ide_makecache_get_file_flags_async (IdeMakecache *self,
- GFile *file,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gchar **ide_makecache_get_file_flags_finish (IdeMakecache *self,
- GAsyncResult *result,
- GError **error);
-void ide_makecache_get_file_targets_async (IdeMakecache *self,
- GFile *file,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-GPtrArray *ide_makecache_get_file_targets_finish (IdeMakecache *self,
- GAsyncResult *result,
- GError **error);
+void ide_makecache_new_for_makefile_async (IdeContext *context,
+ GFile *makefile,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeMakecache *ide_makecache_new_for_makefile_finish (GAsyncResult *result,
+ GError **error);
+GFile *ide_makecache_get_makefile (IdeMakecache *self);
+void ide_makecache_get_file_flags_async (IdeMakecache *self,
+ GFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar **ide_makecache_get_file_flags_finish (IdeMakecache *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_makecache_get_file_targets_async (IdeMakecache *self,
+ GFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GPtrArray *ide_makecache_get_file_targets_finish (IdeMakecache *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_makecache_get_build_targets_async (IdeMakecache *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GPtrArray *ide_makecache_get_build_targets_finish (IdeMakecache *self,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/plugins/build-tools/gbp-build-workbench-addin.c b/plugins/build-tools/gbp-build-workbench-addin.c
index 9511e32..1b6c80a 100644
--- a/plugins/build-tools/gbp-build-workbench-addin.c
+++ b/plugins/build-tools/gbp-build-workbench-addin.c
@@ -300,7 +300,6 @@ gbp_build_workbench_addin_load (IdeWorkbenchAddin *addin,
{
IdeConfigurationManager *configuration_manager;
GbpBuildWorkbenchAddin *self = (GbpBuildWorkbenchAddin *)addin;
- IdeWorkbenchHeaderBar *header;
IdeConfiguration *configuration;
IdePerspective *editor;
IdeContext *context;
@@ -332,23 +331,6 @@ gbp_build_workbench_addin_load (IdeWorkbenchAddin *addin,
g_object_bind_property (self, "result", self->panel, "result", 0);
- header = ide_workbench_get_headerbar (workbench);
-
- /* XXX: Button is hidden until we add run support */
- self->run_button = g_object_new (GTK_TYPE_BUTTON,
- "child", g_object_new (GTK_TYPE_IMAGE,
- "icon-name", "media-playback-start-symbolic",
- "visible", TRUE,
- NULL),
- "visible", FALSE,
- NULL);
- ide_widget_add_style_class (self->run_button, "image-button");
-
- ide_workbench_header_bar_insert_right (header,
- self->run_button,
- GTK_PACK_START,
- 0);
-
self->build_perspective = g_object_new (GBP_TYPE_BUILD_PERSPECTIVE,
"configuration-manager", configuration_manager,
"configuration", configuration,
@@ -464,30 +446,8 @@ gbp_build_workbench_addin_init (GbpBuildWorkbenchAddin *self)
}
static void
-gbp_build_workbench_addin_perpsective_set (IdeWorkbenchAddin *addin,
- IdePerspective *perspective)
-{
- GbpBuildWorkbenchAddin *self = (GbpBuildWorkbenchAddin *)addin;
-
- g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
-
- /* XXX: Hidden until we add run support */
-#if 0
- if (IDE_IS_EDITOR_PERSPECTIVE (perspective))
- {
- gtk_widget_show (self->run_button);
- }
- else
- {
- gtk_widget_hide (self->run_button);
- }
-#endif
-}
-
-static void
workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
{
iface->load = gbp_build_workbench_addin_load;
iface->unload = gbp_build_workbench_addin_unload;
- iface->perspective_set = gbp_build_workbench_addin_perpsective_set;
}
diff --git a/plugins/run-tools/Makefile.am b/plugins/run-tools/Makefile.am
new file mode 100644
index 0000000..c3dc7cf
--- /dev/null
+++ b/plugins/run-tools/Makefile.am
@@ -0,0 +1,27 @@
+if ENABLE_RUN_TOOLS_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = librun-tools-plugin.la
+dist_plugin_DATA = run-tools.plugin
+
+librun_tools_plugin_la_CFLAGS = \
+ $(PLUGIN_CFLAGS) \
+ -DG_LOG_DOMAIN="\"run-tools\"" \
+ $(NULL)
+librun_tools_plugin_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+librun_tools_plugin_la_SOURCES = \
+ gbp-run-plugin.c \
+ gbp-run-workbench-addin.c \
+ gbp-run-workbench-addin.h \
+ $(NULL)
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/run-tools/configure.ac b/plugins/run-tools/configure.ac
new file mode 100644
index 0000000..61b1378
--- /dev/null
+++ b/plugins/run-tools/configure.ac
@@ -0,0 +1,12 @@
+# --enable-run-tools-plugin=yes/no
+AC_ARG_ENABLE([run-tools-plugin],
+ [AS_HELP_STRING([--enable-run-tools-plugin=@<:@yes/no@:>@],
+ [Run with support for run tools and panels.])],
+ [enable_run_tools_plugin=$enableval],
+ [enable_run_tools_plugin=yes])
+
+# for if ENABLE_RUN_TOOLS_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_RUN_TOOLS_PLUGIN, test x$enable_run_tools_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/run-tools/Makefile])
diff --git a/plugins/run-tools/gbp-run-plugin.c b/plugins/run-tools/gbp-run-plugin.c
new file mode 100644
index 0000000..cae5a1d
--- /dev/null
+++ b/plugins/run-tools/gbp-run-plugin.c
@@ -0,0 +1,28 @@
+/* gbp-run-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 <ide.h>
+#include <libpeas/peas.h>
+
+#include "gbp-run-workbench-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module, IDE_TYPE_WORKBENCH_ADDIN,
GBP_TYPE_RUN_WORKBENCH_ADDIN);
+}
diff --git a/plugins/run-tools/gbp-run-workbench-addin.c b/plugins/run-tools/gbp-run-workbench-addin.c
new file mode 100644
index 0000000..eb91e9b
--- /dev/null
+++ b/plugins/run-tools/gbp-run-workbench-addin.c
@@ -0,0 +1,260 @@
+/* gbp-run-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 "gbp-run-workbench-addin.h"
+
+struct _GbpRunWorkbenchAddin
+{
+ GObject parent_instance;
+
+ GCancellable *cancellable;
+ IdeWorkbench *workbench;
+};
+
+static void workbench_addin_init_iface (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpRunWorkbenchAddin, gbp_run_workbench_addin, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
+ workbench_addin_init_iface))
+
+static void
+gbp_run_workbench_addin_run_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunner *runner = (IdeRunner *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RUNNER (runner));
+ g_assert (G_IS_TASK (task));
+
+ if (!ide_runner_run_finish (runner, result, &error))
+ {
+ /* TODO: Use notification api */
+ g_warning ("%s", error->message);
+ g_task_return_error (task, error);
+ IDE_EXIT;
+ }
+
+ /* Update stop command/state */
+
+ g_task_return_boolean (task, TRUE);
+
+ 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);
+
+ 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;
+ g_autoptr(GPtrArray) targets = NULL;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(IdeRunner) runner = NULL;
+ IdeBuildTarget *best_match;
+ IdeRuntime *runtime;
+ 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));
+
+ runtime = g_task_get_task_data (task);
+ 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;
+ }
+
+ runner = ide_runtime_create_runner (runtime, best_match);
+
+ ide_runner_run_async (runner,
+ g_task_get_cancellable (task),
+ gbp_run_workbench_addin_run_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_run_workbench_addin_run (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ GbpRunWorkbenchAddin *self = user_data;
+ IdeConfigurationManager *config_manager;
+ IdeConfiguration *config;
+ IdeBuildSystem *build_system;
+ IdeContext *context;
+ IdeRuntime *runtime;
+ g_autoptr(GTask) task = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
+
+ if (self->cancellable != NULL)
+ g_cancellable_cancel (self->cancellable);
+
+ context = ide_workbench_get_context (self->workbench);
+
+ config_manager = ide_context_get_configuration_manager (context);
+ config = ide_configuration_manager_get_current (config_manager);
+ runtime = ide_configuration_get_runtime (config);
+
+ if (runtime == NULL)
+ {
+ /* TODO: Use notification API */
+ ide_context_warning (context,
+ "%s “%s”",
+ _("Failed to locate runtime"),
+ ide_configuration_get_runtime_id (config));
+ IDE_EXIT;
+ }
+
+ build_system = ide_context_get_build_system (context);
+
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ task = g_task_new (self, self->cancellable, NULL, NULL);
+ g_task_set_source_tag (task, gbp_run_workbench_addin_run);
+ g_task_set_task_data (task, g_object_ref (runtime), g_object_unref);
+
+ ide_build_system_get_build_targets_async (build_system,
+ self->cancellable,
+ gbp_run_workbench_addin_get_build_targets_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_run_workbench_addin_load (IdeWorkbenchAddin *addin,
+ IdeWorkbench *workbench)
+{
+ GbpRunWorkbenchAddin *self = (GbpRunWorkbenchAddin *)addin;
+ IdeWorkbenchHeaderBar *headerbar;
+ g_autoptr(GSimpleActionGroup) group = NULL;
+ GtkWidget *button;
+ static const GActionEntry entries[] = {
+ { "run", gbp_run_workbench_addin_run },
+ };
+
+ g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_WORKBENCH (workbench));
+
+ self->workbench = workbench;
+
+ headerbar = ide_workbench_get_headerbar (workbench);
+ button = g_object_new (GTK_TYPE_BUTTON,
+ "action-name", "run-tools.run",
+ "focus-on-click", FALSE,
+ "child", g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "media-playback-start-symbolic",
+ "visible", TRUE,
+ NULL),
+ "visible", TRUE,
+ NULL);
+ ide_widget_add_style_class (button, "image-button");
+ ide_workbench_header_bar_insert_right (headerbar, button, GTK_PACK_START, 0);
+
+ group = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (group), entries, G_N_ELEMENTS (entries), self);
+ gtk_widget_insert_action_group (GTK_WIDGET (workbench), "run-tools", G_ACTION_GROUP (group));
+}
+
+static void
+gbp_run_workbench_addin_unload (IdeWorkbenchAddin *addin,
+ IdeWorkbench *workbench)
+{
+ GbpRunWorkbenchAddin *self = (GbpRunWorkbenchAddin *)addin;
+
+ g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_WORKBENCH (workbench));
+ g_assert (workbench == self->workbench);
+
+ self->workbench = NULL;
+
+ if (self->cancellable != NULL)
+ {
+ if (!g_cancellable_is_cancelled (self->cancellable))
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ }
+}
+
+static void
+workbench_addin_init_iface (IdeWorkbenchAddinInterface *iface)
+{
+ iface->load = gbp_run_workbench_addin_load;
+ iface->unload = gbp_run_workbench_addin_unload;
+}
+
+static void
+gbp_run_workbench_addin_class_init (GbpRunWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_run_workbench_addin_init (GbpRunWorkbenchAddin *self)
+{
+}
diff --git a/plugins/run-tools/gbp-run-workbench-addin.h b/plugins/run-tools/gbp-run-workbench-addin.h
new file mode 100644
index 0000000..d7c655d
--- /dev/null
+++ b/plugins/run-tools/gbp-run-workbench-addin.h
@@ -0,0 +1,32 @@
+/* gbp-run-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_RUN_WORKBENCH_ADDIN_H
+#define GBP_RUN_WORKBENCH_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_RUN_WORKBENCH_ADDIN (gbp_run_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpRunWorkbenchAddin, gbp_run_workbench_addin, GBP, RUN_WORKBENCH_ADDIN, GObject)
+
+G_END_DECLS
+
+#endif /* GBP_RUN_WORKBENCH_ADDIN_H */
diff --git a/plugins/run-tools/run-tools.plugin b/plugins/run-tools/run-tools.plugin
new file mode 100644
index 0000000..6116bb1
--- /dev/null
+++ b/plugins/run-tools/run-tools.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Module=run-tools-plugin
+Name=Run Tools
+Description=Tools to run a project
+Authors=Christian Hergert <chergert redhat com>
+Copyright=Copyright © 2016 Christian Hergert
+Depends=editor
+Hidden=true
+Builtin=true
+X-Tool-Name=run
+X-Tool-Description=Run a project
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]