[gnome-builder] libide-threading: port subprocess launcher to IdeUnixFDMap



commit b75d8903d6ef70baa2e821b12e7bb9a8ea4e50c5
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 17:49:37 2022 -0700

    libide-threading: port subprocess launcher to IdeUnixFDMap
    
    Also update availability macros and add various argv helpers.

 .../threading/ide-flatpak-subprocess-private.h     |  27 +-
 src/libide/threading/ide-flatpak-subprocess.c      | 261 ++++++------
 src/libide/threading/ide-simple-subprocess.c       |   2 -
 src/libide/threading/ide-subprocess-launcher.c     | 450 +++++++++------------
 src/libide/threading/ide-subprocess-launcher.h     |  75 ++--
 src/libide/threading/ide-subprocess-supervisor.c   |   4 -
 src/libide/threading/ide-subprocess-supervisor.h   |  16 +-
 src/libide/threading/ide-subprocess.c              |  15 -
 src/libide/threading/ide-subprocess.h              |  52 +--
 9 files changed, 388 insertions(+), 514 deletions(-)
---
diff --git a/src/libide/threading/ide-flatpak-subprocess-private.h 
b/src/libide/threading/ide-flatpak-subprocess-private.h
index 64628c69b..3a9cf413a 100644
--- a/src/libide/threading/ide-flatpak-subprocess-private.h
+++ b/src/libide/threading/ide-flatpak-subprocess-private.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "ide-subprocess.h"
+#include "ide-unix-fd-map.h"
 
 G_BEGIN_DECLS
 
@@ -28,23 +29,13 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeFlatpakSubprocess, ide_flatpak_subprocess, IDE, FLATPAK_SUBPROCESS, GObject)
 
-typedef struct
-{
-  gint source_fd;
-  gint dest_fd;
-} IdeBreakoutFdMapping;
-
-IdeSubprocess *_ide_flatpak_subprocess_new (const gchar                 *cwd,
-                                             const gchar * const         *argv,
-                                             const gchar * const         *env,
-                                             GSubprocessFlags             flags,
-                                             gboolean                     clear_flags,
-                                             gint                         stdin_fd,
-                                             gint                         stdout_fd,
-                                             gint                         stderr_fd,
-                                             const IdeBreakoutFdMapping  *fd_map,
-                                             guint                        fd_map_len,
-                                             GCancellable                *cancellable,
-                                             GError                     **error) G_GNUC_INTERNAL;
+IdeSubprocess *_ide_flatpak_subprocess_new (const char          *cwd,
+                                            const char * const  *argv,
+                                            const char * const  *env,
+                                            GSubprocessFlags     flags,
+                                            gboolean             clear_flags,
+                                            IdeUnixFDMap        *unix_fd_map,
+                                            GCancellable        *cancellable,
+                                            GError             **error);
 
 G_END_DECLS
diff --git a/src/libide/threading/ide-flatpak-subprocess.c b/src/libide/threading/ide-flatpak-subprocess.c
index 065f597d4..3a5003c0c 100644
--- a/src/libide/threading/ide-flatpak-subprocess.c
+++ b/src/libide/threading/ide-flatpak-subprocess.c
@@ -58,29 +58,24 @@ struct _IdeFlatpakSubprocess
   gulong connection_closed_handler;
 
   GPid client_pid;
-  gint status;
+  int status;
 
   GSubprocessFlags flags;
 
   /* No reference */
   GThread *spawn_thread;
 
-  gchar **argv;
-  gchar **env;
-  gchar *cwd;
+  char **argv;
+  char **env;
+  char *cwd;
 
-  gchar *identifier;
-
-  gint stdin_fd;
-  gint stdout_fd;
-  gint stderr_fd;
+  char *identifier;
 
   GOutputStream *stdin_pipe;
   GInputStream *stdout_pipe;
   GInputStream *stderr_pipe;
 
-  IdeBreakoutFdMapping *fd_mapping;
-  guint fd_mapping_len;
+  IdeUnixFDMap *unix_fd_map;
 
   GMainContext *main_context;
 
@@ -139,29 +134,31 @@ struct _IdeFlatpakSubprocess
  */
 typedef struct
 {
-  const gchar *stdin_data;
-  gsize stdin_length;
-  gsize stdin_offset;
+  const char          *stdin_data;
+  gsize                stdin_length;
+  gsize                stdin_offset;
 
-  gboolean add_nul;
+  gboolean             add_nul;
 
-  GInputStream *stdin_buf;
+  GInputStream        *stdin_buf;
   GMemoryOutputStream *stdout_buf;
   GMemoryOutputStream *stderr_buf;
 
-  GCancellable *cancellable;
-  GSource      *cancellable_source;
+  GCancellable        *cancellable;
+  GSource             *cancellable_source;
 
-  guint         outstanding_ops;
-  gboolean      reported_error;
+  guint                outstanding_ops;
+  gboolean             reported_error;
 } CommunicateState;
 
 enum {
   PROP_0,
   PROP_ARGV,
+  PROP_CLEAR_ENV,
   PROP_CWD,
   PROP_ENV,
   PROP_FLAGS,
+  PROP_UNIX_FD_MAP,
   N_PROPS
 };
 
@@ -180,7 +177,7 @@ static CommunicateState *ide_flatpak_subprocess_communicate_internal (IdeFlatpak
 
 static GParamSpec *properties [N_PROPS];
 
-static const gchar *
+static const char *
 ide_flatpak_subprocess_get_identifier (IdeSubprocess *subprocess)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)subprocess;
@@ -485,7 +482,7 @@ ide_flatpak_subprocess_get_if_exited (IdeSubprocess *subprocess)
   return WIFEXITED (self->status);
 }
 
-static gint
+static int
 ide_flatpak_subprocess_get_exit_status (IdeSubprocess *subprocess)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)subprocess;
@@ -510,7 +507,7 @@ ide_flatpak_subprocess_get_if_signaled (IdeSubprocess *subprocess)
   return WIFSIGNALED (self->status);
 }
 
-static gint
+static int
 ide_flatpak_subprocess_get_term_sig (IdeSubprocess *subprocess)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)subprocess;
@@ -521,7 +518,7 @@ ide_flatpak_subprocess_get_term_sig (IdeSubprocess *subprocess)
   return WTERMSIG (self->status);
 }
 
-static gint
+static int
 ide_flatpak_subprocess_get_status (IdeSubprocess *subprocess)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)subprocess;
@@ -534,7 +531,7 @@ ide_flatpak_subprocess_get_status (IdeSubprocess *subprocess)
 
 static void
 ide_flatpak_subprocess_send_signal (IdeSubprocess *subprocess,
-                                     gint           signal_num)
+                                     int           signal_num)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)subprocess;
 
