[gnome-builder/wip/chergert/flatpak-breakout] wip: subprocess abstraction



commit 95567e9a58ee7c56e5ad9964166a405bb935a06f
Author: Christian Hergert <chergert redhat com>
Date:   Tue Sep 6 00:23:18 2016 -0700

    wip: subprocess abstraction
    
    To support Building applications while running from inside the sandbox, we
    need to allow breaking out of the sandbox for such tooling. For example,
    we want to run the build tooling for flatpaks from the host system (since
    we wont have sufficient privileges inside the sandbox).
    
    This abstracts our use of GSubprocess so that we can have multiple
    implementations. Sadly, since GSubprocess is not an interface, we cannot
    simply create an alternate implementation.
    
    There are two implementations of the abstraction. IdeSimpleSubprocess is
    just a wrapper around GSubprocess (since our API is almost identical).
    Additionally, we have IdeHostProcess which is a process that runs within
    the host system, via the --allow=devel support in flatpak.

 libide/Makefile.am                                 |    8 +-
 libide/buildsystem/ide-build-command.c             |   23 +-
 libide/buildsystem/ide-build-result.c              |    9 +-
 libide/buildsystem/ide-build-result.h              |    2 +-
 libide/ide-types.h                                 |    3 +
 libide/ide.h                                       |    3 +-
 libide/runner/ide-runner.c                         |   29 +-
 libide/runtimes/ide-runtime.h                      |    2 +-
 libide/subprocess/ide-simple-subprocess.c          |  355 ++++++++++++++++++++
 libide/subprocess/ide-simple-subprocess.h          |   34 ++
 .../ide-subprocess-launcher.c                      |   44 ++-
 .../ide-subprocess-launcher.h                      |   33 ++-
 libide/subprocess/ide-subprocess.c                 |  265 +++++++++++++++
 libide/subprocess/ide-subprocess.h                 |  149 ++++++++
 plugins/autotools/ide-autotools-build-system.c     |   14 +-
 plugins/autotools/ide-autotools-build-task.c       |   64 ++--
 plugins/autotools/ide-makecache.c                  |    4 +-
 plugins/flatpak/gbp-flatpak-runtime.c              |   38 +-
 18 files changed, 958 insertions(+), 121 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index dddce43..5851590 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -124,6 +124,9 @@ libide_1_0_la_public_headers =                            \
        sourceview/ide-source-style-scheme.h              \
        sourceview/ide-source-view-mode.h                 \
        sourceview/ide-source-view.h                      \
+       subprocess/ide-simple-subprocess.h                \
+       subprocess/ide-subprocess.h                       \
+       subprocess/ide-subprocess-launcher.h              \
        symbols/ide-symbol-node.h                         \
        symbols/ide-symbol-resolver.h                     \
        symbols/ide-symbol-tree.h                         \
@@ -175,7 +178,6 @@ libide_1_0_la_public_headers =                            \
        workbench/ide-workbench-addin.h                   \
        workbench/ide-workbench-header-bar.h              \
        workbench/ide-workbench.h                         \
-       workers/ide-subprocess-launcher.h                 \
        workers/ide-worker.h                              \
        $(NULL)
 
@@ -287,6 +289,9 @@ libide_1_0_la_public_sources =                            \
        sourceview/ide-source-style-scheme.c              \
        sourceview/ide-source-view-mode.c                 \
        sourceview/ide-source-view.c                      \
+       subprocess/ide-simple-subprocess.c                \
+       subprocess/ide-subprocess.c                       \
+       subprocess/ide-subprocess-launcher.c              \
        symbols/ide-symbol-node.c                         \
        symbols/ide-symbol-resolver.c                     \
        symbols/ide-symbol-tree.c                         \
@@ -326,7 +331,6 @@ libide_1_0_la_public_sources =                            \
        workbench/ide-workbench-header-bar.c              \
        workbench/ide-workbench-open.c                    \
        workbench/ide-workbench.c                         \
-       workers/ide-subprocess-launcher.c                 \
        workers/ide-worker.c                              \
        $(NULL)
 
