[gnome-builder/wip/chergert/pipeline] wip: start on build pipeline/stages



commit 92172e17a309e2cf562c5d075f960fac4186b655
Author: Christian Hergert <chergert redhat com>
Date:   Wed Dec 14 16:09:36 2016 -0800

    wip: start on build pipeline/stages

 libide/Makefile.am                            |    9 +
 libide/buildsystem/ide-build-pipeline-addin.c |   50 ++
 libide/buildsystem/ide-build-pipeline-addin.h |   49 ++
 libide/buildsystem/ide-build-pipeline.c       |  656 +++++++++++++++++++++++++
 libide/buildsystem/ide-build-pipeline.h       |   86 ++++
 libide/buildsystem/ide-build-stage-launcher.c |  223 +++++++++
 libide/buildsystem/ide-build-stage-launcher.h |   41 ++
 libide/buildsystem/ide-build-stage.c          |  214 ++++++++
 libide/buildsystem/ide-build-stage.h          |   76 +++
 libide/ide-enums.c.in                         |    1 +
 libide/ide-types.h                            |    4 +-
 libide/ide.h                                  |    4 +
 12 files changed, 1412 insertions(+), 1 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 987d890..8a001b2 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -33,6 +33,10 @@ libide_1_0_la_public_headers =                            \
        buildsystem/ide-build-command.h                   \
        buildsystem/ide-build-command-queue.h             \
        buildsystem/ide-build-manager.h                   \
+       buildsystem/ide-build-pipeline.h                  \
+       buildsystem/ide-build-pipeline-addin.h            \
+       buildsystem/ide-build-stage.h                     \
+       buildsystem/ide-build-stage-launcher.h            \
        buildsystem/ide-build-result-addin.h              \
        buildsystem/ide-build-result.h                    \
        buildsystem/ide-build-system.h                    \
@@ -208,6 +212,10 @@ libide_1_0_la_public_sources =                            \
        buildsystem/ide-build-command.c                   \
        buildsystem/ide-build-command-queue.c             \
        buildsystem/ide-build-manager.c                   \
+       buildsystem/ide-build-pipeline.c                  \
+       buildsystem/ide-build-pipeline-addin.c            \
+       buildsystem/ide-build-stage.c                     \
+       buildsystem/ide-build-stage-launcher.c            \
        buildsystem/ide-build-result-addin.c              \
        buildsystem/ide-build-result.c                    \
        buildsystem/ide-build-system.c                    \
@@ -612,6 +620,7 @@ endif
 
 glib_enum_headers =                        \
        buffers/ide-buffer.h               \
+       buildsystem/ide-build-pipeline.h   \
        buildsystem/ide-build-result.h     \
        devices/ide-device.h               \
        diagnostics/ide-diagnostic.h       \