@@ -945,7 +942,7 @@ subprocess_iface_init (IdeSubprocessInterface *iface)
 
 static void
 maybe_create_input_stream (GInputStream **ret,
-                           gint          *fdptr,
+                           int          *fdptr,
                            gboolean       needs_stream)
 {
   g_assert (ret != NULL);
@@ -972,7 +969,7 @@ maybe_create_input_stream (GInputStream **ret,
 
 static void
 maybe_create_output_stream (GOutputStream **ret,
-                            gint           *fdptr,
+                            int           *fdptr,
                             gboolean        needs_stream)
 {
   g_assert (ret != NULL);
@@ -999,7 +996,7 @@ maybe_create_output_stream (GOutputStream **ret,
 
 static void
 ide_flatpak_subprocess_complete_command_locked (IdeFlatpakSubprocess *self,
-                                                 gint                   exit_status)
+                                                int                  exit_status)
 {
   GList *waiting;
 
@@ -1046,10 +1043,10 @@ ide_flatpak_subprocess_complete_command_locked (IdeFlatpakSubprocess *self,
 
 static void
 host_command_exited_cb (GDBusConnection *connection,
-                        const gchar     *sender_name,
-                        const gchar     *object_path,
-                        const gchar     *interface_name,
-                        const gchar     *signal_name,
+                        const char      *sender_name,
+                        const char      *object_path,
+                        const char      *interface_name,
+                        const char      *signal_name,
                         GVariant        *parameters,
                         gpointer         user_data)
 {
@@ -1094,7 +1091,7 @@ host_command_exited_cb (GDBusConnection *connection,
 
 static void
 ide_flatpak_subprocess_cancelled (IdeFlatpakSubprocess *self,
-                                   GCancellable          *cancellable)
+                                  GCancellable         *cancellable)
 {
   IDE_ENTRY;
 
@@ -1107,7 +1104,7 @@ ide_flatpak_subprocess_cancelled (IdeFlatpakSubprocess *self,
 }
 
 static inline void
-maybe_close (gint *fd)
+maybe_close (int *fd)
 {
   g_assert (fd != NULL);
   g_assert (*fd >= -1);
@@ -1120,9 +1117,9 @@ maybe_close (gint *fd)
 
 static void
 ide_flatpak_subprocess_connection_closed (IdeFlatpakSubprocess *self,
-                                           gboolean               remote_peer_vanished,
-                                           const GError          *error,
-                                           GDBusConnection       *connection)
+                                          gboolean              remote_peer_vanished,
+                                          const GError         *error,
+                                          GDBusConnection      *connection)
 {
   g_autoptr(GMutexLocker) locker = NULL;
 
@@ -1143,8 +1140,8 @@ ide_flatpak_subprocess_connection_closed (IdeFlatpakSubprocess *self,
 
 static gboolean
 ide_flatpak_subprocess_initable_init (GInitable     *initable,
-                                       GCancellable  *cancellable,
-                                       GError       **error)
+                                      GCancellable  *cancellable,
+                                      GError       **error)
 {
   IdeFlatpakSubprocess *self = (IdeFlatpakSubprocess *)initable;
   g_autoptr(GVariantBuilder) fd_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{uh}"));
@@ -1153,18 +1150,20 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
   g_autoptr(GVariant) reply = NULL;
   g_autoptr(GVariant) params = NULL;
   guint32 client_pid = 0;
-  gint stdout_pair[2] = { -1, -1 };
-  gint stderr_pair[2] = { -1, -1 };
-  gint stdin_pair[2] = { -1, -1 };
-  gint stdin_handle = -1;
-  gint stdout_handle = -1;
-  gint stderr_handle = -1;
+  int stdout_pair[2] = { -1, -1 };
+  int stderr_pair[2] = { -1, -1 };
+  int stdin_pair[2] = { -1, -1 };
+  int stdin_handle = -1;
+  int stdout_handle = -1;
+  int stderr_handle = -1;
   gboolean ret = FALSE;
   guint flags = FLATPAK_HOST_COMMAND_FLAGS_WATCH_BUS;
+  guint length;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_FLATPAK_SUBPROCESS (self));
+  g_assert (IDE_IS_UNIX_FD_MAP (self->unix_fd_map));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   if (!(self->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, error)))
@@ -1173,18 +1172,14 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
   if (self->clear_env)
     flags |= FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV;
 
-
-  /*
-   * Handle STDIN for the process.
+  /* Handle STDIN for the process.
    *
    * Make sure we handle inherit STDIN, a new pipe (so that the application can
    * get the stdin stream), or simply redirect to /dev/null.
    */
-  if (self->stdin_fd != -1)
+  if (-1 != (stdin_pair[0] = ide_unix_fd_map_steal_stdin (self->unix_fd_map)))
     {
       self->flags &= ~G_SUBPROCESS_FLAGS_STDIN_PIPE;
-      stdin_pair[0] = self->stdin_fd;
-      self->stdin_fd = -1;
     }
   else if (self->flags & G_SUBPROCESS_FLAGS_STDIN_INHERIT)
     {
@@ -1213,18 +1208,15 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
     maybe_close (&stdin_pair[0]);
 
 
-  /*
-   * Setup STDOUT for the process.
+  /* Setup STDOUT for the process.
    *
    * Make sure we redirect STDOUT to our stdout, unless a pipe was requested
    * for the application to read. However, if silence was requested, redirect
    * to /dev/null.
    */
-  if (self->stdout_fd != -1)
+  if (-1 != (stdout_pair[1] = ide_unix_fd_map_steal_stdout (self->unix_fd_map)))
     {
       self->flags &= ~G_SUBPROCESS_FLAGS_STDOUT_PIPE;
-      stdout_pair[1] = self->stdout_fd;
-      self->stdout_fd = -1;
     }
   else if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_SILENCE)
     {
@@ -1253,18 +1245,15 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
     maybe_close (&stdout_pair[1]);
 
 
-  /*
-   * Handle STDERR for the process.
+  /* Handle STDERR for the process.
    *
    * If silence is requested, we simply redirect to /dev/null. If the
    * application requested to read from the subprocesses stderr, then we need
    * to create a pipe. Otherwose, merge stderr into our own stderr.
    */
-  if (self->stderr_fd != -1)
+  if (-1 != (stderr_pair[1] = ide_unix_fd_map_steal_stderr (self->unix_fd_map)))
     {
       self->flags &= ~G_SUBPROCESS_FLAGS_STDERR_PIPE;
-      stderr_pair[1] = self->stderr_fd;
-      self->stderr_fd = -1;
     }
   else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_SILENCE)
     {
@@ -1293,39 +1282,32 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
     maybe_close (&stderr_pair[1]);
 
 
-  /*
-   * Build our FDs for the message.
-   */
+  /* Build our FDs for the message. */
   g_variant_builder_add (fd_builder, "{uh}", 0, stdin_handle);
   g_variant_builder_add (fd_builder, "{uh}", 1, stdout_handle);
   g_variant_builder_add (fd_builder, "{uh}", 2, stderr_handle);
 
 
-  /*
-   * Now add the rest of our FDs that we might need to map in for which
+  /* Now add the rest of our FDs that we might need to map in for which
    * the subprocess launcher tried to map.
    */
-  for (guint i = 0; i < self->fd_mapping_len; i++)
+  length = ide_unix_fd_map_get_length (self->unix_fd_map);
+  for (guint i = 0; i < length; i++)
     {
-      const IdeBreakoutFdMapping *map = &self->fd_mapping[i];
-      g_autoptr(GError) fd_error = NULL;
-      gint dest_handle;
+      int source_fd;
+      int dest_fd;
 
-      dest_handle = g_unix_fd_list_append (fd_list, map->source_fd, &fd_error);
-
-      if (dest_handle != -1)
-        g_variant_builder_add (fd_builder, "{uh}", map->dest_fd, dest_handle);
-      else
-        g_warning ("%s", fd_error->message);
+      if (-1 != (source_fd = ide_unix_fd_map_peek (self->unix_fd_map, i, &dest_fd)))
+        {
+          int dest_handle = g_unix_fd_list_append (fd_list, source_fd, NULL);
 
-      close (map->source_fd);
+          if (dest_handle != -1)
+            g_variant_builder_add (fd_builder, "{uh}", dest_fd, dest_handle);
+        }
     }
 
-  /*
-   * We don't want to allow these FDs to be used again.
-   */
-  self->fd_mapping_len = 0;
-  g_clear_pointer (&self->fd_mapping, g_free);
+  /* We don't want to allow these FDs to be used again. */
+  g_clear_object (&self->unix_fd_map);
 
 
   /*
@@ -1343,10 +1325,10 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
     {
       for (guint i = 0; self->env[i]; i++)
         {
-          const gchar *pair = self->env[i];
-          const gchar *eq = strchr (pair, '=');
-          const gchar *val = eq ? eq + 1 : "";
-          g_autofree gchar *key = eq ? g_strndup (pair, eq - pair) : g_strdup (pair);
+          const char *pair = self->env[i];
+          const char *eq = strchr (pair, '=');
+          const char *val = eq ? eq + 1 : "";
+          g_autofree char *key = eq ? g_strndup (pair, eq - pair) : g_strdup (pair);
 
           g_variant_builder_add (env_builder, "{ss}", key, val);
         }
@@ -1410,7 +1392,7 @@ ide_flatpak_subprocess_initable_init (GInitable     *initable,
 
 #ifdef IDE_ENABLE_TRACE
   {
-    g_autofree gchar *str = g_variant_print (params, TRUE);
+    g_autofree char *str = g_variant_print (params, TRUE);
     IDE_TRACE_MSG ("Calling HostCommand with %s", str);
   }
 #endif
@@ -1474,9 +1456,9 @@ initiable_iface_init (GInitableIface *iface)
   iface->init = ide_flatpak_subprocess_initable_init;
 }
 
-G_DEFINE_TYPE_EXTENDED (IdeFlatpakSubprocess, ide_flatpak_subprocess, G_TYPE_OBJECT, G_TYPE_FLAG_FINAL,
-                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initiable_iface_init)
-                        G_IMPLEMENT_INTERFACE (IDE_TYPE_SUBPROCESS, subprocess_iface_init))
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeFlatpakSubprocess, ide_flatpak_subprocess, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initiable_iface_init)
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_SUBPROCESS, subprocess_iface_init))
 
 static void
 ide_flatpak_subprocess_dispose (GObject *object)
@@ -1522,23 +1504,11 @@ ide_flatpak_subprocess_finalize (GObject *object)
   g_clear_object (&self->stdout_pipe);
   g_clear_object (&self->stderr_pipe);
   g_clear_object (&self->connection);
+  g_clear_object (&self->unix_fd_map);
 
   g_mutex_clear (&self->waiter_mutex);
   g_cond_clear (&self->waiter_cond);
 
-  if (self->stdin_fd != -1)
-    close (self->stdin_fd);
-
-  if (self->stdout_fd != -1)
-    close (self->stdout_fd);
-
-  if (self->stderr_fd != -1)
-    close (self->stderr_fd);
-
-  for (guint i = 0; i < self->fd_mapping_len; i++)
-    close (self->fd_mapping[i].source_fd);
-  g_clear_pointer (&self->fd_mapping, g_free);
-
   G_OBJECT_CLASS (ide_flatpak_subprocess_parent_class)->finalize (object);
 
   IDE_EXIT;
@@ -1546,14 +1516,18 @@ ide_flatpak_subprocess_finalize (GObject *object)
 
 static void
 ide_flatpak_subprocess_get_property (GObject    *object,
-                                      guint       prop_id,
-                                      GValue     *value,
-                                      GParamSpec *pspec)
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
 {
   IdeFlatpakSubprocess *self = IDE_FLATPAK_SUBPROCESS (object);
 
   switch (prop_id)
     {
+    case PROP_CLEAR_ENV:
+      g_value_set_boolean (value, self->clear_env);
+      break;
+
     case PROP_CWD:
       g_value_set_string (value, self->cwd);
       break;
@@ -1570,6 +1544,10 @@ ide_flatpak_subprocess_get_property (GObject    *object,
       g_value_set_flags (value, self->flags);
       break;
 
+    case PROP_UNIX_FD_MAP:
+      g_value_set_object (value, self->unix_fd_map);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -1577,22 +1555,26 @@ ide_flatpak_subprocess_get_property (GObject    *object,
 
 static void
 ide_flatpak_subprocess_set_property (GObject      *object,
-                                      guint         prop_id,
-                                      const GValue *value,
-                                      GParamSpec   *pspec)
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
 {
   IdeFlatpakSubprocess *self = IDE_FLATPAK_SUBPROCESS (object);
 
   switch (prop_id)
     {
-    case PROP_CWD:
-      self->cwd = g_value_dup_string (value);
-      break;
-
     case PROP_ARGV:
       self->argv = g_value_dup_boxed (value);
       break;
 
+    case PROP_CLEAR_ENV:
+      self->clear_env = g_value_get_boolean (value);
+      break;
+
+    case PROP_CWD:
+      self->cwd = g_value_dup_string (value);
+      break;
+
     case PROP_ENV:
       self->env = g_value_dup_boxed (value);
       break;
@@ -1601,6 +1583,12 @@ ide_flatpak_subprocess_set_property (GObject      *object,
       self->flags = g_value_get_flags (value);
       break;
 
+    case PROP_UNIX_FD_MAP:
+      self->unix_fd_map = g_value_dup_object (value);
+      if (self->unix_fd_map == NULL)
+        self->unix_fd_map = ide_unix_fd_map_new ();
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -1616,6 +1604,11 @@ ide_flatpak_subprocess_class_init (IdeFlatpakSubprocessClass *klass)
   object_class->get_property = ide_flatpak_subprocess_get_property;
   object_class->set_property = ide_flatpak_subprocess_set_property;
 
+  properties [PROP_CLEAR_ENV] =
+    g_param_spec_boolean ("clear-env", NULL, NULL,
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
   properties [PROP_CWD] =
     g_param_spec_string ("cwd",
                          "Current Working Directory",
@@ -1645,6 +1638,11 @@ ide_flatpak_subprocess_class_init (IdeFlatpakSubprocessClass *klass)
                         G_SUBPROCESS_FLAGS_NONE,
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_UNIX_FD_MAP] =
+    g_param_spec_object ("unix-fd-map", NULL, NULL,
+                         IDE_TYPE_UNIX_FD_MAP,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
@@ -1653,10 +1651,6 @@ ide_flatpak_subprocess_init (IdeFlatpakSubprocess *self)
 {
   IDE_ENTRY;
 
-  self->stdin_fd = -1;
-  self->stdout_fd = -1;
-  self->stderr_fd = -1;
-
   g_mutex_init (&self->waiter_mutex);
   g_cond_init (&self->waiter_cond);
 
@@ -1664,40 +1658,31 @@ ide_flatpak_subprocess_init (IdeFlatpakSubprocess *self)
 }
 
 IdeSubprocess *
-_ide_flatpak_subprocess_new (const gchar                 *cwd,
-                              const gchar * const         *argv,
-                              const gchar * const         *env,
-                              GSubprocessFlags             flags,
-                              gboolean                     clear_env,
-                              gint                         stdin_fd,
-                              gint                         stdout_fd,
-                              gint                         stderr_fd,
-                              const IdeBreakoutFdMapping  *fd_mapping,
-                              guint                        fd_mapping_len,
-                              GCancellable                *cancellable,
-                              GError                     **error)
+_ide_flatpak_subprocess_new (const char          *cwd,
+                             const char * const  *argv,
+                             const char * const  *env,
+                             GSubprocessFlags     flags,
+                             gboolean             clear_env,
+                             IdeUnixFDMap        *unix_fd_map,
+                             GCancellable        *cancellable,
+                             GError             **error)
 {
   g_autoptr(IdeFlatpakSubprocess) ret = NULL;
 
   g_return_val_if_fail (argv != NULL, NULL);
   g_return_val_if_fail (argv[0] != NULL, NULL);
+  g_return_val_if_fail (!unix_fd_map || IDE_IS_UNIX_FD_MAP (unix_fd_map), NULL);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
 
   ret = g_object_new (IDE_TYPE_FLATPAK_SUBPROCESS,
                       "cwd", cwd,
                       "argv", argv,
+                      "clear-env", clear_env,
                       "env", env,
                       "flags", flags,
+                      "unix-fd-map", unix_fd_map,
                       NULL);
 
-  ret->clear_env = clear_env;
-  ret->stdin_fd = stdin_fd;
-  ret->stdout_fd = stdout_fd;
-  ret->stderr_fd = stderr_fd;
-
-  ret->fd_mapping = g_new0 (IdeBreakoutFdMapping, fd_mapping_len);
-  ret->fd_mapping_len = fd_mapping_len;
-  memcpy (ret->fd_mapping, fd_mapping, sizeof(IdeBreakoutFdMapping) * fd_mapping_len);
-
   if (!g_initable_init (G_INITABLE (ret), cancellable, error))
     return NULL;
 
diff --git a/src/libide/threading/ide-simple-subprocess.c b/src/libide/threading/ide-simple-subprocess.c
index 842811dea..b8b04cbb2 100644
--- a/src/libide/threading/ide-simple-subprocess.c
+++ b/src/libide/threading/ide-simple-subprocess.c
@@ -418,8 +418,6 @@ subprocess_iface_init (IdeSubprocessInterface *iface)
  * Creates a new #IdeSimpleSubprocess wrapping the #GSubprocess.
  *
  * Returns: (transfer full): A new #IdeSubprocess
- *
- * Since: 3.32
  */
 IdeSubprocess *
 ide_simple_subprocess_new (GSubprocess *subprocess)
diff --git a/src/libide/threading/ide-subprocess-launcher.c b/src/libide/threading/ide-subprocess-launcher.c
index 0057d3b01..7e5daf93a 100644
--- a/src/libide/threading/ide-subprocess-launcher.c
+++ b/src/libide/threading/ide-subprocess-launcher.c
@@ -41,33 +41,26 @@
 #include "ide-flatpak-subprocess-private.h"
 #include "ide-simple-subprocess-private.h"
 #include "ide-subprocess-launcher.h"
+#include "ide-unix-fd-map.h"
+
+/* This comes from libide-io but we need access to it */
+#include "../io/ide-shell.h"
 
 #define is_flatpak() (ide_get_process_kind() == IDE_PROCESS_KIND_FLATPAK)
 
 typedef struct
 {
-  GSubprocessFlags  flags;
-
   GPtrArray        *argv;
-  gchar            *cwd;
-  gchar           **environ;
-  GArray           *fd_mapping;
-  gchar            *stdout_file_path;
-
-  gint              stdin_fd;
-  gint              stdout_fd;
-  gint              stderr_fd;
+  char             *cwd;
+  char            **environ;
+  char             *stdout_file_path;
+  IdeUnixFDMap     *unix_fd_map;
 
+  GSubprocessFlags  flags : 14;
   guint             run_on_host : 1;
   guint             clear_env : 1;
 } IdeSubprocessLauncherPrivate;
 
-typedef struct
-{
-  gint source_fd;
-  gint dest_fd;
-} FdMapping;
-
 G_DEFINE_TYPE_WITH_PRIVATE (IdeSubprocessLauncher, ide_subprocess_launcher, G_TYPE_OBJECT)
 
 enum {
@@ -119,7 +112,7 @@ ide_subprocess_launcher_kill_process_group (GCancellable *cancellable,
                                             GSubprocess  *subprocess)
 {
 #ifdef G_OS_UNIX
-  const gchar *ident;
+  const char *ident;
   pid_t pid;
 
   IDE_ENTRY;
@@ -201,10 +194,6 @@ ide_subprocess_launcher_spawn_host_worker (GTask        *task,
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
   g_autoptr(IdeSubprocess) process = NULL;
   g_autoptr(GError) error = NULL;
-  g_autoptr(GArray) fds = NULL;
-  gint stdin_fd = -1;
-  gint stdout_fd = -1;
-  gint stderr_fd = -1;
 
   IDE_ENTRY;
 
@@ -212,56 +201,44 @@ ide_subprocess_launcher_spawn_host_worker (GTask        *task,
 
 #ifdef IDE_ENABLE_TRACE
   {
-    g_autofree gchar *str = NULL;
-    g_autofree gchar *env = NULL;
-    str = g_strjoinv (" ", (gchar **)priv->argv->pdata);
+    g_autofree char *str = NULL;
+    g_autofree char *env = NULL;
+    str = g_strjoinv (" ", (char **)priv->argv->pdata);
     env = priv->environ ? g_strjoinv (" ", priv->environ) : g_strdup ("");
     IDE_TRACE_MSG ("Launching '%s' with environment %s %s parent environment",
                    str, env, priv->clear_env ? "clearing" : "inheriting");
   }
 #endif
 
-  fds = g_steal_pointer (&priv->fd_mapping);
-
-  if (priv->stdin_fd != -1)
-    stdin_fd = dup (priv->stdin_fd);
-
-  if (priv->stdout_fd != -1)
-    stdout_fd = dup (priv->stdout_fd);
-  else if (priv->stdout_file_path != NULL)
-    stdout_fd = open (priv->stdout_file_path, O_WRONLY);
-
-  if (priv->stderr_fd != -1)
-    stderr_fd = dup (priv->stderr_fd);
-
-  process = _ide_flatpak_subprocess_new (priv->cwd,
-                                          (const gchar * const *)priv->argv->pdata,
-                                          (const gchar * const *)priv->environ,
-                                          priv->flags,
-                                          priv->clear_env,
-                                          stdin_fd,
-                                          stdout_fd,
-                                          stderr_fd,
-                                          fds ? (gpointer)fds->data : NULL,
-                                          fds ? fds->len : 0,
-                                          cancellable,
-                                          &error);
-
-  if (process == NULL)
+  if (priv->stdout_file_path != NULL &&
+      !ide_unix_fd_map_open_file (priv->unix_fd_map,
+                                  priv->stdout_file_path, O_WRONLY, STDOUT_FILENO,
+                                  &error))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       IDE_EXIT;
     }
 
-  if (cancellable != NULL)
+  if (!(process = _ide_flatpak_subprocess_new (priv->cwd,
+                                               (const char * const *)priv->argv->pdata,
+                                               (const char * const *)priv->environ,
+                                               priv->flags,
+                                               priv->clear_env,
+                                               priv->unix_fd_map,
+                                               cancellable,
+                                               &error)))
     {
-      g_signal_connect_object (cancellable,
-                               "cancelled",
-                               G_CALLBACK (ide_subprocess_launcher_kill_host_process),
-                               process,
-                               0);
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
     }
 
+  if (cancellable != NULL)
+    g_signal_connect_object (cancellable,
+                             "cancelled",
+                             G_CALLBACK (ide_subprocess_launcher_kill_host_process),
+                             process,
+                             0);
+
   g_task_return_pointer (task, g_steal_pointer (&process), g_object_unref);
 
   IDE_EXIT;
@@ -280,6 +257,7 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
   g_autoptr(IdeSubprocess) wrapped = NULL;
   g_autoptr(GError) error = NULL;
   gpointer child_data = NULL;
+  guint length;
 
   IDE_ENTRY;
 
@@ -289,10 +267,10 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
     child_data = GUINT_TO_POINTER (TRUE);
 
   {
-    g_autofree gchar *str = NULL;
-    g_autofree gchar *env = NULL;
+    g_autofree char *str = NULL;
+    g_autofree char *env = NULL;
 
-    str = g_strjoinv (" ", (gchar **)priv->argv->pdata);
+    str = g_strjoinv (" ", (char **)priv->argv->pdata);
     env = priv->environ ? g_strjoinv (" ", priv->environ) : g_strdup ("");
 
     g_debug ("Launching '%s' from directory '%s' with environment %s %s parent environment",
@@ -306,33 +284,22 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
   if (priv->stdout_file_path != NULL)
     g_subprocess_launcher_set_stdout_file_path (launcher, priv->stdout_file_path);
 
-  if (priv->stdin_fd != -1)
-    {
-      g_subprocess_launcher_take_stdin_fd (launcher, priv->stdin_fd);
-      priv->stdin_fd = -1;
-    }
-
-  if (priv->stdout_fd != -1)
-    {
-      g_subprocess_launcher_take_stdout_fd (launcher, priv->stdout_fd);
-      priv->stdout_fd = -1;
-    }
-
-  if (priv->stderr_fd != -1)
-    {
-      g_subprocess_launcher_take_stderr_fd (launcher, priv->stderr_fd);
-      priv->stderr_fd = -1;
-    }
-
-  if (priv->fd_mapping != NULL)
+  length = ide_unix_fd_map_get_length (priv->unix_fd_map);
+  for (guint i = 0; i < length; i++)
     {
-      g_autoptr(GArray) ar = g_steal_pointer (&priv->fd_mapping);
+      int source_fd;
+      int dest_fd;
 
-      for (guint i = 0; i < ar->len; i++)
+      if (-1 != (source_fd = ide_unix_fd_map_steal (priv->unix_fd_map, i, &dest_fd)))
         {
-          const FdMapping *map = &g_array_index (ar, FdMapping, i);
-
-          g_subprocess_launcher_take_fd (launcher, map->source_fd, map->dest_fd);
+          if (dest_fd == STDIN_FILENO)
+            g_subprocess_launcher_take_stdin_fd (launcher, source_fd);
+          else if (dest_fd == STDOUT_FILENO)
+            g_subprocess_launcher_take_stdout_fd (launcher, source_fd);
+          else if (dest_fd == STDERR_FILENO)
+            g_subprocess_launcher_take_stderr_fd (launcher, source_fd);
+          else
+            g_subprocess_launcher_take_fd (launcher, source_fd, dest_fd);
         }
     }
 
@@ -344,8 +311,8 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
    */
   if (priv->clear_env)
     {
-      gchar *envp[] = { NULL };
-      g_subprocess_launcher_set_environ (launcher, envp);
+      static const char *envp[] = { NULL };
+      g_subprocess_launcher_set_environ (launcher, (char **)envp);
     }
 
   /*
@@ -354,35 +321,30 @@ ide_subprocess_launcher_spawn_worker (GTask        *task,
    */
   if (priv->environ != NULL)
     {
-      for (guint i = 0; priv->environ[i] != NULL; i++)
+      for (guint i = 0; priv->environ[i]; i++)
         {
-          const gchar *pair = priv->environ[i];
-          const gchar *eq = strchr (pair, '=');
-          g_autofree gchar *key = g_strndup (pair, eq - pair);
-          const gchar *val = eq ? eq + 1 : NULL;
+          g_autofree char *key = NULL;
+          g_autofree char *value = NULL;
 
-          g_subprocess_launcher_setenv (launcher, key, val, TRUE);
+          if (ide_environ_parse (priv->environ[i], &key, &value))
+            g_subprocess_launcher_setenv (launcher, key, value, TRUE);
         }
     }
 
-  real = g_subprocess_launcher_spawnv (launcher,
-                                       (const gchar * const *)priv->argv->pdata,
-                                       &error);
-
-  if (real == NULL)
+  if (!(real = g_subprocess_launcher_spawnv (launcher,
+                                             (const char * const *)priv->argv->pdata,
+                                             &error)))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       IDE_EXIT;
     }
 
   if (cancellable != NULL)
-    {
-      g_signal_connect_object (cancellable,
-                               "cancelled",
-                               G_CALLBACK (ide_subprocess_launcher_kill_process_group),
-                               real,
-                               0);
-    }
+    g_signal_connect_object (cancellable,
+                             "cancelled",
+                             G_CALLBACK (ide_subprocess_launcher_kill_process_group),
+                             real,
+                             0);
 
   wrapped = ide_simple_subprocess_new (real);
 
@@ -415,7 +377,7 @@ ide_subprocess_launcher_real_spawn (IdeSubprocessLauncher  *self,
        * that it can get /app/bin too. Since it chains up to us, we wont
        * overwrite PATH in that case (which is what we want).
        */
-      ide_subprocess_launcher_setenv (self, "PATH", SAFE_PATH, FALSE);
+      ide_subprocess_launcher_setenv (self, "PATH", ide_get_user_default_path (), FALSE);
       ide_subprocess_launcher_setenv (self, "HOME", g_get_home_dir (), FALSE);
       ide_subprocess_launcher_setenv (self, "USER", g_get_user_name (), FALSE);
       ide_subprocess_launcher_setenv (self, "LANG", g_getenv ("LANG"), FALSE);
@@ -445,42 +407,12 @@ ide_subprocess_launcher_finalize (GObject *object)
   IdeSubprocessLauncher *self = (IdeSubprocessLauncher *)object;
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
-  if (priv->fd_mapping != NULL)
-    {
-      for (guint i = 0; i < priv->fd_mapping->len; i++)
-        {
-          FdMapping *map = &g_array_index (priv->fd_mapping, FdMapping, i);
-
-          if (map->source_fd != -1)
-            close (map->source_fd);
-        }
-
-      g_clear_pointer (&priv->fd_mapping, g_array_unref);
-    }
-
+  g_clear_object (&priv->unix_fd_map);
   g_clear_pointer (&priv->argv, g_ptr_array_unref);
   g_clear_pointer (&priv->cwd, g_free);
   g_clear_pointer (&priv->environ, g_strfreev);
   g_clear_pointer (&priv->stdout_file_path, g_free);
 
-  if (priv->stdin_fd != -1)
-    {
-      close (priv->stdin_fd);
-      priv->stdin_fd = -1;
-    }
-
-  if (priv->stdout_fd != -1)
-    {
-      close (priv->stdout_fd);
-      priv->stdout_fd = -1;
-    }
-
-  if (priv->stderr_fd != -1)
-    {
-      close (priv->stderr_fd);
-      priv->stderr_fd = -1;
-    }
-
   G_OBJECT_CLASS (ide_subprocess_launcher_parent_class)->finalize (object);
 }
 
@@ -570,14 +502,14 @@ ide_subprocess_launcher_class_init (IdeSubprocessLauncherClass *klass)
                           "Clear Environment",
                           "If the environment should be cleared before setting environment variables.",
                           FALSE,
-                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_CWD] =
     g_param_spec_string ("cwd",
                          "Current Working Directory",
                          "Current Working Directory",
                          NULL,
-                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_FLAGS] =
     g_param_spec_flags ("flags",
@@ -589,17 +521,17 @@ ide_subprocess_launcher_class_init (IdeSubprocessLauncherClass *klass)
 
   properties [PROP_ENVIRON] =
     g_param_spec_boxed ("environ",
-                        "Environ",
-                        "Environ",
+                        "Environment",
+                        "The environment variables for the subprocess",
                         G_TYPE_STRV,
-                        (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+                        (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_RUN_ON_HOST] =
     g_param_spec_boolean ("run-on-host",
                           "Run on Host",
                           "Run on Host",
                           FALSE,
-                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
@@ -611,20 +543,12 @@ ide_subprocess_launcher_init (IdeSubprocessLauncher *self)
 
   priv->clear_env = TRUE;
 
-  priv->stdin_fd = -1;
-  priv->stdout_fd = -1;
-  priv->stderr_fd = -1;
+  priv->unix_fd_map = ide_unix_fd_map_new ();
 
   priv->argv = g_ptr_array_new_with_free_func (g_free);
   g_ptr_array_add (priv->argv, NULL);
 
   priv->cwd = g_strdup (".");
-  /* Prevent inheritance of G_MESSAGES_DEBUG because it brings a lot of problems with IPC
-   * over stdout/stdin because all the debug messages would go to stdout, which means
-   * that the connection would be closed because of invalid data. If needed it can still
-   * be set back if needed, but at least it's a good default.
-   */
-  ide_subprocess_launcher_setenv (self, "G_MESSAGES_DEBUG", "", TRUE);
 }
 
 void
@@ -652,14 +576,14 @@ ide_subprocess_launcher_get_flags (IdeSubprocessLauncher *self)
   return priv->flags;
 }
 
-const gchar * const *
+const char * const *
 ide_subprocess_launcher_get_environ (IdeSubprocessLauncher *self)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), NULL);
 
-  return (const gchar * const *)priv->environ;
+  return (const char * const *)priv->environ;
 }
 
 /**
@@ -669,27 +593,30 @@ ide_subprocess_launcher_get_environ (IdeSubprocessLauncher *self)
  * of environment variables to set
  *
  * Replace the environment variables by a new list of variables.
- *
- * Since: 3.32
  */
 void
 ide_subprocess_launcher_set_environ (IdeSubprocessLauncher *self,
-                                     const gchar * const   *environ_)
+                                     const char * const    *environ_)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
-  if (priv->environ != (gchar **)environ_)
+  if (priv->environ == (char **)environ_)
+    return;
+
+  if ((priv->environ == NULL || environ_ == NULL) ||
+      !g_strv_equal ((const char * const *)priv->environ, environ_))
     {
       g_strfreev (priv->environ);
-      priv->environ = g_strdupv ((gchar **)environ_);
+      priv->environ = g_strdupv ((char **)environ_);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRON]);
     }
 }
 
-const gchar *
+const char *
 ide_subprocess_launcher_getenv (IdeSubprocessLauncher *self,
-                                const gchar           *key)
+                                const char           *key)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -701,8 +628,8 @@ ide_subprocess_launcher_getenv (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_setenv (IdeSubprocessLauncher *self,
-                                const gchar           *key,
-                                const gchar           *value,
+                                const char           *key,
+                                const char           *value,
                                 gboolean               replace)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
@@ -718,7 +645,7 @@ ide_subprocess_launcher_setenv (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_push_argv (IdeSubprocessLauncher *self,
-                                   const gchar           *argv)
+                                   const char           *argv)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -735,8 +662,6 @@ ide_subprocess_launcher_push_argv (IdeSubprocessLauncher *self,
  * Synchronously spawn a process using the internal state.
  *
  * Returns: (transfer full): an #IdeSubprocess or %NULL upon error.
- *
- * Since: 3.32
  */
 IdeSubprocess *
 ide_subprocess_launcher_spawn (IdeSubprocessLauncher  *self,
@@ -751,7 +676,7 @@ ide_subprocess_launcher_spawn (IdeSubprocessLauncher  *self,
 
 void
 ide_subprocess_launcher_set_cwd (IdeSubprocessLauncher *self,
-                                 const gchar           *cwd)
+                                 const char           *cwd)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -768,7 +693,7 @@ ide_subprocess_launcher_set_cwd (IdeSubprocessLauncher *self,
     }
 }
 
-const gchar *
+const char *
 ide_subprocess_launcher_get_cwd (IdeSubprocessLauncher *self)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
@@ -792,8 +717,8 @@ ide_subprocess_launcher_overlay_environment (IdeSubprocessLauncher *self,
       for (guint i = 0; i < n_items; i++)
         {
           g_autoptr(IdeEnvironmentVariable) var = NULL;
-          const gchar *key;
-          const gchar *value;
+          const char *key;
+          const char *value;
 
           var = g_list_model_get_item (G_LIST_MODEL (environment), i);
           key = ide_environment_variable_get_key (var);
@@ -814,12 +739,10 @@ ide_subprocess_launcher_overlay_environment (IdeSubprocessLauncher *self,
  * for each element of @args.
  *
  * If @args is %NULL, this function does nothing.
- *
- * Since: 3.32
  */
 void
 ide_subprocess_launcher_push_args (IdeSubprocessLauncher *self,
-                                   const gchar * const   *args)
+                                   const char * const   *args)
 {
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
@@ -830,11 +753,11 @@ ide_subprocess_launcher_push_args (IdeSubprocessLauncher *self,
     }
 }
 
-gchar *
+char *
 ide_subprocess_launcher_pop_argv (IdeSubprocessLauncher *self)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
-  gchar *ret = NULL;
+  char *ret = NULL;
 
   g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), NULL);
 
@@ -861,8 +784,6 @@ ide_subprocess_launcher_pop_argv (IdeSubprocessLauncher *self)
  * zone and requires the application was built with --allow=devel.
  *
  * Returns: %TRUE if the process should be executed outside the containment zone.
- *
- * Since: 3.32
  */
 gboolean
 ide_subprocess_launcher_get_run_on_host (IdeSubprocessLauncher *self)
@@ -879,8 +800,6 @@ ide_subprocess_launcher_get_run_on_host (IdeSubprocessLauncher *self)
  *
  * Sets the #IdeSubprocessLauncher:run-on-host property. See
  * ide_subprocess_launcher_get_run_on_host() for more information.
- *
- * Since: 3.32
  */
 void
 ide_subprocess_launcher_set_run_on_host (IdeSubprocessLauncher *self,
@@ -926,50 +845,36 @@ ide_subprocess_launcher_set_clear_env (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_take_stdin_fd (IdeSubprocessLauncher *self,
-                                       gint                   stdin_fd)
+                                       int                    stdin_fd)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
+  g_autoptr(GError) error = NULL;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
-  if (priv->stdin_fd != stdin_fd)
-    {
-      if (priv->stdin_fd != -1)
-        close (priv->stdin_fd);
-      priv->stdin_fd = stdin_fd;
-    }
+  ide_unix_fd_map_take (priv->unix_fd_map, stdin_fd, STDIN_FILENO);
 }
 
 void
 ide_subprocess_launcher_take_stdout_fd (IdeSubprocessLauncher *self,
-                                        gint                   stdout_fd)
+                                        int                    stdout_fd)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
-  if (priv->stdout_fd != stdout_fd)
-    {
-      if (priv->stdout_fd != -1)
-        close (priv->stdout_fd);
-      priv->stdout_fd = stdout_fd;
-    }
+  ide_unix_fd_map_take (priv->unix_fd_map, stdout_fd, STDOUT_FILENO);
 }
 
 void
 ide_subprocess_launcher_take_stderr_fd (IdeSubprocessLauncher *self,
-                                        gint                   stderr_fd)
+                                        int                    stderr_fd)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
-  if (priv->stderr_fd != stderr_fd)
-    {
-      if (priv->stderr_fd != -1)
-        close (priv->stderr_fd);
-      priv->stderr_fd = stderr_fd;
-    }
+  ide_unix_fd_map_take (priv->unix_fd_map, stderr_fd, STDERR_FILENO);
 }
 
 /**
@@ -983,7 +888,7 @@ ide_subprocess_launcher_take_stderr_fd (IdeSubprocessLauncher *self,
  */
 void
 ide_subprocess_launcher_set_argv (IdeSubprocessLauncher  *self,
-                                  const gchar * const    *args)
+                                  const char * const    *args)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -1000,20 +905,20 @@ ide_subprocess_launcher_set_argv (IdeSubprocessLauncher  *self,
   g_ptr_array_add (priv->argv, NULL);
 }
 
-const gchar * const *
+const char * const *
 ide_subprocess_launcher_get_argv (IdeSubprocessLauncher *self)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), NULL);
 