diff --git a/libide/buildsystem/ide-build-command.c b/libide/buildsystem/ide-build-command.c
index 3aa69e3..99f71dd 100644
--- a/libide/buildsystem/ide-build-command.c
+++ b/libide/buildsystem/ide-build-command.c
@@ -24,7 +24,8 @@
 #include "buildsystem/ide-build-result.h"
 #include "buildsystem/ide-environment.h"
 #include "runtimes/ide-runtime.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
 
 typedef struct
 {
@@ -46,17 +47,17 @@ ide_build_command_wait_cb (GObject      *object,
                            GAsyncResult *result,
                            gpointer      user_data)
 {
-  GSubprocess *subprocess = (GSubprocess *)object;
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
   g_autoptr(GTask) task = user_data;
   GError *error = NULL;
 
   IDE_ENTRY;
 
-  g_assert (G_IS_SUBPROCESS (subprocess));
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (G_IS_TASK (task));
 
-  if (!g_subprocess_wait_finish (subprocess, result, &error))
+  if (!ide_subprocess_wait_finish (subprocess, result, &error))
     {
       g_task_return_error (task, error);
       IDE_EXIT;
@@ -121,7 +122,7 @@ ide_build_command_real_run (IdeBuildCommand  *self,
 {
   IdeBuildCommandPrivate *priv = ide_build_command_get_instance_private (self);
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
   gboolean ret;
 
   IDE_ENTRY;
@@ -147,7 +148,7 @@ ide_build_command_real_run (IdeBuildCommand  *self,
 
   ide_build_result_log_subprocess (build_result, subprocess);
 
-  ret = g_subprocess_wait (subprocess, cancellable, error);
+  ret = ide_subprocess_wait (subprocess, cancellable, error);
 
   IDE_RETURN (ret);
 }
@@ -163,7 +164,7 @@ ide_build_command_real_run_async (IdeBuildCommand     *self,
 {
   IdeBuildCommandPrivate *priv = ide_build_command_get_instance_private (self);
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
   g_autoptr(GTask) task = NULL;
   GError *error = NULL;
 
@@ -201,10 +202,10 @@ ide_build_command_real_run_async (IdeBuildCommand     *self,
 
   ide_build_result_log_subprocess (build_result, subprocess);
 
-  g_subprocess_wait_async (subprocess,
-                           cancellable,
-                           ide_build_command_wait_cb,
-                           g_steal_pointer (&task));
+  ide_subprocess_wait_async (subprocess,
+                             cancellable,
+                             ide_build_command_wait_cb,
+                             g_steal_pointer (&task));
 
   IDE_EXIT;
 }
diff --git a/libide/buildsystem/ide-build-result.c b/libide/buildsystem/ide-build-result.c
index 6e7045e..f37139e 100644
--- a/libide/buildsystem/ide-build-result.c
+++ b/libide/buildsystem/ide-build-result.c
@@ -29,6 +29,7 @@
 #include "buildsystem/ide-build-result-addin.h"
 #include "diagnostics/ide-source-location.h"
 #include "files/ide-file.h"
+#include "subprocess/ide-subprocess.h"
 
 #define POINTER_MARK(p)   GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)|1)
 #define POINTER_UNMARK(p) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)&~(gsize)1)
@@ -351,27 +352,27 @@ ide_build_result_tail_into (IdeBuildResult    *self,
 
 void
 ide_build_result_log_subprocess (IdeBuildResult *self,
-                                 GSubprocess    *subprocess)
+                                 IdeSubprocess  *subprocess)
 {
   IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
   GInputStream *stdout_stream;
   GInputStream *stderr_stream;
 
   g_return_if_fail (IDE_IS_BUILD_RESULT (self));
-  g_return_if_fail (G_IS_SUBPROCESS (subprocess));
+  g_return_if_fail (IDE_IS_SUBPROCESS (subprocess));
 
   /* ensure lazily created streams are available */
   (void)ide_build_result_get_stderr_stream (self);
   (void)ide_build_result_get_stdout_stream (self);
 
-  stderr_stream = g_subprocess_get_stderr_pipe (subprocess);
+  stderr_stream = ide_subprocess_get_stderr_pipe (subprocess);
   if (stderr_stream)
     ide_build_result_tail_into (self,
                                 IDE_BUILD_RESULT_LOG_STDERR,
                                 stderr_stream,
                                 priv->stderr_writer);
 
-  stdout_stream = g_subprocess_get_stdout_pipe (subprocess);
+  stdout_stream = ide_subprocess_get_stdout_pipe (subprocess);
   if (stdout_stream)
     ide_build_result_tail_into (self,
                                 IDE_BUILD_RESULT_LOG_STDOUT,
diff --git a/libide/buildsystem/ide-build-result.h b/libide/buildsystem/ide-build-result.h
index 26b802f..6df1e7e 100644
--- a/libide/buildsystem/ide-build-result.h
+++ b/libide/buildsystem/ide-build-result.h
@@ -60,7 +60,7 @@ struct _IdeBuildResultClass
 GInputStream  *ide_build_result_get_stdout_stream (IdeBuildResult *result);
 GInputStream  *ide_build_result_get_stderr_stream (IdeBuildResult *result);
 void           ide_build_result_log_subprocess    (IdeBuildResult *result,
-                                                   GSubprocess    *subprocess);
+                                                   IdeSubprocess  *subprocess);
 GTimeSpan      ide_build_result_get_running_time  (IdeBuildResult *self);
 gboolean       ide_build_result_get_running       (IdeBuildResult *self);
 void           ide_build_result_set_running       (IdeBuildResult *self,
diff --git a/libide/ide-types.h b/libide/ide-types.h
index c5afe50..b4aee57 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -122,6 +122,9 @@ typedef struct _IdeSourceSnippets              IdeSourceSnippets;
 
 typedef struct _IdeSourceSnippetsManager       IdeSourceSnippetsManager;
 
+typedef struct _IdeSubprocess                  IdeSubprocess;
+typedef struct _IdeSubprocessLauncher          IdeSubprocessLauncher;
+
 typedef struct _IdeSymbol                      IdeSymbol;
 
 typedef struct _IdeSymbolResolver              IdeSymbolResolver;
diff --git a/libide/ide.h b/libide/ide.h
index 20258c0..1b93c8e 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -115,6 +115,8 @@ G_BEGIN_DECLS
 #include "sourceview/ide-source-map.h"
 #include "sourceview/ide-source-style-scheme.h"
 #include "sourceview/ide-source-view.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
 #include "symbols/ide-symbol-resolver.h"
 #include "symbols/ide-symbol.h"
 #include "symbols/ide-tags-builder.h"
@@ -148,7 +150,6 @@ G_BEGIN_DECLS
 #include "workbench/ide-workbench-addin.h"
 #include "workbench/ide-workbench-header-bar.h"
 #include "workbench/ide-workbench.h"
-#include "workers/ide-subprocess-launcher.h"
 
 #undef IDE_INSIDE
 
diff --git a/libide/runner/ide-runner.c b/libide/runner/ide-runner.c
index 18bf152..f1eeef4 100644
--- a/libide/runner/ide-runner.c
+++ b/libide/runner/ide-runner.c
@@ -27,7 +27,8 @@
 
 #include "runner/ide-runner.h"
 #include "runner/ide-runner-addin.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
 
 typedef struct
 {
@@ -96,14 +97,16 @@ ide_runner_run_wait_cb (GObject      *object,
                         GAsyncResult *result,
                         gpointer      user_data)
 {
-  GSubprocess *subprocess = (GSubprocess *)object;
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
   g_autoptr(GTask) task = user_data;
   GError *error = NULL;
   IdeRunner *self;
 
   IDE_ENTRY;
 
-  g_assert (G_IS_SUBPROCESS (subprocess));
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
 
   self = g_task_get_source_object (task);
 
@@ -111,17 +114,17 @@ ide_runner_run_wait_cb (GObject      *object,
 
   g_signal_emit (self, signals [EXITED], 0);
 
-  if (!g_subprocess_wait_finish (subprocess, result, &error))
+  if (!ide_subprocess_wait_finish (subprocess, result, &error))
     {
       g_task_return_error (task, error);
       IDE_EXIT;
     }
 
-  if (g_subprocess_get_if_exited (subprocess))
+  if (ide_subprocess_get_if_exited (subprocess))
     {
       gint exit_code;
 
-      exit_code = g_subprocess_get_exit_status (subprocess);
+      exit_code = ide_subprocess_get_exit_status (subprocess);
 
       if (exit_code == EXIT_SUCCESS)
         {
@@ -148,7 +151,7 @@ ide_runner_real_run_async (IdeRunner           *self,
   IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
   g_autoptr(GTask) task = NULL;
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
   const gchar *identifier;
   GError *error = NULL;
 
@@ -169,7 +172,7 @@ ide_runner_real_run_async (IdeRunner           *self,
 
   subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, &error);
 
-  g_assert (subprocess == NULL || G_IS_SUBPROCESS (subprocess));
+  g_assert (subprocess == NULL || IDE_IS_SUBPROCESS (subprocess));
 
   if (subprocess == NULL)
     {
@@ -177,14 +180,14 @@ ide_runner_real_run_async (IdeRunner           *self,
       IDE_GOTO (failure);
     }
 
-  identifier = g_subprocess_get_identifier (subprocess);
+  identifier = ide_subprocess_get_identifier (subprocess);
 
   g_signal_emit (self, signals [SPAWNED], 0, identifier);
 
-  g_subprocess_wait_async (subprocess,
-                           cancellable,
-                           ide_runner_run_wait_cb,
-                           g_steal_pointer (&task));
+  ide_subprocess_wait_async (subprocess,
+                             cancellable,
+                             ide_runner_run_wait_cb,
+                             g_steal_pointer (&task));
 
 failure:
   IDE_EXIT;
diff --git a/libide/runtimes/ide-runtime.h b/libide/runtimes/ide-runtime.h
index cccdc3a..28f3931 100644
--- a/libide/runtimes/ide-runtime.h
+++ b/libide/runtimes/ide-runtime.h
@@ -25,7 +25,7 @@
 
 #include "buildsystem/ide-build-target.h"
 #include "runner/ide-runner.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess-launcher.h"
 
 G_BEGIN_DECLS
 
diff --git a/libide/subprocess/ide-simple-subprocess.c b/libide/subprocess/ide-simple-subprocess.c
new file mode 100644
index 0000000..409d278
--- /dev/null
+++ b/libide/subprocess/ide-simple-subprocess.c
@@ -0,0 +1,355 @@
+/* ide-simple-subprocess.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-simple-subprocess"
+
+#include "ide-simple-subprocess.h"
+
+struct _IdeSimpleSubprocess
+{
+  GObject      parent_instance;
+  GSubprocess *subprocess;
+};
+
+static void subprocess_iface_init (IdeSubprocessInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeSimpleSubprocess, ide_simple_subprocess, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_SUBPROCESS, subprocess_iface_init))
+
+static void
+ide_simple_subprocess_finalize (GObject *object)
+{
+  IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)object;
+
+  g_clear_object (&self->subprocess);
+
+  G_OBJECT_CLASS (ide_simple_subprocess_parent_class)->finalize (object);
+}
+
+static void
+ide_simple_subprocess_class_init (IdeSimpleSubprocessClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_simple_subprocess_finalize;
+}
+
+static void
+ide_simple_subprocess_init (IdeSimpleSubprocess *self)
+{
+}
+
+#define WRAP_INTERFACE_METHOD(name, ...) \
+  g_subprocess_##name(IDE_SIMPLE_SUBPROCESS(subprocess)->subprocess, ## __VA_ARGS__)
+
+static const gchar *
+ide_simple_subprocess_get_identifier (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_identifier);
+}
+
+static GInputStream *
+ide_simple_subprocess_get_stdout_pipe (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_stdout_pipe);
+}
+
+static GInputStream *
+ide_simple_subprocess_get_stderr_pipe (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_stderr_pipe);
+}
+
+static GOutputStream *
+ide_simple_subprocess_get_stdin_pipe (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_stdin_pipe);
+}
+
+static gboolean
+ide_simple_subprocess_wait (IdeSubprocess  *subprocess,
+                            GCancellable   *cancellable,
+                            GError        **error)
+{
+  return WRAP_INTERFACE_METHOD (wait, cancellable, error);
+}
+
+static gboolean
+ide_simple_subprocess_wait_check (IdeSubprocess  *subprocess,
+                                  GCancellable   *cancellable,
+                                  GError        **error)
+{
+  return WRAP_INTERFACE_METHOD (wait_check, cancellable, error);
+}
+
+static void
+ide_simple_subprocess_wait_cb (GObject      *object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  GSubprocess *subprocess = (GSubprocess *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  if (!g_subprocess_wait_finish (subprocess, result, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_simple_subprocess_wait_async (IdeSubprocess       *subprocess,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)subprocess;
+  GTask *task = g_task_new (self, cancellable, callback, user_data);
+  g_subprocess_wait_async (self->subprocess, cancellable, ide_simple_subprocess_wait_cb, task);
+}
+
+static gboolean
+ide_simple_subprocess_wait_finish (IdeSubprocess  *subprocess,
+                                   GAsyncResult   *result,
+                                   GError        **error)
+{
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_simple_subprocess_wait_check_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  GSubprocess *subprocess = (GSubprocess *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  if (!g_subprocess_wait_check_finish (subprocess, result, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_simple_subprocess_wait_check_async (IdeSubprocess       *subprocess,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)subprocess;
+  GTask *task = g_task_new (self, cancellable, callback, user_data);
+  g_subprocess_wait_check_async (self->subprocess, cancellable, ide_simple_subprocess_wait_check_cb, task);
+}
+
+static gboolean
+ide_simple_subprocess_wait_check_finish (IdeSubprocess  *subprocess,
+                                         GAsyncResult   *result,
+                                         GError        **error)
+{
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ide_simple_subprocess_get_successful (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_successful);
+}
+
+static gboolean
+ide_simple_subprocess_get_if_exited (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_if_exited);
+}
+
+static gint
+ide_simple_subprocess_get_exit_status (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_exit_status);
+}
+
+static gboolean
+ide_simple_subprocess_get_if_signaled (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_if_signaled);
+}
+
+static gint
+ide_simple_subprocess_get_term_sig (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_term_sig);
+}
+
+static gint
+ide_simple_subprocess_get_status (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (get_status);
+}
+
+static void
+ide_simple_subprocess_send_signal (IdeSubprocess *subprocess,
+                                   gint           signal_num)
+{
+  return WRAP_INTERFACE_METHOD (send_signal, signal_num);
+}
+
+static void
+ide_simple_subprocess_force_exit (IdeSubprocess *subprocess)
+{
+  return WRAP_INTERFACE_METHOD (force_exit);
+}
+
+static gboolean
+ide_simple_subprocess_communicate (IdeSubprocess  *subprocess,
+                                   GBytes         *stdin_buf,
+                                   GCancellable   *cancellable,
+                                   GBytes        **stdout_buf,
+                                   GBytes        **stderr_buf,
+                                   GError        **error)
+{
+  return WRAP_INTERFACE_METHOD (communicate, stdin_buf, cancellable, stdout_buf, stderr_buf, error);
+}
+
+static gboolean
+ide_simple_subprocess_communicate_utf8 (IdeSubprocess  *subprocess,
+                                        const gchar    *stdin_buf,
+                                        GCancellable   *cancellable,
+                                        gchar         **stdout_buf,
+                                        gchar         **stderr_buf,
+                                        GError        **error)
+{
+  return WRAP_INTERFACE_METHOD (communicate_utf8, stdin_buf, cancellable, stdout_buf, stderr_buf, error);
+}
+
+static void
+free_object_pair (gpointer data)
+{
+  gpointer *pair = data;
+
+  g_clear_object (&pair[0]);
+  g_clear_object (&pair[1]);
+  g_free (pair);
+}
+
+static void
+ide_simple_subprocess_communicate_cb (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  GSubprocess *subprocess = (GSubprocess *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GBytes) stdout_buf = NULL;
+  g_autoptr(GBytes) stderr_buf = NULL;
+  gpointer *data;
+
+  if (!g_subprocess_communicate_finish (subprocess, result, &stdout_buf, &stderr_buf, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+
+  data = g_new0 (gpointer, 2);
+  data[0] = g_steal_pointer (&stdout_buf);
+  data[1] = g_steal_pointer (&stderr_buf);
+
+  g_task_return_pointer (task, data, free_object_pair);
+}
+
+static void
+ide_simple_subprocess_communicate_async (IdeSubprocess       *subprocess,
+                                         GBytes              *stdin_buf,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)subprocess;
+  GTask *task = g_task_new (self, cancellable, callback, user_data);
+  g_subprocess_communicate_async (self->subprocess, stdin_buf, cancellable, 
ide_simple_subprocess_communicate_cb, task);
+}
+
+static gboolean
+ide_simple_subprocess_communicate_finish (IdeSubprocess  *subprocess,
+                                          GAsyncResult   *result,
+                                          GBytes        **stdout_buf,
+                                          GBytes        **stderr_buf,
+                                          GError        **error)
+{
+  gpointer *pair;
+
+  pair = g_task_propagate_pointer (G_TASK (result), error);
+
+  if (pair != NULL)
+    {
+      if (stdout_buf != NULL)
+        *stdout_buf = g_steal_pointer (&pair[0]);
+
+      if (stderr_buf != NULL)
+        *stderr_buf = g_steal_pointer (&pair[1]);
+
+      free_object_pair (pair);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+subprocess_iface_init (IdeSubprocessInterface *iface)
+{
+  iface->get_identifier = ide_simple_subprocess_get_identifier;
+  iface->get_stdout_pipe = ide_simple_subprocess_get_stdout_pipe;
+  iface->get_stderr_pipe = ide_simple_subprocess_get_stderr_pipe;
+  iface->get_stdin_pipe = ide_simple_subprocess_get_stdin_pipe;
+  iface->wait = ide_simple_subprocess_wait;
+  iface->wait_check = ide_simple_subprocess_wait_check;
+  iface->wait_async = ide_simple_subprocess_wait_async;
+  iface->wait_finish = ide_simple_subprocess_wait_finish;
+  iface->wait_check_async = ide_simple_subprocess_wait_check_async;
+  iface->wait_check_finish = ide_simple_subprocess_wait_check_finish;
+  iface->get_successful = ide_simple_subprocess_get_successful;
+  iface->get_if_exited = ide_simple_subprocess_get_if_exited;
+  iface->get_exit_status = ide_simple_subprocess_get_exit_status;
+  iface->get_if_signaled = ide_simple_subprocess_get_if_signaled;
+  iface->get_term_sig = ide_simple_subprocess_get_term_sig;
+  iface->get_status = ide_simple_subprocess_get_status;
+  iface->send_signal = ide_simple_subprocess_send_signal;
+  iface->force_exit = ide_simple_subprocess_force_exit;
+  iface->communicate = ide_simple_subprocess_communicate;
+  iface->communicate_utf8 = ide_simple_subprocess_communicate_utf8;
+  iface->communicate_async = ide_simple_subprocess_communicate_async;
+  iface->communicate_finish = ide_simple_subprocess_communicate_finish;
+}
+
+/**
+ * ide_simple_subprocess_new:
+ *
+ * Creates a new #IdeSimpleSubprocess wrapping the #GSubprocess.
+ *
+ * Returns: (transfer full): A new #IdeSubprocess
+ */
+IdeSubprocess *
+ide_simple_subprocess_new (GSubprocess *subprocess)
+{
+  IdeSimpleSubprocess *ret;
+
+  g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL);
+
+  ret = g_object_new (IDE_TYPE_SIMPLE_SUBPROCESS, NULL);
+  ret->subprocess = g_object_ref (subprocess);
+
+  return IDE_SUBPROCESS (ret);
+}
diff --git a/libide/subprocess/ide-simple-subprocess.h b/libide/subprocess/ide-simple-subprocess.h
new file mode 100644
index 0000000..dff3e3a
--- /dev/null
+++ b/libide/subprocess/ide-simple-subprocess.h
@@ -0,0 +1,34 @@
+/* ide-simple-subprocess.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_SIMPLE_SUBPROCESS_H
+#define IDE_SIMPLE_SUBPROCESS_H
+
+#include "ide-subprocess.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SIMPLE_SUBPROCESS (ide_simple_subprocess_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSimpleSubprocess, ide_simple_subprocess, IDE, SIMPLE_SUBPROCESS, GObject)
+
+IdeSubprocess *ide_simple_subprocess_new (GSubprocess *subprocess);
+
+G_END_DECLS
+
+#endif /* IDE_SIMPLE_SUBPROCESS_H */
diff --git a/libide/workers/ide-subprocess-launcher.c b/libide/subprocess/ide-subprocess-launcher.c
similarity index 93%
rename from libide/workers/ide-subprocess-launcher.c
rename to libide/subprocess/ide-subprocess-launcher.c
index 44e6563..6c3f4e7 100644
--- a/libide/workers/ide-subprocess-launcher.c
+++ b/libide/subprocess/ide-subprocess-launcher.c
@@ -28,7 +28,8 @@
 
 #include "buildsystem/ide-environment-variable.h"
 #include "buildsystem/ide-environment.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-simple-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
 
 typedef struct
 {
@@ -104,6 +105,14 @@ ide_subprocess_launcher_new (GSubprocessFlags flags)
 }
 
 static void
+ide_subprocess_launcher_spawn_worker_flatpak (GTask        *task,
+                                              gpointer      source_object,
+                                              gpointer      task_data,
+                                              GCancellable *cancellable)
+{
+}
+
+static void
 ide_subprocess_launcher_spawn_worker (GTask        *task,
                                       gpointer      source_object,
                                       gpointer      task_data,
@@ -112,8 +121,9 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
   IdeSubprocessLauncher *self = source_object;
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
   g_autoptr(GSubprocessLauncher) launcher = NULL;
-  GSubprocess *ret;
-  GError *error = NULL;
+  g_autoptr(GSubprocess) real = NULL;
+  g_autoptr(IdeSubprocess) wrapped = NULL;
+  g_autoptr(GError) error = NULL;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
@@ -130,13 +140,13 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
   g_subprocess_launcher_set_cwd (launcher, priv->cwd);
   if (priv->environ->len > 1)
     g_subprocess_launcher_set_environ (launcher, (gchar **)priv->environ->pdata);
-  ret = g_subprocess_launcher_spawnv (launcher,
-                                      (const gchar * const *)priv->argv->pdata,
-                                      &error);
+  real = g_subprocess_launcher_spawnv (launcher,
+                                       (const gchar * const *)priv->argv->pdata,
+                                       &error);
 
-  if (ret == NULL)
+  if (real == NULL)
     {
-      g_task_return_error (task, error);
+      g_task_return_error (task, g_steal_pointer (&error));
       return;
     }
 
@@ -145,15 +155,17 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
       g_signal_connect_data (cancellable,
                              "cancelled",
                              G_CALLBACK (ide_subprocess_launcher_kill_process_group),
-                             g_object_ref (ret),
+                             g_object_ref (real),
                              (GClosureNotify)g_object_unref,
                              0);
     }
 
-  g_task_return_pointer (task, ret, g_object_unref);
+  wrapped = ide_simple_subprocess_new (real);
+
+  g_task_return_pointer (task, g_steal_pointer (&wrapped), g_object_unref);
 }
 
-static GSubprocess *
+static IdeSubprocess *
 ide_subprocess_launcher_real_spawn_sync (IdeSubprocessLauncher  *self,
                                          GCancellable           *cancellable,
                                          GError                **error)
@@ -189,7 +201,7 @@ ide_subprocess_launcher_real_spawn_async (IdeSubprocessLauncher *self,
   g_task_run_in_thread (task, ide_subprocess_launcher_spawn_worker);
 }
 
-static GSubprocess *
+static IdeSubprocess *
 ide_subprocess_launcher_real_spawn_finish (IdeSubprocessLauncher  *self,
                                            GAsyncResult           *result,
                                            GError                **error)
@@ -447,9 +459,9 @@ ide_subprocess_launcher_spawn_async (IdeSubprocessLauncher *self,
  *
  * Complete a request to asynchronously spawn a process.
  *
- * Returns: (transfer full): A #GSubprocess or %NULL upon error.
+ * Returns: (transfer full): A #IdeSubprocess or %NULL upon error.
  */
-GSubprocess *
+IdeSubprocess *
 ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher  *self,
                                       GAsyncResult           *result,
                                       GError                **error)
@@ -465,9 +477,9 @@ ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher  *self,
  *
  * Synchronously spawn a process using the internal state.
  *
- * Returns: (transfer full): A #GSubprocess or %NULL upon error.
+ * Returns: (transfer full): A #IdeSubprocess or %NULL upon error.
  */
-GSubprocess *
+IdeSubprocess *
 ide_subprocess_launcher_spawn_sync (IdeSubprocessLauncher  *self,
                                     GCancellable           *cancellable,
                                     GError                **error)
diff --git a/libide/workers/ide-subprocess-launcher.h b/libide/subprocess/ide-subprocess-launcher.h
similarity index 80%
rename from libide/workers/ide-subprocess-launcher.h
rename to libide/subprocess/ide-subprocess-launcher.h
index b72813a..4577afa 100644
--- a/libide/workers/ide-subprocess-launcher.h
+++ b/libide/subprocess/ide-subprocess-launcher.h
@@ -33,16 +33,25 @@ struct _IdeSubprocessLauncherClass
 {
   GObjectClass parent_class;
 
-  GSubprocess *(*spawn_sync)   (IdeSubprocessLauncher  *self,
-                                GCancellable           *cancellable,
-                                GError                **error);
-  void         (*spawn_async)  (IdeSubprocessLauncher  *self,
-                                GCancellable           *cancellable,
-                                GAsyncReadyCallback     callback,
-                                gpointer                user_data);
-  GSubprocess *(*spawn_finish) (IdeSubprocessLauncher  *self,
-                                GAsyncResult           *result,
-                                GError                **error);
+  IdeSubprocess *(*spawn_sync)   (IdeSubprocessLauncher  *self,
+                                  GCancellable           *cancellable,
+                                  GError                **error);
+  void           (*spawn_async)  (IdeSubprocessLauncher  *self,
+                                  GCancellable           *cancellable,
+                                  GAsyncReadyCallback     callback,
+                                  gpointer                user_data);
+  IdeSubprocess *(*spawn_finish) (IdeSubprocessLauncher  *self,
+                                  GAsyncResult           *result,
+                                  GError                **error);
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
 };
 
 IdeSubprocessLauncher *ide_subprocess_launcher_new                 (GSubprocessFlags        flags);
@@ -66,14 +75,14 @@ void                   ide_subprocess_launcher_push_args           (IdeSubproces
 void                   ide_subprocess_launcher_push_argv           (IdeSubprocessLauncher  *self,
                                                                     const gchar            *argv);
 gchar                 *ide_subprocess_launcher_pop_argv            (IdeSubprocessLauncher  *self) 
G_GNUC_WARN_UNUSED_RESULT;
-GSubprocess           *ide_subprocess_launcher_spawn_sync          (IdeSubprocessLauncher  *self,
+IdeSubprocess         *ide_subprocess_launcher_spawn_sync          (IdeSubprocessLauncher  *self,
                                                                     GCancellable           *cancellable,
                                                                     GError                **error);
 void                   ide_subprocess_launcher_spawn_async         (IdeSubprocessLauncher  *self,
                                                                     GCancellable           *cancellable,
                                                                     GAsyncReadyCallback     callback,
                                                                     gpointer                user_data);
-GSubprocess           *ide_subprocess_launcher_spawn_finish        (IdeSubprocessLauncher  *self,
+IdeSubprocess         *ide_subprocess_launcher_spawn_finish        (IdeSubprocessLauncher  *self,
                                                                     GAsyncResult           *result,
                                                                     GError                **error);
 
diff --git a/libide/subprocess/ide-subprocess.c b/libide/subprocess/ide-subprocess.c
new file mode 100644
index 0000000..14d6074
--- /dev/null
+++ b/libide/subprocess/ide-subprocess.c
@@ -0,0 +1,265 @@
+/* ide-subprocess.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-subprocess"
+
+#include "ide-subprocess.h"
+
+G_DEFINE_INTERFACE (IdeSubprocess, ide_subprocess, G_TYPE_OBJECT)
+
+static void
+ide_subprocess_default_init (IdeSubprocessInterface *iface)
+{
+}
+
+#define WRAP_INTERFACE_METHOD(self, name, default_return, ...) \
+  ((IDE_SUBPROCESS_GET_IFACE(self)->name != NULL) ? \
+    IDE_SUBPROCESS_GET_IFACE(self)->name (self, ##__VA_ARGS__) : \
+    default_return)
+
+const gchar *
+ide_subprocess_get_identifier (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+  return WRAP_INTERFACE_METHOD (self, get_identifier, NULL);
+}
+
+/**
+ * ide_subprocess_get_stdout_pipe:
+ *
+ * Returns: (transfer none): A #GInputStream or %NULL.
+ */
+GInputStream *
+ide_subprocess_get_stdout_pipe  (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+  return WRAP_INTERFACE_METHOD (self, get_stdout_pipe, NULL);
+}
+
+/**
+ * ide_subprocess_get_stderr_pipe:
+ *
+ * Returns: (transfer none): A #GInputStream or %NULL.
+ */
+GInputStream *
+ide_subprocess_get_stderr_pipe (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+  return WRAP_INTERFACE_METHOD (self, get_stderr_pipe, NULL);
+}
+
+/**
+ * ide_subprocess_get_stdin_pipe:
+ *
+ * Returns: (transfer none): A #GOutputStream or %NULL.
+ */
+GOutputStream *
+ide_subprocess_get_stdin_pipe (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+  return WRAP_INTERFACE_METHOD (self, get_stdin_pipe, NULL);
+}
+
+gboolean
+ide_subprocess_wait (IdeSubprocess  *self,
+                     GCancellable   *cancellable,
+                     GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, wait, FALSE, cancellable, error);
+}
+
+gboolean
+ide_subprocess_wait_check (IdeSubprocess  *self,
+                           GCancellable   *cancellable,
+                           GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, wait_check, FALSE, cancellable, error);
+}
+
+void
+ide_subprocess_wait_async (IdeSubprocess       *self,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  WRAP_INTERFACE_METHOD (self, wait_async, NULL, cancellable, callback, user_data);
+}
+
+gboolean
+ide_subprocess_wait_finish (IdeSubprocess  *self,
+                            GAsyncResult   *result,
+                            GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, wait_finish, FALSE, result, error);
+}
+
+void
+ide_subprocess_wait_check_async (IdeSubprocess       *self,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  WRAP_INTERFACE_METHOD (self, wait_check_async, NULL, cancellable, callback, user_data);
+}
+
+gboolean
+ide_subprocess_wait_check_finish (IdeSubprocess  *self,
+                                  GAsyncResult   *result,
+                                  GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, wait_check_finish, FALSE, result, error);
+}
+
+gboolean
+ide_subprocess_get_successful (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, get_successful, FALSE);
+}
+
+gboolean
+ide_subprocess_get_if_exited (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, get_if_exited, FALSE);
+}
+
+gint
+ide_subprocess_get_exit_status (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+  return WRAP_INTERFACE_METHOD (self, get_exit_status, 0);
+}
+
+gboolean
+ide_subprocess_get_if_signaled (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, get_if_signaled, FALSE);
+}
+
+gint
+ide_subprocess_get_term_sig (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+  return WRAP_INTERFACE_METHOD (self, get_term_sig, 0);
+}
+
+gint
+ide_subprocess_get_status (IdeSubprocess *self)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+  return WRAP_INTERFACE_METHOD (self, get_status, 0);
+}
+
+void
+ide_subprocess_send_signal (IdeSubprocess *self,
+                            gint           signal_num)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS (self));
+
+  WRAP_INTERFACE_METHOD (self, send_signal, NULL, signal_num);
+}
+
+void
+ide_subprocess_force_exit (IdeSubprocess *self)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS (self));
+
+  WRAP_INTERFACE_METHOD (self, force_exit, NULL);
+}
+
+gboolean
+ide_subprocess_communicate (IdeSubprocess  *self,
+                            GBytes         *stdin_buf,
+                            GCancellable   *cancellable,
+                            GBytes        **stdout_buf,
+                            GBytes        **stderr_buf,
+                            GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, communicate, FALSE, stdin_buf, cancellable, stdout_buf, stderr_buf, 
error);
+}
+
+gboolean
+ide_subprocess_communicate_utf8 (IdeSubprocess  *self,
+                                 const gchar    *stdin_buf,
+                                 GCancellable   *cancellable,
+                                 gchar         **stdout_buf,
+                                 gchar         **stderr_buf,
+                                 GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, communicate_utf8, FALSE, stdin_buf, cancellable, stdout_buf, 
stderr_buf, error);
+}
+
+void
+ide_subprocess_communicate_async (IdeSubprocess       *self,
+                                  GBytes              *stdin_buf,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  WRAP_INTERFACE_METHOD (self, communicate_async, NULL, stdin_buf, cancellable, callback, user_data);
+}
+
+gboolean
+ide_subprocess_communicate_finish (IdeSubprocess  *self,
+                                   GAsyncResult   *result,
+                                   GBytes        **stdout_buf,
+                                   GBytes        **stderr_buf,
+                                   GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  return WRAP_INTERFACE_METHOD (self, communicate_finish, FALSE, result, stdout_buf, stderr_buf, error);
+}
diff --git a/libide/subprocess/ide-subprocess.h b/libide/subprocess/ide-subprocess.h
new file mode 100644
index 0000000..c528b16
--- /dev/null
+++ b/libide/subprocess/ide-subprocess.h
@@ -0,0 +1,149 @@
+/* ide-subprocess.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_SUBPROCESS_H
+#define IDE_SUBPROCESS_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SUBPROCESS (ide_subprocess_get_type())
+
+G_DECLARE_INTERFACE (IdeSubprocess, ide_subprocess, IDE, SUBPROCESS, GObject)
+
+struct _IdeSubprocessInterface
+{
+  GTypeInterface parent_interface;
+
+  const gchar   *(*get_identifier)          (IdeSubprocess        *self);
+  GInputStream  *(*get_stdout_pipe)         (IdeSubprocess        *self);
+  GInputStream  *(*get_stderr_pipe)         (IdeSubprocess        *self);
+  GOutputStream *(*get_stdin_pipe)          (IdeSubprocess        *self);
+  gboolean       (*wait)                    (IdeSubprocess        *self,
+                                             GCancellable         *cancellable,
+                                             GError              **error);
+  gboolean       (*wait_check)              (IdeSubprocess        *self,
+                                             GCancellable         *cancellable,
+                                             GError              **error);
+  void           (*wait_async)              (IdeSubprocess        *self,
+                                             GCancellable         *cancellable,
+                                             GAsyncReadyCallback   callback,
+                                             gpointer              user_data);
+  gboolean       (*wait_finish)             (IdeSubprocess        *self,
+                                             GAsyncResult         *result,
+                                             GError              **error);
+  void           (*wait_check_async)        (IdeSubprocess        *self,
+                                             GCancellable         *cancellable,
+                                             GAsyncReadyCallback   callback,
+                                             gpointer              user_data);
+  gboolean       (*wait_check_finish)       (IdeSubprocess        *self,
+                                             GAsyncResult         *result,
+                                             GError              **error);
+  gboolean       (*get_successful)          (IdeSubprocess        *self);
+  gboolean       (*get_if_exited)           (IdeSubprocess        *self);
+  gint           (*get_exit_status)         (IdeSubprocess        *self);
+  gboolean       (*get_if_signaled)         (IdeSubprocess        *self);
+  gint           (*get_term_sig)            (IdeSubprocess        *self);
+  gint           (*get_status)              (IdeSubprocess        *self);
+  void           (*send_signal)             (IdeSubprocess        *self,
+                                             gint                  signal_num);
+  void           (*force_exit)              (IdeSubprocess        *self);
+  gboolean       (*communicate)             (IdeSubprocess        *self,
+                                             GBytes               *stdin_buf,
+                                             GCancellable         *cancellable,
+                                             GBytes              **stdout_buf,
+                                             GBytes              **stderr_buf,
+                                             GError              **error);
+  gboolean       (*communicate_utf8)        (IdeSubprocess        *self,
+                                             const gchar          *stdin_buf,
+                                             GCancellable         *cancellable,
+                                             gchar               **stdout_buf,
+                                             gchar               **stderr_buf,
+                                             GError              **error);
+  void           (*communicate_async)       (IdeSubprocess        *self,
+                                             GBytes               *stdin_buf,
+                                             GCancellable         *cancellable,
+                                             GAsyncReadyCallback   callback,
+                                             gpointer              user_data);
+  gboolean       (*communicate_finish)      (IdeSubprocess        *self,
+                                             GAsyncResult         *result,
+                                             GBytes              **stdout_buf,
+                                             GBytes              **stderr_buf,
+                                             GError              **error);
+};
+
+const gchar   *ide_subprocess_get_identifier     (IdeSubprocess *self);
+GInputStream  *ide_subprocess_get_stdout_pipe    (IdeSubprocess *self);
+GInputStream  *ide_subprocess_get_stderr_pipe    (IdeSubprocess *self);
+GOutputStream *ide_subprocess_get_stdin_pipe     (IdeSubprocess *self);
+gboolean       ide_subprocess_wait               (IdeSubprocess        *self,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+gboolean       ide_subprocess_wait_check         (IdeSubprocess        *self,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+void           ide_subprocess_wait_async         (IdeSubprocess        *self,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+gboolean       ide_subprocess_wait_finish        (IdeSubprocess        *self,
+                                                  GAsyncResult         *result,
+                                                  GError              **error);
+void           ide_subprocess_wait_check_async   (IdeSubprocess        *self,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+gboolean       ide_subprocess_wait_check_finish  (IdeSubprocess        *self,
+                                                  GAsyncResult         *result,
+                                                  GError              **error);
+gboolean       ide_subprocess_get_successful     (IdeSubprocess        *self);
+gboolean       ide_subprocess_get_if_exited      (IdeSubprocess        *self);
+gint           ide_subprocess_get_exit_status    (IdeSubprocess        *self);
+gboolean       ide_subprocess_get_if_signaled    (IdeSubprocess        *self);
+gint           ide_subprocess_get_term_sig       (IdeSubprocess        *self);
+gint           ide_subprocess_get_status         (IdeSubprocess        *self);
+void           ide_subprocess_send_signal        (IdeSubprocess        *self,
+                                                  gint                  signal_num);
+void           ide_subprocess_force_exit         (IdeSubprocess        *self);
+gboolean       ide_subprocess_communicate        (IdeSubprocess        *self,
+                                                  GBytes               *stdin_buf,
+                                                  GCancellable         *cancellable,
+                                                  GBytes              **stdout_buf,
+                                                  GBytes              **stderr_buf,
+                                                  GError              **error);
+gboolean       ide_subprocess_communicate_utf8   (IdeSubprocess        *self,
+                                                  const gchar          *stdin_buf,
+                                                  GCancellable         *cancellable,
+                                                  gchar               **stdout_buf,
+                                                  gchar               **stderr_buf,
+                                                  GError              **error);
+void           ide_subprocess_communicate_async  (IdeSubprocess        *self,
+                                                  GBytes               *stdin_buf,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+gboolean       ide_subprocess_communicate_finish (IdeSubprocess        *self,
+                                                  GAsyncResult         *result,
+                                                  GBytes              **stdout_buf,
+                                                  GBytes              **stderr_buf,
+                                                  GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_SUBPROCESS_H */
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 4f64866..7debea4 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -932,11 +932,11 @@ simple_make_command_cb (GObject      *object,
                         GAsyncResult *result,
                         gpointer      user_data)
 {
-  GSubprocess *subprocess = (GSubprocess *)object;
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
   g_autoptr(GTask) task = user_data;
   GError *error = NULL;
 
-  if (!g_subprocess_wait_check_finish (subprocess, result, &error))
+  if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
     g_task_return_error (task, error);
   else
     g_task_return_boolean (task, TRUE);
@@ -949,7 +949,7 @@ simple_make_command (GFile            *directory,
                      IdeConfiguration *configuration)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
   GCancellable *cancellable;
   IdeRuntime *runtime;
   GError *error = NULL;
@@ -1000,10 +1000,10 @@ simple_make_command (GFile            *directory,
       return;
     }
 
-  g_subprocess_wait_check_async (subprocess,
-                                 g_task_get_cancellable (task),
-                                 simple_make_command_cb,
-                                 g_object_ref (task));
+  ide_subprocess_wait_check_async (subprocess,
+                                   cancellable,
+                                   simple_make_command_cb,
+                                   g_steal_pointer (&task));
 }
 
 static void
diff --git a/plugins/autotools/ide-autotools-build-task.c b/plugins/autotools/ide-autotools-build-task.c
index 8f43973..d3094be 100644
--- a/plugins/autotools/ide-autotools-build-task.c
+++ b/plugins/autotools/ide-autotools-build-task.c
@@ -75,30 +75,30 @@ enum {
   LAST_PROP
 };
 
-static GSubprocess *log_and_spawn     (IdeAutotoolsBuildTask  *self,
-                                       IdeSubprocessLauncher  *launcher,
-                                       GCancellable           *cancellable,
-                                       GError                **error,
-                                       const gchar            *argv0,
-                                       ...) G_GNUC_NULL_TERMINATED;
-static gboolean     step_mkdirs       (GTask                  *task,
-                                       IdeAutotoolsBuildTask  *self,
-                                       WorkerState            *state,
-                                       GCancellable           *cancellable);
-static gboolean     step_autogen      (GTask                  *task,
-                                       IdeAutotoolsBuildTask  *self,
-                                       WorkerState            *state,
-                                       GCancellable           *cancellable);
-static gboolean     step_configure    (GTask                  *task,
-                                       IdeAutotoolsBuildTask  *self,
-                                       WorkerState            *state,
-                                       GCancellable           *cancellable);
-static gboolean     step_make_all     (GTask                  *task,
-                                       IdeAutotoolsBuildTask  *self,
-                                       WorkerState            *state,
-                                       GCancellable           *cancellable);
-static void         apply_environment (IdeAutotoolsBuildTask  *self,
-                                       IdeSubprocessLauncher  *launcher);
+static IdeSubprocess *log_and_spawn     (IdeAutotoolsBuildTask  *self,
+                                         IdeSubprocessLauncher  *launcher,
+                                         GCancellable           *cancellable,
+                                         GError                **error,
+                                         const gchar            *argv0,
+                                         ...) G_GNUC_NULL_TERMINATED;
+static gboolean       step_mkdirs       (GTask                  *task,
+                                         IdeAutotoolsBuildTask  *self,
+                                         WorkerState            *state,
+                                         GCancellable           *cancellable);
+static gboolean       step_autogen      (GTask                  *task,
+                                         IdeAutotoolsBuildTask  *self,
+                                         WorkerState            *state,
+                                         GCancellable           *cancellable);
+static gboolean       step_configure    (GTask                  *task,
+                                         IdeAutotoolsBuildTask  *self,
+                                         WorkerState            *state,
+                                         GCancellable           *cancellable);
+static gboolean       step_make_all     (GTask                  *task,
+                                         IdeAutotoolsBuildTask  *self,
+                                         WorkerState            *state,
+                                         GCancellable           *cancellable);
+static void           apply_environment (IdeAutotoolsBuildTask  *self,
+                                         IdeSubprocessLauncher  *launcher);
 
 static GParamSpec *properties [LAST_PROP];
 static WorkStep workSteps [] = {
@@ -735,7 +735,7 @@ log_in_main (gpointer data)
   return G_SOURCE_REMOVE;
 }
 
-static GSubprocess *
+static IdeSubprocess *
 log_and_spawn (IdeAutotoolsBuildTask  *self,
                IdeSubprocessLauncher  *launcher,
                GCancellable           *cancellable,
@@ -743,7 +743,7 @@ log_and_spawn (IdeAutotoolsBuildTask  *self,
                const gchar           *argv0,
                ...)
 {
-  GSubprocess *ret;
+  IdeSubprocess *ret;
   struct {
     IdeBuildResult *result;
     gchar *message;
@@ -830,7 +830,7 @@ step_autogen (GTask                 *task,
   g_autofree gchar *autogen_sh_path = NULL;
   g_autofree gchar *configure_path = NULL;
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) process = NULL;
+  g_autoptr(IdeSubprocess) process = NULL;
   GError *error = NULL;
 
   g_assert (G_IS_TASK (task));
@@ -889,7 +889,7 @@ step_autogen (GTask                 *task,
 
   ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
 
-  if (!g_subprocess_wait_check (process, cancellable, &error))
+  if (!ide_subprocess_wait_check (process, cancellable, &error))
     {
       g_task_return_error (task, error);
       return FALSE;
@@ -915,7 +915,7 @@ step_configure (GTask                 *task,
                 GCancellable          *cancellable)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) process = NULL;
+  g_autoptr(IdeSubprocess) process = NULL;
   g_autofree gchar *makefile_path = NULL;
   g_autofree gchar *config_log = NULL;
   GError *error = NULL;
@@ -959,7 +959,7 @@ step_configure (GTask                 *task,
 
   ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
 
-  if (!g_subprocess_wait_check (process, cancellable, &error))
+  if (!ide_subprocess_wait_check (process, cancellable, &error))
     {
       g_task_return_error (task, error);
       return FALSE;
@@ -981,7 +981,7 @@ step_make_all  (GTask                 *task,
                 GCancellable          *cancellable)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) process = NULL;
+  g_autoptr(IdeSubprocess) process = NULL;
   const gchar * const *targets;
   const gchar *make = NULL;
   gchar *default_targets[] = { "all", NULL };
@@ -1047,7 +1047,7 @@ step_make_all  (GTask                 *task,
 
       ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
 
-      if (!g_subprocess_wait_check (process, cancellable, &error))
+      if (!ide_subprocess_wait_check (process, cancellable, &error))
         {
           g_task_return_error (task, error);
           return FALSE;
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index 14bd650..c4ecb82 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -1943,7 +1943,7 @@ ide_makecache_get_build_targets_worker (GTask        *task,
 
   for (guint j = 0; j < makedirs->len; j++)
     {
-      g_autoptr(GSubprocess) subprocess = NULL;
+      g_autoptr(IdeSubprocess) subprocess = NULL;
       g_autoptr(GHashTable) amdirs = NULL;
       g_autofree gchar *path = NULL;
       GFile *makedir;
@@ -1970,7 +1970,7 @@ ide_makecache_get_build_targets_worker (GTask        *task,
        * 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))
+      if (!ide_subprocess_communicate_utf8 (subprocess, PRINT_VARS, NULL, &stdout_buf, NULL, &error))
         {
           g_task_return_error (task, error);
           IDE_GOTO (failure);
diff --git a/plugins/flatpak/gbp-flatpak-runtime.c b/plugins/flatpak/gbp-flatpak-runtime.c
index e8e35bc..12fa6cb 100644
--- a/plugins/flatpak/gbp-flatpak-runtime.c
+++ b/plugins/flatpak/gbp-flatpak-runtime.c
@@ -61,11 +61,11 @@ get_build_directory (GbpFlatpakRuntime *self)
 
 static gboolean
 gbp_flatpak_runtime_contains_program_in_path (IdeRuntime   *runtime,
-                                          const gchar  *program,
-                                          GCancellable *cancellable)
+                                              const gchar  *program,
+                                              GCancellable *cancellable)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
 
   g_assert (IDE_IS_RUNTIME (runtime));
   g_assert (program != NULL);
@@ -78,14 +78,14 @@ gbp_flatpak_runtime_contains_program_in_path (IdeRuntime   *runtime,
 
   subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, NULL);
 
-  return (subprocess != NULL) && g_subprocess_wait_check (subprocess, cancellable, NULL);
+  return (subprocess != NULL) && ide_subprocess_wait_check (subprocess, cancellable, NULL);
 }
 
 static void
 gbp_flatpak_runtime_prebuild_worker (GTask        *task,
-                                 gpointer      source_object,
-                                 gpointer      task_data,
-                                 GCancellable *cancellable)
+                                     gpointer      source_object,
+                                     gpointer      task_data,
+                                     GCancellable *cancellable)
 {
   GbpFlatpakRuntime *self = source_object;
   g_autofree gchar *build_path = NULL;
@@ -153,9 +153,9 @@ gbp_flatpak_runtime_prebuild_worker (GTask        *task,
 
 static void
 gbp_flatpak_runtime_prebuild_async (IdeRuntime          *runtime,
-                                GCancellable        *cancellable,
-                                GAsyncReadyCallback  callback,
-                                gpointer             user_data)
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
   g_autoptr(GTask) task = NULL;
@@ -169,8 +169,8 @@ gbp_flatpak_runtime_prebuild_async (IdeRuntime          *runtime,
 
 static gboolean
 gbp_flatpak_runtime_prebuild_finish (IdeRuntime    *runtime,
-                                 GAsyncResult  *result,
-                                 GError       **error)
+                                     GAsyncResult  *result,
+                                     GError       **error)
 {
   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
 
@@ -205,7 +205,7 @@ gbp_flatpak_runtime_create_launcher (IdeRuntime  *runtime,
 
 static void
 gbp_flatpak_runtime_prepare_configuration (IdeRuntime       *runtime,
-                                       IdeConfiguration *configuration)
+                                           IdeConfiguration *configuration)
 {
   g_assert (IDE_IS_RUNTIME (runtime));
   g_assert (IDE_IS_CONFIGURATION (configuration));
@@ -215,9 +215,9 @@ gbp_flatpak_runtime_prepare_configuration (IdeRuntime       *runtime,
 
 static void
 gbp_flatpak_runtime_get_property (GObject    *object,
-                              guint       prop_id,
-                              GValue     *value,
-                              GParamSpec *pspec)
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
 {
   GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
 
@@ -242,9 +242,9 @@ gbp_flatpak_runtime_get_property (GObject    *object,
 
 static void
 gbp_flatpak_runtime_set_property (GObject      *object,
-                              guint         prop_id,
-                              const GValue *value,
-                              GParamSpec   *pspec)
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
 {
   GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
 


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