diff --git a/libide/buildsystem/ide-build-pipeline-addin.c b/libide/buildsystem/ide-build-pipeline-addin.c
new file mode 100644
index 0000000..550e626
--- /dev/null
+++ b/libide/buildsystem/ide-build-pipeline-addin.c
@@ -0,0 +1,50 @@
+/* ide-build-pipeline-addin.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-pipeline-addin"
+
+#include "buildsystem/ide-build-pipeline-addin.h"
+
+G_DEFINE_INTERFACE (IdeBuildPipelineAddin, ide_build_pipeline_addin, G_TYPE_OBJECT)
+
+static void
+ide_build_pipeline_addin_default_init (IdeBuildPipelineAddinInterface *iface)
+{
+}
+
+void
+ide_build_pipeline_addin_load (IdeBuildPipelineAddin *self,
+                               IdeBuildPipeline      *pipeline)
+{
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE_ADDIN (self));
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  if (IDE_BUILD_PIPELINE_ADDIN_GET_IFACE (self)->load)
+    IDE_BUILD_PIPELINE_ADDIN_GET_IFACE (self)->load (self, pipeline);
+}
+
+void
+ide_build_pipeline_addin_unload (IdeBuildPipelineAddin *self,
+                                 IdeBuildPipeline      *pipeline)
+{
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE_ADDIN (self));
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  if (IDE_BUILD_PIPELINE_ADDIN_GET_IFACE (self)->unload)
+    IDE_BUILD_PIPELINE_ADDIN_GET_IFACE (self)->unload (self, pipeline);
+}
diff --git a/libide/buildsystem/ide-build-pipeline-addin.h b/libide/buildsystem/ide-build-pipeline-addin.h
new file mode 100644
index 0000000..3b8f3df
--- /dev/null
+++ b/libide/buildsystem/ide-build-pipeline-addin.h
@@ -0,0 +1,49 @@
+/* ide-build-pipeline-addin.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_PIPELINE_ADDIN_H
+#define IDE_BUILD_PIPELINE_ADDIN_H
+
+#include <gio/gio.h>
+
+#include "ide-build-pipeline.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_PIPELINE_ADDIN (ide_build_pipeline_addin_get_type())
+
+G_DECLARE_INTERFACE (IdeBuildPipelineAddin, ide_build_pipeline_addin, IDE, BUILD_PIPELINE_ADDIN, GObject)
+
+struct _IdeBuildPipelineAddinInterface
+{
+  GTypeInterface type_interface;
+
+  void (*load)   (IdeBuildPipelineAddin *self,
+                  IdeBuildPipeline      *pipeline);
+  void (*unload) (IdeBuildPipelineAddin *self,
+                  IdeBuildPipeline      *pipeline);
+};
+
+void ide_build_pipeline_addin_load   (IdeBuildPipelineAddin *self,
+                                      IdeBuildPipeline      *pipeline);
+void ide_build_pipeline_addin_unload (IdeBuildPipelineAddin *self,
+                                      IdeBuildPipeline      *pipeline);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_PIPELINE_ADDIN_H */
diff --git a/libide/buildsystem/ide-build-pipeline.c b/libide/buildsystem/ide-build-pipeline.c
new file mode 100644
index 0000000..d815b5d
--- /dev/null
+++ b/libide/buildsystem/ide-build-pipeline.c
@@ -0,0 +1,656 @@
+/* ide-build-pipeline.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-pipeline"
+
+#include <libpeas/peas.h>
+
+#include "ide-debug.h"
+#include "ide-enums.h"
+
+#include "buildsystem/ide-build-pipeline.h"
+#include "buildsystem/ide-build-pipeline-addin.h"
+#include "buildsystem/ide-build-stage-launcher.h"
+
+typedef struct
+{
+  IdeBuildPhase  phase;
+  gint           priority;
+  IdeBuildStage *stage;
+} PipelineEntry;
+
+struct _IdeBuildPipeline
+{
+  IdeObject         parent_instance;
+  PeasExtensionSet *addins;
+  IdeConfiguration *configuration;
+  gchar            *builddir;
+  gchar            *srcdir;
+  GArray           *pipeline;
+  gint              position;
+  IdeBuildPhase     requested_mask;
+  guint             failed : 1;
+};
+
+static void ide_build_pipeline_tick (IdeBuildPipeline *self,
+                                     GTask            *task);
+
+G_DEFINE_TYPE (IdeBuildPipeline, ide_build_pipeline, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_CONFIGURATION,
+  N_PROPS
+};
+
+enum {
+  FINISHED,
+  N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+IdeBuildPhase
+ide_build_pipeline_get_phase (IdeBuildPipeline *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), 0);
+
+  if (self->position < 0)
+    return IDE_BUILD_PHASE_NONE;
+  else if (self->position < self->pipeline->len)
+    return g_array_index (self->pipeline, PipelineEntry, self->position).phase;
+  else if (self->failed)
+    return IDE_BUILD_PHASE_FAILED;
+  else
+    return IDE_BUILD_PHASE_FINISHED;
+}
+
+/**
+ * ide_build_pipeline_get_configuration:
+ *
+ * Gets the #IdeConfiguration to use for the pipeline.
+ *
+ * Returns: (transfer none): An #IdeConfiguration
+ */
+IdeConfiguration *
+ide_build_pipeline_get_configuration (IdeBuildPipeline *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+
+  return self->configuration;
+}
+
+static void
+clear_pipeline_entry (gpointer data)
+{
+  PipelineEntry *entry = data;
+
+  g_clear_object (&entry->stage);
+}
+
+static gint
+pipeline_entry_compare (gconstpointer a,
+                        gconstpointer b)
+{
+  const PipelineEntry *entry_a = a;
+  const PipelineEntry *entry_b = b;
+  gint ret;
+
+  ret = (gint)(entry_a->phase & IDE_BUILD_PHASE_MASK)
+      - (gint)(entry_b->phase & IDE_BUILD_PHASE_MASK);
+
+  if (ret == 0)
+    {
+      gint whence_a = (entry_a->phase & IDE_BUILD_PHASE_WHENCE_MASK);
+      gint whence_b = (entry_b->phase & IDE_BUILD_PHASE_WHENCE_MASK);
+
+      if (whence_a != whence_b)
+        {
+          if (whence_a == IDE_BUILD_PHASE_BEFORE)
+            return -1;
+
+          if (whence_b == IDE_BUILD_PHASE_BEFORE)
+            return 1;
+
+          if (whence_a == 0)
+            return -1;
+
+          if (whence_b == 0)
+            return 1;
+
+          g_assert_not_reached ();
+        }
+    }
+
+  if (ret == 0)
+    ret = entry_a->priority - entry_b->priority;
+
+  return ret;
+}
+
+static void
+ide_build_pipeline_set_configuration (IdeBuildPipeline *self,
+                                      IdeConfiguration *configuration)
+{
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
+  g_return_if_fail (IDE_IS_CONFIGURATION (configuration));
+
+  g_set_object (&self->configuration, configuration);
+}
+
+static void
+ide_build_pipeline_real_finished (IdeBuildPipeline *self,
+                                  gboolean          failed)
+{
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+
+  /*
+   * Now that the build is finished, we can aggressively drop our pipeline
+   * stages to help ensure that all references are dropped as soon as
+   * possible.
+   */
+
+  g_clear_object (&self->addins);
+}
+
+static void
+ide_build_pipeline_extension_added (PeasExtensionSet *set,
+                                    PeasPluginInfo   *plugin_info,
+                                    PeasExtension    *exten,
+                                    gpointer          user_data)
+{
+  IdeBuildPipeline *self = user_data;
+  IdeBuildPipelineAddin *addin = (IdeBuildPipelineAddin *)exten;
+
+  IDE_ENTRY;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_BUILD_PIPELINE_ADDIN (addin));
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+
+  ide_build_pipeline_addin_load (addin, self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_build_pipeline_extension_removed (PeasExtensionSet *set,
+                                      PeasPluginInfo   *plugin_info,
+                                      PeasExtension    *exten,
+                                      gpointer          user_data)
+{
+  IdeBuildPipeline *self = user_data;
+  IdeBuildPipelineAddin *addin = (IdeBuildPipelineAddin *)exten;
+
+  IDE_ENTRY;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_BUILD_PIPELINE_ADDIN (addin));
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+
+  ide_build_pipeline_addin_unload (addin, self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_build_pipeline_finalize (GObject *object)
+{
+  IdeBuildPipeline *self = (IdeBuildPipeline *)object;
+
+  g_clear_object (&self->configuration);
+  g_clear_pointer (&self->pipeline, g_array_unref);
+  g_clear_pointer (&self->srcdir, g_free);
+  g_clear_pointer (&self->builddir, g_free);
+
+  G_OBJECT_CLASS (ide_build_pipeline_parent_class)->finalize (object);
+}
+
+static void
+ide_build_pipeline_dispose (GObject *object)
+{
+  IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
+
+  g_clear_object (&self->addins);
+
+  G_OBJECT_CLASS (ide_build_pipeline_parent_class)->dispose (object);
+}
+
+static void
+ide_build_pipeline_constructed (GObject *object)
+{
+  IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
+  IdeContext *context;
+
+  G_OBJECT_CLASS (ide_build_pipeline_parent_class)->constructed (object);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  self->addins = peas_extension_set_new (peas_engine_get_default (),
+                                         IDE_TYPE_BUILD_PIPELINE_ADDIN,
+                                         "context", context,
+                                         NULL);
+
+  g_signal_connect (self->addins,
+                    "extension-added",
+                    G_CALLBACK (ide_build_pipeline_extension_added),
+                    self);
+
+  g_signal_connect (self->addins,
+                    "extension-removed",
+                    G_CALLBACK (ide_build_pipeline_extension_removed),
+                    self);
+
+  peas_extension_set_foreach (self->addins,
+                              ide_build_pipeline_extension_added,
+                              self);
+}
+
+static void
+ide_build_pipeline_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      g_value_set_object (value, ide_build_pipeline_get_configuration (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_pipeline_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      ide_build_pipeline_set_configuration (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_pipeline_class_init (IdeBuildPipelineClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ide_build_pipeline_constructed;
+  object_class->dispose = ide_build_pipeline_dispose;
+  object_class->finalize = ide_build_pipeline_finalize;
+  object_class->get_property = ide_build_pipeline_get_property;
+  object_class->set_property = ide_build_pipeline_set_property;
+
+  /**
+   * IdeBuildPipeline:configuration:
+   *
+   * The configuration to use for the build pipeline.
+   */
+  properties [PROP_CONFIGURATION] =
+    g_param_spec_object ("configuration",
+                         "Configuration",
+                         "Configuration",
+                         IDE_TYPE_CONFIGURATION,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  /**
+   * IdeBuildPipeline::finished:
+   * @self: An #IdeBuildPipeline
+   * @failed: If the build was a failure
+   *
+   * This signal is emitted when the build process has finished executing.
+   * If the build failed to complete all requested stages, then @failed will
+   * be set to %TRUE, otherwise %FALSE.
+   */
+  signals [FINISHED] =
+    g_signal_new_class_handler ("finished",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_LAST,
+                                G_CALLBACK (ide_build_pipeline_real_finished),
+                                NULL, NULL, NULL,
+                                G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+ide_build_pipeline_init (IdeBuildPipeline *self)
+{
+  self->position = -1;
+  self->pipeline = g_array_new (FALSE, FALSE, sizeof (PipelineEntry));
+  g_array_set_clear_func (self->pipeline, clear_pipeline_entry);
+}
+
+static void
+ide_build_pipeline_stage_execute_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  IdeBuildStage *stage = (IdeBuildStage *)object;
+  IdeBuildPipeline *self;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_BUILD_STAGE (stage));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  self = g_task_get_source_object (task);
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+
+  if (!ide_build_stage_execute_finish (stage, result, &error))
+    {
+      self->failed = TRUE;
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  ide_build_pipeline_tick (self, task);
+}
+
+static void
+ide_build_pipeline_tick (IdeBuildPipeline *self,
+                         GTask            *task)
+{
+  GCancellable *cancellable;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+  g_assert (G_IS_TASK (task));
+
+  cancellable = g_task_get_cancellable (task);
+
+  for (self->position++; self->position < self->pipeline->len; self->position++)
+    {
+      const PipelineEntry *entry = &g_array_index (self->pipeline, PipelineEntry, self->position);
+
+      g_assert (entry->stage != NULL);
+      g_assert (IDE_IS_BUILD_STAGE (entry->stage));
+
+      if (entry->phase & self->requested_mask)
+        {
+          ide_build_stage_execute_async (entry->stage,
+                                         cancellable,
+                                         ide_build_pipeline_stage_execute_cb,
+                                         g_object_ref (task));
+          IDE_EXIT;
+        }
+    }
+
+  g_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_build_pipeline_execute_async:
+ *
+ * Asynchronously starts the build pipeline.
+ */
+void
+ide_build_pipeline_execute_async (IdeBuildPipeline    *self,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_build_pipeline_execute_async);
+
+  ide_build_pipeline_tick (self, task);
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_build_pipeline_execute_finish:
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+ */
+gboolean
+ide_build_pipeline_execute_finish (IdeBuildPipeline  *self,
+                                   GAsyncResult      *result,
+                                   GError           **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+void
+ide_build_pipeline_insert_stage (IdeBuildPipeline *self,
+                                 IdeBuildPhase     phase,
+                                 gint              priority,
+                                 IdeBuildStage    *stage)
+{
+  GFlagsClass *klass;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
+  g_return_if_fail (IDE_IS_BUILD_STAGE (stage));
+  g_return_if_fail ((phase & IDE_BUILD_PHASE_MASK) != IDE_BUILD_PHASE_NONE);
+  g_return_if_fail ((phase & IDE_BUILD_PHASE_WHENCE_MASK) == 0 ||
+                    (phase & IDE_BUILD_PHASE_WHENCE_MASK) == IDE_BUILD_PHASE_BEFORE ||
+                    (phase & IDE_BUILD_PHASE_WHENCE_MASK) == IDE_BUILD_PHASE_AFTER);
+
+  if G_UNLIKELY (self->position != -1)
+    {
+      g_warning ("Cannot insert stage into pipeline after execution, ignoring");
+      return;
+    }
+
+  klass = g_type_class_ref (IDE_TYPE_BUILD_PHASE);
+
+  for (guint i = 0; i < klass->n_values; i++)
+    {
+      const GFlagsValue *value = &klass->values[i];
+
+      if ((phase & IDE_BUILD_PHASE_MASK) == value->value)
+        {
+          PipelineEntry entry = { 0 };
+
+          IDE_TRACE_MSG ("Adding stage to pipeline with phase %s and priority %d",
+                         value->value_nick, priority);
+
+          entry.phase = phase;
+          entry.priority = priority;
+          entry.stage = g_object_ref (stage);
+
+          g_array_append_val (self->pipeline, entry);
+          g_array_sort (self->pipeline, pipeline_entry_compare);
+
+          IDE_GOTO (cleanup);
+        }
+    }
+
+  g_warning ("No such pipeline phase %02x", phase);
+
+cleanup:
+  g_type_class_unref (klass);
+
+  IDE_EXIT;
+}
+
+void
+ide_build_pipeline_insert_stage_from_launcher (IdeBuildPipeline      *self,
+                                               IdeBuildPhase          phase,
+                                               gint                   priority,
+                                               IdeSubprocessLauncher *launcher)
+{
+  g_autoptr(IdeBuildStage) stage = NULL;
+  IdeContext *context;
+
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
+  g_return_if_fail ((phase & IDE_BUILD_PHASE_MASK) != IDE_BUILD_PHASE_NONE);
+  g_return_if_fail ((phase & IDE_BUILD_PHASE_WHENCE_MASK) == 0 ||
+                    (phase & IDE_BUILD_PHASE_WHENCE_MASK) == IDE_BUILD_PHASE_BEFORE ||
+                    (phase & IDE_BUILD_PHASE_WHENCE_MASK) == IDE_BUILD_PHASE_AFTER);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  stage = ide_build_stage_launcher_new (context, launcher);
+  ide_build_pipeline_insert_stage (self, phase, priority, stage);
+}
+
+void
+ide_build_pipeline_request_phase (IdeBuildPipeline *self,
+                                  IdeBuildPhase     phase)
+{
+  GFlagsClass *klass;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
+  g_return_if_fail ((phase & IDE_BUILD_PHASE_MASK) != IDE_BUILD_PHASE_NONE);
+
+  phase &= IDE_BUILD_PHASE_MASK;
+
+  if (self->position != -1)
+    {
+      g_warning ("Cannot request phase after execution has started");
+      IDE_EXIT;
+    }
+
+  klass = g_type_class_ref (IDE_TYPE_BUILD_PHASE);
+
+  for (guint i = 0; i < klass->n_values; i++)
+    {
+      const GFlagsValue *value = &klass->values[i];
+
+      if (phase == value->value)
+        {
+          IDE_TRACE_MSG ("requesting pipeline phase %s", value->value_nick);
+          /*
+           * Each flag is a power of two, so we can simply subtract one
+           * to get a mask of all the previous phases.
+           */
+          self->requested_mask |= phase | (phase - 1);
+          IDE_GOTO (cleanup);
+        }
+    }
+
+  g_warning ("No such phase %02x", (guint)phase);
+
+cleanup:
+  g_type_class_unref (klass);
+
+  IDE_EXIT;
+}
+
+const gchar *
+ide_build_pipeline_get_builddir (IdeBuildPipeline *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+
+  return self->builddir;
+}
+
+const gchar *
+ide_build_pipeline_get_srcdir (IdeBuildPipeline *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+
+  return self->srcdir;
+}
+
+static gchar *
+ide_build_pipeline_build_path_va_list (const gchar *prefix,
+                                       const gchar *first_part,
+                                       va_list      args)
+{
+  g_autoptr(GPtrArray) ar = NULL;
+
+  g_assert (prefix != NULL);
+  g_assert (first_part != NULL);
+
+  ar = g_ptr_array_new ();
+  g_ptr_array_add (ar, (gchar *)prefix);
+  do
+    g_ptr_array_add (ar, (gchar *)first_part);
+  while (NULL != (first_part = va_arg (args, const gchar *)));
+  g_ptr_array_add (ar, NULL);
+
+  return g_build_filenamev ((gchar **)ar->pdata);
+}
+
+gchar *
+ide_build_pipeline_build_srcdir_path (IdeBuildPipeline *self,
+                                      const gchar      *first_part,
+                                      ...)
+{
+  gchar *ret;
+  va_list args;
+
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+  g_return_val_if_fail (first_part != NULL, NULL);
+
+  va_start (args, first_part);
+  ret = ide_build_pipeline_build_path_va_list (self->srcdir, first_part, args);
+  va_end (args);
+
+  return ret;
+}
+
+gchar *
+ide_build_pipeline_build_builddir_path (IdeBuildPipeline *self,
+                                        const gchar      *first_part,
+                                        ...)
+{
+  gchar *ret;
+  va_list args;
+
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+  g_return_val_if_fail (first_part != NULL, NULL);
+
+  va_start (args, first_part);
+  ret = ide_build_pipeline_build_path_va_list (self->builddir, first_part, args);
+  va_end (args);
+
+  return ret;
+}
diff --git a/libide/buildsystem/ide-build-pipeline.h b/libide/buildsystem/ide-build-pipeline.h
new file mode 100644
index 0000000..673c9f1
--- /dev/null
+++ b/libide/buildsystem/ide-build-pipeline.h
@@ -0,0 +1,86 @@
+/* ide-build-pipeline.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_PIPELINE_H
+#define IDE_BUILD_PIPELINE_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+
+#include "buildsystem/ide-build-stage.h"
+#include "buildsystem/ide-configuration.h"
+#include "subprocess/ide-subprocess-launcher.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_PIPELINE     (ide_build_pipeline_get_type())
+#define IDE_BUILD_PHASE_MASK        (0xFFFFFF)
+#define IDE_BUILD_PHASE_WHENCE_MASK (IDE_BUILD_PHASE_BEFORE | IDE_BUILD_PHASE_AFTER)
+
+typedef enum
+{
+  IDE_BUILD_PHASE_NONE         = 0,
+  IDE_BUILD_PHASE_PREPARE      = 1 << 0,
+  IDE_BUILD_PHASE_DOWNLOADS    = 1 << 1,
+  IDE_BUILD_PHASE_DEPENDENCIES = 1 << 2,
+  IDE_BUILD_PHASE_AUTOGEN      = 1 << 3,
+  IDE_BUILD_PHASE_CONFIGURE    = 1 << 4,
+  IDE_BUILD_PHASE_PREBUILD     = 1 << 5,
+  IDE_BUILD_PHASE_BUILD        = 1 << 6,
+  IDE_BUILD_PHASE_POSTBUILD    = 1 << 7,
+  IDE_BUILD_PHASE_INSTALL      = 1 << 9,
+  IDE_BUILD_PHASE_EXPORT       = 1 << 12,
+  IDE_BUILD_PHASE_BEFORE       = 1 << 28,
+  IDE_BUILD_PHASE_AFTER        = 1 << 29,
+  IDE_BUILD_PHASE_FINISHED     = 1 << 30,
+  IDE_BUILD_PHASE_FAILED       = 1 << 31,
+} IdeBuildPhase;
+
+G_DECLARE_FINAL_TYPE (IdeBuildPipeline, ide_build_pipeline, IDE, BUILD_PIPELINE, IdeObject)
+
+IdeConfiguration *ide_build_pipeline_get_configuration          (IdeBuildPipeline       *self);
+const gchar      *ide_build_pipeline_get_builddir               (IdeBuildPipeline       *self);
+const gchar      *ide_build_pipeline_get_srcdir                 (IdeBuildPipeline       *self);
+gchar            *ide_build_pipeline_build_srcdir_path          (IdeBuildPipeline       *self,
+                                                                 const gchar            *first_part,
+                                                                 ...) G_GNUC_NULL_TERMINATED;
+gchar            *ide_build_pipeline_build_builddir_path        (IdeBuildPipeline       *self,
+                                                                 const gchar            *first_part,
+                                                                 ...) G_GNUC_NULL_TERMINATED;
+void              ide_build_pipeline_request_phase              (IdeBuildPipeline       *self,
+                                                                 IdeBuildPhase           phase);
+void              ide_build_pipeline_insert_stage               (IdeBuildPipeline       *self,
+                                                                 IdeBuildPhase           phase,
+                                                                 gint                    priority,
+                                                                 IdeBuildStage          *stage);
+void              ide_build_pipeline_insert_stage_from_launcher (IdeBuildPipeline       *self,
+                                                                 IdeBuildPhase           phase,
+                                                                 gint                    priority,
+                                                                 IdeSubprocessLauncher  *launcher);
+void              ide_build_pipeline_execute_async              (IdeBuildPipeline       *self,
+                                                                 GCancellable           *cancellable,
+                                                                 GAsyncReadyCallback     callback,
+                                                                 gpointer                user_data);
+gboolean          ide_build_pipeline_execute_finish             (IdeBuildPipeline       *self,
+                                                                 GAsyncResult           *result,
+                                                                 GError                **error);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_PIPELINE_H */
diff --git a/libide/buildsystem/ide-build-stage-launcher.c b/libide/buildsystem/ide-build-stage-launcher.c
new file mode 100644
index 0000000..e44d2b0
--- /dev/null
+++ b/libide/buildsystem/ide-build-stage-launcher.c
@@ -0,0 +1,223 @@
+/* ide-build-stage-launcher.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-stage-launcher"
+
+#include "ide-debug.h"
+
+#include "buildsystem/ide-build-stage-launcher.h"
+#include "subprocess/ide-subprocess.h"
+
+struct _IdeBuildStageLauncher
+{
+  IdeBuildStage          parent_instance;
+  IdeSubprocessLauncher *launcher;
+};
+
+enum {
+  PROP_0,
+  PROP_LAUNCHER,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (IdeBuildStageLauncher, ide_build_stage_launcher, IDE_TYPE_BUILD_STAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_build_stage_launcher_wait_check_cb (GObject      *object,
+                                        GAsyncResult *result,
+                                        gpointer      user_data)
+{
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
+}
+
+static void
+ide_build_stage_launcher_execute_async (IdeBuildStage       *stage,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  IdeBuildStageLauncher *self = (IdeBuildStageLauncher *)stage;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_STAGE_LAUNCHER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_build_stage_launcher_execute_async);
+
+  if (self->launcher == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVAL,
+                               "Improperly configured %s",
+                               G_OBJECT_TYPE_NAME (self));
+      IDE_EXIT;
+    }
+
+  subprocess = ide_subprocess_launcher_spawn (self->launcher, cancellable, &error);
+
+  if (subprocess == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  ide_subprocess_wait_check_async (subprocess,
+                                   cancellable,
+                                   ide_build_stage_launcher_wait_check_cb,
+                                   g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static gboolean
+ide_build_stage_launcher_execute_finish (IdeBuildStage  *stage,
+                                         GAsyncResult   *result,
+                                         GError        **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_STAGE_LAUNCHER (stage));
+  g_assert (G_IS_TASK (result));
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+ide_build_stage_launcher_finalize (GObject *object)
+{
+  IdeBuildStageLauncher *self = (IdeBuildStageLauncher *)object;
+
+  g_clear_object (&self->launcher);
+
+  G_OBJECT_CLASS (ide_build_stage_launcher_parent_class)->finalize (object);
+}
+
+static void
+ide_build_stage_launcher_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  IdeBuildStageLauncher *self = (IdeBuildStageLauncher *)object;
+
+  switch (prop_id)
+    {
+    case PROP_LAUNCHER:
+      g_value_set_object (value, ide_build_stage_launcher_get_launcher (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_stage_launcher_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  IdeBuildStageLauncher *self = (IdeBuildStageLauncher *)object;
+
+  switch (prop_id)
+    {
+    case PROP_LAUNCHER:
+      self->launcher = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_stage_launcher_class_init (IdeBuildStageLauncherClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeBuildStageClass *build_stage_class = IDE_BUILD_STAGE_CLASS (klass);
+
+  object_class->finalize = ide_build_stage_launcher_finalize;
+  object_class->get_property = ide_build_stage_launcher_get_property;
+  object_class->set_property = ide_build_stage_launcher_set_property;
+
+  build_stage_class->execute_async = ide_build_stage_launcher_execute_async;
+  build_stage_class->execute_finish = ide_build_stage_launcher_execute_finish;
+
+  properties [PROP_LAUNCHER] =
+    g_param_spec_object ("launcher",
+                         "Launcher",
+                         "The subprocess launcher to execute",
+                         IDE_TYPE_SUBPROCESS_LAUNCHER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_build_stage_launcher_init (IdeBuildStageLauncher *self)
+{
+}
+
+/**
+ * ide_build_stage_launcher_get_launcher:
+ *
+ * Returns: (transfer none): An #IdeSubprocessLauncher
+ */
+IdeSubprocessLauncher *
+ide_build_stage_launcher_get_launcher (IdeBuildStageLauncher *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_STAGE_LAUNCHER (self), NULL);
+
+  return self->launcher;
+}
+
+IdeBuildStage *
+ide_build_stage_launcher_new (IdeContext            *context,
+                              IdeSubprocessLauncher *launcher)
+{
+  return g_object_new (IDE_TYPE_BUILD_STAGE_LAUNCHER,
+                       "context", context,
+                       "launcher", launcher,
+                       NULL);
+}
diff --git a/libide/buildsystem/ide-build-stage-launcher.h b/libide/buildsystem/ide-build-stage-launcher.h
new file mode 100644
index 0000000..e3eb2e8
--- /dev/null
+++ b/libide/buildsystem/ide-build-stage-launcher.h
@@ -0,0 +1,41 @@
+/* ide-build-stage-launcher.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_STAGE_LAUNCHER_H
+#define IDE_BUILD_STAGE_LAUNCHER_H
+
+#include <gio/gio.h>
+
+#include "ide-context.h"
+
+#include "buildsystem/ide-build-stage.h"
+#include "subprocess/ide-subprocess-launcher.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_STAGE_LAUNCHER (ide_build_stage_launcher_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeBuildStageLauncher, ide_build_stage_launcher, IDE, BUILD_STAGE_LAUNCHER, 
IdeBuildStage)
+
+IdeBuildStage         *ide_build_stage_launcher_new          (IdeContext            *context,
+                                                              IdeSubprocessLauncher *launcher);
+IdeSubprocessLauncher *ide_build_stage_launcher_get_launcher (IdeBuildStageLauncher *self);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_STAGE_LAUNCHER_H */
diff --git a/libide/buildsystem/ide-build-stage.c b/libide/buildsystem/ide-build-stage.c
new file mode 100644
index 0000000..87a13c9
--- /dev/null
+++ b/libide/buildsystem/ide-build-stage.c
@@ -0,0 +1,214 @@
+/* ide-build-stage.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-build-stage"
+
+#include "ide-build-stage.h"
+
+typedef struct
+{
+  gchar *name;
+} IdeBuildStagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBuildStage, ide_build_stage, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+ide_build_stage_real_execute (IdeBuildStage  *self,
+                              GCancellable   *cancellable,
+                              GError        **error)
+{
+  return TRUE;
+}
+
+static void
+ide_build_stage_real_execute_worker (GTask        *task,
+                                     gpointer      source_object,
+                                     gpointer      task_data,
+                                     GCancellable *cancellable)
+{
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_build_stage_real_execute_async (IdeBuildStage       *self,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_assert (IDE_IS_BUILD_STAGE (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_build_stage_real_execute_async);
+  g_task_run_in_thread (task, ide_build_stage_real_execute_worker);
+}
+
+static gboolean
+ide_build_stage_real_execute_finish (IdeBuildStage  *self,
+                                     GAsyncResult   *result,
+                                     GError        **error)
+{
+  g_assert (IDE_IS_BUILD_STAGE (self));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+IdeBuildStage *
+ide_build_stage_new (void)
+{
+  return g_object_new (IDE_TYPE_BUILD_STAGE, NULL);
+}
+
+const gchar *
+ide_build_stage_get_name (IdeBuildStage *self)
+{
+  IdeBuildStagePrivate *priv = ide_build_stage_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BUILD_STAGE (self), NULL);
+
+  return priv->name;
+}
+
+void
+ide_build_stage_set_name (IdeBuildStage *self,
+                          const gchar   *name)
+{
+  IdeBuildStagePrivate *priv = ide_build_stage_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BUILD_STAGE (self));
+
+  if (g_strcmp0 (name, priv->name) != 0)
+    {
+      g_free (priv->name);
+      priv->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
+    }
+}
+
+static void
+ide_build_stage_finalize (GObject *object)
+{
+  IdeBuildStage *self = (IdeBuildStage *)object;
+  IdeBuildStagePrivate *priv = ide_build_stage_get_instance_private (self);
+
+  g_clear_pointer (&priv->name, g_free);
+
+  G_OBJECT_CLASS (ide_build_stage_parent_class)->finalize (object);
+}
+
+static void
+ide_build_stage_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  IdeBuildStage *self = IDE_BUILD_STAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, ide_build_stage_get_name (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_stage_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  IdeBuildStage *self = IDE_BUILD_STAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      ide_build_stage_set_name (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_stage_class_init (IdeBuildStageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_build_stage_finalize;
+  object_class->get_property = ide_build_stage_get_property;
+  object_class->set_property = ide_build_stage_set_property;
+
+  klass->execute = ide_build_stage_real_execute;
+  klass->execute_async = ide_build_stage_real_execute_async;
+  klass->execute_finish = ide_build_stage_real_execute_finish;
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "The user visible name of the stage",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_build_stage_init (IdeBuildStage *self)
+{
+}
+
+void
+ide_build_stage_execute_async (IdeBuildStage       *self,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_BUILD_STAGE (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_BUILD_STAGE_GET_CLASS (self)->execute_async (self, cancellable, callback, user_data);
+}
+
+gboolean
+ide_build_stage_execute_finish (IdeBuildStage  *self,
+                                GAsyncResult   *result,
+                                GError        **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_STAGE (self), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  return IDE_BUILD_STAGE_GET_CLASS (self)->execute_finish (self, result, error);
+}
diff --git a/libide/buildsystem/ide-build-stage.h b/libide/buildsystem/ide-build-stage.h
new file mode 100644
index 0000000..90747c1
--- /dev/null
+++ b/libide/buildsystem/ide-build-stage.h
@@ -0,0 +1,76 @@
+/* ide-build-stage.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_STAGE_H
+#define IDE_BUILD_STAGE_H
+
+#include <gio/gio.h>
+
+#include "buildsystem/ide-configuration.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_STAGE (ide_build_stage_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeBuildStage, ide_build_stage, IDE, BUILD_STAGE, IdeObject)
+
+struct _IdeBuildStageClass
+{
+  IdeObjectClass parent_class;
+
+  gboolean (*execute)        (IdeBuildStage        *self,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  void     (*execute_async)  (IdeBuildStage        *self,
+                              GCancellable         *cancellable,
+                              GAsyncReadyCallback   callback,
+                              gpointer              user_data);
+  gboolean (*execute_finish) (IdeBuildStage        *self,
+                              GAsyncResult         *result,
+                              GError              **error);
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
+  gpointer _reserved9;
+  gpointer _reserved10;
+  gpointer _reserved11;
+  gpointer _reserved12;
+};
+
+IdeBuildStage *ide_build_stage_new            (void);
+const gchar   *ide_build_stage_get_name       (IdeBuildStage        *self);
+void           ide_build_stage_set_name       (IdeBuildStage        *self,
+                                               const gchar          *name);
+void           ide_build_stage_execute_async  (IdeBuildStage        *self,
+                                               GCancellable         *cancellable,
+                                               GAsyncReadyCallback   callback,
+                                               gpointer              user_data);
+gboolean       ide_build_stage_execute_finish (IdeBuildStage        *self,
+                                               GAsyncResult         *result,
+                                               GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_STAGE_H */
+
diff --git a/libide/ide-enums.c.in b/libide/ide-enums.c.in
index 0f71244..3b8c168 100644
--- a/libide/ide-enums.c.in
+++ b/libide/ide-enums.c.in
@@ -5,6 +5,7 @@
 #include "ide-enums.h"
 
 #include "buffers/ide-buffer.h"
+#include "buildsystem/ide-build-pipeline.h"
 #include "buildsystem/ide-build-result.h"
 #include "devices/ide-device.h"
 #include "diagnostics/ide-diagnostic.h"
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 230ebaa..49a6316 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -35,11 +35,13 @@ typedef struct _IdeBufferChangeMonitor         IdeBufferChangeMonitor;
 
 typedef struct _IdeBufferManager               IdeBufferManager;
 
-typedef struct _IdeBuilder                     IdeBuilder;
 typedef struct _IdeBuildCommand                IdeBuildCommand;
 typedef struct _IdeBuildCommandQueue           IdeBuildCommandQueue;
+typedef struct _IdeBuilder                     IdeBuilder;
 typedef struct _IdeBuildManager                IdeBuildManager;
+typedef struct _IdeBuildPipeline               IdeBuildPipeline;
 typedef struct _IdeBuildResult                 IdeBuildResult;
+typedef struct _IdeBuildStage                  IdeBuildStage;
 typedef struct _IdeBuildSystem                 IdeBuildSystem;
 typedef struct _IdeBuildTarget                 IdeBuildTarget;
 
diff --git a/libide/ide.h b/libide/ide.h
index 78aa113..415b24b 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -38,8 +38,12 @@ G_BEGIN_DECLS
 #include "buildsystem/ide-build-command.h"
 #include "buildsystem/ide-build-command-queue.h"
 #include "buildsystem/ide-build-manager.h"
+#include "buildsystem/ide-build-pipeline.h"
+#include "buildsystem/ide-build-pipeline-addin.h"
 #include "buildsystem/ide-build-result-addin.h"
 #include "buildsystem/ide-build-result.h"
+#include "buildsystem/ide-build-stage.h"
+#include "buildsystem/ide-build-stage-launcher.h"
 #include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-target.h"
 #include "buildsystem/ide-builder.h"


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