-  return (const gchar * const *)priv->argv->pdata;
+  return (const char * const *)priv->argv->pdata;
 }
 
 void
 ide_subprocess_launcher_insert_argv (IdeSubprocessLauncher *self,
                                      guint                  index,
-                                     const gchar           *arg)
+                                     const char           *arg)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -1028,10 +933,10 @@ ide_subprocess_launcher_insert_argv (IdeSubprocessLauncher *self,
 void
 ide_subprocess_launcher_replace_argv (IdeSubprocessLauncher *self,
                                       guint                  index,
-                                      const gchar           *arg)
+                                      const char           *arg)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
-  gchar *old_arg;
+  char *old_arg;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
   g_return_if_fail (priv->argv->len > 0);
@@ -1046,28 +951,21 @@ ide_subprocess_launcher_replace_argv (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_take_fd (IdeSubprocessLauncher *self,
-                                 gint                   source_fd,
-                                 gint                   dest_fd)
+                                 int                    source_fd,
+                                 int                    dest_fd)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
-  FdMapping map = {
-    .source_fd = source_fd,
-    .dest_fd = dest_fd
-  };
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
   g_return_if_fail (source_fd > -1);
   g_return_if_fail (dest_fd > -1);
 
-  if (priv->fd_mapping == NULL)
-    priv->fd_mapping = g_array_new (FALSE, FALSE, sizeof (FdMapping));
-
-  g_array_append_val (priv->fd_mapping, map);
+  ide_unix_fd_map_take (priv->unix_fd_map, source_fd, dest_fd);
 }
 
 void
 ide_subprocess_launcher_set_stdout_file_path (IdeSubprocessLauncher *self,
-                                              const gchar           *stdout_file_path)
+                                              const char           *stdout_file_path)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
@@ -1082,9 +980,9 @@ ide_subprocess_launcher_set_stdout_file_path (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_prepend_path (IdeSubprocessLauncher *self,
-                                      const gchar           *path)
+                                      const char           *path)
 {
-  const gchar *old_path;
+  const char *old_path;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
@@ -1095,7 +993,7 @@ ide_subprocess_launcher_prepend_path (IdeSubprocessLauncher *self,
 
   if (old_path != NULL)
     {
-      g_autofree gchar *new_path = g_strdup_printf ("%s:%s", path, old_path);
+      g_autofree char *new_path = g_strdup_printf ("%s:%s", path, old_path);
       ide_subprocess_launcher_setenv (self, "PATH", new_path, TRUE);
     }
   else
@@ -1106,9 +1004,9 @@ ide_subprocess_launcher_prepend_path (IdeSubprocessLauncher *self,
 
 void
 ide_subprocess_launcher_append_path (IdeSubprocessLauncher *self,
-                                     const gchar           *path)
+                                     const char           *path)
 {
-  const gchar *old_path;
+  const char *old_path;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
 
@@ -1119,7 +1017,7 @@ ide_subprocess_launcher_append_path (IdeSubprocessLauncher *self,
 
   if (old_path != NULL)
     {
-      g_autofree gchar *new_path = g_strdup_printf ("%s:%s", old_path, path);
+      g_autofree char *new_path = g_strdup_printf ("%s:%s", old_path, path);
       ide_subprocess_launcher_setenv (self, "PATH", new_path, TRUE);
     }
   else
@@ -1134,34 +1032,11 @@ ide_subprocess_launcher_get_needs_tty (IdeSubprocessLauncher *self)
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), FALSE);
+  g_return_val_if_fail (IDE_IS_UNIX_FD_MAP (priv->unix_fd_map), FALSE);
 
-  if ((priv->stdin_fd != -1 && isatty (priv->stdin_fd)) ||
-      (priv->stdout_fd != -1 && isatty (priv->stdout_fd)) ||
-      (priv->stderr_fd != -1 && isatty (priv->stderr_fd)))
-    return TRUE;
-
-  if (priv->fd_mapping != NULL)
-    {
-      for (guint i = 0; i < priv->fd_mapping->len; i++)
-        {
-          const FdMapping *fdmap = &g_array_index (priv->fd_mapping, FdMapping, i);
-
-          switch (fdmap->dest_fd)
-            {
-            case STDIN_FILENO:
-            case STDOUT_FILENO:
-            case STDERR_FILENO:
-              if (isatty (fdmap->source_fd))
-                return TRUE;
-              break;
-
-            default:
-              break;
-            }
-        }
-    }
-
-  return FALSE;
+  return ide_unix_fd_map_stdin_isatty (priv->unix_fd_map) ||
+         ide_unix_fd_map_stdout_isatty (priv->unix_fd_map) ||
+         ide_unix_fd_map_stderr_isatty (priv->unix_fd_map);
 }
 
 /**
@@ -1174,32 +1049,19 @@ ide_subprocess_launcher_get_needs_tty (IdeSubprocessLauncher *self)
  * This will always return a value >= 2 (to indicate stdin/stdout/stderr).
  *
  * Returns: an integer for the max-fd
- *
- * Since: 3.34
  */
-gint
+int
 ide_subprocess_launcher_get_max_fd (IdeSubprocessLauncher *self)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
-  gint max_fd = 2;
-
-  g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), 2);
 
-  if (priv->fd_mapping != NULL)
-    {
-      for (guint i = 0; i < priv->fd_mapping->len; i++)
-        {
-          const FdMapping *map = &g_array_index (priv->fd_mapping, FdMapping, i);
-
-          if (map->dest_fd > max_fd)
-            max_fd = map->dest_fd;
-        }
-    }
+  g_return_val_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self), -1);
+  g_return_val_if_fail (IDE_IS_UNIX_FD_MAP (priv->unix_fd_map), -1);
 
-  return max_fd;
+  return ide_unix_fd_map_get_max_dest_fd (priv->unix_fd_map);
 }
 
-const gchar *
+const char *
 ide_subprocess_launcher_get_arg (IdeSubprocessLauncher *self,
                                  guint                  pos)
 {
@@ -1218,7 +1080,7 @@ ide_subprocess_launcher_join_args_for_sh_c (IdeSubprocessLauncher *self,
                                             guint                  start_pos)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
-  const gchar * const *argv;
+  const char * const *argv;
   GString *str;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
@@ -1229,7 +1091,7 @@ ide_subprocess_launcher_join_args_for_sh_c (IdeSubprocessLauncher *self,
 
   for (guint i = start_pos; argv[i] != NULL; i++)
     {
-      g_autofree gchar *quoted_string = g_shell_quote (argv[i]);
+      g_autofree char *quoted_string = g_shell_quote (argv[i]);
 
       if (i > 0)
         g_string_append_c (str, ' ');
@@ -1240,3 +1102,53 @@ ide_subprocess_launcher_join_args_for_sh_c (IdeSubprocessLauncher *self,
   g_ptr_array_add (priv->argv, g_string_free (g_steal_pointer (&str), FALSE));
   g_ptr_array_add (priv->argv, NULL);
 }
+
+/**
+ * ide_subprocess_launcher_push_argv_format: (skip)
+ * @self: a #IdeSubprocessLauncher
+ * @format: a printf-style format string
+ *
+ * Convenience function which allows combining a g_strdup_printf() and
+ * call to ide_subprocess_launcher_push_argv() into one call.
+ *
+ * @format is used to build the argument string which is added using
+ * ide_subprocess_launcher_push_argv() and then freed.
+ */
+void
+ide_subprocess_launcher_push_argv_format (IdeSubprocessLauncher *self,
+                                          const char            *format,
+                                          ...)
+{
+  g_autofree char *arg = NULL;
+  va_list args;
+
+  g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
+  g_return_if_fail (format != NULL);
+
+  va_start (args, format);
+  arg = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  g_return_if_fail (arg != NULL);
+
+  ide_subprocess_launcher_push_argv (self, arg);
+}
+
+void
+ide_subprocess_launcher_push_argv_parsed (IdeSubprocessLauncher *self,
+                                          const char            *args_to_parse)
+{
+  g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
+
+  if (!ide_str_empty0 (args_to_parse))
+    {
+      g_autoptr(GError) error = NULL;
+      g_auto(GStrv) argv = NULL;
+      int argc;
+
+      if (!g_shell_parse_argv (args_to_parse, &argc, &argv, &error))
+        g_warning ("Failed to parse args: %s", error->message);
+      else
+        ide_subprocess_launcher_push_args (self, (const char * const *)argv);
+    }
+}
diff --git a/src/libide/threading/ide-subprocess-launcher.h b/src/libide/threading/ide-subprocess-launcher.h
index d674aaf1b..939fabc13 100644
--- a/src/libide/threading/ide-subprocess-launcher.h
+++ b/src/libide/threading/ide-subprocess-launcher.h
@@ -34,7 +34,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_SUBPROCESS_LAUNCHER (ide_subprocess_launcher_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_DERIVABLE_TYPE (IdeSubprocessLauncher, ide_subprocess_launcher, IDE, SUBPROCESS_LAUNCHER, GObject)
 
 struct _IdeSubprocessLauncherClass
@@ -49,99 +49,106 @@ struct _IdeSubprocessLauncherClass
   gpointer _reserved[8];
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeSubprocessLauncher *ide_subprocess_launcher_new                  (GSubprocessFlags        flags);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 const gchar           *ide_subprocess_launcher_get_cwd              (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_cwd              (IdeSubprocessLauncher  *self,
                                                                      const gchar            *cwd);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GSubprocessFlags       ide_subprocess_launcher_get_flags            (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_flags            (IdeSubprocessLauncher  *self,
                                                                      GSubprocessFlags        flags);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean               ide_subprocess_launcher_get_run_on_host      (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_run_on_host      (IdeSubprocessLauncher  *self,
                                                                      gboolean                run_on_host);
-IDE_AVAILABLE_IN_41
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_prepend_path         (IdeSubprocessLauncher  *self,
                                                                      const gchar            *prepend_path);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_append_path          (IdeSubprocessLauncher  *self,
                                                                      const gchar            *append_path);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean               ide_subprocess_launcher_get_clear_env        (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_clear_env        (IdeSubprocessLauncher  *self,
                                                                      gboolean                clear_env);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 const gchar * const   *ide_subprocess_launcher_get_environ          (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_environ          (IdeSubprocessLauncher  *self,
                                                                      const gchar * const    *environ_);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 const gchar           *ide_subprocess_launcher_getenv               (IdeSubprocessLauncher  *self,
                                                                      const gchar            *key);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_setenv               (IdeSubprocessLauncher  *self,
                                                                      const gchar            *key,
                                                                      const gchar            *value,
                                                                      gboolean                replace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_insert_argv          (IdeSubprocessLauncher  *self,
                                                                      guint                   index,
                                                                      const gchar            *arg);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_replace_argv         (IdeSubprocessLauncher  *self,
                                                                      guint                   index,
                                                                      const gchar            *arg);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_overlay_environment  (IdeSubprocessLauncher  *self,
                                                                      IdeEnvironment         *environment);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 const gchar * const   *ide_subprocess_launcher_get_argv             (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_34
+IDE_AVAILABLE_IN_ALL
 const gchar           *ide_subprocess_launcher_get_arg              (IdeSubprocessLauncher  *self,
                                                                      guint                   pos);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_push_args            (IdeSubprocessLauncher  *self,
                                                                      const gchar * const    *args);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_push_argv            (IdeSubprocessLauncher  *self,
                                                                      const gchar            *argv);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
+void                   ide_subprocess_launcher_push_argv_parsed     (IdeSubprocessLauncher  *self,
+                                                                     const char             *args_to_parse);
+IDE_AVAILABLE_IN_ALL
+void                   ide_subprocess_launcher_push_argv_format     (IdeSubprocessLauncher  *self,
+                                                                     const char             *format,
+                                                                     ...) G_GNUC_PRINTF (2, 3);
+IDE_AVAILABLE_IN_ALL
 gchar                 *ide_subprocess_launcher_pop_argv             (IdeSubprocessLauncher  *self) 
G_GNUC_WARN_UNUSED_RESULT;
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_argv             (IdeSubprocessLauncher  *self,
                                                                      const gchar * const    *args);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeSubprocess         *ide_subprocess_launcher_spawn                (IdeSubprocessLauncher  *self,
                                                                      GCancellable           *cancellable,
                                                                      GError                **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_set_stdout_file_path (IdeSubprocessLauncher  *self,
                                                                      const gchar            
*stdout_file_path);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_take_fd              (IdeSubprocessLauncher  *self,
                                                                      gint                    source_fd,
                                                                      gint                    dest_fd);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_take_stdin_fd        (IdeSubprocessLauncher  *self,
                                                                      gint                    stdin_fd);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_take_stdout_fd       (IdeSubprocessLauncher  *self,
                                                                      gint                    stdout_fd);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_take_stderr_fd       (IdeSubprocessLauncher  *self,
                                                                      gint                    stderr_fd);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean               ide_subprocess_launcher_get_needs_tty        (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_34
+IDE_AVAILABLE_IN_ALL
 gint                   ide_subprocess_launcher_get_max_fd           (IdeSubprocessLauncher  *self);
-IDE_AVAILABLE_IN_3_34
+IDE_AVAILABLE_IN_ALL
 void                   ide_subprocess_launcher_join_args_for_sh_c   (IdeSubprocessLauncher  *self,
                                                                      guint                   start_pos);
 
diff --git a/src/libide/threading/ide-subprocess-supervisor.c 
b/src/libide/threading/ide-subprocess-supervisor.c
index 30baf6a93..e664db653 100644
--- a/src/libide/threading/ide-subprocess-supervisor.c
+++ b/src/libide/threading/ide-subprocess-supervisor.c
@@ -190,8 +190,6 @@ ide_subprocess_supervisor_new (void)
  * ide_subprocess_supervisor_get_launcher:
  *
  * Returns: (nullable) (transfer none): An #IdeSubprocessLauncher or %NULL.
- *
- * Since: 3.32
  */
 IdeSubprocessLauncher *
 ide_subprocess_supervisor_get_launcher (IdeSubprocessSupervisor *self)
@@ -321,8 +319,6 @@ ide_subprocess_supervisor_stop (IdeSubprocessSupervisor *self)
  * called or if there was a failure to spawn the process.
  *
  * Returns: (nullable) (transfer none): An #IdeSubprocess or %NULL.
- *
- * Since: 3.32
  */
 IdeSubprocess *
 ide_subprocess_supervisor_get_subprocess (IdeSubprocessSupervisor *self)
diff --git a/src/libide/threading/ide-subprocess-supervisor.h 
b/src/libide/threading/ide-subprocess-supervisor.h
index 219c7de33..c550879b3 100644
--- a/src/libide/threading/ide-subprocess-supervisor.h
+++ b/src/libide/threading/ide-subprocess-supervisor.h
@@ -33,7 +33,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_SUBPROCESS_SUPERVISOR (ide_subprocess_supervisor_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_DERIVABLE_TYPE (IdeSubprocessSupervisor, ide_subprocess_supervisor, IDE, SUBPROCESS_SUPERVISOR, 
GObject)
 
 struct _IdeSubprocessSupervisorClass
@@ -47,20 +47,20 @@ struct _IdeSubprocessSupervisorClass
   gpointer _reserved[8];
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeSubprocessSupervisor *ide_subprocess_supervisor_new            (void);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeSubprocessLauncher   *ide_subprocess_supervisor_get_launcher   (IdeSubprocessSupervisor *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                     ide_subprocess_supervisor_set_launcher   (IdeSubprocessSupervisor *self,
                                                                    IdeSubprocessLauncher   *launcher);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                     ide_subprocess_supervisor_start          (IdeSubprocessSupervisor *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                     ide_subprocess_supervisor_stop           (IdeSubprocessSupervisor *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeSubprocess           *ide_subprocess_supervisor_get_subprocess (IdeSubprocessSupervisor *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                     ide_subprocess_supervisor_set_subprocess (IdeSubprocessSupervisor *self,
                                                                    IdeSubprocess           *subprocess);
 
diff --git a/src/libide/threading/ide-subprocess.c b/src/libide/threading/ide-subprocess.c
index 8daaa0363..37e5a80d9 100644
--- a/src/libide/threading/ide-subprocess.c
+++ b/src/libide/threading/ide-subprocess.c
@@ -51,8 +51,6 @@ ide_subprocess_get_identifier (IdeSubprocess *self)
  * ide_subprocess_get_stdout_pipe:
  *
  * Returns: (transfer none): a #GInputStream or %NULL.
- *
- * Since: 3.32
  */
 GInputStream *
 ide_subprocess_get_stdout_pipe (IdeSubprocess *self)
@@ -66,8 +64,6 @@ ide_subprocess_get_stdout_pipe (IdeSubprocess *self)
  * ide_subprocess_get_stderr_pipe:
  *
  * Returns: (transfer none): a #GInputStream or %NULL.
- *
- * Since: 3.32
  */
 GInputStream *
 ide_subprocess_get_stderr_pipe (IdeSubprocess *self)
@@ -81,8 +77,6 @@ ide_subprocess_get_stderr_pipe (IdeSubprocess *self)
  * ide_subprocess_get_stdin_pipe:
  *
  * Returns: (transfer none): a #GOutputStream or %NULL.
- *
- * Since: 3.32
  */
 GOutputStream *
 ide_subprocess_get_stdin_pipe (IdeSubprocess *self)
@@ -306,8 +300,6 @@ ide_subprocess_communicate (IdeSubprocess  *self,
  * This process acts identical to g_subprocess_communicate_utf8().
  *
  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_subprocess_communicate_utf8 (IdeSubprocess  *self,
@@ -339,8 +331,6 @@ ide_subprocess_communicate_utf8 (IdeSubprocess  *self,
  *
  * Ensure you've set the proper flags to ensure that you can write to stdin
  * or read from stderr/stdout as necessary.
- *
- * Since: 3.32
  */
 void
 ide_subprocess_communicate_async (IdeSubprocess       *self,
@@ -366,8 +356,6 @@ ide_subprocess_communicate_async (IdeSubprocess       *self,
  * Finishes a request to ide_subprocess_communicate_async().
  *
  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_subprocess_communicate_finish (IdeSubprocess  *self,
@@ -387,7 +375,6 @@ ide_subprocess_communicate_finish (IdeSubprocess  *self,
  * @stdin_buf: (nullable): The data to send to stdin or %NULL
  *
  *
- * Since: 3.32
  */
 void
 ide_subprocess_communicate_utf8_async (IdeSubprocess       *self,
@@ -411,8 +398,6 @@ ide_subprocess_communicate_utf8_async (IdeSubprocess       *self,
  * @error: A location for a #GError, or %NULL
  *
  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_subprocess_communicate_utf8_finish (IdeSubprocess  *self,
diff --git a/src/libide/threading/ide-subprocess.h b/src/libide/threading/ide-subprocess.h
index 583592a88..9c20ea768 100644
--- a/src/libide/threading/ide-subprocess.h
+++ b/src/libide/threading/ide-subprocess.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_SUBPROCESS (ide_subprocess_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_INTERFACE (IdeSubprocess, ide_subprocess, IDE, SUBPROCESS, GObject)
 
 struct _IdeSubprocessInterface
@@ -95,93 +95,93 @@ struct _IdeSubprocessInterface
                                              GError              **error);
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 const gchar   *ide_subprocess_get_identifier          (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GInputStream  *ide_subprocess_get_stdout_pipe         (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GInputStream  *ide_subprocess_get_stderr_pipe         (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GOutputStream *ide_subprocess_get_stdin_pipe          (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_wait                    (IdeSubprocess        *self,
                                                        GCancellable         *cancellable,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_wait_check              (IdeSubprocess        *self,
                                                        GCancellable         *cancellable,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_wait_async              (IdeSubprocess        *self,
                                                        GCancellable         *cancellable,
                                                        GAsyncReadyCallback   callback,
                                                        gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_wait_finish             (IdeSubprocess        *self,
                                                        GAsyncResult         *result,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_wait_check_async        (IdeSubprocess        *self,
                                                        GCancellable         *cancellable,
                                                        GAsyncReadyCallback   callback,
                                                        gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_wait_check_finish       (IdeSubprocess        *self,
                                                        GAsyncResult         *result,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_check_exit_status       (IdeSubprocess        *self,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_get_successful          (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_get_if_exited           (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gint           ide_subprocess_get_exit_status         (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_get_if_signaled         (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gint           ide_subprocess_get_term_sig            (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gint           ide_subprocess_get_status              (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_send_signal             (IdeSubprocess        *self,
                                                        gint                  signal_num);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_force_exit              (IdeSubprocess        *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_communicate             (IdeSubprocess        *self,
                                                        GBytes               *stdin_buf,
                                                        GCancellable         *cancellable,
                                                        GBytes              **stdout_buf,
                                                        GBytes              **stderr_buf,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_communicate_utf8        (IdeSubprocess        *self,
                                                        const gchar          *stdin_buf,
                                                        GCancellable         *cancellable,
                                                        gchar               **stdout_buf,
                                                        gchar               **stderr_buf,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_communicate_async       (IdeSubprocess        *self,
                                                        GBytes               *stdin_buf,
                                                        GCancellable         *cancellable,
                                                        GAsyncReadyCallback   callback,
                                                        gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_communicate_finish      (IdeSubprocess        *self,
                                                        GAsyncResult         *result,
                                                        GBytes              **stdout_buf,
                                                        GBytes              **stderr_buf,
                                                        GError              **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_subprocess_communicate_utf8_async  (IdeSubprocess        *self,
                                                        const gchar          *stdin_buf,
                                                        GCancellable         *cancellable,
                                                        GAsyncReadyCallback   callback,
                                                        gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_subprocess_communicate_utf8_finish (IdeSubprocess        *self,
                                                        GAsyncResult         *result,
                                                        gchar               **stdout_buf,


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